Upgrade
This commit is contained in:
parent
952cb82417
commit
7457f182f3
@ -1,3 +1,9 @@
|
||||
## 2.0.9
|
||||
|
||||
1.Fix bug </br>
|
||||
2.New organization fuction</br>
|
||||
3.New uploadImage method </br>
|
||||
|
||||
## 2.0.8
|
||||
|
||||
1.Fix bug </br>
|
||||
|
@ -49,5 +49,5 @@ android {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation 'io.openim:core-sdk:2.0.8.+@aar'
|
||||
implementation 'io.openim:core-sdk:2.0.9.+@aar'
|
||||
}
|
@ -18,6 +18,7 @@ import io.openim.flutter_openim_sdk.manager.FriendshipManager;
|
||||
import io.openim.flutter_openim_sdk.manager.GroupManager;
|
||||
import io.openim.flutter_openim_sdk.manager.IMManager;
|
||||
import io.openim.flutter_openim_sdk.manager.MessageManager;
|
||||
import io.openim.flutter_openim_sdk.manager.OrganizationManager;
|
||||
import io.openim.flutter_openim_sdk.manager.SignalingManager;
|
||||
import io.openim.flutter_openim_sdk.manager.UserManager;
|
||||
import io.openim.flutter_openim_sdk.manager.WorkMomentsManager;
|
||||
@ -41,6 +42,7 @@ public class FlutterOpenimSdkPlugin implements FlutterPlugin, MethodCallHandler
|
||||
private static GroupManager groupManager;
|
||||
private static SignalingManager signalingManager;
|
||||
private static WorkMomentsManager workMomentsManager;
|
||||
private static OrganizationManager organizationManager;
|
||||
|
||||
public FlutterOpenimSdkPlugin() {
|
||||
}
|
||||
@ -54,6 +56,7 @@ public class FlutterOpenimSdkPlugin implements FlutterPlugin, MethodCallHandler
|
||||
FlutterOpenimSdkPlugin.groupManager = new GroupManager();
|
||||
FlutterOpenimSdkPlugin.signalingManager = new SignalingManager();
|
||||
FlutterOpenimSdkPlugin.workMomentsManager = new WorkMomentsManager();
|
||||
FlutterOpenimSdkPlugin.organizationManager = new OrganizationManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,10 @@
|
||||
package io.openim.flutter_openim_sdk.listener;
|
||||
|
||||
import io.openim.flutter_openim_sdk.util.CommonUtil;
|
||||
|
||||
public class OnOrganizationListener implements open_im_sdk_callback.OnOrganizationListener {
|
||||
@Override
|
||||
public void onOrganizationUpdated() {
|
||||
CommonUtil.emitEvent("organizationListener", "onOrganizationUpdated", null);
|
||||
}
|
||||
}
|
@ -44,4 +44,13 @@ public class IMManager extends BaseManager {
|
||||
);
|
||||
}
|
||||
|
||||
public void uploadImage(MethodCall methodCall, MethodChannel.Result result) {
|
||||
Open_im_sdk.uploadImage(
|
||||
new OnBaseListener(result, methodCall),
|
||||
value(methodCall, "operationID"),
|
||||
value(methodCall, "path"),
|
||||
value(methodCall, "token"),
|
||||
value(methodCall, "obj")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
package io.openim.flutter_openim_sdk.manager;
|
||||
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
import io.openim.flutter_openim_sdk.listener.OnBaseListener;
|
||||
import io.openim.flutter_openim_sdk.listener.OnOrganizationListener;
|
||||
import open_im_sdk.Open_im_sdk;
|
||||
|
||||
public class OrganizationManager extends BaseManager {
|
||||
|
||||
public void setOrganizationListener(MethodCall methodCall, MethodChannel.Result result) {
|
||||
Open_im_sdk.setOrganizationListener(new OnOrganizationListener());
|
||||
}
|
||||
|
||||
public void getSubDepartment(MethodCall methodCall, MethodChannel.Result result) {
|
||||
Open_im_sdk.getSubDepartment(
|
||||
new OnBaseListener(result, methodCall),
|
||||
value(methodCall, "operationID"),
|
||||
value(methodCall, "departmentID"),
|
||||
int2long(methodCall, "offset"),
|
||||
int2long(methodCall, "count")
|
||||
);
|
||||
}
|
||||
|
||||
public void getDepartmentMember(MethodCall methodCall, MethodChannel.Result result) {
|
||||
Open_im_sdk.getDepartmentMember(
|
||||
new OnBaseListener(result, methodCall),
|
||||
value(methodCall, "operationID"),
|
||||
value(methodCall, "departmentID"),
|
||||
int2long(methodCall, "offset"),
|
||||
int2long(methodCall, "count")
|
||||
);
|
||||
}
|
||||
|
||||
public void getUserInDepartment(MethodCall methodCall, MethodChannel.Result result) {
|
||||
Open_im_sdk.getUserInDepartment(
|
||||
new OnBaseListener(result, methodCall),
|
||||
value(methodCall, "operationID"),
|
||||
value(methodCall, "userID")
|
||||
);
|
||||
}
|
||||
|
||||
public void getDepartmentMemberAndSubDepartment(MethodCall methodCall, MethodChannel.Result result) {
|
||||
Open_im_sdk.getDepartmentMemberAndSubDepartment(
|
||||
new OnBaseListener(result, methodCall),
|
||||
value(methodCall, "operationID"),
|
||||
value(methodCall, "departmentID"),
|
||||
int2long(methodCall, "departmentOffset"),
|
||||
int2long(methodCall, "departmentCount"),
|
||||
int2long(methodCall, "memberOffset"),
|
||||
int2long(methodCall, "memberCount")
|
||||
);
|
||||
}
|
||||
}
|
@ -32,6 +32,9 @@ public class IMMananger: BaseServiceManager {
|
||||
Open_im_sdkWakeUp(BaseCallback(result: result), methodCall[string: "operationID"])
|
||||
}
|
||||
|
||||
func uploadImage(methodCall: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
Open_im_sdkUploadImage(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[string: "path"], methodCall[string: "token"], methodCall[string: "obj"])
|
||||
}
|
||||
}
|
||||
|
||||
public class ConnListener: NSObject, Open_im_sdk_callbackOnConnListenerProtocol {
|
||||
|
49
ios/Classes/Module/OrganizationManager.swift
Normal file
49
ios/Classes/Module/OrganizationManager.swift
Normal file
@ -0,0 +1,49 @@
|
||||
import Foundation
|
||||
import OpenIMCore
|
||||
|
||||
public class OrganizationManager: BaseServiceManager {
|
||||
|
||||
public override func registerHandlers() {
|
||||
super.registerHandlers()
|
||||
self["setOrganizationListener"] = setOrganizationListener
|
||||
self["getSubDepartment"] = getSubDepartment
|
||||
self["getDepartmentMember"] = getDepartmentMember
|
||||
self["getUserInDepartment"] = getUserInDepartment
|
||||
self["getDepartmentMemberAndSubDepartment"] = getDepartmentMemberAndSubDepartment
|
||||
}
|
||||
|
||||
func setOrganizationListener(methodCall: FlutterMethodCall, result: @escaping FlutterResult){
|
||||
Open_im_sdkSetOrganizationListener(OrganizationListener(channel: channel))
|
||||
callBack(result)
|
||||
}
|
||||
|
||||
func getSubDepartment(methodCall: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
Open_im_sdkGetSubDepartment(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[string: "departmentID"], methodCall[int: "offset"], methodCall[int: "count"])
|
||||
}
|
||||
|
||||
func getDepartmentMember(methodCall: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
Open_im_sdkGetDepartmentMember(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[string: "departmentID"], methodCall[int: "offset"], methodCall[int: "count"])
|
||||
}
|
||||
|
||||
func getUserInDepartment(methodCall: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
Open_im_sdkGetUserInDepartment(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[string: "userID"])
|
||||
}
|
||||
|
||||
func getDepartmentMemberAndSubDepartment(methodCall: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
Open_im_sdkGetDepartmentMemberAndSubDepartment(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[string: "departmentID"], methodCall[int: "departmentOffset"], methodCall[int: "departmentCount"], methodCall[int: "memberOffset"], methodCall[int: "memberCount"])
|
||||
}
|
||||
}
|
||||
|
||||
public class OrganizationListener: NSObject, Open_im_sdk_callbackOnOrganizationListenerProtocol {
|
||||
|
||||
private let channel:FlutterMethodChannel
|
||||
|
||||
init(channel:FlutterMethodChannel) {
|
||||
self.channel = channel
|
||||
}
|
||||
|
||||
public func onOrganizationUpdated() {
|
||||
CommonUtil.emitEvent(channel: self.channel, method: "organizationListener", type: "onOrganizationUpdated", errCode: nil, errMsg: nil, data: nil)
|
||||
}
|
||||
|
||||
}
|
@ -10,6 +10,7 @@ public class SwiftFlutterOpenimSdkPlugin: NSObject, FlutterPlugin {
|
||||
let userManger: UserManager
|
||||
let signalingManager: SignalingManager
|
||||
let workMomentsManager: WorkMomentsManager
|
||||
let organizationManager: OrganizationManager
|
||||
|
||||
init(channel: FlutterMethodChannel) {
|
||||
self.imManager = IMMananger(channel: channel)
|
||||
@ -20,6 +21,7 @@ public class SwiftFlutterOpenimSdkPlugin: NSObject, FlutterPlugin {
|
||||
self.userManger = UserManager(channel: channel)
|
||||
self.signalingManager = SignalingManager(channel: channel)
|
||||
self.workMomentsManager = WorkMomentsManager(channel: channel)
|
||||
self.organizationManager = OrganizationManager(channel: channel)
|
||||
}
|
||||
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
@ -47,6 +49,8 @@ public class SwiftFlutterOpenimSdkPlugin: NSObject, FlutterPlugin {
|
||||
signalingManager.handleMethod(call: call, result: result)
|
||||
case "workMomentsManager":
|
||||
workMomentsManager.handleMethod(call: call, result: result)
|
||||
case "organizationManager":
|
||||
organizationManager.handleMethod(call: call, result: result)
|
||||
default:
|
||||
print("Handle ManagerName Error: \(managerName) not found")
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ A new Flutter project.
|
||||
s.dependency 'Flutter'
|
||||
s.platform = :ios, '8.0'
|
||||
|
||||
s.dependency 'OpenIMSDKCore','2.0.8'
|
||||
s.dependency 'OpenIMSDKCore','2.0.9'
|
||||
s.static_framework = true
|
||||
# s.vendored_frameworks = 'Framework/*.framework'
|
||||
# Flutter.framework does not contain a i386 slice.
|
||||
|
@ -13,6 +13,7 @@ export 'src/listener/conversation_listener.dart';
|
||||
export 'src/listener/friendship_listener.dart';
|
||||
export 'src/listener/group_listener.dart';
|
||||
export 'src/listener/msg_send_progress_listener.dart';
|
||||
export 'src/listener/organization_listener.dart';
|
||||
export 'src/listener/signaling_listener.dart';
|
||||
export 'src/listener/user_listener.dart';
|
||||
export 'src/listener/workmoments_listener.dart';
|
||||
@ -22,6 +23,7 @@ export 'src/manager/im_group_manager.dart';
|
||||
export 'src/manager/im_manager.dart';
|
||||
export 'src/manager/im_message_manager.dart';
|
||||
export 'src/manager/im_offline_push_manager.dart';
|
||||
export 'src/manager/im_organization_manager.dart';
|
||||
export 'src/manager/im_signaling_manager.dart';
|
||||
export 'src/manager/im_user_manager.dart';
|
||||
export 'src/manager/im_workmoments_manager.dart';
|
||||
@ -29,6 +31,7 @@ export 'src/models/conversation_info.dart';
|
||||
export 'src/models/group_info.dart';
|
||||
export 'src/models/message.dart';
|
||||
export 'src/models/notification_info.dart';
|
||||
export 'src/models/organization_info.dart';
|
||||
export 'src/models/search_info.dart';
|
||||
export 'src/models/signaling_info.dart';
|
||||
export 'src/models/user_info.dart';
|
||||
|
@ -9,4 +9,5 @@ class ListenerType {
|
||||
static final signalingListener = 'signalingListener';
|
||||
static final msgSendProgressListener = "msgSendProgressListener";
|
||||
static final workMomentsListener = "workMomentsListener";
|
||||
static final organizationListener = "organizationListener";
|
||||
}
|
||||
|
9
lib/src/listener/organization_listener.dart
Normal file
9
lib/src/listener/organization_listener.dart
Normal file
@ -0,0 +1,9 @@
|
||||
class OnOrganizationListener {
|
||||
Function()? onOrganizationUpdated;
|
||||
|
||||
OnOrganizationListener({this.onOrganizationUpdated});
|
||||
|
||||
void organizationUpdated() {
|
||||
onOrganizationUpdated?.call();
|
||||
}
|
||||
}
|
@ -130,8 +130,8 @@ class ConversationManager {
|
||||
// _channel.invokeMethod(
|
||||
// 'markSingleMessageHasRead', _buildParam({'userID': userID}));
|
||||
|
||||
/// Mark group chat messages as read
|
||||
/// 标记群聊已读
|
||||
/// Mark group chat all messages as read
|
||||
/// 标记群聊会话已读
|
||||
Future<dynamic> markGroupMessageHasRead({
|
||||
required String groupID,
|
||||
String? operationID,
|
||||
|
@ -16,6 +16,7 @@ class FriendshipManager {
|
||||
|
||||
/// Get friend info by user id
|
||||
/// 查询好友信息
|
||||
/// [uidList] 好友的userID集合
|
||||
Future<List<UserInfo>> getFriendsInfo({
|
||||
required List<String> uidList,
|
||||
String? operationID,
|
||||
@ -30,7 +31,9 @@ class FriendshipManager {
|
||||
.then((value) => Utils.toList(value, (v) => UserInfo.fromJson(v)));
|
||||
|
||||
/// Send an friend application
|
||||
/// 发送一个好友请求
|
||||
/// 发送一个好友请求,需要对方调用同意申请才能成为好友。
|
||||
/// [uid] 被邀请的用户ID
|
||||
/// [reason] 说明
|
||||
Future<dynamic> addFriend({
|
||||
required String uid,
|
||||
String? reason,
|
||||
@ -92,6 +95,8 @@ class FriendshipManager {
|
||||
|
||||
/// Modify friend remark name
|
||||
/// 设置好友备注
|
||||
/// [uid] 好友的userID
|
||||
/// [remark] 好友的备注
|
||||
Future<dynamic> setFriendRemark({
|
||||
required String uid,
|
||||
required String remark,
|
||||
@ -107,6 +112,7 @@ class FriendshipManager {
|
||||
|
||||
/// Add friends to blacklist
|
||||
/// 加入黑名单
|
||||
/// [uid]被加入黑名单的好友ID
|
||||
Future<dynamic> addBlacklist({
|
||||
required String uid,
|
||||
String? operationID,
|
||||
|
@ -74,6 +74,10 @@ class GroupManager {
|
||||
|
||||
/// Get the list of group members
|
||||
/// 分页获取组成员列表
|
||||
/// [groupId] 群ID
|
||||
/// [filter] 过滤成员 1普通成员, 2群主,3管理员,0所有
|
||||
/// [offset] 开始下标
|
||||
/// [count] 总数
|
||||
Future<List<GroupMembersInfo>> getGroupMemberList({
|
||||
required String groupId,
|
||||
int filter = 0,
|
||||
@ -96,6 +100,10 @@ class GroupManager {
|
||||
|
||||
/// Get the list of group members
|
||||
/// 分页获取组成员列表
|
||||
/// [groupId] 群ID
|
||||
/// [filter] 过滤成员 1普通成员, 2群主,3管理员,0所有
|
||||
/// [offset] 开始下标
|
||||
/// [count] 总数
|
||||
Future<List<dynamic>> getGroupMemberListMap({
|
||||
required String groupId,
|
||||
int filter = 0,
|
||||
@ -147,6 +155,12 @@ class GroupManager {
|
||||
|
||||
/// Create a group
|
||||
/// 创建一个组
|
||||
/// [groupName] 群名
|
||||
/// [notification] 公告
|
||||
/// [introduction] 群介绍
|
||||
/// [faceUrl] 群头像
|
||||
/// [ex] 额外信息
|
||||
/// [list] 初创群成员以及其角色
|
||||
Future<GroupInfo> createGroup({
|
||||
String? groupName,
|
||||
String? notification,
|
||||
@ -177,6 +191,12 @@ class GroupManager {
|
||||
|
||||
/// Edit group information
|
||||
/// 编辑组资料
|
||||
/// [groupID] 被编辑的群ID
|
||||
/// [groupName] 新的群名
|
||||
/// [notification] 新的公告
|
||||
/// [introduction] 新的群介绍
|
||||
/// [faceUrl] 新的群头像
|
||||
/// [ex] 新的额外信息
|
||||
Future<dynamic> setGroupInfo({
|
||||
required String groupID,
|
||||
String? groupName,
|
||||
@ -218,7 +238,7 @@ class GroupManager {
|
||||
(value) => Utils.toList(value, (map) => GroupInfo.fromJson(map)));
|
||||
|
||||
/// Apply to join the group
|
||||
/// 申请加入组,需要通过管理员同意。
|
||||
/// 申请加入组,需要通过管理员/群组同意。
|
||||
Future<dynamic> joinGroup({
|
||||
required String gid,
|
||||
String? reason,
|
||||
@ -260,8 +280,8 @@ class GroupManager {
|
||||
'operationID': Utils.checkOperationID(operationID),
|
||||
}));
|
||||
|
||||
/// As the group owner or administrator, get the list of received group members' applications to join the group.
|
||||
/// 作为群主或者管理员,获取收到的群成员申请进群列表。
|
||||
/// As the group owner or administrator, the group member's application to join the group received
|
||||
/// 作为群主或者管理员,收到的群成员入群申请
|
||||
Future<List<GroupApplicationInfo>> getRecvGroupApplicationList(
|
||||
{String? operationID}) =>
|
||||
_channel
|
||||
@ -288,6 +308,7 @@ class GroupManager {
|
||||
|
||||
/// Accept group application
|
||||
/// 管理员或者群主同意某人进入某群
|
||||
/// 注:主动申请入群需要通过管理员/群组处理,被别人拉入群不需要管理员/群组处理
|
||||
Future<dynamic> acceptGroupApplication({
|
||||
required String gid,
|
||||
required String uid,
|
||||
@ -305,6 +326,7 @@ class GroupManager {
|
||||
|
||||
/// Refuse group application
|
||||
/// 管理员或者群主拒绝某人进入某群
|
||||
/// 注:主动申请入群需要通过管理员/群组处理,被别人拉入群不需要管理员/群组处理
|
||||
Future<dynamic> refuseGroupApplication({
|
||||
required String gid,
|
||||
required String uid,
|
||||
@ -334,7 +356,9 @@ class GroupManager {
|
||||
}));
|
||||
|
||||
/// Enable group mute
|
||||
/// 开启群禁言
|
||||
/// 开启群禁言,所有群成员禁止发言
|
||||
/// [groupID] 将开启群禁言的组ID
|
||||
/// [mute] true:开启,false:关闭
|
||||
Future<dynamic> changeGroupMute({
|
||||
required String groupID,
|
||||
required bool mute,
|
||||
@ -350,6 +374,9 @@ class GroupManager {
|
||||
|
||||
/// Mute group members
|
||||
/// 禁言群成员
|
||||
/// [groupID] 群ID
|
||||
/// [userID] 将被禁言的成员ID
|
||||
/// [seconds] 被禁言的时间s,设置为0则为接触禁言
|
||||
Future<dynamic> changeGroupMemberMute({
|
||||
required String groupID,
|
||||
required String userID,
|
||||
@ -367,6 +394,9 @@ class GroupManager {
|
||||
|
||||
/// Set group user nickname
|
||||
/// 设置群成员昵称
|
||||
/// [groupID] 群ID
|
||||
/// [userID] 群成员的用户ID
|
||||
/// [groupNickname] 群昵称
|
||||
Future<dynamic> setGroupMemberNickname({
|
||||
required String groupID,
|
||||
required String userID,
|
||||
|
@ -14,11 +14,14 @@ class IMManager {
|
||||
// late OfflinePushManager offlinePushManager;
|
||||
late SignalingManager signalingManager;
|
||||
late WorkMomentsManager workMomentsManager;
|
||||
late OrganizationManager organizationManager;
|
||||
|
||||
late OnConnectListener _connectListener;
|
||||
late String uid;
|
||||
late UserInfo uInfo;
|
||||
bool isLogined = false;
|
||||
String? token;
|
||||
String? _objectStorage;
|
||||
|
||||
IMManager(this._channel) {
|
||||
conversationManager = ConversationManager(_channel);
|
||||
@ -29,6 +32,7 @@ class IMManager {
|
||||
// offlinePushManager = OfflinePushManager(_channel);
|
||||
signalingManager = SignalingManager(_channel);
|
||||
workMomentsManager = WorkMomentsManager(_channel);
|
||||
organizationManager = OrganizationManager(_channel);
|
||||
_addNativeCallback(_channel);
|
||||
}
|
||||
|
||||
@ -269,6 +273,13 @@ class IMManager {
|
||||
workMomentsManager.listener.recvNewNotification();
|
||||
break;
|
||||
}
|
||||
} else if (call.method == ListenerType.organizationListener) {
|
||||
String type = call.arguments['type'];
|
||||
switch (type) {
|
||||
case 'onOrganizationUpdated':
|
||||
organizationManager.listener.organizationUpdated();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
print(
|
||||
@ -284,6 +295,8 @@ class IMManager {
|
||||
/// [apiAddr] api server ip address
|
||||
/// [wsAddr] webSocket ip address
|
||||
/// [dataDir] data storage directory
|
||||
/// [objectStorage] storage object: cos/minio
|
||||
/// [logLevel] log level: 1-not print
|
||||
///
|
||||
/// 初始化SDK
|
||||
///
|
||||
@ -291,6 +304,8 @@ class IMManager {
|
||||
/// [apiAddr] SDK api地址
|
||||
/// [wsAddr] SDK websocket地址
|
||||
/// [dataDir] SDK数据库存储目录
|
||||
/// [objectStorage] 存储对象 cos/minio
|
||||
/// [logLevel] 日志 1-不打印
|
||||
Future<dynamic> initSDK({
|
||||
required int platform,
|
||||
required String apiAddr,
|
||||
@ -301,7 +316,8 @@ class IMManager {
|
||||
String objectStorage = 'cos',
|
||||
String? operationID,
|
||||
}) {
|
||||
_connectListener = listener;
|
||||
this._connectListener = listener;
|
||||
this._objectStorage = objectStorage;
|
||||
return _channel.invokeMethod(
|
||||
'initSDK',
|
||||
_buildParam(
|
||||
@ -324,6 +340,8 @@ class IMManager {
|
||||
|
||||
/// Login sdk
|
||||
/// 登录
|
||||
/// [uid]用户id
|
||||
/// [token]登录token,从业务服务器上获取
|
||||
Future<UserInfo> login({
|
||||
required String uid,
|
||||
required String token,
|
||||
@ -339,6 +357,7 @@ class IMManager {
|
||||
);
|
||||
this.isLogined = true;
|
||||
this.uid = uid;
|
||||
this.token = token;
|
||||
this.uInfo = await userManager.getSelfUserInfo();
|
||||
return uInfo;
|
||||
}
|
||||
@ -352,6 +371,7 @@ class IMManager {
|
||||
'operationID': Utils.checkOperationID(operationID),
|
||||
}));
|
||||
this.isLogined = false;
|
||||
this.token = null;
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -368,12 +388,33 @@ class IMManager {
|
||||
Future<UserInfo> getLoginUserInfo() async => uInfo;
|
||||
|
||||
/// wakeup
|
||||
/// 从后台回到前台立刻唤醒
|
||||
Future wakeUp({String? operationID}) => _channel.invokeMethod(
|
||||
'wakeUp',
|
||||
_buildParam({
|
||||
'operationID': Utils.checkOperationID(operationID),
|
||||
}));
|
||||
|
||||
/// upload image to server
|
||||
/// 上传图片到服务器
|
||||
/// [path]图片路径
|
||||
/// [token] im token
|
||||
/// [objectStorage] 存储对象 cos/minio
|
||||
Future uploadImage({
|
||||
required String path,
|
||||
String? token,
|
||||
String? objectStorage,
|
||||
String? operationID,
|
||||
}) =>
|
||||
_channel.invokeMethod(
|
||||
'uploadImage',
|
||||
_buildParam({
|
||||
'path': path,
|
||||
'token': token ?? this.token,
|
||||
'obj': objectStorage ?? this._objectStorage,
|
||||
'operationID': Utils.checkOperationID(operationID),
|
||||
}));
|
||||
|
||||
static Map _buildParam(Map param) {
|
||||
param["ManagerName"] = "imManager";
|
||||
return param;
|
||||
|
@ -54,10 +54,12 @@ class MessageManager {
|
||||
.then((value) => Utils.toObj(value, (map) => Message.fromJson(map)));
|
||||
|
||||
/// Find all history message
|
||||
/// 获取聊天记录
|
||||
/// [userID]接收消息的用户id
|
||||
/// [conversationID] 会话id,查询通知是可用
|
||||
/// [groupID]接收消息的组id
|
||||
/// 获取聊天记录(以startMsg为节点,以前的聊天记录)
|
||||
/// [userID] 接收消息的用户id
|
||||
/// [conversationID] 会话id,查询通知时可用
|
||||
/// [groupID] 接收消息的组id
|
||||
/// [startMsg] 从这条消息开始查询[count]条,获取的列表index==length-1为最新消息,所以获取下一页历史记录startMsg=list.first
|
||||
/// [count] 一次拉取的总数
|
||||
Future<List<Message>> getHistoryMessageList({
|
||||
String? userID,
|
||||
String? groupID,
|
||||
@ -93,7 +95,7 @@ class MessageManager {
|
||||
})));
|
||||
|
||||
/// Delete message
|
||||
/// 删除消息
|
||||
/// 删除本地消息
|
||||
Future deleteMessageFromLocalStorage({
|
||||
required Message message,
|
||||
String? operationID,
|
||||
@ -110,45 +112,41 @@ class MessageManager {
|
||||
// _channel.invokeMethod('deleteMessages',
|
||||
// _buildParam({"msgList": msgList.map((e) => e.toJson()).toList()}));
|
||||
|
||||
///
|
||||
Future insertSingleMessageToLocalStorage({
|
||||
/// 插入单聊消息到本地
|
||||
Future<Message> insertSingleMessageToLocalStorage({
|
||||
String? receiverID,
|
||||
String? senderID,
|
||||
Message? message,
|
||||
String? operationID,
|
||||
}) =>
|
||||
_channel.invokeMethod(
|
||||
'insertSingleMessageToLocalStorage',
|
||||
_buildParam({
|
||||
"message": message?.toJson(),
|
||||
"receiverID": receiverID,
|
||||
"senderID": senderID,
|
||||
"operationID": Utils.checkOperationID(operationID),
|
||||
}));
|
||||
_channel
|
||||
.invokeMethod(
|
||||
'insertSingleMessageToLocalStorage',
|
||||
_buildParam({
|
||||
"message": message?.toJson(),
|
||||
"receiverID": receiverID,
|
||||
"senderID": senderID,
|
||||
"operationID": Utils.checkOperationID(operationID),
|
||||
}))
|
||||
.then((value) => Utils.toObj(value, (map) => Message.fromJson(map)));
|
||||
|
||||
///
|
||||
Future insertGroupMessageToLocalStorage({
|
||||
/// 插入群聊消息到本地
|
||||
Future<Message> insertGroupMessageToLocalStorage({
|
||||
String? groupID,
|
||||
String? senderID,
|
||||
Message? message,
|
||||
String? operationID,
|
||||
}) =>
|
||||
_channel.invokeMethod(
|
||||
'insertGroupMessageToLocalStorage',
|
||||
_buildParam({
|
||||
"message": message?.toJson(),
|
||||
"groupID": groupID,
|
||||
"senderID": senderID,
|
||||
"operationID": Utils.checkOperationID(operationID),
|
||||
}));
|
||||
|
||||
/// Query the message according to the message id
|
||||
// Future findMessages({required List<String> messageIDList}) =>
|
||||
// _channel.invokeMethod(
|
||||
// 'findMessages',
|
||||
// _buildParam({
|
||||
// "messageIDList": messageIDList,
|
||||
// }));
|
||||
_channel
|
||||
.invokeMethod(
|
||||
'insertGroupMessageToLocalStorage',
|
||||
_buildParam({
|
||||
"message": message?.toJson(),
|
||||
"groupID": groupID,
|
||||
"senderID": senderID,
|
||||
"operationID": Utils.checkOperationID(operationID),
|
||||
}))
|
||||
.then((value) => Utils.toObj(value, (map) => Message.fromJson(map)));
|
||||
|
||||
/// Mark c2c message as read
|
||||
/// 标记c2c消息已读
|
||||
@ -182,6 +180,7 @@ class MessageManager {
|
||||
|
||||
/// Typing
|
||||
/// 正在输入提示
|
||||
/// [msgTip] 自定义内容
|
||||
Future typingStatusUpdate({
|
||||
required String userID,
|
||||
String? msgTip,
|
||||
@ -212,6 +211,10 @@ class MessageManager {
|
||||
|
||||
/// Create @ message
|
||||
/// 创建@消息
|
||||
/// [text] 输入内容
|
||||
/// [atUserIDList] 被@到的userID集合
|
||||
/// [atUserInfoList] userID跟nickname映射关系,用在界面显示时将id替换为nickname
|
||||
/// [quoteMessage] 引用消息(被回复的消息)
|
||||
Future<Message> createTextAtMessage({
|
||||
required String text,
|
||||
required List<String> atUserIDList,
|
||||
@ -250,6 +253,7 @@ class MessageManager {
|
||||
|
||||
/// Create picture message
|
||||
/// 创建图片消息
|
||||
/// [imagePath] 路径
|
||||
Future<Message> createImageMessageFromFullPath({
|
||||
required String imagePath,
|
||||
String? operationID,
|
||||
@ -273,7 +277,7 @@ class MessageManager {
|
||||
}) =>
|
||||
_channel
|
||||
.invokeMethod(
|
||||
'createSoundMessage',
|
||||
'createSoundMessage',
|
||||
_buildParam({
|
||||
'soundPath': soundPath,
|
||||
"duration": duration,
|
||||
@ -284,6 +288,8 @@ class MessageManager {
|
||||
|
||||
/// Create sound message
|
||||
/// 创建语音消息
|
||||
/// [soundPath] 路径
|
||||
/// [duration] 时长s
|
||||
Future<Message> createSoundMessageFromFullPath({
|
||||
required String soundPath,
|
||||
required int duration,
|
||||
@ -323,6 +329,10 @@ class MessageManager {
|
||||
|
||||
/// Create video message
|
||||
/// 创建视频消息
|
||||
/// [videoPath] 路径
|
||||
/// [videoType] 视频mime类型
|
||||
/// [duration] 时长s
|
||||
/// [snapshotPath] 默认站位图路径
|
||||
Future<Message> createVideoMessageFromFullPath({
|
||||
required String videoPath,
|
||||
required String videoType,
|
||||
@ -362,6 +372,8 @@ class MessageManager {
|
||||
|
||||
/// Create file message
|
||||
/// 创建文件消息
|
||||
/// [filePath] 路径
|
||||
/// [fileName] 文件名
|
||||
Future<Message> createFileMessageFromFullPath({
|
||||
required String filePath,
|
||||
required String fileName,
|
||||
@ -379,6 +391,9 @@ class MessageManager {
|
||||
|
||||
/// Create merger message
|
||||
/// 创建合并消息
|
||||
/// [messageList] 被选中的消息
|
||||
/// [title] 摘要标题
|
||||
/// [summaryList] 摘要内容
|
||||
Future<Message> createMergerMessage({
|
||||
required List<Message> messageList,
|
||||
required String title,
|
||||
@ -398,6 +413,7 @@ class MessageManager {
|
||||
|
||||
/// Create forward message
|
||||
/// 创建转发消息
|
||||
/// [message] 被转发的消息
|
||||
Future<Message> createForwardMessage({
|
||||
required Message message,
|
||||
String? operationID,
|
||||
@ -414,6 +430,9 @@ class MessageManager {
|
||||
|
||||
/// Create location message
|
||||
/// 创建位置消息
|
||||
/// [latitude] 纬度
|
||||
/// [longitude] 经度
|
||||
/// [description] 自定义描述信息
|
||||
Future<Message> createLocationMessage({
|
||||
required double latitude,
|
||||
required double longitude,
|
||||
@ -452,6 +471,8 @@ class MessageManager {
|
||||
|
||||
/// Create quote message
|
||||
/// 创建引用消息
|
||||
/// [text] 回复的内容
|
||||
/// [quoteMsg] 被回复的消息
|
||||
Future<Message> createQuoteMessage({
|
||||
required String text,
|
||||
required Message quoteMsg,
|
||||
@ -483,9 +504,11 @@ class MessageManager {
|
||||
.then((value) => Utils.toObj(value, (map) => Message.fromJson(map)));
|
||||
|
||||
/// Create custom emoji message
|
||||
/// [index] The position of the emoji, such as the position emoji
|
||||
/// [data] Other data, such as url expressions
|
||||
/// 创建自定义表情消息
|
||||
/// [index] The position of the emoji, such as the position emoji(表情的位置,如位置表情)
|
||||
/// [data] Other data, such as url expressions(其他数据,如url表情)
|
||||
/// [index] 位置表情,根据index匹配
|
||||
/// [data] url表情,直接使用url显示
|
||||
Future<Message> createFaceMessage({
|
||||
int index = -1,
|
||||
String? data,
|
||||
@ -571,7 +594,7 @@ class MessageManager {
|
||||
Utils.toObj(value, (map) => SearchResult.fromJson(map)));
|
||||
|
||||
/// Delete message from local and service
|
||||
/// 删除消息
|
||||
/// 删除本地跟服务器的指定的消息
|
||||
Future<dynamic> deleteMessageFromLocalAndSvr({
|
||||
required Message message,
|
||||
String? operationID,
|
||||
@ -584,7 +607,7 @@ class MessageManager {
|
||||
})));
|
||||
|
||||
/// Delete all message from local
|
||||
/// 删除所有消息
|
||||
/// 删除本地所有聊天记录
|
||||
Future<dynamic> deleteAllMsgFromLocal({
|
||||
String? operationID,
|
||||
}) =>
|
||||
@ -595,7 +618,7 @@ class MessageManager {
|
||||
}));
|
||||
|
||||
/// Delete all message from service
|
||||
/// 删除所有消息
|
||||
/// 删除本地跟服务器所有聊天记录
|
||||
Future<dynamic> deleteAllMsgFromLocalAndSvr({
|
||||
String? operationID,
|
||||
}) =>
|
||||
@ -607,6 +630,8 @@ class MessageManager {
|
||||
|
||||
/// Mark conversation message as read
|
||||
/// 标记消息已读
|
||||
/// [conversationID] 会话ID
|
||||
/// [messageIDList] 被标记的消息clientMsgID
|
||||
Future markMessageAsReadByConID({
|
||||
required String conversationID,
|
||||
required List<String> messageIDList,
|
||||
@ -621,7 +646,7 @@ class MessageManager {
|
||||
}));
|
||||
|
||||
/// Clear all c2c history message
|
||||
/// 清空单聊消息记录
|
||||
/// 删除本地跟服务器的单聊聊天记录
|
||||
Future<dynamic> clearC2CHistoryMessageFromLocalAndSvr({
|
||||
required String uid,
|
||||
String? operationID,
|
||||
@ -634,7 +659,7 @@ class MessageManager {
|
||||
}));
|
||||
|
||||
/// Clear all group history
|
||||
/// 清空组消息记录
|
||||
/// 删除本地跟服务器的群聊天记录
|
||||
Future<dynamic> clearGroupHistoryMessageFromLocalAndSvr({
|
||||
required String gid,
|
||||
String? operationID,
|
||||
@ -647,10 +672,12 @@ class MessageManager {
|
||||
}));
|
||||
|
||||
/// Find all history message
|
||||
/// 获取聊天记录
|
||||
/// [userID]接收消息的用户id
|
||||
/// [conversationID] 会话id
|
||||
/// [groupID]接收消息的组id
|
||||
/// 获取聊天记录(以startMsg为节点,新收到的聊天记录),用在全局搜索定位某一条消息,然后此条消息后新增的消息
|
||||
/// [userID] 接收消息的用户id
|
||||
/// [conversationID] 会话id,查询通知时可用
|
||||
/// [groupID] 接收消息的组id
|
||||
/// [startMsg] 从这条消息开始查询[count]条,获取的列表index==length-1为最新消息,所以获取下一页历史记录startMsg=list.last
|
||||
/// [count] 一次拉取的总数
|
||||
Future<List<Message>> getHistoryMessageListReverse({
|
||||
String? userID,
|
||||
String? groupID,
|
||||
|
98
lib/src/manager/im_organization_manager.dart
Normal file
98
lib/src/manager/im_organization_manager.dart
Normal file
@ -0,0 +1,98 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
|
||||
|
||||
class OrganizationManager {
|
||||
MethodChannel _channel;
|
||||
late OnOrganizationListener listener;
|
||||
|
||||
OrganizationManager(this._channel);
|
||||
|
||||
/// Observe organization info changes
|
||||
/// 组织架构发生变化回调
|
||||
Future setOrganizationListener(OnOrganizationListener listener) {
|
||||
this.listener = listener;
|
||||
return _channel.invokeMethod('setOrganizationListener', _buildParam({}));
|
||||
}
|
||||
|
||||
/// Query sub department
|
||||
/// 获取子部门列表
|
||||
Future<List<DeptInfo>> getSubDept({
|
||||
required String departmentID,
|
||||
int offset = 0,
|
||||
int count = 40,
|
||||
String? operationID,
|
||||
}) =>
|
||||
_channel
|
||||
.invokeMethod(
|
||||
'getSubDepartment',
|
||||
_buildParam({
|
||||
'departmentID': departmentID,
|
||||
'offset': offset,
|
||||
'count': count,
|
||||
'operationID': Utils.checkOperationID(operationID),
|
||||
}))
|
||||
.then((value) => Utils.toList(value, (v) => DeptInfo.fromJson(v)));
|
||||
|
||||
/// Get member under a department
|
||||
/// 获取部门下的成员列表
|
||||
Future<List<DeptMemberInfo>> getDeptMember({
|
||||
required String departmentID,
|
||||
int offset = 0,
|
||||
int count = 40,
|
||||
String? operationID,
|
||||
}) =>
|
||||
_channel
|
||||
.invokeMethod(
|
||||
'getDepartmentMember',
|
||||
_buildParam({
|
||||
'departmentID': departmentID,
|
||||
'offset': offset,
|
||||
'count': count,
|
||||
'operationID': Utils.checkOperationID(operationID),
|
||||
}))
|
||||
.then((value) =>
|
||||
Utils.toList(value, (v) => DeptMemberInfo.fromJson(v)));
|
||||
|
||||
/// Get member's department
|
||||
/// 获取成员所在的部门
|
||||
Future<List<UserInDept>> getUserInDept({
|
||||
required String userID,
|
||||
String? operationID,
|
||||
}) =>
|
||||
_channel
|
||||
.invokeMethod(
|
||||
'getUserInDepartment',
|
||||
_buildParam({
|
||||
'userID': userID,
|
||||
'operationID': Utils.checkOperationID(operationID),
|
||||
}))
|
||||
.then((value) => Utils.toList(value, (v) => UserInDept.fromJson(v)));
|
||||
|
||||
/// Get the sub-departments and employees under the department
|
||||
/// 获取部门下的子部门跟员工
|
||||
Future<List<UserInDept>> getDeptMemberAndSubDept({
|
||||
required String departmentID,
|
||||
int departmentOffset = 0,
|
||||
int departmentCount = 40,
|
||||
int memberOffset = 0,
|
||||
int memberCount = 40,
|
||||
String? operationID,
|
||||
}) =>
|
||||
_channel
|
||||
.invokeMethod(
|
||||
'getDepartmentMemberAndSubDepartment',
|
||||
_buildParam({
|
||||
'departmentID': departmentID,
|
||||
'departmentOffset': departmentOffset,
|
||||
'departmentCount': departmentCount,
|
||||
'memberOffset': memberOffset,
|
||||
'memberCount': memberCount,
|
||||
'operationID': Utils.checkOperationID(operationID),
|
||||
}))
|
||||
.then((value) => Utils.toList(value, (v) => UserInDept.fromJson(v)));
|
||||
|
||||
static Map _buildParam(Map param) {
|
||||
param["ManagerName"] = "organizationManager";
|
||||
return param;
|
||||
}
|
||||
}
|
198
lib/src/models/organization_info.dart
Normal file
198
lib/src/models/organization_info.dart
Normal file
@ -0,0 +1,198 @@
|
||||
class DeptInfo {
|
||||
String? departmentID;
|
||||
String? faceURL;
|
||||
String? name;
|
||||
String? parentID;
|
||||
int? order;
|
||||
int? departmentType;
|
||||
int? createTime;
|
||||
int? subDepartmentNum;
|
||||
int? memberNum;
|
||||
String? ex;
|
||||
String? attachedInfo;
|
||||
|
||||
DeptInfo(
|
||||
{this.departmentID,
|
||||
this.faceURL,
|
||||
this.name,
|
||||
this.parentID,
|
||||
this.order,
|
||||
this.departmentType,
|
||||
this.createTime,
|
||||
this.subDepartmentNum,
|
||||
this.memberNum,
|
||||
this.ex,
|
||||
this.attachedInfo});
|
||||
|
||||
DeptInfo.fromJson(Map<String, dynamic> json) {
|
||||
departmentID = json['departmentID'];
|
||||
faceURL = json['faceURL'];
|
||||
name = json['name'];
|
||||
parentID = json['parentID'];
|
||||
order = json['order'];
|
||||
departmentType = json['departmentType'];
|
||||
createTime = json['createTime '];
|
||||
subDepartmentNum = json['subDepartmentNum '];
|
||||
memberNum = json['memberNum '];
|
||||
ex = json['ex '];
|
||||
attachedInfo = json['attachedInfo '];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final data = Map<String, dynamic>();
|
||||
data['departmentID'] = this.departmentID;
|
||||
data['faceURL'] = this.faceURL;
|
||||
data['name'] = this.name;
|
||||
data['parentID'] = this.parentID;
|
||||
data['order'] = this.order;
|
||||
data['departmentType'] = this.departmentType;
|
||||
data['createTime '] = this.createTime;
|
||||
data['subDepartmentNum '] = this.subDepartmentNum;
|
||||
data['memberNum '] = this.memberNum;
|
||||
data['ex '] = this.ex;
|
||||
data['attachedInfo '] = this.attachedInfo;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class DeptMemberInfo {
|
||||
String? userID;
|
||||
String? nickname;
|
||||
String? englishName;
|
||||
String? faceURL;
|
||||
int? gender;
|
||||
String? mobile;
|
||||
String? telephone;
|
||||
int? birth;
|
||||
String? email;
|
||||
String? departmentID;
|
||||
int? order;
|
||||
String? position;
|
||||
int? leader;
|
||||
int? status;
|
||||
int? createTime;
|
||||
String? ex;
|
||||
String? attachedInfo;
|
||||
|
||||
DeptMemberInfo(
|
||||
{this.userID,
|
||||
this.nickname,
|
||||
this.englishName,
|
||||
this.faceURL,
|
||||
this.gender,
|
||||
this.mobile,
|
||||
this.telephone,
|
||||
this.birth,
|
||||
this.email,
|
||||
this.departmentID,
|
||||
this.order,
|
||||
this.position,
|
||||
this.leader,
|
||||
this.status,
|
||||
this.createTime,
|
||||
this.ex,
|
||||
this.attachedInfo});
|
||||
|
||||
DeptMemberInfo.fromJson(Map<String, dynamic> json) {
|
||||
userID = json['userID'];
|
||||
nickname = json['nickname'];
|
||||
englishName = json['englishName'];
|
||||
faceURL = json['faceURL '];
|
||||
gender = json['gender '];
|
||||
mobile = json['mobile '];
|
||||
telephone = json['telephone '];
|
||||
birth = json['birth '];
|
||||
email = json['email '];
|
||||
departmentID = json['departmentID '];
|
||||
order = json['order '];
|
||||
position = json['position '];
|
||||
leader = json['leader '];
|
||||
status = json['status '];
|
||||
createTime = json['createTime '];
|
||||
ex = json['ex '];
|
||||
attachedInfo = json['attachedInfo '];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['userID'] = this.userID;
|
||||
data['nickname'] = this.nickname;
|
||||
data['englishName'] = this.englishName;
|
||||
data['faceURL '] = this.faceURL;
|
||||
data['gender '] = this.gender;
|
||||
data['mobile '] = this.mobile;
|
||||
data['telephone '] = this.telephone;
|
||||
data['birth '] = this.birth;
|
||||
data['email '] = this.email;
|
||||
data['departmentID '] = this.departmentID;
|
||||
data['order '] = this.order;
|
||||
data['position '] = this.position;
|
||||
data['leader '] = this.leader;
|
||||
data['status '] = this.status;
|
||||
data['createTime '] = this.createTime;
|
||||
data['ex '] = this.ex;
|
||||
data['attachedInfo '] = this.attachedInfo;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class UserInDept {
|
||||
DeptInfo? department;
|
||||
DeptMemberInfo? member;
|
||||
|
||||
UserInDept({this.department, this.member});
|
||||
|
||||
UserInDept.fromJson(Map<String, dynamic> json) {
|
||||
department = json['department'] != null
|
||||
? DeptInfo.fromJson(json['department'])
|
||||
: null;
|
||||
member =
|
||||
json['member'] != null ? DeptMemberInfo.fromJson(json['member']) : null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final data = Map<String, dynamic>();
|
||||
if (this.department != null) {
|
||||
data['department'] = this.department!.toJson();
|
||||
}
|
||||
if (this.member != null) {
|
||||
data['member'] = this.member!.toJson();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class DeptMemberAndSubDept {
|
||||
List<DeptInfo>? departmentList;
|
||||
List<DeptMemberInfo>? departmentMemberList;
|
||||
|
||||
DeptMemberAndSubDept({this.departmentList, this.departmentMemberList});
|
||||
|
||||
DeptMemberAndSubDept.fromJson(Map<String, dynamic> json) {
|
||||
if (json['departmentList'] != null) {
|
||||
departmentList = <DeptInfo>[];
|
||||
json['departmentList'].forEach((v) {
|
||||
departmentList!.add(DeptInfo.fromJson(v));
|
||||
});
|
||||
}
|
||||
if (json['departmentMemberList'] != null) {
|
||||
departmentMemberList = <DeptMemberInfo>[];
|
||||
json['departmentMemberList'].forEach((v) {
|
||||
departmentMemberList!.add(DeptMemberInfo.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final data = Map<String, dynamic>();
|
||||
if (this.departmentList != null) {
|
||||
data['departmentList'] =
|
||||
this.departmentList!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
if (this.departmentMemberList != null) {
|
||||
data['departmentMemberList'] =
|
||||
this.departmentMemberList!.map((v) => v.toJson()).toList();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
name: flutter_openim_sdk
|
||||
description: An instant messaging plug-in that supports Android and IOS. And the server is also all open source.
|
||||
version: 2.0.8
|
||||
version: 2.0.9
|
||||
homepage: https://www.rentsoft.cn
|
||||
repository: https://github.com/OpenIMSDK/Open-IM-SDK-Flutter
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user