init
This commit is contained in:
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,20 @@
|
||||
enum TIMCallbackType { API_ERROR, FLUTTER_ERROR, INFO }
|
||||
|
||||
class TIMCallback {
|
||||
TIMCallbackType? type;
|
||||
String? errorMsg;
|
||||
int? errorCode;
|
||||
StackTrace? stackTrace;
|
||||
Object? catchError;
|
||||
int? infoCode;
|
||||
String? infoRecommendText;
|
||||
|
||||
TIMCallback(
|
||||
{this.catchError,
|
||||
this.infoRecommendText,
|
||||
this.errorMsg,
|
||||
this.errorCode,
|
||||
this.stackTrace,
|
||||
this.infoCode,
|
||||
this.type});
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'dart:io';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
|
||||
|
||||
abstract class TIMState<T extends StatefulWidget> extends State<T> {
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
// override这个函数来承接处理onTIMCallback回调。
|
||||
// 目前base层,会自动触发`TIMCallbackType.FLUTTER_ERROR`类型Flutter Framework异常。
|
||||
// 其他类型callback请自行处理。
|
||||
void onTIMCallback(TIMCallback callbackValue) {
|
||||
// TODO: 这里后续看看要不要默认加上报逻辑。
|
||||
}
|
||||
|
||||
bool isAndroidDevice(){
|
||||
return !kIsWeb && Platform.isAndroid;
|
||||
}
|
||||
|
||||
bool isIosDevice(){
|
||||
return !kIsWeb && Platform.isIOS;
|
||||
}
|
||||
|
||||
bool isWebDevice(){
|
||||
return kIsWeb;
|
||||
}
|
||||
|
||||
setTIMState(VoidCallback fn){
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
FlutterError.onError = (FlutterErrorDetails details) {
|
||||
FlutterError.presentError(details);
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.FLUTTER_ERROR,
|
||||
stackTrace: details.stack,
|
||||
errorMsg: "Error from Flutter"));
|
||||
};
|
||||
|
||||
return timBuild(context);
|
||||
}
|
||||
|
||||
// 请override这个方法来render界面
|
||||
Widget timBuild(BuildContext context);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
|
||||
abstract class TIMStatelessWidget extends StatelessWidget {
|
||||
const TIMStatelessWidget({Key? key}) : super(key: key);
|
||||
|
||||
// override这个函数来承接处理onTIMCallback回调。
|
||||
// 目前base层,会自动触发`TIMCallbackType.FLUTTER_ERROR`类型Flutter Framework异常。
|
||||
// 其他类型callback请自行处理。
|
||||
void onTIMCallback(TIMCallback callbackValue) {
|
||||
}
|
||||
|
||||
bool isAndroidDevice(){
|
||||
return !kIsWeb && Platform.isAndroid;
|
||||
}
|
||||
|
||||
bool isIosDevice(){
|
||||
return !kIsWeb && Platform.isIOS;
|
||||
}
|
||||
|
||||
bool isWebDevice(){
|
||||
return kIsWeb;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
FlutterError.onError = (FlutterErrorDetails details) {
|
||||
FlutterError.presentError(details);
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.FLUTTER_ERROR,
|
||||
stackTrace: details.stack,
|
||||
errorMsg: "Error from Flutter"));
|
||||
};
|
||||
|
||||
return timBuild(context);
|
||||
}
|
||||
|
||||
// 请override这个方法来render界面
|
||||
Widget timBuild(BuildContext context);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
class TUIKitBuildValue {
|
||||
// 这里预留以后可以扩展全局通用能力
|
||||
TUITheme theme;
|
||||
|
||||
TUIKitBuildValue({required this.theme});
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
|
||||
class TIMUIKitClass {
|
||||
static final CoreServicesImpl _coreServices =
|
||||
serviceLocator<CoreServicesImpl>();
|
||||
|
||||
static void onTIMCallback(TIMCallback callbackValue) {
|
||||
_coreServices.callOnCallback(callbackValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_state.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme_view_model.dart';
|
||||
|
||||
class TIMUIKitState<T extends StatefulWidget> extends TIMState<T> {
|
||||
final CoreServicesImpl _coreServices = serviceLocator<CoreServicesImpl>();
|
||||
|
||||
|
||||
@override
|
||||
void onTIMCallback(TIMCallback callbackValue) {
|
||||
super.onTIMCallback(callbackValue);
|
||||
_coreServices.callOnCallback(callbackValue);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget timBuild(BuildContext context) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider.value(
|
||||
value: serviceLocator<TUIThemeViewModel>()),
|
||||
],
|
||||
builder: (BuildContext context, Widget? w) {
|
||||
final theme = Provider.of<TUIThemeViewModel>(context).theme;
|
||||
final value = TUIKitBuildValue(theme: theme);
|
||||
return tuiBuild(context, value);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@required
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_stateless_widget.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme_view_model.dart';
|
||||
|
||||
class TIMUIKitStatelessWidget extends TIMStatelessWidget {
|
||||
final CoreServicesImpl _coreServices = serviceLocator<CoreServicesImpl>();
|
||||
|
||||
TIMUIKitStatelessWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
void onTIMCallback(TIMCallback callbackValue) {
|
||||
super.onTIMCallback(callbackValue);
|
||||
_coreServices.callOnCallback(callbackValue);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget timBuild(BuildContext context) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider.value(
|
||||
value: serviceLocator<TUIThemeViewModel>()),
|
||||
],
|
||||
builder: (BuildContext context, Widget? w) {
|
||||
final theme = Provider.of<TUIThemeViewModel>(context).theme;
|
||||
final value = TUIKitBuildValue(theme: theme);
|
||||
return tuiBuild(context, value);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@required
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/base_life_cycle.dart';
|
||||
|
||||
class AddFriendLifeCycle {
|
||||
/// Before requesting to add a user as friend or a contact,
|
||||
/// `true` means can add continually, while `false` will not add.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(
|
||||
String userID, String? remark, String? friendGroup, String? addWording,
|
||||
[BuildContext? context]) shouldAddFriend;
|
||||
|
||||
AddFriendLifeCycle({
|
||||
this.shouldAddFriend = DefaultLifeCycle.defaultAddFriend,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/base_life_cycle.dart';
|
||||
|
||||
class AddGroupLifeCycle {
|
||||
/// Before requesting to add or join to a group,
|
||||
/// `true` means can add continually, while `false` will not add.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(String groupID, String message, [BuildContext? context])
|
||||
shouldAddGroup;
|
||||
|
||||
AddGroupLifeCycle({
|
||||
this.shouldAddGroup = DefaultLifeCycle.defaultAddGroup,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.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_friend_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_class.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
|
||||
typedef MessageFunction = Future<V2TimMessage?> Function(V2TimMessage message);
|
||||
|
||||
typedef MessageFunctionNullCallback = Function(V2TimValueCallback<V2TimMessage> res);
|
||||
|
||||
typedef MessageFunctionOptional = Future<V2TimMessage?> Function(V2TimMessage message);
|
||||
|
||||
typedef MessageListFunction = Future<List<V2TimMessage>> Function(List<V2TimMessage> messageList);
|
||||
|
||||
typedef MessageListFunctionAsync = List<V2TimMessage> Function(List<V2TimMessage> messageList);
|
||||
|
||||
typedef FutureBool = Future<bool>;
|
||||
|
||||
typedef AddFriendFunction = Function(String userID, String? remark, String? friendGroup, String? addWording);
|
||||
|
||||
typedef ConversationListFunction = Future<List<V2TimConversation?>> Function(List<V2TimConversation?> conversationList);
|
||||
|
||||
typedef FriendListFunction = Future<List<V2TimFriendInfo>> Function(List<V2TimFriendInfo> friendList);
|
||||
|
||||
typedef FriendInfoFunction = Future<V2TimFriendInfo?> Function(V2TimFriendInfo? friendInfo);
|
||||
|
||||
/// Here is the default life cycle hooks implementation for all the hooks in TUIKit.
|
||||
abstract class DefaultLifeCycle {
|
||||
static Future<List<V2TimConversation?>> defaultConversationListSolution(List<V2TimConversation?> list) async {
|
||||
return list;
|
||||
}
|
||||
|
||||
static Future<List<V2TimFriendInfo>> defaultFriendListSolution(List<V2TimFriendInfo> list) async {
|
||||
return list;
|
||||
}
|
||||
|
||||
static Future<V2TimMessage> defaultMessageSolution(V2TimMessage message) async {
|
||||
return message;
|
||||
}
|
||||
|
||||
static Future<V2TimMessage> defaultTwoMessagesSolution(V2TimMessage message, [V2TimMessage? repliedMessage]) async {
|
||||
return message;
|
||||
}
|
||||
|
||||
static Future<List<V2TimMessage>> defaultMessageListSolution(List<V2TimMessage> list) async {
|
||||
return list;
|
||||
}
|
||||
|
||||
static List<V2TimMessage> defaultMessageListSolutionAsync(List<V2TimMessage> list) {
|
||||
return list;
|
||||
}
|
||||
|
||||
static Future<bool> defaultAsyncBooleanSolution(dynamic) async {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool defaultBooleanSolution(dynamic) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static defaultNullCallbackSolution(dynamic) {}
|
||||
|
||||
static Future<bool> defaultAddFriend(String userID, String? remark, String? friendGroup, String? addWording,
|
||||
[BuildContext? context]) async {
|
||||
return true;
|
||||
}
|
||||
|
||||
static Future<bool> defaultAddGroup(String groupID, String message, [BuildContext? context]) async {
|
||||
return true;
|
||||
}
|
||||
|
||||
static Future<V2TimFriendInfo?> defaultFriendInfoSolution(V2TimFriendInfo? friendInfo) async {
|
||||
return friendInfo;
|
||||
}
|
||||
|
||||
static Future<void> defaultPopBackRemind() async {
|
||||
// You have to implement the exact life cycle hook in this case.
|
||||
TIMUIKitClass.onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO, infoRecommendText: TIM_t("请传入离开群组生命周期函数,提供返回首页或其他页面的导航方法。"), infoCode: 6661402));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/base_life_cycle.dart';
|
||||
|
||||
class BlockListLifeCycle {
|
||||
/// Before requesting to delete a user from block list,
|
||||
/// `true` means can delete continually, while `false` will not delete.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(List<String> userIDList) shouldDeleteFromBlockList;
|
||||
|
||||
BlockListLifeCycle({
|
||||
this.shouldDeleteFromBlockList = DefaultLifeCycle.defaultAsyncBooleanSolution,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/base_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class ChatLifeCycle {
|
||||
/// Before a new message will be added to historical message list from long connection.
|
||||
/// You may not render this message by return null.
|
||||
MessageFunctionOptional newMessageWillMount;
|
||||
|
||||
/// Before a modified message updated to historical message list UI.
|
||||
MessageFunction modifiedMessageWillMount;
|
||||
|
||||
/// Before a new message will be sent.
|
||||
/// Returns null can block the message from sending.
|
||||
// Future<V2TimMessage?> Function(V2TimMessage message, [V2TimMessage? repliedMessage]) messageWillSend;
|
||||
|
||||
/// After a new message been sent.
|
||||
MessageFunctionNullCallback messageDidSend;
|
||||
|
||||
/// After getting the latest message list from API,
|
||||
/// and before historical message list will be rendered.
|
||||
/// You may add or delete some messages here.
|
||||
MessageListFunction didGetHistoricalMessageList;
|
||||
|
||||
/// Before deleting a message from historical message list,
|
||||
/// `true` means can delete continually, while `false` will not delete.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(String msgID) shouldDeleteMessage;
|
||||
|
||||
/// Before clearing the historical message list,
|
||||
/// `true` means can clear continually, while `false` will not clear.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(String conversationID) shouldClearHistoricalMessageList;
|
||||
|
||||
/// Before rendering a message to message list.
|
||||
bool Function(V2TimMessage msg) messageShouldMount;
|
||||
|
||||
/// Before all message will be rendered on the message list.
|
||||
/// You may add or delete some messages here.
|
||||
MessageListFunctionAsync messageListShouldMount;
|
||||
|
||||
ChatLifeCycle({
|
||||
this.shouldClearHistoricalMessageList = DefaultLifeCycle.defaultAsyncBooleanSolution,
|
||||
this.shouldDeleteMessage = DefaultLifeCycle.defaultAsyncBooleanSolution,
|
||||
this.messageDidSend = DefaultLifeCycle.defaultNullCallbackSolution,
|
||||
this.didGetHistoricalMessageList = DefaultLifeCycle.defaultMessageListSolution,
|
||||
// this.messageWillSend = DefaultLifeCycle.defaultTwoMessagesSolution,
|
||||
this.modifiedMessageWillMount = DefaultLifeCycle.defaultMessageSolution,
|
||||
this.newMessageWillMount = DefaultLifeCycle.defaultMessageSolution,
|
||||
this.messageShouldMount = DefaultLifeCycle.defaultBooleanSolution,
|
||||
this.messageListShouldMount = DefaultLifeCycle.defaultMessageListSolutionAsync,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/base_life_cycle.dart';
|
||||
|
||||
class ConversationLifeCycle {
|
||||
/// Before deleting a conversation, or a channel, from the conversation list,
|
||||
/// `true` means can delete continually, while `false` will not delete.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(String conversationID) shouldDeleteConversation;
|
||||
|
||||
/// Before clearing the historical message for a specific conversation, provided in parameter,
|
||||
/// `true` means can clear continually, while `false` will not clear.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(String conversationID)
|
||||
shouldClearHistoricalMessageForConversation;
|
||||
|
||||
/// Before conversation list will mount or update to conversation page.
|
||||
ConversationListFunction conversationListWillMount;
|
||||
|
||||
ConversationLifeCycle({
|
||||
this.conversationListWillMount =
|
||||
DefaultLifeCycle.defaultConversationListSolution,
|
||||
this.shouldClearHistoricalMessageForConversation =
|
||||
DefaultLifeCycle.defaultAsyncBooleanSolution,
|
||||
this.shouldDeleteConversation = DefaultLifeCycle.defaultAsyncBooleanSolution,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/base_life_cycle.dart';
|
||||
|
||||
class FriendListLifeCycle {
|
||||
/// Before friend list (contacts list) will mount or update to contacts page.
|
||||
FriendListFunction friendListWillMount;
|
||||
|
||||
FriendListLifeCycle({
|
||||
this.friendListWillMount = DefaultLifeCycle.defaultFriendListSolution,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/base_life_cycle.dart';
|
||||
|
||||
class GroupProfileLifeCycle {
|
||||
/// In this case, you have better navigating to you home page or conversation list page,
|
||||
/// due to user request to leave the group, as quitting or disbanding.
|
||||
Future<void> Function() didLeaveGroup;
|
||||
|
||||
GroupProfileLifeCycle({
|
||||
this.didLeaveGroup = DefaultLifeCycle.defaultPopBackRemind,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/base_life_cycle.dart';
|
||||
|
||||
class NewContactLifeCycle {
|
||||
/// Before accepting a friend or contact requirement from other user,
|
||||
/// `true` means can accept continually, while `false` will not accept.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(String userID) shouldAcceptContactApplication;
|
||||
|
||||
/// Before refusing a friend or contact requirement from other user,
|
||||
/// `true` means can refuse continually, while `false` will not refuse.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(String userID) shouldRefuseContactApplication;
|
||||
|
||||
NewContactLifeCycle({
|
||||
this.shouldAcceptContactApplication =
|
||||
DefaultLifeCycle.defaultAsyncBooleanSolution,
|
||||
this.shouldRefuseContactApplication =
|
||||
DefaultLifeCycle.defaultAsyncBooleanSolution,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/base_life_cycle.dart';
|
||||
|
||||
class ProfileLifeCycle {
|
||||
/// Before adding a contact to block list,
|
||||
/// `true` means can add continually, while `false` will not add.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(String userID) shouldAddToBlockList;
|
||||
|
||||
/// Before deleting a contact or friend,
|
||||
/// `true` means can delete continually, while `false` will not delete.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(String userID) shouldDeleteFriend;
|
||||
|
||||
/// Before requesting to add a user as friend or a contact,
|
||||
/// `true` means can add continually, while `false` will not add.
|
||||
/// You can make a second confirmation here by a modal, etc.
|
||||
FutureBool Function(String userID) shouldAddFriend;
|
||||
|
||||
/// After getting the user info of friend or contact,
|
||||
/// and before rendering it to the profile page.
|
||||
FriendInfoFunction didGetFriendInfo;
|
||||
|
||||
/// The callback after the remark to a friend changed.
|
||||
FutureBool Function(String userID) didRemarkUpdated;
|
||||
|
||||
ProfileLifeCycle({
|
||||
this.didRemarkUpdated = DefaultLifeCycle.defaultAsyncBooleanSolution,
|
||||
this.didGetFriendInfo = DefaultLifeCycle.defaultFriendInfoSolution,
|
||||
this.shouldAddToBlockList = DefaultLifeCycle.defaultAsyncBooleanSolution,
|
||||
this.shouldAddFriend = DefaultLifeCycle.defaultAsyncBooleanSolution,
|
||||
this.shouldDeleteFriend = DefaultLifeCycle.defaultAsyncBooleanSolution,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
// ignore_for_file: unnecessary_getters_setters
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimGroupListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/group_change_info_type.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/manager/v2_tim_manager.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_change_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_change_info.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/models/v2_tim_group_info_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_info_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_topic_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_topic_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.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/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
enum UpdateType { groupInfo, memberList, joinApplicationList, groupDismissed, kickedFromGroup }
|
||||
|
||||
class NeedUpdate {
|
||||
final String groupID;
|
||||
final UpdateType updateType;
|
||||
final String extraData;
|
||||
int? groupInfoSubType;
|
||||
String? ownerID;
|
||||
|
||||
NeedUpdate(this.groupID, this.updateType, this.extraData);
|
||||
}
|
||||
|
||||
class TUIGroupListenerModel extends ChangeNotifier {
|
||||
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
||||
V2TimGroupListener? _groupListener;
|
||||
NeedUpdate? _needUpdate;
|
||||
final TUIChatGlobalModel chatViewModel = serviceLocator<TUIChatGlobalModel>();
|
||||
late CoreServicesImpl coreInstance = TIMUIKitCore.getInstance();
|
||||
late V2TIMManager sdkInstance = TIMUIKitCore.getSDKInstance();
|
||||
|
||||
NeedUpdate? get needUpdate => _needUpdate;
|
||||
|
||||
set needUpdate(NeedUpdate? value) {
|
||||
Future.delayed(const Duration(seconds: 0), () {
|
||||
_needUpdate = value;
|
||||
});
|
||||
}
|
||||
|
||||
TUIGroupListenerModel() {
|
||||
_groupListener = V2TimGroupListener(onMemberInvited: (groupID, opUser, memberList) {
|
||||
_needUpdate = NeedUpdate(groupID, UpdateType.memberList, "");
|
||||
notifyListeners();
|
||||
}, onMemberKicked: (groupID, opUser, memberList) async {
|
||||
if (_isLoginUserKickedFromGroup(groupID, memberList)) {
|
||||
_deleteGroupConversation(groupID);
|
||||
|
||||
final groupName = await _getGroupName(groupID);
|
||||
_needUpdate = NeedUpdate(groupID, UpdateType.kickedFromGroup, groupName);
|
||||
notifyListeners();
|
||||
}
|
||||
}, onMemberEnter: (String groupID, List<V2TimGroupMemberInfo> memberList) {
|
||||
_needUpdate = NeedUpdate(groupID, UpdateType.memberList, "");
|
||||
notifyListeners();
|
||||
}, onMemberLeave: (String groupID, V2TimGroupMemberInfo member) {
|
||||
_needUpdate = NeedUpdate(groupID, UpdateType.memberList, "");
|
||||
notifyListeners();
|
||||
}, onGroupInfoChanged: (groupID, changeInfos) {
|
||||
_needUpdate = NeedUpdate(groupID, UpdateType.groupInfo, "");
|
||||
for (V2TimGroupChangeInfo info in changeInfos) {
|
||||
if (info.type == GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_OWNER) {
|
||||
_needUpdate!.groupInfoSubType = GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_OWNER;
|
||||
_needUpdate!.ownerID = info.value;
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
}, onReceiveJoinApplication: (String groupID, V2TimGroupMemberInfo member, String opReason) async {
|
||||
_onReceiveJoinApplication(groupID, member, opReason);
|
||||
chatViewModel.refreshGroupApplicationList();
|
||||
notifyListeners();
|
||||
}, onGroupDismissed: (String groupID, V2TimGroupMemberInfo opUser) async {
|
||||
_deleteGroupConversation(groupID);
|
||||
final groupName = await _getGroupName(groupID);
|
||||
_needUpdate = NeedUpdate(groupID, UpdateType.groupDismissed, groupName);
|
||||
notifyListeners();
|
||||
});
|
||||
}
|
||||
|
||||
setGroupListener() {
|
||||
_groupServices.addGroupListener(listener: _groupListener!);
|
||||
}
|
||||
|
||||
removeGroupListener() {
|
||||
_groupServices.removeGroupListener(listener: _groupListener!);
|
||||
}
|
||||
|
||||
getCommunityCategoryList(String groupID) async {
|
||||
final Map<String, String>? customInfo = await getCommunityCustomInfo(groupID);
|
||||
if (customInfo != null) {
|
||||
final String? categoryListString = customInfo["categoryList"];
|
||||
if (categoryListString != null && categoryListString.isNotEmpty) {
|
||||
return jsonDecode(categoryListString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, String>?> getCommunityCustomInfo(String groupID) async {
|
||||
V2TimValueCallback<List<V2TimGroupInfoResult>> res =
|
||||
await TencentImSDKPlugin.v2TIMManager.getGroupManager().getGroupsInfo(groupIDList: [groupID]);
|
||||
if (res.code != 0) {
|
||||
final V2TimGroupInfoResult? groupInfo = res.data?[0];
|
||||
if (groupInfo != null) {
|
||||
Map<String, String>? customInfo = groupInfo.groupInfo?.customInfo;
|
||||
return customInfo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
setCommunityCategoryList(String groupID, String groupType, List<String> newCategoryList) async {
|
||||
final Map<String, String>? customInfo = await getCommunityCustomInfo(groupID);
|
||||
customInfo?["categoryList"] = jsonEncode(newCategoryList);
|
||||
TencentImSDKPlugin.v2TIMManager.getGroupManager().setGroupInfo(
|
||||
info: V2TimGroupInfo(
|
||||
customInfo: customInfo,
|
||||
groupID: groupID,
|
||||
groupType: groupType,
|
||||
// ...其他资料
|
||||
));
|
||||
}
|
||||
|
||||
addCategoryForTopic(String groupID, String categoryName) {
|
||||
TencentImSDKPlugin.v2TIMManager.getGroupManager().setTopicInfo(
|
||||
topicInfo: V2TimTopicInfo(customString: categoryName),
|
||||
);
|
||||
}
|
||||
|
||||
_onReceiveJoinApplication(String groupID, V2TimGroupMemberInfo member, String opReason) {
|
||||
Future.delayed(const Duration(milliseconds: 500), () => chatViewModel.refreshGroupApplicationList());
|
||||
}
|
||||
|
||||
Future<String> _getGroupName(String groupID) async {
|
||||
final groupInfoList = await sdkInstance.getGroupManager().getGroupsInfo(groupIDList: [groupID]);
|
||||
String groupName = TIM_t("群组");
|
||||
if (groupInfoList.data != null) {
|
||||
groupName = groupInfoList.data!.first!.groupInfo!.groupName!;
|
||||
}
|
||||
return groupName;
|
||||
}
|
||||
|
||||
void _deleteGroupConversation(String groupID) async {
|
||||
sdkInstance.getConversationManager().deleteConversation(conversationID: "group_${groupID}");
|
||||
}
|
||||
|
||||
bool _isLoginUserKickedFromGroup(String groupID, List<V2TimGroupMemberInfo> memberList) {
|
||||
final loginUserInfo = coreInstance.loginInfo;
|
||||
int index = memberList.indexWhere((element) => element.userID == loginUserInfo.userID);
|
||||
if (index > -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
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_friend_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_info.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class UserProfile {
|
||||
late V2TimFriendInfo? friendInfo;
|
||||
late V2TimConversation? conversation;
|
||||
|
||||
UserProfile({required this.friendInfo, required this.conversation});
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/offlinePushInfo.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_cloud_custom_data.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
|
||||
class TUIChatModelTools {
|
||||
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
|
||||
final CoreServicesImpl _coreServices = serviceLocator<CoreServicesImpl>();
|
||||
|
||||
OfflinePushInfo buildMessagePushInfo(V2TimMessage message, String convID, ConvType convType) {
|
||||
String createJSON(String convID) {
|
||||
return "{\"conversationID\": \"$convID\"}";
|
||||
}
|
||||
|
||||
if (globalModel.chatConfig.offlinePushInfo != null) {
|
||||
final customData = globalModel.chatConfig.offlinePushInfo!(message, convID, convType);
|
||||
if (customData != null) {
|
||||
return customData;
|
||||
}
|
||||
}
|
||||
|
||||
String title = globalModel.chatConfig.notificationTitle;
|
||||
|
||||
// If user provides null, use default ext.
|
||||
String ext = globalModel.chatConfig.notificationExt != null
|
||||
? globalModel.chatConfig.notificationExt!(message, convID, convType) ??
|
||||
(convType == ConvType.c2c ? createJSON("c2c_${message.sender}") : createJSON("group_$convID"))
|
||||
: (convType == ConvType.c2c ? createJSON("c2c_${message.sender}") : createJSON("group_$convID"));
|
||||
|
||||
String desc = message.userID ?? message.groupID ?? "";
|
||||
String messageSummary = "";
|
||||
switch (message.elemType) {
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_CUSTOM:
|
||||
messageSummary = TIM_t("自定义消息");
|
||||
break;
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_FACE:
|
||||
messageSummary = TIM_t("表情消息");
|
||||
break;
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_FILE:
|
||||
messageSummary = TIM_t("文件消息");
|
||||
break;
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS:
|
||||
messageSummary = TIM_t("群提示消息");
|
||||
break;
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_IMAGE:
|
||||
messageSummary = TIM_t("图片消息");
|
||||
break;
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_LOCATION:
|
||||
messageSummary = TIM_t("位置消息");
|
||||
break;
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
|
||||
messageSummary = TIM_t("合并转发消息");
|
||||
break;
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_SOUND:
|
||||
messageSummary = TIM_t("语音消息");
|
||||
break;
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_TEXT:
|
||||
messageSummary = message.textElem!.text!;
|
||||
break;
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_VIDEO:
|
||||
messageSummary = TIM_t("视频消息");
|
||||
break;
|
||||
}
|
||||
|
||||
if (globalModel.chatConfig.notificationBody != null) {
|
||||
desc = globalModel.chatConfig.notificationBody!(message, convID, convType) ?? messageSummary;
|
||||
} else {
|
||||
desc = messageSummary;
|
||||
}
|
||||
|
||||
return OfflinePushInfo(
|
||||
title: title,
|
||||
desc: desc,
|
||||
disablePush: false,
|
||||
ext: ext,
|
||||
iOSSound: globalModel.chatConfig.notificationIOSSound,
|
||||
androidSound: globalModel.chatConfig.notificationAndroidSound,
|
||||
ignoreIOSBadge: false,
|
||||
androidOPPOChannelID: globalModel.chatConfig.notificationOPPOChannelID,
|
||||
androidVIVOClassification: 1,
|
||||
);
|
||||
}
|
||||
|
||||
V2TimMessage setUserInfoForMessage(V2TimMessage messageInfo, String? id) {
|
||||
final loginUserInfo = _coreServices.loginUserInfo;
|
||||
if (loginUserInfo != null) {
|
||||
messageInfo.faceUrl = loginUserInfo.faceUrl;
|
||||
messageInfo.nickName = loginUserInfo.nickName;
|
||||
messageInfo.sender = loginUserInfo.userID;
|
||||
}
|
||||
messageInfo.timestamp = (DateTime.now().millisecondsSinceEpoch / 1000).ceil();
|
||||
messageInfo.isSelf = true;
|
||||
messageInfo.id = id;
|
||||
|
||||
return messageInfo;
|
||||
}
|
||||
|
||||
String getMessageSummary(V2TimMessage message, String? Function(V2TimMessage message)? abstractMessageBuilder) {
|
||||
final String? customAbstractMessage = abstractMessageBuilder != null ? abstractMessageBuilder(message) : null;
|
||||
if (customAbstractMessage != null) {
|
||||
return customAbstractMessage;
|
||||
}
|
||||
|
||||
final elemType = message.elemType;
|
||||
switch (elemType) {
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_FACE:
|
||||
return "[表情消息]";
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_CUSTOM:
|
||||
return "[自定义消息]";
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_FILE:
|
||||
return "[文件消息]";
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS:
|
||||
return "[群消息]";
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_IMAGE:
|
||||
return "[图片消息]";
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_LOCATION:
|
||||
return "[位置消息]";
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
|
||||
return "[合并消息]";
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_NONE:
|
||||
return "[没有元素]";
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_SOUND:
|
||||
return "[语音消息]";
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_TEXT:
|
||||
return message.textElem?.text ?? "[文本消息]";
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_VIDEO:
|
||||
return "[视频消息]";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
String getMessageAbstract(V2TimMessage message, String? Function(V2TimMessage message)? abstractMessageBuilder) {
|
||||
final messageAbstract = RepliedMessageAbstract(
|
||||
summary: TIM_t(getMessageSummary(message, abstractMessageBuilder)),
|
||||
elemType: message.elemType,
|
||||
msgID: message.msgID,
|
||||
timestamp: message.timestamp,
|
||||
seq: message.seq);
|
||||
return jsonEncode(messageAbstract.toJson());
|
||||
}
|
||||
|
||||
Future<V2TimMessage?> getExistingMessageByID(
|
||||
{required String msgID, required String conversationID, required ConvType conversationType}) async {
|
||||
final currentHistoryMsgList = globalModel.messageListMap[conversationID] ?? [];
|
||||
final int? targetIndex = currentHistoryMsgList.indexWhere((item) {
|
||||
return item.msgID == msgID;
|
||||
});
|
||||
|
||||
if (targetIndex != null && targetIndex > -1 && currentHistoryMsgList.isNotEmpty) {
|
||||
return currentHistoryMsgList[targetIndex];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> hasZeroSize(String filePath) async {
|
||||
try {
|
||||
final file = File(filePath);
|
||||
final fileSize = await file.length();
|
||||
return fileSize == 0;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,363 @@
|
||||
// ignore_for_file: unnecessary_getters_setters, avoid_print
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/group_member_filter_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/group_member_role.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/group_member_role_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/group_type.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/receive_message_opt_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.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_friend_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_info.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/models/v2_tim_group_member_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_operation_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_operation_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_search_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_search_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/group_profile_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/conversation/conversation_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
|
||||
class TUIGroupProfileModel extends ChangeNotifier {
|
||||
final CoreServicesImpl _coreServices = serviceLocator<CoreServicesImpl>();
|
||||
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
||||
final ConversationService _conversationService = serviceLocator<ConversationService>();
|
||||
final MessageService _messageService = serviceLocator<MessageService>();
|
||||
final FriendshipServices _friendshipServices = serviceLocator<FriendshipServices>();
|
||||
GroupProfileLifeCycle? _lifeCycle;
|
||||
|
||||
V2TimConversation? _conversation;
|
||||
String _groupID = "";
|
||||
List<V2TimFriendInfo>? _contactList;
|
||||
List<V2TimGroupMemberFullInfo?>? _groupMemberList;
|
||||
String _groupMemberListSeq = "0";
|
||||
V2TimGroupInfo? _groupInfo;
|
||||
Function(V2TimGroupMemberFullInfo groupMemberFullInfo, TapDownDetails? tapDetails)? onClickUser;
|
||||
|
||||
GroupProfileLifeCycle? get lifeCycle => _lifeCycle;
|
||||
|
||||
set lifeCycle(GroupProfileLifeCycle? value) {
|
||||
_lifeCycle = value;
|
||||
}
|
||||
|
||||
V2TimConversation? get conversation => _conversation;
|
||||
|
||||
set conversation(V2TimConversation? value) {
|
||||
_conversation = value;
|
||||
}
|
||||
|
||||
String get groupID => _groupID;
|
||||
|
||||
set groupID(String value) {
|
||||
_groupID = value;
|
||||
}
|
||||
|
||||
List<V2TimFriendInfo> get contactList => _contactList ?? [];
|
||||
|
||||
set contactList(List<V2TimFriendInfo> value) {
|
||||
_contactList = value;
|
||||
}
|
||||
|
||||
List<V2TimGroupMemberFullInfo?> get groupMemberList => _groupMemberList ?? [];
|
||||
|
||||
set groupMemberList(List<V2TimGroupMemberFullInfo?> value) {
|
||||
_groupMemberList = value;
|
||||
}
|
||||
|
||||
V2TimGroupInfo? get groupInfo => _groupInfo;
|
||||
|
||||
set groupInfo(V2TimGroupInfo? value) {
|
||||
_groupInfo = value;
|
||||
}
|
||||
|
||||
void loadData(String groupID) {
|
||||
_groupID = groupID;
|
||||
loadGroupInfo(groupID);
|
||||
loadGroupMemberList(groupID: groupID);
|
||||
_loadConversation();
|
||||
_loadContactList();
|
||||
}
|
||||
|
||||
loadGroupInfo(String groupID) async {
|
||||
final groupInfo = await _groupServices.getGroupsInfo(groupIDList: [groupID]);
|
||||
if (groupInfo != null) {
|
||||
final groupRes = groupInfo.first;
|
||||
if (groupRes.resultCode == 0) {
|
||||
_groupInfo = groupRes.groupInfo;
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> loadGroupMemberList({required String groupID, int count = 100, String? seq}) async {
|
||||
final String? nextSeq = await _loadGroupMemberListFunction(groupID: groupID, seq: seq, count: count);
|
||||
if (nextSeq != null && nextSeq != "0" && nextSeq != "") {
|
||||
return await loadGroupMemberList(groupID: groupID, count: count, seq: nextSeq);
|
||||
} else {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> _loadGroupMemberListFunction({required String groupID, int count = 100, String? seq}) async {
|
||||
if (seq == null || seq == "" || seq == "0") {
|
||||
_groupMemberList?.clear();
|
||||
}
|
||||
final res = await _groupServices.getGroupMemberList(
|
||||
groupID: groupID,
|
||||
filter: GroupMemberFilterTypeEnum.V2TIM_GROUP_MEMBER_FILTER_ALL,
|
||||
count: count,
|
||||
nextSeq: seq ?? _groupMemberListSeq);
|
||||
final groupMemberListRes = res.data;
|
||||
if (res.code == 0 && groupMemberListRes != null) {
|
||||
final groupMemberListTemp = groupMemberListRes.memberInfoList ?? [];
|
||||
// TODO
|
||||
outputLogger.i(
|
||||
"loadGroupMemberListfinish,groupMemberListTemp, ${groupMemberListRes.nextSeq}, ${groupMemberListTemp.length}");
|
||||
_groupMemberList = [...?_groupMemberList, ...groupMemberListTemp];
|
||||
_groupMemberListSeq = groupMemberListRes.nextSeq ?? "0";
|
||||
}
|
||||
return groupMemberListRes?.nextSeq;
|
||||
}
|
||||
|
||||
_loadConversation() async {
|
||||
conversation = await _conversationService.getConversation(conversationID: "group_$_groupID");
|
||||
}
|
||||
|
||||
_loadContactList() async {
|
||||
final res = await _friendshipServices.getFriendList();
|
||||
_contactList = res;
|
||||
}
|
||||
|
||||
pinedConversation(bool isPined) async {
|
||||
await _conversationService.pinConversation(conversationID: "group_$_groupID", isPinned: isPined);
|
||||
conversation?.isPinned = isPined;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
setMessageDisturb(bool value) async {
|
||||
final res = await _messageService.setGroupReceiveMessageOpt(
|
||||
groupID: _groupID,
|
||||
opt: value ? ReceiveMsgOptEnum.V2TIM_RECEIVE_NOT_NOTIFY_MESSAGE : ReceiveMsgOptEnum.V2TIM_RECEIVE_MESSAGE);
|
||||
if (res.code == 0) {
|
||||
conversation?.recvOpt =
|
||||
(value ? ReceiveMsgOptEnum.V2TIM_RECEIVE_NOT_NOTIFY_MESSAGE : ReceiveMsgOptEnum.V2TIM_RECEIVE_MESSAGE).index;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<V2TimValueCallback<V2GroupMemberInfoSearchResult>> searchGroupMember(
|
||||
V2TimGroupMemberSearchParam searchParam) async {
|
||||
final res = await _groupServices.searchGroupMembers(searchParam: searchParam);
|
||||
|
||||
if (res.code == 0) {}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<V2TimCallback?> setGroupName(String groupName) async {
|
||||
if (_groupInfo != null) {
|
||||
String? originalGroupName = _groupInfo?.groupName;
|
||||
_groupInfo?.groupName = groupName;
|
||||
V2TimGroupInfo v2timGroupInfo = V2TimGroupInfo(groupID: _groupID, groupType: _groupInfo!.groupType);
|
||||
v2timGroupInfo.groupName = groupName;
|
||||
final response = await _groupServices.setGroupInfo(info: v2timGroupInfo);
|
||||
if (response.code != 0) {
|
||||
_groupInfo?.groupName = originalGroupName;
|
||||
}
|
||||
notifyListeners();
|
||||
return response;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
setGroupNotification(String notification) async {
|
||||
if (_groupInfo != null) {
|
||||
V2TimGroupInfo v2timGroupInfo = V2TimGroupInfo(groupID: _groupID, groupType: _groupInfo!.groupType);
|
||||
v2timGroupInfo.notification = notification;
|
||||
final response = await _groupServices.setGroupInfo(info: v2timGroupInfo);
|
||||
if (response.code == 0) {
|
||||
notifyListeners();
|
||||
_groupInfo?.notification = notification;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getSelfNameCard() {
|
||||
try {
|
||||
final loginUserID = _coreServices.loginUserInfo?.userID;
|
||||
String nameCard = "";
|
||||
if (_groupMemberList != null) {
|
||||
nameCard = groupMemberList.firstWhere((element) => element?.userID == loginUserID)?.nameCard ?? "";
|
||||
}
|
||||
|
||||
return nameCard;
|
||||
} catch (err) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
Future<V2TimCallback?> setNameCard(String nameCard) async {
|
||||
final loginUserID = _coreServices.loginUserInfo?.userID;
|
||||
if (loginUserID != null) {
|
||||
final res = await _groupServices.setGroupMemberInfo(groupID: _groupID, userID: loginUserID, nameCard: nameCard);
|
||||
if (res.code == 0) {
|
||||
final targetIndex = _groupMemberList?.indexWhere((element) => element?.userID == loginUserID);
|
||||
if (targetIndex != -1) {
|
||||
_groupMemberList![targetIndex!]!.nameCard = nameCard;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<V2TimCallback?> setGroupAddOpt(int addOpt) async {
|
||||
if (_groupInfo != null) {
|
||||
int? originalAddopt = _groupInfo?.groupAddOpt;
|
||||
_groupInfo?.groupAddOpt = addOpt;
|
||||
V2TimGroupInfo v2timGroupInfo = V2TimGroupInfo(groupID: _groupID, groupType: _groupInfo!.groupType);
|
||||
v2timGroupInfo.groupAddOpt = addOpt;
|
||||
final response = await _groupServices.setGroupInfo(info: v2timGroupInfo);
|
||||
if (response.code != 0) {
|
||||
_groupInfo?.groupAddOpt = originalAddopt;
|
||||
}
|
||||
notifyListeners();
|
||||
return response;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<V2TimCallback> setMemberToNormal(String userID) async {
|
||||
final res = await _groupServices.setGroupMemberRole(
|
||||
groupID: _groupID, userID: userID, role: GroupMemberRoleTypeEnum.V2TIM_GROUP_MEMBER_ROLE_MEMBER);
|
||||
if (res.code == 0) {
|
||||
final targetIndex = _groupMemberList!.indexWhere((e) => e!.userID == userID);
|
||||
if (targetIndex != -1) {
|
||||
final targetElem = _groupMemberList![targetIndex];
|
||||
targetElem?.role = GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER;
|
||||
_groupMemberList![targetIndex] = targetElem;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<V2TimCallback> setMemberToAdmin(String userID) async {
|
||||
final res = await _groupServices.setGroupMemberRole(
|
||||
groupID: _groupID, userID: userID, role: GroupMemberRoleTypeEnum.V2TIM_GROUP_MEMBER_ROLE_ADMIN);
|
||||
if (res.code == 0) {
|
||||
final targetIndex = _groupMemberList!.indexWhere((e) => e!.userID == userID);
|
||||
if (targetIndex != -1) {
|
||||
final targetElem = _groupMemberList![targetIndex];
|
||||
targetElem?.role = GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN;
|
||||
_groupMemberList![targetIndex] = targetElem;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void onOwnerChanged(String? userID) {
|
||||
if (userID == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 把之前的群主更新为普通成员
|
||||
final preOwnerIndex =
|
||||
_groupMemberList!.indexWhere((e) => e!.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER);
|
||||
if (preOwnerIndex != -1) {
|
||||
final preOwnerElem = _groupMemberList![preOwnerIndex];
|
||||
preOwnerElem?.role = GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_MEMBER;
|
||||
|
||||
print("preOwnerUserID: ${preOwnerElem?.userID}");
|
||||
}
|
||||
|
||||
// 设置新的群主
|
||||
final targetIndex = _groupMemberList!.indexWhere((e) => e!.userID == userID);
|
||||
if (targetIndex != -1) {
|
||||
final targetElem = _groupMemberList![targetIndex];
|
||||
targetElem?.role = GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER;
|
||||
_groupMemberList![targetIndex] = targetElem;
|
||||
|
||||
print("newOwnerUserID: ${targetElem?.userID}");
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool canInviteMember() {
|
||||
final groupType = _groupInfo?.groupType;
|
||||
return groupType == GroupType.Work || groupType == "Private";
|
||||
}
|
||||
|
||||
bool canKickOffMember() {
|
||||
final isGroupOwner = _groupInfo?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER;
|
||||
final isAdmin = _groupInfo?.role == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN;
|
||||
if (_groupInfo?.groupType == GroupType.Work) {
|
||||
/// work 群主才能踢人
|
||||
return isGroupOwner;
|
||||
}
|
||||
|
||||
if (_groupInfo?.groupType == GroupType.Public || _groupInfo?.groupType == GroupType.Meeting) {
|
||||
/// public || meeting 群主和管理员可以踢人
|
||||
return isGroupOwner || isAdmin;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<V2TimCallback?> setMuteAll(bool muteAll) async {
|
||||
if (_groupInfo != null) {
|
||||
_groupInfo?.isAllMuted = muteAll;
|
||||
V2TimGroupInfo v2timGroupInfo = V2TimGroupInfo(groupID: _groupID, groupType: _groupInfo!.groupType);
|
||||
v2timGroupInfo.isAllMuted = muteAll;
|
||||
final response = await _groupServices.setGroupInfo(info: v2timGroupInfo);
|
||||
if (response.code != 0) {
|
||||
_groupInfo?.isAllMuted = muteAll;
|
||||
}
|
||||
notifyListeners();
|
||||
return response;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<V2TimCallback?> muteGroupMember(String userID, bool isMute, int? serverTime) async {
|
||||
const muteTime = 315360000;
|
||||
final res = await _groupServices.muteGroupMember(groupID: _groupID, userID: userID, seconds: isMute ? muteTime : 0);
|
||||
if (res.code == 0) {
|
||||
final targetIndex = _groupMemberList!.indexWhere((e) => e!.userID == userID);
|
||||
if (targetIndex != -1) {
|
||||
final targetElem = _groupMemberList![targetIndex];
|
||||
targetElem?.muteUntil = isMute ? (serverTime ?? 0) + muteTime : 0;
|
||||
_groupMemberList![targetIndex] = targetElem;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<V2TimCallback> kickOffMember(List<String> userIDs) async {
|
||||
final res = await _groupServices.kickGroupMember(groupID: _groupID, memberList: userIDs);
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<V2TimValueCallback<List<V2TimGroupMemberOperationResult>>> inviteUserToGroup(List<String> userIDS) async {
|
||||
final res = await _groupServices.inviteUserToGroup(groupID: _groupID, userList: userIDS);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/friend_type_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/receive_message_opt_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.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_friend_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_operation_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/profile_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/model/profile_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/conversation/conversation_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
|
||||
class TUIProfileViewModel extends ChangeNotifier {
|
||||
final ConversationService _conversationService = serviceLocator<ConversationService>();
|
||||
final FriendshipServices _friendshipServices = serviceLocator<FriendshipServices>();
|
||||
final TUIFriendShipViewModel _friendShipViewModel = serviceLocator<TUIFriendShipViewModel>();
|
||||
final CoreServicesImpl _coreServices = serviceLocator<CoreServicesImpl>();
|
||||
final MessageService _messageService = serviceLocator<MessageService>();
|
||||
|
||||
UserProfile? _userProfile;
|
||||
ProfileLifeCycle? _lifeCycle;
|
||||
bool? _shouldAddToBlackList;
|
||||
int _friendType = 0;
|
||||
bool? _isDisturb;
|
||||
|
||||
UserProfile? get userProfile {
|
||||
return _userProfile;
|
||||
}
|
||||
|
||||
set userProfile(UserProfile? value) {
|
||||
_userProfile = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool? get isDisturb {
|
||||
return _isDisturb;
|
||||
}
|
||||
|
||||
bool? get isAddToBlackList {
|
||||
return _shouldAddToBlackList;
|
||||
}
|
||||
|
||||
int get friendType {
|
||||
return _friendType;
|
||||
}
|
||||
|
||||
set lifeCycle(ProfileLifeCycle? value) {
|
||||
_lifeCycle = value;
|
||||
}
|
||||
|
||||
loadData({required String userID, bool isNeedConversation = true}) async {
|
||||
if (userID.isEmpty) {
|
||||
return;
|
||||
}
|
||||
V2TimFriendInfo? friendUserInfo;
|
||||
V2TimConversation? conversation;
|
||||
final userInfoList = await _friendshipServices.getFriendsInfo(userIDList: [userID]);
|
||||
final checkFriend =
|
||||
await _friendshipServices.checkFriend(userIDList: [userID], checkType: FriendTypeEnum.V2TIM_FRIEND_TYPE_SINGLE);
|
||||
|
||||
if (checkFriend != null) {
|
||||
final res = checkFriend.first;
|
||||
if (res.resultCode == 0) {
|
||||
_friendType = res.resultType;
|
||||
}
|
||||
}
|
||||
|
||||
if (userInfoList != null) {
|
||||
friendUserInfo = userInfoList[0].friendInfo;
|
||||
}
|
||||
|
||||
if (isNeedConversation) {
|
||||
conversation = await _conversationService.getConversation(conversationID: "c2c_$userID");
|
||||
_isDisturb = conversation?.recvOpt == 2;
|
||||
}
|
||||
|
||||
final friendInfo = await _lifeCycle?.didGetFriendInfo(friendUserInfo) ?? friendUserInfo;
|
||||
|
||||
_isDisturb = conversation?.recvOpt == 2;
|
||||
_userProfile = UserProfile(friendInfo: friendInfo, conversation: conversation);
|
||||
|
||||
_shouldAddToBlackList = _friendShipViewModel.blockList.indexWhere((element) => element.userID == userID) > -1;
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<V2TimCallback> pinedConversation(bool isPined, String convID) async {
|
||||
final res = await _conversationService.pinConversation(conversationID: convID, isPinned: isPined);
|
||||
_userProfile?.conversation!.isPinned = isPined;
|
||||
notifyListeners();
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<List<V2TimFriendOperationResult>?> addToBlackList(bool shouldAdd, String userID) async {
|
||||
if (_lifeCycle?.shouldAddToBlockList != null && await _lifeCycle!.shouldAddToBlockList(userID) == false) {
|
||||
return null;
|
||||
}
|
||||
if (shouldAdd) {
|
||||
final res = await _friendshipServices.addToBlackList(userIDList: [userID]);
|
||||
if (res != null && res.isNotEmpty) {
|
||||
final result = res.first;
|
||||
if (result.resultCode == 0) {
|
||||
_shouldAddToBlackList = true;
|
||||
_friendType = 0;
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
return res;
|
||||
} else {
|
||||
final res = await _friendshipServices.deleteFromBlackList(userIDList: [userID]);
|
||||
if (res != null && res.isNotEmpty) {
|
||||
final result = res.first;
|
||||
if (result.resultCode == 0) {
|
||||
_shouldAddToBlackList = false;
|
||||
final checkFriend = await _friendshipServices
|
||||
.checkFriend(userIDList: [userID], checkType: FriendTypeEnum.V2TIM_FRIEND_TYPE_SINGLE);
|
||||
if (checkFriend != null) {
|
||||
final res = checkFriend.first;
|
||||
_friendType = res.resultType;
|
||||
}
|
||||
}
|
||||
}
|
||||
_friendShipViewModel.loadBlockListData();
|
||||
notifyListeners();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
Future<V2TimFriendOperationResult?> deleteFriend(String userID, {bool needUpdateData = true}) async {
|
||||
if (_lifeCycle?.shouldDeleteFriend != null && await _lifeCycle!.shouldDeleteFriend(userID) == false) {
|
||||
return null;
|
||||
}
|
||||
final res = await _friendshipServices
|
||||
.deleteFromFriendList(userIDList: [userID], deleteType: FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH);
|
||||
if (res != null) {
|
||||
_conversationService.deleteConversation(conversationID: "c2c_$userID");
|
||||
if (needUpdateData) {
|
||||
loadData(userID: userID);
|
||||
}
|
||||
return res.first;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<V2TimCallback> changeFriendVerificationMethod(int allowType) async {
|
||||
V2TimUserFullInfo userFullInfo = V2TimUserFullInfo();
|
||||
userFullInfo.allowType = allowType;
|
||||
final res = await _coreServices.setSelfInfo(userFullInfo: userFullInfo);
|
||||
if (res.code == 0) {
|
||||
_userProfile?.friendInfo!.userProfile!.allowType = allowType;
|
||||
notifyListeners();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<V2TimFriendOperationResult?> addFriend(String userID) async {
|
||||
if (_lifeCycle?.shouldAddFriend != null && await _lifeCycle!.shouldAddFriend(userID) == false) {
|
||||
return null;
|
||||
}
|
||||
final res = await _friendshipServices.addFriend(userID: userID, addType: FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH);
|
||||
if (res.code == 0) {
|
||||
loadData(userID: userID);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<V2TimCallback> updateRemarks(String userID, String remark) async {
|
||||
final res = await _friendshipServices.setFriendInfo(userID: userID, friendRemark: remark);
|
||||
|
||||
if (res.code == 0) {
|
||||
_userProfile?.friendInfo!.friendRemark = remark;
|
||||
notifyListeners();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<V2TimCallback> setMessageDisturb(String userID, bool isDisturb) async {
|
||||
final res = await _messageService.setC2CReceiveMessageOpt(
|
||||
userIDList: [userID],
|
||||
opt: isDisturb ? ReceiveMsgOptEnum.V2TIM_RECEIVE_NOT_NOTIFY_MESSAGE : ReceiveMsgOptEnum.V2TIM_RECEIVE_MESSAGE);
|
||||
if (res.code == 0) {
|
||||
_isDisturb = isDisturb;
|
||||
}
|
||||
notifyListeners();
|
||||
return res;
|
||||
}
|
||||
|
||||
updateUserInfo(V2TimUserFullInfo userFullInfo) {
|
||||
if (userFullInfo.nickName != null) {
|
||||
_userProfile?.friendInfo!.userProfile?.nickName = userFullInfo.nickName;
|
||||
}
|
||||
if (userFullInfo.faceUrl != null) {
|
||||
_userProfile?.friendInfo!.userProfile?.faceUrl = userFullInfo.faceUrl;
|
||||
}
|
||||
if (userFullInfo.selfSignature != null) {
|
||||
_userProfile?.friendInfo!.userProfile?.selfSignature = userFullInfo.selfSignature;
|
||||
}
|
||||
if (userFullInfo.gender != null) {
|
||||
_userProfile?.friendInfo!.userProfile?.gender = userFullInfo.gender;
|
||||
}
|
||||
if (userFullInfo.allowType != null) {
|
||||
_userProfile?.friendInfo!.userProfile?.allowType = userFullInfo.allowType;
|
||||
}
|
||||
if (userFullInfo.customInfo != null) {
|
||||
_userProfile?.friendInfo!.userProfile?.customInfo = userFullInfo.customInfo;
|
||||
}
|
||||
if (userFullInfo.role != null) {
|
||||
_userProfile?.friendInfo!.userProfile?.role = userFullInfo.role;
|
||||
}
|
||||
if (userFullInfo.level != null) {
|
||||
_userProfile?.friendInfo!.userProfile?.level = userFullInfo.level;
|
||||
}
|
||||
if (userFullInfo.birthday != null) {
|
||||
_userProfile?.friendInfo!.userProfile?.birthday = userFullInfo.birthday;
|
||||
}
|
||||
}
|
||||
|
||||
Future<V2TimCallback> updateSelfInfo(V2TimUserFullInfo userFullInfo) async {
|
||||
final res = await _coreServices.setSelfInfo(userFullInfo: userFullInfo);
|
||||
|
||||
if (res.code == 0) {
|
||||
updateUserInfo(userFullInfo);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,335 @@
|
||||
// ignore_for_file: unnecessary_getters_setters
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimConversationListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.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_friend_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/conversation_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/conversation/conversation_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||
|
||||
List<T> removeDuplicates<T>(List<T> list, bool Function(T first, T second) isEqual) {
|
||||
List<T> output = [];
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
bool found = false;
|
||||
for (var j = 0; j < output.length; j++) {
|
||||
if (isEqual(list[i], output[j])) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
output.add(list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
class TUIConversationViewModel extends ChangeNotifier {
|
||||
static const String conversationC2CPrefix = "c2c_";
|
||||
static const String conversationGroupPrefix = "group_";
|
||||
|
||||
final TUISelfInfoViewModel selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>();
|
||||
final ConversationService _conversationService = serviceLocator<ConversationService>();
|
||||
final FriendshipServices _friendshipServices = serviceLocator<FriendshipServices>();
|
||||
final TUIChatGlobalModel _chatGlobalModel = serviceLocator<TUIChatGlobalModel>();
|
||||
final MessageService _messageService = serviceLocator<MessageService>();
|
||||
late V2TimConversationListener _conversationListener;
|
||||
List<V2TimConversation?> _conversationList = [];
|
||||
static V2TimConversation? _selectedConversation;
|
||||
Map<String, String> webDraftMap = {};
|
||||
|
||||
bool _haveMoreData = true;
|
||||
int _totalUnReadCount = 0;
|
||||
String? _scrollToConversation;
|
||||
final TUIChatGlobalModel globalChatModel = serviceLocator<TUIChatGlobalModel>();
|
||||
|
||||
String _nextSeq = "0";
|
||||
ConversationLifeCycle? _lifeCycle;
|
||||
|
||||
List<V2TimConversation?> get conversationList {
|
||||
if (PlatformUtils().isWeb) {
|
||||
try {
|
||||
_conversationList.sort((a, b) {
|
||||
return b!.lastMessage!.timestamp!.compareTo(a!.lastMessage!.timestamp!);
|
||||
});
|
||||
|
||||
final pinnedConversation = _conversationList.where((element) => element?.isPinned == true).toList();
|
||||
_conversationList.removeWhere((element) => element?.isPinned == true);
|
||||
_conversationList = [...pinnedConversation, ..._conversationList];
|
||||
// ignore: empty_catches
|
||||
} catch (e) {}
|
||||
} else {
|
||||
_conversationList.sort((a, b) => b!.orderkey!.compareTo(a!.orderkey!));
|
||||
}
|
||||
return _conversationList;
|
||||
}
|
||||
|
||||
V2TimConversation? getConversation(String conversationID) {
|
||||
return _conversationList.firstWhereOrNull((element) => element?.conversationID == conversationID);
|
||||
}
|
||||
|
||||
String? get scrollToConversation => _scrollToConversation;
|
||||
|
||||
set scrollToConversation(String? value) {
|
||||
_scrollToConversation = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void clearScrollToConversation() {
|
||||
_scrollToConversation = null;
|
||||
}
|
||||
|
||||
bool get haveMoreData {
|
||||
return _haveMoreData;
|
||||
}
|
||||
|
||||
int get totalUnReadCount => _totalUnReadCount;
|
||||
|
||||
set totalUnReadCount(int value) {
|
||||
_totalUnReadCount = value;
|
||||
}
|
||||
|
||||
set lifeCycle(ConversationLifeCycle? value) {
|
||||
_lifeCycle = value;
|
||||
}
|
||||
|
||||
set conversationList(List<V2TimConversation?> conversationList) {
|
||||
_conversationList = conversationList;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
set selectedConversation(V2TimConversation? value) {
|
||||
_selectedConversation = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
V2TimConversation? get selectedConversation {
|
||||
return _selectedConversation;
|
||||
}
|
||||
|
||||
static V2TimConversation? of() {
|
||||
return _selectedConversation;
|
||||
}
|
||||
|
||||
TUIConversationViewModel() {
|
||||
_conversationListener = V2TimConversationListener(onConversationChanged: (conversationList) {
|
||||
_onConversationListChanged(conversationList);
|
||||
}, onNewConversation: (conversationList) {
|
||||
_addNewConversation(conversationList);
|
||||
}, onTotalUnreadMessageCountChanged: (totalUnread) {
|
||||
_totalUnReadCount = totalUnread;
|
||||
_chatGlobalModel.totalUnReadCount = totalUnread;
|
||||
notifyListeners();
|
||||
}, onSyncServerFinish: () {
|
||||
// Remove the process to load such a many of conversations after launching
|
||||
if (!PlatformUtils().isWeb) {
|
||||
loadInitConversation();
|
||||
}
|
||||
}, onConversationDeleted: (List<String> conversationIDList) {
|
||||
_onConversationDeleted(conversationIDList);
|
||||
for (var conversationID in conversationIDList) {
|
||||
String resultID = "";
|
||||
if (conversationID.startsWith(conversationC2CPrefix)) {
|
||||
resultID = conversationID.replaceFirst(conversationC2CPrefix, "");
|
||||
} else if (conversationID.startsWith(conversationGroupPrefix)) {
|
||||
resultID = conversationID.replaceFirst(conversationGroupPrefix, "");
|
||||
}
|
||||
|
||||
if (resultID != "") {
|
||||
_chatGlobalModel.removeMessageList(resultID);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadInitConversation() async {
|
||||
await loadData(count: 40);
|
||||
// Remove the process to load such a many of conversations after launching
|
||||
// if (selfInfoViewModel.globalConfig?.isPreloadMessagesAfterInit ?? true) {
|
||||
// _chatGlobalModel.initMessageMapFromLocalDatabase(_conversationList);
|
||||
// }
|
||||
}
|
||||
|
||||
initConversation() async {
|
||||
clearData();
|
||||
loadInitConversation();
|
||||
}
|
||||
|
||||
Future<void> loadData({required int count}) async {
|
||||
_haveMoreData = true;
|
||||
final isRefresh = _nextSeq == "0";
|
||||
final conversationResult = await _conversationService.getConversationList(nextSeq: _nextSeq, count: count);
|
||||
_nextSeq = conversationResult?.nextSeq ?? "";
|
||||
final conversationList = conversationResult?.conversationList;
|
||||
if (conversationList != null) {
|
||||
if (conversationList.isEmpty || conversationList.length < count) {
|
||||
_haveMoreData = false;
|
||||
}
|
||||
List<V2TimConversation?> combinedConversationList = [];
|
||||
if (isRefresh) {
|
||||
combinedConversationList = conversationList;
|
||||
} else {
|
||||
combinedConversationList = [..._conversationList, ...conversationList];
|
||||
}
|
||||
final List<V2TimConversation?> finalConversationList =
|
||||
await _lifeCycle?.conversationListWillMount(combinedConversationList) ?? combinedConversationList;
|
||||
_conversationList = removeDuplicates<V2TimConversation?>(
|
||||
finalConversationList, (item1, item2) => item1?.conversationID == item2?.conversationID);
|
||||
notifyListeners();
|
||||
}
|
||||
_totalUnReadCount = await _conversationService.getTotalUnreadCount();
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
void setSelectedConversation(V2TimConversation conversation) {
|
||||
_selectedConversation = conversation;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<V2TimCallback> pinConversation({
|
||||
required String conversationID,
|
||||
required bool isPinned,
|
||||
}) {
|
||||
return _conversationService.pinConversation(conversationID: conversationID, isPinned: isPinned);
|
||||
}
|
||||
|
||||
Future<V2TimCallback?> clearHistoryMessage({required String convID, required int convType}) async {
|
||||
if (_lifeCycle?.shouldClearHistoricalMessageForConversation != null &&
|
||||
await _lifeCycle!.shouldClearHistoricalMessageForConversation(convID) == false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
globalChatModel.setMessageList(convID, []);
|
||||
|
||||
if (convType == 1) {
|
||||
return _messageService.clearC2CHistoryMessage(userID: convID);
|
||||
} else {
|
||||
return _messageService.clearGroupHistoryMessage(groupID: convID);
|
||||
}
|
||||
}
|
||||
|
||||
searchFriends(String searchKey) async {
|
||||
final res = await _friendshipServices.searchFriends(searchParam: V2TimFriendSearchParam(keywordList: [searchKey]));
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<V2TimCallback?> deleteConversation({required String conversationID}) async {
|
||||
if (_lifeCycle?.shouldDeleteConversation != null &&
|
||||
await _lifeCycle!.shouldDeleteConversation(conversationID) == false) {
|
||||
return null;
|
||||
}
|
||||
final res = await _conversationService.deleteConversation(conversationID: conversationID);
|
||||
if (res.code == 0) {
|
||||
_conversationList.removeWhere((element) => element?.conversationID == conversationID);
|
||||
notifyListeners();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
_onConversationListChanged(List<V2TimConversation> list) {
|
||||
for (int element = 0; element < list.length; element++) {
|
||||
int index = _conversationList.indexWhere((item) => item!.conversationID == list[element].conversationID);
|
||||
if (index > -1) {
|
||||
_conversationList.setAll(index, [list[element]] as List<V2TimConversation?>);
|
||||
} else {
|
||||
_conversationList.add(list[element]);
|
||||
}
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
_onConversationDeleted(List<String> list) {
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
int index = _conversationList.indexWhere((item) => item!.conversationID == list[i]);
|
||||
if (index > -1) {
|
||||
_conversationList.removeAt(index);
|
||||
_conversationList = removeDuplicates<V2TimConversation?>(
|
||||
_conversationList, (item1, item2) => item1?.conversationID == item2?.conversationID);
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
_addNewConversation(List<V2TimConversation> list) {
|
||||
_conversationList.addAll(list);
|
||||
_conversationList = removeDuplicates<V2TimConversation?>(
|
||||
_conversationList, (item1, item2) => item1?.conversationID == item2?.conversationID);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
setConversationListener() {
|
||||
_conversationService.addConversationListener(listener: _conversationListener);
|
||||
}
|
||||
|
||||
removeConversationListener() {
|
||||
_conversationService.removeConversationListener(listener: _conversationListener);
|
||||
}
|
||||
|
||||
Future<V2TimCallback> setConversationDraft({
|
||||
required String conversationID,
|
||||
String? draftText,
|
||||
bool isTopic = false,
|
||||
String? groupID,
|
||||
bool isAllowWeb = true,
|
||||
}) async {
|
||||
assert(!isTopic || (groupID != null && groupID.isNotEmpty),
|
||||
"When 'isTopic' is true, 'groupID' must not be null or empty.");
|
||||
if (PlatformUtils().isWeb && isAllowWeb) {
|
||||
webDraftMap[conversationID] = draftText ?? "";
|
||||
return V2TimCallback(code: 0, desc: "");
|
||||
} else {
|
||||
if (isTopic) {
|
||||
final topicInfoList = await TencentImSDKPlugin.v2TIMManager
|
||||
.getGroupManager()
|
||||
.getTopicInfoList(groupID: groupID!, topicIDList: [conversationID]);
|
||||
final topicInfo = topicInfoList.data?.first.topicInfo;
|
||||
topicInfo?.draftText = draftText;
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getGroupManager().setTopicInfo(topicInfo: topicInfo!);
|
||||
return res;
|
||||
} else {
|
||||
return _conversationService.setConversationDraft(conversationID: conversationID, draftText: draftText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clearWebDraft({
|
||||
required String conversationID,
|
||||
}) {
|
||||
webDraftMap[conversationID] = "";
|
||||
}
|
||||
|
||||
String? getWebDraft({
|
||||
required String conversationID,
|
||||
}) {
|
||||
return TencentUtils.checkString(webDraftMap[conversationID]);
|
||||
}
|
||||
|
||||
clearData() {
|
||||
_conversationList = [];
|
||||
_selectedConversation = null;
|
||||
_nextSeq = "0";
|
||||
_haveMoreData = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
refresh({int count = 100}) {
|
||||
_nextSeq = "0";
|
||||
_haveMoreData = true;
|
||||
loadData(count: count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimFriendshipListener.dart';
|
||||
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/models/v2_tim_friend_application.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_application.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_operation_result.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/models/v2_tim_group_member_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_status.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_status.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/block_list_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/friend_list_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/new_contact_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
|
||||
class TUIFriendShipViewModel extends ChangeNotifier {
|
||||
final FriendshipServices _friendshipServices = serviceLocator<FriendshipServices>();
|
||||
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
||||
final TUISelfInfoViewModel selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>();
|
||||
late V2TimFriendshipListener friendShipListener;
|
||||
List<V2TimFriendApplication?>? _friendApplicationList;
|
||||
List<V2TimFriendInfo>? _friendList;
|
||||
List<V2TimGroupInfo>? _groupList;
|
||||
List<V2TimUserStatus>? _userStatusList;
|
||||
int _friendApplicationAmount = 0;
|
||||
List<V2TimFriendInfo>? _blockList;
|
||||
NewContactLifeCycle? _newContactLifeCycle;
|
||||
FriendListLifeCycle? _contactListLifeCycle;
|
||||
BlockListLifeCycle? _blockListLifeCycle;
|
||||
|
||||
set newContactLifeCycle(NewContactLifeCycle? value) {
|
||||
_newContactLifeCycle = value;
|
||||
}
|
||||
|
||||
set contactListLifeCycle(FriendListLifeCycle? value) {
|
||||
_contactListLifeCycle = value;
|
||||
}
|
||||
|
||||
set blockListLifeCycle(BlockListLifeCycle? value) {
|
||||
_blockListLifeCycle = value;
|
||||
}
|
||||
|
||||
set userStatusList(List<V2TimUserStatus> value) {
|
||||
_userStatusList = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<V2TimUserStatus> get userStatusList => _userStatusList ?? [];
|
||||
|
||||
List<V2TimFriendInfo> get blockList {
|
||||
return _blockList ?? [];
|
||||
}
|
||||
|
||||
List<V2TimGroupInfo> get groupList {
|
||||
return _groupList ?? [];
|
||||
}
|
||||
|
||||
List<V2TimFriendInfo>? get friendList {
|
||||
return _friendList;
|
||||
}
|
||||
|
||||
int get friendApplicationAmount => _friendApplicationAmount;
|
||||
|
||||
List<V2TimFriendApplication?>? get friendApplicationList => _friendApplicationList;
|
||||
|
||||
TUIFriendShipViewModel() {
|
||||
friendShipListener = V2TimFriendshipListener(
|
||||
onFriendApplicationListAdded: (applicationList) {
|
||||
loadContactApplicationData();
|
||||
},
|
||||
onFriendApplicationListDeleted: (userIDList) {
|
||||
loadContactApplicationData();
|
||||
},
|
||||
onFriendApplicationListRead: () {
|
||||
loadContactApplicationData();
|
||||
},
|
||||
onFriendInfoChanged: (infoList) {
|
||||
loadContactListData();
|
||||
},
|
||||
onFriendListAdded: (users) async {
|
||||
await loadContactListData();
|
||||
loadUserStatus();
|
||||
},
|
||||
onFriendListDeleted: (userList) async {
|
||||
await loadContactListData();
|
||||
loadUserStatus();
|
||||
},
|
||||
onBlackListAdd: (infoList) async {
|
||||
await loadBlockListData();
|
||||
loadUserStatus();
|
||||
},
|
||||
onBlackListDeleted: (userList) async {
|
||||
await loadBlockListData();
|
||||
loadUserStatus();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
initFriendShipModel() {
|
||||
loadData();
|
||||
}
|
||||
|
||||
loadData() async {
|
||||
loadContactApplicationData();
|
||||
loadBlockListData();
|
||||
await loadContactListData();
|
||||
loadUserStatus();
|
||||
}
|
||||
|
||||
clearData() {
|
||||
_friendApplicationList = [];
|
||||
_friendApplicationAmount = 0;
|
||||
_friendList = [];
|
||||
_userStatusList = [];
|
||||
_blockList = [];
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
loadUserStatus() async {
|
||||
if (selfInfoViewModel.globalConfig?.isShowOnlineStatus == false || friendList == null || friendList!.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<List<String>> userIDSet = [];
|
||||
final int needHowManyRequest = ((friendList!.length) / 500).ceil();
|
||||
final int amountEachRequest = ((friendList!.length) / needHowManyRequest).ceil();
|
||||
|
||||
for (int i = 0; i < needHowManyRequest; i++) {
|
||||
userIDSet.add(friendList!
|
||||
.getRange(i * amountEachRequest, min(friendList!.length, (i + 1) * amountEachRequest))
|
||||
.map((e) => e.userID)
|
||||
.toList());
|
||||
}
|
||||
|
||||
final List<List<V2TimUserStatus>> userStatus = await Future.wait([
|
||||
...userIDSet.map((userIDList) async {
|
||||
return await _friendshipServices.getUserStatus(userIDList: userIDList);
|
||||
})
|
||||
]);
|
||||
|
||||
final List<V2TimUserStatus> flatUserStatus = [];
|
||||
for (var e in userStatus) {
|
||||
flatUserStatus.addAll(e);
|
||||
}
|
||||
userStatusList = flatUserStatus;
|
||||
}
|
||||
|
||||
loadContactApplicationData() async {
|
||||
final newContactRes = await _friendshipServices.getFriendApplicationList();
|
||||
// Only Received Application
|
||||
_friendApplicationList = newContactRes?.friendApplicationList
|
||||
?.where((item) => item!.type == FriendApplicationTypeEnum.V2TIM_FRIEND_APPLICATION_COME_IN.index)
|
||||
.toList();
|
||||
_friendApplicationAmount = _friendApplicationList?.length ?? 0;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> loadContactListData() async {
|
||||
final List<V2TimFriendInfo> res = await _friendshipServices.getFriendList() ?? [];
|
||||
final memberList = await _contactListLifeCycle?.friendListWillMount(res) ?? res;
|
||||
_friendList = memberList;
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
Future<bool> isFriend(String userID) async {
|
||||
final List<V2TimFriendInfo> res = await _friendshipServices.getFriendList() ?? [];
|
||||
for (V2TimFriendInfo info in res) {
|
||||
if (info.userID == userID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> loadBlockListData() async {
|
||||
final blockListRes = await _friendshipServices.getBlackList();
|
||||
_blockList = blockListRes ?? [];
|
||||
notifyListeners();
|
||||
return;
|
||||
}
|
||||
|
||||
loadGroupListData() async {
|
||||
final groupListRes = await _groupServices.getJoinedGroupList();
|
||||
_groupList = groupListRes ?? [];
|
||||
if (_groupList != null && _groupList!.isNotEmpty) {
|
||||
notifyListeners();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Future<List<V2TimFriendOperationResult>?> deleteFromBlockList(List<String> userIDList) async {
|
||||
if (_blockListLifeCycle?.shouldDeleteFromBlockList != null &&
|
||||
await _blockListLifeCycle!.shouldDeleteFromBlockList(userIDList) == false) {
|
||||
return null;
|
||||
}
|
||||
final res = await _friendshipServices.deleteFromBlackList(userIDList: userIDList);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<V2TimFriendOperationResult?> acceptFriendApplication(
|
||||
String userID,
|
||||
int type,
|
||||
) async {
|
||||
if (_newContactLifeCycle?.shouldAcceptContactApplication != null &&
|
||||
await _newContactLifeCycle!.shouldAcceptContactApplication(userID) == false) {
|
||||
return null;
|
||||
}
|
||||
final res = await _friendshipServices.acceptFriendApplication(
|
||||
responseType: FriendResponseTypeEnum.V2TIM_FRIEND_ACCEPT_AGREE_AND_ADD,
|
||||
type: FriendApplicationTypeEnum.values[type],
|
||||
userID: userID,
|
||||
);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<V2TimFriendOperationResult?> refuseFriendApplication(
|
||||
String userID,
|
||||
int type,
|
||||
) async {
|
||||
if (_newContactLifeCycle?.shouldRefuseContactApplication != null &&
|
||||
await _newContactLifeCycle!.shouldRefuseContactApplication(userID) == false) {
|
||||
return null;
|
||||
}
|
||||
final res = await _friendshipServices.refuseFriendApplication(
|
||||
type: FriendApplicationTypeEnum.values[type],
|
||||
userID: userID,
|
||||
);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<List<V2TimGroupMemberFullInfo?>> getGroupMembersInfo(
|
||||
{required String groupID, required List<String> memberList}) async {
|
||||
final res = await _groupServices.getGroupMembersInfo(groupID: groupID, memberList: memberList);
|
||||
return res.data ?? [];
|
||||
}
|
||||
|
||||
addFriendListener({V2TimFriendshipListener? listener}) {
|
||||
_friendshipServices.addFriendListener(listener: friendShipListener);
|
||||
}
|
||||
|
||||
removeFriendshipListener({V2TimFriendshipListener? listener}) {
|
||||
_friendshipServices.removeFriendListener(listener: friendShipListener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.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_friend_info_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_info_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_search_param.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/models/v2_tim_group_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_result_item.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_search_result_item.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/conversation/conversation_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart';
|
||||
|
||||
enum KeywordListMatchType { V2TIM_KEYWORD_LIST_MATCH_TYPE_OR, V2TIM_KEYWORD_LIST_MATCH_TYPE_AND }
|
||||
|
||||
class TUISearchViewModel extends ChangeNotifier {
|
||||
final FriendshipServices _friendshipServices = serviceLocator<FriendshipServices>();
|
||||
final MessageService _messageService = serviceLocator<MessageService>();
|
||||
final ConversationService _conversationService = serviceLocator<ConversationService>();
|
||||
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
||||
|
||||
List<V2TimFriendInfoResult>? friendList = [];
|
||||
|
||||
List<V2TimMessageSearchResultItem>? msgList = [];
|
||||
int msgPage = 0;
|
||||
int totalMsgCount = 0;
|
||||
|
||||
int totalMsgInConversationCount = 0;
|
||||
List<V2TimMessage> currentMsgListForConversation = [];
|
||||
|
||||
List<V2TimGroupInfo>? groupList = [];
|
||||
|
||||
List<V2TimConversation?> conversationList = [];
|
||||
|
||||
Future<List<V2TimConversation?>?> initConversationMsg() async {
|
||||
final conversationResult = await _conversationService.getConversationList(nextSeq: "0", count: 500);
|
||||
final conversationListData = conversationResult?.conversationList;
|
||||
conversationList = conversationListData ?? [];
|
||||
notifyListeners();
|
||||
return conversationListData;
|
||||
}
|
||||
|
||||
void initSearch() {
|
||||
friendList = [];
|
||||
msgList = [];
|
||||
groupList = [];
|
||||
totalMsgCount = 0;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void searchFriendByKey(String searchKey) async {
|
||||
final searchResult =
|
||||
await _friendshipServices.searchFriends(searchParam: V2TimFriendSearchParam(keywordList: [searchKey]));
|
||||
friendList = searchResult;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void searchGroupByKey(String searchKey) async {
|
||||
final searchResult =
|
||||
await _groupServices.searchGroups(searchParam: V2TimGroupSearchParam(keywordList: [searchKey]));
|
||||
groupList = searchResult.data ?? [];
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void getMsgForConversation(String keyword, String conversationId, int page) async {
|
||||
void clearData() {
|
||||
currentMsgListForConversation = [];
|
||||
totalMsgInConversationCount = 0;
|
||||
}
|
||||
|
||||
if (page == 0) {
|
||||
clearData();
|
||||
}
|
||||
if (keyword.isEmpty) {
|
||||
clearData();
|
||||
return;
|
||||
}
|
||||
final searchResult = await _messageService.searchLocalMessages(
|
||||
searchParam: V2TimMessageSearchParam(
|
||||
keywordList: [keyword],
|
||||
pageIndex: page,
|
||||
pageSize: 30,
|
||||
searchTimePeriod: 0,
|
||||
searchTimePosition: 0,
|
||||
conversationID: conversationId,
|
||||
type: KeywordListMatchType.V2TIM_KEYWORD_LIST_MATCH_TYPE_OR.index,
|
||||
));
|
||||
if (searchResult.code == 0 && searchResult.data != null) {
|
||||
final messageSearchResultItems = searchResult.data!.messageSearchResultItems!
|
||||
.firstWhereOrNull((element) => element.conversationID == conversationId);
|
||||
totalMsgInConversationCount = messageSearchResultItems?.messageCount ?? 0;
|
||||
currentMsgListForConversation = [
|
||||
...currentMsgListForConversation,
|
||||
...(messageSearchResultItems?.messageList ?? [])
|
||||
];
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void searchMsgByKey(String searchKey, bool isFirst) async {
|
||||
if (isFirst == true) {
|
||||
msgPage = 0;
|
||||
msgList = [];
|
||||
totalMsgCount = 0;
|
||||
}
|
||||
final searchResult = await _messageService.searchLocalMessages(
|
||||
searchParam: V2TimMessageSearchParam(
|
||||
keywordList: [searchKey],
|
||||
pageIndex: msgPage,
|
||||
pageSize: 5,
|
||||
searchTimePeriod: 0,
|
||||
searchTimePosition: 0,
|
||||
type: KeywordListMatchType.V2TIM_KEYWORD_LIST_MATCH_TYPE_OR.index,
|
||||
));
|
||||
if (searchResult.code == 0 && searchResult.data != null) {
|
||||
msgPage++;
|
||||
msgList = [...?msgList, ...?searchResult.data!.messageSearchResultItems];
|
||||
totalMsgCount = searchResult.data!.totalCount ?? 0;
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void searchByKey(String? searchKey) async {
|
||||
if (searchKey == null || searchKey.isEmpty) {
|
||||
friendList = [];
|
||||
groupList = [];
|
||||
msgList = [];
|
||||
totalMsgCount = 0;
|
||||
notifyListeners();
|
||||
} else {
|
||||
searchFriendByKey(searchKey);
|
||||
searchMsgByKey(searchKey, true);
|
||||
searchGroupByKey(searchKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_config.dart';
|
||||
|
||||
class TUISelfInfoViewModel extends ChangeNotifier {
|
||||
V2TimUserFullInfo? _loginInfo;
|
||||
TIMUIKitConfig? _globalConfig;
|
||||
|
||||
TIMUIKitConfig? get globalConfig => _globalConfig;
|
||||
|
||||
set globalConfig(TIMUIKitConfig? value) {
|
||||
_globalConfig = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
V2TimUserFullInfo? get loginInfo {
|
||||
return _loginInfo;
|
||||
}
|
||||
|
||||
setLoginInfo(V2TimUserFullInfo? value) {
|
||||
_loginInfo = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class TUISettingModel extends ChangeNotifier {
|
||||
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
|
||||
final String keyTitle = "tencent_chat_uikit_";
|
||||
|
||||
double? _keyboardHeight;
|
||||
double get keyboardHeight => _keyboardHeight ?? 0;
|
||||
set keyboardHeight(double value) {
|
||||
if(value > 40 && _keyboardHeight != value){
|
||||
_keyboardHeight = value;
|
||||
updateLocalSetting("${keyTitle}keyboardHeight", value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
updateLocalSetting(String key, String value) async {
|
||||
SharedPreferences prefs = await _prefs;
|
||||
prefs.setString(key, value);
|
||||
}
|
||||
|
||||
init() async {
|
||||
SharedPreferences prefs = await _prefs;
|
||||
_keyboardHeight = double.parse(prefs.getString("${keyTitle}keyboardHeight") ?? "0");
|
||||
}
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimConversationListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.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_conversation_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_conversation_result.dart';
|
||||
|
||||
abstract class ConversationService {
|
||||
Future<V2TimConversationResult?> getConversationList({
|
||||
required String nextSeq,
|
||||
required int count,
|
||||
});
|
||||
|
||||
Future<V2TimConversation?> getConversation({
|
||||
required String conversationID,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> pinConversation({
|
||||
required String conversationID,
|
||||
required bool isPinned,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> deleteConversation({
|
||||
required String conversationID,
|
||||
});
|
||||
|
||||
Future<void> addConversationListener({
|
||||
required V2TimConversationListener listener,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> setConversationDraft({required String conversationID, String? draftText});
|
||||
|
||||
Future<void> removeConversationListener({V2TimConversationListener? listener});
|
||||
|
||||
Future<int> getTotalUnreadCount();
|
||||
|
||||
Future<V2TimConversation?> getConversationListByConversationId({required String convID});
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimConversationListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.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_conversation_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_conversation_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/conversation/conversation_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
|
||||
class ConversationServicesImpl extends ConversationService {
|
||||
final CoreServicesImpl _coreService = serviceLocator<CoreServicesImpl>();
|
||||
|
||||
@override
|
||||
Future<V2TimConversationResult?> getConversationList({
|
||||
required String nextSeq,
|
||||
required int count,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getConversationManager()
|
||||
.getConversationList(nextSeq: nextSeq, count: count);
|
||||
if (result.code == 0) {
|
||||
return result.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> pinConversation({
|
||||
required String conversationID,
|
||||
required bool isPinned,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getConversationManager()
|
||||
.pinConversation(conversationID: conversationID, isPinned: isPinned);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> deleteConversation({
|
||||
required String conversationID,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getConversationManager()
|
||||
.deleteConversation(conversationID: conversationID);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addConversationListener({
|
||||
required V2TimConversationListener listener,
|
||||
}) {
|
||||
return TencentImSDKPlugin.v2TIMManager.getConversationManager().addConversationListener(listener: listener);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimConversation?> getConversation({
|
||||
required String conversationID,
|
||||
}) async {
|
||||
final res =
|
||||
await TencentImSDKPlugin.v2TIMManager.getConversationManager().getConversation(conversationID: conversationID);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setConversationDraft({required String conversationID, String? draftText}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getConversationManager()
|
||||
.setConversationDraft(conversationID: conversationID, draftText: draftText);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeConversationListener({V2TimConversationListener? listener}) {
|
||||
return TencentImSDKPlugin.v2TIMManager.getConversationManager().removeConversationListener(listener: listener);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimConversation?> getConversationListByConversationId({required String convID}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getConversationManager()
|
||||
.getConversationListByConversationIds(conversationIDList: [convID]);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return (result.data != null && result.data!.isNotEmpty) ? result.data![0] : null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getTotalUnreadCount() async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getConversationManager().getTotalUnreadMessageCount();
|
||||
if (res.code == 0) {
|
||||
return res.data ?? 0;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimSDKListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/log_level_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_config.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
enum AppStatus { foreground, background }
|
||||
|
||||
enum LanguageEnum {
|
||||
/// Chinese, Traditional
|
||||
zhHant,
|
||||
|
||||
/// Chinese, Simplified
|
||||
zhHans,
|
||||
|
||||
/// English
|
||||
en,
|
||||
|
||||
/// Korean
|
||||
ko,
|
||||
|
||||
/// Japanese
|
||||
ja,
|
||||
}
|
||||
|
||||
const languageEnumToString = {
|
||||
LanguageEnum.zhHant: "zh-Hant",
|
||||
LanguageEnum.zhHans: "zh-Hans",
|
||||
LanguageEnum.en: "en",
|
||||
LanguageEnum.ja: "ja",
|
||||
LanguageEnum.ko: "ko",
|
||||
};
|
||||
|
||||
abstract class CoreServices {
|
||||
Future<bool?> init({
|
||||
required int sdkAppID,
|
||||
required LogLevelEnum loglevel,
|
||||
required V2TimSDKListener listener,
|
||||
|
||||
/// Callback from TUIKit invoke, includes IM SDK API error, notify information, Flutter error.
|
||||
ValueChanged<TIMCallback>? onTUIKitCallbackListener,
|
||||
TIMUIKitConfig? config,
|
||||
|
||||
/// only support "en" and "zh" temporally
|
||||
LanguageEnum? language,
|
||||
});
|
||||
|
||||
Future<void> setDataFromNative({
|
||||
required String userId,
|
||||
|
||||
/// Callback from TUIKit invoke, includes IM SDK API error, notify information, Flutter error.
|
||||
ValueChanged<TIMCallback>? onTUIKitCallbackListener,
|
||||
TIMUIKitConfig? config,
|
||||
|
||||
/// only support "en" and "zh" temporally
|
||||
LanguageEnum? language,
|
||||
});
|
||||
|
||||
Future login({
|
||||
required String userID,
|
||||
required String userSig,
|
||||
});
|
||||
|
||||
Future logout();
|
||||
|
||||
Future logoutWithoutClearData();
|
||||
|
||||
Future unInit();
|
||||
|
||||
Future<V2TimValueCallback<List<V2TimUserFullInfo>>> getUsersInfo({
|
||||
required List<String> userIDList,
|
||||
});
|
||||
|
||||
// 注意:uikit的离线推送不支持TPNS
|
||||
// Note: uikit's offline push do not supports TPNS
|
||||
Future<V2TimCallback> setOfflinePushConfig({
|
||||
bool isTPNSToken = false,
|
||||
int businessID,
|
||||
required String token,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> setSelfInfo({
|
||||
required V2TimUserFullInfo userFullInfo,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> setOfflinePushStatus({
|
||||
required AppStatus status,
|
||||
int? totalCount,
|
||||
});
|
||||
|
||||
setTheme({required TUITheme theme});
|
||||
|
||||
setDarkTheme();
|
||||
|
||||
setLightTheme();
|
||||
|
||||
setDeviceType(DeviceType deviceType);
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tools/i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimSDKListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/log_level_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_status.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_status.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_setting_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.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_chat_global_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversation_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_friendship_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_config.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/web_support/uikit_web_support.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_uikit/data_services/core/web_support/uikit_web_support_implement.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 EmptyAvatarBuilder = Widget Function(BuildContext context);
|
||||
|
||||
class LoginInfo {
|
||||
final String userID;
|
||||
final String userSig;
|
||||
final int sdkAppID;
|
||||
final V2TimUserFullInfo? loginUser;
|
||||
|
||||
LoginInfo({this.sdkAppID = 0, this.userSig = "", this.userID = "", this.loginUser});
|
||||
}
|
||||
|
||||
class CoreServicesImpl implements CoreServices {
|
||||
V2TimUserFullInfo? _loginInfo;
|
||||
late int _sdkAppID;
|
||||
late String _userID;
|
||||
late String _userSig;
|
||||
ValueChanged<TIMCallback>? onCallback;
|
||||
VoidCallback? webLoginSuccess;
|
||||
bool isLoginSuccess = false;
|
||||
|
||||
V2TimUserFullInfo? get loginUserInfo {
|
||||
return _loginInfo;
|
||||
}
|
||||
|
||||
LoginInfo get loginInfo {
|
||||
return LoginInfo(sdkAppID: _sdkAppID, userID: _userID, userSig: _userSig, loginUser: _loginInfo);
|
||||
}
|
||||
|
||||
EmptyAvatarBuilder? _emptyAvatarBuilder;
|
||||
|
||||
EmptyAvatarBuilder? get emptyAvatarBuilder {
|
||||
return _emptyAvatarBuilder;
|
||||
}
|
||||
|
||||
setEmptyAvatarBuilder(EmptyAvatarBuilder builder) {
|
||||
_emptyAvatarBuilder = builder;
|
||||
}
|
||||
|
||||
setGlobalConfig(TIMUIKitConfig? config) {
|
||||
final TUISelfInfoViewModel selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>();
|
||||
final TUISettingModel settingModel = serviceLocator<TUISettingModel>();
|
||||
selfInfoViewModel.globalConfig = config;
|
||||
settingModel.init();
|
||||
}
|
||||
|
||||
addIdentifier() {
|
||||
TUIKitWebSupport.addSetterToWindow();
|
||||
TUIKitWebSupport.addIdentifierToWindow();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool?> init(
|
||||
{
|
||||
/// Callback from TUIKit invoke, includes IM SDK API error, notify information, Flutter error.
|
||||
ValueChanged<TIMCallback>? onTUIKitCallbackListener,
|
||||
required int sdkAppID,
|
||||
required LogLevelEnum loglevel,
|
||||
required V2TimSDKListener listener,
|
||||
LanguageEnum? language,
|
||||
String? extraLanguage,
|
||||
TIMUIKitConfig? config,
|
||||
|
||||
/// Specify the current device platform, mobile or desktop, based on your needs.
|
||||
/// TUIKit will automatically determine the platform if no specification is provided. DeviceType? platform,
|
||||
DeviceType? platform,
|
||||
String? uikitLogPath,
|
||||
VoidCallback? onWebLoginSuccess}) async {
|
||||
if (platform != null) {
|
||||
TUIKitScreenUtils.deviceType = platform;
|
||||
}
|
||||
addIdentifier();
|
||||
if (extraLanguage != null) {
|
||||
Future.delayed(const Duration(milliseconds: 1), () {
|
||||
I18nUtils(null, extraLanguage);
|
||||
});
|
||||
} else if (language != null) {
|
||||
Future.delayed(const Duration(milliseconds: 1), () {
|
||||
I18nUtils(null, languageEnumToString[language]);
|
||||
});
|
||||
}
|
||||
if (onTUIKitCallbackListener != null) {
|
||||
onCallback = onTUIKitCallbackListener;
|
||||
}
|
||||
setGlobalConfig(config);
|
||||
_sdkAppID = sdkAppID;
|
||||
webLoginSuccess = onWebLoginSuccess;
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.initSDK(
|
||||
sdkAppID: sdkAppID,
|
||||
loglevel: loglevel,
|
||||
listener: V2TimSDKListener(
|
||||
onConnectFailed: listener.onConnectFailed,
|
||||
onConnectSuccess: () {
|
||||
if (PlatformUtils().isWeb) {
|
||||
didLoginSuccess();
|
||||
if (onWebLoginSuccess != null) {
|
||||
onWebLoginSuccess();
|
||||
}
|
||||
}
|
||||
listener.onConnectSuccess();
|
||||
},
|
||||
onConnecting: listener.onConnecting,
|
||||
onKickedOffline: listener.onKickedOffline,
|
||||
onUserStatusChanged: (List<V2TimUserStatus> userStatusList) {
|
||||
updateUserStatusList(userStatusList);
|
||||
listener.onUserStatusChanged(userStatusList);
|
||||
},
|
||||
onSelfInfoUpdated: (V2TimUserFullInfo info) {
|
||||
listener.onSelfInfoUpdated(info);
|
||||
serviceLocator<TUISelfInfoViewModel>().setLoginInfo(info);
|
||||
_loginInfo = info;
|
||||
},
|
||||
onUserSigExpired: listener.onUserSigExpired));
|
||||
if (result.code == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// This method is used for init the TUIKit after you initialized the IM SDK from Native SDK.
|
||||
@override
|
||||
Future<void> setDataFromNative({
|
||||
/// Callback from TUIKit invoke, includes IM SDK API error, notify information, Flutter error.
|
||||
ValueChanged<TIMCallback>? onTUIKitCallbackListener,
|
||||
LanguageEnum? language,
|
||||
TIMUIKitConfig? config,
|
||||
String? extraLanguage,
|
||||
required String userId,
|
||||
}) async {
|
||||
_userID = userId;
|
||||
if (extraLanguage != null) {
|
||||
Future.delayed(const Duration(milliseconds: 1), () {
|
||||
I18nUtils(null, extraLanguage);
|
||||
});
|
||||
} else if (language != null) {
|
||||
Future.delayed(const Duration(milliseconds: 1), () {
|
||||
I18nUtils(null, languageEnumToString[language]);
|
||||
});
|
||||
}
|
||||
if (onTUIKitCallbackListener != null) {
|
||||
onCallback = onTUIKitCallbackListener;
|
||||
}
|
||||
setGlobalConfig(config);
|
||||
if (!PlatformUtils().isWeb) {
|
||||
didLoginSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
void addInitListener() {
|
||||
final TUIFriendShipViewModel tuiFriendShipViewModel = serviceLocator<TUIFriendShipViewModel>();
|
||||
final TUIConversationViewModel tuiConversationViewModel = serviceLocator<TUIConversationViewModel>();
|
||||
final TUIChatGlobalModel tuiChatViewModel = serviceLocator<TUIChatGlobalModel>();
|
||||
final TUIGroupListenerModel tuiGroupListenerModel = serviceLocator<TUIGroupListenerModel>();
|
||||
|
||||
tuiFriendShipViewModel.addFriendListener();
|
||||
tuiConversationViewModel.setConversationListener();
|
||||
tuiChatViewModel.addAdvancedMsgListener();
|
||||
tuiGroupListenerModel.setGroupListener();
|
||||
}
|
||||
|
||||
void removeListener() {
|
||||
final TUIFriendShipViewModel tuiFriendShipViewModel = serviceLocator<TUIFriendShipViewModel>();
|
||||
final TUIConversationViewModel tuiConversationViewModel = serviceLocator<TUIConversationViewModel>();
|
||||
final TUIChatGlobalModel tuiChatViewModel = serviceLocator<TUIChatGlobalModel>();
|
||||
final TUIGroupListenerModel tuiGroupListenerModel = serviceLocator<TUIGroupListenerModel>();
|
||||
|
||||
tuiFriendShipViewModel.removeFriendshipListener();
|
||||
tuiConversationViewModel.removeConversationListener();
|
||||
tuiChatViewModel.removeAdvanceMsgListener();
|
||||
tuiGroupListenerModel.removeGroupListener();
|
||||
}
|
||||
|
||||
callOnCallback(TIMCallback callbackValue) {
|
||||
if (onCallback != null) {
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
onCallback!(callbackValue);
|
||||
});
|
||||
} else {
|
||||
outputLogger.i("TUIKit Callback: ${callbackValue.type} - ${callbackValue.stackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
initDataModel() {
|
||||
final TUIFriendShipViewModel tuiFriendShipViewModel = serviceLocator<TUIFriendShipViewModel>();
|
||||
final TUIConversationViewModel tuiConversationViewModel = serviceLocator<TUIConversationViewModel>();
|
||||
|
||||
tuiFriendShipViewModel.initFriendShipModel();
|
||||
tuiConversationViewModel.initConversation();
|
||||
}
|
||||
|
||||
clearData() {
|
||||
final TUIFriendShipViewModel tuiFriendShipViewModel = serviceLocator<TUIFriendShipViewModel>();
|
||||
final TUIConversationViewModel tuiConversationViewModel = serviceLocator<TUIConversationViewModel>();
|
||||
final TUIChatGlobalModel tuiChatViewModel = serviceLocator<TUIChatGlobalModel>();
|
||||
|
||||
tuiFriendShipViewModel.clearData();
|
||||
tuiConversationViewModel.clearData();
|
||||
tuiChatViewModel.clearData();
|
||||
}
|
||||
|
||||
updateUserStatusList(List<V2TimUserStatus> newUserStatusList) {
|
||||
try {
|
||||
final TUISelfInfoViewModel selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>();
|
||||
if (selfInfoViewModel.globalConfig?.isShowOnlineStatus == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
final TUIFriendShipViewModel tuiFriendShipViewModel = serviceLocator<TUIFriendShipViewModel>();
|
||||
final currentUserStatusList = tuiFriendShipViewModel.userStatusList;
|
||||
|
||||
for (int i = 0; i < newUserStatusList.length; i++) {
|
||||
final int indexInCurrentUserList =
|
||||
currentUserStatusList.indexWhere((element) => element.userID == newUserStatusList[i].userID);
|
||||
if (indexInCurrentUserList == -1) {
|
||||
currentUserStatusList.add(newUserStatusList[i]);
|
||||
} else {
|
||||
currentUserStatusList[indexInCurrentUserList] = newUserStatusList[i];
|
||||
}
|
||||
}
|
||||
|
||||
tuiFriendShipViewModel.userStatusList = currentUserStatusList;
|
||||
// ignore: empty_catches
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> login({
|
||||
required String userID,
|
||||
required String userSig,
|
||||
}) async {
|
||||
_userID = userID;
|
||||
_userSig = userSig;
|
||||
V2TimCallback result = await TencentImSDKPlugin.v2TIMManager.login(userID: userID, userSig: userSig);
|
||||
if (!PlatformUtils().isWeb) {
|
||||
didLoginSuccess();
|
||||
}
|
||||
if (result.code != 0) {
|
||||
callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorCode: result.code, errorMsg: result.desc));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void didLoginSuccess() async {
|
||||
if (isLoginSuccess == true) {
|
||||
return;
|
||||
}
|
||||
isLoginSuccess = true;
|
||||
addInitListener();
|
||||
initDataModel();
|
||||
|
||||
if (TencentUtils.checkString(_userID) == null) {
|
||||
V2TimValueCallback<String> getLoginUserRes = await TencentImSDKPlugin.v2TIMManager.getLoginUser();
|
||||
if (getLoginUserRes.code == 0) {
|
||||
_userID = getLoginUserRes.data ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
getUsersInfoWithRetry();
|
||||
}
|
||||
|
||||
void getUsersInfoWithRetry() async {
|
||||
V2TimValueCallback<List<V2TimUserFullInfo>>? res;
|
||||
bool success = false;
|
||||
|
||||
while (!success) {
|
||||
res = await getUsersInfo(userIDList: [_userID]);
|
||||
if (res.code == 0 &&
|
||||
res.data != null &&
|
||||
res.data!.isNotEmpty &&
|
||||
res.data!.firstWhereOrNull((element) => element.userID == _userID) != null) {
|
||||
success = true;
|
||||
} else {
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
}
|
||||
}
|
||||
|
||||
_loginInfo = res?.data!.firstWhereOrNull((element) => element.userID == _userID);
|
||||
final TUISelfInfoViewModel selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>();
|
||||
if (_loginInfo != null) {
|
||||
selfInfoViewModel.setLoginInfo(_loginInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
void didLoginOut() {
|
||||
removeListener();
|
||||
clearData();
|
||||
_loginInfo = null;
|
||||
serviceLocator<TUISelfInfoViewModel>().setLoginInfo(_loginInfo);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> logout() async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.logout();
|
||||
isLoginSuccess = false;
|
||||
removeListener();
|
||||
clearData();
|
||||
serviceLocator<TUISelfInfoViewModel>().setLoginInfo(null);
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> logoutWithoutClearData() async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.logout();
|
||||
isLoginSuccess = false;
|
||||
removeListener();
|
||||
serviceLocator<TUISelfInfoViewModel>().setLoginInfo(null);
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future unInit() async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.unInitSDK();
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<List<V2TimUserFullInfo>>> getUsersInfo({
|
||||
required List<String> userIDList,
|
||||
}) {
|
||||
return TencentImSDKPlugin.v2TIMManager.getUsersInfo(userIDList: userIDList);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setOfflinePushConfig({
|
||||
required String token,
|
||||
bool isTPNSToken = false,
|
||||
int? businessID,
|
||||
}) {
|
||||
return TencentImSDKPlugin.v2TIMManager.getOfflinePushManager().setOfflinePushConfig(
|
||||
businessID: businessID?.toDouble() ?? 0,
|
||||
token: token,
|
||||
isTPNSToken: isTPNSToken,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setSelfInfo({
|
||||
required V2TimUserFullInfo userFullInfo,
|
||||
}) {
|
||||
return TencentImSDKPlugin.v2TIMManager.setSelfInfo(userFullInfo: userFullInfo);
|
||||
}
|
||||
|
||||
@override
|
||||
setTheme({required TUITheme theme}) {
|
||||
// 合并传入Theme和默认Theme
|
||||
final TUIThemeViewModel _theme = serviceLocator<TUIThemeViewModel>();
|
||||
Map<String, Color?> jsonMap = Map.from(CommonColor.defaultTheme.toJson());
|
||||
Map<String, Color?> jsonInputThemeMap = Map.from(theme.toJson());
|
||||
|
||||
jsonInputThemeMap.forEach((key, value) {
|
||||
if (value != null) {
|
||||
jsonMap.update(key, (v) => value);
|
||||
}
|
||||
});
|
||||
_theme.theme = TUITheme.fromJson(jsonMap);
|
||||
}
|
||||
|
||||
@override
|
||||
setDarkTheme() {
|
||||
final TUIThemeViewModel _theme = serviceLocator<TUIThemeViewModel>();
|
||||
_theme.theme = TUITheme.dark; //Dark
|
||||
}
|
||||
|
||||
@override
|
||||
setLightTheme() {
|
||||
final TUIThemeViewModel _theme = serviceLocator<TUIThemeViewModel>();
|
||||
_theme.theme = TUITheme.light; //Light
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setOfflinePushStatus({required AppStatus status, int? totalCount}) {
|
||||
if (status == AppStatus.foreground) {
|
||||
return TencentImSDKPlugin.v2TIMManager.getOfflinePushManager().doForeground();
|
||||
} else {
|
||||
return TencentImSDKPlugin.v2TIMManager.getOfflinePushManager().doBackground(unreadCount: totalCount ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
setDeviceType(DeviceType deviceType) {
|
||||
TUIKitScreenUtils.deviceType = deviceType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
class TIMUIKitConfig {
|
||||
/// Control if show online status of friends or contacts.
|
||||
/// This only works with [Ultimate Edition].
|
||||
/// [Default]: true.
|
||||
final bool isShowOnlineStatus;
|
||||
|
||||
/// Controls if allows to check the disk memory after login.
|
||||
/// If the storage space is less than 1GB,
|
||||
/// an callback from `onTUIKitCallbackListener` will be invoked,
|
||||
/// type is `INFO`, while code is 6661403.
|
||||
final bool isCheckDiskStorageSpace;
|
||||
|
||||
/// The asset path of the default avatar image.
|
||||
final String? defaultAvatarAssetPath;
|
||||
|
||||
/// The configuration of border radius for all the avatar shows in TUIKit.
|
||||
final BorderRadius? defaultAvatarBorderRadius;
|
||||
|
||||
/// You can use this function to customize the Modal that shows on desktop.
|
||||
/// Do not specified or return `false` will use our default implementation.
|
||||
final Future<bool> Function(
|
||||
TUIKitWideModalOperationKey operationKey,
|
||||
BuildContext context,
|
||||
Widget Function(VoidCallback closeFunc) child,
|
||||
TUITheme? theme,
|
||||
double? width,
|
||||
double? height,
|
||||
Offset? offset,
|
||||
String? initText,
|
||||
BorderRadius? borderRadius,
|
||||
bool? isDarkBackground,
|
||||
String? title,
|
||||
VoidCallback? onSubmit,
|
||||
Widget? submitWidget,
|
||||
VoidCallback? onConfirm,
|
||||
VoidCallback? onCancel,
|
||||
)? showDesktopModalFunc;
|
||||
|
||||
/// Determines whether TUIKit should preload some messages after initialization for faster message display,
|
||||
/// with a default value of `true`, and backward-compatibility.
|
||||
final bool isPreloadMessagesAfterInit;
|
||||
|
||||
const TIMUIKitConfig( {
|
||||
this.defaultAvatarAssetPath,
|
||||
this.showDesktopModalFunc,
|
||||
this.isPreloadMessagesAfterInit = true,
|
||||
this.defaultAvatarBorderRadius,
|
||||
this.isCheckDiskStorageSpace = true,
|
||||
this.isShowOnlineStatus = true,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
enum TUIKitWideModalOperationKey{
|
||||
|
||||
/// You could use this value for your own Modal usage.
|
||||
custom,
|
||||
|
||||
// The following values are used in TUIKit
|
||||
|
||||
conversationSecondaryMenu,
|
||||
chooseCountry,
|
||||
beforeSendScreenShot,
|
||||
showUserProfileFromChat,
|
||||
addNewContact,
|
||||
showBlockedUsers,
|
||||
chooseContacts,
|
||||
addFriend,
|
||||
addGroup,
|
||||
chooseGroupType,
|
||||
settings,
|
||||
contactUs,
|
||||
aboutUs,
|
||||
showConditionsAndTerms,
|
||||
secondaryClickUserAvatar,
|
||||
forward,
|
||||
messageReadDetails,
|
||||
mergerMessageList,
|
||||
chooseMentionedMembers,
|
||||
chatHistory,
|
||||
groupAddOpt,
|
||||
setMute,
|
||||
setUnmute,
|
||||
setAdmins,
|
||||
deleteAdmin,
|
||||
groupMembersList,
|
||||
addGroupMembers,
|
||||
kickOffGroupMembers,
|
||||
confirmDeleteMessages,
|
||||
confirmClearChatHistory,
|
||||
confirmExitGroup,
|
||||
confirmDisbandGroup,
|
||||
confirmGeneral,
|
||||
unableToSendDueToFolders
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class TUIKitWebSupport {
|
||||
static void addSetterToWindow() {}
|
||||
static void addIdentifierToWindow() {}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// ignore: avoid_web_libraries_in_flutter
|
||||
import 'dart:js';
|
||||
|
||||
class TUIKitWebSupport {
|
||||
static void addSetterToWindow() {}
|
||||
static void addIdentifierToWindow() {
|
||||
// var object = newObject();
|
||||
// setProperty(object, "platform", "flutter_uikit");
|
||||
context['tencent_cloud_im_csig_flutter_for_web_25F_cy'] =
|
||||
"flutter_web_uikit";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimFriendshipListener.dart';
|
||||
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_type_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_application_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_application_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_check_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_check_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_info_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_operation_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_status.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_status.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
|
||||
abstract class FriendshipServices {
|
||||
Future<List<V2TimFriendInfoResult>?> getFriendsInfo({
|
||||
required List<String> userIDList,
|
||||
});
|
||||
|
||||
Future<List<V2TimUserFullInfo>?> getUsersInfo({
|
||||
required List<String> userIDList,
|
||||
});
|
||||
|
||||
Future<List<V2TimFriendOperationResult>?> addToBlackList({
|
||||
required List<String> userIDList,
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<V2TimFriendOperationResult>> addFriend({
|
||||
required String userID,
|
||||
required FriendTypeEnum addType,
|
||||
String? remark,
|
||||
String? friendGroup,
|
||||
String? addSource,
|
||||
String? addWording,
|
||||
});
|
||||
|
||||
Future<List<V2TimFriendOperationResult>?> deleteFromBlackList({
|
||||
required List<String> userIDList,
|
||||
});
|
||||
|
||||
Future<List<V2TimFriendOperationResult>?> deleteFromFriendList({
|
||||
required List<String> userIDList,
|
||||
required FriendTypeEnum deleteType,
|
||||
});
|
||||
|
||||
Future<List<V2TimFriendInfo>?> getFriendList();
|
||||
|
||||
Future<List<V2TimFriendInfoResult>?> searchFriends({
|
||||
required V2TimFriendSearchParam searchParam,
|
||||
});
|
||||
|
||||
Future<List<V2TimFriendInfo>?> getBlackList();
|
||||
|
||||
Future<List<V2TimFriendCheckResult>?> checkFriend({
|
||||
required List<String> userIDList,
|
||||
required FriendTypeEnum checkType,
|
||||
});
|
||||
|
||||
Future<void> addFriendListener({
|
||||
required V2TimFriendshipListener listener,
|
||||
});
|
||||
Future<void> removeFriendListener({
|
||||
V2TimFriendshipListener? listener,
|
||||
});
|
||||
|
||||
Future<V2TimFriendApplicationResult?> getFriendApplicationList();
|
||||
|
||||
Future<V2TimFriendOperationResult?> acceptFriendApplication(
|
||||
{required FriendResponseTypeEnum responseType, required FriendApplicationTypeEnum type, required String userID});
|
||||
|
||||
Future<V2TimFriendOperationResult?> refuseFriendApplication(
|
||||
{required FriendApplicationTypeEnum type, required String userID});
|
||||
|
||||
Future<V2TimCallback> setFriendInfo({
|
||||
required String userID,
|
||||
String? friendRemark,
|
||||
Map<String, String>? friendCustomInfo,
|
||||
});
|
||||
|
||||
Future<List<V2TimUserStatus>> getUserStatus({
|
||||
required List<String> userIDList,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimFriendshipListener.dart';
|
||||
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_type_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_application_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_application_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_check_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_check_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_info_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_operation_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_status.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_status.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/error_message_converter.dart';
|
||||
|
||||
class FriendshipServicesImpl implements FriendshipServices {
|
||||
final CoreServicesImpl _coreService = serviceLocator<CoreServicesImpl>();
|
||||
|
||||
@override
|
||||
Future<List<V2TimFriendInfoResult>?> getFriendsInfo({
|
||||
required List<String> userIDList,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getFriendshipManager().getFriendsInfo(userIDList: userIDList);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimUserFullInfo>?> getUsersInfo({
|
||||
required List<String> userIDList,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getUsersInfo(userIDList: userIDList);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimFriendOperationResult>?> addToBlackList({
|
||||
required List<String> userIDList,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getFriendshipManager().addToBlackList(userIDList: userIDList);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<V2TimFriendOperationResult>> addFriend({
|
||||
required String userID,
|
||||
required FriendTypeEnum addType,
|
||||
String? remark,
|
||||
String? friendGroup,
|
||||
String? addSource,
|
||||
String? addWording,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getFriendshipManager().addFriend(
|
||||
userID: userID,
|
||||
addType: addType,
|
||||
remark: remark,
|
||||
addWording: addWording,
|
||||
friendGroup: friendGroup,
|
||||
addSource: addSource,
|
||||
);
|
||||
if (result.code != 0) {
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: result.desc,
|
||||
errorCode: result.code,
|
||||
infoRecommendText: TIM_t("好友添加失败"),
|
||||
));
|
||||
} else if (result.code == 0 && result.data?.resultCode != 0) {
|
||||
String recommendText = "";
|
||||
if (result.data != null && result.data!.resultCode != null) {
|
||||
recommendText = ErrorMessageConverter.getErrorMessage(result.data!.resultCode!);
|
||||
}
|
||||
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: result.code == 0 ? result.data?.resultInfo : result.desc,
|
||||
errorCode: result.code == 0 ? result.data?.resultCode : result.code,
|
||||
infoRecommendText: recommendText,
|
||||
));
|
||||
} else {
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: result.desc,
|
||||
errorCode: result.code,
|
||||
infoRecommendText: TIM_t("好友添加成功"),
|
||||
));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimFriendOperationResult>?> deleteFromBlackList({
|
||||
required List<String> userIDList,
|
||||
}) async {
|
||||
final res =
|
||||
await TencentImSDKPlugin.v2TIMManager.getFriendshipManager().deleteFromBlackList(userIDList: userIDList);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimFriendOperationResult>?> deleteFromFriendList({
|
||||
required List<String> userIDList,
|
||||
required FriendTypeEnum deleteType,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getFriendshipManager()
|
||||
.deleteFromFriendList(userIDList: userIDList, deleteType: deleteType);
|
||||
if (res.code == 0) {
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: res.desc,
|
||||
errorCode: res.code,
|
||||
infoRecommendText: TIM_t("好友删除成功")));
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: res.desc,
|
||||
errorCode: res.code,
|
||||
infoRecommendText: TIM_t("好友删除失败")));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimFriendInfo>?> getFriendList() async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getFriendshipManager().getFriendList();
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimFriendInfo>?> getBlackList() async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getFriendshipManager().getBlackList();
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimFriendCheckResult>?> checkFriend({
|
||||
required List<String> userIDList,
|
||||
required FriendTypeEnum checkType,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getFriendshipManager()
|
||||
.checkFriend(userIDList: userIDList, checkType: checkType);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addFriendListener({
|
||||
required V2TimFriendshipListener listener,
|
||||
}) {
|
||||
return TencentImSDKPlugin.v2TIMManager.getFriendshipManager().addFriendListener(listener: listener);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeFriendListener({
|
||||
V2TimFriendshipListener? listener,
|
||||
}) {
|
||||
return TencentImSDKPlugin.v2TIMManager.getFriendshipManager().removeFriendListener(listener: listener);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimFriendApplicationResult?> getFriendApplicationList() async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getFriendshipManager().getFriendApplicationList();
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimFriendOperationResult?> acceptFriendApplication({
|
||||
required FriendResponseTypeEnum responseType,
|
||||
required FriendApplicationTypeEnum type,
|
||||
required String userID,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getFriendshipManager().acceptFriendApplication(
|
||||
responseType: responseType,
|
||||
type: type,
|
||||
userID: userID,
|
||||
);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimFriendOperationResult?> refuseFriendApplication(
|
||||
{required FriendApplicationTypeEnum type, required String userID}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getFriendshipManager()
|
||||
.refuseFriendApplication(type: type, userID: userID);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setFriendInfo({
|
||||
required String userID,
|
||||
String? friendRemark,
|
||||
Map<String, String>? friendCustomInfo,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getFriendshipManager()
|
||||
.setFriendInfo(friendRemark: friendRemark, friendCustomInfo: friendCustomInfo, userID: userID);
|
||||
if (res.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimFriendInfoResult>?> searchFriends({
|
||||
required V2TimFriendSearchParam searchParam,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getFriendshipManager().searchFriends(searchParam: searchParam);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimUserStatus>> getUserStatus({
|
||||
required List<String> userIDList,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getUserStatus(userIDList: userIDList);
|
||||
if (res.code == 0) {
|
||||
return res.data ?? [];
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimGroupListener.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/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_application_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_application_result.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/models/v2_tim_group_info_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_info_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_info_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_info_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_operation_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_operation_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_search_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_search_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
|
||||
abstract class GroupServices {
|
||||
Future<List<V2TimGroupInfo>?> getJoinedGroupList();
|
||||
|
||||
Future<List<V2TimGroupInfoResult>?> getGroupsInfo({
|
||||
required List<String> groupIDList,
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<V2TimGroupMemberInfoResult>> getGroupMemberList({
|
||||
required String groupID,
|
||||
required GroupMemberFilterTypeEnum filter,
|
||||
required String nextSeq,
|
||||
int count = 15,
|
||||
int offset = 0,
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<List<V2TimGroupMemberFullInfo>>> getGroupMembersInfo(
|
||||
{required String groupID, required List<String> memberList});
|
||||
|
||||
Future<V2TimCallback> setGroupInfo({
|
||||
required V2TimGroupInfo info,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> setGroupMemberRole({
|
||||
required String groupID,
|
||||
required String userID,
|
||||
required GroupMemberRoleTypeEnum role,
|
||||
});
|
||||
|
||||
getGroupMembersInfoThrottle({required String groupID, required List<String> memberList, Function? callBack});
|
||||
|
||||
Future<V2TimCallback> muteGroupMember({
|
||||
required String groupID,
|
||||
required String userID,
|
||||
required int seconds,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> setGroupMemberInfo({
|
||||
required String groupID,
|
||||
required String userID,
|
||||
String? nameCard,
|
||||
Map<String, String>? customInfo,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> kickGroupMember({
|
||||
required String groupID,
|
||||
required List<String> memberList,
|
||||
String? reason,
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<List<V2TimGroupMemberOperationResult>>> inviteUserToGroup({
|
||||
required String groupID,
|
||||
required List<String> userList,
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<List<V2TimGroupInfo>>> searchGroups({
|
||||
required V2TimGroupSearchParam searchParam,
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<V2GroupMemberInfoSearchResult>> searchGroupMembers({
|
||||
required V2TimGroupMemberSearchParam searchParam,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> joinGroup({
|
||||
required String groupID,
|
||||
required String message,
|
||||
});
|
||||
|
||||
Future<void> addGroupListener({
|
||||
required V2TimGroupListener listener,
|
||||
});
|
||||
|
||||
Future<void> removeGroupListener({
|
||||
V2TimGroupListener? listener,
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<V2TimGroupApplicationResult>> getGroupApplicationList();
|
||||
|
||||
Future<V2TimCallback> acceptGroupApplication({
|
||||
required String groupID,
|
||||
required String fromUser,
|
||||
required String toUser,
|
||||
required int addTime,
|
||||
required int type,
|
||||
String? reason,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> refuseGroupApplication(
|
||||
{String? reason,
|
||||
required int addTime,
|
||||
required String groupID,
|
||||
required String fromUser,
|
||||
required String toUser,
|
||||
required GroupApplicationTypeEnum type});
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimGroupListener.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/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_application_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_application_result.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/models/v2_tim_group_info_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_info_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_info_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_info_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_operation_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_operation_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_search_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_search_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.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/error_message_converter.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart';
|
||||
|
||||
class GroupServicesImpl extends GroupServices {
|
||||
static List<Function?> groupInfoCallBackList = [];
|
||||
final CoreServicesImpl _coreService = serviceLocator<CoreServicesImpl>();
|
||||
final throttleGetGroupInfo = OptimizeUtils.throttle((val) async {
|
||||
String groupID = val["groupID"];
|
||||
List<String> memberList = val["memberList"];
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getGroupManager()
|
||||
.getGroupMembersInfo(groupID: groupID, memberList: memberList);
|
||||
emitGroupCbList(res.data ?? []);
|
||||
clearGroupCbList();
|
||||
}, 1000);
|
||||
|
||||
static emitGroupCbList(List<V2TimGroupMemberFullInfo?> list) {
|
||||
for (var cb in groupInfoCallBackList) {
|
||||
cb!(list);
|
||||
}
|
||||
}
|
||||
|
||||
static clearGroupCbList() {
|
||||
groupInfoCallBackList = [];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimGroupInfo>?> getJoinedGroupList() async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getGroupManager().getJoinedGroupList();
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimGroupInfoResult>?> getGroupsInfo({
|
||||
required List<String> groupIDList,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getGroupManager().getGroupsInfo(groupIDList: groupIDList);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
} else {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void getGroupMembersInfoThrottle(
|
||||
{required String groupID, required List<String> memberList, Function? callBack}) async {
|
||||
if (callBack != null) {
|
||||
groupInfoCallBackList.add(callBack);
|
||||
throttleGetGroupInfo({"groupID": groupID, "memberList": memberList});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<List<V2TimGroupMemberFullInfo>>> getGroupMembersInfo(
|
||||
{required String groupID, required List<String> memberList}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getGroupManager()
|
||||
.getGroupMembersInfo(groupID: groupID, memberList: memberList);
|
||||
if (res.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<V2TimGroupMemberInfoResult>> getGroupMemberList({
|
||||
required String groupID,
|
||||
required GroupMemberFilterTypeEnum filter,
|
||||
required String nextSeq,
|
||||
int count = 15,
|
||||
int offset = 0,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getGroupManager()
|
||||
.getGroupMemberList(groupID: groupID, filter: filter, nextSeq: nextSeq, count: count, offset: offset);
|
||||
if (res.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setGroupInfo({
|
||||
required V2TimGroupInfo info,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getGroupManager().setGroupInfo(info: info);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setGroupMemberRole({
|
||||
required String groupID,
|
||||
required String userID,
|
||||
required GroupMemberRoleTypeEnum role,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getGroupManager()
|
||||
.setGroupMemberRole(groupID: groupID, userID: userID, role: role);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> muteGroupMember({
|
||||
required String groupID,
|
||||
required String userID,
|
||||
required int seconds,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getGroupManager()
|
||||
.muteGroupMember(groupID: groupID, userID: userID, seconds: seconds);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setGroupMemberInfo({
|
||||
required String groupID,
|
||||
required String userID,
|
||||
String? nameCard,
|
||||
Map<String, String>? customInfo,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getGroupManager()
|
||||
.setGroupMemberInfo(groupID: groupID, userID: userID, nameCard: nameCard, customInfo: customInfo);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> kickGroupMember({
|
||||
required String groupID,
|
||||
required List<String> memberList,
|
||||
String? reason,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getGroupManager()
|
||||
.kickGroupMember(groupID: groupID, memberList: memberList, reason: reason);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<List<V2TimGroupMemberOperationResult>>> inviteUserToGroup({
|
||||
required String groupID,
|
||||
required List<String> userList,
|
||||
}) async {
|
||||
final result =
|
||||
await TencentImSDKPlugin.v2TIMManager.getGroupManager().inviteUserToGroup(groupID: groupID, userList: userList);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<List<V2TimGroupInfo>>> searchGroups({
|
||||
required V2TimGroupSearchParam searchParam,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getGroupManager().searchGroups(searchParam: searchParam);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> joinGroup({
|
||||
required String groupID,
|
||||
required String message,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.joinGroup(groupID: groupID, message: message);
|
||||
if (result.code != 0) {
|
||||
String recommendText = ErrorMessageConverter.getErrorMessage(result.code);
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: result.desc,
|
||||
errorCode: result.code,
|
||||
infoRecommendText: recommendText));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<V2GroupMemberInfoSearchResult>> searchGroupMembers({
|
||||
required V2TimGroupMemberSearchParam searchParam,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getGroupManager().searchGroupMembers(param: searchParam);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addGroupListener({
|
||||
required V2TimGroupListener listener,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.addGroupListener(listener: listener);
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeGroupListener({
|
||||
V2TimGroupListener? listener,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.removeGroupListener(listener: listener);
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<V2TimGroupApplicationResult>> getGroupApplicationList() async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getGroupManager().getGroupApplicationList();
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> acceptGroupApplication(
|
||||
{required String groupID,
|
||||
required String fromUser,
|
||||
required String toUser,
|
||||
required int type,
|
||||
required int addTime,
|
||||
String? reason}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getGroupManager().acceptGroupApplication(
|
||||
groupID: groupID,
|
||||
fromUser: fromUser,
|
||||
toUser: toUser,
|
||||
addTime: addTime,
|
||||
type: GroupApplicationTypeEnum.values[type],
|
||||
reason: reason ?? "",
|
||||
);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> refuseGroupApplication(
|
||||
{String? reason,
|
||||
required int addTime,
|
||||
required String groupID,
|
||||
required String fromUser,
|
||||
required String toUser,
|
||||
required GroupApplicationTypeEnum type}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getGroupManager().refuseGroupApplication(
|
||||
groupID: groupID, fromUser: fromUser, toUser: toUser, type: type, addTime: addTime, reason: reason);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,685 @@
|
||||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimAdvancedMsgListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimSimpleMsgListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/get_group_message_read_member_list_filter.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/history_msg_get_type_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/message_priority_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/offlinePushInfo.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/receive_message_opt_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_message_read_member_list.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_message_read_member_list.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_change_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_change_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_list_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_list_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_online_url.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_online_url.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_receipt.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_receipt.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_search_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_msg_create_info_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_msg_create_info_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversation_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/error_message_converter.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||
|
||||
class MessageServiceImpl extends MessageService {
|
||||
final CoreServicesImpl _coreService = serviceLocator<CoreServicesImpl>();
|
||||
final Map<String, List<V2TimMessage>> messageListMap = {};
|
||||
final Map<String, List<V2TimMessage>> sendingMessage = {};
|
||||
|
||||
@override
|
||||
Future<MessageListResponse> getHistoryMessageListV2({
|
||||
HistoryMsgGetTypeEnum getType = HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG,
|
||||
String? userID,
|
||||
String? groupID,
|
||||
int lastMsgSeq = -1,
|
||||
required int count,
|
||||
String? lastMsgID,
|
||||
List<int>? messageTypeList,
|
||||
}) async {
|
||||
bool haveMoreData = true;
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().getHistoryMessageList(
|
||||
count: count,
|
||||
getType: getType,
|
||||
userID: userID,
|
||||
groupID: groupID,
|
||||
lastMsgID: lastMsgID,
|
||||
lastMsgSeq: lastMsgSeq,
|
||||
messageTypeList: messageTypeList);
|
||||
final List<V2TimMessage> responseMessageList = res.data ?? [];
|
||||
final conversationID = userID ?? groupID;
|
||||
final cachedMessageList = messageListMap[conversationID];
|
||||
List<V2TimMessage> combinedMessageList = [];
|
||||
// 加载更多
|
||||
if (lastMsgID != null && cachedMessageList != null) {
|
||||
combinedMessageList = [...cachedMessageList, ...responseMessageList];
|
||||
// 首次加载
|
||||
} else {
|
||||
final bool existSendingMessage =
|
||||
sendingMessage[conversationID] != null && sendingMessage[conversationID]!.isNotEmpty;
|
||||
// 存在未发送完成的消息
|
||||
if (existSendingMessage) {
|
||||
combinedMessageList = [...sendingMessage[conversationID]!, ...responseMessageList];
|
||||
} else {
|
||||
sendingMessage.remove(conversationID);
|
||||
combinedMessageList = responseMessageList;
|
||||
}
|
||||
}
|
||||
if (res.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
}
|
||||
if (responseMessageList.isEmpty ||
|
||||
(!PlatformUtils().isWeb && responseMessageList.length < count) ||
|
||||
(PlatformUtils().isWeb && responseMessageList.length < min(count, 20))) {
|
||||
haveMoreData = false;
|
||||
} else {
|
||||
haveMoreData = true;
|
||||
}
|
||||
return MessageListResponse(haveMoreData: haveMoreData, data: combinedMessageList);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimMessage>> getHistoryMessageList({
|
||||
HistoryMsgGetTypeEnum getType = HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG,
|
||||
String? userID,
|
||||
String? groupID,
|
||||
int lastMsgSeq = -1,
|
||||
required int count,
|
||||
String? lastMsgID,
|
||||
List<int>? messageTypeList,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().getHistoryMessageList(
|
||||
count: count,
|
||||
getType: getType,
|
||||
userID: userID,
|
||||
groupID: groupID,
|
||||
lastMsgID: lastMsgID,
|
||||
lastMsgSeq: lastMsgSeq,
|
||||
messageTypeList: messageTypeList);
|
||||
final reponseMessageList = res.data ?? [];
|
||||
if (res.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
}
|
||||
return reponseMessageList;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMessageListResult?> getHistoryMessageListWithComplete({
|
||||
HistoryMsgGetTypeEnum getType = HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG,
|
||||
String? userID,
|
||||
String? groupID,
|
||||
int lastMsgSeq = 0,
|
||||
required int count,
|
||||
String? lastMsgID,
|
||||
List<int>? messageTypeList,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().getHistoryMessageListV2(
|
||||
count: count,
|
||||
getType: getType,
|
||||
userID: userID,
|
||||
groupID: groupID,
|
||||
lastMsgID: lastMsgID,
|
||||
lastMsgSeq: lastMsgSeq,
|
||||
messageTypeList: messageTypeList);
|
||||
final responseMessageList = res.data;
|
||||
if (res.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
}
|
||||
return responseMessageList;
|
||||
}
|
||||
|
||||
@override
|
||||
Future addSimpleMsgListener({
|
||||
required V2TimSimpleMsgListener listener,
|
||||
}) async {
|
||||
return TencentImSDKPlugin.v2TIMManager.addSimpleMsgListener(listener: listener);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeSimpleMsgListener({V2TimSimpleMsgListener? listener}) {
|
||||
return TencentImSDKPlugin.v2TIMManager.removeSimpleMsgListener(listener: listener);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addAdvancedMsgListener({
|
||||
required V2TimAdvancedMsgListener listener,
|
||||
}) {
|
||||
return TencentImSDKPlugin.v2TIMManager.getMessageManager().addAdvancedMsgListener(listener: listener);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<V2TimGroupMessageReadMemberList>> getGroupMessageReadMemberList({
|
||||
required String messageID,
|
||||
required GetGroupMessageReadMemberListFilter filter,
|
||||
int nextSeq = 0,
|
||||
int count = 100,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.getGroupMessageReadMemberList(messageID: messageID, filter: filter, nextSeq: nextSeq, count: count);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<List<V2TimMessageReceipt>>> getMessageReadReceipts({
|
||||
required List<String> messageIDList,
|
||||
}) async {
|
||||
final result =
|
||||
await TencentImSDKPlugin.v2TIMManager.getMessageManager().getMessageReadReceipts(messageIDList: messageIDList);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> sendMessageReadReceipts({
|
||||
required List<String> messageIDList,
|
||||
}) async {
|
||||
return _retryMarkMessageAsRead(action: () {
|
||||
return TencentImSDKPlugin.v2TIMManager.getMessageManager().sendMessageReadReceipts(messageIDList: messageIDList);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMsgCreateInfoResult?> createTextMessage({required String text}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createTextMessage(text: text);
|
||||
if (res.code == 0) {
|
||||
final messageResult = res.data;
|
||||
return messageResult;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMsgCreateInfoResult?> createCustomMessage({required String data}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createCustomMessage(data: data);
|
||||
if (res.code == 0) {
|
||||
final messageResult = res.data;
|
||||
return messageResult;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMsgCreateInfoResult?> createFaceMessage({required int index, required String data}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createFaceMessage(index: index, data: data);
|
||||
if (res.code == 0) {
|
||||
final messageResult = res.data;
|
||||
return messageResult;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<V2TimMessage>> reSendMessage({required String msgID, bool? onlineUserOnly}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.reSendMessage(msgID: msgID, onlineUserOnly: onlineUserOnly ?? false);
|
||||
if (res.code != 0) {
|
||||
String recommendText = ErrorMessageConverter.getErrorMessage(res.code);
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code, infoRecommendText: recommendText));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMsgCreateInfoResult?> createTextAtMessage(
|
||||
{required String text, required List<String> atUserList}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.createTextAtMessage(text: text, atUserList: atUserList);
|
||||
if (res.code == 0) {
|
||||
final messageResult = res.data;
|
||||
return messageResult;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMsgCreateInfoResult?> createImageMessage(
|
||||
{String? imageName, String? imagePath, dynamic inputElement}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.createImageMessage(imageName: imageName, imagePath: imagePath ?? "", inputElement: inputElement);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMsgCreateInfoResult?> createSoundMessage({
|
||||
required String soundPath,
|
||||
required int duration,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.createSoundMessage(soundPath: soundPath, duration: duration);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<V2TimMessage>> sendMessage({
|
||||
required String id, // 自己创建的ID
|
||||
required String receiver,
|
||||
required String groupID,
|
||||
MessagePriorityEnum priority = MessagePriorityEnum.V2TIM_PRIORITY_NORMAL,
|
||||
bool onlineUserOnly = false,
|
||||
bool isExcludedFromUnreadCount = false,
|
||||
bool needReadReceipt = false,
|
||||
OfflinePushInfo? offlinePushInfo,
|
||||
String? cloudCustomData,
|
||||
String? localCustomData,
|
||||
bool isExcludedFromContentModeration = false,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().sendMessage(
|
||||
id: id,
|
||||
receiver: receiver,
|
||||
groupID: groupID,
|
||||
priority: priority,
|
||||
onlineUserOnly: onlineUserOnly,
|
||||
offlinePushInfo: offlinePushInfo,
|
||||
needReadReceipt: needReadReceipt,
|
||||
localCustomData: localCustomData,
|
||||
cloudCustomData: cloudCustomData,
|
||||
isExcludedFromContentModeration: isExcludedFromContentModeration,
|
||||
);
|
||||
if (result.code != 0) {
|
||||
String recommendText = ErrorMessageConverter.getErrorMessage(result.code);
|
||||
_coreService.callOnCallback(TIMCallback(
|
||||
type: TIMCallbackType.API_ERROR,
|
||||
errorMsg: result.desc,
|
||||
errorCode: result.code,
|
||||
infoRecommendText: recommendText));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> deleteMessageFromLocalStorage({
|
||||
required String msgID,
|
||||
Object? webMessageInstance,
|
||||
}) async {
|
||||
V2TimCallback result;
|
||||
if (kIsWeb) {
|
||||
result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.deleteMessages(msgIDs: [], webMessageInstanceList: [webMessageInstance]);
|
||||
} else {
|
||||
result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().deleteMessageFromLocalStorage(msgID: msgID);
|
||||
}
|
||||
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> revokeMessage({required String msgID, Object? webMessageInstance}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.revokeMessage(msgID: msgID, webMessageInstatnce: webMessageInstance);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> clearC2CHistoryMessage({
|
||||
required String userID,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().clearC2CHistoryMessage(userID: userID);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> clearGroupHistoryMessage({
|
||||
required String groupID,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().clearGroupHistoryMessage(groupID: groupID);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<V2TimCallback> _retryMarkMessageAsRead({
|
||||
required Future<V2TimCallback> Function() action,
|
||||
int retries = 3,
|
||||
}) async {
|
||||
V2TimCallback result;
|
||||
int attempts = 0;
|
||||
do {
|
||||
result = await action();
|
||||
if (result.code == 0) {
|
||||
return result;
|
||||
}
|
||||
attempts++;
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
} while (attempts < retries);
|
||||
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> markC2CMessageAsRead({
|
||||
required String userID,
|
||||
}) {
|
||||
return _retryMarkMessageAsRead(action: () {
|
||||
return TencentImSDKPlugin.v2TIMManager.getConversationManager().cleanConversationUnreadMessageCount(
|
||||
conversationID: "${TUIConversationViewModel.conversationC2CPrefix}$userID",
|
||||
cleanTimestamp: 0,
|
||||
cleanSequence: 0,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> markGroupMessageAsRead({
|
||||
required String groupID,
|
||||
}) {
|
||||
return _retryMarkMessageAsRead(action: () {
|
||||
return TencentImSDKPlugin.v2TIMManager.getConversationManager().cleanConversationUnreadMessageCount(
|
||||
conversationID: "${TUIConversationViewModel.conversationGroupPrefix}$groupID",
|
||||
cleanTimestamp: 0,
|
||||
cleanSequence: 0,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeAdvancedMsgListener({V2TimAdvancedMsgListener? listener}) async {
|
||||
final result =
|
||||
await TencentImSDKPlugin.v2TIMManager.getMessageManager().removeAdvancedMsgListener(listener: listener);
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimMessage>?> downloadMergerMessage({
|
||||
required String msgID,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().downloadMergerMessage(msgID: msgID);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMsgCreateInfoResult?> createForwardMessage({
|
||||
required String msgID,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createForwardMessage(msgID: msgID);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMsgCreateInfoResult?> createMergerMessage({
|
||||
required List<String> msgIDList,
|
||||
required String title,
|
||||
required List<String> abstractList,
|
||||
required String compatibleText,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createMergerMessage(
|
||||
msgIDList: msgIDList, title: title, abstractList: abstractList, compatibleText: compatibleText);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> deleteMessages({required List<String> msgIDs, List<dynamic>? webMessageInstanceList}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.deleteMessages(msgIDs: msgIDs, webMessageInstanceList: webMessageInstanceList);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMsgCreateInfoResult?> createVideoMessage(
|
||||
{String? videoPath, String? type, int? duration, String? snapshotPath, dynamic inputElement}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().createVideoMessage(
|
||||
videoFilePath: videoPath ?? "",
|
||||
type: type ?? "",
|
||||
duration: duration ?? 1,
|
||||
snapshotPath: snapshotPath ?? "",
|
||||
inputElement: inputElement);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<V2TimMessage>> sendReplyMessage({
|
||||
required String id, // 自己创建的ID
|
||||
required String receiver,
|
||||
required String groupID,
|
||||
OfflinePushInfo? offlinePushInfo,
|
||||
bool needReadReceipt = false,
|
||||
required V2TimMessage replyMessage, // 被回复的消息
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().sendReplyMessage(
|
||||
id: id,
|
||||
receiver: receiver,
|
||||
offlinePushInfo: offlinePushInfo,
|
||||
groupID: groupID,
|
||||
needReadReceipt: needReadReceipt,
|
||||
replyMessage: replyMessage);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMsgCreateInfoResult?> createFileMessage(
|
||||
{String? filePath, required String fileName, dynamic inputElement}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.createFileMessage(filePath: filePath ?? "", fileName: fileName, inputElement: inputElement);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimMsgCreateInfoResult?> createLocationMessage(
|
||||
{required String desc, required double longitude, required double latitude}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.createLocationMessage(desc: desc, longitude: longitude, latitude: latitude);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<V2TimMessageSearchResult>> searchLocalMessages(
|
||||
{required V2TimMessageSearchParam searchParam}) async {
|
||||
final result =
|
||||
await TencentImSDKPlugin.v2TIMManager.getMessageManager().searchLocalMessages(searchParam: searchParam);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<V2TimMessage>?> findMessages({
|
||||
required List<String> messageIDList,
|
||||
}) async {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getMessageManager().findMessages(messageIDList: messageIDList);
|
||||
if (res.code == 0) {
|
||||
return res.data;
|
||||
}
|
||||
_coreService.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: res.desc, errorCode: res.code));
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setLocalCustomInt({required String msgID, required int localCustomInt}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.setLocalCustomInt(msgID: msgID, localCustomInt: localCustomInt);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setC2CReceiveMessageOpt({
|
||||
required List<String> userIDList,
|
||||
required ReceiveMsgOptEnum opt,
|
||||
}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.setC2CReceiveMessageOpt(userIDList: userIDList, opt: opt);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setGroupReceiveMessageOpt({
|
||||
required String groupID,
|
||||
required ReceiveMsgOptEnum opt,
|
||||
}) async {
|
||||
final result =
|
||||
await TencentImSDKPlugin.v2TIMManager.getMessageManager().setGroupReceiveMessageOpt(groupID: groupID, opt: opt);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<V2TimMessageChangeInfo>> modifyMessage({required V2TimMessage message}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().modifyMessage(message: message);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> setLocalCustomData({required String msgID, required String localCustomData}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.setLocalCustomData(msgID: msgID, localCustomData: localCustomData);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimValueCallback<V2TimMessageOnlineUrl>> getMessageOnlineUrl({required String msgID}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager.getMessageManager().getMessageOnlineUrl(msgID: msgID);
|
||||
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<V2TimCallback> downloadMessage(
|
||||
{required String msgID, required int messageType, required int imageType, required bool isSnapshot}) async {
|
||||
final result = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.downloadMessage(msgID: msgID, messageType: messageType, imageType: imageType, isSnapshot: isSnapshot);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> translateText(String text, String target) async {
|
||||
final result =
|
||||
await TencentImSDKPlugin.v2TIMManager.getMessageManager().translateText(texts: [text], targetLanguage: target);
|
||||
if (result.code != 0) {
|
||||
_coreService
|
||||
.callOnCallback(TIMCallback(type: TIMCallbackType.API_ERROR, errorMsg: result.desc, errorCode: result.code));
|
||||
}
|
||||
return result.data?[text] ?? "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimAdvancedMsgListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimSimpleMsgListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/get_group_message_read_member_list_filter.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/history_msg_get_type_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/message_priority_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/offlinePushInfo.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/receive_message_opt_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_message_read_member_list.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_message_read_member_list.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_change_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_change_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_list_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_list_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_online_url.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_online_url.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_receipt.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_receipt.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_param.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_search_param.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_search_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_search_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_msg_create_info_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_msg_create_info_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
|
||||
class MessageListResponse {
|
||||
final bool haveMoreData;
|
||||
final List<V2TimMessage> data;
|
||||
|
||||
MessageListResponse({required this.haveMoreData, required this.data});
|
||||
}
|
||||
|
||||
abstract class MessageService {
|
||||
Future<List<V2TimMessage>> getHistoryMessageList({
|
||||
HistoryMsgGetTypeEnum getType = HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG,
|
||||
String? userID,
|
||||
String? groupID,
|
||||
int lastMsgSeq,
|
||||
required int count,
|
||||
String? lastMsgID,
|
||||
List<int>? messageTypeList,
|
||||
});
|
||||
|
||||
Future<V2TimMessageListResult?> getHistoryMessageListWithComplete({
|
||||
HistoryMsgGetTypeEnum getType = HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG,
|
||||
String? userID,
|
||||
String? groupID,
|
||||
int lastMsgSeq,
|
||||
required int count,
|
||||
String? lastMsgID,
|
||||
List<int>? messageTypeList,
|
||||
});
|
||||
|
||||
Future<MessageListResponse> getHistoryMessageListV2({
|
||||
HistoryMsgGetTypeEnum getType = HistoryMsgGetTypeEnum.V2TIM_GET_LOCAL_OLDER_MSG,
|
||||
String? userID,
|
||||
String? groupID,
|
||||
int lastMsgSeq,
|
||||
required int count,
|
||||
String? lastMsgID,
|
||||
List<int>? messageTypeList,
|
||||
});
|
||||
|
||||
Future addSimpleMsgListener({
|
||||
required V2TimSimpleMsgListener listener,
|
||||
});
|
||||
|
||||
Future addAdvancedMsgListener({
|
||||
required V2TimAdvancedMsgListener listener,
|
||||
});
|
||||
|
||||
Future<void> removeSimpleMsgListener({V2TimSimpleMsgListener? listener});
|
||||
|
||||
Future<V2TimMsgCreateInfoResult?> createTextMessage({required String text});
|
||||
|
||||
Future<V2TimMsgCreateInfoResult?> createFaceMessage({required int index, required String data});
|
||||
|
||||
Future<V2TimMsgCreateInfoResult?> createCustomMessage({required String data});
|
||||
|
||||
Future<V2TimMsgCreateInfoResult?> createTextAtMessage({required String text, required List<String> atUserList});
|
||||
|
||||
Future<V2TimValueCallback<V2TimMessage>> sendMessage(
|
||||
{required String id, // 自己创建的ID
|
||||
required String receiver,
|
||||
required String groupID,
|
||||
MessagePriorityEnum priority = MessagePriorityEnum.V2TIM_PRIORITY_NORMAL,
|
||||
bool onlineUserOnly = false,
|
||||
bool isExcludedFromUnreadCount = false,
|
||||
bool needReadReceipt = false,
|
||||
OfflinePushInfo? offlinePushInfo,
|
||||
String? cloudCustomData, // 云自定义消息字段,只能在消息发送前添加
|
||||
String? localCustomData,
|
||||
bool isExcludedFromContentModeration});
|
||||
|
||||
Future<V2TimValueCallback<V2TimMessage>> sendReplyMessage({
|
||||
required String id, // 自己创建的ID
|
||||
required String receiver,
|
||||
required String groupID,
|
||||
OfflinePushInfo? offlinePushInfo,
|
||||
bool needReadReceipt = false,
|
||||
required V2TimMessage replyMessage, // 被回复的消息
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<V2TimMessage>> reSendMessage({required String msgID, bool onlineUserOnly});
|
||||
|
||||
Future<V2TimValueCallback<V2TimMessageChangeInfo>> modifyMessage({required V2TimMessage message});
|
||||
|
||||
Future<V2TimMsgCreateInfoResult?> createImageMessage({String? imageName, String? imagePath, dynamic inputElement});
|
||||
|
||||
Future<V2TimMsgCreateInfoResult?> createVideoMessage(
|
||||
{String? videoPath = "", String? type = "", int? duration = 0, String? snapshotPath = "", dynamic inputElement});
|
||||
|
||||
Future<V2TimMsgCreateInfoResult?> createFileMessage(
|
||||
{String? filePath, required String fileName, dynamic inputElement});
|
||||
|
||||
Future<V2TimMsgCreateInfoResult?> createLocationMessage(
|
||||
{required String desc, required double longitude, required double latitude});
|
||||
|
||||
Future<V2TimMsgCreateInfoResult?> createSoundMessage({
|
||||
required String soundPath,
|
||||
required int duration,
|
||||
});
|
||||
|
||||
Future<V2TimMsgCreateInfoResult?> createForwardMessage({
|
||||
required String msgID,
|
||||
});
|
||||
|
||||
Future<V2TimMsgCreateInfoResult?> createMergerMessage({
|
||||
required List<String> msgIDList,
|
||||
required String title,
|
||||
required List<String> abstractList,
|
||||
required String compatibleText,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> deleteMessageFromLocalStorage({
|
||||
required String msgID,
|
||||
Object? webMessageInstance,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> revokeMessage({required String msgID, Object? webMessageInstance});
|
||||
|
||||
Future<V2TimCallback> clearC2CHistoryMessage({
|
||||
required String userID,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> clearGroupHistoryMessage({
|
||||
required String groupID,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> markC2CMessageAsRead({
|
||||
required String userID,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> markGroupMessageAsRead({
|
||||
required String groupID,
|
||||
});
|
||||
|
||||
Future<void> removeAdvancedMsgListener({V2TimAdvancedMsgListener? listener});
|
||||
|
||||
Future<List<V2TimMessage>?> downloadMergerMessage({
|
||||
required String msgID,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> deleteMessages({required List<String> msgIDs, List<dynamic>? webMessageInstanceList});
|
||||
|
||||
Future<List<V2TimMessage>?> findMessages({
|
||||
required List<String> messageIDList,
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<V2TimMessageSearchResult>> searchLocalMessages({
|
||||
required V2TimMessageSearchParam searchParam,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> setLocalCustomInt({required String msgID, required int localCustomInt});
|
||||
|
||||
Future<V2TimCallback> setLocalCustomData({required String msgID, required String localCustomData});
|
||||
|
||||
Future<V2TimCallback> setC2CReceiveMessageOpt({
|
||||
required List<String> userIDList,
|
||||
required ReceiveMsgOptEnum opt,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> setGroupReceiveMessageOpt({
|
||||
required String groupID,
|
||||
required ReceiveMsgOptEnum opt,
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<V2TimGroupMessageReadMemberList>> getGroupMessageReadMemberList({
|
||||
required String messageID,
|
||||
required GetGroupMessageReadMemberListFilter filter,
|
||||
int nextSeq = 0,
|
||||
int count = 100,
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<List<V2TimMessageReceipt>>> getMessageReadReceipts({
|
||||
required List<String> messageIDList,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> sendMessageReadReceipts({
|
||||
required List<String> messageIDList,
|
||||
});
|
||||
|
||||
Future<V2TimValueCallback<V2TimMessageOnlineUrl>> getMessageOnlineUrl({
|
||||
required String msgID,
|
||||
});
|
||||
|
||||
Future<V2TimCallback> downloadMessage({
|
||||
required String msgID,
|
||||
required int messageType,
|
||||
required int imageType, // 图片类型,仅messageType为图片消息是有效
|
||||
required bool isSnapshot, // 是否是视频封面,仅messageType为视频消息是有效
|
||||
});
|
||||
|
||||
Future<String> translateText(String text, String target);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/listener_model/tui_group_listener_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_model_tools.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversation_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_friendship_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_setting_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/conversation/conversation_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/conversation/conversation_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/group/group_services_implement.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/message/message_service_implement.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_search_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme_view_model.dart';
|
||||
|
||||
final serviceLocator = GetIt.instance;
|
||||
bool boolIsInitailized = false;
|
||||
|
||||
void setupServiceLocator() {
|
||||
if (!boolIsInitailized) {
|
||||
// setting
|
||||
serviceLocator.registerSingleton<TUISettingModel>(TUISettingModel());
|
||||
|
||||
// services
|
||||
serviceLocator.registerSingleton<CoreServicesImpl>(CoreServicesImpl());
|
||||
serviceLocator
|
||||
.registerSingleton<TUISelfInfoViewModel>(TUISelfInfoViewModel());
|
||||
serviceLocator
|
||||
.registerSingleton<ConversationService>(ConversationServicesImpl());
|
||||
serviceLocator.registerSingleton<MessageService>(MessageServiceImpl());
|
||||
serviceLocator
|
||||
.registerSingleton<FriendshipServices>(FriendshipServicesImpl());
|
||||
serviceLocator.registerSingleton<GroupServices>(GroupServicesImpl());
|
||||
|
||||
// view models
|
||||
serviceLocator.registerSingleton<TUIChatGlobalModel>(TUIChatGlobalModel());
|
||||
serviceLocator.registerSingleton<TUIChatModelTools>(TUIChatModelTools());
|
||||
serviceLocator.registerSingleton<TUIConversationViewModel>(
|
||||
TUIConversationViewModel());
|
||||
serviceLocator
|
||||
.registerSingleton<TUIFriendShipViewModel>(TUIFriendShipViewModel());
|
||||
serviceLocator.registerSingleton<TUIThemeViewModel>(TUIThemeViewModel());
|
||||
serviceLocator.registerSingleton<TUISearchViewModel>(TUISearchViewModel());
|
||||
|
||||
// listener models
|
||||
serviceLocator
|
||||
.registerSingleton<TUIGroupListenerModel>(TUIGroupListenerModel());
|
||||
boolIsInitailized = true;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,702 @@
|
||||
{
|
||||
"k_1yemzyd": "收到一條訊息",
|
||||
"k_0ylosxn": "自定義訊息",
|
||||
"k_13sajrj": "貼圖訊息",
|
||||
"k_13sjeb7": "檔案訊息",
|
||||
"k_0yd2ft8": "群提示訊息",
|
||||
"k_13s7mxn": "圖片訊息",
|
||||
"k_13satlt": "位置訊息",
|
||||
"k_00bbtsx": "合並轉發訊息",
|
||||
"k_13sqwu4": "語音訊息",
|
||||
"k_13sqjjp": "影片",
|
||||
"k_1fdhj9g": "該版本不支持此訊息",
|
||||
"k_06pujtm": "同意任何用戶添加好友",
|
||||
"k_0gyhkp5": "需要驗證",
|
||||
"k_121ruco": "拒絕任何人加好友",
|
||||
"k_05nspni": "自定義字段",
|
||||
"k_03fchyy": "群頭像",
|
||||
"k_03i9mfe": "群簡介",
|
||||
"k_03agq58": "群名稱",
|
||||
"k_039xqny": "群通知",
|
||||
"k_003tr0a": "群主",
|
||||
"k_03iqsh4": "$s為 ",
|
||||
"k_191t5n4": "$opUserNickName修改",
|
||||
"k_1pg6aoj": "$opUserNickName退出群組",
|
||||
"k_1f6zt3v": "邀請$invitedMemberString加入群組",
|
||||
"k_0y7zd07": "將$invitedMemberString踢出群組",
|
||||
"k_03c49qt": "去授權",
|
||||
"k_1d5mshh": "用戶$joinedMemberString加入了群組",
|
||||
"k_002wddw": "禁言",
|
||||
"k_0got6f7": "解除禁言",
|
||||
"k_0yenqf0": "$userName 被",
|
||||
"k_0spotql": "將 $adminMember 設置為管理員",
|
||||
"k_0pg5zzj": "系統訊息 $operationType",
|
||||
"k_0ohzb9l": "通話時間:$callTime",
|
||||
"k_1uaqed6": "[自定義]",
|
||||
"k_0z2z7rx": "[語音]",
|
||||
"k_0y39ngu": "[貼圖]",
|
||||
"k_1c7z88n": "[檔案] $fileName",
|
||||
"k_0y1a2my": "[圖片]",
|
||||
"k_0z4fib8": "[影片]",
|
||||
"k_0y24mcg": "[位置]",
|
||||
"k_0pewpd1": "[聊天記錄]",
|
||||
"k_13s8d9p": "未知訊息",
|
||||
"k_1c3us5n": "當前群組不支持@全體成員",
|
||||
"k_11k579v": "發言中有非法語句",
|
||||
"k_003qkx2": "日歷",
|
||||
"k_003n2pz": "相機",
|
||||
"k_03idjo0": "聯絡人",
|
||||
"k_003ltgm": "位置",
|
||||
"k_02k3k86": "咪高風",
|
||||
"k_003pm7l": "相冊",
|
||||
"k_15ao57x": "相冊寫入",
|
||||
"k_164m3jd": "本地存儲",
|
||||
"k_0qba4ns": "想訪問您的$yoursItem",
|
||||
"k_03r6qyx": "我們需要您的同意才能獲取信息",
|
||||
"k_02noktt": "不允許",
|
||||
"k_00043x4": "好",
|
||||
"k_003qzac": "昨天",
|
||||
"k_003r39d": "前天",
|
||||
"k_03fqp9o": "星期天",
|
||||
"k_03ibg5h": "星期一",
|
||||
"k_03i7hu1": "星期二",
|
||||
"k_03iaiks": "星期三",
|
||||
"k_03el9pa": "星期四",
|
||||
"k_03i7ok1": "星期五",
|
||||
"k_03efxyg": "星期六",
|
||||
"k_0oozw9x": "$diffMinutes 分鐘前",
|
||||
"k_003q7ba": "下午",
|
||||
"k_003q7bb": "上午",
|
||||
"k_003pu3h": "現在",
|
||||
"k_13hzn00": "昨天 $yesterday",
|
||||
"k_0n9pyxz": "用戶不存在",
|
||||
"k_1bjwemh": "搜尋用戶 ID",
|
||||
"k_003kv3v": "搜尋",
|
||||
"k_02owlq8": "我的用戶ID: $userID",
|
||||
"k_1wu8h4x": "我是: $showName",
|
||||
"k_16758qw": "添加好友",
|
||||
"k_1shx4d9": "個性簽名: $selfSignature",
|
||||
"k_0i553x0": "填寫驗證信息",
|
||||
"k_031ocwx": "請填寫備註和分組",
|
||||
"k_003ojje": "備註",
|
||||
"k_003lsav": "分組",
|
||||
"k_167bdvq": "我的好友",
|
||||
"k_156b4ut": "好友申請已發送",
|
||||
"k_002r305": "發送",
|
||||
"k_03gu05e": "聊天室",
|
||||
"k_03b4f3p": "會議群",
|
||||
"k_03avj1p": "公開群",
|
||||
"k_03asq2g": "工作群",
|
||||
"k_03b3hbi": "未知群",
|
||||
"k_1loix7s": "群類型: $groupType",
|
||||
"k_1lqbsib": "該群組不存在",
|
||||
"k_03h153m": "搜尋群ID",
|
||||
"k_0oxak3r": "群申請已發送",
|
||||
"k_002rflt": "刪除",
|
||||
"k_1don84v": "無法定位到原訊息",
|
||||
"k_003q5fi": "復製",
|
||||
"k_003prq0": "轉發",
|
||||
"k_002r1h2": "多選",
|
||||
"k_003j708": "引用",
|
||||
"k_003pqpr": "回收",
|
||||
"k_03ezhho": "已復製",
|
||||
"k_11ctfsz": "暫未實現",
|
||||
"k_1hbjg5g": "[群系統訊息]",
|
||||
"k_03tvswb": "[未知訊息]",
|
||||
"k_155cj23": "您回收了一條訊息,",
|
||||
"k_0gapun3": "重新編輯",
|
||||
"k_1uh417q": "$displayName回收了一條訊息",
|
||||
"k_1aszp2k": "您確定要重發這條訊息麽?",
|
||||
"k_003rzap": "確定",
|
||||
"k_003nevv": "取消",
|
||||
"k_0003z7x": "您",
|
||||
"k_002wfe4": "已讀",
|
||||
"k_002wjlg": "未讀",
|
||||
"k_0h1ygf8": "發起通話",
|
||||
"k_0h169j0": "取消通話",
|
||||
"k_0h13jjk": "接受通話",
|
||||
"k_0h19hfx": "拒絕通話",
|
||||
"k_0obi9lh": "超時未接聽",
|
||||
"k_0y9u662": "「$appName」暫不可以開啟此類檔案,你可以使用其他應用開啟並預覽",
|
||||
"k_001nmhu": "用其他應用開啟",
|
||||
"k_1ht1b80": "正在接收中",
|
||||
"k_105682d": "圖片載入失敗",
|
||||
"k_0pytyeu": "圖片保存成功",
|
||||
"k_0akceel": "圖片保存失敗",
|
||||
"k_003rk1s": "保存",
|
||||
"k_04a0awq": "[語音訊息]",
|
||||
"k_105c3y3": "影片載入失敗",
|
||||
"k_176rzr7": "聊天記錄",
|
||||
"k_0d5z4m5": "選擇提醒人",
|
||||
"k_003ngex": "完成",
|
||||
"k_1665ltg": "發起呼叫",
|
||||
"k_003n8b0": "拍攝",
|
||||
"k_003kthh": "照片",
|
||||
"k_003tnp0": "檔案",
|
||||
"k_0jhdhtp": "發送失敗,影片不能大於100MB",
|
||||
"k_119ucng": "圖片不能為空",
|
||||
"k_0w9x8gw": "選擇成功$successPath",
|
||||
"k_13dsw4l": "松開取消",
|
||||
"k_0am7r68": "手指上滑,取消發送",
|
||||
"k_15jl6qw": "說話時間太短!",
|
||||
"k_0gx7vl6": "按住說話",
|
||||
"k_15dlafd": "逐條轉發",
|
||||
"k_15dryxy": "合並轉發",
|
||||
"k_1eyhieh": "確定刪除已選訊息",
|
||||
"k_17fmlyf": "清除聊天",
|
||||
"k_0dhesoz": "取消置頂",
|
||||
"k_002sk7x": "置頂",
|
||||
"k_003ll77": "草稿",
|
||||
"k_03icaxo": "自定義",
|
||||
"k_1969986": "[語音通話]:$callingLastMsgShow",
|
||||
"k_1960dlr": "[視訊通話]:$callingLastMsgShow",
|
||||
"k_1np495n": "$messageString[有人@我]",
|
||||
"k_1m797yi": "$messageString[@所有人]",
|
||||
"k_1uaov41": "查找聊天內容",
|
||||
"k_003kfai": "未知",
|
||||
"k_13dq4an": "自動審批",
|
||||
"k_0l13cde": "管理員審批",
|
||||
"k_11y8c6a": "禁止加群",
|
||||
"k_1kvyskd": "無網絡連接,無法修改",
|
||||
"k_16payqf": "加群方式",
|
||||
"k_0vzvn8r": "修改群名稱",
|
||||
"k_038lh6u": "群管理",
|
||||
"k_0k5wyiy": "設置管理員",
|
||||
"k_0goiuwk": "全員禁言",
|
||||
"k_1g889xx": "全員禁言開啟後,只允許群主和管理員發言。",
|
||||
"k_0wlrefq": "添加需要禁言的群成員",
|
||||
"k_0goox5g": "設置禁言",
|
||||
"k_08daijh": "成功取消管理員身份",
|
||||
"k_0bxm97s": "管理員 ($adminNum/10)",
|
||||
"k_0k5u935": "添加管理員",
|
||||
"k_03enyx5": "群成員",
|
||||
"k_0jayw3z": "群成員($groupMemberNum人)",
|
||||
"k_0h1svv1": "刪除群成員",
|
||||
"k_0h1g636": "添加群成員",
|
||||
"k_0uj7208": "無網絡連接,無法查看群成員",
|
||||
"k_01yfa4o": "$memberCount人",
|
||||
"k_0hpukyx": "查看更多群成員",
|
||||
"k_0qtsar0": "訊息免打擾",
|
||||
"k_0ef2a12": "修改我的群昵稱",
|
||||
"k_1aajych": "僅限中文、字母、數字和下劃線,2-20個字",
|
||||
"k_137pab5": "我的群昵稱",
|
||||
"k_0ivim6d": "暫無群公告",
|
||||
"k_03eq6cn": "群公告",
|
||||
"k_002vxya": "編輯",
|
||||
"k_17fpl3y": "置頂聊天",
|
||||
"k_03es1ox": "群類型",
|
||||
"k_003mz1i": "同意",
|
||||
"k_003lpre": "拒絕",
|
||||
"k_003qk66": "頭像",
|
||||
"k_003lhvk": "昵稱",
|
||||
"k_003ps50": "賬號",
|
||||
"k_15lx52z": "個性簽名",
|
||||
"k_003qgkp": "性別",
|
||||
"k_003m6hr": "生日",
|
||||
"k_0003v6a": "男",
|
||||
"k_00043x2": "女",
|
||||
"k_03bcjkv": "未設置",
|
||||
"k_11s0gdz": "修改昵稱",
|
||||
"k_0p3j4sd": "僅限中字、字母、數字和下劃線",
|
||||
"k_15lyvdt": "修改簽名",
|
||||
"k_0vylzjp": "這個人很懶,什麽也沒寫",
|
||||
"k_1hs7ese": "等上線再改這個",
|
||||
"k_03exjk7": "備註名",
|
||||
"k_0s3skfd": "加入黑名單",
|
||||
"k_0p3b31s": "修改備註名",
|
||||
"k_0003y9x": "無",
|
||||
"k_11zgnfs": "個人資料",
|
||||
"k_03xd79d": "個性簽名: $signature",
|
||||
"k_1tez2xl": "暫無個性簽名",
|
||||
"k_118prbn": "全局搜尋",
|
||||
"k_1m9dftc": "全部聯絡人",
|
||||
"k_0em4gyz": "全部群組",
|
||||
"k_002twmj": "群組",
|
||||
"k_09kga0d": "更多聊天記錄",
|
||||
"k_1ui5lzi": "$count條相關聊天記錄",
|
||||
"k_09khmso": "相關聊天記錄",
|
||||
"k_1kevf4k": "與$receiver的聊天記錄",
|
||||
"k_0vjj2kp": "群組的聊天記錄",
|
||||
"k_003n2rp": "選擇",
|
||||
"k_03ignw6": "所有人",
|
||||
"k_03erpei": "管理員",
|
||||
"k_0qi9tno": "群主、管理員",
|
||||
"k_1m9exwh": "最近聯絡人",
|
||||
"k_119nwqr": "輸入不能為空",
|
||||
"k_0pzwbmg": "影片保存成功",
|
||||
"k_0aktupv": "影片保存失敗",
|
||||
"k_1qbg9xc": "$option8為 ",
|
||||
"k_1wq5ubm": "$option7修改",
|
||||
"k_0y5pu80": "$option6退出群組",
|
||||
"k_0nl7cmd": "邀請$option5加入群組",
|
||||
"k_1ju5iqw": "將$option4踢出群組",
|
||||
"k_1ovt677": "用戶$option3加入了群組",
|
||||
"k_0k05b8b": "$option2 被",
|
||||
"k_0wm4xeb": "系統訊息 $option2",
|
||||
"k_0nbq9v3": "通話時間:$option2",
|
||||
"k_0i1kf53": "[檔案] $option2",
|
||||
"k_1gnnby6": "想訪問您的$option2",
|
||||
"k_1wh4atg": "$option2 分鐘前",
|
||||
"k_07sh7g1": "昨天 $option2",
|
||||
"k_1pj8xzh": "我的用戶ID: $option2",
|
||||
"k_0py1evo": "個性簽名: $option2",
|
||||
"k_1kvj4i2": "$option2回收了一條訊息",
|
||||
"k_1v0lbpp": "「$option2」暫不可以開啟此類檔案,你可以使用其他應用開啟並預覽",
|
||||
"k_0torwfz": "選擇成功$option2",
|
||||
"k_0i1bjah": "$option1回收了一條訊息",
|
||||
"k_1qzxh9q": "通話時間:$option3",
|
||||
"k_0wrgmom": "[語音通話]:$option1",
|
||||
"k_06ix2f0": "[視訊通話]:$option2",
|
||||
"k_08o3z5w": "[檔案] $option1",
|
||||
"k_0ezbepg": "$option2[有人@我]",
|
||||
"k_1ccnht1": "$option2[@所有人]",
|
||||
"k_1k3arsw": "管理員 ($option2/10)",
|
||||
"k_1d4golg": "群成員($option1人)",
|
||||
"k_1bg69nt": "$option1人",
|
||||
"k_00gjqxj": "個性簽名: $option1",
|
||||
"k_0c29cxr": "$option1條相關聊天記錄",
|
||||
"k_1twk5rz": "與$option1的聊天記錄",
|
||||
"k_1vn4xq1": "將 $adminMember 取消管理員",
|
||||
"k_0e35hsw": "為方便您將所拍攝的照片或影片發送給朋友,以及進行視訊通話,請允許我們訪問攝像頭進行拍攝照片和影片。",
|
||||
"k_0dj6yr7": "為方便您發送語音訊息、拍攝影片以及音視訊通話,請允許我們使用咪高風進行錄音。",
|
||||
"k_003qnsl": "存儲",
|
||||
"k_0s3rtpw": "為方便您查看和選擇相冊裏的圖片影片發送給朋友,以及保存內容到設備,請允許我們訪問您設備上的照片、媒體內容。",
|
||||
"k_0tezv85": " 申請獲取$option2",
|
||||
"k_002rety": "權限",
|
||||
"k_18o68ro": "需要授予",
|
||||
"k_1onpf8u": " 相機權限,以正常使用拍攝圖片/影片、視訊通話等功能。",
|
||||
"k_17irga5": " 咪高風權限,以正常使用發送語音訊息、拍攝影片、音視訊通話等功能。",
|
||||
"k_0572kc4": " 訪問照片權限,以正常使用發送圖片、影片等功能。",
|
||||
"k_0slykws": " 訪問相冊寫入權限,以正常使用存儲圖片、影片等功能。",
|
||||
"k_119pkcd": " 檔案讀寫權限,以正常使用在聊天功能中的圖片查看、選擇能力和發送檔案的能力。",
|
||||
"k_0gqewd3": "以後再說",
|
||||
"k_03eq4s1": "去開啟",
|
||||
"k_0nt2uyg": "回到最新位置",
|
||||
"k_04l16at": "$option1條新訊息",
|
||||
"k_13p3w93": "有人@我",
|
||||
"k_18w5uk6": "@所有人",
|
||||
"k_0jmujgh": "其他檔案正在接收中",
|
||||
"k_12s5ept": "訊息詳情",
|
||||
"k_0mxa4f4": "$option1人已讀",
|
||||
"k_061tue3": "$option2人未讀",
|
||||
"k_18qjstb": "轉讓群主",
|
||||
"k_0on1aj2": "有$option2條@我訊息",
|
||||
"k_09j4izl": "[有人@我] ",
|
||||
"k_1oqtjw0": "[@所有人] ",
|
||||
"k_1x5a9vb": "我是: $option1",
|
||||
"k_14n31e7": "進群請求",
|
||||
"k_08nc5j1": "群類型: $option1",
|
||||
"k_1josu12": "$option1 條入群請求",
|
||||
"k_0n2x5s0": "驗證消息: $option2",
|
||||
"k_03c1nx0": "已同意",
|
||||
"k_03aw9w8": "已拒絕",
|
||||
"k_038ryos": "去處理",
|
||||
"k_0gw8pum": "進群申請",
|
||||
"k_1gcvfrj": "請填寫備註名",
|
||||
"k_002v9zj": "确认",
|
||||
"k_10oqrki": "轻触拍照",
|
||||
"k_0f8b3ws": "加载失败",
|
||||
"k_11cm5lm": "手动聚焦",
|
||||
"k_002uzrd": "预览",
|
||||
"k_003qkn3": "录像",
|
||||
"k_003k6a7": "拍照",
|
||||
"k_0bqpqco": "拍照按钮",
|
||||
"k_1626ozl": "停止录像",
|
||||
"k_003lvmu": "前置",
|
||||
"k_003lued": "后置",
|
||||
"k_003lwzh": "外置",
|
||||
"k_002qzi3": "关闭",
|
||||
"k_003pufb": "自动",
|
||||
"k_0apm0ze": "拍照时闪光",
|
||||
"k_157zog5": "始终闪光",
|
||||
"k_0cfyqhy": "$option1 画面预览",
|
||||
"k_0phctlz": "闪光模式: $option2",
|
||||
"k_02vfqe0": "切换至 $option3 摄像头",
|
||||
"k_0f0y9ex": "说话时间太短",
|
||||
"k_0ln70tk": "无法打开URL",
|
||||
"k_11a3jdv": "轻触拍照,长按摄像",
|
||||
"k_1k18miv": "请传入离开群组生命周期函数,提供返回首页或其他页面的导航方法。",
|
||||
"k_1fu9ahv": "全员禁言状态",
|
||||
"k_0gmwbnd": "全员禁言中",
|
||||
"k_0got2zr": "您被禁言",
|
||||
"k_0y9jck8": "你必须自定义search bar,并处理点击跳转",
|
||||
"k_0yum3tv": "如使用自定义区域,请在profileWidgetBuilder传入对应组件",
|
||||
"k_09kalj0": "清空聊天记录",
|
||||
"k_14j5iul": "删除并退出",
|
||||
"k_125ru1w": "解散该群",
|
||||
"k_0jtutmw": "退出后不会接收到此群聊消息",
|
||||
"k_0jtzmqa": "解散后不会接收到此群聊消息",
|
||||
"k_0r8fi93": "好友添加成功",
|
||||
"k_02qw14e": "好友申请已发出",
|
||||
"k_0n3md5x": "当前用户在黑名单",
|
||||
"k_094phq4": "好友添加失败",
|
||||
"k_129scag": "好友删除成功",
|
||||
"k_129uzfn": "好友删除失败",
|
||||
"k_1666isy": "清除好友",
|
||||
"k_1679vrd": "加为好友",
|
||||
"k_1ualc52": "看看对方带来的数据是啥",
|
||||
"k_0szluvp": "设置对方在线状态",
|
||||
"k_0f4rnf8": "该用户已是好友",
|
||||
"k_1tdkom4": "您已是群成员",
|
||||
"k_1p2lyuz": "对方正在输入中...",
|
||||
"k_1g8wfpy": "...共$option1人",
|
||||
"k_12rv9vw": "回应详情",
|
||||
"k_0havgi0": "[查看详情 >>](${linkMessage.link})",
|
||||
"k_0n9p7g8": "群组不存在",
|
||||
"k_1tdh5vn": "您不是群成员",
|
||||
"k_0h1q57v": "暂无群成员",
|
||||
"k_0y5drq1": "[查看详情 >>]($option1)",
|
||||
"k_03pjp61": "[表情消息]",
|
||||
"k_1jpvzul": "[自定义消息]",
|
||||
"k_03u3bh1": "[文件消息]",
|
||||
"k_1odsnsw": "[群消息]",
|
||||
"k_03sel4t": "[图片消息]",
|
||||
"k_03sfw3r": "[位置消息]",
|
||||
"k_03xpuwq": "[合并消息]",
|
||||
"k_07ycxwo": "[没有元素]",
|
||||
"k_03rc9vz": "[文本消息]",
|
||||
"k_046uopf": "[视频消息]",
|
||||
"k_0ehmsun": "设备存储空间不足,建议清理,以获得更好使用体验",
|
||||
"k_003kmos": "图片",
|
||||
"k_002s86q": "视频",
|
||||
"k_06bk5ei": "视频消息仅限 mp4 格式",
|
||||
"k_13opfxf": "Web网页端不支持搜索",
|
||||
"k_1i0o0y2": "暂时仅限 Android/iOS 端",
|
||||
"k_045dtzl": "$option1的聊天记录",
|
||||
"k_0t0131u": "群资料信息",
|
||||
"k_18ok8xz": "消息接收方式",
|
||||
"k_03ax3ks": "群资料",
|
||||
"k_0sqvoqo": "将 $option1 设置为管理员",
|
||||
"k_1gbg1v8": "将 $option1 取消管理员",
|
||||
"k_17k64g4": "群聊创建成功!",
|
||||
"k_05mn217": "暂未安装表情包插件,如需使用表情相关功能,请根据本文档安装:https://cloud.tencent.com/document/product/269/70746",
|
||||
"k_14j17nz": "暂无表情包",
|
||||
"k_0fvjexh": "正在下载中",
|
||||
"k_1cdagzz": "已加入待下载队列,其他文件下载中",
|
||||
"k_0g4vojc": "开始下载",
|
||||
"k_1g32es3": "[调皮]@2x.png",
|
||||
"k_1g8qorz": "[爱你]@2x.png",
|
||||
"k_1g4hmx6": "[爱情]@2x.png",
|
||||
"k_1g6b558": "[爱心]@2x.png",
|
||||
"k_1g3m4su": "[傲慢]@2x.png",
|
||||
"k_1g2jym7": "[白眼]@2x.png",
|
||||
"k_0cgkxuw": "[棒棒糖]@2x.png",
|
||||
"k_1g48br2": "[抱抱]@2x.png",
|
||||
"k_1g49ol8": "[抱拳]@2x.png",
|
||||
"k_1g0ras3": "[爆筋]@2x.png",
|
||||
"k_1ghy881": "[鄙视]@2x.png",
|
||||
"k_1g86bmv": "[闭嘴]@2x.png",
|
||||
"k_1g1xs1p": "[鞭炮]@2x.png",
|
||||
"k_1g8i6ri": "[便便]@2x.png",
|
||||
"k_1g2u5kf": "[擦汗]@2x.png",
|
||||
"k_1g60uwh": "[彩带]@2x.png",
|
||||
"k_1g1o0d0": "[彩球]@2x.png",
|
||||
"k_1g6a6yq": "[菜刀]@2x.png",
|
||||
"k_1g6vqo2": "[差劲]@2x.png",
|
||||
"k_1g0kvjc": "[钞票]@2x.png",
|
||||
"k_1g65x7e": "[车厢]@2x.png",
|
||||
"k_0e1tjol": "[打哈欠]@2x.png",
|
||||
"k_1g65n58": "[大兵]@2x.png",
|
||||
"k_1g7se7o": "[大哭]@2x.png",
|
||||
"k_1g03868": "[蛋糕]@2x.png",
|
||||
"k_1h8nm66": "[刀]@2x.png",
|
||||
"k_1g3dlpi": "[得意]@2x.png",
|
||||
"k_1g3u434": "[灯泡]@2x.png",
|
||||
"k_1giuqs7": "[凋谢]@2x.png",
|
||||
"k_1g8r0r9": "[多云]@2x.png",
|
||||
"k_1g7k6i1": "[发呆]@2x.png",
|
||||
"k_1g44zsp": "[发抖]@2x.png",
|
||||
"k_1g5l96i": "[飞机]@2x.png",
|
||||
"k_1g7wsqj": "[飞吻]@2x.png",
|
||||
"k_1g49luq": "[奋斗]@2x.png",
|
||||
"k_1gixbsm": "[风车]@2x.png",
|
||||
"k_1g6cqbq": "[尴尬]@2x.png",
|
||||
"k_1g6jbw5": "[勾引]@2x.png",
|
||||
"k_1g3lwo1": "[鼓掌]@2x.png",
|
||||
"k_1g13nkj": "[害羞]@2x.png",
|
||||
"k_1g0mt47": "[憨笑]@2x.png",
|
||||
"k_0bxujkf": "[红灯笼]@2x.png",
|
||||
"k_0hhaeh8": "[红双喜]@2x.png",
|
||||
"k_1g0jnts": "[坏笑]@2x.png",
|
||||
"k_1g46g9c": "[挥手]@2x.png",
|
||||
"k_1g4vi9g": "[回头]@2x.png",
|
||||
"k_1gf7hes": "[饥饿]@2x.png",
|
||||
"k_1g6mvsm": "[激动]@2x.png",
|
||||
"k_1gku5mf": "[街舞]@2x.png",
|
||||
"k_1g4hidg": "[惊恐]@2x.png",
|
||||
"k_1gjbrtu": "[惊讶]@2x.png",
|
||||
"k_1g6sand": "[咖啡]@2x.png",
|
||||
"k_1g4s8rj": "[磕头]@2x.png",
|
||||
"k_1g1wn34": "[可爱]@2x.png",
|
||||
"k_1g3l0wd": "[可怜]@2x.png",
|
||||
"k_1ggaon9": "[抠鼻]@2x.png",
|
||||
"k_1ggvcb0": "[骷髅]@2x.png",
|
||||
"k_1h8yqjt": "[酷]@2x.png",
|
||||
"k_0jac97i": "[快哭了]@2x.png",
|
||||
"k_1h8oiby": "[困]@2x.png",
|
||||
"k_1g0s5hg": "[蜡烛]@2x.png",
|
||||
"k_1g1iuer": "[篮球]@2x.png",
|
||||
"k_1g2xjfi": "[冷汗]@2x.png",
|
||||
"k_0s5oyqw": "[礼品袋]@2x.png",
|
||||
"k_1g1qqvf": "[礼物]@2x.png",
|
||||
"k_1g2slew": "[流汗]@2x.png",
|
||||
"k_1g3z9xx": "[流泪]@2x.png",
|
||||
"k_1g6pabn": "[麻将]@2x.png",
|
||||
"k_0pkaxul": "[麦克风]@2x.png",
|
||||
"k_1g7m0zj": "[猫咪]@2x.png",
|
||||
"k_0ibvtpo": "[么么哒]@2x.png",
|
||||
"k_1g1hoh1": "[玫瑰]@2x.png",
|
||||
"k_1gfzeow": "[米饭]@2x.png",
|
||||
"k_1g5l15p": "[面条]@2x.png",
|
||||
"k_1g2hfa6": "[奶瓶]@2x.png",
|
||||
"k_1gix9pj": "[难过]@2x.png",
|
||||
"k_1giqn6g": "[闹钟]@2x.png",
|
||||
"k_1h8kd64": "[怒]@2x.png",
|
||||
"k_1g0vui9": "[怄火]@2x.png",
|
||||
"k_1g1jsj7": "[皮球]@2x.png",
|
||||
"k_1ghdluw": "[啤酒]@2x.png",
|
||||
"k_1gl6ec7": "[瓢虫]@2x.png",
|
||||
"k_1g7gg5p": "[撇嘴]@2x.png",
|
||||
"k_1g8psin": "[乒乓]@2x.png",
|
||||
"k_1gjzu3p": "[汽车]@2x.png",
|
||||
"k_1h8mr0k": "[强]@2x.png",
|
||||
"k_1g45y2n": "[敲打]@2x.png",
|
||||
"k_1gkaxsl": "[青蛙]@2x.png",
|
||||
"k_0jcfnoo": "[糗大了]@2x.png",
|
||||
"k_1g4njy1": "[拳头]@2x.png",
|
||||
"k_1h8mqr3": "[弱]@2x.png",
|
||||
"k_1h926fg": "[色]@2x.png",
|
||||
"k_1g6rtbq": "[沙发]@2x.png",
|
||||
"k_1giirh6": "[删除]@2x.png",
|
||||
"k_1g14ny9": "[闪电]@2x.png",
|
||||
"k_1g6bmsr": "[胜利]@2x.png",
|
||||
"k_1g1rytx": "[示爱]@2x.png",
|
||||
"k_1g52fbz": "[手枪]@2x.png",
|
||||
"k_1h90dam": "[衰]@2x.png",
|
||||
"k_1gigiae": "[睡觉]@2x.png",
|
||||
"k_1gijchz": "[太阳]@2x.png",
|
||||
"k_1g1sgji": "[跳绳]@2x.png",
|
||||
"k_1gjwuri": "[跳跳]@2x.png",
|
||||
"k_1g0juhk": "[偷笑]@2x.png",
|
||||
"k_1h8nzla": "[吐]@2x.png",
|
||||
"k_1g6cv0i": "[委屈]@2x.png",
|
||||
"k_1g46l5g": "[握手]@2x.png",
|
||||
"k_1g2pgkd": "[西瓜]@2x.png",
|
||||
"k_1ging9p": "[下雨]@2x.png",
|
||||
"k_1h8nzil": "[吓]@2x.png",
|
||||
"k_1g7q7wr": "[献吻]@2x.png",
|
||||
"k_1gl6uum": "[香蕉]@2x.png",
|
||||
"k_1g23fys": "[象棋]@2x.png",
|
||||
"k_0j75rdh": "[心碎了]@2x.png",
|
||||
"k_1g6ajj2": "[信封]@2x.png",
|
||||
"k_1g21prz": "[熊猫]@2x.png",
|
||||
"k_1h8octi": "[嘘]@2x.png",
|
||||
"k_1h91zox": "[药]@2x.png",
|
||||
"k_1ghttfl": "[疑问]@2x.png",
|
||||
"k_1ghk7sz": "[阴险]@2x.png",
|
||||
"k_0gl37zz": "[右车头]@2x.png",
|
||||
"k_0ifkj1p": "[右哼哼]@2x.png",
|
||||
"k_0g1yh2e": "[右太极]@2x.png",
|
||||
"k_1g9dkfc": "[雨伞]@2x.png",
|
||||
"k_1g8jl88": "[月亮]@2x.png",
|
||||
"k_1h8lhqj": "[晕]@2x.png",
|
||||
"k_1gi9x2q": "[再见]@2x.png",
|
||||
"k_1g6dwwv": "[炸弹]@2x.png",
|
||||
"k_1fzmkfi": "[折磨]@2x.png",
|
||||
"k_1g6jbiw": "[纸巾]@2x.png",
|
||||
"k_1ggjnwu": "[咒骂]@2x.png",
|
||||
"k_1g4qlq8": "[猪头]@2x.png",
|
||||
"k_1g1lqzz": "[抓狂]@2x.png",
|
||||
"k_1g80j3u": "[转圈]@2x.png",
|
||||
"k_1g0z55s": "[龇牙]@2x.png",
|
||||
"k_1g3ju6v": "[钻戒]@2x.png",
|
||||
"k_0gl51l6": "[左车头]@2x.png",
|
||||
"k_0iflllk": "[左哼哼]@2x.png",
|
||||
"k_0g1y3ir": "[左太极]@2x.png",
|
||||
"k_026hiq5": "消息列表加载中",
|
||||
"k_003tu8k": "爱你",
|
||||
"k_003myvp": "傲慢",
|
||||
"k_003kddw": "白眼",
|
||||
"k_039yfhv": "棒棒糖",
|
||||
"k_003nu3p": "抱抱",
|
||||
"k_003nijr": "抱拳",
|
||||
"k_003mg88": "爆筋",
|
||||
"k_002v17e": "鄙视",
|
||||
"k_003qhy4": "闭嘴",
|
||||
"k_003l5fq": "鞭炮",
|
||||
"k_003uacl": "便便",
|
||||
"k_003oq1g": "擦汗",
|
||||
"k_003qvey": "彩带",
|
||||
"k_003jci7": "彩球",
|
||||
"k_003pyu1": "菜刀",
|
||||
"k_003q97d": "差劲",
|
||||
"k_003po5d": "车厢",
|
||||
"k_03eadb2": "打哈欠",
|
||||
"k_003pnuf": "大兵",
|
||||
"k_003kg57": "蛋糕",
|
||||
"k_003mxkt": "得意",
|
||||
"k_003onu3": "灯泡",
|
||||
"k_002uv8s": "凋谢",
|
||||
"k_003kqy0": "调皮",
|
||||
"k_003tyum": "多云",
|
||||
"k_003pv9u": "发呆",
|
||||
"k_036o6mu": "发抖t",
|
||||
"k_003nogx": "飞机",
|
||||
"k_003q7wg": "飞吻",
|
||||
"k_003m0jd": "奋斗",
|
||||
"k_002ult9": "风车",
|
||||
"k_003r8gt": "尴尬",
|
||||
"k_003qy4u": "勾引",
|
||||
"k_003mnoa": "鼓掌",
|
||||
"k_003lmw8": "害羞",
|
||||
"k_003mb30": "憨笑",
|
||||
"k_03bj41g": "红灯笼",
|
||||
"k_03dxw2f": "红双喜",
|
||||
"k_003mk57": "坏笑",
|
||||
"k_003nmvf": "挥手",
|
||||
"k_003r2i7": "回头",
|
||||
"k_002s6f3": "饥饿",
|
||||
"k_003qd0t": "激动",
|
||||
"k_002vgi4": "街舞",
|
||||
"k_003nz33": "惊恐",
|
||||
"k_002wh4p": "惊讶",
|
||||
"k_003ozpu": "咖啡",
|
||||
"k_003qvs4": "磕头",
|
||||
"k_003l3wb": "可爱",
|
||||
"k_003nuwm": "可怜",
|
||||
"k_002rw1q": "抠鼻",
|
||||
"k_002tujb": "骷髅",
|
||||
"k_00030eq": "酷",
|
||||
"k_03i8ath": "快哭了",
|
||||
"k_000421h": "困",
|
||||
"k_003l5i7": "蜡烛",
|
||||
"k_003j72g": "篮球",
|
||||
"k_003ofwl": "冷汗",
|
||||
"k_02mw65v": "礼品袋",
|
||||
"k_003ku40": "礼物",
|
||||
"k_003ookz": "流汗",
|
||||
"k_003on72": "流泪",
|
||||
"k_003rjy0": "麻将",
|
||||
"k_003q2f8": "猫咪",
|
||||
"k_03et393": "么么哒",
|
||||
"k_003j7j2": "玫瑰",
|
||||
"k_002sr0b": "米饭",
|
||||
"k_003nnza": "面条",
|
||||
"k_003jef9": "奶瓶",
|
||||
"k_002umn0": "难过",
|
||||
"k_002rjib": "闹钟",
|
||||
"k_0003zcn": "怒",
|
||||
"k_003jzwq": "怄火",
|
||||
"k_003j4js": "皮球",
|
||||
"k_002r5ir": "啤酒",
|
||||
"k_002ubu4": "瓢虫",
|
||||
"k_003ppo6": "撇嘴",
|
||||
"k_003ty3o": "乒乓",
|
||||
"k_002vxwe": "汽车",
|
||||
"k_00043hb": "强",
|
||||
"k_003nmbo": "敲打",
|
||||
"k_002tfhq": "青蛙",
|
||||
"k_03i7lrn": "糗大了",
|
||||
"k_003r03m": "拳头",
|
||||
"k_00043h0": "弱",
|
||||
"k_000345z": "色",
|
||||
"k_003qmp9": "沙发",
|
||||
"k_003it8a": "闪电",
|
||||
"k_003pxow": "胜利",
|
||||
"k_003kw8e": "示爱",
|
||||
"k_003n99g": "手枪",
|
||||
"k_00035cl": "衰",
|
||||
"k_002vl3h": "睡觉",
|
||||
"k_002rgqk": "太阳",
|
||||
"k_003m9d1": "跳绳",
|
||||
"k_002vobp": "跳跳",
|
||||
"k_003mkoz": "偷笑",
|
||||
"k_00041px": "吐",
|
||||
"k_003rjh5": "委屈",
|
||||
"k_003j36u": "西瓜",
|
||||
"k_002re92": "下雨",
|
||||
"k_00041py": "吓",
|
||||
"k_003q06o": "献吻",
|
||||
"k_002ubjp": "香蕉",
|
||||
"k_003o2tr": "象棋",
|
||||
"k_03ie6pa": "心碎了",
|
||||
"k_003rao5": "信封",
|
||||
"k_003l3us": "熊猫",
|
||||
"k_000424d": "嘘",
|
||||
"k_00033yi": "药",
|
||||
"k_002qtyy": "疑问",
|
||||
"k_002qe0o": "阴险",
|
||||
"k_03gu7us": "右车头",
|
||||
"k_03ere8m": "右哼哼",
|
||||
"k_003uqk3": "雨伞",
|
||||
"k_003tzdv": "月亮",
|
||||
"k_0003z00": "晕",
|
||||
"k_002vdrd": "再见",
|
||||
"k_003ra1w": "炸弹",
|
||||
"k_003lcad": "折磨",
|
||||
"k_003q7sz": "纸巾",
|
||||
"k_002thn9": "咒骂",
|
||||
"k_003qx7f": "猪头",
|
||||
"k_003l044": "抓狂",
|
||||
"k_003qg4h": "转圈",
|
||||
"k_003kb97": "龇牙",
|
||||
"k_03gu53l": "左车头",
|
||||
"k_03erd1f": "左哼哼",
|
||||
"k_003nyvl": "爱情",
|
||||
"k_003r85z": "爱心",
|
||||
"k_003mk8j": "钞票",
|
||||
"k_003pwfj": "大哭",
|
||||
"k_00042w5": "刀",
|
||||
"k_003nmtr": "握手",
|
||||
"k_03c529p": "右太极",
|
||||
"k_003n4mk": "钻戒",
|
||||
"k_03c5488": "左太极",
|
||||
"k_1llp7tu": "该用户不存在",
|
||||
"k_0tbyqyb": "加载中…",
|
||||
"k_0td1p3f": "保存中…",
|
||||
"k_1klqdh1": "仅限汉字、英文、数字和下划线",
|
||||
"k_03el5lp": "未填写",
|
||||
"k_1ui0gai": "搜索指定内容",
|
||||
"k_003nvk2": "消息",
|
||||
"k_03agld7": "群提示",
|
||||
"k_0elt0kw": "添加群聊",
|
||||
"k_0s3sgel": "移出黑名单",
|
||||
"k_1qqgjra": "$option3条未读消息",
|
||||
"k_0uubyjr": "以下为未读消息",
|
||||
"k_16as7eq": "表情回应",
|
||||
"k_003s12u": "回复",
|
||||
"k_003s38r": "更多",
|
||||
"k_002wkr3": "翻译",
|
||||
"k_13g4hxv": "翻译完成",
|
||||
"k_003molk": "表情",
|
||||
"k_165bbw6": "消息历史",
|
||||
"k_13sqc0z": "清除消息",
|
||||
"k_0glns86": "删除会话",
|
||||
"k_13s99rx": "清空消息",
|
||||
"k_11vsa3j": "退出群组",
|
||||
"k_11vvszp": "解散群组",
|
||||
"k_15i9w72": "群管理员",
|
||||
"k_0p3espj": "设置备注名",
|
||||
"k_118sw9v": "立即搜索",
|
||||
"k_0h20hg5": "视频通话",
|
||||
"k_0h22snw": "语音通话",
|
||||
"k_003lz6t": "对方",
|
||||
"k_1xf4yre": "发送给$option1",
|
||||
"k_003por5": "截图",
|
||||
"k_1rw7s82": " 访问相册中视频权限,以正常使用发送视频等功能。",
|
||||
"k_003rcwm": "打开",
|
||||
"k_1698c42": "在访达中打开",
|
||||
"k_066fxsz": "查看文件夹",
|
||||
"k_0k432y2": "无法发送,包含文件夹",
|
||||
"k_002wb4y": "会话",
|
||||
"k_0od4qyh": "视频文件异常",
|
||||
"k_1bfkxg9": "不支持 0KB 文件的传输",
|
||||
"k_0vvsw7g": "文件处理异常",
|
||||
"k_06e224q": "[消息被管理员撤回]",
|
||||
"k_1u1mjcl": "[消息被撤回]",
|
||||
"k_1qcqxea": "选择多个会话",
|
||||
"k_1qgmc20": "选择一个会话",
|
||||
"k_1d8nx6f": "在新窗口中打开",
|
||||
"k_1hz05ax": "正在下载原始资源,请稍候...",
|
||||
"k_002robo": "清除"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import 'package:tencent_cloud_chat_uikit/import_proxy/import_proxy.dart';
|
||||
|
||||
ImportProxy getImportProxy() =>
|
||||
throw UnsupportedError('Cannot create a ImportProxy.');
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'package:tencent_cloud_chat_uikit/import_proxy/general.dart'
|
||||
// ignore: uri_does_not_exist
|
||||
if (dart.library.html) 'platform/web_import.dart'
|
||||
// ignore: uri_does_not_exist
|
||||
if (dart.library.io) 'platform/native_import.dart';
|
||||
|
||||
abstract class ImportProxy {
|
||||
getFlutterPluginRecord();
|
||||
|
||||
factory ImportProxy() => getImportProxy();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'package:flutter_plugin_record_plus/flutter_plugin_record.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/import_proxy/import_proxy.dart';
|
||||
|
||||
class NativeImport implements ImportProxy {
|
||||
@override
|
||||
FlutterPluginRecord? getFlutterPluginRecord() {
|
||||
return FlutterPluginRecord();
|
||||
}
|
||||
}
|
||||
|
||||
ImportProxy getImportProxy() => NativeImport();
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:tencent_cloud_chat_uikit/import_proxy/import_proxy.dart';
|
||||
|
||||
class WebImport implements ImportProxy {
|
||||
@override
|
||||
void getFlutterPluginRecord() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ImportProxy getImportProxy() => WebImport();
|
||||
@@ -0,0 +1,63 @@
|
||||
library tencent_cloud_chat_uikit;
|
||||
|
||||
import 'package:tencent_cloud_chat_sdk/manager/v2_tim_manager.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'data_services/core/core_services_implements.dart';
|
||||
export 'data_services/core/core_services_implements.dart';
|
||||
|
||||
// Sticker
|
||||
export 'package:tim_ui_kit_sticker_plugin/tim_ui_kit_sticker_plugin.dart';
|
||||
|
||||
// Widgets
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitConversation/tim_uikit_conversation.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_chat.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitProfile/tim_uikit_profile.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitProfile/widget/tim_uikit_operation_item.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitContact/tim_uikit_contact.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroup/tim_uikit_group.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitBlackList/tim_uikit_black_list.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitNewContact/tim_uikit_new_contact.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitNewContact/tim_uikit_unread_count.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroupProfile/tim_uikit_group_profile.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/widgets/unread_message.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitAddFriend/tim_uikit_add_friend.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitAddGroup/tim_uikit_add_group.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_more_panel.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitSearch/tim_uikit_search.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitSearch/tim_uikit_search_msg_detail.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_controller.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitGroup/tim_uikit_group_application_list.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/widgets/link_preview/models/link_preview_content.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/widgets/column_menu.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitProfile/widget/tim_uikit_profile_userinfo_card/tim_uikit_profile_userinfo_card.dart';
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitConversation/tim_ui_kit_conversation_total_unread.dart';
|
||||
|
||||
// Enum
|
||||
export 'package:tencent_cloud_chat_uikit/ui/theme/tim_uikit_message_theme.dart';
|
||||
|
||||
// Controller
|
||||
export 'package:tencent_cloud_chat_uikit/ui/controller/tim_uikit_profile_controller.dart';
|
||||
|
||||
// Config
|
||||
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_chat_config.dart';
|
||||
export 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
// Utils
|
||||
export 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
|
||||
export 'package:tencent_cloud_uikit_core/tencent_cloud_uikit_core.dart';
|
||||
|
||||
class TIMUIKitCore {
|
||||
static CoreServicesImpl getInstance() {
|
||||
setupServiceLocator();
|
||||
return serviceLocator<CoreServicesImpl>();
|
||||
}
|
||||
|
||||
static V2TIMManager getSDKInstance() {
|
||||
return TencentImSDKPlugin.v2TIMManager;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
Color hexToColor(String hexString) {
|
||||
return Color(int.parse(hexString, radix: 16)).withAlpha(255);
|
||||
}
|
||||
|
||||
class CommonColor {
|
||||
static const weakBackgroundColor = Color(0xFFEDEDED);
|
||||
static const weakDividerColor = Color(0xFFE5E6E9);
|
||||
static const primaryColor = Color(0xFF147AFF);
|
||||
static const lightPrimaryColor = Color(0xFFC0E1FF);
|
||||
static const secondaryColor = Color(0xFF147AFF);
|
||||
static const weakTextColor = Color(0xFF999999);
|
||||
static const infoColor = Color(0xFFFF9C19);
|
||||
static const cautionColor = Color(0xFFFF584C);
|
||||
static const ownerColor = Colors.orange;
|
||||
static const adminColor = Colors.blue;
|
||||
static const inputFillColor = Color.fromARGB(0, 242, 243, 245);
|
||||
static const textgrey = Color(0xFFAEA4A3);
|
||||
|
||||
static const defaultTheme = TUITheme(
|
||||
weakBackgroundColor: Color(0xFFEDEDED),
|
||||
weakDividerColor: Color(0xFFE5E6E9),
|
||||
primaryColor: Color(0xFF147AFF),
|
||||
secondaryColor: Color(0xFF147AFF),
|
||||
infoColor: Color(0xFFFF9C19),
|
||||
lightPrimaryColor: Color(0xFFC0E1FF),
|
||||
weakTextColor: Color(0xFF999999),
|
||||
darkTextColor: Color(0xFF444444),
|
||||
cautionColor: Color(0xFFFF584C),
|
||||
ownerColor: Colors.orange,
|
||||
adminColor: Colors.blue,
|
||||
inputFillColor: Color.fromARGB(0, 242, 243, 245),
|
||||
textgrey: Color(0xFFAEA4A3),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,597 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TUITheme {
|
||||
const TUITheme({
|
||||
this.primaryColor = const Color(0xFF00449E),
|
||||
this.secondaryColor = const Color(0xFF147AFF),
|
||||
this.infoColor = const Color(0xFFFF9C19),
|
||||
this.weakBackgroundColor = const Color(0xFFEDEDED),
|
||||
this.wideBackgroundColor = Colors.white,
|
||||
this.weakDividerColor = const Color(0xFFE5E6E9),
|
||||
this.weakTextColor = const Color(0xFF999999),
|
||||
this.darkTextColor = const Color(0xFF444444),
|
||||
this.lightPrimaryColor = const Color(0xFF3371CD),
|
||||
this.textColor,
|
||||
this.cautionColor = const Color(0xFFFF584C),
|
||||
this.ownerColor = Colors.orange,
|
||||
this.adminColor = Colors.blue,
|
||||
this.white = Colors.white,
|
||||
this.black = Colors.black,
|
||||
this.inputFillColor = const Color(0xFFEDEDED),
|
||||
this.textgrey = const Color(0xFFAEA4A3),
|
||||
|
||||
/// 消息列表多选面板背景颜色
|
||||
this.selectPanelBgColor = const Color(0xFFF9F9FA),
|
||||
|
||||
/// 消息列表多选面板文字及icon颜色
|
||||
this.selectPanelTextIconColor = const Color(0xFF37393F),
|
||||
|
||||
/// Appbar 背景颜色
|
||||
this.appbarBgColor = const Color(0xFFF2F3F5),
|
||||
|
||||
/// Appbar 文字颜色
|
||||
this.appbarTextColor = const Color(0xFF010000),
|
||||
|
||||
/// 会话列表背景颜色
|
||||
this.conversationItemBgColor = Colors.white, // 1
|
||||
|
||||
/// 会话列表边框颜色
|
||||
this.conversationItemBorderColor = const Color(0xFFE5E6E9), // 1
|
||||
|
||||
/// 会话列表选中背景颜色
|
||||
this.conversationItemActiveBgColor = const Color(0xFFEDEDED), // 1
|
||||
|
||||
/// 会话列表置顶背景颜色
|
||||
this.conversationItemPinedBgColor = const Color(0xFFEDEDED), // 1
|
||||
|
||||
/// 会话列表Title字体颜色
|
||||
this.conversationItemTitleTextColor = Colors.black, // 1
|
||||
|
||||
/// 会话列表LastMessage字体颜色
|
||||
this.conversationItemLastMessageTextColor = const Color(0xFF999999), // 1
|
||||
|
||||
/// 会话列表Time字体颜色
|
||||
this.conversationItemTitmeTextColor = const Color(0xFF999999), // 1
|
||||
|
||||
/// 会话列表用户在线状态背景色
|
||||
this.conversationItemOnlineStatusBgColor = Colors.green, // 1
|
||||
|
||||
/// 会话列表用户离线状态背景色
|
||||
this.conversationItemOfflineStatusBgColor = Colors.grey, // 1
|
||||
|
||||
/// 会话列表未读数背景颜色
|
||||
this.conversationItemUnreadCountBgColor = const Color(0xFFFF584C), // 1
|
||||
|
||||
/// 会话列表未读数字体颜色
|
||||
this.conversationItemUnreadCountTextColor = Colors.white, // 1
|
||||
|
||||
/// 会话列表草稿字体颜色
|
||||
this.conversationItemDraftTextColor = const Color(0xFFFF584C), // 1
|
||||
|
||||
/// 会话列表收到消息不提醒Icon颜色
|
||||
this.conversationItemNoNotificationIconColor = const Color(0xFF999999), // 1
|
||||
|
||||
/// 会话列表侧滑按钮字体颜色
|
||||
this.conversationItemSliderTextColor = Colors.white, // 1
|
||||
|
||||
/// 会话列表侧滑按钮Clear背景颜色
|
||||
this.conversationItemSliderClearBgColor = const Color(0xFF00449E), // 1
|
||||
|
||||
/// 会话列表侧滑按钮Pin背景颜色
|
||||
this.conversationItemSliderPinBgColor = const Color(0xFFFF9C19), // 1
|
||||
|
||||
/// 会话列表侧滑按钮Delete背景颜色
|
||||
this.conversationItemSliderDeleteBgColor = Colors.red, // 1
|
||||
|
||||
/// 会话列表宽屏模式选中时背景颜色
|
||||
this.conversationItemChooseBgColor = const Color(0xFFE7F0FF), // 1
|
||||
|
||||
/// 聊天页背景颜色
|
||||
this.chatBgColor, // 1
|
||||
|
||||
/// 桌面端消息输入框背景颜色
|
||||
this.desktopChatMessageInputBgColor, // 1
|
||||
|
||||
/// 聊天页背景颜色
|
||||
this.chatTimeDividerTextColor = const Color(0xFF999999), // 1
|
||||
|
||||
/// 聊天页导航栏背景颜色
|
||||
this.chatHeaderBgColor = const Color(0xFFF2F3F5),
|
||||
|
||||
/// 聊天页导航栏Title字体颜色
|
||||
this.chatHeaderTitleTextColor = const Color(0xFF010000),
|
||||
|
||||
/// 聊天页导航栏Back字体颜色
|
||||
this.chatHeaderBackTextColor = const Color(0xFF010000),
|
||||
|
||||
/// 聊天页导航栏Action字体颜色
|
||||
this.chatHeaderActionTextColor = const Color(0xFF010000),
|
||||
|
||||
/// 聊天页历史消息列表字体颜色
|
||||
this.chatMessageItemTextColor = Colors.black, // 1
|
||||
|
||||
/// 聊天页历史消息列表来自自己时背景颜色
|
||||
this.chatMessageItemFromSelfBgColor = const Color(0xFFD1E3FF),
|
||||
|
||||
/// 聊天页历史消息列表来自非自己时背景颜色
|
||||
this.chatMessageItemFromOthersBgColor = const Color(0xFFEDEDED), // 1
|
||||
|
||||
/// 聊天页历史消息列表已读状态字体颜色
|
||||
this.chatMessageItemUnreadStatusTextColor = const Color(0xFF999999), // 1
|
||||
|
||||
/// 聊天页历史消息列表小舌头背景颜色
|
||||
this.chatMessageTongueBgColor = const Color(0xFFAEA4A3),
|
||||
|
||||
/// 聊天页历史消息列表小舌头字体颜色
|
||||
this.chatMessageTongueTextColor = const Color(0xFFAEA4A3),
|
||||
});
|
||||
|
||||
// 应用主色
|
||||
// Primary Color For The App
|
||||
final Color? primaryColor;
|
||||
|
||||
// 应用次色
|
||||
// Secondary Color For The App
|
||||
final Color? secondaryColor;
|
||||
|
||||
// 提示颜色,用于次级操作或提示
|
||||
// Info Color, Used For Secondary Action Or Info
|
||||
final Color? infoColor;
|
||||
|
||||
// 浅背景颜色,比主背景颜色浅,用于填充缝隙或阴影
|
||||
// Weak Background Color, Lighter Than Main Background, Used For Marginal Space Or Shadowy Space
|
||||
final Color? weakBackgroundColor;
|
||||
|
||||
// 宽屏幕:浅白背景颜色,比浅背景颜色浅
|
||||
// Weak Background Color, Lighter Than Main Background, Used For Marginal Space Or Shadowy Space
|
||||
final Color? wideBackgroundColor;
|
||||
|
||||
// 浅分割线颜色,用于分割线或边框
|
||||
// Weak Divider Color, Used For Divider Or Border
|
||||
final Color? weakDividerColor;
|
||||
|
||||
// 浅字色
|
||||
// Weak Text Color
|
||||
final Color? weakTextColor;
|
||||
|
||||
// 深字色
|
||||
// Dark Text Color
|
||||
final Color? darkTextColor;
|
||||
|
||||
// 浅主色,用于AppBar或Panels
|
||||
// Light Primary Color, Used For AppBar Or Several Panels
|
||||
final Color? lightPrimaryColor;
|
||||
|
||||
// 字色
|
||||
// TextColor
|
||||
final Color? textColor;
|
||||
|
||||
// 警示色,用于危险操作
|
||||
// Caution Color, Used For Warning Actions
|
||||
final Color? cautionColor;
|
||||
|
||||
// 群主标识色
|
||||
// Group Owner Identification Color
|
||||
final Color? ownerColor;
|
||||
|
||||
// 群管理员标识色
|
||||
// Group Admin Identification Color
|
||||
final Color? adminColor;
|
||||
|
||||
// 白色
|
||||
// white
|
||||
final Color? white;
|
||||
|
||||
// 黑色
|
||||
// black
|
||||
final Color? black;
|
||||
|
||||
// 输入框填充色
|
||||
// input fill color
|
||||
final Color? inputFillColor;
|
||||
|
||||
// 灰色文本
|
||||
// grey text color
|
||||
final Color? textgrey;
|
||||
|
||||
/// 新版本颜色从这里开始
|
||||
///
|
||||
/// Appbar 背景颜色
|
||||
final Color? appbarBgColor;
|
||||
|
||||
/// Appbar 文字颜色
|
||||
final Color? appbarTextColor;
|
||||
|
||||
/// 消息列表多选面板背景颜色
|
||||
final Color? selectPanelBgColor;
|
||||
|
||||
/// 消息列表多选面板文字及icon颜色
|
||||
final Color? selectPanelTextIconColor;
|
||||
|
||||
/// 会话列表背景颜色
|
||||
final Color? conversationItemBgColor;
|
||||
|
||||
/// 会话列表边框颜色
|
||||
final Color? conversationItemBorderColor;
|
||||
|
||||
/// 会话列表选中背景颜色
|
||||
final Color? conversationItemActiveBgColor;
|
||||
|
||||
/// 会话列表置顶背景颜色
|
||||
final Color? conversationItemPinedBgColor;
|
||||
|
||||
/// 会话列表Title字体颜色
|
||||
final Color? conversationItemTitleTextColor;
|
||||
|
||||
/// 会话列表LastMessage字体颜色
|
||||
final Color? conversationItemLastMessageTextColor;
|
||||
|
||||
/// 会话列表Time字体颜色
|
||||
final Color? conversationItemTitmeTextColor;
|
||||
|
||||
/// 会话列表用户在线状态背景色
|
||||
final Color? conversationItemOnlineStatusBgColor;
|
||||
|
||||
/// 会话列表用户离线状态背景色
|
||||
final Color? conversationItemOfflineStatusBgColor;
|
||||
|
||||
/// 会话列表未读数背景颜色
|
||||
final Color? conversationItemUnreadCountBgColor;
|
||||
|
||||
/// 会话列表未读数字体颜色
|
||||
final Color? conversationItemUnreadCountTextColor;
|
||||
|
||||
/// 会话列表草稿字体颜色
|
||||
final Color? conversationItemDraftTextColor;
|
||||
|
||||
/// 会话列表收到消息不提醒Icon颜色
|
||||
final Color? conversationItemNoNotificationIconColor;
|
||||
|
||||
/// 会话列表侧滑按钮字体颜色
|
||||
final Color? conversationItemSliderTextColor;
|
||||
|
||||
/// 会话列表侧滑按钮Clear背景颜色
|
||||
final Color? conversationItemSliderClearBgColor;
|
||||
|
||||
/// 会话列表侧滑按钮Pin背景颜色
|
||||
final Color? conversationItemSliderPinBgColor;
|
||||
|
||||
/// 会话列表侧滑按钮Delete背景颜色
|
||||
final Color? conversationItemSliderDeleteBgColor;
|
||||
|
||||
/// 会话列表宽屏模式选中时背景颜色
|
||||
final Color? conversationItemChooseBgColor;
|
||||
|
||||
/// 聊天页背景颜色
|
||||
final Color? chatBgColor;
|
||||
|
||||
/// 桌面端消息输入框背景颜色
|
||||
final Color? desktopChatMessageInputBgColor;
|
||||
|
||||
/// 聊天页背景颜色
|
||||
final Color? chatTimeDividerTextColor;
|
||||
|
||||
/// 聊天页导航栏背景颜色
|
||||
final Color? chatHeaderBgColor;
|
||||
|
||||
/// 聊天页导航栏Title字体颜色
|
||||
final Color? chatHeaderTitleTextColor;
|
||||
|
||||
/// 聊天页导航栏Back字体颜色
|
||||
final Color? chatHeaderBackTextColor;
|
||||
|
||||
/// 聊天页导航栏Action字体颜色
|
||||
final Color? chatHeaderActionTextColor;
|
||||
|
||||
/// 聊天页历史消息列表字体颜色
|
||||
final Color? chatMessageItemTextColor;
|
||||
|
||||
/// 聊天页历史消息列表来自自己时背景颜色
|
||||
final Color? chatMessageItemFromSelfBgColor;
|
||||
|
||||
/// 聊天页历史消息列表来自非自己时背景颜色
|
||||
final Color? chatMessageItemFromOthersBgColor;
|
||||
|
||||
/// 聊天页历史消息列表已读状态字体颜色
|
||||
final Color? chatMessageItemUnreadStatusTextColor;
|
||||
|
||||
/// 聊天页历史消息列表小舌头背景颜色
|
||||
final Color? chatMessageTongueBgColor;
|
||||
|
||||
/// 聊天页历史消息列表小舌头字体颜色
|
||||
final Color? chatMessageTongueTextColor;
|
||||
|
||||
static const TUITheme light = TUITheme();
|
||||
static const TUITheme dark = TUITheme();
|
||||
|
||||
MaterialColor get primaryMaterialColor => createMaterialColor(primaryColor!);
|
||||
|
||||
MaterialColor get lightPrimaryMaterialColor =>
|
||||
createMaterialColor(lightPrimaryColor!);
|
||||
|
||||
TUITheme.fromJson(Map<String, dynamic> json)
|
||||
: primaryColor = json['primaryColor'] as Color?,
|
||||
secondaryColor = json['secondaryColor'] as Color?,
|
||||
infoColor = json['infoColor'] as Color?,
|
||||
weakBackgroundColor = json['weakBackgroundColor'] as Color?,
|
||||
wideBackgroundColor = json['wideBackgroundColor'] as Color?,
|
||||
weakDividerColor = json['weakDividerColor'] as Color?,
|
||||
weakTextColor = json['weakTextColor'] as Color?,
|
||||
darkTextColor = json['darkTextColor'] as Color?,
|
||||
lightPrimaryColor = json['lightPrimaryColor'] as Color?,
|
||||
textColor = json['textColor'] as Color?,
|
||||
cautionColor = json['cautionColor'] as Color?,
|
||||
ownerColor = json['ownerColor'] as Color?,
|
||||
white = json['white'] as Color?,
|
||||
black = json['black'] as Color?,
|
||||
inputFillColor = json["inputFillColor"] as Color?,
|
||||
textgrey = json['textgrey'] as Color?,
|
||||
adminColor = json['adminColor'] as Color?,
|
||||
selectPanelBgColor = json['selectPanelBgColor'] as Color?,
|
||||
selectPanelTextIconColor = json['selectPanelTextIconColor'] as Color?,
|
||||
appbarBgColor = json['appbarBgColor'] as Color?,
|
||||
appbarTextColor = json['appbarTextColor'] as Color?,
|
||||
|
||||
/// 会话列表背景颜色
|
||||
conversationItemBgColor = json['conversationItemBgColor'] as Color?,
|
||||
|
||||
/// 会话列表边框颜色
|
||||
conversationItemBorderColor =
|
||||
json['conversationItemBorderColor'] as Color?,
|
||||
|
||||
/// 会话列表选中背景颜色
|
||||
conversationItemActiveBgColor =
|
||||
json['conversationItemActiveBgColor'] as Color?,
|
||||
|
||||
/// 会话列表置顶背景颜色
|
||||
conversationItemPinedBgColor =
|
||||
json['conversationItemPinedBgColor'] as Color?,
|
||||
|
||||
/// 会话列表Title字体颜色
|
||||
conversationItemTitleTextColor =
|
||||
json['conversationItemTitleTextColor'] as Color?,
|
||||
|
||||
/// 会话列表LastMessage字体颜色
|
||||
conversationItemLastMessageTextColor =
|
||||
json['conversationItemLastMessageTextColor'] as Color?,
|
||||
|
||||
/// 会话列表Time字体颜色
|
||||
conversationItemTitmeTextColor =
|
||||
json['conversationItemTitmeTextColor'] as Color?,
|
||||
|
||||
/// 会话列表用户在线状态背景色
|
||||
conversationItemOnlineStatusBgColor =
|
||||
json['conversationItemOnlineStatusBgColor'] as Color?,
|
||||
|
||||
/// 会话列表用户离线状态背景色
|
||||
conversationItemOfflineStatusBgColor =
|
||||
json['conversationItemOfflineStatusBgColor'] as Color?,
|
||||
|
||||
/// 会话列表未读数背景颜色
|
||||
conversationItemUnreadCountBgColor =
|
||||
json['conversationItemUnreadCountBgColor'] as Color?,
|
||||
|
||||
/// 会话列表未读数字体颜色
|
||||
conversationItemUnreadCountTextColor =
|
||||
json['conversationItemUnreadCountTextColor'] as Color?,
|
||||
conversationItemChooseBgColor =
|
||||
json['conversationItemChooseBgColor'] as Color?,
|
||||
|
||||
/// 会话列表草稿字体颜色
|
||||
conversationItemDraftTextColor =
|
||||
json['conversationItemDraftTextColor'] as Color?,
|
||||
|
||||
/// 会话列表收到消息不提醒Icon颜色
|
||||
conversationItemNoNotificationIconColor =
|
||||
json['conversationItemNoNotificationIconColor'] as Color?,
|
||||
|
||||
/// 会话列表侧滑按钮字体颜色
|
||||
conversationItemSliderTextColor =
|
||||
json['conversationItemSliderTextColor'] as Color?,
|
||||
|
||||
/// 会话列表侧滑按钮Clear背景颜色
|
||||
conversationItemSliderClearBgColor =
|
||||
json['conversationItemSliderClearBgColor'] as Color?,
|
||||
|
||||
/// 会话列表侧滑按钮Pin背景颜色
|
||||
conversationItemSliderPinBgColor =
|
||||
json['conversationItemSliderPinBgColor'] as Color?,
|
||||
|
||||
/// 会话列表侧滑按钮Delete背景颜色
|
||||
conversationItemSliderDeleteBgColor =
|
||||
json['conversationItemSliderDeleteBgColor'] as Color?,
|
||||
|
||||
/// 聊天页背景颜色
|
||||
chatBgColor = json['chatBgColor'] as Color?,
|
||||
|
||||
/// 桌面端消息输入框背景颜色
|
||||
desktopChatMessageInputBgColor =
|
||||
json['desktopChatMessageInputBgColor'] as Color?,
|
||||
|
||||
/// 聊天页背景颜色
|
||||
chatTimeDividerTextColor = json['chatTimeDividerTextColor'] as Color?,
|
||||
|
||||
/// 聊天页导航栏背景颜色
|
||||
chatHeaderBgColor = json['chatHeaderBgColor'] as Color?,
|
||||
|
||||
/// 聊天页导航栏Title字体颜色
|
||||
chatHeaderTitleTextColor = json['chatHeaderTitleTextColor'] as Color?,
|
||||
|
||||
/// 聊天页导航栏Back字体颜色
|
||||
chatHeaderBackTextColor = json['chatHeaderBackTextColor'] as Color?,
|
||||
|
||||
/// 聊天页导航栏Action字体颜色
|
||||
chatHeaderActionTextColor = json['chatHeaderActionTextColor'] as Color?,
|
||||
|
||||
/// 聊天页历史消息列表字体颜色
|
||||
chatMessageItemTextColor = json['chatMessageItemTextColor'] as Color?,
|
||||
|
||||
/// 聊天页历史消息列表来自自己时背景颜色
|
||||
chatMessageItemFromSelfBgColor =
|
||||
json['chatMessageItemFromSelfBgColor'] as Color?,
|
||||
|
||||
/// 聊天页历史消息列表来自非自己时背景颜色
|
||||
chatMessageItemFromOthersBgColor =
|
||||
json['chatMessageItemFromOthersBgColor'] as Color?,
|
||||
|
||||
/// 聊天页历史消息列表已读状态字体颜色
|
||||
chatMessageItemUnreadStatusTextColor =
|
||||
json['chatMessageItemUnreadStatusTextColor'] as Color?,
|
||||
|
||||
/// 聊天页历史消息列表小舌头背景颜色
|
||||
chatMessageTongueBgColor = json['chatMessageTongueBgColor'] as Color?,
|
||||
|
||||
/// 聊天页历史消息列表小舌头字体颜色
|
||||
chatMessageTongueTextColor =
|
||||
json['chatMessageTongueTextColor'] as Color?;
|
||||
|
||||
toJson() => <String, dynamic>{
|
||||
'primaryColor': primaryColor,
|
||||
'secondaryColor': secondaryColor,
|
||||
'infoColor': infoColor,
|
||||
'weakBackgroundColor': weakBackgroundColor,
|
||||
'wideBackgroundColor': wideBackgroundColor,
|
||||
'weakDividerColor': weakDividerColor,
|
||||
'weakTextColor': weakTextColor,
|
||||
'darkTextColor': darkTextColor,
|
||||
'lightPrimaryColor': lightPrimaryColor,
|
||||
'textColor': textColor,
|
||||
'cautionColor': cautionColor,
|
||||
'ownerColor': ownerColor,
|
||||
'adminColor': adminColor,
|
||||
'white': white,
|
||||
'black': black,
|
||||
'inputFillColor': inputFillColor,
|
||||
'textgrey': textgrey,
|
||||
|
||||
"selectPanelBgColor": selectPanelBgColor,
|
||||
|
||||
"selectPanelTextIconColor": selectPanelTextIconColor,
|
||||
|
||||
"appbarBgColor": appbarBgColor,
|
||||
|
||||
"appbarTextColor": appbarTextColor,
|
||||
|
||||
/// 会话列表背景颜色
|
||||
"conversationItemBgColor": conversationItemBgColor,
|
||||
|
||||
/// 会话列表边框颜色
|
||||
"conversationItemBorderColor": conversationItemBorderColor,
|
||||
|
||||
/// 会话列表选中背景颜色
|
||||
"conversationItemActiveBgColor": conversationItemActiveBgColor,
|
||||
|
||||
/// 会话列表置顶背景颜色
|
||||
"conversationItemPinedBgColor": conversationItemPinedBgColor,
|
||||
|
||||
/// 会话列表Title字体颜色
|
||||
"conversationItemTitleTextColor": conversationItemTitleTextColor,
|
||||
|
||||
/// 会话列表LastMessage字体颜色
|
||||
"conversationItemLastMessageTextColor":
|
||||
conversationItemLastMessageTextColor,
|
||||
|
||||
/// 会话列表Time字体颜色
|
||||
"conversationItemTitmeTextColor": conversationItemTitmeTextColor,
|
||||
|
||||
/// 会话列表用户在线状态背景色
|
||||
"conversationItemOnlineStatusBgColor":
|
||||
conversationItemOnlineStatusBgColor,
|
||||
|
||||
/// 会话列表用户离线状态背景色
|
||||
"conversationItemOfflineStatusBgColor":
|
||||
conversationItemOfflineStatusBgColor,
|
||||
|
||||
/// 会话列表未读数背景颜色
|
||||
"conversationItemUnreadCountBgColor":
|
||||
conversationItemUnreadCountBgColor,
|
||||
|
||||
/// 会话列表未读数字体颜色
|
||||
"conversationItemUnreadCountTextColor":
|
||||
conversationItemUnreadCountTextColor,
|
||||
|
||||
/// 会话列表草稿字体颜色
|
||||
"conversationItemDraftTextColor": conversationItemDraftTextColor,
|
||||
|
||||
/// 会话列表收到消息不提醒Icon颜色
|
||||
"conversationItemNoNotificationIconColor":
|
||||
conversationItemNoNotificationIconColor,
|
||||
|
||||
/// 会话列表侧滑按钮字体颜色
|
||||
"conversationItemSliderTextColor": conversationItemSliderTextColor,
|
||||
|
||||
/// 会话列表侧滑按钮Clear背景颜色
|
||||
"conversationItemSliderClearBgColor":
|
||||
conversationItemSliderClearBgColor,
|
||||
|
||||
/// 会话列表侧滑按钮Pin背景颜色
|
||||
"conversationItemSliderPinBgColor": conversationItemSliderPinBgColor,
|
||||
|
||||
/// 会话列表侧滑按钮Delete背景颜色
|
||||
"conversationItemSliderDeleteBgColor":
|
||||
conversationItemSliderDeleteBgColor,
|
||||
|
||||
/// 会话列表侧滑按钮Delete背景颜色
|
||||
"conversationItemChooseBgColor": conversationItemChooseBgColor,
|
||||
|
||||
/// 聊天页背景颜色
|
||||
"chatBgColor": chatBgColor,
|
||||
|
||||
/// 桌面端消息输入框背景颜色
|
||||
"desktopChatMessageInputBgColor": desktopChatMessageInputBgColor,
|
||||
|
||||
/// 聊天页背景颜色
|
||||
"chatTimeDividerTextColor": chatTimeDividerTextColor,
|
||||
|
||||
/// 聊天页导航栏背景颜色
|
||||
"chatHeaderBgColor": chatHeaderBgColor,
|
||||
|
||||
/// 聊天页导航栏Title字体颜色
|
||||
"chatHeaderTitleTextColor": chatHeaderTitleTextColor,
|
||||
|
||||
/// 聊天页导航栏Back字体颜色
|
||||
"chatHeaderBackTextColor": chatHeaderBackTextColor,
|
||||
|
||||
/// 聊天页导航栏Action字体颜色
|
||||
"chatHeaderActionTextColor": chatHeaderActionTextColor,
|
||||
|
||||
/// 聊天页历史消息列表字体颜色
|
||||
"chatMessageItemTextColor": chatMessageItemTextColor,
|
||||
|
||||
/// 聊天页历史消息列表来自自己时背景颜色
|
||||
"chatMessageItemFromSelfBgColor": chatMessageItemFromSelfBgColor,
|
||||
|
||||
/// 聊天页历史消息列表来自非自己时背景颜色
|
||||
"chatMessageItemFromOthersBgColor": chatMessageItemFromOthersBgColor,
|
||||
|
||||
/// 聊天页历史消息列表已读状态字体颜色
|
||||
"chatMessageItemUnreadStatusTextColor":
|
||||
chatMessageItemUnreadStatusTextColor,
|
||||
|
||||
/// 聊天页历史消息列表小舌头背景颜色
|
||||
"chatMessageTongueBgColor": chatMessageTongueBgColor,
|
||||
|
||||
/// 聊天页历史消息列表小舌头字体颜色
|
||||
"chatMessageTongueTextColor": chatMessageTongueTextColor,
|
||||
};
|
||||
|
||||
MaterialColor createMaterialColor(Color color) {
|
||||
List strengths = <double>[.05];
|
||||
Map<int, Color> swatch = <int, Color>{};
|
||||
final int r = color.red, g = color.green, b = color.blue;
|
||||
|
||||
for (int i = 1; i < 10; i++) {
|
||||
strengths.add(0.1 * i);
|
||||
}
|
||||
for (var strength in strengths) {
|
||||
final double ds = 0.5 - strength;
|
||||
swatch[(strength * 1000).round()] = Color.fromRGBO(
|
||||
r + ((ds < 0 ? r : (255 - r)) * ds).round(),
|
||||
g + ((ds < 0 ? g : (255 - g)) * ds).round(),
|
||||
b + ((ds < 0 ? b : (255 - b)) * ds).round(),
|
||||
1,
|
||||
);
|
||||
}
|
||||
return MaterialColor(color.value, swatch);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/color.dart';
|
||||
|
||||
class TUIThemeViewModel extends ChangeNotifier {
|
||||
TUITheme _theme = CommonColor.defaultTheme;
|
||||
|
||||
TUITheme get theme {
|
||||
return _theme;
|
||||
}
|
||||
|
||||
set theme(TUITheme theme) {
|
||||
_theme = theme;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
enum V2TimImageTypesEnum {
|
||||
original,
|
||||
big,
|
||||
small,
|
||||
}
|
||||
|
||||
class HistoryMessageDartConstant {
|
||||
static const getCount = 20;
|
||||
|
||||
// ignore: constant_identifier_names
|
||||
static const V2_TIM_IMAGE_TYPES = {
|
||||
'ORIGINAL': 0,
|
||||
'BIG': 1,
|
||||
'SMALL': 2,
|
||||
};
|
||||
|
||||
static Map<V2TimImageTypesEnum, List<String>> imgPriorMap = {
|
||||
V2TimImageTypesEnum.original: oriImgPrior,
|
||||
V2TimImageTypesEnum.big: bigImgPrior,
|
||||
V2TimImageTypesEnum.small: smallImgPrior,
|
||||
};
|
||||
|
||||
// 缩略图优先,大图次之,最后是原图
|
||||
static const smallImgPrior = ['ORIGINAL', 'BIG', 'SMALL'];
|
||||
// 大图优先,原图次之,最后是缩略图
|
||||
static const bigImgPrior = ['SMALL', 'ORIGINAL', 'BIG'];
|
||||
// 原图优先,大图次之,最后是缩略图
|
||||
static const oriImgPrior = ['SMALL', 'BIG', 'ORIGINAL'];
|
||||
|
||||
// 视频、音频已读状态
|
||||
static const int read = 1;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
class TimeConst {
|
||||
static const DAY_SEC = 86400;
|
||||
static const HOUR_SEC = 3600;
|
||||
static const MIN_SEC = 60;
|
||||
|
||||
static const SEC_SERIES = [HOUR_SEC, MIN_SEC];
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
// ignore_for_file: avoid_print, empty_catches
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/history_msg_get_type_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/message_priority_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/offlinePushInfo.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_member_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class TIMUIKitChatController {
|
||||
late TUIChatSeparateViewModel? model;
|
||||
late TIMUIKitInputTextFieldController? textFieldController;
|
||||
late AutoScrollController? scrollController;
|
||||
final TUIChatGlobalModel globalChatModel = serviceLocator<TUIChatGlobalModel>();
|
||||
|
||||
TIMUIKitChatController({TUIChatSeparateViewModel? viewModel}) {
|
||||
if (viewModel != null) {
|
||||
model = viewModel;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> loadHistoryMessageList(
|
||||
{HistoryMsgGetTypeEnum getType = HistoryMsgGetTypeEnum.V2TIM_GET_CLOUD_OLDER_MSG,
|
||||
String? userID,
|
||||
String? groupID,
|
||||
int lastMsgSeq = -1,
|
||||
required int count,
|
||||
String? lastMsgID,
|
||||
LoadDirection direction = LoadDirection.previous}) async {
|
||||
return await model?.loadChatRecord(
|
||||
count: count,
|
||||
getType: getType,
|
||||
lastMsgID: lastMsgID,
|
||||
lastMsgSeq: lastMsgSeq,
|
||||
) ??
|
||||
false;
|
||||
}
|
||||
|
||||
/// Clear the current conversation;
|
||||
@Deprecated("No need to dispose after tencent_cloud_chat_uikit 0.1.4")
|
||||
dispose() {}
|
||||
|
||||
/// Clear the history of current conversation;
|
||||
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
||||
clearHistory([String? convID]) {
|
||||
if (convID != null) {
|
||||
return globalChatModel.setMessageList(convID, []);
|
||||
}
|
||||
return model?.clearHistory();
|
||||
}
|
||||
|
||||
/// Refresh the history message list manually;
|
||||
Future<bool> refreshCurrentHistoryList([String? convID, ConvType? convType]) async {
|
||||
if (model != null) {
|
||||
try {
|
||||
scrollController?.animateTo(
|
||||
scrollController!.position.minScrollExtent,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
} catch (e) {}
|
||||
return model!.loadDataFromController();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Update single message at UI model
|
||||
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
||||
Future<void> updateMessage(
|
||||
{
|
||||
/// The ID of the target conversation
|
||||
String? convID,
|
||||
|
||||
/// The type of the target conversation
|
||||
ConvType? convType,
|
||||
|
||||
/// message ID
|
||||
required String msgID}) async {
|
||||
if (convID != null && convType != null) {
|
||||
return globalChatModel.updateMessageFromController(
|
||||
msgID: msgID, conversationID: convID, conversationType: convType);
|
||||
} else if (model != null) {
|
||||
return model!.updateMessageFromController(msgID: msgID);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/// Sends a message to the specified conversation, or to the current conversation specified on `TIMUIKitChat`.
|
||||
/// You must provide `convType` and either `userID` or `groupID`, only if you use `TIMUIKitChat` without specifying a `TIMUIKitChatController`, you must provide these parameters.
|
||||
Future<V2TimValueCallback<V2TimMessage>?>? sendMessage({
|
||||
required V2TimMessage? messageInfo,
|
||||
|
||||
/// The type of the target conversation: either ConvType.group or ConvType.c2c. Required if using `TIMUIKitChat` without specifying a `TIMUIKitChatController`.
|
||||
ConvType? convType,
|
||||
|
||||
/// The user ID of the target one-to-one conversation. Required if convType is ConvType.c2c.
|
||||
String? userID,
|
||||
|
||||
/// The target group ID. Required if convType is ConvType.group.
|
||||
String? groupID,
|
||||
|
||||
/// A callback function to update the input field when message sending fails.
|
||||
ValueChanged<String>? setInputField,
|
||||
|
||||
/// Offline push information.
|
||||
OfflinePushInfo? offlinePushInfo,
|
||||
|
||||
/// Whether automatically scrolling to the bottom of the message list after sending a message.
|
||||
/// This field solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||
bool isNavigateToMessageListBottom = true,
|
||||
|
||||
/// Message priorities. This field is valid only for group chat messages.
|
||||
/// You are advised to set higher priorities for important messages (such as red packet and gift messages)
|
||||
/// and set lower priorities for frequent but unimportant messages (such as like messages).
|
||||
MessagePriorityEnum priority = MessagePriorityEnum.V2TIM_PRIORITY_NORMAL,
|
||||
|
||||
/// Whether the message can be received only by online users.
|
||||
/// If this field is set to true, the message cannot be pulled in recipient historical message pulling.
|
||||
/// This field is often used to implement weak notification features such as "The other party is typing" or unimportant notifications in the group. This field is not supported by audio-video groups (AVChatRoom).
|
||||
bool? onlineUserOnly,
|
||||
|
||||
/// Whether the message is excluded from the conversation unread message count.
|
||||
bool? isExcludedFromUnreadCount,
|
||||
|
||||
/// Whether a read receipt is required.
|
||||
bool? needReadReceipt,
|
||||
|
||||
/// Cloud custom data (saved in the cloud, will be sent to the peer end,
|
||||
/// and can still be pulled after the app is uninstalled and reinstalled)
|
||||
String? cloudCustomData,
|
||||
|
||||
/// Local custom message data (saved locally, will not be sent to the peer end,
|
||||
/// and will become invalid after the app is uninstalled and reinstalled).
|
||||
String? localCustomData,
|
||||
}) {
|
||||
if (convType != null) {
|
||||
/// Sends a message to the specified conversation.
|
||||
assert((groupID == null) != (userID == null));
|
||||
assert(groupID != null || convType != ConvType.group);
|
||||
assert(userID != null || convType != ConvType.c2c);
|
||||
if (isNavigateToMessageListBottom && scrollController != null) {
|
||||
try {
|
||||
scrollController?.animateTo(
|
||||
scrollController!.position.minScrollExtent,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
} catch (e) {}
|
||||
}
|
||||
return globalChatModel.sendMessageFromController(
|
||||
priority: priority,
|
||||
onlineUserOnly: onlineUserOnly,
|
||||
isExcludedFromUnreadCount: isExcludedFromUnreadCount,
|
||||
needReadReceipt: needReadReceipt,
|
||||
cloudCustomData: cloudCustomData,
|
||||
localCustomData: localCustomData,
|
||||
messageInfo: messageInfo,
|
||||
convType: convType,
|
||||
convID: (convType == ConvType.group ? groupID : userID) ?? "",
|
||||
setInputField: setInputField,
|
||||
offlinePushInfo: offlinePushInfo);
|
||||
} else if (model != null) {
|
||||
/// Sends a message to the current conversation specified on `TIMUIKitChat`. 发送到 `TIMUIKitChat` 中指定的当前对话。
|
||||
if (isNavigateToMessageListBottom && scrollController != null) {
|
||||
try {
|
||||
scrollController?.animateTo(
|
||||
scrollController!.position.minScrollExtent,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
} catch (e) {}
|
||||
}
|
||||
return model!.sendMessageFromController(
|
||||
priority: priority,
|
||||
onlineUserOnly: onlineUserOnly,
|
||||
isExcludedFromUnreadCount: isExcludedFromUnreadCount,
|
||||
needReadReceipt: needReadReceipt,
|
||||
cloudCustomData: cloudCustomData,
|
||||
localCustomData: localCustomData,
|
||||
messageInfo: messageInfo,
|
||||
offlinePushInfo: offlinePushInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Sends a message, replying to another message, to the specified conversation, or to the current conversation specified on `TIMUIKitChat`.
|
||||
/// You must provide `convType` and either `userID` or `groupID`, only if you use `TIMUIKitChat` without specifying a `TIMUIKitChatController`, you must provide these parameters.
|
||||
Future<V2TimValueCallback<V2TimMessage>?>? sendReplyMessage({
|
||||
required String messageText,
|
||||
required V2TimMessage messageBeenReplied,
|
||||
|
||||
/// The type of the target conversation: either ConvType.group or ConvType.c2c. Required if using `TIMUIKitChat` without specifying a `TIMUIKitChatController`.
|
||||
ConvType? convType,
|
||||
|
||||
/// The user ID of the target one-to-one conversation. Required if convType is ConvType.c2c.
|
||||
String? userID,
|
||||
|
||||
/// The target group ID. Required if convType is ConvType.group.
|
||||
String? groupID,
|
||||
|
||||
/// A callback function to update the input field when message sending fails.
|
||||
ValueChanged<String>? setInputField,
|
||||
|
||||
/// Offline push information.
|
||||
OfflinePushInfo? offlinePushInfo,
|
||||
|
||||
/// Whether automatically scrolling to the bottom of the message list after sending a message.
|
||||
/// This field solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||
bool isNavigateToMessageListBottom = true,
|
||||
|
||||
/// Message priorities. This field is valid only for group chat messages.
|
||||
/// You are advised to set higher priorities for important messages (such as red packet and gift messages)
|
||||
/// and set lower priorities for frequent but unimportant messages (such as like messages).
|
||||
MessagePriorityEnum priority = MessagePriorityEnum.V2TIM_PRIORITY_NORMAL,
|
||||
|
||||
/// Whether the message can be received only by online users.
|
||||
/// If this field is set to true, the message cannot be pulled in recipient historical message pulling.
|
||||
/// This field is often used to implement weak notification features such as "The other party is typing" or unimportant notifications in the group. This field is not supported by audio-video groups (AVChatRoom).
|
||||
bool? onlineUserOnly,
|
||||
|
||||
/// Whether the message is excluded from the conversation unread message count.
|
||||
bool? isExcludedFromUnreadCount,
|
||||
|
||||
/// Whether a read receipt is required.
|
||||
bool? needReadReceipt,
|
||||
|
||||
/// Local custom message data (saved locally, will not be sent to the peer end,
|
||||
/// and will become invalid after the app is uninstalled and reinstalled).
|
||||
String? localCustomData,
|
||||
}) {
|
||||
if (convType != null) {
|
||||
/// Sends a message to the specified conversation.
|
||||
assert((groupID == null) != (userID == null));
|
||||
assert(groupID != null || convType != ConvType.group);
|
||||
assert(userID != null || convType != ConvType.c2c);
|
||||
if (isNavigateToMessageListBottom && scrollController != null) {
|
||||
try {
|
||||
scrollController?.animateTo(
|
||||
scrollController!.position.minScrollExtent,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
} catch (e) {}
|
||||
}
|
||||
return globalChatModel.sendReplyMessageFromController(
|
||||
text: messageText,
|
||||
messageBeenReplied: messageBeenReplied,
|
||||
priority: priority,
|
||||
onlineUserOnly: onlineUserOnly,
|
||||
isExcludedFromUnreadCount: isExcludedFromUnreadCount,
|
||||
needReadReceipt: needReadReceipt,
|
||||
localCustomData: localCustomData,
|
||||
convType: convType,
|
||||
convID: (convType == ConvType.group ? groupID : userID) ?? "",
|
||||
setInputField: setInputField,
|
||||
offlinePushInfo: offlinePushInfo);
|
||||
} else if (model != null) {
|
||||
/// Sends a message to the current conversation specified on `TIMUIKitChat`. 发送到 `TIMUIKitChat` 中指定的当前对话。
|
||||
if (isNavigateToMessageListBottom && scrollController != null) {
|
||||
scrollController?.animateTo(
|
||||
scrollController!.position.minScrollExtent,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
}
|
||||
return globalChatModel.sendReplyMessageFromController(
|
||||
text: messageText,
|
||||
messageBeenReplied: messageBeenReplied,
|
||||
priority: priority,
|
||||
onlineUserOnly: onlineUserOnly,
|
||||
isExcludedFromUnreadCount: isExcludedFromUnreadCount,
|
||||
needReadReceipt: needReadReceipt,
|
||||
localCustomData: localCustomData,
|
||||
convType: model!.conversationType ?? ConvType.group,
|
||||
convID: model!.conversationID,
|
||||
setInputField: setInputField,
|
||||
offlinePushInfo: offlinePushInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Send forward message;
|
||||
/// This function solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||
sendForwardMessage({
|
||||
required List<V2TimConversation> conversationList,
|
||||
}) async {
|
||||
model?.sendForwardMessage(conversationList: conversationList);
|
||||
}
|
||||
|
||||
/// Send merger message;
|
||||
/// This method needs use with TIMUIKitChat directly or model been initialized.
|
||||
Future<V2TimValueCallback<V2TimMessage>?> sendMergerMessage({
|
||||
required List<V2TimConversation> conversationList,
|
||||
required String title,
|
||||
required List<String> abstractList,
|
||||
required BuildContext context,
|
||||
}) async {
|
||||
return model?.sendMergerMessage(
|
||||
conversationList: conversationList, title: title, abstractList: abstractList, context: context);
|
||||
}
|
||||
|
||||
/// Set local custom data; returns the bool shows if succeed.
|
||||
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
||||
Future<bool> setLocalCustomData(String msgID, String localCustomData, [String? convID]) async {
|
||||
final String? conversationID = convID ?? model?.conversationID;
|
||||
if (conversationID == null) {
|
||||
return false;
|
||||
}
|
||||
return globalChatModel.setLocalCustomData(msgID, localCustomData, conversationID);
|
||||
}
|
||||
|
||||
/// Set local custom int; returns the bool shows if succeed.
|
||||
/// Please provide `convID`, if you use `TIMUIKitChatController` without specifying to a `TIMUIKitChat`.
|
||||
Future<bool> setLocalCustomInt(String msgID, int localCustomInt, [String? convID]) async {
|
||||
final String? conversationID = convID ?? model?.conversationID;
|
||||
if (conversationID == null) {
|
||||
return false;
|
||||
}
|
||||
return globalChatModel.setLocalCustomInt(msgID, localCustomInt, conversationID);
|
||||
}
|
||||
|
||||
/// Get current conversation, returns UserID or GroupID if in the chat page, returns "" if not.
|
||||
String getCurrentConversation() {
|
||||
return globalChatModel.currentSelectedConv;
|
||||
}
|
||||
|
||||
/// Hide all bottom panels, including the sticker panel and the additional functions panel, on mobile devices.
|
||||
/// This function solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||
void hideAllBottomPanelOnMobile() {
|
||||
textFieldController?.hideAllPanel();
|
||||
}
|
||||
|
||||
/// Mention or @ other members in a group manually.
|
||||
/// This function solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||
void mentionOtherMemberInGroup({required String showNameInMessage, required String userID}) {
|
||||
textFieldController?.longPressToAt(showNameInMessage, userID);
|
||||
}
|
||||
|
||||
/// Set the content within the message input text field.
|
||||
/// This function solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||
void setInputTextField(String text) {
|
||||
textFieldController?.setTextField(text);
|
||||
}
|
||||
|
||||
/// Returns the list of group members of current group chat based on the provided keyword.
|
||||
///
|
||||
/// This method filters the group members based on the given keyword. If the keyword is not provided,
|
||||
/// it returns the entire list of group members. The filtering is performed by checking if the keyword
|
||||
/// is contained within the userID, nickName, or friendRemark properties of each group member.
|
||||
///
|
||||
/// [keyword] (optional) - The keyword to filter the group members. If not provided, the entire list of group members is returned.
|
||||
/// This function solely works when `TIMUIKitChatController` is specified for use within a `TIMUIKitChat`.
|
||||
List<V2TimGroupMemberFullInfo> getGroupMemberList({String? keyword}) {
|
||||
final List<V2TimGroupMemberFullInfo> memberList =
|
||||
(model?.groupMemberList ?? []).whereType<V2TimGroupMemberFullInfo>().toList();
|
||||
|
||||
return TencentUtils.checkString(keyword) == null
|
||||
? memberList
|
||||
: memberList.where((e) {
|
||||
final userID = e.userID;
|
||||
final nickName = e.nickName ?? "";
|
||||
final friendRemark = e.friendRemark ?? "";
|
||||
return userID.contains(keyword!) || nickName.contains(keyword) || friendRemark.contains(keyword);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.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_uikit/business_logic/view_models/tui_conversation_view_model.dart';
|
||||
|
||||
class TIMUIKitConversationController {
|
||||
late TUIConversationViewModel model;
|
||||
|
||||
/// Get the selected conversation currently
|
||||
V2TimConversation? get selectedConversation {
|
||||
return model.selectedConversation;
|
||||
}
|
||||
|
||||
/// Set the selected conversation currently
|
||||
set selectedConversation(V2TimConversation? conversation) {
|
||||
model.selectedConversation = conversation;
|
||||
}
|
||||
|
||||
/// Get the conversation list
|
||||
List<V2TimConversation?> get conversationList {
|
||||
return model.conversationList;
|
||||
}
|
||||
|
||||
/// Set the conversation list
|
||||
set conversationList(List<V2TimConversation?> conversationList) {
|
||||
model.conversationList = conversationList;
|
||||
}
|
||||
|
||||
/// Load the conversation list to UI
|
||||
loadData({int count = 40}) {
|
||||
model.loadData(count: count);
|
||||
}
|
||||
|
||||
/// Reload the conversation list to UI
|
||||
reloadData({int count = 100}) {
|
||||
model.refresh(count: count);
|
||||
}
|
||||
|
||||
/// Pin one conversation to the top
|
||||
Future<V2TimCallback> pinConversation({required String conversationID, required bool isPinned}) {
|
||||
return model.pinConversation(conversationID: conversationID, isPinned: isPinned);
|
||||
}
|
||||
|
||||
/// Set the draft for a conversation
|
||||
Future<V2TimCallback> setConversationDraft({required String conversationID, String? draftText}) {
|
||||
return model.setConversationDraft(conversationID: conversationID, draftText: draftText);
|
||||
}
|
||||
|
||||
/// Clear the historical message in a specific conversation
|
||||
Future<V2TimCallback?>? clearHistoryMessage({required V2TimConversation conversation}) {
|
||||
final convType = conversation.type;
|
||||
final convID = convType == 1 ? conversation.userID : conversation.groupID;
|
||||
if (convType != null && convID != null) {
|
||||
return model.clearHistoryMessage(convID: convID, convType: convType);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Delete a conversation
|
||||
Future<V2TimCallback?> deleteConversation({required String conversationID}) {
|
||||
return model.deleteConversation(conversationID: conversationID);
|
||||
}
|
||||
|
||||
/// Clear the conversation list from UI
|
||||
dispose() {
|
||||
model.clearData();
|
||||
}
|
||||
|
||||
/// Scroll to a specific conversation, this conversation must be existed in conversation list.
|
||||
/// If not exist, invoking `loadData` recursively, until find the target conversation.
|
||||
scrollToConversation(String conversationID) {
|
||||
model.scrollToConversation = conversationID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_operation_result.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_operation_result.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_profile_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/text_input_bottom_sheet.dart';
|
||||
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
class TIMUIKitProfileController {
|
||||
late TUIProfileViewModel model;
|
||||
|
||||
/// Remove a user from friend or contact
|
||||
Future<V2TimFriendOperationResult?> deleteFriend(String userID) {
|
||||
return model.deleteFriend(userID);
|
||||
}
|
||||
|
||||
/// Deprecated: Please use [pinConversation] instead. Pin the conversation to the top
|
||||
@Deprecated("Please use [pinConversation] instead")
|
||||
Future<V2TimCallback> pinedConversation(bool isPined, String convID) {
|
||||
return model.pinedConversation(isPined, convID);
|
||||
}
|
||||
|
||||
/// pin the conversation to the top
|
||||
Future<V2TimCallback> pinConversation(bool isPined, String convID) {
|
||||
return model.pinedConversation(isPined, convID);
|
||||
}
|
||||
|
||||
/// add a user to block list
|
||||
Future<List<V2TimFriendOperationResult>?> addUserToBlackList(bool shouldAdd, String userID) {
|
||||
return model.addToBlackList(shouldAdd, userID);
|
||||
}
|
||||
|
||||
/// Change the friend adding request verification method,
|
||||
/// 0 represents "Accept all friend request",
|
||||
/// 1 represents "Require approval for friend requests",
|
||||
/// 2 represents "reject all friend requests".
|
||||
Future<V2TimCallback> changeFriendVerificationMethod(int allowType) {
|
||||
return model.changeFriendVerificationMethod(allowType);
|
||||
}
|
||||
|
||||
/// update the remarks for other users,
|
||||
Future<V2TimCallback> updateRemarks(String userID, String remark) {
|
||||
return model.updateRemarks(userID, remark);
|
||||
}
|
||||
|
||||
/// set the message from a specific user as not disturb, mute notification
|
||||
Future<V2TimCallback> setMessageDisturb(String userID, bool isDisturb) {
|
||||
return model.setMessageDisturb(userID, isDisturb);
|
||||
}
|
||||
|
||||
/// Show the text input bottom sheet
|
||||
showTextInputBottomSheet({
|
||||
required BuildContext context,
|
||||
required String title,
|
||||
required String tips,
|
||||
required Function(String text) onSubmitted,
|
||||
required TUITheme theme,
|
||||
Offset? initOffset,
|
||||
String? initText,
|
||||
}) {
|
||||
TextInputBottomSheet.showTextInputBottomSheet(
|
||||
context: context,
|
||||
title: title,
|
||||
tips: tips,
|
||||
onSubmitted: onSubmitted,
|
||||
theme: theme,
|
||||
initOffset: initOffset,
|
||||
initText: initText);
|
||||
}
|
||||
|
||||
/// Load the profile data
|
||||
loadData(String userID) {
|
||||
model.loadData(userID: userID);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
model.dispose();
|
||||
}
|
||||
|
||||
/// Add a user as friend or contact
|
||||
Future<V2TimFriendOperationResult?> addFriend(String userID) {
|
||||
return model.addFriend(userID);
|
||||
}
|
||||
|
||||
Future<V2TimCallback> updateSelfSignature(String selfSignature) {
|
||||
V2TimUserFullInfo userFullInfo = V2TimUserFullInfo();
|
||||
userFullInfo.selfSignature = selfSignature;
|
||||
return model.updateSelfInfo(userFullInfo);
|
||||
}
|
||||
|
||||
Future<V2TimCallback> updateNickName(String nickName) {
|
||||
V2TimUserFullInfo userFullInfo = V2TimUserFullInfo();
|
||||
userFullInfo.nickName = nickName;
|
||||
return model.updateSelfInfo(userFullInfo);
|
||||
}
|
||||
|
||||
/// 1:男 2:女
|
||||
Future<V2TimCallback> updateGender(int gender) {
|
||||
V2TimUserFullInfo userFullInfo = V2TimUserFullInfo();
|
||||
userFullInfo.gender = gender;
|
||||
return model.updateSelfInfo(userFullInfo);
|
||||
}
|
||||
|
||||
Future<V2TimCallback> updateBirthday(int birthday) {
|
||||
V2TimUserFullInfo userFullInfo = V2TimUserFullInfo();
|
||||
userFullInfo.birthday = birthday;
|
||||
return model.updateSelfInfo(userFullInfo);
|
||||
}
|
||||
|
||||
Future<V2TimCallback> updateAvatar(String url) {
|
||||
V2TimUserFullInfo userFullInfo = V2TimUserFullInfo();
|
||||
userFullInfo.faceUrl = url;
|
||||
return model.updateSelfInfo(userFullInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MessageThemeData {
|
||||
/// Text style for message text
|
||||
final TextStyle? messageTextStyle;
|
||||
|
||||
/// Text style for user nick name
|
||||
final TextStyle? nickNameTextStyle;
|
||||
|
||||
/// Text style for timeline
|
||||
final TextStyle? timelineTextStyle;
|
||||
|
||||
/// Color for messageBackgroundColor
|
||||
final Color? messageBackgroundColor;
|
||||
|
||||
/// border radius for text message
|
||||
final BorderRadius? messageBorderRadius;
|
||||
|
||||
final BorderRadius? avatarBorderRadius;
|
||||
|
||||
MessageThemeData({
|
||||
this.messageTextStyle,
|
||||
this.messageBackgroundColor,
|
||||
this.messageBorderRadius,
|
||||
this.nickNameTextStyle,
|
||||
this.timelineTextStyle,
|
||||
this.avatarBorderRadius,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
class TencentUtils{
|
||||
static bool isTextNotEmpty(String? text){
|
||||
return text != null && text.isNotEmpty;
|
||||
}
|
||||
|
||||
static String? checkString(String? text){
|
||||
return (text != null && text.isEmpty) ? null : text;
|
||||
}
|
||||
|
||||
static String? checkStringWithoutSpace(String? text){
|
||||
if (text == null || text.trim().isEmpty || text.contains(' ')) {
|
||||
return null;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
static String getFileType(String fileType) {
|
||||
switch (fileType) {
|
||||
case "3gp":
|
||||
return "video/3gpp";
|
||||
case "torrent":
|
||||
return "application/x-bittorrent";
|
||||
case "kml":
|
||||
return "application/vnd.google-earth.kml+xml";
|
||||
case "gpx":
|
||||
return "application/gpx+xml";
|
||||
case "asf":
|
||||
return "video/x-ms-asf";
|
||||
case "avi":
|
||||
return "video/x-msvideo";
|
||||
case "bin":
|
||||
case "class":
|
||||
case "exe":
|
||||
return "application/octet-stream";
|
||||
case "bmp":
|
||||
return "image/bmp";
|
||||
case "c":
|
||||
return "text/plain";
|
||||
case "conf":
|
||||
return "text/plain";
|
||||
case "cpp":
|
||||
return "text/plain";
|
||||
case "doc":
|
||||
return "application/msword";
|
||||
case "docx":
|
||||
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||||
case "xls":
|
||||
case "csv":
|
||||
return "application/vnd.ms-excel";
|
||||
case "xlsx":
|
||||
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
case "gif":
|
||||
return "image/gif";
|
||||
case "gtar":
|
||||
return "application/x-gtar";
|
||||
case "gz":
|
||||
return "application/x-gzip";
|
||||
case "h":
|
||||
return "text/plain";
|
||||
case "htm":
|
||||
return "text/html";
|
||||
case "html":
|
||||
return "text/html";
|
||||
case "jar":
|
||||
return "application/java-archive";
|
||||
case "java":
|
||||
return "text/plain";
|
||||
case "jpeg":
|
||||
return "image/jpeg";
|
||||
case "jpg":
|
||||
return "image/jpeg";
|
||||
case "js":
|
||||
return "application/x-javascript";
|
||||
case "log":
|
||||
return "text/plain";
|
||||
case "m3u":
|
||||
return "audio/x-mpegurl";
|
||||
case "m4a":
|
||||
return "audio/mp4a-latm";
|
||||
case "m4b":
|
||||
return "audio/mp4a-latm";
|
||||
case "m4p":
|
||||
return "audio/mp4a-latm";
|
||||
case "m4u":
|
||||
return "video/vnd.mpegurl";
|
||||
case "m4v":
|
||||
return "video/x-m4v";
|
||||
case "mov":
|
||||
return "video/quicktime";
|
||||
case "mp2":
|
||||
return "audio/x-mpeg";
|
||||
case "mp3":
|
||||
return "audio/x-mpeg";
|
||||
case "mp4":
|
||||
return "video/mp4";
|
||||
case "mpc":
|
||||
return "application/vnd.mpohun.certificate";
|
||||
case "mpe":
|
||||
return "video/mpeg";
|
||||
case "mpeg":
|
||||
return "video/mpeg";
|
||||
case "mpg":
|
||||
return "video/mpeg";
|
||||
case "mpg4":
|
||||
return "video/mp4";
|
||||
case "mpga":
|
||||
return "audio/mpeg";
|
||||
case "msg":
|
||||
return "application/vnd.ms-outlook";
|
||||
case "ogg":
|
||||
return "audio/ogg";
|
||||
case "pdf":
|
||||
return "application/pdf";
|
||||
case "png":
|
||||
return "image/png";
|
||||
case "pps":
|
||||
return "application/vnd.ms-powerpoint";
|
||||
case "ppt":
|
||||
return "application/vnd.ms-powerpoint";
|
||||
case "pptx":
|
||||
return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
|
||||
case "prop":
|
||||
return "text/plain";
|
||||
case "rc":
|
||||
return "text/plain";
|
||||
case "rmvb":
|
||||
return "audio/x-pn-realaudio";
|
||||
case "rtf":
|
||||
return "application/rtf";
|
||||
case "sh":
|
||||
return "text/plain";
|
||||
case "tar":
|
||||
return "application/x-tar";
|
||||
case "tgz":
|
||||
return "application/x-compressed";
|
||||
case "txt":
|
||||
return "text/plain";
|
||||
case "wav":
|
||||
return "audio/x-wav";
|
||||
case "wma":
|
||||
return "audio/x-ms-wma";
|
||||
case "wmv":
|
||||
return "audio/x-ms-wmv";
|
||||
case "wps":
|
||||
return "application/vnd.ms-works";
|
||||
case "xml":
|
||||
return "text/plain";
|
||||
case "z":
|
||||
return "application/x-compress";
|
||||
case "zip":
|
||||
return "application/x-zip-compressed";
|
||||
default:
|
||||
return "*/*";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
class CustomSticker {
|
||||
const CustomSticker(
|
||||
{required this.name, required this.index, this.url, this.unicode});
|
||||
|
||||
final int? unicode;
|
||||
final String name;
|
||||
final int index;
|
||||
final String? url;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
|
||||
class ErrorMessageConverter {
|
||||
static Map<int, String> errorCodeMap = {
|
||||
10007: TIM_t("操作权限不足"),
|
||||
20007: TIM_t("发送单聊消息,被对方拉黑,禁止发送。"),
|
||||
30010: TIM_t("您的好友数已达系统上限"),
|
||||
30014: TIM_t("对方的好友数已达系统上限"),
|
||||
30015: TIM_t("对方已是您的好友"),
|
||||
30515: TIM_t("被加好友在自己的黑名单中"),
|
||||
30516: TIM_t("对方已禁止加好友"),
|
||||
30525: TIM_t("您已被被对方设置为黑名单"),
|
||||
30539: TIM_t("等待好友审核同意"),
|
||||
};
|
||||
|
||||
static String getErrorMessage(int code) {
|
||||
return errorCodeMap[code] ?? "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// ignore_for_file: avoid_print, prefer_typing_uninitialized_variables
|
||||
|
||||
import 'dart:ui';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
|
||||
class Frame {
|
||||
static var orginalCallback;
|
||||
|
||||
static init() {
|
||||
orginalCallback = window.onReportTimings;
|
||||
window.onReportTimings = onReportTimings;
|
||||
}
|
||||
|
||||
// 仅缓存最近 25 帧绘制耗时
|
||||
static const maxframes = 25;
|
||||
static final List<FrameTiming> lastFrames = [];
|
||||
// 基准 VSync 信号周期
|
||||
static const frameInterval =
|
||||
Duration(microseconds: Duration.microsecondsPerSecond ~/ 60);
|
||||
|
||||
static void onReportTimings(List<FrameTiming> timings) {
|
||||
lastFrames.addAll(timings);
|
||||
// 仅保留 25 帧
|
||||
if (lastFrames.length > maxframes) {
|
||||
lastFrames.removeRange(0, lastFrames.length - maxframes);
|
||||
}
|
||||
// 如果有原始帧回调函数,则执行
|
||||
if (orginalCallback != null) {
|
||||
orginalCallback(timings);
|
||||
}
|
||||
outputLogger.i("fps: $fps");
|
||||
}
|
||||
|
||||
static double get fps {
|
||||
int sum = 0;
|
||||
for (FrameTiming timing in lastFrames) {
|
||||
// 计算渲染耗时
|
||||
int duration = timing.timestampInMicroseconds(FramePhase.rasterFinish) -
|
||||
timing.timestampInMicroseconds(FramePhase.buildStart);
|
||||
// 判断耗时是否在 Vsync 信号周期内
|
||||
if (duration < frameInterval.inMicroseconds) {
|
||||
sum += 1;
|
||||
} else {
|
||||
// 有丢帧,向上取整
|
||||
int count = (duration / frameInterval.inMicroseconds).ceil();
|
||||
sum += count;
|
||||
}
|
||||
}
|
||||
return lastFrames.length / sum * 60;
|
||||
}
|
||||
|
||||
static destroy() {
|
||||
window.onReportTimings = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||
|
||||
final outputLogger = TencentCloudChatLog();
|
||||
|
||||
class TencentCloudChatLog{
|
||||
void i(String text){
|
||||
if(!PlatformUtils().isWeb){
|
||||
TencentImSDKPlugin.v2TIMManager
|
||||
.uikitTrace(trace: text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
// ignore_for_file: unrelated_type_equality_checks, avoid_print
|
||||
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/group_change_info_type.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/group_tips_elem_type.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_change_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_change_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_tips_elem.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_tips_elem.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_image.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_image.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_value_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_value_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/tencent_im_sdk_plugin.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/constants/time.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
class MessageUtils {
|
||||
// 判断CallingData的方式和Trtc的方法一致
|
||||
static isCallingData(String data) {
|
||||
try {
|
||||
Map<String, dynamic> customMap = jsonDecode(data);
|
||||
|
||||
if (customMap.containsKey('businessID') && customMap['businessID'] == 1) {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
outputLogger.i("isCallingData json parse error");
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 是否是群组TRTC信息
|
||||
static isGroupCallingMessage(V2TimMessage message) {
|
||||
final isGroup = message.groupID != null;
|
||||
final isCustomMessage = message.elemType == MessageElemType.V2TIM_ELEM_TYPE_CUSTOM;
|
||||
if (isCustomMessage) {
|
||||
final customElemData = message.customElem?.data ?? "";
|
||||
return isCallingData(customElemData) && isGroup;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static String getCustomGroupCreatedOrDismissedString(V2TimMessage message) {
|
||||
try {
|
||||
final isGroup = message.groupID != null;
|
||||
final isCustomMessage = message.elemType == MessageElemType.V2TIM_ELEM_TYPE_CUSTOM;
|
||||
if (isCustomMessage && isGroup) {
|
||||
final data = message.customElem?.data ?? "";
|
||||
Map<String, dynamic> customMap = jsonDecode(data);
|
||||
if (customMap.containsKey('businessID') && customMap['businessID'] == "group_create") {
|
||||
final content = "${customMap['opUser']}${customMap['content']}";
|
||||
return content;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
} catch (e) {
|
||||
outputLogger.i("getCustomGroupCreatedOrDismissedString json parse error");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> _getGroupChangeType(
|
||||
V2TimGroupChangeInfo info, List<V2TimGroupMemberFullInfo?> groupMemberList) async {
|
||||
int? type = info.type;
|
||||
var value = info.value;
|
||||
String s = TIM_t('群资料信息');
|
||||
switch (type) {
|
||||
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_CUSTOM:
|
||||
s = TIM_t("自定义字段");
|
||||
break;
|
||||
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_FACE_URL:
|
||||
s = TIM_t("群头像");
|
||||
break;
|
||||
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_INTRODUCTION:
|
||||
s = TIM_t("群简介");
|
||||
break;
|
||||
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_NAME:
|
||||
s = TIM_t("群名称");
|
||||
break;
|
||||
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_NOTIFICATION:
|
||||
s = TIM_t("群公告");
|
||||
break;
|
||||
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_OWNER:
|
||||
s = TIM_t("群主");
|
||||
final V2TimGroupMemberFullInfo? groupMemberInfo =
|
||||
groupMemberList.firstWhereOrNull((element) => element?.userID == value);
|
||||
if (groupMemberInfo != null) {
|
||||
value = TencentUtils.checkString(groupMemberInfo.friendRemark) ??
|
||||
TencentUtils.checkString(groupMemberInfo.nameCard) ??
|
||||
TencentUtils.checkString(groupMemberInfo.nickName) ??
|
||||
TencentUtils.checkString(groupMemberInfo.userID);
|
||||
} else {
|
||||
final res = await TencentImSDKPlugin.v2TIMManager.getUsersInfo(userIDList: [value ?? ""]);
|
||||
if (res.code == 0) {
|
||||
final List<V2TimUserFullInfo> data = res.data ?? [];
|
||||
if (data.isNotEmpty) {
|
||||
final firstPerson = data[0];
|
||||
value = TencentUtils.checkString(firstPerson.nickName) ?? TencentUtils.checkString(firstPerson.userID);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_SHUT_UP_ALL:
|
||||
s = TIM_t("全员禁言状态");
|
||||
break;
|
||||
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_RECEIVE_MESSAGE_OPT:
|
||||
s = TIM_t("消息接收方式");
|
||||
break;
|
||||
case GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_GROUP_ADD_OPT:
|
||||
s = TIM_t("加群方式");
|
||||
break;
|
||||
}
|
||||
|
||||
final String option8 = s;
|
||||
if (value != null && value.isNotEmpty) {
|
||||
return TIM_t_para("{{option8}}为 ", "$option8为 ")(option8: option8) + value;
|
||||
} else {
|
||||
return option8;
|
||||
}
|
||||
}
|
||||
|
||||
static String? _getOpUserNick(V2TimGroupMemberInfo? opUser) {
|
||||
if (opUser == null) {
|
||||
return "";
|
||||
}
|
||||
return TencentUtils.checkString(opUser.friendRemark) ??
|
||||
TencentUtils.checkString(opUser.nickName) ??
|
||||
TencentUtils.checkString(opUser.userID);
|
||||
}
|
||||
|
||||
static String? _getMemberNickName(V2TimGroupMemberInfo e) {
|
||||
final friendRemark = e.friendRemark;
|
||||
final nameCard = e.nameCard;
|
||||
final nickName = e.nickName;
|
||||
final userID = e.userID;
|
||||
|
||||
if (friendRemark != null && friendRemark != "") {
|
||||
return friendRemark;
|
||||
} else if (nameCard != null && nameCard != "") {
|
||||
return nameCard;
|
||||
} else if (nickName != null && nickName != "") {
|
||||
return nickName;
|
||||
} else {
|
||||
return userID;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> groupTipsMessageAbstract(
|
||||
V2TimGroupTipsElem groupTipsElem, List<V2TimGroupMemberFullInfo?> groupMemberList) async {
|
||||
String displayMessage;
|
||||
final operationType = groupTipsElem.type;
|
||||
final operationMember = groupTipsElem.opMember;
|
||||
final memberList = groupTipsElem.memberList;
|
||||
final opUserNickName = _getOpUserNick(operationMember);
|
||||
switch (operationType) {
|
||||
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_GROUP_INFO_CHANGE:
|
||||
final String? option7 = opUserNickName ?? "";
|
||||
final groupChangeInfoList = groupTipsElem.groupChangeInfoList ?? [];
|
||||
String changedInfoString = "";
|
||||
bool changedValue = false;
|
||||
for (V2TimGroupChangeInfo? element in groupChangeInfoList) {
|
||||
final newText = await _getGroupChangeType(element!, groupMemberList);
|
||||
changedInfoString += (changedInfoString.isEmpty ? "" : " / ") + newText;
|
||||
changedValue = element!.boolValue ?? false;
|
||||
}
|
||||
if (changedInfoString.isEmpty) {
|
||||
changedInfoString = TIM_t("群资料");
|
||||
}
|
||||
if (changedInfoString == TIM_t("全员禁言状态")) {
|
||||
changedInfoString = TIM_t("全员禁言");
|
||||
displayMessage = changedValue == false
|
||||
? TIM_t_para("{{option7}} 取消", "$option7 取消")(option7: option7) + changedInfoString
|
||||
: TIM_t_para("{{option7}} 开启", "$option7 开启")(option7: option7) + changedInfoString;
|
||||
} else {
|
||||
displayMessage = TIM_t_para("{{option7}}修改", "$option7修改")(option7: option7) + changedInfoString;
|
||||
}
|
||||
break;
|
||||
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_QUIT:
|
||||
final String? option6 = opUserNickName ?? "";
|
||||
displayMessage = TIM_t_para("{{option6}}退出群聊", "$option6退出群聊")(option6: option6);
|
||||
break;
|
||||
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_INVITE:
|
||||
final option5 = memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
|
||||
final inviteUser = _getOpUserNick(operationMember);
|
||||
displayMessage = '$inviteUser' + TIM_t_para("邀请{{option5}}加入群组", "邀请$option5加入群组")(option5: option5);
|
||||
break;
|
||||
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_KICKED:
|
||||
final option4 = memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
|
||||
final kickUser = _getOpUserNick(operationMember);
|
||||
displayMessage = '$kickUser' + TIM_t_para("将{{option4}}踢出群组", "将$option4踢出群组")(option4: option4);
|
||||
break;
|
||||
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_JOIN:
|
||||
final option3 = memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
|
||||
displayMessage = TIM_t_para("用户{{option3}}加入了群聊", "用户$option3加入了群聊")(option3: option3);
|
||||
break;
|
||||
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_MEMBER_INFO_CHANGE:
|
||||
displayMessage = groupTipsElem.memberList!.map((e) {
|
||||
final changedMember =
|
||||
groupTipsElem.memberChangeInfoList!.firstWhere((element) => element!.userID == e!.userID);
|
||||
final isMute = changedMember!.muteTime != 0;
|
||||
final option2 = _getMemberNickName(e!);
|
||||
final displayMessage = isMute ? TIM_t("禁言") : TIM_t("解除禁言");
|
||||
return TIM_t_para("{{option2}} 被", "$option2 被")(option2: option2) + displayMessage;
|
||||
}).join("、");
|
||||
break;
|
||||
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_SET_ADMIN:
|
||||
final adminMember = memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
|
||||
final opMember = _getOpUserNick(operationMember);
|
||||
final option1 = adminMember;
|
||||
displayMessage = '$opMember' + TIM_t_para("将 {{option1}} 设置为管理员", "将 $option1 设置为管理员")(option1: option1);
|
||||
break;
|
||||
case GroupTipsElemType.V2TIM_GROUP_TIPS_TYPE_CANCEL_ADMIN:
|
||||
final adminMember = memberList!.map((e) => _getMemberNickName(e!).toString()).join("、");
|
||||
final opMember = _getOpUserNick(operationMember);
|
||||
final option1 = adminMember;
|
||||
displayMessage = '$opMember' + TIM_t_para("将 {{option1}} 取消管理员", "将 $option1 取消管理员")(option1: option1);
|
||||
break;
|
||||
default:
|
||||
final String option2 = operationType.toString();
|
||||
displayMessage = TIM_t_para("系统消息 {{option2}}", "系统消息 $option2")(option2: option2);
|
||||
break;
|
||||
}
|
||||
return displayMessage;
|
||||
}
|
||||
|
||||
static String formatVideoTime(int time) {
|
||||
List<int> times = [];
|
||||
if (time <= 0) return '0:01';
|
||||
if (time >= TimeConst.DAY_SEC) return '1d+';
|
||||
for (int idx = 0; idx < TimeConst.SEC_SERIES.length; idx++) {
|
||||
int sec = TimeConst.SEC_SERIES[idx];
|
||||
if (time >= sec) {
|
||||
times.add((time / sec).floor());
|
||||
time = time % sec;
|
||||
} else if (idx > 0) {
|
||||
times.add(0);
|
||||
}
|
||||
}
|
||||
times.add(time);
|
||||
String formatTime = times[0].toString();
|
||||
for (int idx = 1; idx < times.length; idx++) {
|
||||
if (times[idx] < 10) {
|
||||
formatTime += ':0${times[idx].toString()}';
|
||||
} else {
|
||||
formatTime += ':${times[idx].toString()}';
|
||||
}
|
||||
}
|
||||
return formatTime;
|
||||
}
|
||||
|
||||
static String handleCustomMessageString(V2TimMessage message) {
|
||||
return TIM_t("消息");
|
||||
}
|
||||
|
||||
static Widget wrapMessageTips(Widget child, TUITheme? theme) {
|
||||
return Container(margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 30), child: child);
|
||||
}
|
||||
|
||||
static String getAbstractMessageAsync(V2TimMessage message, List<V2TimGroupMemberFullInfo?> groupMemberList) {
|
||||
final msgType = message.elemType;
|
||||
switch (msgType) {
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_CUSTOM:
|
||||
return handleCustomMessageString(message);
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_SOUND:
|
||||
return TIM_t("[语音]");
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_TEXT:
|
||||
return message.textElem!.text as String;
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_FACE:
|
||||
return TIM_t("[表情]");
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_FILE:
|
||||
final String? option2 = message.fileElem!.fileName ?? "";
|
||||
return TIM_t_para("[文件] {{option2}}", "[文件] $option2")(option2: option2);
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_GROUP_TIPS:
|
||||
return TIM_t("群提示");
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_IMAGE:
|
||||
return TIM_t("[图片]");
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_VIDEO:
|
||||
return TIM_t("[视频]");
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_LOCATION:
|
||||
return TIM_t("[位置]");
|
||||
case MessageElemType.V2TIM_ELEM_TYPE_MERGER:
|
||||
return TIM_t("[聊天记录]");
|
||||
default:
|
||||
return TIM_t("未知消息");
|
||||
}
|
||||
}
|
||||
|
||||
static V2TimImage? getImageFromImgList(List<V2TimImage?>? list, List<String> order) {
|
||||
V2TimImage? img;
|
||||
try {
|
||||
for (String type in order) {
|
||||
img =
|
||||
list?.firstWhere((e) => e?.type == HistoryMessageDartConstant.V2_TIM_IMAGE_TYPES[type], orElse: () => null);
|
||||
}
|
||||
} catch (e) {
|
||||
outputLogger.i('getImageFromImgList error ${e.toString()}');
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
static String getDisplayName(V2TimMessage message) {
|
||||
final friendRemark = message.friendRemark ?? "";
|
||||
final nameCard = message.nameCard ?? "";
|
||||
final nickName = message.nickName ?? "";
|
||||
final sender = message.sender ?? "";
|
||||
final displayName = friendRemark.isNotEmpty
|
||||
? friendRemark
|
||||
: nameCard.isNotEmpty
|
||||
? nameCard
|
||||
: nickName.isNotEmpty
|
||||
? nickName
|
||||
: sender;
|
||||
return displayName.toString();
|
||||
}
|
||||
|
||||
static Future<V2TimValueCallback<V2TimMessage>?> handleMessageError(
|
||||
Future<V2TimValueCallback<V2TimMessage>?> fun, BuildContext context) async {
|
||||
final res = await fun;
|
||||
return handleMessageErrorCode(res, context);
|
||||
}
|
||||
|
||||
static V2TimValueCallback<V2TimMessage>? handleMessageErrorCode(
|
||||
V2TimValueCallback<V2TimMessage>? sendMsgRes, BuildContext context) {
|
||||
if (sendMsgRes == null) return null;
|
||||
|
||||
return sendMsgRes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'dart:async';
|
||||
|
||||
class OptimizeUtils {
|
||||
static throttle(Function func, int milliseconds) {
|
||||
bool enable = true;
|
||||
return (val) {
|
||||
if (enable == true) {
|
||||
enable = false;
|
||||
Future.delayed(Duration(milliseconds: milliseconds), () {
|
||||
enable = true;
|
||||
func(val);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static multiThrottle(Function func, int milliseconds) {
|
||||
bool enable = true;
|
||||
return (val1, val2) {
|
||||
if (enable == true) {
|
||||
enable = false;
|
||||
Future.delayed(Duration(milliseconds: milliseconds), () {
|
||||
enable = true;
|
||||
func(val1, val2);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static debounce(
|
||||
Function func, [
|
||||
Duration delay = const Duration(milliseconds: 100),
|
||||
]) {
|
||||
Timer? timer;
|
||||
return (val) {
|
||||
if (timer != null) {
|
||||
timer?.cancel();
|
||||
}
|
||||
|
||||
timer = Timer(delay, () {
|
||||
func(val);
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.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/ui/utils/platform.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
class PermissionRequestInfo extends StatefulWidget {
|
||||
final Function removeOverLay;
|
||||
final int permissionType;
|
||||
final String appName;
|
||||
|
||||
const PermissionRequestInfo(
|
||||
{Key? key,
|
||||
required this.removeOverLay,
|
||||
required this.permissionType,
|
||||
required this.appName})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _PermissionRequestInfo();
|
||||
}
|
||||
|
||||
class _PermissionRequestInfo extends TIMUIKitState<PermissionRequestInfo>
|
||||
with WidgetsBindingObserver {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.removeOverLay();
|
||||
super.dispose();
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
super.didChangeAppLifecycleState(state);
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
widget.removeOverLay();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final theme = value.theme;
|
||||
|
||||
final permission = {
|
||||
1: {
|
||||
"name": TIM_t("相机"),
|
||||
"icon": "images/chat_permission_icon_camera.png",
|
||||
"text": TIM_t("为方便您将所拍摄的照片或视频发送给朋友,以及进行视频通话,请允许我们访问摄像头进行拍摄照片和视频。")
|
||||
},
|
||||
7: {
|
||||
"name": TIM_t("麦克风"),
|
||||
"icon": "images/chat_permission_icon_mic.png",
|
||||
"text": TIM_t("为方便您发送语音消息、拍摄视频以及音视频通话,请允许我们使用麦克风进行录音。")
|
||||
},
|
||||
9: {
|
||||
"name": TIM_t("相册"),
|
||||
"icon": "images/chat_permission_icon_file.png",
|
||||
"text": TIM_t("为方便您查看和选择相册里的图片视频发送给朋友,以及保存内容到设备,请允许我们访问您设备上的照片、媒体内容。")
|
||||
},
|
||||
15: {
|
||||
"name": TIM_t("存储"),
|
||||
"icon": "images/chat_permission_icon_file.png",
|
||||
"text": TIM_t("为方便您查看和选择相册里的图片视频发送给朋友,以及保存内容到设备,请允许我们访问您设备上的照片、媒体内容。")
|
||||
},
|
||||
32: {
|
||||
"name": TIM_t("相册"),
|
||||
"icon": "images/chat_permission_icon_file.png",
|
||||
"text": TIM_t("为方便您查看和选择相册里的图片视频发送给朋友,以及保存内容到设备,请允许我们访问您设备上的照片、媒体内容。")
|
||||
},
|
||||
}[widget.permissionType];
|
||||
final option2 = permission?["name"] ?? "";
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
child: SafeArea(
|
||||
child: Opacity(
|
||||
opacity: 0.7,
|
||||
child: Container(
|
||||
color: theme.black,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
width: 50,
|
||||
child: Image.asset(
|
||||
permission?["icon"] ?? "",
|
||||
package: "tencent_cloud_chat_uikit",
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Text(
|
||||
TIM_t(widget.appName) +
|
||||
TIM_t_para(" 申请获取{{option2}}", " 申请获取$option2")(
|
||||
option2: option2) +
|
||||
TIM_t("权限"),
|
||||
style: TextStyle(color: theme.white, fontSize: 18),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
permission?["text"] ?? "",
|
||||
style: TextStyle(color: theme.white, fontSize: 16),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Permissions {
|
||||
|
||||
static OverlayEntry? _entry;
|
||||
|
||||
static List<String> _names(BuildContext context) {
|
||||
return <String>[
|
||||
TIM_t("日历"),
|
||||
TIM_t("相机"),
|
||||
TIM_t("联系人"),
|
||||
TIM_t("位置"),
|
||||
'locationAlways',
|
||||
'locationWhenInUse',
|
||||
'mediaLibrary',
|
||||
TIM_t("麦克风"),
|
||||
'phone',
|
||||
TIM_t("照片"),
|
||||
TIM_t("相册写入"),
|
||||
'reminders',
|
||||
'sensors',
|
||||
'sms',
|
||||
'speech',
|
||||
TIM_t("文件"),
|
||||
'ignoreBatteryOptimizations',
|
||||
'notification',
|
||||
'access_media_location',
|
||||
'activity_recognition',
|
||||
'unknown',
|
||||
'bluetooth',
|
||||
'manageExternalStorage',
|
||||
'systemAlertWindow',
|
||||
'requestInstallPackages',
|
||||
'appTrackingTransparency',
|
||||
'criticalAlerts',
|
||||
'accessNotificationPolicy',
|
||||
'bluetoothScan',
|
||||
'bluetoothAdvertise',
|
||||
'bluetoothConnect',
|
||||
'nearbyWifiDevices',
|
||||
TIM_t("视频"),
|
||||
'audio',
|
||||
'scheduleExactAlarm'
|
||||
];
|
||||
}
|
||||
|
||||
static String _permissionText(
|
||||
BuildContext context, String appName, int value) {
|
||||
final _prefix = TIM_t("需要授予");
|
||||
final _postfixList = <String>[
|
||||
TIM_t("日历"),
|
||||
TIM_t(" 相机权限,以正常使用拍摄图片视频、视频通话等功能。"),
|
||||
TIM_t("联系人"),
|
||||
TIM_t("位置"),
|
||||
'locationAlways',
|
||||
'locationWhenInUse',
|
||||
'mediaLibrary',
|
||||
TIM_t(" 麦克风权限,以正常使用发送语音消息、拍摄视频、音视频通话等功能。"),
|
||||
'phone',
|
||||
TIM_t(" 访问照片权限,以正常使用发送图片、视频等功能。"),
|
||||
TIM_t(" 访问相册写入权限,以正常使用存储图片、视频等功能。"),
|
||||
'reminders',
|
||||
'sensors',
|
||||
'sms',
|
||||
'speech',
|
||||
TIM_t(" 文件读写权限,以正常使用在聊天功能中的图片查看、选择能力和发送文件的能力。"),
|
||||
'ignoreBatteryOptimizations',
|
||||
'notification',
|
||||
'access_media_location',
|
||||
'activity_recognition',
|
||||
'unknown',
|
||||
'bluetooth',
|
||||
'manageExternalStorage',
|
||||
'systemAlertWindow',
|
||||
'requestInstallPackages',
|
||||
'appTrackingTransparency',
|
||||
'criticalAlerts',
|
||||
'accessNotificationPolicy',
|
||||
'bluetoothScan',
|
||||
'bluetoothAdvertise',
|
||||
'bluetoothConnect',
|
||||
'nearbyWifiDevices',
|
||||
TIM_t(" 访问相册中视频权限,以正常使用发送视频等功能。"),
|
||||
'audio',
|
||||
'scheduleExactAlarm'
|
||||
];
|
||||
return _prefix + appName + _postfixList[value];
|
||||
}
|
||||
|
||||
static Future<bool> checkPermission(
|
||||
BuildContext context,
|
||||
int value, [
|
||||
TUITheme? theme,
|
||||
bool isShowPermissionPage = true,
|
||||
]) async {
|
||||
final status = await Permission.byValue(value).status;
|
||||
if (status.isGranted || status.isLimited) {
|
||||
return true;
|
||||
}
|
||||
final bool? shouldRequestPermission = await showPermissionConfirmDialog(
|
||||
context,
|
||||
value,
|
||||
theme,
|
||||
isShowPermissionPage,
|
||||
);
|
||||
if (shouldRequestPermission != null && shouldRequestPermission) {
|
||||
final isGranted = await Permission.byValue(value).request().isGranted;
|
||||
_entry?.remove();
|
||||
_entry = null;
|
||||
return isGranted;
|
||||
}
|
||||
return shouldRequestPermission ?? false;
|
||||
}
|
||||
|
||||
static Future<bool> checkPermissionSetBefore(int value) async {
|
||||
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
|
||||
SharedPreferences prefs = await _prefs;
|
||||
final _hasPermissionSetBefore = prefs.getBool("permission$value");
|
||||
return _hasPermissionSetBefore ?? false;
|
||||
}
|
||||
|
||||
static Future<bool> setLocalPermission(int value) async {
|
||||
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
|
||||
SharedPreferences prefs = await _prefs;
|
||||
return await prefs.setBool("permission$value", true);
|
||||
}
|
||||
|
||||
static showPermissionRequestInfoDialog(BuildContext context, value) async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
String appName = packageInfo.appName;
|
||||
final entry = OverlayEntry(builder: (context) {
|
||||
return PermissionRequestInfo(
|
||||
appName: appName,
|
||||
removeOverLay: () {
|
||||
_entry?.remove();
|
||||
_entry = null;
|
||||
},
|
||||
permissionType: value,
|
||||
);
|
||||
});
|
||||
_entry = entry;
|
||||
Overlay.of(context).insert(entry);
|
||||
}
|
||||
|
||||
static Future<bool?> showPermissionConfirmDialog(BuildContext context, value,
|
||||
[TUITheme? theme, bool isShowPermissionPage = true]) async {
|
||||
final platformUtils = PlatformUtils();
|
||||
// 第一次直接走系统文案
|
||||
if (!await checkPermissionSetBefore(value)) {
|
||||
await setLocalPermission(value);
|
||||
if (platformUtils.isAndroid && isShowPermissionPage) {
|
||||
showPermissionRequestInfoDialog(context, value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
String appName = packageInfo.appName;
|
||||
final option2 = _names(context)[value];
|
||||
final permissionText = _permissionText(context, appName, value);
|
||||
|
||||
void closeDialog() {
|
||||
Navigator.of(context).pop(false);
|
||||
}
|
||||
|
||||
void getPermission() async {
|
||||
Navigator.of(context).pop(false);
|
||||
openAppSettings();
|
||||
}
|
||||
|
||||
return showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return platformUtils.isIOS
|
||||
? CupertinoAlertDialog(
|
||||
title: Text("“$appName”" +
|
||||
TIM_t_para(" 想访问您的{{option2}}", " 想访问您的$option2")(
|
||||
option2: option2)),
|
||||
content: Text(permissionText),
|
||||
actions: <Widget>[
|
||||
CupertinoDialogAction(
|
||||
child: Text(TIM_t("以后再说")),
|
||||
onPressed: closeDialog, // 关闭对话框
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: Text(TIM_t("去开启")),
|
||||
onPressed: getPermission,
|
||||
),
|
||||
],
|
||||
)
|
||||
: AlertDialog(
|
||||
content: Text(permissionText),
|
||||
actions: <Widget>[
|
||||
const Divider(),
|
||||
SizedBox(
|
||||
height: 48,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
child: Text(TIM_t("以后再说"),
|
||||
style: TextStyle(
|
||||
color: theme?.black ?? Colors.black,
|
||||
)),
|
||||
onPressed: closeDialog, // 关闭对话框
|
||||
),
|
||||
),
|
||||
const VerticalDivider(),
|
||||
Expanded(
|
||||
child: TextButton(
|
||||
child: Text(TIM_t("去开启"),
|
||||
style: TextStyle(
|
||||
color: theme?.black ?? Colors.black,
|
||||
)),
|
||||
onPressed: getPermission,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
|
||||
class PlatformUtils {
|
||||
PlatformUtils._internal();
|
||||
static late bool _isAndroid;
|
||||
static late bool _isIos;
|
||||
static late bool _isMobile;
|
||||
static late bool _isWeb;
|
||||
static late bool _isWindows;
|
||||
static late bool _isMacOS;
|
||||
static late bool _isLinux;
|
||||
static late bool _isDesktop;
|
||||
static bool _isInstantiation = false;
|
||||
|
||||
factory PlatformUtils() {
|
||||
if (!_isInstantiation) {
|
||||
_isAndroid = !kIsWeb && Platform.isAndroid;
|
||||
_isIos = !kIsWeb && Platform.isIOS;
|
||||
_isMobile = _isAndroid || _isIos;
|
||||
_isWindows = !kIsWeb && Platform.isWindows;
|
||||
_isMacOS = !kIsWeb && Platform.isMacOS;
|
||||
_isLinux = !kIsWeb && Platform.isLinux;
|
||||
_isDesktop = _isMacOS || _isWindows || _isLinux;
|
||||
_isWeb = kIsWeb;
|
||||
_isInstantiation = true;
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
static late final PlatformUtils _instance = PlatformUtils._internal();
|
||||
|
||||
get isAndroid {
|
||||
return _isAndroid;
|
||||
}
|
||||
|
||||
get isWeb {
|
||||
return _isWeb;
|
||||
}
|
||||
|
||||
get isIOS {
|
||||
return _isIos;
|
||||
}
|
||||
|
||||
get isWindows {
|
||||
return _isWindows;
|
||||
}
|
||||
|
||||
get isMacOS {
|
||||
return _isMacOS;
|
||||
}
|
||||
|
||||
get isMobile {
|
||||
return _isMobile;
|
||||
}
|
||||
|
||||
bool get isDesktop => _isDesktop;
|
||||
|
||||
bool get isLinux => _isLinux;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class FadeRoute extends PageRouteBuilder {
|
||||
final Widget page;
|
||||
FadeRoute({required this.page})
|
||||
: super(
|
||||
pageBuilder: (
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
) =>
|
||||
page,
|
||||
transitionsBuilder: (
|
||||
BuildContext context,
|
||||
Animation<double> animation,
|
||||
Animation<double> secondaryAnimation,
|
||||
Widget child,
|
||||
) =>
|
||||
FadeTransition(
|
||||
opacity: animation,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class ScreenshotHelper {
|
||||
static Future<String?> captureScreen() async {
|
||||
await requestScreenRecordingPermission();
|
||||
String directory;
|
||||
|
||||
if(PlatformUtils().isWindows){
|
||||
final String documentsDirectoryPath =
|
||||
"${Platform.environment['USERPROFILE']}";
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
String pkgName = packageInfo.packageName;
|
||||
directory = p.join(documentsDirectoryPath, "Documents", ".TencentCloudChat",
|
||||
pkgName, "screenshots");
|
||||
}else{
|
||||
final dic = await getApplicationSupportDirectory();
|
||||
directory = dic.path;
|
||||
}
|
||||
|
||||
const uuid = Uuid();
|
||||
final fileName = 'screenshot_${uuid.v4()}.png';
|
||||
final filePath = '$directory/$fileName';
|
||||
if (Platform.isMacOS) {
|
||||
// 在macOS平台上使用screencapture工具
|
||||
final result = await Process.run(
|
||||
'screencapture',
|
||||
['-i', '-s', '-o', filePath],
|
||||
);
|
||||
if (result.exitCode == 0) {
|
||||
return filePath;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (Platform.isWindows) {
|
||||
// 在Windows平台上使用snippingtool工具
|
||||
final result = await Process.run(
|
||||
'snippingtool',
|
||||
['/clip', filePath],
|
||||
);
|
||||
if (result.exitCode == 0) {
|
||||
return filePath;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// 不支持的平台
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> requestScreenRecordingPermission() async {
|
||||
if (Platform.isMacOS) {
|
||||
final result = await Process.run(
|
||||
'sh', ['-c', 'echo ${Platform.environment['USER']}']);
|
||||
final username = result.stdout.toString().trim();
|
||||
const script =
|
||||
'tell application "System Events" to return exists (processes where name is "ControlCenter")';
|
||||
final process = await Process.run('osascript', ['-e', script]);
|
||||
final isControlCenterRunning = process.stdout.toString().trim() == 'true';
|
||||
|
||||
if (!isControlCenterRunning) {
|
||||
await Process.run('open', ['-a', 'ControlCenter']);
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
|
||||
final script2 = 'tell application "ControlCenter" to activate\n'
|
||||
'tell application "System Events"\n'
|
||||
' tell process "ControlCenter"\n'
|
||||
' click menu item "Screen Recording" of menu "File" of menu bar 1\n'
|
||||
' delay 0.5\n'
|
||||
' keystroke "$username" & return\n'
|
||||
' end tell\n'
|
||||
'end tell\n';
|
||||
await Process.run('osascript', ['-e', script2]);
|
||||
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
final isScreenRecordingEnabled = await SystemChannels.platform
|
||||
.invokeMethod<bool>('isScreenRecordingEnabled');
|
||||
return isScreenRecordingEnabled ?? false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Size> getImageSize(String imagePath) async {
|
||||
final bytes = await File(imagePath).readAsBytes();
|
||||
final completer = Completer<Size>();
|
||||
final imageStream =
|
||||
Image.memory(bytes).image.resolve(ImageConfiguration.empty);
|
||||
imageStream.addListener(ImageStreamListener((imageInfo, _) {
|
||||
completer.complete(Size(
|
||||
imageInfo.image.width.toDouble(), imageInfo.image.height.toDouble()));
|
||||
}));
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||
|
||||
enum DeviceType { Desktop, Mobile }
|
||||
|
||||
class FormFactor {
|
||||
static double desktop = 900;
|
||||
static double handset = 300;
|
||||
}
|
||||
|
||||
class TUIKitScreenUtils {
|
||||
static DeviceType? deviceType;
|
||||
|
||||
/// Although specifying the `BuildContext` is optional, providing it can prevent layout issues when this widget renders immediately after the app launch.
|
||||
/// If this widget needs to be used at the moment the app launches, it's recommended to provide the `BuildContext` here.
|
||||
static DeviceType getFormFactor([BuildContext? context]) {
|
||||
if (deviceType != null) return deviceType!;
|
||||
|
||||
if (PlatformUtils().isWeb) {
|
||||
final win = WidgetsBinding.instance.platformDispatcher.views.first;
|
||||
final size = win.physicalSize;
|
||||
final screenWidth = size.width / win.devicePixelRatio;
|
||||
final screenHeight = size.height / win.devicePixelRatio;
|
||||
|
||||
final diagonalInInches =
|
||||
sqrt(pow(screenWidth, 2) + pow(screenHeight, 2)) / 96.0;
|
||||
|
||||
deviceType = diagonalInInches < 11.0 ? DeviceType.Mobile : DeviceType.Desktop;
|
||||
return deviceType ?? DeviceType.Mobile;
|
||||
}else{
|
||||
if(context != null){
|
||||
double deviceWidth = MediaQuery.of(context).size.width;
|
||||
double deviceHeight = MediaQuery.of(context).size.height;
|
||||
|
||||
if (deviceWidth > FormFactor.desktop || deviceWidth > deviceHeight * 1.1) {
|
||||
deviceType = DeviceType.Desktop;
|
||||
} else if (deviceWidth > FormFactor.handset) {
|
||||
deviceType = DeviceType.Mobile;
|
||||
}
|
||||
return deviceType ?? DeviceType.Mobile;
|
||||
}else{
|
||||
return DeviceType.Mobile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Widget getDeviceWidget({
|
||||
/// Although specifying the `BuildContext` is optional, providing it can prevent layout issues when this widget renders immediately after the app launch.
|
||||
/// If this widget needs to be used at the moment the app launches, it's recommended to provide the `BuildContext` here.
|
||||
BuildContext? context,
|
||||
required Widget defaultWidget,
|
||||
Widget? desktopWidget,
|
||||
Widget? mobileWidget,
|
||||
}) {
|
||||
deviceType ??= getFormFactor(context);
|
||||
if (deviceType == DeviceType.Desktop) return desktopWidget ?? defaultWidget;
|
||||
return mobileWidget ?? defaultWidget;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_plugin_record_plus/const/play_state.dart';
|
||||
import 'package:flutter_plugin_record_plus/const/response.dart';
|
||||
import 'package:flutter_plugin_record_plus/index.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/import_proxy/import_proxy.dart';
|
||||
|
||||
typedef PlayStateListener = void Function(PlayState playState);
|
||||
typedef SoundInterruptListener = void Function();
|
||||
typedef ResponseListener = void Function(RecordResponse recordResponse);
|
||||
|
||||
class SoundPlayer {
|
||||
final ImportProxy importProxy = ImportProxy();
|
||||
static final FlutterPluginRecord _recorder = FlutterPluginRecord();
|
||||
static SoundInterruptListener? _soundInterruptListener;
|
||||
static bool isInit = false;
|
||||
static final AudioPlayer _audioPlayer = AudioPlayer();
|
||||
|
||||
static initSoundPlayer() {
|
||||
if (!isInit) {
|
||||
_recorder.init();
|
||||
// AudioPlayer.global.setGlobalAudioContext(const AudioContext());
|
||||
isInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> play({required String url}) async {
|
||||
_audioPlayer.stop();
|
||||
if (_soundInterruptListener != null) {
|
||||
_soundInterruptListener!();
|
||||
}
|
||||
await _audioPlayer.setUrl(url);
|
||||
|
||||
await _audioPlayer.play();
|
||||
}
|
||||
|
||||
static stop() {
|
||||
_audioPlayer.stop();
|
||||
|
||||
}
|
||||
|
||||
static dispose() {
|
||||
_audioPlayer.dispose();
|
||||
_recorder.dispose();
|
||||
}
|
||||
|
||||
static StreamSubscription<PlayerState> playStateListener({required void Function(PlayerState)? listener}) => _audioPlayer.playerStateStream.listen(listener);
|
||||
|
||||
static setSoundInterruptListener(SoundInterruptListener listener) {
|
||||
_soundInterruptListener = listener;
|
||||
}
|
||||
|
||||
static removeSoundInterruptListener() {
|
||||
_soundInterruptListener = null;
|
||||
}
|
||||
|
||||
static StreamSubscription<RecordResponse> responseListener(ResponseListener listener) => _recorder.response.listen(listener);
|
||||
|
||||
static StreamSubscription<RecordResponse> responseFromAmplitudeListener(ResponseListener listener) => _recorder.responseFromAmplitude.listen(listener);
|
||||
|
||||
static startRecord() {
|
||||
_recorder.start();
|
||||
}
|
||||
|
||||
static stopRecord() {
|
||||
_recorder.stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
// ignore_for_file: prefer_typing_uninitialized_variables
|
||||
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
|
||||
class TimeAgo {
|
||||
List<String> dayMap() {
|
||||
return [
|
||||
TIM_t("昨天"),
|
||||
TIM_t("前天"),
|
||||
];
|
||||
}
|
||||
|
||||
List<String> weekdayMap() {
|
||||
return ['', TIM_t("星期一"), TIM_t("星期二"), TIM_t("星期三"), TIM_t("星期四"), TIM_t("星期五"), TIM_t("星期六"), TIM_t("星期天")];
|
||||
}
|
||||
|
||||
String getYearMonthDate(DateTime dateTime) {
|
||||
String month = dateTime.month.toString();
|
||||
String date = dateTime.day.toString();
|
||||
return dateTime.year.toString() + '/' + (month.length == 1 ? '0' : '') + month + '/' + (date.length == 1 ? '0' : '') + date;
|
||||
}
|
||||
|
||||
String getMonthDate(DateTime dateTime) {
|
||||
String month = dateTime.month.toString();
|
||||
String date = dateTime.day.toString();
|
||||
return (month.length == 1 ? '0' : '') + month + '/' + (date.length == 1 ? '0' : '') + date;
|
||||
}
|
||||
|
||||
static String getMonth(DateTime dateTime) {
|
||||
String month = dateTime.month.toString();
|
||||
return (month.length == 1 ? '0' : '') + month;
|
||||
}
|
||||
|
||||
static String getDay(DateTime dateTime) {
|
||||
String day = dateTime.day.toString();
|
||||
return (day.length == 1 ? '0' : '') + day;
|
||||
}
|
||||
|
||||
String? getTimeStringForChat(int timeStamp) {
|
||||
final DateTime date = DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000);
|
||||
final DateTime epochLimit = DateTime.utc(1971);
|
||||
|
||||
if (date.isBefore(epochLimit)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Duration duration = DateTime.now().difference(date);
|
||||
final int diffDays = duration.inDays + (duration.inMinutes > DateTime.now().difference(DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day)).inMinutes ? 1 : 0);
|
||||
final int diffMinutes = duration.inMinutes;
|
||||
|
||||
var res;
|
||||
|
||||
// 一个礼拜之内
|
||||
if (diffDays > 0 && diffDays < 7) {
|
||||
if (diffDays <= 2) {
|
||||
res = dayMap()[diffDays - 1];
|
||||
} else {
|
||||
res = weekdayMap()[date.weekday];
|
||||
}
|
||||
} else if (diffDays >= 7) {
|
||||
//当年内
|
||||
if (date.year == DateTime.now().year) {
|
||||
res = getMonthDate(date);
|
||||
} else {
|
||||
res = getYearMonthDate(date);
|
||||
}
|
||||
} else {
|
||||
if (diffMinutes > 1) {
|
||||
if (diffMinutes < 60) {
|
||||
final String option2 = diffMinutes.toString();
|
||||
res = TIM_t_para("{{option2}} 分钟前", "$option2 分钟前")(option2: option2);
|
||||
} else {
|
||||
res = "${date.hour}:${date.minute < 10 ? "0" + date.minute.toString() : date.minute}";
|
||||
// res = "$prefix $timeStr";
|
||||
}
|
||||
} else {
|
||||
res = TIM_t("现在");
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
String getTimeForMessage(int timeStamp) {
|
||||
var nowTime = DateTime.now();
|
||||
nowTime = DateTime(nowTime.year, nowTime.month, nowTime.day);
|
||||
var ftime = DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000);
|
||||
// var preFix = ftime.hour >= 12 ? TIM_t("下午") : TIM_t("上午");
|
||||
final timeStr = DateFormat('HH:mm').format(ftime); // Use 'HH:mm' for 24-hour format
|
||||
// 一年外 年月日 + 时间 (24小时制)
|
||||
if (nowTime.year != ftime.year) {
|
||||
return '${DateFormat('yyyy-MM-dd').format(ftime)} $timeStr';
|
||||
}
|
||||
// 一年内一周外 月日 + 时间 (24小时制)
|
||||
if (ftime.isBefore(nowTime.subtract(const Duration(days: 6)))) {
|
||||
return '${DateFormat('MM-dd').format(ftime)} $timeStr';
|
||||
}
|
||||
// 一周内一天外 星期 + 时间 (24小时制)
|
||||
if (ftime.isBefore(nowTime.subtract(const Duration(days: 1)))) {
|
||||
return '${weekdayMap()[ftime.weekday]} $timeStr';
|
||||
}
|
||||
// 昨日 昨天 + 时间 (24小时制)
|
||||
if (nowTime.day != ftime.day) {
|
||||
String option2 = timeStr;
|
||||
return TIM_t_para("昨天 {{option2}}", "昨天 $option2")(option2: option2);
|
||||
}
|
||||
// 同年月日 时间 (24小时制)
|
||||
return timeStr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
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/friend_type_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.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/life_cycle/add_friend_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitAddFriend/tim_uikit_send_application.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
class TIMUIKitAddFriend extends StatefulWidget {
|
||||
final bool? isShowDefaultGroup;
|
||||
|
||||
/// You may navigate to user profile page, if friendship relationship exists.
|
||||
final Function(String userID) onTapAlreadyFriendsItem;
|
||||
|
||||
/// The life cycle hooks for adding friends and contact business logic
|
||||
final AddFriendLifeCycle? lifeCycle;
|
||||
|
||||
/// The callback function to close the widget upon completion by the parent component.
|
||||
final VoidCallback? closeFunc;
|
||||
|
||||
const TIMUIKitAddFriend(
|
||||
{Key? key,
|
||||
this.isShowDefaultGroup = false,
|
||||
this.lifeCycle,
|
||||
required this.onTapAlreadyFriendsItem,
|
||||
this.closeFunc})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _TIMUIKitAddFriendState();
|
||||
}
|
||||
|
||||
class _TIMUIKitAddFriendState extends TIMUIKitState<TIMUIKitAddFriend> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
final CoreServicesImpl _coreServicesImpl = serviceLocator<CoreServicesImpl>();
|
||||
final FriendshipServices _friendshipServices = serviceLocator<FriendshipServices>();
|
||||
final TUISelfInfoViewModel _selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>();
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
bool isFocused = false;
|
||||
bool showResult = false;
|
||||
List<V2TimUserFullInfo>? searchResult;
|
||||
|
||||
Widget _searchResultItemBuilder(V2TimUserFullInfo friendInfo, TUITheme theme) {
|
||||
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
|
||||
final faceUrl = friendInfo.faceUrl ?? "";
|
||||
final userID = friendInfo.userID ?? "";
|
||||
final String showName =
|
||||
((friendInfo.nickName != null && friendInfo.nickName!.isNotEmpty) ? friendInfo.nickName : userID) ?? "";
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
final checkFriend = await _friendshipServices
|
||||
.checkFriend(userIDList: [userID], checkType: FriendTypeEnum.V2TIM_FRIEND_TYPE_SINGLE);
|
||||
if (checkFriend != null) {
|
||||
final res = checkFriend.first;
|
||||
if (res.resultCode == 0 && res.resultType != 0) {
|
||||
widget.onTapAlreadyFriendsItem(userID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (userID == _selfInfoViewModel.loginInfo?.userID) {
|
||||
widget.onTapAlreadyFriendsItem(userID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDesktopScreen) {
|
||||
if (widget.closeFunc != null) {
|
||||
widget.closeFunc!();
|
||||
}
|
||||
TUIKitWidePopup.showPopupWindow(
|
||||
operationKey: TUIKitWideModalOperationKey.addFriend,
|
||||
context: context,
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
height: MediaQuery.of(context).size.width * 0.4,
|
||||
title: TIM_t("添加好友"),
|
||||
child: (closeFuncSendApplication) => SendApplication(
|
||||
lifeCycle: widget.lifeCycle,
|
||||
isShowDefaultGroup: widget.isShowDefaultGroup ?? false,
|
||||
friendInfo: friendInfo,
|
||||
model: _selfInfoViewModel),
|
||||
);
|
||||
} else {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SendApplication(
|
||||
lifeCycle: widget.lifeCycle,
|
||||
isShowDefaultGroup: widget.isShowDefaultGroup ?? false,
|
||||
friendInfo: friendInfo,
|
||||
model: _selfInfoViewModel)));
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
child: Row(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: isDesktopScreen ? 38 : 48,
|
||||
height: isDesktopScreen ? 38 : 48,
|
||||
margin: const EdgeInsets.only(right: 16),
|
||||
child: Avatar(faceUrl: faceUrl, showName: showName),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
showName,
|
||||
style: TextStyle(color: theme.darkTextColor, fontSize: isDesktopScreen ? 16 : 18),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
"ID: $userID",
|
||||
style: TextStyle(fontSize: 12, color: theme.weakTextColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _searchResultBuilder(List<V2TimUserFullInfo>? searchResult, TUITheme theme) {
|
||||
final noResult = searchResult == null || searchResult.isEmpty;
|
||||
if (noResult) {
|
||||
return [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 20),
|
||||
child: Center(
|
||||
child: Text(TIM_t("该用户不存在"), style: TextStyle(color: theme.weakTextColor, fontSize: 14)),
|
||||
),
|
||||
)
|
||||
];
|
||||
}
|
||||
return searchResult.map((e) => _searchResultItemBuilder(e, theme)).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_focusNode.addListener(() {
|
||||
final _isFocused = _focusNode.hasFocus;
|
||||
isFocused = _isFocused;
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
searchFriend(String params) async {
|
||||
final response = await _coreServicesImpl.getUsersInfo(userIDList: [params]);
|
||||
if (response.code == 0) {
|
||||
setState(() {
|
||||
searchResult = response.data;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
searchResult = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final TUITheme theme = value.theme;
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider.value(value: _selfInfoViewModel),
|
||||
],
|
||||
builder: (BuildContext context, Widget? w) {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
focusNode: _focusNode,
|
||||
controller: _controller,
|
||||
onChanged: (value) {
|
||||
if (value.trim().isEmpty) {
|
||||
setState(() {
|
||||
showResult = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
textInputAction: TextInputAction.search,
|
||||
onSubmitted: (_) {
|
||||
final searchParams = _controller.text;
|
||||
if (searchParams.trim().isNotEmpty) {
|
||||
searchFriend(searchParams);
|
||||
showResult = true;
|
||||
_focusNode.requestFocus();
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Icon(
|
||||
Icons.search_outlined,
|
||||
color: theme.weakTextColor,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: const BorderSide(
|
||||
width: 0,
|
||||
style: BorderStyle.none,
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hintStyle: TextStyle(
|
||||
color: theme.weakTextColor,
|
||||
),
|
||||
fillColor: theme.inputFillColor,
|
||||
filled: true,
|
||||
hintText: TIM_t("搜索用户 ID")),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (showResult)
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: _searchResultBuilder(searchResult, theme),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/friend_type_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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/life_cycle/add_friend_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.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/base_widgets/tim_ui_kit_base.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
class SendApplication extends StatefulWidget {
|
||||
final V2TimUserFullInfo friendInfo;
|
||||
final TUISelfInfoViewModel model;
|
||||
final bool? isShowDefaultGroup;
|
||||
final AddFriendLifeCycle? lifeCycle;
|
||||
|
||||
const SendApplication(
|
||||
{Key? key, this.lifeCycle, required this.friendInfo, required this.model, this.isShowDefaultGroup = false})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _SendApplicationState();
|
||||
}
|
||||
|
||||
class _SendApplicationState extends TIMUIKitState<SendApplication> {
|
||||
final TextEditingController _verficationController = TextEditingController();
|
||||
final TextEditingController _nickNameController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final showName = widget.model.loginInfo?.nickName ?? widget.model.loginInfo?.userID;
|
||||
_verficationController.text = "我是: $showName";
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final TUITheme theme = value.theme;
|
||||
final FriendshipServices _friendshipServices = serviceLocator<FriendshipServices>();
|
||||
|
||||
final faceUrl = widget.friendInfo.faceUrl ?? "";
|
||||
final userID = widget.friendInfo.userID ?? "";
|
||||
final String showName = ((widget.friendInfo.nickName != null && widget.friendInfo.nickName!.isNotEmpty)
|
||||
? widget.friendInfo.nickName
|
||||
: userID) ??
|
||||
"";
|
||||
final option2 = widget.friendInfo.selfSignature ?? "";
|
||||
|
||||
Widget sendApplicationBody() {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
color: theme.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
margin: const EdgeInsets.only(right: 12),
|
||||
child: Avatar(faceUrl: faceUrl, showName: showName),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
showName,
|
||||
style: TextStyle(color: theme.darkTextColor, fontSize: 18),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
"ID: $userID",
|
||||
style: TextStyle(fontSize: 13, color: theme.weakTextColor),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
if (TencentUtils.checkString(option2) != null)
|
||||
Text(
|
||||
TIM_t_para("个性签名: {{option2}}", "个性签名: $option2")(option2: option2),
|
||||
style: TextStyle(fontSize: 13, color: theme.weakTextColor),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: Text(
|
||||
TIM_t("填写验证信息"),
|
||||
style: TextStyle(fontSize: 16, color: theme.weakTextColor),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 6, bottom: 12),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
color: theme.white,
|
||||
child: TextField(
|
||||
// minLines: 1,
|
||||
maxLines: 4,
|
||||
controller: _verficationController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintStyle: TextStyle(color: theme.textgrey),
|
||||
hintText: '',
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: Text(
|
||||
TIM_t("请填写备注"),
|
||||
style: TextStyle(fontSize: 16, color: theme.weakTextColor),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
color: theme.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
margin: const EdgeInsets.only(top: 6),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
TIM_t("备注"),
|
||||
style: TextStyle(color: theme.darkTextColor, fontSize: 16),
|
||||
),
|
||||
SizedBox(
|
||||
width: 50,
|
||||
child: TextField(
|
||||
controller: _nickNameController,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
hintStyle: TextStyle(
|
||||
color: theme.textgrey,
|
||||
),
|
||||
hintText: '',
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
height: 1,
|
||||
),
|
||||
if (widget.isShowDefaultGroup == true)
|
||||
Container(
|
||||
color: theme.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
TIM_t("分组"),
|
||||
style: TextStyle(color: theme.darkTextColor, fontSize: 16),
|
||||
),
|
||||
Text(
|
||||
TIM_t("我的好友"),
|
||||
style: TextStyle(color: theme.darkTextColor, fontSize: 16),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
color: theme.white,
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.only(top: 10),
|
||||
child: TextButton(
|
||||
onPressed: () async {
|
||||
final remark = _nickNameController.text;
|
||||
final addWording = _verficationController.text;
|
||||
final friendGroup = TIM_t("我的好友");
|
||||
|
||||
if (widget.lifeCycle?.shouldAddFriend != null &&
|
||||
await widget.lifeCycle!.shouldAddFriend(userID, remark, friendGroup, addWording, context) ==
|
||||
false) {
|
||||
return;
|
||||
}
|
||||
|
||||
_friendshipServices.addFriend(
|
||||
userID: userID,
|
||||
addType: FriendTypeEnum.V2TIM_FRIEND_TYPE_BOTH,
|
||||
remark: remark,
|
||||
addWording: addWording,
|
||||
friendGroup: friendGroup);
|
||||
},
|
||||
child: Text(TIM_t("发送"))),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return TUIKitScreenUtils.getDeviceWidget(
|
||||
context: context,
|
||||
desktopWidget: Container(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
color: theme.weakBackgroundColor,
|
||||
child: sendApplicationBody(),
|
||||
),
|
||||
defaultWidget: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
TIM_t("添加好友"),
|
||||
style: TextStyle(color: theme.appbarTextColor, fontSize: 17),
|
||||
),
|
||||
shadowColor: theme.white,
|
||||
backgroundColor: theme.appbarBgColor ?? theme.primaryColor,
|
||||
iconTheme: IconThemeData(
|
||||
color: theme.appbarTextColor,
|
||||
),
|
||||
),
|
||||
body: sendApplicationBody(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/group_type.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_uikit/data_services/core/tim_uikit_wide_modal_operation_key.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/add_group_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_friendship_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/conversation/conversation_services.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/theme/tui_theme.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitAddGroup/tim_uikit_send_application.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
|
||||
|
||||
class TIMUIKitAddGroup extends StatefulWidget {
|
||||
/// The life cycle hooks for adding group business logic
|
||||
final AddGroupLifeCycle? lifeCycle;
|
||||
|
||||
/// Navigate to group chat, if user is already a member of the current group.
|
||||
final Function(String groupID, V2TimConversation conversation) onTapExistGroup;
|
||||
|
||||
final VoidCallback? closeFunc;
|
||||
|
||||
const TIMUIKitAddGroup({Key? key, this.lifeCycle, required this.onTapExistGroup, this.closeFunc}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _TIMUIKitAddGroupState();
|
||||
}
|
||||
|
||||
class _TIMUIKitAddGroupState extends TIMUIKitState<TIMUIKitAddGroup> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
||||
final ConversationService _conversationService = serviceLocator<ConversationService>();
|
||||
final TUIFriendShipViewModel friendShipViewModel = serviceLocator<TUIFriendShipViewModel>();
|
||||
List<V2TimGroupInfo>? _addedGroupList;
|
||||
List<V2TimGroupInfo>? groupResult = [];
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
bool isFocused = false;
|
||||
bool showResult = false;
|
||||
|
||||
String _getGroupType(String type) {
|
||||
String groupType;
|
||||
switch (type) {
|
||||
case GroupType.AVChatRoom:
|
||||
groupType = TIM_t("聊天室");
|
||||
break;
|
||||
case GroupType.Meeting:
|
||||
groupType = TIM_t("会议群");
|
||||
break;
|
||||
case GroupType.Public:
|
||||
groupType = TIM_t("公开群");
|
||||
break;
|
||||
case GroupType.Work:
|
||||
groupType = TIM_t("工作群");
|
||||
break;
|
||||
case GroupType.Community:
|
||||
groupType = TIM_t("社群");
|
||||
break;
|
||||
default:
|
||||
groupType = TIM_t("未知群");
|
||||
break;
|
||||
}
|
||||
return groupType;
|
||||
}
|
||||
|
||||
Widget _searchResultItemBuilder(V2TimGroupInfo groupInfo, TUITheme theme) {
|
||||
final faceUrl = groupInfo.faceUrl ?? "";
|
||||
final groupID = groupInfo.groupID;
|
||||
final showName = groupInfo.groupName ?? groupID;
|
||||
final groupType = _getGroupType(groupInfo.groupType);
|
||||
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
final V2TimConversation? groupConversation = await getGroupConversation(groupID);
|
||||
if (groupConversation != null) {
|
||||
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("您已是群成员"), infoCode: 6660202));
|
||||
if (widget.closeFunc != null) {
|
||||
widget.closeFunc!();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDesktopScreen) {
|
||||
if (widget.closeFunc != null) {
|
||||
widget.closeFunc!();
|
||||
}
|
||||
TUIKitWidePopup.showPopupWindow(
|
||||
operationKey: TUIKitWideModalOperationKey.addGroup,
|
||||
context: context,
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
height: MediaQuery.of(context).size.width * 0.4,
|
||||
title: TIM_t("添加群聊"),
|
||||
child: (closeFuncSendApplication) => SendJoinGroupApplication(
|
||||
lifeCycle: widget.lifeCycle,
|
||||
groupInfo: groupInfo,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SendJoinGroupApplication(
|
||||
lifeCycle: widget.lifeCycle,
|
||||
groupInfo: groupInfo,
|
||||
)));
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
child: Row(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: isDesktopScreen ? 38 : 48,
|
||||
height: isDesktopScreen ? 38 : 48,
|
||||
margin: const EdgeInsets.only(right: 16),
|
||||
child: Avatar(faceUrl: faceUrl, showName: showName),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
showName,
|
||||
style: TextStyle(fontSize: isDesktopScreen ? 16 : 18),
|
||||
),
|
||||
Text(
|
||||
"ID: $groupID",
|
||||
style: TextStyle(fontSize: 12, color: theme.weakTextColor),
|
||||
),
|
||||
Text(
|
||||
"群类型: $groupType",
|
||||
style: TextStyle(fontSize: 12, color: theme.weakTextColor),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _searchResultBuilder(List<V2TimGroupInfo>? searchResult, TUITheme theme) {
|
||||
final noResult = searchResult != null && searchResult.isEmpty;
|
||||
if (noResult) {
|
||||
return [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 20),
|
||||
child: Center(
|
||||
child: Text(TIM_t("该群聊不存在"), style: TextStyle(color: theme.weakTextColor, fontSize: 14)),
|
||||
),
|
||||
)
|
||||
];
|
||||
}
|
||||
return searchResult?.map((e) => _searchResultItemBuilder(e, theme)).toList() ?? [];
|
||||
}
|
||||
|
||||
Future<V2TimConversation?> getGroupConversation(String groupID) async {
|
||||
if (_addedGroupList == null || _addedGroupList!.isEmpty) {
|
||||
_addedGroupList = await _groupServices.getJoinedGroupList();
|
||||
}
|
||||
try {
|
||||
if ((_addedGroupList?.firstWhere((groupItem) {
|
||||
return groupItem.groupID == groupID;
|
||||
})) !=
|
||||
null) {
|
||||
V2TimConversation? conversation;
|
||||
conversation = await _conversationService.getConversationListByConversationId(convID: "group_$groupID");
|
||||
if (conversation == null) {
|
||||
await friendShipViewModel.loadGroupListData();
|
||||
if (friendShipViewModel.groupList.indexWhere((element) => element.groupID == groupID) > -1) {
|
||||
final V2TimGroupInfo groupInfo =
|
||||
friendShipViewModel.groupList.firstWhere((element) => element.groupID == groupID);
|
||||
conversation = V2TimConversation(
|
||||
conversationID: "group_$groupID",
|
||||
type: 2,
|
||||
groupID: groupID,
|
||||
showName: groupInfo.groupName,
|
||||
groupType: groupInfo.groupType,
|
||||
);
|
||||
}
|
||||
}
|
||||
return conversation;
|
||||
}
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_focusNode.addListener(() {
|
||||
final _isFocused = _focusNode.hasFocus;
|
||||
isFocused = _isFocused;
|
||||
setState(() {});
|
||||
});
|
||||
initGroupList();
|
||||
}
|
||||
|
||||
void initGroupList() async {
|
||||
// Get the joined group list in previous
|
||||
_addedGroupList = await _groupServices.getJoinedGroupList();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
searchGroup(String params) async {
|
||||
final res = await _groupServices.getGroupsInfo(groupIDList: [params]);
|
||||
if (res != null) {
|
||||
setState(() {
|
||||
groupResult = res.where((e) => e.resultCode == 0).map((e) => e.groupInfo!).toList();
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
groupResult = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final TUITheme theme = value.theme;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
focusNode: _focusNode,
|
||||
controller: _controller,
|
||||
onChanged: (value) {
|
||||
if (value.trim().isEmpty) {
|
||||
setState(() {
|
||||
showResult = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
textInputAction: TextInputAction.search,
|
||||
onSubmitted: (_) {
|
||||
final searchParams = _controller.text;
|
||||
if (searchParams.trim().isNotEmpty) {
|
||||
searchGroup(searchParams);
|
||||
showResult = true;
|
||||
_focusNode.requestFocus();
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Icon(
|
||||
Icons.search_outlined,
|
||||
color: theme.weakTextColor,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: const BorderSide(
|
||||
width: 0,
|
||||
style: BorderStyle.none,
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
hintStyle: TextStyle(
|
||||
color: theme.weakTextColor,
|
||||
),
|
||||
fillColor: theme.inputFillColor,
|
||||
filled: true,
|
||||
hintText: TIM_t("搜索群ID")),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (showResult)
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: _searchResultBuilder(groupResult, theme),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/group_type.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_callback.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_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/life_cycle/add_group_life_cycle.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.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/widgets/avatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
class SendJoinGroupApplication extends StatefulWidget {
|
||||
final V2TimGroupInfo groupInfo;
|
||||
final AddGroupLifeCycle? lifeCycle;
|
||||
|
||||
const SendJoinGroupApplication({Key? key, required this.groupInfo, this.lifeCycle}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _SendJoinGroupApplicationState();
|
||||
}
|
||||
|
||||
class _SendJoinGroupApplicationState extends TIMUIKitState<SendJoinGroupApplication> {
|
||||
final TextEditingController _verficationController = TextEditingController();
|
||||
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
||||
final CoreServicesImpl _coreServicesImpl = serviceLocator<CoreServicesImpl>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final loginUserInfo = _coreServicesImpl.loginUserInfo;
|
||||
final option1 = loginUserInfo?.nickName ?? loginUserInfo?.userID;
|
||||
_verficationController.text = TIM_t_para("我是: {{option1}}", "我是: $option1")(option1: option1);
|
||||
}
|
||||
|
||||
Future<V2TimCallback?> addGroup(String groupID, String message) async {
|
||||
if (widget.lifeCycle?.shouldAddGroup != null &&
|
||||
await widget.lifeCycle!.shouldAddGroup(groupID, message, context) == false) {
|
||||
return null;
|
||||
}
|
||||
return _groupServices.joinGroup(groupID: groupID, message: message);
|
||||
}
|
||||
|
||||
String _getGroupType(String type) {
|
||||
String groupType;
|
||||
switch (type) {
|
||||
case GroupType.AVChatRoom:
|
||||
groupType = TIM_t("聊天室");
|
||||
break;
|
||||
case GroupType.Meeting:
|
||||
groupType = TIM_t("会议群");
|
||||
break;
|
||||
case GroupType.Public:
|
||||
groupType = TIM_t("公开群");
|
||||
break;
|
||||
case GroupType.Work:
|
||||
groupType = TIM_t("工作群");
|
||||
break;
|
||||
case GroupType.Community:
|
||||
groupType = TIM_t("社群");
|
||||
break;
|
||||
default:
|
||||
groupType = TIM_t("未知群");
|
||||
break;
|
||||
}
|
||||
return groupType;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final TUITheme theme = value.theme;
|
||||
|
||||
final faceUrl = widget.groupInfo.faceUrl ?? "";
|
||||
final groupID = widget.groupInfo.groupID;
|
||||
final showName = widget.groupInfo.groupName ?? groupID;
|
||||
final option1 = _getGroupType(widget.groupInfo.groupType);
|
||||
|
||||
Widget sendGroupApplicationBody() {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
color: theme.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
margin: const EdgeInsets.only(right: 12),
|
||||
child: Avatar(faceUrl: faceUrl, showName: showName),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
showName,
|
||||
style: TextStyle(color: theme.darkTextColor, fontSize: 18),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
"ID: $groupID",
|
||||
style: TextStyle(fontSize: 13, color: theme.weakTextColor),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
TIM_t_para("群类型: {{option1}}", "群类型: $option1")(option1: option1),
|
||||
style: TextStyle(fontSize: 12, color: theme.weakTextColor),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: Text(
|
||||
TIM_t("填写验证信息"),
|
||||
style: TextStyle(fontSize: 16, color: theme.weakTextColor),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 6, bottom: 12),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
color: theme.white,
|
||||
child: TextField(
|
||||
maxLines: 4,
|
||||
controller: _verficationController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintStyle: TextStyle(
|
||||
color: theme.textgrey,
|
||||
),
|
||||
hintText: '')),
|
||||
),
|
||||
Container(
|
||||
color: theme.white,
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.only(top: 10),
|
||||
child: TextButton(
|
||||
onPressed: () async {
|
||||
final addWording = _verficationController.text;
|
||||
final res = await addGroup(groupID, addWording);
|
||||
if (res?.code == 0) {
|
||||
onTIMCallback(TIMCallback(
|
||||
type: TIMCallbackType.INFO, infoRecommendText: TIM_t("群申请已发送"), infoCode: 6660201));
|
||||
}
|
||||
},
|
||||
child: Text(TIM_t("发送"))),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return TUIKitScreenUtils.getDeviceWidget(
|
||||
context: context,
|
||||
desktopWidget: sendGroupApplicationBody(),
|
||||
defaultWidget: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
TIM_t("进群申请"),
|
||||
style: TextStyle(color: theme.appbarTextColor, fontSize: 17),
|
||||
),
|
||||
shadowColor: theme.white,
|
||||
backgroundColor: theme.appbarBgColor ?? theme.primaryColor,
|
||||
iconTheme: IconThemeData(
|
||||
color: theme.appbarTextColor,
|
||||
),
|
||||
),
|
||||
body: sendGroupApplicationBody(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_slidable_plus_plus/flutter_slidable_plus_plus.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_friend_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_friend_info.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/life_cycle/block_list_life_cycle.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/utils/screen_utils.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/color.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme_view_model.dart';
|
||||
|
||||
typedef BlackListItemBuilder = Widget Function(BuildContext context, V2TimFriendInfo friendInfo);
|
||||
|
||||
class TIMUIKitBlackList extends StatefulWidget {
|
||||
final void Function(V2TimFriendInfo friendInfo)? onTapItem;
|
||||
final Widget Function(BuildContext context)? emptyBuilder;
|
||||
final BlackListItemBuilder? itemBuilder;
|
||||
|
||||
/// The life cycle hooks for block list business logic
|
||||
final BlockListLifeCycle? lifeCycle;
|
||||
|
||||
const TIMUIKitBlackList({Key? key, this.onTapItem, this.emptyBuilder, this.itemBuilder, this.lifeCycle})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _TIMUIKitBlackListState();
|
||||
}
|
||||
|
||||
class _TIMUIKitBlackListState extends TIMUIKitState<TIMUIKitBlackList> {
|
||||
final TUIFriendShipViewModel _friendshipViewModel = serviceLocator<TUIFriendShipViewModel>();
|
||||
|
||||
_getShowName(V2TimFriendInfo item) {
|
||||
final friendRemark = item.friendRemark ?? "";
|
||||
final nickName = item.userProfile?.nickName ?? "";
|
||||
final userID = item.userID;
|
||||
final showName = nickName != "" ? nickName : userID;
|
||||
return friendRemark != "" ? friendRemark : showName;
|
||||
}
|
||||
|
||||
Widget _itemBuilder(BuildContext context, V2TimFriendInfo friendInfo) {
|
||||
final theme = Provider.of<TUIThemeViewModel>(context).theme;
|
||||
final showName = _getShowName(friendInfo);
|
||||
final faceUrl = friendInfo.userProfile?.faceUrl ?? "";
|
||||
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
|
||||
Widget itemWidget() {
|
||||
return Material(
|
||||
color: theme.wideBackgroundColor,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (widget.onTapItem != null) {
|
||||
widget.onTapItem!(friendInfo);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(top: 10, left: 16, right: 16),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))),
|
||||
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),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 20),
|
||||
child: Text(
|
||||
showName,
|
||||
style: TextStyle(color: theme.black, fontSize: isDesktopScreen ? 14 : 18),
|
||||
),
|
||||
)),
|
||||
if (isDesktopScreen)
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
_friendshipViewModel.deleteFromBlockList([friendInfo.userID]);
|
||||
},
|
||||
child: Text(
|
||||
TIM_t("移出黑名单"),
|
||||
style: TextStyle(color: theme.primaryColor),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return TUIKitScreenUtils.getDeviceWidget(
|
||||
context: context,
|
||||
desktopWidget: itemWidget(),
|
||||
defaultWidget: Slidable(
|
||||
endActionPane: ActionPane(motion: const DrawerMotion(), children: [
|
||||
SlidableAction(
|
||||
onPressed: (context) async {
|
||||
await _friendshipViewModel.deleteFromBlockList([friendInfo.userID]);
|
||||
},
|
||||
backgroundColor: theme.cautionColor ?? CommonColor.cautionColor,
|
||||
foregroundColor: theme.white,
|
||||
label: TIM_t("删除"),
|
||||
autoClose: true,
|
||||
)
|
||||
]),
|
||||
child: itemWidget(),
|
||||
));
|
||||
}
|
||||
|
||||
BlackListItemBuilder _getItemBuilder() {
|
||||
return widget.itemBuilder ?? _itemBuilder;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider.value(value: _friendshipViewModel),
|
||||
],
|
||||
builder: (BuildContext context, Widget? w) {
|
||||
final model = Provider.of<TUIFriendShipViewModel>(context);
|
||||
model.blockListLifeCycle = widget.lifeCycle;
|
||||
final blockList = model.blockList;
|
||||
if (blockList.isNotEmpty) {
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: blockList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final friendInfo = blockList[index];
|
||||
final itemBuilder = _getItemBuilder();
|
||||
return itemBuilder(context, friendInfo);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (widget.emptyBuilder != null) {
|
||||
return widget.emptyBuilder!(context);
|
||||
}
|
||||
|
||||
return Container();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_tongue_item.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||
|
||||
enum MessageListTongueType {
|
||||
none,
|
||||
toLatest,
|
||||
showUnread,
|
||||
atMe,
|
||||
showPrevious,
|
||||
atAll,
|
||||
}
|
||||
|
||||
typedef TongueItemBuilder = Widget Function(
|
||||
VoidCallback onClick, MessageListTongueType valueType, int unreadCount);
|
||||
|
||||
class TIMUIKitHistoryMessageListTongue extends TIMUIKitStatelessWidget {
|
||||
/// the value type currently
|
||||
final MessageListTongueType valueType;
|
||||
|
||||
/// the callback after clicking
|
||||
final VoidCallback onClick;
|
||||
|
||||
/// unread amount currently
|
||||
final int unreadCount;
|
||||
|
||||
/// the builder for tongue item
|
||||
final TongueItemBuilder? tongueItemBuilder;
|
||||
|
||||
/// total amount of messages at me
|
||||
final String atNum;
|
||||
|
||||
final int previousCount;
|
||||
|
||||
TIMUIKitHistoryMessageListTongue({
|
||||
Key? key,
|
||||
required this.valueType,
|
||||
required this.onClick,
|
||||
required this.previousCount,
|
||||
required this.unreadCount,
|
||||
this.tongueItemBuilder,
|
||||
this.atNum = "",
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
late Widget tongueItem;
|
||||
if (tongueItemBuilder != null) {
|
||||
tongueItem = tongueItemBuilder!(onClick, valueType, unreadCount);
|
||||
} else {
|
||||
tongueItem = TIMUIKitTongueItem(
|
||||
onClick: onClick,
|
||||
unreadCount: unreadCount,
|
||||
valueType: valueType,
|
||||
atNum: atNum,
|
||||
previousCount: previousCount,
|
||||
);
|
||||
}
|
||||
return valueType != MessageListTongueType.none ? tongueItem : Container();
|
||||
}
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:scroll_to_index/scroll_to_index.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_at_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_at_info.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/separate_models/tui_chat_separate_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class TIMUIKitHistoryMessageListTongueContainer extends StatefulWidget {
|
||||
final Widget Function(void Function(), MessageListTongueType, int)? tongueItemBuilder;
|
||||
final List<V2TimGroupAtInfo?>? groupAtInfoList;
|
||||
final Function(String targetSeq) scrollToIndexBySeq;
|
||||
final AutoScrollController scrollController;
|
||||
final TUIChatSeparateViewModel model;
|
||||
final V2TimConversation conversation;
|
||||
|
||||
const TIMUIKitHistoryMessageListTongueContainer({
|
||||
Key? key,
|
||||
this.tongueItemBuilder,
|
||||
this.groupAtInfoList,
|
||||
required this.conversation,
|
||||
required this.scrollToIndexBySeq,
|
||||
required this.scrollController,
|
||||
required this.model,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _TIMUIKitHistoryMessageListTongueContainerState();
|
||||
}
|
||||
|
||||
class _TIMUIKitHistoryMessageListTongueContainerState extends TIMUIKitState<TIMUIKitHistoryMessageListTongueContainer> {
|
||||
bool isFinishJumpToAt = false;
|
||||
List<V2TimGroupAtInfo?>? groupAtInfoList = [];
|
||||
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
|
||||
bool isClickShowPrevious = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initScrollListener();
|
||||
groupAtInfoList = widget.groupAtInfoList?.reversed.toList();
|
||||
}
|
||||
|
||||
void changePositionState(HistoryMessagePosition newPosition) {
|
||||
if (globalModel.getMessageListPosition(widget.model.conversationID) != newPosition) {
|
||||
globalModel.setMessageListPosition(widget.model.conversationID, newPosition);
|
||||
}
|
||||
}
|
||||
|
||||
scrollHandler() {
|
||||
final screenHeight = MediaQuery.of(context).size.height;
|
||||
final offset = widget.scrollController.offset;
|
||||
final conversationUnreadCount = widget.model.getConversationUnreadCount();
|
||||
if (offset <= 0.0 && conversationUnreadCount != 0) {
|
||||
widget.model.showLatestUnread();
|
||||
}
|
||||
if (widget.scrollController.offset <= widget.scrollController.position.minScrollExtent &&
|
||||
!widget.scrollController.position.outOfRange &&
|
||||
!widget.model.haveMoreLatestData) {
|
||||
changePositionState(HistoryMessagePosition.bottom);
|
||||
} else if (widget.scrollController.offset <= screenHeight * 1.6 &&
|
||||
widget.scrollController.offset > 0 &&
|
||||
!widget.scrollController.position.outOfRange &&
|
||||
!widget.model.haveMoreLatestData) {
|
||||
changePositionState(HistoryMessagePosition.inTwoScreen);
|
||||
} else if (widget.scrollController.offset > screenHeight * 1.6 &&
|
||||
!widget.scrollController.position.outOfRange &&
|
||||
!widget.model.haveMoreLatestData) {
|
||||
changePositionState(HistoryMessagePosition.awayTwoScreen);
|
||||
}
|
||||
}
|
||||
|
||||
void initScrollListener() {
|
||||
widget.scrollController.addListener(scrollHandler);
|
||||
}
|
||||
|
||||
MessageListTongueType _getTongueValueType(List<V2TimGroupAtInfo?>? groupAtInfoList) {
|
||||
if (globalModel.getMessageListPosition(widget.model.conversationID) == HistoryMessagePosition.notShowLatest) {
|
||||
return MessageListTongueType.none;
|
||||
}
|
||||
if (groupAtInfoList != null && groupAtInfoList.isNotEmpty && !isFinishJumpToAt) {
|
||||
if (groupAtInfoList[0]!.atType == 1) {
|
||||
return MessageListTongueType.atMe;
|
||||
} else {
|
||||
return MessageListTongueType.atAll;
|
||||
}
|
||||
}
|
||||
|
||||
// if ((widget.conversation.unreadCount ?? 0) > 20 && !isClickShowPrevious) {
|
||||
// return MessageListTongueType.showPrevious;
|
||||
// }
|
||||
|
||||
if (globalModel.unreadCountForTongue > 0) {
|
||||
return MessageListTongueType.showUnread;
|
||||
}
|
||||
|
||||
if (globalModel.getMessageListPosition(widget.model.conversationID) == HistoryMessagePosition.awayTwoScreen) {
|
||||
return MessageListTongueType.toLatest;
|
||||
}
|
||||
|
||||
return MessageListTongueType.none;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
widget.scrollController.removeListener(scrollHandler);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
return Selector<TUIChatGlobalModel, Tuple2<HistoryMessagePosition, int>>(
|
||||
builder: (context, value, child) {
|
||||
return Positioned(
|
||||
bottom: _getTongueValueType(groupAtInfoList) != MessageListTongueType.showPrevious ? 16 : null,
|
||||
top: _getTongueValueType(groupAtInfoList) == MessageListTongueType.showPrevious ? 16 : null,
|
||||
right: 16,
|
||||
child: TIMUIKitHistoryMessageListTongue(
|
||||
previousCount: widget.conversation.unreadCount ?? 0,
|
||||
tongueItemBuilder: widget.tongueItemBuilder,
|
||||
unreadCount: value.item2,
|
||||
onClick: () async {
|
||||
if (groupAtInfoList != null && groupAtInfoList!.isNotEmpty) {
|
||||
if (groupAtInfoList?.length == 1) {
|
||||
widget.scrollToIndexBySeq(groupAtInfoList![0]!.seq);
|
||||
|
||||
setState(() {
|
||||
groupAtInfoList = [];
|
||||
isFinishJumpToAt = true;
|
||||
});
|
||||
} else {
|
||||
widget.scrollToIndexBySeq(groupAtInfoList!.removeAt(0)!.seq);
|
||||
}
|
||||
} else if ((widget.conversation.unreadCount ?? 0) > 20 && !isClickShowPrevious) {
|
||||
try {
|
||||
isClickShowPrevious = true;
|
||||
final String? lastSeqString = widget.conversation.lastMessage?.seq;
|
||||
final int? lastSeq =
|
||||
TencentUtils.checkString(lastSeqString) != null ? int.parse(lastSeqString!) : null;
|
||||
final int? previousCount = widget.conversation.unreadCount;
|
||||
if (lastSeq != null && previousCount != null) {
|
||||
final targetSeq = lastSeq - previousCount;
|
||||
await widget.model.loadListForSpecificMessage(seq: targetSeq);
|
||||
// Future.delayed(const Duration(milliseconds: 100), () {
|
||||
// widget.scrollToIndexBySeq((targetSeq).toString());
|
||||
// });
|
||||
}
|
||||
} catch (e) {
|
||||
// TODO: 这里后续加个弹窗提示客户,找消息失败了
|
||||
}
|
||||
// widget.model.loadListForSpecificMessage(seq: count);
|
||||
} else if (value.item1 == HistoryMessagePosition.awayTwoScreen || value.item2 > 0) {
|
||||
widget.model.showLatestUnread();
|
||||
widget.scrollController.animateTo(
|
||||
widget.scrollController.position.minScrollExtent,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.ease,
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
atNum: groupAtInfoList?.length.toString() ?? "",
|
||||
valueType: _getTongueValueType(groupAtInfoList),
|
||||
),
|
||||
);
|
||||
},
|
||||
selector: (c, model) {
|
||||
final messageListPosition = model.getMessageListPosition(widget.model.conversationID);
|
||||
final unreadCountForConversation = model.unreadCountForTongue;
|
||||
return Tuple2(messageListPosition, unreadCountForConversation);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/color.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
class TIMUIKitTongueItem extends TIMUIKitStatelessWidget {
|
||||
/// the callback after clicking
|
||||
final VoidCallback onClick;
|
||||
|
||||
/// the value type currently
|
||||
final MessageListTongueType valueType;
|
||||
|
||||
/// unread amount currently
|
||||
final int unreadCount;
|
||||
|
||||
/// total amount of messages at me
|
||||
final String atNum;
|
||||
|
||||
final int previousCount;
|
||||
|
||||
TIMUIKitTongueItem({
|
||||
Key? key,
|
||||
required this.onClick,
|
||||
required this.valueType,
|
||||
required this.previousCount,
|
||||
required this.unreadCount,
|
||||
required this.atNum,
|
||||
}) : super(key: key);
|
||||
|
||||
Map<MessageListTongueType, String> textType(BuildContext context) {
|
||||
final option1 = unreadCount.toString();
|
||||
final option2 = atNum.toString();
|
||||
// final option3 = previousCount.toString();
|
||||
final String atMeString = option2 != ""
|
||||
? TIM_t_para("有{{option2}}条@我消息", "有$option2条@我消息")(option2: option2)
|
||||
: TIM_t("有人@我");
|
||||
|
||||
return {
|
||||
// MessageListTongueType.showPrevious:
|
||||
// TIM_t_para("{{option3}}条未读消息", "$option3条未读消息")(option3: option3),
|
||||
MessageListTongueType.toLatest: TIM_t("回到最新位置"),
|
||||
MessageListTongueType.showUnread:
|
||||
TIM_t_para("{{option1}}条新消息", "$option1条新消息")(option1: option1),
|
||||
MessageListTongueType.atMe: atMeString,
|
||||
MessageListTongueType.atAll: TIM_t("@所有人"),
|
||||
};
|
||||
}
|
||||
|
||||
final Map<MessageListTongueType, IconData> iconType = {
|
||||
MessageListTongueType.toLatest: Icons.arrow_downward_outlined,
|
||||
MessageListTongueType.showUnread: Icons.arrow_downward_outlined,
|
||||
MessageListTongueType.atMe: Icons.arrow_upward_outlined,
|
||||
MessageListTongueType.atAll: Icons.arrow_upward_outlined,
|
||||
MessageListTongueType.showPrevious: Icons.arrow_upward_outlined,
|
||||
};
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final TUITheme theme = value.theme;
|
||||
return GestureDetector(
|
||||
onTap: onClick,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(color: hexToColor("E5E5E5"), width: 1),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: theme.weakDividerColor ?? hexToColor("E6E9EB"),
|
||||
offset: const Offset(0.0, 0.0),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.all(10),
|
||||
// width: 112,
|
||||
// height: 37,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(right: 6),
|
||||
child: Icon(
|
||||
iconType[valueType],
|
||||
color: theme.primaryColor,
|
||||
size: 12,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
textType(context)[valueType] ?? "",
|
||||
style: TextStyle(
|
||||
color: theme.primaryColor,
|
||||
fontSize: 12.0,
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+531
@@ -0,0 +1,531 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:loading_animation_widget/loading_animation_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:scroll_to_index/scroll_to_index.dart';
|
||||
import 'package:tencent_chat_i18n_tool/tencent_chat_i18n_tool.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.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_at_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_at_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.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/base_widgets/tim_ui_kit_statelesswidget.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/logger.dart';
|
||||
// ignore: unused_import
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_config.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/keepalive_wrapper.dart';
|
||||
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart';
|
||||
import 'TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue_container.dart';
|
||||
|
||||
enum LoadingPlace {
|
||||
none,
|
||||
top,
|
||||
bottom,
|
||||
}
|
||||
|
||||
enum ScrollType { toIndex, toIndexBegin }
|
||||
|
||||
class TIMUIKitHistoryMessageListController extends ChangeNotifier {
|
||||
AutoScrollController? scrollController = AutoScrollController();
|
||||
late ScrollType scrollType;
|
||||
late V2TimMessage targetMessage;
|
||||
|
||||
TIMUIKitHistoryMessageListController({
|
||||
AutoScrollController? scrollController,
|
||||
}) {
|
||||
if (scrollController != null) {
|
||||
this.scrollController = scrollController;
|
||||
}
|
||||
}
|
||||
|
||||
scrollToIndex(V2TimMessage message) {
|
||||
scrollType = ScrollType.toIndex;
|
||||
targetMessage = message;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
scrollToIndexBegin(V2TimMessage message) {
|
||||
scrollType = ScrollType.toIndexBegin;
|
||||
targetMessage = message;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
class TIMUIKitHistoryMessageList extends StatefulWidget {
|
||||
/// message list
|
||||
final List<V2TimMessage?> messageList;
|
||||
|
||||
/// tongue item builder
|
||||
final TongueItemBuilder? tongueItemBuilder;
|
||||
|
||||
/// group at info, it can get from conversation info
|
||||
final List<V2TimGroupAtInfo?>? groupAtInfoList;
|
||||
|
||||
/// use for build message item
|
||||
final Widget Function(BuildContext, V2TimMessage?)? itemBuilder;
|
||||
|
||||
/// can controll message list scroll
|
||||
final TIMUIKitHistoryMessageListController? controller;
|
||||
|
||||
/// use for message jump, if passed will jump to target message.
|
||||
final V2TimMessage? initFindingMsg;
|
||||
|
||||
/// use for load more message
|
||||
final Future<bool> Function(String?, LoadDirection direction, [int?, int?]) onLoadMore;
|
||||
|
||||
/// configuration for list view
|
||||
final TIMUIKitHistoryMessageListConfig? mainHistoryListConfig;
|
||||
|
||||
final TUIChatSeparateViewModel model;
|
||||
|
||||
final bool isAllowScroll;
|
||||
|
||||
final V2TimConversation conversation;
|
||||
|
||||
const TIMUIKitHistoryMessageList(
|
||||
{Key? key,
|
||||
required this.model,
|
||||
required this.messageList,
|
||||
this.itemBuilder,
|
||||
this.controller,
|
||||
required this.onLoadMore,
|
||||
this.tongueItemBuilder,
|
||||
this.groupAtInfoList,
|
||||
this.initFindingMsg,
|
||||
this.isAllowScroll = true,
|
||||
this.mainHistoryListConfig,
|
||||
required this.conversation})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _TIMUIKitHistoryMessageListState();
|
||||
}
|
||||
|
||||
class _TIMUIKitHistoryMessageListState extends TIMUIKitState<TIMUIKitHistoryMessageList> {
|
||||
V2TimMessage? findingMsg;
|
||||
String findingSeq = "";
|
||||
late TIMUIKitHistoryMessageListController _controller;
|
||||
late AutoScrollController _autoScrollController;
|
||||
LoadingPlace loadingPlace = LoadingPlace.none;
|
||||
bool maybeHaveMoreMessageForFind = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = widget.controller ?? TIMUIKitHistoryMessageListController();
|
||||
_autoScrollController = _controller.scrollController ?? AutoScrollController();
|
||||
_controller.addListener(_controllerListener);
|
||||
initFinding();
|
||||
}
|
||||
|
||||
initFinding() async {
|
||||
if (widget.initFindingMsg != null) {
|
||||
await widget.onLoadMore(null, LoadDirection.previous);
|
||||
setState(() {
|
||||
findingMsg = widget.initFindingMsg!;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_controllerListener() {
|
||||
final scrollType = _controller.scrollType;
|
||||
final targetMessage = _controller.targetMessage;
|
||||
switch (scrollType) {
|
||||
case ScrollType.toIndex:
|
||||
_onScrollToIndex(targetMessage);
|
||||
break;
|
||||
case ScrollType.toIndexBegin:
|
||||
_onScrollToIndexBegin(targetMessage);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
Widget _getMessageItemBuilder(V2TimMessage? messageItem) {
|
||||
if (widget.itemBuilder != null) {
|
||||
return widget.itemBuilder!(context, messageItem);
|
||||
}
|
||||
return Container();
|
||||
}
|
||||
|
||||
_getMessageId(int index) {
|
||||
if (widget.messageList[index]!.elemType == 11) {
|
||||
return _getMessageId(index - 1);
|
||||
}
|
||||
return widget.messageList[index]!.msgID;
|
||||
}
|
||||
|
||||
void showCantFindMsg() {
|
||||
findingMsg = null;
|
||||
findingSeq = "";
|
||||
loadingPlace = LoadingPlace.none;
|
||||
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("无法定位到原消息"), infoCode: 6660401));
|
||||
}
|
||||
|
||||
_onScrollToIndex(V2TimMessage targetMsg) async {
|
||||
// This method called by @ messages or messages been searched, aims to jump to target message
|
||||
loadingPlace = LoadingPlace.top;
|
||||
const int singleLoadAmount = kIsWeb ? 15 : 40;
|
||||
final lastTimestamp = widget.messageList[widget.messageList.length - 1]?.timestamp;
|
||||
final msgList = widget.messageList;
|
||||
final targetTimeStamp = targetMsg.timestamp!;
|
||||
|
||||
if (targetTimeStamp >= lastTimestamp!) {
|
||||
// 当前列表里应该有这个消息,试试能不能直接定位到那去
|
||||
bool isFound = false;
|
||||
int targetIndex = 1;
|
||||
for (int i = msgList.length - 1; i >= 0; i--) {
|
||||
final currentMsg = msgList[i];
|
||||
if (currentMsg?.timestamp == targetTimeStamp &&
|
||||
currentMsg?.elemType != 11 &&
|
||||
currentMsg!.msgID == targetMsg.msgID) {
|
||||
// find the target index by timestamp and msgID
|
||||
isFound = true;
|
||||
targetIndex = -i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFound && targetIndex != 1) {
|
||||
findingMsg = null;
|
||||
_autoScrollController.scrollToIndex(
|
||||
targetIndex,
|
||||
preferPosition: AutoScrollPosition.middle,
|
||||
);
|
||||
|
||||
// execute twice for accurate position, as the position located firstly can be wrong
|
||||
_autoScrollController.scrollToIndex(targetIndex, preferPosition: AutoScrollPosition.middle);
|
||||
_autoScrollController.scrollToIndex(targetIndex, preferPosition: AutoScrollPosition.middle);
|
||||
|
||||
widget.model.jumpMsgID = targetMsg.msgID!;
|
||||
loadingPlace = LoadingPlace.none;
|
||||
} else {
|
||||
showCantFindMsg();
|
||||
}
|
||||
} else {
|
||||
if (maybeHaveMoreMessageForFind) {
|
||||
// if the target message not in current message list, load more
|
||||
findingMsg = targetMsg;
|
||||
final lastMsgId = _getMessageId(widget.messageList.length - 1);
|
||||
maybeHaveMoreMessageForFind = await widget.onLoadMore(lastMsgId, LoadDirection.previous, singleLoadAmount);
|
||||
} else {
|
||||
showCantFindMsg();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onScrollToIndexBySeq(String targetSeq) async {
|
||||
// This method called by tongue request jumping to target @ message
|
||||
loadingPlace = LoadingPlace.top;
|
||||
// const int singleLoadAmount = 40;
|
||||
final msgList = widget.messageList;
|
||||
String lastSeq = "";
|
||||
for (int i = msgList.length - 1; i >= 0; i--) {
|
||||
final currentMsg = msgList[i];
|
||||
if (currentMsg!.seq != null && currentMsg.seq != "") {
|
||||
lastSeq = currentMsg.seq!;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (int.parse(lastSeq) <= int.parse(targetSeq)) {
|
||||
bool isFound = false;
|
||||
int targetIndex = 1;
|
||||
String? targetMsgID = "";
|
||||
for (int i = msgList.length - 1; i >= 0; i--) {
|
||||
final currentMsg = msgList[i];
|
||||
if (currentMsg?.seq == targetSeq) {
|
||||
isFound = true;
|
||||
targetMsgID = currentMsg?.msgID;
|
||||
targetIndex = -i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFound && targetIndex != 1) {
|
||||
findingSeq = "";
|
||||
_autoScrollController.scrollToIndex(
|
||||
targetIndex,
|
||||
preferPosition: AutoScrollPosition.middle,
|
||||
);
|
||||
_autoScrollController.scrollToIndex(targetIndex, preferPosition: AutoScrollPosition.middle);
|
||||
if (targetMsgID != null && targetMsgID != "") {
|
||||
widget.model.jumpMsgID = targetMsgID;
|
||||
}
|
||||
loadingPlace = LoadingPlace.none;
|
||||
} else {
|
||||
showCantFindMsg();
|
||||
}
|
||||
} else {
|
||||
if (maybeHaveMoreMessageForFind) {
|
||||
findingSeq = targetSeq;
|
||||
int requestCount = int.parse(lastSeq) - int.parse(targetSeq);
|
||||
maybeHaveMoreMessageForFind = await widget.onLoadMore(
|
||||
_getMessageId(widget.messageList.length - 1), LoadDirection.previous, requestCount, int.parse(lastSeq));
|
||||
} else {
|
||||
showCantFindMsg();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onScrollToIndexBegin(V2TimMessage targetMsg) {
|
||||
final lastTimestamp = widget.messageList[widget.messageList.length - 1]?.timestamp;
|
||||
final msgList = widget.messageList;
|
||||
final int targetTimeStamp = targetMsg.timestamp!;
|
||||
|
||||
if (targetTimeStamp >= lastTimestamp!) {
|
||||
bool isFound = false;
|
||||
int targetIndex = 1;
|
||||
for (int i = msgList.length - 1; i >= 0; i--) {
|
||||
final currentMsg = msgList[i];
|
||||
if (currentMsg?.timestamp == targetTimeStamp &&
|
||||
currentMsg?.elemType != 11 &&
|
||||
currentMsg!.msgID == targetMsg.msgID) {
|
||||
isFound = true;
|
||||
targetIndex = -i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isFound && targetIndex != 1) {
|
||||
_autoScrollController.scrollToIndex(
|
||||
targetIndex,
|
||||
preferPosition: AutoScrollPosition.end,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<V2TimMessage?> _getReceivedMessageList(int receivedMessageListCount) {
|
||||
if (receivedMessageListCount == 0) {
|
||||
return [];
|
||||
}
|
||||
final haveTimeStampMessage = widget.messageList[receivedMessageListCount]?.elemType == 11;
|
||||
final endPoint = haveTimeStampMessage ? receivedMessageListCount + 1 : receivedMessageListCount;
|
||||
return widget.messageList.sublist(0, endPoint).reversed.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
// center key should generate everytime when build method called.
|
||||
final GlobalKey centerKey = GlobalKey();
|
||||
|
||||
final theme = value.theme;
|
||||
if (widget.messageList.isEmpty) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
final messageList = widget.messageList;
|
||||
final globalModel = context.read<TUIChatGlobalModel>();
|
||||
final receivedNewMessageCount = globalModel.receivedNewMessageCount;
|
||||
final shouldShowUnreadMessage = receivedNewMessageCount > 0;
|
||||
final unreadMessageList = _getReceivedMessageList(receivedNewMessageCount);
|
||||
final readMessageList = messageList.sublist(unreadMessageList.length, messageList.length).toList();
|
||||
|
||||
final throttleFunction = OptimizeUtils.multiThrottle((index, LoadDirection direction) async {
|
||||
final msgID = TIMUIKitChatUtils.getMessageIDWithinIndex(readMessageList, index);
|
||||
await widget.onLoadMore(msgID, direction);
|
||||
}, 20);
|
||||
|
||||
final throttleFunctionWithMsgID = OptimizeUtils.multiThrottle((msgID, LoadDirection direction) async {
|
||||
await widget.onLoadMore(msgID, direction);
|
||||
}, 200);
|
||||
|
||||
if (findingMsg != null) {
|
||||
_onScrollToIndex(findingMsg!);
|
||||
} else if (findingSeq != "") {
|
||||
_onScrollToIndexBySeq(findingSeq);
|
||||
}
|
||||
|
||||
String getMessageIdentifier(V2TimMessage? message, int index) {
|
||||
return "${message?.msgID} - ${message?.timestamp} - ${message?.seq} -${message?.id ?? ""}";
|
||||
}
|
||||
|
||||
return Stack(
|
||||
alignment: Alignment.topCenter,
|
||||
children: [
|
||||
Scrollbar(
|
||||
controller: _autoScrollController,
|
||||
child: CustomScrollView(
|
||||
center: shouldShowUnreadMessage ? centerKey : null,
|
||||
key: widget.mainHistoryListConfig?.key,
|
||||
primary: widget.mainHistoryListConfig?.primary,
|
||||
physics: (widget.isAllowScroll == false)
|
||||
? const NeverScrollableScrollPhysics()
|
||||
: widget.mainHistoryListConfig?.physics,
|
||||
// padding: widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
|
||||
// itemExtent: widget.mainHistoryListConfig?.itemExtent,
|
||||
// prototypeItem: widget.mainHistoryListConfig?.prototypeItem,
|
||||
cacheExtent: widget.mainHistoryListConfig?.cacheExtent ?? 1500,
|
||||
semanticChildCount: widget.mainHistoryListConfig?.semanticChildCount,
|
||||
dragStartBehavior: widget.mainHistoryListConfig?.dragStartBehavior ?? DragStartBehavior.start,
|
||||
keyboardDismissBehavior:
|
||||
widget.mainHistoryListConfig?.keyboardDismissBehavior ?? ScrollViewKeyboardDismissBehavior.manual,
|
||||
restorationId: widget.mainHistoryListConfig?.restorationId,
|
||||
clipBehavior: widget.mainHistoryListConfig?.clipBehavior ?? Clip.hardEdge,
|
||||
reverse: true,
|
||||
shrinkWrap: !shouldShowUnreadMessage,
|
||||
controller: _autoScrollController,
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
final messageItem = unreadMessageList[index];
|
||||
if (index == unreadMessageList.length - 1 && widget.model.haveMoreLatestData == true) {
|
||||
throttleFunctionWithMsgID(messageItem?.msgID ?? "", LoadDirection.latest);
|
||||
}
|
||||
outputLogger.i(
|
||||
"Rendering a unread message: ${getMessageIdentifier(messageItem, 0)}, message Type: ${messageItem?.elemType}");
|
||||
return AutoScrollTag(
|
||||
controller: _autoScrollController,
|
||||
index: -index,
|
||||
key: ValueKey(getMessageIdentifier(messageItem, index)),
|
||||
highlightColor: Colors.black.withOpacity(0.1),
|
||||
child: KeepAliveWrapper(
|
||||
keepAlive: messageItem?.elemType == MessageElemType.V2TIM_ELEM_TYPE_SOUND,
|
||||
child: Container(child: _getMessageItemBuilder(messageItem))),
|
||||
);
|
||||
},
|
||||
childCount: unreadMessageList.length,
|
||||
findChildIndexCallback: (Key key) {
|
||||
final ValueKey<String> valueKey = key as ValueKey<String>;
|
||||
final String data = valueKey.value;
|
||||
final int index =
|
||||
unreadMessageList.indexWhere((element) => getMessageIdentifier(element, 0) == data);
|
||||
return index != -1 ? index : null;
|
||||
})),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.zero,
|
||||
key: centerKey,
|
||||
),
|
||||
SliverPadding(
|
||||
padding: widget.mainHistoryListConfig?.padding ?? EdgeInsets.zero,
|
||||
sliver: Selector<TUIChatSeparateViewModel, bool>(
|
||||
selector: (context, model) {
|
||||
return model.haveMoreData;
|
||||
},
|
||||
shouldRebuild: (previous, next) {
|
||||
return previous != next;
|
||||
},
|
||||
builder: (context, haveMoreData, child) {
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
final messageItem = readMessageList[index];
|
||||
if (index == readMessageList.length - 1) {
|
||||
if (haveMoreData) {
|
||||
final lastMessage = globalModel
|
||||
.messageListMap[TencentUtils.checkString(widget.conversation.groupID) ??
|
||||
widget.conversation.userID ??
|
||||
widget.conversation.conversationID]
|
||||
?.last;
|
||||
if (lastMessage != null) {
|
||||
throttleFunctionWithMsgID(lastMessage.msgID ?? "", LoadDirection.previous);
|
||||
} else {
|
||||
throttleFunction(index, messageList);
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
LoadingAnimationWidget.staggeredDotsWave(
|
||||
color: theme.weakTextColor ?? Colors.grey,
|
||||
size: 28,
|
||||
),
|
||||
AutoScrollTag(
|
||||
controller: _autoScrollController,
|
||||
index: -index,
|
||||
key: ValueKey(getMessageIdentifier(messageItem, index)),
|
||||
highlightColor: Colors.black.withOpacity(0.1),
|
||||
child: KeepAliveWrapper(
|
||||
keepAlive: messageItem?.elemType == MessageElemType.V2TIM_ELEM_TYPE_SOUND,
|
||||
child: Container(child: _getMessageItemBuilder(messageItem))),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
if (index == 0 &&
|
||||
widget.model.haveMoreLatestData == true &&
|
||||
globalModel.receivedNewMessageCount < 10) {
|
||||
throttleFunction(index, LoadDirection.latest);
|
||||
}
|
||||
outputLogger.i(
|
||||
"Rendering a read message: ${getMessageIdentifier(messageItem, 0)}, message Type: ${messageItem?.elemType}");
|
||||
return AutoScrollTag(
|
||||
controller: _autoScrollController,
|
||||
index: -index,
|
||||
key: ValueKey(getMessageIdentifier(messageItem, index)),
|
||||
highlightColor: Colors.black.withOpacity(0.1),
|
||||
child: KeepAliveWrapper(
|
||||
keepAlive: messageItem?.elemType == MessageElemType.V2TIM_ELEM_TYPE_SOUND,
|
||||
child: Container(child: _getMessageItemBuilder(messageItem))),
|
||||
);
|
||||
},
|
||||
childCount: readMessageList.length,
|
||||
findChildIndexCallback: (Key key) {
|
||||
final ValueKey<String> valueKey = key as ValueKey<String>;
|
||||
final String data = valueKey.value;
|
||||
final int index =
|
||||
readMessageList.indexWhere((element) => getMessageIdentifier(element, 0) == data);
|
||||
return index > -1 ? index : null;
|
||||
}));
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
TIMUIKitHistoryMessageListTongueContainer(
|
||||
conversation: widget.conversation,
|
||||
model: widget.model,
|
||||
scrollController: _autoScrollController,
|
||||
scrollToIndexBySeq: _onScrollToIndexBySeq,
|
||||
groupAtInfoList: widget.groupAtInfoList,
|
||||
tongueItemBuilder: widget.tongueItemBuilder,
|
||||
),
|
||||
if (loadingPlace == LoadingPlace.top)
|
||||
Positioned(
|
||||
top: 8,
|
||||
child: LoadingAnimationWidget.staggeredDotsWave(
|
||||
color: theme.weakTextColor ?? Colors.grey,
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TIMUIKitHistoryMessageListSelector extends TIMUIKitStatelessWidget {
|
||||
final Widget Function(BuildContext, List<V2TimMessage?>, Widget?) builder;
|
||||
final String conversationID;
|
||||
|
||||
TIMUIKitHistoryMessageListSelector({Key? key, required this.builder, required this.conversationID}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
return Selector<TUIChatGlobalModel, List<V2TimMessage?>>(
|
||||
builder: builder,
|
||||
shouldRebuild: (previous, next) {
|
||||
final isEquals = const DeepCollectionEquality.unordered().equals(previous, next);
|
||||
return !isEquals;
|
||||
},
|
||||
selector: (context, model) {
|
||||
final messageList = model.getMessageList(conversationID) ?? [];
|
||||
return messageList;
|
||||
});
|
||||
}
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// The definition of the following parameters is the same with [ListView.builder]
|
||||
class TIMUIKitHistoryMessageListConfig {
|
||||
Key? key;
|
||||
|
||||
/// {@template flutter.widgets.scroll_view.scrollDirection}
|
||||
/// The axis along which the scroll view scrolls.
|
||||
///
|
||||
/// Defaults to [Axis.vertical].
|
||||
/// {@endtemplate}
|
||||
Axis? scrollDirection;
|
||||
|
||||
/// {@template flutter.widgets.scroll_view.primary}
|
||||
/// Whether this is the primary scroll view associated with the parent
|
||||
/// [PrimaryScrollController].
|
||||
///
|
||||
/// When this is true, the scroll view is scrollable even if it does not have
|
||||
/// sufficient content to actually scroll. Otherwise, by default the user can
|
||||
/// only scroll the view if it has sufficient content. See [physics].
|
||||
///
|
||||
/// Also when true, the scroll view is used for default [ScrollAction]s. If a
|
||||
/// ScrollAction is not handled by an otherwise focused part of the application,
|
||||
/// the ScrollAction will be evaluated using this scroll view, for example,
|
||||
/// when executing [Shortcuts] key events like page up and down.
|
||||
///
|
||||
/// On iOS, this also identifies the scroll view that will scroll to top in
|
||||
/// response to a tap in the status bar.
|
||||
/// {@endtemplate}
|
||||
///
|
||||
/// Defaults to true when [scrollDirection] is [Axis.vertical] and
|
||||
/// [controller] is null.
|
||||
bool? primary;
|
||||
|
||||
/// {@template flutter.widgets.scroll_view.physics}
|
||||
/// How the scroll view should respond to user input.
|
||||
///
|
||||
/// For example, determines how the scroll view continues to animate after the
|
||||
/// user stops dragging the scroll view.
|
||||
///
|
||||
/// Defaults to matching platform conventions. Furthermore, if [primary] is
|
||||
/// false, then the user cannot scroll if there is insufficient content to
|
||||
/// scroll, while if [primary] is true, they can always attempt to scroll.
|
||||
///
|
||||
/// To force the scroll view to always be scrollable even if there is
|
||||
/// insufficient content, as if [primary] was true but without necessarily
|
||||
/// setting it to true, provide an [AlwaysScrollableScrollPhysics] physics
|
||||
/// object, as in:
|
||||
///
|
||||
/// ```dart
|
||||
/// physics: const AlwaysScrollableScrollPhysics(),
|
||||
/// ```
|
||||
///
|
||||
/// To force the scroll view to use the default platform conventions and not
|
||||
/// be scrollable if there is insufficient content, regardless of the value of
|
||||
/// [primary], provide an explicit [ScrollPhysics] object, as in:
|
||||
///
|
||||
/// ```dart
|
||||
/// physics: const ScrollPhysics(),
|
||||
/// ```
|
||||
///
|
||||
/// The physics can be changed dynamically (by providing a new object in a
|
||||
/// subsequent build), but new physics will only take effect if the _class_ of
|
||||
/// the provided object changes. Merely constructing a new instance with a
|
||||
/// different configuration is insufficient to cause the physics to be
|
||||
/// reapplied. (This is because the final object used is generated
|
||||
/// dynamically, which can be relatively expensive, and it would be
|
||||
/// inefficient to speculatively create this object each frame to see if the
|
||||
/// physics should be updated.)
|
||||
/// {@endtemplate}
|
||||
///
|
||||
/// If an explicit [ScrollBehavior] is provided to [scrollBehavior], the
|
||||
/// [ScrollPhysics] provided by that behavior will take precedence after
|
||||
/// [physics].
|
||||
ScrollPhysics? physics;
|
||||
|
||||
/// The amount of space by which to inset the children.
|
||||
EdgeInsetsGeometry? padding;
|
||||
|
||||
/// {@template flutter.widgets.list_view.itemExtent}
|
||||
/// If non-null, forces the children to have the given extent in the scroll
|
||||
/// direction.
|
||||
///
|
||||
/// Specifying an [itemExtent] is more efficient than letting the children
|
||||
/// determine their own extent because the scrolling machinery can make use of
|
||||
/// the foreknowledge of the children's extent to save work, for example when
|
||||
/// the scroll position changes drastically.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SliverFixedExtentList], the sliver used internally when this property
|
||||
/// is provided. It constrains its box children to have a specific given
|
||||
/// extent along the main axis.
|
||||
/// * The [prototypeItem] property, which allows forcing the children's
|
||||
/// extent to be the same as the given widget.
|
||||
/// {@endtemplate}
|
||||
double? itemExtent;
|
||||
|
||||
/// {@template flutter.widgets.list_view.prototypeItem}
|
||||
/// If non-null, forces the children to have the same extent as the given
|
||||
/// widget in the scroll direction.
|
||||
///
|
||||
/// Specifying an [prototypeItem] is more efficient than letting the children
|
||||
/// determine their own extent because the scrolling machinery can make use of
|
||||
/// the foreknowledge of the children's extent to save work, for example when
|
||||
/// the scroll position changes drastically.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SliverPrototypeExtentList], the sliver used internally when this
|
||||
/// property is provided. It constrains its box children to have the same
|
||||
/// extent as a prototype item along the main axis.
|
||||
/// * The [itemExtent] property, which allows forcing the children's extent
|
||||
/// to a given value.
|
||||
/// {@endtemplate}
|
||||
Widget? prototypeItem;
|
||||
|
||||
/// {@macro flutter.rendering.RenderViewportBase.cacheExtent}
|
||||
double? cacheExtent;
|
||||
|
||||
/// The number of children that will contribute semantic information.
|
||||
///
|
||||
/// Some subtypes of [ScrollView] can infer this value automatically. For
|
||||
/// example [ListView] will use the number of widgets in the child list,
|
||||
/// while the [ListView.separated] constructor will use half that amount.
|
||||
///
|
||||
/// For [CustomScrollView] and other types which do not receive a builder
|
||||
/// or list of widgets, the child count must be explicitly provided. If the
|
||||
/// number is unknown or unbounded this should be left unset or set to null.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SemanticsConfiguration.scrollChildCount], the corresponding semantics property.
|
||||
int? semanticChildCount;
|
||||
|
||||
/// {@macro flutter.widgets.scrollable.dragStartBehavior}
|
||||
DragStartBehavior? dragStartBehavior;
|
||||
|
||||
/// {@template flutter.widgets.scroll_view.keyboardDismissBehavior}
|
||||
/// [ScrollViewKeyboardDismissBehavior] the defines how this [ScrollView] will
|
||||
/// dismiss the keyboard automatically.
|
||||
/// {@endtemplate}
|
||||
ScrollViewKeyboardDismissBehavior? keyboardDismissBehavior;
|
||||
|
||||
/// {@macro flutter.widgets.scrollable.restorationId}
|
||||
String? restorationId;
|
||||
|
||||
/// {@macro flutter.material.Material.clipBehavior}
|
||||
///
|
||||
/// Defaults to [Clip.hardEdge].
|
||||
Clip? clipBehavior;
|
||||
|
||||
bool? shrinkWrap;
|
||||
|
||||
TIMUIKitHistoryMessageListConfig(
|
||||
{this.key,
|
||||
this.scrollDirection,
|
||||
this.primary,
|
||||
this.physics,
|
||||
this.padding,
|
||||
this.itemExtent,
|
||||
this.prototypeItem,
|
||||
this.cacheExtent,
|
||||
this.semanticChildCount,
|
||||
this.dragStartBehavior,
|
||||
this.keyboardDismissBehavior,
|
||||
this.restorationId,
|
||||
this.shrinkWrap,
|
||||
this.clipBehavior});
|
||||
}
|
||||
+1460
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,620 @@
|
||||
// ignore_for_file: non_constant_identifier_names, avoid_print
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:open_file/open_file.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_member_role.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/message_elem_type.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/message_status.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/tim_uikit_message_reaction_select_emoji.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/separate_models/tui_chat_separate_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/forward_message_screen.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/color.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
class TIMUIKitMessageTooltip extends StatefulWidget {
|
||||
/// tool tips panel configuration, long press message will show tool tips panel
|
||||
final ToolTipsConfig? toolTipsConfig;
|
||||
|
||||
/// current message
|
||||
final V2TimMessage message;
|
||||
|
||||
/// allow notifi user when send reply message
|
||||
final bool allowAtUserWhenReply;
|
||||
|
||||
/// the callback for long press event, except myself avatar
|
||||
final Function(String? userId, String? nickName)? onLongPressForOthersHeadPortrait;
|
||||
|
||||
final bool isUseMessageReaction;
|
||||
|
||||
/// direction
|
||||
final SelectEmojiPanelPosition selectEmojiPanelPosition;
|
||||
|
||||
/// on add sticker reaction to a message
|
||||
final ValueChanged<int> onSelectSticker;
|
||||
|
||||
/// on close tooltip area
|
||||
final VoidCallback onCloseTooltip;
|
||||
|
||||
final TUIChatSeparateViewModel model;
|
||||
|
||||
final bool isShowMoreSticker;
|
||||
|
||||
final V2TimGroupMemberFullInfo? groupMemberInfo;
|
||||
|
||||
final bool iSUseDefaultHoverBar;
|
||||
|
||||
const TIMUIKitMessageTooltip(
|
||||
{Key? key,
|
||||
this.toolTipsConfig,
|
||||
this.isUseMessageReaction = true,
|
||||
required this.model,
|
||||
required this.message,
|
||||
required this.allowAtUserWhenReply,
|
||||
this.onLongPressForOthersHeadPortrait,
|
||||
required this.selectEmojiPanelPosition,
|
||||
required this.onCloseTooltip,
|
||||
required this.onSelectSticker,
|
||||
this.isShowMoreSticker = false,
|
||||
this.groupMemberInfo,
|
||||
required this.iSUseDefaultHoverBar})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => TIMUIKitMessageTooltipState();
|
||||
}
|
||||
|
||||
class TIMUIKitMessageTooltipState extends TIMUIKitState<TIMUIKitMessageTooltip> {
|
||||
final TUIChatGlobalModel globalModal = serviceLocator<TUIChatGlobalModel>();
|
||||
final TUISelfInfoViewModel selfInfoViewModel = serviceLocator<TUISelfInfoViewModel>();
|
||||
bool isShowMoreSticker = false;
|
||||
bool fileBeenDownloaded = false;
|
||||
String filePath = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
hasFile();
|
||||
isShowMoreSticker = widget.isShowMoreSticker;
|
||||
}
|
||||
|
||||
hasFile() {
|
||||
if (PlatformUtils().isMobile ||
|
||||
(widget.message.fileElem == null && widget.message.imageElem == null && widget.message.videoElem == null)) {
|
||||
fileBeenDownloaded = false;
|
||||
return;
|
||||
}
|
||||
if (PlatformUtils().isWeb) {
|
||||
fileBeenDownloaded = true;
|
||||
return;
|
||||
}
|
||||
if (PlatformUtils().isDesktop) {
|
||||
if (widget.message.fileElem != null) {
|
||||
String savePath = TencentUtils.checkString(globalModal.getFileMessageLocation(widget.message.msgID)) ??
|
||||
TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
|
||||
widget.message.fileElem?.path ??
|
||||
"";
|
||||
File f = File(savePath);
|
||||
if (f.existsSync() && widget.message.msgID != null) {
|
||||
filePath = savePath;
|
||||
fileBeenDownloaded = true;
|
||||
return;
|
||||
}
|
||||
} else if (widget.message.imageElem != null) {
|
||||
if (TencentUtils.checkString(widget.message.imageElem!.imageList![0]!.localUrl) != null &&
|
||||
File(widget.message.imageElem!.imageList![0]!.localUrl!).existsSync()) {
|
||||
fileBeenDownloaded = true;
|
||||
return;
|
||||
}
|
||||
} else if (widget.message.videoElem != null) {
|
||||
if (TencentUtils.checkString(widget.message.videoElem!.localVideoUrl) != null &&
|
||||
File(widget.message.videoElem!.localVideoUrl!).existsSync()) {
|
||||
fileBeenDownloaded = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
fileBeenDownloaded = false;
|
||||
}
|
||||
|
||||
bool isRevocable(int timestamp, int upperTimeLimit) =>
|
||||
((DateTime.now().millisecondsSinceEpoch / 1000).ceil() - timestamp < upperTimeLimit) &&
|
||||
(widget.message.isSelf ?? true);
|
||||
|
||||
Widget ItemInkWell({
|
||||
Widget? child,
|
||||
GestureTapCallback? onTap,
|
||||
}) {
|
||||
return SizedBox(
|
||||
width: 44,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
splashColor: Colors.white,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(bottom: 6, top: 6),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool isAdminCanRecall() {
|
||||
if (widget.model.chatConfig.isGroupAdminRecallEnabled) {
|
||||
final selfMemberInfo = widget.groupMemberInfo ?? widget.model.selfMemberInfo;
|
||||
final selfRole = selfMemberInfo?.role;
|
||||
return selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_ADMIN ||
|
||||
selfRole == GroupMemberRoleType.V2TIM_GROUP_MEMBER_ROLE_OWNER;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_buildLongPressTipItem(TUITheme theme, TUIChatSeparateViewModel model, V2TimMessage message) {
|
||||
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
final isCanRevokeSelf = isRevocable(widget.message.timestamp!, model.chatConfig.upperRecallTime);
|
||||
final shouldShowRevokeAction =
|
||||
(isCanRevokeSelf || isAdminCanRecall()) && widget.message.status != MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL;
|
||||
final shouldShowReplyAction =
|
||||
!(widget.message.customElem?.data != null && MessageUtils.isCallingData(widget.message.customElem!.data!));
|
||||
final shouldShowForwardAction =
|
||||
!(widget.message.customElem?.data != null && MessageUtils.isCallingData(widget.message.customElem!.data!));
|
||||
final tooltipsConfig = widget.toolTipsConfig;
|
||||
final messageCanCopy = widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT ||
|
||||
(isDesktopScreen && widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_IMAGE && fileBeenDownloaded);
|
||||
bool showTranslation = true;
|
||||
if (widget.message.localCustomData != null) {
|
||||
final LocalCustomDataModel localCustomData =
|
||||
LocalCustomDataModel.fromMap(json.decode(TencentUtils.checkString(widget.message.localCustomData) ?? "{}"));
|
||||
if (localCustomData.translatedText != null && localCustomData.translatedText != "") {
|
||||
showTranslation = false;
|
||||
}
|
||||
}
|
||||
|
||||
final dynamicQuote = model.chatConfig.isAtWhenReplyDynamic?.call(widget.message);
|
||||
|
||||
final List<MessageToolTipItem> defaultTipsList = [
|
||||
if (fileBeenDownloaded)
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("打开"),
|
||||
id: "open",
|
||||
iconImageAsset: "images/open_in_new.png",
|
||||
onClick: () => _onTap("open", model)),
|
||||
if (fileBeenDownloaded && PlatformUtils().isDesktop)
|
||||
MessageToolTipItem(
|
||||
label: PlatformUtils().isMacOS ? TIM_t("在访达中打开") : TIM_t("查看文件夹"),
|
||||
id: "finder",
|
||||
iconImageAsset: "images/folder_open.png",
|
||||
onClick: () => _onTap("finder", model)),
|
||||
if (messageCanCopy)
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("复制"),
|
||||
id: "copyMessage",
|
||||
iconImageAsset: "images/copy_message.png",
|
||||
onClick: () => _onTap("copyMessage", model)),
|
||||
if (shouldShowForwardAction && !model.isVoteMessage(widget.message))
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("转发"),
|
||||
id: "forwardMessage",
|
||||
iconImageAsset: "images/forward_message.png",
|
||||
onClick: () => _onTap("forwardMessage", model)),
|
||||
if (shouldShowReplyAction)
|
||||
MessageToolTipItem(
|
||||
label: TIM_t((dynamicQuote ?? model.chatConfig.isAtWhenReply) ? "回复" : "引用"),
|
||||
id: "replyMessage",
|
||||
iconImageAsset: "images/reply_message.png",
|
||||
onClick: () => _onTap("replyMessage", model)),
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("多选"),
|
||||
id: "multiSelect",
|
||||
iconImageAsset: "images/multi_message.png",
|
||||
onClick: () => _onTap("multiSelect", model)),
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("删除"),
|
||||
id: "delete",
|
||||
iconImageAsset: "images/delete_message.png",
|
||||
onClick: () => _onTap("delete", model)),
|
||||
if (showTranslation)
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("翻译"),
|
||||
id: "translate",
|
||||
iconImageAsset: "images/translate.png",
|
||||
onClick: () => _onTap("translate", model)),
|
||||
if (shouldShowRevokeAction)
|
||||
MessageToolTipItem(
|
||||
label: TIM_t("撤回"),
|
||||
id: "revoke",
|
||||
iconImageAsset: "images/revoke_message.png",
|
||||
onClick: () => _onTap("revoke", model)),
|
||||
];
|
||||
final defaultTipsIds = defaultTipsList.map((e) => e.id);
|
||||
List<MessageToolTipItem> defaultFormattedTipsList = defaultTipsList;
|
||||
if (tooltipsConfig != null) {
|
||||
defaultFormattedTipsList = defaultTipsList.where((element) {
|
||||
final type = element.id;
|
||||
if (type == "copyMessage") {
|
||||
return tooltipsConfig.showCopyMessage;
|
||||
}
|
||||
if (type == "forwardMessage") {
|
||||
return tooltipsConfig.showForwardMessage && !(isDesktopScreen && widget.iSUseDefaultHoverBar);
|
||||
}
|
||||
if (type == "replyMessage") {
|
||||
return tooltipsConfig.showReplyMessage && !(isDesktopScreen && widget.iSUseDefaultHoverBar);
|
||||
}
|
||||
if (type == "delete") {
|
||||
return (!PlatformUtils().isWeb) && tooltipsConfig.showDeleteMessage;
|
||||
}
|
||||
if (type == "multiSelect") {
|
||||
return tooltipsConfig.showMultipleChoiceMessage;
|
||||
}
|
||||
|
||||
if (type == "revoke") {
|
||||
return tooltipsConfig.showRecallMessage;
|
||||
}
|
||||
if (type == "translate") {
|
||||
return tooltipsConfig.showTranslation && widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT;
|
||||
}
|
||||
return true;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
final List<MessageToolTipItem>? customList = widget.toolTipsConfig?.additionalMessageToolTips != null
|
||||
? (widget.toolTipsConfig?.additionalMessageToolTips!(message, widget.onCloseTooltip))
|
||||
: [];
|
||||
|
||||
List<MessageToolTipItem> formattedTipsList = [
|
||||
...defaultFormattedTipsList,
|
||||
...?customList,
|
||||
];
|
||||
|
||||
List<dynamic> widgetList = [];
|
||||
if (isDesktopScreen) {
|
||||
widgetList = formattedTipsList
|
||||
.map(
|
||||
(item) => Material(
|
||||
color: Colors.white,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
item.onClick();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Image.asset(
|
||||
item.iconImageAsset,
|
||||
package: defaultTipsIds.contains(item.id) ? 'tencent_cloud_chat_uikit' : null,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
width: 8,
|
||||
),
|
||||
Text(
|
||||
item.label,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.none,
|
||||
color: theme.darkTextColor,
|
||||
fontSize: 12,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
} else {
|
||||
widgetList = formattedTipsList
|
||||
.map(
|
||||
(item) => Material(
|
||||
color: Colors.white,
|
||||
child: ItemInkWell(
|
||||
onTap: () {
|
||||
item.onClick();
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(
|
||||
item.iconImageAsset,
|
||||
package: defaultTipsIds.contains(item.id) ? 'tencent_cloud_chat_uikit' : null,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
width: 60,
|
||||
),
|
||||
Text(
|
||||
item.label,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.none,
|
||||
color: theme.darkTextColor,
|
||||
fontSize: 10,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
if (widgetList.isEmpty && widget.isUseMessageReaction == false) {
|
||||
widget.onCloseTooltip();
|
||||
}
|
||||
|
||||
return widgetList;
|
||||
}
|
||||
|
||||
_onOpenDesktop(String path) {
|
||||
try {
|
||||
if (PlatformUtils().isDesktop && !PlatformUtils().isWindows) {
|
||||
launchUrl(Uri.file(path));
|
||||
} else {
|
||||
OpenFile.open(path);
|
||||
}
|
||||
// ignore: empty_catches
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
_onTap(String operation, TUIChatSeparateViewModel model) async {
|
||||
final messageItem = widget.message;
|
||||
final msgID = messageItem.msgID as String;
|
||||
switch (operation) {
|
||||
case "open":
|
||||
if (widget.message.fileElem != null) {
|
||||
_onOpenDesktop(TencentUtils.checkString(globalModal.getFileMessageLocation(widget.message.msgID)) ??
|
||||
TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
|
||||
widget.message.fileElem?.path ??
|
||||
"");
|
||||
} else if (widget.message.imageElem != null) {
|
||||
_onOpenDesktop(TencentUtils.checkString(widget.message.imageElem!.imageList?[0]?.localUrl) ??
|
||||
TencentUtils.checkString(widget.message.imageElem?.path) ??
|
||||
"");
|
||||
} else if (widget.message.videoElem != null) {
|
||||
_onOpenDesktop(TencentUtils.checkString(widget.message.videoElem!.localVideoUrl) ??
|
||||
TencentUtils.checkString(widget.message.videoElem?.videoPath) ??
|
||||
"");
|
||||
}
|
||||
break;
|
||||
case "finder":
|
||||
String savePath = "";
|
||||
if (widget.message.fileElem != null) {
|
||||
savePath = (TencentUtils.checkString(globalModal.getFileMessageLocation(widget.message.msgID)) ??
|
||||
TencentUtils.checkString(widget.message.fileElem!.localUrl) ??
|
||||
widget.message.fileElem?.path ??
|
||||
"");
|
||||
} else if (widget.message.imageElem != null) {
|
||||
savePath = (TencentUtils.checkString(widget.message.imageElem!.imageList?[0]?.localUrl) ??
|
||||
TencentUtils.checkString(widget.message.imageElem?.path) ??
|
||||
"");
|
||||
} else if (widget.message.videoElem != null) {
|
||||
savePath = (TencentUtils.checkString(widget.message.videoElem!.localVideoUrl) ??
|
||||
TencentUtils.checkString(widget.message.videoElem?.videoPath) ??
|
||||
"");
|
||||
}
|
||||
final String fileDir = path.dirname(savePath);
|
||||
_onOpenDesktop(fileDir);
|
||||
break;
|
||||
case "delete":
|
||||
model.deleteMsg(msgID, webMessageInstance: messageItem.messageFromWeb);
|
||||
break;
|
||||
case "revoke":
|
||||
model.revokeMsg(msgID, !isRevocable(widget.message.timestamp!, model.chatConfig.upperRecallTime),
|
||||
messageItem.messageFromWeb);
|
||||
break;
|
||||
case 'translate':
|
||||
model.translateText(widget.message);
|
||||
break;
|
||||
case "multiSelect":
|
||||
model.updateMultiSelectStatus(true);
|
||||
model.setMessageItemChecked(widget.message, true);
|
||||
break;
|
||||
case "forwardMessage":
|
||||
model.setMessageItemChecked(widget.message, true);
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ForwardMessageScreen(
|
||||
conversationType: ConvType.c2c,
|
||||
model: model,
|
||||
)));
|
||||
break;
|
||||
case "copyMessage":
|
||||
if (widget.message.elemType == MessageElemType.V2TIM_ELEM_TYPE_TEXT) {
|
||||
try {
|
||||
await Clipboard.setData(ClipboardData(text: widget.message.textElem?.text ?? ""));
|
||||
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("已复制"), infoCode: 6660408));
|
||||
// ignore: empty_catches
|
||||
} catch (e) {}
|
||||
}
|
||||
break;
|
||||
case "replyMessage":
|
||||
model.repliedMessage = widget.message;
|
||||
final dynamicQuote = model.chatConfig.isAtWhenReplyDynamic?.call(widget.message);
|
||||
final isSelf = widget.message.isSelf ?? true;
|
||||
final isGroup = TencentUtils.checkString(widget.message.groupID) != null;
|
||||
final isAtWhenReply = !isSelf &&
|
||||
isGroup &&
|
||||
(dynamicQuote ?? widget.allowAtUserWhenReply) &&
|
||||
widget.onLongPressForOthersHeadPortrait != null;
|
||||
|
||||
/// If replying to a self message, do not add a at tag, only requestFocus.
|
||||
widget.onLongPressForOthersHeadPortrait!(
|
||||
!isAtWhenReply ? null : widget.message.sender, !isAtWhenReply ? null : widget.message.nickName);
|
||||
break;
|
||||
default:
|
||||
onTIMCallback(TIMCallback(type: TIMCallbackType.INFO, infoRecommendText: TIM_t("暂未实现"), infoCode: 6660409));
|
||||
}
|
||||
widget.onCloseTooltip();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final TUITheme theme = value.theme;
|
||||
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider.value(value: widget.model),
|
||||
],
|
||||
builder: (BuildContext context, Widget? w) {
|
||||
final TUIChatSeparateViewModel model = Provider.of<TUIChatSeparateViewModel>(context);
|
||||
final bool haveExtraTipsConfig =
|
||||
widget.toolTipsConfig != null && widget.toolTipsConfig?.additionalItemBuilder != null;
|
||||
Widget? extraTipsActionItem = haveExtraTipsConfig
|
||||
? widget.toolTipsConfig!.additionalItemBuilder!(widget.message, widget.onCloseTooltip, null, context)
|
||||
: null;
|
||||
final message = widget.message;
|
||||
return Container(
|
||||
decoration: isDesktopScreen
|
||||
? BoxDecoration(
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0xCCbebebe),
|
||||
offset: Offset(2, 2),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 1,
|
||||
),
|
||||
],
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: hexToColor("dee0e3"),
|
||||
),
|
||||
color: Colors.white,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(10)),
|
||||
)
|
||||
: null,
|
||||
color: isDesktopScreen ? null : Colors.white,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: isDesktopScreen ? 8 : 4),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: min(MediaQuery.of(context).size.width * 0.75, 350),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if ((!isDesktopScreen || widget.isShowMoreSticker) &&
|
||||
widget.isUseMessageReaction &&
|
||||
widget.selectEmojiPanelPosition == SelectEmojiPanelPosition.up)
|
||||
TIMUIKitMessageReactionEmojiSelectPanel(
|
||||
isShowMoreSticker: isShowMoreSticker,
|
||||
onSelect: (int value) => widget.onSelectSticker(value),
|
||||
onClickShowMore: (bool value) {
|
||||
setState(() {
|
||||
isShowMoreSticker = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
if (!isDesktopScreen &&
|
||||
widget.isUseMessageReaction &&
|
||||
widget.selectEmojiPanelPosition == SelectEmojiPanelPosition.up &&
|
||||
isShowMoreSticker == false)
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 6),
|
||||
child: const Divider(
|
||||
thickness: 1,
|
||||
indent: 0,
|
||||
// endIndent: 10,
|
||||
color: Colors.black12)),
|
||||
if (isShowMoreSticker == false)
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (!isDesktopScreen && widget.isUseMessageReaction)
|
||||
Expanded(
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
alignment: TUIKitScreenUtils.getFormFactor(context) == DeviceType.Mobile
|
||||
? WrapAlignment.start
|
||||
: WrapAlignment.start,
|
||||
spacing: 12,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
..._buildLongPressTipItem(theme, model, message),
|
||||
if (extraTipsActionItem != null) extraTipsActionItem
|
||||
],
|
||||
)),
|
||||
if (!isDesktopScreen && !widget.isUseMessageReaction)
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: min(MediaQuery.of(context).size.width * 0.75, 350),
|
||||
),
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
alignment: TUIKitScreenUtils.getFormFactor(context) == DeviceType.Mobile
|
||||
? WrapAlignment.spaceBetween
|
||||
: WrapAlignment.start,
|
||||
spacing: 4,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
..._buildLongPressTipItem(theme, model, message),
|
||||
if (extraTipsActionItem != null) extraTipsActionItem
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isDesktopScreen)
|
||||
Table(columnWidths: const <int, TableColumnWidth>{
|
||||
0: IntrinsicColumnWidth(),
|
||||
}, children: <TableRow>[
|
||||
..._buildLongPressTipItem(theme, model, message).map((e) => TableRow(children: <Widget>[e]))
|
||||
])
|
||||
],
|
||||
),
|
||||
if (!isDesktopScreen &&
|
||||
widget.isUseMessageReaction &&
|
||||
widget.selectEmojiPanelPosition == SelectEmojiPanelPosition.down &&
|
||||
isShowMoreSticker == false)
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 6),
|
||||
child: const Divider(
|
||||
thickness: 1,
|
||||
indent: 0,
|
||||
// endIndent: 10,
|
||||
color: Colors.black12)),
|
||||
if ((!isDesktopScreen || widget.isShowMoreSticker) &&
|
||||
widget.isUseMessageReaction &&
|
||||
widget.selectEmojiPanelPosition == SelectEmojiPanelPosition.down)
|
||||
TIMUIKitMessageReactionEmojiSelectPanel(
|
||||
isShowMoreSticker: isShowMoreSticker,
|
||||
onSelect: (int value) => widget.onSelectSticker(value),
|
||||
onClickShowMore: (bool value) {
|
||||
setState(() {
|
||||
isShowMoreSticker = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
// ignore_for_file: deprecated_member_use_from_same_package
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:scroll_to_index/scroll_to_index.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_at_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_at_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field_controller.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/TIMUIKitTongue/tim_uikit_chat_history_message_list_tongue.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_config.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list_item.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_chat_history_message_list.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_chat_config.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||
import 'package:tim_ui_kit_sticker_plugin/utils/tim_custom_face_data.dart';
|
||||
|
||||
enum LoadingPlace {
|
||||
none,
|
||||
top,
|
||||
bottom,
|
||||
}
|
||||
|
||||
class TIMUIKitHistoryMessageListContainer extends StatefulWidget {
|
||||
final Widget Function(BuildContext, V2TimMessage?)? itemBuilder;
|
||||
final AutoScrollController? scrollController;
|
||||
final String conversationID;
|
||||
final Function(String? userId, String? nickName)? onLongPressForOthersHeadPortrait;
|
||||
final List<V2TimGroupAtInfo?>? groupAtInfoList;
|
||||
final V2TimMessage? initFindingMsg;
|
||||
|
||||
/// message item builder, works for customize all message types and row layout.
|
||||
final MessageItemBuilder? messageItemBuilder;
|
||||
|
||||
/// The controller for text field.
|
||||
final TIMUIKitInputTextFieldController? textFieldController;
|
||||
|
||||
/// the builder for avatar
|
||||
final Widget Function(BuildContext context, V2TimMessage message)? userAvatarBuilder;
|
||||
|
||||
/// the builder for tongue
|
||||
final TongueItemBuilder? tongueItemBuilder;
|
||||
|
||||
final Widget? Function(V2TimMessage message, Function() closeTooltip, [Key? key, BuildContext? context])?
|
||||
extraTipsActionItemBuilder;
|
||||
|
||||
/// conversation type
|
||||
final ConvType conversationType;
|
||||
|
||||
/// Avatar and name in message reaction tap callback.
|
||||
final void Function(String userID, TapDownDetails tapDetails)? onTapAvatar;
|
||||
|
||||
/// Avatar and name in message reaction secondary tap callback.
|
||||
final void Function(String userID, TapDownDetails tapDetails)? onSecondaryTapAvatar;
|
||||
|
||||
@Deprecated(
|
||||
"Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
||||
final bool showNickName;
|
||||
|
||||
final TIMUIKitHistoryMessageListConfig? mainHistoryListConfig;
|
||||
|
||||
/// tool tips panel configuration, long press message will show tool tips panel
|
||||
final ToolTipsConfig? toolTipsConfig;
|
||||
|
||||
final List<CustomEmojiFaceData> customEmojiStickerList;
|
||||
|
||||
final bool isAllowScroll;
|
||||
|
||||
final V2TimConversation conversation;
|
||||
|
||||
final V2TimGroupMemberFullInfo? groupMemberInfo;
|
||||
|
||||
/// This parameter accepts a custom widget to be displayed when the mouse hovers over a message,
|
||||
/// replacing the default message hover action bar.
|
||||
/// Applicable only on desktop platforms.
|
||||
/// If provided, the default message action functionality will appear in the right-click context menu instead.
|
||||
final Widget? Function(V2TimMessage message)? customMessageHoverBarOnDesktop;
|
||||
|
||||
const TIMUIKitHistoryMessageListContainer({
|
||||
Key? key,
|
||||
this.itemBuilder,
|
||||
this.scrollController,
|
||||
required this.conversationID,
|
||||
required this.conversationType,
|
||||
this.userAvatarBuilder,
|
||||
this.onLongPressForOthersHeadPortrait,
|
||||
this.groupAtInfoList,
|
||||
this.messageItemBuilder,
|
||||
this.tongueItemBuilder,
|
||||
this.extraTipsActionItemBuilder,
|
||||
this.isAllowScroll = true,
|
||||
this.onTapAvatar,
|
||||
@Deprecated(
|
||||
"Nickname will not show in one-to-one chat, if you tend to control it in group chat, please use `isShowSelfNameInGroup` and `isShowOthersNameInGroup` from `config: TIMUIKitChatConfig` instead")
|
||||
this.showNickName = true,
|
||||
this.initFindingMsg,
|
||||
this.mainHistoryListConfig,
|
||||
this.toolTipsConfig,
|
||||
this.customEmojiStickerList = const [],
|
||||
this.textFieldController,
|
||||
required this.conversation,
|
||||
this.onSecondaryTapAvatar,
|
||||
this.groupMemberInfo,
|
||||
this.customMessageHoverBarOnDesktop,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _TIMUIKitHistoryMessageListContainerState();
|
||||
}
|
||||
|
||||
class _TIMUIKitHistoryMessageListContainerState extends TIMUIKitState<TIMUIKitHistoryMessageListContainer> {
|
||||
late TIMUIKitHistoryMessageListController _historyMessageListController;
|
||||
|
||||
List<V2TimMessage?> historyMessageList = [];
|
||||
|
||||
Future<bool> requestForData(String? lastMsgID, LoadDirection direction, TUIChatSeparateViewModel model,
|
||||
[int? count, int? lastSeq]) async {
|
||||
if ((direction == LoadDirection.previous) || (direction == LoadDirection.latest && model.haveMoreLatestData)) {
|
||||
return await model.loadChatRecord(
|
||||
direction: direction,
|
||||
count: count ?? (kIsWeb ? 15 : HistoryMessageDartConstant.getCount),
|
||||
lastMsgID: lastMsgID,
|
||||
lastMsgSeq: lastSeq ?? -1,
|
||||
);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Widget Function(BuildContext, V2TimMessage)? _getTopRowBuilder(TUIChatSeparateViewModel model) {
|
||||
if (widget.messageItemBuilder?.messageNickNameBuilder != null) {
|
||||
return (BuildContext context, V2TimMessage message) {
|
||||
return widget.messageItemBuilder!.messageNickNameBuilder!(context, message, model);
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_historyMessageListController = TIMUIKitHistoryMessageListController(scrollController: widget.scrollController);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final chatConfig = Provider.of<TIMUIKitChatConfig>(context);
|
||||
final TUIChatSeparateViewModel model = Provider.of<TUIChatSeparateViewModel>(context, listen: false);
|
||||
|
||||
return TIMUIKitHistoryMessageListSelector(
|
||||
conversationID: model.conversationID,
|
||||
builder: (context, messageList, child) {
|
||||
return TIMUIKitHistoryMessageList(
|
||||
conversation: widget.conversation,
|
||||
model: model,
|
||||
isAllowScroll: widget.isAllowScroll,
|
||||
controller: _historyMessageListController,
|
||||
groupAtInfoList: widget.groupAtInfoList,
|
||||
mainHistoryListConfig: widget.mainHistoryListConfig,
|
||||
itemBuilder: (context, message) {
|
||||
return TIMUIKitHistoryMessageListItem(
|
||||
customMessageHoverBarOnDesktop: widget.customMessageHoverBarOnDesktop,
|
||||
groupMemberInfo: widget.groupMemberInfo,
|
||||
textFieldController: widget.textFieldController,
|
||||
userAvatarBuilder: widget.userAvatarBuilder,
|
||||
customEmojiStickerList: widget.customEmojiStickerList,
|
||||
topRowBuilder: _getTopRowBuilder(model),
|
||||
onScrollToIndex: _historyMessageListController.scrollToIndex,
|
||||
onScrollToIndexBegin: _historyMessageListController.scrollToIndexBegin,
|
||||
toolTipsConfig:
|
||||
widget.toolTipsConfig ?? ToolTipsConfig(additionalItemBuilder: widget.extraTipsActionItemBuilder),
|
||||
message: message!,
|
||||
showAvatar: chatConfig.isShowAvatar,
|
||||
onSecondaryTapForOthersPortrait: widget.onSecondaryTapAvatar,
|
||||
onTapForOthersPortrait: widget.onTapAvatar,
|
||||
messageItemBuilder: widget.messageItemBuilder,
|
||||
onLongPressForOthersHeadPortrait: widget.onLongPressForOthersHeadPortrait,
|
||||
allowAtUserWhenReply: chatConfig.isAtWhenReply,
|
||||
allowAvatarTap: chatConfig.isAllowClickAvatar,
|
||||
allowLongPress: chatConfig.isAllowLongPressMessage,
|
||||
isUseMessageReaction: chatConfig.isUseMessageReaction);
|
||||
},
|
||||
tongueItemBuilder: widget.tongueItemBuilder,
|
||||
initFindingMsg: widget.initFindingMsg,
|
||||
messageList: messageList,
|
||||
onLoadMore: (String? a, LoadDirection direction, [int? b, int? lastSeq]) async {
|
||||
return await requestForData(a, direction, model, b, lastSeq);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
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/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message_receipt.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message_receipt.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_wide_modal_operation_key.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/utils/screen_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/message_read_receipt.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/wide_popup.dart';
|
||||
|
||||
class TIMUIKitMessageReadReceipt extends TIMUIKitStatelessWidget {
|
||||
final V2TimMessage messageItem;
|
||||
final void Function(String, TapDownDetails tapDetails)? onTapAvatar;
|
||||
|
||||
TIMUIKitMessageReadReceipt({Key? key, this.onTapAvatar, required this.messageItem}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final TUITheme theme = value.theme;
|
||||
final TUIChatSeparateViewModel model = Provider.of<TUIChatSeparateViewModel>(context, listen: false);
|
||||
final isDesktopScreen = TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
|
||||
return Selector<TUIChatGlobalModel, V2TimMessageReceipt?>(
|
||||
builder: (context, value, child) {
|
||||
if (model.conversationType == ConvType.c2c) {
|
||||
bool isPeerRead = false;
|
||||
if (value != null) {
|
||||
isPeerRead = value.isPeerRead ?? false;
|
||||
}
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(bottom: 3),
|
||||
margin: const EdgeInsets.only(right: 6),
|
||||
child: Text(
|
||||
isPeerRead ? TIM_t("已读") : TIM_t("未读"),
|
||||
style: TextStyle(color: theme.chatMessageItemUnreadStatusTextColor, fontSize: 12),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
if ((value?.readCount ?? 0) > 0) {
|
||||
if (isDesktopScreen) {
|
||||
TUIKitWidePopup.showPopupWindow(
|
||||
operationKey: TUIKitWideModalOperationKey.messageReadDetails,
|
||||
context: context,
|
||||
width: MediaQuery.of(context).size.width * 0.5,
|
||||
height: MediaQuery.of(context).size.height * 0.8,
|
||||
title: TIM_t("消息详情"),
|
||||
child: (onClose) => MessageReadReceipt(
|
||||
model: model,
|
||||
onTapAvatar: onTapAvatar,
|
||||
messageItem: messageItem,
|
||||
unreadCount: value?.unreadCount ?? 0,
|
||||
readCount: value?.readCount ?? 0));
|
||||
} else {
|
||||
if (value?.unreadCount == 0) {
|
||||
return;
|
||||
}
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => MessageReadReceipt(
|
||||
model: model,
|
||||
onTapAvatar: onTapAvatar,
|
||||
messageItem: messageItem,
|
||||
unreadCount: value?.unreadCount ?? 0,
|
||||
readCount: value?.readCount ?? 0)));
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(bottom: 3, right: 6, left: 6, top: isDesktopScreen ? 2 : 6),
|
||||
child: ((value?.unreadCount ?? 0) == 0 && (value?.readCount ?? 0) > 0)
|
||||
? Icon(
|
||||
Icons.check_circle_outline,
|
||||
size: 18,
|
||||
color: theme.weakTextColor,
|
||||
)
|
||||
: Container(
|
||||
width: 14,
|
||||
height: 14,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
width: 1.3,
|
||||
color: (value?.readCount ?? 0) > 0 ? theme.primaryColor! : theme.weakTextColor!)),
|
||||
child: (value?.readCount ?? 0) > 0
|
||||
? Text(
|
||||
'${value?.readCount ?? 0}',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 8, color: theme.primaryColor),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
selector: (c, model) {
|
||||
return model.getMessageReadReceipt(messageItem.msgID ?? "");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_message.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_message.dart';
|
||||
|
||||
class TIMUIKitChatUtils {
|
||||
static String? getMessageIDWithinIndex(List<V2TimMessage?> messageList, int index) {
|
||||
if (messageList[index]!.elemType == 11) {
|
||||
if (index > 0) {
|
||||
return getMessageIDWithinIndex(messageList, index - 1);
|
||||
}
|
||||
}
|
||||
return messageList[index]!.msgID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
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/V2TimFriendshipListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimGroupListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/group_change_info_type.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.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/views/TIMUIKitChat/TIMUIKitAppBar/tim_uikit_appbar_title.dart';
|
||||
import 'package:tuple/tuple.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.dart';
|
||||
|
||||
class TIMUIKitAppBar extends StatefulWidget implements PreferredSizeWidget {
|
||||
/// Appbar config
|
||||
final AppBar? config;
|
||||
|
||||
/// Allow show conversation total unread count
|
||||
final bool showTotalUnReadCount;
|
||||
|
||||
/// Conversation id
|
||||
final String conversationID;
|
||||
|
||||
/// conversation name
|
||||
final String conversationShowName;
|
||||
|
||||
/// If allow update the conversation show name automatically.
|
||||
final bool isConversationShowNameFixed;
|
||||
|
||||
final bool showC2cMessageEditStatus;
|
||||
|
||||
final GestureTapDownCallback? onClickTitle;
|
||||
|
||||
const TIMUIKitAppBar({
|
||||
Key? key,
|
||||
this.config,
|
||||
this.isConversationShowNameFixed = false,
|
||||
this.showTotalUnReadCount = true,
|
||||
this.conversationID = "",
|
||||
this.conversationShowName = "",
|
||||
this.showC2cMessageEditStatus = true,
|
||||
this.onClickTitle,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Size get preferredSize =>
|
||||
config?.preferredSize ?? const Size.fromHeight(56.0);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _TIMUIKitAppBarState();
|
||||
}
|
||||
|
||||
class _TIMUIKitAppBarState extends TIMUIKitState<TIMUIKitAppBar> {
|
||||
final FriendshipServices _friendshipServices =
|
||||
serviceLocator<FriendshipServices>();
|
||||
final GroupServices _groupServices = serviceLocator<GroupServices>();
|
||||
|
||||
V2TimFriendshipListener? _friendshipListener;
|
||||
V2TimGroupListener? _groupListener;
|
||||
|
||||
String _conversationShowName = "";
|
||||
|
||||
_addConversationShowNameListener() {
|
||||
_friendshipListener = V2TimFriendshipListener(
|
||||
onFriendInfoChanged: (infoList) {
|
||||
try {
|
||||
final changedInfo = infoList.firstWhere(
|
||||
(element) => element.userID == widget.conversationID,
|
||||
);
|
||||
if (changedInfo.friendRemark != null &&
|
||||
changedInfo.friendRemark!.isNotEmpty) {
|
||||
_conversationShowName = changedInfo.friendRemark!;
|
||||
setState(() {});
|
||||
} else {
|
||||
_conversationShowName = (changedInfo.userProfile?.nickName ??
|
||||
changedInfo.userProfile?.userID) ??
|
||||
"";
|
||||
}
|
||||
// ignore: empty_catches
|
||||
} catch (e) {}
|
||||
},
|
||||
);
|
||||
if (_friendshipListener != null) {
|
||||
_friendshipServices.addFriendListener(listener: _friendshipListener!);
|
||||
}
|
||||
}
|
||||
|
||||
_addGroupListener() {
|
||||
_groupListener = V2TimGroupListener(
|
||||
onGroupInfoChanged: (groupID, changeInfos) {
|
||||
try {
|
||||
if (groupID == widget.conversationID) {
|
||||
final groupNameChangeInfo = changeInfos.firstWhere((element) =>
|
||||
element.type ==
|
||||
GroupChangeInfoType.V2TIM_GROUP_INFO_CHANGE_TYPE_NAME);
|
||||
if (groupNameChangeInfo.value != null) {
|
||||
_conversationShowName = groupNameChangeInfo.value!;
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
// ignore: empty_catches
|
||||
} catch (e) {}
|
||||
},
|
||||
);
|
||||
if (_groupListener != null) {
|
||||
_groupServices.addGroupListener(listener: _groupListener!);
|
||||
}
|
||||
}
|
||||
|
||||
String _getTotalUnReadCount(int unreadCount) {
|
||||
return unreadCount < 99 ? unreadCount.toString() : "99";
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_conversationShowName = widget.conversationShowName;
|
||||
if (!widget.isConversationShowNameFixed) {
|
||||
_addConversationShowNameListener();
|
||||
_addGroupListener();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
if (!widget.isConversationShowNameFixed) {
|
||||
_groupServices.removeGroupListener(listener: _groupListener);
|
||||
_friendshipServices.removeFriendListener(listener: _friendshipListener);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(TIMUIKitAppBar oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.conversationShowName != widget.conversationShowName) {
|
||||
if (widget.conversationShowName.isNotEmpty) {
|
||||
setState(() {
|
||||
_conversationShowName = widget.conversationShowName;
|
||||
});
|
||||
} else {
|
||||
updateTitleFuture();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateTitleFuture() async {
|
||||
try {
|
||||
final res = await _friendshipServices
|
||||
.getFriendsInfo(userIDList: [widget.conversationID]);
|
||||
if (res != null && res.isNotEmpty && res.first.resultCode == 0) {
|
||||
setState(() {
|
||||
_conversationShowName = res.first.friendInfo?.userProfile?.nickName ??
|
||||
res.first.friendInfo?.userProfile?.userID ??
|
||||
"";
|
||||
});
|
||||
}
|
||||
// ignore: empty_catches
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final TUITheme theme = value.theme;
|
||||
|
||||
final setAppbar = widget.config;
|
||||
final TUIChatSeparateViewModel chatVM =
|
||||
Provider.of<TUIChatSeparateViewModel>(context);
|
||||
final isDesktopScreen =
|
||||
TUIKitScreenUtils.getFormFactor(context) == DeviceType.Desktop;
|
||||
return AppBar(
|
||||
backgroundColor: setAppbar?.backgroundColor ??
|
||||
theme.chatHeaderBgColor ??
|
||||
theme.appbarBgColor ??
|
||||
theme.primaryColor,
|
||||
actionsIconTheme: setAppbar?.actionsIconTheme,
|
||||
foregroundColor: setAppbar?.foregroundColor,
|
||||
elevation: setAppbar?.elevation ?? (isDesktopScreen ? 0 : 1),
|
||||
bottom: setAppbar?.bottom,
|
||||
bottomOpacity: setAppbar?.bottomOpacity ?? 1.0,
|
||||
titleSpacing: setAppbar?.titleSpacing,
|
||||
automaticallyImplyLeading: setAppbar?.automaticallyImplyLeading ?? false,
|
||||
shadowColor: setAppbar?.shadowColor ?? theme.weakDividerColor,
|
||||
excludeHeaderSemantics: setAppbar?.excludeHeaderSemantics ?? false,
|
||||
toolbarHeight: setAppbar?.toolbarHeight,
|
||||
titleTextStyle: setAppbar?.titleTextStyle,
|
||||
toolbarOpacity: setAppbar?.toolbarOpacity ?? 1.0,
|
||||
toolbarTextStyle: setAppbar?.toolbarTextStyle,
|
||||
|
||||
// textTheme: setAppbar?.textTheme,
|
||||
iconTheme: setAppbar?.iconTheme ??
|
||||
const IconThemeData(
|
||||
color: Colors.white,
|
||||
),
|
||||
title: TIMUIKitAppBarTitle(
|
||||
title: setAppbar?.title,
|
||||
onClick: widget.onClickTitle,
|
||||
textStyle: TextStyle(
|
||||
color: theme.appbarTextColor ?? hexToColor("010000"), fontSize: 16),
|
||||
conversationShowName: _conversationShowName,
|
||||
showC2cMessageEditStatus: widget.showC2cMessageEditStatus,
|
||||
fromUser: widget.conversationID,
|
||||
),
|
||||
centerTitle: setAppbar?.centerTitle ?? (isDesktopScreen ? false : true),
|
||||
leadingWidth: setAppbar?.leadingWidth ?? (isDesktopScreen ? 8 : 70),
|
||||
leading: Selector<TUIChatGlobalModel, Tuple2<bool, int>>(
|
||||
builder: (context, data, _) {
|
||||
final isMultiSelect = data.item1;
|
||||
final unReadCount = data.item2;
|
||||
return (!isDesktopScreen && isMultiSelect)
|
||||
? TextButton(
|
||||
onPressed: () {
|
||||
chatVM.updateMultiSelectStatus(false);
|
||||
},
|
||||
child: Text(
|
||||
TIM_t('取消'),
|
||||
style: TextStyle(
|
||||
color: theme.appbarTextColor ?? hexToColor("010000"),
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
)
|
||||
: setAppbar?.leading ??
|
||||
(isDesktopScreen
|
||||
? Container()
|
||||
: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
constraints: const BoxConstraints(),
|
||||
icon: Icon(
|
||||
Icons.arrow_back_ios,
|
||||
color: hexToColor("010000"),
|
||||
size: 17,
|
||||
),
|
||||
onPressed: () async {
|
||||
chatVM.repliedMessage = null;
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
if (widget.showTotalUnReadCount &&
|
||||
unReadCount > 0)
|
||||
Container(
|
||||
width: 22,
|
||||
height: 22,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: theme.cautionColor,
|
||||
),
|
||||
child:
|
||||
Text(_getTotalUnReadCount(unReadCount)),
|
||||
),
|
||||
],
|
||||
));
|
||||
},
|
||||
shouldRebuild: (prev, next) =>
|
||||
prev.item1 != next.item1 || prev.item2 != next.item2,
|
||||
selector: (_, model) =>
|
||||
Tuple2(chatVM.isMultiSelect, model.totalUnReadCount)),
|
||||
actions: setAppbar?.actions,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
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_uikit/business_logic/view_models/tui_chat_global_model.dart';
|
||||
|
||||
class TIMUIKitAppBarTitle extends StatelessWidget {
|
||||
final Widget? title;
|
||||
final String conversationShowName;
|
||||
final bool showC2cMessageEditStatus;
|
||||
final String fromUser;
|
||||
final GestureTapDownCallback? onClick;
|
||||
final TextStyle? textStyle;
|
||||
|
||||
const TIMUIKitAppBarTitle(
|
||||
{Key? key,
|
||||
this.title,
|
||||
this.textStyle,
|
||||
required this.conversationShowName,
|
||||
required this.showC2cMessageEditStatus,
|
||||
required this.fromUser, this.onClick})
|
||||
: super(key: key);
|
||||
|
||||
Widget titleText(String text){
|
||||
return InkWell(
|
||||
onTapDown: onClick,
|
||||
child: Text(
|
||||
text,
|
||||
style: textStyle ??
|
||||
const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 17,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// String conversationShowName;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
int status = Provider.of<TUIChatGlobalModel>(context, listen: true)
|
||||
.getC2cMessageEditStatus(fromUser);
|
||||
if (status == 0) {
|
||||
if (title != null) {
|
||||
return title!;
|
||||
}
|
||||
return titleText(conversationShowName,);
|
||||
} else {
|
||||
if (showC2cMessageEditStatus) {
|
||||
return titleText(
|
||||
TIM_t("对方正在输入中..."),);
|
||||
|
||||
} else {
|
||||
if (title != null) {
|
||||
return title!;
|
||||
}
|
||||
return titleText(
|
||||
conversationShowName,);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
List<Map<String, Object>> messageReactionEmojiData = [
|
||||
{"name": "red heart", "unicode": 10084},
|
||||
{"name": "OK", "unicode": 128076},
|
||||
{"name": "thumbs up", "unicode": 128077},
|
||||
{"name": "flexed biceps", "unicode": 128170},
|
||||
{"name": "waving hand", "unicode": 128079},
|
||||
{"name": "handshake", "unicode": 129309},
|
||||
{"name": "folded hands", "unicode": 128591},
|
||||
{"name": "raised fist", "unicode": 9994},
|
||||
{"name": "thumbs down", "unicode": 128078},
|
||||
{"name": "smiling face with sunglasses", "unicode": 128526},
|
||||
{"name": "GRINNING FACE WITH SMILING EYES", "unicode": 128513},
|
||||
{"name": "SMILING FACE WITH SMILING EYES", "unicode": 128522},
|
||||
{"name": "smiling face with hearts", "unicode": 129392},
|
||||
{"name": "SMILING FACE WITH HEART-SHAPED EYES", "unicode": 128525},
|
||||
{"name": "FACE WITH TEARS OF JOY", "unicode": 128514},
|
||||
{"name": "flushed face", "unicode": 128563},
|
||||
{"name": "SMILING FACE WITH OPEN MOUTH AND COLD SWEAT", "unicode": 128517},
|
||||
{"name": "FACE WITH COLD SWEAT", "unicode": 128531},
|
||||
{"name": "loudly crying face", "unicode": 128557},
|
||||
{"name": "CONFOUNDED FACE", "unicode": 128534},
|
||||
{"name": "KISSING FACE WITH CLOSED EYES", "unicode": 128538},
|
||||
{
|
||||
"name": "FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES",
|
||||
"unicode": 128541
|
||||
},
|
||||
{"name": "DISAPPOINTED FACE", "unicode": 128542},
|
||||
{"name": "ANGRY FACE", "unicode": 128544},
|
||||
{"name": "POUTING FACE", "unicode": 128545},
|
||||
{"name": "CRYING FACE", "unicode": 128546},
|
||||
{"name": "FACE WITH LOOK OF TRIUMPH", "unicode": 128548},
|
||||
{"name": "FEARFUL FACE", "unicode": 128552},
|
||||
{"name": "SLEEPY FACE", "unicode": 128554},
|
||||
{"name": "FACE WITH NO GOOD GESTURE", "unicode": 128581},
|
||||
{"name": "FACE WITH OK GESTURE", "unicode": 128582},
|
||||
];
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_group_member_full_info.dart'
|
||||
if (dart.library.html) 'package:tencent_cloud_chat_sdk/web/compatible_models/v2_tim_group_member_full_info.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_self_info_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/theme/color.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
class TIMUIKitMessageReactionDetail extends StatefulWidget {
|
||||
/// the index of the current emoji sticker
|
||||
final int currentStickerIndex;
|
||||
|
||||
/// the list of member
|
||||
final List<V2TimGroupMemberFullInfo?>? memberList;
|
||||
|
||||
/// message reaction map
|
||||
final Map<String, dynamic> messageReaction;
|
||||
|
||||
/// the sticker list from message reaction
|
||||
final List<int> stickerList;
|
||||
|
||||
final Function(String userID, TapDownDetails tapDetails)? onTapAvatar;
|
||||
|
||||
const TIMUIKitMessageReactionDetail(
|
||||
{required this.currentStickerIndex,
|
||||
this.memberList,
|
||||
required this.messageReaction,
|
||||
Key? key,
|
||||
required this.stickerList,
|
||||
this.onTapAvatar})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => TIMUIKitMessageReactionDetailState();
|
||||
}
|
||||
|
||||
class TIMUIKitMessageReactionDetailState extends TIMUIKitState<TIMUIKitMessageReactionDetail>
|
||||
with TickerProviderStateMixin {
|
||||
final TUISelfInfoViewModel selfInfoModel = serviceLocator<TUISelfInfoViewModel>();
|
||||
|
||||
Widget getUserItem(String userID, TUITheme theme, Function(String userID, TapDownDetails tapDetails)? onTapAvatar) {
|
||||
V2TimGroupMemberFullInfo? memberInfo;
|
||||
String showName = userID;
|
||||
try {
|
||||
memberInfo = widget.memberList?.firstWhere((element) => element?.userID == userID);
|
||||
if (memberInfo != null) {
|
||||
if (memberInfo.friendRemark != null && memberInfo.friendRemark!.isNotEmpty) {
|
||||
showName = memberInfo.friendRemark!;
|
||||
} else if (memberInfo.nameCard != null && memberInfo.nameCard!.isNotEmpty) {
|
||||
showName = memberInfo.nameCard!;
|
||||
} else if (memberInfo.nickName != null && memberInfo.nickName!.isNotEmpty) {
|
||||
showName = memberInfo.nickName!;
|
||||
} else {
|
||||
showName = memberInfo.userID;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// e
|
||||
}
|
||||
|
||||
return InkWell(
|
||||
onTapDown: (tapDetails) {
|
||||
if (onTapAvatar != null) {
|
||||
if (userID != selfInfoModel.loginInfo?.userID) {
|
||||
onTapAvatar(userID, tapDetails);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: theme.weakDividerColor ?? CommonColor.weakDividerColor))),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(right: 12),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: 40,
|
||||
child: (memberInfo?.faceUrl != null)
|
||||
? Avatar(faceUrl: memberInfo!.faceUrl!, showName: userID)
|
||||
: Container(),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.only(top: 20, bottom: 20, right: 28),
|
||||
child: Text(
|
||||
showName,
|
||||
style: TextStyle(color: theme.black, fontSize: 18),
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget stickerItem(int sticker, int length) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 12, bottom: 12),
|
||||
padding: const EdgeInsets.only(left: 10, right: 10, top: 2, bottom: 1),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0x198a8a8a),
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(bottom: 3),
|
||||
child: Text(
|
||||
String.fromCharCode(sticker),
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 6),
|
||||
child: Text(length.toString()),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget getStickerNameList(
|
||||
int sticker, TUITheme theme, Function(String userID, TapDownDetails tapDetails)? onTapAvatar) {
|
||||
final nameList = widget.messageReaction[sticker.toString()];
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [...nameList.map((e) => getUserItem(e, theme, onTapAvatar))],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final theme = value.theme;
|
||||
return DefaultTabController(
|
||||
initialIndex: widget.currentStickerIndex,
|
||||
length: widget.stickerList.length,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(bottom: 4),
|
||||
child: TabBar(
|
||||
isScrollable: true,
|
||||
labelColor: theme.primaryColor,
|
||||
labelStyle: const TextStyle(fontWeight: FontWeight.bold),
|
||||
unselectedLabelColor: hexToColor("62626b"),
|
||||
unselectedLabelStyle: const TextStyle(fontWeight: FontWeight.normal),
|
||||
indicatorSize: TabBarIndicatorSize.label,
|
||||
indicatorColor: theme.primaryColor ?? hexToColor("62626b"),
|
||||
tabs: [
|
||||
...widget.stickerList.map((element) {
|
||||
return stickerItem(element, widget.messageReaction[element.toString()].length);
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
children: widget.stickerList
|
||||
.map((int sticker) => getStickerNameList(sticker, theme, widget.onTapAvatar))
|
||||
.toList()))
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
import 'package:flutter/material.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/ui/utils/screen_utils.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/message_reaction_emoji.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_emoji_panel.dart'
|
||||
as emoji;
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/emoji.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/widgets/extended_wrap/extended_wrap.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/color.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/theme/tui_theme.dart';
|
||||
|
||||
enum SelectEmojiPanelPosition { up, down }
|
||||
|
||||
class TIMUIKitMessageReactionEmojiSelectPanel extends StatefulWidget {
|
||||
final ValueChanged<int> onSelect;
|
||||
final bool isShowMoreSticker;
|
||||
final ValueChanged<bool> onClickShowMore;
|
||||
|
||||
const TIMUIKitMessageReactionEmojiSelectPanel(
|
||||
{Key? key,
|
||||
required this.onSelect,
|
||||
required this.isShowMoreSticker,
|
||||
required this.onClickShowMore})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() =>
|
||||
TIMUIKitMessageReactionEmojiSelectPanelState();
|
||||
}
|
||||
|
||||
class TIMUIKitMessageReactionEmojiSelectPanelState
|
||||
extends TIMUIKitState<TIMUIKitMessageReactionEmojiSelectPanel> {
|
||||
bool isShowMore = false;
|
||||
|
||||
_buildSimplePanel(TUITheme theme) {
|
||||
final List<Map<String, Object>> emojiData = messageReactionEmojiData;
|
||||
final isDesktopScreen = TUIKitScreenUtils.getFormFactor() == DeviceType.Desktop;
|
||||
return Material(
|
||||
color: Colors.white,
|
||||
child: ExtendedWrap(
|
||||
maxLines: widget.isShowMoreSticker ? 5 : 1,
|
||||
spacing: 18,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
runSpacing: 24,
|
||||
children: [
|
||||
if(!isDesktopScreen)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
widget.onClickShowMore(!widget.isShowMoreSticker);
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 34,
|
||||
child: Icon(
|
||||
widget.isShowMoreSticker
|
||||
? Icons.cancel_outlined
|
||||
: Icons.add_circle_outline_outlined,
|
||||
color: hexToColor("444444"),
|
||||
size: 26),
|
||||
),
|
||||
),
|
||||
...emojiData.map(
|
||||
(e) {
|
||||
var item = Emoji.fromJson(e);
|
||||
return SizedBox(
|
||||
// width: 50,
|
||||
child: InkWell(
|
||||
splashColor: Colors.white,
|
||||
onTap: () {
|
||||
widget.onSelect(item.unicode);
|
||||
},
|
||||
child: emoji.EmojiItem(
|
||||
name: item.name,
|
||||
unicode: item.unicode,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList()
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
|
||||
final TUITheme theme = value.theme;
|
||||
|
||||
return Container(
|
||||
child: _buildSimplePanel(theme),
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user