diff --git a/docs/.vuepress/public/images/demo/web_react_login.png b/docs/.vuepress/public/images/demo/web_react_login.png index d55cba5d..0d852a4d 100644 Binary files a/docs/.vuepress/public/images/demo/web_react_login.png and b/docs/.vuepress/public/images/demo/web_react_login.png differ diff --git a/docs/.vuepress/public/images/uikit/chatuikit/feature/web/common/message_read_receipt.png b/docs/.vuepress/public/images/uikit/chatuikit/feature/web/common/message_read_receipt.png index 2423e920..c612c9c8 100644 Binary files a/docs/.vuepress/public/images/uikit/chatuikit/feature/web/common/message_read_receipt.png and b/docs/.vuepress/public/images/uikit/chatuikit/feature/web/common/message_read_receipt.png differ diff --git "a/docs/.vuepress/public/images/uikit/chatuikit/feature/web/common/\344\270\213\350\275\275.zip" "b/docs/.vuepress/public/images/uikit/chatuikit/feature/web/common/\344\270\213\350\275\275.zip" new file mode 100644 index 00000000..4f6c3b86 Binary files /dev/null and "b/docs/.vuepress/public/images/uikit/chatuikit/feature/web/common/\344\270\213\350\275\275.zip" differ diff --git a/docs/.vuepress/public/images/web/message_receipt.png b/docs/.vuepress/public/images/web/message_receipt.png new file mode 100644 index 00000000..dce0c84b Binary files /dev/null and b/docs/.vuepress/public/images/web/message_receipt.png differ diff --git a/docs/.vuepress/sidebar/document.ts b/docs/.vuepress/sidebar/document.ts index 4ff03a56..8f37b84b 100644 --- a/docs/.vuepress/sidebar/document.ts +++ b/docs/.vuepress/sidebar/document.ts @@ -25,9 +25,6 @@ const documentSidebar = [ { text: 'Demo(EaseIM App)体验', link: 'demo.html', except: ['web', 'windows', 'react-native', 'flutter', 'unity', 'server-side'] }, { text: '快速开始', link: 'quickstart.html', except: ['windows', 'react-native', 'flutter', 'unity', 'server-side'] }, { text: '快速开始', link: 'quickstart.html', only: ['windows', 'react-native', 'flutter', 'unity'] }, - { text: '按需导入 SDK(推荐)', link: 'import_sdk_minicore.html', only: ['web'] }, - { text: 'SDK 集成概述', link: 'overview.html', only: ['android', 'ios', 'web', 'harmonyos', 'flutter'] }, - { text: 'SDK 集成概述', link: 'overview.html', only: ['windows', 'react-native', 'unity'] }, // { text: '私有云 SDK 集成配置', link: 'privatecloud.html', except: ['windows', 'server-side', 'react-native', 'flutter', 'unity'] }, { text: 'SDK 更新日志', link: 'releasenote.html', except: ['server-side']}, /*{ text: 'API reference', link: 'apireference.html', only: ['android', 'ios', 'web', 'windows', 'react-native', 'flutter', 'unity']},*/ @@ -40,8 +37,19 @@ const documentSidebar = [ except: ['applet', 'electron','linux'] }, { - text: '基础功能', + text: '用户指南', children: [ + { text: '集成 SDK', link: 'integration.html' }, + { text: '初始化', link: 'initialization.html' }, + { + text: '登录', + collapsible: true, + children: [ + { text: '登录介绍', link: 'login.html' }, + { text: '连接', link: 'connection.html' }, + { text: '多设备登录', link: 'multi_device.html' }, + ] + }, { text: '消息管理', collapsible: true, @@ -53,6 +61,7 @@ const documentSidebar = [ { text: '搜索消息', link: 'message_search.html', except: ['web', 'harmonyos']}, { text: '消息回执', link: 'message_receipt.html'}, { text: '修改消息', link: 'message_modify.html'}, + { text: '消息表情回复', link: 'reaction.html' }, { text: '转发消息', link: 'message_forward.html', except: ['web']}, { text: '导入和插入消息', link: 'message_import_insert.html', except: ['web']}, { text: '更新消息', link: 'message_update.html', except: ['web']}, @@ -60,6 +69,7 @@ const documentSidebar = [ { text: '置顶消息', link: 'message_pin.html', except: ['harmonyos']}, { text: '翻译消息', link: 'message_translation.html', except: ['harmonyos']}, { text: '只投在线用户', link: 'message_deliver_only_online.html'}, + { text: '消息审核(举报)', link: 'moderation.html', except: ['harmonyos']}, { text: '获取消息流量统计', link: 'message_traffic_statis.html', only: ['android', 'ios'] }, ] }, @@ -70,15 +80,13 @@ const documentSidebar = [ { text: '会话介绍', link: 'conversation_overview.html' }, { text: '会话列表', link: 'conversation_list.html' }, { text: '本地会话', link: 'conversation_local.html', only: ['web'] }, - { text: '会话已读回执', link: 'conversation_receipt.html', only: ['android', 'ios', 'web'] }, + { text: '会话已读回执', link: 'conversation_receipt.html' }, { text: '会话未读数', link: 'conversation_unread.html', except: ['web'] }, { text: '置顶会话', link: 'conversation_pin.html' }, { text: '会话标记', link: 'conversation_mark.html' }, { text: '删除会话', link: 'conversation_delete.html' }, ] }, - { text: '管理用户属性', link: 'userprofile.html' }, - { text: '管理用户关系', link: 'user_relationship.html' }, { text: '群组管理', collapsible: true, @@ -87,6 +95,8 @@ const documentSidebar = [ { text: '创建和管理群组', link: 'group_manage.html' }, { text: '管理群组成员', link: 'group_members.html' }, { text: '管理群组属性', link: 'group_attributes.html' }, + { text: '管理子区', link: 'thread.html', except: ['harmonyos'] }, + { text: '管理子区消息', link: 'thread_message.html', except: ['harmonyos'] } ] }, { @@ -99,13 +109,16 @@ const documentSidebar = [ { text: '管理聊天室属性', link: 'room_attributes.html' }, ] }, - ], - except: ['applet', 'server-side', 'electron','linux'] - }, - { - text: '进阶功能', - children: [ - { + { + text: '用户相关', + collapsible: true, + children: [ + { text: '用户关系', link: 'user_relationship.html' }, + { text: '用户属性', link: 'userprofile.html' }, + { text: '在线状态订阅', link: 'presence.html' }, + ] + }, + { text: '离线推送', collapsible: true, children: [ @@ -139,33 +152,33 @@ const documentSidebar = [ { text: 'FAQ', link: 'push/push_solution.html', only: ['android', 'ios','harmonyos']}, ] }, - { text: '登录多个设备', link: 'multi_device.html' }, - { text: '管理在线状态订阅', link: 'presence.html' }, - { text: '消息表情回复', link: 'reaction.html' }, - { - text: '子区管理', - collapsible: true, - children: [ - { text: '管理子区', link: 'thread.html', except: ['harmonyos'] }, - { text: '管理子区消息', link: 'thread_message.html', except: ['harmonyos'] } - ] - }, - { text: '消息审核(举报)', link: 'moderation.html', except: ['harmonyos']}, ], except: ['applet','server-side','electron','linux'] }, { - text: '其他', + text: '错误排查', children: [ { text: '错误码', link: 'error.html' }, - //{ text: 'EaseIMKit 使用指南', link: 'easeimkit.html', except: ['web', 'windows', 'react-native', 'flutter', 'unity'] }, - { text: 'EaseCallKit 使用指南', link: 'easecallkit.html', except: ['web', 'windows', 'react-native', 'flutter', 'unity', 'harmonyos'] }, + { text: '日志', link: 'log.html', except: ['flutter'] }, + ], + except: ['applet', 'server-side','electron','linux'] + }, + { + text: 'CallKit 使用指南', + children: [ + { text: 'EaseCallKit 使用指南', link: 'easecallkit.html', only: ['android', 'ios'] }, { text: 'CallKit 使用指南', link: 'easecallkit.html', only: ['web'] }, - { text: '苹果隐私策略', link: 'privacy_policy.html', only: ['ios'] }, ], except: ['applet', 'server-side','electron','linux'] }, + { + text: '苹果隐私策略', + children: [ + { text: '苹果隐私策略', link: 'privacy_policy.html', only: ['ios'] }, + ], + except: ['applet', 'server-side','electron','linux'] + }, { text: '精简版 SDK', children: [ @@ -182,22 +195,30 @@ const documentSidebar = [ only: ['applet'] }, { - text: '集成介绍', + text: '用户指南', children: [ - { text: '微信小程序', link: 'wechat.html' }, - { text: 'QQ 小程序', link: 'qq.html' }, - { text: '百度小程序', link: 'baidu.html' }, - { text: '抖音小程序', link: 'bytedance.html' }, - { text: '支付宝小程序', link: 'alipay.html' }, - { text: 'Uniapp 全平台', link: 'uniapp.html' }, - { text: '小程序 API 文档', link: 'apidoc.html' }, - ], - only: ['applet'] - }, - { - text: '基本功能', - children: [ - { text: '初始化及登录', link: 'initialization.html' }, + { + text: '集成介绍', + collapsible: true, + children: [ + { text: '微信小程序', link: 'wechat.html' }, + { text: 'QQ 小程序', link: 'qq.html' }, + { text: '百度小程序', link: 'baidu.html' }, + { text: '抖音小程序', link: 'bytedance.html' }, + { text: '支付宝小程序', link: 'alipay.html' }, + { text: 'Uniapp 全平台', link: 'uniapp.html' }, + ], + }, + { text: '初始化', link: 'initialization.html' }, + { + text: '登录', + collapsible: true, + children: [ + { text: '登录介绍', link: 'login.html' }, + { text: '连接', link: 'connection.html' }, + { text: '多设备登录', link: 'multi_device.html' }, + ], + }, { text: '消息管理', collapsible: true, @@ -207,11 +228,13 @@ const documentSidebar = [ { text: '获取历史消息', link: 'message_retrieve.html' }, { text: '撤回消息', link: 'message_recall.html' }, { text: '消息回执', link: 'message_receipt.html' }, + { text: '消息表情回复', link: 'reaction.html' }, { text: '修改消息', link: 'message_modify.html' }, { text: '删除消息', link: 'message_delete.html' }, { text: '置顶消息', link: 'message_pin.html' }, { text: '翻译消息', link: 'message_translation.html' }, - { text: '只投在线用户', link: 'message_deliver_only_online.html'}, + { text: '只投在线用户', link: 'message_deliver_only_online.html'}, + { text: '消息审核(举报)', link: 'moderation.html'}, ] }, { @@ -226,8 +249,6 @@ const documentSidebar = [ { text: '删除会话', link: 'conversation_delete.html'}, ] }, - { text: '管理用户属性', link: 'userprofile.html' }, - { text: '管理用户关系', link: 'user_relationship.html' }, { text: '群组管理', collapsible: true, @@ -236,6 +257,14 @@ const documentSidebar = [ { text: '创建和管理群组', link: 'group_manage.html' }, { text: '管理群组成员', link: 'group_members.html' }, { text: '管理群组属性', link: 'group_attributes.html' }, + { + text: '子区管理', + collapsible: true, + children: [ + { text: '管理子区', link: 'thread.html' }, + { text: '管理子区消息', link: 'thread_message.html' } + ] + }, ] }, { @@ -248,12 +277,15 @@ const documentSidebar = [ { text: '管理聊天室属性', link: 'room_attributes.html' }, ] }, - ], - only: ['applet'] - }, - { - text: '进阶功能', - children: [ + { + text: '用户相关', + collapsible: true, + children: [ + { text: '用户关系', link: 'user_relationship.html' }, + { text: '用户属性', link: 'userprofile.html' }, + { text: '在线状态订阅', link: 'presence.html' }, + ] + }, { text: '离线推送', collapsible: true, children: [ @@ -265,25 +297,21 @@ const documentSidebar = [ { text: 'uni-app 离线推送', link: 'push/uniapp_push.html' } ] }, - { text: '登录多个设备', link: 'multi_device.html' }, - { text: '管理在线状态订阅', link: 'presence.html' }, - { text: '消息表情回复', link: 'reaction.html' }, - { - text: '子区管理', - collapsible: true, - children: [ - { text: '管理子区', link: 'thread.html' }, - { text: '管理子区消息', link: 'thread_message.html' } - ] - }, - { text: '消息审核(举报)', link: 'moderation.html'}, ], only: ['applet'] }, { - text: '其他帮助', + text: '错误排查', children: [ { text: '错误码', link: 'error.html' }, + { text: '日志', link: 'log.html' }, + ], + only: ['applet'] + }, + { + text: '其他帮助', + children: [ + { text: '小程序 API 文档', link: 'apidoc.html' }, { text: 'Uniapp 生成原生 Android、iOS 应用', link: 'uniappnativeapp.html' }, { text: '小程序模板使用指南', link: 'uniappuikit.html' }, { text: '如何配置服务器域名', link: 'serverconfig.html' }, diff --git a/docs/.vuepress/sidebar/index.ts b/docs/.vuepress/sidebar/index.ts index f93ba934..dcf351b9 100644 --- a/docs/.vuepress/sidebar/index.ts +++ b/docs/.vuepress/sidebar/index.ts @@ -20,6 +20,7 @@ export const zhSidebar = sidebar({ children: [ { text: "产品概述", link: "introduction.html" }, { text: "特性介绍", + collapsible: true, children: [ { text: "消息", collapsible: true, @@ -67,22 +68,6 @@ export const zhSidebar = sidebar({ { text: "Demo 体验及源码地址", link: "demo_sourcecode_address.html" }, ], }, - { - text: "快速开始", - children: [ - { text: "使用环信 App Token 鉴权", link: "easemob_app_token.html" }, - { text: "使用环信 User Token 鉴权", link: "easemob_user_token.html" }, - { text: "快速开始 Android", link: "/document/android/quickstart.html" }, - { text: "快速开始 iOS", link: "/document/ios/quickstart.html" }, - { text: "快速开始 Web", link: "/document/web/quickstart.html" }, - { text: "快速开始 Applet", link: "/document/applet/wechat.html" }, - { text: "快速开始 HarmonyOS", link: "/document/harmonyos/quickstart.html" }, - { text: "快速开始 Flutter", link: "/document/flutter/quickstart.html" }, - { text: "快速开始 React Native", link: "/document/react-native/quickstart.html" }, - { text: "快速开始 Unity", link: "/document/unity/quickstart.html" }, - { text: "快速开始 Windows", link: "/document/windows/quickstart.html" }, - ], - }, { text: "内容审核", children: [ @@ -338,6 +323,7 @@ export const zhSidebar = sidebar({ children: [ { text: "群 @ 消息", link: "solution_common/group_@.html" }, { text: "消息引用", link: "solution_common/message_quote.html" }, + { text: "实现输入指示器", link: "solution_common/typing_indication.html" }, { text: "迁移到环信", link: "solution_common/migrate_to_easemob.html" }, ], }, diff --git a/docs/README.md b/docs/README.md index c542bf13..55ecefbe 100644 --- a/docs/README.md +++ b/docs/README.md @@ -106,6 +106,9 @@ starter: - icon: /sdk/iOS.svg link: /document/ios/easecallkit.html text: iOS + - icon: /sdk/web.svg + link: /document/web/easecallkit.html + text: Web projects: - title: SDK/服务端功能 features: @@ -351,10 +354,10 @@ projects: link: /document/ios/message_forward.html - icon: /sdk/web.svg text: Web - link: /document/web/message_send_receive.html#发送合并消息 + link: /document/web/message_send_receive.html#发送和接收合并消息 - icon: /sdk/applet.svg text: 小程序 - link: /document/applet/message_send_receive.html#发送合并消息 + link: /document/applet/message_send_receive.html#发送和接收合并消息 - icon: /sdk/harmonyos.svg text: HarmonyOS link: /document/harmonyos/message_forward.html diff --git a/docs/document/android/connection.md b/docs/document/android/connection.md new file mode 100644 index 00000000..8dd95080 --- /dev/null +++ b/docs/document/android/connection.md @@ -0,0 +1,67 @@ +# 连接 + +应用客户端成功连接到环信服务器后,才能使用环信即时通讯 SDK 的收发消息等功能。 + +你调用 `loginWithToken` 或 `login` 方法登录后,客户端 SDK 会自动连接环信服务器。关于登录详情,请参见[登录文档](login.html)。 + +## 监听连接状态 + +你可以通过注册连接监听确认连接状态。 + +```java +EMConnectionListener connectionListener = new EMConnectionListener() { + @Override + public void onConnected() { + + } + @Override + public void onDisconnected(int errorCode) { + + } + + @Override + public void onLogout(int errorCode) { + + } + + @Override + public void onTokenWillExpire() { + + } + + @Override + public void onTokenExpired() { + + } + // 连接成功,开始从服务器拉取离线消息时触发。 + // 注意:如果本次登录服务器没有离线消息,不会触发该回调。 + @Override + public void onOfflineMessageSyncStart() { + + } + // 离线用户上线后从服务器拉取离线消息结束时触发。 + // 注意:如果再拉取离线过程中因网络或其他原因导致连接断开,不会触发该回调。 + @Override + public void onOfflineMessageSyncFinish() { + + } +}; +// 注册连接状态监听 +EMClient.getInstance().addConnectionListener(connectionListener); +// 移除连接状态监听 +EMClient.getInstance().removeConnectionListener(connectionListener); +``` + +## 自动重连 + +登录后,如果由于网络信号弱、切换网络等引起的连接中断,SDK 会自动尝试重连。重连成功或者失败时分别会收到 `onConnected` 和 `onDisconnected` 通知。 + +不过,SDK 在以下情况下会停止自动重连。你需要调用 `login` 方法登录。 + +- 用户调用了 SDK 的登出方法 `logout` 主动退出登录。 +- 登录时鉴权错误,例如, token 无效(错误码 104)或已过期(错误码 108)。 +- 用户在其他的设备上更改了密码,导致此设备上自动登录失败,提示错误码 216。 +- 用户的账号被从服务器端删除,提示错误码 207。 +- 用户在另一设备登录,将当前设备上登录的用户踢出,提示错误码 206。 +- 用户登录设备数量超过限制,提示错误码 214。 +- 应用程序的日活跃用户数量(DAU)或月活跃用户数量(MAU)达到上限,提示错误码 8。 \ No newline at end of file diff --git a/docs/document/android/conversation_receipt.md b/docs/document/android/conversation_receipt.md index e71e0f21..68880530 100644 --- a/docs/document/android/conversation_receipt.md +++ b/docs/document/android/conversation_receipt.md @@ -16,6 +16,13 @@ 2. 消息接收方进入会话页面,阅读消息后,调用 `ackConversationRead` 方法发送会话已读回执。 3. 消息发送方通过监听 `OnConversationRead` 回调接收会话已读回执。 +## 前提条件 + +开始前,请确保满足以下条件: + +- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 +- 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 + ## 实现方法 参考以下步骤在单聊中实现会话已读回执: diff --git a/docs/document/android/easecallkit.md b/docs/document/android/easecallkit.md index 3cd958cf..31fb46f5 100644 --- a/docs/document/android/easecallkit.md +++ b/docs/document/android/easecallkit.md @@ -8,7 +8,7 @@ **利用 `EaseCallKit` 通话过程中,使用环信 ID 加入频道,方便音视频视图中显示用户名。如果用户不使用 `EaseCallKit` 而直接调用声网 API,也可以直接使用数字 UID 加入频道。** -:::notice +:::tip 本 UI 库只和移动端 3.8.0 及以上版本 Demo 互通。3.8.1 的 UI 库使用声网数字 uid 加入频道,而 3.8.0 使用字符串加入频道,3.8.1 版本不与 3.8.0 互通,Demo 中 EaseCallKit 使用的 token 和 UID 均由你自己生成。若你需要使用声网对应的音视频服务,需单独在声网申请。 ::: @@ -59,7 +59,7 @@ EaseCallKit 集成在环信开源 IM Demo 中,你可以通过进入 [环信 De implementation 'io.hyphenate:ease-call-kit:3.8.9' ``` -:::notice +:::tip `EaseCallKit` 必须依赖环信 IM SDK (即 hyphenate-chat) ,因而在使用 `EaseCallKit` 时必须同时添加环信 IM SDK 依赖。 ::: diff --git a/docs/document/android/group_manage.md b/docs/document/android/group_manage.md index bce7842e..7ce149c1 100644 --- a/docs/document/android/group_manage.md +++ b/docs/document/android/group_manage.md @@ -91,7 +91,7 @@ EMClient.getInstance().groupManager().createGroup(groupName, desc, allMembers, r 仅群主可以调用 `destroyGroup` 方法解散群组。群组解散时,其他群组成员收到 `EMGroupChangeListener#onGroupDestroyed` 回调并被踢出群组。 -:::notice +:::tip 该操作是危险操作,解散群组后,将删除本地数据库及内存中的群相关信息及群会话。 ::: diff --git a/docs/document/android/group_members.md b/docs/document/android/group_members.md index 173ef0e1..6bd45fc3 100644 --- a/docs/document/android/group_members.md +++ b/docs/document/android/group_members.md @@ -232,7 +232,7 @@ EMClient.getInstance().groupManager().asyncRemoveUsersFromGroup("GroupId", userL 你可调用 `EMGroupManager#asyncFetchGroupMembersAttributes` 方法根据指定的属性 key 获取多个群成员的自定义属性。 -:::notice +:::tip 每次最多可获取 10 个群成员的自定义属性。 ::: diff --git a/docs/document/android/initialization.md b/docs/document/android/initialization.md new file mode 100644 index 00000000..6cb7476b --- /dev/null +++ b/docs/document/android/initialization.md @@ -0,0 +1,24 @@ +# SDK 初始化 + +初始化是使用 SDK 的必要步骤,需在所有接口方法调用前完成。 + +如果进行多次初始化操作,只有第一次初始化以及相关的参数生效。 + +:::tip +需要在主进程中进行初始化。 +::: + +## 前提条件 + +有效的环信即时通讯 IM 开发者账号和 App key,详见[环信即时通讯云控制台的相关文档](/product/enable_and_configure_IM.html#创建应用)。 + +## 初始化 + +初始化示例代码: + +```java +EMOptions options = new EMOptions(); +options.setAppKey("Your appkey"); +......// 其他 EMOptions 配置。 +EMClient.getInstance().init(context, options); +``` diff --git a/docs/document/android/integration.md b/docs/document/android/integration.md new file mode 100644 index 00000000..8aa916b6 --- /dev/null +++ b/docs/document/android/integration.md @@ -0,0 +1,163 @@ +# 集成 SDK + +本文介绍如何将环信即时通讯 IM SDK 集成到你的 Android 项目。 + +## 开发环境要求 + +- Android Studio 3.0 或以上版本; +- Android SDK API 等级 21 或以上; +- Android 5.0 或以上版本的设备。 + +## 集成 SDK + +选择如下任意一种方式将环信即时通讯 IM SDK 集成到你的项目中。 + +:::tip + +1. 以下集成方式只需选择一种,同时使用多种集成方式可能会报错。 +2. 请点击查看[发版说明](releasenote.html)获得最新版本号。 + +::: + +### 方法一:使用 mavenCentral 自动集成 + +:::tip + +该方法仅适用于 v3.8.2 或以上版本。 + +::: + +1. 在项目的 `build.gradle` 中添加 `mavenCentral()` 仓库。 + +```gradle +buildscript { + repositories { + ... + mavenCentral() + } + ... +} + +allprojects { + repositories { + ... + mavenCentral() + } +} +``` + +2. 在 `module` 的 `build.gradle` 中添加如下依赖: + +```gradle +... +dependencies { + ... + // x.y.z 请填写具体版本号,必须为 3.8.2 或以上版本。 + // 可通过 SDK 发版说明获得最新版本号。 + implementation 'io.hyphenate:hyphenate-chat:x.x.x' +} +``` + +:::tip +如果使用 3.8.0 之前的版本,gradle 依赖需要按照下面格式添加: +::: + +```gradle +implementation 'io.hyphenate:hyphenate-sdk:3.7.5' // 完整版本,包含音视频功能 + +implementation 'io.hyphenate:hyphenate-sdk-lite:3.7.5' // 精简版,只包含IM功能 +``` + +### 方法二:手动复制 SDK 文件 + +打开 SDK 下载页面,获取最新版的环信即时通讯 IM Android SDK,然后解压。 + +![img](@static/images/android/sdk-files.png) + +将 SDK 包内 libs 路径下的如下文件,拷贝到你的项目路径下: + +| 文件或文件夹 | 项目路径 | +| :------------------- | :--------------------- | +| `hyphenatechat_xxx.jar` 文件 | `/app/libs/ ` | +| `arm64-v8a` 文件夹 | `/app/src/main/jniLibs/` | +| `armeabi-v7a` 文件夹 | `/app/src/main/jniLibs/` | +| `x86` 文件夹 | `/app/src/main/jniLibs/` | +| `x86_64` 文件夹 | `/app/src/main/jniLibs/` | + +如果对生成的 `apk` 大小比较敏感,我们建议使用 `jar` 方式,并且手工拷贝 `so`,而不是使用 `Aar`,因为 `Aar` 方式会把各个平台的 `so` 文件都包含在其中。采用 `jar` 方式,可以仅保留一个 `ARCH` 目录,建议仅保留 `armeabi-v7a`,这样虽然在对应平台执行的速度会降低,但是能有效减小 `apk` 的大小。 + +### 方法三:动态加载 .so 库文件 + +为了减少应用安装包的大小,SDK 提供了 `EMOptions#setNativeLibBasePath` 方法支持动态加载 SDK 所需的 `.so` 文件。以 SDK 4.5.0 为例,`.so` 文件包括 `libcipherdb.so` 和 `libhyphenate.so` 两个文件。**从 4.11.0 开始,`.so`文件还包含 `libaosl.so` 文件**。 + +该功能的实现步骤如下: + +1. 下载最新版本的 SDK 并解压缩。 +2. 集成 `hyphenatechat_4.5.0.jar` 到你的项目中。 +3. 将所有架构的 `.so` 文件上传到你的服务器,并确保应用程序可以通过网络下载目标架构的 `.so` 文件。 +4. 应用运行时,会检查 `.so` 文件是否存在。如果未找到,应用会下载该 `.so` 文件并将其保存到你自定义的应用程序的私有目录中。 +5. 调用 `EMClient#init` 初始化时,将 `.so` 文件所在的 app 私有目录作为参数设置进 `EMOptions#setNativeLibBasePath` 方法中。 +6. 调用 `EMClient#init` 初始化后,SDK 会自动从指定路径加载 `.so` 文件。 + +:::tip +1. 该方法仅适合手动集成 Android SDK,不适用于通过 Maven Central 集成。 +2. so 库的路径取决于 `EMOptions#setNativeLibBasePath` 方法的 `path` 参数: +- 若设置了 `path` 参数,SDK 内部会使用 `System.load` 从设置的路径下搜索和加载 so 库。该路径必须为有效的 app 的私有目录路径。 +- `path` 参数为空或者不调用该方法时,SDK 内部会使用 `system.loadLibrary` 从系统默认路径中搜索并加载 so 库。 +::: + +```java +//假设用户已经通过动态下发的方式,将环信 SDK 中的 libcipherdb.so 和 libhyphenate.so 两个 so 库,放到 app 的 /data/data/packagename/files 目录下。 +String filesPath = mContext.getFilesDir().getAbsolutePath(); + +EMOptions options = new EMOptions(); +options.setNativeLibBasePath(filesPath); + +EMClient.getInstance().init(mContext, options); + +``` + +### 添加项目权限 + +根据场景需要,在 `/app/src/main/AndroidManifest.xml` 文件中添加如下行,获取相应的设备权限: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +### 防止代码混淆 + +在 `app/proguard-rules.pro` 文件中添加如下行,防止混淆 SDK 的代码: + +```java +-keep class com.hyphenate.** {*;} +-dontwarn com.hyphenate.** +``` \ No newline at end of file diff --git a/docs/document/android/log.md b/docs/document/android/log.md new file mode 100644 index 00000000..b1fb04d4 --- /dev/null +++ b/docs/document/android/log.md @@ -0,0 +1,27 @@ +# SDK 日志 + +环信即时通讯 IM 日志记录 SDK 相关的信息和事件。环信技术支持团队帮你排查问题时可能会请你发送 SDK 日志。 + +## 输出信息到日志文件 + +默认情况下,SDK 最多可生成和保存三个文件,`easemob.log` 和两个 `easemob_YYYY-MM-DD_HH-MM-SS.log` 文件。这些文件为 UTF-8 编码,每个不超过 2 MB。SDK 会将最新的日志写入 `easemob.log` 文件,写满时则会将其重命名为对应时间点的 `easemob_YYYY-MM-DD_HH-MM-SS.log` 文件,若日志文件超过三个,则会删除最早的文件。 + +例如,SDK 在 2024 年 1 月 1 日上午 8:00:00 记录日志时会生成 `easemob.log` 文件,若在 8:30:00 将 `easemob.log` 文件写满则会将其重命名为 `easemob_2024-01-01_08-30-00.log` 文件,随后在 9:30:30 和 10:30:30 分别生成了 `easemob_2024-01-01_09-30-30.log` 和 `easemob_2024-01-01_10-30-30.log` 文件,则此时 `easemob_2024-01-01_08-30-00.log` 文件会被移除。 + +SDK 默认输出调试信息(所有日志,包括调试信息、警告和错误),如果只需输出错误日志,需要关闭调试模式。 + +```java +// 需要在 SDK 初始化后调用 +EMClient.getInstance().setDebugMode(false); +``` + +## 获取本地日志 + +打开以下目录,获取本地日志。在下列代码中,你需要进行如下替换: + +- `{应用包名}` 替换为应用的包名,例如 `com.hyphenate.chatuidemo`。 +- `{App Key}` 需要替换为应用的环信 App Key。 + +```shell +adb pull /sdcard/android/data/{应用包名}/{App Key}/core_log +``` \ No newline at end of file diff --git a/docs/document/android/login.md b/docs/document/android/login.md new file mode 100644 index 00000000..25d14676 --- /dev/null +++ b/docs/document/android/login.md @@ -0,0 +1,127 @@ +# 登录 + +初始化 IM SDK 后,你需要首先调用接口登录。登录成功后,才能使用 IM 的功能。 + +## 用户注册 + +用户注册模式分为以下两种: + +- 开放注册:一般在体验 Demo 和测试环境时使用,正式环境中不推荐使用该方式注册环信账号。要使用开放注册,需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**开放注册**。只有打开该开关,才能使用客户端或 [REST API](/document/server-side/account_system.html#开放注册单个用户)开放注册用户。 + +示例代码如下所示: + + ```java +// 注册失败会抛出 HyphenateException。 +EMClient.getInstance().createAccount(mAccount, mPassword);// 同步方法。 +``` + +- 授权注册:通过环信提供的 REST API 注册环信用户账号,注册后保存到你的服务器或返给客户端。要使用授权注册,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**授权注册**。相关的 REST API 介绍,详见[授权注册单个用户](/document/server-side/account_system.html#授权注册单个用户)和[批量授权注册用户](/document/server-side/account_system.html#批量授权注册用户)的接口介绍。 + +除此以外,可以在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建正式环境下和测试环境下的用户,详见[创建用户相关介绍](/product/enable_and_configure_IM.html#创建-im-用户)。 + +## 主动登录 + +1. **用户 ID + token** 是更加安全的登录方式。 + +测试环境下,你在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建用户后,环信服务器会自动为这些用户分配用户 Token,详见[测试环境下创建用户的介绍](/product/enable_and_configure_IM.html#测试环境)。 + +使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 + +```java +EMClient.getInstance().loginWithToken(mAccount, mToken, new EMCallBack() { + // 登录成功回调 + @Override + public void onSuccess() { + + } + + // 登录失败回调,包含错误信息 + @Override + public void onError(int code, String error) { + + } +}); +``` + +2. **用户 ID + 密码**登录是传统的登录方式。用户名和密码均由你的终端用户自行决定,密码需要符合[密码规则要求](/document/server-side/account_system.html#开放注册单个用户)。 + +```java +EMClient.getInstance().login(mAccount, mPassword, new EMCallBack() { + // 登录成功回调 + @Override + public void onSuccess() { + + } + + // 登录失败回调,包含错误信息 + @Override + public void onError(int code, String error) { + + } + +}); +``` + +## 自动登录 + +初始化时,你可以设置 `EMOptions#setAutoLogin` 选项确定是否自动登录。如果设置为自动登录,则登录成功之后,后续初始化 SDK 时会自动登录。 + +## 获取当前登录的用户 + +你可以调用 `EMClient#getCurrentUser` 方法获取当前登录用户的用户 ID。 + +## 获取登录状态 + +你可以调用 `EMClient#isLoggedIn` 方法获取当前用户的登录状态。 + +## 退出登录 + +你可以调用 `logout` 方法退出登录。退出登录后,你不会再收到其他用户发送的消息。 + +同步方法: + +```java +EMClient.getInstance().logout(true); +``` + +异步方法: + +```java +EMClient.getInstance().logout(true, new EMCallBack() { + + @Override + public void onSuccess() { + + } + + @Override + public void onError(int code, String message) { + + } +}); +``` + +:::tip + +1. 如果集成了 FCM 等第三方推送,`logout` 方法中 `unbindToken` 参数需设为 `true`,退出时会解绑设备 token,否则可能会出现退出了,还能收到消息推送通知的现象。 +有时可能会遇到网络问题而解绑失败,app 处理时可以弹出提示框让用户选择,是否继续退出(弹出框提示继续退出能收到消息的风险),如果用户选择继续退出,传 `false` 再调用 `logout` 方法退出成功。当然也可以失败后还是返回退出成功,然后在后台起个线程不断调用 `logout` 方法直到成功。这样存在风险,即用户杀掉了 app,网络恢复后用户还会继续收到消息。 + +2. 如果调用异步退出方法,在收到 `onsuccess` 回调后才能去调用 IM 相关方法,例如 `loginWithToken`。 +::: + +## 账号切换 + +若在 app 中从当前账号切换到其他账号,你需要首先调用 `logout` 方法登出,然后再调用 `loginWithToken` 或 `login` 方法登录。 + +## 多设备登录 + +除了单端单设备登录,环信即时通讯 IM 支持同一账号在多端的多个设备上登录。多设备登录时,若同端设备数量超过限制,新登录的设备会将之前登录的设备踢下线。 + +关于多设备登录场景中的设备数量限制、互踢策略以及信息同步,详见[多设备登录文档](multi_device.html)。 + + +## 更多 + +### 登录被封禁账号的提示 + +在环信即时通讯控制台或调用 REST API 封禁用户账号后,若仍使用该账号登录,SDK会返回 "service is disabled"(305 错误), 可以根据用户这个返回值来进行相应的提示或者处理。 diff --git a/docs/document/android/message_receipt.md b/docs/document/android/message_receipt.md index 8804e117..c93139af 100644 --- a/docs/document/android/message_receipt.md +++ b/docs/document/android/message_receipt.md @@ -38,6 +38,7 @@ - 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 +- 要使用群消息已读回执功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 ## 实现方法 diff --git a/docs/document/android/message_send_receive.md b/docs/document/android/message_send_receive.md index 91baa67e..54e0570a 100644 --- a/docs/document/android/message_send_receive.md +++ b/docs/document/android/message_send_receive.md @@ -1,51 +1,21 @@ # 发送和接收消息 - +环信即时通讯 IM Android SDK 通过 `EMChatManager` 类和 `EMMessage` 类实现文本、图片、音频、视频和文件等类型的消息的发送和接收。 -登录即时通讯服务后,用户可以在单聊、群聊、聊天室中发送如下类型的消息: +- 对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。 -- 文字消息,包含超链接和表情消息。 -- 附件消息,包含图片、语音、视频及文件消息。 -- 位置消息。 -- 透传消息。 -- 自定义消息。 -- 合并消息。 -- 定向消息。 +- 对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 -对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 - -针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 - -本文介绍如何使用即时通讯 IM Android SDK 实现发送和接收这些类型的消息。 - -## 技术原理 - -环信即时通讯 IM Android SDK 通过 `EMChatManager` 类和 `EMMessage` 类实现消息的发送和接收。 - -其中,发送和接收消息的逻辑如下: - -1. 发送方调用相应 Create 方法创建文本、文件、附件等类型的消息; -2. 发送方调用 `SendMessage` 发送消息; -3. 通过 `addMessageListener` 添加消息接收的回调通知。 - -消息收发流程如下: - -1. 用户 A 发送一条消息到环信的消息服务器; -2. 单聊消息时,服务器投递消息给用户 B;对于群聊时消息,服务器投递给群内其他每一个成员; -3. 用户收到消息。 - -![img](/images/android/sendandreceivemsg.png) +单聊、群组聊天和聊天室的消息发送控制,详见[消息发送控制](/product/product_message_overview.html#消息发送控制)文档。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html)。 +- 完成 SDK 初始化,详见 [初始化文档](initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -## 实现方法 - -### 发送文本消息 +## 发送和接收文本消息 1. 首先,利用 `EMMessage` 类构造一条消息。 @@ -63,16 +33,6 @@ message.setChatType(EMMessage.ChatType.Chat); EMClient.getInstance().chatManager().sendMessage(message); ``` -对于聊天室消息,可设置消息优先级。示例代码如下: - -```java - EMMessage message = EMMessage.createTextSendMessage(content, conversationId); - message.setChatType(ChatType.ChatRoom); - // 聊天室消息的优先级。如果不设置,默认值为 `PriorityNormal`,即“普通”优先级。 - message.setPriority(EMChatRoomMessagePriority.PriorityHigh); - sendMessage(message); -``` - 2. 通过 `EMChatManager` 将该消息发出。发送消息时可以设置 `EMCallBack` 的实例,获取消息发送状态。 ```java @@ -96,11 +56,7 @@ message.setMessageStatusCallback(new EMCallBack() { EMClient.getInstance().chatManager().sendMessage(message); ``` -若初始化时打开了 `EMOptions#setUseReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 - -### 接收消息 - -你可以用注册监听 `EMMessageListener` 接收消息。该 `EMMessageListener` 可以多次添加,请记得在不需要的时候移除 `listener`,如在 `activity` 的 `onDestroy()` 时。 +3. 你可以用注册监听 `EMMessageListener` 接收消息。该 `EMMessageListener` 可以多次添加,请记得在不需要的时候移除 `listener`,如在 `activity` 的 `onDestroy()` 时。 在新消息到来时,你会收到 `onMessageReceived` 的回调,消息接收时可能是一条,也可能是多条。你可以在该回调里遍历消息队列,解析并显示收到的消息。若在初始化时打开了 `EMOptions#setIncludeSendMessageInMessageListener` 开关,则该回调中会返回发送成功的消息。 @@ -121,7 +77,7 @@ EMClient.getInstance().chatManager().addMessageListener(msgListener); EMClient.getInstance().chatManager().removeMessageListener(msgListener); ``` -### 发送和接收附件类型的消息 +## 发送和接收附件类型的消息 除文本消息外,SDK 还支持发送附件类型消息,包括语音、图片、视频和文件消息。 @@ -131,33 +87,7 @@ EMClient.getInstance().chatManager().removeMessageListener(msgListener); 2. 接收附件消息。SDK 自动下载语音消息,默认自动下载图片和视频的缩略图。若下载原图、视频和文件,需调用 `downloadAttachment` 方法。 3. 获取附件的服务器地址和本地路径。 -此外,发送附件类型消息时,可以在 `onProgress` 回调中获取附件上传的进度,以百分比表示,示例代码如下: - -```java -// 发送消息时可以设置 `EMCallBack` 的实例,获得消息发送的状态。可以在该回调中更新消息的显示状态。例如,消息发送失败后的提示等等。 - message.setMessageStatusCallback(new EMCallBack() { - @Override - public void onSuccess() { - // 发送消息成功 - dialog.dismiss(); - } - @Override - public void onError(int code, String error) { - // 发送消息失败 - } - - // 消息发送的状态,这里只用于附件类型的消息。 - @Override - public void onProgress(int progress, String status) { - - } - - }); - // 发送消息 - EMClient.getInstance().chatManager().sendMessage(message); -``` - -#### 发送和接收语音消息 +### 发送和接收语音消息 发送和接收语音消息的过程如下: @@ -175,7 +105,7 @@ EMClient.getInstance().chatManager().sendMessage(message); 3. 接收方收到语音消息时,自动下载语音文件。 -4. 接收方收到 `onMessageReceived` 回调,调用 `getRemoteUrl` 或 `getLocalUri` 方法获取语音文件的服务器地址或本地路径,从而获取语音文件。 +4. 接收方收到 [onMessageReceived 回调](#发送和接收文本消息),调用 `getRemoteUrl` 或 `getLocalUri` 方法获取语音文件的服务器地址或本地路径,从而获取语音文件。 ```java EMVoiceMessageBody voiceBody = (EMVoiceMessageBody) msg.getBody(); @@ -185,11 +115,12 @@ String voiceRemoteUrl = voiceBody.getRemoteUrl(); Uri voiceLocalUri = voiceBody.getLocalUri(); ``` -#### 发送和接收图片消息 +### 发送和接收图片消息 发送和接收图片消息的流程如下: 1. 发送方调用 `createImageSendMessage` 方法传入图片的本地资源标志符 URI、设置是否发送原图以及接收方的用户 ID (群聊或聊天室分别为群组 ID 或聊天室 ID)创建图片消息,然后调用 `sendMessage` 方法发送该消息。SDK 会将图片上传至环信服务器,服务器自动生成图片缩略图。 + ```java // `imageUri` 为图片本地资源标志符,`false` 为不发送原图(默认超过 100 KB 的图片会压缩后发给对方),若需要发送原图传 `true`,即设置 `original` 参数为 `true`。 EMMessage message = EMMessage.createImageSendMessage(imageUri, false, toChatUsername); @@ -202,9 +133,10 @@ EMClient.getInstance().chatManager().sendMessage(message); 2. 接收方收到图片消息,自动下载图片缩略图。 -SDK 默认自动下载缩略图,即 `EMClient.getInstance().getOptions().setAutoDownloadThumbnail(true)`。若设置为手动下载缩略图,即 `EMClient.getInstance().getOptions().setAutoDownloadThumbnail(false)`,需调用 `EMClient.getInstance().chatManager().downloadThumbnail(message)` 下载。 +- 默认情况下,SDK 自动下载缩略图,即 `EMClient.getInstance().getOptions().setAutoDownloadThumbnail(true)`。 +- 若设置为手动下载缩略图,即 `EMClient.getInstance().getOptions().setAutoDownloadThumbnail(false)`,需调用 `EMClient.getInstance().chatManager().downloadThumbnail(message)` 下载。 -3. 接收方收到 `onMessageReceived` 回调,调用 `downloadAttachment` 下载原图。 +3. 接收方收到 [onMessageReceived 回调](#发送和接收文本消息),调用 `downloadAttachment` 下载原图。 ```java @Override @@ -248,7 +180,7 @@ Uri imgLocalUri = imgBody.getLocalUri(); Uri thumbnailLocalUri = imgBody.thumbnailLocalUri(); ``` -#### 发送和接收视频消息 +### 发送和接收视频消息 发送和接收视频消息的流程如下: @@ -267,11 +199,9 @@ if (chatType == CHATTYPE_GROUP) EMClient.getInstance().chatManager().sendMessage(message); ``` -3. 接收方收到视频消息时,自动下载视频缩略图。 - -SDK 默认自动下载缩略图,即 `EMClient.getInstance().getOptions().setAutoDownloadThumbnail(true)`。若设置为手动下载缩略图,即 `EMClient.getInstance().getOptions().setAutoDownloadThumbnail(false)`,需调用 `EMClient.getInstance().chatManager().downloadThumbnail(message)` 下载。 +3. 接收方收到视频消息时,自动下载视频缩略图。你可以设置自动或手动下载视频缩略图,该设置与图片缩略图相同,详见[设置图片缩略图自动下载](#发送和接收图片消息)。 -4. 接收方收到 `onMessageReceived` 回调,可以调用 `EMClient.getInstance().chatManager().downloadAttachment(message)` 方法下载视频原文件。 +4. 接收方收到 [onMessageReceived 回调](#发送和接收文本消息),可以调用 `EMClient.getInstance().chatManager().downloadAttachment(message)` 方法下载视频原文件。 ```java /** @@ -309,7 +239,7 @@ Uri localUri = ((EMVideoMessageBody) body).getLocalUri(); Uri localThumbUri = ((EMVideoMessageBody) body).thumbnailLocalUri(); ``` -#### 发送和接收文件消息 +### 发送和接收文件消息 发送和接收文件消息的流程如下: @@ -325,7 +255,7 @@ if (chatType == CHATTYPE_GROUP) EMClient.getInstance().chatManager().sendMessage(message); ``` -2. 接收方收到 `onMessageReceived` 回调,调用 `downloadAttachment` 方法下载文件。 +2. 接收方收到 [onMessageReceived 回调](#发送和接收文本消息),调用 `downloadAttachment` 方法下载文件。 ```java /** @@ -360,9 +290,11 @@ String fileRemoteUrl = fileMessageBody.getRemoteUrl(); Uri fileLocalUri = fileMessageBody.getLocalUri(); ``` -### 发送和接收位置消息 +## 发送和接收位置消息 -当你要发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 +1. 创建和发送位置消息。 + +发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。 ```java // `latitude` 为纬度,`longitude` 为经度,`locationAddress` 为具体位置内容。 @@ -374,14 +306,23 @@ if (chatType == CHATTYPE_GROUP) EMClient.getInstance().chatManager().sendMessage(message); ``` -### 发送和接收透传消息 +2. 接收位置消息与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + + 接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 -透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。(透传消息不会存入本地数据库中,所以在 UI 上不会显示)。具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 “em_” 和 “easemob::” 开头的 action 为内部保留字段,注意不要使用。 +## 发送和接收透传消息 + +透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。 + +具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 `em_` 和 `easemob::` 开头的 action 为内部保留字段,注意不要使用。 :::tip -透传消息发送后,不支持撤回。 +- 透传消息发送后,不支持撤回。 +- 透传消息不会存入本地数据库中,所以在 UI 上不会显示。 ::: +1. 创建和发送透传消息。 + ```java EMMessage cmdMsg = EMMessage.createSendMessage(EMMessage.Type.CMD); // 支持单聊、群聊和聊天室,默认为单聊。 @@ -401,7 +342,7 @@ cmdMsg.addBody(cmdBody); EMClient.getInstance().chatManager().sendMessage(cmdMsg); ``` -请注意透传消息的接收方,也是由单独的回调进行通知,方便用户进行不同的处理。 +2. 接收方通过 `onMessageReceived` 和 `onCmdMessageReceived` 回调接收透传消息,方便用户进行不同的处理。 ```java EMMessageListener msgListener = new EMMessageListener(){ @@ -416,105 +357,12 @@ EMMessageListener msgListener = new EMMessageListener(){ } ``` -#### 通过透传消息实现输入指示器 - -输入指示器显示其他用户何时输入消息。通过该功能,用户之间可进行有效沟通,增加了用户对聊天应用中交互的期待感。 - -你可以通过透传消息实现输入指示器。下图为输入指示器的工作原理。 - -![img](/images/common/typing_indicator.png) - - -监听用户 A 的输入状态。一旦有文本输入,通过透传消息将输入状态发送给用户 B,用户 B 收到该消息,了解到用户 A 正在输入文本。 - -- 用户 A 向用户 B 发送消息,通知其开始输入文本。 -- 收到消息后,如果用户 B 与用户 A 的聊天页面处于打开状态,则显示用户 A 的输入指示器。 -- 如果用户 B 在几秒后未收到用户 A 的输入,则自动取消输入指示器。 - -:::tip -用户 A 可根据需要设置透传消息发送间隔。 -::: - -以下示例代码展示如何发送输入状态的透传消息。 - -```java -//发送表示正在输入的透传消息 -private static final String MSG_TYPING_BEGIN = "TypingBegin"; -private long previousChangedTimeStamp; - -private void textChange() { - long currentTimestamp = System.currentTimeMillis(); - if(currentTimestamp - previousChangedTimeStamp > 5) { - sendBeginTyping(); - previousChangedTimeStamp = currentTimestamp; - } -} - -private void sendBeginTyping() { - EMMessage beginMsg = EMMessage.createSendMessage(EMMessage.Type.CMD); - EMCmdMessageBody body = new EMCmdMessageBody(MSG_TYPING_BEGIN); - // 将该透传消息只发送给在线用户 - body.deliverOnlineOnly(true); - beginMsg.addBody(body); - beginMsg.setTo(toChatUsername); - EMClient.getInstance().chatManager().sendMessage(beginMsg); -} -``` - -以下示例代码展示如何接受和解析输入状态的透传消息。 - -```java -private static final int TYPING_SHOW_TIME = 10000; -private static final int MSG_TYPING_END = 1; -private Handler typingHandler; - -private void initTypingHandler() { - typingHandler = new Handler(Looper.myLooper()) { - @Override - public void handleMessage(@NonNull Message msg) { - switch (msg.what) { - case MSG_TYPING_END : - cancelTimer(); - break; - } - } - }; -} - -@Override -public void onCmdMessageReceived(List messages) { - for (EMMessage msg : messages) { - if(!TextUtils.equals(msg.conversationId(), currentConversationId)) { - return; - } - EMCmdMessageBody body = (EMCmdMessageBody) msg.getBody(); - if(TextUtils.equals(body.action(), MSG_TYPING_BEGIN)) { - // 这里需更新 UI,显示“对方正在输入” - beginTimer(); - } - } -} - -private void beginTimer() { - if(typingHandler != null) { - typingHandler.removeMessages(MSG_TYPING_END); - typingHandler.sendEmptyMessageDelayed(MSG_TYPING_END, TYPING_SHOW_TIME); - } -} - -private void cancelTimer() { - // 这里需更新 UI,不再显示“对方正在输入” - if(typingHandler != null) { - typingHandler.removeCallbacksAndMessages(null); - } -} - -``` - -### 发送自定义类型消息 +## 发送和接收自定义类型消息 除了几种消息之外,你可以自己定义消息类型,方便业务处理,即首先设置一个消息类型名称,然后可添加多种自定义消息。 +1. 创建和发送自定义类型消息。 + ```java EMMessage customMessage = EMMessage.createSendMessage(EMMessage.Type.CUSTOM); // `event` 为需要传递的自定义消息事件,比如礼物消息,可以设置: @@ -531,7 +379,9 @@ customMessage.setChatType(chatType); EMClient.getInstance().chatManager().sendMessage(customMessage); ``` -### 发送和接收合并消息 +2. 接收自定义消息与其他类型消息一致,详见[接收文本消息](#发送和接收文本消息)。 + +## 发送和接收合并消息 为了方便消息互动,即时通讯 IM 自 4.1.0 版本开始支持将多个消息合并在一起进行转发。你可以采取以下步骤进行消息的合并转发: @@ -584,11 +434,11 @@ message.setMessageStatusCallback(new EMCallBack() { EMClient.getInstance().chatManager().sendMessage(message); ``` -接收合并消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +#### 接收和解析合并消息 -对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 +接收合并消息与接收普通消息的操作相同,详见[接收文本消息](#发送和接收文本消息)。 -#### 解析合并消息 +对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 合并消息实际上是一种附件消息。收到合并消息后,你可以调用 `downloadAndParseCombineMessage` 方法下载合并消息附件并解析出原始消息列表。 @@ -611,7 +461,7 @@ EMClient.getInstance().chatManager().downloadAndParseCombineMessage(combineMessa }); ``` -### 发送和接收定向消息 +## 发送和接收定向消息 发送定向消息是指向群组或聊天室的单个或多个指定的成员发送消息,其他成员不会收到该消息。 @@ -645,9 +495,9 @@ EMMessage message = EMMessage.createTextSendMessage(content, groupId); EMClient.getInstance().chatManager().sendMessage(message); ``` -接收群定向消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +接收群定向消息与接收普通消息的操作相同,详见[接收文本消息](#发送和接收文本消息)。 -### 使用消息扩展字段 +## 使用消息扩展字段 当 SDK 提供的消息类型不满足需求时,你可以通过消息扩展字段传递自定义的内容,从而生成自己需要的消息类型。 @@ -665,6 +515,59 @@ message.getStringAttribute("attribute1",null); message.getBooleanAttribute("attribute2", false) ``` -## 注意事项 +## 更多 + +### 设置聊天室消息优先级 + +针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 + +对于聊天室消息,可设置消息优先级。示例代码如下: + +```java + EMMessage message = EMMessage.createTextSendMessage(content, conversationId); + message.setChatType(ChatType.ChatRoom); + // 聊天室消息的优先级。如果不设置,默认值为 `PriorityNormal`,即“普通”优先级。 + message.setPriority(EMChatRoomMessagePriority.PriorityHigh); + sendMessage(message); +``` + +### 获取发送附件消息的进度 + +发送附件类型消息时,可以在 `onProgress` 回调中获取附件上传的进度,以百分比表示,示例代码如下: + +```java +// 发送消息时可以设置 `EMCallBack` 的实例,获得消息发送的状态。可以在该回调中更新消息的显示状态。例如,消息发送失败后的提示等等。 + message.setMessageStatusCallback(new EMCallBack() { + @Override + public void onSuccess() { + // 发送消息成功 + dialog.dismiss(); + } + @Override + public void onError(int code, String error) { + // 发送消息失败 + } + + // 消息发送的状态,这里只用于附件类型的消息。 + @Override + public void onProgress(int progress, String status) { + + } + + }); + // 发送消息 + EMClient.getInstance().chatManager().sendMessage(message); +``` + +### 发送消息前的内容审核 + +- 内容审核关注消息 body + +[内容审核服务会关注消息 body 中指定字段的内容,不同类型的消息审核不同的字段](/product/moderation/moderation_mechanism.html),若创建消息时在这些字段中传入了很多业务信息,可能会影响审核效果。因此,创建消息时需要注意内容审核的字段不涉及业务信息,建议业务信息放在扩展字段中。 + +- 设置发送方收到内容审核替换后的内容 + +若初始化时打开了 `EMOptions#setUseReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 + + -[内容审核服务会关注消息 body 中指定字段的内容,不同类型的消息审核不同的字段](/product/moderation/moderation_mechanism.html),若创建消息时在这些字段中传入了很多业务信息,可能会影响审核效果。因此,创建消息时需要注意内容审核的字段不涉及业务信息,建议业务信息放在扩展字段中。 \ No newline at end of file diff --git a/docs/document/android/multi_device.md b/docs/document/android/multi_device.md index 42c3dcfc..2aa1d1f0 100644 --- a/docs/document/android/multi_device.md +++ b/docs/document/android/multi_device.md @@ -96,7 +96,7 @@ EMClient.getInstance().chatManager().sendMessage(message); 初始化 SDK 时,你可以调用 `EMOptions#setCustomDeviceName` 方法设置登录设备的名称。设置后,若因达到了登录设备数量限制而导致在已登录的设备上强制退出时,被踢设备收到的 `EMConnectionListener#onLogout` 回调会包含导致该设备被踢下线的自定义设备名称。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -138,7 +138,7 @@ EMClient.getInstance().chatManager().sendMessage(message); 2. 初始化 SDK 时,调用 `EMOptions#setCustomOSPlatform` 方法自定义设置登录设备的平台。确保该方法中的 `platform` 参数的值与环信控制台的**添加自定义平台**对话框中设置的**设备平台**的值相同。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -154,7 +154,7 @@ EMClient.getInstance().chatManager().sendMessage(message); 初始化 SDK 时,你可以调用 `EMOptions#setLoginCustomExt` 方法设置登录设备的自定义扩展信息。设置后,若因达到了登录设备数量限制而导致在已登录的设备上强制退出时(`206` 错误,`USER_LOGIN_ANOTHER_DEVICE`),被踢设备收到的 `EMConnectionListener#onLogout` 回调会包含导致该设备被踢下线的新登录设备的自定义扩展信息。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -188,7 +188,7 @@ EMClient.getInstance().chatManager().sendMessage(message); 你可以调用 `kickDevice` 或 `kickDeviceWithToken` 方法通过传入用户 ID 和登录密码或用户 token 将指定账号从单个登录设备踢下线。调用这两种方法前,你需要首先通过 `EMClient#getLoggedInDevicesFromServer` 和 `EMDeviceInfo#getResource` 方法获取设备 ID。 -:::notice +:::tip 不登录也可以使用该接口。 ::: @@ -204,7 +204,7 @@ EMClient.getInstance().kickDevice(username, password, deviceInfos.get(selectedIn 你可以调用 `kickAllDevices` 或 `kickAllDevicesWithToken` 方法通过传入用户 ID 和登录密码或用户 token 将指定账号从所有登录设备踢下线。 -:::notice +:::tip 不登录也可以使用该接口。 ::: diff --git a/docs/document/android/overview.md b/docs/document/android/overview.md index 3ac6e7b2..dcd70d91 100644 --- a/docs/document/android/overview.md +++ b/docs/document/android/overview.md @@ -41,7 +41,7 @@ options.setAppKey("Your appkey"); EMClient.getInstance().init(context, options); ``` -:::notice +:::tip 需要在主进程中进行初始化。 ::: @@ -69,7 +69,7 @@ EMClient.getInstance().init(context, options); EMClient.getInstance().createAccount(mAccount, mPassword);// 同步方法。 ``` -:::notice +:::tip 该注册模式为在客户端注册,旨在方便测试,并不推荐在正式环境中使用。 ::: @@ -109,7 +109,7 @@ EMClient.getInstance().login(mAccount, mPassword, new EMCallBack() { **用户 ID + token** 是更加安全的登录方式。token 可以通过调用 REST API 获取。详见 [环信用户 token 的获取](/document/server-side/easemob_user_token.html)。 -:::notice +:::tip 使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 ::: diff --git a/docs/document/android/privatecloud.md b/docs/document/android/privatecloud.md index 65c3a6d1..32269bbd 100644 --- a/docs/document/android/privatecloud.md +++ b/docs/document/android/privatecloud.md @@ -15,7 +15,7 @@ emOptions.enableDNSConfig(false);//默认是 true,在私有云下,需要关 EMClient.getInstance().init(context,emOptions);//最后初始化 SDK ``` -:::notice +:::tip 如果需要配置 HTTPS 只需在 REST 地址前加一个前缀。 ::: diff --git a/docs/document/android/quickstart.md b/docs/document/android/quickstart.md index e906ce17..083c9d1c 100644 --- a/docs/document/android/quickstart.md +++ b/docs/document/android/quickstart.md @@ -40,19 +40,11 @@ ### 2. 集成 SDK -选择如下任意一种方式将环信即时通讯 IM SDK 集成到你的项目中。 +你可以使用 mavenCentral 自动集成。该方法仅适用于 v3.8.2 或以上版本。 -:::notice +除此之外,你还可以通过手动复制 SDK 文件和动态加载 `.so` 库文件的方法集成 IM SDK,详见[集成文档](integration.html)。 -- 以下集成方式只需选择一种,同时使用多种集成方式可能会报错。 -- 请点击查看[发版说明](releasenote.html)获得最新版本号。 - ::: - -#### 方法一:使用 mavenCentral 自动集成 - -该方法仅适用于 v3.8.2 或以上版本。 - -1. 在项目的 `build.gradle` 中添加 `mavenCentral()`仓库。 +1. 在项目的 `build.gradle` 中添加 `mavenCentral()` 仓库。 ```gradle buildscript { @@ -83,9 +75,7 @@ dependencies { } ``` -获取最新 SDK 的版本号:[SDK 更新日志](releasenote.html) - -:::notice +:::tip 如果使用 3.8.0 之前的版本,gradle 依赖需要按照下面格式添加: ::: @@ -95,54 +85,7 @@ implementation 'io.hyphenate:hyphenate-sdk:3.7.5' // 完整版本,包含音视 implementation 'io.hyphenate:hyphenate-sdk-lite:3.7.5' // 精简版,只包含IM功能 ``` -#### 方法二:手动复制 SDK 文件 - -打开 [SDK 下载](https://www.easemob.com/download/im)页面,获取最新版的环信即时通讯 IM Android SDK,然后解压。 - -![img](/images/android/sdk-files.png) - -将 SDK 包内 libs 路径下的如下文件,拷贝到你的项目路径下: - -| 文件或文件夹 | 项目路径 | -| :------------------- | :--------------------- | -| hyphenatechat_xxx.jar 文件 | /app/libs/ | -| arm64-v8a 文件夹 | /app/src/main/jniLibs/ | -| armeabi-v7a 文件夹 | /app/src/main/jniLibs/ | -| x86 文件夹 | /app/src/main/jniLibs/ | -| x86_64 文件夹 | /app/src/main/jniLibs/ | - -如果对生成的 `apk` 大小比较敏感,我们建议使用 `jar` 方式,并且手工拷贝 `so`,而不是使用 `Aar`,因为 `Aar` 方式会把各个平台的 `so` 文件都包含在其中。采用 `jar` 方式,可以仅保留一个 `ARCH` 目录,建议仅保留 `armeabi-v7a`,这样虽然在对应平台执行的速度会降低,但是能有效减小 `apk` 的大小。 - -#### 方法三:动态加载 .so 库文件 - -为了减少应用安装包的大小,SDK 从 4.5.0 开始提供了 `EMOptions#setNativeLibBasePath` 方法支持动态加载 SDK 所需的 `.so` 文件。以 SDK 4.5.0 为例,`.so` 文件包括 `libcipherdb.so` 和 `libhyphenate.so` 两个文件。**从 4.11.0 开始,`.so` 文件还包含 `libaosl.so` 文件。** - -该功能的实现步骤如下: - -1. 下载最新版本的 SDK 并解压缩。 -2. 集成 `hyphenatechat_4.5.0.jar` 到你的项目中。 -3. 将所有架构的 `.so` 文件上传到你的服务器,并确保应用程序可以通过网络下载目标架构的 `.so` 文件。 -4. 应用运行时,会检查 `.so` 文件是否存在。如果未找到,应用会下载该 `.so` 文件并将其保存到你自定义的应用程序的私有目录中。 -5. 调用 `EMClient#init` 初始化时,将 `.so` 文件所在的 app 私有目录作为参数设置进 `EMOptions#setNativeLibBasePath` 方法中。 -6. 调用 `EMClient#init` 初始化后,SDK 会自动从指定路径加载 `.so` 文件。 - -:::tip -1. 该方法仅适合手动集成 Android SDK,不适用于通过 Maven Central 集成。 -2. so 库的路径取决于 `EMOptions#setNativeLibBasePath` 方法的 `path` 参数: -- `path` 参数为空或者不调用该方法时,SDK 内部会使用 `system.loadLibrary` 从系统默认路径中搜索并加载 so 库。 -- `path` 参数不为空时,SDK 内部会使用 `System.load` 从设置的路径下搜索和加载 so 库。该路径必须为有效的 app 的私有目录路径。 -::: - -```java -//假设用户已经通过动态下发的方式,将环信 SDK 中的 libcipherdb.so 和 libhyphenate.so 两个 so 库,放到 app 的 /data/data/packagename/files 目录下。 -String filesPath = mContext.getFilesDir().getAbsolutePath(); - -EMOptions options = new EMOptions(); -options.setNativeLibBasePath(filesPath); - -EMClient.getInstance().init(mContext, options); - -``` +若要查看最新版本号,请点击[这里](releasenote.html)。 ### 3. 添加项目权限 @@ -184,7 +127,7 @@ EMClient.getInstance().init(mContext, options); ### 4. 防止代码混淆 -在 app/proguard-rules.pro 文件中添加如下行,防止混淆 SDK 的代码: +在 `app/proguard-rules.pro` 文件中添加如下行,防止混淆 SDK 的代码: ```java -keep class com.hyphenate.** {*;} @@ -223,7 +166,7 @@ android { ### 1. SDK 初始化 -在主进程中进行初始化: +在**主进程**中进行初始化: ```java EMOptions options = new EMOptions(); @@ -249,7 +192,7 @@ try { } ``` -:::notice +:::tip 该注册模式为在客户端注册,主要用于测试,简单方便,但不推荐在正式环境中使用,需要在[环信控制台](https://console.easemob.com/user/login)中手动开通开放注册功能;正式环境中应使用服务器端调用 Restful API 注册,具体见[注册单个用户](/document/server-side/account_system.html#开放注册单个用户)。 ::: @@ -279,7 +222,7 @@ EMClient.getInstance().login(mAccount, mPassword, new EMCallBack() { }); ``` -:::notice +:::tip 1. 除了注册监听器,其他的 SDK 操作均需在登录之后进行。 2. 登录成功后需要调用 `EMClient.getInstance().chatManager().loadAllConversations();` 和 `EMClient.getInstance().groupManager().loadAllGroups();`,确保进入主页面后本地会话和群组均加载完毕。 3. 如果之前登录过,App 长期在后台运行后切换到前台运行可能会导致加载到内存的群组和会话为空。为了避免这种情况,可在主页面的 `oncreate` 里也添加这两种方法,不过,最好将这两种方法放在程序的开屏页。 diff --git a/docs/document/android/reaction.md b/docs/document/android/reaction.md index fa1fc4f2..e6749273 100644 --- a/docs/document/android/reaction.md +++ b/docs/document/android/reaction.md @@ -4,7 +4,7 @@ 环信即时通讯 IM 提供消息表情回复(下文统称 “Reaction”)功能。用户可以在单聊和群聊中对消息添加、删除表情。表情可以直观地表达情绪,利用 Reaction 可以提升用户的使用体验。同时在群组中,利用 Reaction 可以发起投票,根据不同表情的追加数量来确认投票。 -:::notice +:::tip 目前 Reaction 仅适用于单聊和群组。聊天室暂不支持 Reaction 功能。 ::: diff --git a/docs/document/android/releasenote.md b/docs/document/android/releasenote.md index a3e35789..d4ddd608 100644 --- a/docs/document/android/releasenote.md +++ b/docs/document/android/releasenote.md @@ -74,7 +74,7 @@ ### 新增特性 -- [IM SDK] 新增[从服务器拉取离线消息的开始和结束的事件回调](/document/android/overview.html#连接状态相关): `EMConnectionListener#onOfflineMessageSyncStart` 和 `EMConnectionListener#onOfflineMessageSyncFinish`。 +- [IM SDK] 新增[从服务器拉取离线消息的开始和结束的事件回调](/document/android/connection): `EMConnectionListener#onOfflineMessageSyncStart` 和 `EMConnectionListener#onOfflineMessageSyncFinish`。 - [IM SDK] 新增 `EMGroupManager#asyncCheckIfInMuteList` 接口,可以查看当前用户是否在群组禁言名单中。 - [IM SDK] 原消息置顶接口 `EMChatManager#asyncPinMessage` 和 `EMChatManager#asyncUnPinMessage` [增加对单聊会话中置顶消息的支持](message_pin.html)。接口无变化。 - [IM SDK] 新增 `EMRecallMessageInfo#getConversationId` 接口,在撤回消息的 `EMMessageListener#onMessageRecalledWithExt` 事件中[返回被撤回的消息所属的会话 ID](message_recall.html#设置消息撤回监听)。 @@ -249,8 +249,8 @@ - [IM SDK] 新增 [EMChatManager#asyncDeleteAllMsgsAndConversations](message_delete.html#清空聊天记录)方法,用于清空当前用户的聊天记录,包括消息和会话,同时可以选择是否清除服务端的聊天记录。 - [IM SDK] 新增 [EMChatManager#searchMsgFromDB(java.lang.String, long, int, java.lang.String, com.hyphenate.chat.EMConversation.EMSearchDirection, com.hyphenate.chat.EMConversation.EMMessageSearchScope)](message_search.html#根据搜索范围搜索所有会话中的消息) 和 [EMConversation#searchMsgFromDB(java.lang.String, long, int, java.lang.String, com.hyphenate.chat.EMConversation.EMSearchDirection, com.hyphenate.chat.EMConversation.EMMessageSearchScope)](message_search.html#根据搜索范围搜索当前会话中的消息),可以在根据关键字搜索消息时,选择搜索范围,如只搜索消息内容、只搜索消息扩展信息以及同时搜索消息内容以及扩展信息。 -- [IM SDK] 新增 [EMOptions#setUseReplacedMessageContents](message_send_receive.html#发送文本消息) 开关。开启后,发送消息时如果被内容审核进行了内容替换,发送方可以获取替换后的内容。 -- [IM SDK] 新增 [EMOptions#setIncludeSendMessageInMessageListener](message_send_receive.html#接收消息) 开关。开启后,在 `EMMessageListener#onMessageReceived` 回调里增加发送成功的消息。 +- [IM SDK] 新增 [EMOptions#setUseReplacedMessageContents](message_send_receive.html#发送和接收文本消息) 开关。开启后,发送消息时如果被内容审核进行了内容替换,发送方可以获取替换后的内容。 +- [IM SDK] 新增 [EMOptions#setIncludeSendMessageInMessageListener](message_send_receive.html#发送和接收文本消息) 开关。开启后,在 `EMMessageListener#onMessageReceived` 回调里增加发送成功的消息。 - [IM SDK] 新增 [EMOptions#setRegardImportedMsgAsRead](message_retrieve.html#从服务器获取指定会话的消息) 开关。开启后,[利用服务端接口](/document/server-side/message_import.html)导入的消息,客户端上通过[漫游拉取](message_retrieve.html#从服务器获取指定会话的消息)到后,这些消息为已读状态,会话中未读取的消息数量,即 `EMConversation#getUnreadMsgCount` 的返回值不发生变化。若该开关为关闭状态,`EMConversation#getUnreadMsgCount` 的返回值会增加。 ### 优化 @@ -468,7 +468,7 @@ ### 新增特性 -- [IM SDK] 新增[聊天室消息优先级](message_send_receive.html)。 +- [IM SDK] 新增[聊天室消息优先级](message_send_receive.html#设置聊天室消息优先级)。 - [IM SDK] 群组信息更新后的 `EMGroupChangeListener#onSpecificationChanged` 回调中添加更新后的群组信息。 ### 优化 diff --git a/docs/document/android/room_members.md b/docs/document/android/room_members.md index fdba58ce..a09b005e 100644 --- a/docs/document/android/room_members.md +++ b/docs/document/android/room_members.md @@ -212,7 +212,7 @@ EMClient.getInstance().chatroomManager().removeFromChatRoomWhiteList(chatRoomId, 仅聊天室所有者和管理员可以调用 `EMChatRoomManager#muteChatRoomMembers` 方法将指定成员添加至聊天室禁言列表。被禁言的成员和其他未操作的聊天室管理员或聊天室所有者收到 `EMChatRoomChangeListener#onMuteListAdded` 回调。 -:::notice +:::tip 聊天室所有者可禁言聊天室所有成员,聊天室管理员可禁言聊天室普通成员。 ::: @@ -229,7 +229,7 @@ EMChatRoom chatRoom = EMClient.getInstance().chatroomManager().muteChatRoomMembe 仅聊天室所有者和管理员可以调用 `EMChatRoomManager#unMuteChatRoomMembers` 方法将成员移出聊天室禁言列表。被解除禁言的成员和其他未操作的聊天室管理员或聊天室所有者收到 `EMChatRoomChangeListener#onMuteListRemoved` 回调。 -:::notice +:::tip 聊天室所有者可对聊天室所有成员解除禁言,聊天室管理员可对聊天室普通成员解除禁言。 ::: diff --git a/docs/document/android/thread.md b/docs/document/android/thread.md index a8f71196..b568e9af 100644 --- a/docs/document/android/thread.md +++ b/docs/document/android/thread.md @@ -62,7 +62,7 @@ EMClient.getInstance().chatThreadManager().createChatThread(parentId, messageId, 单设备登录时,子区所属群组的所有成员均会收到 `EMChatThreadChangeListener#onChatThreadDestroyed` 回调;多设备登录时,其他设备会同时收到 `EMMultiDeviceListener#onThreadEvent` 回调,回调事件为 `THREAD_DESTROY`。 -:::notice +:::tip 解散子区或解散子区所在的群组后,将删除本地数据库及内存中关于该子区的全部数据,需谨慎操作。 ::: diff --git a/docs/document/android/thread_message.md b/docs/document/android/thread_message.md index c72ff5e5..91c39d7a 100644 --- a/docs/document/android/thread_message.md +++ b/docs/document/android/thread_message.md @@ -6,22 +6,13 @@ ## 技术原理 -环信即时通讯 IM Android SDK 提供 `EMChatManager`、`EMMessage` 和 `EMChatThread` 类,用于管理子区消息,支持你通过调用 API 在项目中实现如下功能: - -- 发送子区消息 -- 接收子区消息 -- 撤回子区消息 -- 获取子区消息 +环信即时通讯 IM Android SDK 提供 `EMChatManager`、`EMMessage` 和 `EMChatThread` 类,用于管理子区消息,支持你通过调用 API 在项目中实现发送、接收、撤回和获取子区消息。 消息收发流程如下: -1. 客户端从应用服务器获取 token。 -2. 客户端 A 和 B 登录即时通讯。 -3. 客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 - -![img](/images/android/sendandreceivemsg.png) +客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 -子区创建和查看如下图: +子区创建和查看如下图所示: ![img](/images/android/threads.png) @@ -40,7 +31,7 @@ ### 发送子区消息 -发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html#发送文本消息)。唯一不同的是,发送子区消息需要指定标记 `isChatThreadMessage` 为 `true`。 +发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html#发送和接收文本消息)。唯一不同的是,发送子区消息需要指定标记 `isChatThreadMessage` 为 `true`。 示例代码如下: @@ -71,7 +62,7 @@ EMClient.getInstance().chatManager().sendMessage(message); ### 接收子区消息 -接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#接收消息),此处只介绍子区消息和其他消息的区别。 +接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#发送和接收文本消息),此处只介绍子区消息和其他消息的区别。 子区有新增消息时,子区所属群组的所有成员收到 `EMChatThreadChangeListener#onChatThreadUpdated` 回调,子区成员收到 `EMMessageListener#onMessageReceived` 回调。 diff --git a/docs/document/android/userprofile.md b/docs/document/android/userprofile.md index 94b430f3..b8048604 100644 --- a/docs/document/android/userprofile.md +++ b/docs/document/android/userprofile.md @@ -10,7 +10,7 @@ 本文介绍如何通过管理用户属性设置、更新、存储并获取实时消息用户的相关信息。 -:::notice +:::tip 为保证用户信息安全,SDK 仅支持用户设置或更新自己的用户属性。 ::: diff --git a/docs/document/applet/alipay.md b/docs/document/applet/alipay.md index df23f3a0..02194976 100644 --- a/docs/document/applet/alipay.md +++ b/docs/document/applet/alipay.md @@ -24,6 +24,8 @@ request 合法域名: 3. https://a3.easemob.com 4. https://a4.easemob.com 5. https://a5.easemob.com +7. https://rs.chat.agora.io +8. https://rs.easemob.com ::: :::tip diff --git a/docs/document/applet/baidu.md b/docs/document/applet/baidu.md index 63be5fef..5d66b553 100644 --- a/docs/document/applet/baidu.md +++ b/docs/document/applet/baidu.md @@ -24,6 +24,8 @@ request 合法域名: 3. https://a3.easemob.com 4. https://a4.easemob.com 5. https://a5.easemob.com +6. https://rs.chat.agora.io +7. https://rs.easemob.com ::: :::tip @@ -31,7 +33,7 @@ socket 合法域名: wss://im-api-wechat.easemob.com(3.0 IM SDK) ::: -:::notice +:::tip 为满足不同客户的业务需求,环信在多地部署了数据中心。不同数据中心的 REST API 请求域名、WebSocket 访问域名不同。请根据您所在数据中心进行配置。 ::: @@ -84,7 +86,7 @@ import SDK from "../sdk/Easemob-chat-miniProgram"; // 3.0 SDK #### 实例调用方式 -实例化 SDK,并挂载在全局对象下 +实例化 SDK,并挂载在全局对象下。 ```javascript //实例化 SDK 对象 diff --git a/docs/document/applet/bytedance.md b/docs/document/applet/bytedance.md index 56b0d139..7f5e1067 100644 --- a/docs/document/applet/bytedance.md +++ b/docs/document/applet/bytedance.md @@ -25,6 +25,8 @@ request 合法域名: 4. https://a4.easemob.com 5. https://a5.easemob.com 6. https://a1-chatfile.easemob.com +7. https://rs.chat.agora.io +8. https://rs.easemob.com ::: :::tip @@ -32,7 +34,7 @@ socket 合法域名: wss://im-api-wechat.easemob.com(3.0 IM SDK) ::: -:::notice +:::tip 为满足不同客户的业务需求,环信在多地部署了数据中心。不同数据中心的 REST API 请求域名、WebSocket 访问域名不同。请根据您所在数据中心进行配置。 ::: @@ -81,7 +83,7 @@ import SDK from "../sdk/Easemob-chat-miniProgram"; // 3.0 SDK #### 实例调用方式 -实例化 SDK,并挂载在全局对象下 +实例化 SDK,并挂载在全局对象下。 ```javascript //实例化 SDK 对象 diff --git a/docs/document/applet/connection.md b/docs/document/applet/connection.md new file mode 100644 index 00000000..29fef139 --- /dev/null +++ b/docs/document/applet/connection.md @@ -0,0 +1,43 @@ +# 连接 + +应用客户端成功连接到环信服务器后,才能使用环信即时通讯 SDK 的收发消息等功能。 + +你调用 `open` 方法登录后,客户端 SDK 会自动连接环信服务器。关于登录详情,请参见[登录文档](login.html)。 + +## 监听连接状态 + +```javascript +conn.addEventHandler("connectionListener", { + onConnected: () => { + console.log("连接成功"); + }, + // 自 4.8.0 版本,`onDisconnected` 事件新增断开原因回调参数, 告知用户触发 `onDisconnected` 的原因。 + onDisconnected: () => { + console.log("连接断开"); + }, + onReconnecting: () => { + console.log("重连中"); + }; +}); +``` + +## 自动重连 + +登录后,SDK 在以下情况下会尝试自动重连: + +- 网络断开 + +- 网络切换 + +- 非主动调用登出 + +不过,SDK 在以下情况下会停止自动重连。你需要调用 `open` 方法登录。 + +- 用户调用了 SDK 的登出方法 `close` 主动退出登录。 +- 登录时鉴权错误,例如, token 无效(错误码 104)或已过期(错误码 108)。 +- 用户在其他的设备上更改了密码,导致此设备上自动登录失败,提示错误码 216。 +- 用户的账号被从服务器端删除,提示错误码 207。 +- 用户在另一设备登录,将当前设备上登录的用户踢出,提示错误码 206。 +- 用户登录设备数量超过限制,提示错误码 214。 +- 应用程序的日活跃用户数量(DAU)或月活跃用户数量(MAU)达到上限,提示错误码 8。 + diff --git a/docs/document/applet/initialization.md b/docs/document/applet/initialization.md index 1b0edeee..3ecbc551 100644 --- a/docs/document/applet/initialization.md +++ b/docs/document/applet/initialization.md @@ -1,26 +1,10 @@ -# 初始化及登录 +# SDK 初始化 - - -本页介绍小程序 SDK 集成过程中的初始化及登录。 +初始化是使用 SDK 的必要步骤,需在所有接口方法调用前完成。 ## 前提条件 -开始前,请注册有效的环信即时通讯 IM 开发者账号且获得 App key,见 [环信即时通讯云管理后台](https://console.easemob.com/user/login)。 - -## 引入 SDK - -对于 JavaScript SDK,导入代码如下: - -```javascript -import EC from "easemob-websdk"; -``` - -对于 TypeScript SDK,导入代码如下, EasemobChat 是 SDK 类型的命名空间。 - -```javascript -import EC, { EasemobChat } from "easemob-websdk"; -``` +有效的环信即时通讯 IM 开发者账号和 App key,详见[环信即时通讯云控制台的相关文档](/product/enable_and_configure_IM.html#创建应用)。 ## SDK 初始化 @@ -50,185 +34,3 @@ const conn = new EC.connection({ | `apiUrl` | String | 是 | 指定的 REST 服务器。在未开启 DNS 的情况下使用,一般适用于开发者要实现数据隔离、特别注重数据安全的场景。要获取该服务器地址,需在环信控制台的**即时通讯 > 服务概览**页面,查看**域名配置**表格中的 **Rest Api** 设置。| | `url` | String | 是 | 指定的消息服务器。在未开启 DNS 的情况下使用,一般适用于开发者要实现数据隔离、特别注重数据安全的场景。 要获取该服务器地址,需在环信控制台的**即时通讯 > 服务概览**页面,查看**域名配置**表格中的**微信小程序**或**支付宝小程序**设置。 | -## 注册用户 - -本节介绍三种用户注册方式。 - -### 控制台注册 - -通过控制台注册用户,详见[创建 IM 用户](/product/enable_and_configure_IM.html#创建-im-用户)。 - -### REST API 注册 - -请参考 [注册用户](/document/server-side/account_system.html#注册用户)。 - -### SDK 注册 - -若支持 SDK 注册,需登录[环信即时通讯云控制台](https://console.easemob.com/user/login),选择 **即时通讯** > **服务概览**,将 **设置**下的 **用户注册模式** 设置为 **开放注册**。 - -```javascript -conn - .registerUser({ - /** 用户 ID。 */ - username: string, - /** 密码。 */ - password: string - }) - .then((res) => { - console.log(res); - }); -``` - -## 用户登录 - -SDK 不支持自动登录,只支持通过以下方式手动登录: - -- 用户 ID + 密码 -- 用户 ID + token - -登录时传入的用户 ID 必须为 String 类型,支持的字符集详见[用户注册的 RESTful 接口](/document/server-side/account_system.html#注册用户)。 - -调用登录接口后,收到 `onConnected` 回调表明 SDK 与环信服务器连接成功。 - -## 手动登录 - -**用户 ID +密码** 登录是传统的登录方式。用户 ID 和密码都是你的终端用户自行决定,密码需要符合密码规则要求。 - -```javascript -conn - .open({ - user: "username", - pwd: "password", - }) - .then(() => { - console.log("login success"); - }) - .catch((reason) => { - console.log("login fail", reason); - }); -``` - -**用户 ID + token** 是更加安全的登录方式。token 可以通过调用 REST API 获取,详见 [环信用户 token 的获取](/product/easemob_user_token.html)。 - -:::notice -使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 -::: - -```javascript -conn - .open({ - user: "username", - accessToken: "token", - }) - .then(() => { - console.log("login success"); - }) - .catch((reason) => { - console.log("login fail", reason); - }); -``` - -登录重试机制如下: - -- 登录时,若服务器返回明确的失败原因,例如,token 不正确,SDK 不会重试登录。 -- 若登录因超时失败,SDK 会重试登录。 - -## 退出登录 - -```typescript -conn.close(); -``` - -## 连接状态相关 - -你可以通过注册连接监听器确认连接状态。 - -```javascript -conn.addEventHandler("handlerId", { - onConnected: () => { - console.log("onConnected"); - }, - // 自 4.8.0 版本,`onDisconnected` 事件新增断开原因回调参数, 告知用户触发 `onDisconnected` 的原因。 - onDisconnected: () => { - console.log("onDisconnected"); - }, - onTokenWillExpire: () => { - console.log("onTokenWillExpire"); - }, - onTokenExpired: () => { - console.log("onTokenExpired"); - }, -}); -``` - -### 断网自动重连 - -如果由于网络信号弱、切换网络等引起的连接中断,系统会自动尝试重连。重连成功或者失败分别会收到 `onConnected` 和 `onDisconnected` 通知。 - -你可以设置最大重连次数 `autoReconnectNumMax`,该参数默认为 5 次。 - -### 被动退出登录 - -对于 `onDisconnected` 通知,错误码(`errorCode`)可能为以下几种,建议 App 返回登录界面。 - -| 错误码 | 描述 | -| :------------------------------------------------- | :------------------------- | -| WEBIM_CONNCTION_USER_LOGIN_ANOTHER_DEVICE=206 | 用户已经在其他设备登录。 | -| WEBIM_CONNCTION_USER_REMOVED=207 | 用户账户已经被移除。 | -| WEBIM_CONNCTION_USER_KICKED_BY_CHANGE_PASSWORD=216 | 由于密码变更被踢下线。 | -| WEBIM_CONNCTION_USER_KICKED_BY_OTHER_DEVICE=217 | 由于其他设备登录被踢下线。 | - -## 输出信息到日志文件 - -开启日志输出: - -```javascript -logger.enableAll(); -``` -- 设置日志不输出到控制台: - -```javascript -logger.setConsoleLogVisibility(false) -``` - -- 监听 SDK 日志事件: - -```javascript -logger.onLog = (log)=>{ - console.log('im logger', log) -} -``` - -关闭日志输出: - -```javascript -logger.disableAll(); -``` - -设置日志输出等级: - -```javascript -// 0 - 5 或者 'TRACE','DEBUG','INFO','WARN','ERROR','SILENT'; -logger.setLevel(0); -``` - -设置缓存日志: - -```javascript -logger.setConfig({ - useCache: false, // 是否缓存 - maxCache: 3 * 1024 * 1024, // 最大缓存字节 -}); -// 缓存全部等级日志 -logger.setLevel(0); -``` - -下载日志: - -```javascript -logger.download(); -``` - -## 日志上报 - -自 4.8.1 版本,小程序 SDK 支持日志上报功能, 即将日志会上传到环信服务器。该功能默认关闭,如有需要, 在初始化时,可将 `enableReportLogs` 参数设置为 `true`,然后联系商务开通日志上报功能。 \ No newline at end of file diff --git a/docs/document/applet/log.md b/docs/document/applet/log.md new file mode 100644 index 00000000..7de982af --- /dev/null +++ b/docs/document/applet/log.md @@ -0,0 +1,58 @@ +# SDK 日志 + +环信即时通讯 IM 日志记录 SDK 相关的信息和事件。环信技术支持团队帮你排查问题时可能会请你发送 SDK 日志。 + +## 输出信息到日志文件 + +开启日志输出: + +```javascript +logger.enableAll(); +``` +- 设置日志不输出到控制台: + +```javascript +logger.setConsoleLogVisibility(false) +``` + +- 监听 SDK 日志事件: + +```javascript +logger.onLog = (log)=>{ + console.log('im logger', log) +} +``` + +关闭日志输出: + +```javascript +logger.disableAll(); +``` + +设置日志输出等级: + +```javascript +// 0 - 5 或者 'TRACE','DEBUG','INFO','WARN','ERROR','SILENT'; +logger.setLevel(0); +``` + +设置缓存日志: + +```javascript +logger.setConfig({ + useCache: false, // 是否缓存 + maxCache: 3 * 1024 * 1024, // 最大缓存字节 +}); +// 缓存全部等级日志 +logger.setLevel(0); +``` + +下载日志: + +```javascript +logger.download(); +``` + +## 日志上报 + +自 4.8.1 版本,小程序 SDK 支持日志上报功能, 即将日志会上传到环信服务器。该功能默认关闭,如有需要, 在初始化时,可将 `enableReportLogs` 参数设置为 `true`,然后联系商务开通日志上报功能。 \ No newline at end of file diff --git a/docs/document/applet/login.md b/docs/document/applet/login.md new file mode 100644 index 00000000..9aab07e2 --- /dev/null +++ b/docs/document/applet/login.md @@ -0,0 +1,72 @@ +# 登录 + +初始化 IM SDK 后,你需要首先调用接口登录。登录成功后,才能使用 IM 的功能。 + +## 用户注册 + +用户注册支持以下方式: + +- 开放注册:一般在体验 Demo 和测试环境时使用,正式环境中不推荐使用该方式注册环信账号。要使用开放注册,需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**开放注册**。只有打开该开关,才能使用客户端或 [REST API](/document/server-side/account_system.html#开放注册单个用户)开放注册用户。 + +示例代码如下: + +```javascript +conn.registerUser({ + username: "user1", + password: "xxx", +}); +``` + +- 授权注册:通过环信提供的 REST API 注册环信用户账号,注册后保存到你的服务器或返给客户端。要使用授权注册,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**授权注册**。相关的 REST API 介绍,详见[授权注册单个用户](/document/server-side/account_system.html#授权注册单个用户)和[批量授权注册用户](/document/server-side/account_system.html#批量授权注册用户)的接口介绍。 + +除此以外,可以在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建正式环境下和测试环境下的用户,详见[创建用户相关介绍](/product/enable_and_configure_IM.html#创建-im-用户)。 + +## 登录方式 + +1. **用户 ID + token** 是更加安全的登录方式。 + +测试环境下,你在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建用户后,环信服务器会自动为这些用户分配用户 Token,详见[测试环境下创建用户的介绍](/product/enable_and_configure_IM.html#测试环境)。 + +使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 + +```javascript +conn + .open({ + user: "username", + accessToken: "token", + }) + .then(() => { + console.log("login success"); + }) + .catch((reason) => { + console.log("login fail", reason); + }); +``` +2. **用户 ID + 密码** 登录是传统的登录方式。用户名和密码均由你的终端用户自行决定,密码需要符合[密码规则要求](/document/server-side/account_system.html#开放注册单个用户)。 + +```javascript +conn + .open({ + user: "username", + pwd: "password", + }) + .then(() => { + console.log("login success"); + }) + .catch((reason) => { + console.log("login fail", reason); + }); +``` + +## 退出登录 + +```javascript +conn.close(); +``` + +## 多设备登录 + +除了单端单设备登录,环信即时通讯 IM 支持同一账号在多端的多个设备上登录。多设备登录时,若同端设备数量超过限制,新登录的设备会将之前登录的设备踢下线。 + +关于多设备登录场景中的设备数量限制、互踢策略以及信息同步,详见[多设备登录文档](multi_device.html)。 + diff --git a/docs/document/applet/message_send_receive.md b/docs/document/applet/message_send_receive.md index b501365a..672b17ae 100644 --- a/docs/document/applet/message_send_receive.md +++ b/docs/document/applet/message_send_receive.md @@ -2,56 +2,27 @@ -登录即时通讯服务后,用户可以在单聊、群聊、聊天室中发送如下类型的消息: +环信即时通讯 IM 的小程序 SDK 可以实现文本、图片、音频、视频和文件等类型的消息的发送和接收。 -- 文字消息,包含超链接和表情; -- 附件消息,包含图片、语音、视频及文件消息; -- 位置消息; -- 透传消息; -- 自定义消息; -- 合并消息。 +- 对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。 -对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 +- 对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 -针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 - -本文介绍如何使用即时通讯 IM SDK 实现小程序发送和接收这些类型的消息。 - -## 技术原理 - -环信即时通讯 IM SDK 可以实现消息的发送和接收。 - -发送和接收消息: - -- 消息发送方调用 `create` 方法创建文本、文件或附件消息。 -- 消息发送方调用 `send` 方法发送消息。 -- 消息接收方调用 `addEventHandler` 监听消息事件,并在相应回调中接收消息。 - -消息收发流程如下: - -1. 用户 A 发送一条消息到环信的消息服务器。 -2. 单聊时消息服务器发消息给用户 B,群聊时发消息给群内其他每个成员。 -3. 用户收到消息。 - -![img](/images/web/sendandreceivemsg.png) +单聊、群组聊天和聊天室的消息发送控制,详见[消息发送控制](/product/product_message_overview.html#消息发送控制)文档。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](wechat.html)。 +- 完成 SDK 初始化,详见 [初始化文档](initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -## 实现方法 +## 发送和接收文本消息 -### 发送文本消息 - -使用 `Message` 类创建并发送文本消息。 +1. 使用 `Message` 类创建并发送文本消息。 默认情况下,SDK 对单个用户发送消息的频率未做限制。如果你联系了环信商务设置了该限制,一旦在单聊、群聊或聊天室中单个用户的消息发送频率超过设定的上限,SDK 会上报错误,即错误码 509 `MESSAGE_CURRENT_LIMITING`。 -示例代码如下: - ```javascript // 发送文本消息。 function sendTextMessage() { @@ -76,109 +47,57 @@ function sendTextMessage() { } ``` -对于聊天室消息,可设置消息优先级。示例代码如下: - -```javascript -// 发送文本消息。 -function sendTextMessage() { - let option = { - type: "txt", - msg: "message content", - // 聊天室消息的优先级。如果不设置,默认值为 `normal`,即“普通”优先级。 - priority: "high" - to: "chat room ID", - chatType: "chatRoom", - }; - let msg = WebIM.message.create(opt); - conn.send(msg).then(()=>{ - console.log("Send message success"); - }).catch((e)=>{ - console.log("Send message fail"); - }); -} -``` - -若初始化时打开了 `useReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 +2. 你可以通过 `addEventHandler` 注册监听器监听消息事件。你可以添加多个事件。当不再监听事件时,请确保删除监听器。 -### 接收消息 +当消息到达时,接收方会收到 `onTextMessage` 回调。每个回调包含一条或多条消息。你可以遍历消息列表,并可以解析和展示回调中的消息。 -你可以通过 `addEventHandler` 注册监听器监听消息事件。你可以添加多个事件。当不再监听事件时,请确保删除监听器。 - -当消息到达时,接收方会收到 `onXXXMessage` 回调。每个回调包含一条或多条消息。你可以遍历消息列表,并可以解析和展示回调中的消息。 +对于聊天室消息,你可以通过消息的 `broadcast` 属性判断该消息是否为[通过 REST API 发送的聊天室全局广播消息](/document/server-side/message_chatroom.html#发送聊天室全局广播消息)。 ```javascript // 使用 `addEventHandler` 监听回调事件 -WebIM.conn.addEventHandler("eventName", { - // SDK 与环信服务器连接成功。 - onConnected: function (message) {}, - // SDK 与环信服务器断开连接。 - onDisconnected: function (message) {}, - // 当前用户收到文本消息。 +conn.addEventHandler("eventName", { onTextMessage: function (message) {}, - // 当前用户收到图片消息。 - onImageMessage: function (message) {}, - // 当前用户收到透传消息。 - onCmdMessage: function (message) {}, - // 当前用户收到语音消息。 - onAudioMessage: function (message) {}, - // 当前用户收到位置消息。 - onLocationMessage: function (message) {}, - // 当前用户收到文件消息。 - onFileMessage: function (message) {}, - // 当前用户收到自定义消息。 - onCustomMessage: function (message) {}, - // 当前用户收到视频消息。 - onVideoMessage: function (message) {}, - // 当前用户订阅的其他用户的在线状态更新。 - onPresence: function (message) {}, - // 当前用户收到好友邀请。 - onContactInvited: function (msg) {}, - // 联系人被删除。 - onContactDeleted: function (msg) {}, - // 新增联系人。 - onContactAdded: function (msg) {}, - // 当前用户发送的好友请求被拒绝。 - onContactRefuse: function (msg) {}, - // 当前用户发送的好友请求被同意。 - onContactAgreed: function (msg) {}, - // 当前用户收到群组邀请。 - onGroupEvent: function (message) {}, - // 本机网络连接成功。 - onOnline: function () {}, - // 本机网络掉线。 - onOffline: function () {}, - // 调用过程中出现错误。 - onError: function (message) {}, - // 当前用户收到的消息被消息发送方撤回。 - onRecallMessage: function (message) {}, - // 当前用户发送的消息被接收方收到。 - onReceivedMessage: function (message) {}, - // 当前用户收到消息送达回执。 - onDeliveredMessage: function (message) {}, - // 当前用户收到消息已读回执。 - onReadMessage: function (message) {}, - // 当前用户收到会话已读回执。 - onChannelMessage: function (message) {}, + }); ``` -### 发送附件消息 +## 发送和接收附件消息 语音、图片、视频和文件消息本质上是附件消息。发送和接收附件消息的流程如下: 1. 创建和发送附件类型消息。SDK 将附件上传到环信服务器,获取消息的基本信息以及服务器上附件文件的路径。 - 对于图片消息来说,服务器会自动生成图片的缩略图;而对于视频消息来说,视频的首帧为缩略图。 + 对于图片消息来说,环信服务器会自动生成图片缩略图;而对于视频消息来说,视频的首帧为缩略图。 2. 接收附件消息。 接收方可以自行下载语音、图片、图片缩略图、视频和文件。 + +对于消息附件,你也可以将附件上传到自己的服务器,而不是环信服务器,然后发送消息。这种情况下,需要在 SDK 初始化时将 [`Connection` 类中的 `useOwnUploadFun` 参数](https://doc.easemob.com/jsdoc/classes/Connection.Connection-1.html)设置为 `true`。例如,对于图片消息,上传附件后,调用 `sendPrivateUrlImg` 方法传入图片的 URL 发送图片消息。 -#### 发送语音消息 +```javascript +function sendPrivateUrlImg() { + let option = { + chatType: "singleChat", + // 消息类型。 + type: "img", + // 图片文件的 URL 地址。 + url: "img url", + // 消息接收方:单聊为对方用户 ID,群聊和聊天室分别为群组 ID 和聊天室 ID。 + to: "username", + }; + // 创建一条图片消息。 + let msg = WebIM.message.create(option); + // 调用 `send` 方法发送该图片消息。 + conn.send(msg); +} +``` + +### 发送和接收语音消息 在发送语音消息前,你应该在 app 级别实现录音,提供录制的语音文件的 URI 和时长(单位为秒)。 -参考以下代码示例创建和发送语音消息: +1. 创建和发送语音消息。 ```javascript /** @@ -217,7 +136,7 @@ function sendPrivateAudio(tempFilePath, duration) { }; let msg = WebIM.message.create(option); // 调用 `send` 方法发送该语音消息。 - WebIM.conn + conn .send(msg) .then((res) => { // 语音消息成功发送。 @@ -232,9 +151,25 @@ function sendPrivateAudio(tempFilePath, duration) { } ``` -#### 发送图片消息 +2. 接收方收到 `onAudioMessage` 回调,根据消息 `url` 字段获取语音文件的服务器地址,从而获取语音文件。 -参考以下代码示例创建和发送图片消息: +```javascript +// 使用 `addEventHandler` 监听回调事件 +conn.addEventHandler("eventName", { + // 当前用户收到语音消息。 + onAudioMessage: function (message) { + // 语音文件在服务器的地址。 + console.log(message.url); + }, +}); + +``` + +### 发送和接收图片消息 + +发送和接收图片消息的流程如下: + +1. 创建和发送图片消息。 ```javascript function sendImage() { @@ -300,7 +235,7 @@ function sendPrivateImg(res) { }; let msg = WebIM.message.create(option); // 调用 `send` 方法发送该图片消息。 - WebIM.conn + conn .send(msg) .then((res) => { // 图片消息成功发送。 @@ -318,35 +253,20 @@ function sendPrivateImg(res) { } ``` -#### 发送 URL 图片消息 - -你也可以将图片上传到自己的服务器,而不是环信服务器,然后调用 `sendPrivateUrlImg` 方法传入图片的 URL 发送图片消息。 - -发送 URL 图片消息前,确保将 `Connection` 类中的 `useOwnUploadFun` 设置为 `true`。 +2. 接收方收到 `onImageMessage` 回调,根据消息 `url` 字段获取图片文件的服务器地址,从而获取图片文件。 ```javascript -function sendPrivateUrlImg() { - let option = { - chatType: "singleChat", - // 消息类型。 - type: "img", - // 图片文件的 URL 地址。 - url: "img url", - // 消息接收方:单聊为对方用户 ID,群聊和聊天室分别为群组 ID 和聊天室 ID。 - to: "username", - }; - // 创建一条图片消息。 - let msg = WebIM.message.create(option); - // 调用 `send` 方法发送该图片消息。 - WebIM.conn.send(msg); -} +// 使用 `addEventHandler` 监听回调事件 +conn.addEventHandler("eventName", { + onImageMessage: function (message) {}, +}); ``` -#### 发送视频消息 +### 发送和接收视频消息 在发送视频消息之前,应在 app 级别实现视频捕获以及捕获文件的上传。 -参考以下代码示例创建和发送视频消息: +1. 创建和发送视频消息。 ```javascript function sendPrivateVideo(){ @@ -404,11 +324,27 @@ function sendPrivateVideo(){ }, ``` -#### 发送文件消息 + +2. 接收方收到 `onVideoMessage` 回调,根据消息 `url` 字段获取视频文件的服务器地址,从而获取视频文件。 + +```javascript +// 使用 `addEventHandler` 监听回调事件 +conn.addEventHandler("eventName", { + // 当前用户收到视频消息。 + onVideoMessage: function (message) { + // 视频文件在服务器的地址。 + console.log(message.url); + // 视频首帧缩略图文件在服务器的地址。 + console.log(message.thumb); + }, +}); +``` + +### 发送和接收文件消息 发送文件消息前,应先选择文件。微信小程序仅支持从客户端会话选择文件。 -参考以下代码示例创建、发送和接收文件消息: +1. 创建和发送文件消息。 ```javascript // 发送文件消息。 @@ -463,7 +399,7 @@ function sendFileMessage() { // 创建消息。 const msg = WebIM.message.create(option); // 发送消息。 - WebIM.conn + conn .send(msg) .then((res) => { console.log(res, "send file message success"); @@ -478,10 +414,27 @@ function sendFileMessage() { } ``` -### 发送位置消息 +2. 接收方收到 `onFileMessage` 回调,根据消息 `url` 字段获取文件的服务器地址,从而获取文件。 + +```javascript +// 使用 `addEventHandler` 监听回调事件 +conn.addEventHandler("eventName", { + // 当前用户收到文件消息。 + onFileMessage: function (message) { + // 文件在服务器的地址。 + console.log(message.url); + }, +}); + +``` -当你需要发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 +## 发送和接收位置消息 +发送和接收位置消息的流程如下: + +1. 创建和发送位置消息。 + +发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。 ```javascript const sendLocMsg = () => { let option = { @@ -502,7 +455,16 @@ const sendLocMsg = () => { }; ``` -### 发送透传消息 +2. 接收方收到 `onLocationMessage` 回调,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 + +```javascript +// 使用 `addEventHandler` 监听回调事件 +conn.addEventHandler("eventName", { + onLocationMessage: function (message) {}, +}); +``` + +## 发送和接收透传消息 透传消息是通知指定用户采取特定操作的命令消息。接收方自己处理透传消息。 @@ -510,7 +472,7 @@ const sendLocMsg = () => { 透传消息发送后,不支持撤回。 ::: -参考以下代码示例发送和接收透传消息: +1. 创建和发送透传消息。 ```javascript function sendCMDMessage() { @@ -529,7 +491,7 @@ function sendCMDMessage() { // 创建一条透传消息。 let msg = WebIM.message.create(option); // 调用 `send` 方法发送该透传消息。 - WebIM.conn + conn .send(msg) .then((res) => { // 消息成功发送回调。 @@ -542,92 +504,22 @@ function sendCMDMessage() { } ``` -#### 通过透传消息实现输入指示器 - -输入指示器显示其他用户何时输入消息。通过该功能,用户之间可进行有效沟通,设定对聊天应用程序中新交互的期望。你可以通过透传消息实现输入指示器。 - -你可以通过透传消息实现输入指示器。下图为输入指示器的工作原理。 +2. 接收方通过 `onCmdMessage` 回调接收透传消息。 -![img](/images/common/typing_indicator.png) - -监听用户 A 的输入状态。一旦有文本输入,通过透传消息将输入状态发送给用户 B,用户 B 收到该消息,了解到用户 A 正在输入文本。 - -- 用户 A 向用户 B 发送消息,通知其开始输入文本。 -- 收到消息后,如果用户 B 与用户 A 的聊天页面处于打开状态,则显示用户 A 的输入指示器。 -- 如果用户 B 在几秒后未收到用户 A 的输入,则自动取消输入指示器。 - -:::notice -用户 A 可根据需要设置透传消息发送间隔。 -::: - -以下示例代码展示如何发送输入状态的透传消息。 - -发送输入状态的用户。 - -```typescript -let previousChangedTimeStamp = 0; -// 监听输入状态的变化 -const onInputChange = function () { - const currentTimestamp = new Date().getTime(); - if (currentTimestamp - previousChangedTimeStamp > 5000) { - sendBeginTyping(); - previousChangedTimeStamp = currentTimestamp; - } -}; - -// 创建输入状态消息并发送 -const sendBeginTyping = function () { - const option = { - // 会话类型:单聊、群聊和聊天室分别为 `singleChat`、`groupChat` 和 `chatRoom`。 - chatType: "singleChat", - // 消息类型。 - type: "cmd", - // 消息接收方:单聊为对方用户 ID,群聊和聊天室分别为群组 ID 和聊天室 ID。 - to: "", - // 用户自定义操作。 - action: "TypingBegin", - }; - const typingMessage = message.create(option); - - connection - .send(typingMessage) - .then(() => { - console.log("success"); - }) - .catch((e) => { - console.log("fail"); - }); -}; -``` - -接收输入状态的用户。 - -```typescript -// 设置状态监听器 -let timer; -conn.addEventHandler("message", { - onCmdMessage: (msg) => { - console.log("onCmdMessage", msg); - if (msg.action === "TypingBegin") { - // 这里需更新 UI,显示“对方正在输入” - beginTimer(); - } - }, +```javascript +// 使用 `addEventHandler` 监听回调事件 +conn.addEventHandler("eventName", { + onCmdMessage: function (message) {}, }); - -const beginTimer = () => { - timer && clearTimeout(timer); - timer = setTimeout(() => { - // 这里需更新 UI,不再显示“对方正在输入” - }, 5000); -}; ``` -### 发送自定义消息 + +## 发送和接收自定义消息 自定义消息为用户自定义的键值对,包括消息类型和消息内容。 +发送和接收自定义消息的流程如下: -参考以下示例代码创建和发送自定义消息: +1. 创建和发送自定义消息。 ```javascript function sendCustomMsg() { @@ -643,6 +535,7 @@ function sendCustomMsg() { // 会话类型:单聊、群聊和聊天室分别为 `singleChat`、`groupChat` 和 `chatRoom`。 chatType: "singleChat", customEvent, + // key 和 value 只支持字符串类型,否则客户端无法解析。 customExts, // 消息扩展字段,不能设置为空,即设置为 "ext:null" 会出错。 ext: {}, @@ -650,7 +543,7 @@ function sendCustomMsg() { // 创建一条自定义消息。 let msg = WebIM.message.create(option); // 调用 `send` 方法发送该自定义消息。 - WebIM.conn + conn .send(msg) .then((res) => { // 消息成功发送回调。 @@ -663,7 +556,16 @@ function sendCustomMsg() { } ``` -### 发送合并消息 +2. 接收方通过 `onCustomMessage` 回调接收自定义消息。 + +```javascript +// 使用 `addEventHandler` 监听回调事件 +conn.addEventHandler("eventName", { + onCustomMessage: function (message) {}, +}); +``` + +## 发送和接收合并消息 为了方便消息互动,即时通讯 IM 自 4.2.0 版本开始支持将多个消息合并在一起进行转发。你可以采取以下步骤进行消息的合并转发: @@ -671,7 +573,7 @@ function sendCustomMsg() { 2. 发送合并消息。 3. 对端收到合并消息后进行解析,获取原始消息列表。 -:::notice +:::tip 该功能在 uniapp 中暂不支持运行到原生手机端。 @@ -690,11 +592,11 @@ function sendCustomMsg() { | `title` | String | 合并消息的标题。 | | `summary` | String | 合并消息的概要。 | | `compatibleText` | String | 合并消息的兼容文本。
兼容文本起向下兼容不支持消息合并转发的版本的作用。当支持合并消息的 SDK 向不支持合并消息的低版本 SDK 发送消息时,低版本的 SDK 会将该属性解析为文本消息的消息内容。 | -| `messageIdList` | MessagesType[] | 合并消息的原始消息 ID 列表。该列表最多包含 300 个消息 ID。 | +| `messageList` | MessagesType[] | 合并消息的消息列表。该列表最多包含 300 个消息。 | | `onFileUploadComplete` | (data: { url: string; secret: string;}) => void | 合并消息文件上传完成的回调。 | | `onFileUploadError` | (error: any) => void | 合并消息文件上传失败的回调。 | -:::notice +:::tip 1. 合并转发支持嵌套,最多支持 10 层嵌套,每层最多 300 条消息。 2. 只有成功发送或接收的消息才能合并转发。 ::: @@ -712,6 +614,7 @@ let option = { messageList: [ { type: "txt", + chatType: "singleChat", // ... }, ], @@ -719,7 +622,7 @@ let option = { option.url = data.url; }, }; -let msg = WebIM.message; +let msg = WebIM.message.create(option); conn.send .send(msg) .then((res) => { @@ -730,11 +633,11 @@ conn.send }); ``` -接收合并消息与[接收普通消息](#接收消息)的操作相同,唯一不同是对于合并消息来说,消息接收事件为 `onCombineMessage`。 +#### 接收和解析合并消息 -对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 +接收合并消息与接收普通消息的操作相同,唯一不同是对于合并消息来说,消息接收事件为 `onCombineMessage`。 -#### 解析合并消息 +对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 合并消息实际上是一种附件消息。收到合并消息后,你可以调用 `downloadAndParseCombineMessage` 方法下载合并消息附件并解析出原始消息列表。 @@ -749,13 +652,13 @@ connection }); ``` -### 发送定向消息 +## 发送和接收定向消息 发送定向消息是指向群组或聊天室的单个或多个指定的成员发送消息,其他成员不会收到该消息。 该功能适用于文本消息、图片消息和音视频消息等全类型消息,最多可向群组或聊天室的 20 个成员发送定向消息。 -:::notice +:::tip 1. 仅 SDK 4.2.0 及以上版本支持。 2. 定向消息不写入服务端会话列表,不计入服务端会话的未读消息数。 3. 群组定向消息的漫游功能默认关闭,使用前需联系商务开通。 @@ -792,9 +695,9 @@ function sendTextMessage() { } ``` -接收定向消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +接收定向消息与接收普通消息的操作相同,详见各类消息的接收描述。 -### 使用消息扩展 +## 使用消息扩展 如果上述类型的消息无法满足要求,你可以使用消息扩展为消息添加属性。这种情况可用于更复杂的消息传递场景,例如消息中需要携带被回复的消息内容或者是图文消息等场景。 @@ -814,8 +717,8 @@ function sendTextMessage() { }, }; let msg = WebIM.message.create(option); - // 调用 `send` 方法发送该扩展消息。 - WebIM.conn + // 调用 `send` 方法发送该扩展消息。 + conn .send(msg) .then((res) => { console.log("send private text Success"); @@ -826,6 +729,40 @@ function sendTextMessage() { } ``` -## 注意事项 +## 更多 + +### 设置聊天室消息优先级 + +针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 + +对于聊天室消息,可设置消息优先级,包括高、普通和低优先级。示例代码如下: + +```javascript +// 发送文本消息。 +function sendTextMessage() { + let option = { + type: "txt", + msg: "message content", + // 聊天室消息的优先级。如果不设置,默认值为 `normal`,即“普通”优先级。 + priority: "high" + to: "chat room ID", + chatType: "chatRoom", + }; + let msg = WebIM.message.create(option); + conn.send(msg).then(()=>{ + console.log("Send message success"); + }).catch((e)=>{ + console.log("Send message fail"); + }); +} +``` + +### 发送消息前的内容审核 + +- 内容审核关注消息 body [内容审核服务会关注消息 body 中指定字段的内容,不同类型的消息审核不同的字段](/product/moderation/moderation_mechanism.html),若创建消息时在这些字段中传入了很多业务信息,可能会影响审核效果。因此,创建消息时需要注意内容审核的字段不涉及业务信息,建议业务信息放在扩展字段中。 + +- 设置发送方收到内容审核替换后的内容 + +若初始化时打开了 `EMOptions#useReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 \ No newline at end of file diff --git a/docs/document/applet/overview.md b/docs/document/applet/overview.md index d6aa9b3a..a75c72b2 100644 --- a/docs/document/applet/overview.md +++ b/docs/document/applet/overview.md @@ -4,7 +4,7 @@ 环信小程序 SDK 为各端小程序开发提供一套完整的技术解决方案,在各端小程序的开发环境下,集成 IM 相关的功能更加便捷、高效。让您的小程序快速获得安全稳定的 IM 能力,集成简单,使用方便,帮助您快速拓展业务,赢得先机。 -:::notice +:::tip SDK 目前支持微信、QQ、百度小程序、抖音、uni-app 编译的原生 Android 以及 iOS。 ::: @@ -19,7 +19,7 @@ SDK 目前支持微信、QQ、百度小程序、抖音、uni-app 编译的原生 - 安卓: [https://www.pgyer.com/h4XF](https://www.pgyer.com/h4XF) - iOS: [https://www.pgyer.com/9ISC](https://www.pgyer.com/9ISC) -:::notice +:::tip - 小程序 Demo 只包含部分 IM 功能,详细参考 **功能说明** - 各小程序 Demo 源码可在官网 [下载页](https://www.easemob.com/download/im) 获取 diff --git a/docs/document/applet/qq.md b/docs/document/applet/qq.md index 1320280b..8d7c85e7 100644 --- a/docs/document/applet/qq.md +++ b/docs/document/applet/qq.md @@ -28,6 +28,8 @@ request 合法域名: 3. https://a3.easemob.com 4. https://a4.easemob.com 5. https://a5.easemob.com +6. https://rs.chat.agora.io +7. https://rs.easemob.com ::: :::tip @@ -35,7 +37,7 @@ socket 合法域名: wss://im-api-wechat.easemob.com(3.0 IM SDK) ::: -:::notice +:::tip 为满足不同客户的业务需求,环信在多地部署了数据中心。不同数据中心的 REST API 请求域名、WebSocket 访问域名不同。请根据您所在数据中心进行配置。 ::: @@ -88,7 +90,7 @@ import SDK from "../sdk/Easemob-chat-miniProgram"; // 3.0 SDK #### 实例调用方式 -实例化 SDK,并挂载在全局对象下 +实例化 SDK,并挂载在全局对象下。 ```javascript // 实例化 SDK 对象 diff --git a/docs/document/applet/releasenote.md b/docs/document/applet/releasenote.md index 15d23fb9..301ff53d 100644 --- a/docs/document/applet/releasenote.md +++ b/docs/document/applet/releasenote.md @@ -118,7 +118,7 @@ - [IM SDK] [聊天室和群组成员进出事件增加成员人数 `memberCount` 字段](room_manage.html#实时更新聊天室成员人数)。 - [IM SDK] 新增 [deleteAllMessagesAndConversations](message_delete.html#单向清空服务端的聊天记录) 方法, 用于清空当前用户的聊天记录,包括消息和会话。 - [IM SDK] 新增 [getSelfIdsOnOtherPlatform](multi_device.html#获取当前用户的其他登录设备的登录-id-列表) 方法, 可以获取当前用户其他登录设备的登录 ID 列表,实现对指定设备发送消息。 -- [IM SDK] 新增 [useReplacedMessageContents](message_send_receive.html#发送文本消息) 开关。开启后,发送消息时如果被内容审核进行了内容替换,发送方可以获取替换后的内容。 +- [IM SDK] 新增 [useReplacedMessageContents](message_send_receive.html#发送和接收文本消息) 开关。开启后,发送消息时如果被内容审核进行了内容替换,发送方可以获取替换后的内容。 ### 优化 @@ -170,9 +170,9 @@ ### 新增特性 -- [IM SDK] 新增[合并转发消息功能](message_send_receive.html#发送合并消息)。 +- [IM SDK] 新增[合并转发消息功能](message_send_receive.html#发送和接收合并消息)。 - [IM SDK] 新增[消息修改功能](message_modify.html)。 -- [IM SDK] 新增[在群组或聊天室会话中发送定向消息](message_send_receive.html#发送定向消息)。通过在构建消息的方法 `create` 中添加 `receiverList` 参数实现该特性。 +- [IM SDK] 新增[在群组或聊天室会话中发送定向消息](message_send_receive.html#发送和接收定向消息)。通过在构建消息的方法 `create` 中添加 `receiverList` 参数实现该特性。 ### 修复 @@ -226,7 +226,7 @@ - [IM SDK] [创建群组方法 `createGroup`](group_manage.html#创建群组) 和[修改群信息方法 `modifyGroup`](group_attributes.html#修改群组信息) 新增 `ext` 字段支持群扩展信息。 - [IM SDK] 群组通知事件增加[群组信息修改事件 `updateInfo`](group_manage.html#监听群组事件)。 -- [IM SDK] 新增[聊天室消息优先级](message_send_receive.html)。 +- [IM SDK] 新增[聊天室消息优先级](#设置聊天室消息优先级)。 - [IM SDK] 支持同时[对多个群组成员禁言和解除禁言](group_members.html#管理群组禁言)。 ## 版本 V4.1.1 Dev 2022-9-26 diff --git a/docs/document/applet/serverconfig.md b/docs/document/applet/serverconfig.md index ed35af40..54dbf9bb 100644 --- a/docs/document/applet/serverconfig.md +++ b/docs/document/applet/serverconfig.md @@ -27,7 +27,7 @@ 7. 登陆环信后台获取 socket 域名,进入即时通讯-服务概览页,即可看到如图的域名展示。 -:::notice +:::tip 抖音小程序、QQ 小程序、百度小程序、uniapp 全平台等同微信小程序的域名一致,使用微信小程序栏的域名即可。 ::: diff --git a/docs/document/applet/uniapp.md b/docs/document/applet/uniapp.md index f48f6a51..686f8310 100644 --- a/docs/document/applet/uniapp.md +++ b/docs/document/applet/uniapp.md @@ -15,7 +15,7 @@ ![img](/images/applet/applet-demo.png) -:::notice +:::tip - 小程序 Demo 只包含部分 IM 功能,详细参考 **功能说明**。 - Uni-app Demo Git 源码地址 [https://github.com/easemob/webim-uniapp-demo](https://github.com/easemob/webim-uniapp-demo) @@ -80,6 +80,8 @@ request 合法域名,uploadFile 合法域名,downloadFile 合法域名 8. https://a41.easemob.com 9. https://a51.easemob.com 10. https://a1-chatfile.easemob.com +11. https://rs.chat.agora.io +12. https://rs.easemob.com ::: :::tip diff --git a/docs/document/applet/uniappnativeapp.md b/docs/document/applet/uniappnativeapp.md index 40fc70f0..c97714d2 100644 --- a/docs/document/applet/uniappnativeapp.md +++ b/docs/document/applet/uniappnativeapp.md @@ -9,7 +9,7 @@ - 安卓: [https://www.pgyer.com/h4XF](https://www.pgyer.com/h4XF) - iOS: [https://www.pgyer.com/9ISC](https://www.pgyer.com/9ISC) -:::notice +:::tip - Demo 只包含部分 IM 功能,详细参考 **功能说明** - Uni-app Demo Git 源码地址 [https://github.com/easemob/webim-uniapp-demo](https://github.com/easemob/webim-uniapp-demo)::: @@ -29,7 +29,7 @@ 1. 下载 HBuilderx 编辑器 https://www.dcloud.io/hbuilderx.html -:::notice +:::tip 项目中 HBuilderx 目前使用的最新版本,如果此前下载过 HBuilderx,为保证开发环境统一请更新到最新版本 ::: @@ -65,7 +65,7 @@ import SDK from "../sdk/webimSDK3.x.x"; // 3.0sdk #### 实例调用方式 -实例化 SDK,并挂载在全局对象下 +实例化 SDK,并挂载在全局对象下。 ```javascript //实例化 SDK 对象 diff --git a/docs/document/applet/uniappuikit.md b/docs/document/applet/uniappuikit.md index 419af9db..bb0fac12 100644 --- a/docs/document/applet/uniappuikit.md +++ b/docs/document/applet/uniappuikit.md @@ -18,7 +18,7 @@ HBuilderx ⇒ 工具 ⇒ 插件安装 ⇒ 安装新插件 ⇒ 前往插件市场 ### 集成说明 -:::notice +:::tip 小程序模版必须依赖环信 IM SDK,因而在使用小程序模版时必须同时添加环信 IM SDK 依赖。 ::: @@ -111,7 +111,7 @@ import longPressModal from "@/components/longPressModal/index"; - `hidePop` – Function,显隐框操作事件 - `popStyle` – string,样式 -:::notice +:::tip 使用长按组件时候需要先获取当前窗口,再根据当前点击的位置来显示组件框: ::: diff --git a/docs/document/applet/wechat.md b/docs/document/applet/wechat.md index 2c70fda3..2f798e99 100644 --- a/docs/document/applet/wechat.md +++ b/docs/document/applet/wechat.md @@ -31,6 +31,8 @@ request 合法域名: 4. https://a4.easemob.com 5. https://a5.easemob.com 6. https://a1-chatfile.easemob.com +7. https://rs.chat.agora.io +8. https://rs.easemob.com ::: :::tip @@ -56,7 +58,7 @@ downloadFile 合法域名: 8. https://a1-chatfile.easemob.com ::: -:::notice +:::tip 为满足不同客户的业务需求,环信在多地部署了数据中心。不同数据中心的 REST API 请求域名、WebSocket 访问域名不同。请根据您所在数据中心进行配置。 ::: @@ -105,7 +107,7 @@ import SDK from "../sdk/Easemob-chat-miniProgram"; // 3.0sdk #### 实例调用方式 -实例化 SDK,并挂载在全局对象下 +实例化 SDK,并挂载在全局对象下。 ```javascript //实例化SDK对象 diff --git a/docs/document/electron/message.md b/docs/document/electron/message.md index fc540a96..86f79eb1 100644 --- a/docs/document/electron/message.md +++ b/docs/document/electron/message.md @@ -4,7 +4,7 @@ - 发送消息 -- 接受消息 +- 接收消息 同时对于发送不超过2分钟的消息,允许主动撤回。 diff --git a/docs/document/flutter/connection.md b/docs/document/flutter/connection.md new file mode 100644 index 00000000..fc72cc64 --- /dev/null +++ b/docs/document/flutter/connection.md @@ -0,0 +1,49 @@ +# 连接 + +应用客户端成功连接到环信服务器后,才能使用环信即时通讯 SDK 的收发消息等功能。 + +你调用 `loginWithToken` 方法登录后,客户端 SDK 会自动连接环信服务器。关于登录详情,请参见[登录文档](login.html)。 + +## 监听连接状态 + +你可以通过注册连接监听确认连接状态。 + +```dart +// 添加监听 +EMClient.getInstance.addConnectionEventHandler( + 'Identifier', + EMConnectionEventHandler( + onConnected: () {}, + onDisconnected: () {}, + onTokenDidExpire: () {}, + onTokenWillExpire: () {}, + onUserAuthenticationFailed: () {}, + onUserDidForbidByServer: () {}, + onUserDidChangePassword: () {}, + onUserDidLoginFromOtherDevice: (info) {}, + onUserDidLoginTooManyDevice: () {}, + onUserDidRemoveFromServer: () {}, + onUserKickedByOtherDevice: () {}, + onAppActiveNumberReachLimit: () {}, + onOfflineMessageSyncStart: () {}, + onOfflineMessageSyncFinish: () {}, + ), +); + +// 移除监听 +EMClient.getInstance.removeConnectionEventHandler('Identifier'); +``` + +## 自动重连 + +登录后,如果由于网络信号弱、切换网络等引起的连接中断,SDK 会自动尝试重连。重连成功或者失败时分别会收到 `onConnected` 和 `onDisconnected` 通知。 + +不过,SDK 在以下情况下会停止自动重连。你需要调用 `login` 方法登录。 + +- 用户调用了 SDK 的登出方法 `logout` 主动退出登录。 +- 登录时鉴权错误,例如, token 无效(错误码 104)或已过期(错误码 108)。 +- 用户在其他的设备上更改了密码,导致此设备上自动登录失败,提示错误码 216。 +- 用户的账号被从服务器端删除,提示错误码 207。 +- 用户在另一设备登录,将当前设备上登录的用户踢出,提示错误码 206。 +- 用户登录设备数量超过限制,提示错误码 214。 +- 应用程序的日活跃用户数量(DAU)或月活跃用户数量(MAU)达到上限,提示错误码 8。 \ No newline at end of file diff --git a/docs/document/flutter/conversation_receipt.md b/docs/document/flutter/conversation_receipt.md new file mode 100644 index 00000000..5b74f3bc --- /dev/null +++ b/docs/document/flutter/conversation_receipt.md @@ -0,0 +1,74 @@ +# 会话已读回执 + +会话已读回执指接收方进入会话页面,阅读会话中的所有消息后,调用接口向服务器发送会话已读回执,服务器将该回执回调给消息发送方,消息发送方将会收到会话已读回调。在多端多设备登录下,接收方的其他设备也会收到该回调。 + +目前,单聊和群组聊天支持会话已读回执。本文介绍如何使用环信即时通讯 IM Flutter SDK 实现会话已读回执功能。 + +会话已读回执的效果示例,如下图所示: + +![img](/images/uikit/chatuikit/feature/conversation/conversation_read.png) + +## 技术原理 + + 单聊会话已读回执实现的流程如下: + + 1. 设置 `EMOptions#requireAck` 为 `true` 开启已读回执功能。 + 2. 消息接收方进入会话页面,阅读消息后,调用 `EMChatManager#sendConversationReadAck` 方法发送会话已读回执。 + 3. 消息发送方通过监听 `EMChatEventHandler#onConversationRead` 回调接收会话已读回执, 同时会话中的消息会变为“对方已读”状态,即 `EMMessage#hasReadAck` 会变为 `true`。 + +## 前提条件 + +开始前,请确保满足以下条件: + +- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 +- 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 + + ## 实现方法 + + 参考以下步骤在单聊中实现会话已读回执: + + 1. 开启已读回执功能,即 SDK 初始化时设置 `EMOptions#requireAck` 为 `true`。 + + ```dart +// 设置是否需要接收方已读确认,默认为 true + final options = EMOptions( + appKey: appKey, + requireAck: true, + ); + ``` + + 2. 接收方发送会话已读回执。 + +消息接收方进入会话页面,查看会话中是否有未读消息。若有,调用 `EMChatManager#sendConversationReadAck` 方法发送会话已读回执,没有则不发送。该方法为异步方法,需要捕捉异常。 + +若会话中存在多条未读消息,建议调用该方法,因为若调用发送消息已读回执方法 `ackMessageRead`,则需要调用多次。 + +```dart +try { + await EMClient.getInstance.chatManager + .sendConversationReadAck(conversationId); +} on EMError catch (e) { + debugPrint(e.toString()); +} +``` + +3. 消息发送方监听会话已读回执的回调。 + +同一用户 ID 登录多设备的情况下,用户在一台设备上发送会话已读回执,其他设备会收到 `EMChatEventHandler#onConversationRead` 回调。 + +:::tip +对于群组聊天,会话已读回执只用于清空服务端的群组会话的未读数,消息发送方不会通过 `EMChatEventHandler#onConversationRead` 回调收到会话已读回执。 +::: + +```dart +EMClient.getInstance.chatManager.addEventHandler( + 'identifier', + EMChatEventHandler( + onConversationRead: (from, to) {}, + ), +); +``` + +## 会话已读回执和消息未读数 + +消息接收方调用 `EMChatManager#sendConversationReadAck` 方法发送会话已读回执,开发者可调用 `EMConversation#markAllMessagesAsRead` 方法将所有未读消息设置为已读,即将该会话的未读消息数清零。 diff --git a/docs/document/flutter/group_manage.md b/docs/document/flutter/group_manage.md index 9c7c954a..e917625d 100644 --- a/docs/document/flutter/group_manage.md +++ b/docs/document/flutter/group_manage.md @@ -78,7 +78,7 @@ try { 仅群主可以调用 `DestroyGroup` 方法解散群组。群组解散时,其他群组成员收到 `EMGroupEventHandler#onGroupDestroyed` 事件并被踢出群组。 -:::notice +:::tip 解散群组后,将删除本地数据库及内存中的群相关信息及群会话,谨慎操作。 ::: @@ -99,7 +99,7 @@ try { - 群主或群管理员同意入群申请,申请人收到 `EMGroupEventHandler#onRequestToJoinAcceptedFromGroup` 事件,其他群成员收到`EMGroupEventHandler#onMemberJoinedFromGroup` 事件; - 群主或群管理员拒绝入群申请,申请人收到 `EMGroupEventHandler#onRequestToJoinDeclinedFromGroup` 事件。 -:::notice +:::tip 用户只能申请加入公开群组,私有群组不支持用户申请入群。 ::: diff --git a/docs/document/flutter/group_members.md b/docs/document/flutter/group_members.md index ad34d6a6..573e682b 100644 --- a/docs/document/flutter/group_members.md +++ b/docs/document/flutter/group_members.md @@ -109,7 +109,7 @@ try { 你可调用 `EMGroupManager#fetchMembersAttributes` 方法根据指定的属性 key 获取多个群成员的自定义属性。 -:::notice +:::tip 每次最多可获取 10 个群成员的自定义属性。 ::: diff --git a/docs/document/flutter/initialization.md b/docs/document/flutter/initialization.md new file mode 100644 index 00000000..c8fb342a --- /dev/null +++ b/docs/document/flutter/initialization.md @@ -0,0 +1,22 @@ +# SDK 初始化 + +初始化是使用 SDK 的必要步骤,需在所有接口方法调用前完成。 + +如果进行多次初始化操作,只有第一次初始化以及相关的参数生效。 + +:::tip +需要在主进程中进行初始化。 +::: + +## 前提条件 + +有效的环信即时通讯 IM 开发者账号和 App key,详见[环信即时通讯云控制台的相关文档](enable_and_configure_IM.html#创建应用)。 + +## 初始化 + +初始化示例代码: + +```dart +final options = EMOptions(appKey: appKey); +await EMClient.getInstance.init(options); +``` diff --git a/docs/document/flutter/integration.md b/docs/document/flutter/integration.md new file mode 100644 index 00000000..b01ee875 --- /dev/null +++ b/docs/document/flutter/integration.md @@ -0,0 +1,55 @@ +# 集成 SDK + +本文介绍如何将环信即时通讯 IM SDK 集成到你的 Flutter 项目中。 + +## 开发环境要求 + +- Flutter 2.0.0 或以上版本; +- Dart 2.12 或以上版本; + +### 使用命令创建项目 + +```dart +flutter create quick_start +``` + +### 设置 Android + +1. 打开文件 `quick_start/android/app/build.gradle` 在文件最后添加: + +```dart +android { + defaultConfig { + minSdkVersion 21 + } +} +``` + +2. 打开文件 `quick_start/android/app/src/main/AndroidManifest.xml`,在 `` 下添加: + +```xml + + + +``` + +3. 在 `quick_start/android/app/proguard-rules.pro` 中设置免混淆规则: + +```java +-keep class com.hyphenate.** {*;} +-dontwarn com.hyphenate.** +``` + +### 设置 iOS + +iOS 需要 iOS 11.0 以上版本。 + +打开文件 `quick_start/ios/Runner.xcodeproj`,选择 **TARGETS > General > Deployment info**, 设置 iOS 版本为 11.0。 + +### 集成 SDK + +```shell +cd quick_start +flutter pub add im_flutter_sdk +flutter pub get +``` diff --git a/docs/document/flutter/login.md b/docs/document/flutter/login.md new file mode 100644 index 00000000..da0af7f8 --- /dev/null +++ b/docs/document/flutter/login.md @@ -0,0 +1,86 @@ +# 登录 + +初始化 IM SDK 后,你需要首先调用接口登录。登录成功后,才能使用 IM 的功能。 + +## 用户注册 + +用户注册模式分为以下两种: + +- 开放注册:一般在体验 Demo 和测试环境时使用,正式环境中不推荐使用该方式注册环信账号。要使用开放注册,需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**开放注册**。只有打开该开关,才能使用客户端或 [REST API](/document/server-side/account_system.html#开放注册单个用户)开放注册用户。 + +示例代码如下所示: + +```dart +try { + await EMClient.getInstance.createAccount(userId, password); +} on EMError catch (e) {} +``` + +- 授权注册:通过环信提供的 REST API 注册环信用户账号,注册后保存到你的服务器或返给客户端。要使用授权注册,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**授权注册**。相关的 REST API 介绍,详见[授权注册单个用户](/document/server-side/account_system.html#授权注册单个用户)和[批量授权注册用户](/document/server-side/account_system.html#批量授权注册用户)的接口介绍。 + +除此以外,可以在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建正式环境下和测试环境下的用户,详见[创建用户相关介绍](/product/enable_and_configure_IM.html#创建-im-用户)。 + +## 主动登录 + +1. **用户 ID + token** 是更加安全的登录方式。token 可以通过调用 REST API 获取。 详见 [环信用户 token 的获取](/document/server-side/easemob_user_token.html)。 + +测试环境下,你在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建用户后,环信服务器会自动为这些用户分配用户 Token,详见[测试环境下创建用户的介绍](/product/enable_and_configure_IM.html#测试环境)。 + +使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 + +```dart +try { + await EMClient.getInstance.loginWithToken(userId, token); +} on EMError catch (e) { + debugPrint("loginWithToken error: ${e.code} ${e.description}"); +} +``` + +2. **用户 ID + 密码** 登录是传统的登录方式。用户名和密码都是你的终端用户自行决定,密码需要符合密码规则要求。 + +```dart +try { + await EMClient.getInstance.login(userId, password); +} on EMError catch (e) {} +``` + +## 自动登录 + +初始化时,你可以设置 `EMOptions#autoLogin` 选项确定是否自动登录。如果设置为自动登录,则登录成功之后,后续初始化 SDK 时会自动登录。 + +## 获取当前登录的用户 + +你可以调用 `EMClient#currentUserId` 方法获取当前登录用户的用户 ID。 + +## 获取登录状态 + +你可以调用 `EMClient#isLoginBefore` 方法获取当前用户的登录状态。 + +## 退出登录 + +你可以调用 `EMClient#logout` 方法退出登录。退出登录后,你不会再收到其他用户发送的消息。 + +```dart +try { + await EMClient.getInstance.logout(); +} on EMError catch (e) { + debugPrint("logout error: ${e.code} ${e.description}"); +} +``` + +## 账号切换 + +若在 app 中从当前账号切换到其他账号,你需要首先调用 `logout` 方法登出,然后再调用 `loginWithToken` 方法登录。 + +## 多设备登录 + +除了单端单设备登录,环信即时通讯 IM 支持同一账号在多端的多个设备上登录。多设备登录时,若同端设备数量超过限制,新登录的设备会将之前登录的设备踢下线。 + +关于多设备登录场景中的设备数量限制、互踢策略以及信息同步,详见[多设备登录文档](multi_device.html)。 + + +## 更多 + +### 登录被封禁账号的提示 + +在环信即时通讯控制台或调用 REST API 封禁用户账号后,若仍使用该账号登录,SDK会返回 "service is disabled"(305 错误), 可以根据用户这个返回值来进行相应的提示或者处理。 diff --git a/docs/document/flutter/message_receipt.md b/docs/document/flutter/message_receipt.md index 3e6a2329..2063434e 100644 --- a/docs/document/flutter/message_receipt.md +++ b/docs/document/flutter/message_receipt.md @@ -1,40 +1,36 @@ -# 消息回执 +# 实现消息回执 -单聊会话支持消息送达回执、会话已读回执和消息已读回执,发送方发送消息后可及时了解接收方是否及时收到并阅读了信息,也可以了解整个会话是否已读。 +**单聊会话支持消息送达回执和消息已读回执**,发送方发送消息后可及时了解接收方是否及时收到并阅读了消息。 -群聊会话只支持消息已读回执。群成员在发送消息时,可以设置该消息是否需要已读回执。仅专业版及以上版本支持群消息已读回执功能。若要使用该功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 +**群聊会话只支持消息已读回执,不支持送达回执**。群成员在发送消息时,可以设置该消息是否需要已读回执。要使用该功能,你需要[在环信即时通讯云控制台上开通该功能](/product/enable_and_configure_IM.html#设置群消息已读回执),具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 -:::tip -仅单聊消息支持送达回执,群聊消息不支持。 -::: +消息送达回执和已读回执的效果示例,如下图所示: -本文介绍如何使用环信即时通讯 IM Flutter SDK 实现单聊和群聊的消息回执功能。 +![img](/images/android/message_receipt.png) ## 技术原理 -环信即时通讯 IM Flutter SDK 通过 `EMChatManager` 类提供消息的送达回执和已读回执功能。核心方法如下: +使用环信即时通讯 IM Flutter SDK 可以实现消息的送达回执与已读回执。 -- `EMOptions.requireDeliveryAck` 送达回执的全局开关; -- `EMOptions.requireAck` 已读回执的全局开关; -- `EMChatManager.sendConversationReadAck` 发送会话的已读回执; -- `EMChatManager.sendMessageReadAck` 发送单聊消息的已读回执; -- `EMChatManager.sendGroupMessageReadAck` 发送群组消息的已读回执; +- 单聊消息送达回执的逻辑如下: -实现送达和已读回执的逻辑分别如下: + 1. 你可以通过设置 `EMOptions#requireDeliveryAck` 为 `true` 开启送达回执功能。 + 2. 消息接收方收到消息后,SDK 自动向发送方触发送达回执。 + 3. 消息发送方通过监听 `EMChatEventHandler#onMessagesDelivered` 回调接收消息送达回执。 -- 消息送达回执 +- 单聊消息已读回执的逻辑如下: - 1. 消息发送方在发送消息前通过 `EMOptions.requireDeliveryAck` 开启送达回执功能。 - 2. 消息接收方收到消息后,SDK 自动向发送方触发送达回执。 - 3. 消息发送方通过监听 `onMessageDelivered` 回调接收消息送达回执。 + 1. 你可以通过设置 `EMOptions#requireAck` 为 `true` 开启已读回执功能。 + 2. 消息接收方收到消息后,调用 `EMChatManager#sendMessageReadAck` 方法发送消息已读回执。 + 3. 消息发送方通过监听 `EMMessageListener#onMessagesRead` 回调接收消息已读回执。 -- 会话及消息已读回执 +- 群聊消息已读回执的逻辑如下: - 1. 消息发送方在发送消息前通话 `EMOptions.requireAck` 开启已读回执功能。 - 2. 消息接收方收到或阅读消息后,调用 API `SendConversationReadAck` 或 `SendMessageReadAck` 发送会话或消息已读回执。 - 3. 消息发送方通过监听 `onConversationRead` 或 `onMessagesRead` 回调接收会话或消息已读回执。 + 1. 你可以通过设置 `EMOptions#requireAck` 为 `true` 开启消息已读回执功能。 + 2. 发送方在群组中发送消息时设置 `EMMessage#needGroupAck` 为 `true` 要求接收方返回消息已读回执。 + 3. 接收方收到或阅读消息后通过 `EMChatManager#sendGroupMessageReadAck` 方法发送群组消息的已读回执。 ## 前提条件 @@ -42,171 +38,193 @@ - 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -- 群消息已读回执功能仅在环信 IM 专业版及以上版本支持该功能。若要使用该功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 +- 要使用群消息已读回执功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 ## 实现方法 -### 消息送达回执 +### 单聊消息送达回执 -1. 发送方开启全局送达回执。当接收方收到消息后,SDK 底层会自动进行消息送达回执。 +1. 开启消息送达功能,即 SDK 初始化时将 `EMOptions#requireDeliveryAck` 设置为 `true`。 ```dart -// sdk app key -String appKey = "appKey"; -// 开启消息送达回执 -bool requireDeliveryAck = true; -EMOptions options = EMOptions( - appKey: appKey, - requireDeliveryAck: requireDeliveryAck, -); -await EMClient.getInstance.init(options); +// 设置是否需要接收方送达确认,默认 `false` 即不需要。 + final options = EMOptions( + appKey: appKey, + requireDeliveryAck: true, + ); ``` -2. 发送方监听事件 `onMessagesDelivered` 回调,收到接收方的送达回执。 +2. 接收方收到消息后,SDK 自动向发送方触发送达回执。 + +3. 发送方监听 `EMChatEventHandler#onMessagesDelivered` 事件,收到接收方的送达回执。你可以在收到该通知时,显示消息的送达状态。 ```dart -// 添加监听器 -EMClient.getInstance.chatManager.addEventHandler( - "UNIQUE_HANDLER_ID", - EMChatEventHandler( - onMessagesDelivered: (list) => {}, - ), -); + EMClient.getInstance.chatManager.addEventHandler( + 'identifier', + EMChatEventHandler( + onMessagesDelivered: (messages) {}, + ), + ); ``` -### 消息和会话的已读回执 +### 单聊消息已读回执 -消息已读回执用于告知单聊或群聊中的用户接收方已阅读其发送的消息。为降低消息已读回执方法的调用次数,SDK 还支持在单聊中使用会话已读回执功能,用于获知接收方是否阅读了会话中的未读消息。 +单聊既支持单条消息已读回执,也支持[会话已读回执](conversation_receipt.html)。我们建议你结合使用这两种回执,见实现步骤的描述。 -#### 单聊 +单聊消息的已读回执有效期与消息在服务端的存储时间一致,即在服务器存储消息期间均可发送已读回执。消息在服务端的存储时间与你订阅的套餐包有关,详见[产品价格](/product/pricing.html#套餐包功能详情)。 -单聊既支持消息已读回执,也支持会话已读回执。我们建议你按照如下逻辑结合使用两种回执结合使用,减少发送消息已读回执数量。 +参考如下步骤在单聊中实现消息已读回执。 -- 聊天页面未打开时,若有未读消息,进入聊天页面,发送会话已读回执; -- 聊天页面打开时,若收到消息,发送消息已读回执。 +1. App 开启已读回执功能,即 SDK 初始化时将 `EMOptions#requireAck` 设置为 `true`。 -##### 会话已读回执 +```dart + final options = EMOptions( + appKey: appKey, + requireAck: true, + ); +``` -参考如下步骤在单聊中实现会话已读回执。 +2. 接收方发送消息已读回执。 -1. 开启全局的消息已读回执开关。如果全局设置不开启,消息和会话的相应设置也无法生效。 +- 聊天页面未打开时,若有未读消息,进入聊天页面,发送会话已读回执。这种方式可避免发送多个消息已读回执。 ```dart -EMOptions options = EMOptions( - appKey: appKey, - requireAck: true, -); -EMClient.getInstance.init(options); +try { + await EMClient.getInstance.chatManager + .sendConversationReadAck(conversationId); +} on EMError catch (e) { + debugPrint(e.toString()); +} ``` -2. 接收方执行会话已读回执操作。进入会话页面,查看会话中是否有未读消息。若有,发送会话已读回执,没有则不再发送。 +- 聊天页面打开时,若收到消息,发送单条消息已读回执。 ```dart -String convId = "convId"; try { - await EMClient.getInstance.chatManager.sendConversationReadAck(convId); + EMClient.getInstance.chatManager.sendMessageReadAck(message); } on EMError catch (e) { - + debugPrint('Failed to send message read ack: ${e.description}'); } ``` -3. 发送方监听 `onConversationRead` 回调,接收会话已读回执。 +3. 消息发送方监听消息已读回调。 + +消息发送方可以通过 `EMChatEventHandler#onMessagesRead` 事件监听指定消息是否已读,示例代码如下: ```dart EMClient.getInstance.chatManager.addEventHandler( - "UNIQUE_HANDLER_ID", + 'identifier', EMChatEventHandler( - onConversationRead: (from, to) => {}, + onMessagesRead: (messages) {}, ), ); ``` -> 同一用户 ID 登录多设备的情况下,用户在一台设备上发送会话已读回执,服务器会将会话的未读消息数置为 `0`,同时其他设备会收到 `onConversationRead` 回调。 +### 群聊消息已读回执 -##### 消息已读回执 +对于群聊,群成员发送消息时,可以设置该消息是否需要已读回执。若需要,每个群成员阅读消息后,应该调用`EMChatManager#sendGroupMessageReadAck` 方法发送已读回执,阅读该消息的群成员数量即为已读回执的数量。 -单聊消息的已读回执有效期与消息在服务端的存储时间一致,即在服务器存储消息期间均可发送已读回执。消息在服务端的存储时间与你订阅的套餐包有关,详见[产品价格](/product/pricing.html#套餐包功能详情)。 +群消息已读回执特性的使用限制如下表所示: -参考如下步骤在单聊中实现消息已读回执。 +| 使用限制| 默认 | 描述 | +| :--------- | :----- | :------- | +| 功能开通 | 关闭 | 若要使用该功能,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **功能配置** > **功能配置总览**> **基础功能**页签下,搜索找到 **消息已读回执(群聊)** 开通功能。具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 | +| 使用权限 | 所有群成员 | 默认情况下,所有群成员发送消息时可要求已读回执。如果仅需群主和群管理员发消息时要求已读回执,可联系商务修改。 | +| 已读回执有效期 | 3 天 | 群聊已读回执的有效期为 3 天,即群组中的消息发送时间超过 3 天,服务器不记录阅读该条消息的群组成员,也不会发送已读回执。 | +| 群规模 | 200 人 | 该特性最大支持 200 人的群组。如果超过 200 人/群,群成员发送的消息不会返回已读回执。你可以联系商务提升群成员人数上限。 | +| 查看返回已读回执数量 | 消息发送方 | 对消息返回的已读回执数量(或返回已读回执的人数),默认仅消息发送方可查看。如需所有群成员均可查看,可联系商务开通。 | + +你可以按以下步骤实现群消息已读回执特性: + +1. 开启已读回执功能,即 SDK 初始化时将 `EMOptions#requireAck` 设置为 `true`。 -1. 开启全局的消息已读回执开关。如果全局设置不开启,消息和会话的相应设置也无法生效。 +该功能开启后,接收方阅读消息后,SDK 底层会自动进行消息已读回执。 ```dart -EMOptions options = EMOptions( - appKey: "appKey", - requireAck: true, -); -EMClient.getInstance.init(options); + final options = EMOptions( + appKey: appKey, + requireAck: true, + ); ``` -2. 消息发送方监听 `onMessagesRead` 事件。 +2. 发送方发送消息时设置 `EMMessage#needGroupAck` 属性为 `true`。 + +与单聊消息的 app 层级设置已读回执功能不同,群聊消息是在发送消息时设置指定消息是否需要已读回执。 ```dart -EMClient.getInstance.chatManager.addEventHandler( - "UNIQUE_HANDLER_ID", - EMChatEventHandler( - onMessagesRead: (list) => {}, - ), +EMMessage message = EMMessage.createTxtSendMessage( + targetId: targetId, + content: 'content', ); +message.needGroupAck = true; ``` -3. 消息接收方发送已读回执 +3. 发送群组消息的已读回执。 ```dart -try { - EMClient.getInstance.chatManager.sendMessageReadAck(msg); -} on EMError catch (e) { - +void sendGroupReadAck(EMMessage message) async { + if (message.needGroupAck != true) return; + try { + await EMClient.getInstance.chatManager.sendGroupMessageReadAck(message.msgId, message.conversationId!); + } on EMError catch (e) { + debugPrint('Failed to send group read ack: ${e.description}'); + } } ``` -### 群聊 - -对于群聊,群成员发送消息时,可以设置该消息是否需要已读回执。若需要,每个群成员在阅读消息后,SDK 均会发送已读回执,即阅读该消息的群成员数量即为已读回执的数量。群聊已读回执的有效期为 3 天,即群组中的消息发送时间超过 3 天,服务器不记录阅读该条消息的群组成员,也不会发送已读回执。 - -群消息已读回执特性的使用限制如下表所示: - -| 使用限制| 默认 | 描述 | -| :--------- | :----- | :------- | -| 功能开通 | 关闭 | 若要使用该功能,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **功能配置** > **功能配置总览**> **基础功能**页签下,搜索找到 **消息已读回执(群聊)** 开通功能。具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 | -| 使用权限 | 所有群成员 | 默认情况下,所有群成员发送消息时可要求已读回执。如果仅需群主和群管理员发消息时要求已读回执,可联系商务修改。 | -| 已读回执有效期 | 3 天 | 群聊已读回执的有效期为 3 天,即群组中的消息发送时间超过 3 天,服务器不记录阅读该条消息的群组成员,也不会发送已读回执。 | -| 群规模 | 200 人 | 该特性最大支持 200 人的群组。如果超过 200 人/群,群成员发送的消息不会返回已读回执。你可以联系商务提升群成员人数上限。 | -| 查看返回已读回执数量 | 消息发送方 | 对消息返回的已读回执数量(或返回已读回执的人数),默认仅消息发送方可查看。如需所有群成员均可查看,可联系商务开通。 | +4. 消息发送方监听群组消息已读回调。 -你可以按以下步骤实现群消息已读回执特性: +群消息已读回调在 `EMMessageListener#onGroupMessageRead` 中实现。 -1. 消息发送方需要知道群组消息是否已读,需要监听 `onGroupMessageRead` 事件。 +发送方接收到群组消息已读回执后,调用 `EMMessage#groupAckCount()` 方法会得到最新的已读数量 ```dart EMClient.getInstance.chatManager.addEventHandler( - "UNIQUE_HANDLER_ID", + 'identifier', EMChatEventHandler( - onGroupMessageRead: (list) => {}, + onGroupMessageRead: (groupMessageAcks) { + }, ), ); ``` -2. 群成员发送消息时若需已读回执,需设置 `needGroupAck` 为 `true`。 +5. 消息发送方获取群组消息的已读回执详情。 + +你可以调用 `EMChatManager#fetchGroupAcks` 方法从服务器获取单条消息的已读回执的详情。 ```dart -// 设置消息类型为群消息 -msg.chatType = ChatType.GroupChat; - // 设置本条消息需要群消息回执 -msg.needGroupAck = true; try { - await EMClient.getInstance.chatManager.sendMessage(msg); + await EMClient.getInstance.chatManager.fetchGroupAcks('msgId', 'groupId'); } on EMError catch (e) { - + debugPrint('Failed to fetch group acks: ${e.description}'); } ``` -3. 群组里面的接收方收到消息,调用 `sendGroupMessageReadAck` 告知消息发送方消息已读。成功发送后,消息发送方会收到 `onGroupMessageRead` 回调。 +### 查看消息送达和已读状态 + +对于单聊消息,本地通过 `EMMessage#hasDeliverAck` 字段存储消息送达状态。 + +对于单聊消息,本地通过以下字段存储消息已读状态: + +| 字段 | 描述 | +| :--------- | :----- | +| `EMMessage#hasRead` | 用户是否已读了该消息。如果是自己发送的消息,该字段的值固定为 `true`。| +| `EMMessage#hasReadAck` | 是否(消息接收方)已发送或(消息发送方)已收到消息已读回执。如果是自己发送的消息,记录的是对方是否已读。如果是对方的消息,则记录的是自己是否发送过已读回执。 | + +对于群聊消息,本地数据库通过以下字段存储消息已读状态: + +| 字段 | 描述 | +| :--------- | :----- | +| `EMMessage#hasReadAck` | 用户是否已读了该消息。如果是自己发送的消息,该字段的值固定为 `true`。| + +通过方法 `EMMessage#groupAckCount` 查询已阅读消息的群成员数量。 + +### 已读回执与未读消息数 + +- 会话已读回执发送后,开发者需要调用 `EMConversation#markAllMessagesAsRead` 方法将该会话的所有消息置为已读,即会话的未读消息数清零。 + +- 消息已读回执发送后,开发者需要调用 `EMConversation#markMessageAsRead` 方法将该条消息置为已读,否则则消息未读数不会有变化。 + + + -```dart -try { - EMClient.getInstance.chatManager.sendGroupMessageReadAck(msgId, groupId); -} on EMError catch (e) { - // 发送群消息已读失败,错误代码:e.code,错误描述:e.description -} -``` diff --git a/docs/document/flutter/message_send_receive.md b/docs/document/flutter/message_send_receive.md index 6f822abb..36388018 100644 --- a/docs/document/flutter/message_send_receive.md +++ b/docs/document/flutter/message_send_receive.md @@ -1,50 +1,21 @@ # 发送和接收消息 - +环信即时通讯 IM Flutter SDK 通过 `EMChatManager` 和 `EMMessage` 类实现文本、图片、音频、视频和文件等类型的消息的发送和接收。 -登录即时通讯服务后,用户可以在单聊、群聊、聊天室中发送如下类型的消息: +- 对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。 -- 文字消息,包含超链接和表情消息。 -- 附件消息,包含图片、语音、视频及文件消息。 -- 位置消息。 -- 透传消息。 -- 自定义消息。 -- 合并消息。 +- 对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 -对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 - -针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 - -本文介绍如何使用即时通讯 IM SDK 实现发送和接收这些类型的消息。 - -## 技术原理 - -环信即时通讯 IM Flutter SDK 通过 `EMChatManager` 和 `EMMessage` 类实现消息的发送和接收。 - -其中,发送和接收消息的逻辑如下: - -1. 发送方调用相应 Create 方法创建文本、文件、附件等类型的消息; -2. 发送方再调用 `SendMessage` 发送消息; -3. 接收方通过 `EMChatEventHandler` 中的方法监听消息回调事件。在收到 `onMessagesReceived` 后,即表示成功接收到消息。 - -消息收发流程如下: - -1. 用户 A 发送一条消息到环信的消息服务器; -2. 单聊消息时,服务器投递消息给用户 B;对于群聊时消息,服务器投递给群内其他每一个成员; -3. 用户收到消息。 - -![img](/images/android/sendandreceivemsg.png) +单聊、群组聊天和聊天室的消息发送控制,详见[消息发送控制](/product/product_message_overview.html#消息发送控制)文档。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html)。 +- 完成 SDK 初始化,详见 [初始化文档](initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -## 实现方法 - -### 发送文本消息 +## 发送和接收文本消息 1. 首先,利用 `EMMessage` 类构造一条消息。 @@ -67,21 +38,6 @@ final msg = EMMessage.createTxtSendMessage( EMClient.getInstance.chatManager.sendMessage(msg); ``` -对于聊天室,可以设置消息优先级,示例代码树下: - -```dart -final msg = EMMessage.createTxtSendMessage( - targetId: conversationId, - content: 'hello', - chatType: ChatType.ChatRoom, -); - -// 聊天室消息的优先级。如果不设置,默认值为 `Normal`,即“普通”优先级。 -msg.chatroomMessagePriority = ChatRoomMessagePriority.High; -EMClient.getInstance.chatManager.sendMessage(msg); - -``` - 2. 通过 `EMChatManager` 将消息发出。发送时可以设置 `ChatMessageEvent` ,获取消息发送状态。 ```dart @@ -120,11 +76,7 @@ EMClient.getInstance.chatManager.sendMessage(message).then((value) { }); ``` -若初始化时打开了 `ChatOptions#useReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 - -### 接收消息 - -你可以添加 `EMChatEventHandler` 监听器接收消息。`EMChatEventHandler` 可以多次添加。请记得在不需要的时候移除该监听器,如在 `dispose` 时。 +3. 你可以添加 `EMChatEventHandler` 监听器接收消息。`EMChatEventHandler` 可以多次添加。请记得在不需要的时候移除该监听器,如在 `dispose` 时。 在新消息到来时,你会收到 `onMessagesReceived` 事件,消息接收时可能是一条,也可能是多条。你可以在该回调里遍历消息队列,解析并显示收到的消息。 @@ -161,7 +113,7 @@ class _ChatMessagesPageState extends State { } ``` -### 发送和接收附件类型的消息 +## 发送和接收附件类型的消息 附件消息的发送和接收过程如下: @@ -169,30 +121,7 @@ class _ChatMessagesPageState extends State { 2. 接收附件消息。SDK 自动下载语音消息,默认自动下载图片和视频的缩略图。若下载原图、视频和文件,需调用 `downloadAttachment` 方法。 3. 获取附件的服务器地址和本地路径。 -此外,发送附件类型消息时,可以在 `ChatMessageEvent#onProgress` 回调中获取附件上传的进度(百分比),以 int 表示,范围为 [0, 100],示例代码如下: - -```dart - -final handler = ChatMessageEvent( - // 消息发送成功回调,msgId 为消息原始 ID,msg 为发送完成后的消息。 - onSuccess: (msgId, msg) {}, - // 附件上传进度回调,msgId 为消息原始 ID,progress 为消息发送进度(百分比), 范围[0, 100]。 - onProgress: (msgId, progress) {}, - // 消息发送失败回调,msgId 为消息原始 ID,msg 为发送完成后的消息,error 为错误原因。 - onError: (msgId, msg, error) {}, -); - -/// 添加监听 -EMClient.getInstance.chatManager.addMessageEvent( - 'UNIQUE_HANDLER_ID', - handler, -); - -/// 移除监听 -EMClient.getInstance.chatManager.removeMessageEvent('UNIQUE_HANDLER_ID'); -``` - -#### 发送和接收语音消息 +### 发送和接收语音消息 发送和接收语音消息的过程如下: @@ -210,7 +139,7 @@ EMClient.getInstance.chatManager.sendMessage(msg); ``` 3. 接收方收到语音消息时,自动下载语音文件。 -4. 接收方收到 `EMChatEventHandler#onMessagesReceived` 回调,调用 `remotePath` 或 `localPath` 方法获取语音文件的服务器地址或本地路径,从而获取语音文件。 +4. 接收方收到 [EMChatEventHandler#onMessagesReceived 回调](#发送和接收文本消息),调用 `remotePath` 或 `localPath` 方法获取语音文件的服务器地址或本地路径,从而获取语音文件。 ```dart if(msg.body.type == MessageType.VOICE) { @@ -222,12 +151,11 @@ if(msg.body.type == MessageType.VOICE) { ``` -#### 发送和接收图片消息 +### 发送和接收图片消息 发送和接收图片消息的流程如下: -1. 发送方调用 `EMMessage#createImageSendMessage` 方法传入接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID)和图片文件的 filePath,创建图片消息,然后调用 `sendMessage` 方法发送该消息。SDK 会将图片上传至环信服务器,服务器自动生成图片缩略图。 - +1. 发送方调用 `EMMessage#createImageSendMessage` 方法传入接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID)和图片文件的 `filePath`,创建图片消息,然后调用 `sendMessage` 方法发送该消息。SDK 会将图片上传至环信服务器,服务器自动生成图片缩略图。 ```dart final imgMsg = EMMessage.createImageSendMessage( @@ -239,8 +167,11 @@ EMClient.getInstance.chatManager.sendMessage(imgMsg); ``` 2. 接收方收到图片消息,自动下载图片缩略图。 + +- 默认情况下,SDK 自动下载缩略图,即 `EMOptions#isAutoDownloadThumbnail` 设置为 `true`。 +- 若设置为手动下载缩略图,即 `EMOptions#isAutoDownloadThumbnail` 设置为 `false`,需调用 `EMChatManage#downloadThumbnail` 下载。 -3. 接收方收到 `EMChatEventHandler#onMessagesReceived` 回调,调用 `downloadAttachment` 下载大图。 +3. 接收方收到 [EMChatEventHandler#onMessagesReceived 回调](#发送和接收文本消息),调用 `downloadAttachment` 下载大图。 ```dart EMClient.getInstance.chatManager.addMessageEvent( @@ -276,7 +207,7 @@ body.remotePath; body.thumbnailRemotePath; ``` -#### 发送和接收视频消息 +### 发送和接收视频消息 发送和接收视频消息的流程如下: @@ -284,8 +215,7 @@ body.thumbnailRemotePath; 你可以设置发送消息结果回调,用于接收消息发送进度或者发送结果,如发送成功或失败。为此,需实现 `EMChatManager#addMessageEvent` 接口。 -2. 发送方调用 `EMMessage#createVideoSendMessage` 方法传入接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID),图片文件的 filePath、创建视频消息,然后调用 `sendMessage` 方法发送消息。SDK 会将视频文件上传至消息服务器。若需要视频缩略图,你需自行获取视频首帧的路径,将该路径传入 `createVideoSendMessage` 方法。 - +2. 发送方调用 `EMMessage#createVideoSendMessage` 方法传入接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID),图片文件的 `filePath`、创建视频消息,然后调用 `sendMessage` 方法发送消息。SDK 会将视频文件上传至消息服务器。若需要视频缩略图,你需自行获取视频首帧的路径,将该路径传入 `createVideoSendMessage` 方法。 ```dart @@ -300,9 +230,9 @@ EMClient.getInstance.chatManager.sendMessage(videoMsg); ``` -3. 接收方收到视频消息时,自动下载视频缩略图。 +3. 接收方收到视频消息时,自动下载视频缩略图。你可以设置自动或手动下载视频缩略图,该设置与图片缩略图相同,详见[设置图片缩略图自动下载](#发送和接收图片消息)。 -4. 接收方收到 `EMChatEventHandler#onMessagesReceived` 回调,调用 `downloadAttachment` 下载视频文件。 +4. 接收方收到 [EMChatEventHandler#onMessagesReceived 回调](#发送和接收文本消息),调用 `downloadAttachment` 下载视频文件。 ```dart @@ -340,7 +270,7 @@ body.remotePath; body.thumbnailRemotePath; ``` -#### 发送和接收文件消息 +### 发送和接收文件消息 发送和接收文件消息的流程如下: @@ -355,7 +285,7 @@ final fileMsg = EMMessage.createFileSendMessage( EMClient.getInstance.chatManager.sendMessage(fileMsg); ``` -2. 接收方收到 `EMChatEventHandler#onMessagesReceived` 回调,调用 `downloadAttachment` 下载文件。 +2. 接收方收到 [EMChatEventHandler#onMessagesReceived 回调](#发送和接收文本消息),调用 `downloadAttachment` 下载文件。 ```dart EMClient.getInstance.chatManager.addMessageEvent( @@ -387,9 +317,11 @@ body.localPath; body.remotePath; ``` -### 发送和接收位置消息 +## 发送和接收位置消息 -当你要发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 +1. 创建和发送位置消息。 + +发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。 ```dart final localMsg = EMMessage.createLocationSendMessage( @@ -402,14 +334,23 @@ final localMsg = EMMessage.createLocationSendMessage( EMClient.getInstance.chatManager.sendMessage(localMsg); ``` -### 发送和接收透传消息 +2. 接收位置消息与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + + 接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 -透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。(透传消息不会存入本地数据库中,所以在 UI 上不会显示)。具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 “em_” 和 “easemob::” 开头的 action 为内部保留字段,注意不要使用。 +## 发送和接收透传消息 + +透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。 + +具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 `em_` 和 `easemob::` 开头的 action 为内部保留字段,注意不要使用。 :::tip -透传消息发送后,不支持撤回。 +- 透传消息发送后,不支持撤回。 +- 透传消息不会存入本地数据库中,所以在 UI 上不会显示。 ::: +1. 创建和发送透传消息。 + ```dart final cmdMsg = EMMessage.createCmdSendMessage( targetId: targetId, @@ -419,7 +360,8 @@ final cmdMsg = EMMessage.createCmdSendMessage( EMClient.getInstance.chatManager.sendMessage(cmdMsg); ``` -请注意透传消息的接收方,也是由单独的回调进行通知,方便用户进行不同的处理。 + +2. 接收方通过 `onCmdMessagesReceived` 回调接收透传消息,方便用户进行不同的处理。 ```dart final handler = EMChatEventHandler( @@ -440,91 +382,12 @@ EMClient.getInstance.chatManager.removeEventHandler( ); ``` -#### 通过透传消息实现输入指示器 - - -输入指示器显示其他用户何时输入消息。通过该功能,用户之间可进行有效沟通,增加了用户对聊天应用中交互的期待感。 - -你可以通过透传消息实现输入指示器。下图为输入指示器的工作原理。 - -![img](/images/common/typing_indicator.png) - - -监听用户 A 的输入状态。一旦有文本输入,通过透传消息将输入状态发送给用户 B,用户 B 收到该消息,了解到用户 A 正在输入文本。 - -- 用户 A 向用户 B 发送消息,通知其开始输入文本。 -- 收到消息后,如果用户 B 与用户 A 的聊天页面处于打开状态,则显示用户 A 的输入指示器。 -- 如果用户 B 在几秒后未收到用户 A 的输入,则自动取消输入指示器。 - -:::tip -用户 A 可根据需要设置透传消息发送间隔。 -::: - -以下示例代码展示如何发送输入状态的透传消息。 - -```dart -//发送表示正在输入的透传消息 -final String msgTypingBegin = "TypingBegin"; - -void textChange() { - int currentTimestamp = getCurrentTimestamp(); - if (currentTimestamp - _previousChangedTimeStamp > 5) { - _sendBeginTyping(); - _previousChangedTimeStamp = currentTimestamp; - } -} - -void _sendBeginTyping() async { - var msg = EMMessage.createCmdSendMessage( - targetId: conversationId, - action: msgTypingBegin, - // 将该透传消息只发送给在线用户 - deliverOnlineOnly: true, - ); - msg.chatType = ChatType.Chat; - EMClient.getInstance.chatManager.sendMessage(msg); -} - -``` - -以下示例代码展示如何接受和解析输入状态的透传消息。 - -```dart -final int typingTime = 10; -Timer? _timer; - -void onCmdMessagesReceived(List list) { - for (var msg in list) { - if (msg.conversationId != currentConversationId) { - continue; - } - EMCmdMessageBody body = msg.body as EMCmdMessageBody; - if (body.action == msgTypingBegin) { - // 这里需更新 UI,显示“对方正在输入” - beginTimer(); - } - } -} - -void beginTimer() { - _timer = Timer.periodic( - Duration(seconds: typingTime), - (timer) { - // 这里需更新 UI,显示“对方正在输入” - cancelTimer(); - }, - ); -} - -void cancelTimer() { - _timer?.cancel(); -} -``` - -### 发送自定义类型消息 +## 发送自定义类型消息 除了几种消息之外,你可以自己定义消息类型,方便业务处理,即首先设置一个消息类型名称,然后可添加多种自定义消息。 +接收自定义消息与其他类型消息一致,详见[接收文本消息](#发送和接收文本消息)。 + ```dart final customMsg = EMMessage.createCustomSendMessage( targetId: targetId, @@ -537,7 +400,7 @@ final customMsg = EMMessage.createCustomSendMessage( EMClient.getInstance.chatManager.sendMessage(customMsg); ``` -### 发送和接收合并消息 +## 发送和接收合并消息 为了方便消息互动,即时通讯 IM 自 4.1.0 版本开始支持将多个消息合并在一起进行转发。你可以采取以下步骤进行消息的合并转发: @@ -577,11 +440,12 @@ final combineMsg = EMMessage.createCombineSendMessage( EMClient.getInstance.chatManager.sendMessage(combineMsg); ``` -接收合并消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 -对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 +#### 接收和解析合并消息 -#### 解析合并消息 +接收合并消息与接收普通消息的操作相同,详见[接收文本消息](#发送和接收文本消息)。 + +对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 合并消息实际上是一种附件消息。收到合并消息后,你可以调用 `EMChatManager#fetchCombineMessageDetail` 方法下载合并消息附件并解析出原始消息列表。 @@ -601,7 +465,7 @@ try { ``` -### 发送和接收定向消息 +## 发送和接收定向消息 发送定向消息是指向群组或聊天室的单个或多个指定的成员发送消息,其他成员不会收到该消息。 @@ -634,9 +498,9 @@ msg.receiverList = ['userId1', 'userId2']; EMClient.getInstance.chatManager.sendMessage(msg); ``` -接收群定向消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +接收群定向消息与接收普通消息的操作相同,详见[接收文本消息](#发送和接收文本消息)。 -### 使用消息扩展字段 +## 使用消息扩展字段 当 SDK 提供的消息类型不满足需求时,你可以通过消息扩展字段传递自定义的内容,从而生成自己需要的消息类型。 @@ -654,6 +518,57 @@ try { } on EMError catch (e) {} ``` -## 注意事项 +## 更多 + +### 设置聊天室消息优先级 + +针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 + +对于聊天室消息,可设置消息优先级,包括高、普通和低优先级。示例代码如下: + +```dart +final msg = EMMessage.createTxtSendMessage( + targetId: conversationId, + content: 'hello', + chatType: ChatType.ChatRoom, +); + +// 聊天室消息的优先级。如果不设置,默认值为 `Normal`,即“普通”优先级。 +msg.chatroomMessagePriority = ChatRoomMessagePriority.High; +EMClient.getInstance.chatManager.sendMessage(msg); + +``` + +### 获取发送附件消息的进度 + +发送附件类型消息时,可以在 `ChatMessageEvent#onProgress` 回调中获取附件上传的进度(百分比),以 int 表示,范围为 [0, 100],示例代码如下: + +```dart +final handler = ChatMessageEvent( + // 消息发送成功回调,msgId 为消息原始 ID,msg 为发送完成后的消息。 + onSuccess: (msgId, msg) {}, + // 附件上传进度回调,msgId 为消息原始 ID,progress 为消息发送进度(百分比), 范围[0, 100]。 + onProgress: (msgId, progress) {}, + // 消息发送失败回调,msgId 为消息原始 ID,msg 为发送完成后的消息,error 为错误原因。 + onError: (msgId, msg, error) {}, +); + +/// 添加监听 +EMClient.getInstance.chatManager.addMessageEvent( + 'UNIQUE_HANDLER_ID', + handler, +); + +/// 移除监听 +EMClient.getInstance.chatManager.removeMessageEvent('UNIQUE_HANDLER_ID'); +``` + +### 发送消息前的内容审核 + +- 内容审核关注消息 body + +[内容审核服务会关注消息 body 中指定字段的内容,不同类型的消息审核不同的字段](/product/moderation/moderation_mechanism.html),若创建消息时在这些字段中传入了很多业务信息,可能会影响审核效果。因此,创建消息时需要注意内容审核的字段不涉及业务信息,建议业务信息放在扩展字段中。 + +- 设置发送方收到内容审核替换后的内容 -[内容审核服务会关注消息 body 中指定字段的内容,不同类型的消息审核不同的字段](/product/moderation/moderation_mechanism.html),若创建消息时在这些字段中传入了很多业务信息,可能会影响审核效果。因此,创建消息时需要注意内容审核的字段不涉及业务信息,建议业务信息放在扩展字段中。 \ No newline at end of file +若初始化时打开了 `ChatOptions#useReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 \ No newline at end of file diff --git a/docs/document/flutter/moderation.md b/docs/document/flutter/moderation.md index 9e052761..b80e39c2 100644 --- a/docs/document/flutter/moderation.md +++ b/docs/document/flutter/moderation.md @@ -27,7 +27,7 @@ 举报违规消息的示例代码如下: -```typescript +```dart // messageId:要举报的消息 ID。 // tag:非法消息的标签。你需要自定义标签,例如`涉政`或`广告`。该字段对应环信即时通讯云控制台的消息举报记录页面的`词条标记`字段。 // reason:举报原因。你需要自行填写举报原因,最长不能超过 512 字节。该字段对应环信即时通讯云控制台的消息举报记录页面的`举报原因`字段。 diff --git a/docs/document/flutter/multi_device.md b/docs/document/flutter/multi_device.md index efdc34be..490739e9 100644 --- a/docs/document/flutter/multi_device.md +++ b/docs/document/flutter/multi_device.md @@ -96,7 +96,7 @@ try { 初始化 SDK 时,你可以调用 `EMOptions#deviceName` 方法设置登录设备的名称。设置后,若因达到了登录设备数量限制而导致在已登录的设备上强制退出时,被踢设备收到的 `EMConnectionListener#onUserDidLoginFromOtherDevice` 回调会包含导致该设备被踢下线的自定义设备名称。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -128,7 +128,7 @@ EMClient.getInstance.addConnectionEventHandler( 2. 初始化 SDK 时,调用 `EMOptions#osType` 方法自定义设置登录设备的平台。确保该方法中的 `osType` 参数的值与环信控制台的**添加自定义平台**对话框中设置的**设备平台**的值相同。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -143,7 +143,7 @@ EMClient.getInstance.init(options); 初始化 SDK 时,你可以调用 `EMOptions.loginExtension` 方法设置登录设备的自定义扩展信息。设置后,若因达到了登录设备数量限制而导致在已登录的设备上强制退出时(例如 Android 平台提示 `206` 错误,`USER_LOGIN_ANOTHER_DEVICE`),被踢设备收到的 `EMConnectionEventHandler#onUserDidLoginFromOtherDevice` 回调会包含导致该设备被踢下线的新登录设备的自定义扩展信息。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -176,7 +176,7 @@ EMClient.getInstance.removeConnectionEventHandler("identifier"); 你可以调用 `kickDevice` 方法通过传入用户 ID 和登录密码或用户 token 将指定账号从单个登录设备踢下线。被踢设备会收到 `onUserKickedByOtherDevice` 回调。调用该方法前,你需要首先通过 `EMClient#fetchLoggedInDevices` 方法获取设备 ID。 -:::notice +:::tip 不登录也可以使用该接口。 ::: @@ -206,7 +206,7 @@ try { 你可以调用 `kickAllDevices` 方法通过传入用户 ID 和登录密码或用户 token 将指定账号从所有登录设备踢下线。 -:::notice +:::tip 不登录也可以使用该接口。 ::: diff --git a/docs/document/flutter/overview.md b/docs/document/flutter/overview.md index 534988e8..fda9ca61 100644 --- a/docs/document/flutter/overview.md +++ b/docs/document/flutter/overview.md @@ -53,7 +53,7 @@ try { } on EMError catch (e) {} ``` -:::notice +:::tip 该注册模式为在客户端注册,旨在方便测试,并不推荐在正式环境中使用。 ::: @@ -82,7 +82,7 @@ try { **用户 ID + token** 是更加安全的登录方式。token 可以通过调用 REST API 获取。 详见 [环信用户 token 的获取](/document/server-side/easemob_user_token.html)。 -:::notice +:::tip 使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 ::: diff --git a/docs/document/flutter/reaction.md b/docs/document/flutter/reaction.md index 2839eda7..0c39bfce 100644 --- a/docs/document/flutter/reaction.md +++ b/docs/document/flutter/reaction.md @@ -4,7 +4,7 @@ 环信即时通讯 IM 提供消息表情回复(下文统称 “Reaction”)功能。用户可以在单聊和群聊中对消息添加、删除表情。表情可以直观地表达情绪,利用 Reaction 可以提升用户的使用体验。同时在群组中,利用 Reaction 可以发起投票,根据不同表情的追加数量来确认投票。 -:::notice +:::tip 目前 Reaction 仅适用于单聊和群组。聊天室暂不支持 Reaction 功能。 ::: diff --git a/docs/document/flutter/room_members.md b/docs/document/flutter/room_members.md index bd2b68ab..d41dddac 100644 --- a/docs/document/flutter/room_members.md +++ b/docs/document/flutter/room_members.md @@ -211,7 +211,7 @@ try { 仅聊天室所有者和管理员可以调用 `EMChatRoomManager#muteChatRoomMembers` 方法将指定成员添加至聊天室禁言列表。被禁言后,该成员和其他未操作的聊天室管理员或聊天室所有者收到 `EMChatRoomEventHandler#onMuteListAddedFromChatRoom` 事件。 -:::notice +:::tip 聊天室所有者可禁言聊天室所有成员,聊天室管理员可禁言聊天室普通成员。 ::: @@ -232,7 +232,7 @@ try { 仅聊天室所有者和管理员可以调用 `EMChatRoomManager#unMuteChatRoomMembers` 方法将成员移出聊天室禁言列表。被解除禁言后,该成员和其他未操作的聊天室管理员或聊天室所有者收到 `EMChatRoomEventHandler#onMuteListRemovedFromChatRoom` 事件。 -:::notice +:::tip 聊天室所有者可对聊天室所有成员解除禁言,聊天室管理员可对聊天室普通成员解除禁言。 ::: diff --git a/docs/document/flutter/thread.md b/docs/document/flutter/thread.md index ac43f163..9a5b0b8b 100644 --- a/docs/document/flutter/thread.md +++ b/docs/document/flutter/thread.md @@ -63,7 +63,7 @@ try { 单设备登录时,子区所属群组的所有成员均会收到 `EMChatThreadEventHandler#onChatThreadDestroy` 事件;多设备登录时,其他设备会同时收到 `EMMultiDeviceEventHandler#onChatThreadEvent` 事件,回调事件为 `EMMultiDevicesEvent#CHAT_THREAD_DESTROY`。 -:::notice +:::tip 解散子区或解散子区所在的群组后,将删除本地数据库及内存中关于该子区的全部数据,需谨慎操作。 ::: diff --git a/docs/document/flutter/thread_message.md b/docs/document/flutter/thread_message.md index d189d141..a78aa794 100644 --- a/docs/document/flutter/thread_message.md +++ b/docs/document/flutter/thread_message.md @@ -6,22 +6,11 @@ ## 技术原理 -即时通讯 IM Flutter SDK 提供 `EMChatThreadManager`、`EMMessage` 和 `EMChatThread` 类,用于管理子区消息,支持你通过调用 API 在项目中实现如下功能: +即时通讯 IM Flutter SDK 提供 `EMChatThreadManager`、`EMMessage` 和 `EMChatThread` 类,用于管理子区消息,支持你通过调用 API 在项目中实现发送、接收、撤回和获取子区消息。 -- 发送子区消息 -- 接收子区消息 -- 撤回子区消息 -- 获取子区消息 +消息收发流程如下: -下图展示在客户端发送和接收消息的工作流程: - -![img](/images/android/sendandreceivemsg.png) - -如上图所示,消息收发流程如下: - -1. 客户端从应用服务器获取 token。 -2. 客户端 A 和 B 登录即时通讯。 -3. 客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 +客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 子区创建和查看如下图: @@ -41,7 +30,7 @@ ### 发送子区消息 -发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html#发送消息)。唯一不同的是,发送子区消息需要指定标记 `isChatThreadMessage` 为 `true`。 +发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html)。唯一不同的是,发送子区消息需要指定标记 `isChatThreadMessage` 为 `true`。 示例代码如下: @@ -61,7 +50,7 @@ EMClient.getInstance.chatManager.sendMessage(msg); ### 接收子区消息 -接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#接收消息),此处只介绍子区消息和其他消息的区别。 +接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#发送和接收文本消息),此处只介绍子区消息和其他消息的区别。 子区有新增消息时,子区所属群组的所有成员收到 `EMChatThreadEventHandler#onChatThreadUpdated` 事件,子区成员收到 `EMChatEventHandler#onMessagesReceived` 事件。 diff --git a/docs/document/flutter/userprofile.md b/docs/document/flutter/userprofile.md index daa5f959..844d5f49 100644 --- a/docs/document/flutter/userprofile.md +++ b/docs/document/flutter/userprofile.md @@ -8,7 +8,7 @@ 本文介绍如何通过管理用户属性设置、更新、存储并获取实时消息用户的相关信息。 -:::notice +:::tip 为保证用户信息安全,SDK 仅支持用户设置或更新自己的用户属性。 ::: diff --git a/docs/document/harmonyos/connection.md b/docs/document/harmonyos/connection.md new file mode 100644 index 00000000..38761b07 --- /dev/null +++ b/docs/document/harmonyos/connection.md @@ -0,0 +1,55 @@ +# 连接 + +应用客户端成功连接到环信服务器后,才能使用环信即时通讯 SDK 的收发消息等功能。 + +你调用 `loginWithToken` 或 `login` 方法登录后,客户端 SDK 会自动连接环信服务器。关于登录详情,请参见[登录文档](login.html)。 + +## 监听连接状态 + +你可以通过注册连接监听确认连接状态。 + +```typescript +let connectionListener: ConnectionListener = { + onConnected: (): void => { + // 长连接建立 + }, + onDisconnected: (errorCode: number): void => { + // 长连接断开 + }, + onLogout: (errorCode: number, info: LoginExtInfo): void => { + // 触发退出,需要主动调用 ChatClient#logout 方法 + }, + onTokenExpired: (): void => { + // 使用 token 登录时,token 过期触发。 + }, + onTokenWillExpire: (): void => { + // 使用 token 登录时,token 将要过期时触发。 + // 注意:如果本次登录服务器没有离线消息,不会触发该回调。 + }, + onOfflineMessageSyncStart: () => { + // 连接成功,开始从服务器拉取离线消息时触发。 + }, + onOfflineMessageSyncFinish: () => { + // 离线用户上线后从服务器拉取离线消息结束时触发。 + // 注意:如果再拉取离线过程中因网络或其他原因导致连接断开,不会触发该回调。 + } +} +// 注册连接状态监听 +ChatClient.getInstance().addConnectionListener(connectionListener); +// 移除连接状态监听 +ChatClient.getInstance().removeConnectionListener(connectionListener); +``` + +## 自动重连 + +登录后,如果由于网络信号弱、切换网络等引起的连接中断,SDK 会自动尝试重连。重连成功或者失败时分别会收到 `onConnected` 和 `onDisconnected` 通知。 + +不过,SDK 在以下情况下会停止自动重连。你需要调用 `login` 方法登录。 + +- 用户调用了 SDK 的登出方法 `logout` 主动退出登录。 +- 登录时鉴权错误,例如, token 无效(错误码 104)或已过期(错误码 108)。 +- 用户在其他的设备上更改了密码,导致此设备上自动登录失败,提示错误码 216。 +- 用户的账号被从服务器端删除,提示错误码 207。 +- 用户在另一设备登录,将当前设备上登录的用户踢出,提示错误码 206。 +- 用户登录设备数量超过限制,提示错误码 214。 +- 应用程序的日活跃用户数量(DAU)或月活跃用户数量(MAU)达到上限,提示错误码 8。 \ No newline at end of file diff --git a/docs/document/harmonyos/conversation_delete.md b/docs/document/harmonyos/conversation_delete.md index 76dae318..6366e6f1 100644 --- a/docs/document/harmonyos/conversation_delete.md +++ b/docs/document/harmonyos/conversation_delete.md @@ -28,7 +28,7 @@ 示例代码如下: -```TypeScript +```typescript //获取指定的会话 ID。 let conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); @@ -45,12 +45,12 @@ ChatClient.getInstance().chatManager()?.deleteConversationFromServer(conversatio 你可以删除本地会话和历史消息,示例代码如下: -```TypeScript +```typescript // 删除指定用户的会话,如果需要保留历史消息,`isDeleteServerMessages` 设置为 `false`。 ChatClient.getInstance().chatManager()?.deleteConversation(conversationId, isDeleteServerMessages); ``` -```TypeScript +```typescript // 删除指定会话中指定的一条历史消息。 let conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); if (conversation) { diff --git a/docs/document/harmonyos/conversation_list.md b/docs/document/harmonyos/conversation_list.md index 48c98ef8..93ec0258 100644 --- a/docs/document/harmonyos/conversation_list.md +++ b/docs/document/harmonyos/conversation_list.md @@ -36,7 +36,7 @@ 示例代码如下: -```TypeScript +```typescript // limit: 每页返回的会话数。取值范围为 [1,50]。 // cursor: 开始获取数据的游标位置。若获取数据时传空字符串(""),SDK 从最新活跃的会话开始获取。 let limit = 50; @@ -54,7 +54,7 @@ ChatClient.getInstance().chatManager()?.fetchConversationsFromServer(limit, curs 示例代码如下: -```TypeScript +```typescript let conversations = ChatClient.getInstance().chatManager()?.getAllConversationsBySort(); ``` diff --git a/docs/document/harmonyos/conversation_mark.md b/docs/document/harmonyos/conversation_mark.md index fd9d56cd..bf23b571 100644 --- a/docs/document/harmonyos/conversation_mark.md +++ b/docs/document/harmonyos/conversation_mark.md @@ -8,7 +8,7 @@ 你需要自行维护会话标记与具体业务含义(比如 `MARK_0` 为重要会话)之间的映射关系。例如: -```TypeScript +```typescript let mapping = new HashMap(); mapping.set(MarkType.MARK_0,"important"); mapping.set(MarkType.MARK_1,"normal"); @@ -48,7 +48,7 @@ mapping.set(MarkType.MARK_4,"girls"); 对会话添加标记,例如会话标星,并不影响会话的其他逻辑,例如会话的未读消息数。 ::: -```TypeScript +```typescript let conversationId = "huanhuan"; let ids = new Array(); ids.push(conversationId); @@ -65,7 +65,7 @@ ChatClient.getInstance().chatManager()?.addConversationMark(ids, MarkType.MARK_0 调用该方法会同时移除本地和服务器端会话的标记。 -```TypeScript +```typescript let conversationId = "huanhuan"; let ids = new Array(); ids.push(conversationId); @@ -80,7 +80,7 @@ ChatClient.getInstance().chatManager()?.removeConversationMark(ids, MarkType.MAR 你可以调用 `fetchConversationsFromServerWithFilter` 方法根据会话标记从服务器分页获取会话列表。SDK 会按会话标记的时间的倒序返回会话列表,每个会话对象中包含会话 ID、会话类型、是否为置顶状态、置顶时间(对于未置顶的会话,值为 `0`)、会话标记以及最新一条消息。从服务端拉取会话列表后会更新本地会话列表。 -```TypeScript +```typescript // filter:会话查询选项,包括会话标记和每页获取的会话条数(最多可获取 10 条)。下面的代码以查询服务端所有标记了 `MarkType.MARK_0` 的会话为例。 let filter: ConversationFilter = {markType: MarkType.MARK_0, pageSize: 10}; ChatClient.getInstance().chatManager()?.fetchConversationsFromServerWithFilter(filter).then(result => { @@ -94,7 +94,7 @@ ChatClient.getInstance().chatManager()?.fetchConversationsFromServerWithFilter(f 对于本地会话,你可以调用 `getConversations` 方法获取本地所有会话后自己进行会话过滤。下面以查询标记了 `MarkType.MARK_0` 的所有本地会话为例。 -```TypeScript +```typescript //最终的查询结果全部放入 result 中。 let result = new Array(); let conversations = ChatClient.getInstance().chatManager()?.getConversations(); @@ -116,7 +116,7 @@ if(conversations && conversations.length > 0){ 你可以调用 `Conversation#marks` 方法获取本地单个会话的所有标记,示例代码如下: -```TypeScript +```typescript let conversation = ChatClient.getInstance().chatManager()?.getConversation("conversationId"); let marks = conversation?.marks(); ``` diff --git a/docs/document/harmonyos/conversation_overview.md b/docs/document/harmonyos/conversation_overview.md index 3b33f0ec..16ad24a7 100644 --- a/docs/document/harmonyos/conversation_overview.md +++ b/docs/document/harmonyos/conversation_overview.md @@ -46,7 +46,7 @@ 示例代码如下: -```TypeScript +```typescript let listener: ConversationListener = { // 收到会话已读的事件。该事件在以下场景中触发: // 1. 当消息接收方调用 `ackConversationRead()` 方法,SDK 会执行此回调, diff --git a/docs/document/harmonyos/conversation_pin.md b/docs/document/harmonyos/conversation_pin.md index 9faa223f..73eae5d7 100644 --- a/docs/document/harmonyos/conversation_pin.md +++ b/docs/document/harmonyos/conversation_pin.md @@ -31,7 +31,7 @@ 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.pinConversation(conversationId, isPinned).then(()=> { // success logic }).catch((e: ChatError) => { @@ -49,7 +49,7 @@ ChatClient.getInstance().chatManager()?.pinConversation(conversationId, isPinned 示例代码如下: -```TypeScript +```typescript // limit: 每页返回的会话数。取值范围为 [1,50]。 // cursor: 开始获取数据的游标位置。若获取数据时传 `null` 或者空字符串(""),SDK 从最新置顶的会话开始查询。 let limit = 10; diff --git a/docs/document/harmonyos/conversation_receipt.md b/docs/document/harmonyos/conversation_receipt.md new file mode 100644 index 00000000..6ab9279f --- /dev/null +++ b/docs/document/harmonyos/conversation_receipt.md @@ -0,0 +1,66 @@ +# 会话已读回执 + +会话已读回执指接收方进入会话页面,阅读会话中的所有消息后,调用接口向服务器发送会话已读回执,服务器将该回执回调给消息发送方,消息发送方将会收到会话已读回调。在多端多设备登录下,接收方的其他设备也会收到该回调。 + +目前,单聊和群组聊天支持会话已读回执。本文介绍如何使用环信即时通讯 IM HarmonyOS SDK 实现会话已读回执功能。 + +会话已读回执的效果示例,如下图所示: + +![img](/images/uikit/chatuikit/feature/conversation/conversation_read.png) + +## 技术原理 + + 单聊会话已读回执实现的流程如下: + + 1. 设置 `ChatOptions#setRequireReadAck` 为 `true` 开启已读回执功能。 + 2. 消息接收方进入会话页面,阅读消息后,调用 `ackConversationRead` 方法发送会话已读回执。 + 3. 消息发送方通过监听 `onConversationRead` 回调接收会话已读回执。 + +## 前提条件 + +开始前,请确保满足以下条件: + +- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 +- 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 + + ## 实现方法 + + 参考以下步骤在单聊中实现会话已读回执: + + 1. 开启已读回执功能,即 SDK 初始化时设置 `ChatOptions#setRequireReadAck` 为 `true`。 + + ```typescript +// 设置是否需要消息已读回执,设为 `true`。 +options.setRequireReadAck(true); + ``` + + 2. 接收方发送会话已读回执。 + +消息接收方进入会话页面,查看会话中是否有未读消息。若有,调用 `ackConversationRead` 方法发送会话已读回执,没有则不发送。 + +若会话中存在多条未读消息,建议调用该方法,因为若调用发送消息已读回执方法 `ackMessageRead`,则需要调用多次。 + +```typescript +ChatClient.getInstance().chatManager()?.ackConversationRead(conversationId); +``` + +3. 消息发送方监听会话已读回执的回调。 + +同一用户 ID 登录多设备的情况下,用户在一台设备上发送会话已读回执,其他设备会收到 `onConversationRead` 回调。 + +:::tip +对于群组聊天,会话已读回执只用于清空服务端的群组会话的未读数,消息发送方不会通过 `onConversationRead` 回调收到会话已读回执。 +::: + +```typescript +let conversationListener: ConversationListener = { + onConversationRead: (from: string, to: string): void => { + // 会话已读回调 + } +} +ChatClient.getInstance().chatManager()?.addConversationListener(conversationListener); +``` + +## 会话已读回执和消息未读数 + +消息接收方调用 `ackConversationRead` 方法发送会话已读回执,开发者可调用 `Conversation#markAllMessagesAsRead` 方法将所有未读消息设置为已读,即将该会话的未读消息数清零。 diff --git a/docs/document/harmonyos/conversation_unread.md b/docs/document/harmonyos/conversation_unread.md index a18b65e9..07b854b8 100644 --- a/docs/document/harmonyos/conversation_unread.md +++ b/docs/document/harmonyos/conversation_unread.md @@ -26,7 +26,7 @@ 你可以调用 `getUnreadMsgCount` 获取本地指定会话的未读消息数,示例代码如下: -```TypeScript +```typescript let conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); if (conversation) { let unreadMsgCount = conversation.getUnreadMsgCount(); @@ -37,7 +37,7 @@ if (conversation) { 你可以调用 `markAllConversationsAsRead` 方法将本地所有会话设为已读,即将所有会话的未读消息数清零,示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.markAllConversationsAsRead(); ``` @@ -45,7 +45,7 @@ ChatClient.getInstance().chatManager()?.markAllConversationsAsRead(); 你可以调用 `markAllMessagesAsRead` 方法对指定会话的未读消息数清零,示例代码如下: -```TypeScript +```typescript let conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); conversation?.markAllMessagesAsRead(); ``` @@ -54,7 +54,7 @@ conversation?.markAllMessagesAsRead(); 你可以调用 `markMessageAsRead` 方法将指定会话的单条未读消息置为已读。 -```TypeScript +```typescript let conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); conversation?.markMessageAsRead(messageId); ``` diff --git a/docs/document/harmonyos/group_attributes.md b/docs/document/harmonyos/group_attributes.md index 7d780f48..b7860763 100644 --- a/docs/document/harmonyos/group_attributes.md +++ b/docs/document/harmonyos/group_attributes.md @@ -31,7 +31,7 @@ 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.changeGroupName(groupId, changedGroupName).then(res => console.log(res.groupName())); ``` @@ -41,7 +41,7 @@ ChatClient.getInstance().groupManager()?.changeGroupName(groupId, changedGroupNa 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.changeGroupDescription(groupId, description).then(res => console.log(res.description())); ``` @@ -51,7 +51,7 @@ ChatClient.getInstance().groupManager()?.changeGroupDescription(groupId, descrip 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.updateGroupAnnouncement(groupId, announcement).then(res => console.log(res.announcement())); ``` @@ -61,7 +61,7 @@ ChatClient.getInstance().groupManager()?.updateGroupAnnouncement(groupId, announ 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.fetchGroupAnnouncement(groupId).then(res => console.log(res.announcement())); ``` @@ -73,7 +73,7 @@ ChatClient.getInstance().groupManager()?.fetchGroupAnnouncement(groupId).then(re 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.uploadGroupSharedFile(groupId, filePath, callBack).then((res: SharedFile)=> { // success logic }); @@ -83,7 +83,7 @@ ChatClient.getInstance().groupManager()?.uploadGroupSharedFile(groupId, filePath 所有群成员均可调用 `downloadGroupSharedFile` 方法下载群组共享文件。 -```TypeScript +```typescript let sharedFiles: Array = await ChatClient.getInstance().groupManager()?.fetchGroupSharedFileList(groupId, pageNum, pageSize); // 获取需要的共享文件信息 let sharedFile = sharedFiles[index]; @@ -103,7 +103,7 @@ ChatClient.getInstance().groupManager()?.downloadGroupSharedFile(groupId, shared 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.deleteGroupSharedFile(groupId, fileId).then(()=> console.log("delete success")); ``` @@ -111,7 +111,7 @@ ChatClient.getInstance().groupManager()?.deleteGroupSharedFile(groupId, fileId). 所有群成员均可以调用 `fetchGroupSharedFileList` 方法从服务器获取群组的共享文件列表。 -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.fetchGroupSharedFileList(groupId, pageNum, pageSize).then((res)=> { // success logic }); @@ -123,7 +123,7 @@ ChatClient.getInstance().groupManager()?.fetchGroupSharedFileList(groupId, pageN 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.updateGroupExtension(groupId, extension).then(res => console.log(res.extension())); ``` diff --git a/docs/document/harmonyos/group_manage.md b/docs/document/harmonyos/group_manage.md index 0358438c..1208dd15 100644 --- a/docs/document/harmonyos/group_manage.md +++ b/docs/document/harmonyos/group_manage.md @@ -78,7 +78,7 @@ 用户加入群组后,将可以收到群消息。示例代码如下: -```TypeScript +```typescript let option: GroupOptions = { groupName: "groupName", desc: "A description of a group", @@ -95,13 +95,13 @@ ChatClient.getInstance().groupManager()?.createGroup(option).then(res => console 仅群主可以调用 `destroyGroup` 方法解散群组。群组解散时,其他群组成员收到 `GroupChangeListener#onGroupDestroyed` 回调并被踢出群组。 -:::notice +:::tip 该操作是危险操作,解散群组后,将删除本地数据库及内存中的群相关信息及群会话。 ::: 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.destroyGroup(groupId).then(() => console.log("Destroy group success")); ``` @@ -117,7 +117,7 @@ ChatClient.getInstance().groupManager()?.destroyGroup(groupId).then(() => consol 示例代码如下: -```TypeScript +```typescript // 根据群组 ID 从本地获取群组详情。 let group: Group | undefined = ChatClient.getInstance().groupManager()?.getGroup(groupId); @@ -141,7 +141,7 @@ let isMsgBlocked: boolean = group.isMsgBlocked(); ### 获取群成员列表 -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.fetchGroupMembers(groupId, pageSize, cursor).then((res) => { // success logic }); @@ -153,7 +153,7 @@ ChatClient.getInstance().groupManager()?.fetchGroupMembers(groupId, pageSize, cu 示例代码如下: -```TypeScript +```typescript // pageIndex:当前页码,从 0 开始。 // pageSize:每页期望返回的群组数。取值范围为[1,20]。 ChatClient.getInstance().groupManager()?.fetchJoinedGroupsFromServer(pageNum, pageSize).then((res) => { @@ -163,13 +163,13 @@ ChatClient.getInstance().groupManager()?.fetchJoinedGroupsFromServer(pageNum, pa - 用户可以调用 `getAllGroups` 方法加载本地群组列表。为了保证数据的正确性,需要先从服务器获取自己加入和创建的群组列表。示例代码如下: -```TypeScript +```typescript let groupList = await ChatClient.getInstance().groupManager()?.getAllGroups(); ``` - 用户还可以分页获取公开群列表: -```TypeScript +```typescript let result = await ChatClient.getInstance().groupManager()?.fetchPublicGroupsFromServer(pageSize, cursor); if (result) { let groupsList = result.getResult(); @@ -181,7 +181,7 @@ if (result) { 你可以调用 `GroupManager#fetchJoinedGroupsCount` 方法从服务器获取当前用户已加入的群组数量。单个用户可加入群组数量的上限取决于订阅的即时通讯的套餐包,详见[产品价格](/product/pricing.html#套餐包功能详情)。 -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.fetchJoinedGroupsCount().then(res => console.log(res.toString())); ``` @@ -193,7 +193,7 @@ ChatClient.getInstance().groupManager()?.fetchJoinedGroupsCount().then(res => co 群成员可以调用 `blockGroupMessage` 方法屏蔽群消息。屏蔽群消息后,该成员不再从指定群组接收群消息,群主和群管理员不能进行此操作。示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.blockGroupMessage(groupId).then(res => console.log(res.groupName())); ``` @@ -201,7 +201,7 @@ ChatClient.getInstance().groupManager()?.blockGroupMessage(groupId).then(res => 群成员可以调用 `unblockGroupMessage` 方法解除屏蔽群消息。示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.unblockGroupMessage(groupId).then(res => console.log(res.groupName())); ``` @@ -211,7 +211,7 @@ ChatClient.getInstance().groupManager()?.unblockGroupMessage(groupId).then(res = 示例代码如下: -```TypeScript +```typescript // 1、获取群组详情 ChatClient.getInstance().groupManager()?.fetchGroupFromServer(groupId).then((group) => { // 2、检查用户是否屏蔽了该群的消息 @@ -227,7 +227,7 @@ ChatClient.getInstance().groupManager()?.fetchGroupFromServer(groupId).then((gro 示例代码如下: -```TypeScript +```typescript // 创建一个群组事件监听 // 在该方法的举例中,用户 A 表示当前用户。 let groupListener: GroupListener = { diff --git a/docs/document/harmonyos/group_members.md b/docs/document/harmonyos/group_members.md index 7f77f03e..780b1353 100644 --- a/docs/document/harmonyos/group_members.md +++ b/docs/document/harmonyos/group_members.md @@ -52,7 +52,7 @@ 1. 调用 `fetchPublicGroupsFromServer` 方法从服务器获取公开群列表,查询到想要加入的群组 ID。示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.fetchPublicGroupsFromServer(pageSize, cursor).then((result)=> { let groupList = result.getResult(); let cursor = result.getNextCursor(); @@ -65,7 +65,7 @@ ChatClient.getInstance().groupManager()?.fetchPublicGroupsFromServer(pageSize, c 示例代码如下: - ```TypeScript + ```typescript ChatClient.getInstance().groupManager()?.joinGroup(groupId).then((group) => { // success logic }); @@ -73,7 +73,7 @@ ChatClient.getInstance().groupManager()?.fetchPublicGroupsFromServer(pageSize, c - 调用 `applyJoinToGroup` 方法加入需要群主或管理员审批的公开群,即 `GroupStyle` 设置为 `GroupStylePublicJoinNeedApproval`。示例代码如下: - ```TypeScript + ```typescript ChatClient.getInstance().groupManager()?.applyJoinToGroup(groupId, "your reason").then((group) => { // success logic }); @@ -87,7 +87,7 @@ ChatClient.getInstance().groupManager()?.fetchPublicGroupsFromServer(pageSize, c 示例代码如下: - ```TypeScript + ```typescript ChatClient.getInstance().groupManager()?.acceptApplication(groupId, userId).then((group) => { // success logic }); @@ -97,7 +97,7 @@ ChatClient.getInstance().groupManager()?.fetchPublicGroupsFromServer(pageSize, c 示例代码如下: - ```TypeScript + ```typescript ChatClient.getInstance().groupManager()?.declineApplication(groupId, userId, "your reason").then((group) => { // success logic }); @@ -113,7 +113,7 @@ ChatClient.getInstance().groupManager()?.fetchPublicGroupsFromServer(pageSize, c - 群主或群管理员加人,需要调用 `addUsersToGroup` 方法: - ```TypeScript + ```typescript ChatClient.getInstance().groupManager()?.addUsersToGroup(groupId, newMembers).then((group) => { // success logic }); @@ -123,7 +123,7 @@ ChatClient.getInstance().groupManager()?.fetchPublicGroupsFromServer(pageSize, c 对于私有群,`GroupStyle` 设置为 `GroupStylePrivateMemberCanInvite` 时,所有群成员均可以邀请人进群。 - ```TypeScript + ```typescript ChatClient.getInstance().groupManager()?.inviteUsers(groupId, newMembers, "your reason").then((group) => { // success logic }); @@ -133,7 +133,7 @@ ChatClient.getInstance().groupManager()?.fetchPublicGroupsFromServer(pageSize, c - 受邀用户同意加入群组,需要调用 `acceptInvitation` 方法。 - ```TypeScript + ```typescript ChatClient.getInstance().groupManager()?.acceptInvitation(groupId).then((group) => { // success logic }); @@ -141,7 +141,7 @@ ChatClient.getInstance().groupManager()?.fetchPublicGroupsFromServer(pageSize, c - 受邀人拒绝入群组,需要调用 `declineInvitation` 方法。 - ```TypeScript + ```typescript ChatClient.getInstance().groupManager()?.declineInvitation(groupId, "your reason").then(() => { // success logic }); @@ -157,7 +157,7 @@ ChatClient.getInstance().groupManager()?.fetchPublicGroupsFromServer(pageSize, c 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.leaveGroup(groupId).then(() => { // success logic }); @@ -169,7 +169,7 @@ ChatClient.getInstance().groupManager()?.leaveGroup(groupId).then(() => { - 移出群成员,示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.removeUsersFromGroup(groupId, members).then((group) => { // success logic }); @@ -191,7 +191,7 @@ ChatClient.getInstance().groupManager()?.removeUsersFromGroup(groupId, members). 示例代码如下: -```TypeScript +```typescript let attributes = new Map(); attributes.set('key1', 'value1'); attributes.set('key2', 'value2'); @@ -208,7 +208,7 @@ ChatClient.getInstance().groupManager()?.setMemberAttributes(groupId, member, at 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.fetchMemberAttributes(groupId, member).then(resultMap => { // success logic }).catch((e: ChatError) => { @@ -220,13 +220,13 @@ ChatClient.getInstance().groupManager()?.fetchMemberAttributes(groupId, member). 你可调用 `GroupManager#fetchMembersAttributes` 方法根据指定的属性 key 获取多个群成员的自定义属性。 -:::notice +:::tip 每次最多可获取 10 个群成员的自定义属性。 ::: 示例代码如下: -```TypeScript +```typescript // keys:要获取自定义属性的 key 的数组。若 keys 为空数组或不传则获取这些成员的所有自定义属性。 ChatClient.getInstance().groupManager()?.fetchMembersAttributes(groupId, members, keys).then(resultMap => { // success logic @@ -243,7 +243,7 @@ ChatClient.getInstance().groupManager()?.fetchMembersAttributes(groupId, members 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.changeOwner(groupId, newOwner).then((group) => { // success logic }); @@ -257,7 +257,7 @@ ChatClient.getInstance().groupManager()?.changeOwner(groupId, newOwner).then((gr 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.addGroupAdmin(groupId, admin).then((group) => { // success logic }); @@ -271,7 +271,7 @@ ChatClient.getInstance().groupManager()?.addGroupAdmin(groupId, admin).then((gro 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.removeGroupAdmin(groupId, admin).then((group) => { // success logic }); @@ -281,7 +281,7 @@ ChatClient.getInstance().groupManager()?.removeGroupAdmin(groupId, admin).then(( 获取群管理员列表示例如下: -```TypeScript +```typescript // 获取内存中管理员列表。 let adminList: Array = group.adminList(); ``` @@ -298,7 +298,7 @@ let adminList: Array = group.adminList(); 即使开启了群组全员禁言,群组白名单中的成员仍可以发送群组消息。不过,禁言列表上的用户即使加入了群白名单仍无法在群组中发送消息。 -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.addToGroupWhitelist(groupId, members).then(() => { // success logic }); @@ -310,7 +310,7 @@ ChatClient.getInstance().groupManager()?.addToGroupWhitelist(groupId, members).t 群成员被移除群白名单后,该群成员及其他未操作的群管理员和群主将会收到群组事件回调 `GroupChangeListener#onWhitelistRemoved`。 -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.removeFromGroupWhitelist(groupId, members).then(() => { // success logic }); @@ -320,7 +320,7 @@ ChatClient.getInstance().groupManager()?.removeFromGroupWhitelist(groupId, membe 所有群成员可以调用 `checkIfInGroupWhitelist` 方法检查自己是否在群白名单中,示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.checkIfInGroupWhitelist(groupId).then((res) => { // success logic }); @@ -330,7 +330,7 @@ ChatClient.getInstance().groupManager()?.checkIfInGroupWhitelist(groupId).then(( 仅群主和群管理员可以调用 `fetchGroupWhitelist` 方法从服务器获取当前群组的白名单。 -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.fetchGroupWhitelist(groupId).then((whitelist) => { // success logic }); @@ -344,7 +344,7 @@ ChatClient.getInstance().groupManager()?.fetchGroupWhitelist(groupId).then((whit 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.blockUsers(groupId, userIds).then((group) => { // success logic }); @@ -356,7 +356,7 @@ ChatClient.getInstance().groupManager()?.blockUsers(groupId, userIds).then((grou 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.unblockUsers(groupId, userIds).then((group) => { // success logic }); @@ -368,7 +368,7 @@ ChatClient.getInstance().groupManager()?.unblockUsers(groupId, userIds).then((gr 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.fetchGroupBlocklist(groupId, pageNum, pageSize).then((blocklist) => { // success logic }); @@ -384,7 +384,7 @@ ChatClient.getInstance().groupManager()?.fetchGroupBlocklist(groupId, pageNum, p 仅群主和群管理员可以调用 `muteGroupMembers` 方法将指定成员添加至群组禁言列表。群成员被群主或者群管理员加入禁言列表中后,被禁言成员和其他未操作的管理员或者群主将会收到群组事件回调 `GroupChangeListener#onMutelistAdded`。群成员被加入群禁言列表后,将不能够发言,即使其被加入群白名单也不能发言。 -```TypeScript +```typescript // `duration`:禁言时间。传 -1 表示永久禁言。 ChatClient.getInstance().groupManager()?.muteGroupMembers(groupId, muteMembers, duration).then((group) => { // success logic @@ -397,7 +397,7 @@ ChatClient.getInstance().groupManager()?.muteGroupMembers(groupId, muteMembers, 群成员被移出禁言列表后可以在群组中正常发送消息,被移出的群成员及其他未操作的管理员或者群主将会收到群组事件回调 `GroupChangeListener#onMutelistRemoved`。 -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.unmuteGroupMembers(groupId, members).then((group) => { // success logic }); @@ -407,7 +407,7 @@ ChatClient.getInstance().groupManager()?.unmuteGroupMembers(groupId, members).th 所有群成员可以调用 `checkIfInGroupMutelist` 方法检查自己是否在群禁言列表中,示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.checkIfInGroupMutelist(groupId).then((res) => { // success logic }); @@ -419,7 +419,7 @@ ChatClient.getInstance().groupManager()?.checkIfInGroupMutelist(groupId).then((r 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.fetchGroupMutelist(groupId, pageNum, pageSize).then((res) => { // success logic }); @@ -435,7 +435,7 @@ ChatClient.getInstance().groupManager()?.fetchGroupMutelist(groupId, pageNum, pa 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.muteAllMembers(groupId).then((group) => { // success logic }); @@ -447,7 +447,7 @@ ChatClient.getInstance().groupManager()?.muteAllMembers(groupId).then((group) => 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().groupManager()?.unmuteAllMembers(groupId).then((group) => { // success logic }); diff --git a/docs/document/harmonyos/initialization.md b/docs/document/harmonyos/initialization.md new file mode 100644 index 00000000..8066ccaf --- /dev/null +++ b/docs/document/harmonyos/initialization.md @@ -0,0 +1,20 @@ +# SDK 初始化 + +初始化是使用 SDK 的必要步骤,需在所有接口方法调用前完成。 + +如果进行多次初始化操作,只有第一次初始化以及相关的参数生效。 + +## 前提条件 + +有效的环信即时通讯 IM 开发者账号和 App key,详见[环信即时通讯云控制台的相关文档](enable_and_configure_IM.html#创建应用)。 + +## 初始化 + +初始化示例代码: + +```typescript +let options = new ChatOptions("Your appkey"); +......// 其他 ChatOptions 配置。 +// 初始化时传入上下文以及 options +ChatClient.getInstance().init(context, options); +``` diff --git a/docs/document/harmonyos/integration.md b/docs/document/harmonyos/integration.md new file mode 100644 index 00000000..cfb49175 --- /dev/null +++ b/docs/document/harmonyos/integration.md @@ -0,0 +1,70 @@ +# 集成 SDK + +本文介绍如何将环信即时通讯 IM SDK 集成到你的 HarmonyOS 项目。 + +## 开发环境要求 + +- DevEco Studio NEXT Release(5.0.3.900)及以上; +- HarmonyOS SDK API 12 及以上; +- HarmonyOS NEXT.0.0.71 或以上版本的设备。 + +## 集成 SDK + +打开 [SDK 下载](https://www.easemob.com/download/im)页面,获取最新版的环信即时通讯 IM HarmonyOS SDK,得到 `har` 形式的 SDK 文件。 + +将 SDK 文件,拷贝到 `entry` 模块或者其他需要的模块下的 `libs` 目录。 + +修改模块目录的 `oh-package.json5` 文件,在 `dependencies` 节点增加依赖声明。 + +```json +{ + "dependencies": { + "@easemob/chatsdk": "file:./libs/chatsdk-x.x.x.har" + } +} +``` +最后单击 **File > Sync and Refresh Project** 按钮,直到同步完成。 + +### 添加项目权限 + +在模块的 `module.json5` ,例如:`entry` 模块的 `module.json5` 中,配置示例如下: + +```json +{ + module: { + requestPermissions: [ + { + name: "ohos.permission.GET_NETWORK_INFO", + }, + { + name: "ohos.permission.INTERNET", + }, + ], + }, +} +``` + +### 在工程 `build-profile.json5` 中设置支持字节码 HAR 包。 + +修改工程级 `build-profile.json5` 文件,在 `products` 节点下设置 `useNormalizedOHMUrl` 为 `true`。 + +```json +{ + "app": { + "products": [ + { + "buildOption": { + "strictMode": { + "useNormalizedOHMUrl": true + } + } + } + ] + } +} +```` + +:::tip +- 此配置需要将 `DevEco Studio` 升级到 `Beta2(5.0.3.502)` 及以上版本。 +- SDK 1.3.0 及以上版本采用字节码构建方式打包,必须设置 `useNormalizedOHMUrl` 为 `true`。 +::: \ No newline at end of file diff --git a/docs/document/harmonyos/log.md b/docs/document/harmonyos/log.md new file mode 100644 index 00000000..46eea3aa --- /dev/null +++ b/docs/document/harmonyos/log.md @@ -0,0 +1,27 @@ +# SDK 日志 + +环信即时通讯 IM 日志记录 SDK 相关的信息和事件。环信技术支持团队帮你排查问题时可能会请你发送 SDK 日志。 + +## 输出信息到日志文件 + +环信即时通讯 IM 日志记录 SDK 相关的信息和事件。环信技术支持团队帮你排查问题时可能会请你发送 SDK 日志。 + +默认情况下,SDK 最多可生成和保存三个文件,`easemob.log` 和两个 `easemob_YYYY-MM-DD_HH-MM-SS.log` 文件。这些文件为 UTF-8 编码,每个不超过 2 MB。SDK 会将最新的日志写入 `easemob.log` 文件,写满时则会将其重命名为对应时间点的 `easemob_YYYY-MM-DD_HH-MM-SS.log` 文件,若日志文件超过三个,则会删除最早的文件。 + +例如,SDK 在 2024 年 1 月 1 日上午 8:00:00 记录日志时会生成 `easemob.log` 文件,若在 8:30:00 将 `easemob.log` 文件写满则会将其重命名为 `easemob_2024-01-01_08-30-00.log` 文件,随后在 9:30:30 和 10:30:30 分别生成了 `easemob_2024-01-01_09-30-30.log` 和 `easemob_2024-01-01_10-30-30.log` 文件,则此时 `easemob_2024-01-01_08-30-00.log` 文件会被移除。 + +SDK 默认输出调试信息(所有日志,包括调试信息、警告和错误),如果只需输出错误日志,需要关闭调试模式。 + +```typescript +ChatLog.setLogLevel(ChatLogLevel.ERROR_LEVEL); +``` + +## 获取本地日志 + +打开以下目录,获取本地日志。 + +``` +hdc file recv /data/app/el2/100/base/{应用包名}/{App Key}/core_log +``` + +获取本地日志,需要将 `{应用包名}` 替换为应用的包名,例如 `com.hyphenate.chatuidemo`;`{App Key}` 需要替换为应用的环信 App Key。 \ No newline at end of file diff --git a/docs/document/harmonyos/login.md b/docs/document/harmonyos/login.md new file mode 100644 index 00000000..4483b3b1 --- /dev/null +++ b/docs/document/harmonyos/login.md @@ -0,0 +1,91 @@ +# 登录 + +初始化 IM SDK 后,你需要首先调用接口登录。登录成功后,才能使用 IM 的功能。 + +## 用户注册 + +用户注册模式分为以下两种: + +- 开放注册:一般在体验 Demo 和测试环境时使用,正式环境中不推荐使用该方式注册环信账号。要使用开放注册,需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**开放注册**。只有打开该开关,才能使用客户端或 [REST API](/document/server-side/account_system.html#开放注册单个用户)开放注册用户。 + +示例代码如下所示: + +```typescript +ChatClient.getInstance().createAccount(userId, pwd).then(()=> { + // success logic +}); +``` + +- 授权注册:通过环信提供的 REST API 注册环信用户账号,注册后保存到你的服务器或返给客户端。要使用授权注册,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**授权注册**。相关的 REST API 介绍,详见[授权注册单个用户](/document/server-side/account_system.html#授权注册单个用户)和[批量授权注册用户](/document/server-side/account_system.html#批量授权注册用户)的接口介绍。 + +除此以外,可以在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建正式环境下和测试环境下的用户,详见[创建用户相关介绍](/product/enable_and_configure_IM.html#创建-im-用户)。 + +## 主动登录 + +**用户 ID + token** 是更加安全的登录方式。 + +测试环境下,你在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建用户后,环信服务器会自动为这些用户分配用户 Token,详见[测试环境下创建用户的介绍](/product/enable_and_configure_IM.html#测试环境)。 + +使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 + +```typescript +ChatClient.getInstance().loginWithToken(userId, token).then(() => { + // 登录成功回调 +}).catch((e: ChatError) => { + // 登录失败回调,包含错误信息 +}); +``` + +**用户 ID + 密码** 登录是传统的登录方式。用户名和密码均由你的终端用户自行决定,密码需要符合[密码规则要求](/document/server-side/account_system.html#开放注册单个用户)。 + +```typescript +ChatClient.getInstance().login(userId, pwd).then(() => { + // 登录成功回调 +}).catch((e: ChatError) => { + // 登录失败回调,包含错误信息 +}); +``` + +## 自动登录 + +初始化时,你可以设置 `ChatOptions#setAutoLogin` 选项确定是否自动登录。如果设置为自动登录,则登录成功之后,后续初始化 SDK 时会自动登录。 + +## 获取当前登录的用户 + +你可以调用 `ChatClient#getCurrentUser` 方法获取当前登录用户的用户 ID。 + +## 获取登录状态 + +你可以调用 `ChatClient#isLoggedIn` 方法获取当前用户的登录状态。 + +## 退出登录 + +你可以调用 `logout` 方法退出登录。退出登录后,你不会再收到其他用户发送的消息。 + +```typescript +ChatClient.getInstance().logout().then(()=> { + // success logic +}) +``` + +:::tip + +1. 如果集成了鸿蒙推送,`logout` 方法中 `unbindToken` 参数需设为 `true`,退出时会解绑设备 token,否则可能会出现退出了,还能收到消息推送通知的现象。 +2. 有时可能会遇到网络问题而解绑失败,app 处理时可以弹出提示框让用户选择,是否继续退出(弹出框提示继续退出能收到消息的风险),如果用户选择继续退出,传 `false` 再调用 `logout` 方法退出成功。 + +::: + +## 账号切换 + +若在 app 中从当前账号切换到其他账号,你需要首先调用 `logout` 方法登出,然后再调用 `loginWithToken` 或 `login` 方法登录。 + +## 多设备登录 + +除了单端单设备登录,环信即时通讯 IM 支持同一账号在多端的多个设备上登录。多设备登录时,若同端设备数量超过限制,新登录的设备会将之前登录的设备踢下线。 +关于多设备登录场景中的设备数量限制、互踢策略以及信息同步,详见[多设备登录文档](multi_device.html)。 + +## 更多 + +### 登录被封禁账号的提示 + +在环信即时通讯控制台或调用 REST API 封禁用户账号后,若仍使用该账号登录,SDK会返回 "service is disabled"(305 错误), 可以根据用户这个返回值来进行相应的提示或者处理。 diff --git a/docs/document/harmonyos/message_delete.md b/docs/document/harmonyos/message_delete.md index caf83b10..cac4cc0e 100644 --- a/docs/document/harmonyos/message_delete.md +++ b/docs/document/harmonyos/message_delete.md @@ -31,7 +31,7 @@ 示例代码如下: -```TypeScript +```typescript // 按时间删除消息 ChatClient.getInstance().chatManager()?.removeMessagesFromServer(conversationId, conversationType, beforeTimeStamp).then(()=> { // success logic @@ -47,7 +47,7 @@ ChatClient.getInstance().chatManager()?.removeMessagesFromServer(conversationId, 你可以删除本地指定会话的所有消息,示例代码如下: -```TypeScript +```typescript let conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); if(conversation) { conversation.clearAllMessages(); @@ -58,7 +58,7 @@ if(conversation) { 你可以删除本地单个会话的指定消息,示例代码如下: -```TypeScript +```typescript let conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); if(conversation) { conversation.removeMessage(messageId); diff --git a/docs/document/harmonyos/message_deliver_only_online.md b/docs/document/harmonyos/message_deliver_only_online.md index cd0a382c..d3c2cfc4 100644 --- a/docs/document/harmonyos/message_deliver_only_online.md +++ b/docs/document/harmonyos/message_deliver_only_online.md @@ -27,7 +27,7 @@ 下面以发送文本消息为例进行说明: -```TypeScript +```typescript // 创建一条文本消息,`content` 为消息文字内容。 // `conversationId` 为消息接收方,单聊时为对端用户 ID、群聊时为群组 ID。 let message = ChatMessage.createTextSendMessage(conversationId, content); diff --git a/docs/document/harmonyos/message_forward.md b/docs/document/harmonyos/message_forward.md index e7a9964e..d33e65a7 100644 --- a/docs/document/harmonyos/message_forward.md +++ b/docs/document/harmonyos/message_forward.md @@ -26,7 +26,7 @@ 你可以在单聊、群组聊天、聊天室中转发所有类型的消息。对于附件类型的消息,转发时无需重新上传附件,不过,若消息过期(即由于超过了存储时间已从环信服务器上删除),转发后接收方可查看附件地址,但无法下载附件。 -```TypeScript +```typescript // messageId 为要转发的消息 ID。 let messageId = ""; let targetMessage = ChatClient.getInstance().chatManager()?.getMessage(messageId); diff --git a/docs/document/harmonyos/message_import_insert.md b/docs/document/harmonyos/message_import_insert.md index 4a17e0f5..2c2198b7 100644 --- a/docs/document/harmonyos/message_import_insert.md +++ b/docs/document/harmonyos/message_import_insert.md @@ -30,7 +30,7 @@ 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.importMessages(msgs); ``` @@ -44,7 +44,7 @@ ChatClient.getInstance().chatManager()?.importMessages(msgs); 示例代码如下: -```TypeScript +```typescript // 将消息插入到指定会话中。 let conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); if (!conversation) { diff --git a/docs/document/harmonyos/message_modify.md b/docs/document/harmonyos/message_modify.md index 5f0f558c..4dd1d390 100644 --- a/docs/document/harmonyos/message_modify.md +++ b/docs/document/harmonyos/message_modify.md @@ -29,7 +29,7 @@ 示例代码如下: -```TypeScript +```typescript let messageBody = new TextMessageBody("new content"); ChatClient.getInstance().chatManager()?.modifyMessage(messageId, messageBody).then((result) => { // 消息修改成功的逻辑 @@ -42,7 +42,7 @@ ChatClient.getInstance().chatManager()?.modifyMessage(messageId, messageBody).th 若通过 RESTful API 修改自定义消息,消息的接收方也通过 `ChatMessageListener#onMessageContentChanged` 事件接收修改后的自定义消息。 ::: -```TypeScript +```typescript let listener: ChatMessageListener = { onMessageReceived: (messages: ChatMessage[]): void => { diff --git a/docs/document/harmonyos/message_pin.md b/docs/document/harmonyos/message_pin.md index 5a29ba27..654f1de3 100644 --- a/docs/document/harmonyos/message_pin.md +++ b/docs/document/harmonyos/message_pin.md @@ -31,7 +31,7 @@ 对于单个会话来说,默认可置顶 20 条消息。你可以联系环信商务提升该上限,最大可调整至 100。 -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.pinMessage(messageId).then(() => { // success logic }).catch((e: ChatError) => { @@ -45,7 +45,7 @@ ChatClient.getInstance().chatManager()?.pinMessage(messageId).then(() => { 单聊、群组或聊天室中的所有成员均可取消置顶消息,不论该消息由哪个成员置顶。取消置顶消息后,该会话的置顶消息列表中不再包含该消息。 -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.unpinMessage(messageId).then(() => { // success logic }).catch((e: ChatError) => { @@ -62,7 +62,7 @@ ChatClient.getInstance().chatManager()?.unpinMessage(messageId).then(() => { 2. 若消息置顶后,用户撤回了该消息,则该消息从服务端移除,所有用户在从服务器拉取置顶消息列表时无法拉取到该消息。 ::: -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.fetchPinnedMessagesFromServer(conversationId).then((result: Array) => { // success logic }).catch((e: ChatError) => { @@ -72,7 +72,7 @@ ChatClient.getInstance().chatManager()?.fetchPinnedMessagesFromServer(conversati 你可以调用 `Conversation#getPinnedMessages` 方法从本地取单个会话中的置顶消息。 -```TypeScript +```typescript // conversationId:会话 ID。 const conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); conversation?.getPinnedMessages().then((result: Array) => { @@ -89,7 +89,7 @@ conversation?.getPinnedMessages().then((result: Array) => { - 若消息为置顶状态,该类返回消息置顶的时间以及操作者的用户 ID。 - 若消息为非置顶状态,该类返回 `undefined`。 -```TypeScript +```typescript const pinnedInfo = message.getPinnedInfo() if (pinnedInfo) { // 获取消息置顶信息 @@ -102,7 +102,7 @@ if (pinnedInfo) { ## 监听消息置顶事件 -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.addMessageListener({ onMessageReceived:(messages: Array) => { // received logic diff --git a/docs/document/harmonyos/message_recall.md b/docs/document/harmonyos/message_recall.md index a9d3ecae..39203a1d 100644 --- a/docs/document/harmonyos/message_recall.md +++ b/docs/document/harmonyos/message_recall.md @@ -29,7 +29,7 @@ 调用该方法后,服务端的该条消息(历史消息,离线消息或漫游消息)以及消息发送方和接收方的内存和数据库中的消息均会被移除,消息的接收方会收到 `onMessageRecalled` 事件。对于附件类型消息,包括图片、音频和视频和文件消息,撤回消息后,消息附件也相应删除。 -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.recallMessage(message, ext).then(()=> { // success logic }) @@ -42,7 +42,7 @@ ChatClient.getInstance().chatManager()?.recallMessage(message, ext).then(()=> { - 若用户在线接收了消息,消息撤回时,该事件中的 `RecallMessageInfo` 中的 `getRecallMessage` 为撤回的消息的内容,`getRecallMessageId` 为返回撤回的消息的 ID。 - 若消息发送和撤回时接收方离线,该事件中的 `RecallMessageInfo` 中的 `getRecallMessage` 为空,`getRecallMessageId` 为返回撤回的消息的 ID。 -```TypeScript +```typescript onMessageRecalled?: (messages: Array) => void; ``` diff --git a/docs/document/harmonyos/message_receipt.md b/docs/document/harmonyos/message_receipt.md index f497f5fa..2d08bc7f 100644 --- a/docs/document/harmonyos/message_receipt.md +++ b/docs/document/harmonyos/message_receipt.md @@ -1,43 +1,36 @@ -# 消息回执 +# 实现消息回执 -单聊会话支持消息送达回执、会话已读回执和消息已读回执,发送方发送消息后可及时了解接收方是否及时收到并阅读了信息,也可以了解整个会话是否已读。 +**单聊会话支持消息送达回执和消息已读回执**,发送方发送消息后可及时了解接收方是否及时收到并阅读了消息。 -群聊会话只支持消息已读回执。群成员在发送消息时,可以设置该消息是否需要已读回执。仅专业版及以上版本支持群消息已读回执功能。若要使用该功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 +**群聊会话只支持消息已读回执,不支持送达回执**。群成员在发送消息时,可以设置该消息是否需要已读回执。要使用该功能,你需要[在环信即时通讯云控制台上开通该功能](/product/enable_and_configure_IM.html#设置群消息已读回执),具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 -:::tip -仅单聊消息支持送达回执,群聊消息不支持。 -::: +消息送达回执和已读回执的效果示例,如下图所示: -本文介绍如何使用环信即时通讯 IM HarmonyOS SDK 实现单聊和群聊的消息回执功能。 +![img](/images/android/message_receipt.png) ## 技术原理 -使用环信即时通讯 IM SDK 可以实现消息的送达回执与已读回执,核心方法如下: +使用环信即时通讯 IM HarmonyOS SDK 可以实现消息的送达回执与已读回执。 -- `ChatOptions#setRequireDeliveryAck` 开启送达回执; -- `ChatManager#ackConversationRead` 发出指定会话的已读回执; -- `ChatManager#ackMessageRead` 发出指定消息的已读回执; -- `ChatManager#ackGroupMessageRead` 发出群组消息的已读回执。 +- 单聊消息送达回执的逻辑如下: -送达和已读回执逻辑分别如下: + 1. 你可以通过设置 `ChatOptions.setRequireDeliveryAck` 为 `true` 开启送达回执功能。 + 2. 消息接收方收到消息后,SDK 自动向发送方触发送达回执。 + 3. 消息发送方通过监听 `ChatMessageListener#onMessageDelivered` 回调接收消息送达回执。 -单聊消息送达回执: +- 单聊消息已读回执的逻辑如下: -1. 消息发送方在发送消息前通过 `ChatOptions.setRequireDeliveryAck` 开启送达回执功能; -2. 消息接收方收到消息后,SDK 自动向发送方触发送达回执; -3. 消息发送方通过监听 `OnMessageDelivered` 回调接收消息送达回执。 + 1. 你可以通过设置 `ChatOptions.setRequireReadAck` 为 `true` 开启已读回执功能。 + 2. 消息接收方收到消息后,调用 `ChatManager#ackMessageRead` 方法发送消息已读回执。 + 3. 消息发送方通过监听 `ChatMessageListener#onMessageRead` 回调接收消息已读回执。 -已读回执: +- 群聊消息已读回执的逻辑如下: -- 单聊会话及消息已读回执 - 1. 调用 `ChatOptions.setRequireReadAck` 设置需要发送已读回执,传 `true`; - 2. 消息接收方收到或阅读消息后,调用 API `ackConversationRead` 或 `ackMessageRead` 发送会话或消息已读回执; - 3. 消息发送方通过监听 `ConversationListener#OnConversationRead` 或 `ChatMessageListener#OnMessageRead` 回调接收会话或消息已读回执。 -- 群聊只支持消息已读回执: - 1. 你可以通过设置 `ChatMessage.isNeedGroupAck` 为 `true` 开启群聊消息已读回执功能; - 2. 消息接收方收到或阅读消息后通过 `ackGroupMessageRead` 发送群组消息的已读回执。 + 1. 你可以通过设置 `ChatMessage.isNeedGroupAck` 为 `true` 开启消息已读回执功能。 + 2. 发送方在群组中发送消息时设置 `EMMessage#setIsNeedGroupAck` 为 `true` 要求接收方返回消息已读回执。 + 3. 接收方收到或阅读消息后通过 `ChatManager#ackGroupMessageRead` 方法发送群组消息的已读回执。 ## 前提条件 @@ -45,22 +38,24 @@ - 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -- 群消息已读回执功能仅在环信 IM 专业版及以上版本支持该功能。若要使用该功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 - +- 要使用群消息已读回执功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 + ## 实现方法 -### 消息送达回执 +### 单聊消息送达回执 -若在消息送达时收到通知,你可以打开消息送达开关,这样在消息到达对方设备时你可以收到通知。 +1. 开启消息送达功能,即 SDK 初始化时将 `ChatOptions.setRequireDeliveryAck` 设置为 `true`。 -```TypeScript +```typescript // 设置是否需要接收方送达确认,默认 `false` 即不需要。 -options.setRequireDeliveryAck(false); +options.setRequireDeliveryAck(true); ``` -`onMessageDelivered` 回调是对方收到消息时的通知,你可以在收到该通知时,显示消息的送达状态。 +2. 接收方收到消息后,SDK 自动向发送方触发送达回执。 + +3. 发送方监听 `ChatMessageListener#onMessageDelivered` 事件,收到接收方的送达回执。你可以在收到该通知时,显示消息的送达状态。 -```TypeScript +```typescript let msgListener: ChatMessageListener = { onMessageReceived: (messages: ChatMessage[]): void => { // 收到消息 @@ -75,66 +70,33 @@ ChatClient.getInstance().chatManager()?.addMessageListener(msgListener); ChatClient.getInstance().chatManager()?.removeMessageListener(msgListener); ``` -### 消息已读回执 - -消息已读回执用于告知单聊或群聊中的用户接收方已阅读其发送的消息。为降低消息已读回执方法的调用次数,SDK 还支持在单聊中使用会话已读回执功能,用于获知接收方是否阅读了会话中的未读消息。 - -#### 单聊 +### 单聊消息已读回执 -单聊既支持消息已读回执,也支持会话已读回执。我们建议你按照如下逻辑结合使用两种回执,减少发送消息已读回执数量。 +单聊既支持单条消息已读回执,也支持[会话已读回执](conversation_receipt.html)。我们建议你结合使用这两种回执,见实现步骤的描述。 -- 聊天页面未打开时,若有未读消息,进入聊天页面,发送会话已读回执; -- 聊天页面打开时,若收到消息,发送消息已读回执。 +单聊消息的已读回执有效期与消息在服务端的存储时间一致,即在服务器存储消息期间均可发送已读回执。消息在服务端的存储时间与你订阅的套餐包有关,详见[产品价格](/product/pricing.html#套餐包功能详情)。 -第一步是在设置中打开已读回执开关: +参考如下步骤在单聊中实现消息已读回执。 -```TypeScript -// 设置是否需要消息已读回执,设为 `true`。 +1. App 开启已读回执功能,即 SDK 初始化时将 `ChatOptions.setRequireReadAck` 设置为 `true`。 +```typescript +// 设置是否需要接收方已读确认,默认为true options.setRequireReadAck(true); ``` -##### 会话已读回执 - - 参考以下步骤在单聊中实现会话已读回执。 +1. 接收方发送消息已读回执。 - 1. 接收方发送会话已读回执。 - - 消息接收方进入会话页面,查看会话中是否有未读消息。若有,发送会话已读回执,没有则不再发送。 - -```TypeScript +- 聊天页面打开时,若收到消息,发送单条消息已读回执。 + +```typescript ChatClient.getInstance().chatManager()?.ackConversationRead(conversationId); ``` -2. 消息发送方监听会话已读回执的回调。 - -```TypeScript -let conversationListener: ConversationListener = { - onConversationRead: (from: string, to: string): void => { - // 会话已读回调 - } -} -ChatClient.getInstance().chatManager()?.addConversationListener(conversationListener); -``` - -> 同一用户 ID 登录多设备的情况下,用户在一台设备上发送会话已读回执,服务器会将会话的未读消息数置为 `0`,同时其他设备会收到 `OnConversationRead` 回调。 - -##### 消息已读回执 - -单聊消息的已读回执有效期与消息在服务端的存储时间一致,即在服务器存储消息期间均可发送已读回执。消息在服务端的存储时间与你订阅的套餐包有关,详见[产品价格](/product/pricing.html#套餐包功能详情)。 - -参考如下步骤在单聊中实现消息已读回执。 - - 1. 接收方发送已读回执消息。 - - 消息接收方进入会话时,发送会话已读回执。 - -```TypeScript -ChatClient.getInstance().chatManager()?.ackMessageRead(messageId); -``` - -在会话页面,接收到消息时,根据消息类型发送消息已读回执,如下所示: +- 接收方在会话页面,接收到消息时,再根据消息类型发送单个消息已读回执。 + + 聊天页面未打开时,若有未读消息,进入聊天页面,发送会话已读回执。这种方式可避免发送多个消息已读回执。 -```TypeScript +```typescript let msgListener: ChatMessageListener = { onMessageReceived: (messages: ChatMessage[]): void => { // 收到消息 @@ -146,6 +108,7 @@ let msgListener: ChatMessageListener = { ChatClient.getInstance().chatManager()?.addMessageListener(msgListener); private sendReadAck(message: ChatMessage) { + // 这里是接收的消息,未发送过已读回执且是单聊。 if (message.getDirection() === MessageDirection.RECEIVE && !message.isReceiverRead() && message.getChatType() === ChatType.Chat) { @@ -156,11 +119,11 @@ private sendReadAck(message: ChatMessage) { } ``` -2. 消息发送方监听消息已读回调。 +3. 消息发送方监听消息已读回调。 -你可以调用接口监听指定消息是否已读,示例代码如下: +消息发送方可以通过 `ChatMessageListener.onMessageRead` 事件监听指定消息是否已读,示例代码如下: -```TypeScript +```typescript let msgListener: ChatMessageListener = { ...... onMessageRead: (messages: ChatMessage[]): void => { @@ -171,9 +134,9 @@ let msgListener: ChatMessageListener = { ChatClient.getInstance().chatManager()?.addMessageListener(msgListener); ``` -#### 群聊 +### 群聊消息已读回执 -对于群聊,群成员发送消息时,可以设置该消息是否需要已读回执。若需要,每个群成员在阅读消息后,SDK 均会发送已读回执,即阅读该消息的群成员数量即为已读回执的数量。 +对于群聊,群成员发送消息时,可以设置该消息是否需要已读回执。若需要,每个群成员阅读消息后,应该调用`ChatManager.ackGroupMessageRead` 方法发送已读回执,阅读该消息的群成员数量即为已读回执的数量。 群消息已读回执特性的使用限制如下表所示: @@ -187,9 +150,20 @@ ChatClient.getInstance().chatManager()?.addMessageListener(msgListener); 你可以按以下步骤实现群消息已读回执特性: -1. 群成员发送消息时若需已读回执,需设置 `ChatMessage` 的方法 `setIsNeedGroupAck()` 为 `true`。 +1. 开启已读回执功能,即 SDK 初始化时将 `ChatOptions.setRequireReadAck` 设置为 `true`。 + +该功能开启后,接收方阅读消息后,SDK 底层会自动进行消息已读回执。 -```TypeScript +```typescript +// 设置是否需要接收方已读确认,默认为 `true`。 +options.setRequireReadAck(true); +``` + +2. 发送方发送消息时设置 `ChatMessage.setIsNeedGroupAck` 属性为 `true`。 + +与单聊消息的 app 层级设置已读回执功能不同,群聊消息是在发送消息时设置指定消息是否需要已读回执。 + +```typescript let message = ChatMessage.createTextSendMessage(to, content); if (!message) { return; @@ -198,32 +172,66 @@ message.setChatType(ChatType.GroupChat); message.setIsNeedGroupAck(true); ``` -2. 消息接收方发送群组消息的已读回执。 +3. 消息接收方发送群组消息的已读回执。 -```TypeScript +```typescript if (message.isNeedGroupAck() && !message.isUnread()) { ChatClient.getInstance().chatManager()?.ackGroupMessageRead(message, ext); message.setUnread(false); } ``` -3. 消息发送方监听群组消息已读回调。 +4. 消息发送方监听群组消息已读回调。 -群消息已读回调在消息监听类 `ChatMessageListener` 中。 +群消息已读回调在 `ChatMessageListener#onGroupMessageRead` 中实现。 -```TypeScript -// 接收到群组消息体的已读回执, 消息的接收方已经阅读此消息。 -onGroupMessageRead?: (groupReadAcks: Array) => void; -``` +发送方接收到群组消息已读回执后,其发出消息的属性 `ChatMessage.groupAckCount` 会有相应变化。 -接收到群组消息已读回执后,发出消息的属性 `ChatMessage#groupAckCount` 会有相应变化; +```typescript +let msgListener: ChatMessageListener = { + ...... + onGroupMessageRead?: (groupReadAcks: Array) => { + // 接收到群组消息体的已读回执, 消息的接收方已经阅读此消息。 + } + ...... +} +ChatClient.getInstance().chatManager()?.addMessageListener(msgListener); +``` -4. 消息发送方获取群组消息的已读回执详情。 +5. 消息发送方获取群组消息的已读回执详情。 -如果想实现群消息已读回执的列表显示,可以通过下列接口获取到已读回执的详情。 +你可以调用 `ChatManager#fetchGroupReadAcks` 方法从服务器获取单条消息的已读回执的详情。 -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.fetchGroupReadAcks(messageId, pageSize, startAckId).then((result) => { // success logic }) -``` \ No newline at end of file +``` + +### 查看消息送达和已读状态 + +对于单聊消息,本地通过 `ChatMessage.isDelivered` 字段存储消息送达状态。 + +对于单聊消息,本地通过以下字段存储消息已读状态: + +| 字段 | 描述 | +| :--------- | :----- | +| `ChatMessage.isUnread` | 用户是否已读了该消息。如果是自己发送的消息,该字段的值固定为 `true`。| +| `ChatMessage.isReceiverRead` | 是否(消息接收方)已发送或(消息发送方)已收到消息已读回执。如果是自己发送的消息,记录的是对方是否已读。如果是对方的消息,则记录的是自己是否发送过已读回执。 | + +对于群聊消息,本地数据库通过以下字段存储消息已读状态: + +| 字段 | 描述 | +| :--------- | :----- | +| `ChatMessage.isUnread` | 用户是否已读了该消息。如果是自己发送的消息,该字段的值固定为 `true`。| +| `ChatMessage.groupAckCount` | 已阅读消息的群成员数量。 | + +### 已读回执与未读消息数 + +- 会话已读回执发送后,开发者需要调用 `Conversation.markAllMessagesAsRead` 方法将该会话的所有消息置为已读,即会话的未读消息数清零。 + +- 消息已读回执发送后,开发者需要调用 `Conversation.markMessageAsRead` 方法将该条消息置为已读,则消息未读数会有变化。 + + + + diff --git a/docs/document/harmonyos/message_retrieve.md b/docs/document/harmonyos/message_retrieve.md index 3cfe3dcc..ec519b11 100644 --- a/docs/document/harmonyos/message_retrieve.md +++ b/docs/document/harmonyos/message_retrieve.md @@ -43,7 +43,7 @@ 2. 历史消息在服务器上的存储时间与产品的套餐包相关,详见[产品套餐包详情](/product/pricing.html#套餐包功能详情)。 ::: -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.fetchHistoryMessages(conversationId, conversationType, pageSize, cursor, fetchMessageOption).then((result) => { let cursor = result.getNextCursor(); let list = result.getResult(); @@ -57,7 +57,7 @@ ChatClient.getInstance().chatManager()?.fetchHistoryMessages(conversationId, con 你可以调用 `getMessage` 方法根据消息 ID 获取本地存储的指定消息。如果消息不存在会返回空值。 -```TypeScript +```typescript // msgId:要获取消息的消息 ID。 let msg = ChatClient.getInstance().chatManager()?.getMessage(msgId); if (msg) { @@ -69,7 +69,7 @@ if (msg) { 你可以调用 `getMsgCountInRange` 方法从 SDK 本地数据库中获取会话在某个时间段内的全部消息数。 -```TypeScript +```typescript //conversationId:会话 ID let conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); if (!conversation) { diff --git a/docs/document/harmonyos/message_search.md b/docs/document/harmonyos/message_search.md index 0801318a..aaadc34c 100644 --- a/docs/document/harmonyos/message_search.md +++ b/docs/document/harmonyos/message_search.md @@ -29,7 +29,7 @@ 你可以调用 `Conversation#loadMoreMessagesFromDB` 方法从指定消息 ID 开始分页加载数据库中的消息,示例代码如下: -```TypeScript +```typescript // conversationId:会话 ID。 const conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); // startMsgId: 查询的起始消息 ID。该参数设置后,SDK 从指定的消息 ID 开始按消息检索方向加载。如果传入消息的 ID 为空,SDK 忽略该参数。 @@ -42,7 +42,7 @@ const messages = conversation?.loadMoreMessagesFromDB(startMsgId, pageSize, dire 你可以调用 `ChatManager#searchMessagesFromDB(keywords: string, timestamp: number, maxCount: number, from?: string, direction?: SearchDirection)` 方法根据关键字搜索本地数据库中指定用户发送的消息,示例代码如下: -```TypeScript +```typescript // keywords:搜索关键字; // timestamp:搜索的起始时间戳; // maxCount:每次获取的消息数量,取值范围为 [1,400]。 @@ -53,7 +53,7 @@ const messages = ChatClient.getInstance().chatManager()?.searchMessagesFromDB(ke 你可以调用 `Conversation#searchMessagesByKeywords` 方法根据关键字搜索本地数据库中`单个会话`中指定用户发送的消息,示例代码如下: -```TypeScript +```typescript // conversationId:会话 ID。 const conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); // keywords:搜索关键字; @@ -72,7 +72,7 @@ const messages = conversation?.searchMessagesByKeywords(keywords, timestamp, max 使用设置多个消息类型搜索消息的功能,需将 SDK 升级至 V1.4.0 或以上版本。 ::: -```TypeScript +```typescript const types = [ContentType.TXT, ContentType.IMAGE]; // timestamp:查询的起始消息 Unix 时间戳,单位为毫秒。该参数设置后,SDK 从指定时间戳的消息开始,按消息搜索方向获取。如果该参数设置为负数,SDK 从当前时间开始搜索。 // maxCount:每次获取的消息数量,取值范围为 [1,400]。 @@ -83,7 +83,7 @@ const messages = ChatClient.getInstance().chatManager()?.searchMessagesFromDB(ty 你可以调用 `Conversation#searchMessagesByType` 方法通过设置单个或多个消息类型搜索本地数据库中`指定会话`的消息,示例代码如下: -```TypeScript +```typescript // conversationId:会话 ID。 const conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); const types = [ContentType.TXT, ContentType.IMAGE]; @@ -98,7 +98,7 @@ const messages = conversation?.searchMessagesByType(types, timestamp, maxCount, 你可以调用 `Conversation#searchMessagesFromDB(timestamp: number, maxCount: number, direction?: SearchDirection)` 方法设置消息时间戳、消息数量和搜索方向等条件搜索当前会话中的消息。 -```TypeScript +```typescript // conversationId:会话 ID。 const conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); // timestamp:查询的起始消息 Unix 时间戳,单位为毫秒。该参数设置后,SDK 从指定时间戳的消息开始,按消息搜索方向获取。如果该参数设置为负数,SDK 从当前时间开始搜索。 @@ -111,7 +111,7 @@ const messages = conversation?.searchMessagesFromDB(timestamp, maxCount, directi 你可以调用 `Conversation#searchMessagesBetweenTime` 方法设置消息起始时间戳、结束时间戳和消息数量等条件搜索当前会话中的消息。 -```TypeScript +```typescript // conversationId:会话 ID。 const conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); // startTimestamp: 搜索的起始时间戳。单位为毫秒。 diff --git a/docs/document/harmonyos/message_send_receive.md b/docs/document/harmonyos/message_send_receive.md index b296fa1c..12672904 100644 --- a/docs/document/harmonyos/message_send_receive.md +++ b/docs/document/harmonyos/message_send_receive.md @@ -1,59 +1,27 @@ # 发送和接收消息 - +环信即时通讯 IM HarmonyOS SDK 通过 `EMChatManager` 类和 `EMMessage` 类实现文本、图片、音频、视频和文件等类型的消息的发送和接收。 -登录即时通讯服务后,用户可以在单聊、群聊、聊天室中发送如下类型的消息: +- 对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。 -- 文字消息,包含超链接和表情消息。 -- 附件消息,包含图片、语音、视频及文件消息。 -- 位置消息。 -- 透传消息。 -- 自定义消息。 -- 合并消息。 -- 定向消息。 +- 对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 -对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 - -针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 - -本文介绍如何使用即时通讯 IM HarmonyOS SDK 实现发送和接收这些类型的消息。 - -## 技术原理 - -环信即时通讯 IM HarmonyOS SDK 通过 `ChatManager` 类和 `ChatMessage` 类实现消息的发送和接收。 - -其中,发送和接收消息的逻辑如下: - -1. 发送方调用相应 `create` 方法创建文本、文件、附件等类型的消息; -2. 发送方调用 `sendMessage` 发送消息; -3. 通过 `addMessageListener` 添加消息接收的回调通知。 - -消息收发流程如下: - -1. 用户 A 发送一条消息到环信的消息服务器; -2. 单聊消息时,服务器投递消息给用户 B;对于群聊时消息,服务器投递给群内其他每一个成员; -3. 用户收到消息。 - -![img](/images/android/sendandreceivemsg.png) +单聊、群组聊天和聊天室的消息发送控制,详见[消息发送控制](/product/product_message_overview.html#消息发送控制)文档。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html)。 +- 完成 SDK 初始化,详见 [初始化文档](initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -## 实现方法 - -### 发送文本消息 +## 发送和接收文本消息 1. 首先,利用 `ChatMessage` 类构造一条消息。 默认情况下,SDK 对单个用户发送消息的频率未做限制。如果你联系了环信商务设置了该限制,一旦在单聊、群聊或聊天室中单个用户的消息发送频率超过设定的上限,SDK 会上报错误,即错误码 509 `MESSAGE_CURRENT_LIMITING`。 -示例代码: - -```TypeScript +```typescript // 创建一条文本消息,`content` 为消息文字内容。 // `conversationId` 为消息接收方,单聊时为对端用户 ID、群聊时为群组 ID,聊天室时为聊天室 ID。 let message = ChatMessage.createTextSendMessage(conversationId, content); @@ -66,22 +34,9 @@ message.setChatType(ChatType.Chat); ChatClient.getInstance().chatManager()?.sendMessage(message); ``` -对于聊天室消息,可设置消息优先级。示例代码如下: - -```TypeScript -let message = ChatMessage.createTextSendMessage(conversationId, content); -if (!message) { - return; -} -message.setChatType(ChatType.ChatRoom); -// 聊天室消息的优先级。如果不设置,默认值为 `PriorityNormal`,即“普通”优先级。 -message.setPriority(ChatroomMessagePriority.PriorityHigh); -sendMessage(message); -``` - 2. 通过 `ChatManager` 将该消息发出。发送消息时可以设置 `ChatCallback` 的实例,获取消息发送状态。 -```TypeScript +```typescript // 发送消息时可以设置 `ChatCallback` 的实例,获得消息发送的状态。可以在该回调中更新消息的显示状态。例如消息发送失败后的提示等等。 let callback: ChatCallback = { onSuccess: (): void => { @@ -99,15 +54,13 @@ message.setMessageStatusCallback(callback); ChatClient.getInstance().chatManager()?.sendMessage(message); ``` -### 接收消息 - -你可以用注册监听 `ChatMessageListener` 接收消息。该 `ChatMessageListener` 可以多次添加,请记得在不需要的时候移除 `listener`。 +3. 你可以用注册监听 `ChatMessageListener` 接收消息。该 `ChatMessageListener` 可以多次添加,请记得在不需要的时候移除 `listener`。 在新消息到来时,你会收到 `onMessageReceived` 的回调,消息接收时可能是一条,也可能是多条。你可以在该回调里遍历消息队列,解析并显示收到的消息。 对于聊天室消息,你可以通过消息的 `ChatMessage#isBroadcast` 属性判断该消息是否为[通过 REST API 发送的聊天室全局广播消息](/document/server-side/message_chatroom.html#发送聊天室全局广播消息)。 -```TypeScript +```typescript let msgListener: ChatMessageListener = { onMessageReceived: (messages: ChatMessage[]): void => { // 收到消息,遍历消息队列,解析和显示。 @@ -119,7 +72,7 @@ ChatClient.getInstance().chatManager()?.addMessageListener(msgListener); ChatClient.getInstance().chatManager()?.removeMessageListener(msgListener); ``` -### 发送和接收附件类型的消息 +## 发送和接收附件类型的消息 除文本消息外,SDK 还支持发送附件类型消息,包括语音、图片、视频和文件消息。 @@ -129,34 +82,14 @@ ChatClient.getInstance().chatManager()?.removeMessageListener(msgListener); 2. 接收附件消息。SDK 自动下载语音消息,默认自动下载图片和视频的缩略图。若下载原图、视频和文件,需调用 `downloadAttachment` 方法。 3. 获取附件的服务器地址和本地路径。 -此外,发送附件类型消息时,可以在 `onProgress` 回调中获取附件上传的进度,以百分比表示,示例代码如下: - -```TypeScript -// 发送消息时可以设置 `ChatCallback` 的实例,获得消息发送的状态。可以在该回调中更新消息的显示状态。例如,消息发送失败后的提示等等。 - let callback: ChatCallback = { - onSuccess: (): void => { - // 发送消息成功 - }, - onError: (code: number, error: string): void => { - // 发送消息失败 - }, - onProgress: (progress: number): void => { - // 消息发送的状态,这里只用于附件类型的消息。 - } -} -message.setMessageStatusCallback(callback); - // 发送消息。 -ChatClient.getInstance().chatManager()?.sendMessage(message); -``` - -#### 发送和接收语音消息 +### 发送和接收语音消息 发送和接收语音消息的过程如下: 1. 发送语音消息前,在应用层录制语音文件。 2. 发送方调用 `createVoiceSendMessage` 方法传入接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID)、语音文件的路径和语音时长创建语音消息,然后调用 `sendMessage` 方法发送消息。SDK 会将语音文件上传至环信服务器。 -```TypeScript +```typescript // `filePathOrUri` 为语音文件的本地路径或者文件的 URI,`duration` 为语音时长(单位为秒)。 let message = ChatMessage.createVoiceSendMessage(to, filePathOrUri, duration); if (!message) { @@ -170,9 +103,9 @@ ChatClient.getInstance().chatManager()?.sendMessage(message); 3. 接收方收到语音消息时,自动下载语音文件。 -4. 接收方收到 `onMessageReceived` 回调,调用 `getRemoteUrl` 或 `getLocalPath` 方法获取语音文件的服务器地址或本地路径,从而获取语音文件。 +4. 接收方收到 [onMessageReceived 回调](#发送和接收文本消息),调用 `getRemoteUrl` 或 `getLocalPath` 方法获取语音文件的服务器地址或本地路径,从而获取语音文件。 -```TypeScript +```typescript let voiceBody = message.getBody() as VoiceMessageBody; // 获取语音文件在服务器的地址。 let voiceRemoteUrl = voiceBody.getRemoteUrl(); @@ -180,12 +113,13 @@ let voiceRemoteUrl = voiceBody.getRemoteUrl(); let voiceLocalPath = voiceBody.getLocalPath(); ``` -#### 发送和接收图片消息 +### 发送和接收图片消息 发送和接收图片消息的流程如下: 1. 发送方调用 `createImageSendMessage` 方法传入图片的本地资源标志符 URI、设置是否发送原图以及接收方的用户 ID (群聊或聊天室分别为群组 ID 或聊天室 ID)创建图片消息,然后调用 `sendMessage` 方法发送该消息。SDK 会将图片上传至环信服务器,服务器自动生成图片缩略图。 -```TypeScript + +```typescript // `imageFilePathOrUri` 为图片本地路径或者Uri。 let message = ChatMessage.createImageSendMessage(toChatUsername, imageFilePathOrUri); // 会话类型,包含 `Chat`、`GroupChat` 和 `ChatRoom`,表示单聊、群聊或聊天室,默认为单聊。 @@ -196,11 +130,12 @@ ChatClient.getInstance().chatManager()?.sendMessage(message); 2. 接收方收到图片消息,自动下载图片缩略图。 -SDK 默认自动下载缩略图,即 `ChatOptions.setAutoDownloadThumbnail` 设置为 `true`。若设置为手动下载缩略图,即 `ChatOptions.setAutoDownloadThumbnail` 设置为 `false` ,需调用 `ChatClient.getInstance().chatManager()?.downloadThumbnail(message)` 下载。 +- 默认情况下,SDK 自动下载缩略图,即 `ChatOptions.setAutoDownloadThumbnail` 设置为 `true`。 +- 若设置为手动下载缩略图,即 `ChatOptions.setAutoDownloadThumbnail` 设置为 `false` ,需调用 `ChatClient.getInstance().chatManager()?.downloadThumbnail(message)` 下载。 -3. 接收方收到 `onMessageReceived` 回调,调用 `downloadAttachment` 下载原图。 +3. 接收方收到 [onMessageReceived 回调](#发送和接收文本消息),调用 `downloadAttachment` 下载原图。 -```TypeScript +```typescript let msgListener: ChatMessageListener = { onMessageReceived: (messages: ChatMessage[]): void => { messages.forEach( message => { @@ -227,7 +162,7 @@ let msgListener: ChatMessageListener = { 4. 获取图片消息的缩略图和附件。 -```TypeScript +```typescript let imgBody = message.getBody() as ImageMessageBody; // 从服务器端获取图片文件。 let imgRemoteUrl = imgBody.getRemoteUrl(); @@ -239,7 +174,7 @@ let imgLocalPath = imgBody.getLocalPath(); let thumbnailLocalPath = imgBody.getThumbnailLocalPath(); ``` -#### 发送和接收视频消息 +### 发送和接收视频消息 发送和接收视频消息的流程如下: @@ -247,7 +182,7 @@ let thumbnailLocalPath = imgBody.getThumbnailLocalPath(); 2. 发送方调用 `createVideoSendMessage` 方法传入接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID)、视频文件的本地路径、视频时长以及缩略图的本地存储路径,然后调用 `sendMessage` 方法发送消息。SDK 会将视频文件上传至消息服务器。若需要视频缩略图,你需自行获取视频首帧的路径,将该路径传入 `createVideoSendMessage` 方法。 -```TypeScript +```typescript // 在应用层获取视频首帧 let thumbPath = this.getThumbPath(videoPath); let message = ChatMessage.createVideoSendMessage(toChatUsername, videoPath, videoLength, thumbPath); @@ -260,13 +195,11 @@ message.setChatType(ChatType.GroupChat); ChatClient.getInstance().chatManager()?.sendMessage(message); ``` -3. 接收方收到视频消息时,自动下载视频缩略图。 - -SDK 默认自动下载缩略图,即 `ChatOptions.setAutoDownloadThumbnail` 设置为 `true` 。若设置为手动下载缩略图,即 `ChatOptions.setAutoDownloadThumbnail` 设置为 `false`,需调用 `ChatClient.getInstance().chatManager()?.downloadThumbnail(message)` 下载。 +3. 接收方收到视频消息时,自动下载视频缩略图。你可以设置自动或手动下载视频缩略图,该设置与图片缩略图相同,详见[设置图片缩略图自动下载](#发送和接收图片消息)。 -4. 接收方收到 `onMessageReceived` 回调,可以调用 `ChatClient.getInstance().chatManager()?.downloadAttachment(message)` 方法下载视频原文件。 +4. 接收方收到 [onMessageReceived 回调](#发送和接收文本消息),可以调用 `ChatClient.getInstance().chatManager()?.downloadAttachment(message)` 方法下载视频原文件。 -```TypeScript +```typescript let msgListener: ChatMessageListener = { onMessageReceived: (messages: ChatMessage[]): void => { messages.forEach( message => { @@ -293,7 +226,7 @@ let msgListener: ChatMessageListener = { 5. 获取视频缩略图和视频原文件。 -```TypeScript +```typescript let body = message.getBody() as VideoMessageBody; // 从服务器端获取视频文件。 let imgRemoteUrl = body.getRemoteUrl(); @@ -305,13 +238,13 @@ let localPath = body.getLocalPath(); let localThumbPath = body.getThumbnailLocalPath(); ``` -#### 发送和接收文件消息 +### 发送和接收文件消息 发送和接收文件消息的流程如下: 1. 发送方调用 `createFileSendMessage` 方法传入接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID)和文件的本地路径创建文件消息,然后调用 `sendMessage` 方法发送文件消息。SDK 将文件上传至环信服务器。 -```TypeScript +```typescript // `fileLocalPathOrUri` 为本地文件路径或者本地文件Uri。 let message = ChatMessage.createFileSendMessage(toChatUsername, fileLocalPathOrUri); if (!message) { @@ -323,9 +256,9 @@ message.setChatType(ChatType.GroupChat); ChatClient.getInstance().chatManager()?.sendMessage(message); ``` -2. 接收方收到 `onMessageReceived` 回调,调用 `downloadAttachment` 方法下载文件。 +2. 接收方收到 [onMessageReceived 回调](#发送和接收文本消息),调用 `downloadAttachment` 方法下载文件。 -```TypeScript +```typescript /** * 下载文件。 */ @@ -349,7 +282,7 @@ private downloadFile(message: ChatMessage) { 3. 调用以下方法从服务器或本地获取文件附件: -```TypeScript +```typescript let fileMessageBody = message.getBody() as FileMessageBody; // 从服务器获取文件。 let fileRemoteUrl = fileMessageBody.getRemoteUrl(); @@ -357,11 +290,13 @@ let fileRemoteUrl = fileMessageBody.getRemoteUrl(); let fileLocalPath = fileMessageBody.getLocalPath(); ``` -### 发送和接收位置消息 +## 发送和接收位置消息 -当你要发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 +1. 创建和发送位置消息。 + +发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。 -```TypeScript +```typescript // `latitude` 为纬度,`longitude` 为经度,`locationAddress` 为具体位置内容。 let message = ChatMessage.createLocationSendMessage(toChatUsername, latitude, longitude, locationAddress); if (!message) { @@ -373,15 +308,24 @@ message.setChatType(ChatType.GroupChat); ChatClient.getInstance().chatManager()?.sendMessage(message); ``` -### 发送和接收透传消息 +2. 接收位置消息与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + + 接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 -透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。(透传消息不会存入本地数据库中,所以在 UI 上不会显示)。具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 “em_” 和 “easemob::” 开头的 action 为内部保留字段,注意不要使用。 +## 发送和接收透传消息 + +透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。 + +具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 `em_` 和 `easemob::` 开头的 action 为内部保留字段,注意不要使用。 :::tip -透传消息发送后,不支持撤回。 +- 透传消息发送后,不支持撤回。 +- 透传消息不会存入本地数据库中,所以在 UI 上不会显示。 ::: -```TypeScript +1. 创建和发送透传消息。 + +```typescript let action = "action1"; // `action` 可以自定义。 let cmdBody = new CmdMessageBody(action); @@ -395,9 +339,9 @@ if (!cmdMsg) { ChatClient.getInstance().chatManager()?.sendMessage(cmdMsg); ``` -请注意透传消息的接收方,也是由单独的回调进行通知,方便用户进行不同的处理。 +2. 接收方通过 `onMessageReceived` 和 `onCmdMessageReceived` 回调接收透传消息,方便用户进行不同的处理。 -```TypeScript +```typescript let msgListener: ChatMessageListener = { onMessageReceived: (messages: ChatMessage[]): void => { // 接收到消息 @@ -408,11 +352,13 @@ let msgListener: ChatMessageListener = { } ``` -### 发送自定义类型消息 +## 发送自定义类型消息 除了几种消息之外,你可以自己定义消息类型,方便业务处理,即首先设置一个消息类型名称,然后可添加多种自定义消息。 -```TypeScript +1. 创建和发送自定义类型消息。 + +```typescript // `event` 为需要传递的自定义消息事件,比如礼物消息,可以设置: let event = "gift"; let customBody = new CustomMessageBody(event); @@ -425,7 +371,9 @@ let customMessage = ChatMessage.createSendMessage(to, customBody, ChatType.Group ChatClient.getInstance().chatManager()?.sendMessage(customMessage); ``` -### 发送和接收合并消息 +2. 接收自定义消息与其他类型消息一致,详见[接收文本消息](#发送和接收文本消息)。 + +## 发送和接收合并消息 为了方便消息互动,SDK 支持将多个消息合并在一起进行转发。你可以采取以下步骤进行消息的合并转发: @@ -459,7 +407,7 @@ ChatClient.getInstance().chatManager()?.sendMessage(customMessage); 示例代码如下: -```TypeScript +```typescript let title = "A和B的聊天记录"; let summary = "A:这是A的消息内容\nB:这是B的消息内容"; let compatibleText = "您当前的版本不支持该消息,请升级到最新版本"; @@ -476,11 +424,11 @@ message.setMessageStatusCallback(messageCallback); ChatClient.getInstance().chatManager()?.sendMessage(message); ``` -接收合并消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +#### 接收和解析合并消息 -对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 +接收合并消息与接收普通消息的操作相同,详见[接收文本消息](#发送和接收文本消息)。 -#### 解析合并消息 +对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 合并消息实际上是一种附件消息。收到合并消息后,你可以调用 `downloadAndParseCombineMessage` 方法下载合并消息附件并解析出原始消息列表。 @@ -489,7 +437,7 @@ ChatClient.getInstance().chatManager()?.sendMessage(message); - 若附件已存在,该方法会直接解析附件并返回原始消息列表。 - 若附件不存在,该方法首先下载附件,然后解析附件并返回原始消息列表。 -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.downloadAndParseCombineMessage(message).then((result) => { // 处理并展示消息列表 }).catch((e: ChatError) => { @@ -497,7 +445,7 @@ ChatClient.getInstance().chatManager()?.downloadAndParseCombineMessage(message). }); ``` -### 发送和接收定向消息 +## 发送和接收定向消息 发送定向消息是指向群组或聊天室的单个或多个指定的成员发送消息,其他成员不会收到该消息。 @@ -517,7 +465,7 @@ ChatClient.getInstance().chatManager()?.downloadAndParseCombineMessage(message). 下面以文本消息为例介绍如何发送定向消息,示例代码如下: -```TypeScript +```typescript // 创建一条文本消息。 let message = ChatMessage.createTextSendMessage(groupId, content); // 会话类型:群组和聊天室聊天,分别为 `GroupChat` 和 `ChatRoom`。 @@ -528,15 +476,15 @@ message.setReceiverList(receives); ChatClient.getInstance().chatManager()?.sendMessage(message); ``` -接收群定向消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +接收群定向消息与接收普通消息的操作相同,详见[接收消息](#发送和接收文本消息)。 -### 使用消息扩展字段 +## 使用消息扩展字段 当 SDK 提供的消息类型不满足需求时,你可以通过消息扩展字段传递自定义的内容,从而生成自己需要的消息类型。 当目前消息类型不满足用户需求时,可以在扩展部分保存更多信息,例如消息中需要携带被回复的消息内容或者是图文消息等场景。 -```TypeScript +```typescript let message = ChatMessage.createTxtSendMessage(toChatUsername, content); // 增加自定义属性。 let attributes = new Map(); @@ -550,6 +498,48 @@ let attr1 = exts.get("attribute1") as string; let attr2 = exts.get("attribute2") as boolean; ``` -## 注意事项 +## 更多 + +### 设置聊天室消息优先级 + +针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 + +对于聊天室消息,可设置消息优先级,包括高、普通和低优先级。示例代码如下: + +```typescript +let message = ChatMessage.createTextSendMessage(conversationId, content); +if (!message) { + return; +} +message.setChatType(ChatType.ChatRoom); +// 聊天室消息的优先级。如果不设置,默认值为 `PriorityNormal`,即“普通”优先级。 +message.setPriority(ChatroomMessagePriority.PriorityHigh); +sendMessage(message); +``` + +### 获取发送附件消息的进度 + +发送附件类型消息时,可以在 `onProgress` 回调中获取附件上传的进度,以百分比表示,示例代码如下: + +```typescript +// 发送消息时可以设置 `ChatCallback` 的实例,获得消息发送的状态。可以在该回调中更新消息的显示状态。例如,消息发送失败后的提示等等。 + let callback: ChatCallback = { + onSuccess: (): void => { + // 发送消息成功 + }, + onError: (code: number, error: string): void => { + // 发送消息失败 + }, + onProgress: (progress: number): void => { + // 消息发送的状态,这里只用于附件类型的消息。 + } +} +message.setMessageStatusCallback(callback); + // 发送消息。 +ChatClient.getInstance().chatManager()?.sendMessage(message); +``` + +### 发送消息前的内容审核 [内容审核服务会关注消息 body 中指定字段的内容,不同类型的消息审核不同的字段](/product/moderation/moderation_mechanism.html),若创建消息时在这些字段中传入了很多业务信息,可能会影响审核效果。因此,创建消息时需要注意内容审核的字段不涉及业务信息,建议业务信息放在扩展字段中。 + diff --git a/docs/document/harmonyos/message_update.md b/docs/document/harmonyos/message_update.md index 523f31fd..1df4be17 100644 --- a/docs/document/harmonyos/message_update.md +++ b/docs/document/harmonyos/message_update.md @@ -25,13 +25,13 @@ - 直接调用 `ChatManager#updateMessage` 方法更新 SDK 本地数据库中的消息。 -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.updateMessage(message); ``` - 若正在使用 `Conversation` 类,可以先获取会话,再调用 `Conversation#updateMessage` 方法更新 SDK 本地数据库会话中的消息。 -```TypeScript +```typescript let conversation = ChatClient.getInstance().chatManager()?.getConversation(conversationId); if (!conversation) { return; diff --git a/docs/document/harmonyos/multi_device.md b/docs/document/harmonyos/multi_device.md index 2dd8b285..1685b5b2 100644 --- a/docs/document/harmonyos/multi_device.md +++ b/docs/document/harmonyos/multi_device.md @@ -55,7 +55,7 @@ HarmonyOS SDK 初始化时会生成登录 ID 用于在多设备登录和消息 你可以调用 `getSelfIdsOnOtherPlatform` 方法获取其他登录设备的登录 ID 列表,然后选择目标登录 ID 作为消息接收方向指定设备发送消息。 -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.getSelfIdsOnOtherPlatform().then(ids => { // 选择一个登录 ID 作为消息接收方。 let toChatUsername = ids[0]; @@ -75,11 +75,11 @@ ChatClient.getInstance().contactManager()?.getSelfIdsOnOtherPlatform().then(ids 初始化 SDK 时,你可以调用 `ChatOptions#setLoginCustomExt` 方法设置登录设备的自定义扩展信息。设置后,若因达到了登录设备数量限制而导致在已登录的设备上强制退出时(`206` 错误,`USER_LOGIN_ANOTHER_DEVICE`),被踢设备收到的 `ConnectionListener#onLogout` 回调会包含导致该设备被踢下线的新登录设备的自定义扩展信息。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: -```TypeScript +```typescript let options = new ChatOptions("您的AppKey"); options.setLoginCustomExt("您要设置的自定义扩展信息"); ChatClient.getInstance().init(this.context, options); @@ -106,7 +106,7 @@ ChatClient.getInstance().addConnectionListener({ 你需要先实现 `MultiDeviceListener` 类监听其他设备上的操作,然后调用 `addMultiDeviceListener` 方法添加多设备监听。 -```TypeScript +```typescript //实现 `MultiDeviceListener` 监听其他设备上的操作。 let multiDeviceListener: MultiDevicesListener = { onContactEvent: (event: MultiDevicesEvent, target: string, ext: string): void => { @@ -224,7 +224,7 @@ ChatClient.getInstance().removeMultiDevicesListener(multiDeviceListener); 当 PC 端和移动端登录同一个账号时,在移动端可以通过调用方法获取到 PC 端的登录 ID。该登录 ID 相当于特殊的好友用户 ID,可以直接使用于聊天,使用方法与好友的用户 ID 类似。 -```TypeScript +```typescript ChatClient.getInstance().contactManager().getSelfIdsOnOtherPlatform().then(result => { // success logic }); diff --git a/docs/document/harmonyos/overview.md b/docs/document/harmonyos/overview.md index e7ef9ff7..6f4ed692 100644 --- a/docs/document/harmonyos/overview.md +++ b/docs/document/harmonyos/overview.md @@ -32,7 +32,7 @@ ohos.permission.GET_NETWORK_INFO 初始化示例代码: -```TypeScript +```typescript let options = new ChatOptions("Your appkey"); ......// 其他 ChatOptions 配置。 // 初始化时传入上下文以及 options @@ -58,13 +58,13 @@ ChatClient.getInstance().init(context, options); 若支持 SDK 注册,需登录[环信即时通讯云控制台](https://console.easemob.com/user/login),选择 **即时通讯** > **服务概览**,将 **设置**下的 **用户注册模式** 设置为 **开放注册**。 -```TypeScript +```typescript ChatClient.getInstance().createAccount(userId, pwd).then(()=> { // success logic }); ``` -:::notice +:::tip 该注册模式为在客户端注册,旨在方便测试,并不推荐在正式环境中使用。 ::: @@ -85,7 +85,7 @@ ChatClient.getInstance().createAccount(userId, pwd).then(()=> { **用户 ID + 密码** 登录是传统的登录方式。用户名和密码均由你的终端用户自行决定,密码需要符合密码规则要求。 -```TypeScript +```typescript ChatClient.getInstance().login(userId, pwd).then(() => { // 登录成功回调 }).catch((e: ChatError) => { @@ -95,11 +95,11 @@ ChatClient.getInstance().login(userId, pwd).then(() => { **用户 ID + token** 是更加安全的登录方式。token 可以通过调用 REST API 获取。详见 [环信用户 token 的获取](/document/server-side/easemob_user_token.html)。 -:::notice +:::tip 使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 ::: -```TypeScript +```typescript ChatClient.getInstance().loginWithToken(userId, token).then(() => { // 登录成功回调 }).catch((e: ChatError) => { @@ -120,7 +120,7 @@ ChatClient.getInstance().loginWithToken(userId, token).then(() => { ## 退出登录 -```TypeScript +```typescript ChatClient.getInstance().logout().then(()=> { // success logic }) @@ -129,7 +129,7 @@ ChatClient.getInstance().logout().then(()=> { 你可以通过注册连接监听确认连接状态。 -```TypeScript +```typescript let connectionListener: ConnectionListener = { onConnected: (): void => { // 长连接建立 @@ -193,7 +193,7 @@ ChatClient.getInstance().removeConnectionListener(connectionListener); SDK 默认输出调试信息(所有日志,包括调试信息、警告和错误),如果只需输出错误日志,需要关闭调试模式。 -```TypeScript +```typescript ChatLog.setLogLevel(ChatLogLevel.ERROR_LEVEL); ``` diff --git a/docs/document/harmonyos/presence.md b/docs/document/harmonyos/presence.md index ec104533..47e84268 100644 --- a/docs/document/harmonyos/presence.md +++ b/docs/document/harmonyos/presence.md @@ -50,7 +50,7 @@ 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().presenceManager()?.subscribePresences(userId, 1 * 24 * 3600).then(result => { // success logic }).catch((e: ChatError) => { @@ -71,7 +71,7 @@ ChatClient.getInstance().presenceManager()?.subscribePresences(userId, 1 * 24 * 用户在线时,可调用 `PresenceManager#publishPresence` 方法发布自定义在线状态: -```TypeScript +```typescript ChatClient.getInstance().presenceManager()?.publishPresence("自定义状态").then(() => { // success logic }).catch((e: ChatError) => { @@ -87,7 +87,7 @@ ChatClient.getInstance().presenceManager()?.publishPresence("自定义状态").t 示例代码如下: -```TypeScript +```typescript let presenceListener: PresenceListener = { onPresenceUpdated: (presences: Presence[]): void => { // presence logic @@ -104,7 +104,7 @@ ChatClient.getInstance().presenceManager()?.addListener(presenceListener); 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().presenceManager()?.unsubscribePresences(userId).then(() => { // success logic }).catch((e: ChatError) => { @@ -118,7 +118,7 @@ ChatClient.getInstance().presenceManager()?.unsubscribePresences(userId).then(() 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().presenceManager()?.fetchSubscribedMembers(pageNumber, pageSize).then(result => { // success logic }).catch((e: ChatError) => { @@ -132,7 +132,7 @@ ChatClient.getInstance().presenceManager()?.fetchSubscribedMembers(pageNumber, p 示例代码如下: -```TypeScript +```typescript // contacts:要查询状态的用户 ID,每次最多可传 100 个用户 ID。 ChatClient.getInstance().presenceManager()?.fetchPresenceStatus(contacts).then(result => { // success logic diff --git a/docs/document/harmonyos/privatecloud.md b/docs/document/harmonyos/privatecloud.md index 8715e807..82cd261b 100644 --- a/docs/document/harmonyos/privatecloud.md +++ b/docs/document/harmonyos/privatecloud.md @@ -6,7 +6,7 @@ SDK 默认指向公有云地址,在部署私有云后,需要将地址重新指向到新的地址,以下是地址修改方法: -```TypeScript +```typescript let options = new ChatOptions();//实例化 EMOptions 对象 options.setRestServer("10.10.10.10:00");//设置私有云 REST 地址(ip/域名:port) options.setIMServer("10.10.10.10");//设置私有云 IM 地址 @@ -14,11 +14,11 @@ options.setImPort(100);//设置私有云 IM 端口号 ChatClient.getInstance().init(options);//最后初始化 SDK ``` -:::notice +:::tip 如果需要配置 HTTPS 只需在 REST 地址前加一个前缀。 ::: -```TypeScript +```typescript options.setRestServer("https://10.10.10.10:00");//设置私有云 REST 地址(ip:port) ``` @@ -27,7 +27,7 @@ options.setRestServer("https://10.10.10.10:00");//设置私有云 REST 地址( 1. 服务器端配置 DNS 地址表 2. 从服务器端动态获取地址 -```TypeScript +```typescript let options = new ChatOptions(); options.setDnsURL("url");//从服务器端动态获取地址 ChatClient.getInstance().init(options);//初始化 SDK diff --git a/docs/document/harmonyos/push/push_display.md b/docs/document/harmonyos/push/push_display.md index 0353aaff..017924c3 100644 --- a/docs/document/harmonyos/push/push_display.md +++ b/docs/document/harmonyos/push/push_display.md @@ -9,7 +9,7 @@ 你可以调用 `updatePushNickname` 设置推送通知中显示的昵称,如以下代码示例所示: -```TypeScript +```typescript ChatClient.getInstance().pushManager()?.updatePushNickname(pushNickname).then(() => { // success logic }).catch((e: ChatError) => { @@ -19,7 +19,7 @@ ChatClient.getInstance().pushManager()?.updatePushNickname(pushNickname).then(() 你也可以调用 `updatePushDisplayStyle` 设置推送通知的显示样式,如下代码示例所示: -```TypeScript +```typescript // 设置为简单样式。 const displayStyle = PushDisplayStyle.SimpleBanner; ChatClient.getInstance().pushManager()?.updatePushDisplayStyle(displayStyle).then(() => { @@ -29,12 +29,11 @@ ChatClient.getInstance().pushManager()?.updatePushDisplayStyle(displayStyle).the }) ``` -`PushDisplayStyle` 是枚举类型。 - -| 参数 | 描述 | -| :--------------- | :---------------------- | -| `SimpleBanner` | 显示 “你有一条新消息”。 | -| `MessageSummary` | 显示消息内容。 | +| 参数设置 | 推送显示 | 图片 | +| :--------- | :----- |:------------- | +|
- `PushDisplayStyle`:(默认)`SimpleBanner`
- `nickname`:设置或不设置 |
- 推送标题:**您有一条新消息**
- 推送内容:**请点击查看** | ![img](/images/android/push/push_displayattribute_1.png)| +|
- `PushDisplayStyle`:`MessageSummary`
- `nickname`:设置具体值 |
- 推送标题:**您有一条新消息**
- 推送内容:**消息发送方的推送昵称:消息内容** |![img](/images/android/push/push_displayattribute_2.png) | +|
- `PushDisplayStyle`:`MessageSummary`
- `nickname`:不设置 |
- 推送标题:**您有一条新消息**
- 推送内容:**消息发送方的 IM 用户 ID: 消息内容** | ![img](/images/android/push/push_displayattribute_3.png)| ## 使用推送模板 @@ -80,7 +79,7 @@ ChatClient.getInstance().pushManager()?.updatePushDisplayStyle(displayStyle).the 这种情况下,创建消息时无需传入 `title_args` 和 `content_args` 参数。 -```TypeScript +```typescript // 下面以文本消息为例,其他类型的消息设置方法相同。 const message = ChatMessage.createTextSendMessage(conversationId, "消息内容"); if (message) { @@ -127,7 +126,7 @@ if (message) { 您收到了一条消息
请及时查看 -```TypeScript +```typescript // 下面以文本消息为例,其他类型的消息设置方法相同。 const message = ChatMessage.createTextSendMessage(conversationId, "消息内容"); if (message) { @@ -154,7 +153,7 @@ if (message) { 若发送方在发送消息时使用了推送模板,则推送通知栏中的显示内容以发送方的推送模板为准。 ::: -```TypeScript +```typescript ChatClient.getInstance().pushManager()?.setPushTemplate("自定义模板名称").then(() => { // success logic }).catch((e: ChatError) => { diff --git a/docs/document/harmonyos/push/push_harmony.md b/docs/document/harmonyos/push/push_harmony.md index 8d9c2d83..d075e708 100644 --- a/docs/document/harmonyos/push/push_harmony.md +++ b/docs/document/harmonyos/push/push_harmony.md @@ -41,7 +41,7 @@ > 配置 `client_id` 的 `value` 时,不能通过 `resource` 中的值配置(例如 `$media.icon`),请直接写入 `client_id` 的值。 -```TypeScript +```typescript "module": { "name": "entry", "type": "xxx", @@ -64,7 +64,7 @@ ## 步骤四 在 SDK 初始化时配置应用的推送 Client ID -```TypeScript +```typescript // ChatOptions 需要传入 appkey 参数。 let options = new ChatOptions("Your appKey"); // 传入 AppGallery Connect 获取到的 ClientID。 @@ -77,7 +77,7 @@ ChatClient.getInstance().init(context, options); 你可以设置 `PushListener` 监听 Push Token 的上传结果。 -```TypeScript +```typescript private pushListener: PushListener = { onError: (error: ChatError) => { // push token 绑定失败。 diff --git a/docs/document/harmonyos/push/push_notification_mode_dnd.md b/docs/document/harmonyos/push/push_notification_mode_dnd.md index 255fc3b3..21bc6195 100644 --- a/docs/document/harmonyos/push/push_notification_mode_dnd.md +++ b/docs/document/harmonyos/push/push_notification_mode_dnd.md @@ -60,7 +60,7 @@ 你可以调用 `PushManager#syncConversationsSilentModeFromServer` 方法从服务器同步所有会话的推送通知方式设置。同步后成功后的结果会存储到本地数据库,然后你可以通过`Conversation#pushRemindType` 查询当前会话的推送通知方式。 -```TypeScript +```typescript // 从服务器同步会话的推送通知方式 ChatClient.getInstance().pushManager()?.syncConversationsSilentModeFromServer().then(() => { // success logic @@ -80,7 +80,7 @@ if (conversation) { 在本地调用 `PushManager#setSilentModeForConversation` 设置会话的推送通知方式,在多设备事件 `MultiDevicesListener#onConversationEvent` 里会回调当前操作,此时参数 `event` 的值为 `MultiDevicesEvent#CONVERSATION_MUTE_INFO_CHANGED`。 -```TypeScript +```typescript //对会话设置推送通知方式 const remindType: PushRemindType = PushRemindType.ALL; ChatClient.getInstance().pushManager()?.setSilentModeForConversation(conversationId, conversationType, remindType).then(result => { @@ -125,7 +125,7 @@ ChatClient.getInstance().addMultiDevicesListener(multiDevicesListener); 你可以调用 `setSilentModeForAll` 设置 app 级别的推送通知,并通过指定 `SilentModeParam` 字段设置推送通知方式和免打扰模式,如下代码示例所示: -```TypeScript +```typescript // 设置推送通知方式为 `MENTION_ONLY`。 const params: PushRemindType = PushRemindType.MENTION_ONLY; @@ -147,7 +147,7 @@ ChatClient.getInstance().pushManager()?.setSilentModeForAll(params).then(result 你可以调用 `getSilentModeForAll` 获取 app 级别的推送通知设置,如以下代码示例所示: -```TypeScript +```typescript ChatClient.getInstance().pushManager()?.getSilentModeForAll().then(result => { // success logic const remindType = result.getRemindType(); @@ -182,7 +182,7 @@ ChatClient.getInstance().pushManager()?.getSilentModeForAll().then(result => { 你可以调用 `setSilentModeForConversation` 设置指定会话的推送通知,并通过指定 `PushRemindType` 字段设置推送通知方式和免打扰模式,如以下代码示例所示: -```TypeScript +```typescript // 设置推送通知方式为 `MENTION_ONLY`。 const remindType: PushRemindType = PushRemindType.MENTION_ONLY; @@ -197,7 +197,7 @@ ChatClient.getInstance().pushManager()?.setSilentModeForConversation(conversatio 你可以调用 `getSilentModeForConversation` 获取指定会话的推送通知设置,如以下代码示例所示: -```TypeScript +```typescript ChatClient.getInstance().pushManager()?.getSilentModeForConversation(conversationId, conversationType).then(result => { // 获取会话是否设置了推送通知方式。 const enable = result.isConversationRemindTypeEnabled(); @@ -218,7 +218,7 @@ ChatClient.getInstance().pushManager()?.getSilentModeForConversation(conversatio 你可以调用 `getSilentModeForConversations` 获取多个会话的推送通知设置,如以下代码示例所示: -```TypeScript +```typescript ChatClient.getInstance().pushManager()?.getSilentModeForConversations(conversations).then(result => { // success logic }).catch((e: ChatError) => { @@ -232,7 +232,7 @@ ChatClient.getInstance().pushManager()?.getSilentModeForConversations(conversati 以下代码示例显示了如何清除会话的推送通知方式: -```TypeScript +```typescript // 清除指定会话的推送通知方式设置。清除后,该会话会采用 app 的设置。 ChatClient.getInstance().pushManager()?.clearRemindTypeForConversation(conversationId, conversationType).then(() => { // success logic diff --git a/docs/document/harmonyos/quickstart.md b/docs/document/harmonyos/quickstart.md index f01d287c..998b02d8 100644 --- a/docs/document/harmonyos/quickstart.md +++ b/docs/document/harmonyos/quickstart.md @@ -56,7 +56,7 @@ ] } } -```` +``` :::tip - 此配置需要将 `DevEco Studio` 升级到 `Beta2(5.0.3.502)` 及以上版本。 @@ -111,7 +111,7 @@ ### 1. SDK 初始化 -```TypeScript +```typescript let options = new ChatOptions("Your appkey"); ......// 其他 ChatOptions 配置。 // 初始化时传入上下文以及options @@ -122,13 +122,13 @@ ChatClient.getInstance().init(context, options); 测试期间,可以使用如下代码创建账户: -```TypeScript +```typescript ChatClient.getInstance().createAccount(userId, pwd).then(()=> { // success logic }); ``` -:::notice +:::tip 该注册模式为在客户端注册,主要用于测试,简单方便,但不推荐在正式环境中使用,需要在[环信控制台](https://console.easemob.com/user/login)中手动开通开放注册功能;正式环境中应使用服务器端调用 Restful API 注册,具体见[注册单个用户](/document/server-side/account_system.html#开放注册单个用户)。 ::: @@ -136,19 +136,19 @@ ChatClient.getInstance().createAccount(userId, pwd).then(()=> { 使用如下代码实现用户登录: -```TypeScript +```typescript ChatClient.getInstance().login(userId, pwd).then(() => { // success logic }) ``` -:::notice +:::tip 1. 除了注册监听器,其他的 SDK 操作均需在登录之后进行。 ::: ### 4. 发送一条单聊消息 -```TypeScript +```typescript // `content` 为要发送的文本内容,`toChatUsername` 为对方的账号。 let message = ChatMessage.createTextSendMessage(toChatUsername, content); if (!message) { diff --git a/docs/document/harmonyos/reaction.md b/docs/document/harmonyos/reaction.md index d190fbb5..d4cce98a 100644 --- a/docs/document/harmonyos/reaction.md +++ b/docs/document/harmonyos/reaction.md @@ -4,7 +4,7 @@ 环信即时通讯 IM 提供消息表情回复(下文统称 “Reaction”)功能。用户可以在单聊和群聊中对消息添加、删除表情。表情可以直观地表达情绪,利用 Reaction 可以提升用户的使用体验。同时在群组中,利用 Reaction 可以发起投票,根据不同表情的追加数量来确认投票。 -:::notice +:::tip 目前 Reaction 仅适用于单聊和群组。聊天室暂不支持 Reaction 功能。 ::: @@ -43,7 +43,7 @@ Reaction 场景示例如下: 示例代码如下: -```TypeScript +```typescript // 添加 Reaction。 ChatClient.getInstance().chatManager()?.addReaction(message.getMsgId(), reaction).then(()=> { // success logic @@ -70,7 +70,7 @@ ChatClient.getInstance().chatManager()?.addMessageListener(listener); 示例代码如下: -```TypeScript +```typescript // 添加 Reaction。 ChatClient.getInstance().chatManager()?.removeReaction(message.getMsgId(), reaction).then(()=> { // success logic @@ -95,7 +95,7 @@ ChatClient.getInstance().chatManager()?.addMessageListener(listener); 调用 `fetchReactions` 可以从服务器获取多条指定消息的 Reaction 概览列表,列表内容包含 Reaction 内容,添加或移除 Reaction 的用户数量,以及添加或移除 Reaction 的前三个用户的用户 ID。示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatManager()?.fetchReactions(msgIds, ChatType.Chat, groupId).then((result) => { // success logic }).catch((e: ChatError) => { @@ -107,7 +107,7 @@ ChatClient.getInstance().chatManager()?.fetchReactions(msgIds, ChatType.Chat, gr 调用 `fetchReactionDetail` 可以从服务器获取指定 Reaction 的详情,包括 Reaction 内容,添加或移除 Reaction 的用户数量以及添加或移除 Reaction 的全部用户列表。示例代码如下: -```TypeScript +```typescript let fetchReactionDetailParam: FetchReactionDetailParams = { messageId: messageId, reaction: reaction, diff --git a/docs/document/harmonyos/releasenote.md b/docs/document/harmonyos/releasenote.md index 5de78336..80a53de4 100644 --- a/docs/document/harmonyos/releasenote.md +++ b/docs/document/harmonyos/releasenote.md @@ -44,7 +44,7 @@ - `ChatOptions#setLoginCustomExt`:设置设备的扩展信息; - `ChatOptions#getLoginCustomExt`:获取设备的扩展信息。 - `ConnectionListener#onLogout(errorCode: number, info: LoginExtInfo)`:多设备登录场景下,若当前设备被新登录设备踢下线,被踢设备收到的事件中会携带新设备的扩展信息。 -- 新增[从服务器拉取离线消息的开始和结束的事件回调](overview.html#连接状态相关): `ConnectionListener#onOfflineMessageSyncStart` 和 `ConnectionListener#onOfflineMessageSyncFinish`。 +- 新增[从服务器拉取离线消息的开始和结束的事件回调](connection.html#监听连接状态): `ConnectionListener#onOfflineMessageSyncStart` 和 `ConnectionListener#onOfflineMessageSyncFinish`。 - 新增 `GroupManager#checkIfInGroupMutelist` 接口,可以[查看当前用户是否在群组禁言列表中](group_members.html#检查自己是否在禁言列表中)。 - 新增 [错误码 213 ChatError#USER_BIND_ANOTHER_DEVICE](error.html),用于当用户达到登录设备上线时,当前设备无法登录的场景。 - 在撤回消息的 `ChatMessageListener#onMessageRecalled` 事件中[返回被撤回的消息所属的会话 ID](message_recall.html#设置消息撤回监听)。 diff --git a/docs/document/harmonyos/room_attributes.md b/docs/document/harmonyos/room_attributes.md index 37f7a8d8..e7e25ca0 100644 --- a/docs/document/harmonyos/room_attributes.md +++ b/docs/document/harmonyos/room_attributes.md @@ -31,7 +31,7 @@ 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.fetchChatroomAnnouncement(chatRoomId).then(announcement => console.log(announcement)); ``` @@ -41,7 +41,7 @@ ChatClient.getInstance().chatroomManager()?.fetchChatroomAnnouncement(chatRoomId 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.changeChatroomAnnouncement(chatRoomId, announcement).then(chatroom => { // success logic }); @@ -53,7 +53,7 @@ ChatClient.getInstance().chatroomManager()?.changeChatroomAnnouncement(chatRoomI 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.changeChatroomName(chatRoomId, newName).then(chatroom => { // success logic }); @@ -65,7 +65,7 @@ ChatClient.getInstance().chatroomManager()?.changeChatroomName(chatRoomId, newNa 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.changeChatroomDescription(chatRoomId, newDescription).then(chatroom => { // success logic }); @@ -79,7 +79,7 @@ ChatClient.getInstance().chatroomManager()?.changeChatroomDescription(chatRoomId 示例代码如下: -```TypeScript +```typescript // 举例,如有'key1'和'key2'两个聊天室自定义 key 。 const keyArr = ['key1', 'key2']; ChatClient.getInstance().chatroomManager()?.fetchChatroomAttributes(chatroomId, keyArr).then(mapResult => { @@ -91,7 +91,7 @@ ChatClient.getInstance().chatroomManager()?.fetchChatroomAttributes(chatroomId, 如果不设置`keyArr` 参数,则表示获取聊天室所有自定义属性。 -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.fetchChatroomAttributes(chatroomId).then(mapResult => { // success logic }).catch((e: ChatError) => { @@ -105,7 +105,7 @@ ChatClient.getInstance().chatroomManager()?.fetchChatroomAttributes(chatroomId). 示例代码如下: -```TypeScript +```typescript let params: SetChatroomAttributeParams = { chatroomId: chatroomId,// 聊天室 ID attributeKeyOrMap: 'key',// 聊天室属性 key @@ -124,7 +124,7 @@ ChatClient.getInstance().chatroomManager()?.setChatroomAttributes(params).then(r 示例代码如下: -```TypeScript +```typescript let attributeMap = new Map(); attributeMap.set('key1', 'value1'); attributeMap.set('key2', 'value2'); @@ -156,7 +156,7 @@ ChatClient.getInstance().chatroomManager()?.setChatroomAttributes(params).then(r 示例代码如下: -```TypeScript +```typescript let params: RemoveChatroomAttributeParams = { chatroomId: chatroomId,// 聊天室 ID attributeKey: 'key',// 聊天室属性 key diff --git a/docs/document/harmonyos/room_manage.md b/docs/document/harmonyos/room_manage.md index 1c48390f..be5aa1b3 100644 --- a/docs/document/harmonyos/room_manage.md +++ b/docs/document/harmonyos/room_manage.md @@ -46,7 +46,7 @@ 示例代码如下: -```TypeScript +```typescript // 获取公开聊天室列表,每次最多可获取 1,000 个。 ChatClient.getInstance().chatroomManager()?.fetchPublicChatroomsFromServer(pageNumber, pageSize).then(roomArray => { // success logic @@ -60,7 +60,7 @@ ChatClient.getInstance().chatroomManager()?.joinChatroom(chatRoomId).then(room = 同时,你可以调用 `ChatroomManager#joinChatroom(roomId: string, leaveOtherRooms?: boolean, ext?: string)` 方法,设置加入聊天室时携带的扩展信息,并指定是否退出所有其他聊天室。调用该方法后,聊天室内其他成员会收到 `ChatroomListener#onMemberJoined(roomId: string, userId: string, ext?: string)` 回调,当用户加入聊天室携带了扩展信息时,聊天室内其他人可以在用户加入聊天室的回调中,获取到扩展信息。 -```TypeScript +```typescript const ext = "chatroom ext info"; const leaveOtherRooms = true; ChatClient.getInstance().chatroomManager()?.joinChatroom(chatroomId, leaveOtherRooms, ext).then(room => { @@ -77,7 +77,7 @@ ChatClient.getInstance().chatroomManager()?.joinChatroom(chatroomId, leaveOtherR 示例代码如下: -```TypeScript +```typescript // 注册聊天室回调 ChatClient.getInstance().chatroomManager()?.addListener(chatroomListener); // 移除聊天室回调 @@ -86,7 +86,7 @@ ChatClient.getInstance().chatroomManager()?.removeListener(chatroomListener); 具体事件如下: -```TypeScript +```typescript interface ChatroomListener{ /** * 有新成员加入聊天室。 diff --git a/docs/document/harmonyos/room_members.md b/docs/document/harmonyos/room_members.md index 9501c943..a7ca63a5 100644 --- a/docs/document/harmonyos/room_members.md +++ b/docs/document/harmonyos/room_members.md @@ -34,7 +34,7 @@ 示例代码如下: -```TypeScript +```typescript //cursor:从该游标位置开始取数据。首次调用 cursor 传空字符串,从最新数据开始获取。 //pageSize:每页期望返回的成员数,最大值为 1,000。 ChatClient.getInstance().chatroomManager()?.fetchChatroomMembers(chatroomId, cursor, pageSize).then(cursorResult => { @@ -50,7 +50,7 @@ ChatClient.getInstance().chatroomManager()?.fetchChatroomMembers(chatroomId, cur 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.leaveChatroom(chatRoomId).then(()=> { // success logic }); @@ -60,7 +60,7 @@ ChatClient.getInstance().chatroomManager()?.leaveChatroom(chatRoomId).then(()=> 示例代码如下: -```TypeScript +```typescript let options = new ChatOptions(); options.setDeleteMessagesOnLeaveChatroom(false); ``` @@ -77,7 +77,7 @@ options.setDeleteMessagesOnLeaveChatroom(false); 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.removeChatroomMembers(chatroomId, members).then(room => { // success logic }); @@ -104,7 +104,7 @@ ChatClient.getInstance().chatroomManager()?.removeChatroomMembers(chatroomId, me 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.blockChatroomMembers(chatroomId, members).then(room => { // success logic }); @@ -116,7 +116,7 @@ ChatClient.getInstance().chatroomManager()?.blockChatroomMembers(chatroomId, mem 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.unblockChatroomMembers(chatroomId, members).then(room => { // success logic }); @@ -128,7 +128,7 @@ ChatClient.getInstance().chatroomManager()?.unblockChatroomMembers(chatroomId, m 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.fetchChatroomBlocklist(chatroomId, pageNum, pageSize).then(blocklist => { // success logic }); @@ -146,7 +146,7 @@ ChatClient.getInstance().chatroomManager()?.fetchChatroomBlocklist(chatroomId, p 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.fetchChatroomWhitelist(chatroomId).then(whitelist => { // success logic }); @@ -156,7 +156,7 @@ ChatClient.getInstance().chatroomManager()?.fetchChatroomWhitelist(chatroomId).t 所有聊天室成员可以调用 `checkIfInChatRoomWhiteList` 方法检查自己是否在聊天室白名单中,示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.checkIfInWhitelist(chatroomId).then(inWhitelist => { // success logic }); @@ -168,7 +168,7 @@ ChatClient.getInstance().chatroomManager()?.checkIfInWhitelist(chatroomId).then( 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.addToChatroomWhitelist(chatroomId, members).then(room => { // success logic }); @@ -180,7 +180,7 @@ ChatClient.getInstance().chatroomManager()?.addToChatroomWhitelist(chatroomId, m 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.removeFromChatroomWhitelist(chatroomId, members).then(room => { // success logic }); @@ -192,13 +192,13 @@ ChatClient.getInstance().chatroomManager()?.removeFromChatroomWhitelist(chatroom 仅聊天室所有者和管理员可以调用 `ChatroomManager#muteChatroomMembers` 方法将指定成员添加至聊天室禁言列表,操作者外其他成员收到 `ChatroomListener#onMutelistAdded` 回调。 -:::notice +:::tip 聊天室所有者可禁言聊天室所有成员,聊天室管理员可禁言聊天室普通成员。 ::: 示例代码如下: -```TypeScript +```typescript // `duration`:禁言时间。传 -1 表示永久禁言。 ChatClient.getInstance().chatroomManager()?.muteChatroomMembers(chatroomId, members, duration).then(room => { // success logic @@ -209,13 +209,13 @@ ChatClient.getInstance().chatroomManager()?.muteChatroomMembers(chatroomId, memb 仅聊天室所有者和管理员可以调用 `ChatroomManager#unmuteChatroomMembers` 方法将成员移出聊天室禁言列表。被解除禁言后,其他成员收到 `ChatroomListener#onMutelistRemoved` 回调。 -:::notice +:::tip 聊天室所有者可对聊天室所有成员解除禁言,聊天室管理员可对聊天室普通成员解除禁言。 ::: 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.unmuteChatroomMembers(chatroomId, members).then(room => { // success logic }); @@ -227,7 +227,7 @@ ChatClient.getInstance().chatroomManager()?.unmuteChatroomMembers(chatroomId, me 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.fetchChatroomMutes(chatroomId, pageNum, pageSize).then(result => { // success logic }); @@ -245,7 +245,7 @@ ChatClient.getInstance().chatroomManager()?.fetchChatroomMutes(chatroomId, pageN 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.muteAllMembers(chatroomId).then(room => { // success logic }); @@ -257,7 +257,7 @@ ChatClient.getInstance().chatroomManager()?.muteAllMembers(chatroomId).then(room 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.unmuteAllMembers(chatroomId).then(room => { // success logic }); @@ -271,7 +271,7 @@ ChatClient.getInstance().chatroomManager()?.unmuteAllMembers(chatroomId).then(ro 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.changeChatroomOwner(chatroomId, newOwner).then(room => { // success logic }); @@ -283,7 +283,7 @@ ChatClient.getInstance().chatroomManager()?.changeChatroomOwner(chatroomId, newO 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.addChatroomAdmin(chatroomId, admin).then(room => { // success logic }); @@ -295,7 +295,7 @@ ChatClient.getInstance().chatroomManager()?.addChatroomAdmin(chatroomId, admin). 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().chatroomManager()?.removeChatroomAdmin(chatroomId, admin).then(room => { // success logic }); diff --git a/docs/document/harmonyos/user_relationship.md b/docs/document/harmonyos/user_relationship.md index b26501a6..fe219eda 100644 --- a/docs/document/harmonyos/user_relationship.md +++ b/docs/document/harmonyos/user_relationship.md @@ -40,7 +40,7 @@ SDK 提供用户关系管理功能,包括好友列表管理和黑名单管理 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.addContact(toAddUsername, reason); ``` @@ -48,7 +48,7 @@ ChatClient.getInstance().contactManager()?.addContact(toAddUsername, reason); 请监听与好友请求相关事件的回调,这样当用户收到好友请求,可以调用接受请求的 RESTful API 添加好友。服务器不会重复下发与好友请求相关的事件,建议退出应用时保存相关的请求数据。设置监听示例代码如下: -```TypeScript +```typescript let contactListener: ContactListener = { onContactAdded : (userId: string) => { // 增加联系人时回调此方法。 @@ -71,7 +71,7 @@ ChatClient.getInstance().contactManager()?.addContactListener(contactListener); 3. 收到好友请求后,可以选择同意加好友申请或者拒绝加好友申请,示例代码如下: -```TypeScript +```typescript // 同意好友申请。 ChatClient.getInstance().contactManager()?.acceptInvitation(userId).then(()=>{ // success logic @@ -90,7 +90,7 @@ ChatClient.getInstance().contactManager()?.declineInvitation(userId).then(()=>{ 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.deleteContact(userId, isKeepConversation).then(()=> { // success logic }); @@ -104,7 +104,7 @@ ChatClient.getInstance().contactManager()?.deleteContact(userId, isKeepConversat 好友备注的长度不能超过 100 个字符。 -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.setContactRemark(userId, remark).then(()=> { // success logic }); @@ -116,7 +116,7 @@ ChatClient.getInstance().contactManager()?.setContactRemark(userId, remark).then - 一次性获取服务端好友列表。 -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.fetchAllContactsFromServer().then(contacts => { // success logic }); @@ -124,7 +124,7 @@ ChatClient.getInstance().contactManager()?.fetchAllContactsFromServer().then(con - 分页获取服务端好友列表。 -```TypeScript +```typescript // limit 的取值范围为 [1,50] let contacts = new Array(); let cursor = ""; @@ -148,7 +148,7 @@ private doAsyncFetchAllContactsFromServer(contacts: Array, cursor: stri 此外,你也可以调用 `fetchAllContactsIDFromServer` 方法从服务器获取所有好友的列表,该列表只包含好友的用户 ID。 -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.fetchAllContactsIDFromServer().then(result => { // success logic }); @@ -164,7 +164,7 @@ ChatClient.getInstance().contactManager()?.fetchAllContactsIDFromServer().then(r - 一次性获取本地好友列表。 -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.getContactsFromLocal().then(result => { // success logic }); @@ -174,7 +174,7 @@ ChatClient.getInstance().contactManager()?.getContactsFromLocal().then(result => 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.allContacts().then(result => { // success logic }); @@ -190,7 +190,7 @@ ChatClient.getInstance().contactManager()?.allContacts().then(result => { 用户可以将任何其他用户添加到黑名单列表,无论该用户是否是好友。好友被加入黑名单后仍在好友列表上显示。 -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.addUsersToBlocklist(userIds).then(()=> { // success logic }); @@ -200,7 +200,7 @@ ChatClient.getInstance().contactManager()?.addUsersToBlocklist(userIds).then(()= 你可以调用 `removeUserFromBlockList` 将用户从黑名单移除,用户发送消息等行为将恢复。 -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.removeUserFromBlockList(userId).then(()=> { // success logic }); @@ -210,7 +210,7 @@ ChatClient.getInstance().contactManager()?.removeUserFromBlockList(userId).then( 你可以调用 `getBlockListFromServer` 从服务端获取黑名单列表。示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.getBlockListFromServer().then(result => { // success logic }); @@ -222,7 +222,7 @@ ChatClient.getInstance().contactManager()?.getBlockListFromServer().then(result 示例代码如下: -```TypeScript +```typescript ChatClient.getInstance().contactManager()?.blockList().then(result => { // success logic }); diff --git a/docs/document/harmonyos/userprofile.md b/docs/document/harmonyos/userprofile.md index 456757cf..11500616 100644 --- a/docs/document/harmonyos/userprofile.md +++ b/docs/document/harmonyos/userprofile.md @@ -8,7 +8,7 @@ 本文介绍如何通过管理用户属性设置、更新、存储并获取实时消息用户的相关信息。 -:::notice +:::tip 为保证用户信息安全,SDK 仅支持用户设置或更新自己的用户属性。 ::: @@ -36,7 +36,7 @@ 参考如下示例代码,在你的项目中当前用户设置自己的所有属性或者仅设置某一项属性。 -```TypeScript +```typescript // 设置所有用户属性。 let userInfo: UserInfo = { nickname: "easemob", @@ -85,7 +85,7 @@ ChatClient.getInstance().userInfoManager()?.updateUserInfo(userInfo).then(result 示例代码如下: -```TypeScript +```typescript // 获取一个或多个用户的所有属性,一次调用用户 ID 数量不超过 100。 let userIds = new Array(); userIds.push(this.userId); @@ -100,7 +100,7 @@ ChatClient.getInstance().userInfoManager()?.fetchUserInfoById(userIds).then(resu 用户可以从服务端获取指定用户的指定用户属性信息。 -```TypeScript +```typescript let userIds = new Array(); userIds.push(this.userId); let userTypes = new Array(); diff --git a/docs/document/ios/connection.md b/docs/document/ios/connection.md new file mode 100644 index 00000000..df358ead --- /dev/null +++ b/docs/document/ios/connection.md @@ -0,0 +1,54 @@ +# 连接 + +应用客户端成功连接到环信服务器后,才能使用环信即时通讯 SDK 的收发消息等功能。 + +你调用 `login` 方法登录后,客户端 SDK 会自动连接环信服务器。关于登录详情,请参见[登录文档](login.html)。 + +## 监听连接状态 + +你可以通过注册连接监听确认连接状态。 + +```swift +override func viewDidLoad() { + ... + // 注册连接状态监听,在 SDK 初始化之后调用。 + EMClient.shared().add(self, delegateQueue: nil) + ... +} + +extension ViewController: EMClientDelegate { + // 连接成功,开始从服务器拉取离线消息时触发。 + // 注意:如果本次登录服务器没有离线消息,不会触发该回调。 + func onOfflineMessageSyncStart() { + } + // 离线用户上线后从服务器拉取离线消息结束时触发。 + // 注意:如果再拉取离线过程中因网络或其他原因导致连接断开,不会触发该回调。 + func onOfflineMessageSyncFinish() { + } + + // 连接状态变更时触发该回调 + func connectionStateDidChange(_ aConnectionState: EMConnectionState) { + + } + + // token 已过期 + func tokenDidExpire(_ aErrorCode: EMErrorCode) { + + } +} + +``` + +## 自动重连 + +登录后,如果由于网络信号弱、切换网络等引起的连接中断,SDK 会自动尝试重连。重连成功或者失败时会收到 `connectionStateDidChange` 通知。 + +不过,SDK 在以下情况下会停止自动重连。你需要调用 `login` 方法登录。 + +- 用户调用了 SDK 的登出方法 `logout` 主动退出登录。 +- 登录时鉴权错误,例如, token 无效(错误码 104)或已过期(错误码 108)。 +- 用户在其他的设备上更改了密码,导致此设备上自动登录失败,提示错误码 216。 +- 用户的账号被从服务器端删除,提示错误码 207。 +- 用户在另一设备登录,将当前设备上登录的用户踢出,提示错误码 206。 +- 用户登录设备数量超过限制,提示错误码 214。 +- 应用程序的日活跃用户数量(DAU)或月活跃用户数量(MAU)达到上限,提示错误码 8。 \ No newline at end of file diff --git a/docs/document/ios/conversation_list.md b/docs/document/ios/conversation_list.md index 5551ea11..2103d704 100644 --- a/docs/document/ios/conversation_list.md +++ b/docs/document/ios/conversation_list.md @@ -54,7 +54,7 @@ NSString *cursor = @""; 若使用该功能,需将 SDK 升级至 4.6.0。 ::: -```Swift +```swift EMClient.shared().chatManager?.filterConversationsFromDB(cleanMemoryCache: true, filter: { conversation in //case1: 判断 ext 中是否含有某个 key 然后返回 `true` or `false` //case2: 根据会话类型返回 Boolean 值 @@ -102,7 +102,7 @@ SDK 初始化时,你可以设置 `EMOptions#autoLoadConversations` 属性, 你可以调用 `cleanConversationsMemoryCache` 方法,清除本地内存中的所有会话,从而释放内存。 -```Swift +```swift EMClient.shared().chatManager?.cleanConversationsMemoryCache() ``` @@ -114,7 +114,7 @@ EMClient.shared().chatManager?.cleanConversationsMemoryCache() 2. 获取会话前清空内存中的会话:调用 `filterConversationsFromDB` 获取过滤的会话时,将 `cleanMemoryCache` 参数传 `true`。 3. 监控到内存较高时(开发者自己实现)释放内存: 调用 `cleanConversationsMemoryCache` 方法清空内存中的会话,释放内存。 -```Swift +```swift //step 1:SDK 初始化时,关闭自动加载会话开关。 option.autoLoadConversations = false diff --git a/docs/document/ios/conversation_mark.md b/docs/document/ios/conversation_mark.md index 7abc6ebe..0e885623 100644 --- a/docs/document/ios/conversation_mark.md +++ b/docs/document/ios/conversation_mark.md @@ -8,7 +8,7 @@ 你需要自行维护会话标记与具体业务含义(比如 `EMMarkType0` 为重要会话)之间的映射关系。例如: -```Objective-C +```objectivec NSDictionary* mapping = @{ @(EMMarkType0): @"important", @(EMMarkType1): @"normal", @@ -48,7 +48,7 @@ NSDictionary* mapping = @{ 对会话添加标记,例如会话标星,并不影响会话的其他逻辑,例如会话的未读消息数。 ::: -```Objective-C +```objectivec [EMClient.sharedClient.chatManager addConversationMark:@[@"conversationId1"] mark:EMMarkType0 completion:^(EMError * _Nullable aError) { }]; @@ -60,7 +60,7 @@ NSDictionary* mapping = @{ 调用该方法会同时移除本地和服务器端会话的标记。 -```Objective-C +```objectivec [EMClient.sharedClient.chatManager removeConversationMark:@[@"conversationId1"] mark:EMMarkType0 completion:^(EMError * _Nullable aError) { }]; @@ -71,7 +71,7 @@ NSDictionary* mapping = @{ 你可以调用 `getConversationsFromServerWithCursor` 方法根据会话标记从服务器分页获取会话列表。SDK 会按会话标记的时间的倒序返回会话列表,每个会话对象中包含会话 ID、会话类型、是否为置顶状态、置顶时间(对于未置顶的会话,值为 `0`)、会话标记以及最新一条消息。从服务端拉取会话列表后会更新本地会话列表。 -```Objective-C +```objectivec //cursor:查询的开始位置。若传入空字符串,SDK 从最新标记操作的会话开始获取。 // filter:会话查询选项,包括会话标记和每页获取的会话条数(最多可获取 10 条)。 EMConversationFilter* filter = [[EMConversationFilter alloc] initWithMark:EMMarkType0 pageSize:10]; @@ -84,7 +84,7 @@ EMConversationFilter* filter = [[EMConversationFilter alloc] initWithMark:EMMark 对于本地会话,你可以调用 `getAllConversations` 方法获取本地所有会话后自己进行会话过滤。下面以查询标记了 `EMMarkType0` 的所有本地会话为例。 -```Objective-C +```objectivec //最终的查询结果全部放入 result 中。 NSMutableArray* result = [NSMutableArray array]; NSArray* allConversations = [EMClient.sharedClient.chatManager getAllConversations]; @@ -99,7 +99,7 @@ for (EMConversation* conversation in allConversations) { 要获取本地单个会话的所有标记,你需要首先调用 `getConversationWithConvId` 方法获取本地单个会话,然后从会话的 `marks` 属性中查看该会话的所有标记,示例代码如下: -```Objective-C +```objectivec EMConversation* conversation = [EMClient.sharedClient.chatManager getConversationWithConvId:@"conversationId"]; NSArray* marks = conversation.marks; ``` diff --git a/docs/document/ios/conversation_overview.md b/docs/document/ios/conversation_overview.md index c4646737..cc4ddb0d 100644 --- a/docs/document/ios/conversation_overview.md +++ b/docs/document/ios/conversation_overview.md @@ -48,7 +48,7 @@ 示例代码如下: -```Objective-C +```objectivec // 收到会话已读的事件。该事件在以下场景中触发: // 1. 当消息接收方调用 `ackConversationRead()` 方法,SDK 会执行此回调, // 会将本地数据库中该会话中消息的 `isAcked` 属性置为 `true`。 diff --git a/docs/document/ios/conversation_receipt.md b/docs/document/ios/conversation_receipt.md index b46f10b7..94ef2147 100644 --- a/docs/document/ios/conversation_receipt.md +++ b/docs/document/ios/conversation_receipt.md @@ -16,6 +16,13 @@ 2. 消息接收方进入会话页面,阅读消息后,调用 `ackConversationRead` 方法发送会话已读回执; 3. 消息发送方通过监听 `onConversationRead` 回调接收会话已读回执。 +## 前提条件 + +开始前,请确保满足以下条件: + +- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 +- 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 + ## 实现方法 参考以下步骤在单聊中实现会话已读回执: diff --git a/docs/document/ios/easecallkit.md b/docs/document/ios/easecallkit.md index ae5d39e9..91043c6a 100644 --- a/docs/document/ios/easecallkit.md +++ b/docs/document/ios/easecallkit.md @@ -12,7 +12,7 @@ **`EaseCallKit` 在通话过程中,使用环信 ID 加入频道,方便音视频视图中显示用户名。如果用户不使用 `EaseCallKit` 而直接调用声网 API,也可以直接使用数字 UID 加入频道。** -:::notice +:::tip 本 UI 库只和移动端 3.8.0 以上版本 Demo 互通。3.8.1 的 UI 库使用声网数字 UID 加入频道,而 3.8.0 使用字符串加入频道,3.8.1 版本不与 3.8.0 互通,Demo 中 EaseCallKit 使用的 token 和 UID 均由你自己生成。若你需要使用声网对应的音视频服务,需单独在声网申请。 ::: diff --git a/docs/document/ios/group_manage.md b/docs/document/ios/group_manage.md index da5d5289..33f3dff9 100644 --- a/docs/document/ios/group_manage.md +++ b/docs/document/ios/group_manage.md @@ -94,13 +94,13 @@ NSArray *members = @{@"member1",@"member2"}; error:nil]; ``` -:::notice +:::tip 如果 `options.IsInviteNeedConfirm` 设置为 `false`,即直接加被邀请人进群。在此情况下,被邀请人设置非自动进群是不起作用的。 ::: ### 解散群组 -:::notice +:::tip 该操作只能群主才能进行。该操作是危险操作,解散群组后,将删除本地数据库及内存中的群相关信息及群会话。 ::: @@ -114,7 +114,7 @@ NSArray *members = @{@"member1",@"member2"}; ### 获取群组详情 -:::notice +:::tip 从 3.7.4 版本开始支持是否获取群组成员参数 `fetchMembers`。 对于公有群,用户即使不加入群也能获取群组详情,而对于私有群,用户只有加入了群组才能获取群详情。 ::: diff --git a/docs/document/ios/group_members.md b/docs/document/ios/group_members.md index 2c06c006..3e775ec5 100644 --- a/docs/document/ios/group_members.md +++ b/docs/document/ios/group_members.md @@ -52,7 +52,7 @@ 1. 调用 `getPublicGroupsFromServerWithCursor` 方法从服务器获取公开群列表,查询到想要加入的群组 ID。示例代码如下: -```objective-c +```objectivec NSMutableArray *memberList = [[NSMutableArray alloc]init]; NSInteger pageSize = 50; NSString *cursor = nil; @@ -73,7 +73,7 @@ do { - 如果公开群无需群主或管理员审批,即 `EMGroupStyle` 为 `EMGroupStylePublicOpenJoin`,申请人会直接加入群组,其他群成员会收到 `EMGroupManagerDelegate#userDidJoinGroup` 回调。 - 如果公开群需要群主或管理员审批,即 `EMGroupStyle` 为 `EMGroupStylePublicJoinNeedApproval`,申请人不会直接加入群组,群主和群管理员收到 `EMGroupManagerDelegate#joinGroupRequestDidReceive` 回调。待群主或管理员同意入群申请后,申请人会收到joinGroupRequestDidApprove的回调。示例代码如下: -```objective-c +```objectivec // 异步方法 [[EMClient sharedClient].groupManager requestToJoinPublicGroup:@"groupId" message:nil completion:^(EMGroup *aGroup1, EMError *aError) { if (aError == nil) { @@ -93,7 +93,7 @@ do { 示例代码如下: -```objective-c +```objectivec // 异步方法 [[EMClient sharedClient].groupManager approveJoinGroupRequest:@"groupId" sender:@"userId" completion:^(EMGroup *aGroup, EMError *aError) { @@ -104,7 +104,7 @@ do { 示例代码如下: -```objective-c +```objectivec // 异步方法 [[EMClient sharedClient].groupManager declineJoinGroupRequest:@"groupId" sender:@"userId" reason:@"reason" completion:^(EMGroup *aGroup, EMError *aError) { @@ -121,7 +121,7 @@ do { - 群主或群管理员可以邀请人入群,对于私有群 `EMGroupStyle` 设置为 `EMGroupStylePrivateMemberCanInvite` 时,普通群成员也可以邀请人进群。邀请人入群需要调用 `addMembers` 方法: - ```objective-c + ```objectivec // 异步方法 [[EMClient sharedClient].groupManager addMembers:@{@"member1",@"member2"} toGroup:@"groupID" @@ -133,14 +133,14 @@ do { - 受邀用户同意加入群组,需要调用 `acceptInvitationFromGroup` 方法。 - ```objective-c + ```objectivec [[EMClient sharedClient].groupManager acceptInvitationFromGroup:@"groupId" inviter:@"userId" completion:^(EMGroup *aGroup, EMError *aError) { }]; ``` - 受邀人拒绝入群组,需要调用 `declineGroupInvitation` 方法。 - ```objective-c + ```objectivec [[EMClient sharedClient].groupManager declineGroupInvitation:@"groupId" inviter:@"inviter" reason:@"reason" completion:^(EMError *aError) { }]; @@ -219,7 +219,7 @@ do { 你可调用 `EMGroupManager#fetchMembersAttributes` 方法根据指定的属性 key 获取多个群成员的自定义属性。 -:::notice +:::tip 每次最多可获取 10 个群成员的自定义属性。 ::: diff --git a/docs/document/ios/initialization.md b/docs/document/ios/initialization.md new file mode 100644 index 00000000..c1764cf1 --- /dev/null +++ b/docs/document/ios/initialization.md @@ -0,0 +1,19 @@ +# SDK 初始化 + +初始化是使用 SDK 的必要步骤,需在所有接口方法调用前完成。 + +如果进行多次初始化操作,只有第一次初始化以及相关的参数生效。 + +## 前提条件 + +有效的环信即时通讯 IM 开发者账号和 App key,详见[环信即时通讯云控制台的相关文档](/product/enable_and_configure_IM.html#创建应用)。 + +## 初始化 + +初始化示例代码: + +```swift +let options = EMOptions(appkey: "Your appkey") +......// 其他 EMOptions 配置。 +EMClient.shared().initializeSDK(with: options) +``` diff --git a/docs/document/ios/integration.md b/docs/document/ios/integration.md new file mode 100644 index 00000000..25e8f743 --- /dev/null +++ b/docs/document/ios/integration.md @@ -0,0 +1,46 @@ +# 集成 SDK + +本文介绍如何将环信即时通讯 IM SDK 集成到你的 iOS 项目。 + +## 开发环境要求 + +- Xcode (推荐最新版本)。 +- 安装 iOS 10.0 或更高版本的 iOS 模拟器或 Apple 设备。 +- CocoaPods 1.10.1 或更高版本。 + +## 集成 SDK + +选择如下任意一种方式将环信即时通讯 IM SDK 集成到你的项目中。 + +:::tip + +1. 以下集成方式只需选择一种,同时使用多种集成方式可能会报错。 +2. 请点击查看[发版说明](releasenote.html)获得最新版本号。 + +::: + +### 方法一:使用 Cocoapods 自动集成 + +1. 在 **Terminal** 里进入项目根目录,并运行 pod init 命令。项目文件夹下会生成一个 Podfile 文本文件。 +打开 **Podfile** 文件,修改文件为如下内容: + +```ruby +# platform :ios, '10.0' + + target 'EMChatQuickstart' do + pod 'HyphenateChat' + end + ``` + +2. 运行 `pod update` 命令更新本地库版本。 +3. 运行 `pod install` 命令安装 HyphenateChat SDK。成功安装后,**Terminal** 中会显示 Pod installation complete!,此时项目文件夹下会生成一个 `xcworkspace` 文件。 + +### 方法二:手动复制 SDK 文件 + +打开 SDK 下载页面,获取最新版的环信即时通讯 IM SDK SDK,然后解压。 + +将 SDK 包内的 `HyphenateChat.xcframework` 拖入到你的工程中。 + +:::tip +如果使用`4.11.0`及以上版本的SDK,需要将`aosl.xcframework`和`HyphenateChat.xcframework`同时导入到工程中。 +::: diff --git a/docs/document/ios/log.md b/docs/document/ios/log.md new file mode 100644 index 00000000..4db03b4a --- /dev/null +++ b/docs/document/ios/log.md @@ -0,0 +1,39 @@ +# SDK 日志 + +环信即时通讯 IM 日志记录 SDK 相关的信息和事件。环信技术支持团队帮你排查问题时可能会请你发送 SDK 日志。 + +## 输出信息到日志文件 + +默认情况下,SDK 最多可生成和保存三个文件,`easemob.log` 和两个 `easemob_YYYY-MM-DD_HH-MM-SS.log` 文件。这些文件为 UTF-8 编码,每个不超过 2 MB。SDK 会将最新的日志写入 `easemob.log` 文件,写满时则会将其重命名为对应时间点的 `easemob_YYYY-MM-DD_HH-MM-SS.log` 文件,若日志文件超过三个,则会删除最早的文件。 + +例如,SDK 在 2024 年 1 月 1 日上午 8:00:00 记录日志时会生成 `easemob.log` 文件,若在 8:30:00 将 `easemob.log` 文件写满则会将其重命名为 `easemob_2024-01-01_08-30-00.log` 文件,随后在 9:30:30 和 10:30:30 分别生成了 `easemob_2024-01-01_09-30-30.log` 和 `easemob_2024-01-01_10-30-30.log` 文件,则此时 `easemob_2024-01-01_08-30-00.log` 文件会被移除。 + +SDK 的 `EMOptions#logLevel` 指定了日志输出级别,默认为 `EMLogLevelDebug`,即所有等级日志。 + +- (默认)EMLogLevelDebug:所有等级的日志; +- EMLogLevelWarning:警告及错误; +- EMLogLevelError:错误。 + +开发阶段若希望在 XCode console 上输出 SDK 日志,可在 SDK 初始化时打开开关。 + +```objectivec +EMOptions* option = [EMOptions optionsWithAppkey:@"<#appkey#>"]; +// 日志输出到 XCode console +option.enableConsoleLog = YES; +// 调整日志输出级别,默认为所有级别。 +option.logLevel = EMLogLevelDebug; +[EMClient.sharedClient initializeSDKWithOptions:option]; +``` + +## 获取本地日志 + +SDK 会写入日志文件到本地。日志文件路径如下:沙箱 Library/Application Support/HyphenateSDK/easemobLog。 + +以真机为例,获取本地日志过程如下: + +- 打开 Xcode,连接设备,选择 **Xcode** > **Window** > **Devices and Simulators**。 +- 进入 **Devices** 选项卡,在左侧选择目标设备,例如 Easemob IM,点击设置图标,然后选择 **Download Container**。 + +![img](/images/ios/overview_fetchlogfile.png) + +日志文件 `easemob.log` 文件在下载包的 `AppData/Library/Application Support/HyphenateSDK/easemobLog` 目录下。 \ No newline at end of file diff --git a/docs/document/ios/login.md b/docs/document/ios/login.md new file mode 100644 index 00000000..b40d3185 --- /dev/null +++ b/docs/document/ios/login.md @@ -0,0 +1,115 @@ +# 登录 + +初始化 IM SDK 后,你需要首先调用接口登录。登录成功后,才能使用 IM 的功能。 + +## 用户注册 + +用户注册模式分为以下两种: + +- 开放注册:一般在体验 Demo 和测试环境时使用,正式环境中不推荐使用该方式注册环信账号。要使用开放注册,需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**开放注册**。只有打开该开关,才能使用客户端或 [REST API](/document/server-side/account_system.html#开放注册单个用户)开放注册用户。 + +示例代码如下所示: + +```objectivec +// 异步方法 +[[EMClient sharedClient] registerWithUsername:@"username" + password:@"your password" + completion:^(NSString *aUsername, EMError *aError) { + }]; +``` + +- 授权注册:通过环信提供的 REST API 注册环信用户账号,注册后保存到你的服务器或返给客户端。要使用授权注册,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**授权注册**。相关的 REST API 介绍,详见[授权注册单个用户](/document/server-side/account_system.html#授权注册单个用户)和[批量授权注册用户](/document/server-side/account_system.html#批量授权注册用户)的接口介绍。 + +除此以外,可以在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建用户,详见[创建用户相关介绍](/product/enable_and_configure_IM.html#创建-im-用户)。 + +## 主动登录 + +1. **用户 ID + token** 是更加安全的登录方式。 + +使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 + +```swift +EMClient.shared().login(withUsername: "userId", token: "token") { userId, err in + if err == nil { + // 登录成功 + } else { + // 登录失败 + } +} +``` + +2. **用户 ID + 密码** 是传统的登录方式。用户名和密码均由你的终端用户自行决定,密码需要符合密码规则要求。 + +```objectivec + //SDK 初始化 `EMOptions` 时可以传入 `loginExtensionInfo` 属性投递给被踢下线的设备。该属性需要开启多设备登录的情况下才能生效。 + EMOptions *options = [EMOptions optionsWithAppkey:<#AppKey#>]; + options.loginExtensionInfo = @"you was kicked out by other device"; + [EMClient.sharedClient initializeSDKWithOptions:options]; +// 异步方法 +[[EMClient sharedClient] loginWithUsername:@"username" + password:@"your password" + completion:^(NSString *aUsername, EMError *aError) { + +}]; +``` + +## 自动登录 + +初始化时,你可以设置 `EMOptions#isAutoLogin` 选项确定是否自动登录。如果设置为自动登录,则登录成功之后,后续初始化 SDK 时会自动登录,并收到以下回调。 + +```swift +extension ViewController: EMClientDelegate { + func autoLoginDidCompleteWithError(_ aError: EMError?) { + + } +} +``` + +## 获取当前登录的用户 + +你可以调用 `EMClient.shared().currentUsername` 方法获取当前登录用户的用户 ID。 + +## 获取登录状态 + +你可以调用 `EMClient.shared().isLoggedIn` 方法获取当前用户的登录状态。 + +## 退出登录 + +你可以调用 `logout` 方法退出登录。退出登录后,你不会再收到其他用户发送的消息。 + +异步方法: + +```swift +EMClient.shared().logout(true) { aError in + if aError == nil { + // 退出成功 + } else { + // 退出失败 + } +} +``` + +:::tip + +1. 如果集成了 APNs 等第三方推送,`logout` 方法中 `aIsUnbindDeviceToken` 参数需设为 `true`,退出时会解绑设备 token,否则可能会出现退出了,还能收到消息推送通知的现象。 +有时可能会遇到网络问题而解绑失败,app 处理时可以弹出提示框让用户选择,是否继续退出(弹出框提示继续退出能收到消息的风险),如果用户选择继续退出,传 `false` 再调用 `logout` 方法退出成功。当然也可以失败后还是返回退出成功,然后在后台起个线程不断调用 `logout` 方法直到成功。这样存在风险,即用户杀掉了 app,网络恢复后用户还会继续收到消息。 + +2. 如果调用异步退出方法,在收到 `completion` 回调后才能去调用 IM 相关方法,例如 `login`。 +::: + +## 账号切换 + +若在 app 中从当前账号切换到其他账号,你需要首先调用 `logout` 方法登出,然后再调用 `login` 方法登录,此时`aIsUnbindDeviceToken`参数需设为`false`。 + +## 多设备登录 + +除了单端单设备登录,环信即时通讯 IM 支持同一账号在多端的多个设备上登录。多设备登录时,若同端设备数量超过限制,新登录的设备会将之前登录的设备踢下线。 + +关于多设备登录场景中的设备数量限制、互踢策略以及信息同步,详见[多设备登录文档](multi_device.html)。 + + +## 更多 + +### 登录被封禁账号的提示 + +在环信即时通讯控制台或调用 REST API 封禁用户账号后,若仍使用该账号登录,SDK会返回 "service is disabled"(305 错误), 可以根据用户这个返回值来进行相应的提示或者处理。 diff --git a/docs/document/ios/message_delete.md b/docs/document/ios/message_delete.md index 4556cd83..929c7cc9 100644 --- a/docs/document/ios/message_delete.md +++ b/docs/document/ios/message_delete.md @@ -54,7 +54,7 @@ 示例代码如下: -```Objectivec +```objectivec // 按时间删除消息 [self.conversation removeMessagesFromServerWithTimeStamp:message.timestamp completion:^(EMError * _Nullable aError) { diff --git a/docs/document/ios/message_deliver_only_online.md b/docs/document/ios/message_deliver_only_online.md index 06d764f9..3c6e6b95 100644 --- a/docs/document/ios/message_deliver_only_online.md +++ b/docs/document/ios/message_deliver_only_online.md @@ -27,7 +27,7 @@ 下面以发送文本消息为例进行说明: -```objective-c +```objectivec // 调用 initWithText 创建文本消息。`content` 为文本消息的内容。 EMTextMessageBody *textMessageBody = [[EMTextMessageBody alloc] initWithText:content]; // 消息接收方:单聊为对端用户的 ID,群聊为群组 ID。 diff --git a/docs/document/ios/message_modify.md b/docs/document/ios/message_modify.md index 0a6f12c5..5edbebab 100644 --- a/docs/document/ios/message_modify.md +++ b/docs/document/ios/message_modify.md @@ -35,7 +35,7 @@ 示例代码如下: -```ObjectiveC +```objectivec [EMClient.sharedClient.chatManager modifyMessage:model.message.messageId body:body completion:^(EMError * _Nullable error, EMChatMessage * _Nullable message) { if (!error) { //refresh UI diff --git a/docs/document/ios/message_receipt.md b/docs/document/ios/message_receipt.md index a4a9a459..cc52278e 100644 --- a/docs/document/ios/message_receipt.md +++ b/docs/document/ios/message_receipt.md @@ -38,8 +38,8 @@ - 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -- 在环信即时通讯云控制台开启群消息已读回执功能。 - +- 要使用群消息已读回执功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 + ## 实现方法 ### 单聊消息送达回执 diff --git a/docs/document/ios/message_search.md b/docs/document/ios/message_search.md index 844802b6..809e6d66 100644 --- a/docs/document/ios/message_search.md +++ b/docs/document/ios/message_search.md @@ -27,7 +27,7 @@ 你可以调用 `loadMessagesWithKeyword` 方法根据关键字搜索本地数据库中单个会话中指定用户发送的消息,示例代码如下: -```Objective-C +```objectivec // 同步方法,异步方法见[EMConversation loadMessagesWithKeyword:timestamp:count:fromUser:searchDirection:completion] EMConversation* conversation = [EMClient.sharedClient.chatManager getConversationWithConvId:@"conversationId"]; NSArray *messages = [conversation loadMessagesWithKeyword:@"keyword" timestamp:0 count:50 fromUser:nil searchDirection:EMMessageSearchDirectionDown]; @@ -41,7 +41,7 @@ NSArray *messages = [conversation loadMessagesWithKeyword:@"key 若使用该功能,需将 SDK 升级至 V4.4.0 或以上版本。 ::: -```Swift +```swift EMClient.shared().chatManager?.loadMessages(withKeyword: "keyword", timestamp: 0, count: 50, fromUser: "", searchDirection: .down, scope: .content, completion: { messages, aError in }) @@ -71,7 +71,7 @@ if let conversation = EMClient.shared().chatManager?.getConversationWithConvId(" 若使用该功能,需将 SDK 升级至 V4.7.0 或以上版本。 ::: -```Swift +```swift // count:要查询的消息条数。取值范围为 [1,400]。 // fromuser:会话中发送方的用户 ID。若传空字符串,搜索对发送方不限制。 EMClient.shared().chatManager?.searchMessages (withTypes: [NSNumber (integerLiteral: @@ -88,7 +88,7 @@ EMMessageBodyType.text.rawValue), NSNumber (integerLiteral: EMMessageBodyType.im 若使用该功能,需将 SDK 升级至 V4.7.0 或以上版本。 ::: -```Swift +```swift // count:要查询的消息条数。取值范围为 [1,400]。 // fromuser:当前会话中发送方的用户 ID。若传空字符串,搜索对发送方不限制。 EMConversation().searchMessages(withTypes: [NSNumber(integerLiteral: EMMessageBodyType.text.rawValue),NSNumber(integerLiteral: EMMessageBodyType.image.rawValue)], timestamp: -1, count: 20, fromUser: "send message user id", searchDirection: .up) { messages, error in diff --git a/docs/document/ios/message_send_receive.md b/docs/document/ios/message_send_receive.md index 044dce7c..044d904f 100644 --- a/docs/document/ios/message_send_receive.md +++ b/docs/document/ios/message_send_receive.md @@ -2,56 +2,28 @@ -登录即时通讯服务后,用户可以在单聊、群聊、聊天室中发送如下类型的消息: +环信即时通讯 IM iOS SDK 通过 `ChatManager` 类和 `EMChatMessage` 类实现文本、图片、音频、视频和文件等类型的消息的发送和接收。 -- 文字消息,包含超链接和表情消息。 -- 附件消息,包含图片、语音、视频及文件消息。 -- 位置消息。 -- 透传消息。 -- 自定义消息。 -- 合并消息。 -- 定向消息。 +- 对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。 -对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 +- 对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 -针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 - -本文介绍如何使用即时通讯 IM SDK 实现发送和接收这些类型的消息。 - -## 技术原理 - -环信即时通讯 IM iOS SDK 提供 `ChatManager` 类和 `EMChatMessage` 类,支持发送和接收消息,其中包含如下主要方法: - -- `sendMessage` 发送消息给某个用户,群组或者聊天室; -- `addMessageListener` 添加消息接收的回调通知; -- `ackConversationRead` 发送会话已读通知; -- `ackMessageRead` 发送指定消息已读的通知; - -消息的收发流程如下: - -1. 用户 A 发送一条消息到环信即时通讯 IM 消息服务器。 -2. 消息服务器投递消息给用户 B,用户 B 收到该消息。 - -![img](/images/android/sendandreceivemsg.png) +单聊、群组聊天和聊天室的消息发送控制,详见[消息发送控制](/product/product_message_overview.html#消息发送控制)文档。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html)。 +- 完成 SDK 初始化,详见 [初始化文档](initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -## 实现方法 +## 发送和接收文本消息 -### 发送文本消息 +1. 你可以利用 `EMChatMessage` 类构造一条消息,然后通过 `ChatManager` 将该消息发出。 -你可以利用 `EMChatMessage` 类构造一条消息,然后通过 `ChatManager` 将该消息发出。 +默认情况下,SDK 对单个用户发送消息的频率未做限制。如果你联系了环信商务设置了该限制,一旦在单聊、群聊或聊天室中单个用户的消息发送频率超过设定的上限,SDK 会上报错误,即错误码 509 `EMErrorMessageCurrentLimiting`。示例代码如下: -默认情况下,SDK 对单个用户发送消息的频率未做限制。如果你联系了环信商务设置了该限制,一旦在单聊、群聊或聊天室中单个用户的消息发送频率超过设定的上限,SDK 会上报错误,即错误码 509 `EMErrorMessageCurrentLimiting`。 - -示例代码: - -```objective-c +```objectivec // 调用 initWithText 创建文本消息。`content` 为文本消息的内容。 EMTextMessageBody *textMessageBody = [[EMTextMessageBody alloc] initWithText:content]; // 消息接收方,单聊为对端用户的 ID,群聊为群组 ID,聊天室为聊天室 ID。 @@ -68,22 +40,7 @@ message.chatType = EMChatTypeChatRoom; ``` -对于聊天室消息,可设置消息优先级。示例代码如下: - -```Objectivec -EMTextMessageBody* textBody = [[EMTextMessageBody alloc] initWithText:@"Hi"]; -EMChatMessage* message = [[EMChatMessage alloc] initWithConversationID:@"roomId" body:textBody ext:nil]; -message.chatType = EMChatTypeChatRoom; -// 聊天室消息的优先级。如果不设置,默认值为 `Normal`,即“普通”优先级。 -message.priority = EMChatRoomMessagePriorityHigh; -[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:nil]; -``` - -若初始化时打开了 `EMOptions#useReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。该属性只能在调用 `initializeSDKWithOptions` 时设置,而且 app 运行过程中不能修改该参数的设置。 - -### 接收消息 - -你可以用注册监听 `EMChatManagerDelegate` 接收消息。该 `EMChatManagerDelegate` 可以多次添加,请记得在不需要的时候移除 `Delegate`,如在`ViewController` `dealloc()` 时。 +2. 你可以用注册监听 `EMChatManagerDelegate` 接收消息。该 `EMChatManagerDelegate` 可以多次添加,请记得在不需要的时候移除 `Delegate`,如在 `ViewController` `dealloc()` 时。 在新消息到来时,你会收到 `messagesDidReceive` 的回调,消息接收时可能是一条,也可能是多条。你可以在该回调里遍历消息队列,解析并显示收到的消息。若在初始化时打开了 `EMOptions#includeSendMessageInMessageListener` 开关,则该回调中会返回发送成功的消息。 @@ -111,7 +68,7 @@ message.priority = EMChatRoomMessagePriorityHigh; } ``` -### 发送和接收附件类型的消息 +## 发送和接收附件类型的消息 除文本消息外,SDK 还支持发送附件类型消息,包括语音、图片、视频和文件消息。 @@ -121,22 +78,12 @@ message.priority = EMChatRoomMessagePriorityHigh; 2. 接收附件消息。SDK 自动下载语音消息,默认自动下载图片和视频的缩略图。若下载原图、视频和文件,需调用 `downloadAttachment` 方法。 3. 获取附件的服务器地址和本地路径。 -此外,发送附件类型消息时,可以在 progress 回调中获取附件上传的进度,以百分比表示,示例代码如下: - -```objectivec -// 发送消息时可以设置 completion 回调,在该回调中更新消息的显示状态。例如消息发送失败后的提示等等。 -[[EMClient sharedClient].chatManager sendMessage:message progress:^(int progress) { - // progress 为附件上传进度块的百分比。 -} completion:^(EMChatMessage *message, EMError *error) { - // error 为发送结果,message 为发送的消息。 -}]; -``` - -#### 发送和接收语音消息 +### 发送和接收语音消息 发送和接收语音消息的过程如下: 1. 发送语音消息前,在应用层录制语音文件。 + 2. 发送方调用 `initWithLocalPath` 和 `initWithConversationID` 方法传入语音文件的 URI、语音时长和接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID)创建语音消息,然后调用 `sendMessage` 方法发送消息。SDK 会将文件上传至环信服务器。 ```objectivec @@ -151,7 +98,7 @@ message.chatType = EMChatTypeGroupChat; 3. 接收方收到语音消息时,自动下载语音文件。 -4. 接收方收到 `messagesDidReceive` 回调,调用 `remotePath` 或 `localPath` 方法获取语音文件的服务器地址或本地路径,从而获取语音文件。 +4. 接收方收到 [messagesDidReceive 回调](#发送和接收文本消息),调用 `remotePath` 或 `localPath` 方法获取语音文件的服务器地址或本地路径,从而获取语音文件。 ```objectivec EMVoiceMessageBody *voiceBody = (EMVoiceMessageBody *)message.body; @@ -161,11 +108,11 @@ NSString *voiceRemotePath = voiceBody.remotePath; NSString *voiceLocalPath = voiceBody.localPath; ``` -#### 发送和接收图片消息 +### 发送和接收图片消息 发送和接收图片消息的流程如下: -1. 发送方调用 `initWithData` 和 `initWithConversationID` 方法传入图片的本地资源标志符 URI、设置是否发送原图以及接收方的用户 ID (群聊或聊天室分别为群组 ID 或聊天室 ID)创建图片消息,然后调用 `sendMessage` 方法发送该消息。SDK 会将图片上传至环信服务器,服务器自动生成图片缩略图。 +1. 发送方调用 `initWithData` 和 `initWithConversationID` 方法传入图片的本地资源标志符 URI、设置是否发送原图以及接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID)创建图片消息,然后调用 `sendMessage` 方法发送该消息。SDK 会将图片上传至环信服务器,服务器自动生成图片缩略图。 ```objectivec // `imageData` 为图片本地资源,`displayName` 为附件的显示名称。 @@ -193,9 +140,10 @@ NSString *thumbnailLocalPath = body.thumbnailLocalPath; 2. 接收方收到图片消息,自动下载图片缩略图。 -SDK 默认自动下载缩略图,即 `[EMClient sharedClient].options.isAutoDownloadThumbnail;` 为 `YES`。若设置为手动下载缩略图,即 `[EMClient sharedClient].options.isAutoDownloadThumbnail(NO);`,需调用 `[[EMClient sharedClient].chatManager downloadMessageThumbnail:message progress:nil completion:nil];` 下载。 +- 默认情况下,SDK 自动下载缩略图,即 `[EMClient sharedClient].options.isAutoDownloadThumbnail;` 为 `YES`。 +- 若设置为手动下载缩略图,即 `[EMClient sharedClient].options.isAutoDownloadThumbnail(NO);`,需调用 `[[EMClient sharedClient].chatManager downloadMessageThumbnail:message progress:nil completion:nil];` 下载。 -3. 接收方收到 `messagesDidReceive` 回调,调用 `downloadMessageAttachment` 下载原图。 +3. 接收方收到 [messagesDidReceive 回调](#发送和接收文本消息),调用 `downloadMessageAttachment` 下载原图。 下载完成后,在回调里调用相应消息 `body` 的 `thumbnailLocalPath` 获取缩略图路径。 @@ -216,7 +164,7 @@ NSString *thumbnailLocalPath = imageBody.thumbnailLocalPath; }]; ``` -#### 发送和接收视频消息 +### 发送和接收视频消息 发送和接收视频消息的流程如下: @@ -236,11 +184,9 @@ message.chatType = EMChatTypeGroupChat; [[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:nil]; ``` -3. 接收方收到视频消息时,自动下载视频缩略图。 +3. 接收方收到视频消息时,自动下载视频缩略图。你可以设置自动或手动下载视频缩略图,该设置与图片缩略图相同,详见[设置图片缩略图自动下载](#发送和接收图片消息)。 -SDK 默认自动下载缩略图,即 `[EMClient sharedClient].options.isAutoDownloadThumbnail;` 为 `YES`。若设置为手动下载缩略图,即 `[EMClient sharedClient].options.isAutoDownloadThumbnail(NO);`,需调用 `[[EMClient sharedClient].chatManager downloadMessageThumbnail:message progress:nil completion:nil];` 下载。 - -4. 接收方收到 `messagesDidReceive` 回调,可以调用 `downloadMessageAttachment` 方法下载视频原文件。 +4. 接收方收到 [messagesDidReceive 回调](#发送和接收文本消息),可以调用 `downloadMessageAttachment` 方法下载视频原文件。 5. 获取视频缩略图和视频原文件。 @@ -257,7 +203,7 @@ NSString *localPath = body.localPath; NSString *thumbnailLocalPath = body.thumbnailLocalPath; ``` -#### 发送和接收文件消息 +### 发送和接收文件消息 发送和接收文件消息的流程如下: @@ -273,7 +219,7 @@ message.chatType = EMChatTypeGroupChat; [[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:nil]; ``` -2. 接收方收到 `messagesDidReceive` 回调,调用 `downloadMessageAttachment` 方法下载文件。 +2. 接收方收到 [messagesDidReceive 回调](#发送和接收文本消息),调用 `downloadMessageAttachment` 方法下载文件。 ```objectivec [[EMClient sharedClient].chatManager downloadMessageAttachment:message progress:nil completion:^(EMChatMessage *message, EMError *error) { @@ -293,9 +239,11 @@ NSString *remotePath = body.remotePath; NSString *localPath = body.localPath; ``` -### 发送位置消息 +## 发送和接收位置消息 -当你需要发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 +1. 创建和发送位置消息。 + +发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。 ```objectivec // `latitude` 为纬度,`longitude` 为经度,`address` 为具体位置内容。 @@ -308,16 +256,25 @@ message.chatType = EMChatTypeGroupChat; [[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:nil]; ``` -### 发送透传消息 +2. 接收位置消息与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + + 接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 + +## 发送和接收透传消息 -可以把透传消息理解为一条指令,通过发送这条指令给对方,通知对方要执行的操作,收到消息可以自定义处理。(透传消息不会存入本地数据库中,所以在 UI 上不会显示)。具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 “em_” 和 “easemob::” 开头的 `action` 为内部保留字段,注意不要使用。 +可将透传消息理解为一条指令,通过发送这条指令给对方,通知对方要执行的操作,收到消息可以自定义处理。 + +具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 `em_` 和 `easemob::` 开头的 `action` 为内部保留字段,注意不要使用。 透传消息适用于更新头像、更新昵称等场景。 :::tip -透传消息发送后,不支持撤回。 +- 透传消息发送后,不支持撤回。 +- 透传消息不会存入本地数据库中,所以在 UI 上不会显示。 ::: +1. 创建和发送透传消息。 + ```objectivec // `action` 自定义 `NSString` 类型的命令内容。 EMCmdMessageBody *body = [[EMCmdMessageBody alloc] initWithAction:action]; @@ -331,7 +288,7 @@ EMCmdMessageBody *body = [[EMCmdMessageBody alloc] initWithAction:action]; [[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:nil]; ``` -请注意透传消息的接收方,也是由单独的回调进行通知,方便用户进行不同的处理。 +2. 接收方通过 `cmdMessagesDidReceive` 回调接收透传消息,方便用户进行不同的处理。 ```objectivec // 收到透传消息。 @@ -343,110 +300,12 @@ EMCmdMessageBody *body = [[EMCmdMessageBody alloc] initWithAction:action]; } ``` -#### 通过透传消息实现输入指示器 - -输入指示器显示其他用户何时输入消息。通过该功能,用户之间可进行有效沟通,增加了用户对聊天应用中交互的期待感。 - -你可以通过透传消息实现输入指示器。 - -下图为输入指示器的工作原理。 - -![img](/images/common/typing_indicator.png) - -监听用户 A 的输入状态。一旦有文本输入,通过透传消息将输入状态发送给用户 B,用户 B 收到该消息,了解到用户 A 正在输入文本。 - -- 用户 A 向用户 B 发送消息,通知其开始输入文本。 -- 收到消息后,如果用户 B 与用户 A 的聊天页面处于打开状态,则显示用户 A 的输入指示器。 -- 如果用户 B 在几秒后未收到用户 A 的输入,则自动取消输入指示器。 - -:::tip -用户 A 可根据需要设置透传消息发送间隔。 -::: - -以下示例代码展示如何发送输入状态的透传消息。 - -```objectivec -//发送表示正在输入的透传消息 -#define MSG_TYPING_BEGIN @"TypingBegin" - -- (void)textViewDidChange:(UITextView *)textView -{ - long long currentTimestamp = [self getCurrentTimestamp]; - // 5 秒内不能重复发送消息 - if ((currentTimestamp - _previousChangedTimeStamp) > 5) { - // 发送开始输入的透传消息 - [self _sendBeginTyping]; - _previousChangedTimeStamp = currentTimestamp; - } -} - -- (void)_sendBeginTyping -{ - EMCmdMessageBody *body = [[EMCmdMessageBody alloc] initWithAction:MSG_TYPING_BEGIN]; - body.isDeliverOnlineOnly = YES; - EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId body:body ext:nil]; - [[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:nil]; -} -``` - -以下示例代码展示如何接受和解析输入状态的透传消息。 - -```objectivec -#define TypingTimerCountNum 10 -- (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages -{ - NSString *conId = self.currentConversation.conversationId; - for (EMChatMessage *message in aCmdMessages) { - if (![conId isEqualToString:message.conversationId]) { - continue; - } - EMCmdMessageBody *body = (EMCmdMessageBody *)message.body; - // 收到正在输入的透传消息 - if ([body.action isEqualToString:MSG_TYPING_BEGIN]) { - if (_receiveTypingCountDownNum == 0) { - [self startReceiveTypingTimer]; - }else { - _receiveTypingCountDownNum = TypingTimerCountNum; - } - } - - } -} - -- (void)startReceiveTypingTimer { - [self stopReceiveTypingTimer]; - _receiveTypingCountDownNum = TypingTimerCountNum; - _receiveTypingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(startReceiveCountDown) userInfo:nil repeats:YES]; - - [[NSRunLoop currentRunLoop] addTimer:_receiveTypingTimer forMode:UITrackingRunLoopMode]; - [_receiveTypingTimer fire]; - // 这里需更新 UI,显示“对方正在输入” -} - -- (void)startReceiveCountDown -{ - if (_receiveTypingCountDownNum == 0) { - [self stopReceiveTypingTimer]; - // 这里需更新 UI,不再显示“对方正在输入” - - return; - } - _receiveTypingCountDownNum--; -} - -- (void)stopReceiveTypingTimer { - _receiveTypingCountDownNum = 0; - if (_receiveTypingTimer) { - [_receiveTypingTimer invalidate]; - _receiveTypingTimer = nil; - } -} -``` - -### 发送自定义类型消息 +## 发送和接收自定义类型消息 除了几种消息之外,你可以自己定义消息类型,方便业务处理,即首先设置一个消息类型名称,然后可添加多种自定义消息。 +1. 创建和发送自定义类型消息。 + ```objectivec // event 为需要传递的自定义消息事件,比如名片消息,可以设置 "userCard";`ext` 为事件扩展字段,比如可以设置 `uid`,`nickname`,`avatar`。 EMCustomMessageBody* body = [[EMCustomMessageBody alloc] initWithEvent:@"userCard" ext:@{@"uid":aUid ,@"nickname":aNickName,@"avatar":aUrl}]; @@ -458,7 +317,9 @@ message.chatType = EMChatTypeGroupChat; [[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:nil]; ``` -### 发送和接收合并消息 +2. 接收自定义消息与其他类型消息一致,详见[接收文本消息](#发送和接收文本消息)。 + +## 发送和接收合并消息 为了方便消息互动,即时通讯 IM 自 4.1.0 版本开始支持将多个消息合并在一起进行转发。你可以采取以下步骤进行消息的合并转发: @@ -492,7 +353,7 @@ message.chatType = EMChatTypeGroupChat; 示例代码如下: -```Objective-C +```objectivec EMCombineMessageBody* combineMsgBody = [[EMCombineMessageBody alloc] initWithTitle:@"combineTitle" summary:@"combineSummary" compatibleText:@"combineCompatibleText" messageIdList:@[@"messageId1",@"messageId2"]]; EMChatMessage* msg = [[EMChatMessage alloc] initWithConversationID:@"conversationId" body:combineMsgBody ext:nil]; [EMClient.sharedClient.chatManager sendMessage:msg progress:nil completion:^(EMChatMessage * _Nullable message, EMError * _Nullable error) { @@ -500,11 +361,11 @@ EMChatMessage* msg = [[EMChatMessage alloc] initWithConversationID:@"conversatio }]; ``` -接收合并消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +#### 接收和解析合并消息 -对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 +接收合并消息与接收普通消息的操作相同,详见[接收消息](#发送和接收文本消息)。 -#### 解析合并消息 +对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 合并消息实际上是一种附件消息。收到合并消息后,你可以调用 `downloadAndParseCombineMessage` 方法下载合并消息附件并解析出原始消息列表。 @@ -513,7 +374,7 @@ EMChatMessage* msg = [[EMChatMessage alloc] initWithConversationID:@"conversatio - 若附件已存在,该方法会直接解析附件并返回原始消息列表。 - 若附件不存在,该方法首先下载附件,然后解析附件并返回原始消息列表。 -```Objective-C +```objectivec - (void)messagesDidReceive:(NSArray *)aMessages { @@ -528,7 +389,7 @@ EMChatMessage* msg = [[EMChatMessage alloc] initWithConversationID:@"conversatio } ``` -### 发送和接收定向消息 +## 发送和接收定向消息 发送定向消息是指向群组或聊天室的单个或多个指定的成员发送消息,其他成员不会收到该消息。 @@ -563,9 +424,9 @@ msg.receiverList = @[@"A",@"B"]; }]; ``` -接收定向消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +接收定向消息与接收普通消息的操作相同,详见[接收文本消息](#发送和接收文本消息)。 -### 使用消息扩展字段 +## 使用消息扩展字段 当 SDK 提供的消息类型不满足需求时,你可以通过消息扩展字段来传递自定义的内容,从而生成自己需要的消息类型。 @@ -590,6 +451,43 @@ message.chatType = EMChatTypeChat; } ``` -## 注意事项 +## 更多 + +### 设置聊天室消息优先级 + +针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 + +对于聊天室消息,可设置消息优先级。示例代码如下: + +```objectivec +EMTextMessageBody* textBody = [[EMTextMessageBody alloc] initWithText:@"Hi"]; +EMChatMessage* message = [[EMChatMessage alloc] initWithConversationID:@"roomId" body:textBody ext:nil]; +message.chatType = EMChatTypeChatRoom; +// 聊天室消息的优先级。如果不设置,默认值为 `Normal`,即“普通”优先级。 +message.priority = EMChatRoomMessagePriorityHigh; +[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:nil]; +``` + +### 获取发送附件消息的进度 + +发送附件类型消息时,可以在 `progress` 回调中获取附件上传的进度,以百分比表示,示例代码如下: + +```objectivec +// 发送消息时可以设置 completion 回调,在该回调中更新消息的显示状态。例如消息发送失败后的提示等等。 +[[EMClient sharedClient].chatManager sendMessage:message progress:^(int progress) { + // progress 为附件上传进度块的百分比。 +} completion:^(EMChatMessage *message, EMError *error) { + // error 为发送结果,message 为发送的消息。 +}]; +``` + +### 发送消息前的内容审核 + +- 内容审核关注消息 body [内容审核服务会关注消息 body 中指定字段的内容,不同类型的消息审核不同的字段](/product/moderation/moderation_mechanism.html),若创建消息时在这些字段中传入了很多业务信息,可能会影响审核效果。因此,创建消息时需要注意内容审核的字段不涉及业务信息,建议业务信息放在扩展字段中。 + +- 设置发送方收到内容审核替换后的内容 + +若初始化时打开了 `EMOptions#useReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。该属性只能在调用 `initializeSDKWithOptions` 时设置,而且 app 运行过程中不能修改该参数的设置。 + diff --git a/docs/document/ios/message_translation.md b/docs/document/ios/message_translation.md index f54d9589..0dfa0a3d 100644 --- a/docs/document/ios/message_translation.md +++ b/docs/document/ios/message_translation.md @@ -8,7 +8,7 @@ - 自动翻译:发送方发送消息时,SDK 根据发送方设置的目标语言自动翻译文本内容,然后将消息原文和译文一起发送给接收方。 -:::notice +:::tip 翻译服务由 Microsoft Azure Translator API 提供支持。可以在 [Microsoft Azure Translator Language Support](https://docs.microsoft.com/en-us/azure/cognitive-services/translator/language-support) 页面查看目前支持的语言。 ::: diff --git a/docs/document/ios/multi_device.md b/docs/document/ios/multi_device.md index 7d7ef7d8..cdfed52c 100644 --- a/docs/document/ios/multi_device.md +++ b/docs/document/ios/multi_device.md @@ -97,7 +97,7 @@ iOS SDK 初始化时会生成登录 ID 用于在多设备登录和消息推送 初始化 SDK 时,你可以调用 `initializeSDKWithOptions` 方法时设置 `EMOptions#customDeviceName` 属性自定义登录设备的名称。设置设备名称后,若登录设备时因达到了登录设备数量限制而导致在已登录的设备上强制退出时,被踢设备收到的 `userAccountDidLoginFromOtherDevice` 回调里会包含导致该设备被踢下线的自定义设备名称。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -122,7 +122,7 @@ option.customDeviceName = @"XXX的iPad"; 2. 初始化 SDK 时,调用 `initializeSDKWithOptions` 方法设置 `EMOptions#customOSType` 属性添加自定义平台。确保该属性的值与环信控制台的**新增自定义平台**对话框中设置的**设备平台**的值相同。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -138,7 +138,7 @@ option.customOSType = 60; 初始化 SDK 时,可通过 `EMOptions#loginExtensionInfo` 属性设置设备扩展信息。设置后,多设备场景下,登录该设备后,若因达到了登录设备数量限制而导致当前登录设备被踢下线(`206` 错误,`EMErrorUserLoginOnAnotherDevice`),被踢设备收到的 `EMClientDelegate#userAccountDidLoginFromOtherDeviceWithInfo` 回调中会包含该设备的自定义扩展信息。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -155,7 +155,7 @@ EMClient.sharedClient.option.loginExtensionInfo = @"you was kicked out by other 你可以调用 `kickDeviceWithUsername` 方法通过传入用户 ID 和登录密码或用户 token 将指定账号从单个登录的设备踢下线。你需要首先调用 `getLoggedInDevicesFromServerWithUsername` 方法获取设备 ID。 -:::notice +:::tip 不登录也可以使用该接口。 ::: @@ -177,7 +177,7 @@ NSString *password = @""; 你可以调用 `kickAllDevicesWithUsername` 或 `kickAllDevicesWithUserId` 方法通过传入用户 ID 和登录密码或用户 token 将指定账号从所有登录设备都踢下线。 -:::notice +:::tip 不登录也可以使用该接口。 ::: diff --git a/docs/document/ios/overview.md b/docs/document/ios/overview.md index 307ba1db..45ca34bc 100644 --- a/docs/document/ios/overview.md +++ b/docs/document/ios/overview.md @@ -45,7 +45,7 @@ EMOptions *options = [EMOptions optionsWithAppkey:@"<#appkey#>"]; 若支持 SDK 注册,需登录[环信即时通讯云控制台](https://console.easemob.com/user/login),选择 **即时通讯** > **服务概览**,将 **设置**下的 **用户注册模式** 设置为 **开放注册**。 -```Objective-C +```objectivec // 异步方法 [[EMClient sharedClient] registerWithUsername:@"username" password:@"your password" @@ -53,7 +53,7 @@ EMOptions *options = [EMOptions optionsWithAppkey:@"<#appkey#>"]; }]; ``` -:::notice +:::tip 该注册模式为在客户端注册,旨在方便测试,并不推荐在正式环境中使用。 ::: @@ -74,7 +74,7 @@ EMOptions *options = [EMOptions optionsWithAppkey:@"<#appkey#>"]; **用户 ID + 密码** 是传统的登录方式。用户名和密码均由你的终端用户自行决定,密码需要符合密码规则要求。 -```Objective-C +```objectivec //SDK 初始化 `EMOptions` 时可以传入 `loginExtensionInfo` 属性投递给被踢下线的设备。该属性需要开启多设备登录的情况下才能生效。 EMOptions *options = [EMOptions optionsWithAppkey:<#AppKey#>]; options.loginExtensionInfo = @"you was kicked out by other device"; @@ -91,7 +91,7 @@ EMOptions *options = [EMOptions optionsWithAppkey:@"<#appkey#>"]; **用户 ID + token** 是更加安全的登录方式。token 可以通过调用 REST API 获取,详见 [环信用户 token 的获取](/document/server-side/easemob_user_token.html)。 -:::notice +:::tip 使用 token 登录时需要处理 token 过期的问题,比如每次登录时更新 token 等机制。 ::: diff --git a/docs/document/ios/privatecloud.md b/docs/document/ios/privatecloud.md index 0464f1e8..ec3d3cf1 100644 --- a/docs/document/ios/privatecloud.md +++ b/docs/document/ios/privatecloud.md @@ -16,7 +16,7 @@ options.rtcUrlDomain= "wss://mprtc.easemob.com";//设置多人音视频的服务 [[EMClient sharedClient] initializeSDKWithOptions:options]; ``` -:::notice +:::tip 如果需要配置 HTTPS 只需加一个属性即可。 ::: diff --git a/docs/document/ios/push/push_apns.md b/docs/document/ios/push/push_apns.md index 5434f612..86c3c992 100644 --- a/docs/document/ios/push/push_apns.md +++ b/docs/document/ios/push/push_apns.md @@ -142,7 +142,7 @@ ### **步骤二 将证书名称传递给 SDK** -```plaintext +```objectivec - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // 注册推送。 [application registerForRemoteNotifications]; @@ -164,7 +164,7 @@ DeviceToken 注册后,iOS 系统会通过以下方式将 DeviceToken 回调给你,你需要把 DeviceToken 传给 SDK。 -``` +```objectivec - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { // 异步方法 [EMClient.sharedClient registerForRemoteNotificationsWithDeviceToken:deviceToken completion:^(EMError *aError) { diff --git a/docs/document/ios/push/push_display.md b/docs/document/ios/push/push_display.md index 6222947a..d31813cd 100644 --- a/docs/document/ios/push/push_display.md +++ b/docs/document/ios/push/push_display.md @@ -187,7 +187,7 @@ message.chatType = EMChatTypeChat; 若发送方在发送消息时使用了推送模板,则推送通知栏中的显示内容以发送方的推送模板为准。 ::: -```objective-C +```objectivec [EMClient.sharedClient.pushManager setPushTemplate:@"templateName" completion:^(EMError * _Nullable aError) { }]; @@ -197,7 +197,7 @@ message.chatType = EMChatTypeChat; 创建推送消息时,你可以设置消息扩展字段自定义要显示的推送标题 `em_push_title` 和推送内容 `em_push_content`。 -```plaintext +```objectivec EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"test"]; EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId from:currentUsername to:conversationId body:body ext:nil]; message.ext = @{@"em_apns_ext":@{ @@ -223,7 +223,7 @@ message.chatType = EMChatTypeChat; **解析的内容** -```plaintext +```json { "aps":{ "alert":{ diff --git a/docs/document/ios/push/push_extension.md b/docs/document/ios/push/push_extension.md index 7d078704..8c639903 100644 --- a/docs/document/ios/push/push_extension.md +++ b/docs/document/ios/push/push_extension.md @@ -6,7 +6,7 @@ ## 自定义推送字段 -```plaintext +```objectivec EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"test"]; EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId from:currentUsername to:conversationId body:body ext:nil]; message.ext = @{@"em_apns_ext":@{@"extern":@"custom string"}}; @@ -26,7 +26,7 @@ message.chatType = EMChatTypeChat; **解析的内容** -```plaintext +```json { "apns": { "alert": { @@ -61,7 +61,7 @@ message.chatType = EMChatTypeChat; 更多内容可以参考苹果官方文档:[生成远程推送通知](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification?language=objc)。 -```plaintext +```objectivec EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"test"]; EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId from:currentUsername to:conversationId body:body ext:nil]; message.ext = @{@"em_apns_ext":@{@"em_push_sound":@"custom.caf"}}; @@ -81,7 +81,7 @@ message.chatType = EMChatTypeChat; **解析的内容** -```plaintext +```json { "aps":{ "alert":{ @@ -109,7 +109,7 @@ message.chatType = EMChatTypeChat; 使用该方式设置后,本条消息会忽略接收方的免打扰设置,不论是否处于免打扰时间段都会正常向对方推送通知; -```plaintext +```objectivec EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"test"]; EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId from:currentUsername to:conversationId body:body ext:nil]; message.ext = @{@"em_force_notification":@YES}; @@ -130,7 +130,7 @@ message.ext = @{@"em_force_notification":@YES}; 发送静默消息和免打扰模式下均为不推送消息,区别在于发送静默消息为发送方在发送消息时设置,而免打扰模式为接收方设置在指定时间段内不接收推送通知。 -```plaintext +```objectivec EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"test"]; EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId from:currentUsername to:conversationId body:body ext:nil]; message.ext = @{@"em_ignore_notification":@YES}; @@ -149,7 +149,7 @@ message.ext = @{@"em_ignore_notification":@YES}; 如果你的目标平台是 iOS 10.0 或以上版本,你可以参考如下代码实现 [`UNNotificationServiceExtension`](https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension?language=objc) 的富文本推送功能。 -```plaintext +```objectivec EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"test"]; EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId from:currentUsername to:conversationId body:body ext:nil]; message.ext = @{@"em_apns_ext":@{@"em_push_mutable_content":@YES}}; @@ -167,7 +167,8 @@ message.chatType = EMChatTypeChat; | `em_push_mutable_content` | 是否使用富文本推送通知(`em_apns_ext`):
- `YES`:富文本推送通知;
- (默认)`NO`:普通推送通知。
该字段名固定,不可修改。 | 接收方收到富文本推送时,会进入回调 `didReceiveNotificationRequest:withContentHandler:`,示例代码如下: -```Objective-C + +```objectivec - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { // 推送扩展字段 NSDictionary *userInfo = request.content.userInfo; @@ -179,7 +180,7 @@ message.chatType = EMChatTypeChat; **解析的内容** -```plaintext +```json { "aps":{ "alert":{ diff --git a/docs/document/ios/push/push_parsing.md b/docs/document/ios/push/push_parsing.md index aafe3d0e..f3f46b92 100644 --- a/docs/document/ios/push/push_parsing.md +++ b/docs/document/ios/push/push_parsing.md @@ -15,7 +15,7 @@ - 若 app 中不使用 `SceneDelegate`,iOS 系统会通过 `application:didFinishLaunchingWithOptions:` 方法中的 `launchOptions` 将推送中的用户自定义信息传递给 app。你需要查看 `launchOptions` 参数获取推送内容。 -```plaintext +```objectivec - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; } @@ -24,7 +24,7 @@ 推送中的用户自定义信息的数据结构: -```plaintext +```json { "aps":{ "alert":{ diff --git a/docs/document/ios/reaction.md b/docs/document/ios/reaction.md index 39db3797..9fb2b455 100644 --- a/docs/document/ios/reaction.md +++ b/docs/document/ios/reaction.md @@ -4,7 +4,7 @@ 环信即时通讯 IM 提供消息表情回复(下文统称 “Reaction”)功能。用户可以在单聊和群聊中对消息添加、删除表情。表情可以直观地表达情绪,利用 Reaction 可以提升用户的使用体验。同时在群组中,利用 Reaction 可以发起投票,根据不同表情的追加数量来确认投票。 -:::notice +:::tip 目前 Reaction 仅适用于单聊和群组。聊天室暂不支持 Reaction 功能。 ::: diff --git a/docs/document/ios/releasenote.md b/docs/document/ios/releasenote.md index 66624185..f728f452 100644 --- a/docs/document/ios/releasenote.md +++ b/docs/document/ios/releasenote.md @@ -92,7 +92,7 @@ end ### 新增特性 -- [IM SDK] 新增[从服务器拉取离线消息的开始和结束的事件回调](overview.html#连接状态相关): `EMClientDelegate#onOfflineMessageSyncStart` 和`EMClientDelegate#onOfflineMessageSyncFinish`。 +- [IM SDK] 新增[从服务器拉取离线消息的开始和结束的事件回调](connection.html#监听连接状态): `EMClientDelegate#onOfflineMessageSyncStart` 和`EMClientDelegate#onOfflineMessageSyncFinish`。 - [IM SDK] 新增 `IEMGroupManager#isMemberInMuteListFromServerWithGroupId:completion:` 接口,可以查看当前用户是否在群组禁言名单中。 - [IM SDK] 原消息置顶接口 `IEMChatManager#pinMessage` 和 `IEMChatManager#unpinMessage` [增加对单聊会话中置顶消息的支持](message_pin.html)。接口参数无变化。 - [IM SDK] 新增 `EMRecallMessageInfo#conversationId` 属性,在撤回消息的 `messagesInfoDidRecall` 事件中[返回被撤回的消息所属的会话 ID](message_recall.html#设置消息撤回监听) @@ -233,8 +233,8 @@ end - [IM SDK] 新增 [EMChatManager#deleteAllMessagesAndConversations:completion:](message_delete.html#清空聊天记录) 方法,用于清空当前用户的聊天记录,包括消息和会话,同时可以选择是否清除服务端的聊天记录。 - [IM SDK] 新增 [EMChatManager#loadMessagesWithKeyword:timestamp:count:fromUser:searchDirection:scope:completion:](message_search.html#根据搜索范围搜索所有会话中的消息) 和[EMConversation#loadMessagesWithKeyword:timestamp:count:fromUser:searchDirection:scope:completion:](message_search.html#根据搜索范围搜索当前会话中的消息),可以在根据关键字搜索消息时,选择搜索范围,如只搜索消息内容、只搜索消息扩展信息以及同时搜索消息内容以及扩展信息。 -- [IM SDK] 新增 [EMOptions#useReplacedMessageContents](message_send_receive.html#发送文本消息) 开关。开启后,发送消息时如果被内容审核进行了内容替换,发送方可以获取替换后的内容。 -- [IM SDK] 新增 [EMOptions#includeSendMessageInMessageListener](message_send_receive.html#接收消息) 开关。开启后,在 `messagesDidReceive` 回调里增加发送成功的消息。 +- [IM SDK] 新增 [EMOptions#useReplacedMessageContents](message_send_receive.html#发送消息前的内容审核) 开关。开启后,发送消息时如果被内容审核进行了内容替换,发送方可以获取替换后的内容。 +- [IM SDK] 新增 [EMOptions#includeSendMessageInMessageListener](message_send_receive.html#发送和接收文本消息) 开关。开启后,在 `messagesDidReceive` 回调里增加发送成功的消息。 - [IM SDK] 新增 [EMOptions#regardImportMessagesAsRead](message_retrieve.html#从服务器获取指定会话的消息) 开关。开启后,[利用服务端接口](/document/server-side/message_import.html)导入的消息,客户端上通过[漫游拉取](message_retrieve.html#从服务器获取指定会话的消息)到后,这些消息为已读状态,会话中未读取的消息数量 `EMConversation#unreadMessagesCount` 不发生变化。若该开关为关闭状态,`EMConversation#unreadMessagesCount` 的数量会增加。 ### 优化 @@ -410,7 +410,7 @@ end ### 新增特性 -- [IM SDK] 新增[聊天室消息优先级](message_send_receive.html)。 +- [IM SDK] 新增[聊天室消息优先级](message_send_receive.html#设置聊天室消息优先级)。 - [IM SDK] 群组信息更新后的 `EMGroupManagerDelegate#groupSpecificationDidUpdate` 回调中添加更新后的群组信息。 ### 优化 diff --git a/docs/document/ios/room_manage.md b/docs/document/ios/room_manage.md index 6ec57fc9..e46df4eb 100644 --- a/docs/document/ios/room_manage.md +++ b/docs/document/ios/room_manage.md @@ -210,7 +210,7 @@ SDK 中提供了聊天室事件的监听接口。你可以通过注册聊天室 2. 收到通知事件后,通过 `EMChatroom#occupantsCount` 获取聊天室当前人数。 -```Swift +```swift extension ViewController: EMChatroomManagerDelegate { func userDidJoin(_ aChatroom: EMChatroom, user aUsername: String) { let memberCount = aChatroom.occupantsCount diff --git a/docs/document/ios/room_members.md b/docs/document/ios/room_members.md index f0f9ef36..7d699b4d 100644 --- a/docs/document/ios/room_members.md +++ b/docs/document/ios/room_members.md @@ -196,7 +196,7 @@ EMError *error = nil; 仅聊天室所有者和管理员可以调用 `muteMembers` 方法将指定成员添加至聊天室禁言列表。被禁言的成员和其他未操作的聊天室管理员或聊天室所有者收到 `chatroomMuteListDidUpdate:addedMutedMembers` 回调。 -:::notice +:::tip 聊天室所有者可禁言聊天室所有成员,聊天室管理员可禁言聊天室普通成员。 ::: @@ -213,7 +213,7 @@ EMError *error = nil; 仅聊天室所有者和管理员可以调用 `unmuteMembers` 方法将成员移出聊天室禁言列表。被解除禁言的成员和其他未操作的聊天室管理员或聊天室所有者收到 `chatroomMuteListDidUpdate: removedMutedMembers` 回调。 -:::notice +:::tip 聊天室所有者可对聊天室所有成员解除禁言,聊天室管理员可对聊天室普通成员解除禁言。 ::: diff --git a/docs/document/ios/thread.md b/docs/document/ios/thread.md index facf794b..10114217 100644 --- a/docs/document/ios/thread.md +++ b/docs/document/ios/thread.md @@ -58,7 +58,7 @@ 单设备登录时,子区所属群组的所有成员均会收到 `EMChatThreadManagerDelegate#onChatThreadDestroyed` 回调;多设备登录时,其他设备会同时收到 `- (void)multiDevicesThreadEventDidReceive:(EMMultiDevicesEvent)aEvent threadId:(NSString *)aThreadId ext:(id)aExt;` 回调,回调事件为 `EMMultiDevicesEventThreadDestroy`。 -:::notice +:::tip 解散子区后,将删除本地数据库及内存中的群相关信息及群会话,谨慎操作。 ::: diff --git a/docs/document/ios/thread_message.md b/docs/document/ios/thread_message.md index a3b12a8b..a2b63d8f 100644 --- a/docs/document/ios/thread_message.md +++ b/docs/document/ios/thread_message.md @@ -6,20 +6,11 @@ ## 技术原理 -环信即时通讯 IM iOS SDK 提供 `EMManager`、`EMChatMessage` 和 `EMChatThreadInfo` 类,用于管理子区消息,支持你通过调用 API 在项目中实现如下功能: - -- 发送子区消息 -- 接收子区消息 -- 撤回子区消息 -- 获取子区消息 +环信即时通讯 IM iOS SDK 提供 `EMManager`、`EMChatMessage` 和 `EMChatThreadInfo` 类,用于管理子区消息,支持你通过调用 API 在项目中实现发送、接收、撤回和获取子区消息。 消息收发流程如下: -1. 客户端从应用服务器获取 token。 -2. 客户端 A 和 B 登录即时通讯。 -3. 客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 - -![img](/images/android/sendandreceivemsg.png) +客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 子区创建和查看如下图: @@ -40,7 +31,7 @@ ### 发送子区消息 -发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html#发送文本消息)。唯一不同的是,发送子区消息需要指定标记 `isChatThread` 为 `YES`。 +发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html)。唯一不同的是,发送子区消息需要指定标记 `isChatThread` 为 `YES`。 单设备登录时,子区所属群组的所有成员会收到 `EMChatThreadManagerDelegate#onChatThreadUpdated` 回调。 @@ -65,7 +56,7 @@ message.isChatThread = self.isChatThread; ### 接收子区消息 -接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#接收消息),此处只介绍子区消息和其他消息的区别。 +接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#发送和接收文本消息),此处只介绍子区消息和其他消息的区别。 子区成员可以设置消息监听回调 `EMManagerDelegate#messagesDidReceive` 对子区消息的接收进行监听。 diff --git a/docs/document/ios/user_relationship.md b/docs/document/ios/user_relationship.md index 81aac6bf..59381b97 100644 --- a/docs/document/ios/user_relationship.md +++ b/docs/document/ios/user_relationship.md @@ -144,7 +144,7 @@ if (!aError) { 好友备注的长度不能超过 100 个字符。 -```objective-c +```objectivec [EMClient.sharedClient.contactManager setContactRemark:@"userId" remark:@"remark" completion:^(EMContact * _Nullable contact, EMError * _Nullable aError) { }]; diff --git a/docs/document/ios/userprofile.md b/docs/document/ios/userprofile.md index c3dc673e..227ceb71 100644 --- a/docs/document/ios/userprofile.md +++ b/docs/document/ios/userprofile.md @@ -10,7 +10,7 @@ 本文介绍如何通过管理用户属性设置、更新、存储并获取实时消息用户的相关信息。 -:::notice +:::tip 为保证用户信息安全,SDK 仅支持用户设置或更新自己的用户属性。 ::: diff --git a/docs/document/react-native/connection.md b/docs/document/react-native/connection.md new file mode 100644 index 00000000..e7b75f2f --- /dev/null +++ b/docs/document/react-native/connection.md @@ -0,0 +1,80 @@ +# 连接 + +应用客户端成功连接到环信服务器后,才能使用环信即时通讯 SDK 的收发消息等功能。 + +你调用 `login` 方法登录后,客户端 SDK 会自动连接环信服务器。关于登录详情,请参见[登录文档](login.html)。 + +## 监听连接状态 + +你可以通过注册连接监听确认连接状态。 + +```typescript +ChatClient.getInstance().addConnectionListener({ + onConnected(): void { + console.log("onConnected"); + }, + onDisconnected(): void { + console.log("onDisconnected"); + }, + onAppActiveNumberReachLimit(): void { + console.log("onAppActiveNumberReachLimit"); + }, + + onUserDidLoginFromOtherDevice(deviceName?: string): void { + console.log("onUserDidLoginFromOtherDevice", deviceName); + }, + + onUserDidLoginFromOtherDeviceWithInfo(params: { + deviceName?: string; + ext?: string; + }): void { + console.log("onUserDidLoginFromOtherDeviceWithInfo", params); + }, + + onUserDidRemoveFromServer(): void { + console.log("onUserDidRemoveFromServer"); + }, + + onUserDidForbidByServer(): void { + console.log("onUserDidForbidByServer"); + }, + + onUserDidChangePassword(): void { + console.log("onUserDidChangePassword"); + }, + + onUserDidLoginTooManyDevice(): void { + console.log("onUserDidLoginTooManyDevice"); + }, + + onUserKickedByOtherDevice(): void { + console.log("onUserKickedByOtherDevice"); + }, + + onUserAuthenticationFailed(): void { + console.log("onUserAuthenticationFailed"); + }, + + onOfflineMessageSyncFinish(): void { + console.log("onOfflineMessageSyncFinish"); + }, + + onOfflineMessageSyncStart(): void { + console.log("onOfflineMessageSyncStart"); + }, +} as ChatConnectEventListener); +``` + +## 自动重连 + +登录后,如果由于网络信号弱、切换网络等引起的连接中断,SDK 会自动尝试重连。重连成功或者失败时分别会收到 `onConnected` 和 `onDisconnected` 通知。 + +不过,SDK 在以下情况下会停止自动重连。你需要调用 `login` 方法登录。 + +- 用户调用了 SDK 的登出方法 `logout` 主动退出登录。 +- 登录时鉴权错误,例如, token 无效(错误码 104)或已过期(错误码 108)。 +- 用户在其他的设备上更改了密码,导致此设备上自动登录失败,提示错误码 216。 +- 用户的账号被从服务器端删除,提示错误码 207。 +- 用户在另一设备登录,将当前设备上登录的用户踢出,提示错误码 206。 +- 用户登录设备数量超过限制,提示错误码 214。 +- 应用程序的日活跃用户数量(DAU)或月活跃用户数量(MAU)达到上限,提示错误码 8。 diff --git a/docs/document/react-native/conversation_receipt.md b/docs/document/react-native/conversation_receipt.md new file mode 100644 index 00000000..b556f7de --- /dev/null +++ b/docs/document/react-native/conversation_receipt.md @@ -0,0 +1,74 @@ +# 会话已读回执 + +会话已读回执指接收方进入会话页面,阅读会话中的所有消息后,调用接口向服务器发送会话已读回执,服务器将该回执回调给消息发送方,消息发送方将会收到会话已读回调。在多端多设备登录下,接收方的其他设备也会收到该回调。 + +目前,单聊和群组聊天支持会话已读回执。本文介绍如何使用环信即时通讯 IM RN SDK 实现会话已读回执功能。 + +会话已读回执的效果示例,如下图所示: + +![img](/images/uikit/chatuikit/feature/conversation/conversation_read.png) + +## 技术原理 + +单聊会话已读回执实现的流程如下: + +1. 设置 `ChatOptions#requireAck` 为 `true` 开启已读回执功能。 +2. 消息接收方进入会话页面,阅读消息后,调用 `markAllMessagesAsRead` 方法发送会话已读回执。 +3. 消息发送方通过监听 `onConversationHasRead` 回调接收会话已读回执。 + +## 前提条件 + +开始前,请确保满足以下条件: + +- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 +- 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 + +## 实现方法 + +参考以下步骤在单聊中实现会话已读回执: + +1. 开启已读回执功能,即 SDK 初始化时设置 `ChatOptions#requireAck` 为 `true`。 + +```typescript +// 设置是否需要接收方已读确认,默认为 true +options.requireAck = true; +``` + +2. 接收方发送会话已读回执。 + +消息接收方进入会话页面,查看会话中是否有未读消息。若有,调用 `markAllMessagesAsRead` 方法发送会话已读回执,没有则不发送。该方法为异步方法,需要捕捉异常。 + +若会话中存在多条未读消息,建议调用该方法,因为若调用发送消息已读回执方法 `ackMessageRead`,则需要调用多次。 + +```typescript +const convId = ""; // 会话ID。 +const convType = 0; // 会话类型。单聊。 +ChatClient.getInstance() + .chatManager.markAllMessagesAsRead(convId, convType) + .then(() => { + console.log("markAllMessagesAsRead success"); + }) + .catch((e) => { + console.log(e); + }); +``` + +3. 消息发送方监听会话已读回执的回调。 + +同一用户 ID 登录多设备的情况下,用户在一台设备上发送会话已读回执,其他设备会收到 `onConversationHasRead` 回调。 + +:::tip +对于群组聊天,会话已读回执只用于清空服务端的群组会话的未读数,消息发送方不会通过 `onConversationHasRead` 回调收到会话已读回执。 +::: + +```typescript +ChatClient.getInstance().chatManager.addMessageListener({ + onConversationRead: (from: string, to?: string) => { + console.log(`onConversationRead: `, from, to); + }, +}); +``` + +## 会话已读回执和消息未读数 + +消息接收方调用 `markAllMessagesAsRead` 方法发送会话已读回执,开发者可调用 `ChatConversation#markAllMessagesAsRead` 方法将所有未读消息设置为已读,即将该会话的未读消息数清零。 diff --git a/docs/document/react-native/group_attributes.md b/docs/document/react-native/group_attributes.md index c2106729..5a966829 100644 --- a/docs/document/react-native/group_attributes.md +++ b/docs/document/react-native/group_attributes.md @@ -17,7 +17,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,详见 [初始化](initialization.html)和[快速开始](quickstart.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 了解群组和群成员的数量限制,详见 [套餐包详情](https://www.easemob.com/pricing/im)。 diff --git a/docs/document/react-native/group_manage.md b/docs/document/react-native/group_manage.md index cdc71e46..930fde97 100644 --- a/docs/document/react-native/group_manage.md +++ b/docs/document/react-native/group_manage.md @@ -21,7 +21,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,详见 [初始化](initialization.html)及[快速开始](quickstart.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 了解群组和群成员的数量限制,详见 [套餐包详情](https://www.easemob.com/pricing/im)。 @@ -93,7 +93,7 @@ ChatClient.getInstance() 仅群主可以调用 `destroyGroup` 方法解散群组。群组解散时,其他群组成员收到 `ChatGroupEventListener#onGroupDestroyed` 回调并被踢出群组。 -:::notice +:::tip 解散群组后,将删除本地数据库及内存中的群相关信息及群会话,谨慎操作。 ::: diff --git a/docs/document/react-native/group_members.md b/docs/document/react-native/group_members.md index ca043edc..688a85f1 100644 --- a/docs/document/react-native/group_members.md +++ b/docs/document/react-native/group_members.md @@ -21,7 +21,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html); +- 完成 SDK 初始化,详见 [初始化](initialization.html)及[快速开始](quickstart.html); - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html); - 了解群成员角色,详见 [群组概述](group_overview.html); - 了解群组和群成员的数量限制,详见 [套餐包详情](https://www.easemob.com/pricing/im)。 @@ -285,7 +285,7 @@ ChatClient.getInstance() 你可以调用 `fetchMembersAttributes` 方法根据指定的属性 key 获取多个群成员的自定义属性。 -:::notice +:::tip 每次最多可获取 10 个群成员的自定义属性。 ::: diff --git a/docs/document/react-native/initialization.md b/docs/document/react-native/initialization.md new file mode 100644 index 00000000..f430dbbd --- /dev/null +++ b/docs/document/react-native/initialization.md @@ -0,0 +1,32 @@ +# SDK 初始化 + +初始化是使用 SDK 的必要步骤,需在所有接口方法调用前完成。 + +如果进行多次初始化操作,只有第一次初始化以及相关的参数生效。 + +:::tip +需要在主进程中进行初始化。 +::: + +## 前提条件 + +有效的环信即时通讯 IM 开发者账号和 App key,详见[环信即时通讯云控制台的相关文档](enable_and_configure_IM.html#创建应用)。 + +## 初始化 + +初始化示例代码: + +```typescript +ChatClient.getInstance() + .init( + new ChatOptions({ + appKey: appKey, + }), + ) + .then(() => { + console.log("init: success"); + }) + .catch((reason) => { + console.error(reason); + }); +``` diff --git a/docs/document/react-native/integration.md b/docs/document/react-native/integration.md new file mode 100644 index 00000000..0277756a --- /dev/null +++ b/docs/document/react-native/integration.md @@ -0,0 +1,38 @@ +# 集成 SDK + +本文介绍如何将环信即时通讯 IM SDK 集成到你的 React Native 项目中。 + +## 开发环境需求 + +- React Native 0.66.5 or above +- NodeJs 16 or above (Recommended 18 or above) + +## 集成到项目中 + +打开终端,添加依赖到项目中 + +```sh +yarn add react-native-chat-sdk +``` + +or + +```sh +npm i --save react-native-chat-sdk +``` + +## 添加权限 + +目前需要基本的网络通讯权限。 + +对于 ios 平台:内置网络权限。 + +对于 android 平台: + +更新 `AndroidManifest.xml` 文件内容,增加需要的权限。 + +```xml + + + +``` diff --git a/docs/document/react-native/log.md b/docs/document/react-native/log.md new file mode 100644 index 00000000..e30f731e --- /dev/null +++ b/docs/document/react-native/log.md @@ -0,0 +1,10 @@ + +# SDK 日志 + +环信即时通讯 IM 日志记录 SDK 相关的信息和事件。环信技术支持团队帮你排查问题时可能会请你发送 SDK 日志。 + +如果开启日志调试模式,会通过控制台输出 SDK 日志。`debugModel` 设置为 `true`。 + +```typescript +chatlog.log(`${ChatClient.TAG}: login: `, userName, "******", isPassword); +``` diff --git a/docs/document/react-native/login.md b/docs/document/react-native/login.md new file mode 100644 index 00000000..bfdfff55 --- /dev/null +++ b/docs/document/react-native/login.md @@ -0,0 +1,95 @@ +# 登录 + +初始化 IM SDK 后,你需要首先调用接口登录。登录成功后,才能使用 IM 的功能。 + +## 用户注册 + +用户注册模式分为以下两种: + +- 开放注册:一般在体验 Demo 和测试环境时使用,正式环境中不推荐使用该方式注册环信账号。要使用开放注册,需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**开放注册**。只有打开该开关,才能使用客户端或 [REST API](/document/server-side/account_system.html#开放注册单个用户)开放注册用户。 + +示例代码如下所示: + +```typescript +ChatClient.getInstance() + .createAccount(username, password) + .then((value: any) => { + console.log("createAccount: success", value); + }) + .catch((reason: any) => { + console.log("createAccount: fail", reason); + }); +``` + +- 授权注册:通过环信提供的 REST API 注册环信用户账号,注册后保存到你的服务器或返给客户端。要使用授权注册,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**授权注册**。相关的 REST API 介绍,详见[授权注册单个用户](/document/server-side/account_system.html#授权注册单个用户)和[批量授权注册用户](/document/server-side/account_system.html#批量授权注册用户)的接口介绍。 + +除此以外,可以在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建正式环境下和测试环境下的用户,详见[创建用户相关介绍](/product/enable_and_configure_IM.html#创建-im-用户)。 + +## 主动登录 + +测试环境下,你在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建用户后,环信服务器会自动为这些用户分配用户 Token,详见[测试环境下创建用户的介绍](/product/enable_and_configure_IM.html#测试环境)。 + +使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 + +```typescript +// userId: 用户ID +// userPassword: 用户密码或者token +// isPassword: 是否使用密码 +ChatClient.getInstance() + .login(userId, userPassword, isPassword) + .then(() => { + console.log("login success."); + }) + .catch((error) => { + console.log("login fail: ", error); + }); +``` + +## 自动登录 + +初始化时,你可以设置 `ChatOptions#autoLogin` 选项确定是否自动登录。如果设置为自动登录,则登录成功之后,后续初始化 SDK 时会自动登录。 + +## 获取当前登录的用户 + +你可以调用 `ChatClient#getCurrentUsername` 方法获取当前登录用户的用户 ID。 + +## 获取登录状态 + +你可以调用 `ChatClient#isLoginBefore` 方法获取当前用户的登录状态。 + +## 退出登录 + +你可以调用 `logout` 方法退出登录。退出登录后,你不会再收到其他用户发送的消息。 + +```typescript +// unbindDeviceToken: 是否基础推送token绑定 +ChatClient.getInstance() + .logout(unbindDeviceToken) + .then(() => { + console.log("logout: success."); + }) + .catch((e) => { + console.log(e); + }); +``` + +:::tip + +1. 如果集成了第三方推送,`logout` 方法中 `unbindDeviceToken` 参数需设为 `true`,退出时会解绑设备 token,否则可能会出现退出了,还能收到消息推送通知的现象。 +2. 有时可能会遇到网络问题而解绑失败,app 处理时可以弹出提示框让用户选择,是否继续退出(弹出框提示继续退出能收到消息的风险),如果用户选择继续退出,传 `false` 再调用 `logout` 方法退出成功。当然也可以失败后还是返回退出成功,然后在后台起个线程不断调用 `logout` 方法直到成功。这样存在风险,即用户杀掉了 app,网络恢复后用户还会继续收到消息。 + +## 账号切换 + +若在 app 中从当前账号切换到其他账号,你需要首先调用 `logout` 方法登出,然后再调用 `login` 方法登录。 + +## 多设备登录 + +除了单端单设备登录,环信即时通讯 IM 支持同一账号在多端的多个设备上登录。多设备登录时,若同端设备数量超过限制,新登录的设备会将之前登录的设备踢下线。 + +关于多设备登录场景中的设备数量限制、互踢策略以及信息同步,详见[多设备登录文档](multi_device.html)。 + +## 更多 + +### 登录被封禁账号的提示 + +在环信即时通讯控制台或调用 REST API 封禁用户账号后,若仍使用该账号登录,SDK 会返回 "service is disabled"(305 错误), 可以根据用户这个返回值来进行相应的提示或者处理。 diff --git a/docs/document/react-native/message_delete.md b/docs/document/react-native/message_delete.md index ecdaadf1..866d6071 100644 --- a/docs/document/react-native/message_delete.md +++ b/docs/document/react-native/message_delete.md @@ -18,7 +18,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,并连接到服务器,详见 [初始化](initialization.html)及[连接](connection.html)文档。 - 了解环信即时通讯 IM API 的使用限制,详见 [使用限制](/product/limitation.html)。 ## 实现方法 diff --git a/docs/document/react-native/message_deliver_only_online.md b/docs/document/react-native/message_deliver_only_online.md index 47c92c01..07c7e7ff 100644 --- a/docs/document/react-native/message_deliver_only_online.md +++ b/docs/document/react-native/message_deliver_only_online.md @@ -27,7 +27,7 @@ 下面以发送文本消息为例进行说明: -```ts +```typescript // 创建一条文本消息,`content` 为消息文字内容。 // `conversationId` 为消息接收方,单聊时为对端用户 ID、群聊时为群组 ID。 // `conversationIdType` 会话类型:单聊为 ChatMessageChatType.PeerChat,群聊为 ChatMessageChatType.GroupChat diff --git a/docs/document/react-native/message_import_insert.md b/docs/document/react-native/message_import_insert.md index c0a0ffcb..25ad6b43 100644 --- a/docs/document/react-native/message_import_insert.md +++ b/docs/document/react-native/message_import_insert.md @@ -14,7 +14,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,并连接到服务器,详见 [初始化](initialization.html)及[连接](connection.html)文档。 - 了解环信即时通讯 IM API 的使用限制,详见 [使用限制](/product/limitation.html)。 ## 实现方法 diff --git a/docs/document/react-native/message_modify.md b/docs/document/react-native/message_modify.md index 9ac328bb..8226e81f 100644 --- a/docs/document/react-native/message_modify.md +++ b/docs/document/react-native/message_modify.md @@ -27,7 +27,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,并连接到服务器,详见 [初始化](initialization.html)及[连接](connection.html)文档。 - 了解环信即时通讯 IM API 的使用限制,详见 [使用限制](/product/limitation.html)。 ## 实现方法 diff --git a/docs/document/react-native/message_receipt.md b/docs/document/react-native/message_receipt.md index 0a521a64..696a80be 100644 --- a/docs/document/react-native/message_receipt.md +++ b/docs/document/react-native/message_receipt.md @@ -1,339 +1,235 @@ -# 消息回执 +# 实现消息回执 -单聊会话支持消息送达回执、会话已读回执和消息已读回执,发送方发送消息后可及时了解接收方是否及时收到并阅读了信息,也可以了解整个会话是否已读。 -群聊会话只支持消息已读回执。群成员在发送消息时,可以设置该消息是否需要已读回执。仅专业版及以上版本支持群消息已读回执功能。若要使用该功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 +**单聊会话支持消息送达回执和消息已读回执**,发送方发送消息后可及时了解接收方是否及时收到并阅读了消息。 -:::tip -仅单聊消息支持送达回执,群聊消息不支持。 -::: +**群聊会话只支持消息已读回执,不支持送达回执**。群成员在发送消息时,可以设置该消息是否需要已读回执。要使用该功能,你需要[在环信即时通讯云控制台上开通该功能](/product/enable_and_configure_IM.html#设置群消息已读回执),具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 -本文介绍如何使用环信即时通讯 IM React Native SDK 实现单聊和群聊的消息回执功能。 +消息送达回执和已读回执的效果示例,如下图所示: + +![img](/images/android/message_receipt.png) ## 技术原理 -环信即时通讯 IM React Native SDK 通过 `ChatManager` 类提供消息的送达回执和已读回执功能: +使用环信即时通讯 IM RN SDK 可以实现消息的送达回执与已读回执。 + +- 单聊消息送达回执的逻辑如下: + + 1. 你可以通过设置 `ChatOptions#requireDeliveryAck` 为 `true` 开启送达回执功能。 + 2. 消息接收方收到消息后,SDK 自动向发送方触发送达回执。 + 3. 消息发送方通过监听 `ChatMessageEventListener#onMessagesDelivered` 回调接收消息送达回执。 + +- 单聊消息已读回执的逻辑如下: -- 消息送达回执 -- 消息和会话的已读回执 -- 群聊已读回执 -- 获取群组已读回执信息 -- 获取群组已读回执数目 + 1. 你可以通过设置 `ChatOptions#requireAck` 为 `true` 开启已读回执功能。 + 2. 消息接收方收到消息后,调用 `ChatManager#markMessageAsRead` 方法发送消息已读回执。 + 3. 消息发送方通过监听 `ChatMessageEventListener#onMessagesRead` 回调接收消息已读回执。 -实现送达和已读回执的逻辑分别如下: +- 群聊消息已读回执的逻辑如下: -- 单聊消息送达回执 - 1. 消息发送方在发送消息前通过 `ChatOptions.requireDeliveryAck` 开启送达回执功能。 - 2. 消息接收方收到消息后,SDK 自动向发送方触发送达回执。 - 3. 消息发送方通过监听 `onMessageDelivered` 回调接收消息送达回执。 -- 单聊会话及消息已读回执 - 1. 消息发送方在发送消息前通话 `ChatOptions.requireAck` 开启已读回执功能。 - 2. 消息接收方收到或阅读消息后,调用 API `ChatManager.sendConversationReadAck` 或 `ChatManager.sendMessageReadAck` 发送会话或消息已读回执。 - 3. 消息发送方通过监听 `onConversationRead` 或 `onMessageRead` 回调接收会话或消息已读回执。 -- 群聊只支持消息已读回执: - 1. 你可以通过设置 `ChatOptions.NeedGroupAck` 为 `true` 开启群聊消息已读回执功能; - 2. 消息接收方收到或阅读消息后通过 `ChatManager.sendGroupMessageReadAck` 发送群组消息的已读回执。 + 1. 你可以通过设置 `ChatOptions#requireAck` 为 `true` 开启消息已读回执功能。 + 2. 发送方在群组中发送消息时设置 `ChatMessage#needGroupAck` 为 `true` 要求接收方返回消息已读回执。 + 3. 接收方收到或阅读消息后通过 `ChatManager#markMessageAsRead` 方法发送群组消息的已读回执。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -- 群消息已读回执功能仅在环信 IM 专业版及以上版本支持该功能。若要使用该功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 +- 要使用群消息已读回执功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 ## 实现方法 -### 消息送达回执 +### 单聊消息送达回执 -1. 发送方开启全局送达回执。当接收方收到消息后,SDK 底层会自动进行消息送达回执。 +1. 开启消息送达功能,即 SDK 初始化时将 `ChatOptions#requireDeliveryAck` 设置为 `true`。 ```typescript -// 设置 app key -const appKey = "appKey"; -// 开启消息送达回执 -const requireDeliveryAck = true; -ChatClient.getInstance() - .init( - new ChatOptions({ - appKey, - requireDeliveryAck, - }) - ) - .then(() => { - console.log("init sdk success"); - }) - .catch((reason) => { - console.log("init sdk fail.", reason); - }); +// 设置是否需要接收方送达确认,默认 `false` 即不需要。 +options.requireDeliveryAck = true; ``` -2. 发送方监听事件 `onMessagesDelivered` 回调,收到接收方的送达回执。 +2. 接收方收到消息后,SDK 自动向发送方触发送达回执。 + +3. 发送方监听 `ChatMessageEventListener#onMessagesDelivered` 事件,收到接收方的送达回执。你可以在收到该通知时,显示消息的送达状态。 ```typescript -class ChatMessageEvent implements ChatMessageEventListener { - onMessagesDelivered(messages: ChatMessage[]): void { - console.log(`onMessagesDelivered: `, messages); - } - // ... -} -// 添加监听器 -const listener = new ChatMessageEvent(); -ChatClient.getInstance().chatManager.addMessageListener(listener); +ChatClient.getInstance().chatManager.addMessageListener({ + onMessagesDelivered: (messages: ChatMessage[]) => { + console.log(`onMessagesDelivered:`, messages); + }, +}); ``` -### 消息和会话的已读回执 +### 单聊消息已读回执 -消息已读回执用于告知单聊或群聊中的用户接收方已阅读其发送的消息。为降低消息已读回执方法的调用次数,SDK 还支持在单聊中使用会话已读回执功能,用于获知接收方是否阅读了会话中的未读消息。 +单聊既支持单条消息已读回执,也支持[会话已读回执](conversation_receipt.html)。我们建议你结合使用这两种回执,见实现步骤的描述。 -#### 单聊 +单聊消息的已读回执有效期与消息在服务端的存储时间一致,即在服务器存储消息期间均可发送已读回执。消息在服务端的存储时间与你订阅的套餐包有关,详见[产品价格](/product/pricing.html#套餐包功能详情)。 -单聊既支持消息已读回执,也支持会话已读回执。我们建议你按照如下逻辑结合使用两种回执结合使用,减少发送消息已读回执数量。 +参考如下步骤在单聊中实现消息已读回执。 -- 聊天页面未打开时,若有未读消息,进入聊天页面,发送会话已读回执; -- 聊天页面打开时,若收到消息,发送消息已读回执。 +1. App 开启已读回执功能,即 SDK 初始化时将 `ChatOptions#requireAck` 设置为 `true`。 -##### 会话已读回执 +```typescript +// 设置是否需要接收方已读确认,默认为true +options.requireAck = true; +``` -参考如下步骤在单聊中实现会话已读回执。 +2. 接收方发送消息已读回执。 -1. 开启全局的消息已读回执开关。如果全局设置不开启,消息和会话的相应设置也无法生效。 +- 聊天页面打开时,若收到消息,发送单条消息已读回执。 ```typescript -// 设置 app key -const appKey = "appKey"; -// 开启消息已读回执 -const requireAck = true; ChatClient.getInstance() - .init( - new ChatOptions({ - appKey, - requireAck, - requireDeliveryAck, - }) - ) + .chatManager.markAllMessagesAsRead(convId, convType) .then(() => { - console.log("init sdk success"); + console.log("markAllMessagesAsRead success."); }) - .catch((reason) => { - console.log("init sdk fail.", reason); + .catch((e) => { + console.log("markAllMessagesAsRead fail.", e); }); ``` -2. 接收方执行会话已读回执操作。进入会话页面,查看会话中是否有未读消息。若有,发送会话已读回执,没有则不再发送。 +- 聊天页面未打开时,若有未读消息,进入聊天页面,发送会话已读回执。这种方式可避免发送多个消息已读回执。 ```typescript -// 会话 ID -const convId = "convId"; -// 执行操作 -ChatClient.getInstance() - .chatManager.sendConversationReadAck(convId) - .then(() => { - console.log("send conversation read success"); - }) - .catch((reason) => { - console.log("send conversation read fail.", reason); - }); +ChatClient.getInstance().chatManager.addMessageListener({ + onMessagesReceived: (messages: ChatMessage[]) => { + console.log(`onMessagesReceived:`, messages); + for (const msg of messages) { + ChatClient.getInstance() + .chatManager.markMessageAsRead(convId, convType, msg.msgId) + .then(() => { + console.log("markMessageAsRead success."); + }) + .catch((e) => { + console.log("markMessageAsRead fail.", e); + }); + } + }, +}); ``` -3. 发送方监听 `onConversationRead` 回调,接收会话已读回执。 +3. 消息发送方监听消息已读回调。 + +消息发送方可以通过 `ChatMessageEventListener#onMessageRead` 事件监听指定消息是否已读,示例代码如下: ```typescript -class ChatMessageEvent implements ChatMessageEventListener { - onConversationRead(from: string, to?: string): void { - console.log(`onConversationRead: `, from, to); - } - // ... -} -// 添加监听器 -const listener = new ChatMessageEvent(); -ChatClient.getInstance().chatManager.addMessageListener(listener); +ChatClient.getInstance().chatManager.addMessageListener({ + onMessagesRead: (messages: ChatMessage[]) => { + console.log(`onMessagesRead:`, messages); + }, +}); ``` -> 同一用户 ID 登录多设备的情况下,用户在一台设备上发送会话已读回执,服务器会将会话的未读消息数置为 `0`,同时其他设备会收到 `onConversationRead` 回调。 +### 群聊消息已读回执 -##### 消息已读回执 +对于群聊,群成员发送消息时,可以设置该消息是否需要已读回执。若需要,每个群成员阅读消息后,应该调用`ChatManager#markMessageAsRead` 方法发送已读回执,阅读该消息的群成员数量即为已读回执的数量。 -单聊消息的已读回执有效期与消息在服务端的存储时间一致,即在服务器存储消息期间均可发送已读回执。消息在服务端的存储时间与你订阅的套餐包有关,详见[产品价格](/product/pricing.html#套餐包功能详情)。 +群消息已读回执特性的使用限制如下表所示: -参考如下步骤在单聊中实现消息已读回执。 +| 使用限制 | 默认 | 描述 | +| :------------------- | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 功能开通 | 关闭 | 若要使用该功能,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **功能配置** > **功能配置总览**> **基础功能**页签下,搜索找到 **消息已读回执(群聊)** 开通功能。具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 | +| 使用权限 | 所有群成员 | 默认情况下,所有群成员发送消息时可要求已读回执。如果仅需群主和群管理员发消息时要求已读回执,可联系商务修改。 | +| 已读回执有效期 | 3 天 | 群聊已读回执的有效期为 3 天,即群组中的消息发送时间超过 3 天,服务器不记录阅读该条消息的群组成员,也不会发送已读回执。 | +| 群规模 | 200 人 | 该特性最大支持 200 人的群组。如果超过 200 人/群,群成员发送的消息不会返回已读回执。你可以联系商务提升群成员人数上限。 | +| 查看返回已读回执数量 | 消息发送方 | 对消息返回的已读回执数量(或返回已读回执的人数),默认仅消息发送方可查看。如需所有群成员均可查看,可联系商务开通。 | -1. 开启全局的消息已读回执开关。如果全局设置不开启,消息和会话的相应设置也无法生效。 +你可以按以下步骤实现群消息已读回执特性: -```typescript -// 设置 app key -const appKey = "appKey"; -// 开启消息已读回执 -const requireAck = true; -ChatClient.getInstance() - .init( - new ChatOptions({ - appKey, - requireAck, - requireDeliveryAck, - }) - ) - .then(() => { - console.log("init sdk success"); - }) - .catch((reason) => { - console.log("init sdk fail.", reason); - }); -``` +1. 开启已读回执功能,即 SDK 初始化时将 `ChatOptions#requireAck` 设置为 `true`。 -2. 消息发送方监听 `onMessagesRead` 事件。 +该功能开启后,接收方阅读消息后,SDK 底层会自动进行消息已读回执。 ```typescript -class ChatMessageEvent implements ChatMessageEventListener { - onMessagesRead(messages: ChatMessage[]): void { - // 收到消息已读 - console.log(`onMessagesRead: `, messages); - } - // ... -} -// 添加监听器 -const listener = new ChatMessageEvent(); -ChatClient.getInstance().chatManager.addMessageListener(listener); +// 设置是否需要接受方已读确认,默认为 `true`。 +options.requireAck = true; ``` -3. 消息发送方发送消息,并等待接收方已读。 +2. 发送方发送消息时设置 `ChatMessage#needGroupAck` 属性为 `true`。 + +与单聊消息的 app 层级设置已读回执功能不同,群聊消息是在发送消息时设置指定消息是否需要已读回执。 ```typescript -// 发送消息 -// 设置消息开启已读回执 -msg.hasReadAck = true; -// 执行发送消息 -ChatClient.getInstance() - .chatManager.sendMessage(msg) - .then(() => { - // 消息发送动作完成,会在这里打印日志。 - console.log("send message success."); - }) - .catch((reason) => { - // 消息发送动作失败,会在这里打印日志。 - console.log("send message fail.", reason); - }); +const convId; // 会话ID。 +const content; // 会话内容。 +ChatMessage message = ChatMessage.createTextMessage(convId, content); +message.needGroupAck = true; ``` -4. 接收方查看消息,并调用 `sendMessageReadAck` 方法告知发送方消息已读。成功调用后,消息发送方会收到 `onMessageRead` 回调。 +3. 消息接收方发送群组消息的已读回执。 ```typescript -// 接收的需要已读回执的消息 -const msg; -// 执行发送已读回执操作 +const convId; // 会话ID。 +const convType; // 会话类型。群组为 1。 +const msgId; // 消息ID。 ChatClient.getInstance() - .chatManager.sendMessageReadAck(msg) + .chatManager.markMessageAsRead(convId, convType, msgId) .then(() => { - console.log("send message read success"); + console.log("markMessageAsRead success."); }) - .catch((reason) => { - console.log("send message read fail.", reason); + .catch((e) => { + console.log("markMessageAsRead fail.", e); }); ``` -### 群聊已读回执 - -对于群聊,群成员发送消息时,可以设置该消息是否需要已读回执。若需要,每个群成员在阅读消息后,SDK 均会发送已读回执,即阅读该消息的群成员数量即为已读回执的数量。 - -群消息已读回执特性的使用限制如下表所示: - -| 使用限制| 默认 | 描述 | -| :--------- | :----- | :------- | -| 功能开通 | 关闭 | 若要使用该功能,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **功能配置** > **功能配置总览**> **基础功能**页签下,搜索找到 **消息已读回执(群聊)** 开通功能。具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 | -| 使用权限 | 所有群成员 | 默认情况下,所有群成员发送消息时可要求已读回执。如果仅需群主和群管理员发消息时要求已读回执,可联系商务修改。 | -| 已读回执有效期 | 3 天 | 群聊已读回执的有效期为 3 天,即群组中的消息发送时间超过 3 天,服务器不记录阅读该条消息的群组成员,也不会发送已读回执。 | -| 群规模 | 200 人 | 该特性最大支持 200 人的群组。如果超过 200 人/群,群成员发送的消息不会返回已读回执。你可以联系商务提升群成员人数上限。 | -| 查看返回已读回执数量 | 消息发送方 | 对消息返回的已读回执数量(或返回已读回执的人数),默认仅消息发送方可查看。如需所有群成员均可查看,可联系商务开通。 | +4. 消息发送方监听群组消息已读回调。 -你可以按以下步骤实现群消息已读回执特性: +群消息已读回调在 `ChatMessageEventListener#onGroupMessageRead` 中实现。 -1. 消息发送方需要知道群组消息是否已读,需要监听 `onGroupMessageRead` 事件。 +发送方接收到群组消息已读回执后,其发出消息的属性 `ChatMessage#groupAckCount` 会有相应变化。 ```typescript -class ChatMessageEvent implements ChatMessageEventListener { - onGroupMessageRead(groupMessageAcks: ChatGroupMessageAck[]): void { - // 收到消息已读 - console.log(`onGroupMessageRead: `, messages); - } - // ... -} -// 添加监听器 -const listener = new ChatMessageEvent(); -ChatClient.getInstance().chatManager.addMessageListener(listener); +ChatClient.getInstance().chatManager.addMessageListener({ + onGroupMessageRead: (messages: ChatMessage[]) => { + console.log(`onGroupMessageRead:`, messages); + }, +}); ``` -2. 发送群组消息。并设置 `needGroupAck` 为 `true`,表示需要群组消息已读回执。 +5. 消息发送方获取群组消息的已读回执详情。 + +你可以调用 `ChatManager#fetchGroupAcks` 方法从服务器获取单条消息的已读回执的详情。 ```typescript -// 发送群组消息 -// 设置本条消息需要群消息回执 -msg.needGroupAck = true; -// 执行发送消息 +const msgId; // 消息ID。 +const groupId; // 群组ID。 +const startAckId; //回执ID。 +const pageSize; // 每页信息。 ChatClient.getInstance() - .chatManager.sendMessage(msg) - .then(() => { - // 消息发送动作完成,会在这里打印日志。 - console.log("send message success."); + .chatManager.fetchGroupAcks(msgId, groupId, startAckId, pageSize) + .then((result) => { + console.log("fetchGroupAcks success.", result); }) - .catch((reason) => { - // 消息发送动作失败,会在这里打印日志。 - console.log("send message fail.", reason); + .catch((e) => { + console.log("fetchGroupAcks fail.", e); }); ``` -3. 群组里面的接收方收到消息,调用 `sendGroupMessageReadAck` 方法告知消息发送方消息已读。成功发送后,消息发送方会收到 `onMessageRead` 回调。 +### 查看消息送达和已读状态 -```typescript -// 发送已读回执 -// 需要设置消息已读的 ID -const msgId; -// 指定群组 -const groupId; -// 执行已读回执 -ChatClient.getInstance() - .chatManager.sendGroupMessageReadAck(msgId, groupId) - .then(() => { - // 消息发送动作完成,会在这里打印日志。 - console.log("send message read success."); - }) - .catch((reason) => { - // 消息发送动作失败,会在这里打印日志。 - console.log("send message read fail.", reason); - }); -``` +对于单聊消息,本地通过 `ChatMessage#hasDeliverAck` 字段存储消息送达状态。 -### 获取群组已读回执信息 +对于单聊消息,本地通过以下字段存储消息已读状态: -所有用户可以调用 `fetchGroupAcks` 方法获取指定范围的群组消息的已读回执。 +| 字段 | 描述 | +| :--------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ChatMessage#hasRead` | 用户是否已读了该消息。如果是自己发送的消息,该字段的值固定为 `true`。 | +| `ChatMessage#hasReadAck` | 是否(消息接收方)已发送或(消息发送方)已收到消息已读回执。如果是自己发送的消息,记录的是对方是否已读。如果是对方的消息,则记录的是自己是否发送过已读回执。 | -```typescript -// msgId: 消息 ID -// groupId:群组 ID -// startAckId: 查询起始的已读回执 ID。首次调用为空,SDK 从最新的已读回执开始按服务器接收回执时间的逆序获取。后续调用从 ChatCursorResult 中的 cursor 获取。 -// pageSize:期望请求的最大数量,取值范围是 0-400 -ChatClient.getInstance() - .chatManager.fetchGroupAcks(msgId, groupId, startAckId, pageSize) - .then((acks) => { - console.log("get message ack success: ", acks); - }) - .catch((reason) => { - console.log("get message ack fail.", reason); - }); -``` +对于群聊消息,本地数据库通过以下字段存储消息已读状态: -### 获取群组已读回执数目 +| 字段 | 描述 | +| :-------------------------- | :-------------------------------------------------------------------- | +| `ChatMessage#hasRead` | 用户是否已读了该消息。如果是自己发送的消息,该字段的值固定为 `true`。 | +| `ChatMessage#groupAckCount` | 已阅读消息的群成员数量。 | -所有用户可以调用 `groupAckCount` 方法通过消息 ID 找到消息,在通过消息获取群组已读回执数量。 +### 已读回执与未读消息数 -```typescript -// msgId: 消息 ID -ChatClient.getInstance() - .chatManager.groupAckCount(msgId) - .then((count) => { - console.log("get message ack count success: ", count); - }) - .catch((reason) => { - console.log("get message ack count fail.", reason); - }); -``` \ No newline at end of file +- 会话已读回执发送后,开发者需要调用 `ChatConversation#markAllMessagesAsRead` 方法将该会话的所有消息置为已读,即会话的未读消息数清零。 + +- 消息已读回执发送后,开发者需要调用 `ChatConversation#markMessageAsRead` 方法将该条消息置为已读,则消息未读数会有变化。 diff --git a/docs/document/react-native/message_retrieve.md b/docs/document/react-native/message_retrieve.md index 7f825a00..2becd451 100644 --- a/docs/document/react-native/message_retrieve.md +++ b/docs/document/react-native/message_retrieve.md @@ -25,7 +25,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,并连接到服务器,详见 [初始化](initialization.html)及[连接](connection.html)文档。 - 了解环信即时通讯 IM API 的使用限制,详见 [使用限制](/product/limitation.html)。 ## 实现方法 @@ -50,7 +50,7 @@ 3. 历史消息在服务器上的存储时间与产品的套餐包相关,详见[产品套餐包详情](/product/pricing.html#套餐包功能详情)。 ::: -```tsx +```typescript // convId 会话 ID:单聊、群聊和聊天室分别为对端用户 ID、群组 ID 和聊天室 ID。 // convType 会话类型:单聊、群聊和聊天室分别为 PeerChat、GroupChat 和 RoomChat。 // cursor 查询的起始消息 ID。若该参数设置为空字符串,从最新消息开始。 diff --git a/docs/document/react-native/message_search.md b/docs/document/react-native/message_search.md index 8f671a75..610832b4 100644 --- a/docs/document/react-native/message_search.md +++ b/docs/document/react-native/message_search.md @@ -17,7 +17,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,并连接到服务器,详见 [初始化](initialization.html)及[连接](connection.html)文档。 - 了解环信即时通讯 IM API 的使用限制,详见 [使用限制](/product/limitation.html)。 ## 实现方法 diff --git a/docs/document/react-native/message_send_receive.md b/docs/document/react-native/message_send_receive.md index fc7e281f..ad684c1c 100644 --- a/docs/document/react-native/message_send_receive.md +++ b/docs/document/react-native/message_send_receive.md @@ -2,49 +2,22 @@ -登录即时通讯服务后,用户可以在单聊、群聊、聊天室中发送如下类型的消息: +环信即时通讯 IM React Native SDK 通过 `ChatManager` 类和 `ChatMessage` 类实现文本、图片、音频、视频和文件等类型的消息的发送和接收。 -- 文字消息,包含超链接和表情消息。 -- 附件消息,包含图片、语音、视频及文件消息。 -- 位置消息。 -- 透传消息。 -- 自定义消息。 -- 合并消息。 +- 对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。 -对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 +- 对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 -针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 - -本文介绍如何使用即时通讯 IM React Native SDK 实现发送和接收这些类型的消息。 - -## 技术原理 - -环信即时通讯 IM React Native SDK 通过 `ChatManager` 类和 `ChatMessage` 类实现消息的发送和接收。 - -其中,发送和接收消息的逻辑如下: - -1. 发送方调用相应创建方法创建文本、文件、附件等类型的消息; -2. 发送方调用发送方法发送消息; -3. 通过监听器接收消息。 - -消息收发流程如下: - -1. 用户 A 发送一条消息到环信的消息服务器; -2. 单聊消息时,服务器投递消息给用户 B;对于群聊时消息,服务器投递给群内其他每一个成员; -3. 用户收到消息。 - -![img](/images/android/sendandreceivemsg.png) +单聊、群组聊天和聊天室的消息发送控制,详见[消息发送控制](/product/product_message_overview.html#消息发送控制)文档。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html)。 +- 完成 SDK 初始化,详见 [初始化文档](initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -## 实现方法 - -### 发送文本消息 +## 发送和接收文本消息 1. 首先,利用 `ChatMessage` 类构造一条消息。 @@ -53,7 +26,7 @@ 示例代码: ```typescript -// 设置发送的消息类型。消息类型共支持 8 种。具体详见 `ChatMessageType` 枚举类型。 +// 设置发送的消息类型。详见 `ChatMessageType` 枚举类型。 const messageType = ChatMessageType.TXT; // 设置消息接收对象。单聊时为对端用户 ID、群聊时为群组 ID,聊天室时为聊天室 ID。 const targetId = "john"; @@ -91,28 +64,13 @@ ChatClient.getInstance() }); ``` -对于聊天室消息,可设置消息优先级。示例代码如下: - -```typescript -// 对于聊天室消息,还可以设置消息优先级。 -if (msg.chatType === ChatMessageChatType.ChatRoom) { - msg.messagePriority = priority; -} -``` - 2. 通过 `ChatManager` 将该消息发出。发送消息时可以设置 `EMCallBack` 的实例,获取消息发送状态。 ```typescript ChatClient.getInstance().chatManager.sendMessage(msg!, callback).then().catch(); ``` -若初始化时打开了 `ChatOptions#useReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 - -### 接收消息 - -你可以用注册监听 `ChatMessageEventListener` 接收消息。 - -该监听可添加多次,可在不需要的时移除。 +3. 你可以用注册监听 `ChatMessageEventListener` 接收消息。该监听可添加多次,可在不需要的时移除。 在新消息到来时,你会收到 `onMessagesReceived` 的回调,消息接收时可能是一条,也可能是多条。你可以在该回调里遍历消息队列,解析并显示收到的消息。若在初始化时打开了 `ChatOptions#messagesReceiveCallbackIncludeSend` 开关,则该回调中会返回发送成功的消息。 @@ -138,7 +96,7 @@ ChatClient.getInstance().chatManager.removeMessageListener(listener); ChatClient.getInstance().chatManager.removeAllMessageListener(); ``` -### 发送和接收附件类型的消息 +## 发送和接收附件类型的消息 除文本消息外,SDK 还支持发送附件类型消息,包括语音、图片、视频和文件消息。 @@ -148,20 +106,7 @@ ChatClient.getInstance().chatManager.removeAllMessageListener(); 2. 接收附件消息。SDK 自动下载语音消息,默认自动下载图片和视频的缩略图。若下载原图、视频和文件,需调用下载附件方法。 3. 获取附件的服务器地址和本地路径。 -此外,发送附件类型消息时,可以在 `onProgress` 回调中获取附件上传的进度,以百分比表示,示例代码如下: - -```typescript -ChatClient.getInstance() - .chatManager.sendMessage(msg, { - onProgress(localMsgId: string, progress: number): void { - console.log("send message progress."); - }, - } as ChatMessageStatusCallback) - .then() - .catch(); -``` - -#### 发送和接收语音消息 +### 发送和接收语音消息 发送和接收语音消息的过程如下: @@ -186,7 +131,7 @@ EMClient.getInstance().chatManager().sendMessage(msg, callback).then().catch(); 4. 接收方收到 `onMessagesReceived` 回调,消息对象属性包括语音文件的服务器地址 `msg.body.remotePath`或本地路径 `msg.body.localPath`,从而获取语音文件。 -#### 发送和接收图片消息 +### 发送和接收图片消息 发送和接收图片消息的流程如下: @@ -228,7 +173,7 @@ ChatClient.getInstance() .catch(); ``` -3. 对于接收方,收到 `onMessagesReceived` 回调,调用 `downloadAttachment` 下载原图。 +3. 接收方收到 [onMessageReceived 回调](#发送和接收文本消息),调用 `downloadAttachment` 下载原图。 ```typescript ChatClient.getInstance() @@ -239,7 +184,7 @@ ChatClient.getInstance() 4. 获取图片消息的附件信息可以通过图片消息的消息体对象 `body` 获取。 -#### 发送和接收视频消息 +### 发送和接收视频消息 发送和接收视频消息的流程如下: @@ -268,9 +213,9 @@ const msg = ChatMessage.createVideoMessage(targetId, filePath, chatType, { EMClient.getInstance().chatManager().sendMessage(msg, callback).then().catch(); ``` -3. 接收方收到视频消息时,自动下载视频缩略图,和[图片消息缩略图](#发送和接收图片消息)类似。 +3. 接收方收到视频消息时,自动下载视频缩略图。你可以设置自动或手动下载视频缩略图,该设置与图片缩略图相同,详见[设置图片缩略图自动下载](#发送和接收图片消息)。 -4. 对于接收方,收到 `onMessagesReceived` 回调,可以调用 `downloadAttachment` 方法下载视频原文件,和[图片消息附件下载](#发送和接收图片消息)类似。 +4. 接收方收到 [onMessageReceived 回调](#发送和接收文本消息),可以调用 `downloadAttachment` 方法下载视频原文件。 ```typescript ChatClient.getInstance() @@ -281,7 +226,7 @@ ChatClient.getInstance() 5. 视频消息的信息可以通过消息体 `body` 对象获取。 -#### 发送和接收文件消息 +### 发送和接收文件消息 发送和接收文件消息的流程如下: @@ -308,9 +253,9 @@ ChatClient.getInstance() .catch(); ``` -3. 通过文件消息对象的消息体对象 `body`获取文件信息。 +3. 通过文件消息对象的消息体对象 `body` 获取文件信息。 -### 发送和接收位置消息 +## 发送和接收位置消息 当你要发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 @@ -330,12 +275,15 @@ const msg = ChatMessage.createLocationMessage( EMClient.getInstance().chatManager().sendMessage(msg, callback).then().catch(); ``` -### 发送和接收透传消息 +## 发送和接收透传消息 + +透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。 -透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。(透传消息不会存入本地数据库中,所以在 UI 上不会显示)。具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 “em_” 和 “easemob::” 开头的 action 为内部保留字段,注意不要使用。 +具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 `em_` 和 `easemob::` 开头的 action 为内部保留字段,注意不要使用。 :::tip -透传消息发送后,不支持撤回。 +- 透传消息发送后,不支持撤回。 +- 透传消息不会存入本地数据库中,所以在 UI 上不会显示。 ::: ```typescript @@ -357,55 +305,13 @@ let listener = new (class implements ChatMessageEventListener { ChatClient.getInstance().chatManager.addMessageListener(listener); ``` -#### 通过透传消息实现输入指示器 - -输入指示器显示其他用户何时输入消息。通过该功能,用户之间可进行有效沟通,增加了用户对聊天应用中交互的期待感。 - -你可以通过透传消息实现输入指示器。下图为输入指示器的工作原理。 - -![img](/images/common/typing_indicator.png) - -监听用户 A 的输入状态。一旦有文本输入,通过透传消息将输入状态发送给用户 B,用户 B 收到该消息,了解到用户 A 正在输入文本。 - -- 用户 A 向用户 B 发送消息,通知其开始输入文本。 -- 收到消息后,如果用户 B 与用户 A 的聊天页面处于打开状态,则显示用户 A 的输入指示器。 -- 如果用户 B 在几秒后未收到用户 A 的输入,则自动取消输入指示器。 - -:::tip - -用户 A 可根据需要设置透传消息发送间隔。 - -::: - -以下示例代码展示如何发送输入状态的透传消息。 - -```typescript -// 发送自己在输入状态中的命令消息 -const action = "inputting"; -const msg = ChatMessage.createCmdMessage(targetId, action, chatType); -EMClient.getInstance().chatManager().sendMessage(msg, callback).then().catch(); -``` - -以下示例代码展示如何接受和解析输入状态的透传消息。 +## 发送自定义类型消息 -```typescript -let listener = new (class implements ChatMessageEventListener { - onCmdMessagesReceived(messages: ChatMessage[]): void { - // 收到命令消息 - for (msg of messages) { - // 过略消息 - if (msg.body.action === "inputting") { - // todo: 界面显示正在输入中的状态 - } - } - } -})(); -ChatClient.getInstance().chatManager.addMessageListener(listener); -``` +除了几种消息之外,你可以自己定义消息类型,方便业务处理,即首先设置一个消息类型名称,然后可添加多种自定义消息。 -### 发送自定义类型消息 +接收自定义消息与其他类型消息一致,详见[接收文本消息](#发送和接收文本消息)。 -除了几种消息之外,你可以自己定义消息类型,方便业务处理,即首先设置一个消息类型名称,然后可添加多种自定义消息。 +以下为创建和发送自定义类型消息的示例代码: ```typescript // 构建自定义消息 @@ -418,7 +324,7 @@ const msg = ChatMessage.createCustomMessage(targetId, event, chatType, { EMClient.getInstance().chatManager().sendMessage(msg, callback).then().catch(); ``` -### 发送和接收合并消息 +## 发送和接收合并消息 为了方便消息互动,即时通讯 IM 自 1.2.0 版本开始支持将多个消息合并在一起进行转发,例如,发送聊天记录。 @@ -461,11 +367,11 @@ const msg = ChatMessage.createCombineMessage(targetId, msgIdList, chatType, { EMClient.getInstance().chatManager().sendMessage(msg, callback).then().catch(); ``` -接收合并消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +#### 接收和解析合并消息 -对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 +接收合并消息与接收普通消息的操作相同,详见[接收消息](#发送和接收文本消息)。 -#### 解析合并消息 +对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 合并消息实际上是一种附件消息。收到合并消息后,你可以调用 `fetchCombineMessageDetail` 方法获取原始消息列表。 @@ -486,7 +392,7 @@ ChatClient.getInstance() }); ``` -### 发送和接收定向消息 +## 发送和接收定向消息 发送定向消息是指向群组或聊天室的单个或多个指定的成员发送消息,其他成员不会收到该消息。 @@ -517,9 +423,9 @@ ChatClient.getInstance().chatManager.sendMessage(msg, { } as ChatMessageStatusCallback); ``` -接收群定向消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +接收群定向消息与接收普通消息的操作相同,详见[接收消息](#发送和接收文本消息)。 -### 使用消息扩展字段 +## 使用消息扩展字段 当 SDK 提供的消息类型不满足需求时,你可以通过消息扩展字段传递自定义的内容,从而生成自己需要的消息类型。 @@ -536,6 +442,42 @@ msg.attributes = { EMClient.getInstance().chatManager().sendMessage(msg, callback).then().catch(); ``` -## 注意事项 +## 更多 + +### 设置聊天室消息优先级 + +针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 + +对于聊天室消息,可设置消息优先级,包括高、普通和低优先级。示例代码如下: + +```typescript +// 对于聊天室消息,还可以设置消息优先级。 +if (msg.chatType === ChatMessageChatType.ChatRoom) { + msg.messagePriority = priority; +} +``` + +### 获取发送附件消息的进度 + +发送附件类型消息时,可以在 `onProgress` 回调中获取附件上传的进度,以百分比表示,示例代码如下: + +```typescript +ChatClient.getInstance() + .chatManager.sendMessage(msg, { + onProgress(localMsgId: string, progress: number): void { + console.log("send message progress."); + }, + } as ChatMessageStatusCallback) + .then() + .catch(); +``` + +### 发送消息前的内容审核 + +- 内容审核关注消息 body [内容审核服务会关注消息 body 中指定字段的内容,不同类型的消息审核不同的字段](/product/moderation/moderation_mechanism.html),若创建消息时在这些字段中传入了很多业务信息,可能会影响审核效果。因此,创建消息时需要注意内容审核的字段不涉及业务信息,建议业务信息放在扩展字段中。 + +- 设置发送方收到内容审核替换后的内容 + +若初始化时打开了 `ChatOptions#useReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 \ No newline at end of file diff --git a/docs/document/react-native/message_translation.md b/docs/document/react-native/message_translation.md index 7e2d76d1..5b563582 100644 --- a/docs/document/react-native/message_translation.md +++ b/docs/document/react-native/message_translation.md @@ -11,7 +11,7 @@ 开始前,请确保满足以下条件: -1. 完成 SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +1. 完成 SDK 初始化,详见 [初始化](initialization.html)文档。 2. 了解环信即时通讯 IM API 的 [使用限制](/product/limitation.html)。 3. 已在 [环信即时通讯云控制台](https://console.easemob.com/user/login) 开通翻译功能。 4. 该功能由 Microsoft Azure Translation API 提供,因此开始前请确保你了解该功能支持的目标语言。详见 [翻译语言支持](https://learn.microsoft.com/zh-cn/azure/ai-services/translator/language-support)。 diff --git a/docs/document/react-native/message_update.md b/docs/document/react-native/message_update.md index 8998d40d..12d23377 100644 --- a/docs/document/react-native/message_update.md +++ b/docs/document/react-native/message_update.md @@ -14,7 +14,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,并连接到服务器,详见 [初始化](initialization.html)及[连接](connection.html)文档。 - 了解环信即时通讯 IM API 的使用限制,详见 [使用限制](/product/limitation.html)。 ## 实现方法 diff --git a/docs/document/react-native/moderation.md b/docs/document/react-native/moderation.md index 2f85a408..25829678 100644 --- a/docs/document/react-native/moderation.md +++ b/docs/document/react-native/moderation.md @@ -19,7 +19,7 @@ 开始前,请确保满足以下条件: -1. 完成 SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +1. 完成 SDK 初始化,详见 [初始化](initialization.html)文档。 2. 了解环信即时通讯 IM API 的 [使用限制](/product/limitation.html)。 3. 已在 [环信即时通讯云控制台开通消息举报功能](/product/enable_and_configure_IM.html#消息举报)。 diff --git a/docs/document/react-native/multi_device.md b/docs/document/react-native/multi_device.md index f9c32469..56a2d999 100644 --- a/docs/document/react-native/multi_device.md +++ b/docs/document/react-native/multi_device.md @@ -51,7 +51,7 @@ ## 前提条件 -开始前,请确保满足完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +开始前,请确保满足完成 SDK 初始化,并连接到服务器,详见 [初始化](initialization.html)及[连接](connection.html)文档。 ## 实现方法 @@ -110,7 +110,7 @@ ChatClient.getInstance() 即时通讯 IM 自 1.2.0 版本开始支持自定义设置登录设备的名称。这样在多设备场景下,若有设备被踢下线,你就能知道是被哪个设备挤下线的。设备名称 `deviceName` 可以通过 `getLoggedInDevicesFromServer` 返回对象 `ChatDeviceInfo` 获取。如果没有设置则返回默认值,即设备型号。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -145,7 +145,7 @@ ChatClient.getInstance() 2. 初始化 SDK。确保该方法中的 `customOSType` 参数的值与环信控制台的**添加自定义平台**对话框中设置的**设备平台**的值相同。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -172,7 +172,7 @@ ChatClient.getInstance() 初始化 SDK 时,你可以利用 `ChatOptions#loginExtraInfo` 属性设置登录设备的自定义扩展信息。设置后,若因达到了登录设备数量限制而导致在已登录的设备上强制退出时(`206` 错误,Android 为 `USER_LOGIN_ANOTHER_DEVICE`,iOS 为 `EMErrorUserLoginOnAnotherDevice`),被踢设备收到的 `ChatConnectEventListener#onUserDidLoginFromOtherDeviceWithInfo` 回调会包含导致该设备被踢下线的新登录设备的自定义扩展信息。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: @@ -217,7 +217,7 @@ ChatClient.getInstance() 你可以调用 `kickDevice` 方法将指定账号从单个登录设备踢下线。调用该方法前,你需要首先通过 `ChatClient#getLoggedInDevicesFromServer` 方法获取设备 ID。 -:::notice +:::tip 不登录也可以使用该接口。 ::: @@ -242,7 +242,7 @@ ChatClient.getInstance() 你可以调用 `kickAllDevices` 方法通过传入用户 ID 和登录密码或用户 token 将指定账号从所有登录设备踢下线。 -:::notice +:::tip 不登录也可以使用该接口。 ::: diff --git a/docs/document/react-native/overview.md b/docs/document/react-native/overview.md index b45c056f..2e7e77f0 100644 --- a/docs/document/react-native/overview.md +++ b/docs/document/react-native/overview.md @@ -96,7 +96,7 @@ ChatClient.getInstance() }); ``` -:::notice +:::tip 该注册模式为在客户端注册,旨在方便测试,并不推荐在正式环境中使用。 ::: @@ -130,7 +130,7 @@ ChatClient.getInstance() **用户 ID + token** 是更加安全的登录方式。token 可以通过调用 REST API 获取。 详见 [环信用户 token 的获取](/document/server-side/easemob_user_token.html)。 -:::notice +:::tip 使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 ::: diff --git a/docs/document/react-native/presence.md b/docs/document/react-native/presence.md index ca635a86..efe71689 100644 --- a/docs/document/react-native/presence.md +++ b/docs/document/react-native/presence.md @@ -36,7 +36,7 @@ 使用在线状态功能前,请确保满足以下条件: -1. 完成 `1.0.5 或以上版本` SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +1. 完成 `1.0.5 或以上版本` SDK 初始化,详见 [初始化](initialization.html)文档。 2. 了解环信即时通讯 IM API 的 [使用限制](/product/limitation.html)。 3. 已联系商务开通在线状态订阅功能。 diff --git a/docs/document/react-native/push/push_display.md b/docs/document/react-native/push/push_display.md index 3677f8b3..2736fe5e 100644 --- a/docs/document/react-native/push/push_display.md +++ b/docs/document/react-native/push/push_display.md @@ -102,7 +102,7 @@ ChatClient.getInstance() 这种情况下,创建消息时无需传入 `titleArgs` 和 `contentArgs` 参数。 -```ts +```typescript const message = ChatMessage.createTextMessage("", ""); // 设置推送模板 message.attributes = { @@ -152,7 +152,7 @@ ChatClient.getInstance().chatManager.sendMessage(message, { ![img](/images/android/push/push_template_custom_example.png) -```ts +```typescript const message = ChatMessage.createTextMessage("", ""); // 设置推送模板 message.attributes = { @@ -180,7 +180,7 @@ ChatClient.getInstance().chatManager.sendMessage(message, { 若发送方在发送消息时使用了推送模板,则推送通知栏中的显示内容以发送方的推送模板为准。 ::: -```ts +```typescript ChatClient.getInstance() .pushManager.selectPushTemplate("") .then(() => { diff --git a/docs/document/react-native/push/push_get_device_token.md b/docs/document/react-native/push/push_get_device_token.md index 0835d227..bada1fe8 100644 --- a/docs/document/react-native/push/push_get_device_token.md +++ b/docs/document/react-native/push/push_get_device_token.md @@ -84,13 +84,13 @@ end 1. 添加头文件: -```objc + ```objectivec #import ``` 2. 在 `AppDelegate` 的 `- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions` 中添加代码: -```objc + ```objectivec [[PushClient sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions]; ``` @@ -98,19 +98,19 @@ end 在 `AppDelegate` 的 `- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken` 中添加代码: -```objc + ```objectivec [[PushClient sharedInstance] application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; ``` 在 `AppDelegate` 的 `- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;` 中添加代码: -```objc + ```objectivec [[PushClient sharedInstance] application:application didFailToRegisterForRemoteNotificationsWithError:error]; ``` 在 `AppDelegate` 的 `- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler` 中添加代码: -```objc + ```objectivec [[PushClient sharedInstance] application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; ``` @@ -239,7 +239,7 @@ registerActivityLifecycleCallbacks(new PushActivityLifecycleCallbacks()); #### 步骤一 初始化推送 SDK -```tsx +```typescript import { ChatPushClient, getPlatform, @@ -277,7 +277,7 @@ ChatPushClient.getInstance() 添加推送监听器,通过该监听器获取推送 token 或者失败结果。 -```tsx +```typescript ChatPushClient.getInstance().addListener({ onError: (error) => { ToastAndroid.show("onError" + JSON.stringify(error), ToastAndroid.SHORT); @@ -292,7 +292,7 @@ ChatPushClient.getInstance().addListener({ 通过 `onReceivePushToken` 接收推送 Token。 -```tsx +```typescript ChatPushClient.getInstance() .getTokenAsync() .then(() => { diff --git a/docs/document/react-native/push/push_send_token_to_server.md b/docs/document/react-native/push/push_send_token_to_server.md index f8e25af9..a9b3ff9c 100644 --- a/docs/document/react-native/push/push_send_token_to_server.md +++ b/docs/document/react-native/push/push_send_token_to_server.md @@ -23,7 +23,7 @@ yarn add react-native-chat-sdk ![img](/images/react-native/push/push_get_certificate_name.png) -```tsx +```typescript import { getPlatform, getDeviceType } from "react-native-push-collection"; import { ChatClient, ChatOptions, ChatPushConfig } from "react-native-chat-sdk"; @@ -48,7 +48,7 @@ const pushType = React.useMemo(() => { ### 步骤三 初始化即时通讯 IM SDK -```tsx +```typescript ChatClient.getInstance() .init( new ChatOptions({ @@ -69,7 +69,7 @@ ChatClient.getInstance() ### 步骤四 初始化推送 SDK -```tsx +```typescript ChatPushClient.getInstance() .init({ platform: getPlatform(), @@ -99,7 +99,7 @@ ChatPushClient.getInstance() ### 步骤五 更新服务端的推送 Token -```tsx +```typescript ChatClient.getInstance() .updatePushConfig( new ChatPushConfig({ diff --git a/docs/document/react-native/reaction.md b/docs/document/react-native/reaction.md index 9c9be58a..5ea4cb59 100644 --- a/docs/document/react-native/reaction.md +++ b/docs/document/react-native/reaction.md @@ -23,7 +23,7 @@ Reaction 场景示例如下: 开始前,请确保满足以下条件: -1. 完成 `1.0.5 或以上版本` SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +1. 完成 `1.0.5 或以上版本` SDK 初始化,详见 [初始化](initialization.html)文档。 2. 了解环信即时通讯 IM API 的 [使用限制](/product/limitation.html)。 3. 已联系商务开通 Reaction 功能。 diff --git a/docs/document/react-native/releasenote.md b/docs/document/react-native/releasenote.md index 03972894..f6bf02f0 100644 --- a/docs/document/react-native/releasenote.md +++ b/docs/document/react-native/releasenote.md @@ -7,7 +7,7 @@ ### 新增特性 - 依赖的原生 SDK 升级到版本:iOS 4.11.0 和 Android 4.11.0。 -- 更新服务器连接状态监听器 `ChatConnectEventListener`,新增 `onOfflineMessageSyncStart` 和 `onOfflineMessageSyncFinish` 事件,表示[从服务器拉取离线消息的开始和结束](overview.html#连接状态相关)。 +- 更新服务器连接状态监听器 `ChatConnectEventListener`,新增 `onOfflineMessageSyncStart` 和 `onOfflineMessageSyncFinish` 事件,表示[从服务器拉取离线消息的开始和结束](connection.html)。 - 新增 `ChatManager#getMessageCount` 方法,用于获取数据库中的消息总数。 ### 优化 @@ -37,7 +37,7 @@ ### 优化 - 原生平台优化弱网服务器连接成功率。 -- 作废 `ChatConnectEventListener` 中的 `onUserDidLoginFromOtherDevice` 事件,由 [onUserDidLoginFromOtherDeviceWithInfo](overview.html#连接状态相关) 替代。 +- 作废 `ChatConnectEventListener` 中的 `onUserDidLoginFromOtherDevice` 事件,由 [onUserDidLoginFromOtherDeviceWithInfo](connection.html) 替代。 - 作废 `fetchHistoryMessages` 接口,由 [fetchHistoryMessagesByOptions](message_retrieve.html#从服务器获取指定会话的消息) 替代。 - 作废 `joinChatRoom` 接口,由 [joinChatRoomEx](room_manage.html#加入聊天室) 替代。 diff --git a/docs/document/react-native/room_attributes.md b/docs/document/react-native/room_attributes.md index 9cd46841..8380bcfe 100644 --- a/docs/document/react-native/room_attributes.md +++ b/docs/document/react-native/room_attributes.md @@ -19,7 +19,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,详见 [初始化](initialization.html)文档。 - 了解环信即时通讯 IM 的 [使用限制](/product/limitation.html)。 - 了解聊天室的数量限制,详见 [套餐包详情](https://www.easemob.com/pricing/im)。 diff --git a/docs/document/react-native/room_manage.md b/docs/document/react-native/room_manage.md index 84869390..f0a47381 100644 --- a/docs/document/react-native/room_manage.md +++ b/docs/document/react-native/room_manage.md @@ -23,7 +23,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,详见 [初始化](initialization.html)文档。 - 了解环信即时通讯 IM 的 [使用限制](/product/limitation.html)。 - 了解环信即时通讯 IM 不同版本的聊天室相关数量限制,详见 [环信即时通讯 IM 价格](https://www.easemob.com/pricing/im)。 - 只有超级管理员才有创建聊天室的权限,因此你还需要确保已调用 RESTful API 添加了超级管理员,详见 [添加聊天室超级管理员](/document/server-side/chatroom_superadmin.html)。 diff --git a/docs/document/react-native/room_members.md b/docs/document/react-native/room_members.md index ee0226bb..f63e4783 100644 --- a/docs/document/react-native/room_members.md +++ b/docs/document/react-native/room_members.md @@ -20,7 +20,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,详见 [初始化](initialization.html)文档。 - 了解环信即时通讯 IM 的 [使用限制](/product/limitation.html)。 - 了解环信即时通讯 IM 聊天室相关限制,详见 [环信即时通讯 IM 价格](https://www.easemob.com/pricing/im)。 @@ -240,7 +240,7 @@ ChatClient.getInstance() 仅聊天室所有者和管理员可以调用 `muteChatRoomMembers` 方法将指定成员添加至聊天室禁言列表。被禁言后,该成员和其他未操作的聊天室管理员或聊天室所有者收到 `onMuteListAddedV2` 回调。 -:::notice +:::tip 聊天室所有者可禁言聊天室所有成员,聊天室管理员可禁言聊天室普通成员。 ::: @@ -262,7 +262,7 @@ ChatClient.getInstance() 仅聊天室所有者和管理员可以调用 `unMuteChatRoomMembers` 方法将成员移出聊天室禁言列表。被解除禁言后,该成员和其他未操作的聊天室管理员或聊天室所有者收到 `onMuteListRemoved` 回调。 -:::notice +:::tip 聊天室所有者可对聊天室所有成员解除禁言,聊天室管理员可对聊天室普通成员解除禁言。 ::: diff --git a/docs/document/react-native/thread.md b/docs/document/react-native/thread.md index 857516fe..cfed9e9b 100644 --- a/docs/document/react-native/thread.md +++ b/docs/document/react-native/thread.md @@ -22,7 +22,7 @@ 开始前,请确保满足以下条件: -- 完成 `1.0.5 或以上版本` SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 `1.0.5 或以上版本` SDK 初始化,详见 [初始化](initialization.html)文档。 - 了解环信即时通讯 IM API 的 [使用限制](/product/limitation.html)。 - 了解子区和子区成员数量限制,详见 [使用限制](/product/limitation.html)。 - 联系商务开通子区功能。 @@ -59,7 +59,7 @@ ChatClient.getInstance() 单设备登录时,子区所属群组的所有成员均会收到 `ChatMessageEventListener#onChatMessageThreadDestroyed` 回调;多设备登录时,其他设备会同时收到 `ChatMultiDeviceEventListener#onThreadEvent` 回调,回调事件为 `THREAD_DESTROY`。 -:::notice +:::tip 解散子区或解散子区所在的群组后,将删除本地数据库及内存中关于该子区的全部数据,需谨慎操作。 ::: diff --git a/docs/document/react-native/thread_message.md b/docs/document/react-native/thread_message.md index 53aacbd7..1c6c4964 100644 --- a/docs/document/react-native/thread_message.md +++ b/docs/document/react-native/thread_message.md @@ -6,20 +6,11 @@ ## 技术原理 -即时通讯 IM React Native SDK 提供 `ChatManager`、`ChatMessage` 和 `ChatMessageThread` 类,用于管理子区消息,支持你通过调用 API 在项目中实现如下功能: +即时通讯 IM React Native SDK 提供 `ChatManager`、`ChatMessage` 和 `ChatMessageThread` 类,用于管理子区消息,支持你通过调用 API 在项目中实现发送、接收、撤回和获取子区消息。 -- 发送子区消息 -- 接收子区消息 -- 撤回子区消息 -- 获取子区消息 +消息收发流程如下: -![img](/images/android/sendandreceivemsg.png) - -如上图所示,消息收发流程如下: - -1. 用户 A 发送一条消息到消息服务器; -2. 对于子区消息,服务器投递给子区内其他每一个成员; -3. 用户收到消息。 +客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 子区创建和查看如下图: @@ -29,7 +20,7 @@ 开始前,请确保满足以下条件: -- 已集成 `1.0.5 或以上版本` SDK 的基本功能,完成 SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 已集成 `1.0.5 或以上版本` SDK 的基本功能,完成 SDK 初始化,详见 [初始化](initialization.html)文档。 - 了解即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 联系商务开通子区功能。 @@ -39,7 +30,7 @@ ### 发送子区消息 -发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html#发送消息)。唯一不同的是,发送子区消息需要指定标记 `isChatThread` 为 `true`。 +发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html)。唯一不同的是,发送子区消息需要指定标记 `isChatThread` 为 `true`。 示例代码如下: @@ -69,7 +60,7 @@ ChatClient.getInstance() ### 接收子区消息 -接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#接收消息),此处只介绍子区消息和其他消息的区别。 +接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#发送和接收文本消息),此处只介绍子区消息和其他消息的区别。 子区有新增消息时,子区所属群组的所有成员收到 `ChatMessageEventListener#onChatMessageThreadUpdated` 回调,子区成员收到 `ChatMessageEventListener#onMessagesReceived` 回调。 diff --git a/docs/document/react-native/user_relationship.md b/docs/document/react-native/user_relationship.md index 272a9cde..ab36cb35 100644 --- a/docs/document/react-native/user_relationship.md +++ b/docs/document/react-native/user_relationship.md @@ -29,7 +29,7 @@ SDK 提供用户关系管理功能,包括好友列表管理和黑名单管理 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,并连接到服务器,详见 [初始化](initialization.html)文档。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 ## 实现方法 diff --git a/docs/document/react-native/userprofile.md b/docs/document/react-native/userprofile.md index 234f158f..53137913 100644 --- a/docs/document/react-native/userprofile.md +++ b/docs/document/react-native/userprofile.md @@ -8,7 +8,7 @@ 本文介绍如何通过管理用户属性设置、更新、存储并获取实时消息用户的相关信息。 -:::notice +:::tip 为保证用户信息安全,SDK 仅支持 Chat 用户设置或更新自己的用户属性。 ::: @@ -24,7 +24,7 @@ 设置用户属性前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,详见 [初始化](initialization.html)文档。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 ## 实现方法 diff --git a/docs/document/server-side/account_system.md b/docs/document/server-side/account_system.md index b8b58a32..26aeb346 100644 --- a/docs/document/server-side/account_system.md +++ b/docs/document/server-side/account_system.md @@ -274,7 +274,7 @@ POST https://{host}/{org_name}/{app_name}/users ##### 请求 body -:::notice +:::tip 单次请求最多可注册 60 个用户 ID。 ::: diff --git a/docs/document/server-side/callback_offline_push.md b/docs/document/server-side/callback_offline_push.md index 1bd06258..38ee92f7 100644 --- a/docs/document/server-side/callback_offline_push.md +++ b/docs/document/server-side/callback_offline_push.md @@ -68,7 +68,7 @@ | `invalid message` | 无效的消息(协议内容一般不会有这个错误)。 | | `expire message` | 过期的消息,推送延迟超过一天的消息,不再推送。 | | `user ignore push` | 主动免打扰。 | -| `ignore push device id` | 忽略用户设备推送(扩展限制接收或不接受推送设备)。 | +| `ignore push device id` | 忽略用户设备推送(扩展限制接收或不接收推送设备)。 | | `invalid VOIP notification` | 无效的 APNs VoIP 类型推送。 | | `get push token fail` | 获取推送 token 失败。 | | `push yet but fail ` | 已经推送,但是返回失败。 | diff --git a/docs/document/server-side/chatroom_manage.md b/docs/document/server-side/chatroom_manage.md index c0ba4c60..e30b3755 100644 --- a/docs/document/server-side/chatroom_manage.md +++ b/docs/document/server-side/chatroom_manage.md @@ -4,7 +4,7 @@ 本文介绍聊天室管理相关接口,包括添加、获取、修改和解散聊天室等相关操作。 -:::notice +:::tip 仅聊天室超级管理员具有在客户端创建聊天室的权限。 ::: @@ -167,7 +167,7 @@ GET https://{host}/{org_name}/{app_name}/users/{username}/joined_chatrooms?pagen | `pagenum` | Int | 否 | 当前页码,默认值为 1。 | | `pagesize` | Int | 否 | 每页返回的聊天室数量,取值范围为 [1,1000],默认值为 `1000`。该参数仅在分页获取时为必需。若传入的值超过了 `1000`,则返回 1000 个聊天室。 | -:::notice +:::tip 若查询参数 `pagenum` 和 `pagesize` 均不传,服务器返回用户最新加入的 500 个聊天室。 ::: @@ -378,7 +378,7 @@ POST https://{host}/{org_name}/{app_name}/chatrooms | :------------ | :----- | :------- | :------------------------------------------------------------------------------ | | `name` | String | 是 | 聊天室名称,最大长度为 128 个字符。 | | `description` | String | 是 | 聊天室描述,最大长度为 512 个字符。 | -| `maxusers` | Int | 否 | 聊天室最大成员数(包括聊天室所有者)。取值范围为 [1,10,000],默认值为 `1000`。如需调整请联系商务。 | +| `maxusers` | Int | 否 | 聊天室最大成员数(包括聊天室所有者)。取值范围为 [1, 10,000],默认值为 `1000`。如需调整请联系商务。 | | `owner` | String | 是 | 聊天室所有者。 | | `members` | Array | 否 | 聊天室普通成员和管理员的用户 ID 数组,不包含聊天室所有者的用户 ID。该数组可包含的元素数量不超过 `maxusers` 的值。若传该参数,确保至少设置一个数组元素。 创建聊天室时拉入的这些用户若从未登录过,会一直存在于聊天室中。 | | `custom` | String | 否 | 聊天室扩展信息,例如,可以给聊天室添加业务相关的标记,不能超过 8 KB。 | diff --git a/docs/document/server-side/java_server_sdk.md b/docs/document/server-side/java_server_sdk.md index b3bdb279..026dfef0 100644 --- a/docs/document/server-side/java_server_sdk.md +++ b/docs/document/server-side/java_server_sdk.md @@ -98,7 +98,7 @@ public class UserService { API 的返回值是响应式的,如果希望阻塞,可以使用上面例子中的 `.block()`。 -:::notice +:::tip 如果你的项目不是响应式的编程,那么请在调用的 Server SDK API 的结尾添加 `.block()` 对使用的 API 添加 try/catch ,如果使用的 API 没有抛出异常,代表请求成功,反之则请求失败,通过异常 `EMException` 对象的 `getErrorCode()/getMessage()` 拿到错误码以及错误描述。 ::: diff --git a/docs/document/server-side/message_chatroom.md b/docs/document/server-side/message_chatroom.md index dcf3cfe4..57936dea 100644 --- a/docs/document/server-side/message_chatroom.md +++ b/docs/document/server-side/message_chatroom.md @@ -80,7 +80,7 @@ POST https://{host}/{org_name}/{app_name}/messages/chatrooms 下表为发送各类消息的通用请求体,为 JSON 对象,是所有消息的外层结构。与单聊消息类似,不同类型的消息的请求体只是 `body` 字段内容存在差异。 -:::notice +:::tip 聊天室消息的通用请求体中的参数与[发送单聊消息](message_single.html)类似,唯一区别在于聊天室中的 `to` 字段表示消息接收方聊天室 ID 数组并增加了 `chatroom_msg_level` 参数用于设置消息优先级。
::: diff --git a/docs/document/server-side/message_download.md b/docs/document/server-side/message_download.md index 912d7ed7..bc8a1972 100644 --- a/docs/document/server-side/message_download.md +++ b/docs/document/server-side/message_download.md @@ -201,7 +201,7 @@ GET https://{host}/{org_name}/{app_name}/chatfiles/{file_uuid} curl -X GET -H 'Accept: application/octet-stream' -H 'Authorization: Bearer ' -H 'share-secret: f0Vr-uyyEeiHpHmsu53XXXXXXXXZYgyLkdfsZ4xo2Z0cSBnB' 'https://XXXX/XXXX/XXXX/chatfiles/7f456bf0-XXXX-XXXX-b630-777db304f26c'-o /Users/test/easemob/image/image.JPG ``` -:::notice +:::tip 上述请求示例中,`/Users/test/easemob/image/image.JPG` 为环信即时通讯 IM 的本地文件路径,使用时请替换为自己的文件路径,否则会请求失败。 ::: diff --git a/docs/document/server-side/message_group.md b/docs/document/server-side/message_group.md index c075e910..586c6ef2 100644 --- a/docs/document/server-side/message_group.md +++ b/docs/document/server-side/message_group.md @@ -808,7 +808,7 @@ curl -X POST -i "https://XXXX/XXXX/XXXX/messages/chatgroups" \ 你可以向群组中指定的一个或多个成员发送消息,但单次仅支持指定一个群组。对于定向消息,只有作为接收方的指定成员才能看到消息,其他群成员则看不到该消息。 -:::notice +:::tip 1. 定向消息不写入会话列表,不计入群组会话的未读消息数。 2. 定向消息不支持消息漫游功能,因此从服务器拉取漫游消息时,不包含定向消息。 3. 群组中发送的定向消息均同步给发送方。 diff --git a/docs/document/server-side/message_import.md b/docs/document/server-side/message_import.md index 2283e1ab..905aaac3 100644 --- a/docs/document/server-side/message_import.md +++ b/docs/document/server-side/message_import.md @@ -206,7 +206,7 @@ POST https://{host}/{org_name}/{app_name}/messages/chatgroups/import | `msg_timestamp` | Long | 否 | 要导入的消息的时间戳,单位为毫秒。若不传该参数,环信服务器会将导入的消息的时间戳设置为当前时间。 | | `need_download` | Bool | 否 | 是否需要下载附件并上传到服务器。
- `true`:是。这种情况下,需确保附件地址可直接访问,没有访问权限的限制。
- (默认)`false`:否。 | -:::notice +:::tip 与发送消息类似,不同类型的消息只是 `body` 字段内容存在差异。详见 [发送群聊消息](message_group.html)。 ::: diff --git a/docs/document/server-side/overview.md b/docs/document/server-side/overview.md index 0bfd2021..23662489 100644 --- a/docs/document/server-side/overview.md +++ b/docs/document/server-side/overview.md @@ -47,7 +47,7 @@ ![img](/images/applet/service_overview.png) -:::notice +:::tip 1. 为满足不同客户的业务需求,环信在多地部署了数据中心。不同数据中心的 REST API 请求域名不同。请根据您所在数据中心选择请求域名。 2. 国内 VIP 区、客服专区客户请联系商务经理索要 REST API 请求地址。 3. 支持 HTTP 和 HTTPS。 diff --git a/docs/document/server-side/presence.md b/docs/document/server-side/presence.md index cd16e986..557bc5aa 100644 --- a/docs/document/server-side/presence.md +++ b/docs/document/server-side/presence.md @@ -6,7 +6,7 @@ 关于用户的在线、离线和自定义状态的定义,详见[用户在线状态管理](/product/product_user_presence.html)。 -:::notice +:::tip 使用该特性前,你需要联系商务开通。 ::: diff --git a/docs/document/server-side/push.md b/docs/document/server-side/push.md index 49cc247d..e95de3c7 100644 --- a/docs/document/server-side/push.md +++ b/docs/document/server-side/push.md @@ -547,7 +547,7 @@ PUT https://{host}/{org}/{app}/users/{userId}/notification/{chattype}/{key} | `ignoreInterval` | String | 每天触发离线推送免打扰的时间段,精确到分钟,格式为 HH:MM-HH:MM,例如 08:30-10:00。该时间为 24 小时制,免打扰时间段的开始时间和结束时间中的小时数和分钟数的取值范围分别为 [00,23] 和 [00,59]。
该参数的设置说明如下:
- 该参数仅针对 app 生效,对单聊或群聊不生效。
- 该参数设置后,免打扰模式每天定时触发。例如,开始时间为 `08:00`,结束时间为 `10:00`,免打扰模式在每天的 8:00-10:00 内生效。若你在 11:00 设置开始时间为 `08:00`,结束时间为 `12:00`,则免打扰模式在当天的 11:00-12:00 生效,以后每天均在 8:00-12:00 生效。
- 若开始时间和结束时间相同,则全天免打扰。
- 若结束时间早于开始时间,则免打扰模式在每天的开始时间到次日的结束时间内生效。例如,开始时间为 `10:00`,结束时间为 `08:00`,则免打扰模式的在当天的 10:00 到次日的 8:00 生效。
- 目前仅支持在每天的一个指定时间段内开启免打扰模式,不支持多个免打扰时间段,新的设置会覆盖之前的设置。
- 若不设置该参数,传空字符串。
- 若该参数和 `ignoreDuration` 均设置,免打扰模式当日在这两个时间段均生效,例如,上午 8:00 将 app 级的 `ignoreInterval` 设置为 8:00-10:00,`ignoreDuration` 设置为 14400000 毫秒(4 个小时),则 app 在今天 8:00-12:00 和以后每天 8:00-10:00 处于免打扰模式。| 否 | | `ignoreDuration` | Long | 离线推送免打扰时长,单位为毫秒。该参数的取值范围为 [0,604800000],`0` 表示该参数无效,`604800000` 表示免打扰模式持续 7 天。
- 该参数对 app 和单聊和群聊均生效。
- 与 `ignoreInterval` 的设置每天生效不同,该参数为一次有效,设置后立即生效,例如,上午 8:00 将 app 层级的 `ignoreDuration` 设置为 14400000 毫秒(4 个小时),则 app 只在今天 8:00-12:00 处于免打扰模式。
- 若该参数和 `ignoreInterval` 均设置,免打扰模式当日在这两个时间段均生效,例如,上午 8:00 将 app 级的 `ignoreInterval` 设置为 8:00-10:00,`ignoreDuration` 设置为 14400000 毫秒(4 个小时),则 app 在今天 8:00-12:00 和以后每天 8:00-10:00 处于免打扰模式。 | -:::notice +:::tip 对于 app 和 app 中的所有会话,免打扰模式的设置,即 `ignoreInterval` 和 `ignoreDuration` 参数设置,优先于推送通知方式(`type`)的设置。例如,假设在 app 级别指定了免打扰时间段,并将指定会话的推送通知方式设置为 `ALL`,则 app 进入免打扰模式,你不会收到任何推送通知。 ::: diff --git a/docs/document/server-side/reaction.md b/docs/document/server-side/reaction.md index 6f5fd100..a1f08f26 100644 --- a/docs/document/server-side/reaction.md +++ b/docs/document/server-side/reaction.md @@ -343,7 +343,7 @@ GET https://{host}/{org_name}/{app_name}/reaction/user/{userId}/detail?msgId={ms | `limit` | Int | 否 | 每页显示添加 Reaction 的用户数量。取值范围为 [1,50],默认值为 `50`。 | | `cursor` | String | 否 | 查询游标,指定数据查询的起始位置,分页获取时使用。 | -:::notice +:::tip 分页获取时,服务器按用户 Reaction 添加时间的正序返回。若 `limit` 和 `cursor` 均不传值,服务器返回最早添加 Reaction 的 50 个用户。 diff --git a/docs/document/server-side/userprofile.md b/docs/document/server-side/userprofile.md index 8fe98a24..d76411c8 100644 --- a/docs/document/server-side/userprofile.md +++ b/docs/document/server-side/userprofile.md @@ -8,7 +8,7 @@ 环信 IM 提供 RESTful API 接口方便开发者管理服务端的用户属性信息。 -:::notice +:::tip 为保证用户信息安全,环信即时通讯 IM 仅支持用户本人或 app 管理员设置用户属性。 ::: diff --git a/docs/document/unity/connection.md b/docs/document/unity/connection.md new file mode 100644 index 00000000..9e55f4f7 --- /dev/null +++ b/docs/document/unity/connection.md @@ -0,0 +1,83 @@ +# 连接 + +应用客户端成功连接到环信服务器后,才能使用环信即时通讯 SDK 的收发消息等功能。 + +你调用 `loginWithToken` 或 `login` 方法登录后,客户端 SDK 会自动连接环信服务器。关于登录详情,请参见[登录文档](login.html)。 + +## 监听连接状态 + +你可以通过注册连接监听确认连接状态。 + +```C-Sharp + +// 监听器建议在初始化完成之后,登录之前设置,这样可确保收到登录通知。 +class ConnectionDelegate : IConnectionDelegate +{ + public void OnConnected() + { + } + public void OnDisconnected() + { + } + public void OnAuthFailed() + { + } + public void OnRemovedFromServer() + { + } + public void OnLoginTooManyDevice() + { + } + public void OnChangedIMPwd() + { + } + public void OnKickedByOtherDevice() + { + } + public void OnLoggedOtherDevice(string deviceName) + { + } + public void OnForbidByServer() + { + } + public void OnTokenExpired() + { + } + public void OnTokenWillExpire() + { + } + public void OnAppActiveNumberReachLimitation() + { + } + // 连接成功,开始从服务器拉取离线消息时触发。 + // 注意:如果本次登录服务器没有离线消息,不会触发该回调。 + public void OnOfflineMessageSyncStart() + { + } + // 离线用户上线后从服务器拉取离线消息结束时触发。 + // 注意:如果再拉取离线过程中因网络或其他原因导致连接断开,不会触发该回调。 + public void OnOfflineMessageSyncFinish() + { + } +} +// 添加连接监听器 +ConnectionDelegate connectionDelegate = new ConnectionDelegate(); +SDKClient.Instance.AddConnectionDelegate(connectionDelegate); + +// 移除连接监听器(退出程序时建议移除) +SDKClient.Instance.DeleteConnectionDelegate(connectionDelegate); +``` + +## 自动重连 + +登录后,如果由于网络信号弱、切换网络等引起的连接中断,SDK 会自动尝试重连。重连成功或者失败时分别会收到 `onConnected` 和 `onDisconnected` 通知。 + +不过,SDK 在以下情况下会停止自动重连。你需要调用 `login` 方法登录。 + +- 用户调用了 SDK 的登出方法 `logout` 主动退出登录。 +- 登录时鉴权错误,例如, token 无效(错误码 104)或已过期(错误码 108)。 +- 用户在其他的设备上更改了密码,导致此设备上自动登录失败,提示错误码 216。 +- 用户的账号被从服务器端删除,提示错误码 207。 +- 用户在另一设备登录,将当前设备上登录的用户踢出,提示错误码 206。 +- 用户登录设备数量超过限制,提示错误码 214。 +- 应用程序的日活跃用户数量(DAU)或月活跃用户数量(MAU)达到上限,提示错误码 8。 \ No newline at end of file diff --git a/docs/document/unity/conversation_receipt.md b/docs/document/unity/conversation_receipt.md new file mode 100644 index 00000000..09a852e5 --- /dev/null +++ b/docs/document/unity/conversation_receipt.md @@ -0,0 +1,79 @@ +# 会话已读回执 + +会话已读回执指接收方进入会话页面,阅读会话中的所有消息后,调用接口向服务器发送会话已读回执,服务器将该回执回调给消息发送方,消息发送方将会收到会话已读回调。在多端多设备登录下,接收方的其他设备也会收到该回调。 + +目前,单聊和群组聊天支持会话已读回执。本文介绍如何使用环信即时通讯 IM Unity SDK 实现会话已读回执功能。 + +会话已读回执的效果示例,如下图所示: + +![img](/images/uikit/chatuikit/feature/conversation/conversation_read.png) + +## 技术原理 + + 单聊会话已读回执实现的流程如下: + + 1. 设置 `Options#RequireAck` 为 `true` 开启已读回执功能。 + 2. 消息接收方进入会话页面,阅读消息后,调用 `SendConversationReadAck` 方法发送会话已读回执。 + 3. 消息发送方通过监听 `OnConversationRead` 回调接收会话已读回执。 + +## 前提条件 + +开始前,请确保满足以下条件: + +- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 +- 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 + + ## 实现方法 + + 参考以下步骤在单聊中实现会话已读回执: + + 1. 开启已读回执功能,即 SDK 初始化时设置 `Options#RequireAck` 为 `true`。 + + ```csharp +// 设置是否需要接收方已读确认,默认为 true +options.RequireAck = true; + ``` + + 2. 接收方发送会话已读回执。 + +消息接收方进入会话页面,查看会话中是否有未读消息。若有,调用 `SendConversationReadAck` 方法发送会话已读回执,没有则不发送。该方法为异步方法。 + +若会话中存在多条未读消息,建议调用该方法,因为若调用发送消息已读回执方法 `SendMessageReadAck`,则需要调用多次。 + +```csharp +SDKClient.Instance.ChatManager.SendConversationReadAck(conversationId, new CallBack( + onSuccess: () => + { + }, + onError: (code, desc) => + { + } +)); +``` + +3. 消息发送方监听会话已读回执的回调。 + +同一用户 ID 登录多设备的情况下,用户在一台设备上发送会话已读回执,其他设备会收到 `OnConversationRead` 回调。 + +:::tip +对于群组聊天,会话已读回执只用于清空服务端的群组会话的未读数,消息发送方不会通过 `OnConversationRead` 回调收到会话已读回执。 +::: + +```csharp +public class MyDelegate : IChatManagerDelegate +{ + //... + public void OnConversationRead(string from, string to) + { + // 添加刷新页面通知等逻辑 + } + //... +} + +MyDelegate myDelegate = new MyDelegate(); +SDKClient.Instance.ChatManager.AddChatManagerDelegate(myDelegate); +``` + +## 会话已读回执和消息未读数 + +消息接收方调用 `SendConversationReadAck` 方法发送会话已读回执,开发者可调用 `Conversation#MarkAllMessageAsRead` 方法将所有未读消息设置为已读,即将该会话的未读消息数清零。 diff --git a/docs/document/unity/group_manage.md b/docs/document/unity/group_manage.md index 5a38d54a..81b49831 100644 --- a/docs/document/unity/group_manage.md +++ b/docs/document/unity/group_manage.md @@ -74,7 +74,7 @@ SDKClient.Instance.GroupManager.CreateGroup(groupname, option, desc, members, ca - 群主和群管理员同意入群申请,申请人收到 `IGroupManagerDelegate#OnRequestToJoinAcceptedFromGroup` 回调,其他群成员收到 `IGroupManagerDelegate#OnMemberJoinedFromGroup` 回调; - 群主和群管理员拒绝入群申请,申请人收到 `IGroupManagerDelegate#OnRequestToJoinDeclinedFromGroup` 回调。 -:::notice +:::tip 用户只能申请加入公开群组,私有群组不支持用户申请入群。 ::: @@ -111,7 +111,7 @@ SDKClient.Instance.GroupManager.JoinPublicGroup(groupId, new CallBack( 仅群主可以调用 `DestroyGroup` 方法解散群组。群组解散时,其他群组成员收到 `OnDestroyedFromGroup` 回调并被踢出群组。 -:::notice +:::tip 该操作只有群主才能进行,是危险操作,解散群组后,将删除本地数据库及内存中的群相关信息及群会话。 ::: diff --git a/docs/document/unity/group_members.md b/docs/document/unity/group_members.md index 5b056cdf..7932faab 100644 --- a/docs/document/unity/group_members.md +++ b/docs/document/unity/group_members.md @@ -104,7 +104,7 @@ SDKClient.Instance.GroupManager.SetMemberAttributes(groupId, userId, dict, new C 你可调用 `GroupManager#FetchMemberAttributes` 方法根据指定的属性 key 获取多个群成员的自定义属性。 -:::notice +:::tip 每次最多可获取 10 个群成员的自定义属性。 ::: diff --git a/docs/document/unity/initialization.md b/docs/document/unity/initialization.md new file mode 100644 index 00000000..5fd98786 --- /dev/null +++ b/docs/document/unity/initialization.md @@ -0,0 +1,23 @@ +# SDK 初始化 + +初始化是使用 SDK 的必要步骤,需在所有接口方法调用前完成。 + +如果进行多次初始化操作,只有第一次初始化以及相关的参数生效。 + +:::tip +需要在主进程中进行初始化。 +::: + +## 前提条件 + +有效的环信即时通讯 IM 开发者账号和 App key,详见[环信即时通讯云控制台的相关文档](enable_and_configure_IM.html#创建应用)。 + +## 初始化 + +初始化示例代码: + +```csharp +var options = new Options("appkey"); //将该参数设置为你的 App Key +//其他 Options 配置。 +SDKClient.Instance.InitWithOptions(options); +``` diff --git a/docs/document/unity/integration.md b/docs/document/unity/integration.md new file mode 100644 index 00000000..dde4070e --- /dev/null +++ b/docs/document/unity/integration.md @@ -0,0 +1,22 @@ +# 集成 SDK + +本文介绍如何将环信即时通讯 IM SDK 集成到你的 Unity 项目。 + +## 开发环境要求 + +- Unity Editor 2019.4.28 或以上版本 +- Unity SDK 1.0.5 或以上 +- 操作系统与编译器要求 + +| 开发平台 | 操作系统版本 | 编译器版本 | +| :------------------- | :----- | :-------------------------------------------- | +| Android | Android 5.0 或以上 | Android Studio 3.0 或以上 | +| iOS | iOS 10 或以上 | Xcode 建议最新版本 | +| macOS | macOS 10.0 或以上 | Xcode 9.0 或以上、Visual Studio for Mac 2019 或以上 | +| Windows | Windows 10 或以上 | Microsoft Visual Studio 2019 或以上 | + +## 集成 SDK + +1. [下载 Unity SDK](https://www.easemob.com/download/im)。 + +2. 在 Unity Editor 中,选择 **Assets > Import Package > Custom Package...**,然后选择下载的 Unity package。在弹出的 **Import Unity Package** 页面,点击右下角的 **Import**,导入 Unity package。 \ No newline at end of file diff --git a/docs/document/unity/log.md b/docs/document/unity/log.md new file mode 100644 index 00000000..441c4141 --- /dev/null +++ b/docs/document/unity/log.md @@ -0,0 +1,54 @@ +# SDK 日志 + +环信即时通讯 IM 日志记录 SDK 相关的信息和事件。环信技术支持团队帮你排查问题时可能会请你发送 SDK 日志。 + +## 输出信息到日志文件 + +默认情况下,SDK 最多可生成和保存三个文件,`easemob.log` 和两个 `easemob_YYYY-MM-DD_HH-MM-SS.log` 文件。这些文件为 UTF-8 编码,每个不超过 2 MB。SDK 会将最新的日志写入 `easemob.log` 文件,写满时则会将其重命名为对应时间点的 `easemob_YYYY-MM-DD_HH-MM-SS.log` 文件,若日志文件超过三个,则会删除最早的文件。 + +例如,SDK 在 2024 年 1 月 1 日上午 8:00:00 记录日志时会生成 `easemob.log` 文件,若在 8:30:00 将 `easemob.log` 文件写满则会将其重命名为 `easemob_2024-01-01_08-30-00.log` 文件,随后在 9:30:30 和 10:30:30 分别生成了 `easemob_2024-01-01_09-30-30.log` 和 `easemob_2024-01-01_10-30-30.log` 文件,则此时 `easemob_2024-01-01_08-30-00.log` 文件会被移除。 + +SDK 默认不输出调试信息(所有日志,包括调试信息、警告和错误),只需输出错误日志。若需调试信息,首先要开启调试模式。 + +```csharp +Options options = new Options("YourAppKey"); +options.DebugMode = true; +SDKClient.Instance.InitWithOptions(options); +``` + +## 获取本地日志 + +Unity 分为 4 个端:Unity Mac、Unity Windows、Unity iOS、Unity Android。 + +- Unity Mac + +日志路径: /Users/XXX/Library/Application Support/YYY/ZZZ/sdkdata/easemobLog 或者 /Users/XXX/Library/Application Support/com.YYY.ZZZ/sdkdata/easemobLog + +XXX: Mac 用户名; YYY: Unity 中设置的公司名称,如果没有设置则为 `DefaultCompany`,ZZZ 为 app 名称。 + +- Unity Windows + +日志路径:C:\Users\XXX\AppData\LocalLow\YYY\ZZZ\sdkdata\easemobLog + +XXX:Windows 用户名; YYY: Unity 中设置的公司名称,如果没有设置则为 `DefaultCompany`,ZZZ 为 app 名称。 + +- Unity iOS + +本地日志的获取与 iOS 的相同,详见 [iOS 本地日志的获取](/document/ios/log.html#获取本地日志)。 + +日志路径:沙箱 Library/Application Support/HyphenateSDK/easemobLog。 + +以真机为例,获取本地日志过程如下: + +- 打开 Xcode,连接设备,选择 **Xcode** > **Window** > **Devices and Simulators**。 +- 进入 **Devices** 选项卡,在左侧选择目标设备,例如 Easemob IM,点击设置图标,然后选择 **Download Container**。 + +![img](/images/ios/overview_fetchlogfile.png) + +日志文件 `easemob.log` 文件在下载包的 AppData/Library/Application Support/HyphenateSDK/easemobLog 目录下。 + +- Unity Android + +在 Android Studio 中,选择 **View** > **Tool Windows** > **Device File Explorer**,然后浏览设备上的文件夹。 + +日志路径为 /data/data//sdkdata/easemobLog。 \ No newline at end of file diff --git a/docs/document/unity/login.md b/docs/document/unity/login.md new file mode 100644 index 00000000..1204fb2d --- /dev/null +++ b/docs/document/unity/login.md @@ -0,0 +1,144 @@ +# 登录 + +初始化 IM SDK 后,你需要首先调用接口登录。登录成功后,才能使用 IM 的功能。 + +## 用户注册 + +用户注册模式分为以下两种: + +- 开放注册:一般在体验 Demo 和测试环境时使用,正式环境中不推荐使用该方式注册环信账号。要使用开放注册,需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**开放注册**。只有打开该开关,才能使用客户端或 [REST API](/document/server-side/account_system.html#开放注册单个用户)开放注册用户。 + +示例代码如下所示: + +```csharp +SDKClient.Instance.CreateAccount(username, password, + callback: new CallBack( + + onSuccess: () => { + Debug.Log("CreateAccount succeed"); + }, + + onError: (code, desc) => { + Debug.Log($"CreateAccount failed, code: {code} ; desc: {desc}"); + } + ) +); +``` + +- 授权注册:通过环信提供的 REST API 注册环信用户账号,注册后保存到你的服务器或返给客户端。要使用授权注册,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**授权注册**。相关的 REST API 介绍,详见[授权注册单个用户](/document/server-side/account_system.html#授权注册单个用户)和[批量授权注册用户](/document/server-side/account_system.html#批量授权注册用户)的接口介绍。 + +除此以外,可以在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建正式环境下和测试环境下的用户,详见[创建用户相关介绍](/product/enable_and_configure_IM.html#创建-im-用户)。 + +## 主动登录 + +1. **用户 ID + token** 是更加安全的登录方式。 + +测试环境下,你在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建用户后,环信服务器会自动为这些用户分配用户 Token,详见[测试环境下创建用户的介绍](/product/enable_and_configure_IM.html#测试环境)。 + +使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 + +```csharp +SDKClient.Instance.LoginWithToken(username, token, true, + callback: new CallBack( + + onSuccess: () => + { + Debug.Log("login succeed"); + }, + + onError: (code, desc) => + { + if (code == 200) + { + Debug.Log("Already login.");; + } + else + { + Debug.Log($"login failed, code: {code} ; desc: {desc}"); + } + } + ) +); +``` + +2. **用户 ID + 密码**登录是传统的登录方式。用户名和密码均由你的终端用户自行决定,密码需要符合[密码规则要求](/document/server-side/account_system.html#开放注册单个用户)。 + +```csharp +SDKClient.Instance.Login(username, password, + callback: new CallBack( + + onSuccess: () => + { + Debug.Log("login succeed"); + }, + + onError: (code, desc) => + { + if (code == 200) + { + Debug.Log("Already login.");; + } + else + { + Debug.Log($"login failed, code: {code} ; desc: {desc}"); + } + } + ) +); + +``` + +## 自动登录 + +暂时不支持自动登录。 + +## 获取当前登录的用户 + +你可以调用 `SDKClient#CurrentUsername` 方法获取当前登录用户的用户 ID。 + +## 获取登录状态 + +你可以调用 `SDKClient#IsLoggedIn` 方法获取当前用户的登录状态。 + +## 退出登录 + +你可以调用 `Logout` 方法退出登录。退出登录后,你不会再收到其他用户发送的消息。 + +异步方法: + +```csharp +SDKClient.Instance.Logout(false, + callback: new CallBack( + onSuccess: () => + { + Debug.Log("Logout succeed"); + }, + + onError: (code, desc) => + { + Debug.Log($"Logout failed, code:{code}, desc:{desc}"); + } + ) +); +``` + +:::tip +1. 如果调用退出方法,在收到 `onSuccess` 回调后才能去调用 IM 相关方法,例如 `Login`。 +::: + +## 账号切换 + +若在 app 中从当前账号切换到其他账号,你需要首先调用 `Logout` 方法登出,然后再调用 `LoginWithToken` 或 `Login` 方法登录。 + +## 多设备登录 + +除了单端单设备登录,环信即时通讯 IM 支持同一账号在多端的多个设备上登录。多设备登录时,若同端设备数量超过限制,新登录的设备会将之前登录的设备踢下线。 + +关于多设备登录场景中的设备数量限制、互踢策略以及信息同步,详见[多设备登录文档](multi_device.html)。 + + +## 更多 + +### 登录被封禁账号的提示 + +在环信即时通讯控制台或调用 REST API 封禁用户账号后,若仍使用该账号登录,SDK会返回 "service is disabled"(305 错误), 可以根据用户这个返回值来进行相应的提示或者处理。 diff --git a/docs/document/unity/message_modify.md b/docs/document/unity/message_modify.md index 034402cc..52f005a7 100644 --- a/docs/document/unity/message_modify.md +++ b/docs/document/unity/message_modify.md @@ -27,7 +27,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,并连接到服务器,详见 [初始化](initialization.html)及[连接](connection.html)文档。 - 了解环信即时通讯 IM API 的使用限制,详见 [使用限制](/product/limitation.html)。 ## 实现方法 diff --git a/docs/document/unity/message_receipt.md b/docs/document/unity/message_receipt.md index 14134c50..ea40405a 100644 --- a/docs/document/unity/message_receipt.md +++ b/docs/document/unity/message_receipt.md @@ -1,239 +1,195 @@ -# 消息回执 +# 实现消息回执 -单聊会话支持消息送达回执、会话已读回执和消息已读回执,发送方发送消息后可及时了解接收方是否及时收到并阅读了信息,也可以了解整个会话是否已读。 +**单聊会话支持消息送达回执和消息已读回执**,发送方发送消息后可及时了解接收方是否及时收到并阅读了消息。 -群聊会话只支持消息已读回执。群成员在发送消息时,可以设置该消息是否需要已读回执。仅专业版及以上版本支持群消息已读回执功能。若要使用该功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 +**群聊会话只支持消息已读回执,不支持送达回执**。群成员在发送消息时,可以设置该消息是否需要已读回执。要使用该功能,你需要[在环信即时通讯云控制台上开通该功能](/product/enable_and_configure_IM.html#设置群消息已读回执),具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 -:::tip -仅单聊消息支持送达回执,群聊消息不支持。 -::: +消息送达回执和已读回执的效果示例,如下图所示: -本文介绍如何使用环信即时通讯 IM Unity SDK 实现单聊和群聊的消息回执功能。 +![img](/images/android/message_receipt.png) ## 技术原理 -环信即时通讯 IM Unity SDK 通过 `IChatManager` 类提供消息的送达回执和已读回执功能,包含的核心方法如下: +使用环信即时通讯 IM Unity/Windows SDK 可以实现消息的送达回执与已读回执。 -- `Options.RequireDeliveryAck` 开启送达回执; -- `IChatManager.SendConversationReadAck` 发出指定会话的已读回执; -- `IChatManager.SendMessageReadAck` 发出指定单聊消息的已读回执; -- `SendReadAckForGroupMessage` 发送群组消息的已读回执。 +- 单聊消息送达回执的逻辑如下: -实现消息送达和已读回执的逻辑分别如下: + 1. 你可以通过设置 `Options#RequireDeliveryAck` 为 `true` 开启送达回执功能。 + 2. 消息接收方收到消息后,SDK 自动向发送方触发送达回执。 + 3. 消息发送方通过监听 `IChatManagerDelegate#OnMessagesDelivered` 回调接收消息送达回执。 -- 单聊消息送达回执: +- 单聊消息已读回执的逻辑如下: - 1. 消息发送方在发送消息前通过 `ChatOptions.RequireDeliveryAck` 开启送达回执功能; + 1. 你可以通过设置 `Options#RequireAck` 为 `true` 开启已读回执功能。 + 2. 消息接收方收到消息后,调用 `ChatManager#SendMessageReadAck` 方法发送消息已读回执。 + 3. 消息发送方通过监听 `IChatManagerDelegate#OnMessagesRead` 回调接收消息已读回执。 - 2. 消息接收方收到消息后,SDK 自动向发送方触发送达回执; +- 群聊消息已读回执的逻辑如下: - 3. 消息发送方通过监听 `OnMessageDelivered` 回调接收消息送达回执。 - -- 单聊会话及消息已读回执 - - 1. 设置 `RequireAck` 为 `true`; - - 2. 消息接收方收到或阅读消息后,调用 API `SendConversationReadAck` 或 `SendMessageReadAck` 发送会话或消息已读回执; - - 3. 消息发送方通过监听 `OnConversationRead` 或 `OnMessageRead` 回调接收会话或消息已读回执。 - -- 群聊只支持消息已读回执: - - 1. 你可以通过设置 `isNeedGroupAck` 开启群聊消息已读回执功能; - - 2. 消息接收方收到或阅读消息后通过 `SendReadAckForGroupMessage` 发送群组消息的已读回执。 + 1. 你可以通过设置 `Options#RequireAck` 为 `true` 开启消息已读回执功能。 + 2. 发送方在群组中发送消息时设置 `Message#IsNeedGroupAck` 为 `true` 要求接收方返回消息已读回执。 + 3. 接收方收到或阅读消息后通过 `ChatManager#SendReadAckForGroupMessage` 方法发送群组消息的已读回执。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html); -- 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html); -- 群消息已读回执功能仅在环信 IM 专业版及以上版本支持该功能。若要使用该功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 +- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 +- 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 +- 要使用群消息已读回执功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 ## 实现方法 -### 消息送达回执 +### 单聊消息送达回执 -1. 发送方若要接收消息送达回执,你需要将 `Options` 中的 `RequireDeliveryAck` 设为 `true`。当接收方收到消息后,SDK 底层会自动进行消息送达回执。 +1. 开启消息送达功能,即 SDK 初始化时将 `Options#RequireDeliveryAck` 设置为 `true`。 ```csharp -// 设置是否需要消息送达回执,设为 `true`。 -Options.RequireDeliveryAck = true; +// 设置是否需要接收方送达确认,默认 `false` 即不需要。 +options.RequireDeliveryAck = true; ``` -2. 发送方监听事件 `OnMessagesDelivered` 回调,收到接收方的送达回执。 +2. 接收方收到消息后,SDK 自动向发送方触发送达回执。 + +3. 发送方监听 `IChatManagerDelegate#OnMessagesDelivered` 事件,收到接收方的送达回执。你可以在收到该通知时,显示消息的送达状态。 ```csharp -// 继承并实现 `IChatManagerDelegate`。 -public class ChatManagerDelegate : IChatManagerDelegate { +public class MyDelegate : IChatManagerDelegate +{ + //... - // 收到已送达回执。 - public void OnMessagesDelivered(List messages) - { - } + // 收到消息。 + public void OnMessagesReceived(List messages); { + } + // 收到已送达回执。 + public void OnMessagesDelivered(List messages); { } -// 注册监听器。 -ChatManagerDelegate adelegate = new ChatManagerDelegate(); -SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); - -// 移除监听器。 -SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(adelegate); -``` + //... +} -### 消息和会话的已读回执 +MyDelegate myDelegate = new MyDelegate(); -消息已读回执用于告知单聊或群聊中的用户接收方已阅读其发送的消息。为降低消息已读回执方法的调用次数,SDK 还支持在单聊中使用会话已读回执功能,用于获知接收方是否阅读了会话中的未读消息。 +//注册消息监听 +SDKClient.Instance.ChatManager.AddChatManagerDelegate(myDelegate); +记得在不需要的时候移除 listener +SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(myDelegate); +``` -#### 单聊 +### 单聊消息已读回执 -单聊既支持消息已读回执,也支持会话已读回执。我们建议你按照如下逻辑结合使用两种回执,减少发送消息已读回执数量。 +单聊既支持单条消息已读回执,也支持[会话已读回执](conversation_receipt.html)。我们建议你结合使用这两种回执,见实现步骤的描述。 -- 聊天页面未打开时,若有未读消息,进入聊天页面,发送会话已读回执; -- 聊天页面打开时,若收到消息,发送消息已读回执。 +单聊消息的已读回执有效期与消息在服务端的存储时间一致,即在服务器存储消息期间均可发送已读回执。消息在服务端的存储时间与你订阅的套餐包有关,详见[产品价格](/product/pricing.html#套餐包功能详情)。 -第一步是在设置中打开已读回执开关: +参考如下步骤在单聊中实现消息已读回执。 +1. App 开启已读回执功能,即 SDK 初始化时将 `Options#RequireAck` 设置为 `true`。 ```csharp -// 设置是否需要消息已读回执,设为 `true`。 -Options.RequireReadAck = true; +// 设置是否需要接收方已读确认,默认为true +options.RequireAck = true; ``` -##### 会话已读回执 - -参考以下步骤在单聊中实现会话已读回执。 - -1. 接收方发送会话已读回执。 +1. 接收方发送消息已读回执。 -消息接收方进入会话页面,查看会话中是否有未读消息。若有,发送会话已读回执,没有则不再发送。 +- 聊天页面未打开时,若有未读消息,进入聊天页面,发送会话已读回执。这种方式可避免发送多个消息已读回执。 ```csharp SDKClient.Instance.ChatManager.SendConversationReadAck(conversationId, new CallBack( - onSuccess: () => { - - }, - onError:(code, desc) => { - - } -)); -``` - -2. 消息发送方监听会话已读回执的回调。 - -```csharp -// 继承并实现 `IChatManagerDelegate`。 -public class ChatManagerDelegate : IChatManagerDelegate { - // 收到已读回执。`from` 表示发送该会话已读回执的消息接收方,`to` 表示收到该会话已读回执的消息发送方。 - public void OnConversationRead(string from, string to) + onSuccess: () => + { + UIManager.SuccessAlert(transform); + }, + onError: (code, desc) => { + UIManager.ErrorAlert(transform, code, desc); } -} - -// 注册监听器。 -ChatManagerDelegate adelegate = new ChatManagerDelegate() -SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); - -// 移除监听器。 -SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(adelegate); +)); ``` -同一用户 ID 登录多设备的情况下,用户在一台设备上发送会话已读回执,服务器会将会话的未读消息数置为 `0`,同时其他设备会收到 `OnConversationRead` 回调。 - -##### 消息已读回执 - -单聊消息的已读回执有效期与消息在服务端的存储时间一致,即在服务器存储消息期间均可发送已读回执。消息在服务端的存储时间与你订阅的套餐包有关,详见[产品价格](/product/pricing.html#套餐包功能详情)。 - -参考如下步骤在单聊中实现消息已读回执。 - -1. 接收方发送已读回执消息。 - -消息接收方进入会话时,发送会话已读回执。 +- 聊天页面打开时,若收到消息,发送单条消息已读回执。 ```csharp -SDKClient.Instance.ChatManager.SendConversationReadAck(conversationId, new CallBack( - onSuccess: () => { - - }, - onError:(code, desc) => { +public class MyDelegate : IChatManagerDelegate +{ + //... + // 收到消息。 + public void OnMessagesReceived(List messages); { + ...... + SendReadAck(message); + ...... } -)); -``` - -2.在会话页面,接收到消息时,根据消息类型发送消息已读回执,如下所示: - -```csharp -// 继承并实现 `IChatManagerDelegate`。 -public class ChatManagerDelegate : IChatManagerDelegate { - // 收到已送达回执。 - public void OnMessageReceived(List messages) - { - ...... - sendReadAck(message); - ...... - } + //... } +MyDelegate myDelegate = new MyDelegate(); -// 注册监听器。 -ChatManagerDelegate adelegate = new ChatManagerDelegate() -SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); +//注册消息监听 +SDKClient.Instance.ChatManager.AddChatManagerDelegate(myDelegate); -// 发送已读回执。 -public void sendReadAck(Message message) { +/** +* 发送已读回执。 +* @param message +*/ +public void SendReadAck(Message message) +{ // 这里是接收的消息,未发送过已读回执且是单聊。 - if(message.Direction == MessageDirection.RECEIVE - && !message.HasReadAck - && message.MessageType == MessageType.Chat) { + if (message.Direction == MessageDirection.RECEIVE + && !message.HasReadAck + && message.MessageType == MessageType.Chat) + { MessageBodyType type = message.Body.Type; - // 视频、语音及文件需要点击后再发送,可根据需求进行调整。 - if(type == MessageBodyType.VIDEO || type == MessageBodyType.VOICE || type == MessageBodyType.FILE) { + // 视频,语音及文件需要点击后再发送,可以根据需求进行调整。 + if (type == MessageBodyType.VIDEO || type == MessageBodyType.VOICE || type == MessageBodyType.FILE) + { return; } - SDKClient.Instance.ChatManager.SendMessageReadAck(message.MsgId, new CallBack( - onSuccess: () => { - - }, - onError: (code, desc) => { - - } - ); + onSuccess: () => + { + }, + onError: (code, desc) => + { + } + )); } } ``` 3. 消息发送方监听消息已读回调。 -你可以调用接口监听指定消息是否已读,示例代码如下: +消息发送方可以通过 `IChatManagerDelegate#OnMessagesRead` 事件监听指定消息是否已读,示例代码如下: ```csharp -// 继承并实现 `IChatManagerDelegate`。 -public class ChatManagerDelegate : IChatManagerDelegate { +public class MyDelegate : IChatManagerDelegate +{ + //... - // 收到消息已读回执。 - public void OnMessagesRead(string from, string to) - { - } + public void OnMessagesRead(List messages); { + // 添加刷新消息等逻辑。 + } + + //... } -// 注册监听器。 -ChatManagerDelegate adelegate = new ChatManagerDelegate() -SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); -// 移除监听器。 -SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(adelegate); +MyDelegate myDelegate = new MyDelegate(); + +//注册消息监听 +SDKClient.Instance.ChatManager.AddChatManagerDelegate(myDelegate); +记得在不需要的时候移除 listener +SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(myDelegate); ``` -#### 群聊 +### 群聊消息已读回执 -对于群聊,群成员发送消息时,可以设置该消息是否需要已读回执。若需要,每个群成员在阅读消息后,SDK 均会发送已读回执,即阅读该消息的群成员数量即为已读回执的数量。 +对于群聊,群成员发送消息时,可以设置该消息是否需要已读回执。若需要,每个群成员阅读消息后,应该调用`ChatManager#SendReadAckForGroupMessage` 方法发送已读回执,阅读该消息的群成员数量即为已读回执的数量。 群消息已读回执特性的使用限制如下表所示: @@ -247,65 +203,116 @@ SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(adelegate); 你可以按以下步骤实现群消息已读回执特性: -1. 群成员发送消息时若需已读回执,需设置 `Message` 的 `IsNeedGroupAck` 为 `true`。 +1. 开启已读回执功能,即 SDK 初始化时将 `Options#RequireAck` 设置为 `true`。 + +该功能开启后,接收方阅读消息后,SDK 底层会自动进行消息已读回执。 +```csharp +// 设置是否需要接收方已读确认,默认为 `true`。 +options.RequireAck = true; +``` + +2. 发送方发送消息时设置 `Message#IsNeedGroupAck` 属性为 `true`。 + +与单聊消息的 app 层级设置已读回执功能不同,群聊消息是在发送消息时设置指定消息是否需要已读回执。 ```csharp -Message msg = Message.CreateTextSendMessage("to", "hello world"); +Message msg = Message.CreateTextSendMessage(to, content); msg.IsNeedGroupAck = true; ``` -2. 消息接收方发送群组消息的已读回执。 +3. 消息接收方发送群组消息的已读回执。 ```csharp -void SendReadAckForGroupMessage(string messageId, string ackContent) -{ - SDKClient.Instance.ChatManager.SendReadAckForGroupMessage(messageId, ackContent,callback: new CallBack( - onSuccess: () => - { +public void sendAckMessage(EMMessage message) { - }, - onError: (code, desc) => - { + if (message.HasReadAck) { + return; + } - } - )); + // 多设备登录时,无需再向自己发送的消息发送已读回执。 + if (SDKClient.Instance.CurrentUsername.CompareTo(message.From)) { + return; } + + if (message.IsNeedGroupAck() && !message.IsRead) { + string to = message.ConversationId; + string msgId = message.MsgId; + SDKClient.Instance.ChatManager.SendReadAckForGroupMessage(message.MsgId, “”, new CallBack( + onSuccess: () => + { + }, + onError: (code, desc) => + { + } + )); + message.IsRead = true; + } +} ``` -3. 消息发送方监听群组消息已读回调。 +4. 消息发送方监听群组消息已读回调。 + +群消息已读回调在 `IChatManagerDelegate#OnGroupMessageRead` 中实现。 -群组消息已读回调在消息监听类 `IChatManagerDelegate` 中实现。 +发送方接收到群组消息已读回执后,其发出消息的属性 `Message#GroupAckCount` 会有相应变化。 ```csharp -// 继承并实现 `IChatManagerDelegate`。 -public class ChatManagerDelegate : IChatManagerDelegate { +public class MyDelegate : IChatManagerDelegate +{ + //... + // 接收到群组消息体的已读回执, 表明消息的接收方已经阅读此消息。 + public void OnGroupMessageRead(List list); { - // 收到群组消息的已读回执, 表明消息的接收方已阅读此消息。 - public void OnGroupMessageRead(List list) - { + } - } + //... } -// 注册监听器。 -ChatManagerDelegate adelegate = new ChatManagerDelegate() -SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); +MyDelegate myDelegate = new MyDelegate(); + +//注册消息监听 +SDKClient.Instance.ChatManager.AddChatManagerDelegate(myDelegate); +记得在不需要的时候移除 listener +SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(myDelegate); ``` -4. 消息发送方获取群组消息的已读回执详情。 +5. 消息发送方获取群组消息的已读回执详情。 -你可以调用 `FetchGroupReadAcks` 获取群组消息的已读回执的详情,示例代码如下: +你可以调用 `ChatManager#FetchGroupReadAcks` 方法从服务器获取单条消息的已读回执的详情。 ```csharp -// startAckId:查询起始的已读回执 ID。首次调用为空,SDK 从最新的已读回执开始按服务器接收回执时间的逆序获取。后续调用从 CursorResult 中的 cursor 获取。 -// pageSize:每页获取群消息已读回执的条数。 -SDKClient.Instance.ChatManager.FetchGroupReadAcks(messageId, groupId, startAckId, pageSize, new ValueCallBack>( - onSuccess: (list) => - { - // 页面刷新等操作。 - }, - onError: (code, desc) => - { - } -)); +//messageId 消息 ID。 +//groupId 群组 ID。 +//pageSize 每页获取群消息已读回执的条数。取值范围[1,50]。 +//startAckId 已读回执的 ID,如果为空,从最新的回执向前开始获取。 +//callBack 结果回调,成功执行 {@link ValueCallBack#onSuccess(Object)},失败执行 {@link ValueCallBack#onError(int, String)}。 +public void FetchGroupReadAcks(string messageId, string groupId, int pageSize = 20, string startAckId = null, ValueCallBack> callback = null) ``` + +### 查看消息送达和已读状态 + +对于单聊消息,本地通过 `Message#HasDeliverAck` 字段存储消息送达状态。 + +对于单聊消息,本地通过以下字段存储消息已读状态: + +| 字段 | 描述 | +| :--------- | :----- | +| `Message#IsRead` | 用户是否已读了该消息。如果是自己发送的消息,该字段的值固定为 `true`。| +| `Message#HasReadAck` | 是否(消息接收方)已发送或(消息发送方)已收到消息已读回执。如果是自己发送的消息,记录的是对方是否已读。如果是对方的消息,则记录的是自己是否发送过已读回执。 | + +对于群聊消息,本地数据库通过以下字段存储消息已读状态: + +| 字段 | 描述 | +| :--------- | :----- | +| `Message#IsRead` | 用户是否已读了该消息。如果是自己发送的消息,该字段的值固定为 `true`。| +| `Message#GroupAckCount` | 已阅读消息的群成员数量。 | + +### 已读回执与未读消息数 + +- 会话已读回执发送后,开发者需要调用 `Conversation#MarkAllMessageAsRead` 方法将该会话的所有消息置为已读,即会话的未读消息数清零。 + +- 消息已读回执发送后,开发者需要调用 `Conversation#MarkMessageAsRead` 方法将该条消息置为已读,则消息未读数会有变化。 + + + + diff --git a/docs/document/unity/message_send_receive.md b/docs/document/unity/message_send_receive.md index fa872637..d2b4d876 100644 --- a/docs/document/unity/message_send_receive.md +++ b/docs/document/unity/message_send_receive.md @@ -1,53 +1,23 @@ # 发送和接收消息 - +环信即时通讯 IM Unity SDK 通过 `IChatManager` 和 `Message` 类实现文本、图片、音频、视频和文件等类型的消息的发送和接收。 -登录即时通讯 IM app 后,用户可以在单聊、群聊、聊天室中发送如下类型的消息: +- 对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。 -- 文字消息,包含超链接和表情消息。 -- 附件消息,包含图片、语音、视频及文件消息。 -- 位置消息。 -- 透传消息。 -- 自定义消息。 -- 合并消息。 -- 定向消息。 +- 对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 -对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 - -针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性 。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 - -本文介绍如何使用即时通讯 IM SDK 实现发送和接收这些类型的消息。 - -## 技术原理 - -环信即时通讯 IM Unity SDK 通过 `IChatManager` 和 `Message` 类实现消息的发送和接收。 - -其中,发送和接收消息的逻辑如下: - -1. 发送方调用相应 `Create` 方法创建文本、文件、附件等类型的消息; -2. 发送方调用 `SendMessage` 发送消息; -3. 接收方通过 `AddChatManagerDelegate` 方法监听消息回调事件。在收到 `OnMessagesReceived` 后,即表示成功接收到消息。 - -消息收发流程如下: - -1. 用户 A 发送一条消息到环信的消息服务器; -2. 单聊消息时,服务器投递消息给用户 B;对于群聊时消息,服务器投递给群内其他每一个成员; -3. 用户收到消息。 - -![img](/images/android/sendandreceivemsg.png) +单聊、群组聊天和聊天室的消息发送控制,详见[消息发送控制](/product/product_message_overview.html#消息发送控制)文档。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html)。 +- 完成 SDK 初始化,详见 [初始化文档](initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -## 实现方法 +## 发送和接收文本消息 -### 发送文本消息 - -你可以利用 `Message` 类构造一个消息,然后通过 `IChatManager` 将该消息发出。 +1. 你可以利用 `Message` 类构造一个消息,然后通过 `IChatManager` 将该消息发出。 默认情况下,SDK 对单个用户发送消息的频率未做限制。如果你联系了环信商务设置了该限制,一旦在单聊、群聊或聊天室中单个用户的消息发送频率超过设定的上限,SDK 会上报错误,即错误码 509 `MESSAGE_CURRENT_LIMITING`。 @@ -79,11 +49,7 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -若初始化时打开了 `Options#UseReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 - -### 接收消息 - -你可以用注册监听 `IChatManagerDelegate` 接收消息。该 `IChatManagerDelegate` 可以多次添加,请记得在不需要的时候移除该监听。如在析构 `IChatManagerDelegate` 的继承实例之前。 +2. 你可以用注册监听 `IChatManagerDelegate` 接收消息。该 `IChatManagerDelegate` 可以多次添加,请记得在不需要的时候移除该监听。如在析构 `IChatManagerDelegate` 的继承实例之前。 在新消息到来时,你会收到 `OnMessagesReceived` 的回调,消息接收时可能是一条,也可能是多条。你可以在该回调里遍历消息队列,解析并显示收到的消息。若在初始化时打开了 `Options#IncludeSendMessageInMessageListener` 开关,则该回调中会返回发送成功的消息。 @@ -108,11 +74,17 @@ SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(adelegate); ``` -### 发送和接收附件消息 +## 发送和接收附件消息 + +除文本消息外,SDK 还支持发送附件类型消息,包括语音、图片、视频和文件消息。 + +附件消息的发送和接收过程如下: -除文本消息外,还有几种其他类型的消息,其中语音,图片,短视频,文件等消息,是通过先将附件上传到消息服务器的方式实现。收到语音时,会自动下载,而图片和视频会自动下载缩略图。文件消息不会自动下载附件,接收方需调用下载附件的 API,具体实现参考下文。 +1. 创建和发送附件类型消息。SDK 将附件上传到环信服务器。 +2. 接收附件消息。SDK 自动下载语音消息,默认自动下载图片和视频的缩略图。若下载原图、视频和文件,需调用 `DownloadAttachment` 方法。 +3. 获取附件的服务器地址和本地路径。 -#### 发送和接收语音消息 +### 发送和接收语音消息 发送语音消息时,应用层需完成语音文件录制的功能,提供语音文件的 URI 和语音时长。 @@ -143,6 +115,8 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` +语音消息的接收与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + 接收方收到语音消息后,参考如下示例代码获取语音消息的附件: ```csharp @@ -163,11 +137,11 @@ else { } ``` -#### 发送和接收图片消息 +### 发送和接收图片消息 -图片消息默认会被压缩后发出,可通过设置 `original` 参数为 `true` 发送原图。 +1. 创建并发送图片消息。 -参考如下示例代码,创建并发送图片消息: +图片消息默认会被压缩后发出,可通过设置 `original` 参数为 `true` 发送原图。 ```csharp //`localPath` 为图片本地资源路径。 @@ -196,7 +170,14 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -接收方收到图片消息后,参考如下示例代码获取图片消息的缩略图和附件: +2. 图片消息的接收与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + +接收方收到图片消息后,参考如下示例代码获取图片消息的缩略图和附件。 + +- 接收方如果设置了自动下载,即 `Options.IsAutoDownload` 为 `true`,SDK 接收到消息后会下载缩略图 +- 如果未设置自动下载,需主动调用 `SDKClient.Instance.ChatManager.DownloadThumbnail` 下载。 + +下载完成后,调用相应消息 `msg.Body` 的 `ThumbnailLocalPath` 获取缩略图路径。 ```csharp //注意:这里的 "Message ID" 是消息发送成功以后(`CallBack` 中的 `onSuccess` 被触发以后),被发送消息的 ID。 @@ -223,15 +204,11 @@ else { } ``` -接收方如果设置了自动下载,即 `Options.IsAutoDownload` 为 `true`,SDK 接收到消息后会下载缩略图;如果未设置自动下载,需主动调用 `SDKClient.Instance.ChatManager.DownloadThumbnail` 下载。 +### 发送和接收视频消息 -下载完成后,调用相应消息 `msg.Body` 的 `ThumbnailLocalPath` 去获取缩略图路径。 +发送视频消息时,应用层需要完成视频文件的选取或者录制。视频消息支持给出视频的时长作为参数,发送给接收方。 -#### 发送和接收短视频消息 - -发送短视频消息时,应用层需要完成视频文件的选取或者录制。视频消息支持给出视频的时长作为参数,发送给接收方。 - -参考如下示例代码,创建并发送短视频消息: +1. 创建并发送视频消息。 ```csharp Message msg = Message.CreateVideoSendMessage(toChatUsername, localPath, displayName, thumbnailLocalPath, fileSize, duration, width, height); @@ -251,13 +228,9 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -默认情况下,当收件人收到短视频消息时,SDK 会下载视频消息的缩略图。 - -如果不希望 SDK 自动下载视频缩略图,可以将 `Options.IsAutoDownload` 设置为 `false`。 +2. 接收方收到视频消息时,自动下载视频缩略图。你可以设置自动或手动下载视频缩略图,该设置与图片缩略图相同,详见[设置图片缩略图自动下载](#发送和接收图片消息)。 -如果未设置自动下载,需主动调用 `SDKClient.Instance.ChatManager.DownloadThumbnail` 下载。下载完成后,使用相应消息 `Body` 的 `ThumbnailLocalPath` 成员获取缩略图路径。 - -短视频文件本身需要通过 `SDKClient.Instance.ChatManager.DownloadAttachment` 下载,下载完成后,使用相应消息 `Body` 的 `LocalPath` 成员获取短视频文件路径。 +视频文件本身需要通过 `SDKClient.Instance.ChatManager.DownloadAttachment` 下载,下载完成后,使用相应消息 `Body` 的 `LocalPath` 成员获取视频文件路径。 ```csharp // 接收到视频消息需先下载附件才能打开。 @@ -270,7 +243,7 @@ SDKClient.Instance.ChatManager.DownloadAttachment("Message ID", new CallBack( { if (msg.Body.Type == ChatSDK.MessageBodyType.VIDEO) { ChatSDK.MessageBody.VideoBody vb = (ChatSDK.MessageBody.VideoBody)msg.Body; - //从本地获取短视频文件路径。 + //从本地获取视频文件路径。 string videoLocalUri = vb.LocalPath; //这里可以根据本地路径打开文件。 } @@ -289,9 +262,9 @@ SDKClient.Instance.ChatManager.DownloadAttachment("Message ID", new CallBack( )); ``` -#### 发送和接收文件消息 +### 发送和接收文件消息 -参考如下示例代码创建并发送文件消息: +1. 创建并发送文件消息。 ```csharp // localPath 为文件本地路径,displayName 为消息显示名称,fileSize 为文件大小。 @@ -335,9 +308,13 @@ else { ``` -### 发送位置消息 +2. 文件消息的接收与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + +## 发送和接收位置消息 -当你需要发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 +1. 创建和发送位置消息。 + +发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。 ```csharp //`latitude` 为纬度,`longitude` 为经度,`locationAddress` 为具体位置内容,`buildingName` 为建筑名称。 @@ -353,14 +330,23 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -### 发送和接收透传消息 +2. 接收位置消息与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + + 接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 + +## 发送和接收透传消息 -透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。(透传消息不会存入本地数据库中,所以在 UI 上不会显示)。具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 “em_” 和 “easemob::” 开头的 action 为内部保留字段,注意不要使用。 +透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。 + +具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 `em_` 和 `easemob::` 开头的 action 为内部保留字段,注意不要使用。 :::tip -透传消息发送后,不支持撤回。 +- 透传消息发送后,不支持撤回。 +- 透传消息不会存入本地数据库中,所以在 UI 上不会显示。 ::: +1. 创建和发送透传消息。 + ```csharp //`action` 可以自定义。 string action = "actionXXX"; @@ -375,7 +361,7 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -请注意透传消息的接收方,也是由单独的回调进行通知,方便用户进行不同的处理。 +2. 接收方通过 `OnMessagesReceived` 和 `OnCmdMessagesReceived` 回调接收透传消息,方便用户进行不同的处理。 ```csharp // 继承并实现 `IChatManagerDelegate`。 @@ -399,79 +385,12 @@ SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); ``` -#### 通过透传消息实现输入指示器 - -输入指示器显示其他用户何时输入消息。通过该功能,用户之间可进行有效沟通,增加了用户对聊天应用中交互的期待感。 - -你可以通过透传消息实现输入指示器。下图为输入指示器的工作原理。 - -![img](/images/common/typing_indicator.png) - -监听用户 A 的输入状态。一旦有文本输入,通过透传消息将输入状态发送给用户 B,用户 B 收到该消息,了解到用户 A 正在输入文本。 - -- 用户 A 向用户 B 发送消息,通知其开始输入文本。 -- 收到消息后,如果用户 B 与用户 A 的聊天页面处于打开状态,则显示用户 A 的输入指示器。 -- 如果用户 B 在几秒后未收到用户 A 的输入,则自动取消输入指示器。 - -:::tip - -用户 A 可根据需要设置透传消息发送间隔。 - -::: - -以下示例代码展示如何发送输入状态的透传消息。 - -```csharp -//发送表示正在输入的透传消息 -string msgTypingBegin = "TypingBegin"; - -void textChange() { - int currentTimestamp = getCurrentTimestamp(); - if (currentTimestamp - _previousChangedTimeStamp > 5) { - _sendBeginTyping(); - _previousChangedTimeStamp = currentTimestamp; - } -} - -void _sendBeginTyping() { - var msg = Message.CreateCmdSendMessage( - username: conversationId, - action: msgTypingBegin, - deliverOnlineOnly: true, - ); - msg.chatType = MessageType.Chat; - SDKClient.getInstance.chatManager.sendMessage(msg); -} - -``` - -以下示例代码展示如何接受和解析输入状态的透传消息。 - -```csharp -int typingTime = 10; - -void OnCmdMessagesReceived(List list) { - for (var msg in list) { - if (msg.ConversationId != currentConversationId) { - continue; - } - MessageBody.CmdBody body = msg.Body as MessageBody.CmdBody; - if (body.Action == msgTypingBegin) { - // 这里需更新 UI,显示“对方正在输入” - - Timer timer = new Timer((state) => - { - // 这里需更新 UI,不再显示“对方正在输入” - }, null, typingTime, Timeout.Infinite); - } - } -} -``` - -### 发送自定义类型消息 +## 发送和接收自定义类型消息 除了几种消息之外,你可以自己定义消息类型,方便业务处理,即首先设置一个消息类型名称,然后可添加多种自定义消息。 +1. 创建和发送自定义类型消息。 + ```csharp //`event` 为字符串类型的自定义事件,比如礼物消息,可以设置: string event = "gift"; @@ -491,7 +410,10 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -### 发送和接收合并消息 +2. 接收自定义消息与其他类型消息一致,详见[接收文本消息](#发送和接收文本消息)。 + +## 发送和接收合并消息 + 为了方便消息互动,即时通讯 IM 自 1.2.0 版本开始支持将多个消息合并在一起进行转发。你可以采取以下步骤进行消息的合并转发: 1. 利用原始消息列表创建一条合并消息。 @@ -536,11 +458,11 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -接收合并消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +#### 接收和解析合并消息 -对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 +接收合并消息与接收普通消息的操作相同,详见[接收消息](#发送和接收文本消息)。 -#### 解析合并消息 +对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 合并消息实际上是一种附件消息。收到合并消息后,你可以调用 `FetchCombineMessageDetail` 方法下载合并消息附件并解析出原始消息列表。 @@ -560,7 +482,7 @@ SDKClient.Instance.ChatManager.FetchCombineMessageDetail(msg, new ValueCallBack< )); ``` -### 发送和接收定向消息 +## 发送和接收定向消息 发送定向消息是指向群组或聊天室的单个或多个指定的成员发送消息,其他成员不会收到该消息。 @@ -604,9 +526,9 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -接收群定向消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +接收群定向消息与接收普通消息的操作相同,详见[接收文本消息](#发送和接收文本消息)。 -### 使用消息扩展字段 +## 使用消息扩展字段 当 SDK 提供的消息类型不满足需求时,你可以通过消息扩展字段传递自定义的内容,从而生成自己需要的消息类型。 @@ -643,7 +565,47 @@ if (found) { } ``` -## 注意事项 +## 更多 + +### 设置聊天室消息优先级 + +针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 + +对于聊天室消息,可设置消息优先级,包括高、普通和低优先级。示例代码如下: + +```csharp +//创建一条文本消息,`content` 为消息文字内容。 +// `conversationId` 为消息接收方,单聊为对端用户的 ID,群聊为群组 ID,聊天室时为聊天室 ID。 +Message msg = Message.CreateTextSendMessage(conversationId, content); + +//设置会话类型,即 `Message` 类的 `MessageType` 属性。 +//聊天室为 `Room`。 +msg.MessageType = MessageType.Room; + +//对于聊天室消息,可设置消息优先级。 +msg.MessageType = MessageType.Room; +//聊天室消息的优先级。如果不设置,默认值为 `RoomMessagePriority.Normal`,即“普通”优先级。 +msg.SetRoomMessagePriority(RoomMessagePriority.High); + +//发送消息。 +//发送消息时可设置 `CallBack` 的实例,获得消息发送状态。可在该回调中更新消息的显示状态。例如消息发送失败后的提示等。 +SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( + onSuccess: () => { + Debug.Log($"{msg.MsgId}发送成功"); + }, + onError:(code, desc) => { + Debug.Log($"{msg.MsgId}发送失败,errCode={code}, errDesc={desc}"); + } +)); +``` + +### 发送消息前的内容审核 + +- 内容审核关注消息 body [内容审核服务会关注消息 body 中指定字段的内容,不同类型的消息审核不同的字段](/product/moderation/moderation_mechanism.html),若创建消息时在这些字段中传入了很多业务信息,可能会影响审核效果。因此,创建消息时需要注意内容审核的字段不涉及业务信息,建议业务信息放在扩展字段中。 +- 设置发送方收到内容审核替换后的内容 + +若初始化时打开了 `Options#UseReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 + diff --git a/docs/document/unity/overview.md b/docs/document/unity/overview.md index 3df164f3..b7a0d44b 100644 --- a/docs/document/unity/overview.md +++ b/docs/document/unity/overview.md @@ -42,7 +42,7 @@ SDKClient.Instance.InitWithOptions(options); | `DeleteMessagesAsExitGroup` | 是否需要在离开群组时自动删除聊天历史消息。设置为 `true` 则在退出群组的时候,会删除聊天记录。 | | `DeleteMessagesAsExitRoom` | 是否需要在离开聊天室时自动删除聊天历史消息。设置为 `true` 则在退出聊天室的时候,会删除记录。 | | `IsRoomOwnerLeaveAllowed` | 是否允许聊天室所有者离开聊天室。设置为 `true` 则允许。详见 [聊天室](room_overview.html) 章节。 | -| `IsAutoDownload` | 是否开启自动下载。设置为 `true` 则收到图片、视频、音频、语音消息会自动下载。详见 [消息](message_send_receive.html#接收消息) 章节。 | +| `IsAutoDownload` | 是否开启自动下载。设置为 `true` 则收到图片、视频、音频、语音消息会自动下载。详见 [消息](message_send_receive.html#发送和接收图片消息) 章节。 | ## 注册用户 @@ -78,7 +78,7 @@ SDKClient.Instance.CreateAccount(username, password, ); ``` -:::notice +:::tip 该注册模式为在客户端注册,旨在方便测试,并不推荐在正式环境中使用。 ::: @@ -123,7 +123,7 @@ SDKClient.Instance.Login(username, password, 2. **用户 ID + token** 是更加安全的登录方式。token 可以通过调用 REST API 获取,详见 [环信用户 token 的获取](/document/server-side/easemob_user_token.html)。 -:::notice +:::tip 使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 ::: @@ -279,7 +279,7 @@ XXX:Windows 用户名; YYY: Unity 中设置的公司名称,如果没有设 - Unity iOS -本地日志的获取与 iOS 的相同,详见 [iOS 本地日志的获取](/document/ios/overview.html#获取本地日志)。 +本地日志的获取与 iOS 的相同,详见 [iOS 本地日志的获取](/document/ios/log.html#获取本地日志)。 日志路径:沙箱 Library/Application Support/HyphenateSDK/easemobLog。 diff --git a/docs/document/unity/quickstart.md b/docs/document/unity/quickstart.md index 423eb80a..7502412a 100644 --- a/docs/document/unity/quickstart.md +++ b/docs/document/unity/quickstart.md @@ -50,7 +50,7 @@ 如果正常打开,该流程结束。 - :::notice + :::tip 如果 Demo 项目与本地 Unity Editor 版本不一致,你需要进行以下操作: 1. 在弹出的 **Editor version not installed** 提示框下方,选择 **Choose another Editor version**。 @@ -58,7 +58,7 @@ 2. 在弹出的 **Select Editor version and platform** 窗口中,选择本地安装的 Editor 版本,并根据后续提示打开项目。 ::: - :::notice + :::tip 如果碰到'SpriteRenderer' does not contain a definition for 'IsUsingDeformableBuffer'这类异常,需要进行重置操作。 在**Help**菜单下,点击**Reset Packages to defaults**将包恢复默认设置。 ::: @@ -79,7 +79,7 @@ 在 Unity Editor 中左侧导航栏下方,选择 **Project** 页签,然后选择 **Assets** 目录下的 **Scripts** 目录,双击 **TestCode.cs** 文件打开 Visual Studio。 -:::notice +:::tip 如果双击 **TestCode.cs** 文件无法打开 Visual Studio 开发环境,需将 Visual Studio 配置为 Unity 的外部工具:点击左上角的 Unity 菜单(Windows 为 **Edit**,Mac 为 **Unity**),依次选择 **Preference > External Tools > External Script Editor**,将脚本编辑器设置为 Visual Studio。 ::: @@ -118,7 +118,7 @@ SDKClient.Instance.CreateAccount(username: Username.text, Password.text, callbac )); ``` -:::notice +:::tip 该注册模式在客户端实现,简单方便,主要用于测试,但不推荐在正式环境中使用。正式环境中应使用服务器端调用 Restful API 进行注册,详见 [注册单个用户](/document/server-side/account_system.html#开放注册单个用户)。 ::: @@ -306,7 +306,7 @@ SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(this); ![图片](/images/unity/unity-running.png) -:::notice +:::tip 若未安装 iOS Build Support,运行项目前,将 `Assets/ChatSDK/Scripts/Editor` 路径下的 `iOSBuildSetting.cs` 文件移除项目文件夹。 1. 注册用户:在 **user id** 文本框中输入用户 ID,在 **password** 文本框中输入密码,点击 **Sign up** 进行用户注册。注册结果会在下方显示。可创建两个用户,例如 **quickstart_sender** 和 **quickstart_receiver**,分别用于发送和接收消息。 diff --git a/docs/document/unity/reaction.md b/docs/document/unity/reaction.md index 23e41342..5dc42ab6 100644 --- a/docs/document/unity/reaction.md +++ b/docs/document/unity/reaction.md @@ -4,7 +4,7 @@ 环信即时通讯 IM 提供消息表情回复(下文统称 “Reaction”)功能。用户可以在单聊和群聊中对消息添加、删除表情。表情可以直观地表达情绪,利用 Reaction 可以提升用户的使用体验。同时在群组中,利用 Reaction 可以发起投票,根据不同表情的追加数量来确认投票。 -:::notice +:::tip 目前 Reaction 仅适用于单聊和群组。聊天室暂不支持 Reaction 功能。 diff --git a/docs/document/unity/room_members.md b/docs/document/unity/room_members.md index 0ae1be57..f24907d1 100644 --- a/docs/document/unity/room_members.md +++ b/docs/document/unity/room_members.md @@ -233,7 +233,7 @@ SDKClient.Instance.RoomManager.RemoveAllowListMembers(roomId, list, new CallBack 仅聊天室所有者和管理员可以调用 `MuteRoomMembers` 方法将指定成员添加至聊天室禁言列表。被禁言后,该成员和其他未操作的聊天室管理员或聊天室所有者收到 `OnMuteListAddedFromRoom` 回调。 -:::notice +:::tip 聊天室所有者可禁言聊天室所有成员,聊天室管理员可禁言聊天室普通成员。 ::: @@ -253,7 +253,7 @@ SDKClient.Instance.RoomManager.MuteRoomMembers(roomId, members, new CallBack( 仅聊天室所有者和管理员可以调用 `UnMuteRoomMembers` 方法将成员移出聊天室禁言列表。被移出的群成员及其他未操作的管理员或者群主将会收到群组事件 `OnMuteListRemovedFromRoom`。 -:::notice +:::tip 聊天室所有者可对聊天室所有成员解除禁言,聊天室管理员可对聊天室普通成员解除禁言。 ::: diff --git a/docs/document/unity/thread.md b/docs/document/unity/thread.md index 7560d8c6..1662b475 100644 --- a/docs/document/unity/thread.md +++ b/docs/document/unity/thread.md @@ -64,7 +64,7 @@ SDKClient.Instance.ThreadManager.CreateThread(threadName, msgId, groupid, new Va 单设备登录时,子区所属群组的所有成员均会收到 `IChatThreadManagerDelegate#onThreadNotifyChange` 回调;多设备登录时,其他设备会同时收到 `IMultiDeviceDelegate#onThreadMultiDevicesEvent` 回调,回调事件为 `THREAD_DESTROY`。 -:::notice +:::tip 解散子区或解散子区所在的群组后,将删除本地数据库及内存中关于该子区的全部数据,需谨慎操作。 ::: diff --git a/docs/document/unity/thread_message.md b/docs/document/unity/thread_message.md index 0cd80e2c..2188bf5e 100644 --- a/docs/document/unity/thread_message.md +++ b/docs/document/unity/thread_message.md @@ -6,20 +6,11 @@ ## 技术原理 -环信即时通讯 IM Unity SDK 提供 `IChatManager`、`Message` 和 `IChatThreadManager` 类,用于管理子区消息,支持你通过调用 API 在项目中实现如下功能: - -- 发送子区消息 -- 接收子区消息 -- 撤回子区消息 -- 获取子区消息 +环信即时通讯 IM Unity SDK 提供 `IChatManager`、`Message` 和 `IChatThreadManager` 类,用于管理子区消息,支持你通过调用 API 在项目中实现发送、接收、撤回和获取子区消息。 消息收发流程如下: -1. 客户端从应用服务器获取 token。 -2. 客户端 A 和 B 登录即时通讯。 -3. 客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 - -![img](/images/android/sendandreceivemsg.png) +客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 子区创建和查看如下图: @@ -40,7 +31,7 @@ ### 发送子区消息 -发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html#发送文本消息)。唯一不同的是,发送子区消息需要指定标记 `IsThread` 为 `true`。 +发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html)。唯一不同的是,发送子区消息需要指定标记 `IsThread` 为 `true`。 示例代码如下: @@ -67,7 +58,7 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( ### 接收子区消息 -接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#接收消息),此处只介绍子区消息和其他消息的区别。 +接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#发送和接收文本消息),此处只介绍子区消息和其他消息的区别。 子区有新增消息时,子区所属群组的所有成员收到 `IChatThreadManagerDelegate#OnUpdateMyThread` 回调,子区成员收到 `IChatManagerDelegate#OnMessagesReceived` 回调。 diff --git a/docs/document/unity/userprofile.md b/docs/document/unity/userprofile.md index 62086128..0560fef2 100644 --- a/docs/document/unity/userprofile.md +++ b/docs/document/unity/userprofile.md @@ -8,7 +8,7 @@ 本文介绍如何通过管理用户属性设置、更新、存储并获取实时消息用户的相关信息。 -:::notice +:::tip 为保证用户信息安全,SDK 仅支持用户设置或更新自己的用户属性。 ::: diff --git a/docs/document/web/connection.md b/docs/document/web/connection.md new file mode 100644 index 00000000..a9b2867f --- /dev/null +++ b/docs/document/web/connection.md @@ -0,0 +1,45 @@ +# 连接 + +应用客户端成功连接到环信服务器后,才能使用环信即时通讯 SDK 的收发消息等功能。 + +你调用 `open` 方法登录后,客户端 SDK 会自动连接环信服务器。关于登录详情,请参见[登录文档](login.html)。 + +## 监听连接状态 + +```javascript +conn.addEventHandler("connectionListener", { + onConnected: () => { + console.log("连接成功"); + }, + // 自 4.8.0 版本,`onDisconnected` 事件新增断开原因回调参数, 告知用户触发 `onDisconnected` 的原因。 + onDisconnected: () => { + console.log("连接断开"); + }, + onReconnecting: () => { + console.log("重连中"); + }; +}); +``` + +## 自动重连 + +登录后,SDK 在以下情况下会尝试自动重连: + +- 网络断开 + +- 网络切换 + +- 非主动调用登出 + +登录后,如果由于网络信号弱、切换网络等引起的连接中断,SDK 会自动尝试重连。 + +不过,SDK 在以下情况下会停止自动重连。你需要调用 `open` 方法登录。 + +- 用户调用了 SDK 的登出方法 `close` 主动退出登录。 +- 登录时鉴权错误,例如, token 无效(错误码 104)或已过期(错误码 108)。 +- 用户在其他的设备上更改了密码,导致此设备上自动登录失败,提示错误码 216。 +- 用户的账号被从服务器端删除,提示错误码 207。 +- 用户在另一设备登录,将当前设备上登录的用户踢出,提示错误码 206。 +- 用户登录设备数量超过限制,提示错误码 214。 +- 应用程序的日活跃用户数量(DAU)或月活跃用户数量(MAU)达到上限,提示错误码 8。 +``` diff --git a/docs/document/web/conversation_local.md b/docs/document/web/conversation_local.md index ef0c6025..f43b4ff1 100644 --- a/docs/document/web/conversation_local.md +++ b/docs/document/web/conversation_local.md @@ -22,7 +22,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 4.2.1 或以上版本初始化,详见 [SDK 集成概述](overview.html#sdk-初始化); +- 完成 SDK 4.2.1 或以上版本初始化,详见 [初始化](initialization.html)文档; - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html); - [按需导入 SDK](import_sdk_minicore.html),[集成本地存储插件](#集成本地存储插件)。 diff --git a/docs/document/web/error.md b/docs/document/web/error.md index 3f4fbcd3..17d05a18 100644 --- a/docs/document/web/error.md +++ b/docs/document/web/error.md @@ -72,7 +72,7 @@ error.type === statusCode.WEBIM_CONNCTION_USER_NOT_ASSIGN_ERROR 其中 `error` | 1100 | PRESENCE_PARAM_EXCEED | 发布自定义在线状态时,参数长度超出限制。 | | 1101 | REACTION_ALREADY_ADDED | Reaction 重复添加。 | | 1102 | REACTION_CREATING | 创建 Reaction 时,其他人正在创建。 | -| 1103 | REACTION_OPERATION_IS_ILLEGAL | 用户对该 Reaction 没有操作权限:没有添加过该 Reaction 的用户进行删除操作,或者单聊消息非发送者和非接受者进行添加 Reaction 操作。 | +| 1103 | REACTION_OPERATION_IS_ILLEGAL | 用户对该 Reaction 没有操作权限:没有添加过该 Reaction 的用户进行删除操作,或者单聊消息非发送者和非接收者进行添加 Reaction 操作。 | | 1200 | TRANSLATION_NOT_VALID | 传入的语言 code 不合法。 | | 1201 | TRANSLATION_TEXT_TOO_LONG | 翻译的文本过长。 | | 1204 | TRANSLATION_FAILED | 获取翻译服务失败。 | diff --git a/docs/document/web/group_members.md b/docs/document/web/group_members.md index 265a4845..a4e10f36 100644 --- a/docs/document/web/group_members.md +++ b/docs/document/web/group_members.md @@ -164,7 +164,7 @@ connection.removeGroupMembers({groupId: 'groupId', users: ['user1', 'user2']}) 你可使用 `getGroupMembersAttributes` 方法根据指定的属性 key 获取多个群成员的自定义属性。 -:::notice +:::tip 每次最多可获取 10 个群成员的自定义属性。 ::: diff --git a/docs/document/web/initialization.md b/docs/document/web/initialization.md new file mode 100644 index 00000000..5e5d236b --- /dev/null +++ b/docs/document/web/initialization.md @@ -0,0 +1,21 @@ +# SDK 初始化 + +初始化是使用 SDK 的必要步骤,需在所有接口方法调用前完成。 + +## 前提条件 + +有效的环信即时通讯 IM 开发者账号和 App key,详见[环信即时通讯云控制台的相关文档](enable_and_configure_IM.html#创建应用)。 + +## 初始化参数说明 + +```javascript +import ChatSDK from "easemob-websdk"; +const conn = new ChatSDK.connection({ + appKey: "Your appKey", +}); +``` + +| 参数 | 类型 | 是否必需 | 描述 | +| ------------- | ------ | -------- | -------------------------- | +| appKey | String | 是 | 环信应用的唯一标识 | + diff --git a/docs/document/web/integration.md b/docs/document/web/integration.md new file mode 100644 index 00000000..105cd142 --- /dev/null +++ b/docs/document/web/integration.md @@ -0,0 +1,211 @@ +# 集成 SDK + +本文介绍如何将环信即时通讯 IM SDK 集成到你的 Web 项目。 + +## 开发环境要求 + +- 支持的浏览器: + - Chrome 54+ + - Firefox 10+ + - Safari 6+ + +## 1. 使用 npm 安装 SDK + +```bash +npm install easemob-websdk +``` + +## 2. 引入 SDK + +你可以通过以下方式引入 SDK,**推荐按需导入 SDK 文件,从而减少 SDK 体积**。 + +### (推荐)按需导入 SDK + +SDK 提供了灵活的模块化设计,允许开发者根据需求引入功能模块,并将其注册到 miniCore 中使用。 + +miniCore 是一个基座,支持登录登出和发送消息等[基础功能](https://doc.easemob.com/jsdoc/classes/Connection.Connection-1.html),而且包含消息对象。因此,若只使用收发消息功能,则只需引入 miniCore。若使用其他功能,miniCore 支持使用插件的方式引入其他功能模块。按需引入模块的方式实现了不同模块的灵活组合,从而避免不必要的代码加载,减小了应用程序的体积。 + +:::tip +1. 只有按需导入 SDK 的方式才支持[本地会话管理功能](conversation_local.html)。 +2. 小程序 uniapp 不支持使用 miniCore 的集成方式。 +::: + +#### 支持按需导入的 SDK 模块 + +| 功能 | 导入文件 | 使用方式 | +| :--------------- | :--------------------------- | :---------------- | +| 联系人和消息管理 | import \* as contactPlugin from "easemob-websdk/contact/contact"; | miniCore.usePlugin(contactPlugin, "contact"); | +| 群组 | import \* as groupPlugin from "easemob-websdk/group/group"; | miniCore.usePlugin(groupPlugin, "group"); | +| 聊天室 | import \* as chatroomPlugin from "easemob-websdk/chatroom/chatroom"; | miniCore.usePlugin(chatroomPlugin, "chatroom"); | +| 子区 | import \* as threadPlugin from "easemob-websdk/thread/thread"; | miniCore.usePlugin(threadPlugin, "thread"); | +| 翻译 | import \* as translationPlugin from "easemob-websdk/translation/translation"; | miniCore.usePlugin(translationPlugin, "translation"); | +| 在线状态订阅 | import \* as presencePlugin from "easemob-websdk/presence/presence"; | miniCore.usePlugin(presencePlugin, "presence"); | +| 会话免打扰 | import \* as silentPlugin from "easemob-websdk/silent/silent"; | miniCore.usePlugin(silentPlugin, "silent"); | + +#### 按需导入 SDK 模块 + +##### 1. 安装 SDK + +首先,通过 npm、yarn 或者其他包管理工具进行安装 SDK。 + +```bash +# npm +npm install easemob-websdk + +# yarn +yarn add easemob-websdk +``` + +##### 2. 引入 SDK 和所需模块 + +根据项目需求引入相应的功能模块。例如,引入用户关系模块: + +```javascript +import MiniCore from "easemob-websdk/miniCore/miniCore"; +import * as contactPlugin from "easemob-websdk/contact/contact"; +``` + +##### 3. 注册模块到 miniCore + +将引入的功能模块注册到 miniCore 中: + +```javascript +const miniCore = new MiniCore({ + appKey: "your appKey", +}); + +// "contact" 为固定值 +miniCore.usePlugin(contactPlugin, "contact"); +``` + +##### 4. 使用注册的模块 + +注册所需模块后,即可在项目中使用这些模块提供的功能: + +```javascript +// 获取联系人列表 +miniCore.contact.getContacts(); +``` + +#### 与整体导入的接口差别 + +通过按需导入的 SDK 与通过 [JavaScript](#引入-javascript-sdk)和 [TavaScript](#引入-typescript-sdk)导入的 SDK 在接口使用方面类似,唯一差别是后者将所有方法都挂载到 `connection` 类, 而使用 miniCore 时,基础的登录登出方法挂载在 miniCore 上,其他功能模块上的方法挂载在相应的模块上。本节以登录/登出、事件监听和发送消息为例进行说明。 + +- 登录与登出 + +示例代码如下: + +```javascript +// 登录 +miniCore.open({ + username: "username", + password: "password", + // accessToken: 'token' +}); + +// 登出 +miniCore.close(); +``` + +- 事件监听 + +示例代码如下: + +```javascript +miniCore.addEventHandler("handlerId", { + onTextMessage: (message) => { + console.log(message); + }, +}); +``` + +- 发送消息 + +示例代码如下: + +```javascript +import { EasemobChat } from "easemob-websdk"; +//发送文本消息 +const sendTextMsg = () => { + const option: EasemobChat.CreateTextMsgParameters = { + chatType: "singleChat", + type: "txt", + to: "to", + msg: "hello", + }; + const msg = miniCore.Message.create(option); + miniCore + .send(msg) + .then((res: any) => { + console.log("发送成功", res, msg); + }) + .catch((err: any) => { + console.log("发送失败", err); + }); +}; +``` + +### 引入 JavaScript SDK + +```javascript +import EC from "easemob-websdk"; +``` + +### 引入 TypeScript SDK + +在下面的导入代码中,`EasemobChat` 是 SDK 类型的命名空间。 + +```javascript +import EC, { EasemobChat } from "easemob-websdk"; +``` + +### 从官网获取并导入 SDK + +1. 下载 [Easemob Chat SDK for Web](https://www.easemob.com/download/im)。将 Web SDK 中的 `Easemob-chat.js` 文件保存到你的项目下。 + +2. 在 `index.html` 文件中,对 `index.js` 文件进行引用。 + +```javascript + +``` + +### Nuxt 或 Next 项目中引入 SDK + +对于服务端渲染框架, 如 Nuxt、Next 等,需要在客户端渲染阶段引入 SDK。 + +1. Nuxt 项目, 你可以在 mounted 生命周期动态导入 SDK: + +```javascript +export default { + mounted: () => { + import("easemob-websdk").then((res) => { + const EC = res.default; + console.log(EC, "easemob websdk"); + const conn = new EC.connection({ + appKey: "your appkey" + }); + }); + } +}; +``` + +2. 对于 Next 项目, 要使用客户端组件,你可以在文件顶部的导入上方添加 `use client` 指令。 + +```typescript +'use client' + +import { useEffect } from 'react' + +export default function Home() { + useEffect(() => { + import('easemob-websdk').then((res)=>{ + const EC = res.default; + console.log(EC, "easemob websdk"); + const conn = new EC.connection({ + appKey: "your appkey" + }); + }) + }, []) +} +``` + diff --git a/docs/document/web/log.md b/docs/document/web/log.md new file mode 100644 index 00000000..870f3c0e --- /dev/null +++ b/docs/document/web/log.md @@ -0,0 +1,58 @@ +# SDK 日志 + +环信即时通讯 IM 日志记录 SDK 相关的信息和事件。环信技术支持团队帮你排查问题时可能会请你发送 SDK 日志。 + +## 输出信息到日志文件 + +开启日志输出: + +```javascript +logger.enableAll(); +``` +- 设置日志不输出到控制台: + +```javascript +logger.setConsoleLogVisibility(false) +``` + +- 监听 SDK 日志事件: + +```javascript +logger.onLog = (log)=>{ + console.log('im logger', log) +} +``` + +关闭日志输出: + +```javascript +logger.disableAll(); +``` + +设置日志输出等级: + +```javascript +// 0 - 5 或者 'TRACE','DEBUG','INFO','WARN','ERROR','SILENT'; +logger.setLevel(0); +``` + +设置缓存日志: + +```javascript +logger.setConfig({ + useCache: false, // 是否缓存 + maxCache: 3 * 1024 * 1024, // 最大缓存字节 +}); +// 缓存全部等级日志 +logger.setLevel(0); +``` + +下载日志: + +```javascript +logger.download(); +``` + +## 日志上报 + +自 4.8.1 版本,Web SDK 支持日志上报功能, 即将日志会上传到环信的服务器。该功能默认关闭,如有需要, 可联系商务开通。 \ No newline at end of file diff --git a/docs/document/web/login.md b/docs/document/web/login.md new file mode 100644 index 00000000..d9e012a6 --- /dev/null +++ b/docs/document/web/login.md @@ -0,0 +1,73 @@ +# 登录 + +初始化 IM SDK 后,你需要首先调用接口登录。登录成功后,才能使用 IM 的功能。 + +## 用户注册 + +用户注册支持以下方式: + +- 开放注册:一般在体验 Demo 和测试环境时使用,正式环境中不推荐使用该方式注册环信账号。要使用开放注册,需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**开放注册**。只有打开该开关,才能使用客户端或 [REST API](/document/server-side/account_system.html#开放注册单个用户)开放注册用户。 + +示例代码如下所示: + +```javascript +conn.registerUser({ + username: "user1", + password: "xxx", +}); +``` + +- 授权注册:通过环信提供的 REST API 注册环信用户账号,注册后保存到你的服务器或返给客户端。要使用授权注册,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**授权注册**。相关的 REST API 介绍,详见[授权注册单个用户](/document/server-side/account_system.html#授权注册单个用户)和[批量授权注册用户](/document/server-side/account_system.html#批量授权注册用户)的接口介绍。 + +除此以外,可以在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建正式环境下和测试环境下的用户,详见[创建用户相关介绍](/product/enable_and_configure_IM.html#创建-im-用户)。 + +## 登录方式 + +1. **用户 ID + token** 是更加安全的登录方式。 + +测试环境下,你在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建用户后,环信服务器会自动为这些用户分配用户 Token,详见[测试环境下创建用户的介绍](/product/enable_and_configure_IM.html#测试环境)。 + +使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 + +```javascript +conn + .open({ + user: "username", + accessToken: "token", + }) + .then(() => { + console.log("login success"); + }) + .catch((reason) => { + console.log("login fail", reason); + }); +``` + +2. **用户 ID + 密码**登录是传统的登录方式。用户 ID 和密码均由你的终端用户自行决定,密码需要符合[密码规则要求](/document/server-side/account_system.html#开放注册单个用户)。 + +```javascript +conn + .open({ + user: "username", + pwd: "password", + }) + .then(() => { + console.log("login success"); + }) + .catch((reason) => { + console.log("login fail", reason); + }); +``` + +## 退出登录 + +```javascript +conn.close(); +``` + +## 多设备登录 + +除了单端单设备登录,环信即时通讯 IM 支持同一账号在多端的多个设备上登录。多设备登录时,若同端设备数量超过限制,新登录的设备会将之前登录的设备踢下线。 + +关于多设备登录场景中的设备数量限制、互踢策略以及信息同步,详见[多设备登录文档](multi_device.html)。 + diff --git a/docs/document/web/message_modify.md b/docs/document/web/message_modify.md index b0dec98d..9f8836c7 100644 --- a/docs/document/web/message_modify.md +++ b/docs/document/web/message_modify.md @@ -27,7 +27,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [初始化](initialization.html)文档。 - 了解环信即时通讯 IM API 的使用限制,详见 [使用限制](/product/limitation.html)。 ## 实现方法 diff --git a/docs/document/web/message_receipt.md b/docs/document/web/message_receipt.md index bb0aa327..c3359683 100644 --- a/docs/document/web/message_receipt.md +++ b/docs/document/web/message_receipt.md @@ -6,13 +6,9 @@ **群聊会话只支持消息已读回执,不支持送达回执**。群成员在发送消息时,可以设置该消息是否需要已读回执。要使用该功能,你需要[在环信即时通讯云控制台上开通该功能](/product/enable_and_configure_IM.html#设置群消息已读回执),具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 -- 消息送达回执的效果示例,如下图所示: +- 消息送达回执和已读回执的效果示例,如下图所示: -![img](/images/uikit/chatuikit/feature/web/common/message_delivery_receipt.png) - -- 消息已读回执的效果示例,如下图所示: - -![img](/images/uikit/chatuikit/feature/web/common/message_read_receipt.png) +![img](/images/web/message_receipt.png) ## 技术原理 diff --git a/docs/document/web/message_send_receive.md b/docs/document/web/message_send_receive.md index 1af1023a..8765f46e 100644 --- a/docs/document/web/message_send_receive.md +++ b/docs/document/web/message_send_receive.md @@ -2,55 +2,24 @@ -登录环信即时通讯后,用户可以在单聊、群聊、聊天室中发送如下类型的消息: +环信即时通讯 IM Web SDK 可以实现文本、图片、音频、视频和文件等类型的消息的发送和接收。 -- 文字消息,包含超链接和表情; -- 附件消息,包含图片、语音、视频及文件消息; -- 位置消息; -- 透传消息; -- 自定义消息; -- 合并消息; -- 定向消息。 +- 对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。 -对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 +- 对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 -:::tip -对于聊天室消息,环信即时通讯提供消息分级功能,将消息的优先级划分为高、普通和低三种级别,高优先级的消息会优先送达。你可以在创建消息时对指定聊天室消息类型或指定成员的消息设置为高优先级,确保这些消息优先送达。这种方式确保在聊天室内消息并发量很大或消息发送频率过高时,重要消息能够优先送达,从而提升重要消息的可靠性。 -当服务器的负载较高时,会优先丢弃低优先级的消息,将资源留给高优先级的消息。不过,消息分级功能只确保消息优先到达,并不保证必达。服务器负载过高的情况下,即使是高优先级消息依然会被丢弃。 -::: - -本文介绍如何使用即时通讯 IM Web SDK 实现发送和接收这些类型的消息。 - -## 技术原理 - -环信即时通讯 IM Web SDK 可以实现消息的发送和接收。 - -发送和接收消息: - -- 消息发送方调用 `create` 方法创建文本、文件或附件消息。 -- 消息发送方调用 `send` 方法发送消息。 -- 消息接收方调用 `addEventHandler` 监听消息事件,并在相应回调中接收消息。 - -消息收发流程如下: - -1. 用户 A 发送一条消息到环信的消息服务器。 -2. 单聊时消息服务器发消息给用户 B,群聊时发消息给群内其他每个成员。 -3. 用户收到消息。 - -![img](/images/web/sendandreceivemsg.png) +单聊、群组聊天和聊天室的消息发送控制,详见[消息发送控制](/product/product_message_overview.html#消息发送控制)文档。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html)。 +- 完成 SDK 初始化,详见 [初始化文档](initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -## 实现方法 - -### 发送文本消息 +## 发送和接收文本消息 -使用 `Message` 类创建并发送文本消息。示例代码如下: +1. 使用 `Message` 类创建并发送文本消息。示例代码如下: 默认情况下,SDK 对单个用户发送消息的频率未做限制。如果你联系了环信商务设置了该限制,一旦在单聊、群聊或聊天室中单个用户的消息发送频率超过设定的上限,SDK 会上报错误,即错误码 509 `MESSAGE_CURRENT_LIMITING`。 @@ -78,95 +47,20 @@ function sendTextMessage() { } ``` -对于聊天室消息,可设置消息优先级。示例代码如下: - -```javascript -// 发送文本消息。 -function sendTextMessage() { - let option = { - type: "txt", - msg: "message content", - // 聊天室消息的优先级。如果不设置,默认值为 `normal`,即“普通”优先级。 - priority: "high" - to: "chat room ID", - chatType: "chatRoom", - }; - let msg = WebIM.message.create(option); - conn.send(msg).then(()=>{ - console.log("Send message success"); - }).catch((e)=>{ - console.log("Send message fail"); - }); -} -``` - -若初始化时打开了 `useReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 - -### 接收消息 +2. 你可以通过 `addEventHandler` 注册监听器监听消息事件。你可以添加多个事件。当不再监听事件时,请确保删除监听器。 -你可以通过 `addEventHandler` 注册监听器监听消息事件。你可以添加多个事件。当不再监听事件时,请确保删除监听器。 - -当消息到达时,接收方会收到 `onXXXMessage` 回调。每个回调包含一条或多条消息。你可以遍历消息列表,并可以解析和展示回调中的消息。 +当消息到达时,接收方会收到 `onTextMessage` 回调。每个回调包含一条或多条消息。你可以遍历消息列表,并可以解析和展示回调中的消息。 对于聊天室消息,你可以通过消息的 `broadcast` 属性判断该消息是否为[通过 REST API 发送的聊天室全局广播消息](/document/server-side/message_chatroom.html#发送聊天室全局广播消息)。 ```javascript // 使用 `addEventHandler` 监听回调事件 conn.addEventHandler("eventName", { - // SDK 与环信服务器连接成功。 - onConnected: function (message) {}, - // SDK 与环信服务器断开连接。 - onDisconnected: function (message) {}, - // 当前用户收到文本消息。 onTextMessage: function (message) {}, - // 当前用户收到图片消息。 - onImageMessage: function (message) {}, - // 当前用户收到透传消息。 - onCmdMessage: function (message) {}, - // 当前用户收到语音消息。 - onAudioMessage: function (message) {}, - // 当前用户收到位置消息。 - onLocationMessage: function (message) {}, - // 当前用户收到文件消息。 - onFileMessage: function (message) {}, - // 当前用户收到自定义消息。 - onCustomMessage: function (message) {}, - // 当前用户收到视频消息。 - onVideoMessage: function (message) {}, - // 当前用户订阅的其他用户的在线状态更新。 - onPresence: function (message) {}, - // 当前用户收到好友邀请。 - onContactInvited: function (msg) {}, - // 联系人被删除。 - onContactDeleted: function (msg) {}, - // 新增联系人。 - onContactAdded: function (msg) {}, - // 当前用户发送的好友请求被拒绝。 - onContactRefuse: function (msg) {}, - // 当前用户发送的好友请求被同意。 - onContactAgreed: function (msg) {}, - // 当前用户收到群组邀请。 - onGroupEvent: function (message) {}, - // 本机网络连接成功。 - onOnline: function () {}, - // 本机网络掉线。 - onOffline: function () {}, - // 调用过程中出现错误。 - onError: function (message) {}, - // 当前用户收到的消息被消息发送方撤回。 - onRecallMessage: function (message) {}, - // 当前用户发送的消息被接收方收到。 - onReceivedMessage: function (message) {}, - // 当前用户收到消息送达回执。 - onDeliveredMessage: function (message) {}, - // 当前用户收到消息已读回执。 - onReadMessage: function (message) {}, - // 当前用户收到会话已读回执。 - onChannelMessage: function (message) {}, }); ``` -### 发送附件消息 +## 发送和接收附件消息 语音、图片、视频和文件消息本质上是附件消息。发送和接收附件消息的流程如下: @@ -198,11 +92,11 @@ function sendPrivateUrlImg() { } ``` -#### 发送和接收语音消息 +### 发送和接收语音消息 -发送语音消息前,你应该在 app 级别实现录音,提供录制的语音文件的 URI 和时长。 +发送语音消息前,你应该在 app 级别实现录音,提供录制的语音文件的 URI 和时长(单位为秒)。 -参考以下代码示例创建和发送语音消息: +1. 创建和发送语音消息。 ```javascript function sendPrivateAudio() { @@ -256,7 +150,7 @@ function sendPrivateAudio() { } ``` -接收方收到 `onAudioMessage` 回调,根据消息 `url` 字段获取语音文件的服务器地址,从而获取语音文件。 +2. 接收方收到 `onAudioMessage` 回调,根据消息 `url` 字段获取语音文件的服务器地址,从而获取语音文件。 ```javascript // 使用 `addEventHandler` 监听回调事件 @@ -270,11 +164,13 @@ conn.addEventHandler("eventName", { ``` -#### 发送和接收图片消息 +### 发送和接收图片消息 对于图片消息,服务器会根据用户设置的 `thumbnailHeight` 和 `thumbnailWidth` 参数自动生成图片的缩略图。若这两个参数未传,则图片的高度和宽度均默认为 170 像素。你也可以在 [环信即时通讯控制台](https://console.easemob.com/user/login)的 `服务概览` 页面的 `设置` 区域修改该默认值。 -请参考以下代码示例创建和发送图片消息: +发送和接收图片消息的流程如下: + +1. 创建和发送图片消息。 ```javascript function sendPrivateImg() { @@ -332,7 +228,7 @@ function sendPrivateImg() { } ``` -接收方收到 `onImageMessage` 回调,根据消息 `url` 字段获取图片文件的服务器地址,从而获取图片文件。 +2. 接收方收到 `onImageMessage` 回调,根据消息 `url` 字段获取图片文件的服务器地址,从而获取图片文件。 ```javascript // 使用 `addEventHandler` 监听回调事件 @@ -348,11 +244,11 @@ conn.addEventHandler("eventName", { ``` -#### 发送和接收视频消息 +### 发送和接收视频消息 在发送视频消息之前,应在 app 级别实现视频捕获,获得捕获的视频文件的时长,单位为秒。 -参考以下代码示例创建和发送视频消息: +1. 创建和发送视频消息。 ```javascript function sendPrivateVideo() { @@ -406,7 +302,7 @@ function sendPrivateVideo() { } ``` -接收方收到 `onVideoMessage` 回调,根据消息 `url` 字段获取视频文件的服务器地址,从而获取视频文件。 +2. 接收方收到 `onVideoMessage` 回调,根据消息 `url` 字段获取视频文件的服务器地址,从而获取视频文件。 ```javascript // 使用 `addEventHandler` 监听回调事件 @@ -422,9 +318,9 @@ conn.addEventHandler("eventName", { ``` -#### 发送和接收文件消息 +### 发送和接收文件消息 -参考以下代码示例创建、发送和接收文件消息: +1. 创建和发送文件消息。 ```javascript function sendPrivateFile() { @@ -481,7 +377,7 @@ function sendPrivateFile() { } ``` -接收方收到 `onFileMessage` 回调,根据消息 `url` 字段获取文件的服务器地址,从而获取文件。 +2. 接收方收到 `onFileMessage` 回调,根据消息 `url` 字段获取文件的服务器地址,从而获取文件。 ```javascript // 使用 `addEventHandler` 监听回调事件 @@ -495,9 +391,13 @@ conn.addEventHandler("eventName", { ``` -### 发送位置消息 +## 发送和接收位置消息 + +发送和接收位置消息的流程如下: -当你需要发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 +1. 创建和发送位置消息。 + +发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。 ```javascript const sendLocMsg = () => { @@ -525,11 +425,25 @@ const sendLocMsg = () => { } ``` -### 发送透传消息 +2. 接收方收到 `onLocationMessage` 回调,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 + +```javascript +// 使用 `addEventHandler` 监听回调事件 +conn.addEventHandler("eventName", { + // 当前用户收到文件消息。 + onLocationMessage: function (message) {}, +}); +``` + +## 发送和接收透传消息 透传消息是通知指定用户采取特定操作的命令消息。接收方自己处理透传消息。 -参考以下代码示例发送和接收透传消息: +:::tip +透传消息发送后,不支持撤回。 +::: + +1. 创建和发送透传消息。 ```javascript function sendCMDMessage() { @@ -561,98 +475,26 @@ function sendCMDMessage() { } ``` -#### 通过透传消息实现输入指示器 - -输入指示器显示其他用户何时输入消息。通过该功能,用户之间可进行有效沟通,设定对聊天应用程序中新交互的期望。你可以通过透传消息实现输入指示器。 - -你可以通过透传消息实现输入指示器。下图为输入指示器的工作原理。 - -![img](/images/common/typing_indicator.png) - -监听用户 A 的输入状态。一旦有文本输入,通过透传消息将输入状态发送给用户 B,用户 B 收到该消息,了解到用户 A 正在输入文本。 - -- 用户 A 向用户 B 发送消息,通知其开始输入文本。 -- 收到消息后,如果用户 B 与用户 A 的聊天页面处于打开状态,则显示用户 A 的输入指示器。 -- 如果用户 B 在几秒后未收到用户 A 的输入,则自动取消输入指示器。 - -:::notice - -用户 A 可根据需要设置透传消息发送间隔。 - -::: - -以下示例代码展示如何发送输入状态的透传消息。 - -发送输入状态的用户。 - -```typescript -let previousChangedTimeStamp = 0; -// 监听输入状态的变化 -const onInputChange = function () { - const currentTimestamp = new Date().getTime(); - if (currentTimestamp - previousChangedTimeStamp > 5000) { - sendBeginTyping(); - previousChangedTimeStamp = currentTimestamp; - } -}; +2. 接收方通过 `onCmdMessage` 回调接收透传消息。 -// 创建输入状态消息并发送 -const sendBeginTyping = function () { - const option = { - // 会话类型:单聊、群聊和聊天室分别为 `singleChat`、`groupChat` 和 `chatRoom`。 - chatType: "singleChat", - // 消息类型。 - type: "cmd", - // 消息接收方:单聊为对方用户 ID,群聊和聊天室分别为群组 ID 和聊天室 ID。 - to: "", - // 用户自定义操作。 - action: "TypingBegin", - }; - const typingMessage = message.create(option); - - connection - .send(typingMessage) - .then(() => { - console.log("success"); - }) - .catch((e) => { - console.log("fail"); - }); -}; -``` - -接收输入状态的用户。 - -```typescript -// 设置状态监听器 -let timer; -conn.addEventHandler("message", { - onCmdMessage: (msg) => { - console.log("onCmdMessage", msg); - if (msg.action === "TypingBegin") { - // 这里需更新 UI,显示“对方正在输入” - beginTimer(); - } - }, +```javascript +// 使用 `addEventHandler` 监听回调事件 +conn.addEventHandler("eventName", { + onCmdMessage: function (message) {}, }); - -const beginTimer = () => { - timer && clearTimeout(timer); - timer = setTimeout(() => { - // 这里需更新 UI,不再显示“对方正在输入” - }, 5000); -}; ``` -### 发送自定义消息 +## 发送和接收自定义消息 自定义消息为用户自定义的键值对,包括消息类型和消息内容。 -参考以下示例代码创建和发送自定义消息: +发送和接收自定义消息的流程如下: + +1. 创建和发送自定义消息。 ```javascript function sendCustomMsg() { - // 自定义事件。 + // 设置自定义事件。 let customEvent = "customEvent"; // 通过键值对设置自定义消息内容。 let customExts = {}; @@ -666,7 +508,7 @@ function sendCustomMsg() { customEvent, // key 和 value 只支持字符串类型,否则客户端无法解析。 customExts, - // 消息扩展字段,不能设置为空,即设置为 "ext:null" 这种形式会出错。 + // 消息扩展字段,不能设置为空,即设置为 "ext:null" 会出错。 ext: {}, }; // 创建一条自定义消息。 @@ -685,7 +527,16 @@ function sendCustomMsg() { } ``` -### 发送合并消息 +2. 接收方通过 `onCustomMessage` 回调接收自定义消息。 + +```javascript +// 使用 `addEventHandler` 监听回调事件 +conn.addEventHandler("eventName", { + onCustomMessage: function (message) {}, +}); +``` + +## 发送和接收合并消息 为了方便消息互动,即时通讯 IM 自 4.2.0 版本开始支持将多个消息合并在一起进行转发。你可以采取以下步骤进行消息的合并转发: @@ -711,7 +562,7 @@ function sendCustomMsg() { | `onFileUploadComplete` | (data: { url: string; secret: string;}) => void | 合并消息文件上传完成的回调。 | | `onFileUploadError` | (error: any) => void | 合并消息文件上传失败的回调。 | -:::notice +:::tip 1. 合并转发支持嵌套,最多支持 10 层嵌套,每层最多 300 条消息。 2. 只有成功发送或接收的消息才能合并转发。 ::: @@ -748,11 +599,11 @@ conn.send }); ``` -接收合并消息与[接收普通消息](#接收消息)的操作相同,唯一不同是对于合并消息来说,消息接收事件为 `onCombineMessage`。 +#### 接收和解析合并消息 -对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 +接收合并消息与接收普通消息的操作相同,唯一不同是对于合并消息来说,消息接收事件为 `onCombineMessage`。 -#### 解析合并消息 +对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 合并消息实际上是一种附件消息。收到合并消息后,你可以调用 `downloadAndParseCombineMessage` 方法下载合并消息附件并解析出原始消息列表。 @@ -767,13 +618,13 @@ connection }); ``` -### 发送定向消息 +## 发送和接收定向消息 发送定向消息是指向群组或聊天室的单个或多个指定的成员发送消息,其他成员不会收到该消息。 该功能适用于文本消息、图片消息和音视频消息等全类型消息,最多可向群组或聊天室的 20 个成员发送定向消息。 -:::notice +:::tip 1. 仅 SDK 4.1.7 及以上版本支持。 2. 定向消息不写入服务端会话列表,不计入服务端会话的未读消息数。 3. 群组定向消息的漫游功能默认关闭,使用前需联系商务开通。 @@ -810,9 +661,9 @@ function sendTextMessage() { } ``` -接收定向消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +接收定向消息与接收普通消息的操作相同,详见各类消息的接收描述。 -### 使用消息扩展 +## 使用消息扩展 如果上述消息类型无法满足要求,你可以使用消息扩展为消息添加属性。这种情况可用于更复杂的消息传递场景,例如消息中需要携带被回复的消息内容或者是图文消息等场景。 @@ -823,7 +674,7 @@ function sendTextMessage() { msg: "message content", to: "username", chatType: "singleChat", - // 消息扩展信息。扩展字段为可选,若带有该字段,值不能为空,即 "ext:null" 会出错。 + // 设置消息扩展信息。扩展字段为可选,若带有该字段,值不能为空,即 "ext:null" 会出错。 ext: { key1: "Self-defined value1", key2: { @@ -844,6 +695,40 @@ function sendTextMessage() { } ``` -## 注意事项 +## 更多 + +### 设置聊天室消息优先级 + +针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 + +对于聊天室消息,可设置消息优先级,包括高、普通和低优先级。示例代码如下: + +```javascript +// 发送文本消息。 +function sendTextMessage() { + let option = { + type: "txt", + msg: "message content", + // 聊天室消息的优先级。如果不设置,默认值为 `normal`,即“普通”优先级。 + priority: "high" + to: "chat room ID", + chatType: "chatRoom", + }; + let msg = WebIM.message.create(option); + conn.send(msg).then(()=>{ + console.log("Send message success"); + }).catch((e)=>{ + console.log("Send message fail"); + }); +} +``` + +### 发送消息前的内容审核 + +- 内容审核关注消息 body [内容审核服务会关注消息 body 中指定字段的内容,不同类型的消息审核不同的字段](/product/moderation/moderation_mechanism.html),若创建消息时在这些字段中传入了很多业务信息,可能会影响审核效果。因此,创建消息时需要注意内容审核的字段不涉及业务信息,建议业务信息放在扩展字段中。 + +- 设置发送方收到内容审核替换后的内容 + +若初始化时打开了 `useReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 \ No newline at end of file diff --git a/docs/document/web/multi_device.md b/docs/document/web/multi_device.md index ab13d456..a19a666d 100644 --- a/docs/document/web/multi_device.md +++ b/docs/document/web/multi_device.md @@ -56,7 +56,7 @@ conn.getSelfIdsOnOtherPlatform().then((res) => { 初始化 SDK 时,你可以调用 `setLoginInfoCustomExt` 方法设置登录设备的自定义扩展信息。设置后,若因达到了登录设备数量限制而导致在已登录的设备上强制退出时(`206` 错误,`WEBIM_CONNCTION_USER_LOGIN_ANOTHER_DEVICE`),被踢设备收到的 `onDisconnected` 回调会包含导致该设备被踢下线的新登录设备的自定义扩展信息。 -:::notice +:::tip 登录成功后才会将该设置发送到服务器。 ::: diff --git a/docs/document/web/overview.md b/docs/document/web/overview.md index 31953d38..3dbb4920 100644 --- a/docs/document/web/overview.md +++ b/docs/document/web/overview.md @@ -118,7 +118,7 @@ conn }); ``` -:::notice +:::tip 该注册模式为在客户端注册,旨在方便测试,并不推荐在正式环境中使用。 ::: @@ -153,7 +153,7 @@ conn 2. **用户 ID + token** 是更加安全的登录方式。token 可以通过调用 REST API 获取,详见 [环信用户 token 的获取](/product/easemob_user_token.html)。 -:::notice +:::tip 使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 ::: diff --git a/docs/document/web/quickstart.md b/docs/document/web/quickstart.md index e98b54ff..353a62b8 100644 --- a/docs/document/web/quickstart.md +++ b/docs/document/web/quickstart.md @@ -117,7 +117,7 @@ Easemob_quickstart
`index.js` 的内容如下。本文使用 import 方法导入 SDK,并使用 webpack 对 JavaScript 文件进行打包,以避免浏览器兼容性问题。你需要分别将代码中的 `` 替换为你之前获取的 App Key。 -```Javascript +```javascript import WebIM from 'easemob-websdk' const appKey = "" @@ -213,9 +213,9 @@ window.onload = function () { } } ``` -:::notice +:::tip 对于 Typescript,通过以下代码引入类型声明: -```JavaScript +```javascript import WebIM, { EasemobChat } from 'easemob-websdk' ``` ::: @@ -249,7 +249,7 @@ import WebIM, { EasemobChat } from 'easemob-websdk' 2.在项目根目录中添加 `webpack.config.js` 文件,用于配置 webpack。文件内容如下: -```Javascript +```javascript const path = require('path'); module.exports = { @@ -291,45 +291,4 @@ $ npm run build $ npm run start:dev ``` -项目启动后,在页面输入用户名和密码进行注册,然后利用该用户名和密码登录。登录成功后,输入对方的用户名和要发送的消息,点击**发送**按钮发送消息,可同时打开另一页面相互收发消息。 - -### 6. 参考信息 - -可通过以下两种方式集成 SDK: - -#### 方法一:通过 npm 安装并导入 SDK - -1. 在 `package.json` 中的 `dependencies` 字段中加入 `easemob-websdk` 及对应版本: - -```json -{ - "name": "web", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "dependencies": { - "easemob-websdk": "latest" - }, - "author": "", - "license": "ISC" -} -``` - -2. 在你的 `index.js` 文件中导入 `easemob-websdk` 模块: - -```JavaScript -import WebIM from 'easemob-websdk' -``` - -#### 方法二:从官网获取并导入 SDK - -1. 下载 [Easemob Chat SDK for Web](https://www.easemob.com/download/im)。将 `demo/src/config` 中的 Easemob-chat 文件保存到你的项目下。 - -2. 在 `index.html` 文件中,对 `index.js` 文件进行引用。 - -```JavaScript - -``` +项目启动后,在页面输入用户名和密码进行注册,然后利用该用户名和密码登录。登录成功后,输入对方的用户名和要发送的消息,点击**发送**按钮发送消息,可同时打开另一页面相互收发消息。 \ No newline at end of file diff --git a/docs/document/web/reaction.md b/docs/document/web/reaction.md index d5509684..adc1e511 100644 --- a/docs/document/web/reaction.md +++ b/docs/document/web/reaction.md @@ -4,7 +4,7 @@ 环信即时通讯 IM 提供消息表情回复(下文统称 “Reaction”)功能。用户可以在单聊和群聊中对消息添加、删除表情。表情可以直观地表达情绪,利用 Reaction 可以提升用户的使用体验。同时在群组中,利用 Reaction 可以发起投票,根据不同表情的追加数量确认投票。 -:::notice +:::tip 目前 Reaction 仅适用于单聊和群组。聊天室暂不支持 Reaction 功能。 ::: diff --git a/docs/document/web/releasenote.md b/docs/document/web/releasenote.md index 68635d65..4ad540ed 100644 --- a/docs/document/web/releasenote.md +++ b/docs/document/web/releasenote.md @@ -48,7 +48,7 @@ ### 新增特性 - [IM SDK] 新增 `getSilentModeRemindTypeConversations` 方法,用于分页获取所有设置了推送通知方式的会话。 -- [IM SDK] 新增[从服务器拉取离线消息的开始和结束的事件回调](overview.html#连接状态相关): `onOfflineMessageSyncStart` 和 `onOfflineMessageSyncFinish`。 +- [IM SDK] 新增[从服务器拉取离线消息的开始和结束的事件回调](connection.html): `onOfflineMessageSyncStart` 和 `onOfflineMessageSyncFinish`。 - [IM SDK] 原消息置顶接口 `pinMessage` 和 `unpinMessage` [增加对单聊会话中置顶消息的支持](message_pin.html)。接口无变化。 - [IM SDK] `onMultiDeviceEvent` 新增以下两个离线推送相关的多设备通知事件: - `setSilentModeForConversation`:若你调用了 `setSilentModeForConversation` API [设置指定会话的推送通知方式或免打扰时间](/document/web/push/push_notification_mode_dnd.html#设置单个会话的推送通知),其他设备会收到该事件。 @@ -69,13 +69,13 @@ ### 新增特性 -- [IM SDK] 新增[日志上报](overview.html#日志上报)功能, 即将日志会上传到环信的服务器。该功能默认关闭,如有需要, 可联系商务开通。 +- [IM SDK] 新增[日志上报](log.html#日志上报)功能, 即将日志会上传到环信的服务器。该功能默认关闭,如有需要, 可联系商务开通。 ## 版本 V4.8.0 Dev 2024-07-01 ### 新增特性 -- [IM SDK] [`onDisconnected` 事件](overview.html#连接状态相关)新增断开原因回调参数, 告知用户触发 `onDisconnected` 的原因。 +- [IM SDK] [`onDisconnected` 事件](connection.html)新增断开原因回调参数, 告知用户触发 `onDisconnected` 的原因。 - [IM SDK] 新增[设备登录时允许携带自定义消息,并将其传递给被踢的设备](multi_device.html#设置登录设备的扩展信息): - `setLoginInfoCustomExt`:设置登录设备的扩展信息。 - `onDisconnected`:多设备登录场景下,若当前设备被新登录设备踢下线,被踢设备收到的事件中会携带新设备的扩展信息。 @@ -96,7 +96,7 @@ - [IM SDK] 新增 `getJoinedChatRooms` 方法,用于[获取当前用户加入的聊天室列表](room_manage.html#获取当前用户加入的聊天室列表)。 - [IM SDK] [撤回消息](message_recall.html#实现方法)接口 `recallMessage` 中新增 `ext` 参数,支持传入自定义字符串,设置扩展信息。 -- [IM SDK] SDK logger 中新增 `setConsoleLogVisibility` 方法,用于[设置日志是否输出到控制台](overview.html#输出信息到日志文件)。 +- [IM SDK] SDK logger 中新增 `setConsoleLogVisibility` 方法,用于[设置日志是否输出到控制台](log.html#输出信息到日志文件)。 ### 修复 @@ -135,7 +135,7 @@ - [IM SDK] [聊天室和群组成员进出事件增加成员人数 `memberCount` 字段](room_manage.html#实时更新聊天室成员人数)。 - [IM SDK] 新增 [deleteAllMessagesAndConversations](message_delete.html#清空聊天记录) 方法,用于清空当前用户的聊天记录,包括消息和会话。 - [IM SDK] 新增 [getSelfIdsOnOtherPlatform](multi_device.html#获取当前用户的其他登录设备的登录-id-列表) 方法,可以获取当前用户其他登录设备的登录 ID 列表,实现对指定设备发送消息。 -- [IM SDK] 新增 [useReplacedMessageContents](message_send_receive.html#发送文本消息) 开关。开启后,发送消息时如果被内容审核进行了内容替换,发送方可以获取替换后的内容。 +- [IM SDK] 新增 [useReplacedMessageContents](message_send_receive.html#发送消息前的内容审核) 开关。开启后,发送消息时如果被内容审核进行了内容替换,发送方可以获取替换后的内容。 ### 优化 @@ -171,7 +171,7 @@ ### 新增特性 -- [IM SDK] [发送消息方法 `Send`](message_send_receive.html#发送文本消息) 的成功回调参数 `SendMsgResult` 中新增 `message` 字段,用于返回成功发送的消息对象。 +- [IM SDK] [发送消息方法 `Send`](message_send_receive.html) 的成功回调参数 `SendMsgResult` 中新增 `message` 字段,用于返回成功发送的消息对象。 - [IM SDK] MiniCore SDK 增加 logger 实例。 ### 优化 @@ -209,7 +209,7 @@ ### 新增特性 -- [IM SDK] 新增[合并转发消息功能](message_send_receive.html#发送合并消息)。 +- [IM SDK] 新增[合并转发消息功能](message_send_receive.html#发送和接收合并消息)。 - [IM SDK] 新增[消息修改功能](message_modify.html)。 ### 修复 @@ -223,9 +223,9 @@ 1. 新增 `pinConversation` 方法实现[会话置顶和取消置顶](conversation_pin.html#置顶-取消置顶会话)。 2. 新增 `getServerPinnedConversations` 方法[分页获取服务器端的置顶会话列表](conversation_pin.html#获取服务端的置顶会话列表)。 3. 新增 `getServerConversations` 方法[分页获取排序后的服务端会话列表](conversation_list.html#从服务器分页获取会话列表)。原接口 `getConversationlist` 已废弃。 -4. 新增[在群组或聊天室会话中发送定向消息](message_send_receive.html#发送定向消息)。通过在构建消息的方法 `create` 中添加 `receiverList` 参数实现该特性。 +4. 新增[在群组或聊天室会话中发送定向消息](message_send_receive.html#发送和接收定向消息)。通过在构建消息的方法 `create` 中添加 `receiverList` 参数实现该特性。 5. 在从服务器获取历史消息的方法 `getHistoryMessages` 的返回数据中新增 `isLast` 字段表示返回的是否为最后一页数据。 -6. 在构建图片消息的方法 `create` 中新增 [`thumbnailWidth` 和 `thumbnailHeight`](message_send_receive.html#发送图片消息) 参数用于设置缩略图的宽度和高度。 +6. 在构建图片消息的方法 `create` 中新增 [`thumbnailWidth` 和 `thumbnailHeight`](message_send_receive.html#发送和接收图片消息) 参数用于设置缩略图的宽度和高度。 7. 新增以下 SDK 登录失败原因,在控制台上提示: - [错误码 50,MAX_LIMIT](error.html):新增应用的日活跃用户数(DAU)超限、在线用户数量超限和月活跃用户数(MAU)超限错误提示。 - [错误码 2, WEBIM_CONNCTION_AUTH_ERROR](error.html) :新增 Token 无效提示。 @@ -285,7 +285,7 @@ - [IM SDK] [创建群组方法 `createGroup`](group_manage.html#创建群组) 和[修改群信息方法 `modifyGroup`](group_attributes.html#修改群组信息) 新增 `ext` 字段支持群扩展信息。 - [IM SDK] 群组通知事件增加[群组信息修改事件 `updateInfo`](group_manage.html#监听群组事件)。 -- [IM SDK] 新增[聊天室消息优先级](message_send_receive.html)。 +- [IM SDK] 新增[聊天室消息优先级](message_send_receive.html#设置聊天室消息优先级)。 - [IM SDK] 支持同时[对多个群组成员禁言和解除禁言](group_members.html#管理群组禁言)。 ### 优化 @@ -978,6 +978,6 @@ - 修复 bug。demo 联系人过多时的样式问题。 - 修复 bug。conn = new Easemob.im.Connection();变量名不为 conn 或者 conn 不是全局变量时接收不到消息。 - 修复 bug。群组离线消息当作陌生人消息处理。 -- 修复 bug。IE 浏览器接受文本消息以换行符开始时会遮挡联系人名称。 +- 修复 bug。IE 浏览器接收文本消息以换行符开始时会遮挡联系人名称。 - 修复 bug。在线用户被邀请加入群组不能实时显示,必须重新登录。 - 丰富相关文档内容。 diff --git a/docs/document/web/room_members.md b/docs/document/web/room_members.md index 6f372939..fa17b6ed 100644 --- a/docs/document/web/room_members.md +++ b/docs/document/web/room_members.md @@ -150,7 +150,7 @@ conn.getChatRoomAllowlist(option).then(res => console.log(res)); 所有聊天室成员可以调用 `isInChatRoomAllowlist` 方法检查自己是否在聊天室白名单中。 -:::notice +:::tip 聊天室的管理员可查询所有用户,普通成员只能查询自己。 ::: @@ -213,7 +213,7 @@ conn.getChatRoomMuteList(option).then(res => console.log(res)) 仅聊天室所有者和管理员可调用 `muteChatRoomMember` 方法将指定成员添加至聊天室禁言列表。被禁言的成员和其他未操作的聊天室管理员或聊天室所有者收到 `muteMember` 事件。 -:::notice +:::tip 聊天室所有者可禁言聊天室所有成员,聊天室管理员可禁言聊天室普通成员。 ::: @@ -232,7 +232,7 @@ conn.muteChatRoomMember(option).then(res => console.log(res)) 仅聊天室所有者和管理员可调用 `unmuteChatRoomMember` 方法将一组成员解除禁言。 -:::notice +:::tip 聊天室所有者可对聊天室所有成员解除禁言,聊天室管理员可对聊天室普通成员解除禁言。被解除禁言的成员和其他未操作的聊天室管理员或聊天室所有者收到 `unmuteMember` 事件。 ::: diff --git a/docs/document/web/thread_message.md b/docs/document/web/thread_message.md index d625473a..999e75c1 100644 --- a/docs/document/web/thread_message.md +++ b/docs/document/web/thread_message.md @@ -6,20 +6,11 @@ ## 技术原理 -环信即时通讯 IM SDK 支持你通过调用 API 在项目中实现如下功能: - -- 发送子区消息 -- 接收子区消息 -- 撤回子区消息 -- 获取子区消息 +环信即时通讯 IM SDK 支持你通过调用 API 在项目中用于管理子区消息,包括发送、接收、撤回和获取子区消息。 消息收发流程如下: -1. 客户端从应用服务器获取 token。 -2. 客户端 A 和 B 登录即时通讯。 -3. 客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 - -![img](/images/android/sendandreceivemsg.png) +客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 子区创建和查看如下图: @@ -74,7 +65,7 @@ function sendTextMessage() { ### 接收子区消息 -可以通过 `addEventHandler` 注册监听器接收各类消息的回调,详情参考 [接收消息](message_send_receive.html#接收消息)。 +可以通过 `addEventHandler` 注册监听器接收各类消息的回调,详情参考 [接收消息](message_send_receive.html#发送和接收文本消息)。 示例代码如下: diff --git a/docs/document/web/userprofile.md b/docs/document/web/userprofile.md index 9450fc17..a0ebb3d0 100644 --- a/docs/document/web/userprofile.md +++ b/docs/document/web/userprofile.md @@ -10,7 +10,7 @@ 本文介绍如何管理用户属性,包括设置、更新、存储并获取用户的相关信息。 -:::notice +:::tip - 用户属性存储在环信服务器。如果你有安全方面的顾虑,环信建议你自行管理用户属性。 - 为保证信息安全,app 用户只能修改自己的用户属性。只有 app 管理员可以修改其他用户的用户属性。 ::: diff --git a/docs/document/windows/connection.md b/docs/document/windows/connection.md new file mode 100644 index 00000000..2f06217e --- /dev/null +++ b/docs/document/windows/connection.md @@ -0,0 +1,83 @@ +# 连接 + +应用客户端成功连接到环信服务器后,才能使用环信即时通讯 SDK 的收发消息等功能。 + +你调用 `loginWithToken` 或 `login` 方法登录后,客户端 SDK 会自动连接环信服务器。关于登录详情,请参见[登录文档](login.html)。 + +## 监听连接状态 + +你可以通过注册连接监听确认连接状态。 + +```csharp + +// 监听器建议在初始化完成之后,登录之前设置,这样可确保收到登录通知。 +class ConnectionDelegate : IConnectionDelegate +{ + public void OnConnected() + { + } + public void OnDisconnected() + { + } + public void OnAuthFailed() + { + } + public void OnRemovedFromServer() + { + } + public void OnLoginTooManyDevice() + { + } + public void OnChangedIMPwd() + { + } + public void OnKickedByOtherDevice() + { + } + public void OnLoggedOtherDevice(string deviceName) + { + } + public void OnForbidByServer() + { + } + public void OnTokenExpired() + { + } + public void OnTokenWillExpire() + { + } + public void OnAppActiveNumberReachLimitation() + { + } + // 连接成功,开始从服务器拉取离线消息时触发。 + // 注意:如果本次登录服务器没有离线消息,不会触发该回调。 + public void OnOfflineMessageSyncStart() + { + } + // 离线用户上线后从服务器拉取离线消息结束时触发。 + // 注意:如果再拉取离线过程中因网络或其他原因导致连接断开,不会触发该回调。 + public void OnOfflineMessageSyncFinish() + { + } +} +// 添加连接监听器 +ConnectionDelegate connectionDelegate = new ConnectionDelegate(); +SDKClient.Instance.AddConnectionDelegate(connectionDelegate); + +// 移除连接监听器(退出程序时建议移除) +SDKClient.Instance.DeleteConnectionDelegate(connectionDelegate); +``` + +## 自动重连 + +登录后,如果由于网络信号弱、切换网络等引起的连接中断,SDK 会自动尝试重连。重连成功或者失败时分别会收到 `onConnected` 和 `onDisconnected` 通知。 + +不过,SDK 在以下情况下会停止自动重连。你需要调用 `login` 方法登录。 + +- 用户调用了 SDK 的登出方法 `logout` 主动退出登录。 +- 登录时鉴权错误,例如, token 无效(错误码 104)或已过期(错误码 108)。 +- 用户在其他的设备上更改了密码,导致此设备上自动登录失败,提示错误码 216。 +- 用户的账号被从服务器端删除,提示错误码 207。 +- 用户在另一设备登录,将当前设备上登录的用户踢出,提示错误码 206。 +- 用户登录设备数量超过限制,提示错误码 214。 +- 应用程序的日活跃用户数量(DAU)或月活跃用户数量(MAU)达到上限,提示错误码 8。 \ No newline at end of file diff --git a/docs/document/windows/conversation_receipt.md b/docs/document/windows/conversation_receipt.md new file mode 100644 index 00000000..4c794d7d --- /dev/null +++ b/docs/document/windows/conversation_receipt.md @@ -0,0 +1,79 @@ +# 会话已读回执 + +会话已读回执指接收方进入会话页面,阅读会话中的所有消息后,调用接口向服务器发送会话已读回执,服务器将该回执回调给消息发送方,消息发送方将会收到会话已读回调。在多端多设备登录下,接收方的其他设备也会收到该回调。 + +目前,单聊和群组聊天支持会话已读回执。本文介绍如何使用环信即时通讯 IM Windows SDK 实现会话已读回执功能。 + +会话已读回执的效果示例,如下图所示: + +![img](/images/uikit/chatuikit/feature/conversation/conversation_read.png) + +## 技术原理 + + 单聊会话已读回执实现的流程如下: + + 1. 设置 `Options#RequireAck` 为 `true` 开启已读回执功能。 + 2. 消息接收方进入会话页面,阅读消息后,调用 `SendConversationReadAck` 方法发送会话已读回执。 + 3. 消息发送方通过监听 `OnConversationRead` 回调接收会话已读回执。 + +## 前提条件 + +开始前,请确保满足以下条件: + +- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 +- 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 + + ## 实现方法 + + 参考以下步骤在单聊中实现会话已读回执: + + 1. 开启已读回执功能,即 SDK 初始化时设置 `Options#RequireAck` 为 `true`。 + + ```csharp +// 设置是否需要接收方已读确认,默认为 true +options.RequireAck = true; + ``` + + 2. 接收方发送会话已读回执。 + +消息接收方进入会话页面,查看会话中是否有未读消息。若有,调用 `SendConversationReadAck` 方法发送会话已读回执,没有则不发送。该方法为异步方法。 + +若会话中存在多条未读消息,建议调用该方法,因为若调用发送消息已读回执方法 `SendMessageReadAck`,则需要调用多次。 + +```csharp +SDKClient.Instance.ChatManager.SendConversationReadAck(conversationId, new CallBack( + onSuccess: () => + { + }, + onError: (code, desc) => + { + } +)); +``` + +3. 消息发送方监听会话已读回执的回调。 + +同一用户 ID 登录多设备的情况下,用户在一台设备上发送会话已读回执,其他设备会收到 `OnConversationRead` 回调。 + +:::tip +对于群组聊天,会话已读回执只用于清空服务端的群组会话的未读数,消息发送方不会通过 `OnConversationRead` 回调收到会话已读回执。 +::: + +```csharp +public class MyDelegate : IChatManagerDelegate +{ + //... + public void OnConversationRead(string from, string to) + { + // 添加刷新页面通知等逻辑 + } + //... +} + +MyDelegate myDelegate = new MyDelegate(); +SDKClient.Instance.ChatManager.AddChatManagerDelegate(myDelegate); +``` + +## 会话已读回执和消息未读数 + +消息接收方调用 `SendConversationReadAck` 方法发送会话已读回执,开发者可调用 `Conversation#MarkAllMessageAsRead` 方法将所有未读消息设置为已读,即将该会话的未读消息数清零。 diff --git a/docs/document/windows/error.md b/docs/document/windows/error.md index d4aa33b4..3aeb03a6 100644 --- a/docs/document/windows/error.md +++ b/docs/document/windows/error.md @@ -124,7 +124,7 @@ SDKClient.Instance.Login(username, passwd, | 1299 | THIRD_DEFAULT_FAILED | 除第三方内容审核服务的其他服务的消息审核结果为“拒绝”。 | | 1300 | REACTION_REACH_LIMIT | 该消息的 Reaction 数量已达到限制。 | | 1301 | REACTION_HAS_BEEN_OPERATED | Reaction 重复添加。 | -| 1302 | REACTION_OPERATION_IS_ILLEGAL | 没有操作权限:用户对该 Reaction 没有操作权限。例如,没有添加过该 Reaction 的用户进行删除操作,或者单聊消息非发送者和非接受者进行添加 Reaction 操作。 | +| 1302 | REACTION_OPERATION_IS_ILLEGAL | 没有操作权限:用户对该 Reaction 没有操作权限。例如,没有添加过该 Reaction 的用户进行删除操作,或者单聊消息非发送者和非接收者进行添加 Reaction 操作。 | | 1400 | THREAD_NOT_EXIST | 未找到该子区,该子区不存在。 | | 1401 | THREAD_ALREADY_EXIST | 该消息 ID 下子区已存在,重复添加子区。 | | 1402 | THREAD_CREATE_MESSAGE_ILLEGAL | 创建子区的消息无效:创建子区时父消息被撤回了,或者无法使用。 | diff --git a/docs/document/windows/group_manage.md b/docs/document/windows/group_manage.md index 5a38d54a..81b49831 100644 --- a/docs/document/windows/group_manage.md +++ b/docs/document/windows/group_manage.md @@ -74,7 +74,7 @@ SDKClient.Instance.GroupManager.CreateGroup(groupname, option, desc, members, ca - 群主和群管理员同意入群申请,申请人收到 `IGroupManagerDelegate#OnRequestToJoinAcceptedFromGroup` 回调,其他群成员收到 `IGroupManagerDelegate#OnMemberJoinedFromGroup` 回调; - 群主和群管理员拒绝入群申请,申请人收到 `IGroupManagerDelegate#OnRequestToJoinDeclinedFromGroup` 回调。 -:::notice +:::tip 用户只能申请加入公开群组,私有群组不支持用户申请入群。 ::: @@ -111,7 +111,7 @@ SDKClient.Instance.GroupManager.JoinPublicGroup(groupId, new CallBack( 仅群主可以调用 `DestroyGroup` 方法解散群组。群组解散时,其他群组成员收到 `OnDestroyedFromGroup` 回调并被踢出群组。 -:::notice +:::tip 该操作只有群主才能进行,是危险操作,解散群组后,将删除本地数据库及内存中的群相关信息及群会话。 ::: diff --git a/docs/document/windows/group_members.md b/docs/document/windows/group_members.md index feaa7742..fd7b85f3 100644 --- a/docs/document/windows/group_members.md +++ b/docs/document/windows/group_members.md @@ -99,7 +99,7 @@ SDKClient.Instance.GroupManager.SetMemberAttributes(groupId, userId, dict, new C 你可调用 `GroupManager#FetchMemberAttributes` 方法根据指定的属性 key 获取多个群成员的自定义属性。 -:::notice +:::tip 每次最多可获取 10 个群成员的自定义属性。 ::: diff --git a/docs/document/windows/initialization.md b/docs/document/windows/initialization.md new file mode 100644 index 00000000..5fd98786 --- /dev/null +++ b/docs/document/windows/initialization.md @@ -0,0 +1,23 @@ +# SDK 初始化 + +初始化是使用 SDK 的必要步骤,需在所有接口方法调用前完成。 + +如果进行多次初始化操作,只有第一次初始化以及相关的参数生效。 + +:::tip +需要在主进程中进行初始化。 +::: + +## 前提条件 + +有效的环信即时通讯 IM 开发者账号和 App key,详见[环信即时通讯云控制台的相关文档](enable_and_configure_IM.html#创建应用)。 + +## 初始化 + +初始化示例代码: + +```csharp +var options = new Options("appkey"); //将该参数设置为你的 App Key +//其他 Options 配置。 +SDKClient.Instance.InitWithOptions(options); +``` diff --git a/docs/document/windows/integration.md b/docs/document/windows/integration.md new file mode 100644 index 00000000..c501cc4d --- /dev/null +++ b/docs/document/windows/integration.md @@ -0,0 +1,26 @@ +# 集成 SDK + +本文介绍如何将环信即时通讯 IM SDK 集成到你的 Windows 项目。 + +## 开发环境要求 + +- Windows SDK 1.0.5 或以上; +- Visual Studio IDE 2019 或以上; +- .Net Framework 4.5.2 或以上; +- 目前 Windows SDK 仅支持 64 位运行模式; + +## 集成 SDK + +1. 点击[这里](https://www.easemob.com/download/im) 下载 Windows SDK。下载的 NuGet 包一般存放在 `C:\Users\XXX\Downloads` (XXX 为本机用户名) 中。 + +2. 将下载的 NuGet 包拷贝到自己的工作目录下,例如 `D:\workspace\WinSDK`,下面的说明以此目录举例。 + +3. 在 Visual Studio 开发环境中,右键点击 **windows-example** 项目,选择 管理 NuGet 程序包 (N)...; + +4. 在弹出的 **NuGet:windows-example** 页签中,点击右上角的齿轮图标会弹出 NuGet 程序包源的设置窗体。点击窗体右上角的 **+** 按钮,在包源的文本框内会出现 **Package source** 一栏,点击选中,并修改文本框下的 **名称** 和 **源**。例如 **名称** 可以设置为 **Local Package source**,**源** 则设置为第 2 步中的目录,**D:\workspace\WinSDK**,点击**确定**。 + +5. 在 **NuGet:windows-example** 页签,在右上角的**程序包源**处点击下拉菜单,选中刚刚配置的包源名称**Local Package source**。 + +6. 在 **NuGet:windows-example** 页签上部,选中**浏览**,在下面搜索框的右边,勾选 **包括预发行版**,此时下面的区域会出现 **agora_chat_sdk** (如果没有出现,点击搜索框右侧的刷新按钮),选中这一栏,右边会出现一个向下的小箭头,点击进行安装,或者点击右侧栏最右边的**安装**按钮。 + +7. 在弹出的**预览更改**窗体中,点击**确定**按钮。Windows SDK 的 NuGet 包集成完毕。 \ No newline at end of file diff --git a/docs/document/windows/log.md b/docs/document/windows/log.md new file mode 100644 index 00000000..a66c613e --- /dev/null +++ b/docs/document/windows/log.md @@ -0,0 +1,21 @@ +# SDK 日志 + +环信即时通讯 IM 日志记录 SDK 相关的信息和事件。环信技术支持团队帮你排查问题时可能会请你发送 SDK 日志。 + +## 输出信息到日志文件 + +默认情况下,SDK 最多可生成和保存三个文件,`easemob.log` 和两个 `easemob_YYYY-MM-DD_HH-MM-SS.log` 文件。这些文件为 UTF-8 编码,每个不超过 2 MB。SDK 会将最新的日志写入 `easemob.log` 文件,写满时则会将其重命名为对应时间点的 `easemob_YYYY-MM-DD_HH-MM-SS.log` 文件,若日志文件超过三个,则会删除最早的文件。 + +例如,SDK 在 2024 年 1 月 1 日上午 8:00:00 记录日志时会生成 `easemob.log` 文件,若在 8:30:00 将 `easemob.log` 文件写满则会将其重命名为 `easemob_2024-01-01_08-30-00.log` 文件,随后在 9:30:30 和 10:30:30 分别生成了 `easemob_2024-01-01_09-30-30.log` 和 `easemob_2024-01-01_10-30-30.log` 文件,则此时 `easemob_2024-01-01_08-30-00.log` 文件会被移除。 + +SDK 默认不输出调试信息(所有日志,包括调试信息、警告和错误),只需输出错误日志。若需调试信息,首先要开启调试模式。 + +```csharp +Options options = new Options("YourAppKey"); +options.DebugMode = true; +SDKClient.Instance.InitWithOptions(options); +``` + +## 获取本地日志 + +Windows SDK 日志位于可执行程序同级目录下的 `sdkdata\easemobLog` 目录中。 \ No newline at end of file diff --git a/docs/document/windows/login.md b/docs/document/windows/login.md new file mode 100644 index 00000000..48bc78e9 --- /dev/null +++ b/docs/document/windows/login.md @@ -0,0 +1,143 @@ +# 登录 + +初始化 IM SDK 后,你需要首先调用接口登录。登录成功后,才能使用 IM 的功能。 + +## 用户注册 + +用户注册模式分为以下两种: + +- 开放注册:一般在体验 Demo 和测试环境时使用,正式环境中不推荐使用该方式注册环信账号。要使用开放注册,需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**开放注册**。只有打开该开关,才能使用客户端或 [REST API](/document/server-side/account_system.html#开放注册单个用户)开放注册用户。 + +示例代码如下所示: + +```csharp +SDKClient.Instance.CreateAccount(username, password, + callback: new CallBack( + + onSuccess: () => { + Debug.Log("CreateAccount succeed"); + }, + + onError: (code, desc) => { + Debug.Log($"CreateAccount failed, code: {code} ; desc: {desc}"); + } + ) +); +``` + +- 授权注册:通过环信提供的 REST API 注册环信用户账号,注册后保存到你的服务器或返给客户端。要使用授权注册,你需要在[环信即时通讯云控制台](https://console.easemob.com/user/login)的**即时通讯** > **服务概览**的**设置**区域,将**用户注册模式**设置为**授权注册**。相关的 REST API 介绍,详见[授权注册单个用户](/document/server-side/account_system.html#授权注册单个用户)和[批量授权注册用户](/document/server-side/account_system.html#批量授权注册用户)的接口介绍。 + +除此以外,可以在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建正式环境下和测试环境下的用户,详见[创建用户相关介绍](/product/enable_and_configure_IM.html#创建-im-用户)。 + +## 主动登录 + +1. **用户 ID + token** 是更加安全的登录方式。 + +测试环境下,你在[环信即时通讯云控制台](https://console.easemob.com/user/login)创建用户后,环信服务器会自动为这些用户分配用户 Token,详见[测试环境下创建用户的介绍](/product/enable_and_configure_IM.html#测试环境)。 + +使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 + +```csharp +SDKClient.Instance.LoginWithToken(username, token, true, + callback: new CallBack( + + onSuccess: () => + { + Debug.Log("login succeed"); + }, + + onError: (code, desc) => + { + if (code == 200) + { + Debug.Log("Already login.");; + } + else + { + Debug.Log($"login failed, code: {code} ; desc: {desc}"); + } + } + ) +); +``` + +2. **用户 ID + 密码**登录是传统的登录方式。用户名和密码均由你的终端用户自行决定,密码需要符合[密码规则要求](/document/server-side/account_system.html#开放注册单个用户)。 + +```csharp +SDKClient.Instance.Login(username, password, + callback: new CallBack( + + onSuccess: () => + { + Debug.Log("login succeed"); + }, + + onError: (code, desc) => + { + if (code == 200) + { + Debug.Log("Already login.");; + } + else + { + Debug.Log($"login failed, code: {code} ; desc: {desc}"); + } + } + ) +); + +``` + +## 自动登录 + +暂时不支持自动登录。 + +## 获取当前登录的用户 + +你可以调用 `SDKClient#CurrentUsername` 方法获取当前登录用户的用户 ID。 + +## 获取登录状态 + +你可以调用 `SDKClient#IsLoggedIn` 方法获取当前用户的登录状态。 + +## 退出登录 + +你可以调用 `Logout` 方法退出登录。退出登录后,你不会再收到其他用户发送的消息。 + +异步方法: + +```csharp +SDKClient.Instance.Logout(false, + callback: new CallBack( + onSuccess: () => + { + Debug.Log("Logout succeed"); + }, + + onError: (code, desc) => + { + Debug.Log($"Logout failed, code:{code}, desc:{desc}"); + } + ) +); +``` + +:::tip +如果调用退出方法,在收到 `onSuccess` 回调后才能去调用 IM 相关方法,例如 `Login`。 +::: + +## 账号切换 + +若在 app 中从当前账号切换到其他账号,你需要首先调用 `Logout` 方法登出,然后再调用 `LoginWithToken` 或 `Login` 方法登录。 + +## 多设备登录 + +除了单端单设备登录,环信即时通讯 IM 支持同一账号在多端的多个设备上登录。多设备登录时,若同端设备数量超过限制,新登录的设备会将之前登录的设备踢下线。 + +关于多设备登录场景中的设备数量限制、互踢策略以及信息同步,详见[多设备登录文档](multi_device.html)。 + +## 更多 + +### 登录被封禁账号的提示 + +在环信即时通讯控制台或调用 REST API 封禁用户账号后,若仍使用该账号登录,SDK会返回 "service is disabled"(305 错误), 可以根据用户这个返回值来进行相应的提示或者处理。 diff --git a/docs/document/windows/message_modify.md b/docs/document/windows/message_modify.md index 034402cc..7bbd613a 100644 --- a/docs/document/windows/message_modify.md +++ b/docs/document/windows/message_modify.md @@ -27,7 +27,7 @@ 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html) 及 [SDK 集成概述](overview.html)。 +- 完成 SDK 初始化,并连接到服务器,详见 [初始化](initialization.html) 及 [连接](connection.html)。 - 了解环信即时通讯 IM API 的使用限制,详见 [使用限制](/product/limitation.html)。 ## 实现方法 diff --git a/docs/document/windows/message_receipt.md b/docs/document/windows/message_receipt.md index 89de7705..92179117 100644 --- a/docs/document/windows/message_receipt.md +++ b/docs/document/windows/message_receipt.md @@ -1,239 +1,195 @@ -# 消息回执 +# 实现消息回执 -单聊会话支持消息送达回执、会话已读回执和消息已读回执,发送方发送消息后可及时了解接收方是否及时收到并阅读了信息,也可以了解整个会话是否已读。 +**单聊会话支持消息送达回执和消息已读回执**,发送方发送消息后可及时了解接收方是否及时收到并阅读了消息。 -群聊会话只支持消息已读回执。群成员在发送消息时,可以设置该消息是否需要已读回执。仅专业版及以上版本支持群消息已读回执功能。若要使用该功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 +**群聊会话只支持消息已读回执,不支持送达回执**。群成员在发送消息时,可以设置该消息是否需要已读回执。要使用该功能,你需要[在环信即时通讯云控制台上开通该功能](/product/enable_and_configure_IM.html#设置群消息已读回执),具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 -:::tip -仅单聊消息支持送达回执,群聊消息不支持。 -::: +消息送达回执和已读回执的效果示例,如下图所示: -本文介绍如何使用环信即时通讯 IM Windows SDK 实现单聊和群聊的消息回执功能。 +![img](/images/android/message_receipt.png) ## 技术原理 -环信即时通讯 IM Windows SDK 通过 `IChatManager` 类提供消息的送达回执和已读回执功能,包含的核心方法如下: +使用环信即时通讯 IM Unity/Windows SDK 可以实现消息的送达回执与已读回执。 -- `Options.RequireDeliveryAck` 开启送达回执; -- `IChatManager.SendConversationReadAck` 发出指定会话的已读回执; -- `IChatManager.SendMessageReadAck` 发出指定单聊消息的已读回执; -- `SendReadAckForGroupMessage` 发送群组消息的已读回执。 +- 单聊消息送达回执的逻辑如下: -实现消息送达和已读回执的逻辑分别如下: + 1. 你可以通过设置 `Options#RequireDeliveryAck` 为 `true` 开启送达回执功能。 + 2. 消息接收方收到消息后,SDK 自动向发送方触发送达回执。 + 3. 消息发送方通过监听 `IChatManagerDelegate#OnMessagesDelivered` 回调接收消息送达回执。 -- 单聊消息送达回执 +- 单聊消息已读回执的逻辑如下: - 1. 消息发送方在发送消息前通过 `ChatOptions.RequireDeliveryAck` 开启送达回执功能; + 1. 你可以通过设置 `Options#RequireAck` 为 `true` 开启已读回执功能。 + 2. 消息接收方收到消息后,调用 `ChatManager#SendMessageReadAck` 方法发送消息已读回执。 + 3. 消息发送方通过监听 `IChatManagerDelegate#OnMessagesRead` 回调接收消息已读回执。 - 2. 消息接收方收到消息后,SDK 自动向发送方触发送达回执; +- 群聊消息已读回执的逻辑如下: - 3. 消息发送方通过监听 `OnMessageDelivered` 回调接收消息送达回执。 - -- 单聊会话及消息已读回执 - - 1. 设置 `RequireAck` 为 `true`; - - 2. 消息接收方收到消息后,调用 API `SendConversationReadAck` 或 `SendMessageReadAck` 发送会话或消息已读回执; - - 3. 消息发送方通过监听 `OnConversationRead` 或 `OnMessageRead` 回调接收会话或消息已读回执。 - -- 群聊只支持消息已读回执 - - 1. 你可以通过设置 `isNeedGroupAck` 开启群聊消息已读回执功能; - - 2. 消息接收方收到消息后通过 `SendReadAckForGroupMessage` 发送群组消息的已读回执。 + 1. 你可以通过设置 `Options#RequireAck` 为 `true` 开启消息已读回执功能。 + 2. 发送方在群组中发送消息时设置 `Message#IsNeedGroupAck` 为 `true` 要求接收方返回消息已读回执。 + 3. 接收方收到或阅读消息后通过 `ChatManager#SendReadAckForGroupMessage` 方法发送群组消息的已读回执。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html); -- 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html); -- 群消息已读回执功能仅在环信 IM 专业版及以上版本支持该功能。若要使用该功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 +- 完成 SDK 初始化,并连接到服务器,详见 [快速开始](quickstart.html)。 +- 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 +- 要使用群消息已读回执功能,需在[环信即时通讯云控制台](https://console.easemob.com/user/login)开通,具体费用详见[产品价格](/product/pricing.html#增值服务费用)。 ## 实现方法 -### 消息送达回执 +### 单聊消息送达回执 -1. 发送方若要接收消息送达回执,你需要将 `Options` 中的 `RequireDeliveryAck` 设为 `true`。当接收方收到消息后,SDK 底层会自动进行消息送达回执。 +1. 开启消息送达功能,即 SDK 初始化时将 `Options#RequireDeliveryAck` 设置为 `true`。 ```csharp -// 设置是否需要消息送达回执,设为 `true`。 -Options.RequireDeliveryAck = true; +// 设置是否需要接收方送达确认,默认 `false` 即不需要。 +options.RequireDeliveryAck = true; ``` -2. 发送方监听事件 `OnMessagesDelivered` 回调,收到接收方的送达回执。 +2. 接收方收到消息后,SDK 自动向发送方触发送达回执。 + +3. 发送方监听 `IChatManagerDelegate#OnMessagesDelivered` 事件,收到接收方的送达回执。你可以在收到该通知时,显示消息的送达状态。 ```csharp -// 继承并实现 `IChatManagerDelegate`。 -public class ChatManagerDelegate : IChatManagerDelegate { +public class MyDelegate : IChatManagerDelegate +{ + //... - // 收到已送达回执。 - public void OnMessagesDelivered(List messages) - { - } + // 收到消息。 + public void OnMessagesReceived(List messages); { + } + // 收到已送达回执。 + public void OnMessagesDelivered(List messages); { } -// 注册监听器。 -ChatManagerDelegate adelegate = new ChatManagerDelegate(); -SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); - -// 移除监听器。 -SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(adelegate); -``` + //... +} -### 消息和会话的已读回执 +MyDelegate myDelegate = new MyDelegate(); -消息已读回执用于告知单聊或群聊中的用户接收方已阅读其发送的消息。为降低消息已读回执方法的调用次数,SDK 还支持在单聊中使用会话已读回执功能,用于获知接收方是否阅读了会话中的未读消息。 +//注册消息监听 +SDKClient.Instance.ChatManager.AddChatManagerDelegate(myDelegate); +记得在不需要的时候移除 listener +SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(myDelegate); +``` -#### 单聊 +### 单聊消息已读回执 -单聊既支持消息已读回执,也支持会话已读回执。我们建议你按照如下逻辑结合使用两种回执,减少发送消息已读回执数量。 +单聊既支持单条消息已读回执,也支持[会话已读回执](conversation_receipt.html)。我们建议你结合使用这两种回执: -- 聊天页面未打开时,若有未读消息,进入聊天页面,发送会话已读回执; -- 聊天页面打开时,若收到消息,发送消息已读回执。 +单聊消息的已读回执有效期与消息在服务端的存储时间一致,即在服务器存储消息期间均可发送已读回执。消息在服务端的存储时间与你订阅的套餐包有关,详见[产品价格](/product/pricing.html#套餐包功能详情)。 -第一步是在设置中打开已读回执开关: +参考如下步骤在单聊中实现消息已读回执。 +1. App 开启已读回执功能,即 SDK 初始化时将 `Options#RequireAck` 设置为 `true`。 ```csharp -// 设置是否需要消息已读回执,设为 `true`。 -Options.RequireReadAck = true; +// 设置是否需要接收方已读确认,默认为true +options.RequireAck = true; ``` -##### 会话已读回执 - -参考以下步骤在单聊中实现会话已读回执。 - -1. 接收方发送会话已读回执。 +2. 接收方发送消息已读回执。 -消息接收方进入会话页面,查看会话中是否有未读消息。若有,发送会话已读回执,没有则不再发送。 +- 聊天页面未打开时,若有未读消息,进入聊天页面,发送会话已读回执。这种方式可避免发送多个消息已读回执。 ```csharp SDKClient.Instance.ChatManager.SendConversationReadAck(conversationId, new CallBack( - onSuccess: () => { - - }, - onError:(code, desc) => { - - } -)); -``` - -2. 消息发送方监听会话已读回执的回调。 - -```csharp -// 继承并实现 `IChatManagerDelegate`。 -public class ChatManagerDelegate : IChatManagerDelegate { - // 收到已读回执。`from` 表示发送该会话已读回执的消息接收方,`to` 表示收到该会话已读回执的消息发送方。 - public void OnConversationRead(string from, string to) + onSuccess: () => + { + UIManager.SuccessAlert(transform); + }, + onError: (code, desc) => { + UIManager.ErrorAlert(transform, code, desc); } -} - -// 注册监听器。 -ChatManagerDelegate adelegate = new ChatManagerDelegate() -SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); - -// 移除监听器。 -SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(adelegate); +)); ``` -同一用户 ID 登录多设备的情况下,用户在一台设备上发送会话已读回执,服务器会将会话的未读消息数置为 `0`,同时其他设备会收到 `OnConversationRead` 回调。 - -##### 消息已读回执 - -单聊消息的已读回执有效期与消息在服务端的存储时间一致,即在服务器存储消息期间均可发送已读回执。消息在服务端的存储时间与你订阅的套餐包有关,详见[产品价格](/product/pricing.html#套餐包功能详情)。 - -参考如下步骤在单聊中实现消息已读回执。 - -1. 接收方发送已读回执消息。 - -消息接收方进入会话时,发送会话已读回执。 +- 聊天页面打开时,若收到消息,发送单条消息已读回执。 ```csharp -SDKClient.Instance.ChatManager.SendConversationReadAck(conversationId, new CallBack( - onSuccess: () => { - - }, - onError:(code, desc) => { +public class MyDelegate : IChatManagerDelegate +{ + //... + // 收到消息。 + public void OnMessagesReceived(List messages); { + ...... + SendReadAck(message); + ...... } -)); -``` - -2.在会话页面,接收到消息时,根据消息类型发送消息已读回执,如下所示: - -```csharp -// 继承并实现 `IChatManagerDelegate`。 -public class ChatManagerDelegate : IChatManagerDelegate { - // 收到已送达回执。 - public void OnMessageReceived(List messages) - { - ...... - sendReadAck(message); - ...... - } + //... } +MyDelegate myDelegate = new MyDelegate(); -// 注册监听器。 -ChatManagerDelegate adelegate = new ChatManagerDelegate() -SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); +//注册消息监听 +SDKClient.Instance.ChatManager.AddChatManagerDelegate(myDelegate); -// 发送已读回执。 -public void sendReadAck(Message message) { +/** +* 发送已读回执。 +* @param message +*/ +public void SendReadAck(Message message) +{ // 这里是接收的消息,未发送过已读回执且是单聊。 - if(message.Direction == MessageDirection.RECEIVE - && !message.HasReadAck - && message.MessageType == MessageType.Chat) { + if (message.Direction == MessageDirection.RECEIVE + && !message.HasReadAck + && message.MessageType == MessageType.Chat) + { MessageBodyType type = message.Body.Type; - // 视频、语音及文件需要点击后再发送,可根据需求进行调整。 - if(type == MessageBodyType.VIDEO || type == MessageBodyType.VOICE || type == MessageBodyType.FILE) { + // 视频,语音及文件需要点击后再发送,可以根据需求进行调整。 + if (type == MessageBodyType.VIDEO || type == MessageBodyType.VOICE || type == MessageBodyType.FILE) + { return; } - SDKClient.Instance.ChatManager.SendMessageReadAck(message.MsgId, new CallBack( - onSuccess: () => { - - }, - onError: (code, desc) => { - - } - ); + onSuccess: () => + { + }, + onError: (code, desc) => + { + } + )); } } ``` 3. 消息发送方监听消息已读回调。 -你可以调用接口监听指定消息是否已读,示例代码如下: +消息发送方可以通过 `IChatManagerDelegate#OnMessagesRead` 事件监听指定消息是否已读,示例代码如下: ```csharp -// 继承并实现 `IChatManagerDelegate`。 -public class ChatManagerDelegate : IChatManagerDelegate { +public class MyDelegate : IChatManagerDelegate +{ + //... - // 收到消息已读回执。 - public void OnMessagesRead(string from, string to) - { - } + public void OnMessagesRead(List messages); { + // 添加刷新消息等逻辑。 + } + + //... } -// 注册监听器。 -ChatManagerDelegate adelegate = new ChatManagerDelegate() -SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); -// 移除监听器。 -SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(adelegate); +MyDelegate myDelegate = new MyDelegate(); + +//注册消息监听 +SDKClient.Instance.ChatManager.AddChatManagerDelegate(myDelegate); +记得在不需要的时候移除 listener +SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(myDelegate); ``` -#### 群聊 +### 群聊消息已读回执 -对于群聊,群成员发送消息时,可以设置该消息是否需要已读回执。若需要,每个群成员在阅读消息后,SDK 均会发送已读回执,即阅读该消息的群成员数量即为已读回执的数量。 +对于群聊,群成员发送消息时,可以设置该消息是否需要已读回执。若需要,每个群成员阅读消息后,应该调用`ChatManager#SendReadAckForGroupMessage` 方法发送已读回执,阅读该消息的群成员数量即为已读回执的数量。 群消息已读回执特性的使用限制如下表所示: @@ -243,69 +199,120 @@ SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(adelegate); | 使用权限 | 所有群成员 | 默认情况下,所有群成员发送消息时可要求已读回执。如果仅需群主和群管理员发消息时要求已读回执,可联系商务修改。 | | 已读回执有效期 | 3 天 | 群聊已读回执的有效期为 3 天,即群组中的消息发送时间超过 3 天,服务器不记录阅读该条消息的群组成员,也不会发送已读回执。 | | 群规模 | 200 人 | 该特性最大支持 200 人的群组。如果超过 200 人/群,群成员发送的消息不会返回已读回执。你可以联系商务提升群成员人数上限。 | -| 查看返回已读回执数量 | 消息发送方 | 对消息返回的已读回执数量(或返回已读回执的人数),默认仅消息发送方可查看。如需所有群成员均可查看,可联系商务开通。 | +| 查看返回已读回执数量 | 消息发送方 | 对消息返回的已读回执数量(或返回已读回执的人数),默认仅消息发送方可查看。如需所有群成员均可查看,可联系商务开通。 | 你可以按以下步骤实现群消息已读回执特性: -1. 群成员发送消息时若需已读回执,需设置 `Message` 的 `IsNeedGroupAck` 为 `true`。 +1. 开启已读回执功能,即 SDK 初始化时将 `Options#RequireAck` 设置为 `true`。 + +该功能开启后,接收方阅读消息后,SDK 底层会自动进行消息已读回执。 +```csharp +// 设置是否需要接收方已读确认,默认为 `true`。 +options.RequireAck = true; +``` + +2. 发送方发送消息时设置 `Message#IsNeedGroupAck` 属性为 `true`。 + +与单聊消息的 app 层级设置已读回执功能不同,群聊消息是在发送消息时设置指定消息是否需要已读回执。 ```csharp -Message msg = Message.CreateTextSendMessage("to", "hello world"); +Message msg = Message.CreateTextSendMessage(to, content); msg.IsNeedGroupAck = true; ``` -2. 消息接收方发送群组消息的已读回执。 +3. 消息接收方发送群组消息的已读回执。 ```csharp -void SendReadAckForGroupMessage(string messageId, string ackContent) -{ - SDKClient.Instance.ChatManager.SendReadAckForGroupMessage(messageId, ackContent,callback: new CallBack( - onSuccess: () => - { +public void sendAckMessage(EMMessage message) { - }, - onError: (code, desc) => - { + if (message.HasReadAck) { + return; + } - } - )); + // 多设备登录时,无需再向自己发送的消息发送已读回执。 + if (SDKClient.Instance.CurrentUsername.CompareTo(message.From)) { + return; } + + if (message.IsNeedGroupAck() && !message.IsRead) { + string to = message.ConversationId; + string msgId = message.MsgId; + SDKClient.Instance.ChatManager.SendReadAckForGroupMessage(message.MsgId, “”, new CallBack( + onSuccess: () => + { + }, + onError: (code, desc) => + { + } + )); + message.IsRead = true; + } +} ``` -3. 消息发送方监听群组消息已读回调。 +4. 消息发送方监听群组消息已读回调。 + +群消息已读回调在 `IChatManagerDelegate#OnGroupMessageRead` 中实现。 -群组消息已读回调在消息监听类 `IChatManagerDelegate` 中实现。 +发送方接收到群组消息已读回执后,其发出消息的属性 `Message#GroupAckCount` 会有相应变化。 ```csharp -// 继承并实现 `IChatManagerDelegate`。 -public class ChatManagerDelegate : IChatManagerDelegate { +public class MyDelegate : IChatManagerDelegate +{ + //... + // 接收到群组消息体的已读回执, 表明消息的接收方已经阅读此消息。 + public void OnGroupMessageRead(List list); { - // 收到群组消息的已读回执, 表明消息的接收方已阅读此消息。 - public void OnGroupMessageRead(List list) - { + } - } + //... } -// 注册监听器。 -ChatManagerDelegate adelegate = new ChatManagerDelegate() -SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); +MyDelegate myDelegate = new MyDelegate(); + +//注册消息监听 +SDKClient.Instance.ChatManager.AddChatManagerDelegate(myDelegate); +记得在不需要的时候移除 listener +SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(myDelegate); ``` -4. 消息发送方获取群组消息的已读回执详情。 +5. 消息发送方获取群组消息的已读回执详情。 -你可以调用 `FetchGroupReadAcks` 获取群组消息的已读回执的详情,示例代码如下: +你可以调用 `ChatManager#FetchGroupReadAcks` 方法从服务器获取单条消息的已读回执的详情。 ```csharp -// startAckId:查询起始的已读回执 ID。首次调用为空,SDK 从最新的已读回执开始按服务器接收回执时间的逆序获取。后续调用从 CursorResult 中的 cursor 获取。 -// pageSize:每页获取群消息已读回执的条数。 -SDKClient.Instance.ChatManager.FetchGroupReadAcks(messageId, groupId, startAckId, pageSize, new ValueCallBack>( - onSuccess: (list) => - { - // 页面刷新等操作。 - }, - onError: (code, desc) => - { - } -)); +//messageId 消息 ID。 +//groupId 群组 ID。 +//pageSize 每页获取群消息已读回执的条数。取值范围[1,50]。 +//startAckId 已读回执的 ID,如果为空,从最新的回执向前开始获取。 +//callBack 结果回调,成功执行 {@link ValueCallBack#onSuccess(Object)},失败执行 {@link ValueCallBack#onError(int, String)}。 +public void FetchGroupReadAcks(string messageId, string groupId, int pageSize = 20, string startAckId = null, ValueCallBack> callback = null) ``` + +### 查看消息送达和已读状态 + +对于单聊消息,本地通过 `Message#HasDeliverAck` 字段存储消息送达状态。 + +对于单聊消息,本地通过以下字段存储消息已读状态: + +| 字段 | 描述 | +| :--------- | :----- | +| `Message#IsRead` | 用户是否已读了该消息。如果是自己发送的消息,该字段的值固定为 `true`。| +| `Message#HasReadAck` | 是否(消息接收方)已发送或(消息发送方)已收到消息已读回执。如果是自己发送的消息,记录的是对方是否已读。如果是对方的消息,则记录的是自己是否发送过已读回执。 | + +对于群聊消息,本地数据库通过以下字段存储消息已读状态: + +| 字段 | 描述 | +| :--------- | :----- | +| `Message#IsRead` | 用户是否已读了该消息。如果是自己发送的消息,该字段的值固定为 `true`。| +| `Message#GroupAckCount` | 已阅读消息的群成员数量。 | + +### 已读回执与未读消息数 + +- 会话已读回执发送后,开发者需要调用 `Conversation#MarkAllMessageAsRead` 方法将该会话的所有消息置为已读,即会话的未读消息数清零。 + +- 消息已读回执发送后,开发者需要调用 `Conversation#MarkMessageAsRead` 方法将该条消息置为已读,则消息未读数会有变化。 + + + + diff --git a/docs/document/windows/message_send_receive.md b/docs/document/windows/message_send_receive.md index 1634e3df..dcab60e0 100644 --- a/docs/document/windows/message_send_receive.md +++ b/docs/document/windows/message_send_receive.md @@ -1,53 +1,23 @@ # 发送和接收消息 - +环信即时通讯 IM Unity SDK 通过 `IChatManager` 和 `Message` 类实现文本、图片、音频、视频和文件等类型的消息的发送和接收。 -登录即时通讯 IM app 后,用户可以在单聊、群聊、聊天室中发送如下类型的消息: +- 对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。 -- 文字消息,包含超链接和表情消息。 -- 附件消息,包含图片、语音、视频及文件消息。 -- 位置消息。 -- 透传消息。 -- 自定义消息。 -- 合并消息。 -- 定向消息。 +- 对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 -对于单聊,环信即时通信 IM 默认支持陌生人之间发送消息,即无需添加好友即可聊天。若仅允许好友之间发送单聊消息,你需要你需要[开启好友关系检查](/product/enable_and_configure_IM.html#好友关系检查)。对于群组和聊天室,用户每次只能向所属的单个群组和聊天室发送消息。 - -针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性 。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 - -本文介绍如何使用即时通讯 IM SDK 实现发送和接收这些类型的消息。 - -## 技术原理 - -环信即时通讯 IM SDK 通过 `IChatManager` 和 `Message` 类实现消息的发送和接收。 - -其中,发送和接收消息的逻辑如下: - -1. 发送方调用相应 `Create` 方法创建文本、文件、附件等类型的消息; -2. 发送方调用 `SendMessage` 发送消息; -3. 接收方通过 `AddChatManagerDelegate` 方法监听消息回调事件。在收到 `OnMessagesReceived` 后,即表示成功接收到消息。 - -消息收发流程如下: - -1. 用户 A 发送一条消息到环信的消息服务器; -2. 单聊消息时,服务器投递消息给用户 B;对于群聊时消息,服务器投递给群内其他每一个成员; -3. 用户收到消息。 - -![img](/images/android/sendandreceivemsg.png) +单聊、群组聊天和聊天室的消息发送控制,详见[消息发送控制](/product/product_message_overview.html#消息发送控制)文档。 ## 前提条件 开始前,请确保满足以下条件: -- 完成 SDK 初始化,详见 [快速开始](quickstart.html)。 +- 完成 SDK 初始化,详见 [初始化文档](initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 -## 实现方法 +## 发送和接收文本消息 -### 发送文本消息 - -你可以利用 `Message` 类构造一个消息,然后通过 `IChatManager` 将该消息发出。 +1. 你可以利用 `Message` 类构造一个消息,然后通过 `IChatManager` 将该消息发出。 默认情况下,SDK 对单个用户发送消息的频率未做限制。如果你联系了环信商务设置了该限制,一旦在单聊、群聊或聊天室中单个用户的消息发送频率超过设定的上限,SDK 会上报错误,即错误码 509 `MESSAGE_CURRENT_LIMITING`。 @@ -79,13 +49,7 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -若初始化时打开了 `Options#UseReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 - -### 接收消息 - -你可以用注册监听 `IChatManagerDelegate` 接收消息。 - -该 `IChatManagerDelegate` 可以多次添加,请记得在不需要的时候移除该监听。如在析构 `IChatManagerDelegate` 的继承实例之前。 +2. 你可以用注册监听 `IChatManagerDelegate` 接收消息。该 `IChatManagerDelegate` 可以多次添加,请记得在不需要的时候移除该监听。如在析构 `IChatManagerDelegate` 的继承实例之前。 在新消息到来时,你会收到 `OnMessagesReceived` 的回调,消息接收时可能是一条,也可能是多条。你可以在该回调里遍历消息队列,解析并显示收到的消息。若在初始化时打开了 `Options#IncludeSendMessageInMessageListener` 开关,则该回调中会返回发送成功的消息。 @@ -110,11 +74,17 @@ SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); SDKClient.Instance.ChatManager.RemoveChatManagerDelegate(adelegate); ``` -### 发送和接收附件消息 +## 发送和接收附件消息 + +除文本消息外,SDK 还支持发送附件类型消息,包括语音、图片、视频和文件消息。 -除文本消息外,还有几种其他类型的消息,其中语音,图片,短视频,文件等消息,是通过先将附件上传到消息服务器的方式实现。收到语音时,会自动下载,而图片和视频会自动下载缩略图。文件消息不会自动下载附件,接收方需调用下载附件的 API,具体实现参考下文。 +附件消息的发送和接收过程如下: -#### 发送和接收语音消息 +1. 创建和发送附件类型消息。SDK 将附件上传到环信服务器。 +2. 接收附件消息。SDK 自动下载语音消息,默认自动下载图片和视频的缩略图。若下载原图、视频和文件,需调用 `DownloadAttachment` 方法。 +3. 获取附件的服务器地址和本地路径。 + +### 发送和接收语音消息 发送语音消息时,应用层需完成语音文件录制的功能,提供语音文件的 URI 和语音时长。 @@ -145,6 +115,8 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` +语音消息的接收与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + 接收方收到语音消息后,参考如下示例代码获取语音消息的附件: ```csharp @@ -165,11 +137,11 @@ else { } ``` -#### 发送和接收图片消息 +### 发送和接收图片消息 -图片消息默认会被压缩后发出,可通过设置 `original` 参数为 `true` 发送原图。 +1. 创建并发送图片消息。 -参考如下示例代码,创建并发送图片消息: +图片消息默认会被压缩后发出,可通过设置 `original` 参数为 `true` 发送原图。 ```csharp //`localPath` 为图片本地资源路径。 @@ -198,7 +170,14 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -接收方收到图片消息后,参考如下示例代码获取图片消息的缩略图和附件: +2. 图片消息的接收与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + +接收方收到图片消息后,参考如下示例代码获取图片消息的缩略图和附件。 + +- 接收方如果设置了自动下载,即 `Options.IsAutoDownload` 为 `true`,SDK 接收到消息后会下载缩略图 +- 如果未设置自动下载,需主动调用 `SDKClient.Instance.ChatManager.DownloadThumbnail` 下载。 + +下载完成后,调用相应消息 `msg.Body` 的 `ThumbnailLocalPath` 获取缩略图路径。 ```csharp //注意:这里的 "Message ID" 是消息发送成功以后(`CallBack` 中的 `onSuccess` 被触发以后),被发送消息的 ID。 @@ -225,15 +204,11 @@ else { } ``` -接收方如果设置了自动下载,即 `Options.IsAutoDownload` 为 `true`,SDK 接收到消息后会下载缩略图;如果未设置自动下载,需主动调用 `SDKClient.Instance.ChatManager.DownloadThumbnail` 下载。 - -下载完成后,调用相应消息 `msg.Body` 的 `ThumbnailLocalPath` 去获取缩略图路径。 - -#### 发送和接收短视频消息 +### 发送和接收视频消息 -发送短视频消息时,应用层需要完成视频文件的选取或者录制。视频消息支持给出视频的时长作为参数,发送给接收方。 +发送视频消息时,应用层需要完成视频文件的选取或者录制。视频消息支持给出视频的时长作为参数,发送给接收方。 -参考如下示例代码,创建并发送短视频消息: +1. 创建并发送视频消息: ```csharp Message msg = Message.CreateVideoSendMessage(toChatUsername, localPath, displayName, thumbnailLocalPath, fileSize, duration, width, height); @@ -253,13 +228,9 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -默认情况下,当收件人收到短视频消息时,SDK 会下载视频消息的缩略图。 +2. 接收方收到视频消息时,自动下载视频缩略图。你可以设置自动或手动下载视频缩略图,该设置与图片缩略图相同,详见[设置图片缩略图自动下载](#发送和接收图片消息)。 -如果不希望 SDK 自动下载视频缩略图,可以将 `Options.IsAutoDownload` 设置为 `false`。 - -如果未设置自动下载,需主动调用 `SDKClient.Instance.ChatManager.DownloadThumbnail` 下载。下载完成后,使用相应消息 `Body` 的 `ThumbnailLocalPath` 成员获取缩略图路径。 - -短视频文件本身需要通过 `SDKClient.Instance.ChatManager.DownloadAttachment` 下载,下载完成后,使用相应消息 `Body` 的 `LocalPath` 成员获取短视频文件路径。 +视频文件本身需要通过 `SDKClient.Instance.ChatManager.DownloadAttachment` 下载,下载完成后,使用相应消息 `Body` 的 `LocalPath` 成员获取视频文件路径。 ```csharp // 接收到视频消息需先下载附件才能打开。 @@ -272,7 +243,7 @@ SDKClient.Instance.ChatManager.DownloadAttachment("Message ID", new CallBack( { if (msg.Body.Type == ChatSDK.MessageBodyType.VIDEO) { ChatSDK.MessageBody.VideoBody vb = (ChatSDK.MessageBody.VideoBody)msg.Body; - //从本地获取短视频文件路径。 + //从本地获取视频文件路径。 string videoLocalUri = vb.LocalPath; //这里可以根据本地路径打开文件。 } @@ -291,9 +262,9 @@ SDKClient.Instance.ChatManager.DownloadAttachment("Message ID", new CallBack( )); ``` -#### 发送和接收文件消息 +### 发送和接收文件消息 -参考如下示例代码创建并发送文件消息: +1. 创建并发送文件消息。 ```csharp // localPath 为文件本地路径,displayName 为消息显示名称,fileSize 为文件大小。 @@ -337,9 +308,13 @@ else { ``` -### 发送位置消息 +2. 文件消息的接收与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + +## 发送和接收位置消息 -当你需要发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 +1. 创建和发送位置消息。 + +发送位置时,需要集成第三方的地图服务,获取到位置点的经纬度信息。 ```csharp //`latitude` 为纬度,`longitude` 为经度,`locationAddress` 为具体位置内容,`buildingName` 为建筑名称。 @@ -355,14 +330,23 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -### 发送和接收透传消息 +2. 接收位置消息与文本消息一致,详见[接收文本消息](#发送和接收文本消息)。 + + 接收方接收到位置消息时,需要将该位置的经纬度,借由第三方的地图服务,将位置在地图上显示出来。 -透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。(透传消息不会存入本地数据库中,所以在 UI 上不会显示)。具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 “em_” 和 “easemob::” 开头的 action 为内部保留字段,注意不要使用。 +## 发送和接收透传消息 + +透传消息可视为命令消息,通过发送这条命令给对方,通知对方要进行的操作,收到消息可以自定义处理。 + +具体功能可以根据自身业务需求自定义,例如实现头像、昵称的更新等。另外,以 `em_` 和 `easemob::` 开头的 action 为内部保留字段,注意不要使用。 :::tip -透传消息发送后,不支持撤回。 +- 透传消息发送后,不支持撤回。 +- 透传消息不会存入本地数据库中,所以在 UI 上不会显示。 ::: +1. 创建和发送透传消息。 + ```csharp //`action` 可以自定义。 string action = "actionXXX"; @@ -377,7 +361,7 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -请注意透传消息的接收方,也是由单独的回调进行通知,方便用户进行不同的处理。 +2. 接收方通过 `OnMessagesReceived` 和 `OnCmdMessagesReceived` 回调接收透传消息,方便用户进行不同的处理。 ```csharp // 继承并实现 `IChatManagerDelegate`。 @@ -401,79 +385,12 @@ SDKClient.Instance.ChatManager.AddChatManagerDelegate(adelegate); ``` -#### 通过透传消息实现输入指示器 - -输入指示器显示其他用户何时输入消息。通过该功能,用户之间可进行有效沟通,增加了用户对聊天应用中交互的期待感。 - -你可以通过透传消息实现输入指示器。下图为输入指示器的工作原理。 - -![img](/images/common/typing_indicator.png) - -监听用户 A 的输入状态。一旦有文本输入,通过透传消息将输入状态发送给用户 B,用户 B 收到该消息,了解到用户 A 正在输入文本。 - -- 用户 A 向用户 B 发送消息,通知其开始输入文本。 -- 收到消息后,如果用户 B 与用户 A 的聊天页面处于打开状态,则显示用户 A 的输入指示器。 -- 如果用户 B 在几秒后未收到用户 A 的输入,则自动取消输入指示器。 - -:::tip - -用户 A 可根据需要设置透传消息发送间隔。 - -::: - -以下示例代码展示如何发送输入状态的透传消息。 - -```csharp -//发送表示正在输入的透传消息 -string msgTypingBegin = "TypingBegin"; - -void textChange() { - int currentTimestamp = getCurrentTimestamp(); - if (currentTimestamp - _previousChangedTimeStamp > 5) { - _sendBeginTyping(); - _previousChangedTimeStamp = currentTimestamp; - } -} - -void _sendBeginTyping() { - var msg = Message.CreateCmdSendMessage( - username: conversationId, - action: msgTypingBegin, - deliverOnlineOnly: true, - ); - msg.chatType = MessageType.Chat; - SDKClient.getInstance.chatManager.sendMessage(msg); -} - -``` - -以下示例代码展示如何接受和解析输入状态的透传消息。 - -```csharp -int typingTime = 10; - -void OnCmdMessagesReceived(List list) { - for (var msg in list) { - if (msg.ConversationId != currentConversationId) { - continue; - } - MessageBody.CmdBody body = msg.Body as MessageBody.CmdBody; - if (body.Action == msgTypingBegin) { - // 这里需更新 UI,显示“对方正在输入” - - Timer timer = new Timer((state) => - { - // 这里需更新 UI,不再显示“对方正在输入” - }, null, typingTime, Timeout.Infinite); - } - } -} -``` - -### 发送自定义类型消息 +## 发送和接收自定义类型消息 除了几种消息之外,你可以自己定义消息类型,方便业务处理,即首先设置一个消息类型名称,然后可添加多种自定义消息。 +1. 创建和发送自定义类型消息的示例代码: + ```csharp //`event` 为字符串类型的自定义事件,比如礼物消息,可以设置: string event = "gift"; @@ -493,7 +410,9 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -### 发送和接收合并消息 +2. 接收自定义消息与其他类型消息一致,详见[接收文本消息](#发送和接收文本消息)。 + +## 发送和接收合并消息 为了方便消息互动,即时通讯 IM 自 1.2.0 版本开始支持将多个消息合并在一起进行转发。你可以采取以下步骤进行消息的合并转发: @@ -539,11 +458,11 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -接收合并消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +#### 接收和解析合并消息 -对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 +接收合并消息与接收普通消息的操作相同,详见[接收消息](#发送和接收文本消息)。 -#### 解析合并消息 +对于不支持合并转发消息的 SDK 版本,该类消息会被解析为文本消息,消息内容为 `compatibleText` 携带的内容,其他字段会被忽略。 合并消息实际上是一种附件消息。收到合并消息后,你可以调用 `FetchCombineMessageDetail` 方法下载合并消息附件并解析出原始消息列表。 @@ -563,7 +482,7 @@ SDKClient.Instance.ChatManager.FetchCombineMessageDetail(msg, new ValueCallBack< )); ``` -### 发送和接收定向消息 +## 发送和接收定向消息 发送定向消息是指向群组或聊天室的单个或多个指定的成员发送消息,其他成员不会收到该消息。 @@ -607,9 +526,9 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( )); ``` -接收群定向消息与接收普通消息的操作相同,详见[接收消息](#接收消息)。 +接收群定向消息与接收普通消息的操作相同,详见[接收文本消息](#发送和接收文本消息)。 -### 使用消息扩展字段 +## 使用消息扩展字段 当 SDK 提供的消息类型不满足需求时,你可以通过消息扩展字段传递自定义的内容,从而生成自己需要的消息类型。 @@ -646,7 +565,47 @@ if (found) { } ``` -## 注意事项 +## 更多 + +### 设置聊天室消息优先级 + +针对聊天室消息并发量较大的场景,即时通讯服务提供消息分级功能。你可以通过设置消息优先级,将消息划分为高、普通和低三种级别。你可以在创建消息时,将指定消息类型,或指定成员的所有消息设置为高优先级,确保此类消息优先送达。这种方式可以确保在聊天室内消息并发量较大或消息发送频率过高的情况下,服务器首先丢弃低优先级消息,将资源留给高优先级消息,确保重要消息(如打赏、公告等)优先送达,以此提升重要消息的可靠性。请注意,该功能并不保证高优先级消息必达。在聊天室内消息并发量过大的情况下,为保证用户实时互动的流畅性,即使是高优先级消息仍然会被丢弃。 + +对于聊天室消息,可设置消息优先级,包括高、普通和低优先级。示例代码如下: + +```csharp +//创建一条文本消息,`content` 为消息文字内容。 +// `conversationId` 为消息接收方,单聊为对端用户的 ID,群聊为群组 ID,聊天室时为聊天室 ID。 +Message msg = Message.CreateTextSendMessage(conversationId, content); + +//设置会话类型,即 `Message` 类的 `MessageType` 属性。 +//聊天室为 `Room`。 +msg.MessageType = MessageType.Room; + +//对于聊天室消息,可设置消息优先级。 +msg.MessageType = MessageType.Room; +//聊天室消息的优先级。如果不设置,默认值为 `RoomMessagePriority.Normal`,即“普通”优先级。 +msg.SetRoomMessagePriority(RoomMessagePriority.High); + +//发送消息。 +//发送消息时可设置 `CallBack` 的实例,获得消息发送状态。可在该回调中更新消息的显示状态。例如消息发送失败后的提示等。 +SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( + onSuccess: () => { + Debug.Log($"{msg.MsgId}发送成功"); + }, + onError:(code, desc) => { + Debug.Log($"{msg.MsgId}发送失败,errCode={code}, errDesc={desc}"); + } +)); +``` + +### 发送消息前的内容审核 + +- 内容审核关注消息 body [内容审核服务会关注消息 body 中指定字段的内容,不同类型的消息审核不同的字段](/product/moderation/moderation_mechanism.html),若创建消息时在这些字段中传入了很多业务信息,可能会影响审核效果。因此,创建消息时需要注意内容审核的字段不涉及业务信息,建议业务信息放在扩展字段中。 +- 设置发送方收到内容审核替换后的内容 + +若初始化时打开了 `Options#UseReplacedMessageContents` 开关,发送文本消息时如果被内容审核(Moderation)进行了内容替换,发送方会收到替换后的内容。若该开关为关闭状态,则发送方不会收到替换后的内容。 + diff --git a/docs/document/windows/overview.md b/docs/document/windows/overview.md index af96a514..885782d7 100644 --- a/docs/document/windows/overview.md +++ b/docs/document/windows/overview.md @@ -42,7 +42,7 @@ SDKClient.Instance.InitWithOptions(options); | `DeleteMessagesAsExitGroup` | 是否需要在离开群组时自动删除聊天历史消息。设置为 `true` 则在退出群组的时候,会删除聊天记录。 | | `DeleteMessagesAsExitRoom` | 是否需要在离开聊天室时自动删除聊天历史消息。设置为 `true` 则在退出聊天室的时候,会删除记录。 | | `IsRoomOwnerLeaveAllowed` | 是否允许聊天室所有者离开聊天室。设置为 `true` 则允许。详见 [聊天室](room_overview.html) 章节。 | -| `IsAutoDownload` | 是否开启自动下载。设置为 `true` 则收到图片、视频、音频、语音消息会自动下载。详见 [消息](message_send_receive.html#接收消息) 章节。 | +| `IsAutoDownload` | 是否开启自动下载。设置为 `true` 则收到图片、视频、音频、语音消息会自动下载。详见 [消息](message_send_receive.html#发送和接收图片消息) 章节。 | ## 注册用户 @@ -78,7 +78,7 @@ SDKClient.Instance.CreateAccount(username, password, ); ``` -:::notice +:::tip 该注册模式为在客户端注册,旨在方便测试,并不推荐在正式环境中使用。 ::: @@ -123,7 +123,7 @@ SDKClient.Instance.Login(username, password, 2. **用户 ID + token** 是更加安全的登录方式。token 可以通过调用 REST API 获取,详见 [环信用户 token 的获取](/document/server-side/easemob_user_token.html)。 -:::notice +:::tip 使用 token 登录时需要处理 token 过期的问题,比如在每次登录时更新 token 等机制。 ::: diff --git a/docs/document/windows/quickstart.md b/docs/document/windows/quickstart.md index 149a097a..d7c7e466 100644 --- a/docs/document/windows/quickstart.md +++ b/docs/document/windows/quickstart.md @@ -98,7 +98,7 @@ SDKClient.Instance.CreateAccount(username, password, callback: new CallBack( )); ``` -:::notice +:::tip 该注册模式为在客户端注册,主要用于测试,简单方便,但不推荐在正式环境中使用;正式环境中应使用服务器端调用 Restful API 注册,具体见:[注册单个用户](https://docs-im.easemob.com/ccim/rest/accountsystem#注册单个用户)。 ::: diff --git a/docs/document/windows/reaction.md b/docs/document/windows/reaction.md index 59dcbefa..79728351 100644 --- a/docs/document/windows/reaction.md +++ b/docs/document/windows/reaction.md @@ -4,7 +4,7 @@ 环信即时通讯 IM 提供消息表情回复(下文统称 “Reaction”)功能。用户可以在单聊和群聊中对消息添加、删除表情。表情可以直观地表达情绪,利用 Reaction 可以提升用户的使用体验。同时在群组中,利用 Reaction 可以发起投票,根据不同表情的追加数量来确认投票。 -:::notice +:::tip 目前 Reaction 仅适用于单聊和群组。聊天室暂不支持 Reaction 功能。 diff --git a/docs/document/windows/room_members.md b/docs/document/windows/room_members.md index 048efb04..b8d2857a 100644 --- a/docs/document/windows/room_members.md +++ b/docs/document/windows/room_members.md @@ -227,7 +227,7 @@ SDKClient.Instance.RoomManager.RemoveAllowListMembers(roomId, list, new CallBack 仅聊天室所有者和管理员可以调用 `MuteRoomMembers` 方法将指定成员添加至聊天室禁言列表。被禁言后,该成员和其他未操作的聊天室管理员或聊天室所有者收到 `OnMuteListAddedFromRoom` 回调。 -:::notice +:::tip 聊天室所有者可禁言聊天室所有成员,聊天室管理员可禁言聊天室普通成员。 ::: @@ -247,7 +247,7 @@ SDKClient.Instance.RoomManager.MuteRoomMembers(roomId, members, new CallBack( 仅聊天室所有者和管理员可以调用 `UnMuteRoomMembers` 方法将成员移出聊天室禁言列表。被解除禁言后,其他成员收到 `OnMuteListRemovedFromRoom` 回调。 -:::notice +:::tip 聊天室所有者可对聊天室所有成员解除禁言,聊天室管理员可对聊天室普通成员解除禁言。 ::: diff --git a/docs/document/windows/thread.md b/docs/document/windows/thread.md index db597c76..1b090fa4 100644 --- a/docs/document/windows/thread.md +++ b/docs/document/windows/thread.md @@ -64,7 +64,7 @@ SDKClient.Instance.ThreadManager.CreateThread(threadName, msgId, groupid, new Va 单设备登录时,子区所属群组的所有成员均会收到 `IChatThreadManagerDelegate#onThreadNotifyChange` 回调;多设备登录时,其他设备会同时收到 `IMultiDeviceDelegate#onThreadMultiDevicesEvent` 回调,回调事件为 `THREAD_DESTROY`。 -:::notice +:::tip 解散子区或解散子区所在的群组后,将删除本地数据库及内存中关于该子区的全部数据,需谨慎操作。 ::: diff --git a/docs/document/windows/thread_message.md b/docs/document/windows/thread_message.md index 639f30fd..ac75320f 100644 --- a/docs/document/windows/thread_message.md +++ b/docs/document/windows/thread_message.md @@ -6,20 +6,11 @@ ## 技术原理 -环信即时通讯 IM windows SDK 提供 `IChatManager`、`Message` 和 `IChatThreadManager` 类,用于管理子区消息,支持你通过调用 API 在项目中实现如下功能: - -- 发送子区消息 -- 接收子区消息 -- 撤回子区消息 -- 获取子区消息 +环信即时通讯 IM windows SDK 提供 `IChatManager`、`Message` 和 `IChatThreadManager` 类,用于管理子区消息,支持你通过调用 API 在项目中实现发送、接收、撤回和获取子区消息。 消息收发流程如下: -1. 客户端从应用服务器获取 token。 -2. 客户端 A 和 B 登录即时通讯。 -3. 客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 - -![img](/images/android/sendandreceivemsg.png) +客户端 A 向客户端 B 发送消息。消息发送至即时通讯 IM 服务器,服务器将消息传递给客户端 B。对于子区消息,服务器投递给子区内其他每一个成员。客户端 B 收到消息后,SDK 触发事件。客户端 B 监听事件并获取消息。 子区创建和查看如下图: @@ -39,7 +30,7 @@ ### 发送子区消息 -发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html#发送文本消息)。唯一不同的是,发送子区消息需要指定标记 `IsThread` 为 `true`。 +发送子区消息和发送群组消息的方法基本一致,详情请参考 [发送消息](message_send_receive.html#发送和接收文本消息)。唯一不同的是,发送子区消息需要指定标记 `IsThread` 为 `true`。 示例代码如下: @@ -66,7 +57,7 @@ SDKClient.Instance.ChatManager.SendMessage(ref msg, new CallBack( ### 接收子区消息 -接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#接收消息),此处只介绍子区消息和其他消息的区别。 +接收消息的具体逻辑,请参考 [接收消息](message_send_receive.html#发送和接收文本消息),此处只介绍子区消息和其他消息的区别。 子区有新增消息时,子区所属群组的所有成员收到 `IChatThreadManagerDelegate#OnUpdateMyThread` 回调,子区成员收到 `IChatManagerDelegate#OnMessagesReceived` 回调。 diff --git a/docs/document/windows/userprofile.md b/docs/document/windows/userprofile.md index 960aa896..da0bab3a 100644 --- a/docs/document/windows/userprofile.md +++ b/docs/document/windows/userprofile.md @@ -8,7 +8,7 @@ 本文介绍如何通过管理用户属性设置、更新、存储并获取实时消息用户的相关信息。 -:::notice +:::tip 为保证用户信息安全,SDK 仅支持用户设置或更新自己的用户属性。 ::: diff --git a/docs/private/im/uc_android_private.md b/docs/private/im/uc_android_private.md index c412cf44..e9a36709 100644 --- a/docs/private/im/uc_android_private.md +++ b/docs/private/im/uc_android_private.md @@ -15,7 +15,7 @@ emOptions.enableDNSConfig(false);//默认是 true,在私有云下,需要关 EMClient.getInstance().init(context,emOptions);//最后初始化 SDK ``` -:::notice +:::tip 如果需要配置 HTTPS 只需在 REST 地址前加一个前缀。 ::: diff --git a/docs/private/im/uc_configure.md b/docs/private/im/uc_configure.md index 446d9334..3c65eaeb 100644 --- a/docs/private/im/uc_configure.md +++ b/docs/private/im/uc_configure.md @@ -18,7 +18,7 @@ 2. 在 **创建应用** 对话框中,输入新应用的相关信息,点击 **创建** 按钮创建应用。 -:::notice +:::tip 1. `appname`:你设置的应用名称,用于生成 App Key。该参数的值只能包含小写字母、数字和连字符,不能超过 32 个字符。 2. `Appkey`:即时通讯服务分配给每个应用的唯一标识,由 `orgname` 和 `appname` 参数的值组成,生成后无法修改。 ::: @@ -113,7 +113,7 @@ 1. 在环信即时通讯云的左侧导航栏中,选择 **服务管理** > **白名单服务** 。 2. 在 **白名单服务** 页面,单击 **添加IP**,在弹出的 **添加 IP** 对话框中,输入 IP 地址。 -:::notice +:::tip 1. 每次只能输入 1 个 IP 地址,最多可添加 8 个。 2. 添加 IP 地址成功后 10 分钟后生效。 3. 若白名单列表为空,则所有 IP 地址均可发 REST 消息。 diff --git a/docs/private/im/uc_iOS_private.md b/docs/private/im/uc_iOS_private.md index a271754c..01f346d8 100644 --- a/docs/private/im/uc_iOS_private.md +++ b/docs/private/im/uc_iOS_private.md @@ -16,7 +16,7 @@ options.rtcUrlDomain= "wss://mprtc.easemob.com";//设置多人音视频的服务 [[EMClient sharedClient] initializeSDKWithOptions:options]; ``` -:::notice +:::tip 如果需要配置 HTTPS 只需加一个属性即可。 ::: diff --git a/docs/private/media/conference_android.md b/docs/private/media/conference_android.md index b2f62336..917d7f81 100644 --- a/docs/private/media/conference_android.md +++ b/docs/private/media/conference_android.md @@ -144,7 +144,7 @@ dependencies { ``` -:::notice +:::tip 在创建应用后,获取EASEMOB_APPKEY对应的value ::: @@ -193,7 +193,7 @@ DemoHelper还有一个重要的功能就是设置[EMConferenceListener](http://s 通过这个监听可以在加入会议的时候获取到已经在会议的流或人员, 调用EMConferenceManager#addConferenceListener(EMConferenceListener listener)方法指定回调监听 成员加入或离开会议,数据流更新等。 -:::notice +:::tip 回调监听中的所有方法运行在子线程中,请勿在其中操作UI ::: @@ -353,7 +353,7 @@ pirvate void pubLocalStream() { }); } ``` -:::notice +:::tip 注意:如果是纯音频会议,只需要在发布数据流时将EMStreamParam中的setVideoOff置为true即可, 共享桌面时候发布的是桌面流 EMConferenceStream.StreamType.DESKTOP ::: @@ -409,7 +409,7 @@ pirvate void pubLocalStream() { 成员B可以调用EMConferenceManager#exitConference接口离开会议, 会议中的其他成员会收到回调EMConferenceListener#onMemberExited -:::notice +:::tip 注意:当最后一个成员调用leave接口后,会议会自动销毁 ::: ``` @@ -586,7 +586,7 @@ EMClient.getInstance().chatManager().sendMessage(message); ``` EMClient.getInstance().conferenceManager().destoryConference() ``` -:::notice +:::tip 注意:只有管理员角色可以调用这个接口,可以在会议中显式调用这个接口,强制结束进行中的会议,会议中其他人在EMConferenceListener#onPassiveLeave回调里收到 error为 -411,message为 reason-conference-dismissed,收到这个以后调EMClient.getInstance().conferenceManager().exitConference() 主动退出会议。 ::: ``` @@ -666,7 +666,7 @@ EMClient.getInstance().conferenceManager().destroyConference(new EMValueCallBack {...}; } ``` -:::notice +:::tip 注意:设置会议人数限制只有是加入会议的第一个设置才有效,也就是会议的创建者。 ::: @@ -1057,7 +1057,7 @@ isRecord(是否支持录制)和isMergeRecord(是否支持和流)这两个参数 {...}; } ``` -:::notice +:::tip 注意:设置会议人数限制只有是加入会议的第一个设置才有效,也就是会议的创建者。 ::: @@ -1256,7 +1256,7 @@ EMClient.getInstance().conferenceManager().unsubscribe(emConferenceStream, new E // 关闭视频传输 EMClient.getInstance().conferenceManager().closeVideoTransfer(); ``` -:::notice +:::tip 以上这四个方法都是修改 stream,群里其他成员都会收到 EMConferenceListener.onStreamUpdate()回调。 ::: ``` @@ -1769,7 +1769,7 @@ public void publishDesktop() { 管理员可以调用kickMember api强制将成员从会议中移除。 -:::notice +:::tip 只有管理员角色可以调用踢人接口,可以在会议中踢走主播,被踢的主播在EMConferenceListener#onPassiveLeave回调里 收到 error为 -412,message为 reason-beenkicked 。
收到这个以后调用 EMClient.getInstance().conferenceManager(). exitConference()主动退出会议。 diff --git a/docs/private/media/conference_ios.md b/docs/private/media/conference_ios.md index 24992a5b..d413cfb8 100644 --- a/docs/private/media/conference_ios.md +++ b/docs/private/media/conference_ios.md @@ -433,7 +433,7 @@ SDK没有提供邀请接口,可以自己实现,比如使用环信IM通过发 [[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:nil]; } ``` -:::notice +:::tip 使用环信IM邀请多个人时,建议使用群组消息。如果使用单聊发消息请注意每条消息中间的时间间隔,以防触发环信的垃圾消息防御机制。 被邀请人解析出邀请消息中带的confrId和password,调用SDK的join接口加入会议,成为会议成员且角色是Speaker。 diff --git a/docs/private/media/conference_uniapp.md b/docs/private/media/conference_uniapp.md index 3a139822..f1176761 100644 --- a/docs/private/media/conference_uniapp.md +++ b/docs/private/media/conference_uniapp.md @@ -6,7 +6,7 @@ 环信为用户使用uni-app开发原生应用并拥有音视频功能,特别提供了适配uni-app的音视频SDK和[uni-app原生插件](https://ext.dcloud.net.cn/plugin?id=3507),用户可以集成后快速在android和ios原生客户端使用音视频功能。 -:::notice +:::tip 目前音视频插件仅支持运行在iOS以及Android,暂不支持运行在微信小程序使用。如需运行到微信小程序我们为此提供了小程序音视频SDK,请移步小程序集成多人音视频文档部分。 ::: 使用 uni-app 集成多人音视频SDK,需要下载环信uni-app原生插件**emlive-pusher**、**emlive-player**配合一起使用。音视频SDK依赖IM SDK, 所以要先集成IM,并把IM SDK实例挂载在全局变量下:uni.WebIM = websdk, 可以参考[uni-app demo](https://download-sdk.oss-cn-beijing.aliyuncs.com/mp/downloads/webim-uniapp-demo.zip) utils/WebIM 文件。 diff --git a/docs/private/media/conference_web.md b/docs/private/media/conference_web.md index 855a4efa..ac19362a 100644 --- a/docs/private/media/conference_web.md +++ b/docs/private/media/conference_web.md @@ -91,7 +91,7 @@ emedia.config({ ### 6. 进入会议 -:::notice +:::tip 加入会议之前必须先要调用 emedia.mgr.setIdentity 方法设置 emedia 对象的memName 、token。 ::: ``` @@ -163,7 +163,7 @@ emedia.mgr.streamBindVideo(pushedStream, videoTag); #### 8.1 监听 onStreamAdded 方法 场景:当有远端流加入时。 -:::notice +:::tip 自己发布的本地流也会触发 onStreamAdded 方法, stream.located() == true 为自己发的本地流, 也可以在这里绑定 video标签,显示本地流。 @@ -298,7 +298,7 @@ emedia.mgr.onNetworkDisconnect = streamId => {} //streamId 会议中的流 ID 这里的创建会议 不同于快速集成中的加入会议,这里的是属于另一套逻辑,建议使用快速集成中的加入会议 -:::notice +:::tip 如果只单纯的创建,没进行操作,则创建者不是会议的成员,没有相应的角色,不能进行其他操作。 ::: ##### **1.1 调用 emedia.mgr.createConference 方法创建会议** @@ -372,7 +372,7 @@ const confr = await emedia.mgr.createConference(params); ``` ##### **2.2 调用 emedia.mgr.joinRoom 加入会议时配置** -:::notice +:::tip 只有第一个加入会议的人,配置的才有效,参数需要放到 config 对象中。 ::: ``` @@ -933,7 +933,7 @@ emedia.mgr.grantRole(option.confr, option.memberNames, option.role) #### **4. 角色降级** -:::notice +:::tip 注意:只能角色降级 从管理员到主播、从主播到观众、从管理员到观众,不可逆向操作 ::: 与会成员调用 **emedia.mgr.degradeRole** 方法就会角色降级。 @@ -945,7 +945,7 @@ emedia.mgr.degradeRole(confrId, [memName], toRole); #### **5. 管理员踢人** -:::notice +:::tip 只有管理员可踢人 ::: ``` @@ -956,7 +956,7 @@ emedia.mgr.kickMembersById(confr, memberNames) #### **6. 管理员执行全体静音/取消全体静音** -:::notice +:::tip 只有管理员可操作,其他角色操作不生效,管理员不会被静音 ::: @@ -984,7 +984,7 @@ emedia.mgr.onUnMuteAll = () => { #### **7. 管理员指定成员静音/取消指定成员静音** -:::notice +:::tip 只有管理员可操作,其他角色操作不生效 ::: diff --git a/docs/private/media/one2one_android.md b/docs/private/media/one2one_android.md index 0630099f..dbfa3035 100644 --- a/docs/private/media/one2one_android.md +++ b/docs/private/media/one2one_android.md @@ -143,7 +143,7 @@ dependencies { ... ``` -:::notice +:::tip 在创建应用后,获取EASEMOB_APPKEY对应的value ::: @@ -224,7 +224,7 @@ protected void setGlobalListeners(){ 通过addCallStateChangeListener监听通话状态,在onCreate里面增加这个监听,包括网络连接状态呼入电话状态,听电话状态等都可以在这监听。
-:::notice +:::tip 在收到 DISCONNECTED回调时才能finish当前页面保证通话所占用的资源都释放完,然后开始下一个通话; ::: @@ -370,7 +370,7 @@ EMClient.getInstance().callManager().switchCamera(); #### 最大音频码率 通话之前,可以设置通话音频的最大音频码率,设置方法如下 -:::notice +:::tip 设置最大音频比特率,取值范围 6 ~ 510。 ::: diff --git a/docs/private/media/rest_record.md b/docs/private/media/rest_record.md index a82f62ec..28f480d0 100644 --- a/docs/private/media/rest_record.md +++ b/docs/private/media/rest_record.md @@ -35,7 +35,7 @@ -:::notice +:::tip 1、通话时长<5s可能会出现服务器录制失败的情况。
2、生成录制文件所花的时间和通话时长有关,通话时间越长,生成录制文件的时间也越长。一般情况下,通话时间1小时以下,在通话结束后2小时可以获取到,为了保证录制文件数据完整建议在24小时后获取。 ::: diff --git a/docs/private/media/scenario_live.md b/docs/private/media/scenario_live.md index 6a093641..454c172e 100644 --- a/docs/private/media/scenario_live.md +++ b/docs/private/media/scenario_live.md @@ -26,7 +26,7 @@ - 多人音视频互动直播的参会者除了主播外,还有观众。观众只能观看,如果要发布音频和视频,需要申请上麦,切换到主播角色。 2. 从技术架构角度看,多人音视频会议和多人音视频互动直播使用的是相同的技术架构--**媒体流发布和订阅的技术架构**,主要区别是: -:::notice +:::tip 发布是指参会者发布媒体流(即发言,包括视频流和音频流)到服务器,其他人收到发布事件然后去订阅拉取媒体流。 ::: - 多人音视频会议场景中,在创建会议时指定默认角色为主播,即每个参会者加入会议后都可以发言。 diff --git a/docs/private/media/scenario_meeting.md b/docs/private/media/scenario_meeting.md index 16aab2aa..fb3db517 100644 --- a/docs/private/media/scenario_meeting.md +++ b/docs/private/media/scenario_meeting.md @@ -24,7 +24,7 @@ - 会议室的人员管理:获取会议室参会人名列表、踢人,设置观众为主播,设置主播为观众等 - 多端支持:Android、iOS、Web、PC、小程序 -:::notice +:::tip 在多人视频会议场景下用到的某些功能需要在特定的浏览器、版本或插件下才能使用。
所以为了简化用户体验,不推荐直接使用浏览器,**建议引导用户使用本地应用(如PC端应用)**。 ::: @@ -96,7 +96,7 @@ Demo下载地址请见: [demo下载](common_clientsdk#场景demo及源码下 1. 创建房间:在本页面,输入一个不存在的房间号和密码,可以创建新房间
2. 加入房间:如果输入当前存在的房间号,则加入现有房间 -:::notice +:::tip 请选择“以主播身份进入房间”。
“以观众身份进入房间”属于环信多人音视频互动直播API的一部分,使用说明请见 [互动直播介绍](scenario_live) ::: @@ -112,7 +112,7 @@ Demo下载地址请见: [demo下载](common_clientsdk#场景demo及源码下 - 上麦、下麦 - 退出会议室 -:::notice +:::tip “上麦/下麦”属于环信多人音视频互动直播API的一部分,使用说明请见 [互动直播介绍](scenario_live) ::: diff --git a/docs/private/media/scenario_tc-host.md b/docs/private/media/scenario_tc-host.md index b5fa62e5..1f3434f9 100644 --- a/docs/private/media/scenario_tc-host.md +++ b/docs/private/media/scenario_tc-host.md @@ -3,7 +3,7 @@ ## 前言 本文将简要分享语音聊天室中主持玩法的的应用场景,并讲述基于环信语聊解决方案,主持玩法的实现步骤。 -:::notice +:::tip `仅特定的私有化版本SDK支持语聊解决方案.` 开发者如有需求,请联系商务咨询。 ::: diff --git a/docs/private/media/scenario_tc-robmic.md b/docs/private/media/scenario_tc-robmic.md index 05d0433a..05773243 100644 --- a/docs/private/media/scenario_tc-robmic.md +++ b/docs/private/media/scenario_tc-robmic.md @@ -3,7 +3,7 @@ ## 前言 本文将简要分享语音聊天室中抢麦玩法的的应用场景,并讲述基于环信语聊解决方案,实现抢麦玩法的步骤。 -:::notice +:::tip `仅特定的私有化版本SDK支持语聊解决方案.` 开发者如有需求,请联系商务咨询。 ::: diff --git a/docs/private/media/scenario_tc-scene.md b/docs/private/media/scenario_tc-scene.md index 0c77a56d..0cfb1186 100644 --- a/docs/private/media/scenario_tc-scene.md +++ b/docs/private/media/scenario_tc-scene.md @@ -3,7 +3,7 @@ ## 前言 本文简要分享语音聊天室中临场模式的使用场景,并概述基于环信音视频会议实现临场模式切换的主要步骤。 -:::notice +:::tip `仅特定的私有化版本SDK支持语聊解决方案.` 开发者如有需求,请联系商务咨询。 ::: diff --git a/docs/private/media/scenario_tc.md b/docs/private/media/scenario_tc.md index a2f7f017..f4604c31 100644 --- a/docs/private/media/scenario_tc.md +++ b/docs/private/media/scenario_tc.md @@ -3,7 +3,7 @@ ## 前言 本文将简要分享几个语音聊天室的应用场景,并讲述基于环信 SDK ,实现语音连麦聊天室的步骤。 -:::notice +:::tip `仅特定的私有化版本SDK支持语聊解决方案.` 开发者如有需求,请联系商务咨询。 ::: diff --git a/docs/product/circle/category_mgmt_android.md b/docs/product/circle/category_mgmt_android.md index 149f8d52..91b3ebdf 100644 --- a/docs/product/circle/category_mgmt_android.md +++ b/docs/product/circle/category_mgmt_android.md @@ -24,7 +24,7 @@ 开始前,请确保满足以下条件: -- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.2 版本的初始化,详见 [IM SDK 初始化](/document/android/overview.html#sdk-初始化) +- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.2 版本的初始化,详见 [IM SDK 初始化](/document/android/initialization.html) - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 了解 Circle 的使用限制,详见 [使用限制](circle_overview.html#限制条件)。 diff --git a/docs/product/circle/category_mgmt_ios.md b/docs/product/circle/category_mgmt_ios.md index a9e36728..c1e83166 100644 --- a/docs/product/circle/category_mgmt_ios.md +++ b/docs/product/circle/category_mgmt_ios.md @@ -24,7 +24,7 @@ 开始前,请确保满足以下条件: -- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.1 版本的初始化,详见 [IM SDK 初始化](/document/ios/overview.html#sdk-初始化)。 +- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.1 版本的初始化,详见 [IM SDK 初始化](/document/ios/initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 了解 Circle 的使用限制,详见 [使用限制](circle_overview.html#限制条件)。 diff --git a/docs/product/circle/category_mgmt_web.md b/docs/product/circle/category_mgmt_web.md index 9df312ad..30d36297 100644 --- a/docs/product/circle/category_mgmt_web.md +++ b/docs/product/circle/category_mgmt_web.md @@ -24,7 +24,7 @@ 开始前,请确保满足以下条件: -- 完成环信即时通讯 IM Web Circle SDK 的初始化。Circle SDK 初始化与 IM SDK 相同,详见 [IM SDK 初始化](/document/web/overview.html#sdk-初始化)。 +- 完成环信即时通讯 IM Web Circle SDK 的初始化。Circle SDK 初始化与 IM SDK 相同,详见 [IM SDK 初始化](/document/web/initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 了解 Circle 的使用限制,详见 [使用限制](circle_overview.html#限制条件)。 diff --git a/docs/product/circle/channel_mgmt_android.md b/docs/product/circle/channel_mgmt_android.md index 0048621d..b609e7e3 100644 --- a/docs/product/circle/channel_mgmt_android.md +++ b/docs/product/circle/channel_mgmt_android.md @@ -23,7 +23,7 @@ 开始前,请确保满足以下条件: -- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.2 版本的初始化,详见 [IM SDK 初始化](/document/android/overview.html#sdk-初始化)。 +- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.2 版本的初始化,详见 [IM SDK 初始化](/document/android/initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 了解 Circle 的使用限制,详见 [使用限制](circle_overview.html#限制条件)。 @@ -295,7 +295,7 @@ EMClient.getInstance().chatCircleManager().fetchPrivateChannelsInCategory(server ### 发送消息 -在频道中发送消息与在群组中发送消息的方式类似,唯一的区别在于接收方需要设置为频道 ID。详见 [发送群聊消息](/document/android/message_send_receive.html#发送文本消息)。 +在频道中发送消息与在群组中发送消息的方式类似,唯一的区别在于接收方需要设置为频道 ID。详见 [发送群聊消息](/document/android/message_send_receive.html)。 ### 管理频道成员 diff --git a/docs/product/circle/channel_mgmt_ios.md b/docs/product/circle/channel_mgmt_ios.md index 3ed57bd1..e2c9ffa5 100644 --- a/docs/product/circle/channel_mgmt_ios.md +++ b/docs/product/circle/channel_mgmt_ios.md @@ -23,7 +23,7 @@ 开始前,请确保满足以下条件: -- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.1 版本的初始化,详见 [IM SDK 初始化](/document/ios/overview.html#sdk-初始化)。 +- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.1 版本的初始化,详见 [IM SDK 初始化](/document/ios/initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 了解 Circle 的使用限制,详见 [使用限制](circle_overview.html#限制条件)。 @@ -183,7 +183,7 @@ EMClient.shared().circleManager?.fetchPrivateChannels(inCategory: self.serverId, ### 发送消息 -在频道中发送消息与在群组中发送消息的方式类似,唯一的区别在于接收方需要设置为频道 ID。详见 [发送群聊消息](/document/ios/message_send_receive.html#发送文本消息)。 +在频道中发送消息与在群组中发送消息的方式类似,唯一的区别在于接收方需要设置为频道 ID。详见 [发送群聊消息](/document/ios/message_send_receive.html)。 ### 管理频道成员 diff --git a/docs/product/circle/channel_mgmt_web.md b/docs/product/circle/channel_mgmt_web.md index 2beb08c1..4bcee085 100644 --- a/docs/product/circle/channel_mgmt_web.md +++ b/docs/product/circle/channel_mgmt_web.md @@ -23,7 +23,7 @@ 开始前,请确保满足以下条件: -- 完成环信即时通讯 Web Circle SDK 的初始化。Circle SDK 初始化与 IM SDK 相同,详见 [IM SDK 初始化](/document/web/overview.html#sdk-初始化)。 +- 完成环信即时通讯 Web Circle SDK 的初始化。Circle SDK 初始化与 IM SDK 相同,详见 [IM SDK 初始化](/document/web/initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 了解 Circle 的使用限制,详见 [使用限制](circle_overview.html#限制条件)。 @@ -247,7 +247,7 @@ WebIM.conn.getCategoryPrivateChannels(options).then((res) => { ### 发送消息 -在频道中发送消息与在群组中发送消息的方式类似,唯一的区别在于接收方需要设置为频道 ID。详见 [发送群聊消息](/document/web/message_send_receive.html#发送文本消息)。 +在频道中发送消息与在群组中发送消息的方式类似,唯一的区别在于接收方需要设置为频道 ID。详见 [发送群聊消息](/document/web/message_send_receive.html)。 ### 管理频道成员 diff --git a/docs/product/circle/server_mgmt_android.md b/docs/product/circle/server_mgmt_android.md index 43c82a53..57ebbc3f 100644 --- a/docs/product/circle/server_mgmt_android.md +++ b/docs/product/circle/server_mgmt_android.md @@ -23,7 +23,7 @@ 开始前,请确保满足以下条件: -- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.2 版本的初始化,详见 [IM SDK 初始化](/document/android/overview.html#sdk-初始化) +- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.2 版本的初始化,详见 [IM SDK 初始化](/document/android/initialization.html) - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 了解 Circle 的使用限制,详见 [使用限制](circle_overview.html#限制条件)。 diff --git a/docs/product/circle/server_mgmt_ios.md b/docs/product/circle/server_mgmt_ios.md index 8614df22..d47e24cc 100644 --- a/docs/product/circle/server_mgmt_ios.md +++ b/docs/product/circle/server_mgmt_ios.md @@ -23,7 +23,7 @@ 开始前,请确保满足以下条件: -- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.1 版本的初始化,详见 [IM SDK 初始化](/document/ios/overview.html#sdk-初始化)。 +- 完成 Circle SDK 的初始化,即完成 IM SDK 3.9.9.1 版本的初始化,详见 [IM SDK 初始化](/document/ios/initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 了解 Circle 的使用限制,详见 [使用限制](circle_overview.html#限制条件)。 diff --git a/docs/product/circle/server_mgmt_web.md b/docs/product/circle/server_mgmt_web.md index 009c1503..6d5bc5e4 100644 --- a/docs/product/circle/server_mgmt_web.md +++ b/docs/product/circle/server_mgmt_web.md @@ -23,7 +23,7 @@ 开始前,请确保满足以下条件: -- 完成环信即时通讯 IM Web Circle SDK 的初始化。Circle SDK 初始化与 IM SDK 相同,详见 [IM SDK 初始化](/document/web/overview.html#sdk-初始化)。 +- 完成环信即时通讯 IM Web Circle SDK 的初始化。Circle SDK 初始化与 IM SDK 相同,详见 [IM SDK 初始化](/document/web/initialization.html)。 - 了解环信即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 - 了解 Circle 的使用限制,详见 [使用限制](circle_overview.html#限制条件)。 diff --git a/docs/product/easemob_app_token.md b/docs/product/easemob_app_token.md index ff34a3f8..4cac4d9b 100644 --- a/docs/product/easemob_app_token.md +++ b/docs/product/easemob_app_token.md @@ -10,7 +10,7 @@ 获取 token 时,服务器会返回 token 有效期,即响应中的 `expires_in` 字段的值。由于网络延迟等原因,系统不保证 token 在此值表示的有效期内绝对有效。如果发现 token 使用异常,如返回 HTTP 状态码 401,请重新获取新的 token。 -:::notice +:::tip 请不要频繁向服务器发送获取 token 的请求,同一账号发送此请求超过一定频率会被服务器封禁。 ::: diff --git a/docs/product/enable_and_configure_IM.md b/docs/product/enable_and_configure_IM.md index 3cbb5080..719a5bc7 100644 --- a/docs/product/enable_and_configure_IM.md +++ b/docs/product/enable_and_configure_IM.md @@ -18,7 +18,7 @@ 2. 在 **创建应用** 对话框中,输入新应用的相关信息,点击 **创建** 按钮创建应用。新建应用的服务版本默认为免费版。 -:::notice +:::tip 1. `appname`:你设置的应用名称,用于生成 App Key。该参数的值只能包含小写字母、数字和连字符,不能超过 32 个字符。 2. `Appkey`:即时通讯服务分配给每个应用的唯一标识,由 `orgname` 和 `appname` 参数的值组成,生成后无法修改。 3. `数据中心`:设置后无法修改。 @@ -54,7 +54,7 @@ | 请选择服务版本 | 服务版本的选择如下:
- 当 App Key 为免费版时,可购买专业版或旗舰版,不可单独购买增值服务。
- 当 App Key 为专业版时,可购买旗舰版或单独购买增值服务。| | 可选增值服务 | 请根据实际需求选择增值功能:
- 单个群成员数上限和单个用户可加入群组数上限:不支持叠加购买;以单个群成员数上限举例说明,如果旗舰版购买了 8000 人/群的增值服务,则表示单个群成员上限配置提升至 8000 人/群。
- 消息云存储时长和 REST API 接口调用频率上限:支持可叠加购买,即支持多倍购买;以消息云存储时长举例说明,如购买时选择了 10 份,则表示消息云存储时长延长 300 天(30 天*10)。 | -:::notice +:::tip 服务购买页显示的预估费用仅用于做参考,预估费用仅包含基础费用,具体费用以每月账单为准。 ::: @@ -341,7 +341,7 @@ 1. 在环信即时通讯云的左侧导航栏中,选择 **即时通讯** > **功能配置** > **功能配置总览**。在 **功能配置总览** 页面的 **基础功能** 页签中搜索找到 **REST-IP 白名单**,单击右侧的 **设置**(已开通状态下),打开**安全配置**页面。 2. 在 **安全配置** 页面,单击 **添加IP**,在弹出的 **添加 IP** 对话框中,输入 IP 地址。 -:::notice +:::tip 1. 每次只能输入 1 个 IP 地址,最多可添加 8 个。 2. 添加 IP 地址成功后 10 分钟后生效。 3. 若白名单列表为空,则所有 IP 地址均可发 REST 消息。 diff --git a/docs/product/faq_quality_issues.md b/docs/product/faq_quality_issues.md index d2dbce6c..f898bd53 100644 --- a/docs/product/faq_quality_issues.md +++ b/docs/product/faq_quality_issues.md @@ -13,7 +13,7 @@ 可能是以下原因: - 用户 A 消息发送失败; -- 接受消息的用户 ID 错误; +- 接收消息的用户 ID 错误; - 用户 B 把 A 设为黑名单了; - 用户 B 在其他设备上登录时接收了消息; - 用户 B 离线消息数量超过 500 条等。 @@ -52,7 +52,7 @@ #### 一、App 未上线时,使用环信即时通讯 IM SDK 集成阶段测试用户出现消息丢失 - 用户 B 在线的情况下收不到用户 A 发的群组消息: - 1. 检查用户 A 在构建消息时,传的消息类型是不是群聊类型的,如果不是则用户 B 收不到用户 A 发的群组消息,详见 [Android 版构建消息](/document/android/message_send_receive.html#发送文本消息) 或 [iOS 版构建消息](/document/ios/message_send_receive.html#发送文本消息)。 + 1. 检查用户 A 在构建消息时,传的消息类型是不是群聊类型的,如果不是则用户 B 收不到用户 A 发的群组消息,详见 [Android 版构建消息](/document/android/message_send_receive.html#发送和接收文本消息) 或 [iOS 版构建消息](/document/ios/message_send_receive.html#发送和接收文本消息)。 2. 检查用户 A 发送的消息是否成功,可以根据 SDK 发消息方法返回的结果判断消息是否发送成功,如果发送失败,则用户 B 收不到用户 A 发送的群组消息。 3. 检查用户 A 给用户 B 发消息时,传的群组 ID 是否正确(是否为 A 与 B 共同加入的群组 ID),如果传的不是正确的群组 ID,那么用户 B 收不到消息用户 A 发的群组消息。 4. 检查用户 B 是否在群组中,可以获取群组详情,看群组中是否有用户 B,详见 [获取群组详情](/document/server-side/group_manage.html#获取群组详情)。 diff --git a/docs/product/glossary.md b/docs/product/glossary.md index 8151fa4a..48258b39 100644 --- a/docs/product/glossary.md +++ b/docs/product/glossary.md @@ -114,7 +114,7 @@ CallKit 提供应用内聊天中音视频通话的页面和 UI 组件,帮助 ### 离线 -离线是指用户成功登出环信即时通讯 IM 系统或与即时通讯 IM 系统断开连接后的状态。用户登出即时通讯 IM 系统之后,无法发送和接受消息,在下次登录后可以接收到离线消息。 +离线是指用户成功登出环信即时通讯 IM 系统或与即时通讯 IM 系统断开连接后的状态。用户登出即时通讯 IM 系统之后,无法发送和接收消息,在下次登录后可以接收到离线消息。 ## 封禁用户 diff --git a/docs/product/introduction.md b/docs/product/introduction.md index f12b6163..2f051c41 100644 --- a/docs/product/introduction.md +++ b/docs/product/introduction.md @@ -20,7 +20,7 @@ | :--------- | :----- | | 服务端集成 | [环信即时通讯 REST API 概览](/document/server-side/overview.html)。 | | 客户端 Demo 体验 |
- [Android Demo(EaseIM App)](/document/android/demo.html)
- [iOS Demo(EaseIM App)](/document/ios/demo.html)
- [React Demo(WebIM)](/document/web/demo_react.html)
- [Vue Demo(WebIM)](/document/web/demo_vue.html) | -| 客户端集成|
- [Android 快速开始](/document/android/quickstart.html) 和 [集成概述](/document/android/overview.html)
- [iOS 快速开始](/document/ios/quickstart.html)和 [集成概述](/document/ios/overview.html)
- [Web 快速开始](/document/web/quickstart.html)和[集成概述](/document/web/overview.html)
- [Windows 快速开始](/document/windows/quickstart.html)和 [集成概述](/document/windows/overview.html)
- [Linux 集成概述](/document/linux/overview.html)
- [Unity 快速开始](/document/unity/quickstart.html)和[集成概述](/document/unity/overview.html)
- [Flutter 快速开始](/document/flutter/quickstart.html)和[集成概述](/document/flutter/overview.html);
- [React Native 快速开始](/document/react-native/quickstart.html)和[集成概述](/document/react-native/overview.html)
- [Electron 集成概述](/document/electron/overview.html)
- [uni-app 集成概述](/document/applet/uniapp.html)| +| 客户端集成|
- [Android 快速开始](/document/android/quickstart.html) 和 [集成概述](/document/android/integration.html)
- [iOS 快速开始](/document/ios/quickstart.html)和 [集成概述](/document/ios/integration.html)
- [Web 快速开始](/document/web/quickstart.html)和[集成概述](/document/web/integration.html)
- [Windows 快速开始](/document/windows/quickstart.html)和 [集成概述](/document/windows/integration.html)
- [Linux 集成概述](/document/linux/overview.html)
- [Unity 快速开始](/document/unity/quickstart.html)和[集成概述](/document/unity/integration.html)
- [Flutter 快速开始](/document/flutter/quickstart.html)和[集成概述](/document/flutter/integration.html);
- [React Native 快速开始](/document/react-native/quickstart.html)和[集成概述](/document/react-native/integration.html)
- [Electron 集成概述](/document/electron/overview.html)
- [uni-app 集成概述](/document/applet/uniapp.html)| ## 功能概述 diff --git a/docs/product/limitation.md b/docs/product/limitation.md index cee2e63f..efd895fb 100644 --- a/docs/product/limitation.md +++ b/docs/product/limitation.md @@ -1,37 +1,10 @@ -# 限制条件 +# 产品相关限制 本文简要介绍环信即时通讯 IM 的使用限制条件,包括调用频率、字符串大小和编码格式等。 -## 用户注册 - -- 用户 ID:长度不能超过 64 字节,支持以下字符集: - - 26 个小写英文字母 a-z; - - 26 个大写英文字母 A-Z; - - 10 个数字 0-9; - - “_”, “-”, “.”。 - -:::tip -- 该参数不区分大小写,因此 Aa 和 aa 为相同用户名。 -- 请确保同一个 app 下,用户 ID 唯一。 -- 用户 ID 是会公开的信息,请勿使用 UUID、邮箱地址、手机号等敏感信息。 -::: - -- 密码:用户的登录密码,长度不可超过 64 个字符。 - -## 用户属性 - -- 默认单个用户的属性总长不得超过 2 KB。 -- 默认单个 app 下所有用户的属性总长度不得超过 10 GB。 - -## 用户关系 - -- 单个 App Key 下的每个用户的好友数量上限与套餐包版本相关,详见[套餐包功能详情](/product/pricing.html#套餐包功能详情)。 -- 好友备注的长度不能超过 100 个字符。 -- 每个用户的黑名单最多可存 500 个用户。 - -## 消息 +## 消息相关 ### 消息大小 @@ -56,6 +29,10 @@ ### 消息存储 - **历史消息**:在服务器上的存储时间与你订阅的套餐包有关,详见[产品价格](/product/pricing.html#套餐包功能详情)。 +- **聊天记录文件**:可从服务端获取用户的聊天记录文件。 + - 单次请求获取从指定起始时间开始一小时内的发送的聊天记录文件。 + - 你最多可以获取最近 3 天的聊天记录。若要提升该限制,你需要联系环信商务。 + - 查询历史消息记录时存在一定延时,无法实时获取。 - **消息附件/文件**:默认情况下,消息附件,例如图片、音频、视频和其他文件可存储 7 天。若要提升其中一个上限,请联系商务。消息附件的大小及存储时间限制与群组共享文件的相同。如果消息附件的其中一个限制进行了上调,群组共享文件的对应限制也会随之自动调整,反之亦然。 - **离线消息**:对于单聊和群聊,离线消息默认保存 7 天。对于每个终端用户,所有的单聊会话可存储 500 条离线消息,所有的群聊会话可存储 200 条离线消息。若超过存储天数和条数的上限,最新的离线消息会挤掉最早的。如需提升上限,可联系商务。 - **各类事件通知**:事件通知的存储时间与消息的存储一致。 @@ -115,11 +92,22 @@ 该功能只支持单聊和群组聊天,不支持聊天室。 +### 消息表情回复 + +- 一个消息表情即为一个 Reaction,若不同用户重复添加同一消息表情,Reaction 数量计为 1。 +- 对于同一条 Reaction,一个用户只能添加一次,重复添加会报错误 1301。 +- 每条消息默认可添加 20 个 Reaction,若需提升该上限,需联系环信商务。 +- 创建 Reaction 时,设置的表情 ID 长度不能超过 128 个字符,对支持的字符集类型没有限制,但服务端与客户端的设置必须保持一致。若使用特殊字符,获取和删除 Reaction 时需对特殊字符进行 URL 编码。 + ### 获取消息流量统计 - SDK 只支持统计该功能开启后最近 30 天内发送和接收的消息。 - 仅 Android and iOS 端 SDK 支持该功能。 +### 消息举报 + +举报消息的原因不能超过 512 字节。 + ## 会话 ### 会话列表 @@ -163,6 +151,11 @@ - 群组共享文件在服务器的存储时间与消息附件相同,即默认为 7 天。如果需要提升限制,请联系商务。 如果消息附件在服务器上的存储时间进行了上调,群组共享文件的存储时间也会随之自动调整,反之亦然。 +### 子区 + +- 单个 app 下的子区总数默认为 10 万,如需调整请联系商务。 +- 子区名称,不能超过 64 个字符。 + ## 聊天室 不同套餐版本支持的聊天室总数,详见[套餐包功能详情](pricing.html#套餐包功能详情)。 @@ -192,25 +185,42 @@ 每个聊天室属性 value 不能超过 4096 个字符。 -## 子区 +## 用户相关 -- 单个 app 下的子区总数默认为 10 万,如需调整请联系商务。 -- 子区名称,不能超过 64 个字符。 +### 用户注册 + +- 用户 ID:长度不能超过 64 字节,支持以下字符集: + - 26 个小写英文字母 a-z; + - 26 个大写英文字母 A-Z; + - 10 个数字 0-9; + - “_”, “-”, “.”。 -## 在线状态订阅 +:::tip +- 该参数不区分大小写,因此 Aa 和 aa 为相同用户名。 +- 请确保同一个 app 下,用户 ID 唯一。 +- 用户 ID 是会公开的信息,请勿使用 UUID、邮箱地址、手机号等敏感信息。 +::: + +- 密码:用户的登录密码,长度不可超过 64 个字符。 + +### 用户属性 + +- 默认单个用户的属性总长不得超过 2 KB。 +- 默认单个 app 下所有用户的属性总长度不得超过 10 GB。 + +### 用户关系 + +- 单个 App Key 下的每个用户的好友数量上限与套餐包版本相关,详见[套餐包功能详情](/product/pricing.html#套餐包功能详情)。 +- 好友备注的长度不能超过 100 个字符。 +- 每个用户的黑名单最多可存 500 个用户。 + +### 在线状态订阅 - 订阅时长最长为 30 天,过期需重新订阅。如果未过期的情况下重复订阅,新设置的有效期会覆盖之前的有效期。 - 每次调用接口最多只能订阅 100 个账号,若数量较大需多次调用。 - 每个用户 ID 订阅的用户数不超过 3000。如果超过 3000,后续订阅也会成功,但默认会将订阅剩余时长较短的替代。 - 每个用户最多可被 3000 个用户订阅。 -## 消息表情回复 - -- 一个消息表情即为一个 Reaction,若不同用户重复添加同一消息表情,Reaction 数量计为 1。 -- 对于同一条 Reaction,一个用户只能添加一次,重复添加会报错误 1301。 -- 每条消息默认可添加 20 个 Reaction,若需提升该上限,需联系环信商务。 -- 创建 Reaction 时,设置的表情 ID 长度不能超过 128 个字符,对支持的字符集类型没有限制,但服务端与客户端的设置必须保持一致。若使用特殊字符,获取和删除 Reaction 时需对特殊字符进行 URL 编码。 - ## 离线推送 ### 推送昵称 @@ -222,15 +232,6 @@ - 中文; - 特殊字符。 -### 免打扰时间 - -免打扰时间参数的说明如下表所示: - -| 免打扰时间参数 | 类型 |描述 | 应用范围 | -| :------------------- | :--------------------------- | :--------------------------- | :------------------- | -| 免打扰时间段 | Int |每天定时触发离线推送免打扰的时间段,采用 24 小时制,精确到分钟,格式为 H:M-H:M,例如 8:30-10:0,开始时间和结束时间中的小时数和分钟数的取值范围分别为 [0,23] 和 [0,59]。免打扰时间段的设置说明如下:
- 开始时间和结束时间设置后,免打扰模式每天定时触发。例如,若该时间段设置为 `8:0`-`10:0`,免打扰模式在每天的 8:00-10:00 内生效。若你在 11:00 设置开始时间为 `8:0`,结束时间为 `12:0`,则免打扰模式在当天的 11:00-12:00 生效,以后每天均在 8:00-12:00 生效。
- 若开始时间和结束时间相同,免打扰模式则全天生效。不过,若设置为 `0:0`-`0:0`,则关闭免打扰模式。
- 若结束时间早于开始时间,则免打扰模式在每天的开始时间到次日的结束时间内生效。例如,开始时间为 `10:0`,结束时间为 `8:0`,则免打扰模式的在当天的 10:00 到次日的 8:00 生效。
- 目前仅支持在每天的一个指定时间段内开启免打扰模式,不支持多个免打扰时间段,新的设置会覆盖之前的设置。
- 若不设置该参数,传空字符串。
- 若该参数和 `SILENT_MODE_DURATION` 均设置,免打扰模式当日在这两个时间段均生效,例如,上午 8:00 将 `SILENT_MODE_INTERVAL` 设置为 8:0-10:0,`SILENT_MODE_DURATION` 设置为 240 分钟(4 个小时),则 app 在当天 8:00-12:00 和以后每天 8:00-10:00 处于免打扰模式。 | 仅针对 app 生效,对单聊或群聊不生效。 | -| 免打扰时长 | Int |免打扰时长,单位为分钟。免打扰时长的取值范围为 [0,10080],`0` 表示该参数无效,`10080` 表示免打扰模式持续 7 天。
与免打扰时间段的设置每天生效不同,该参数为一次有效。设置后立即生效,例如,上午 8:00 将 app 层级的 `SILENT_MODE_DURATION` 设置为 240 分钟(4 个小时),则 app 在当天 8:00-12:00 处于免打扰模式。
- 若该参数和 `SILENT_MODE_INTERVAL` 均设置,免打扰模式当日在这两个时间段均生效,例如,上午 8:00 将 app 级的 `SILENT_MODE_INTERVAL` 设置为 8:00-10:00,免打扰时长设置为 240 分钟(4 个小时),则 app 在当前 8:00-12:00 和以后每天 8:00-10:00 处于免打扰模式。 | App 或单聊/群聊会话。| - ### 推送模板名称 - 添加默认的推送模板时,模板名称应设置为 `default`。 @@ -245,10 +246,6 @@ - 自定义设置登录设备的平台时,设备平台的取值范围为 [1,100],设备数量的取值范围为 [0,4]。 -## 消息举报 - -举报消息的原因不能超过 512 字节。 - ## 调用频率限制 关于 RESTf API 的调用频率限制,详见 [Rest API 调用频率限制](limitationapi.html)。 diff --git a/docs/product/moderation/keyword_review.md b/docs/product/moderation/keyword_review.md index bef21e24..bf045010 100644 --- a/docs/product/moderation/keyword_review.md +++ b/docs/product/moderation/keyword_review.md @@ -27,7 +27,7 @@ ### 第二步 开通关键词审核 -:::notice +:::tip 关键词审核功能为增值服务,需单独购买。服务开通后立即扣费,自动续费。 ::: @@ -45,7 +45,7 @@ 1. 选择**即时通讯** > **内容审核** > **关键词审核**配置关键词名单。 -:::notice +:::tip 1. 关键词名单包含白名单和黑名单,即**审核建议**参数为**忽略**时为白名单,设置为**疑似**和**拒绝**时为黑名单。 2. 每个应用最多可配置 10 个名单, 每个名单最多可添加 10,000 个关键词,即每个应用最多可配置 100,000 个词条。添加的关键词在 15 分钟后生效。 ::: @@ -89,7 +89,7 @@ 单击**文件导入**,然后选择文件上传。 -:::notice +:::tip 支持 .txt 格式的纯文本文件,每个文件不能超过 2 MB。
文件上传后应确保单个名单的词条总数不超过 10,000,否则超出部分将添加失败。 ::: diff --git a/docs/product/pricing.md b/docs/product/pricing.md index 6e3aac2f..2114f0d9 100644 --- a/docs/product/pricing.md +++ b/docs/product/pricing.md @@ -23,7 +23,7 @@ - **套餐包费用**:IM 套餐包分为免费版、专业版和旗舰版。创建应用后默认为免费版,你可以根据业务需求进行选择。各版本包含的功能与限制,详见[套餐包功能详情](#套餐包功能详情)。 - **套餐包外超量费用**:超出套餐包免费额度以外的 DAU 和群组和聊天室总数上限所需支付的费用。 -:::notice +:::tip 每个套餐包仅对单个 App Key 生效。 ::: @@ -98,7 +98,7 @@ | :------------ | :----- | :------- | :------------ | :------------ | | 消息云存储 | 包含历史消息记录文件和漫游消息 | 预付费 | 每延长 30 天,500 元/月 | 每延长 30 天,500元/月 | | 扩展单个群成员数上限 | 对单个 App Key 内的所有群组生效 | 预付费 | 3000 人/群,500 元/月 | 8000 人/群,500 元/月 | -| 扩展单个用户可加入群组数上限 | 对单个 App Key 内的所有用户生效 | 预付费 | 2000 群/人:1000 元/月 | — | +| 扩展单个用户可加入群组数上限 | 对单个 App Key 内的所有用户生效 | 预付费 | 2000 群/人:1000 元/月 | 5000 群/人:1000 元/月 | | 群聊消息已读回执 | 功能介绍详见[群聊消息已读回执](/document/android/message_receipt.html#群聊)。 | 预付费 | 1000 元/月 | — | | 全局禁言 | 功能介绍详见[全局禁言](/document/server-side/user_global_mute.html)。 | 预付费 | 500 元/月 | — | | 回调 | 功能介绍详见[回调](/document/server-side/callback_overview.html)。 | 预付费 | 1000 元/月 | — | @@ -110,7 +110,7 @@ | 消息人工审核 | 功能介绍详见[消息人工审核](https://docs-im.easemob.com/ccim/moderation/censor)。 | 预付费 |1000 元/月 | 1000 元/月 | | 质量监控 | 功能介绍详见[请求质量](request_quality_overview.html)。 | 预付费 | 2000 元/月 | 2000 元/月 | -:::notice +:::tip 1. 翻译功能的价格请咨询环信商务。 2. 内容审核功能的价格详见[内容审核计费说明](https://docs-im.easemob.com/ccim/moderation/price_domestic)。 ::: @@ -164,7 +164,7 @@ 2. 点击对应功能的 **操作** 一栏的 **升级**,进入**即时通讯IM 服务版本** 页面。 3. 查看各套餐包的功能详情,根据业务需要选择套餐包,选择 **我已阅读并同意《环信云服务购买协议》**,点击 **提交订单**,完成支付。 -:::notice +:::tip 1. 原版本下订阅的所有增值服务会同时取消订阅并退还剩余费用。若你仍需要增值服务,请重新订阅。 2. 如果需降级或取消套餐包服务,请联系环信商务。 3. 套餐包降级时,目标版本不包含的功能会关闭。某些功能,例如回调,在关闭后会导致数据清除,无法恢复。 @@ -202,7 +202,7 @@ 群组免费峰值群组和聊天室数100个/月100000个/月100000个/月 群组总数上限(解散后不计数)100无限制无限制 单个群成员数上限100人/群300人/群(可调*)3000人/群(可调*) -单个用户可加入群组数上限100个群/人600个群/人(可调*)100000个群/人 +单个用户可加入群组数上限100个群/人600个群/人(可调*)3000个群/人(可调*) 群组管理✓✓✓ 聊天室聊天室总数上限(支持大型直播场景)—无限制无限制 聊天室消息优先级—✓✓ diff --git a/docs/product/product_dynamics.md b/docs/product/product_dynamics.md index a858d543..78e55eab 100644 --- a/docs/product/product_dynamics.md +++ b/docs/product/product_dynamics.md @@ -15,14 +15,14 @@ | 动态名称 | 动态描述 | 发布时间 | 相关文档 | | :----- | :------- | :---------------- | :---------------- | -| HarmonyOS SDK 1.4.0 开发版发布 | **新增特性**:
- 新增[置顶消息功能](/document/harmonyos/message_pin.html#消息置顶)。
- 新增[根据单个或多个消息类型,搜索本地数据库中所有会话或单个会话中的消息](/document/harmonyos/message_search.html#根据消息类型搜索会话消息)。
- 支持[获取 SDK 本地数据库中会话某个时间段内的全部消息数](/document/harmonyos/message_retrieve.html#获取会话在一定时间内的消息数)。
- 支持[会话推送通知方式的本地存储](/document/harmonyos/push/push_notification_mode_dnd.html#从服务器获取所有会话的推送通知方式设置),并支持从服务器获取所有会话的推送通知方式的设置。
- 支持[设备登录时携带自定义扩展信息并传递给被踢的设备](/document/harmonyos/multi_device.html#设置登录设备的扩展信息),应用于被踢设备展示提示信息或进行业务判断。
- 支持用户上线后从服务端拉取离线消息时[收到拉取开始和结束的通知](/document/harmonyos/overview.html#连接状态相关)。
- 支持[查看当前用户是否在群组禁言列表中](/document/harmonyos/group_members.html#检查自己是否在禁言列表中)。
- 支持[错误码 213 ChatError#USER_BIND_ANOTHER_DEVICE](/document/harmonyos/error.html),用于当用户达到登录设备上线时,当前设备无法登录的场景。
- 支持在撤回消息的事件中[返回被撤回的消息所属的会话 ID](/document/harmonyos/message_recall.html#设置消息撤回监听)。
- 支持[加入聊天室时携带扩展信息,并指定是否退出之前加入的全部聊天室](/document/harmonyos/room_manage.html#加入聊天室)。当用户加入聊天室携带了扩展信息时,聊天室内其他人可以在用户加入聊天室的回调中,获取到扩展信息。
- 支持[从服务端单向删除聊天室漫游消息](/document/harmonyos/message_delete.html#单向删除服务端的历史消息)。| 2024-09-30 | [HarmonyOS 1.4.0 更新日志](/document/harmonyos/releasenote.html#版本-v1-4-0-dev-2024-09-30-开发版) | +| HarmonyOS SDK 1.4.0 开发版发布 | **新增特性**:
- 新增[置顶消息功能](/document/harmonyos/message_pin.html#消息置顶)。
- 新增[根据单个或多个消息类型,搜索本地数据库中所有会话或单个会话中的消息](/document/harmonyos/message_search.html#根据消息类型搜索会话消息)。
- 支持[获取 SDK 本地数据库中会话某个时间段内的全部消息数](/document/harmonyos/message_retrieve.html#获取会话在一定时间内的消息数)。
- 支持[会话推送通知方式的本地存储](/document/harmonyos/push/push_notification_mode_dnd.html#从服务器获取所有会话的推送通知方式设置),并支持从服务器获取所有会话的推送通知方式的设置。
- 支持[设备登录时携带自定义扩展信息并传递给被踢的设备](/document/harmonyos/multi_device.html#设置登录设备的扩展信息),应用于被踢设备展示提示信息或进行业务判断。
- 支持用户上线后从服务端拉取离线消息时[收到拉取开始和结束的通知](/document/harmonyos/connection.html)。
- 支持[查看当前用户是否在群组禁言列表中](/document/harmonyos/group_members.html#检查自己是否在禁言列表中)。
- 支持[错误码 213 ChatError#USER_BIND_ANOTHER_DEVICE](/document/harmonyos/error.html),用于当用户达到登录设备上线时,当前设备无法登录的场景。
- 支持在撤回消息的事件中[返回被撤回的消息所属的会话 ID](/document/harmonyos/message_recall.html#设置消息撤回监听)。
- 支持[加入聊天室时携带扩展信息,并指定是否退出之前加入的全部聊天室](/document/harmonyos/room_manage.html#加入聊天室)。当用户加入聊天室携带了扩展信息时,聊天室内其他人可以在用户加入聊天室的回调中,获取到扩展信息。
- 支持[从服务端单向删除聊天室漫游消息](/document/harmonyos/message_delete.html#单向删除服务端的历史消息)。| 2024-09-30 | [HarmonyOS 1.4.0 更新日志](/document/harmonyos/releasenote.html#版本-v1-4-0-dev-2024-09-30-开发版) | | HarmonyOS SDK 1.3.0 开发版发布 | **新增特性**:
- HarmonyOS 端新增[群成员自定义属性](/document/harmonyos/group_members.html#管理群成员的自定义属性)功能。
- HarmonyOS 端新增[设置推送通知的显示内容](/document/harmonyos/push/push_display.html) 、[推送通知方式和免打扰模式功能](/document/harmonyos/push/push_notification_mode_dnd.html)。
- HarmonyOS 端新增[用户在线状态订阅](/document/harmonyos/presence.html)功能。
- 新增[聊天室自定义属性](/document/harmonyos/room_attributes.html#管理聊天室自定义属性-key-value)功能。 | 2024-09-09 | [HarmonyOS 1.3.0 更新日志](/document/harmonyos/releasenote.html#版本-v1-3-0-dev-2024-09-10-开发版) | ## 2024-08 | 动态名称 | 动态描述 | 发布时间 | 相关文档 | | :----- | :------- | :---------------- | :---------------- | -| SDK 4.9.0 开发版发布 | **新增特性**:
- 用户上线后从服务端拉取离线消息时,会[收到拉取开始和结束的通知](/document/android/overview.html#连接状态相关)。
- 单聊会话支持[消息置顶](/document/android/message_pin.html)。
- 移动端支持查看当前用户是否在指定的群组的禁言列表中。
- 移动端撤回消息后,你[收到的通知中会体现消息所属的会话 ID](/document/android/message_recall.html#设置消息撤回监听)。
- Web 端可[查看 app 下设置了推送通知方式(接收所有人通知、只接收 @ 我的通知和不接收任何通知)的所有会话](/document/web/push/push_notification_mode_dnd.html#获取设置了推送通知方式的所有会话)。
- 对于 Web 端,你若设置了指定会话的推送通知方式或免打扰时长或时间段,[其他设备会收到事件通知](/document/web/push/push_notification_mode_dnd.html#设置单个会话的推送通知)。
- 对于 Web 端,你若清除了会话的推送通知方式,[其他设备会收到事件通知](/document/web/push/push_notification_mode_dnd.html#清除单个会话的推送通知方式的设置)。| 2024-08-30 |
- [Android 4.9.0 更新日志](/document/android/releasenote.html#版本-v4-9-0-dev-2024-08-30-开发版)
- [iOS 4.9.0 更新日志](/document/ios/releasenote.html#版本-v4-9-0-dev-2024-08-30-开发版)
- [Web 4.9.0 更新日志](/document/web/releasenote.html#版本-v4-9-0-dev-2024-08-30-开发版) | +| SDK 4.9.0 开发版发布 | **新增特性**:
- 用户上线后从服务端拉取离线消息时,会[收到拉取开始和结束的通知](/document/android/connection.html)。
- 单聊会话支持[消息置顶](/document/android/message_pin.html)。
- 移动端支持查看当前用户是否在指定的群组的禁言列表中。
- 移动端撤回消息后,你[收到的通知中会体现消息所属的会话 ID](/document/android/message_recall.html#设置消息撤回监听)。
- Web 端可[查看 app 下设置了推送通知方式(接收所有人通知、只接收 @ 我的通知和不接收任何通知)的所有会话](/document/web/push/push_notification_mode_dnd.html#获取设置了推送通知方式的所有会话)。
- 对于 Web 端,你若设置了指定会话的推送通知方式或免打扰时长或时间段,[其他设备会收到事件通知](/document/web/push/push_notification_mode_dnd.html#设置单个会话的推送通知)。
- 对于 Web 端,你若清除了会话的推送通知方式,[其他设备会收到事件通知](/document/web/push/push_notification_mode_dnd.html#清除单个会话的推送通知方式的设置)。| 2024-08-30 |
- [Android 4.9.0 更新日志](/document/android/releasenote.html#版本-v4-9-0-dev-2024-08-30-开发版)
- [iOS 4.9.0 更新日志](/document/ios/releasenote.html#版本-v4-9-0-dev-2024-08-30-开发版)
- [Web 4.9.0 更新日志](/document/web/releasenote.html#版本-v4-9-0-dev-2024-08-30-开发版) | ## 2024-07 @@ -33,7 +33,7 @@ | 动态名称 | 动态描述 | 发布时间 | 相关文档 | | :----- | :------- | :---------------- | :---------------- | | HarmonyOS SDK 1.2.0 开发版发布 |**新增特性**:
- HarmonyOS 端新增[表情回复 Reaction](/document/harmonyos/reaction.html)功能。
- HarmonyOS 端新增[会话标记](/document/harmonyos/conversation_mark.html)功能。
- HarmonyOS 端新增[会话置顶](/document/harmonyos/conversation_pin.html)功能。
- HarmonyOS 端新增[用户属性](/document/harmonyos/userprofile.html)功能。 | 2024-07-11 | [HarmonyOS 1.2.0 更新日志](/document/harmonyos/releasenote.html#版本-v1-2-0-dev-2024-07-11-开发版) | -| HarmonyOS SDK 1.1.0 开发版发布 |**新增特性**:
- HarmonyOS 端新增[修改消息](/document/harmonyos/message_modify.html)功能。
- HarmonyOS 端新增[自定义消息](/document/harmonyos/message_send_receive.html#发送自定义类型消息)功能。
- HarmonyOS 端新增[合并转发消息](/document/harmonyos/message_send_receive.html#发送和接收合并消息)功能。
- HarmonyOS 端新增[离线推送](/document/harmonyos/overview.html)功能。 | 2024-07-01 | [HarmonyOS 1.1.0 更新日志](/document/harmonyos/releasenote.html#版本-v1-1-0-dev-2024-07-01-开发版) | +| HarmonyOS SDK 1.1.0 开发版发布 |**新增特性**:
- HarmonyOS 端新增[修改消息](/document/harmonyos/message_modify.html)功能。
- HarmonyOS 端新增[自定义消息](/document/harmonyos/message_send_receive.html#发送自定义类型消息)功能。
- HarmonyOS 端新增[合并转发消息](/document/harmonyos/message_send_receive.html#发送和接收合并消息)功能。
- HarmonyOS 端新增[离线推送](/document/harmonyos/push/push_overview.html)功能。 | 2024-07-01 | [HarmonyOS 1.1.0 更新日志](/document/harmonyos/releasenote.html#版本-v1-1-0-dev-2024-07-01-开发版) | ## 2024-06 @@ -45,7 +45,7 @@ | 动态名称 | 动态描述 | 发布时间 | 相关文档 | | :----- | :------- | :---------------- | :---------------- | -| SDK 4.6.0 开发版发布 | **新增特性**:
- 客户端[消息撤回时支持携带自定义信息](/document/android/message_recall.html#实现方法)。
- 客户端支持离线期间撤回的消息通知给接收方。
- 移动端支持[自定义筛选获取本地会话列表](/document/android/conversation_list.html#获取本地所有或筛选的会话)。
- 移动端支持[清除内存中的会话](/document/android/conversation_list.html#清除内存中的会话),并举例说明如何[降低会话占用内存](/document/android/conversation_list.html#降低会话占用内存的实例)。
- Android 端添加绑定推送 token 成功与否的回调。
- Web/小程序端增加接口支持[获取当前用户加入和创建的聊天室](/document/web/room_manage.html#获取当前用户加入的聊天室列表)。
- Web/小程序 端支持 [logger 日志不显示在控制台](/document/web/overview.html#输出信息到日志文件)。
**重大变更**
1. **Android**:
从 V4.6.0 版本开始会启用 Kotlin 语言编写的新的 EaseIM App 项目与 EaseIMKIt 项目,老版本的项目将逐渐不再维护,请参考:
- [EaseIMKIt 文档](https://doc.easemob.com/uikit/chatuikit/android/chatuikit_overview.html)
- [EaseIM App 项目](https://github.com/easemob/chat-android-kotlin)
2. **iOS**:
从 V4.6.0 版本开始会启用 Swift 语言编写的新的 `EaseChatUIKit` 与 `EaseChatDemo`,老版本 Demo 和 UIKit 逐渐不再维护,请参考:
- [UIKit 文档](https://doc.easemob.com/uikit/chatuikit/ios/chatuikit_overview.html)
- [Demo 源码](https://github.com/easemob/chat-ios/tree/SwiftDemo) | 2024-04-30 |
- [Android 4.6.0 更新日志](/document/android/releasenote.html#版本-v4-6-0-dev-2024-04-30-开发版)
- [iOS 4.6.0 更新日志](/document/ios/releasenote.html#版本-v4-6-0-dev-2024-04-30-开发版)
- [Web 4.7.0 更新日志](/document/web/releasenote.html#版本-v4-7-0-dev-2024-04-30-开发版)
- [小程序 4.7.0 更新日志](/document/applet/releasenote.html#版本-v4-7-0-dev-2024-04-30-开发版)。 | +| SDK 4.6.0 开发版发布 | **新增特性**:
- 客户端[消息撤回时支持携带自定义信息](/document/android/message_recall.html#实现方法)。
- 客户端支持离线期间撤回的消息通知给接收方。
- 移动端支持[自定义筛选获取本地会话列表](/document/android/conversation_list.html#获取本地所有或筛选的会话)。
- 移动端支持[清除内存中的会话](/document/android/conversation_list.html#清除内存中的会话),并举例说明如何[降低会话占用内存](/document/android/conversation_list.html#降低会话占用内存的实例)。
- Android 端添加绑定推送 token 成功与否的回调。
- Web/小程序端增加接口支持[获取当前用户加入和创建的聊天室](/document/web/room_manage.html#获取当前用户加入的聊天室列表)。
- Web/小程序 端支持 [logger 日志不显示在控制台](/document/web/log.html#输出信息到日志文件)。
**重大变更**
1. **Android**:
从 V4.6.0 版本开始会启用 Kotlin 语言编写的新的 EaseIM App 项目与 EaseIMKIt 项目,老版本的项目将逐渐不再维护,请参考:
- [EaseIMKIt 文档](https://doc.easemob.com/uikit/chatuikit/android/chatuikit_overview.html)
- [EaseIM App 项目](https://github.com/easemob/chat-android-kotlin)
2. **iOS**:
从 V4.6.0 版本开始会启用 Swift 语言编写的新的 `EaseChatUIKit` 与 `EaseChatDemo`,老版本 Demo 和 UIKit 逐渐不再维护,请参考:
- [UIKit 文档](https://doc.easemob.com/uikit/chatuikit/ios/chatuikit_overview.html)
- [Demo 源码](https://github.com/easemob/chat-ios/tree/SwiftDemo) | 2024-04-30 |
- [Android 4.6.0 更新日志](/document/android/releasenote.html#版本-v4-6-0-dev-2024-04-30-开发版)
- [iOS 4.6.0 更新日志](/document/ios/releasenote.html#版本-v4-6-0-dev-2024-04-30-开发版)
- [Web 4.7.0 更新日志](/document/web/releasenote.html#版本-v4-7-0-dev-2024-04-30-开发版)
- [小程序 4.7.0 更新日志](/document/applet/releasenote.html#版本-v4-7-0-dev-2024-04-30-开发版)。 | | 动态名称 | 动态描述 | 发布时间 | 相关文档 | | :----- | :------- | :---------------- | :---------------- | @@ -57,7 +57,7 @@ | 动态名称 | 动态描述 | 发布时间 | 相关文档 | | :----- | :------- | :---------------- | :---------------- | -| SDK 4.4.0 开发版发布 | **新增特性**:
- 客户端可[清空聊天记录](/document/android/message_delete.html#清空聊天记录):单个用户包含本地或服务端记录。
- 客户端发送消息时如果被内容审核进行了内容替换,你可以通过[设置开关](/document/android/message_send_receive.html#发送文本消息)决定发送方是否可以获取替换后的内容。
- 移动端的[本地消息搜索可选择搜索范围](/document/android/message_search.html#根据搜索范围搜索当前会话中的消息),如只搜索消息内容、只搜索消息扩展信息以及同时搜索消息内容以及扩展信息。
- 移动端接收消息的回调支持通过[设置开关](/document/android/message_send_receive.html#接收消息)设置是否返回发送成功的消息。
- [Web 端支持向指定设备发消息](/document/web/multi_device.html#获取当前用户的其他登录设备的登录-id-列表),例如,电脑端给手机端发消息,登录同一账号的多个设备均会收到消息。
- Web 端聊天室和群组成员进出事件增加成员人数 `memberCount` 字段。
**优化**:
- 移动端群组全员禁言状态存储到本地数据库,下次登录时可以直接从本地获取到。
- 移动端转发合并消息时导致的附件重复上传问题。| 2024-01-30 |
- [Android 4.4.0 更新日志](/document/android/releasenote.html#版本-v4-4-0-dev-2024-01-30-开发版)
- [iOS 4.4.0 更新日志](/document/ios/releasenote.html#版本-v4-4-0-dev-2024-01-30-开发版)
- [Web 4.5.0 更新日志](/document/web/releasenote.html#版本-v4-5-0-dev-2024-01-30-开发版)
- [小程序 4.5.0 更新日志](/document/applet/releasenote.html#版本-v4-5-0-dev-2024-01-30-开发版)。 | +| SDK 4.4.0 开发版发布 | **新增特性**:
- 客户端可[清空聊天记录](/document/android/message_delete.html#清空聊天记录):单个用户包含本地或服务端记录。
- 客户端发送消息时如果被内容审核进行了内容替换,你可以通过[设置开关](/document/android/message_send_receive.html#发送消息前的内容审核)决定发送方是否可以获取替换后的内容。
- 移动端的[本地消息搜索可选择搜索范围](/document/android/message_search.html#根据搜索范围搜索当前会话中的消息),如只搜索消息内容、只搜索消息扩展信息以及同时搜索消息内容以及扩展信息。
- 移动端接收消息的回调支持通过[设置开关](/document/android/message_send_receive.html#发送消息前的内容审核)设置是否返回发送成功的消息。
- [Web 端支持向指定设备发消息](/document/web/multi_device.html#获取当前用户的其他登录设备的登录-id-列表),例如,电脑端给手机端发消息,登录同一账号的多个设备均会收到消息。
- Web 端聊天室和群组成员进出事件增加成员人数 `memberCount` 字段。
**优化**:
- 移动端群组全员禁言状态存储到本地数据库,下次登录时可以直接从本地获取到。
- 移动端转发合并消息时导致的附件重复上传问题。| 2024-01-30 |
- [Android 4.4.0 更新日志](/document/android/releasenote.html#版本-v4-4-0-dev-2024-01-30-开发版)
- [iOS 4.4.0 更新日志](/document/ios/releasenote.html#版本-v4-4-0-dev-2024-01-30-开发版)
- [Web 4.5.0 更新日志](/document/web/releasenote.html#版本-v4-5-0-dev-2024-01-30-开发版)
- [小程序 4.5.0 更新日志](/document/applet/releasenote.html#版本-v4-5-0-dev-2024-01-30-开发版)。 | ## 2023-12 diff --git a/docs/product/product_message_overview.md b/docs/product/product_message_overview.md index 82bbab3e..1eda5490 100644 --- a/docs/product/product_message_overview.md +++ b/docs/product/product_message_overview.md @@ -120,11 +120,11 @@ Web 和小程序端无本地消息存储。 默认情况下,消息附件可在服务器存储 **7** 天。若要提升该限制,你需要联系商务。消息附件的大小及存储时间限制与群组共享文件的相同。如果消息附件的其中一个限制进行了上调,群组共享文件的对应限制也会随之自动调整,反之亦然。 -#### 离线消息存储 +### 离线消息存储 对于单聊和群聊,离线消息默认保存 **7** 天。对于每个终端用户,所有的单聊会话可存储 500 条离线消息,所有的群聊会话可存储 200 条离线消息。若超过存储天数和条数的上限,最新的离线消息会挤掉最早的。如需提升上限,可联系商务。 -#### 事件通知存储 +### 事件通知存储 各类事件通知的存储时间与消息的存储一致。 diff --git a/docs/product/product_thread_overview.md b/docs/product/product_thread_overview.md index 434a450e..bd234b89 100644 --- a/docs/product/product_thread_overview.md +++ b/docs/product/product_thread_overview.md @@ -22,7 +22,7 @@ | 功能 | 描述 | | :--------- | :----- | | 发送子区消息 | 发送子区消息和发送群组消息的方法基本一致。唯一不同的是,发送子区消息需要指定是否是子区的标记。| -| 接收子区消息 | 接收子区消息与接收单聊、群聊和聊天室相同,详见[接收消息](/document/android/message_send_receive.html#接收消息)。 | +| 接收子区消息 | 接收子区消息与接收单聊、群聊和聊天室相同,详见[接收消息](/document/android/message_send_receive.htm)。 | | 撤回子区消息 | 撤回子区消息的逻辑与撤回单聊、群聊和聊天室相同。消息撤回后,子区所属群组的所有成员收到子区更新回调,子区成员收到子区消息撤回的回调。 | | 获取子区消息 | 你可以从服务器或本地获取单个子区的消息。 | diff --git a/docs/product/solution_common/group_@.md b/docs/product/solution_common/group_@.md index 878db472..c91c543a 100644 --- a/docs/product/solution_common/group_@.md +++ b/docs/product/solution_common/group_@.md @@ -51,7 +51,7 @@ @tab Java -```Java +```java JSONArray atUserList = new JSONArray(); atUserList.put("@User1"); atUserList.put("@User2"); @@ -67,7 +67,7 @@ EMClient.getInstance().chatManager().sendMessage(msg); @tab Kotlin -```Kotlin +```kotlin val atUserList = JSONArray() atUserList.put("@User1") atUserList.put("@User2") @@ -131,7 +131,7 @@ client.send(message); @tab Java -```Java +```java private void handleMentionedMessage(EMMessage messages) { try { String groupId = msg.getTo(); @@ -170,7 +170,7 @@ private void handleMentionedMessage(EMMessage messages) { @tab Kotlin -```Kotlin +```kotlin private fun handleMentionedMessage(messages: EMMessage) { try { val groupId: String = messages.to diff --git a/docs/product/solution_common/message_quote.md b/docs/product/solution_common/message_quote.md index 2d2659f6..87ec72aa 100644 --- a/docs/product/solution_common/message_quote.md +++ b/docs/product/solution_common/message_quote.md @@ -194,7 +194,7 @@ sendMessage(message){ @tab Java -```Java +```java private void handleQuotedMessage(EMMessage message) { if(message.ext() != null && message.ext().containsKey("msgQuote")) { String msgQuote = message.getStringAttribute("msgQuote",""); @@ -238,7 +238,7 @@ private void handleQuotedMessage(EMMessage message) { @tab Kotlin -```Kotlin +```kotlin private fun handleMentionedMessage(message: EMMessage) { message.ext()?.let { if (it.containsKey("msgQuote")){ diff --git a/docs/product/solution_common/typing_indication.md b/docs/product/solution_common/typing_indication.md new file mode 100644 index 00000000..6f32c9c6 --- /dev/null +++ b/docs/product/solution_common/typing_indication.md @@ -0,0 +1,393 @@ +# 通过透传消息实现输入指示器 + +输入指示器显示其他用户何时输入消息。通过该功能,用户之间可进行有效沟通,增加了用户对聊天应用中交互的期待感。 + +## 前提条件 + +开始前,请确保满足以下条件: + +- 完成 SDK 初始化,详见各端快速开始,例如 [Android 端](/document/android/quickstart.html)。 +- 了解即时通讯 IM 的使用限制,详见 [使用限制](/product/limitation.html)。 + +## 实现过程 + +你可以通过透传消息实现输入指示器。下图为输入指示器的工作原理。 + +![img](/images/common/typing_indicator.png) + +监听用户 A 的输入状态。一旦有文本输入,通过透传消息将输入状态发送给用户 B,用户 B 收到该消息,了解到用户 A 正在输入文本。 + +- 用户 A 向用户 B 发送消息,通知其开始输入文本。 +- 收到消息后,如果用户 B 与用户 A 的聊天页面处于打开状态,则显示用户 A 的输入指示器。 +- 如果用户 B 在几秒后未收到用户 A 的输入,则自动取消输入指示器。 + +:::tip +用户 A 可根据需要设置透传消息发送间隔。 +::: + +### 发送输入状态的透传消息 + +以下示例代码展示如何发送输入状态的透传消息。 + +::: tabs#code + +@tab Android + +```java +//发送表示正在输入的透传消息 +private static final String MSG_TYPING_BEGIN = "TypingBegin"; +private long previousChangedTimeStamp; + +private void textChange() { + long currentTimestamp = System.currentTimeMillis(); + if(currentTimestamp - previousChangedTimeStamp > 5) { + sendBeginTyping(); + previousChangedTimeStamp = currentTimestamp; + } +} + +private void sendBeginTyping() { + EMMessage beginMsg = EMMessage.createSendMessage(EMMessage.Type.CMD); + EMCmdMessageBody body = new EMCmdMessageBody(MSG_TYPING_BEGIN); + // 将该透传消息只发送给在线用户 + body.deliverOnlineOnly(true); + beginMsg.addBody(body); + beginMsg.setTo(toChatUsername); + EMClient.getInstance().chatManager().sendMessage(beginMsg); +} +``` + +@tab iOS + +```objectivec +//发送表示正在输入的透传消息 +#define MSG_TYPING_BEGIN @"TypingBegin" + +- (void)textViewDidChange:(UITextView *)textView +{ + long long currentTimestamp = [self getCurrentTimestamp]; + // 5 秒内不能重复发送消息 + if ((currentTimestamp - _previousChangedTimeStamp) > 5) { + // 发送开始输入的透传消息 + [self _sendBeginTyping]; + _previousChangedTimeStamp = currentTimestamp; + } +} + +- (void)_sendBeginTyping +{ + EMCmdMessageBody *body = [[EMCmdMessageBody alloc] initWithAction:MSG_TYPING_BEGIN]; + body.isDeliverOnlineOnly = YES; + EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId body:body ext:nil]; + [[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:nil]; +} +``` + +@tab Web/Applet + +```typescript +let previousChangedTimeStamp = 0; +// 监听输入状态的变化 +const onInputChange = function () { + const currentTimestamp = new Date().getTime(); + if (currentTimestamp - previousChangedTimeStamp > 5000) { + sendBeginTyping(); + previousChangedTimeStamp = currentTimestamp; + } +}; + +// 创建输入状态消息并发送 +const sendBeginTyping = function () { + const option = { + // 会话类型:单聊、群聊和聊天室分别为 `singleChat`、`groupChat` 和 `chatRoom`。 + chatType: "singleChat", + // 消息类型。 + type: "cmd", + // 消息接收方:单聊为对方用户 ID,群聊和聊天室分别为群组 ID 和聊天室 ID。 + to: "", + // 用户自定义操作。 + action: "TypingBegin", + }; + const typingMessage = message.create(option); + + connection + .send(typingMessage) + .then(() => { + console.log("success"); + }) + .catch((e) => { + console.log("fail"); + }); +}; +``` + +@tab Flutter + +```dart +//发送表示正在输入的透传消息 +final String msgTypingBegin = "TypingBegin"; + +void textChange() { + int currentTimestamp = getCurrentTimestamp(); + if (currentTimestamp - _previousChangedTimeStamp > 5) { + _sendBeginTyping(); + _previousChangedTimeStamp = currentTimestamp; + } +} + +void _sendBeginTyping() async { + var msg = EMMessage.createCmdSendMessage( + targetId: conversationId, + action: msgTypingBegin, + // 将该透传消息只发送给在线用户 + deliverOnlineOnly: true, + ); + msg.chatType = ChatType.Chat; + EMClient.getInstance.chatManager.sendMessage(msg); +} + +``` + +@tab React Native + +```typescript +// 发送自己在输入状态中的命令消息 +const action = "inputting"; +const msg = ChatMessage.createCmdMessage(targetId, action, chatType); +EMClient.getInstance().chatManager().sendMessage(msg, callback).then().catch(); +``` + +@tab Unity/Windows + +```csharp +//发送表示正在输入的透传消息 +string msgTypingBegin = "TypingBegin"; + +void textChange() { + int currentTimestamp = getCurrentTimestamp(); + if (currentTimestamp - _previousChangedTimeStamp > 5) { + _sendBeginTyping(); + _previousChangedTimeStamp = currentTimestamp; + } +} + +void _sendBeginTyping() { + var msg = Message.CreateCmdSendMessage( + username: conversationId, + action: msgTypingBegin, + deliverOnlineOnly: true, + ); + msg.chatType = MessageType.Chat; + SDKClient.getInstance.chatManager.sendMessage(msg); +} + +``` +::: + +### 接收和解析输入状态的透传消息 + +以下示例代码展示如何接收和解析输入状态的透传消息。 + +::: tabs#code + +@tab Android + +```java +private static final int TYPING_SHOW_TIME = 10000; +private static final int MSG_TYPING_END = 1; +private Handler typingHandler; + +private void initTypingHandler() { + typingHandler = new Handler(Looper.myLooper()) { + @Override + public void handleMessage(@NonNull Message msg) { + switch (msg.what) { + case MSG_TYPING_END : + cancelTimer(); + break; + } + } + }; +} + +@Override +public void onCmdMessageReceived(List messages) { + for (EMMessage msg : messages) { + if(!TextUtils.equals(msg.conversationId(), currentConversationId)) { + return; + } + EMCmdMessageBody body = (EMCmdMessageBody) msg.getBody(); + if(TextUtils.equals(body.action(), MSG_TYPING_BEGIN)) { + // 这里需更新 UI,显示“对方正在输入” + beginTimer(); + } + } +} + +private void beginTimer() { + if(typingHandler != null) { + typingHandler.removeMessages(MSG_TYPING_END); + typingHandler.sendEmptyMessageDelayed(MSG_TYPING_END, TYPING_SHOW_TIME); + } +} + +private void cancelTimer() { + // 这里需更新 UI,不再显示“对方正在输入” + if(typingHandler != null) { + typingHandler.removeCallbacksAndMessages(null); + } +} + +``` + +@tab iOS + +```objectivec +#define TypingTimerCountNum 10 +- (void)cmdMessagesDidReceive:(NSArray *)aCmdMessages +{ + NSString *conId = self.currentConversation.conversationId; + for (EMChatMessage *message in aCmdMessages) { + if (![conId isEqualToString:message.conversationId]) { + continue; + } + EMCmdMessageBody *body = (EMCmdMessageBody *)message.body; + // 收到正在输入的透传消息 + if ([body.action isEqualToString:MSG_TYPING_BEGIN]) { + if (_receiveTypingCountDownNum == 0) { + [self startReceiveTypingTimer]; + }else { + _receiveTypingCountDownNum = TypingTimerCountNum; + } + } + + } +} + +- (void)startReceiveTypingTimer { + [self stopReceiveTypingTimer]; + _receiveTypingCountDownNum = TypingTimerCountNum; + _receiveTypingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(startReceiveCountDown) userInfo:nil repeats:YES]; + + [[NSRunLoop currentRunLoop] addTimer:_receiveTypingTimer forMode:UITrackingRunLoopMode]; + [_receiveTypingTimer fire]; + // 这里需更新 UI,显示“对方正在输入” +} + +- (void)startReceiveCountDown +{ + if (_receiveTypingCountDownNum == 0) { + [self stopReceiveTypingTimer]; + // 这里需更新 UI,不再显示“对方正在输入” + + return; + } + _receiveTypingCountDownNum--; +} + +- (void)stopReceiveTypingTimer { + _receiveTypingCountDownNum = 0; + if (_receiveTypingTimer) { + [_receiveTypingTimer invalidate]; + _receiveTypingTimer = nil; + } +} +``` + +@tab Web/Applet + +```typescript +// 设置状态监听器 +let timer; +conn.addEventHandler("message", { + onCmdMessage: (msg) => { + console.log("onCmdMessage", msg); + if (msg.action === "TypingBegin") { + // 这里需更新 UI,显示“对方正在输入” + beginTimer(); + } + }, +}); + +const beginTimer = () => { + timer && clearTimeout(timer); + timer = setTimeout(() => { + // 这里需更新 UI,不再显示“对方正在输入” + }, 5000); +}; +``` + +@tab Flutter + +```dart +final int typingTime = 10; +Timer? _timer; + +void onCmdMessagesReceived(List list) { + for (var msg in list) { + if (msg.conversationId != currentConversationId) { + continue; + } + EMCmdMessageBody body = msg.body as EMCmdMessageBody; + if (body.action == msgTypingBegin) { + // 这里需更新 UI,显示“对方正在输入” + beginTimer(); + } + } +} + +void beginTimer() { + _timer = Timer.periodic( + Duration(seconds: typingTime), + (timer) { + // 这里需更新 UI,显示“对方正在输入” + cancelTimer(); + }, + ); +} + +void cancelTimer() { + _timer?.cancel(); +} +``` + +@tab React Native + +```typescript +let listener = new (class implements ChatMessageEventListener { + onCmdMessagesReceived(messages: ChatMessage[]): void { + // 收到命令消息 + for (msg of messages) { + // 过略消息 + if (msg.body.action === "inputting") { + // todo: 界面显示正在输入中的状态 + } + } + } +})(); +ChatClient.getInstance().chatManager.addMessageListener(listener); +``` + +@tab Unity/Windows + +```csharp +int typingTime = 10; + +void OnCmdMessagesReceived(List list) { + for (var msg in list) { + if (msg.ConversationId != currentConversationId) { + continue; + } + MessageBody.CmdBody body = msg.Body as MessageBody.CmdBody; + if (body.Action == msgTypingBegin) { + // 这里需更新 UI,显示“对方正在输入” + + Timer timer = new Timer((state) => + { + // 这里需更新 UI,不再显示“对方正在输入” + }, null, typingTime, Timeout.Infinite); + } + } +} +``` +::: diff --git a/docs/product/voiceroom/demo_scenario_introduction.md b/docs/product/voiceroom/demo_scenario_introduction.md index f3b5c14e..15febeae 100644 --- a/docs/product/voiceroom/demo_scenario_introduction.md +++ b/docs/product/voiceroom/demo_scenario_introduction.md @@ -2,7 +2,7 @@ ## 场景描述 -环信语聊房(Easemob Chat Room)场景方案是环信打造的一款低门槛、高可用的语聊房场景方案。该场景方案融合了环信即时通讯云的聊天室和声网 RTC 的音频技术,提供市面主流的语聊房 App 的功能,其核心功能包括房间管理、麦位控制、聊天打赏和音频特效等,覆盖语音游戏、语音社交、相亲交友等场景,能够较为全面满足客户的语聊房开发需求。同时,环信 IM 的聊天室中的 [KV 属性管理和自动销毁](/document/ios/room_attributes.html)以及[消息优先级](/document/ios/message_send_receive.html)等功能能够对语聊房的功能性进行有效补充和拓展。 +环信语聊房(Easemob Chat Room)场景方案是环信打造的一款低门槛、高可用的语聊房场景方案。该场景方案融合了环信即时通讯云的聊天室和声网 RTC 的音频技术,提供市面主流的语聊房 App 的功能,其核心功能包括房间管理、麦位控制、聊天打赏和音频特效等,覆盖语音游戏、语音社交、相亲交友等场景,能够较为全面满足客户的语聊房开发需求。同时,环信 IM 的聊天室中的 [KV 属性管理和自动销毁](/document/ios/room_attributes.html)以及[消息优先级](/document/ios/message_send_receive.html#设置聊天室消息优先级)等功能能够对语聊房的功能性进行有效补充和拓展。 ## 功能列表 diff --git a/docs/push/push_androidvendor_msgclassification.md b/docs/push/push_androidvendor_msgclassification.md index 8bc0fab0..6f405e44 100644 --- a/docs/push/push_androidvendor_msgclassification.md +++ b/docs/push/push_androidvendor_msgclassification.md @@ -141,7 +141,7 @@ QPS计算规则只适用于中国大陆。 目前私信处于公测阶段,需要申请才能开启私信通道,私信申请请参考[OPPO PUSH通道升级公测邀请](https://open.oppomobile.com/wiki/doc#id=10614) -:::notice +:::tip OPPO推送服务将于2023年1月15日优化推送数量限制规则,对已申请正式权限的应用增加区分应用类别的推送频控限制。 ::: @@ -159,7 +159,7 @@ OPPO暂未对外公布详细推送速率限制。 通知内容:200字符(中英文均以一个计算) -:::notice +:::tip 超过限制则该条消息无法通过OPPO厂商通道发送,环信在线通道不受影响。 ::: @@ -257,7 +257,7 @@ MIUI日联网设备数的查询路径:推送运营平台-推送统计-用户 通知内容:128字符(中英文字符均计算为1) -:::notice +:::tip 超过限制则该条消息无法通过小米厂商通道发送,环信在线通道不受影响。 ::: diff --git a/docs/push/push_apns_deliver_statistics.md b/docs/push/push_apns_deliver_statistics.md index d3c59817..d6b92a81 100644 --- a/docs/push/push_apns_deliver_statistics.md +++ b/docs/push/push_apns_deliver_statistics.md @@ -4,7 +4,7 @@ 你需要在应用里创建推送服务扩展(推送扩展服务仅支持 iOS 10 及以上版本),在推送扩展里导入环信扩展服务 SDK,调用对应的 API。 -:::notice +:::tip APNs 的点击已在 SDK 内部实现,无需单独处理。 ::: @@ -70,13 +70,13 @@ APNs 的送达统计 SDK 下载地址: 在 **NotificationService.m** 里引入头文件: -```objectiveC +```objectivec #import ``` 在系统提供处理推送的方法里调用 EMPushServiceExt 的两个方法,这两个方法必须都调用,且设置 Appkey的方法需要先调用。 -```objectiveC +```objectivec - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; diff --git a/docs/push/push_certificate_config.md b/docs/push/push_certificate_config.md index 439e7b59..1de2c500 100644 --- a/docs/push/push_certificate_config.md +++ b/docs/push/push_certificate_config.md @@ -12,6 +12,6 @@ iOS端推送集成:[iOS 推送集成](push_integration_process_ios.html) ![img](/images/instantpush/push_certificate_add.png) -:::notice +:::tip Push与IM使用相同的SDK,证书配置可以通用。 ::: \ No newline at end of file diff --git a/docs/push/push_createnotification.md b/docs/push/push_createnotification.md index ce290c86..40c302bb 100644 --- a/docs/push/push_createnotification.md +++ b/docs/push/push_createnotification.md @@ -25,7 +25,7 @@ #### 推送目标: - 全量用户 -- 指定用户:接受人以英文逗号分开,最多 1000 个接收人 +- 指定用户:接收人以英文逗号分开,最多 1000 个接收人 - 指定标签用户:支持最多选择3个标签,支持交集运算 ![img](/images/instantpush/push_user_certain.png) diff --git a/docs/push/push_createproduct_app.md b/docs/push/push_createproduct_app.md index 722c32cf..34284999 100644 --- a/docs/push/push_createproduct_app.md +++ b/docs/push/push_createproduct_app.md @@ -33,7 +33,7 @@ APP创建成功后,将显示至【应用列表】中,选中创建的APP, ![img](/images/instantpush/push_sdk_download.png) -:::notice +:::tip 环信IM用户可直接使用,无需进行移动端集成。 ::: diff --git a/docs/push/push_integration_note_android.md b/docs/push/push_integration_note_android.md index 7da02ef2..a2212428 100644 --- a/docs/push/push_integration_note_android.md +++ b/docs/push/push_integration_note_android.md @@ -8,7 +8,7 @@ SDK 导入:[Android SDK 导入](/document/android/quickstart.html#_2-集成-sdk) -注册登录:[注册登录](/document/android/overview.html#注册用户) +注册登录:[注册登录](/document/android/login.html#用户注册) **手机权限** diff --git a/docs/push/push_integration_note_ios.md b/docs/push/push_integration_note_ios.md index fbc0d39a..6e4890d7 100644 --- a/docs/push/push_integration_note_ios.md +++ b/docs/push/push_integration_note_ios.md @@ -53,7 +53,7 @@ demo 中的 SDK 文件夹为 **Hyphenate SDK**,将 SDK 文件夹拖入到工 在 Xcode 中,向 **General > Embedded Binaries** 中添加依赖库。 -:::notice +:::tip 将**Do Not Embed** 改成**Embed & Sign**。 ::: @@ -69,7 +69,7 @@ demo 中的 SDK 文件夹为 **Hyphenate SDK**,将 SDK 文件夹拖入到工 第 2 步:在工程的 AppDelegate 中的以下方法中,调用 SDK 对应方法。 -```objectiveC +```objectivec - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //AppKey:注册的 AppKey,详细见下面注释。 @@ -102,7 +102,7 @@ demo 中的 SDK 文件夹为 **Hyphenate SDK**,将 SDK 文件夹拖入到工 登录:调用 SDK 的登录接口进行的操作。建议使用异步登录方法,防止网络不好的情况下,出现卡 UI 主线程的情况出现。 -```objectiveC +```objectivec [[EMClient sharedClient] loginWithUsername:@"8001" password:@"111111" completion:^(NSString *aUsername, EMError *aError) { if (!aError) { NSLog(@"登录成功"); @@ -118,7 +118,7 @@ demo 中的 SDK 文件夹为 **Hyphenate SDK**,将 SDK 文件夹拖入到工 ##### 1.注册开启推送通知 -```objectiveC +```objectivec if (NSClassFromString(@"UNUserNotificationCenter")) { //注册推送,用于 iOS 10 及以上版本。 [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError *error) { @@ -150,11 +150,11 @@ if (NSClassFromString(@"UNUserNotificationCenter")) { ##### 2.将获得的 deviceToken 传到 SDK -:::notice +:::tip 如果是 iOS 13 及以上的系统,请将 SDK 更新至 v3.6.4 或以上版本。 ::: -```objectiveC +```objectivec // 将获得的 deviceToken 传给 SDK。 - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { @@ -166,7 +166,7 @@ if (NSClassFromString(@"UNUserNotificationCenter")) { 3.开启环信推送处理 -```objectiveC +```objectivec [[EMLocalNotificationManager sharedManager] launchWithDelegate:self]; ``` @@ -178,7 +178,7 @@ if (NSClassFromString(@"UNUserNotificationCenter")) { 实现以下两个代理,通过 completionHandler 您可以更改通知方式: -```objectiveC +```objectivec - (void)emuserNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { NSDictionary *userInfo = notification.request.content.userInfo; @@ -206,7 +206,7 @@ if (NSClassFromString(@"UNUserNotificationCenter")) { 通过下面代理获取推送相关信息: -```objectiveC +```objectivec //如果需要获取数据,只实现这一个代理方法即可。 - (void)emGetNotificationMessage:(UNNotification *)notification state:(EMNotificationState)state { @@ -229,7 +229,7 @@ if (NSClassFromString(@"UNUserNotificationCenter")) { 推送通知透传消息获取 -```objectiveC +```objectivec //当应用收到环信推送透传消息时,此方法会被调用。 - (void)emDidReceivePushSilentMessage:(NSDictionary *)messageDic { @@ -241,7 +241,7 @@ if (NSClassFromString(@"UNUserNotificationCenter")) { iOS 的本地通知管理模块 `UNUserNotificationCenter` 是单例,一个 App 中只能有一个实例。如果在启用 SDK 在线推送后,App 又重写了 `[UNUserNotificationCenter currentNotificationCenter].delegate`,会将 SDK 中的 delegate 覆盖,此时,需要在 App 实现的 `UNUserNotificationCenterDelegate` 中调用 SDK 的相关处理,过程如下: -```objectiveC +```objectivec - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { [[EMLocalNotificationManager sharedManager] userNotificationCenter:center willPresentNotification:notification withCompletionHandler:completionHandler]; diff --git a/docs/push/push_integration_process_ios.md b/docs/push/push_integration_process_ios.md index 974ebf36..992178fd 100644 --- a/docs/push/push_integration_process_ios.md +++ b/docs/push/push_integration_process_ios.md @@ -61,13 +61,13 @@ pod install #### 1. 引入头文件 -```objectiveC +```objectivec #import ``` #### 2. 初始化 EMPush -```objectiveC +```objectivec EMPushClientOptions *option = [EMPushClientOptions optionsWithAppkey:@"appkey"]; option.enableConsoleLog = YES; option.isAutoLogin = YES; @@ -82,7 +82,7 @@ option.apnsCertName = @"apnsname"; 强烈建议开发者通过后台调用 REST 接口去注册环信 ID,不建议使用客户端注册。 -```objectiveC +```objectivec [EMPushClient registerWithUsername:@"name" password:@"pswd" completion:^(NSString *aUsername, EMError *aError) { if (!aError) { // 注册完成。 @@ -94,7 +94,7 @@ option.apnsCertName = @"apnsname"; #### 4. 连接服务器 -```objectiveC +```objectivec [EMPushClient connectWithUsername:name password:pswd completion:^(NSString *aUsername, EMError *aError) { if (!aError) { // 连接到服务器。 @@ -106,7 +106,7 @@ option.apnsCertName = @"apnsname"; #### 5. 断开服务器连接 -```objectiveC +```objectivec [EMPushClient disConnect:YES completion:^(EMError *aError) { if (!aError) { // 断开服务器连接。 @@ -120,6 +120,6 @@ option.apnsCertName = @"apnsname"; 添加代理的类必须实现 `EMClientDelegate`,进行代理实现。 -```objectiveC +```objectivec [EMPushClient addConnectDelegate:self delegateQueue:nil]; ``` \ No newline at end of file diff --git a/docs/uikit/chatroomuikit/ios/roomuikit_best_practice.md b/docs/uikit/chatroomuikit/ios/roomuikit_best_practice.md index f5867011..cd4a4db6 100644 --- a/docs/uikit/chatroomuikit/ios/roomuikit_best_practice.md +++ b/docs/uikit/chatroomuikit/ios/roomuikit_best_practice.md @@ -8,7 +8,7 @@ 初始化 ChatroomUIKit 时,你可以传入 `option` 参数,设置各种选项。 -```Swift +```swift let option = ChatroomUIKitInitialOptions.ChatOptions() option.enableConsoleLog = true option.autoLogin = true @@ -19,7 +19,7 @@ let error = ChatroomUIKitClient.shared.setup(appKey,option: option) 你可以通过使用工程中的用户对象并遵守 `UserInfoProtocol` 协议登录 ChatroomUIKit,示例代码如下: -```Swift +```swift public final class YourAppUser: NSObject,UserInfoProtocol { public func toJsonObject() -> Dictionary? { @@ -53,7 +53,7 @@ ChatroomUIKitClient.shared.login(user: YourAppUser(), token: "token", completion 3. 添加视图。 -```Swift +```swift let options = ChatroomUIKitInitialOptions.UIOptions() options.bottomDataSource = self.bottomBarDatas() // 实现显示礼物消息区域。 @@ -70,7 +70,7 @@ addSubView(roomView) 你可以调用 `registerRoomEventsListener` 方法添加监听器用于监听 ChatroomUIKit 事件和错误。 -```Swift +```swift ChatroomUIKitClient.shared.registerRoomEventsListener(self) ``` diff --git a/docs/uikit/chatroomuikit/ios/roomuikit_customize.md b/docs/uikit/chatroomuikit/ios/roomuikit_customize.md index a48d2089..9f94aae1 100644 --- a/docs/uikit/chatroomuikit/ios/roomuikit_customize.md +++ b/docs/uikit/chatroomuikit/ios/roomuikit_customize.md @@ -42,7 +42,7 @@ 下面以如何自定义礼物弹幕视图 Cell 为例介绍如何自定义控件。首先,继承 `GiftMessageCell`,添加自己的逻辑,然后在 ChatroomUIKit 中注册新类替换原有的类。 -````Swift +````swift class CustomGiftMessageViewCell: GiftMessageCell { lazy var redDot: UIView = { UIView().backgroundColor(.red).cornerRadius(.large) diff --git a/docs/uikit/chatroomuikit/ios/roomuikit_theme.md b/docs/uikit/chatroomuikit/ios/roomuikit_theme.md index a799b8a2..50419f16 100644 --- a/docs/uikit/chatroomuikit/ios/roomuikit_theme.md +++ b/docs/uikit/chatroomuikit/ios/roomuikit_theme.md @@ -38,7 +38,7 @@ ChatroomUIKit 中的所有颜色均使用 HSLA 颜色模型定义,该模型是 通过调整 HSLA 模型的色相值,你可以实现精确的色彩控制。 -```Swift +```swift Appearance.primaryHue = 191/360.0 Appearance.secondaryHue = 210/360.0 Appearance.errorHue = 189/360.0 @@ -53,13 +53,13 @@ Theme.switchTheme(style: .custom) 1. 在 `Theme` 类中注册你的视图。 -```Swift +```swift Theme.registerSwitchThemeViews(view: self) ``` 2. 在上述视图中实现 `ThemeSwitchProtocol` 协议。 -```Swift +```swift extension YourView: ThemeSwitchProtocol { } diff --git a/docs/uikit/chatroomuikit/react-native/roomuikit_theme.md b/docs/uikit/chatroomuikit/react-native/roomuikit_theme.md index e77f17b7..11d1265a 100644 --- a/docs/uikit/chatroomuikit/react-native/roomuikit_theme.md +++ b/docs/uikit/chatroomuikit/react-native/roomuikit_theme.md @@ -33,7 +33,7 @@ setTheme(theme === light ? dark : light); `usePresetPalette` 可以使用内置的默认主题颜色,也可以使用 `useCreatePalette` 设置自定义主题颜色。 -```Swift +```swift // 自定义颜色。通过修改颜色具体值,组件中对应颜色将统一改变。 // 参考这里:https://www.figma.com/file/OX2dUdilAKHahAh9VwX8aI/Streamuikit?type=design&node-id=101-41012&mode=design&t=Fzou3Gwsk4owLLbr-4 const { createPalette } = useCreatePalette({ diff --git a/docs/uikit/chatuikit/android/chatfeature_message.md b/docs/uikit/chatuikit/android/chatfeature_message.md index 3fd63791..457c16f2 100644 --- a/docs/uikit/chatuikit/android/chatfeature_message.md +++ b/docs/uikit/chatuikit/android/chatfeature_message.md @@ -290,7 +290,7 @@ class ChatThreadActivity:ChatUIKitThreadActivity() { #### 自定义输入状态指示 UI -本功能使用 SDK 的透传消息实现,详见 [SDK 相关文档](/document/android/message_send_receive.html#通过透传消息实现输入指示器)。 +本功能使用 SDK 的透传消息实现,详见 [SDK 相关文档](/document/product/typing_indication.html)。 用户需要监听透传消息回调处理导航相关 UI 显示效果。 diff --git a/docs/uikit/chatuikit/android/chatuikit_integrated.md b/docs/uikit/chatuikit/android/chatuikit_integrated.md index 5b99449d..841c9eec 100644 --- a/docs/uikit/chatuikit/android/chatuikit_integrated.md +++ b/docs/uikit/chatuikit/android/chatuikit_integrated.md @@ -19,8 +19,9 @@ 在 app 项目 `build.gradle.kts` 中添加以下依赖: ```kotlin -implementation("io.hyphenate:ease-chat-kit:4.7.0") +implementation("io.hyphenate:ease-chat-kit:4.11.1") ``` +若要查看最新版本号,请点击[这里](https://central.sonatype.com/artifact/io.hyphenate/ease-chat-kit/versions)。 ### 本地依赖 @@ -49,6 +50,21 @@ implementation(project(mapOf("path" to ":ease-im-kit"))) -dontwarn com.hyphenate.** ``` +## 初始化 + +在使用 UIKit 的控件前,必须要先初始化。例如在 `Application` 中: + +```kotlin +class DemoApplication: Application() { + + override fun onCreate() { + val options = ChatOptions() + options.appKey = "你的appkey" + ChatUIKitClient.init(this, options) + } +} +``` + ## 快速搭建页面 ### 创建聊天页面 diff --git a/docs/uikit/chatuikit/android/chatuikit_quickstart.md b/docs/uikit/chatuikit/android/chatuikit_quickstart.md index 0a92eb96..29a73c24 100644 --- a/docs/uikit/chatuikit/android/chatuikit_quickstart.md +++ b/docs/uikit/chatuikit/android/chatuikit_quickstart.md @@ -59,6 +59,7 @@ ```kotlin implementation("io.hyphenate:ease-chat-kit:4.11.1") ``` +若要查看 UIKit 的最新版本号,请点击[这里](https://central.sonatype.com/artifact/io.hyphenate/ease-chat-kit/versions)。 **本地依赖** @@ -168,7 +169,9 @@ implementation(project(mapOf("path" to ":ease-im-kit"))) ### 第二步 实现代码逻辑 -1. 实现登录和退出页面。 +1. 初始化UIKit。 + +2. 实现登录和退出页面。 :::tip 若你已集成了即时通讯 IM SDK,SDK 的所有用户 ID 均可用于登录单群聊 UIKit。 @@ -176,7 +179,9 @@ implementation(project(mapOf("path" to ":ease-im-kit"))) 你需要在环信控制台[创建 IM 用户](/product/enable_and_configure_IM.html#创建-im-用户),登录时传入用户 ID 和密码。 -打开 `MainActivity` 文件,并替换为如下代码: +完整实现示例代码: + +打开 `MainActivity` 文件,并替换为如下代码。 ```kotlin package com.easemob.quickstart @@ -311,7 +316,7 @@ fun Context.showToast(msg: String) { } ``` -2. 点击 `Sync Project with Gradle Files` 同步工程。现在可以测试你的应用了。 +3. 点击 `Sync Project with Gradle Files` 同步工程。现在可以测试你的应用了。 ### 第三步 发送第一条消息 diff --git a/docs/uikit/chatuikit/android/releasenote.md b/docs/uikit/chatuikit/android/releasenote.md index 539f1047..16695c6e 100644 --- a/docs/uikit/chatuikit/android/releasenote.md +++ b/docs/uikit/chatuikit/android/releasenote.md @@ -1,6 +1,6 @@ # Android 单群聊 UIKit 更新日志 -## 版本 V4.11.0 +## 版本 V4.11.1 ### 新增特性 diff --git a/docs/uikit/chatuikit/android/ui_historic.md b/docs/uikit/chatuikit/android/ui_historic.md index 47255f17..9ef689d8 100644 --- a/docs/uikit/chatuikit/android/ui_historic.md +++ b/docs/uikit/chatuikit/android/ui_historic.md @@ -34,7 +34,7 @@ EaseIMKit 支持 Gradle 接入和 Module 源码集成 #### Gradle 接入集成 -:::notice 重大变动 +:::tip 重大变动 远程仓库统一由 JCenter 迁移到 `MavenCentral`,依赖库的域名由 “com.hyphenate” 修改为 “io.hyphenate”,详见 [环信即时通讯 IM Android 快速开始](/document/android/quickstart.html)。 ::: @@ -45,7 +45,7 @@ implementation 'io.hyphenate:hyphenate-chat:xxx版本' **EaseIMKit 必须依赖环信 IM SDK,因而在使用 EaseIMKit 时必须同时添加环信 IM SDK 依赖。** -:::notice +:::tip 1. IM SDK **3.8.0** 版本以后,远程依赖的 `artifactId` 修改为 `hyphenate-chat`,且该版本以后中不再包含音视频相关逻辑。 2. IM SDK **3.8.0** 以下,远程依赖,包含音视频的 `artifactId` 为 `hyphenate-sdk`,不包含音视频的 `artifactId` 为 `hyphenate-sdk-lite`。如果想使用不包含音视频通话的 SDK,用 `implementation 'io.hyphenate:hyphenate-sdk-lite:xxx版本`'。 @@ -72,7 +72,7 @@ EaseIMKit 中位置消息使用的是百度地图定位 jar 包,为了防止 在 application 的 onCreate 下调用初始化 EaseIMKit 的方法。 -:::notice +:::tip EaseIMKit 初始化里已包含 SDK 的初始化,不需要再去调用 SDK 的初始化。 ::: @@ -102,7 +102,7 @@ EaseIMKit 提供了 ChatUIKitConversationListFragment,需要将其或者其子 ![img](/images/android/easeim.jpeg) -:::notice +:::tip 要实现自定义头像及昵称,请参考 [设置头像和昵称](/document/android/userprofile.html#设置当前用户的属性)。 ::: @@ -615,7 +615,7 @@ public class ChatTxtNewAdapterDelegate extends EaseMessageAdapterDelegate () @@ -186,7 +186,7 @@ extension MessageListViewModel { 将上述继承的对象初始化后,在 `EaseChatUIKit` 中进行注册。 -```Swift +```swift ComponentsRegister.shared.MessageRenderEntity = MineMessageEntity.self ComponentsRegister.shared.Conversation = MineConversationInfo.self @@ -198,7 +198,7 @@ extension MessageListViewModel { 例如,在以下示例代码中,会话中收到新消息时显示为 "[红包]",主要调整了在非文本消息类型的 `else` 中根据自定义消息的 `event` 显示对应的内容。 -```Swift +```swift import UIKit import EaseChatUIKit diff --git a/docs/uikit/chatuikit/ios/chatuikit_custom_chat.md b/docs/uikit/chatuikit/ios/chatuikit_custom_chat.md index 5832b085..81922bd1 100644 --- a/docs/uikit/chatuikit/ios/chatuikit_custom_chat.md +++ b/docs/uikit/chatuikit/ios/chatuikit_custom_chat.md @@ -35,7 +35,7 @@ 如果需要复用已有逻辑再增加新逻辑,则只需重载对应方法后调用 `super.xxx`,例如: -```Swift +```swift override open func refresh(entity: MessageEntity) { super.refresh(entity:entity) //继续你的新逻辑 @@ -72,7 +72,7 @@ 你可以将不需要的功能移除,也还可添加表情回应(`MessageReaction`)和话题(`MessageThread`)。**注意,添加表情回应和话题功能前,需在[环信即时通讯控制台](https://console.easemob.com/user/login)开通。** -```Swift +```swift //是否显示消息话题。 if self.messageThread { Appearance.chat.contentStyle.append(.withMessageThread) @@ -111,13 +111,13 @@ 1. 若实现发送附件消息时弹出类似微信样式的弹窗,可进行如下设置: -```Swift +```swift Appearance.chat.messageAttachmentMenuStyle = .followInput ``` 2. 若实现消息长按后弹出仿系统 `UIActionSheet` 样式的弹窗,可进行如下设置: -```Swift +```swift Appearance.chat.messageAttachmentMenuStyle = .actionSheet ``` @@ -179,7 +179,7 @@ 你可以通过 `Appearance.chat.messageLongPressedActions = value` 设置聊天页面长按消息后的 `ActionSheet` 菜单项。你可以继承 `MessageListController`,重载页面中的方法,移除或添加长按消息后的动作(action)。 -```Swift +```swift override func filterMessageActions(message: MessageEntity) -> [ActionSheetItemProtocol] { if let ext = message.message.ext,let value = ext[callIdentifier] as? String,value == callValue { return [ @@ -195,7 +195,7 @@ override func filterMessageActions(message: MessageEntity) -> [ActionSheetItemPr 获取某个 action 的点击事件,示例代码: -```Swift +```swift Appearance.chat.messageLongPressedActions.first { $0.tag == "xxx" }?.action = { [weak self ] in //action handler } @@ -207,13 +207,13 @@ override func filterMessageActions(message: MessageEntity) -> [ActionSheetItemPr 1. 若实现消息长按后弹出类似微信样式的弹窗,可进行如下设置: -```Swift +```swift Appearance.chat.messageLongPressMenuStyle = .withArrow ``` 2. 若实现消息长按后弹出仿系统 `UIActionSheet` 样式的弹窗,可进行如下设置: -```Swift +```swift Appearance.chat.messageLongPressMenuStyle = .actionSheet ``` @@ -236,7 +236,7 @@ override func filterMessageActions(message: MessageEntity) -> [ActionSheetItemPr 获取某个 action 的点击事件,示例代码: -```Swift +```swift Appearance.chat.inputExtendActions.first { $0.tag == "xxx" }?.action = { [weak self ] in //action handler } diff --git a/docs/uikit/chatuikit/ios/chatuikit_custom_contact_details.md b/docs/uikit/chatuikit/ios/chatuikit_custom_contact_details.md index bf50c903..2a668c8f 100644 --- a/docs/uikit/chatuikit/ios/chatuikit_custom_contact_details.md +++ b/docs/uikit/chatuikit/ios/chatuikit_custom_contact_details.md @@ -14,7 +14,7 @@ 联系人详情页面中右上角按钮 `...` 点击后弹出 `ActionSheet` 菜单中的数据源可配项`Appearance.contact.moreActions`。你可以增加或删除菜单项,示例代码如下: -```Swift +```swift //增加菜单项 Appearance.contact.moreActions.append(ActionSheetItem(title: "new list item", type: .destructive, tag: "contact_custom")) //删除菜单项 @@ -23,7 +23,7 @@ 获取该数组中某单个项的点击事件,示例如下: -```Swift +```swift if let item = Appearance.contact.moreActions.first(where: { $0.tag == "xxx" }) { item.actionClosure = { [weak self] _ in //do something @@ -40,7 +40,7 @@ 联系人详情页面 Header 中按钮 `CollectionView` 中数据源可配项 `Appearance.contact.detailExtensionActionItems`,主要功能包括聊天、音视频通话等。关于事件监听,详见[设置点击右侧图片显示的联系人操作](#设置点击右侧图片显示的联系人操作)。首先继承联系人详情页面,然后将继承后的群详情页面注册入 `EaseChatUIKit`,即 `ComponentsRegister.shared.ContactInfoController = MineContactDetailViewController.self`,增加可配项,示例如下所示: -```Swift +```swift final class MineContactDetailViewController: ContactInfoViewController { override func createHeader() -> DetailInfoHeader { diff --git a/docs/uikit/chatuikit/ios/chatuikit_custom_contact_list.md b/docs/uikit/chatuikit/ios/chatuikit_custom_contact_list.md index b42379cb..11c1df62 100644 --- a/docs/uikit/chatuikit/ios/chatuikit_custom_contact_list.md +++ b/docs/uikit/chatuikit/ios/chatuikit_custom_contact_list.md @@ -20,7 +20,7 @@ 下面的示例代码展示如何增加或删减数据项: -```Swift +```swift //增加数据项 Appearance.contact.listHeaderExtensionActions.append(ContactListHeaderItem(featureIdentify: "New", featureName: "NewFeature", featureIcon: UIImage(named: "NewFeature"))) //删减数据项 @@ -29,7 +29,7 @@ 获取该数组中某单个项的点击事件,示例如下: -```Swift +```swift if let item = Appearance.contact.listHeaderExtensionActions.first(where: { $0.featureIdentify == "NewFriendRequest" }) { item.actionClosure = { [weak self] _ in //do something @@ -58,7 +58,7 @@ 自定义联系人列表 TableView,需要重载联系人列表页面中的 `createContactList` 方法后,返回你继承 `EaseChatUIKit` 中 `ContactView` 后的类对象。关于在导航栏中实现业务逻辑,详见 `ContactView.swift` 类。示例代码如下: -```Swift +```swift override open func createContactList() -> ContactView { CustomContactView(frame: CGRect(x: 0, y: self.search.frame.maxY+5, width: self.view.frame.width, height: self.view.frame.height-NavigationHeight-BottomBarHeight-(self.tabBarController?.tabBar.frame.height ?? 49)), style: .plain) } @@ -70,7 +70,7 @@ 1. 继承 `EaseChatUIKit` 中的 `ContactCell` 类创建新的自定义类 `CustomContactCell`,然后进行如下代码设置: -```Swift +```swift ComponentsRegister.shared.ContactsCell = CustomContactCell.self ``` @@ -78,7 +78,7 @@ 如果需要复用已有逻辑再增加新逻辑,则只需重载对应方法后调用 `super.xxx`,例如: -```Swift +```swift override open func refresh(profile: EaseProfileProtocol) { super.refresh(profile: profile) //继续你的新逻辑 diff --git a/docs/uikit/chatuikit/ios/chatuikit_custom_conversation_list.md b/docs/uikit/chatuikit/ios/chatuikit_custom_conversation_list.md index d8799605..cbbb7635 100644 --- a/docs/uikit/chatuikit/ios/chatuikit_custom_conversation_list.md +++ b/docs/uikit/chatuikit/ios/chatuikit_custom_conversation_list.md @@ -27,7 +27,7 @@ 1. 在 Demo 中继承 `EaseChatUIKit` 中的 `EaseChatNavigationBar` 类创建自己的会话列表页面导航栏,例如 `CustomConversationNavigationBar`。 2. 重载 `createNavigation()` 方法并返回你使用 `CustomConversationNavigationBar` 创建的对象。示例代码如下: -```Swift +```swift override func createNavigationBar() -> EaseChatNavigationBar { CustomConversationNavigationBar(showLeftItem: false,rightImages: [UIImage(named: "add", in: .chatBundle, with: nil,hiddenAvatar: false)]) } @@ -64,7 +64,7 @@ 你可以利用 `Appearance.conversation.listMoreActions = value` 设置点击会话列表右上角的 `+` 之后的 `ActionSheet` 的菜单项。你可以增加或删除菜单项,示例代码如下: -```Swift +```swift //新增菜单项 Appearance.conversation.listMoreActions.append(ActionSheetItem(title: "new list item", type: .destructive, tag: "custom")) //删除菜单项 @@ -79,7 +79,7 @@ 对于导航点击事件的监听,你需要重载会话列表页面中的 `navigationClick` 方法,然后根据对应的点击区域做对应的处理,示例代码如下: -```Swift +```swift override func navigationClick(type: EaseChatNavigationBarClickEvent, indexPath: IndexPath?) { switch type { case .back: self.backAction() @@ -97,7 +97,7 @@ 自定义会话列表 TableView,需要重载会话列表页面中的 `createList` 方法后,返回你继承 `EaseChatUIKit` 中 `ConversationList` 后的类对象。关于在导航栏中实现业务逻辑,详见 `ConversationList.swift` 类。示例代码如下: -```Swift +```swift override open func createList() -> ConversationList { CustomConversationList(frame: CGRect(x: 0, y: self.search.frame.maxY+5, width: self.view.frame.width, height: self.view.frame.height-NavigationHeight-BottomBarHeight-(self.tabBarController?.tabBar.frame.height ?? 49)), style: .plain) } @@ -109,7 +109,7 @@ 1. 继承 `EaseChatUIKit` 中的 `ConversationCell` 类创建新的自定义类 `CustomConversationCell`,然后进行如下代码设置: -```Swift +```swift ComponentsRegister.shared.ConversationCell = CustomConversationCell.self ``` @@ -117,7 +117,7 @@ 如果需要复用已有逻辑再增加新逻辑,则只需重载对应方法后调用 `super.xxx`,例如: -```Swift +```swift override open func refresh(info: ConversationInfo) { super.refresh(info:info) //继续你的新逻辑即可 @@ -161,7 +161,7 @@ 默认情况下,左滑菜单项包括禁言、置顶会话和删除会话,右滑菜单包括会话已读和唤起更多菜单 `ActionSheet`。因为是枚举数组,只支持删减菜单项,不能新增。 -```Swift +```swift //Remove Appearance.conversation.swipeLeftActions.removeAll { $0 == .more } ``` @@ -174,7 +174,7 @@ 你可以利用 `Appearance.conversation.moreActions = value` 设置右滑会话后出现的 `...` 菜单项点击后的 `ActionSheet` 的菜单项。你可以添加或删减菜单项,示例代码如下: -```Swift +```swift //添加菜单项 Appearance.conversation.moreActions.append(ActionSheetItem(title: "new list item", type: .destructive, tag: "custom")) //删减菜单项 @@ -183,7 +183,7 @@ 获取该数组中某一项的点击事件,示例代码如下: -```Swift +```swift if let item = Appearance.conversation.listMoreActions.first(where: { $0.tag == "xxx" }) { item.actionClosure = { [weak self] _ in //do something diff --git a/docs/uikit/chatuikit/ios/chatuikit_custom_group_details.md b/docs/uikit/chatuikit/ios/chatuikit_custom_group_details.md index 9fc460c6..e731ad1f 100644 --- a/docs/uikit/chatuikit/ios/chatuikit_custom_group_details.md +++ b/docs/uikit/chatuikit/ios/chatuikit_custom_group_details.md @@ -14,7 +14,7 @@ 群详情页面中右上角按钮 `...` 点击后弹出 `ActionSheet` 菜单中的数据源可配项 `Appearance.contact.moreActions`。你可以增加或删除菜单项,示例代码如下: -```Swift +```swift //增加菜单项 Appearance.contact.moreActions.append(ActionSheetItem(title: "new list item", type: .destructive, tag: "contact_custom")) //删除菜单项 @@ -23,7 +23,7 @@ 获取该数组中某单个项的点击事件,示例如下所示: -```Swift +```swift if let item = Appearance.contact.moreActions.first(where: { $0.tag == "xxx" }) { item.actionClosure = { [weak self] _ in //do something @@ -40,7 +40,7 @@ 群组详情页面 Header 中按钮 `CollectionView` 中数据源可配项 `Appearance.contact.detailExtensionActionItems`,主要功能包括聊天、音视频通话等。关于事件监听,详见[设置点击右侧图片显示的群组操作](#设置点击右侧图片显示的群组操作)。首先,继承群组详情页面,然后,将继承后的群详情页面注册入 `EaseChatUIKit`,即 `ComponentsRegister.shared.GroupInfoController = MineGroupDetailViewController.self`,增加可配项,示例如下所示: -```Swift +```swift final class MineGroupDetailViewController: GroupInfoViewController { override func cleanHistoryMessages() { diff --git a/docs/uikit/chatuikit/ios/chatuikit_internationalization.md b/docs/uikit/chatuikit/ios/chatuikit_internationalization.md index 018221af..faf329be 100644 --- a/docs/uikit/chatuikit/ios/chatuikit_internationalization.md +++ b/docs/uikit/chatuikit/ios/chatuikit_internationalization.md @@ -6,7 +6,7 @@ 示例代码: -```Swift +```swift Appearance.ease_chat_language = .Chinese Appearance.ease_chat_language = .English ``` diff --git a/docs/uikit/chatuikit/ios/chatuikit_quickstart.md b/docs/uikit/chatuikit/ios/chatuikit_quickstart.md index 92396c31..399a910f 100644 --- a/docs/uikit/chatuikit/ios/chatuikit_quickstart.md +++ b/docs/uikit/chatuikit/ios/chatuikit_quickstart.md @@ -89,7 +89,7 @@ public final class YourAppUser: NSObject, EaseProfileProtocol { 1. 在控制台[关闭好友关系检查功能](/product/enable_and_configure_IM.html#好友关系检查),即无需添加好友即可聊天。 2. 调用 `init` 方法将在控制台上创建的用户的用户 ID 传入 `conversationId` 参数,向该用户发送消息。 -```Swift +```swift let vc = ComponentsRegister.shared.MessageViewController.init(conversationId: <#创建用户的id#>, chatType: .chat) //或者 push 或者 present 都可 ControllerStack.toDestination(vc: vc) diff --git a/docs/uikit/chatuikit/ios/chatuikit_theme.md b/docs/uikit/chatuikit/ios/chatuikit_theme.md index fb890f25..21b806dd 100644 --- a/docs/uikit/chatuikit/ios/chatuikit_theme.md +++ b/docs/uikit/chatuikit/ios/chatuikit_theme.md @@ -48,7 +48,7 @@ EaseChatUIKit 中的所有颜色均使用 HSLA 颜色模型定义,该模型是 通过调整 HSLA 模型的色相值,你可以实现精确的色彩控制。 -```Swift +```swift Appearance.primaryHue = 191/360.0 Appearance.secondaryHue = 210/360.0 Appearance.errorHue = 189/360.0 @@ -63,13 +63,13 @@ Theme.switchTheme(style: .custom) 1. 在 `Theme` 类中注册你的视图。 -```Swift +```swift Theme.registerSwitchThemeViews(view: self) ``` 2. 在上述视图中实现 `ThemeSwitchProtocol` 协议。 -```Swift +```swift extension YourView: ThemeSwitchProtocol { } diff --git a/docs/uikit/chatuikit/ios/ui_historic.md b/docs/uikit/chatuikit/ios/ui_historic.md index 3474d759..9c71830a 100644 --- a/docs/uikit/chatuikit/ios/ui_historic.md +++ b/docs/uikit/chatuikit/ios/ui_historic.md @@ -35,7 +35,7 @@ pod 'EaseIMKit' 需要在 `Podfile` 文件加上 `use_frameworks!` -:::notice +:::tip EaseIMKit: 对应 HyphenateChat SDK(HyphenateChat 不包含实时音视频,EaseIMKit 不包含音视频,EaseIM 依赖音视频库 EaseCallKit 后实现了音视频功能) EaseIMKit 中包含了拍照,发语音,发图片,发视频,发位置,发文件的功能,需要使用录音,摄像头,相册,地理位置的权限。需要在您项目的 info.plist 中添加对应权限。 diff --git a/docs/uikit/chatuikit/react-native/chatfeature_message.md b/docs/uikit/chatuikit/react-native/chatfeature_message.md index 721f7b2e..f215094d 100644 --- a/docs/uikit/chatuikit/react-native/chatfeature_message.md +++ b/docs/uikit/chatuikit/react-native/chatfeature_message.md @@ -184,7 +184,7 @@ export function App() { #### 自定义输入状态指示 UI -本功能使用 SDK 的透传消息实现,详见 [SDK 相关文档](/document/android/message_send_receive.html#通过透传消息实现输入指示器)。 +本功能使用 SDK 的透传消息实现,详见 [SDK 相关文档](/document/product/typing_indication.html)。 如果需要自定义正在输入组件样式,需要自定义聊天页面组件的导航栏组件,可以参考 `ConversationDetailNavigationBar` 组件。 diff --git a/docs/uikit/chatuikit/react-native/ui_conversation.md b/docs/uikit/chatuikit/react-native/ui_conversation.md index bac5c954..948ac09b 100644 --- a/docs/uikit/chatuikit/react-native/ui_conversation.md +++ b/docs/uikit/chatuikit/react-native/ui_conversation.md @@ -43,7 +43,7 @@ export default function ChatScreen(): JSX.Element { 你可以设置 `ConversationListFragment` 中的 `RenderItem` 自定义会话样式,例如自定义头像、消息未读数以及消息时间戳等。 -:::notice +:::tip 如果开启侧滑功能,则需要设置侧滑组件的宽度。 ::: diff --git a/docs/uikit/chatuikit/web/chatfeature_message.md b/docs/uikit/chatuikit/web/chatfeature_message.md index 912cadc9..34bcde22 100644 --- a/docs/uikit/chatuikit/web/chatfeature_message.md +++ b/docs/uikit/chatuikit/web/chatfeature_message.md @@ -272,5 +272,5 @@ features.chat.messageInput.typing = false; #### 自定义 -本功能使用 SDK 的透传消息实现,详见 [SDK 相关文档](/document/web/message_send_receive.html#通过透传消息实现输入指示器)。 +本功能使用 SDK 的透传消息实现,详见 [SDK 相关文档](/document/product/typing_indication.html)。 diff --git a/docs/uikit/chatuikit/web/chatuikit_conversation.md b/docs/uikit/chatuikit/web/chatuikit_conversation.md index 943226bd..44834cf9 100644 --- a/docs/uikit/chatuikit/web/chatuikit_conversation.md +++ b/docs/uikit/chatuikit/web/chatuikit_conversation.md @@ -302,8 +302,6 @@ const Conversation = () => { }; ``` -![img](/images/uikit/chatuikit/web/custom_conversation_list_action.png) - diff --git a/docs/uikit/chatuikit/web/chatuikit_login.md b/docs/uikit/chatuikit/web/chatuikit_login.md index 8a1c4f00..39270780 100644 --- a/docs/uikit/chatuikit/web/chatuikit_login.md +++ b/docs/uikit/chatuikit/web/chatuikit_login.md @@ -20,7 +20,7 @@ const App = () => { }; ``` -若要手动登录登出,你可以获取即时通讯 IM SDK connection 实例,然后[调用 SDK 的 API 进行登录登出](/document/web/overview.html#手动登录)。 +若要手动登录登出,你可以获取即时通讯 IM SDK connection 实例,然后[调用 SDK 的 API 进行登录登出](/document/web/login.html#登录方式)。 ```javascript import { useClient } from "easemob-chat-uikit";