更新云信相关代码
This commit is contained in:
parent
a297e0452f
commit
5954f51701
File diff suppressed because it is too large
Load Diff
@ -5,13 +5,15 @@
|
||||
"name": "default",
|
||||
"type": "HarmonyOS",
|
||||
"material": {
|
||||
"certpath": "/Users/gandanxiangzhao/.ohos/config/default_harmony_0p9qf45UN48IqoSyKhLtcLhTMwMj81GJRnBkhg7Axm4=.cer",
|
||||
"keyAlias": "debugKey",
|
||||
"keyPassword": "0000001AA50378305356D7D7EEDE463DB4E4B1B23C35A2615C29ED413C2E2DC30778DFE346C51C100F7D",
|
||||
"profile": "/Users/gandanxiangzhao/.ohos/config/default_harmony_0p9qf45UN48IqoSyKhLtcLhTMwMj81GJRnBkhg7Axm4=.p7b",
|
||||
"storeFile": "D:/202076work/gdxzExport.p12",
|
||||
"storePassword": "00000018C50E504AB8CC43CCB52E64D656B0131573F764452F4F1FED54B55C3ECC0010D2BD946612",
|
||||
"keyAlias": "gdxz",
|
||||
"keyPassword": "00000018EB40FA6E3F3E03EBCA9DCC01DFAB5A57692D48BC16DDF2182CD2CD08E1E583F524397C53",
|
||||
"signAlg": "SHA256withECDSA",
|
||||
"storeFile": "/Users/gandanxiangzhao/.ohos/config/default_harmony_0p9qf45UN48IqoSyKhLtcLhTMwMj81GJRnBkhg7Axm4=.p12",
|
||||
"storePassword": "0000001A29D969B0C08E79F7AC25E39AEB74F09540DED4C7BEF6ABF0671F85B598C8F911C2943D3B741C"
|
||||
"profile": "D:/202076work/profile测试Debug.p7b",
|
||||
"certpath": "D:/202076work/鸿蒙专家端测试证书.cer"
|
||||
// "profile": "D:/202076work/release鸿蒙Release.p7b",
|
||||
// "certpath": "D:/202076work/鸿蒙专家端发布证书.cer"
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -30,9 +32,9 @@
|
||||
}
|
||||
],
|
||||
"buildModeSet": [
|
||||
{
|
||||
"name": "debug"
|
||||
},
|
||||
// {
|
||||
// "name": "debug"
|
||||
// },
|
||||
{
|
||||
"name": "release"
|
||||
}
|
||||
@ -74,7 +76,35 @@
|
||||
{
|
||||
"name": "scene_single_video",
|
||||
"srcPath": "./scene_single_video"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "corekit",
|
||||
"srcPath": "./corekit"
|
||||
},
|
||||
{
|
||||
"name": "chatkit",
|
||||
"srcPath": "./chatkit"
|
||||
},
|
||||
{
|
||||
"name": "chatkit_ui",
|
||||
"srcPath": "./chatkit_ui"
|
||||
},
|
||||
{
|
||||
"name": "netease",
|
||||
"srcPath": "./features/netease"
|
||||
},
|
||||
{
|
||||
"name": "conversationkit_ui",
|
||||
"srcPath": "./conversationkit_ui"
|
||||
},
|
||||
{
|
||||
"name": "common",
|
||||
"srcPath": "./common"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "localconversationkit_ui",
|
||||
"srcPath": "./localconversationkit_ui"
|
||||
}
|
||||
]
|
||||
}
|
||||
6
chatkit/.gitignore
vendored
Normal file
6
chatkit/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/node_modules
|
||||
/oh_modules
|
||||
/.preview
|
||||
/build
|
||||
/.cxx
|
||||
/.test
|
||||
17
chatkit/BuildProfile.ets
Normal file
17
chatkit/BuildProfile.ets
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Use these variables when you tailor your ArkTS code. They must be of the const type.
|
||||
*/
|
||||
export const HAR_VERSION = '10.1.0';
|
||||
export const BUILD_MODE_NAME = 'release';
|
||||
export const DEBUG = false;
|
||||
export const TARGET_NAME = 'default';
|
||||
|
||||
/**
|
||||
* BuildProfile Class is used only for compatibility purposes.
|
||||
*/
|
||||
export default class BuildProfile {
|
||||
static readonly HAR_VERSION = HAR_VERSION;
|
||||
static readonly BUILD_MODE_NAME = BUILD_MODE_NAME;
|
||||
static readonly DEBUG = DEBUG;
|
||||
static readonly TARGET_NAME = TARGET_NAME;
|
||||
}
|
||||
78
chatkit/Index.ets
Normal file
78
chatkit/Index.ets
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
export { ChatKitClient, currentConversationChanged } from './src/main/ets/ChatKitClient'
|
||||
|
||||
export { ChatRepo } from './src/main/ets/repo/ChatRepo'
|
||||
|
||||
export { ContactRepo } from './src/main/ets/repo/ContactRepo'
|
||||
|
||||
export { TeamRepo } from './src/main/ets/repo/TeamRepo'
|
||||
|
||||
export { SettingRepo } from './src/main/ets/repo/SettingRepo'
|
||||
|
||||
export { StorageRepo } from './src/main/ets/repo/StorageRepo'
|
||||
|
||||
export { NEUserWithFriend } from './src/main/ets/model/NEUserWithFriend'
|
||||
|
||||
export { NEFriendUserCache } from './src/main/ets/NEFriendUserCache'
|
||||
|
||||
export { ConversationRepo } from './src/main/ets/repo/ConversationRepo'
|
||||
|
||||
export { LocalConversationRepo } from './src/main/ets/repo/LocalConversationRepo'
|
||||
|
||||
export { IsDiscussion } from './src/main/ets/utils/Utils'
|
||||
|
||||
export { ErrorUtils } from './src/main/ets/utils/ErrorUtils'
|
||||
|
||||
export { TeamMemberWithUser, TeamMemberResult } from './src/main/ets/model/TeamMemberWithUser'
|
||||
|
||||
export { PersonSelectParam } from './src/main/ets/model/PersonSelectParam'
|
||||
|
||||
export { ConversationSelectParam } from './src/main/ets/model/ConversationSelectParam'
|
||||
|
||||
export { ConversationSelectedParam } from './src/main/ets/model/ConversationSelectedParam'
|
||||
|
||||
export { CustomMessageUtils } from './src/main/ets/utils/CustomMessageUtils'
|
||||
|
||||
export { MergedMessageAttachment,
|
||||
MergeMessageAbstract,
|
||||
MessageUploadInfo } from './src/main/ets/model/CustomMessageAttachment'
|
||||
|
||||
export { mergedMessageNickKey,
|
||||
mergedMessageAvatarKey,
|
||||
multiForwardFileName,
|
||||
mergedMessageMaxDepth,
|
||||
mergedMessageLimitCount,
|
||||
singleMessageLimitCount,
|
||||
deleteMessagesLimitCount,
|
||||
mergedMessageCustomType,
|
||||
mergedMessageCellHeight,
|
||||
conversationSelectLimitCount,
|
||||
keyExtensionLastOptType,
|
||||
keyExtensionAtAll,
|
||||
typeExtensionAllowAll,
|
||||
typeExtensionAllowManager,
|
||||
collectionTypeOffset,
|
||||
keyReplyMsgKey
|
||||
} from './src/main/ets/constant/Constant'
|
||||
|
||||
export { TeamMemberCache } from './src/main/ets/cache/TeamMemberCache'
|
||||
|
||||
export { AitModel, accountAll, aitKey, getAitModelFromJson, YxAitMsg } from './src/main/ets/model/ait/AitModel'
|
||||
|
||||
export { AitMessage } from './src/main/ets/model/ait/AitMessage'
|
||||
|
||||
export { AitSegment } from './src/main/ets/model/ait/AitSegment'
|
||||
|
||||
export { AitAllPermission } from './src/main/ets/model/ait/AitAllPermission'
|
||||
|
||||
export { KitLogger as Logger } from './src/main/ets/logger/AppLogger'
|
||||
|
||||
export { IMKitConfigCenter } from './src/main/ets/IMKitConfigCenter'
|
||||
|
||||
export { TeamSettingParam } from './src/main/ets/model/TeamSettingParam'
|
||||
31
chatkit/build-profile.json5
Normal file
31
chatkit/build-profile.json5
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"apiType": "stageMode",
|
||||
"buildOption": {
|
||||
},
|
||||
"buildOptionSet": [
|
||||
{
|
||||
"name": "release",
|
||||
"arkOptions": {
|
||||
"obfuscation": {
|
||||
"ruleOptions": {
|
||||
"enable": false,
|
||||
"files": [
|
||||
"./obfuscation-rules.txt"
|
||||
]
|
||||
},
|
||||
"consumerFiles": [
|
||||
"./consumer-rules.txt"
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
"targets": [
|
||||
{
|
||||
"name": "default"
|
||||
},
|
||||
{
|
||||
"name": "ohosTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
0
chatkit/consumer-rules.txt
Normal file
0
chatkit/consumer-rules.txt
Normal file
13
chatkit/hvigorfile.ts
Normal file
13
chatkit/hvigorfile.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { harTasks } from '@ohos/hvigor-ohos-plugin';
|
||||
|
||||
export default {
|
||||
system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
|
||||
plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
|
||||
}
|
||||
23
chatkit/obfuscation-rules.txt
Normal file
23
chatkit/obfuscation-rules.txt
Normal file
@ -0,0 +1,23 @@
|
||||
# Define project specific obfuscation rules here.
|
||||
# You can include the obfuscation configuration files in the current module's build-profile.json5.
|
||||
#
|
||||
# For more details, see
|
||||
# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5
|
||||
|
||||
# Obfuscation options:
|
||||
# -disable-obfuscation: disable all obfuscations
|
||||
# -enable-property-obfuscation: obfuscate the property names
|
||||
# -enable-toplevel-obfuscation: obfuscate the names in the global scope
|
||||
# -compact: remove unnecessary blank spaces and all line feeds
|
||||
# -remove-log: remove all console.* statements
|
||||
# -print-namecache: print the name cache that contains the mapping from the old names to new names
|
||||
# -apply-namecache: reuse the given cache file
|
||||
|
||||
# Keep options:
|
||||
# -keep-property-name: specifies property names that you want to keep
|
||||
# -keep-global-name: specifies names that you want to keep in the global scope
|
||||
|
||||
-enable-property-obfuscation
|
||||
-enable-toplevel-obfuscation
|
||||
-enable-filename-obfuscation
|
||||
-enable-export-obfuscation
|
||||
132
chatkit/oh-package-lock.json5
Normal file
132
chatkit/oh-package-lock.json5
Normal file
@ -0,0 +1,132 @@
|
||||
{
|
||||
"meta": {
|
||||
"stableOrder": true
|
||||
},
|
||||
"lockfileVersion": 3,
|
||||
"ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
|
||||
"specifiers": {
|
||||
"@nimkit/corekit@../corekit": "@nimkit/corekit@../corekit",
|
||||
"@nimsdk/base@../oh_modules/.ohpm/@nimsdk+conversation@10.9.10/oh_modules/@nimsdk/conversation/libs/base.har": "@nimsdk/base@../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har",
|
||||
"@nimsdk/base@../oh_modules/.ohpm/@nimsdk+friend@10.9.10/oh_modules/@nimsdk/friend/libs/base.har": "@nimsdk/base@../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har",
|
||||
"@nimsdk/base@../oh_modules/.ohpm/@nimsdk+message@10.9.10/oh_modules/@nimsdk/message/libs/base.har": "@nimsdk/base@../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har",
|
||||
"@nimsdk/base@../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har": "@nimsdk/base@../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har",
|
||||
"@nimsdk/base@../oh_modules/.ohpm/@nimsdk+team@10.9.10/oh_modules/@nimsdk/team/libs/base.har": "@nimsdk/base@../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har",
|
||||
"@nimsdk/base@../oh_modules/.ohpm/@nimsdk+user@10.9.10/oh_modules/@nimsdk/user/libs/base.har": "@nimsdk/base@../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har",
|
||||
"@nimsdk/base@10.9.10": "@nimsdk/base@../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har",
|
||||
"@nimsdk/conversation@10.9.10": "@nimsdk/conversation@10.9.10",
|
||||
"@nimsdk/friend@10.9.10": "@nimsdk/friend@10.9.10",
|
||||
"@nimsdk/message@10.9.10": "@nimsdk/message@10.9.10",
|
||||
"@nimsdk/nim@10.9.10": "@nimsdk/nim@10.9.10",
|
||||
"@nimsdk/team@10.9.10": "@nimsdk/team@10.9.10",
|
||||
"@nimsdk/user@10.9.10": "@nimsdk/user@10.9.10",
|
||||
"@nimsdk/vendor@1.0.0": "@nimsdk/vendor@1.0.0",
|
||||
"class-transformer@^0.5.1": "class-transformer@0.5.1",
|
||||
"reflect-metadata@^0.1.13": "reflect-metadata@0.2.1"
|
||||
},
|
||||
"packages": {
|
||||
"@nimkit/corekit@../corekit": {
|
||||
"name": "@nimkit/corekit",
|
||||
"version": "1.1.0",
|
||||
"resolved": "../corekit",
|
||||
"registryType": "local"
|
||||
},
|
||||
"@nimsdk/base@../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har": {
|
||||
"name": "@nimsdk/base",
|
||||
"version": "10.9.10",
|
||||
"resolved": "../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har",
|
||||
"registryType": "local",
|
||||
"dependencies": {
|
||||
"@nimsdk/vendor": "1.0.0"
|
||||
}
|
||||
},
|
||||
"@nimsdk/conversation@10.9.10": {
|
||||
"name": "@nimsdk/conversation",
|
||||
"version": "10.9.10",
|
||||
"integrity": "sha512-1HLvs19/GJAHeIOCN0OiKlowkg6dzZwvZK0Jqu7tAcYGcLl4+G/Z3pwsGHhv+E2Tzs8FHZCqbESMgSh+LNyt/g==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/conversation/-/conversation-10.9.10.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@nimsdk/base": "file:./libs/base.har",
|
||||
"@nimsdk/vendor": "1.0.0"
|
||||
}
|
||||
},
|
||||
"@nimsdk/friend@10.9.10": {
|
||||
"name": "@nimsdk/friend",
|
||||
"version": "10.9.10",
|
||||
"integrity": "sha512-JVACpT8xqLLaN8D26YHmwfsS1dHFQvBnP3Jyk9El89P2trn/2ZFLvnQjxzyBDsqJRUtNFfIrN+TK7Idmud4ACQ==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/friend/-/friend-10.9.10.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@nimsdk/base": "file:./libs/base.har",
|
||||
"@nimsdk/vendor": "1.0.0"
|
||||
}
|
||||
},
|
||||
"@nimsdk/message@10.9.10": {
|
||||
"name": "@nimsdk/message",
|
||||
"version": "10.9.10",
|
||||
"integrity": "sha512-f59rWiM4SjhhxNftRUt9vg7lIwkGycV/aL8J3omH+Te4SMbUGolwDGErDr7adtZ3tDUThtxxgU8n5tD28TBRtA==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/message/-/message-10.9.10.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@nimsdk/base": "file:./libs/base.har",
|
||||
"@nimsdk/vendor": "1.0.0"
|
||||
}
|
||||
},
|
||||
"@nimsdk/nim@10.9.10": {
|
||||
"name": "@nimsdk/nim",
|
||||
"version": "10.9.10",
|
||||
"integrity": "sha512-WpT8vBTld92ExtH30Ffsm+xq6BW6/UFj8SuhJrcQaZY3AYf9sg+d+euqx/dFzjZin5cWRxd/yoodBiVcGfsM4w==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/nim/-/nim-10.9.10.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@nimsdk/base": "file:./libs/base.har",
|
||||
"@nimsdk/vendor": "1.0.0"
|
||||
}
|
||||
},
|
||||
"@nimsdk/team@10.9.10": {
|
||||
"name": "@nimsdk/team",
|
||||
"version": "10.9.10",
|
||||
"integrity": "sha512-T4YSN395VXQr1TDX2B24DmGYuvUgUqE7wndbleR980wEyki9IfhC2VxxJ1yajhxVlVkfmuBjCB/eKWL0zLzu5A==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/team/-/team-10.9.10.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@nimsdk/base": "file:./libs/base.har",
|
||||
"@nimsdk/vendor": "1.0.0"
|
||||
}
|
||||
},
|
||||
"@nimsdk/user@10.9.10": {
|
||||
"name": "@nimsdk/user",
|
||||
"version": "10.9.10",
|
||||
"integrity": "sha512-KyWVDDPbymj3qoC8Y0mB8umgvLg89Y2cB02tM35oSG8IW95C936v5ogip2Jk7qAfabXxI/XTyy5wQoW1z950JA==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/user/-/user-10.9.10.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@nimsdk/base": "file:./libs/base.har",
|
||||
"@nimsdk/vendor": "1.0.0"
|
||||
}
|
||||
},
|
||||
"@nimsdk/vendor@1.0.0": {
|
||||
"name": "@nimsdk/vendor",
|
||||
"version": "1.0.0",
|
||||
"integrity": "sha512-q49MJM6PfucNs8jvLP56a2etyqRfZCeJaMa1BT9vO4sIgwt15bin+hpUWZ1qkflBs9YkDb2nMIX5O8zt556muw==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/vendor/-/vendor-1.0.0.har",
|
||||
"registryType": "ohpm"
|
||||
},
|
||||
"class-transformer@0.5.1": {
|
||||
"name": "class-transformer",
|
||||
"version": "0.5.1",
|
||||
"integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/class-transformer/-/class-transformer-0.5.1.tgz",
|
||||
"shasum": "24147d5dffd2a6cea930a3250a677addf96ab336",
|
||||
"registryType": "ohpm"
|
||||
},
|
||||
"reflect-metadata@0.2.1": {
|
||||
"name": "reflect-metadata",
|
||||
"version": "0.2.1",
|
||||
"integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/reflect-metadata/-/reflect-metadata-0.2.1.tgz",
|
||||
"shasum": "8d5513c0f5ef2b4b9c3865287f3c0940c1f67f74",
|
||||
"registryType": "ohpm"
|
||||
}
|
||||
}
|
||||
}
|
||||
21
chatkit/oh-package.json5
Normal file
21
chatkit/oh-package.json5
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "@nimkit/chatkit",
|
||||
"version": "10.1.0",
|
||||
"description": "Please describe the basic information.",
|
||||
"main": "Index.ets",
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@nimsdk/conversation": "10.9.10",
|
||||
"@nimsdk/message": "10.9.10",
|
||||
"@nimsdk/team": "10.9.10",
|
||||
"@nimsdk/user": "10.9.10",
|
||||
"@nimsdk/friend": "10.9.10",
|
||||
"@nimsdk/nim": "10.9.10",
|
||||
"@nimsdk/base": "10.9.10",
|
||||
"@nimkit/corekit": "file:../corekit",
|
||||
"class-transformer": "^0.5.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
// 用于嵌套对象@Type反射
|
||||
}
|
||||
}
|
||||
185
chatkit/src/main/ets/ChatKitClient.ets
Normal file
185
chatkit/src/main/ets/ChatKitClient.ets
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { CoreKitClient } from '@nimkit/corekit';
|
||||
import {
|
||||
IM_SDK_VERSION,
|
||||
NIM,
|
||||
NIMInterface,
|
||||
V2NIMConnectStatus,
|
||||
V2NIMDataSyncState,
|
||||
V2NIMDataSyncType,
|
||||
V2NIMEnableServiceType,
|
||||
V2NIMError,
|
||||
V2NIMLoginStatus,
|
||||
V2NIMMessageRevokeNotification
|
||||
} from '@nimsdk/base';
|
||||
import { ContactRepo } from '../../../Index';
|
||||
import { KitLogger } from './logger/AppLogger';
|
||||
import { LoggerKitImpl } from './logger/LoggerKitImpl';
|
||||
import { ChatRepo } from './repo/ChatRepo';
|
||||
import { saveLocalRevokeMessageFormOther } from './utils/MessageUtils';
|
||||
|
||||
export const currentConversationChanged: string = 'CurrentConversationChanged'
|
||||
|
||||
export class ChatKitClient {
|
||||
declare static nim: NIMInterface
|
||||
static haveSyncedConversation: boolean = false
|
||||
//是否主动离开群
|
||||
static selfLeaveTeam = false
|
||||
static hasInitListener = false
|
||||
static currentConversationId: string = ''
|
||||
static networkAvailable: boolean = true
|
||||
static logger: KitLogger | undefined = undefined
|
||||
|
||||
static init(nimSdk: NIMInterface, appKey: string, disableLog?: boolean) {
|
||||
ChatKitClient.nim = nimSdk
|
||||
if (disableLog !== true) {
|
||||
if (nimSdk instanceof NIM) {
|
||||
let logger = new LoggerKitImpl(nimSdk.context.cacheDir)
|
||||
ChatKitClient.logger = new KitLogger(logger)
|
||||
}
|
||||
}
|
||||
ChatKitClient.haveSyncedConversation = false
|
||||
ChatKitClient.initListener()
|
||||
CoreKitClient.init({
|
||||
appKey: appKey,
|
||||
imVersion: IM_SDK_VERSION,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 长连接是否断开
|
||||
* 可用于判断网络是否断开
|
||||
*/
|
||||
static connectBroken() {
|
||||
return ChatKitClient.nim.loginService.getConnectStatus() !== V2NIMConnectStatus.V2NIM_CONNECT_STATUS_CONNECTED
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前会话id
|
||||
* @param conversationId 当前会话id
|
||||
*/
|
||||
static setCurrentConversationId(conversationId: string) {
|
||||
ChatKitClient.currentConversationId = conversationId
|
||||
getContext().eventHub.emit(currentConversationChanged, conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除当前会话id
|
||||
*/
|
||||
static clearCurrentConversationId() {
|
||||
ChatKitClient.currentConversationId = ''
|
||||
getContext().eventHub.emit(currentConversationChanged, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前会话id
|
||||
* @returns 当前会话id
|
||||
*/
|
||||
static getCurrentConversationId(): string {
|
||||
return ChatKitClient.currentConversationId
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否登录
|
||||
* @returns
|
||||
*/
|
||||
static hasLogin(): boolean {
|
||||
return ChatKitClient.nim != null && ChatKitClient.nim.loginService.getLoginUser() != null
|
||||
}
|
||||
|
||||
static getLoginUserId(): string {
|
||||
return ChatKitClient.nim.loginService.getLoginUser()
|
||||
}
|
||||
|
||||
/**
|
||||
* IM 主数据是否同步完成
|
||||
* @returns
|
||||
*/
|
||||
static isMainDataSynced(): boolean {
|
||||
let dataSync = ChatKitClient.nim.loginService.getDataSync()
|
||||
if (dataSync != null) {
|
||||
for (const item of dataSync) {
|
||||
if (item.type === V2NIMDataSyncType.V2NIM_DATA_SYNC_TYPE_MAIN &&
|
||||
item.state === V2NIMDataSyncState.V2NIM_DATA_SYNC_STATE_COMPLETED) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static isLocalConversation(): boolean {
|
||||
return ChatKitClient.nim.isServiceEnable(V2NIMEnableServiceType.LOCAL_CONVERSATION)
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待登录后执行
|
||||
*/
|
||||
static runAfterLoggedIn(fn: Function) {
|
||||
if (ChatKitClient.nim.loginService?.getLoginStatus() === V2NIMLoginStatus.V2NIM_LOGIN_STATUS_LOGINED) {
|
||||
fn()
|
||||
} else {
|
||||
const onLoginStatusFunc = (status: V2NIMLoginStatus) => {
|
||||
if (status === V2NIMLoginStatus.V2NIM_LOGIN_STATUS_LOGINED) {
|
||||
fn()
|
||||
ChatKitClient.nim.loginService.off('onLoginStatus', onLoginStatusFunc)
|
||||
}
|
||||
}
|
||||
ChatKitClient.nim.loginService.on('onLoginStatus', onLoginStatusFunc)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 销毁 清理监听
|
||||
*/
|
||||
static onDestroy() {
|
||||
ChatKitClient.hasInitListener = false
|
||||
ChatKitClient.haveSyncedConversation = false
|
||||
ChatKitClient.selfLeaveTeam = false
|
||||
ChatKitClient.currentConversationId = ''
|
||||
ChatRepo.offRevokeMessage(ChatKitClient.onRevokeFun)
|
||||
ChatKitClient.nim.conversationService?.off('onSyncFinished', ChatKitClient.onSyncFinishedFun)
|
||||
ChatKitClient.nim.loginService.off('onDataSync', ChatKitClient.dataSyncFun)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化监听
|
||||
*/
|
||||
private static initListener() {
|
||||
if (ChatKitClient.hasInitListener) {
|
||||
return
|
||||
}
|
||||
console.info("netease ChatKitClient initListener ");
|
||||
// 数据同步监听
|
||||
ChatKitClient.nim.conversationService?.on('onSyncFinished', ChatKitClient.onSyncFinishedFun)
|
||||
ChatKitClient.nim.loginService.on('onDataSync', ChatKitClient.dataSyncFun)
|
||||
// 消息撤回监听,消息撤回后,会收到通知
|
||||
ChatRepo.onRevokeMessage(ChatKitClient.onRevokeFun)
|
||||
ChatKitClient.hasInitListener = true
|
||||
|
||||
}
|
||||
|
||||
private static onSyncFinishedFun = () => {
|
||||
console.debug(`Performance Test onSyncFinishedFun`)
|
||||
ChatKitClient.haveSyncedConversation = true
|
||||
}
|
||||
private static dataSyncFun = (type: V2NIMDataSyncType, state: V2NIMDataSyncState, error?: V2NIMError) => {
|
||||
if (state === V2NIMDataSyncState.V2NIM_DATA_SYNC_STATE_COMPLETED) {
|
||||
ContactRepo.getFriendList()
|
||||
}
|
||||
}
|
||||
private static onRevokeFun = (messages: V2NIMMessageRevokeNotification[]) => {
|
||||
messages.forEach((msg, index, messages) => {
|
||||
if (msg.messageRefer.conversationId !== ChatKitClient.currentConversationId) {
|
||||
saveLocalRevokeMessageFormOther(msg.messageRefer.conversationId, msg, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
13
chatkit/src/main/ets/IMKitConfigCenter.ets
Normal file
13
chatkit/src/main/ets/IMKitConfigCenter.ets
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
@ObservedV2
|
||||
export class IMKitConfigCenter {
|
||||
// 是否使用本地会话列表
|
||||
@Trace static enableLocalConversation: boolean = true;
|
||||
}
|
||||
|
||||
194
chatkit/src/main/ets/NEFriendUserCache.ets
Normal file
194
chatkit/src/main/ets/NEFriendUserCache.ets
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { ChatKitClient } from './ChatKitClient';
|
||||
import { NEUserWithFriend } from './model/NEUserWithFriend';
|
||||
import { V2NIMFriend, V2NIMUser } from '@nimsdk/base';
|
||||
import { ContactRepo } from './repo/ContactRepo';
|
||||
|
||||
interface updateFriendInfoParams {
|
||||
user?: V2NIMUser;
|
||||
friend?: V2NIMFriend;
|
||||
friendUser?: NEUserWithFriend;
|
||||
}
|
||||
|
||||
/// 好友信息缓存,只缓存好友
|
||||
@ObservedV2
|
||||
export class NEFriendUserCache {
|
||||
@Trace public static mineUserCache?: NEUserWithFriend
|
||||
// 黑名单账号集合
|
||||
public blockAccountList: string[] = []
|
||||
// 好友列表,包括黑名单中好友
|
||||
public friendCache: Map<string, NEUserWithFriend> = new Map<string, NEUserWithFriend>()
|
||||
|
||||
private constructor() {
|
||||
ContactRepo.addListener()
|
||||
ContactRepo.getFriendList()
|
||||
ContactRepo.getUserListFromCloud([ChatKitClient.getLoginUserId()])
|
||||
ContactRepo.getBlockList()
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
if (!AppStorage.get<NEFriendUserCache>(NEFriendUserCache.name)) {
|
||||
let instance = new NEFriendUserCache()
|
||||
AppStorage.setOrCreate(NEFriendUserCache.name, instance);
|
||||
}
|
||||
return AppStorage.get<NEFriendUserCache>(NEFriendUserCache.name) as NEFriendUserCache;
|
||||
}
|
||||
|
||||
/// 是否是自己
|
||||
public static isMe(accountId: string): Boolean {
|
||||
return NEFriendUserCache.mineUserCache?.user?.accountId === accountId
|
||||
}
|
||||
|
||||
/// 好友缓存是否为空
|
||||
public isEmpty(): Boolean {
|
||||
if (this.friendCache == undefined || this.friendCache.size === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/// 是否是好友
|
||||
public isFriend(accountId: string): Boolean {
|
||||
return this.friendCache.has(accountId) && !this.blockAccountList.includes(accountId)
|
||||
}
|
||||
|
||||
/// 添加(更新)好友信息
|
||||
public updateFriendInfo(params: updateFriendInfoParams) {
|
||||
let accountId = ""
|
||||
if (params.user) {
|
||||
accountId = params.user.accountId
|
||||
let friendUser = this.friendCache.get(accountId)
|
||||
if (friendUser) {
|
||||
friendUser.user = params.user
|
||||
} else {
|
||||
this.friendCache.set(accountId, new NEUserWithFriend({
|
||||
user: params.user
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if (params.friend) {
|
||||
accountId = params.friend.accountId
|
||||
let friendUser = this.friendCache.get(accountId)
|
||||
if (friendUser) {
|
||||
friendUser.friend = params.friend
|
||||
friendUser.user = params.friend.userProfile
|
||||
} else {
|
||||
this.friendCache.set(accountId, new NEUserWithFriend({
|
||||
friend: params.friend
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if (params.friendUser && params.friendUser.user?.accountId) {
|
||||
accountId = params.friendUser.user.accountId
|
||||
this.friendCache.set(accountId, params.friendUser)
|
||||
}
|
||||
}
|
||||
|
||||
/// 使用好友列表初始化缓存
|
||||
public loadFriendList(friends: V2NIMFriend[]) {
|
||||
friends.forEach((friend: V2NIMFriend) => {
|
||||
this.updateFriendInfo({
|
||||
friend: friend
|
||||
})
|
||||
})
|
||||
|
||||
ContactRepo.listener.emit('loadFriendCache')
|
||||
}
|
||||
|
||||
/// 获取缓存的好友信息
|
||||
public getFriendById(accountId: string) {
|
||||
return this.friendCache.get(accountId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过用户ID列表获取用户信息
|
||||
* @param accIds
|
||||
* @returns
|
||||
*/
|
||||
public getFriendsByIds(accIds: string[]) {
|
||||
let result: NEUserWithFriend[] = []
|
||||
accIds.forEach((accId) => {
|
||||
let user = this.friendCache.get(accId)
|
||||
if (user) {
|
||||
result.push(user)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
/// 获取缓存的好友信息列表,包含黑名单中的好友
|
||||
public getFriendList() {
|
||||
return Array.from(this.friendCache.values())
|
||||
}
|
||||
|
||||
/// 获取缓存的好友信息列表,不包含黑名单中的好友
|
||||
public getFriendListNotInBlocklist() {
|
||||
let friends: Map<string, NEUserWithFriend> = new Map<string, NEUserWithFriend>()
|
||||
this.friendCache.forEach((value, key) => {
|
||||
if (!this.blockAccountList.includes(key)) {
|
||||
friends.set(key, value)
|
||||
}
|
||||
})
|
||||
|
||||
return Array.from(friends.values())
|
||||
}
|
||||
|
||||
/// 获取缓存的黑名单列表
|
||||
public getBlocklist(): string[] {
|
||||
return this.blockAccountList
|
||||
}
|
||||
|
||||
/// 删除好友信息缓存
|
||||
public removeFriendInfo(accountId: string) {
|
||||
this.friendCache.delete(accountId)
|
||||
ContactRepo.listener.emit('removeFriendInfo', accountId)
|
||||
}
|
||||
|
||||
/// 删除所有好友信息缓存
|
||||
public removeAllFriendInfo() {
|
||||
this.friendCache.clear()
|
||||
this.blockAccountList = []
|
||||
ContactRepo.removeListener()
|
||||
AppStorage.delete(NEFriendUserCache.name)
|
||||
}
|
||||
|
||||
/// 初始化黑名单
|
||||
public initBlockAccountSet(blockList: string[]) {
|
||||
this.blockAccountList = blockList
|
||||
}
|
||||
|
||||
/// 是否是黑名单账号
|
||||
public isBlockAccount(accountId: string) {
|
||||
return this.blockAccountList.includes(accountId)
|
||||
}
|
||||
|
||||
/// 更新黑名单
|
||||
public addBlockAccount(accountId: string) {
|
||||
if (!this.blockAccountList.includes(accountId)) {
|
||||
this.blockAccountList.push(accountId)
|
||||
ContactRepo.listener.emit('addBlockAccount', accountId)
|
||||
}
|
||||
}
|
||||
|
||||
/// 移除黑名单账号
|
||||
public removeBlockAccount(accountId: string) {
|
||||
if (this.blockAccountList.includes(accountId)) {
|
||||
for (let index = 0; index < this.blockAccountList.length; index++) {
|
||||
if (this.blockAccountList[index] === accountId) {
|
||||
this.blockAccountList.splice(index, 1)
|
||||
ContactRepo.listener.emit('removeBlockAccount', accountId)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
379
chatkit/src/main/ets/cache/TeamMemberCache.ets
vendored
Normal file
379
chatkit/src/main/ets/cache/TeamMemberCache.ets
vendored
Normal file
@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import {
|
||||
V2NIMError,
|
||||
V2NIMFriend,
|
||||
V2NIMQueryDirection,
|
||||
V2NIMTeam,
|
||||
V2NIMTeamMember,
|
||||
V2NIMTeamMemberRole,
|
||||
V2NIMTeamType,
|
||||
V2NIMUser
|
||||
} from '@nimsdk/base';
|
||||
import { ChatKitClient } from '../ChatKitClient';
|
||||
import { TeamMemberWithUser } from '../model/TeamMemberWithUser';
|
||||
import { TeamRepo } from '../repo/TeamRepo';
|
||||
|
||||
@ObservedV2
|
||||
export class TeamMemberCache {
|
||||
static logTag = 'TeamMemberCache'
|
||||
//全成员列表
|
||||
@Trace membersMap: Map<string, TeamMemberWithUser> = new Map()
|
||||
//自己的群身份
|
||||
@Trace mineTeamMember: V2NIMTeamMember | undefined = undefined
|
||||
//群组Id
|
||||
teamId: string = ''
|
||||
//群组
|
||||
team?: V2NIMTeam = undefined
|
||||
//下次拉去的token
|
||||
nextToken = ''
|
||||
//是否拉完了
|
||||
isFinish = false
|
||||
private static instance: TeamMemberCache
|
||||
|
||||
private constructor() {
|
||||
|
||||
}
|
||||
|
||||
public static getInstance(): TeamMemberCache {
|
||||
if (!TeamMemberCache.instance) {
|
||||
TeamMemberCache.instance = new TeamMemberCache()
|
||||
}
|
||||
return TeamMemberCache.instance
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化信息
|
||||
* @param teamId
|
||||
* @param team
|
||||
*/
|
||||
intTeamInfo(teamId: string, team?: V2NIMTeam) {
|
||||
if (this.teamId !== teamId) {
|
||||
this.membersMap.clear()
|
||||
this.mineTeamMember = undefined
|
||||
this.teamId = teamId
|
||||
this.team = team
|
||||
this.isFinish = false
|
||||
this.initMemberChangeListener()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为空
|
||||
* @returns
|
||||
*/
|
||||
isEmpty(): boolean {
|
||||
return this.membersMap.size <= 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要拉取
|
||||
* @returns
|
||||
*/
|
||||
needFetchMember(): boolean {
|
||||
if (this.membersMap.size <= 0) {
|
||||
return true
|
||||
}
|
||||
if (this.membersMap.size < 100 && this.isFinish === false) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 好友信息变更
|
||||
* @param friend
|
||||
*/
|
||||
onFriendInfoChanged(friend: V2NIMFriend) {
|
||||
let member = this.membersMap.get(friend.accountId)
|
||||
if (member) {
|
||||
member.friendInfo = friend
|
||||
this.membersMap.set(friend.accountId, member)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 群乘以信息变更
|
||||
* @param members
|
||||
*/
|
||||
onTeamMemberInfoUpdated(members: V2NIMTeamMember[]) {
|
||||
if (members[0].teamId === this.teamId) {
|
||||
members.forEach((m) => {
|
||||
let member = this.membersMap.get(m.accountId)
|
||||
if (member) {
|
||||
member.teamMember = m
|
||||
this.membersMap.set(m.accountId, member)
|
||||
if (m.accountId === ChatKitClient.getLoginUserId()) {
|
||||
this.mineTeamMember = m
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 离开群
|
||||
* @param members
|
||||
*/
|
||||
onTeamMemberLeft(members: V2NIMTeamMember[]) {
|
||||
if (members[0].teamId === this.teamId) {
|
||||
let removeIds: string[] = members.map(member => member.accountId)
|
||||
removeIds.forEach((e) => {
|
||||
this.membersMap.delete(e)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢人
|
||||
* @param accId
|
||||
* @param members
|
||||
*/
|
||||
onTeamMemberKicked(accId: string, members: V2NIMTeamMember[]) {
|
||||
if (members[0].teamId === this.teamId) {
|
||||
let removeIds: string[] = members.map(member => member.accountId)
|
||||
removeIds.forEach((e) => {
|
||||
this.membersMap.delete(e)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 成员加入
|
||||
* @param members
|
||||
*/
|
||||
onTeamMemberJoined(members: V2NIMTeamMember[]) {
|
||||
if (members[0].teamId === this.teamId) {
|
||||
let accIds = members.map(m => m.accountId)
|
||||
TeamRepo.getTeamMembersByIds(this.teamId, V2NIMTeamType.V2NIM_TEAM_TYPE_NORMAL,
|
||||
accIds).then((newMembers) => {
|
||||
newMembers.forEach((m) => {
|
||||
this.membersMap.set(m.getAccId(), m)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有成员的accId
|
||||
* @returns
|
||||
*/
|
||||
getAllMemberAccounts(): string[] {
|
||||
return Array.from(this.membersMap.keys())
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的成员列表
|
||||
* @returns
|
||||
*/
|
||||
getAllMembers(): TeamMemberWithUser[] {
|
||||
return Array.from(this.membersMap.values()).sort((a, b) => this.sortTeamMember(a, b))
|
||||
}
|
||||
|
||||
// 群更新
|
||||
onTeamInfoUpdated = (team: V2NIMTeam) => {
|
||||
if (team.teamId === this.teamId) {
|
||||
this.team = team
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 刷新群信息
|
||||
*/
|
||||
onSyncFinished = () => {
|
||||
if (this.teamId) {
|
||||
TeamRepo.getTeamInfo(this.teamId, V2NIMTeamType.V2NIM_TEAM_TYPE_NORMAL)
|
||||
.then((team) => {
|
||||
this.team = team
|
||||
})
|
||||
TeamRepo.getTeamMembersByIds(
|
||||
this.teamId, V2NIMTeamType.V2NIM_TEAM_TYPE_NORMAL, [ChatKitClient.getLoginUserId()]
|
||||
)
|
||||
.then((members: TeamMemberWithUser[]) => {
|
||||
if (members.length > 0) {
|
||||
this.mineTeamMember = members[0].teamMember
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户信息变更
|
||||
* @param users
|
||||
*/
|
||||
onUserProfileChanged(users: V2NIMUser[]) {
|
||||
users.forEach((user) => {
|
||||
let member = this.membersMap.get(user.accountId)
|
||||
if (member) {
|
||||
member.userInfo = user
|
||||
this.membersMap.set(user.accountId, member)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 初始化成员信息变化回调
|
||||
*/
|
||||
initMemberChangeListener() {
|
||||
//用户信息变更
|
||||
ChatKitClient.nim.userService?.on('onUserProfileChanged', this.onUserProfileChanged.bind(this))
|
||||
//好友信息变更
|
||||
ChatKitClient.nim.friendService?.on('onFriendInfoChanged', this.onFriendInfoChanged.bind(this))
|
||||
//群成员信息变更
|
||||
ChatKitClient.nim.teamService?.on('onTeamMemberInfoUpdated', this.onTeamMemberInfoUpdated.bind(this))
|
||||
ChatKitClient.nim.teamService?.on('onTeamMemberLeft', this.onTeamMemberLeft.bind(this))
|
||||
ChatKitClient.nim.teamService?.on('onTeamMemberKicked', this.onTeamMemberKicked.bind(this))
|
||||
ChatKitClient.nim.teamService?.on('onTeamMemberJoined', this.onTeamMemberJoined.bind(this))
|
||||
//群信息变更
|
||||
ChatKitClient.nim.teamService?.on('onTeamInfoUpdated', this.onTeamInfoUpdated.bind(this))
|
||||
|
||||
//断网
|
||||
ChatKitClient.nim.teamService?.on('onSyncFinished', this.onSyncFinished.bind(this))
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化群信息
|
||||
*/
|
||||
async getTeam(): Promise<V2NIMTeam | undefined> {
|
||||
if (!this.team) {
|
||||
this.team = await TeamRepo.getTeamInfo(this.teamId, V2NIMTeamType.V2NIM_TEAM_TYPE_NORMAL)
|
||||
}
|
||||
return this.team
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取全量成员
|
||||
*/
|
||||
async loadAllTeamMember() {
|
||||
if (this.team) {
|
||||
while (!this.isFinish) {
|
||||
await this.getMemberList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getMoreMemberList() {
|
||||
if (!this.isFinish) {
|
||||
await this.getMemberList()
|
||||
}
|
||||
}
|
||||
|
||||
async getMemberList() {
|
||||
let result = await TeamRepo.getTeamMembers(this.teamId,
|
||||
V2NIMTeamType.V2NIM_TEAM_TYPE_NORMAL, {
|
||||
nextToken: this.nextToken,
|
||||
direction: V2NIMQueryDirection.V2NIM_QUERY_DIRECTION_ASC
|
||||
}).catch((e: V2NIMError) => {
|
||||
console.debug(`${TeamMemberCache.logTag} getTeamMembers error code = ${e.code}`)
|
||||
})
|
||||
if (result) {
|
||||
this.isFinish = result.finished
|
||||
this.nextToken = result.nextToken
|
||||
result.memberList.forEach((e) => {
|
||||
this.membersMap.set(e.getAccId(), e)
|
||||
})
|
||||
let mineMember = result.memberList.find(member => member.getAccId() === ChatKitClient.getLoginUserId())
|
||||
if (mineMember) {
|
||||
this.mineTeamMember = mineMember.teamMember
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//群成员排序
|
||||
sortTeamMember(a: TeamMemberWithUser, b: TeamMemberWithUser): number {
|
||||
if (a.teamMember.memberRole === b.teamMember.memberRole) {
|
||||
return a.teamMember.joinTime - b.teamMember.joinTime
|
||||
}
|
||||
if (a.teamMember.memberRole === V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_OWNER) {
|
||||
return -1
|
||||
}
|
||||
if (b.teamMember.memberRole === V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_OWNER) {
|
||||
return 1
|
||||
}
|
||||
if (a.teamMember.memberRole === V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_MANAGER) {
|
||||
return -1
|
||||
}
|
||||
if (b.teamMember.memberRole === V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_MANAGER) {
|
||||
return 1
|
||||
}
|
||||
return a.teamMember.joinTime - b.teamMember.joinTime
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自己的成员信息
|
||||
* @returns
|
||||
*/
|
||||
async getMineMember(): Promise<V2NIMTeamMember | undefined> {
|
||||
if (!this.mineTeamMember) {
|
||||
let mineMember = (await TeamRepo.getTeamMembersByIds(
|
||||
this.teamId, V2NIMTeamType.V2NIM_TEAM_TYPE_NORMAL, [ChatKitClient.getLoginUserId()]
|
||||
)
|
||||
).find(m => m.getAccId() === ChatKitClient.getLoginUserId())
|
||||
if (mineMember) {
|
||||
this.membersMap.set(mineMember.getAccId(), mineMember)
|
||||
this.mineTeamMember = mineMember.teamMember
|
||||
}
|
||||
}
|
||||
return this.mineTeamMember
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据accId 获取具体的成员
|
||||
* @param account
|
||||
* @returns
|
||||
*/
|
||||
async getMemberById(account: string): Promise<TeamMemberWithUser | undefined> {
|
||||
let member = this.membersMap.get(account)
|
||||
if (member) {
|
||||
return member
|
||||
} else {
|
||||
let result = (await TeamRepo.getTeamMembersByIds(
|
||||
this.teamId, V2NIMTeamType.V2NIM_TEAM_TYPE_NORMAL, [account]
|
||||
))
|
||||
if (result.length > 0) {
|
||||
const m = result[0]
|
||||
this.membersMap.set(m.getAccId(), m)
|
||||
return m
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.membersMap.clear()
|
||||
this.mineTeamMember = undefined
|
||||
this.teamId = ''
|
||||
this.team = undefined
|
||||
this.isFinish = false
|
||||
this.nextToken = ''
|
||||
//用户信息变更
|
||||
ChatKitClient.nim.userService?.off('onUserProfileChanged', this.onUserProfileChanged.bind(this))
|
||||
//好友信息变更
|
||||
ChatKitClient.nim.friendService?.off('onFriendInfoChanged', this.onFriendInfoChanged.bind(this))
|
||||
//群成员信息变更
|
||||
ChatKitClient.nim.teamService?.off('onTeamMemberInfoUpdated', this.onTeamMemberInfoUpdated.bind(this))
|
||||
ChatKitClient.nim.teamService?.off('onTeamMemberLeft', this.onTeamMemberLeft.bind(this))
|
||||
ChatKitClient.nim.teamService?.off('onTeamMemberKicked', this.onTeamMemberKicked.bind(this))
|
||||
ChatKitClient.nim.teamService?.off('onTeamMemberJoined', this.onTeamMemberJoined.bind(this))
|
||||
//群信息变更
|
||||
ChatKitClient.nim.teamService?.off('onTeamInfoUpdated', this.onTeamInfoUpdated.bind(this))
|
||||
ChatKitClient.nim.teamService?.off('onSyncFinished', this.onSyncFinished.bind(this))
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否已经加载了所有成员
|
||||
* @returns
|
||||
*/
|
||||
haveLoadAllMember(): boolean {
|
||||
return this.isFinish
|
||||
}
|
||||
}
|
||||
47
chatkit/src/main/ets/constant/Constant.ets
Normal file
47
chatkit/src/main/ets/constant/Constant.ets
Normal file
@ -0,0 +1,47 @@
|
||||
// 用于显示合并后的消息的发送方Nick的key
|
||||
export const mergedMessageNickKey: string = 'mergedMessageNickKey';
|
||||
|
||||
// 用于显示合并后的消息的发送方avatar的key
|
||||
export const mergedMessageAvatarKey: string = 'mergedMessageAvatarKey';
|
||||
|
||||
// 合并转发消息文件名前缀
|
||||
export const multiForwardFileName = "multiForward"
|
||||
|
||||
/// 合并转发消息最大层数(深度): 3
|
||||
export let mergedMessageMaxDepth = 3
|
||||
|
||||
/// 合并转发消息限制条数: 100
|
||||
export let mergedMessageLimitCount = 100
|
||||
|
||||
/// 逐条转发消息限制条数: 10
|
||||
export let singleMessageLimitCount = 10
|
||||
|
||||
/// 批量删除消息限制条数: 50
|
||||
export let deleteMessagesLimitCount = 50
|
||||
|
||||
/// 合并转发自定义消息 type: 101
|
||||
export let mergedMessageCustomType = 101
|
||||
|
||||
/// 合并转发自定义消息 cellHeight: 130
|
||||
export let mergedMessageCellHeight: number = 130
|
||||
|
||||
/// 会话选择器最大选择数量: 9
|
||||
export let conversationSelectLimitCount: number = 9
|
||||
|
||||
// 最新操作的类型
|
||||
export let keyExtensionLastOptType = "lastOpt"
|
||||
|
||||
// 群自定义配置参数,用于是否群中所有人都可以@所有人配置的KEY值
|
||||
export let keyExtensionAtAll = "yxAllowAt"
|
||||
|
||||
// 群自定义配置参数,用于是否群中管理员可以@所有人,允许所有群成员@所有人
|
||||
export let typeExtensionAllowAll = "all"
|
||||
|
||||
// 群自定义配置参数,用于是否群中管理员可以@所有人,只允许管理员@所有人
|
||||
export let typeExtensionAllowManager = "manager"
|
||||
|
||||
/// 收藏类型与消息类型映射(在类型基础上+1000)
|
||||
export let collectionTypeOffset = 1000
|
||||
|
||||
/// 回复消息key, 用于不使用 thread 的消息回复方案
|
||||
export let keyReplyMsgKey = "yxReplyMsg"
|
||||
62
chatkit/src/main/ets/logger/AppLogger.ets
Normal file
62
chatkit/src/main/ets/logger/AppLogger.ets
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { LogLevel } from '@nimsdk/base'
|
||||
import { LoggerKitImpl } from './LoggerKitImpl'
|
||||
|
||||
export class KitLogger {
|
||||
private core: LoggerKitImpl
|
||||
|
||||
public constructor(core: LoggerKitImpl) {
|
||||
this.core = core
|
||||
}
|
||||
|
||||
public async debug(label?: string, message?: string, ...args: string[]): Promise<void> {
|
||||
try {
|
||||
label = label ? label : 'Debug'
|
||||
message = message ? message : 'logMessage'
|
||||
const level = LogLevel.Debug
|
||||
this.core.write(level, label, message, args)
|
||||
} catch (e) {
|
||||
this.core.write(LogLevel.Error, label ?? '', 'debug', ['write error'])
|
||||
}
|
||||
}
|
||||
|
||||
public async info(label?: string, message?: string, ...args: string[]): Promise<void> {
|
||||
try {
|
||||
label = label ? label : 'Info'
|
||||
message = message ? message : 'logMessage'
|
||||
const level = LogLevel.Info
|
||||
this.core.write(level, label, message, args)
|
||||
} catch (e) {
|
||||
this.core.write(LogLevel.Error, label ?? '', 'debug', ['write error'])
|
||||
}
|
||||
}
|
||||
|
||||
public async warn(label?: string, message?: string, ...args: string[]): Promise<void> {
|
||||
try {
|
||||
label = label ? label : 'Warn'
|
||||
message = message ? message : 'logMessage'
|
||||
const level = LogLevel.Warn
|
||||
this.core.write(level, label, message, args)
|
||||
} catch (e) {
|
||||
this.core.write(LogLevel.Error, label ?? '', 'debug', ['write error'])
|
||||
}
|
||||
}
|
||||
|
||||
public async error(label?: string, message?: string, ...args: string[]): Promise<void> {
|
||||
try {
|
||||
label = label ? label : 'Error'
|
||||
message = message ? message : 'logMessage'
|
||||
const level = LogLevel.Error
|
||||
this.core.write(level, label, message, args)
|
||||
} catch (e) {
|
||||
this.core.write(LogLevel.Error, label ?? '', 'debug', ['write error'])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
248
chatkit/src/main/ets/logger/LoggerKitImpl.ets
Normal file
248
chatkit/src/main/ets/logger/LoggerKitImpl.ets
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { taskpool } from '@kit.ArkTS';
|
||||
import { LogLevel, NetWorkingInStance, NIMEStrAnyObj, V2NIMErrorImpl } from '@nimsdk/base';
|
||||
|
||||
import fs from '@ohos.file.fs';
|
||||
|
||||
@Concurrent
|
||||
async function writeLogger(filePath: string, level: string, label: string, message: string, state: string,
|
||||
...args: NIMEStrAnyObj[]) {
|
||||
// CELLULAR = 0, WIFI = 1, ETHERNET = 3, VPN = 4
|
||||
const netInfo = NetWorkingInStance.getInstance().getNetInfoSync()
|
||||
const customInfo = `${state}_${netInfo.netType}_${netInfo.isConnected}`
|
||||
// date formatter
|
||||
const date = new Date()
|
||||
const milliseconds: number = date.getMilliseconds()
|
||||
const formattedMilliseconds: string = ("00" + milliseconds).slice(-3);
|
||||
const dateStr = `${date.getMonth() +
|
||||
1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}:${formattedMilliseconds}`
|
||||
const prefix = `[${dateStr}]: ${level}: ${customInfo}: ${label}: ${message} `
|
||||
// args formatter
|
||||
let tempStr = args.map((item) => {
|
||||
if (item instanceof V2NIMErrorImpl) {
|
||||
let desc = `${item.name}\n code: ${item.code}\n message: "${item.message}"\n detail: ${item.detail ?
|
||||
JSON.stringify(item.detail) : ''}`
|
||||
if (item?.detail?.rawError) {
|
||||
desc += `\n rawError: ${item.detail.rawError.message}`
|
||||
}
|
||||
return desc
|
||||
} else if (item instanceof Error) {
|
||||
return item && item.message ? item.message : item
|
||||
} else if (typeof item === 'object') {
|
||||
return JSON.stringify(item).replace(/^\[|\]$/g, "").replace(/^\[|\]$/g, "")
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
}).join(' ')
|
||||
|
||||
tempStr = tempStr.replace(/^"|"$/g, '')
|
||||
|
||||
const logMessage = tempStr ? `${prefix + ': ' + tempStr}\n` : `${prefix}\n`
|
||||
|
||||
const file: fs.File = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE | fs.OpenMode.APPEND)
|
||||
fs.writeSync(file.fd, logMessage)
|
||||
fs.closeSync(file)
|
||||
}
|
||||
|
||||
const TAG = '[LoggerServiceImpl]'
|
||||
|
||||
export class LoggerKitImpl {
|
||||
// 本地日志,默认保存 15 天
|
||||
private static readonly LoggerFileEffectivePeriod: number = 15
|
||||
logDir: string = ''
|
||||
// core: NIM
|
||||
sequenceRunner: taskpool.SequenceRunner
|
||||
logLevel: LogLevel
|
||||
isOpenConsoleLog: boolean
|
||||
private currentLogPath: string
|
||||
|
||||
public constructor(cacheDir: string, logLevel?: LogLevel, isOpenConsoleLog?: boolean) {
|
||||
// core.loggerService = this
|
||||
// this.core = core
|
||||
this.logDir = cacheDir + '/nim_kit_log'
|
||||
this.checkLoggerEffectivePeriod(this.logDir)
|
||||
const formattedDate = this.formattedDate()
|
||||
this.currentLogPath = this.logDir + `/nim_kit_${formattedDate}.txt`
|
||||
// this.core.eventBus.on('LoggerServiceImpl/onUploadLogFiles', () => {
|
||||
// this.uploadZipLogFile(false)
|
||||
// })
|
||||
|
||||
this.sequenceRunner = new taskpool.SequenceRunner()
|
||||
this.logLevel = logLevel ?? LogLevel.Debug
|
||||
this.isOpenConsoleLog = isOpenConsoleLog ?? false
|
||||
}
|
||||
|
||||
async write(level: LogLevel, label: string, message: string, ...args: NIMEStrAnyObj[]): Promise<void> {
|
||||
|
||||
let state = 'U'
|
||||
// const isForeground = this.core.settingService?.v2IGetIsForeground()
|
||||
// if (typeof isForeground !== 'undefined') {
|
||||
// state = isForeground ? 'F' : 'B'
|
||||
// }
|
||||
|
||||
if (this.isWriteLog(level)) {
|
||||
const filePath = this.currentLogPath
|
||||
const task = new taskpool.Task(writeLogger, filePath, level, label, message, state, args)
|
||||
this.sequenceRunner.execute(task)
|
||||
}
|
||||
|
||||
this.consoleLog(level, label, message, args)
|
||||
}
|
||||
|
||||
isWriteLog(level: LogLevel): boolean {
|
||||
switch (this.logLevel) {
|
||||
case LogLevel.Debug:
|
||||
return true;
|
||||
case LogLevel.Info:
|
||||
return level !== LogLevel.Debug;
|
||||
case LogLevel.Warn:
|
||||
return level !== LogLevel.Debug && level !== LogLevel.Info;
|
||||
case LogLevel.Error:
|
||||
return level === LogLevel.Error;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
consoleLog(level: LogLevel, label: string, message: string, args: NIMEStrAnyObj[]) {
|
||||
// console log
|
||||
message = message.slice(0, 2000)
|
||||
if (this.isOpenConsoleLog) {
|
||||
if (level === LogLevel.Debug) {
|
||||
console.debug(label, message, args[0].length === 0 ? '' : JSON.stringify(args[0]))
|
||||
} else if (level === LogLevel.Info) {
|
||||
console.info(label, message, args[0].length === 0 ? '' : JSON.stringify(args[0]))
|
||||
} else if (level === LogLevel.Error) {
|
||||
console.error(label, message, args[0].length === 0 ? '' : JSON.stringify(args[0]))
|
||||
} else if (level === LogLevel.Warn) {
|
||||
console.warn(label, message, args[0].length === 0 ? '' : JSON.stringify(args[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// async uploadSDKLogs(isActive: boolean): Promise<string> {
|
||||
// try {
|
||||
// this.core.logger.info(TAG, 'uploadSDKLogs', isActive)
|
||||
// return await this.uploadZipLogFile(isActive)
|
||||
// } catch (e) {
|
||||
// this.core.logger.error(TAG, 'uploadSDKLogs', isActive, e)
|
||||
// if (e instanceof V2NIMErrorImpl || e.name === 'V2NIMError') {
|
||||
// throw e as V2NIMErrorImpl
|
||||
// } else {
|
||||
// throw new V2NIMErrorImpl({
|
||||
// code: V2NIMErrorCode.V2NIM_ERROR_CODE_FILE_UPLOAD_FAILED,
|
||||
// detail: {
|
||||
// reason: 'upload log file: error: ' + `${JSON.stringify(e)}`
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
public getLogDirectory(): string {
|
||||
return this.logDir
|
||||
}
|
||||
|
||||
public getLogFilePath(): string {
|
||||
return this.currentLogPath
|
||||
}
|
||||
|
||||
async checkLoggerEffectivePeriod(fileDir: string): Promise<void> {
|
||||
try {
|
||||
// 获取log list
|
||||
if (!fs.accessSync(this.logDir)) {
|
||||
fs.mkdirSync(this.logDir, true);
|
||||
}
|
||||
|
||||
let fileList: Array<string> = await fs.listFile(fileDir, { recursion: false, listNum: 0 })
|
||||
// 大于等于 15 时,淘汰最久的
|
||||
if (fileList.length >= LoggerKitImpl.LoggerFileEffectivePeriod) {
|
||||
fileList.sort((a, b) => a.localeCompare(b))
|
||||
let oldestName: string = fileList[0]
|
||||
let oldestFilePath = `${fileDir} + ${oldestName}`
|
||||
await fs.unlink(oldestFilePath)
|
||||
}
|
||||
} catch (e) {
|
||||
this.write(LogLevel.Error, 'checkLoggerEffectivePeriod', `fail:${JSON.stringify(e)}, filename ${fileDir}`)
|
||||
}
|
||||
}
|
||||
|
||||
// async uploadZipLogFile(isActive: boolean): Promise<string> {
|
||||
// let outFile = this.getOutputZipFilePath()
|
||||
// try {
|
||||
// this.core.logger.info(TAG, 'uploadZipLogFile', isActive)
|
||||
// // zip log file
|
||||
// const fileList: Array<string> = await fs.listFile(this.logDir, { recursion: false, listNum: 0 })
|
||||
// const zipList = fileList.map(item => this.logDir + '/' + item)
|
||||
// await zlib.compressFiles(zipList, outFile, {
|
||||
// level: zlib.CompressLevel.COMPRESS_LEVEL_DEFAULT_COMPRESSION,
|
||||
// memLevel: zlib.MemLevel.MEM_LEVEL_MAX,
|
||||
// strategy: zlib.CompressStrategy.COMPRESS_STRATEGY_HUFFMAN_ONLY
|
||||
// })
|
||||
//
|
||||
// const result: UploadFileResult = await this.core.storageService.uploadFileTask({
|
||||
// taskId: guid(),
|
||||
// uploadParams: {
|
||||
// filePath: outFile,
|
||||
// sceneName: V2NIMStorageSceneConfig.DEFAULT_SYSTEM().sceneName,
|
||||
// } as V2NIMUploadFileParams
|
||||
// })
|
||||
// // 上传成功删除本地zip 文件
|
||||
// await this.uploadSendLog(result.url, isActive)
|
||||
// await fs.unlink(outFile)
|
||||
// this.core.logger.info(TAG, 'uploadZipLogFile succeed')
|
||||
// return result.url
|
||||
// } catch (e) {
|
||||
// await fs.unlink(outFile)
|
||||
// this.core.logger.error(TAG, 'uploadZipLogFile', isActive, e)
|
||||
// if (e instanceof V2NIMErrorImpl || e.name === 'V2NIMError') {
|
||||
// throw e as V2NIMErrorImpl
|
||||
// } else {
|
||||
// throw new V2NIMErrorImpl({
|
||||
// code: V2NIMErrorCode.V2NIM_ERROR_CODE_FILE_UPLOAD_FAILED,
|
||||
// detail: {
|
||||
// reason: 'upload log file: error: ' + `${JSON.stringify(e)}`
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// private async uploadSendLog(url: string, isActive: boolean): Promise<void> {
|
||||
// try {
|
||||
// this.core.logger.info(TAG, 'uploadSendLog', url, isActive)
|
||||
// const activeType = isActive ? 1 : 0
|
||||
// const uploadLogUrlRequest: UploadLogUrlRequest = new UploadLogUrlRequest(
|
||||
// url,
|
||||
// new UploadLogUrlParams(activeType)
|
||||
// )
|
||||
// await this.core.sendCmd('uploadLogUrl', uploadLogUrlRequest)
|
||||
// } catch (e) {
|
||||
// this.core.logger.error(TAG, 'uploadSendLog', url, isActive, e)
|
||||
// }
|
||||
// }
|
||||
|
||||
private getOutputZipFilePath(): string {
|
||||
const filepath = this.logDir + `/nim_log.zip`
|
||||
if (fs.accessSync(filepath)) {
|
||||
fs.unlinkSync(filepath)
|
||||
}
|
||||
|
||||
return filepath
|
||||
}
|
||||
|
||||
private formattedDate(): string {
|
||||
const date = new Date()
|
||||
const formattedDate = Intl.DateTimeFormat("en-US",
|
||||
{ year: "numeric", month: "numeric", day: "numeric" }
|
||||
).format(date)
|
||||
.replace(/\//g, '_');
|
||||
return formattedDate
|
||||
}
|
||||
}
|
||||
31
chatkit/src/main/ets/logger/LoggerRequest.ets
Normal file
31
chatkit/src/main/ets/logger/LoggerRequest.ets
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Request params of protocol:
|
||||
* uploadLogUrl
|
||||
*/
|
||||
export class UploadLogUrlRequest {
|
||||
public url: string
|
||||
public info: UploadLogUrlParams
|
||||
|
||||
public constructor(url: string, info: UploadLogUrlParams) {
|
||||
this.url = url
|
||||
this.info = info
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Params of 'UploadLogUrlRequest'
|
||||
*/
|
||||
export class UploadLogUrlParams {
|
||||
public sdklogUploadType: number
|
||||
|
||||
public constructor(sdklogUploadType: number) {
|
||||
this.sdklogUploadType = sdklogUploadType
|
||||
}
|
||||
}
|
||||
14
chatkit/src/main/ets/model/ConversationSelectModel.ets
Normal file
14
chatkit/src/main/ets/model/ConversationSelectModel.ets
Normal file
@ -0,0 +1,14 @@
|
||||
// 转发选择页面数据模型
|
||||
@ObservedV2
|
||||
export class ConversationSelectModel {
|
||||
// 会话id
|
||||
conversationId?: string
|
||||
// 会话名称
|
||||
name?: string
|
||||
// 会话头像
|
||||
avatar?: ResourceStr
|
||||
// 是否已选中
|
||||
@Trace isSelected: boolean = false
|
||||
// 会话人数,用于展示群人数(单聊默认为 0,群聊为群人数)
|
||||
memberCount: number = 0
|
||||
}
|
||||
23
chatkit/src/main/ets/model/ConversationSelectParam.ets
Normal file
23
chatkit/src/main/ets/model/ConversationSelectParam.ets
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import { ConversationSelectModel } from './ConversationSelectModel';
|
||||
|
||||
|
||||
export class ConversationSelectParam {
|
||||
filterList?: string[] = [];
|
||||
selectLimit: number = 200;
|
||||
onSureButtonClick?: (selectedList: ConversationSelectModel[]) => void = undefined
|
||||
|
||||
constructor(filterList: string[] = [],
|
||||
selectLimit: number = 200,
|
||||
onSureButtonClick?: (selectedList: ConversationSelectModel[]) => void
|
||||
) {
|
||||
this.filterList = filterList
|
||||
this.selectLimit = selectLimit
|
||||
this.onSureButtonClick = onSureButtonClick
|
||||
}
|
||||
}
|
||||
20
chatkit/src/main/ets/model/ConversationSelectedParam.ets
Normal file
20
chatkit/src/main/ets/model/ConversationSelectedParam.ets
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import { ConversationSelectModel } from './ConversationSelectModel';
|
||||
|
||||
|
||||
export class ConversationSelectedParam {
|
||||
selectedList?: ConversationSelectModel[] = [];
|
||||
onRemoveButtonClick?: (item: ConversationSelectModel) => void = undefined // 移除按钮点击事件
|
||||
|
||||
constructor(selectedList?: ConversationSelectModel[],
|
||||
onRemoveButtonClick?: (item: ConversationSelectModel) => void
|
||||
) {
|
||||
this.selectedList = selectedList
|
||||
this.onRemoveButtonClick = onRemoveButtonClick
|
||||
}
|
||||
}
|
||||
57
chatkit/src/main/ets/model/CustomMessageAttachment.ets
Normal file
57
chatkit/src/main/ets/model/CustomMessageAttachment.ets
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
// 自定义消息附件
|
||||
export interface CustomMessageAttachment {
|
||||
// 自定义消息类型
|
||||
type?: string;
|
||||
}
|
||||
|
||||
// 合并消息
|
||||
export interface MergedMessageAttachment extends CustomMessageAttachment {
|
||||
|
||||
// 会话id
|
||||
sessionId?: string;
|
||||
|
||||
// 会话名称
|
||||
sessionName?: string;
|
||||
|
||||
// 合并消息上传NOS后的url
|
||||
url?: string;
|
||||
|
||||
// 合并消息文件的md5
|
||||
md5?: string;
|
||||
|
||||
// 合并消息的深度
|
||||
depth?: number;
|
||||
|
||||
// 合并消息的摘要,用于在消息列表展示,默认三条
|
||||
abstracts?: MergeMessageAbstract[];
|
||||
|
||||
// 合并消息的id,用于标识合并消息
|
||||
// 通[NIMMessage.uuid]获取
|
||||
messageId?: string;
|
||||
}
|
||||
|
||||
// 合并转发消息的缩略
|
||||
export interface MergeMessageAbstract {
|
||||
// 消息展示的nick,只取fromNick,没有就accId
|
||||
senderNick: string;
|
||||
|
||||
// 内容,不是Text的显示缩略
|
||||
content: string;
|
||||
|
||||
// 发送方的accId
|
||||
userAccId: string;
|
||||
|
||||
}
|
||||
|
||||
// 消息上传后的信息
|
||||
export interface MessageUploadInfo {
|
||||
url: string;
|
||||
md5: string;
|
||||
}
|
||||
64
chatkit/src/main/ets/model/NEUserWithFriend.ets
Normal file
64
chatkit/src/main/ets/model/NEUserWithFriend.ets
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { V2NIMFriend, V2NIMUser } from '@nimsdk/base';
|
||||
|
||||
interface NEUserWithFriendParams {
|
||||
friend?: V2NIMFriend;
|
||||
user?: V2NIMUser;
|
||||
}
|
||||
|
||||
@ObservedV2
|
||||
export class NEUserWithFriend {
|
||||
@Trace user?: V2NIMUser
|
||||
@Trace friend?: V2NIMFriend
|
||||
|
||||
constructor(params: NEUserWithFriendParams) {
|
||||
this.friend = params.friend
|
||||
if (params.user) {
|
||||
this.user = params.user
|
||||
} else {
|
||||
this.user = params.friend?.userProfile
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取显示名称
|
||||
/// (备注) > 昵称 > accid
|
||||
/// - Parameter showAlias: 是否优先显示备注
|
||||
public showName(showAlias: boolean = true): string {
|
||||
if (showAlias && this.friend?.alias && this.friend.alias.length > 0) {
|
||||
return this.friend?.alias
|
||||
}
|
||||
|
||||
if (this.user?.name && this.user.name.length > 0) {
|
||||
return this.user.name
|
||||
}
|
||||
|
||||
return this.user?.accountId ?? ""
|
||||
}
|
||||
|
||||
/// 获取简称 (尾部截取)
|
||||
/// - Parameter showAlias: 是否优先显示备注
|
||||
/// - Parameter count: 尾部截取长度
|
||||
public shortName(showAlias: boolean = true, count: number = 2): string {
|
||||
let name = this.showName(showAlias)
|
||||
if (name) {
|
||||
const start: number = (name.length - count) > 0 ? name.length - count : 0
|
||||
const end: number = name.length
|
||||
return name.substring(start, end)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取好友备注
|
||||
* @returns
|
||||
*/
|
||||
public getAlias() {
|
||||
return this.friend?.alias
|
||||
}
|
||||
}
|
||||
21
chatkit/src/main/ets/model/PersonSelectParam.ets
Normal file
21
chatkit/src/main/ets/model/PersonSelectParam.ets
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { NEUserWithFriend } from './NEUserWithFriend';
|
||||
|
||||
export class PersonSelectParam {
|
||||
filterList?: string[] = [];
|
||||
selectLimit: number = 200;
|
||||
onClickSureButton?: (selectedList: NEUserWithFriend[]) => void = undefined
|
||||
|
||||
constructor(onClickSureButton?: (selectedList: NEUserWithFriend[]) => void, filterList: string[] = [],
|
||||
selectLimit: number = 200) {
|
||||
this.filterList = filterList
|
||||
this.selectLimit = selectLimit
|
||||
this.onClickSureButton = onClickSureButton
|
||||
}
|
||||
}
|
||||
14
chatkit/src/main/ets/model/RevokeMessageExtension.ets
Normal file
14
chatkit/src/main/ets/model/RevokeMessageExtension.ets
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
export interface RevokeMessageExtension {
|
||||
revoke_message_local: boolean,
|
||||
revoke_message_local_time: number,
|
||||
revoke_message_local_edit: boolean,
|
||||
revoke_message_local_content: string,
|
||||
revoke_message_client_id: string,
|
||||
}
|
||||
131
chatkit/src/main/ets/model/TeamMemberWithUser.ets
Normal file
131
chatkit/src/main/ets/model/TeamMemberWithUser.ets
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { V2NIMFriend, V2NIMTeamMember, V2NIMTeamMemberRole, V2NIMUser } from '@nimsdk/base'
|
||||
|
||||
/**
|
||||
* 群成员,好友,用户信息的集合
|
||||
*/
|
||||
export class TeamMemberWithUser {
|
||||
//群成员信息
|
||||
teamMember: V2NIMTeamMember
|
||||
//好友信息
|
||||
friendInfo?: V2NIMFriend
|
||||
//用户信息
|
||||
userInfo?: V2NIMUser
|
||||
|
||||
constructor(teamMember: V2NIMTeamMember,
|
||||
friendInfo?: V2NIMFriend, userInfo?: V2NIMUser) {
|
||||
this.teamMember = teamMember
|
||||
this.friendInfo = friendInfo
|
||||
this.userInfo = userInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取头像
|
||||
* @returns
|
||||
*/
|
||||
getAvatar(): string | undefined {
|
||||
return this.userInfo?.avatar
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取头像nick
|
||||
* @returns
|
||||
*/
|
||||
getAvatarName(): string {
|
||||
let avatarName = ''
|
||||
if (this.friendInfo && this.friendInfo.alias &&
|
||||
this.friendInfo.alias.length > 0) {
|
||||
avatarName = this.friendInfo.alias
|
||||
} else if (this.userInfo && this.userInfo.name && this.userInfo.name.length > 0) {
|
||||
avatarName = this.userInfo.name
|
||||
} else {
|
||||
avatarName = this.teamMember.accountId
|
||||
}
|
||||
return avatarName.length > 2 ? avatarName.substring(avatarName.length - 2, avatarName.length) : avatarName
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取@的名称,不包含好友备注
|
||||
*/
|
||||
getAitName(): string {
|
||||
//群昵称
|
||||
if (this.teamMember.teamNick &&
|
||||
this.teamMember.teamNick.length > 0) {
|
||||
return this.teamMember.teamNick
|
||||
}
|
||||
|
||||
//用户名
|
||||
if (this.userInfo && this.userInfo.name && this.userInfo.name.length > 0) {
|
||||
return this.userInfo.name
|
||||
}
|
||||
//最后accId
|
||||
return this.teamMember.accountId
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是群主
|
||||
* @returns
|
||||
*/
|
||||
isOwner(): boolean {
|
||||
return this.teamMember.memberRole === V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_OWNER
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是管理员
|
||||
* @returns
|
||||
*/
|
||||
isManager(): boolean {
|
||||
return this.teamMember.memberRole === V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_MANAGER
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取昵称
|
||||
* @returns
|
||||
*/
|
||||
getNickname(): string {
|
||||
//优先好友备注
|
||||
if (this.friendInfo && this.friendInfo.alias &&
|
||||
this.friendInfo.alias.length > 0) {
|
||||
return this.friendInfo.alias
|
||||
}
|
||||
|
||||
//其次群昵称
|
||||
if (this.teamMember.teamNick &&
|
||||
this.teamMember.teamNick.length > 0) {
|
||||
return this.teamMember.teamNick
|
||||
}
|
||||
|
||||
//再次用户名
|
||||
if (this.userInfo && this.userInfo.name && this.userInfo.name.length > 0) {
|
||||
return this.userInfo.name
|
||||
}
|
||||
//最后accId
|
||||
return this.teamMember.accountId
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群成员Id
|
||||
* @returns
|
||||
*/
|
||||
getAccId(): string {
|
||||
return this.teamMember.accountId
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 群成员请求返回结果
|
||||
*/
|
||||
export interface TeamMemberResult {
|
||||
//是否结束
|
||||
finished: boolean
|
||||
//下次请求token
|
||||
nextToken: string
|
||||
//成员列表
|
||||
memberList: TeamMemberWithUser[]
|
||||
}
|
||||
4
chatkit/src/main/ets/model/TeamSettingParam.ets
Normal file
4
chatkit/src/main/ets/model/TeamSettingParam.ets
Normal file
@ -0,0 +1,4 @@
|
||||
export interface TeamSettingParam {
|
||||
teamId: string,
|
||||
memberIds: string[]
|
||||
}
|
||||
14
chatkit/src/main/ets/model/ait/AitAllPermission.ets
Normal file
14
chatkit/src/main/ets/model/ait/AitAllPermission.ets
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @所有人的权限
|
||||
*/
|
||||
export interface AitAllPermission {
|
||||
yxAllowAt: string
|
||||
lastOpt: string
|
||||
}
|
||||
63
chatkit/src/main/ets/model/ait/AitMessage.ets
Normal file
63
chatkit/src/main/ets/model/ait/AitMessage.ets
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import { AitSegment } from './AitSegment'
|
||||
import { Type } from 'class-transformer'
|
||||
import 'reflect-metadata'
|
||||
|
||||
export class AitMessage {
|
||||
/**
|
||||
* 账号
|
||||
*/
|
||||
accountId: string = ""
|
||||
//@的文本
|
||||
text: string
|
||||
//@的信息存储,一个文案可能被@多次
|
||||
@Type(() => AitSegment)
|
||||
segments: AitSegment[] = []
|
||||
|
||||
constructor(text: string, accountId: string) {
|
||||
this.text = text
|
||||
this.accountId = accountId
|
||||
}
|
||||
|
||||
addSegment(start: number, end: number, broken?: boolean) {
|
||||
let segment: AitSegment = {
|
||||
start: start,
|
||||
end: end,
|
||||
broken: broken ?? false
|
||||
}
|
||||
this.segments.push(...[segment])
|
||||
}
|
||||
|
||||
removeSegment(start: number, end: number) {
|
||||
let index = this.segments.findIndex(e => e.start === start && e.end === end)
|
||||
if (index >= 0) {
|
||||
this.segments.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
valid(): boolean {
|
||||
if (this.segments.length < 0) {
|
||||
return false
|
||||
}
|
||||
for (let segment of this.segments) {
|
||||
if (!segment.broken) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
segmentToMap(segment: AitSegment): Map<string, number | boolean> {
|
||||
let map = new Map<string, number | boolean>()
|
||||
map.set('start', segment.start)
|
||||
map.set('end', segment.end)
|
||||
map.set('broken', segment.broken ?? false)
|
||||
return map
|
||||
}
|
||||
}
|
||||
|
||||
271
chatkit/src/main/ets/model/ait/AitModel.ets
Normal file
271
chatkit/src/main/ets/model/ait/AitModel.ets
Normal file
@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import { AitMessage } from './AitMessage'
|
||||
import { plainToClass, Transform, Type } from 'class-transformer'
|
||||
import 'reflect-metadata'
|
||||
import { AitSegment } from './AitSegment'
|
||||
import { JSON } from '@kit.ArkTS'
|
||||
|
||||
//@所有人的Account
|
||||
export const accountAll: string = "ait_all"
|
||||
|
||||
//@key
|
||||
export const aitKey: string = 'yxAitMsg'
|
||||
|
||||
/**
|
||||
* 云信@功能集合
|
||||
*/
|
||||
export interface YxAitMsg {
|
||||
yxAitMsg?: Map<string, AitMessage>
|
||||
}
|
||||
|
||||
export class AitModel {
|
||||
/**
|
||||
* key 为AccountID
|
||||
*/
|
||||
@Type(() => Map)
|
||||
@Transform((param) => {
|
||||
const map = new Map<string, AitMessage>();
|
||||
if (param.value instanceof Map) {
|
||||
let params = param.value as Map<string, AitMessage>
|
||||
params.forEach((
|
||||
value, key
|
||||
) => {
|
||||
map.set(key, value)
|
||||
})
|
||||
}
|
||||
return map;
|
||||
})
|
||||
aitBlocks: Map<string, AitMessage> = new Map()
|
||||
|
||||
reset() {
|
||||
this.aitBlocks.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个@的用户
|
||||
* @param deletedText 文本
|
||||
* @param endIndex 删除的文本的光标所在位置
|
||||
* @param length 删除的文本的长度
|
||||
* @returns
|
||||
*/
|
||||
deleteAitUser(deletedText: string, endIndex: number, length: number): AitMessage | null {
|
||||
//如果deletedText为空,直接返回
|
||||
if (deletedText.length <= 0) {
|
||||
return null
|
||||
}
|
||||
let len: number = deletedText.length;
|
||||
//如果aitBlocks 中有Value值和deletedText不匹配则返回
|
||||
let removedBlack: AitMessage | null = null;
|
||||
|
||||
for (let aitMsg of this.aitBlocks.values()) {
|
||||
for (let segment of aitMsg.segments) {
|
||||
if (endIndex < segment.start) {
|
||||
segment.start -= length;
|
||||
segment.end -= length;
|
||||
continue;
|
||||
}
|
||||
if (len < segment.end + 1 ||
|
||||
deletedText.substring(segment.start, segment.end + 1) !== aitMsg.text) {
|
||||
removedBlack = new AitMessage(aitMsg.text, "");
|
||||
removedBlack.addSegment(segment.start, segment.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (removedBlack !== null) {
|
||||
this.removeSegment(removedBlack, length);
|
||||
}
|
||||
|
||||
return removedBlack;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文本
|
||||
* @param endIndex 删除后光标的位置
|
||||
* @param length 删除的长度
|
||||
*/
|
||||
deleteText(endIndex: number, length: number) {
|
||||
let removedBlack: AitMessage | null = null;
|
||||
for (let aitMsg of this.aitBlocks.values()) {
|
||||
let deletedSegment: AitSegment | null = null
|
||||
for (let segment of aitMsg.segments) {
|
||||
if (endIndex < segment.start) {
|
||||
segment.start -= length;
|
||||
segment.end -= length;
|
||||
continue;
|
||||
} else if (endIndex < segment.end) {
|
||||
deletedSegment = segment
|
||||
}
|
||||
}
|
||||
if (deletedSegment !== null) {
|
||||
aitMsg.removeSegment(deletedSegment.start, deletedSegment.end)
|
||||
}
|
||||
if (aitMsg.segments.length <= 0) {
|
||||
removedBlack = aitMsg
|
||||
}
|
||||
}
|
||||
if (removedBlack !== null) {
|
||||
this.removeSegment(removedBlack, length)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除aitMsg中的segment,如果aitMsg中的segment为空,则删除aitMsg
|
||||
* 同时处理其他aitMsg中的segment的位置
|
||||
* @param removedBlack
|
||||
* @param deletedLen 删除的文本的长度,如果为正数则其他后面的前移,如果为负数,则其他不变
|
||||
*/
|
||||
removeSegment(removedBlack: AitMessage, deletedLen: number): void {
|
||||
// 删除aitMsg中的segment,如果aitMsg中的segment为空,则删除aitMsg
|
||||
let aitMsg: AitMessage | undefined = undefined
|
||||
let removeKey: string = ''
|
||||
this.aitBlocks.forEach((value, key) => {
|
||||
if (value.text === removedBlack.text) {
|
||||
aitMsg = value
|
||||
removeKey = key
|
||||
}
|
||||
})
|
||||
if (aitMsg) {
|
||||
//artTs 语法要求,重新拷贝
|
||||
const aitMessage: AitMessage = aitMsg
|
||||
const start: number = removedBlack.segments[0].start;
|
||||
const end: number = removedBlack.segments[0].end;
|
||||
// 该段文字的长度,加上删除的长度,因为其在前面已经移了deletedLen位
|
||||
const length = end - start + 1 - deletedLen;
|
||||
aitMessage.removeSegment(start, end);
|
||||
if (aitMessage.segments.length === 0) {
|
||||
this.aitBlocks.delete(removeKey);
|
||||
}
|
||||
if (deletedLen > 0) {
|
||||
for (const aitMsg of this.aitBlocks.values()) {
|
||||
for (const segment of aitMsg.segments) {
|
||||
if (end <= segment.start) {
|
||||
segment.start -= length;
|
||||
segment.end -= length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户是否被@
|
||||
* @param accId
|
||||
* @returns
|
||||
*/
|
||||
isUserBeenAit(accId: string | null): boolean {
|
||||
if (accId === null) {
|
||||
return false;
|
||||
}
|
||||
for (let key of this.aitBlocks.keys()) {
|
||||
if (key === accountAll) {
|
||||
return true;
|
||||
}
|
||||
if (key === accId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝
|
||||
* @param aitModel
|
||||
*/
|
||||
fork(aitModel: AitModel) {
|
||||
this.aitBlocks.clear()
|
||||
this.aitBlocks = aitModel.aitBlocks
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据插入后的Text 文案, segment 移位或者删除。
|
||||
* @param changedText
|
||||
* @param endIndex
|
||||
* @param length
|
||||
*/
|
||||
insertText(endIndex: number, length: number): void {
|
||||
let removedBlack: AitMessage | null = null;
|
||||
const start: number = endIndex - length;
|
||||
for (let aitMsg of this.aitBlocks.values()) {
|
||||
for (let segment of aitMsg.segments) {
|
||||
if (start <= segment.start) {
|
||||
segment.start += length;
|
||||
segment.end += length;
|
||||
continue;
|
||||
}
|
||||
if (endIndex > segment.start && endIndex <= segment.end) {
|
||||
removedBlack = new AitMessage(aitMsg.text, "");
|
||||
removedBlack.addSegment(segment.start, segment.end);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removedBlack !== null) {
|
||||
this.removeSegment(removedBlack, -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加@成员
|
||||
* @param account
|
||||
* @param name
|
||||
* @param start
|
||||
*/
|
||||
addAitMember(account: string, name: string, start: number): void {
|
||||
for (let aitMsg of this.aitBlocks.values()) {
|
||||
for (let segment of aitMsg.segments) {
|
||||
if (start <= segment.start) {
|
||||
segment.start += name.length;
|
||||
segment.end += name.length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
let aitBlock: AitMessage | undefined = this.aitBlocks.get(account);
|
||||
if (aitBlock === undefined) {
|
||||
aitBlock = new AitMessage(name, account);
|
||||
this.aitBlocks.set(account, aitBlock);
|
||||
}
|
||||
const end: number = start + name.length - 1;
|
||||
aitBlock.addSegment(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension 解析获得AitModel
|
||||
* @param extension
|
||||
* @returns
|
||||
*/
|
||||
export function getAitModelFromJson(extension?: string): AitModel | undefined {
|
||||
if (extension) {
|
||||
try {
|
||||
const obj: Record<string, Object> = JSON.parse(extension) as Record<string, Object>
|
||||
for (let key of Object.entries(obj)) {
|
||||
if (key[0] === aitKey) {
|
||||
let trans: AitModelTrans = {
|
||||
aitBlocks: key[1]
|
||||
}
|
||||
let aitModel: AitModel = plainToClass(AitModel, trans, { enableImplicitConversion: true })
|
||||
if (aitModel.aitBlocks.size > 0) {
|
||||
return aitModel
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch (e) {
|
||||
console.error('parse json error', JSON.stringify(e), JSON.stringify(extension))
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
interface AitModelTrans {
|
||||
aitBlocks: object
|
||||
}
|
||||
18
chatkit/src/main/ets/model/ait/AitSegment.ets
Normal file
18
chatkit/src/main/ets/model/ait/AitSegment.ets
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* 记录@消息的最小单位
|
||||
*/
|
||||
export class AitSegment {
|
||||
//@消息的起点
|
||||
start: number = 0;
|
||||
//@消息的终点位置
|
||||
end: number = 0;
|
||||
//是否已经被破坏
|
||||
broken: boolean = false
|
||||
}
|
||||
514
chatkit/src/main/ets/repo/ChatRepo.ets
Normal file
514
chatkit/src/main/ets/repo/ChatRepo.ets
Normal file
@ -0,0 +1,514 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
V2NIMAddCollectionParams,
|
||||
V2NIMCollection,
|
||||
V2NIMCollectionOption,
|
||||
V2NIMMessage,
|
||||
V2NIMMessageDeletedNotification,
|
||||
V2NIMMessageListOption,
|
||||
V2NIMMessagePin,
|
||||
V2NIMMessagePinNotification,
|
||||
V2NIMMessageRefer,
|
||||
V2NIMMessageRevokeNotification,
|
||||
V2NIMMessageRevokeParams,
|
||||
V2NIMMessageSearchParams,
|
||||
V2NIMP2PMessageReadReceipt,
|
||||
V2NIMSendMessageParams,
|
||||
V2NIMSendMessageResult,
|
||||
V2NIMTeamMessageReadReceipt,
|
||||
V2NIMTeamMessageReadReceiptDetail
|
||||
} from '@nimsdk/base';
|
||||
import { ChatKitClient } from '../ChatKitClient';
|
||||
|
||||
export class ChatRepo {
|
||||
/**
|
||||
* 发送消息
|
||||
* @param sendFn 消息接受处理方法
|
||||
*/
|
||||
static onSendMessage(sendFn: (message: V2NIMMessage) => void) {
|
||||
ChatKitClient.nim.messageService?.on('onSendMessage', sendFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
* @param receiveFn 消息接受处理方法
|
||||
*/
|
||||
static offSendMessage(receiveFn: (message: V2NIMMessage) => void) {
|
||||
ChatKitClient.nim.messageService?.off('onSendMessage', receiveFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受消息
|
||||
* @param receiveFn 消息接受处理方法
|
||||
*/
|
||||
static onReceiverMessage(receiveFn: (messageList: V2NIMMessage[]) => void) {
|
||||
ChatKitClient.nim.messageService?.on('onReceiveMessages', receiveFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受消息
|
||||
* @param receiveFn 消息接受处理方法
|
||||
*/
|
||||
static offReceiverMessage(receiveFn: (messageList: V2NIMMessage[]) => void) {
|
||||
ChatKitClient.nim.messageService?.off('onReceiveMessages', receiveFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除消息
|
||||
* @param receiveFn 消息接受处理方法
|
||||
*/
|
||||
static onDeleteMessage(deleteFn: (messageList: V2NIMMessageDeletedNotification[]) => void) {
|
||||
ChatKitClient.nim.messageService?.on('onMessageDeletedNotifications', deleteFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除消息
|
||||
* @param receiveFn 消息接受处理方法
|
||||
*/
|
||||
static offDeleteMessage(deleteFn: (messageList: V2NIMMessageDeletedNotification[]) => void) {
|
||||
ChatKitClient.nim.messageService?.off('onMessageDeletedNotifications', deleteFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册撤回消息监听
|
||||
* @param receiveFn 消息接受处理方法
|
||||
*/
|
||||
static onRevokeMessage(revokeFn: (messageList: V2NIMMessageRevokeNotification[]) => void) {
|
||||
ChatKitClient.nim.messageService?.on('onMessageRevokeNotifications', revokeFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消注册撤回消息监听
|
||||
* @param receiveFn 消息接受处理方法
|
||||
*/
|
||||
static offRevokeMessage(revokeFn: (messageList: V2NIMMessageRevokeNotification[]) => void) {
|
||||
ChatKitClient.nim.messageService?.off('onMessageRevokeNotifications', revokeFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册单聊消息已读回执监听
|
||||
* @param receiptsFn 单聊消息已读回执处理方法
|
||||
*/
|
||||
static onP2PMessageReadReceipts(receiptsFn: (messageList: V2NIMP2PMessageReadReceipt[]) => void) {
|
||||
ChatKitClient.nim.messageService?.on('onReceiveP2PMessageReadReceipts', receiptsFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消注册单聊消息已读回执监听
|
||||
* @param receiptsFn 单聊消息已读回执处理方法
|
||||
*/
|
||||
static offP2PMessageReadReceipts(receiptsFn: (messageList: V2NIMP2PMessageReadReceipt[]) => void) {
|
||||
ChatKitClient.nim.messageService?.off('onReceiveP2PMessageReadReceipts', receiptsFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册群消息已读回执监听
|
||||
* @param receiptsFn 群消息已读回执处理方法
|
||||
*/
|
||||
static onReceiveTeamMessageReadReceipts(receiptsFn: (messageList: V2NIMTeamMessageReadReceipt[]) => void) {
|
||||
ChatKitClient.nim.messageService?.on('onReceiveTeamMessageReadReceipts', receiptsFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消注册群消息已读回执监听
|
||||
* @param receiptsFn 群消息已读回执处理方法
|
||||
*/
|
||||
static offReceiveTeamMessageReadReceipts(receiptsFn: (messageList: V2NIMTeamMessageReadReceipt[]) => void) {
|
||||
ChatKitClient.nim.messageService?.off('onReceiveTeamMessageReadReceipts', receiptsFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册PIN消息监听
|
||||
* @param pinFn PIN消息监听处理方法
|
||||
*/
|
||||
static onMessagePinNotification(pinFn: (notification: V2NIMMessagePinNotification) => void) {
|
||||
ChatKitClient.nim.messageService?.on('onMessagePinNotification', pinFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消注册PIN消息监听
|
||||
* @param pinFn PIN消息监听处理方法
|
||||
*/
|
||||
static offMessagePinNotification(pinFn: (notification: V2NIMMessagePinNotification) => void) {
|
||||
ChatKitClient.nim.messageService?.off('onMessagePinNotification', pinFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册更新消息监听
|
||||
* @param modifyFn PIN消息监听处理方法
|
||||
*/
|
||||
static onReceiveMessagesModified(modifyFn: (messageList: V2NIMMessage[]) => void) {
|
||||
ChatKitClient.nim.messageService?.on('onReceiveMessagesModified', modifyFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消更新消息监听
|
||||
* @param modifyFn PIN消息监听处理方法
|
||||
*/
|
||||
static offReceiveMessagesModified(modifyFn: (messageList: V2NIMMessage[]) => void) {
|
||||
ChatKitClient.nim.messageService?.off('onReceiveMessagesModified', modifyFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回消息
|
||||
* @param receiveFn 消息接受处理方法
|
||||
*/
|
||||
static removeAllListeners(revokeFn: (messageList: V2NIMMessageRevokeNotification[]) => void) {
|
||||
ChatKitClient.nim.messageService?.removeAllListeners('onMessageRevokeNotifications');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询消息
|
||||
* @param option
|
||||
* @returns
|
||||
*/
|
||||
static async getMessageList(option: V2NIMMessageListOption): Promise<V2NIMMessage[]> {
|
||||
let messageList = await ChatKitClient.nim.messageService!!.getMessageList(option)
|
||||
return messageList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 messageRefers 批量查询消息
|
||||
* @param option
|
||||
* @returns
|
||||
*/
|
||||
static async getMessageListByRefers(messageRefers: V2NIMMessageRefer[]): Promise<V2NIMMessage[]> {
|
||||
let messageList = await ChatKitClient.nim.messageService!!.getMessageListByRefers(messageRefers)
|
||||
return messageList ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检索云端的消息
|
||||
*
|
||||
* @param params 检索参数
|
||||
*/
|
||||
static async searchCloudMessages(params: V2NIMMessageSearchParams): Promise<V2NIMMessage[]> {
|
||||
let messageList = await ChatKitClient.nim.messageService!!.searchCloudMessages(params)
|
||||
return messageList ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param message 消息体, 由 V2NIMMessageCreator 的对应方法创建
|
||||
* @param conversationId 会话 id
|
||||
* @param params 发送消息相关配置参数
|
||||
* @param progress 发送消息进度回调. 作用于需要上传的文件,图片,音视频消息
|
||||
*/
|
||||
static async sendMessage(
|
||||
message: V2NIMMessage,
|
||||
conversationId: string,
|
||||
params?: V2NIMSendMessageParams,
|
||||
progress?: (percentage: number) => void
|
||||
): Promise<V2NIMSendMessageResult | undefined> {
|
||||
return ChatKitClient.nim.messageService?.sendMessage(message, conversationId, params, progress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复消息
|
||||
*
|
||||
* @param message 需要发送的消息体, 由 V2NIMMessageCreator 的对应方法创建
|
||||
* @param replyMessage 被回复的消息
|
||||
* @param params 发送消息相关配置参数
|
||||
* @param progress 发送消息进度回调. 作用于需要上传的文件,图片,音视频消息
|
||||
*/
|
||||
static async replyMessage(message: V2NIMMessage, replyMessage: V2NIMMessage, params?: V2NIMSendMessageParams,
|
||||
progress?: (percentage: number) => void): Promise<V2NIMSendMessageResult> {
|
||||
const res = await ChatKitClient.nim.messageService?.replyMessage(message, replyMessage, params, progress);
|
||||
return res!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入一条本地消息, 该消息不会发送。该消息不会多端同步,只是本端显示
|
||||
* @param message 需要插入的消息体
|
||||
* @param conversationId 会话 ID
|
||||
* @param senderId 消息发送者账号
|
||||
* @param createTime 指定插入消息时间
|
||||
* @returns 插入成功的 message
|
||||
*/
|
||||
static async insertMessageToLocal(message: V2NIMMessage, conversationId: string, senderId?: string,
|
||||
createTime?: number): Promise<V2NIMMessage> {
|
||||
const res = ChatKitClient.nim.messageService?.insertMessageToLocal(message, conversationId, senderId, createTime);
|
||||
return res!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤回消息
|
||||
*
|
||||
* @param message 需要撤回的消息
|
||||
* @param params 撤回消息相关参数
|
||||
*/
|
||||
static async revokeMessage(message: V2NIMMessage, params?: V2NIMMessageRevokeParams): Promise<void> {
|
||||
const res = await ChatKitClient.nim.messageService?.revokeMessage(message, params);
|
||||
return res!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单条消息
|
||||
*
|
||||
* 注: 操作成功后, SDK 会抛出事件 {@link V2NIMMessageListener.onMessageDeletedNotifications | V2NIMMessageListener.onMessageDeletedNotifications}
|
||||
*
|
||||
* @param message 需要删除的消息
|
||||
* @param serverExtension 扩展字段
|
||||
*/
|
||||
static async deleteMessage(message: V2NIMMessage, serverExtension?: string,
|
||||
onlyDeleteLocal?: boolean): Promise<void> {
|
||||
const res = await ChatKitClient.nim.messageService?.deleteMessage(message, serverExtension, onlyDeleteLocal);
|
||||
return res!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除消息
|
||||
*
|
||||
* 注: 操作成功后, SDK 会抛出事件 {@link V2NIMMessageListener.onMessageDeletedNotifications | V2NIMMessageListener.onMessageDeletedNotifications} <br/>
|
||||
* - 所删除的消息必须是同一会话的消息 <br/>
|
||||
* - 限制一次最多删除 50 条消息
|
||||
*
|
||||
* @param messages 需要删除的消息
|
||||
* @param serverExtension 扩展字段
|
||||
*/
|
||||
static async deleteMessages(messages: V2NIMMessage[], serverExtension?: string,
|
||||
onlyDeleteLocal?: boolean): Promise<void> {
|
||||
const res = await ChatKitClient.nim.messageService?.deleteMessages(messages, serverExtension, onlyDeleteLocal);
|
||||
return res!;
|
||||
}
|
||||
|
||||
/**
|
||||
* pin 一条消息
|
||||
*
|
||||
* @param message 需要被 pin 的消息体
|
||||
* @param serverExtension 扩展字段
|
||||
*
|
||||
* 注: 操作成功后, SDK 会抛出事件 {@link V2NIMMessageListener.onMessagePinNotification | V2NIMMessageListener.onMessagePinNotification}
|
||||
*/
|
||||
static async pinMessage(message: V2NIMMessage, serverExtension?: string): Promise<void> {
|
||||
const res = await ChatKitClient.nim.messageService?.pinMessage(message, serverExtension);
|
||||
return res!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消一条Pin消息
|
||||
*
|
||||
* @param messageRefer 需要被取消 pin 的消息摘要
|
||||
* @param serverExtension 扩展字段
|
||||
*/
|
||||
static async unpinMessage(messageRefer: V2NIMMessageRefer, serverExtension?: string): Promise<void> {
|
||||
const res = await ChatKitClient.nim.messageService?.unpinMessage(messageRefer, serverExtension);
|
||||
return res!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 pin 消息列表
|
||||
*
|
||||
* @param conversationId 会话 ID
|
||||
*/
|
||||
static async getPinnedMessageList(conversationId: string): Promise<V2NIMMessagePin[]> {
|
||||
const res = ChatKitClient.nim.messageService?.getPinnedMessageList(conversationId);
|
||||
return res!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个收藏
|
||||
*
|
||||
* @param params 添加收藏的相关参数
|
||||
*/
|
||||
static async addCollection(params: V2NIMAddCollectionParams): Promise<V2NIMCollection> {
|
||||
const res = await ChatKitClient.nim.messageService?.addCollection(params)
|
||||
return res!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除相关收藏
|
||||
*
|
||||
* @param collections 需要移除的相关收藏
|
||||
*/
|
||||
static async removeCollections(collections: V2NIMCollection[]): Promise<number> {
|
||||
const res = await ChatKitClient.nim.messageService?.removeCollections(collections)
|
||||
return res!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按条件分页获取收藏信息
|
||||
*
|
||||
* @param option 查询参数
|
||||
*/
|
||||
static async getCollectionListByOption(option: V2NIMCollectionOption): Promise<V2NIMCollection[]> {
|
||||
const res = ChatKitClient.nim.messageService?.getCollectionListByOption(option)
|
||||
return res!;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造文本消息
|
||||
*
|
||||
* @param text 文本内容. 不允许为空字符串
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* const message = nim.V2NIMMessageCreator.createTextMessage('hello world')
|
||||
* ```
|
||||
*/
|
||||
static createTextMessage(text: string): V2NIMMessage {
|
||||
return ChatKitClient.nim.messageCreator.createTextMessage(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造图片消息
|
||||
*
|
||||
* @param imagePath 图片文件路径
|
||||
* @param name 文件显示名称
|
||||
* @param sceneName 场景名
|
||||
* @param width 图片宽度.
|
||||
* @param height 图片高度.
|
||||
*/
|
||||
static async createImageMessage(imagePath: string, name?: string, sceneName?: string, width?: number,
|
||||
height?: number): Promise<V2NIMMessage> {
|
||||
return ChatKitClient.nim.messageCreator.createImageMessage(imagePath, name, sceneName, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造语音消息
|
||||
*
|
||||
* @param audioPath 语音文件地址
|
||||
* @param name 文件显示名称
|
||||
* @param sceneName 场景名
|
||||
* @param duration 音频时长
|
||||
*/
|
||||
static async createAudioMessage(audioPath: string, name?: string, sceneName?: string,
|
||||
duration?: number): Promise<V2NIMMessage> {
|
||||
return ChatKitClient.nim.messageCreator.createAudioMessage(audioPath, name, sceneName, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造视频消息
|
||||
*
|
||||
* @param videoPath 视频文件地址
|
||||
* @param name 文件显示名称
|
||||
* @param sceneName 场景名
|
||||
* @param duration 视频时长
|
||||
* @param width 视频宽度
|
||||
* @param height 视频高度
|
||||
*/
|
||||
static async createVideoMessage(videoPath: string, name?: string, sceneName?: string, duration?: number,
|
||||
width?: number, height?: number): Promise<V2NIMMessage> {
|
||||
return ChatKitClient.nim.messageCreator.createVideoMessage(videoPath, name, sceneName, duration, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造文件消息
|
||||
*
|
||||
* @param filePath 文件地址
|
||||
* @param name 文件显示名称
|
||||
* @param sceneName 场景名
|
||||
*/
|
||||
static async createFileMessage(filePath: string, name?: string, sceneName?: string): Promise<V2NIMMessage> {
|
||||
return ChatKitClient.nim.messageCreator.createFileMessage(filePath, name, sceneName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造地理位置消息
|
||||
*
|
||||
* @param latitude 纬度
|
||||
* @param longitude 经度
|
||||
* @param address 详细位置信息
|
||||
*/
|
||||
static createLocationMessage(latitude: number, longitude: number, address: string): V2NIMMessage {
|
||||
return ChatKitClient.nim.messageCreator.createLocationMessage(latitude, longitude, address);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造自定义消息消息
|
||||
*
|
||||
* @param text 文本
|
||||
* @param rawAttachment 自定义的附件内容
|
||||
*/
|
||||
static createCustomMessage(text: string, rawAttachment: string): V2NIMMessage {
|
||||
return ChatKitClient.nim.messageCreator.createCustomMessage(text, rawAttachment);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造转发消息,消息内容与原消息一样
|
||||
*
|
||||
* https://overmind.hz.netease.com/206/requirement/issues/OMIM-50521
|
||||
*
|
||||
* 其它端怕抛 error 导致应用崩溃,所以这里不抛 error,而是返回 null
|
||||
*
|
||||
* @param message 需要转发的消息体
|
||||
*/
|
||||
static createForwardMessage(message: V2NIMMessage): V2NIMMessage | null {
|
||||
return ChatKitClient.nim.messageCreator.createForwardMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造提示消息
|
||||
*
|
||||
* @param text 提示文本
|
||||
*/
|
||||
static createTipsMessage(text: string): V2NIMMessage {
|
||||
return ChatKitClient.nim.messageCreator.createTipsMessage(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入一条本地消息, 该消息不会发送。该消息不会多端同步,只是本端显示
|
||||
* @param message 需要插入的消息体
|
||||
* @param conversationId 会话 ID
|
||||
*/
|
||||
static saveLocalMessage(msg: V2NIMMessage, conversationId: string, senderId: string, createTime: number) {
|
||||
ChatKitClient.nim.messageService?.insertMessageToLocal(msg, conversationId, senderId, createTime)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询点对点消息已读回执
|
||||
* @param conversationId 会话 id
|
||||
*/
|
||||
static async getP2PMessageReceipt(conversationId: string): Promise<V2NIMP2PMessageReadReceipt | undefined> {
|
||||
return await ChatKitClient.nim.messageService?.getP2PMessageReceipt(conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群消息已读回执状态详情
|
||||
*
|
||||
* @param msg 需要查询已读回执状态的消息
|
||||
* @param memberAccountIds 查找指定的账号列表已读未读
|
||||
* 为空表示查询全部
|
||||
* 同时作用于已读未读账号列表和人数
|
||||
*/
|
||||
static getTeamMessageReceiptDetail(msg: V2NIMMessage,
|
||||
memberAccountIds?: string[]): Promise<V2NIMTeamMessageReadReceiptDetail> {
|
||||
return ChatKitClient.nim.messageService!!.getTeamMessageReceiptDetail(
|
||||
msg, memberAccountIds
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息已读回执
|
||||
*
|
||||
* @param message 点对点会话收到的对方最后一条消息
|
||||
*/
|
||||
static async sendP2PMessageReceipt(message: V2NIMMessage): Promise<void> {
|
||||
return ChatKitClient.nim.messageService?.sendP2PMessageReceipt(message)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群消息已读回执状态
|
||||
*
|
||||
* @param messages 获取群消息已读回执状态. 限制一批最多 50 个且所有消息必须属于同一个会话
|
||||
*/
|
||||
static async getTeamMessageReceipts(messages: V2NIMMessage[]): Promise<V2NIMTeamMessageReadReceipt[] | undefined> {
|
||||
return ChatKitClient.nim.messageService?.getTeamMessageReceipts(messages)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送群消息已读回执
|
||||
*
|
||||
* @param messages 需要发送已读回执的消息列表. 限制一批最多 50 个且所有消息必须属于同一个会话
|
||||
*/
|
||||
static async sendTeamMessageReceipt(messages: V2NIMMessage[]): Promise<void> {
|
||||
return ChatKitClient.nim.messageService?.sendTeamMessageReceipts(messages)
|
||||
}
|
||||
}
|
||||
338
chatkit/src/main/ets/repo/ContactRepo.ets
Normal file
338
chatkit/src/main/ets/repo/ContactRepo.ets
Normal file
@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
V2NIMCheckFriendResult,
|
||||
V2NIMFriend,
|
||||
V2NIMFriendAddApplication,
|
||||
V2NIMFriendAddApplicationQueryOption,
|
||||
V2NIMFriendAddApplicationResult,
|
||||
V2NIMFriendAddParams,
|
||||
V2NIMFriendDeleteParams,
|
||||
V2NIMFriendDeletionType,
|
||||
V2NIMFriendSearchOption,
|
||||
V2NIMFriendSetParams,
|
||||
V2NIMLoginStatus,
|
||||
V2NIMUser,
|
||||
V2NIMUserSearchOption,
|
||||
V2NIMUserUpdateParams
|
||||
} from '@nimsdk/base';
|
||||
import { ChatKitClient } from '../ChatKitClient';
|
||||
import { NEUserWithFriend } from '../model/NEUserWithFriend';
|
||||
import { NEFriendUserCache } from '../NEFriendUserCache';
|
||||
|
||||
export class ContactRepo {
|
||||
public static listener = getContext(this).eventHub
|
||||
static onBlockListAddedFun = async (user: V2NIMUser) => {
|
||||
NEFriendUserCache.getInstance().addBlockAccount(user.accountId)
|
||||
}
|
||||
static onLoginStatusFun = (status: V2NIMLoginStatus) => {
|
||||
switch (status) {
|
||||
case V2NIMLoginStatus.V2NIM_LOGIN_STATUS_LOGOUT:
|
||||
NEFriendUserCache.getInstance().removeAllFriendInfo()
|
||||
NEFriendUserCache.mineUserCache = undefined
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
static onUserProfileChangedFun = async (users: V2NIMUser[]) => {
|
||||
users.forEach((user) => {
|
||||
if (NEFriendUserCache.getInstance().isFriend(user.accountId)) {
|
||||
NEFriendUserCache.getInstance().updateFriendInfo({
|
||||
user: user
|
||||
})
|
||||
}
|
||||
if (NEFriendUserCache.isMe(user.accountId) && NEFriendUserCache.mineUserCache) {
|
||||
NEFriendUserCache.mineUserCache.user = user
|
||||
}
|
||||
})
|
||||
|
||||
ContactRepo.listener.emit('updateUserInfo', users)
|
||||
}
|
||||
static onBlockListRemovedFun = (accountId: string) => {
|
||||
NEFriendUserCache.getInstance().removeBlockAccount(accountId)
|
||||
}
|
||||
static onFriendAddedFun = async (friend: V2NIMFriend) => {
|
||||
NEFriendUserCache.getInstance().updateFriendInfo({
|
||||
friend: friend
|
||||
})
|
||||
ContactRepo.removeUserFromBlockList(friend.accountId)
|
||||
ContactRepo.listener.emit('updateFriendInfo', friend)
|
||||
// 更新 user 信息至最新
|
||||
ContactRepo.getUserListFromCloud([friend.accountId])
|
||||
}
|
||||
static onFriendDeletedFun = (accountId: string, deletionType: V2NIMFriendDeletionType) => {
|
||||
NEFriendUserCache.getInstance().removeFriendInfo(accountId)
|
||||
}
|
||||
static onFriendInfoChangedFun = (friend: V2NIMFriend) => {
|
||||
NEFriendUserCache.getInstance().updateFriendInfo({
|
||||
friend: friend
|
||||
})
|
||||
ContactRepo.listener.emit('updateFriendInfo', friend)
|
||||
}
|
||||
|
||||
static addListener() {
|
||||
ChatKitClient.nim.loginService.on('onLoginStatus', ContactRepo.onLoginStatusFun)
|
||||
ChatKitClient.nim.userService?.on('onUserProfileChanged', ContactRepo.onUserProfileChangedFun)
|
||||
ChatKitClient.nim.userService?.on('onBlockListAdded', ContactRepo.onBlockListAddedFun)
|
||||
ChatKitClient.nim.userService?.on('onBlockListRemoved', ContactRepo.onBlockListRemovedFun)
|
||||
ChatKitClient.nim.friendService?.on('onFriendAdded', ContactRepo.onFriendAddedFun)
|
||||
ChatKitClient.nim.friendService?.on('onFriendDeleted', ContactRepo.onFriendDeletedFun)
|
||||
ChatKitClient.nim.friendService?.on('onFriendInfoChanged', ContactRepo.onFriendInfoChangedFun)
|
||||
}
|
||||
|
||||
static removeListener() {
|
||||
ChatKitClient.nim.loginService.off('onLoginStatus', ContactRepo.onLoginStatusFun)
|
||||
ChatKitClient.nim.userService?.off('onUserProfileChanged', ContactRepo.onUserProfileChangedFun)
|
||||
ChatKitClient.nim.userService?.off('onBlockListAdded', ContactRepo.onBlockListAddedFun)
|
||||
ChatKitClient.nim.userService?.off('onBlockListRemoved', ContactRepo.onBlockListRemovedFun)
|
||||
ChatKitClient.nim.friendService?.off('onFriendAdded', ContactRepo.onFriendAddedFun)
|
||||
ChatKitClient.nim.friendService?.off('onFriendDeleted', ContactRepo.onFriendDeletedFun)
|
||||
ChatKitClient.nim.friendService?.off('onFriendInfoChanged', ContactRepo.onFriendInfoChangedFun)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户账号列表获取用户资料
|
||||
*
|
||||
* @param accountIds 用户 Id 列表。最大为 150 个
|
||||
*/
|
||||
static async getUserList(accountIds: string[]): Promise<V2NIMUser[]> {
|
||||
return await ChatKitClient.nim.userService?.getUserList(accountIds) ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户账号列表获取用户资料-从云端获取
|
||||
*
|
||||
* 注: 其结果会更新本地数据, 建议在需要实时感知用户最新的信息的场景下使用
|
||||
*
|
||||
* @param accountIds 用户 Id 列表。最大为 150 个
|
||||
*/
|
||||
static async getUserListFromCloud(accountIds: string[]): Promise<V2NIMUser[]> {
|
||||
const userList = await ChatKitClient.nim.userService?.getUserListFromCloud(accountIds) ?? []
|
||||
|
||||
let friendList: V2NIMUser[] = []
|
||||
userList.forEach((user) => {
|
||||
const accountId = user.accountId
|
||||
if (accountId === ChatKitClient.getLoginUserId()) {
|
||||
NEFriendUserCache.mineUserCache = new NEUserWithFriend({
|
||||
user: user
|
||||
})
|
||||
}
|
||||
|
||||
if (NEFriendUserCache.getInstance().isFriend(accountId)) {
|
||||
friendList.push(user)
|
||||
NEFriendUserCache.getInstance().updateFriendInfo({
|
||||
user: user
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (friendList.length > 0) {
|
||||
ContactRepo.listener.emit('updateUserInfo', friendList)
|
||||
}
|
||||
|
||||
return userList
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新自己的用户资料。调用该 API 后,会触发 onUserProfileChanged 事件
|
||||
*
|
||||
* @param updateParams 更新参数
|
||||
*/
|
||||
static async updateSelfUserProfile(updateParams: V2NIMUserUpdateParams): Promise<void> {
|
||||
await ChatKitClient.nim.userService?.updateSelfUserProfile(updateParams)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加用户到黑名单中
|
||||
*
|
||||
* @param accountId 用户 Id
|
||||
*/
|
||||
static async addUserToBlockList(accountId: string): Promise<void> {
|
||||
await ChatKitClient.nim.userService?.addUserToBlockList(accountId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从黑名单中移除用户
|
||||
*
|
||||
* @param accountId 用户 Id
|
||||
*/
|
||||
static async removeUserFromBlockList(accountId: string): Promise<void> {
|
||||
await ChatKitClient.nim.userService?.removeUserFromBlockList(accountId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取黑名单列表
|
||||
*/
|
||||
static async getBlockList(): Promise<string[]> {
|
||||
const blockList = await ChatKitClient.nim.userService?.getBlockList() ?? []
|
||||
NEFriendUserCache.getInstance().initBlockAccountSet(blockList)
|
||||
return blockList
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关键词搜索用户信息
|
||||
*
|
||||
* @param option 搜索选项
|
||||
*/
|
||||
static async searchUserByOption(option: V2NIMUserSearchOption): Promise<V2NIMUser[]> {
|
||||
return await ChatKitClient.nim.userService?.searchUserByOption(option) ?? []
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加/申请好友
|
||||
* @param accountId 好友 ID
|
||||
* @param params 申请相关参数
|
||||
*/
|
||||
static async addFriend(accountId: string, params: V2NIMFriendAddParams): Promise<void> {
|
||||
await ChatKitClient.nim.friendService?.addFriend(accountId, params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除好友
|
||||
*
|
||||
* @param accountId 好友 ID
|
||||
* @param params 删除相关参数
|
||||
*/
|
||||
static async deleteFriend(accountId: string, params: V2NIMFriendDeleteParams): Promise<void> {
|
||||
await ChatKitClient.nim.friendService?.deleteFriend(accountId, params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受好友申请
|
||||
*
|
||||
* @param accountId 好友 ID
|
||||
*/
|
||||
static async acceptAddApplication(application: V2NIMFriendAddApplication): Promise<void> {
|
||||
await ChatKitClient.nim.friendService?.acceptAddApplication(application)
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝好友申请
|
||||
*
|
||||
* @param accountId 好友 ID
|
||||
* @param postscript 拒绝时的附言
|
||||
*/
|
||||
static async rejectAddApplication(application: V2NIMFriendAddApplication, postscript?: string): Promise<void> {
|
||||
await ChatKitClient.nim.friendService?.rejectAddApplication(application, postscript)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有好友申请
|
||||
*
|
||||
*/
|
||||
static async clearAllAddApplication(): Promise<void> {
|
||||
await ChatKitClient.nim.friendService?.clearAllAddApplication()
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置好友信息
|
||||
*
|
||||
* @param accountId 好友 ID
|
||||
* @param params 设置好友信息参数
|
||||
*/
|
||||
static async setFriendInfo(accountId: string, params: V2NIMFriendSetParams): Promise<void> {
|
||||
await ChatKitClient.nim.friendService?.setFriendInfo(accountId, params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取好友列表
|
||||
*/
|
||||
static async getFriendList(): Promise<V2NIMFriend[]> {
|
||||
let friendList = await ChatKitClient.nim.friendService?.getFriendList() ?? []
|
||||
if (AppStorage.get<NEFriendUserCache>(NEFriendUserCache.name)) {
|
||||
NEFriendUserCache.getInstance().loadFriendList(friendList)
|
||||
}
|
||||
|
||||
return friendList
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据账号 ID 获取好友信息
|
||||
*
|
||||
* @param accountIds 好友 ID 列表
|
||||
*/
|
||||
static async getFriendByIds(accountIds: string[]): Promise<V2NIMFriend[]> {
|
||||
return await ChatKitClient.nim.friendService?.getFriendByIds(accountIds) ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据账号 ID 获取用户信息(包含好友信息)
|
||||
*
|
||||
* @param accountIds 用户 ID 列表
|
||||
*/
|
||||
static async getUserWithFriendByIds(accountIds: string[]): Promise<NEUserWithFriend[]> {
|
||||
let userWithFriends: NEUserWithFriend[] = []
|
||||
const users = await ContactRepo.getUserListFromCloud(accountIds)
|
||||
const friends = await ContactRepo.getFriendByIds(accountIds)
|
||||
|
||||
let friendsMap: Map<string, NEUserWithFriend> = new Map<string, NEUserWithFriend>()
|
||||
for (const friend of friends) {
|
||||
if (friend.accountId) {
|
||||
friendsMap.set(friend.accountId, new NEUserWithFriend({
|
||||
friend: friend
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const user of users) {
|
||||
const friend = friendsMap.get(user.accountId)?.friend
|
||||
let userWithFriend: NEUserWithFriend = new NEUserWithFriend({
|
||||
friend: friend,
|
||||
user: user
|
||||
})
|
||||
if (friend) {
|
||||
NEFriendUserCache.getInstance().updateFriendInfo({
|
||||
friendUser: userWithFriend
|
||||
})
|
||||
}
|
||||
userWithFriends.push(userWithFriend)
|
||||
}
|
||||
|
||||
return userWithFriends
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据账号 ID 检查好友状态
|
||||
* @param accountIds 好友 ID列表
|
||||
*/
|
||||
static async checkFriend(accountIds: string[]): Promise<V2NIMCheckFriendResult | undefined> {
|
||||
return await ChatKitClient.nim.friendService?.checkFriend(accountIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取申请添加好友列表通知
|
||||
*/
|
||||
static async getAddApplicationList(option: V2NIMFriendAddApplicationQueryOption): Promise<V2NIMFriendAddApplicationResult | undefined> {
|
||||
return await ChatKitClient.nim.friendService?.getAddApplicationList(option)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置好友申请已读
|
||||
*/
|
||||
static async setAddApplicationRead(): Promise<void> {
|
||||
await ChatKitClient.nim.friendService?.setAddApplicationRead()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未读的好友申请数量
|
||||
*/
|
||||
static async getAddApplicationUnreadCount(): Promise<number> {
|
||||
return await ChatKitClient.nim.friendService?.getAddApplicationUnreadCount() ?? 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关键词搜索好友
|
||||
* @param option 搜索好友的条件
|
||||
*/
|
||||
static async searchFriendByOption(option: V2NIMFriendSearchOption): Promise<V2NIMFriend[]> {
|
||||
return await ChatKitClient.nim.friendService?.searchFriendByOption(option) ?? []
|
||||
}
|
||||
}
|
||||
282
chatkit/src/main/ets/repo/ConversationRepo.ets
Normal file
282
chatkit/src/main/ets/repo/ConversationRepo.ets
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
V2NIMConversation,
|
||||
V2NIMConversationFilter,
|
||||
V2NIMConversationOperationResult,
|
||||
V2NIMConversationOption,
|
||||
V2NIMConversationResult,
|
||||
V2NIMConversationType,
|
||||
V2NIMConversationUpdate
|
||||
} from '@nimsdk/base';
|
||||
import { ChatKitClient } from '../ChatKitClient';
|
||||
|
||||
export class ConversationRepo {
|
||||
/**
|
||||
* 获取会话列表
|
||||
*
|
||||
* @param offset 分页偏移量. 首页应传 0, 其他页数据使用返回的 offset
|
||||
* @param limit 分页拉取数量,不建议超过 100
|
||||
*/
|
||||
static async getConversationList(offset: number, limit: number): Promise<V2NIMConversationResult | null> {
|
||||
return await ChatKitClient.nim.conversationService?.getConversationList(offset, limit) ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
* 置顶会话
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 则触发事件 {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged}
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
* @param stickTop 是否置顶. true: 置顶, false: 取消置顶.
|
||||
*/
|
||||
static async stickTopConversation(conversationId: string, stickTop: boolean): Promise<void> {
|
||||
return await ChatKitClient.nim.conversationService?.stickTopConversation(conversationId, stickTop)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除会话
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 会抛出事件 {@link V2NIMConversationListener.onConversationDeleted | V2NIMConversationListener.onConversationCreated}
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
* @param clearMessage 是否删除会话对应的历史消息. 默认为 false
|
||||
*/
|
||||
static async deleteConversation(conversationId: string, clearMessage?: boolean): Promise<void> {
|
||||
return await ChatKitClient.nim.conversationService?.deleteConversation(conversationId, clearMessage)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话列表. 可以指定筛选条件,按会话类型,未读等
|
||||
*
|
||||
* @param offset 会话标记. 首页应传 0, 其他页数据使用返回的 offset
|
||||
* @param limit 分页拉取数量, 不建议超过100
|
||||
* @param option 查询选项
|
||||
*
|
||||
*/
|
||||
static async getConversationListByOption(offset: number, limit: number,
|
||||
option: V2NIMConversationOption): Promise<V2NIMConversationResult | undefined> {
|
||||
return await ChatKitClient.nim.conversationService?.getConversationListByOption(offset, limit, option)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话 id 获取单条会话
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
*/
|
||||
static async getConversation(conversationId: string): Promise<V2NIMConversation | undefined> {
|
||||
return await ChatKitClient.nim.conversationService?.getConversation(conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话 id 获取会话列表
|
||||
*
|
||||
* @param conversationIds 会话 id 列表
|
||||
*
|
||||
*/
|
||||
static async getConversationListByIds(conversationIds: string[]): Promise<V2NIMConversation[] | undefined> {
|
||||
return await ChatKitClient.nim.conversationService?.getConversationListByIds(conversationIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建会话
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 会抛出事件 {@link V2NIMConversationListener.onConversationCreated | V2NIMConversationListener.onConversationCreated}
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
*
|
||||
*/
|
||||
static async createConversation(conversationId: string): Promise<V2NIMConversation | undefined> {
|
||||
return await ChatKitClient.nim.conversationService?.createConversation(conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除会话
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 会抛出事件 {@link V2NIMConversationListener.onConversationDeleted | V2NIMConversationListener.onConversationDeleted}
|
||||
*
|
||||
* @param conversationIds 会话 id 列表
|
||||
* @param clearMessage 是否删除会话对应的历史消息. 默认为 false
|
||||
* @returns 返回操作失败的列表,列表的对象包含会话 id 以及错误信息.
|
||||
*/
|
||||
static async deleteConversationListByIds(conversationIds: string[],
|
||||
clearMessage?: boolean): Promise<V2NIMConversationOperationResult[] | undefined> {
|
||||
return await ChatKitClient.nim.conversationService?.deleteConversationListByIds(conversationIds, clearMessage)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会话
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 触发事件 {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged}
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
* @param updateInfo 欲更新的信息
|
||||
*
|
||||
*/
|
||||
static async updateConversation(conversationId: string, updateInfo: V2NIMConversationUpdate): Promise<void> {
|
||||
return await ChatKitClient.nim.conversationService?.updateConversation(conversationId, updateInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会话的本地扩展字段
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 触发事件 {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged}
|
||||
*
|
||||
* 注2: 字段只能存在内存里, 不能持久化存储. 登出或者重新初始化后 localExtension 都会再次成为空字符串.
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
* @param localExtension 本地扩展信息
|
||||
*
|
||||
*/
|
||||
static async updateConversationLocalExtension(conversationId: string, localExtension: string): Promise<void> {
|
||||
return await ChatKitClient.nim.conversationService?.updateConversationLocalExtension(conversationId, localExtension)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部会话的总的未读数
|
||||
*
|
||||
*/
|
||||
static getTotalUnreadCount(): number | undefined {
|
||||
return ChatKitClient.nim.conversationService?.getTotalUnreadCount()
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 id 列表获取会话的未读数
|
||||
*
|
||||
* @param conversationIds 会话 id 列表
|
||||
*
|
||||
*/
|
||||
static async getUnreadCountByIds(conversationIds: string[]): Promise<number | undefined> {
|
||||
return await ChatKitClient.nim.conversationService?.getUnreadCountByIds(conversationIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据过滤参数获取相应的未读信息
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
*/
|
||||
static async getUnreadCountByFilter(filter: V2NIMConversationFilter): Promise<number | undefined> {
|
||||
return await ChatKitClient.nim.conversationService?.getUnreadCountByFilter(filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有会话总的未读数
|
||||
*
|
||||
* 注: 当该方法调用后,SDK 可能给开发者抛出以下的事件
|
||||
*
|
||||
* {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onTotalUnreadCountChanged | V2NIMConversationListener.onTotalUnreadCountChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onUnreadCountChangedByFilter | V2NIMConversationListener.onUnreadCountChangedByFilter}
|
||||
*/
|
||||
static async clearTotalUnreadCount(): Promise<void> {
|
||||
return await ChatKitClient.nim.conversationService?.clearTotalUnreadCount()
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话 id 列表清空相应会话的未读数
|
||||
*
|
||||
* 注: 当该方法调用后,SDK 可能给开发者抛出以下的事件
|
||||
*
|
||||
* {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onTotalUnreadCountChanged | V2NIMConversationListener.onTotalUnreadCountChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onUnreadCountChangedByFilter | V2NIMConversationListener.onUnreadCountChangedByFilter}
|
||||
*
|
||||
* @param conversationIds 会话 id 列表
|
||||
* @returns 返回操作失败结果的列表
|
||||
*/
|
||||
static async clearUnreadCountByIds(conversationIds: string[]): Promise<V2NIMConversationOperationResult[] | undefined> {
|
||||
return await ChatKitClient.nim.conversationService?.clearUnreadCountByIds(conversationIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除对应指定分组下的会话的未读数
|
||||
*
|
||||
* 注: 当该方法调用后,SDK 可能给开发者抛出以下的事件
|
||||
*
|
||||
* {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onTotalUnreadCountChanged | V2NIMConversationListener.onTotalUnreadCountChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onUnreadCountChangedByFilter | V2NIMConversationListener.onUnreadCountChangedByFilter}
|
||||
*
|
||||
* @param groupId 指定的会话分组 id
|
||||
*/
|
||||
static async clearUnreadCountByGroupId(groupId: string): Promise<void> {
|
||||
return await ChatKitClient.nim.conversationService?.clearUnreadCountByGroupId(groupId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除对应指定类型下的会话的未读数
|
||||
*
|
||||
* 注: 当该方法调用后,SDK 可能给开发者抛出以下的事件
|
||||
*
|
||||
* {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onTotalUnreadCountChanged | V2NIMConversationListener.onTotalUnreadCountChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onUnreadCountChangedByFilter | V2NIMConversationListener.onUnreadCountChangedByFilter}
|
||||
*
|
||||
* @param types 指定的会话类型列表
|
||||
*/
|
||||
static async clearUnreadCountByTypes(types: V2NIMConversationType[]): Promise<void> {
|
||||
return await ChatKitClient.nim.conversationService?.clearUnreadCountByTypes(types)
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅指定过滤条件的会话未读数变化
|
||||
*
|
||||
* 注1: 当订阅该条件后,该 filter 下的未读数发生变化时, 触发 {@link V2NIMConversationListener.onUnreadCountChangedByFilter | V2NIMConversationListener.onUnreadCountChangedByFilter} 事件
|
||||
*
|
||||
* 注2: 同一种 filter 只能被订阅一次, 第二次的调用不会有任何效果
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
*/
|
||||
static subscribeUnreadCountByFilter(filter: V2NIMConversationFilter) {
|
||||
return ChatKitClient.nim.conversationService?.subscribeUnreadCountByFilter(filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅指定过滤条件的会话未读变化
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
*/
|
||||
static unsubscribeUnreadCountByFilter(filter: V2NIMConversationFilter) {
|
||||
return ChatKitClient.nim.conversationService?.unsubscribeUnreadCountByFilter(filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记会话已读时间戳
|
||||
*
|
||||
* 注: 当该方法调用后,SDK 可能给多端账户抛出以下的事件
|
||||
*
|
||||
* {@link V2NIMConversationListener.onConversationReadTimeUpdated | V2NIMConversationListener.onConversationReadTimeUpdated} <br/>
|
||||
*
|
||||
*/
|
||||
static async markConversationRead(conversationId: string): Promise<number | undefined> {
|
||||
return await ChatKitClient.nim.conversationService?.markConversationRead(conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话已读时间戳。该时间包含多端已读时间戳
|
||||
*/
|
||||
static async getConversationReadTime(conversationId: string): Promise<number | undefined> {
|
||||
return await ChatKitClient.nim.conversationService?.getConversationReadTime(conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除会话的所有监听
|
||||
*/
|
||||
static removeAllConversationListener() {
|
||||
//移除
|
||||
ChatKitClient.nim.conversationService?.removeAllListeners('onSyncFinished')
|
||||
|
||||
//会话创建
|
||||
ChatKitClient.nim.conversationService?.removeAllListeners("onConversationCreated")
|
||||
|
||||
//会话删除
|
||||
ChatKitClient.nim.conversationService?.removeAllListeners("onConversationDeleted")
|
||||
//会话更新
|
||||
ChatKitClient.nim.conversationService?.removeAllListeners("onConversationChanged")
|
||||
}
|
||||
}
|
||||
282
chatkit/src/main/ets/repo/LocalConversationRepo.ets
Normal file
282
chatkit/src/main/ets/repo/LocalConversationRepo.ets
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
V2NIMConversationType,
|
||||
V2NIMLocalConversation,
|
||||
V2NIMLocalConversationFilter,
|
||||
V2NIMLocalConversationOperationResult,
|
||||
V2NIMLocalConversationOption,
|
||||
V2NIMLocalConversationResult,
|
||||
} from '@nimsdk/base';
|
||||
import { ChatKitClient } from '../ChatKitClient';
|
||||
|
||||
export class LocalConversationRepo {
|
||||
/**
|
||||
* 获取会话列表
|
||||
*
|
||||
* @param offset 分页偏移量. 首页应传 0, 其他页数据使用返回的 offset
|
||||
* @param limit 分页拉取数量,不建议超过 100
|
||||
*/
|
||||
static async getConversationList(offset: number, limit: number): Promise<V2NIMLocalConversationResult | null> {
|
||||
return await ChatKitClient.nim.localConversationService?.getConversationList(offset, limit) ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
* 置顶会话
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 则触发事件 {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged}
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
* @param stickTop 是否置顶. true: 置顶, false: 取消置顶.
|
||||
*/
|
||||
static async stickTopConversation(conversationId: string, stickTop: boolean): Promise<void> {
|
||||
return await ChatKitClient.nim.localConversationService?.stickTopConversation(conversationId, stickTop)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除会话
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 会抛出事件 {@link V2NIMConversationListener.onConversationDeleted | V2NIMConversationListener.onConversationCreated}
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
* @param clearMessage 是否删除会话对应的历史消息. 默认为 false
|
||||
*/
|
||||
static async deleteConversation(conversationId: string, clearMessage?: boolean): Promise<void> {
|
||||
return await ChatKitClient.nim.localConversationService?.deleteConversation(conversationId, clearMessage)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话列表. 可以指定筛选条件,按会话类型,未读等
|
||||
*
|
||||
* @param offset 会话标记. 首页应传 0, 其他页数据使用返回的 offset
|
||||
* @param limit 分页拉取数量, 不建议超过100
|
||||
* @param option 查询选项
|
||||
*
|
||||
*/
|
||||
static async getConversationListByOption(offset: number, limit: number,
|
||||
option: V2NIMLocalConversationOption): Promise<V2NIMLocalConversationResult | undefined> {
|
||||
return await ChatKitClient.nim.localConversationService?.getConversationListByOption(offset, limit, option)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话 id 获取单条会话
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
*/
|
||||
static async getConversation(conversationId: string): Promise<V2NIMLocalConversation | undefined> {
|
||||
return await ChatKitClient.nim.localConversationService?.getConversation(conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话 id 获取会话列表
|
||||
*
|
||||
* @param conversationIds 会话 id 列表
|
||||
*
|
||||
*/
|
||||
static async getConversationListByIds(conversationIds: string[]): Promise<V2NIMLocalConversation[] | undefined> {
|
||||
return await ChatKitClient.nim.localConversationService?.getConversationListByIds(conversationIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建会话
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 会抛出事件 {@link V2NIMConversationListener.onConversationCreated | V2NIMConversationListener.onConversationCreated}
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
*
|
||||
*/
|
||||
static async createConversation(conversationId: string): Promise<V2NIMLocalConversation | undefined> {
|
||||
return await ChatKitClient.nim.localConversationService?.createConversation(conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除会话
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 会抛出事件 {@link V2NIMConversationListener.onConversationDeleted | V2NIMConversationListener.onConversationDeleted}
|
||||
*
|
||||
* @param conversationIds 会话 id 列表
|
||||
* @param clearMessage 是否删除会话对应的历史消息. 默认为 false
|
||||
* @returns 返回操作失败的列表,列表的对象包含会话 id 以及错误信息.
|
||||
*/
|
||||
static async deleteConversationListByIds(conversationIds: string[],
|
||||
clearMessage?: boolean): Promise<V2NIMLocalConversationOperationResult[] | undefined> {
|
||||
return await ChatKitClient.nim.localConversationService?.deleteConversationListByIds(conversationIds, clearMessage)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会话
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 触发事件 {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged}
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
* @param updateInfo 欲更新的信息
|
||||
*
|
||||
*/
|
||||
// static async updateConversation(conversationId: string, updateInfo: V2NIMLocalConversationUpdate): Promise<void> {
|
||||
// return await ChatKitClient.nim.localConversationService?.updateConversation(conversationId, updateInfo)
|
||||
// }
|
||||
|
||||
/**
|
||||
* 更新会话的本地扩展字段
|
||||
*
|
||||
* 注: 在操作成功且是有效的操作时, 触发事件 {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged}
|
||||
*
|
||||
* 注2: 字段只能存在内存里, 不能持久化存储. 登出或者重新初始化后 localExtension 都会再次成为空字符串.
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
* @param localExtension 本地扩展信息
|
||||
*
|
||||
*/
|
||||
static async updateConversationLocalExtension(conversationId: string, localExtension: string): Promise<void> {
|
||||
return await ChatKitClient.nim.localConversationService?.updateConversationLocalExtension(conversationId,
|
||||
localExtension)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部会话的总的未读数
|
||||
*
|
||||
*/
|
||||
static getTotalUnreadCount(): number | undefined {
|
||||
return ChatKitClient.nim.localConversationService?.getTotalUnreadCount()
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 id 列表获取会话的未读数
|
||||
*
|
||||
* @param conversationIds 会话 id 列表
|
||||
*
|
||||
*/
|
||||
static async getUnreadCountByIds(conversationIds: string[]): Promise<number | undefined> {
|
||||
return await ChatKitClient.nim.localConversationService?.getUnreadCountByIds(conversationIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据过滤参数获取相应的未读信息
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
*/
|
||||
static async getUnreadCountByFilter(filter: V2NIMLocalConversationFilter): Promise<number | undefined> {
|
||||
return await ChatKitClient.nim.localConversationService?.getUnreadCountByFilter(filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有会话总的未读数
|
||||
*
|
||||
* 注: 当该方法调用后,SDK 可能给开发者抛出以下的事件
|
||||
*
|
||||
* {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onTotalUnreadCountChanged | V2NIMConversationListener.onTotalUnreadCountChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onUnreadCountChangedByFilter | V2NIMConversationListener.onUnreadCountChangedByFilter}
|
||||
*/
|
||||
static async clearTotalUnreadCount(): Promise<void> {
|
||||
return await ChatKitClient.nim.localConversationService?.clearTotalUnreadCount()
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会话 id 列表清空相应会话的未读数
|
||||
*
|
||||
* 注: 当该方法调用后,SDK 可能给开发者抛出以下的事件
|
||||
*
|
||||
* {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onTotalUnreadCountChanged | V2NIMConversationListener.onTotalUnreadCountChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onUnreadCountChangedByFilter | V2NIMConversationListener.onUnreadCountChangedByFilter}
|
||||
*
|
||||
* @param conversationIds 会话 id 列表
|
||||
* @returns 返回操作失败结果的列表
|
||||
*/
|
||||
static async clearUnreadCountByIds(conversationIds: string[]): Promise<V2NIMLocalConversationOperationResult[] | undefined> {
|
||||
return await ChatKitClient.nim.localConversationService?.clearUnreadCountByIds(conversationIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除对应指定分组下的会话的未读数
|
||||
*
|
||||
* 注: 当该方法调用后,SDK 可能给开发者抛出以下的事件
|
||||
*
|
||||
* {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onTotalUnreadCountChanged | V2NIMConversationListener.onTotalUnreadCountChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onUnreadCountChangedByFilter | V2NIMConversationListener.onUnreadCountChangedByFilter}
|
||||
*
|
||||
* @param groupId 指定的会话分组 id
|
||||
*/
|
||||
// static async clearUnreadCountByGroupId(groupId: string): Promise<void> {
|
||||
// return await ChatKitClient.nim.localConversationService?.clearUnreadCountByGroupId(groupId)
|
||||
// }
|
||||
|
||||
/**
|
||||
* 清除对应指定类型下的会话的未读数
|
||||
*
|
||||
* 注: 当该方法调用后,SDK 可能给开发者抛出以下的事件
|
||||
*
|
||||
* {@link V2NIMConversationListener.onConversationChanged | V2NIMConversationListener.onConversationChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onTotalUnreadCountChanged | V2NIMConversationListener.onTotalUnreadCountChanged} <br/>
|
||||
* {@link V2NIMConversationListener.onUnreadCountChangedByFilter | V2NIMConversationListener.onUnreadCountChangedByFilter}
|
||||
*
|
||||
* @param types 指定的会话类型列表
|
||||
*/
|
||||
static async clearUnreadCountByTypes(types: V2NIMConversationType[]): Promise<void> {
|
||||
return await ChatKitClient.nim.localConversationService?.clearUnreadCountByTypes(types)
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅指定过滤条件的会话未读数变化
|
||||
*
|
||||
* 注1: 当订阅该条件后,该 filter 下的未读数发生变化时, 触发 {@link V2NIMConversationListener.onUnreadCountChangedByFilter | V2NIMConversationListener.onUnreadCountChangedByFilter} 事件
|
||||
*
|
||||
* 注2: 同一种 filter 只能被订阅一次, 第二次的调用不会有任何效果
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
*/
|
||||
static subscribeUnreadCountByFilter(filter: V2NIMLocalConversationFilter) {
|
||||
return ChatKitClient.nim.localConversationService?.subscribeUnreadCountByFilter(filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅指定过滤条件的会话未读变化
|
||||
*
|
||||
* @param filter 过滤条件
|
||||
*/
|
||||
static unsubscribeUnreadCountByFilter(filter: V2NIMLocalConversationFilter) {
|
||||
return ChatKitClient.nim.localConversationService?.unsubscribeUnreadCountByFilter(filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记会话已读时间戳
|
||||
*
|
||||
* 注: 当该方法调用后,SDK 可能给多端账户抛出以下的事件
|
||||
*
|
||||
* {@link V2NIMConversationListener.onConversationReadTimeUpdated | V2NIMConversationListener.onConversationReadTimeUpdated} <br/>
|
||||
*
|
||||
*/
|
||||
static async markConversationRead(conversationId: string): Promise<number | undefined> {
|
||||
return await ChatKitClient.nim.localConversationService?.markConversationRead(conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话已读时间戳。该时间包含多端已读时间戳
|
||||
*/
|
||||
static async getConversationReadTime(conversationId: string): Promise<number | undefined> {
|
||||
return await ChatKitClient.nim.localConversationService?.getConversationReadTime(conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除会话的所有监听
|
||||
*/
|
||||
static removeAllConversationListener() {
|
||||
//移除
|
||||
ChatKitClient.nim.localConversationService?.removeAllListeners('onSyncFinished')
|
||||
|
||||
//会话创建
|
||||
ChatKitClient.nim.localConversationService?.removeAllListeners("onConversationCreated")
|
||||
|
||||
//会话删除
|
||||
ChatKitClient.nim.localConversationService?.removeAllListeners("onConversationDeleted")
|
||||
//会话更新
|
||||
ChatKitClient.nim.localConversationService?.removeAllListeners("onConversationChanged")
|
||||
}
|
||||
}
|
||||
103
chatkit/src/main/ets/repo/SettingRepo.ets
Normal file
103
chatkit/src/main/ets/repo/SettingRepo.ets
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { V2NIMDndConfig, V2NIMP2PMessageMuteMode, V2NIMTeamMessageMuteMode, V2NIMTeamType } from '@nimsdk/base';
|
||||
import { ChatKitClient } from '../ChatKitClient';
|
||||
|
||||
export class SettingRepo {
|
||||
/**
|
||||
* 获取会话消息免打扰状态
|
||||
*
|
||||
* 注: 若会话类型为群, 则群消息免打扰模式为 {@link V2NIMTeamMessageMuteMode.V2NIM_TEAM_MESSAGE_MUTE_MODE_OFF | V2NIMTeamMessageMuteMode.V2NIM_TEAM_MESSAGE_MUTE_MODE_OFF} 返回为false. 其他的返回 true.
|
||||
*
|
||||
* @param conversationId 会话 id
|
||||
* @return mute 是否被免打扰
|
||||
*/
|
||||
static async getConversationMuteStatus(conversationId: string): Promise<boolean | undefined> {
|
||||
return await ChatKitClient.nim.settingService?.getConversationMuteStatus(conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置群消息免打扰模式
|
||||
*
|
||||
* @param teamId 群组ID
|
||||
* @param teamType 群组类型
|
||||
* @param muteMode 群消息免打扰模式
|
||||
*/
|
||||
static async setTeamMessageMuteMode(teamId: string, teamType: V2NIMTeamType,
|
||||
muteMode: V2NIMTeamMessageMuteMode): Promise<void> {
|
||||
await ChatKitClient.nim.settingService?.setTeamMessageMuteMode(teamId, teamType, muteMode)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群消息免打扰模式
|
||||
*
|
||||
* @param teamId 群组ID
|
||||
* @param teamType 群组类型
|
||||
* @return muteMode 群消息免打扰模式
|
||||
*/
|
||||
static async getTeamMessageMuteMode(teamId: string,
|
||||
teamType: V2NIMTeamType): Promise<V2NIMTeamMessageMuteMode | undefined> {
|
||||
return await ChatKitClient.nim.settingService?.getTeamMessageMuteMode(teamId, teamType)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置点对点消息免打扰模式
|
||||
*
|
||||
* @param accountId 目标账号 ID
|
||||
* @param muteMode 设置用户的免打扰模式
|
||||
*/
|
||||
static async setP2PMessageMuteMode(accountId: string, muteMode: V2NIMP2PMessageMuteMode): Promise<void> {
|
||||
await ChatKitClient.nim.settingService?.setP2PMessageMuteMode(accountId, muteMode)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户消息免打扰模式
|
||||
*
|
||||
* @param accountId 目标账号 ID
|
||||
* @return muteMode p2p 类型消息免打扰模式
|
||||
*/
|
||||
static async getP2PMessageMuteMode(accountId: string): Promise<V2NIMP2PMessageMuteMode | undefined> {
|
||||
return await ChatKitClient.nim.settingService?.getP2PMessageMuteMode(accountId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取点对点消息免打扰列表。
|
||||
*
|
||||
* 返回 V2NIMP2PMessageMuteMode 为 V2NIM_P2P_MESSAGE_MUTE_MODE_ON 的 accountId 列表。
|
||||
*/
|
||||
static async getP2PMessageMuteList(): Promise<string[]> {
|
||||
return await ChatKitClient.nim.settingService?.getP2PMessageMuteList() ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当桌面端在线时,移动端是否需要推送
|
||||
*
|
||||
* @param need 桌面端在线时,移动端是否需要推送
|
||||
*/
|
||||
static async setPushMobileOnDesktopOnline(need: boolean): Promise<void> {
|
||||
await ChatKitClient.nim.settingService?.setPushMobileOnDesktopOnline(need)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Apns免打扰与详情显示
|
||||
*
|
||||
* @param config 免打扰与详情配置参数
|
||||
*/
|
||||
static async setDndConfig(config: V2NIMDndConfig): Promise<void> {
|
||||
await ChatKitClient.nim.settingService?.setDndConfig(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Apns免打扰与详情显示
|
||||
*
|
||||
* @return 免打扰与详情配置参数
|
||||
*/
|
||||
static async getDndConfig(): Promise<V2NIMDndConfig | undefined> {
|
||||
return await ChatKitClient.nim.settingService?.getDndConfig()
|
||||
}
|
||||
}
|
||||
104
chatkit/src/main/ets/repo/StorageRepo.ets
Normal file
104
chatkit/src/main/ets/repo/StorageRepo.ets
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
V2NIMGetMediaResourceInfoResult,
|
||||
V2NIMMessageAttachment,
|
||||
V2NIMProgressCallback,
|
||||
V2NIMSize,
|
||||
V2NIMStorageScene,
|
||||
V2NIMUploadFileParams,
|
||||
V2NIMUploadFileTask
|
||||
} from '@nimsdk/base';
|
||||
import { ChatKitClient } from '../ChatKitClient';
|
||||
|
||||
export class StorageRepo {
|
||||
/**
|
||||
* 设置自定义场景
|
||||
*
|
||||
* @param sceneName 自定义的场景名
|
||||
* @param expireTime 文件过期时间. 单位秒. 数值要求大于等于 86400 秒, 即 1 天.
|
||||
*/
|
||||
static addCustomStorageScene(sceneName: string, expireTime: number): V2NIMStorageScene | undefined {
|
||||
return ChatKitClient.nim.storageService?.addCustomStorageScene(sceneName, expireTime)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询存储场景列表
|
||||
*/
|
||||
static getStorageSceneList(): V2NIMStorageScene[] {
|
||||
return ChatKitClient.nim.storageService?.getStorageSceneList() ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件上传任务
|
||||
*
|
||||
* @param fileParams 上传文件参数
|
||||
* @returns 上传任务
|
||||
*/
|
||||
static createUploadFileTask(fileParams: V2NIMUploadFileParams): V2NIMUploadFileTask | null {
|
||||
return ChatKitClient.nim.storageService?.createUploadFileTask(fileParams) ?? null
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param fileTask 上传任务,createUploadTask 函数返回值
|
||||
* @returns 文件的 url
|
||||
*/
|
||||
static async uploadFile(fileTask: V2NIMUploadFileTask, progress: V2NIMProgressCallback): Promise<string | undefined> {
|
||||
return await ChatKitClient.nim.storageService?.uploadFile(fileTask, progress)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消文件上传
|
||||
*
|
||||
* @param fileTask 上传任务,createUploadTask 函数返回值
|
||||
*/
|
||||
static async cancelUploadFile(fileTask: V2NIMUploadFileTask): Promise<void> {
|
||||
return await ChatKitClient.nim.storageService?.cancelUploadFile(fileTask)
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*
|
||||
* @param url 下载文件 url
|
||||
* @param filePath 文件下载存放的本地路径
|
||||
* @returns 文件的 url
|
||||
*/
|
||||
static async downloadFile(url: string, filePath: string,
|
||||
progress: V2NIMProgressCallback): Promise<string | undefined> {
|
||||
return await ChatKitClient.nim.storageService?.downloadFile(url, filePath, progress)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消文件下载
|
||||
*
|
||||
* @param url 下载文件 url
|
||||
* @returns
|
||||
*/
|
||||
static async cancelDownloadFile(url: string): Promise<void> {
|
||||
return await ChatKitClient.nim.storageService?.cancelDownloadFile(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* 短链接转长链接
|
||||
*
|
||||
* @param url 文件远程地址
|
||||
* @returns 文件的 url
|
||||
*/
|
||||
static async shortUrlToLong(url: string): Promise<string | undefined> {
|
||||
return await ChatKitClient.nim.storageService?.shortUrlToLong(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成图片缩略链接
|
||||
*/
|
||||
static async getImageThumbUrl(attachment: V2NIMMessageAttachment, thumbSize: V2NIMSize): Promise<V2NIMGetMediaResourceInfoResult | undefined> {
|
||||
return await ChatKitClient.nim.storageService?.getImageThumbUrl(attachment, thumbSize)
|
||||
}
|
||||
}
|
||||
649
chatkit/src/main/ets/repo/TeamRepo.ets
Normal file
649
chatkit/src/main/ets/repo/TeamRepo.ets
Normal file
@ -0,0 +1,649 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
V2NIMAntispamConfig,
|
||||
V2NIMCreateTeamParams,
|
||||
V2NIMCreateTeamResult,
|
||||
V2NIMTeam,
|
||||
V2NIMTeamAgreeMode,
|
||||
V2NIMTeamChatBannedMode,
|
||||
V2NIMTeamInviteMode,
|
||||
V2NIMTeamJoinActionInfo,
|
||||
V2NIMTeamJoinActionInfoQueryOption,
|
||||
V2NIMTeamJoinActionInfoResult,
|
||||
V2NIMTeamJoinMode,
|
||||
V2NIMTeamMember,
|
||||
V2NIMTeamMemberListResult,
|
||||
V2NIMTeamMemberQueryOption,
|
||||
V2NIMTeamMemberRole,
|
||||
V2NIMTeamMemberSearchOption,
|
||||
V2NIMTeamMemberSearchResult,
|
||||
V2NIMTeamType,
|
||||
V2NIMTeamUpdateExtensionMode,
|
||||
V2NIMTeamUpdateInfoMode,
|
||||
V2NIMUpdateSelfMemberInfoParams,
|
||||
V2NIMUpdateTeamInfoParams,
|
||||
V2NIMUser
|
||||
} from '@nimsdk/base'
|
||||
import { ChatKitClient } from '../ChatKitClient'
|
||||
import { TeamMemberResult, TeamMemberWithUser } from '../model/TeamMemberWithUser'
|
||||
import { NEFriendUserCache } from '../NEFriendUserCache'
|
||||
import { ContactRepo } from './ContactRepo'
|
||||
|
||||
|
||||
interface TeamExtension {
|
||||
im_ui_kit_group: boolean
|
||||
}
|
||||
|
||||
export class TeamRepo {
|
||||
//默认群头像
|
||||
public static teamDefaultIcons = [
|
||||
"https://s.netease.im/safe/ABg8YjWQWvcqO6sAAAAAAAAAAAA?_im_url=1",
|
||||
"https://s.netease.im/safe/ABg8YjmQWvcqO6sAAAAAAAABAAA?_im_url=1",
|
||||
"https://s.netease.im/safe/ABg8YjyQWvcqO6sAAAAAAAABAAA?_im_url=1",
|
||||
"https://s.netease.im/safe/ABg8YkCQWvcqO6sAAAAAAAABAAA?_im_url=1",
|
||||
"https://s.netease.im/safe/ABg8YkSQWvcqO6sAAAAAAAABAAA?_im_url=1"
|
||||
]
|
||||
|
||||
/**
|
||||
* 创建讨论组
|
||||
* @param inviteeAccountIds
|
||||
* @param postscript
|
||||
* @param antispamConfig
|
||||
* @returns
|
||||
*/
|
||||
static async createGroupTeam(
|
||||
inviteeAccountIds?: string[],
|
||||
postscript?: string,
|
||||
antispamConfig?: V2NIMAntispamConfig): Promise<V2NIMCreateTeamResult | undefined> {
|
||||
let createParams: V2NIMCreateTeamParams = {
|
||||
name: TeamRepo.getTeamNameByMemberId(inviteeAccountIds),
|
||||
teamType: V2NIMTeamType.V2NIM_TEAM_TYPE_NORMAL,
|
||||
joinMode: V2NIMTeamJoinMode.V2NIM_TEAM_JOIN_MODE_FREE,
|
||||
inviteMode: V2NIMTeamInviteMode.V2NIM_TEAM_INVITE_MODE_ALL,
|
||||
agreeMode: V2NIMTeamAgreeMode.V2NIM_TEAM_AGREE_MODE_NO_AUTH,
|
||||
updateInfoMode: V2NIMTeamUpdateInfoMode.V2NIM_TEAM_UPDATE_INFO_MODE_ALL,
|
||||
updateExtensionMode: V2NIMTeamUpdateExtensionMode.V2NIM_TEAM_UPDATE_EXTENSION_MODE_ALL,
|
||||
serverExtension: JSON.stringify({ 'im_ui_kit_group': true }),
|
||||
avatar: TeamRepo.teamDefaultIcons[Math.floor(Math.random() * 5)]
|
||||
}
|
||||
return TeamRepo.createTeam(createParams,
|
||||
inviteeAccountIds, postscript, antispamConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为讨论组
|
||||
* @param team
|
||||
* @returns
|
||||
*/
|
||||
static isGroupTeam(team: V2NIMTeam) {
|
||||
if (team.serverExtension && team.serverExtension.length > 0) {
|
||||
try {
|
||||
let obj = JSON.parse(team.serverExtension) as object | undefined
|
||||
if (obj && obj['im_ui_kit_group'] === true) {
|
||||
return true
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('TeamRepo isGroupTeam json parse error')
|
||||
}
|
||||
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建高级群
|
||||
* @param inviteeAccountIds
|
||||
* @param postscript
|
||||
* @param antispamConfig
|
||||
* @returns
|
||||
*/
|
||||
static async createAdvanceTeam(
|
||||
inviteeAccountIds?: string[],
|
||||
postscript?: string,
|
||||
antispamConfig?: V2NIMAntispamConfig): Promise<V2NIMCreateTeamResult | undefined> {
|
||||
let createParams: V2NIMCreateTeamParams = {
|
||||
name: TeamRepo.getTeamNameByMemberId(inviteeAccountIds),
|
||||
teamType: V2NIMTeamType.V2NIM_TEAM_TYPE_NORMAL,
|
||||
joinMode: V2NIMTeamJoinMode.V2NIM_TEAM_JOIN_MODE_FREE,
|
||||
inviteMode: V2NIMTeamInviteMode.V2NIM_TEAM_INVITE_MODE_MANAGER,
|
||||
agreeMode: V2NIMTeamAgreeMode.V2NIM_TEAM_AGREE_MODE_NO_AUTH,
|
||||
updateInfoMode: V2NIMTeamUpdateInfoMode.V2NIM_TEAM_UPDATE_INFO_MODE_MANAGER,
|
||||
updateExtensionMode: V2NIMTeamUpdateExtensionMode.V2NIM_TEAM_UPDATE_EXTENSION_MODE_MANAGER,
|
||||
avatar: TeamRepo.teamDefaultIcons[Math.floor(Math.random() * 5)]
|
||||
}
|
||||
return TeamRepo.createTeam(createParams,
|
||||
inviteeAccountIds, postscript, antispamConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个群组
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 操作者端(群主), SDK 抛出: {@link V2NIMTeamListener.onTeamCreated | V2NIMTeamListener.onTeamCreated}
|
||||
* - agreeMode 需要被邀请者同意
|
||||
* - 被操作者端, SDK会抛出: {@link V2NIMTeamListener.onReceiveTeamJoinActionInfo | V2NIMTeamListener.onReceiveTeamJoinActionInfo}
|
||||
* - agreeMode 不需被邀请者同意
|
||||
* - 被操作者端, SDK会抛出: {@link V2NIMTeamListener.onTeamJoined | V2NIMTeamListener.onTeamJoined}
|
||||
* - 其他成员端, SDK会抛出: {@link V2NIMTeamListener.onTeamMemberJoined | V2NIMTeamListener.onTeamMemberJoined}
|
||||
*
|
||||
* @param createTeamParams 创建群组参数
|
||||
* @param invitorAccountIds 群组创建时,同时邀请加入群的成员列表
|
||||
* @param postscript 群组创建时,邀请入群的附言
|
||||
* @param antispamConfig 反垃圾参数. 如果开启了安全通,默认采用安全通,该配置不需要配置.
|
||||
* 如果有审核需求,且直接对接易盾,则需要传入该配置
|
||||
*/
|
||||
static async createTeam(createTeamParams: V2NIMCreateTeamParams,
|
||||
inviteeAccountIds?: string[],
|
||||
postscript?: string,
|
||||
antispamConfig?: V2NIMAntispamConfig): Promise<V2NIMCreateTeamResult | undefined> {
|
||||
return await ChatKitClient.nim.teamService?.createTeam(createTeamParams, inviteeAccountIds, postscript,
|
||||
antispamConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改群组信息
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 全员用户端,SDK会抛出: {@link V2NIMTeamListener.onTeamInfoUpdated | V2NIMTeamListener.onTeamInfoUpdated}
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param updateTeamInfoParams 更新群组信息参数
|
||||
* @param antispamConfig 反垃圾参数. 如果开启了安全通,默认采用安全通,该配置不需要配置.
|
||||
* 如果有审核需求,且直接对接易盾,则需要传入该配置
|
||||
*/
|
||||
static async updateTeamInfo(teamId: string,
|
||||
teamType: V2NIMTeamType,
|
||||
updateTeamInfoParams: V2NIMUpdateTeamInfoParams,
|
||||
antispamConfig?: V2NIMAntispamConfig): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.updateTeamInfo(teamId, teamType, updateTeamInfoParams, antispamConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出群组
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 操作者(自己本端),SDK会抛出: {@link V2NIMTeamListener.onTeamLeft | V2NIMTeamListener.onTeamLeft}
|
||||
* - 群内其它用户端, SDK会抛出: {@link V2NIMTeamListener.onTeamMemberLeft | V2NIMTeamListener.onTeamMemberLeft}
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
*/
|
||||
static async leaveTeam(teamId: string, teamType: V2NIMTeamType): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.leaveTeam(teamId, teamType)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群组信息
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
*/
|
||||
static async getTeamInfo(teamId: string, teamType: V2NIMTeamType): Promise<V2NIMTeam | undefined> {
|
||||
return await ChatKitClient.nim.teamService?.getTeamInfo(teamId, teamType)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前已经加入的群组列表
|
||||
*
|
||||
* 注: 群组有效且自己在群中
|
||||
*
|
||||
* @param teamTypes 群类型列表. 若不传入这个字段, 代表这个过滤条件不生效, 则查询所有群组
|
||||
*/
|
||||
static async getJoinedTeamList(teamTypes?: V2NIMTeamType[]): Promise<V2NIMTeam[]> {
|
||||
return await ChatKitClient.nim.teamService?.getJoinedTeamList(teamTypes) ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前已经加入的群组数量
|
||||
*
|
||||
* 注: 群组有效且自己在群中
|
||||
*
|
||||
* @param teamTypes 群类型列表. 若不传入这个字段, 代表这个过滤条件不生效, 则查询所有群组
|
||||
*/
|
||||
static async getJoinedTeamCount(teamTypes?: V2NIMTeamType[]): Promise<number | undefined> {
|
||||
return await ChatKitClient.nim.teamService?.getJoinedTeamCount(teamTypes)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据群组ID获取群组信息
|
||||
*
|
||||
* 每次最多100个群组ID. 先查本地数据,本地缺失再查询云端
|
||||
*
|
||||
* @param teamIds 群组ID列表
|
||||
* @param teamType 群组类型
|
||||
*/
|
||||
static async getTeamInfoByIds(teamIds: string[],
|
||||
teamType: V2NIMTeamType): Promise<V2NIMTeam[]> {
|
||||
return await ChatKitClient.nim.teamService?.getTeamInfoByIds(teamIds, teamType) ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
* 解散群组
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 全员, SDK会抛出: {@link V2NIMTeamListener.onTeamDismissed | V2NIMTeamListener.onTeamDismissed}
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
*/
|
||||
static async dismissTeam(teamId: string, teamType: V2NIMTeamType): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.dismissTeam(teamId, teamType)
|
||||
}
|
||||
|
||||
/**
|
||||
* 邀请成员加入群
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - agreeMode 需要被邀请者同意
|
||||
* - 被操作者端, SDK会抛出: {@link V2NIMTeamListener.onReceiveTeamJoinActionInfo | V2NIMTeamListener.onReceiveTeamJoinActionInfo}
|
||||
* - agreeMode 不需要被邀请者同意
|
||||
* - 被操作者端, SDK会抛出: {@link V2NIMTeamListener.onTeamJoined | V2NIMTeamListener.onTeamJoined}
|
||||
* - 其他成员端, SDK会抛出: {@link V2NIMTeamListener.onTeamMemberJoined | V2NIMTeamListener.onTeamMemberJoined}
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param inviteeAccountIds 邀请加入群的成员账号列表
|
||||
* @param postscript 邀请入群的附言
|
||||
* @returns 邀请失败的账号列表
|
||||
*/
|
||||
static async inviteMember(teamId: string,
|
||||
teamType: V2NIMTeamType,
|
||||
inviteeAccountIds: string[],
|
||||
postscript?: string): Promise<string[]> {
|
||||
return await ChatKitClient.nim.teamService?.inviteMember(teamId, teamType, inviteeAccountIds, postscript) ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受邀请入群
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 操作者(既接受邀请用户)端, SDK会抛出: {@link V2NIMTeamListener.onTeamJoined | V2NIMTeamListener.onTeamJoined}
|
||||
* - 其他成员端, SDK会抛出: {@link V2NIMTeamListener.onTeamMemberJoined | V2NIMTeamListener.onTeamMemberJoined}
|
||||
*
|
||||
* @param invitationInfo 邀请入群的信息
|
||||
*/
|
||||
static async acceptInvitation(invitationInfo: V2NIMTeamJoinActionInfo): Promise<V2NIMTeam | undefined> {
|
||||
return await ChatKitClient.nim.teamService?.acceptInvitation(invitationInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
* 拒绝邀请入群
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 群主或管理员端, SDK会抛出: {@link V2NIMTeamListener.onReceiveTeamJoinActionInfo | V2NIMTeamListener.onReceiveTeamJoinActionInfo}‘
|
||||
*
|
||||
* @param invitationInfo 邀请入群的信息
|
||||
*/
|
||||
static async rejectInvitation(invitationInfo: V2NIMTeamJoinActionInfo, postscript?: string): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.rejectInvitation(invitationInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢出群组成员
|
||||
*
|
||||
* 注1: 只有群主有权限操作改接口
|
||||
*
|
||||
* 注2: 操作成功后, 触发事件的规则如下:
|
||||
* - 被操作者(既被踢用户),SDK会抛出: {@link V2NIMTeamListener.onTeamLeft | V2NIMTeamListener.onTeamLeft}
|
||||
* - 其他成员端,SDK会抛出: {@link V2NIMTeamListener.onTeamMemberKicked | V2NIMTeamListener.onTeamMemberKicked}
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param memberAccountIds 踢出群组的成员账号列表
|
||||
*/
|
||||
static async kickMember(teamId: string, teamType: V2NIMTeamType, memberAccountIds: string[]): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.kickMember(teamId, teamType, memberAccountIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* (用户)申请加入群组
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - joinMode 自由加入
|
||||
* - 操作者端,SDK 会抛出: {@link V2NIMTeamListener.onTeamJoined | V2NIMTeamListener.onTeamJoined}
|
||||
* - 其他成员端, SDK 会抛出: {@link V2NIMTeamListener.onTeamMemberJoined | V2NIMTeamListener.onTeamMemberJoined}
|
||||
* - joinMode 群主管理员同意
|
||||
* - 群主或管理员端,SDK 会抛出 {@link V2NIMTeamListener.onReceiveTeamJoinActionInfo | V2NIMTeamListener.onReceiveTeamJoinActionInfo}
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param postscript 申请附言
|
||||
* @returns 对应的群信息
|
||||
*/
|
||||
static async applyJoinTeam(teamId: string,
|
||||
teamType: V2NIMTeamType,
|
||||
postscript?: string): Promise<V2NIMTeam | undefined> {
|
||||
return await ChatKitClient.nim.teamService?.applyJoinTeam(teamId, teamType, postscript)
|
||||
}
|
||||
|
||||
/**
|
||||
* (管理员)接受(用户的)入群申请
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 被操作者(既被同意用户),SDK会抛出: {@link V2NIMTeamListener.onTeamJoined | V2NIMTeamListener.onTeamJoined}
|
||||
* - 其他成员, SDK会抛出: {@link V2NIMTeamListener.onTeamMemberJoined | V2NIMTeamListener.onTeamMemberJoined}
|
||||
*
|
||||
* @param applicationInfo 该申请的相关信息
|
||||
*/
|
||||
static async acceptJoinApplication(applicationInfo: V2NIMTeamJoinActionInfo): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.acceptJoinApplication(applicationInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
* (管理员)拒绝(用户的)入群申请
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 被操作用户(既被拒绝用户), SDK会抛出: {@link V2NIMTeamListener.onReceiveTeamJoinActionInfo | V2NIMTeamListener.onReceiveTeamJoinActionInfo}
|
||||
*
|
||||
* @param applicationInfo 该申请的相关信息
|
||||
*/
|
||||
static async rejectJoinApplication(applicationInfo: V2NIMTeamJoinActionInfo, postscript?: string): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.rejectJoinApplication(applicationInfo, postscript)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置成员角色
|
||||
*
|
||||
* 注1: 本操作只有群主可操作, 且只能在普通成员与管理员直接角色切换, 如果成员设置角色与当前角色一致,默认请求成功
|
||||
*
|
||||
* 注2: 操作成功后, 触发事件的规则如下:
|
||||
* - 所有成员,SDK会抛出: @link V2NIMTeamListener.onTeamMemberInfoUpdated | V2NIMTeamListener.onTeamMemberInfoUpdated}
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param memberAccountIds 待操作的群组的成员账号列表
|
||||
* @param memberRole 新的角色类型
|
||||
*/
|
||||
static async updateTeamMemberRole(teamId: string,
|
||||
teamType: V2NIMTeamType,
|
||||
memberAccountIds: string[],
|
||||
memberRole: V2NIMTeamMemberRole): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.updateTeamMemberRole(teamId, teamType, memberAccountIds, memberRole)
|
||||
}
|
||||
|
||||
/**
|
||||
* 移交群主
|
||||
*
|
||||
* 注1: 本操作只有群主可操作
|
||||
*
|
||||
* 注2: 操作成功后, 触发事件的规则如下:
|
||||
* - 所有成员,SDK会抛出: {@link V2NIMTeamListener.onTeamInfoUpdated | V2NIMTeamListener.onTeamInfoUpdated}‘
|
||||
* - 若入参 leave 为 true:
|
||||
* - 操作者, SDK会抛出:onTeamLeft
|
||||
* - 其它成员, SDK会抛出:onTeamMemberLeft
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param accountId 新群主的账号 ID
|
||||
* @param leave 转让群主后, 操作者是否同时退出该群. 默认为 false
|
||||
* @returns 该操作的时间戳
|
||||
*/
|
||||
static async transferTeamOwner(teamId: string,
|
||||
teamType: V2NIMTeamType,
|
||||
accountId: string,
|
||||
leave?: boolean): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.transferTeamOwner(teamId, teamType, accountId, leave)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改自己的群成员信息
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 所有成员, SDK会抛出: {@link V2NIMTeamListener.onTeamMemberInfoUpdated | V2NIMTeamListener.onTeamMemberInfoUpdated}
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param memberInfoParams 被修改的字段
|
||||
*/
|
||||
static async updateSelfTeamMemberInfo(teamId: string,
|
||||
teamType: V2NIMTeamType,
|
||||
memberInfoParams: V2NIMUpdateSelfMemberInfoParams): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.updateSelfTeamMemberInfo(teamId, teamType, memberInfoParams)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改群成员昵称
|
||||
*
|
||||
* 注: 只有群主和管理员拥有此权限可操作
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 所有成员,SDK会抛出: {@link V2NIMTeamListener.onTeamMemberInfoUpdated | V2NIMTeamListener.onTeamMemberInfoUpdated}
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param accountId 新群主的账号 ID
|
||||
* @param nick 昵称
|
||||
*/
|
||||
static async updateTeamMemberNick(teamId: string,
|
||||
teamType: V2NIMTeamType,
|
||||
accountId: string,
|
||||
nick: string): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.updateTeamMemberNick(teamId, teamType, accountId, nick)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置群组禁言模式
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 所有成员,SDK会抛出: {@link V2NIMTeamListener.onTeamInfoUpdated | V2NIMTeamListener.onTeamInfoUpdated}
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param chatBannedMode 禁言模式
|
||||
*/
|
||||
static async setTeamChatBannedMode(teamId: string,
|
||||
teamType: V2NIMTeamType,
|
||||
chatBannedMode: V2NIMTeamChatBannedMode): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.setTeamChatBannedMode(teamId, teamType, chatBannedMode)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置群组成员聊天禁言状态
|
||||
*
|
||||
* 注: 操作成功后, 触发事件的规则如下:
|
||||
* - 所有成员, SDK会抛出: {@link V2NIMTeamListener.onTeamMemberInfoUpdated | V2NIMTeamListener.onTeamMemberInfoUpdated}
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param accountId 被修改成员的账号
|
||||
* @param chatBanned 群组中聊天是否被禁言
|
||||
*/
|
||||
static async setTeamMemberChatBannedStatus(teamId: string, teamType: V2NIMTeamType, accountId: string,
|
||||
chatBanned: boolean): Promise<void> {
|
||||
await ChatKitClient.nim.teamService?.setTeamMemberChatBannedStatus(teamId, teamType, accountId, chatBanned)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群成员列表
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param memberRoles 成员角色
|
||||
* @returns 查询结果
|
||||
*/
|
||||
static async getTeamMemberList(teamId: string,
|
||||
teamType: V2NIMTeamType,
|
||||
queryOption: V2NIMTeamMemberQueryOption): Promise<V2NIMTeamMemberListResult | undefined> {
|
||||
return await ChatKitClient.nim.teamService?.getTeamMemberList(teamId, teamType, queryOption)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据账号 ID 列表获取群组成员列表
|
||||
*
|
||||
* @param teamId 群组id
|
||||
* @param teamType 群组类型
|
||||
* @param accountIds 成员的账号 ID 列表
|
||||
* @returns 成员列表
|
||||
*/
|
||||
static async getTeamMemberListByIds(teamId: string,
|
||||
teamType: V2NIMTeamType,
|
||||
accountIds: string[]): Promise<V2NIMTeamMember[]> {
|
||||
return await ChatKitClient.nim.teamService?.getTeamMemberListByIds(teamId, teamType, accountIds) ?? []
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群加入相关信息
|
||||
*
|
||||
* @param option 查询参数
|
||||
*/
|
||||
static async getTeamJoinActionInfoList(option: V2NIMTeamJoinActionInfoQueryOption): Promise<V2NIMTeamJoinActionInfoResult | undefined> {
|
||||
return await ChatKitClient.nim.teamService?.getTeamJoinActionInfoList(option)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关键字搜索群信息
|
||||
* - 混合搜索高级群和超大群,like匹配
|
||||
* - 只搜索群名称
|
||||
*/
|
||||
static async searchTeamByKeyword(keyword: string): Promise<V2NIMTeam[]> {
|
||||
// TODO: 等待SDK实现
|
||||
// return await ChatKitClient.nim.teamService?.searchTeamByKeyword(keyword) ?? []
|
||||
return []
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关键字搜索群成员
|
||||
*
|
||||
* @param searchOption 搜索参数
|
||||
* @param success 成功回调
|
||||
* @param failure 失败回调
|
||||
*/
|
||||
static async searchTeamMembers(searchOption: V2NIMTeamMemberSearchOption): Promise<V2NIMTeamMemberSearchResult | undefined> {
|
||||
// TODO: 等待SDK实现
|
||||
// return await ChatKitClient.nim.teamService?.searchTeamMembers(searchOption)
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取群成员列表,包含用户和好友信息
|
||||
* @param teamId
|
||||
* @param teamType
|
||||
* @param limit
|
||||
* @param nextToken
|
||||
* @returns
|
||||
*/
|
||||
static async getTeamMembers(teamId: string,
|
||||
teamType: V2NIMTeamType, queryOption: V2NIMTeamMemberQueryOption): Promise<TeamMemberResult | undefined> {
|
||||
let teamMemberResult = await TeamRepo.getTeamMemberList(
|
||||
teamId,
|
||||
teamType,
|
||||
queryOption
|
||||
)
|
||||
|
||||
let teamMembers: TeamMemberWithUser[] | undefined = teamMemberResult?.memberList.map((teamMember) => {
|
||||
return new TeamMemberWithUser(teamMember,
|
||||
NEFriendUserCache.getInstance().getFriendById(teamMember.accountId)?.friend,
|
||||
NEFriendUserCache.getInstance().getFriendById(teamMember.accountId)?.user)
|
||||
})
|
||||
|
||||
let noUserAccIdList = teamMembers?.filter((member) => {
|
||||
return !member.userInfo
|
||||
}).map((teamMember) => {
|
||||
return teamMember.teamMember.accountId
|
||||
})
|
||||
|
||||
if (noUserAccIdList && noUserAccIdList.length > 0) {
|
||||
let userList = await ContactRepo.getUserList(noUserAccIdList)
|
||||
|
||||
let userMap = userList.reduce((acc, cur, _index) => {
|
||||
acc.set(cur.accountId, cur)
|
||||
return acc
|
||||
}, new Map<string, V2NIMUser>())
|
||||
|
||||
teamMembers?.forEach((member) => {
|
||||
if (!member.userInfo) {
|
||||
member.userInfo = userMap.get(member.teamMember.accountId)
|
||||
}
|
||||
})
|
||||
}
|
||||
if (teamMemberResult) {
|
||||
let result: TeamMemberResult = {
|
||||
finished: teamMemberResult?.finished,
|
||||
nextToken: teamMemberResult?.nextToken,
|
||||
memberList: teamMembers ?? []
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据成员id查询群成员的信息
|
||||
* @param teamId
|
||||
* @param teamType
|
||||
* @param accountIds
|
||||
* @returns
|
||||
*/
|
||||
static async getTeamMembersByIds(teamId: string,
|
||||
teamType: V2NIMTeamType, accountIds: string[]): Promise<TeamMemberWithUser[]> {
|
||||
let teamMemberList: V2NIMTeamMember[] = []
|
||||
try {
|
||||
teamMemberList = await TeamRepo.getTeamMemberListByIds(
|
||||
teamId,
|
||||
teamType,
|
||||
accountIds
|
||||
)
|
||||
console.debug(`TeamRepo getTeamMembersByIds result = ${teamMemberList.length} `);
|
||||
} catch (e) {
|
||||
console.error(`TeamRepo getTeamMembersByIds ${e}`);
|
||||
}
|
||||
|
||||
let teamMembers: TeamMemberWithUser[] = teamMemberList.map((teamMember) => {
|
||||
return new TeamMemberWithUser(teamMember,
|
||||
NEFriendUserCache.getInstance().getFriendById(teamMember.accountId)?.friend,
|
||||
NEFriendUserCache.getInstance().getFriendById(teamMember.accountId)?.user)
|
||||
})
|
||||
|
||||
let noUserAccIdList = teamMembers?.filter((member) => {
|
||||
return !member.userInfo
|
||||
}).map((teamMember) => {
|
||||
return teamMember.teamMember.accountId
|
||||
})
|
||||
|
||||
if (noUserAccIdList && noUserAccIdList.length > 0) {
|
||||
let userList = await ContactRepo.getUserList(noUserAccIdList)
|
||||
|
||||
let userMap = userList.reduce((acc, cur, _index) => {
|
||||
acc.set(cur.accountId, cur)
|
||||
return acc
|
||||
}, new Map<string, V2NIMUser>())
|
||||
|
||||
teamMembers?.forEach((member) => {
|
||||
if (!member.userInfo) {
|
||||
member.userInfo = userMap.get(member.teamMember.accountId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return teamMembers
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据群成员生成群名
|
||||
* @param inviteeAccountIds
|
||||
* @returns
|
||||
*/
|
||||
private static getTeamNameByMemberId(inviteeAccountIds?: string[]): string {
|
||||
if (inviteeAccountIds) {
|
||||
let inviteeUser = NEFriendUserCache.getInstance().getFriendsByIds(inviteeAccountIds)
|
||||
let inviteeNames = inviteeUser.map(user => user.user?.name ?? user.user?.accountId)
|
||||
let teamName = inviteeNames.slice(0, Math.min(inviteeNames.length, 30)).join('、')
|
||||
let mineName = NEFriendUserCache.mineUserCache?.showName(false)
|
||||
teamName = mineName + '、' + teamName
|
||||
return teamName.slice(0, Math.min(teamName.length, 30))
|
||||
}
|
||||
return ''
|
||||
|
||||
}
|
||||
}
|
||||
57
chatkit/src/main/ets/utils/CustomMessageUtils.ets
Normal file
57
chatkit/src/main/ets/utils/CustomMessageUtils.ets
Normal file
@ -0,0 +1,57 @@
|
||||
import { V2NIMMessageAttachment } from '@nimsdk/base';
|
||||
import { mergedMessageCustomType } from '../constant/Constant';
|
||||
import { MergedMessageAttachment } from '../model/CustomMessageAttachment';
|
||||
|
||||
export class CustomMessageUtils {
|
||||
public static attachmentOfCustomMessage(attachment: V2NIMMessageAttachment) {
|
||||
if (attachment.raw) {
|
||||
try {
|
||||
let attachmentObject = JSON.parse(attachment.raw) as object
|
||||
return attachmentObject
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
public static typeOfCustomMessage(attachment: V2NIMMessageAttachment) {
|
||||
let customAttachment = CustomMessageUtils.attachmentOfCustomMessage(attachment)
|
||||
if (customAttachment) {
|
||||
return customAttachment["type"] as number
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
public static dataOfCustomMessage(attachment: V2NIMMessageAttachment) {
|
||||
let customAttachment = CustomMessageUtils.attachmentOfCustomMessage(attachment)
|
||||
let type = CustomMessageUtils.typeOfCustomMessage(attachment)
|
||||
if (type === mergedMessageCustomType) {
|
||||
return customAttachment?.["data"] as MergedMessageAttachment
|
||||
}
|
||||
return customAttachment?.["data"] as object
|
||||
}
|
||||
|
||||
public static heightOfCustomMessage(attachment: V2NIMMessageAttachment) {
|
||||
let customAttachment = CustomMessageUtils.attachmentOfCustomMessage(attachment)
|
||||
if (customAttachment) {
|
||||
return customAttachment["customHeight"] as number
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
/// 是否是【未知消息】
|
||||
public static isUnknownMessage(attachment?: V2NIMMessageAttachment) {
|
||||
if (attachment) {
|
||||
const customType = CustomMessageUtils.typeOfCustomMessage(attachment)
|
||||
switch (customType) {
|
||||
case mergedMessageCustomType:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
54
chatkit/src/main/ets/utils/ErrorUtils.ets
Normal file
54
chatkit/src/main/ets/utils/ErrorUtils.ets
Normal file
@ -0,0 +1,54 @@
|
||||
import { promptAction } from '@kit.ArkUI';
|
||||
import { V2NIMErrorCode } from '@nimsdk/base';
|
||||
import { ChatKitClient } from '../ChatKitClient';
|
||||
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
export class ErrorUtils {
|
||||
static handleErrorToast(errorCode: number) {
|
||||
|
||||
let msg: ResourceStr = $r('app.string.unknown_error');
|
||||
switch (errorCode) {
|
||||
case V2NIMErrorCode.V2NIM_ERROR_CODE_PIN_LIMIT:
|
||||
msg = $r('app.string.chat_pin_limit_error_tips')
|
||||
break
|
||||
case V2NIMErrorCode.V2NIM_ERROR_CODE_COLLECTION_LIMIT:
|
||||
msg = $r('app.string.chat_collection_limit_error_tips')
|
||||
break
|
||||
case V2NIMErrorCode.V2NIM_ERROR_CODE_ILLEGAL_STATE:
|
||||
msg = $r('app.string.chat_network_error_tips')
|
||||
break
|
||||
}
|
||||
|
||||
try {
|
||||
promptAction.showToast({
|
||||
message: msg,
|
||||
alignment: Alignment.Bottom
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(`showToast args error code is ${error.code}, message is ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
static checkNetworkAndToast(): boolean {
|
||||
if (ChatKitClient.connectBroken()) {
|
||||
ErrorUtils.showToast($r('app.string.chat_network_error_tips'))
|
||||
}
|
||||
return !ChatKitClient.connectBroken()
|
||||
}
|
||||
|
||||
private static showToast(msg: ResourceStr) {
|
||||
try {
|
||||
promptAction.showToast({
|
||||
message: msg,
|
||||
alignment: Alignment.Bottom
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(`showToast args error code is ${error.code}, message is ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
chatkit/src/main/ets/utils/EventHubUtil.ets
Normal file
25
chatkit/src/main/ets/utils/EventHubUtil.ets
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
export class EventHubUtil {
|
||||
private eventhub = getContext(this).eventHub
|
||||
|
||||
/// 订阅事件
|
||||
public on(eventName: string, callback: Function) {
|
||||
this.eventhub.on(eventName, callback)
|
||||
}
|
||||
|
||||
/// 取消订阅事件
|
||||
public off(eventName: string, callback?: Function) {
|
||||
this.eventhub.off(eventName, callback)
|
||||
}
|
||||
|
||||
/// 触发事件
|
||||
public emit(eventName: string, params: Object) {
|
||||
this.eventhub.emit(eventName, params)
|
||||
}
|
||||
}
|
||||
53
chatkit/src/main/ets/utils/MessageUtils.ets
Normal file
53
chatkit/src/main/ets/utils/MessageUtils.ets
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import { systemDateTime } from '@kit.BasicServicesKit';
|
||||
import { V2NIMMessage, V2NIMMessageRevokeNotification } from '@nimsdk/base';
|
||||
import { ChatKitClient } from '../ChatKitClient';
|
||||
import { RevokeMessageExtension } from '../model/RevokeMessageExtension';
|
||||
import { ChatRepo } from '../repo/ChatRepo';
|
||||
|
||||
// 保存撤回消息到本地
|
||||
export function saveLocalRevokeMessage(conversationId: string, msg: V2NIMMessage, edit: boolean) {
|
||||
// let currentTime = systemDateTime.getTime()
|
||||
// let revokeText = getContext().resourceManager.getStringByNameSync('chat_msg_undo_tips');
|
||||
// let revokeMsg = ChatKitClient.nim.messageCreator.createTextMessage(revokeText)
|
||||
// revokeMsg.serverExtension = msg.serverExtension
|
||||
// revokeMsg.threadReply = msg.threadReply
|
||||
// let localExtension = {
|
||||
// revoke_message_local: true,
|
||||
// revoke_message_local_time: currentTime,
|
||||
// revoke_message_client_id: msg.messageClientId,
|
||||
// revoke_message_local_edit: edit,
|
||||
// revoke_message_local_content: msg.text ?? ''
|
||||
//
|
||||
// } as RevokeMessageExtension
|
||||
//
|
||||
// revokeMsg.localExtension = JSON.stringify(localExtension)
|
||||
// let createTime = msg.createTime + 10
|
||||
// console.debug('netease saveLocalRevokeMessage:', revokeMsg.localExtension)
|
||||
// ChatRepo.saveLocalMessage(revokeMsg, conversationId, ChatKitClient.getLoginUserId(), createTime)
|
||||
}
|
||||
|
||||
// 保存他人撤回消息到本地
|
||||
export function saveLocalRevokeMessageFormOther(conversationId: string, msgNotify: V2NIMMessageRevokeNotification,
|
||||
edit: boolean) {
|
||||
// let currentTime = systemDateTime.getTime()
|
||||
// let revokeText = getContext().resourceManager.getStringByNameSync('chat_msg_undo_tips');
|
||||
// let revokeMsg = ChatKitClient.nim.messageCreator.createTextMessage(revokeText)
|
||||
// let localExtension = {
|
||||
// revoke_message_local: false,
|
||||
// revoke_message_local_time: currentTime,
|
||||
// revoke_message_client_id: msgNotify.messageRefer.messageClientId,
|
||||
// revoke_message_local_edit: edit,
|
||||
// revoke_message_local_content: ''
|
||||
// } as RevokeMessageExtension
|
||||
//
|
||||
// revokeMsg.localExtension = JSON.stringify(localExtension)
|
||||
// console.debug('netease saveLocalRevokeMessageFormOther:', revokeMsg.localExtension)
|
||||
// let createTime = msgNotify.messageRefer.createTime + 10;
|
||||
// ChatRepo.saveLocalMessage(revokeMsg, conversationId, msgNotify.revokeAccountId, createTime)
|
||||
}
|
||||
18
chatkit/src/main/ets/utils/Utils.ets
Normal file
18
chatkit/src/main/ets/utils/Utils.ets
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { V2NIMTeam } from '@nimsdk/base';
|
||||
|
||||
const discussTeamKey = "im_ui_kit_group" // 讨论组识别关键字
|
||||
|
||||
/// 判断是否是讨论组
|
||||
export function IsDiscussion(team?: V2NIMTeam) {
|
||||
if (team?.serverExtension && team.serverExtension.includes(discussTeamKey)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
11
chatkit/src/main/module.json5
Normal file
11
chatkit/src/main/module.json5
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"module": {
|
||||
"name": "chatkit",
|
||||
"type": "har",
|
||||
"deviceTypes": [
|
||||
"default",
|
||||
"tablet",
|
||||
"2in1"
|
||||
]
|
||||
}
|
||||
}
|
||||
76
chatkit/src/main/resources/base/element/string.json
Normal file
76
chatkit/src/main/resources/base/element/string.json
Normal file
@ -0,0 +1,76 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "unknown_error",
|
||||
"value": "未知错误"
|
||||
},
|
||||
{
|
||||
"name": "chat_network_error_tips",
|
||||
"value": "当前网络不可用,请检查你的网络设置。"
|
||||
},
|
||||
{
|
||||
"name": "chat_pin_limit_error_tips",
|
||||
"value": "PIN消息已达最大限制"
|
||||
},
|
||||
{
|
||||
"name": "chat_collection_limit_error_tips",
|
||||
"value": "收藏数已达到限制值"
|
||||
},
|
||||
{
|
||||
"name": "chatMessageNonsupportType",
|
||||
"value": "[当前版本暂不支持该消息体]"
|
||||
},
|
||||
{
|
||||
"name": "audioMessageType",
|
||||
"value": "[语音消息]"
|
||||
},
|
||||
{
|
||||
"name": "imageMessageType",
|
||||
"value": "[图片消息]"
|
||||
},
|
||||
{
|
||||
"name": "videoMessageType",
|
||||
"value": "[视频消息]"
|
||||
},
|
||||
{
|
||||
"name": "locationMessageType",
|
||||
"value": "[地理位置]"
|
||||
},
|
||||
{
|
||||
"name": "fileMessageType",
|
||||
"value": "[文件消息]"
|
||||
},
|
||||
{
|
||||
"name": "notificationMessageType",
|
||||
"value": "[通知消息]"
|
||||
},
|
||||
{
|
||||
"name": "tipMessageType",
|
||||
"value": "[提醒消息]"
|
||||
},
|
||||
{
|
||||
"name": "chatHistoryBrief",
|
||||
"value": "聊天记录"
|
||||
},
|
||||
{
|
||||
"name": "msg_type_rtc_video",
|
||||
"value": "[视频通话]"
|
||||
},
|
||||
{
|
||||
"name": "msg_type_rtc_audio",
|
||||
"value": "[语音通话]"
|
||||
},
|
||||
{
|
||||
"name": "msg_send_failed_in_block",
|
||||
"value": "对方已将你拉黑,发送消息失败"
|
||||
},
|
||||
{
|
||||
"name": "msg_send_failed_no_friend",
|
||||
"value": "双方好友关系已解除,如需沟通,请申请"
|
||||
},
|
||||
{
|
||||
"name": "msg_send_failed_friend_application",
|
||||
"value": "好友验证"
|
||||
}
|
||||
]
|
||||
}
|
||||
76
chatkit/src/main/resources/en_US/element/string.json
Normal file
76
chatkit/src/main/resources/en_US/element/string.json
Normal file
@ -0,0 +1,76 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "unknown_error",
|
||||
"value": "Unknown error"
|
||||
},
|
||||
{
|
||||
"name": "chat_network_error_tips",
|
||||
"value": "network error"
|
||||
},
|
||||
{
|
||||
"name": "chat_pin_limit_error_tips",
|
||||
"value": "PIN Message Limit reached"
|
||||
},
|
||||
{
|
||||
"name": "chat_collection_limit_error_tips",
|
||||
"value": "Collection Message Limit reached"
|
||||
},
|
||||
{
|
||||
"name": "chatMessageNonsupportType",
|
||||
"value": "[Nonsupport Message]"
|
||||
},
|
||||
{
|
||||
"name": "audioMessageType",
|
||||
"value": "[Audio Message]"
|
||||
},
|
||||
{
|
||||
"name": "imageMessageType",
|
||||
"value": "[Image Message]"
|
||||
},
|
||||
{
|
||||
"name": "videoMessageType",
|
||||
"value": "[Video Message]"
|
||||
},
|
||||
{
|
||||
"name": "locationMessageType",
|
||||
"value": "[Location Message]"
|
||||
},
|
||||
{
|
||||
"name": "fileMessageType",
|
||||
"value": "[File Message]"
|
||||
},
|
||||
{
|
||||
"name": "notificationMessageType",
|
||||
"value": "[Notification Message]"
|
||||
},
|
||||
{
|
||||
"name": "tipMessageType",
|
||||
"value": "[Tip Message]"
|
||||
},
|
||||
{
|
||||
"name": "chatHistoryBrief",
|
||||
"value": "Chat History"
|
||||
},
|
||||
{
|
||||
"name": "msg_type_rtc_video",
|
||||
"value": "[Video Chat]"
|
||||
},
|
||||
{
|
||||
"name": "msg_type_rtc_audio",
|
||||
"value": "[Audio Chat]"
|
||||
},
|
||||
{
|
||||
"name": "msg_send_failed_in_block",
|
||||
"value": "The other party has blocked you, message sending failed"
|
||||
},
|
||||
{
|
||||
"name": "msg_send_failed_no_friend",
|
||||
"value": "The relationship between both parties has been terminated, please "
|
||||
},
|
||||
{
|
||||
"name": "msg_send_failed_friend_application",
|
||||
"value": "reapply for contact"
|
||||
}
|
||||
]
|
||||
}
|
||||
76
chatkit/src/main/resources/zh_CN/element/string.json
Normal file
76
chatkit/src/main/resources/zh_CN/element/string.json
Normal file
@ -0,0 +1,76 @@
|
||||
{
|
||||
"string": [
|
||||
{
|
||||
"name": "unknown_error",
|
||||
"value": "未知错误"
|
||||
},
|
||||
{
|
||||
"name": "chat_network_error_tips",
|
||||
"value": "当前网络不可用,请检查你的网络设置。"
|
||||
},
|
||||
{
|
||||
"name": "chat_pin_limit_error_tips",
|
||||
"value": "PIN消息已达最大限制"
|
||||
},
|
||||
{
|
||||
"name": "chat_collection_limit_error_tips",
|
||||
"value": "收藏数已达到限制值"
|
||||
},
|
||||
{
|
||||
"name": "chatMessageNonsupportType",
|
||||
"value": "[当前版本暂不支持该消息体]"
|
||||
},
|
||||
{
|
||||
"name": "audioMessageType",
|
||||
"value": "[语音消息]"
|
||||
},
|
||||
{
|
||||
"name": "imageMessageType",
|
||||
"value": "[图片消息]"
|
||||
},
|
||||
{
|
||||
"name": "videoMessageType",
|
||||
"value": "[视频消息]"
|
||||
},
|
||||
{
|
||||
"name": "locationMessageType",
|
||||
"value": "[地理位置]"
|
||||
},
|
||||
{
|
||||
"name": "fileMessageType",
|
||||
"value": "[文件消息]"
|
||||
},
|
||||
{
|
||||
"name": "notificationMessageType",
|
||||
"value": "[通知消息]"
|
||||
},
|
||||
{
|
||||
"name": "tipMessageType",
|
||||
"value": "[提醒消息]"
|
||||
},
|
||||
{
|
||||
"name": "chatHistoryBrief",
|
||||
"value": "聊天记录"
|
||||
},
|
||||
{
|
||||
"name": "msg_type_rtc_video",
|
||||
"value": "[视频通话]"
|
||||
},
|
||||
{
|
||||
"name": "msg_type_rtc_audio",
|
||||
"value": "[语音通话]"
|
||||
},
|
||||
{
|
||||
"name": "msg_send_failed_in_block",
|
||||
"value": "对方已将你拉黑,发送消息失败"
|
||||
},
|
||||
{
|
||||
"name": "msg_send_failed_no_friend",
|
||||
"value": "双方好友关系已解除,如需沟通,请申请"
|
||||
},
|
||||
{
|
||||
"name": "msg_send_failed_friend_application",
|
||||
"value": "好友验证"
|
||||
}
|
||||
]
|
||||
}
|
||||
42
chatkit/src/ohosTest/ets/test/Ability.test.ets
Normal file
42
chatkit/src/ohosTest/ets/test/Ability.test.ets
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { hilog } from '@kit.PerformanceAnalysisKit';
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from '@ohos/hypium';
|
||||
|
||||
export default function abilityTest() {
|
||||
describe('ActsAbilityTest', () => {
|
||||
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
|
||||
beforeAll(() => {
|
||||
// Presets an action, which is performed only once before all test cases of the test suite start.
|
||||
// This API supports only one parameter: preset action function.
|
||||
})
|
||||
beforeEach(() => {
|
||||
// Presets an action, which is performed before each unit test case starts.
|
||||
// The number of execution times is the same as the number of test cases defined by **it**.
|
||||
// This API supports only one parameter: preset action function.
|
||||
})
|
||||
afterEach(() => {
|
||||
// Presets a clear action, which is performed after each unit test case ends.
|
||||
// The number of execution times is the same as the number of test cases defined by **it**.
|
||||
// This API supports only one parameter: clear action function.
|
||||
})
|
||||
afterAll(() => {
|
||||
// Presets a clear action, which is performed after all test cases of the test suite end.
|
||||
// This API supports only one parameter: clear action function.
|
||||
})
|
||||
it('assertContain', 0, () => {
|
||||
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
|
||||
hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
|
||||
let a = 'abc';
|
||||
let b = 'b';
|
||||
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
|
||||
expect(a).assertContain(b);
|
||||
expect(a).assertEqual(a);
|
||||
})
|
||||
})
|
||||
}
|
||||
12
chatkit/src/ohosTest/ets/test/List.test.ets
Normal file
12
chatkit/src/ohosTest/ets/test/List.test.ets
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import abilityTest from './Ability.test';
|
||||
|
||||
export default function testsuite() {
|
||||
abilityTest();
|
||||
}
|
||||
13
chatkit/src/ohosTest/module.json5
Normal file
13
chatkit/src/ohosTest/module.json5
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"module": {
|
||||
"name": "chatkit_test",
|
||||
"type": "feature",
|
||||
"deviceTypes": [
|
||||
"default",
|
||||
"tablet",
|
||||
"2in1"
|
||||
],
|
||||
"deliveryWithInstall": true,
|
||||
"installationFree": false
|
||||
}
|
||||
}
|
||||
12
chatkit/src/test/List.test.ets
Normal file
12
chatkit/src/test/List.test.ets
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import localUnitTest from './LocalUnit.test';
|
||||
|
||||
export default function testsuite() {
|
||||
localUnitTest();
|
||||
}
|
||||
40
chatkit/src/test/LocalUnit.test.ets
Normal file
40
chatkit/src/test/LocalUnit.test.ets
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from '@ohos/hypium';
|
||||
|
||||
export default function localUnitTest() {
|
||||
describe('localUnitTest', () => {
|
||||
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
|
||||
beforeAll(() => {
|
||||
// Presets an action, which is performed only once before all test cases of the test suite start.
|
||||
// This API supports only one parameter: preset action function.
|
||||
});
|
||||
beforeEach(() => {
|
||||
// Presets an action, which is performed before each unit test case starts.
|
||||
// The number of execution times is the same as the number of test cases defined by **it**.
|
||||
// This API supports only one parameter: preset action function.
|
||||
});
|
||||
afterEach(() => {
|
||||
// Presets a clear action, which is performed after each unit test case ends.
|
||||
// The number of execution times is the same as the number of test cases defined by **it**.
|
||||
// This API supports only one parameter: clear action function.
|
||||
});
|
||||
afterAll(() => {
|
||||
// Presets a clear action, which is performed after all test cases of the test suite end.
|
||||
// This API supports only one parameter: clear action function.
|
||||
});
|
||||
it('assertContain', 0, () => {
|
||||
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
|
||||
let a = 'abc';
|
||||
let b = 'b';
|
||||
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
|
||||
expect(a).assertContain(b);
|
||||
expect(a).assertEqual(a);
|
||||
});
|
||||
});
|
||||
}
|
||||
7
chatkit_ui/.gitignore
vendored
Normal file
7
chatkit_ui/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/node_modules
|
||||
/oh_modules
|
||||
/.preview
|
||||
/build
|
||||
/.cxx
|
||||
/.test
|
||||
/oh-package-lock.json5
|
||||
17
chatkit_ui/BuildProfile.ets
Normal file
17
chatkit_ui/BuildProfile.ets
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Use these variables when you tailor your ArkTS code. They must be of the const type.
|
||||
*/
|
||||
export const HAR_VERSION = '10.1.0';
|
||||
export const BUILD_MODE_NAME = 'release';
|
||||
export const DEBUG = false;
|
||||
export const TARGET_NAME = 'default';
|
||||
|
||||
/**
|
||||
* BuildProfile Class is used only for compatibility purposes.
|
||||
*/
|
||||
export default class BuildProfile {
|
||||
static readonly HAR_VERSION = HAR_VERSION;
|
||||
static readonly BUILD_MODE_NAME = BUILD_MODE_NAME;
|
||||
static readonly DEBUG = DEBUG;
|
||||
static readonly TARGET_NAME = TARGET_NAME;
|
||||
}
|
||||
16
chatkit_ui/Index.ets
Normal file
16
chatkit_ui/Index.ets
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
export { NEEmojiManager } from './src/main/ets/manager/NEEmojiManager'
|
||||
|
||||
export { ChatP2PPage } from './src/main/ets/pages/ChatP2PPage'
|
||||
|
||||
export { ForwardMessageDialog } from './src/main/ets/view/ForwardMessageDialog'
|
||||
|
||||
export { TextMessageDetailDialog } from './src/main/ets/view/TextMessageDetailDialog'
|
||||
|
||||
export { MessageItemClick } from './src/main/ets/view/MessageItemClick'
|
||||
28
chatkit_ui/build-profile.json5
Normal file
28
chatkit_ui/build-profile.json5
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"apiType": "stageMode",
|
||||
"buildOption": {
|
||||
},
|
||||
"buildOptionSet": [
|
||||
{
|
||||
"name": "release",
|
||||
"arkOptions": {
|
||||
"obfuscation": {
|
||||
"ruleOptions": {
|
||||
"enable": false,
|
||||
"files": [
|
||||
"./obfuscation-rules.txt"
|
||||
]
|
||||
},
|
||||
"consumerFiles": [
|
||||
"./consumer-rules.txt"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
"targets": [
|
||||
{
|
||||
"name": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
0
chatkit_ui/consumer-rules.txt
Normal file
0
chatkit_ui/consumer-rules.txt
Normal file
13
chatkit_ui/hvigorfile.ts
Normal file
13
chatkit_ui/hvigorfile.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { harTasks } from '@ohos/hvigor-ohos-plugin';
|
||||
|
||||
export default {
|
||||
system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
|
||||
plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
|
||||
}
|
||||
23
chatkit_ui/obfuscation-rules.txt
Normal file
23
chatkit_ui/obfuscation-rules.txt
Normal file
@ -0,0 +1,23 @@
|
||||
# Define project specific obfuscation rules here.
|
||||
# You can include the obfuscation configuration files in the current module's build-profile.json5.
|
||||
#
|
||||
# For more details, see
|
||||
# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5
|
||||
|
||||
# Obfuscation options:
|
||||
# -disable-obfuscation: disable all obfuscations
|
||||
# -enable-property-obfuscation: obfuscate the property names
|
||||
# -enable-toplevel-obfuscation: obfuscate the names in the global scope
|
||||
# -compact: remove unnecessary blank spaces and all line feeds
|
||||
# -remove-log: remove all console.* statements
|
||||
# -print-namecache: print the name cache that contains the mapping from the old names to new names
|
||||
# -apply-namecache: reuse the given cache file
|
||||
|
||||
# Keep options:
|
||||
# -keep-property-name: specifies property names that you want to keep
|
||||
# -keep-global-name: specifies names that you want to keep in the global scope
|
||||
|
||||
-enable-property-obfuscation
|
||||
-enable-toplevel-obfuscation
|
||||
-enable-filename-obfuscation
|
||||
-enable-export-obfuscation
|
||||
19
chatkit_ui/oh-package.json5
Normal file
19
chatkit_ui/oh-package.json5
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@nimkit/chatkit_ui",
|
||||
"version": "10.1.0",
|
||||
"description": "Please describe the basic information.",
|
||||
"main": "Index.ets",
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@nimkit/common": "file:../common",
|
||||
"@nimkit/chatkit": "file:../chatkit",
|
||||
"@nimkit/corekit": "file:../corekit",
|
||||
"@nimsdk/base": "10.9.10",
|
||||
"class-transformer": "^0.5.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
'@nimkit/markdown': "1.1.0",
|
||||
"@itcast/basic":"file:../commons/basic"
|
||||
// 用于嵌套对象@Type反射
|
||||
}
|
||||
}
|
||||
16
chatkit_ui/src/main/ets/ChatKitConfig.ets
Normal file
16
chatkit_ui/src/main/ets/ChatKitConfig.ets
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
export class ChatKitConfig {
|
||||
//消息撤回时间限制
|
||||
static messageRevokeTimeLimit: number = 2 * 60 * 1000;
|
||||
//消息时间展示的间隔,默认5分钟
|
||||
static messageTimeGap: number = 5 * 60 * 1000;
|
||||
//消息分页拉取,每页大小
|
||||
static chatMessagePageSize: number = 50;
|
||||
// 消息已读未读功能
|
||||
static messageReadState: boolean = true;
|
||||
}
|
||||
365
chatkit_ui/src/main/ets/common/ChatUtils.ets
Normal file
365
chatkit_ui/src/main/ets/common/ChatUtils.ets
Normal file
@ -0,0 +1,365 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { filePreview } from '@kit.PreviewKit';
|
||||
import { BusinessError, systemDateTime } from '@kit.BasicServicesKit';
|
||||
import { NEChatMoreOperationData, NEChatMoreOperationType } from '../model/NEChatMoreOperationData';
|
||||
import { NIMMessageInfo } from '../model/NIMMessageInfo';
|
||||
import { V2NIMMessageLocationAttachment, V2NIMMessageSendingState, V2NIMMessageType } from '@nimsdk/base';
|
||||
import { sceneMap } from '@kit.MapKit';
|
||||
import { common } from '@kit.AbilityKit';
|
||||
import { MessageOperationItem, MessageOperationType } from '../model/MessageOperationItem';
|
||||
import { ChatConst } from '../constants/ChatConst';
|
||||
import { ChatKitClient } from '@nimkit/chatkit';
|
||||
import { ChatKitConfig } from '../ChatKitConfig';
|
||||
import { isSupportMessage } from '../common/MessageHelper';
|
||||
|
||||
|
||||
export function openFileWithApp(fileUri: string, fileName: string, fileExt: string, context: Context) {
|
||||
let displayInfo: filePreview.DisplayInfo = {
|
||||
x: 100,
|
||||
y: 100,
|
||||
};
|
||||
let fileInfo: filePreview.PreviewInfo = {
|
||||
title: fileName,
|
||||
uri: fileUri,
|
||||
mimeType: getOpenFileType(fileExt)
|
||||
};
|
||||
filePreview.openPreview(context, fileInfo, displayInfo).then(() => {
|
||||
console.info('Succeeded in opening preview');
|
||||
}).catch((err: BusinessError) => {
|
||||
console.error(`Failed to open preview, err.code = ${err.code}, err.message = ${err.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
export function showLocationDetail(msg: NIMMessageInfo, context: common.UIAbilityContext) {
|
||||
let attachment = msg.message.attachment as V2NIMMessageLocationAttachment
|
||||
|
||||
let queryLocationOptions: sceneMap.LocationQueryOptions = {
|
||||
location: {
|
||||
latitude: attachment.latitude,
|
||||
longitude: attachment.longitude
|
||||
},
|
||||
name: attachment.address,
|
||||
address: attachment.address,
|
||||
};
|
||||
// 拉起地点详情页
|
||||
sceneMap.queryLocation(context, queryLocationOptions).then(() => {
|
||||
console.info("netease QueryLocation", "Succeeded in querying location.");
|
||||
}).catch((err: BusinessError) => {
|
||||
console.error("netease QueryLocation", `Failed to query Location, code: ${err.code}, message: ${err.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
export function setupMoreOperationData() {
|
||||
let operationMoreDataList: Array<NEChatMoreOperationData> = Array()
|
||||
// 照片功能
|
||||
const imageOperationData = new NEChatMoreOperationData();
|
||||
imageOperationData.operationTitle = $r("app.string.photo_take");
|
||||
imageOperationData.type = NEChatMoreOperationType.Image;
|
||||
imageOperationData.imageSource = "app.media.im_icon_images";
|
||||
operationMoreDataList.push(imageOperationData);
|
||||
// 拍摄功能
|
||||
const videoOperationData = new NEChatMoreOperationData();
|
||||
videoOperationData.operationTitle = $r("app.string.chat_camera_take");
|
||||
videoOperationData.type = NEChatMoreOperationType.Video;
|
||||
videoOperationData.imageSource = "app.media.im_icon_camera";
|
||||
operationMoreDataList.push(videoOperationData);
|
||||
//快捷回复
|
||||
const replyOperationData = new NEChatMoreOperationData();
|
||||
replyOperationData.operationTitle = $r("app.string.reply");
|
||||
replyOperationData.type = NEChatMoreOperationType.Reply;
|
||||
replyOperationData.imageSource = "app.media.quck_message";
|
||||
operationMoreDataList.push(replyOperationData);
|
||||
//患教
|
||||
const teachOperationData = new NEChatMoreOperationData();
|
||||
teachOperationData.operationTitle = $r("app.string.teach");
|
||||
teachOperationData.type = NEChatMoreOperationType.Teach;
|
||||
teachOperationData.imageSource = "app.media.patient_teach_call";
|
||||
operationMoreDataList.push(teachOperationData);
|
||||
//出停诊
|
||||
const outOperationData = new NEChatMoreOperationData();
|
||||
outOperationData.operationTitle = $r("app.string.outpatient");
|
||||
outOperationData.type = NEChatMoreOperationType.Outpatient;
|
||||
outOperationData.imageSource = "app.media.outpatient_true";
|
||||
operationMoreDataList.push(outOperationData);
|
||||
//商城
|
||||
const shopOperationData = new NEChatMoreOperationData();
|
||||
shopOperationData.operationTitle = $r("app.string.shopping");
|
||||
shopOperationData.type = NEChatMoreOperationType.Shopping;
|
||||
shopOperationData.imageSource = "app.media.ytx_chattingfooter_shopping";
|
||||
operationMoreDataList.push(shopOperationData);
|
||||
//互联网医院
|
||||
const hospatilOperationData = new NEChatMoreOperationData();
|
||||
hospatilOperationData.operationTitle = $r("app.string.hospital");
|
||||
hospatilOperationData.type = NEChatMoreOperationType.Hospital;
|
||||
hospatilOperationData.imageSource = "app.media.ytx_chatting_hospital";
|
||||
operationMoreDataList.push(hospatilOperationData);
|
||||
// // 位置功能
|
||||
// const locationOperationData = new NEChatMoreOperationData();
|
||||
// locationOperationData.operationTitle = $r("app.string.chat_send_location");
|
||||
// locationOperationData.type = NEChatMoreOperationType.Location;
|
||||
// locationOperationData.imageSource = "app.media.ic_chat_more_location";
|
||||
// operationMoreDataList.push(locationOperationData);
|
||||
//
|
||||
// // 文件功能
|
||||
// const fileOperationData = new NEChatMoreOperationData();
|
||||
// fileOperationData.operationTitle = $r("app.string.chat_send_file");
|
||||
// fileOperationData.type = NEChatMoreOperationType.File;
|
||||
// fileOperationData.imageSource = "app.media.ic_public_chat_file";
|
||||
// operationMoreDataList.push(fileOperationData);
|
||||
|
||||
return operationMoreDataList
|
||||
}
|
||||
|
||||
export function getOpenFileType(fileExt: string): string {
|
||||
|
||||
if (fileExt.includes('txt')) {
|
||||
return 'text/plain'
|
||||
} else if (fileExt.includes('cpp')) {
|
||||
return 'text/x-c++src'
|
||||
} else if (fileExt.includes('c')) {
|
||||
return 'text/x-csrc'
|
||||
} else if (fileExt.includes('h')) {
|
||||
return 'text/x-chdr'
|
||||
} else if (fileExt.includes('java')) {
|
||||
return 'text/x-java'
|
||||
} else if (fileExt.includes('xhtml')) {
|
||||
return 'application/xhtml+xml'
|
||||
} else if (fileExt.includes('xml')) {
|
||||
return 'text/xml'
|
||||
} else if (fileExt.includes('html') || fileExt.includes('htm')) {
|
||||
return 'text/html'
|
||||
} else if (fileExt.includes('pdf')) {
|
||||
return 'application/pdf'
|
||||
} else if (fileExt.includes('jpg')) {
|
||||
return 'image/jpeg'
|
||||
} else if (fileExt.includes('png')) {
|
||||
return 'image/png'
|
||||
} else if (fileExt.includes('gif')) {
|
||||
return 'image/gif'
|
||||
} else if (fileExt.includes('webp')) {
|
||||
return 'image/webp'
|
||||
} else if (fileExt.includes('bmp')) {
|
||||
return 'image/bmp'
|
||||
} else if (fileExt.includes('svg')) {
|
||||
return 'image/svg+xml'
|
||||
} else if (fileExt.includes('m4a')) {
|
||||
return 'audio/mp4a-latm'
|
||||
} else if (fileExt.includes('mp3')) {
|
||||
return 'audio/mpeg'
|
||||
} else if (fileExt.includes('aac')) {
|
||||
return 'audio/aac'
|
||||
} else if (fileExt.includes('ogg')) {
|
||||
return 'audio/ogg'
|
||||
} else if (fileExt.includes('wav')) {
|
||||
return 'audio/x-wav'
|
||||
} else if (fileExt.includes('mp4')) {
|
||||
return 'video/mp4'
|
||||
} else if (fileExt.includes('mkv')) {
|
||||
return 'video/x-matroska'
|
||||
} else if (fileExt.includes('ts')) {
|
||||
return 'video/mp2ts'
|
||||
}
|
||||
|
||||
return ' '
|
||||
}
|
||||
|
||||
export function sliceContent(content: string | undefined, maxsize: number, isCenter: boolean): string {
|
||||
if (!content) {
|
||||
return ''
|
||||
}
|
||||
let result = content
|
||||
if (content.length > maxsize) {
|
||||
if (isCenter) {
|
||||
let startIndex = Math.floor(maxsize * 0.75)
|
||||
let lastIndex = content.length - 2
|
||||
let targetStr = content.charAt(startIndex)
|
||||
let lastTargetStr = content.charAt(lastIndex)
|
||||
// 判断是否为高位
|
||||
if (isCharacterEmoji(targetStr) && !isLastCharacterEmoji(targetStr)) {
|
||||
startIndex++
|
||||
}
|
||||
let nick = content.substring(0, startIndex + 1) + "..."
|
||||
if (isLastCharacterEmoji(lastTargetStr)) {
|
||||
lastIndex++
|
||||
}
|
||||
result = nick + content.substring(lastIndex)
|
||||
} else {
|
||||
let lastIndex = maxsize - 2
|
||||
let lastTargetStr = content.charAt(lastIndex)
|
||||
// 判断是否为高位
|
||||
if (isCharacterEmoji(lastTargetStr) && !isLastCharacterEmoji(lastTargetStr)) {
|
||||
lastIndex++
|
||||
}
|
||||
result = content.substring(0, lastIndex + 1) + "..."
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// 最后是否为表情符号的最后一位
|
||||
export function isLastCharacterEmoji(str: string): boolean {
|
||||
// 使用正则表达式匹配表情符号
|
||||
const emojiRegex = /[\uDC00-\uDFFF]+$/u
|
||||
|
||||
return emojiRegex.test(str)
|
||||
}
|
||||
|
||||
// 最后是否为表情符号
|
||||
export function isCharacterEmoji(str: string): boolean {
|
||||
// 使用正则表达式匹配表情符号
|
||||
const emojiRegex = /[\uD83C-\uDBFF\uDC00-\uDFFF]+$/u
|
||||
|
||||
return emojiRegex.test(str)
|
||||
}
|
||||
|
||||
export function getOperateMenu(operateMsg: NIMMessageInfo | undefined): MessageOperationItem[] {
|
||||
let operationMoreDataList: MessageOperationItem[] = []
|
||||
let localExtension = operateMsg?.message.localExtension;
|
||||
if (localExtension == undefined || !localExtension.includes(ChatConst.revokeLocalKey)) {
|
||||
|
||||
if (operateMsg !== undefined &&
|
||||
operateMsg.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_TEXT) {
|
||||
let copyItem = new MessageOperationItem()
|
||||
copyItem.operationType = MessageOperationType.Copy;
|
||||
copyItem.operationText = $r('app.string.chat_operation_copy');
|
||||
copyItem.operationImage = $r('app.media.ic_chat_menu_copy');
|
||||
operationMoreDataList.push(copyItem)
|
||||
}
|
||||
|
||||
// if (operateMsg !== undefined &&
|
||||
// operateMsg.message.sendingState == V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED
|
||||
// && isSupportMessage(operateMsg)) {
|
||||
// if (operateMsg.message.messageType !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL) {
|
||||
//
|
||||
// let replyItem = new MessageOperationItem()
|
||||
// replyItem.operationType = MessageOperationType.Reply;
|
||||
// replyItem.operationText = $r('app.string.chat_operation_reply');
|
||||
// replyItem.operationImage = $r('app.media.ic_chat_menu_reply');
|
||||
// operationMoreDataList.push(replyItem);
|
||||
//
|
||||
//
|
||||
// if (operateMsg.message.messageType !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_AUDIO) {
|
||||
// let translateItem = new MessageOperationItem()
|
||||
// translateItem.operationType = MessageOperationType.Forward;
|
||||
// translateItem.operationText = $r('app.string.chat_operation_forward');
|
||||
// translateItem.operationImage = $r('app.media.ic_chat_menu_translate');
|
||||
// operationMoreDataList.push(translateItem);
|
||||
// }
|
||||
//
|
||||
// let pinItem = new MessageOperationItem()
|
||||
// if (operateMsg?.isPinMsg) {
|
||||
// pinItem.operationText = $r('app.string.chat_operation_unpin');
|
||||
// pinItem.operationType = MessageOperationType.Unpin;
|
||||
// } else {
|
||||
// pinItem.operationType = MessageOperationType.Pin;
|
||||
// pinItem.operationText = $r('app.string.chat_operation_pin');
|
||||
//
|
||||
// }
|
||||
// pinItem.operationImage = $r('app.media.ic_chat_menu_pin');
|
||||
// operationMoreDataList.push(pinItem);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
let deleteItem = new MessageOperationItem()
|
||||
deleteItem.operationType = MessageOperationType.Delete;
|
||||
deleteItem.operationText = $r('app.string.chat_operation_delete');
|
||||
deleteItem.operationImage = $r('app.media.ic_chat_menu_delete');
|
||||
operationMoreDataList.push(deleteItem);
|
||||
|
||||
if (operateMsg !== undefined &&
|
||||
operateMsg.message.sendingState == V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED
|
||||
&& isSupportMessage(operateMsg) && operateMsg.message.messageType !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL
|
||||
&& systemDateTime.getTime() - operateMsg.message.createTime < ChatKitConfig.messageRevokeTimeLimit
|
||||
&& operateMsg.message.senderId == ChatKitClient.getLoginUserId()) {
|
||||
|
||||
let undoItem = new MessageOperationItem()
|
||||
undoItem.operationImage = $r('app.media.ic_chat_menu_revoke');
|
||||
undoItem.operationText = $r('app.string.chat_operation_undo');
|
||||
undoItem.operationType = MessageOperationType.Undo;
|
||||
operationMoreDataList.push(undoItem)
|
||||
}
|
||||
|
||||
// let selectItem = new MessageOperationItem()
|
||||
// selectItem.operationType = MessageOperationType.Select;
|
||||
// selectItem.operationText = $r('app.string.chat_operation_multi_select');
|
||||
// selectItem.operationImage = $r('app.media.ic_chat_menu_multi_select');
|
||||
// operationMoreDataList.push(selectItem);
|
||||
|
||||
// if (operateMsg !== undefined &&
|
||||
// operateMsg.message.sendingState === V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED
|
||||
// && isSupportMessage(operateMsg)
|
||||
// && operateMsg.message.messageType !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL) {
|
||||
// let collectionItem = new MessageOperationItem()
|
||||
// collectionItem.operationType = MessageOperationType.Collection;
|
||||
// collectionItem.operationText = $r('app.string.chat_operation_collection');
|
||||
// collectionItem.operationImage = $r('app.media.ic_chat_menu_collection');
|
||||
// operationMoreDataList.push(collectionItem);
|
||||
// }
|
||||
}
|
||||
|
||||
return operationMoreDataList
|
||||
}
|
||||
|
||||
export function getOperateMenuSize(msgInfo: NIMMessageInfo | undefined) {
|
||||
let count = 0
|
||||
if (msgInfo == undefined) {
|
||||
return count
|
||||
}
|
||||
let localExtension = msgInfo?.message.localExtension;
|
||||
if (localExtension == undefined || !localExtension.includes(ChatConst.revokeLocalKey)) {
|
||||
// 多选和删除
|
||||
count = count + 2
|
||||
if (msgInfo !== undefined &&
|
||||
msgInfo.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_TEXT) {
|
||||
// 复制
|
||||
count++
|
||||
}
|
||||
|
||||
if (msgInfo.message.sendingState == V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED
|
||||
&& isSupportMessage(msgInfo)) {
|
||||
|
||||
if (msgInfo.message.messageType !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL) {
|
||||
// 回复、收藏
|
||||
count = count + 2
|
||||
if (msgInfo.message.messageType !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_AUDIO) {
|
||||
// 转发、标记
|
||||
count = count + 2
|
||||
} else {
|
||||
// 标记
|
||||
count++
|
||||
}
|
||||
|
||||
if (msgInfo.getMessageType() !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL &&
|
||||
systemDateTime.getTime() - msgInfo.message.createTime < ChatKitConfig.messageRevokeTimeLimit
|
||||
&& msgInfo.message.senderId == ChatKitClient.getLoginUserId()) {
|
||||
// 撤回
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算消息长按弹窗宽度
|
||||
* @param msg 长按的消息
|
||||
* @returns 消息长按弹窗宽度
|
||||
*/
|
||||
export function computeOperateViewWidth(msg: NIMMessageInfo | undefined): number {
|
||||
return ChatConst.menuItemWidth * Math.min(getOperateMenuSize(msg), ChatConst.menuItemColumnNum)
|
||||
}
|
||||
|
||||
export function computeOperateViewHeight(msg: NIMMessageInfo | undefined): number {
|
||||
return ChatConst.menuItemHeight * (Math.ceil(getOperateMenuSize(msg) / ChatConst.menuItemColumnNum))
|
||||
}
|
||||
|
||||
57
chatkit_ui/src/main/ets/common/DateUtils.ets
Normal file
57
chatkit_ui/src/main/ets/common/DateUtils.ets
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { systemDateTime } from '@kit.BasicServicesKit';
|
||||
|
||||
export class DateUtils {
|
||||
static formatTime(msgTime: number, lastTime?: number): string {
|
||||
const msgDate = new Date(msgTime);
|
||||
const nowDate = new Date(systemDateTime.getTime());
|
||||
|
||||
const yearOptions: Intl.DateTimeFormatOptions = {
|
||||
hour12: false,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
};
|
||||
|
||||
const monthOptions: Intl.DateTimeFormatOptions = {
|
||||
hour12: false,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
};
|
||||
|
||||
const minuteOptions: Intl.DateTimeFormatOptions = {
|
||||
hour12: false,
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
};
|
||||
|
||||
if (nowDate.getFullYear() !== msgDate.getFullYear()) {
|
||||
return msgDate.toLocaleString('zh-CN', yearOptions);
|
||||
} else if (nowDate.getMonth() !== msgDate.getMonth() || nowDate.getUTCDate() !== msgDate.getUTCDate()) {
|
||||
return msgDate.toLocaleString('zh-CN', monthOptions);
|
||||
} else {
|
||||
return msgDate.toLocaleString('zh-CN', minuteOptions);
|
||||
}
|
||||
}
|
||||
|
||||
static formatCallTime(time: number): string {
|
||||
const hour: number = Math.floor(time / 3600);
|
||||
const minute: number = Math.floor((time % 3600) / 60);
|
||||
const second: number = time % 60;
|
||||
if (hour === 0) {
|
||||
return `${minute.toString().padStart(2, '0')}:${second.toString().padStart(2, '0')}`;
|
||||
}
|
||||
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${second.toString()
|
||||
.padStart(2, '0')}`;
|
||||
}
|
||||
}
|
||||
23
chatkit_ui/src/main/ets/common/DeviceUtils.ets
Normal file
23
chatkit_ui/src/main/ets/common/DeviceUtils.ets
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
export class DeviceUtils {
|
||||
static windowPXWidth: number = 0;
|
||||
static windowPXHeight: number = 0;
|
||||
static rootDirPath: string = '';
|
||||
private static msgLineWidth: number = 0;
|
||||
|
||||
static getMessageLineWidth(): number {
|
||||
DeviceUtils.msgLineWidth = DeviceUtils.windowPXWidth * 0.75 - vp2px(50);
|
||||
return DeviceUtils.msgLineWidth;
|
||||
}
|
||||
|
||||
static getPinMessageLineWidth(): number {
|
||||
DeviceUtils.msgLineWidth = DeviceUtils.windowPXWidth - vp2px(72);
|
||||
return DeviceUtils.msgLineWidth;
|
||||
}
|
||||
}
|
||||
63
chatkit_ui/src/main/ets/common/FileUtils.ets
Normal file
63
chatkit_ui/src/main/ets/common/FileUtils.ets
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { fileShare } from '@kit.CoreFileKit';
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
|
||||
export class FileUtils {
|
||||
static formatFileSize(bytes: number): string {
|
||||
if (bytes < 1024) {
|
||||
return bytes + "B";
|
||||
} else if (bytes < 1024 * 1024) {
|
||||
return (bytes / 1024).toFixed(2) + "KB";
|
||||
} else if (bytes < 1024 * 1024 * 1024) {
|
||||
return (bytes / (1024 * 1024)).toFixed(2) + "MB";
|
||||
} else {
|
||||
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + "GB";
|
||||
}
|
||||
}
|
||||
|
||||
static getFileExtension(filename: string): string {
|
||||
if (filename && filename.length > 0) {
|
||||
const dot = filename.lastIndexOf('.');
|
||||
if (dot > -1 && dot < filename.length - 1) {
|
||||
return filename.substring(dot + 1);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static openFile(path: string) {
|
||||
try {
|
||||
let uri = path;
|
||||
let policyInfo: fileShare.PolicyInfo = {
|
||||
uri: uri,
|
||||
operationMode: fileShare.OperationMode.READ_MODE,
|
||||
};
|
||||
let policies: Array<fileShare.PolicyInfo> = [policyInfo];
|
||||
fileShare.activatePermission(policies).then(() => {
|
||||
console.info("activatePermission successfully");
|
||||
|
||||
}).catch(async (err: BusinessError<Array<fileShare.PolicyErrorResult>>) => {
|
||||
console.error("activatePermission failed with error message: " + err.message + ", error code: " + err.code);
|
||||
if (err.code == 13900001 && err.data) {
|
||||
for (let i = 0; i < err.data.length; i++) {
|
||||
console.error("error code : " + JSON.stringify(err.data[i].code));
|
||||
console.error("error uri : " + JSON.stringify(err.data[i].uri));
|
||||
console.error("error reason : " + JSON.stringify(err.data[i].message));
|
||||
if (err.data[i].code == fileShare.PolicyErrorCode.PERMISSION_NOT_PERSISTED) {
|
||||
await fileShare.persistPermission(policies);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
let err: BusinessError = error as BusinessError;
|
||||
console.error('activatePermission failed with err: ' + JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
1236
chatkit_ui/src/main/ets/common/MessageHelper.ets
Normal file
1236
chatkit_ui/src/main/ets/common/MessageHelper.ets
Normal file
File diff suppressed because it is too large
Load Diff
55
chatkit_ui/src/main/ets/constants/ChatConst.ets
Normal file
55
chatkit_ui/src/main/ets/constants/ChatConst.ets
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Common constants for all features.
|
||||
*/
|
||||
export class ChatConst {
|
||||
//消息分页拉取,每页大小
|
||||
static readonly chatMessagePageSize: number = 50;
|
||||
// 图片消息图片的高度
|
||||
static readonly imageMessageHeight: number = 124;
|
||||
// 图片消息图片的宽度
|
||||
static readonly imageMessageWidth: number = 222;
|
||||
// 合并转发消息高度
|
||||
static readonly mergedMessageHeight: number = 130;
|
||||
// 合并转发消息最小宽度
|
||||
static readonly mergedMessageMinHeight: number = 80;
|
||||
// 消息单行高度 vp
|
||||
static readonly messageLineHeight: number = 60;
|
||||
// 文本消息单行高度 vp
|
||||
static readonly textLineHeight: number = 20;
|
||||
//文本消息字体大小
|
||||
static readonly messageTextFontSize: number = 14;
|
||||
// 地图消息默认高度 vp
|
||||
static readonly mapMessageHeight: number = 160;
|
||||
// 文件消息默认宽度 vp
|
||||
static readonly fileMessageWidth: number = 60;
|
||||
// 语音消息默认宽度 vp
|
||||
static readonly audioMessageWidth: number = 35;
|
||||
// 消息输入区域高度
|
||||
static readonly messageInputAreaHeight: number = 250;
|
||||
// 消息长按菜单每个item的宽度
|
||||
static readonly menuItemWidth: number = 60;
|
||||
static readonly menuItemHeight: number = 50;
|
||||
// 消息长按菜单列数
|
||||
static readonly menuItemColumnNum: number = 5;
|
||||
// PIN 消息操作者名称最大长度
|
||||
static readonly pinOperatorNameMaxLen: number = 15;
|
||||
// 消息撤回本地存储,是否为本端撤回消息
|
||||
static readonly revokeLocalKey: string = 'revoke_message_local';
|
||||
// 消息撤回本地存储,撤回时间
|
||||
static readonly revokeLocalTimeKey: string = 'revoke_message_local_time';
|
||||
// 消息撤回本地存储,撤回的消息clientId
|
||||
static readonly revokeMsgClientIdKey: string = 'revoke_message_client_id';
|
||||
// 消息撤回本地存储,撤回的消息内容
|
||||
static readonly revokeMsgTextKey: string = 'revoke_message_local_content';
|
||||
// 消息撤回本地存储,撤回的消息是否为编辑状态
|
||||
static readonly revokeMsgEditKey: string = 'revoke_message_local_edit';
|
||||
// 消息删除限制
|
||||
static readonly messageDeleteLimit: number = 50;
|
||||
}
|
||||
150
chatkit_ui/src/main/ets/manager/AudioManager.ets
Normal file
150
chatkit_ui/src/main/ets/manager/AudioManager.ets
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import { audio } from '@kit.AudioKit';
|
||||
import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit';
|
||||
import { fileIo } from '@kit.CoreFileKit';
|
||||
|
||||
const TAG = 'Audio manager';
|
||||
|
||||
class Options {
|
||||
offset?: number;
|
||||
length?: number;
|
||||
}
|
||||
|
||||
let bufferSize: number = 0;
|
||||
let audioCapture: audio.AudioCapturer | undefined = undefined;
|
||||
let audioStreamInfo: audio.AudioStreamInfo = {
|
||||
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率
|
||||
channels: audio.AudioChannel.CHANNEL_2, // 通道
|
||||
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
|
||||
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
|
||||
}
|
||||
let audioCaptureInfo: audio.AudioCapturerInfo = {
|
||||
source: audio.SourceType.SOURCE_TYPE_MIC, // 音源类型
|
||||
capturerFlags: 0 // 音频采集器标志
|
||||
}
|
||||
let audioCaptureOptions: audio.AudioCapturerOptions = {
|
||||
streamInfo: audioStreamInfo,
|
||||
capturerInfo: audioCaptureInfo
|
||||
}
|
||||
|
||||
export class AudioManager {
|
||||
public static instance: AudioManager = new AudioManager()
|
||||
file?: fileIo.File
|
||||
|
||||
private constructor() {
|
||||
console.log('net ease AudioManager constructor')
|
||||
this.setup();
|
||||
}
|
||||
|
||||
// 初始化,创建实例,设置监听事件
|
||||
setup() {
|
||||
// 创建Audio Capture实例
|
||||
audio.createAudioCapturer(audioCaptureOptions, (err, capture) => {
|
||||
if (err) {
|
||||
console.error(`net ease Invoke createAudioCapture failed, code is ${err.code}, message is ${err.message}`);
|
||||
return;
|
||||
}
|
||||
console.info(`net ease ${TAG}: create AudioCapture success`);
|
||||
audioCapture = capture;
|
||||
if (audioCapture !== undefined) {
|
||||
// (audioCapture as audio.AudioCapture).on('readData', readDataCallback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 开始一次音频采集
|
||||
public start(filePath: string) {
|
||||
if (audioCapture !== undefined) {
|
||||
|
||||
// let path = getContext().cacheDir;
|
||||
// let filePath = path + '/StarWars10s-2C-48000-4SW.wav';
|
||||
|
||||
let file: fileIo.File = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
|
||||
this.file = file;
|
||||
let readDataCallback = (buffer: ArrayBuffer) => {
|
||||
let options: Options = {
|
||||
offset: bufferSize,
|
||||
length: buffer.byteLength
|
||||
}
|
||||
fileIo.writeSync(file.fd, buffer, options);
|
||||
bufferSize += buffer.byteLength;
|
||||
}
|
||||
|
||||
audioCapture.on('readData', readDataCallback);
|
||||
|
||||
let stateGroup = [audio.AudioState.STATE_PREPARED, audio.AudioState.STATE_PAUSED, audio.AudioState.STATE_STOPPED];
|
||||
if (stateGroup.indexOf((audioCapture as audio.AudioCapturer).state.valueOf()) ===
|
||||
-1) { // 当且仅当状态为STATE_PREPARED、STATE_PAUSED和STATE_STOPPED之一时才能启动采集
|
||||
console.error(`net ease ${TAG}: start failed`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 启动采集
|
||||
(audioCapture as audio.AudioCapturer).start((err: BusinessError) => {
|
||||
if (err) {
|
||||
console.error('net ease Capture start failed.');
|
||||
} else {
|
||||
console.info('net ease Capture start success.');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 停止采集
|
||||
public stop(callback: AsyncCallback<boolean>) {
|
||||
if (audioCapture !== undefined && this.file != null) {
|
||||
// 只有采集器状态为STATE_RUNNING或STATE_PAUSED的时候才可以停止
|
||||
if ((audioCapture as audio.AudioCapturer).state.valueOf() !== audio.AudioState.STATE_RUNNING &&
|
||||
(audioCapture as audio.AudioCapturer).state.valueOf() !== audio.AudioState.STATE_PAUSED) {
|
||||
console.info('net ease Capture is not running or paused');
|
||||
return;
|
||||
}
|
||||
|
||||
//停止采集
|
||||
(audioCapture as audio.AudioCapturer).stop((err: BusinessError) => {
|
||||
if (err) {
|
||||
console.error('net ease Capture stop failed.');
|
||||
if (callback) {
|
||||
callback(err, false);
|
||||
}
|
||||
} else {
|
||||
fileIo.close(this.file);
|
||||
console.info('net ease Capture stop success.');
|
||||
if (callback) {
|
||||
callback(err, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 销毁实例,释放资源
|
||||
release() {
|
||||
if (audioCapture !== undefined) {
|
||||
// 采集器状态不是STATE_RELEASED或STATE_NEW状态,才能release
|
||||
if ((audioCapture as audio.AudioCapturer).state.valueOf() === audio.AudioState.STATE_RELEASED ||
|
||||
(audioCapture as audio.AudioCapturer).state.valueOf() === audio.AudioState.STATE_NEW) {
|
||||
console.info('net ease Capture already released');
|
||||
return;
|
||||
}
|
||||
|
||||
//释放资源
|
||||
(audioCapture as audio.AudioCapturer).release((err: BusinessError) => {
|
||||
if (err) {
|
||||
console.error('net ease Capture release failed.');
|
||||
} else {
|
||||
console.info('net ease Capture release success.');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
159
chatkit_ui/src/main/ets/manager/AudioPlayerManager.ets
Normal file
159
chatkit_ui/src/main/ets/manager/AudioPlayerManager.ets
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { media } from '@kit.MediaKit';
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
import audio from '@ohos.multimedia.audio'; // 导入audio模块
|
||||
import { CommonConstants } from '@nimkit/common';
|
||||
|
||||
export class AudioPlayerManager {
|
||||
static logTag: string = 'AudioPlayerManager'
|
||||
static instance: AudioPlayerManager = new AudioPlayerManager()
|
||||
onStart?: () => void
|
||||
onFinish?: () => void
|
||||
avPlayer?: media.AVPlayer
|
||||
//正在播放的URL
|
||||
playingUrl?: string
|
||||
//audioRender
|
||||
audioRenderer?: audio.AudioRenderer
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
// 注册avplayer回调函数
|
||||
setAVPlayerCallback(avPlayer: media.AVPlayer) {
|
||||
// error回调监听函数,当avPlayer在操作过程中出现错误时调用 reset接口触发重置流程
|
||||
avPlayer.on('error', (err: BusinessError) => {
|
||||
console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
|
||||
avPlayer.reset(); // 调用reset重置资源,触发idle状态
|
||||
})
|
||||
// 状态机变化回调函数
|
||||
avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
|
||||
switch (state) {
|
||||
case 'idle': // 成功调用reset接口后触发该状态机上报
|
||||
console.debug(`${AudioPlayerManager.logTag}`, 'AVPlayer state idle called.');
|
||||
break;
|
||||
case 'initialized': // avplayer 设置播放源后触发该状态上报
|
||||
console.debug(`${AudioPlayerManager.logTag}`, 'AVPlayer state initialized called.');
|
||||
avPlayer.prepare();
|
||||
break;
|
||||
case 'prepared': // prepare调用成功后上报该状态机
|
||||
console.debug(`${AudioPlayerManager.logTag}`, 'AVPlayer state prepared called.');
|
||||
// avPlayer.audioInterruptMode = audio.InterruptMode.INDEPENDENT_MODE
|
||||
avPlayer.play(); // 调用播放接口开始播放
|
||||
break;
|
||||
case 'playing': // play成功调用后触发该状态机上报
|
||||
console.debug(`${AudioPlayerManager.logTag}`, 'AVPlayer state playing called.');
|
||||
if (this.onStart) {
|
||||
this.onStart()
|
||||
}
|
||||
break;
|
||||
case 'completed': // 播放结束后触发该状态机上报
|
||||
console.debug(`${AudioPlayerManager.logTag}`, 'AVPlayer state completed called.');
|
||||
avPlayer.stop(); //调用播放结束接口
|
||||
break;
|
||||
case 'stopped': // stop接口成功调用后触发该状态机上报
|
||||
console.debug(`${AudioPlayerManager.logTag}`, 'AVPlayer state stopped called.');
|
||||
avPlayer.reset(); // 调用reset接口初始化avplayer状态
|
||||
if (this.onFinish) {
|
||||
this.onFinish()
|
||||
}
|
||||
break;
|
||||
case 'released':
|
||||
console.debug(`${AudioPlayerManager.logTag}`, 'AVPlayer state released called.');
|
||||
break;
|
||||
default:
|
||||
console.info('AVPlayer state unknown called.');
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
||||
// 监听interrupt
|
||||
avPlayer.on('audioInterrupt', (interruptEvent) => {
|
||||
console.debug(`${AudioPlayerManager.logTag} audioInterrupt ${interruptEvent}`)
|
||||
if (this.onFinish) {
|
||||
this.onFinish()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//设置语言播放设备
|
||||
async setAudioPlayDevice() {
|
||||
let audioStreamInfo: audio.AudioStreamInfo = {
|
||||
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100,
|
||||
channels: audio.AudioChannel.CHANNEL_1,
|
||||
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
|
||||
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
|
||||
};
|
||||
|
||||
let audioRendererInfo: audio.AudioRendererInfo = {
|
||||
usage: audio.StreamUsage.STREAM_USAGE_VOICE_MESSAGE,
|
||||
rendererFlags: 0
|
||||
};
|
||||
|
||||
let audioRendererOptions: audio.AudioRendererOptions = {
|
||||
streamInfo: audioStreamInfo,
|
||||
rendererInfo: audioRendererInfo
|
||||
};
|
||||
|
||||
|
||||
this.audioRenderer = await audio.createAudioRenderer(audioRendererOptions)
|
||||
let earpieceOpen = AppStorage.get<boolean>(CommonConstants.KEY_SETTING_EARPIECE_MODE)
|
||||
console.log(`${AudioPlayerManager.logTag} , earpieceOpen = ${earpieceOpen}`)
|
||||
await this.audioRenderer.setDefaultOutputDevice((earpieceOpen ?? true) ? audio.DeviceType.EARPIECE :
|
||||
audio.DeviceType.SPEAKER)
|
||||
if (this.audioRenderer?.state !== audio.AudioState.STATE_RUNNING) {
|
||||
await this.audioRenderer.start()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 通过url设置网络地址来实现播放直播码流
|
||||
async avPlayerLive(url: string, onStart?: () => void,
|
||||
onFinish?: () => void) {
|
||||
await this.setAudioPlayDevice()
|
||||
// 创建avPlayer实例对象
|
||||
if (!this.avPlayer) {
|
||||
this.avPlayer = await media.createAVPlayer();
|
||||
}
|
||||
|
||||
if (this.onFinish) {
|
||||
this.onFinish()
|
||||
}
|
||||
|
||||
//重复点击,如果已经播放则停止播放,并返回直接返回
|
||||
if (this.playingUrl === url
|
||||
&& (this.avPlayer?.state === 'prepared' ||
|
||||
this.avPlayer?.state === 'playing')) {
|
||||
this.playingUrl = undefined
|
||||
await this.avPlayer?.reset()
|
||||
return
|
||||
}
|
||||
|
||||
await this.avPlayer?.reset()
|
||||
this.playingUrl = url
|
||||
|
||||
this.onStart = onStart
|
||||
|
||||
this.onFinish = onFinish
|
||||
|
||||
// 创建状态机变化回调函数
|
||||
this.setAVPlayerCallback(this.avPlayer);
|
||||
this.avPlayer.url = url
|
||||
}
|
||||
|
||||
async stopPlayAll() {
|
||||
console.log(`${AudioPlayerManager.logTag}, stopPlayAll`)
|
||||
if (this.onFinish) {
|
||||
this.onFinish()
|
||||
}
|
||||
await this.avPlayer?.reset()
|
||||
await this.audioRenderer?.stop()
|
||||
this.audioRenderer = undefined
|
||||
}
|
||||
}
|
||||
129
chatkit_ui/src/main/ets/manager/AudioRecordManager.ets
Normal file
129
chatkit_ui/src/main/ets/manager/AudioRecordManager.ets
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { media } from '@kit.MediaKit';
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
import fs from '@ohos.file.fs';
|
||||
|
||||
let avProfile: media.AVRecorderProfile = {
|
||||
audioBitrate: 100000, // 音频比特率
|
||||
audioChannels: 2, // 音频声道数
|
||||
audioCodec: media.CodecMimeType.AUDIO_AAC, // 音频编码格式,当前只支持aac
|
||||
audioSampleRate: 48000, // 音频采样率
|
||||
fileFormat: media.ContainerFormatType.CFT_MPEG_4A, // 封装格式,当前只支持m4a
|
||||
}
|
||||
|
||||
|
||||
let avConfig: media.AVRecorderConfig = {
|
||||
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, // 音频输入源,这里设置为麦克风
|
||||
profile: avProfile,
|
||||
url: '', // 参考应用文件访问与管理开发示例新建并读写一个文件
|
||||
};
|
||||
|
||||
export class AudioRecordManager {
|
||||
public static instance: AudioRecordManager = new AudioRecordManager()
|
||||
private avRecorder?: media.AVRecorder;
|
||||
|
||||
// 初始化
|
||||
public setup() {
|
||||
if (this.avRecorder === undefined) {
|
||||
media.createAVRecorder().then((recorder: media.AVRecorder) => {
|
||||
console.log('net ease AudioRecordManager create success');
|
||||
this.avRecorder = recorder;
|
||||
this.setupAudioRecordListen();
|
||||
}, (error: BusinessError) => {
|
||||
console.error(`net ease createAVRecorder failed ${error}`);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 注册audioRecorder回调函数
|
||||
setAudioRecorderCallback() {
|
||||
if (this.avRecorder !== undefined) {
|
||||
// 状态机变化回调函数
|
||||
this.avRecorder.on('stateChange', (state: media.AVRecorderState, reason: media.StateChangeReason) => {
|
||||
console.log(`net ease AudioRecorder current state is ${state}`);
|
||||
})
|
||||
// 错误上报回调函数
|
||||
this.avRecorder.on('error', (err: BusinessError) => {
|
||||
console.error(`net ease AudioRecorder failed, code is ${err.code}, message is ${err.message}`);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async startRecordingProcess(path: string) {
|
||||
if (this.avRecorder !== undefined) {
|
||||
await this.avRecorder.release();
|
||||
this.avRecorder = undefined;
|
||||
}
|
||||
// await FileUtils.createFile(path)
|
||||
|
||||
// 1.创建录制实例
|
||||
this.avRecorder = await media.createAVRecorder();
|
||||
this.setAudioRecorderCallback();
|
||||
// 2.获取录制文件fd赋予avConfig里的url;参考FilePicker文档
|
||||
let file = fs.openSync(path, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
|
||||
let url = 'fd://' + file.fd
|
||||
|
||||
console.log("net ease AudioRecorder url is: " + url);
|
||||
avConfig.url = url;
|
||||
|
||||
// 3.配置录制参数完成准备工作
|
||||
await this.avRecorder.prepare(avConfig);
|
||||
// 4.开始录制
|
||||
await this.avRecorder.start();
|
||||
|
||||
console.log('net ease AudioRecorder start success')
|
||||
}
|
||||
|
||||
// 暂停录制对应的流程
|
||||
async pauseRecordingProcess() {
|
||||
if (this.avRecorder != undefined && this.avRecorder.state === 'started') { // 仅在started状态下调用pause为合理状态切换
|
||||
await this.avRecorder.pause();
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复录制对应的流程
|
||||
async resumeRecordingProcess() {
|
||||
if (this.avRecorder != undefined && this.avRecorder.state === 'paused') { // 仅在paused状态下调用resume为合理状态切换
|
||||
await this.avRecorder.resume();
|
||||
}
|
||||
}
|
||||
|
||||
// 停止录制对应的流程
|
||||
async stopRecordingProcess() {
|
||||
if (this.avRecorder != undefined) {
|
||||
// 1. 停止录制
|
||||
if (this.avRecorder.state === 'started'
|
||||
|| this.avRecorder.state === 'paused') { // 仅在started或者paused状态下调用stop为合理状态切换
|
||||
await this.avRecorder.stop();
|
||||
}
|
||||
// 2.重置
|
||||
await this.avRecorder.reset();
|
||||
// 3.释放录制实例
|
||||
await this.avRecorder.release();
|
||||
this.avRecorder = undefined;
|
||||
// 4.关闭录制文件fd
|
||||
}
|
||||
}
|
||||
|
||||
// 回调监听
|
||||
private setupAudioRecordListen() {
|
||||
if (this.avRecorder !== undefined) {
|
||||
// 状态上报回调函数
|
||||
this.avRecorder.on('stateChange', (state: media.AVRecorderState, reason: media.StateChangeReason) => {
|
||||
console.log(`net ease current state is ${state}`);
|
||||
// 用户可以在此补充状态发生切换后想要进行的动作
|
||||
})
|
||||
|
||||
// 错误上报回调函数
|
||||
this.avRecorder.on('error', (err: BusinessError) => {
|
||||
console.error(`net ease avRecorder failed, code is ${err.code}, message is ${err.message}`);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
108
chatkit_ui/src/main/ets/manager/NEAudioMetaManager.ets
Normal file
108
chatkit_ui/src/main/ets/manager/NEAudioMetaManager.ets
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { media } from '@kit.MediaKit';
|
||||
import { fileIo } from '@kit.CoreFileKit';
|
||||
import { AsyncCallback } from '@kit.BasicServicesKit';
|
||||
import { image } from '@kit.ImageKit';
|
||||
|
||||
const TAG = 'net ease'
|
||||
|
||||
export class NEAudioMetaManager {
|
||||
public static instance: NEAudioMetaManager = new NEAudioMetaManager()
|
||||
|
||||
// 获取音频专辑封面(Resource 文件夹下)
|
||||
async fetchMetadataFromFdSrcByCallback(fileName: string, callback: AsyncCallback<image.PixelMap>) {
|
||||
if (canIUse("SystemCapability.Multimedia.Media.AVMetadataExtractor")) {
|
||||
// 创建AVMetadataExtractor对象
|
||||
let avMetadataExtractor: media.AVMetadataExtractor = await media.createAVMetadataExtractor()
|
||||
|
||||
// 设置fdSrc
|
||||
avMetadataExtractor.fdSrc = await getContext(this).resourceManager.getRawFd(fileName);
|
||||
|
||||
// 获取元数据(callback模式)
|
||||
avMetadataExtractor.fetchMetadata((error, metadata) => {
|
||||
if (error) {
|
||||
console.error(TAG, `fetchMetadata callback failed, err = ${JSON.stringify(error)}`)
|
||||
return
|
||||
}
|
||||
console.info(TAG, `fetchMetadata callback success, genre: ${metadata.genre}`)
|
||||
})
|
||||
|
||||
//获取专辑封面(callback模式)
|
||||
avMetadataExtractor.fetchAlbumCover((err, pixelMap) => {
|
||||
if (err) {
|
||||
console.error(TAG, `fetchAlbumCover callback failed, err = ${JSON.stringify(err)}`)
|
||||
return
|
||||
}
|
||||
if (callback) {
|
||||
callback(err, pixelMap);
|
||||
}
|
||||
|
||||
// 释放资源(callback模式)
|
||||
avMetadataExtractor.release((error) => {
|
||||
if (error) {
|
||||
console.error(TAG, `release failed, err = ${JSON.stringify(error)}`)
|
||||
return
|
||||
}
|
||||
console.info(TAG, `release success.`)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 使用fs文件系统打开沙箱地址获取媒体文件地址,设置dataSrc属性,获取音频元数据并打印
|
||||
async fetchMetadataFromDataSrc(filePath: string, callback: AsyncCallback<media.AVMetadata>) {
|
||||
|
||||
let fd: number = fileIo.openSync(filePath, 0o0).fd;
|
||||
let fileSize: number = fileIo.statSync(filePath).size;
|
||||
// 设置dataSrc描述符,通过callback从文件中获取资源,写入buffer中
|
||||
let dataSrc: media.AVDataSrcDescriptor = {
|
||||
fileSize: fileSize,
|
||||
callback: (buffer, len, pos) => {
|
||||
if (buffer == undefined || len == undefined || pos == undefined) {
|
||||
console.error(TAG, `dataSrc callback param invalid`)
|
||||
return -1
|
||||
}
|
||||
|
||||
class Option {
|
||||
offset: number | undefined = 0;
|
||||
length: number | undefined = len;
|
||||
position: number | undefined = pos;
|
||||
}
|
||||
|
||||
let options = new Option();
|
||||
let num = fileIo.readSync(fd, buffer, options)
|
||||
console.info(TAG, 'readAt end, num: ' + num)
|
||||
if (num > 0 && fileSize >= pos) {
|
||||
return num;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (canIUse("SystemCapability.Multimedia.Media.AVMetadataExtractor")) {
|
||||
// 创建AVMetadataExtractor对象
|
||||
let avMetadataExtractor = await media.createAVMetadataExtractor()
|
||||
// 设置dataSrc
|
||||
avMetadataExtractor.dataSrc = dataSrc;
|
||||
|
||||
// 获取元数据(promise模式)
|
||||
let metadata = await avMetadataExtractor.fetchMetadata()
|
||||
console.info(TAG, `get meta data, mimeType: ${metadata.mimeType}`)
|
||||
|
||||
// 获取专辑封面(promise模式)
|
||||
if (callback) {
|
||||
callback(undefined, metadata);
|
||||
}
|
||||
|
||||
// 释放资源(promise模式)
|
||||
avMetadataExtractor.release()
|
||||
console.info(TAG, `release data source success.`)
|
||||
}
|
||||
}
|
||||
}
|
||||
157
chatkit_ui/src/main/ets/manager/NEEmojiManager.ets
Normal file
157
chatkit_ui/src/main/ets/manager/NEEmojiManager.ets
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import common from '@ohos.app.ability.common';
|
||||
import util from '@ohos.util';
|
||||
|
||||
export enum NIMEmoticonType {
|
||||
file = 1,
|
||||
delete = 2,
|
||||
}
|
||||
|
||||
export enum EmojiParseType {
|
||||
// 表情
|
||||
emoji = 1,
|
||||
// 文本
|
||||
text = 2,
|
||||
}
|
||||
|
||||
export class NEEmojiParseResult {
|
||||
// 解析类型
|
||||
type: EmojiParseType = EmojiParseType.text;
|
||||
//开始坐标
|
||||
startIndex: number = 0
|
||||
// 解析文本内容
|
||||
text?: string;
|
||||
// 解析表情
|
||||
emoji?: NIMInputEmoticon;
|
||||
}
|
||||
|
||||
export class NIMInputEmoticon {
|
||||
type: NIMEmoticonType = NIMEmoticonType.file;
|
||||
id?: string;
|
||||
tag?: string;
|
||||
file?: string;
|
||||
}
|
||||
|
||||
export class EmojiDataModel {
|
||||
sourceData: Array<NIMInputEmoticon> = [];
|
||||
}
|
||||
|
||||
export class NEEmojiManager {
|
||||
public static instance: NEEmojiManager = new NEEmojiManager()
|
||||
dataModel: EmojiDataModel = new EmojiDataModel();
|
||||
public deleteEmoji: NIMInputEmoticon = new NIMInputEmoticon();
|
||||
// 根据文件名获取表情
|
||||
private fileEmojiCache: Map<string, NIMInputEmoticon> = new Map();
|
||||
// 根据tag获取表情
|
||||
private tagEmojiCache: Map<string, NIMInputEmoticon> = new Map();
|
||||
private pattern = /\[([^\]]+)\]/g;
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
// 初始化表情管理器
|
||||
public setup(): void {
|
||||
// 获取表情列表
|
||||
if (this.dataModel.sourceData.length === 0) {
|
||||
this.getEmojiList();
|
||||
}
|
||||
}
|
||||
|
||||
// 获取表情列表
|
||||
async getEmojiList() {
|
||||
this.deleteEmoji.file = 'emoji_del_normal.png';
|
||||
this.deleteEmoji.type = NIMEmoticonType.delete;
|
||||
let context = getContext() as common.UIAbilityContext;
|
||||
context.resourceManager.getRawFileContent('emoji_en.json', (err, rawFile) => {
|
||||
if (err) {
|
||||
console.error('net ease getRawFileContent failed, err is: ' + err)
|
||||
return
|
||||
}
|
||||
try {
|
||||
let textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true })
|
||||
let retStr = textDecoder.decode(rawFile);
|
||||
let dataModel: EmojiDataModel = JSON.parse(retStr);
|
||||
this.dataModel = dataModel;
|
||||
dataModel.sourceData.forEach((emoji) => {
|
||||
emoji.type = NIMEmoticonType.file;
|
||||
if (emoji.file !== undefined) {
|
||||
this.fileEmojiCache.set(emoji.file, emoji);
|
||||
}
|
||||
if (emoji.tag !== undefined) {
|
||||
this.tagEmojiCache.set(emoji.tag, emoji);
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('net ease get emoji list failed, err is: ' + e)
|
||||
}
|
||||
})
|
||||
this.fileEmojiCache.set(this.deleteEmoji.file, this.deleteEmoji);
|
||||
}
|
||||
|
||||
getEmojiByName(fileName: string): NIMInputEmoticon | undefined {
|
||||
return this.fileEmojiCache.get(fileName)
|
||||
}
|
||||
|
||||
getEmojiByTag(tag: string): NIMInputEmoticon | undefined {
|
||||
return this.tagEmojiCache.get(tag)
|
||||
}
|
||||
|
||||
public hasEmoji(inputString: string): boolean {
|
||||
let match = this.pattern.exec(inputString)
|
||||
if (match !== null) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public parseEmojiText(inputString: string): Array<NEEmojiParseResult> {
|
||||
// const inputString: string = "This is a [sample] input [string] with [multiple] occurrences of [brackets].";
|
||||
let array = new Array<NEEmojiParseResult>();
|
||||
let finish = false;
|
||||
let lastMatchIndex = 0;
|
||||
while (finish == false) {
|
||||
let match = this.pattern.exec(inputString)
|
||||
if (match !== null) {
|
||||
let tag = match[0];
|
||||
let emoji = this.getEmojiByTag(tag);
|
||||
if (emoji !== undefined) {
|
||||
const nonMatchString = inputString.substring(lastMatchIndex, match.index);
|
||||
if (nonMatchString.length > 0) {
|
||||
let textResult = new NEEmojiParseResult();
|
||||
textResult.type = EmojiParseType.text;
|
||||
textResult.text = nonMatchString;
|
||||
textResult.startIndex = lastMatchIndex
|
||||
array.push(textResult);
|
||||
}
|
||||
lastMatchIndex = match.index + match[0].length;
|
||||
let emojiResult = new NEEmojiParseResult();
|
||||
emojiResult.type = EmojiParseType.emoji;
|
||||
emojiResult.emoji = emoji;
|
||||
emojiResult.startIndex = match.index
|
||||
array.push(emojiResult);
|
||||
}
|
||||
} else {
|
||||
if (lastMatchIndex < inputString.length) {
|
||||
let textResult = new NEEmojiParseResult();
|
||||
textResult.type = EmojiParseType.text;
|
||||
textResult.text = inputString.substring(lastMatchIndex, inputString.length);
|
||||
textResult.startIndex = lastMatchIndex
|
||||
array.push(textResult);
|
||||
}
|
||||
finish = true;
|
||||
}
|
||||
}
|
||||
if (array.length == 0) {
|
||||
let textResult = new NEEmojiParseResult();
|
||||
textResult.type = EmojiParseType.text;
|
||||
textResult.text = inputString;
|
||||
array.push(textResult);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
121
chatkit_ui/src/main/ets/manager/ait/AitManager.ets
Normal file
121
chatkit_ui/src/main/ets/manager/ait/AitManager.ets
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import { accountAll, AitMessage } from '@nimkit/chatkit';
|
||||
import { AitModel } from '@nimkit/chatkit/src/main/ets/model/ait/AitModel';
|
||||
|
||||
export class AitManager {
|
||||
aitModel: AitModel = new AitModel()
|
||||
|
||||
getAitModel(): AitModel | undefined {
|
||||
if (this.aitModel.aitBlocks.size > 0) {
|
||||
return this.aitModel
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
// 通过@文本添加@用户
|
||||
addAitWithText(account: string, name: string, startIndex: number): void {
|
||||
this.aitModel.addAitMember(account, name, startIndex);
|
||||
}
|
||||
|
||||
// 清理@用户,在发送之后调用
|
||||
cleanAit(): void {
|
||||
this.aitModel.reset();
|
||||
}
|
||||
|
||||
// 复制@用户信息,用户撤回消息使用
|
||||
forkAit(aitContactsModel: AitModel): void {
|
||||
this.aitModel.fork(aitContactsModel);
|
||||
}
|
||||
|
||||
// @用户 是否在文本最后,如果在文本最后,需要在文本后面添加空格
|
||||
aitEnd(text: string): boolean {
|
||||
const len = text.length;
|
||||
for (const element of this.aitModel.aitBlocks.values()) {
|
||||
for (const segment of element.segments) {
|
||||
if (segment.start < len && segment.end >= len) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据插入后的Text 文案, segment 移位或者删除。
|
||||
* 返回被删除的AitMsg信息
|
||||
* @param deletedText 删除后的字符串
|
||||
* @param endIndex 删除的结束位置
|
||||
* @param length 删除的长度
|
||||
* @returns
|
||||
*/
|
||||
deleteAitWithText(deletedText: string, endIndex: number, length: number): AitMessage | null {
|
||||
return this.aitModel.deleteAitUser(deletedText, endIndex, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文本
|
||||
* @param endIndex
|
||||
* @param length
|
||||
*/
|
||||
deleteText(endIndex: number, length: number) {
|
||||
this.aitModel.deleteText(endIndex, length)
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增Text输入,但输入的不是@
|
||||
* 会进行移位或者删除,如果在@XXX 中插入文本 @XXX 会被删除
|
||||
* @param endIndex 输入的结束位置
|
||||
* @param length 输入的长度
|
||||
*/
|
||||
addTextWithoutAit(endIndex: number, length: number): void {
|
||||
this.aitModel.insertText(endIndex, length);
|
||||
}
|
||||
|
||||
// 获取需要推送的用户列表
|
||||
getPushList(): string[] | undefined {
|
||||
const pushList: string[] = [];
|
||||
for (const key of this.aitModel.aitBlocks.keys()) {
|
||||
if (key === accountAll) {
|
||||
// pushList.length = 0;
|
||||
// const teamMembers: string[] = TeamMemberCache.getInstance().getAllMemberAccounts()
|
||||
// pushList.push(...teamMembers);
|
||||
return pushList;
|
||||
} else {
|
||||
pushList.push(key);
|
||||
}
|
||||
}
|
||||
if (pushList.length > 0) {
|
||||
return pushList;
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
// 是否已经在@列表中
|
||||
haveBeAit(account: string): boolean {
|
||||
return this.aitModel.aitBlocks.has(account)
|
||||
}
|
||||
|
||||
// 是否有@成员
|
||||
haveAitMember(): boolean {
|
||||
return Object.keys(this.aitModel.aitBlocks).length > 0;
|
||||
}
|
||||
|
||||
// 光标移动到@后自动到后面
|
||||
resetAitCursor(baseIndex: number): number {
|
||||
for (const aitMsg of this.aitModel.aitBlocks.values()) {
|
||||
for (const segment of aitMsg.segments) {
|
||||
if (segment.start < baseIndex && segment.end + 1 > baseIndex) {
|
||||
return segment.end + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return baseIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
17
chatkit_ui/src/main/ets/model/ChatAitNode.ets
Normal file
17
chatkit_ui/src/main/ets/model/ChatAitNode.ets
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import { AitSegment } from '@nimkit/chatkit/src/main/ets/model/ait/AitSegment';
|
||||
|
||||
export interface ChatAitNode {
|
||||
//文本
|
||||
text: string,
|
||||
|
||||
//片段
|
||||
segment?: AitSegment
|
||||
//accId
|
||||
account?: string
|
||||
}
|
||||
429
chatkit_ui/src/main/ets/model/ChatInfo.ets
Normal file
429
chatkit_ui/src/main/ets/model/ChatInfo.ets
Normal file
@ -0,0 +1,429 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { ChatKitClient } from '@nimkit/chatkit';
|
||||
import { V2NIMMessagePin } from '@nimsdk/base';
|
||||
import { V2NIMMessage, V2NIMMessageSendingState } from '@nimsdk/base/src/main/ets/nim/sdk/V2NIMMessageService';
|
||||
import { NIMMessageInfo } from '../model/NIMMessageInfo';
|
||||
|
||||
@ObservedV2
|
||||
export class ChatInfo {
|
||||
@Trace conversationName: string | undefined = undefined;
|
||||
@Trace msgList: MessageDataSource = new MessageDataSource();
|
||||
@Trace downloadProgressMap: Map<string, number> = new Map();
|
||||
@Trace isReceiveMsg: boolean = false;
|
||||
@Trace scrollIndex: number = -1; // 是否需要滚动到指定位置
|
||||
msgMap: Map<string, NIMMessageInfo> = new Map();
|
||||
@Trace msgPinMap: Map<string, V2NIMMessagePin> = new Map();
|
||||
conversationId: string = '';
|
||||
targetId: string = '';
|
||||
|
||||
constructor(conversationId: string) {
|
||||
this.conversationId = conversationId;
|
||||
this.targetId = ChatKitClient.nim.conversationIdUtil.parseConversationTargetId(conversationId)
|
||||
}
|
||||
|
||||
setConversationId(conversationId: string) {
|
||||
this.conversationId = conversationId;
|
||||
this.targetId = ChatKitClient.nim.conversationIdUtil.parseConversationTargetId(conversationId)
|
||||
}
|
||||
|
||||
setReceiveMsg(receiveMsg: boolean) {
|
||||
this.isReceiveMsg = receiveMsg;
|
||||
}
|
||||
|
||||
setScrollIndex(index: number) {
|
||||
this.scrollIndex = index
|
||||
}
|
||||
|
||||
getConversationName(): string {
|
||||
return '';
|
||||
};
|
||||
|
||||
getChatUserAvatarUrl(param?: string | V2NIMMessage): string {
|
||||
return '';
|
||||
};
|
||||
|
||||
getChatUserAvatarName(param?: string | V2NIMMessage): string {
|
||||
if (param as V2NIMMessage) {
|
||||
return (param as V2NIMMessage).senderId
|
||||
}
|
||||
return param as string;
|
||||
};
|
||||
|
||||
getChatUserShowName(param?: string | V2NIMMessage, alias: boolean = true, teamNick: boolean = true): string {
|
||||
if (param as V2NIMMessage) {
|
||||
return (param as V2NIMMessage).senderId
|
||||
}
|
||||
return param as string;
|
||||
};
|
||||
|
||||
getCurrentUserShowName(): string {
|
||||
return this.targetId;
|
||||
};
|
||||
|
||||
getCurrentUserAvatarUrl(message?: V2NIMMessage): string {
|
||||
return '';
|
||||
};
|
||||
|
||||
getCurrentUserAvatarName(): string {
|
||||
return this.targetId;
|
||||
};
|
||||
|
||||
updateMessageStatus(message: V2NIMMessage): boolean {
|
||||
if (message.sendingState === V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_SENDING) {
|
||||
return false
|
||||
}
|
||||
let targetMessage =
|
||||
this.msgList.getMessageList().find(msg => msg.message.messageClientId === message.messageClientId);
|
||||
if (targetMessage !== undefined) {
|
||||
targetMessage.updateMessageStatus(message)
|
||||
// 如果是消息 message crateTime 》 lastMessage crateTime 需要调整消息位置
|
||||
this.moveMessage(message)
|
||||
return true
|
||||
} else {
|
||||
this.insertMessage(message)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
addPinMessage(pinMsg: V2NIMMessagePin[]) {
|
||||
pinMsg.forEach(pin => {
|
||||
this.msgPinMap.set(pin.messageRefer.messageClientId, pin)
|
||||
this.msgMap.get(pin.messageRefer.messageClientId)?.setPinMessage(pin)
|
||||
})
|
||||
}
|
||||
|
||||
resetPinMessage(pinMsg: V2NIMMessagePin[]) {
|
||||
this.msgPinMap.clear()
|
||||
this.msgList.messageData.forEach(msg => {
|
||||
msg.setPinMessage(undefined)
|
||||
})
|
||||
pinMsg.forEach(pin => {
|
||||
this.msgPinMap.set(pin.messageRefer.messageClientId, pin)
|
||||
this.msgMap.get(pin.messageRefer.messageClientId)?.setPinMessage(pin)
|
||||
})
|
||||
}
|
||||
|
||||
removePinMessage(pinMsg: V2NIMMessagePin[]) {
|
||||
pinMsg.forEach(pin => {
|
||||
this.msgPinMap.delete(pin.messageRefer.messageClientId)
|
||||
this.msgMap.get(pin.messageRefer.messageClientId)?.setPinMessage(undefined)
|
||||
})
|
||||
}
|
||||
|
||||
updateMessageReadReceipt(message: NIMMessageInfo) {
|
||||
|
||||
}
|
||||
|
||||
moveMessage(message: V2NIMMessage){
|
||||
// 如果是消息 message crateTime 》 lastMessage crateTime 需要调整消息位置
|
||||
const copiedArray = [...this.msgList.messageData];
|
||||
const oldIndex = this.msgList.searchPosition(message.messageClientId)
|
||||
const updatedElement = copiedArray[oldIndex];
|
||||
// 按新的createTime降序排序
|
||||
const sortedArray = [...copiedArray].sort((a, b) => a.message.createTime - b.message.createTime);
|
||||
const newIndex = sortedArray.findIndex(element => element === updatedElement)
|
||||
// 位置发生变换,进行reload
|
||||
if (oldIndex != newIndex) {
|
||||
this.msgList.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
insertMessage(message: V2NIMMessage): NIMMessageInfo {
|
||||
let addIndex = this.msgList.searchInsertPosition(message.createTime)
|
||||
if (addIndex > this.msgList.totalCount()) {
|
||||
let result = this.pushMessage(message)
|
||||
this.setReceiveMsg(true)
|
||||
return result
|
||||
} else {
|
||||
let lastMessageTime = 0
|
||||
if (addIndex > 0) {
|
||||
lastMessageTime = this.msgList.getMessageList()[addIndex - 1]?.message.createTime;
|
||||
}
|
||||
let msgInfo = new NIMMessageInfo(message)
|
||||
msgInfo.setLastMessageTime(lastMessageTime)
|
||||
this.updateMessageReadReceipt(msgInfo)
|
||||
this.msgMap.set(message.messageClientId, msgInfo)
|
||||
if (this.msgPinMap.has(message.messageClientId)) {
|
||||
msgInfo.setPinMessage(this.msgPinMap.get(message.messageClientId))
|
||||
}
|
||||
this.msgList.addData(addIndex, msgInfo)
|
||||
|
||||
// 非尾部插入。
|
||||
// 该情况下可能展示时间,但是可能与新的一条消息时间间隔太小,会重复展示相同时间。因此该条展示后,需要检查与下条的展示时间是否相同。若相同,则隐藏下条消息时间。
|
||||
const changeIndex = addIndex + 1
|
||||
if (changeIndex < this.msgList.totalCount()) {
|
||||
const nextMsg: NIMMessageInfo = this.msgList.getData(changeIndex)
|
||||
nextMsg.setLastMessageTime(message.createTime)
|
||||
this.msgList.replaceData(changeIndex, nextMsg)
|
||||
}
|
||||
|
||||
return msgInfo;
|
||||
}
|
||||
}
|
||||
|
||||
pushMessage(message: V2NIMMessage): NIMMessageInfo {
|
||||
let current = this.getMessage(message.messageClientId)
|
||||
if (current !== undefined) {
|
||||
return current;
|
||||
}
|
||||
let lastMessageTime = 0
|
||||
if (this.msgList.totalCount() > 0) {
|
||||
lastMessageTime = this.msgList.getMessageList()[this.msgList.totalCount() - 1]?.message.createTime;
|
||||
}
|
||||
let msgInfo = new NIMMessageInfo(message)
|
||||
msgInfo.setLastMessageTime(lastMessageTime)
|
||||
this.updateMessageReadReceipt(msgInfo)
|
||||
this.msgList.push(msgInfo);
|
||||
this.msgMap.set(message.messageClientId, msgInfo)
|
||||
if (this.msgPinMap.has(message.messageClientId)) {
|
||||
msgInfo.setPinMessage(this.msgPinMap.get(message.messageClientId))
|
||||
}
|
||||
return msgInfo;
|
||||
}
|
||||
|
||||
pushModifyMessage(message: V2NIMMessage): NIMMessageInfo | undefined {
|
||||
const current: NIMMessageInfo | undefined = this.getMessage(message.messageClientId)
|
||||
if (current !== undefined) {
|
||||
const idx: number = this.msgList.searchPosition(message.messageClientId) // TODO 目前以遍历方式更新,modify 频繁情况下(如流式消息)存在性能优化项。临时方案
|
||||
if (idx >= 0) {
|
||||
current.message = message
|
||||
this.msgList.getData(idx).message = message
|
||||
this.msgList.getData(idx).messageHeight = -1 // 重置高度,重新计算高度
|
||||
this.msgList.notifyDataChange(idx)
|
||||
}
|
||||
return current;
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
pushMessageInfo(msgInfo: NIMMessageInfo) {
|
||||
let current = this.getMessage(msgInfo.message.messageClientId)
|
||||
if (current !== undefined) {
|
||||
return;
|
||||
}
|
||||
let lastMessageTime = this.msgList.getMessageList()[this.msgList.totalCount() - 1]?.message.createTime;
|
||||
msgInfo.setLastMessageTime(lastMessageTime)
|
||||
this.updateMessageReadReceipt(msgInfo)
|
||||
this.msgMap.set(msgInfo.message.messageClientId, msgInfo)
|
||||
if (this.msgPinMap.has(msgInfo.message.messageClientId)) {
|
||||
msgInfo.setPinMessage(this.msgPinMap.get(msgInfo.message.messageClientId))
|
||||
}
|
||||
this.msgList.push(msgInfo);
|
||||
}
|
||||
|
||||
unshiftMessage(messageList: V2NIMMessage[]): NIMMessageInfo[] {
|
||||
let result: NIMMessageInfo[] = [];
|
||||
for (let index = 0; index < messageList.length; index++) {
|
||||
let msg = new NIMMessageInfo(messageList[index]);
|
||||
if (index < messageList.length - 1) {
|
||||
msg.setLastMessageTime(messageList[index+1].createTime);
|
||||
}
|
||||
this.msgMap.set(msg.message.messageClientId, msg)
|
||||
if (this.msgPinMap.has(msg.message.messageClientId)) {
|
||||
msg.setPinMessage(this.msgPinMap.get(msg.message.messageClientId))
|
||||
}
|
||||
this.msgList.unshift(msg)
|
||||
result.push(msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
deleteMessage(msgClientId: string) {
|
||||
this.msgList.delete(msgClientId)
|
||||
this.msgMap.delete(msgClientId)
|
||||
}
|
||||
|
||||
revokeMessage(msgClientId: string) {
|
||||
this.deleteMessage(msgClientId)
|
||||
}
|
||||
|
||||
getMessage(msgClientId: string): NIMMessageInfo | undefined {
|
||||
return this.msgMap.get(msgClientId)
|
||||
}
|
||||
|
||||
searchPosition(msgClientId: string): number {
|
||||
return this.msgList.searchPosition(msgClientId)
|
||||
}
|
||||
|
||||
public cleanMessage() {
|
||||
this.msgList.clean()
|
||||
this.msgMap.clear()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Basic implementation of IDataSource to handle data listener
|
||||
export class MessageDataSource implements IDataSource {
|
||||
messageData: NIMMessageInfo[] = [];
|
||||
private listeners: DataChangeListener[] = [];
|
||||
|
||||
public totalCount(): number {
|
||||
return this.messageData.length;
|
||||
}
|
||||
|
||||
public getData(index: number): NIMMessageInfo {
|
||||
return this.messageData[index];
|
||||
}
|
||||
|
||||
public addData(index: number, data: NIMMessageInfo): void {
|
||||
this.messageData.splice(index, 0, data);
|
||||
this.notifyDataAdd(index);
|
||||
}
|
||||
|
||||
public replaceData(index: number, data: NIMMessageInfo): void {
|
||||
this.messageData.splice(index, 1, data);
|
||||
this.notifyDataChange(index);
|
||||
}
|
||||
|
||||
public push(data: NIMMessageInfo): void {
|
||||
this.messageData.push(data);
|
||||
this.notifyDataAdd(this.messageData.length - 1);
|
||||
}
|
||||
|
||||
public unshift(data: NIMMessageInfo): void {
|
||||
this.messageData.unshift(data);
|
||||
this.notifyDataAdd(0);
|
||||
}
|
||||
|
||||
public unshiftList(data: NIMMessageInfo[]): void {
|
||||
data.forEach(element => {
|
||||
this.messageData.unshift(element);
|
||||
})
|
||||
|
||||
this.notifyDatasetChange([{
|
||||
type: DataOperationType.ADD,
|
||||
index: 0,
|
||||
count: data.length
|
||||
}]);
|
||||
}
|
||||
|
||||
public pushList(data: NIMMessageInfo[]): void {
|
||||
data.forEach(element => {
|
||||
this.messageData.push(element);
|
||||
});
|
||||
this.notifyDataAdd(this.messageData.length - 1);
|
||||
}
|
||||
|
||||
public delete(msgClientId: string) {
|
||||
this.messageData.forEach((msg, index, msgList) => {
|
||||
if (msgClientId === msg.message.messageClientId) {
|
||||
this.messageData.splice(index, 1)
|
||||
this.notifyDataDelete(index)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public deleteWithIndex(index: number) {
|
||||
if (index >= 0 && index < this.messageData.length) {
|
||||
this.messageData.splice(index, 1)
|
||||
this.notifyDataDelete(index)
|
||||
}
|
||||
}
|
||||
|
||||
public reloadData(): void {
|
||||
// 1. 元素重新排序
|
||||
this.messageData.sort((a, b) => a.message.createTime - b.message.createTime)
|
||||
// 2. 通知视图更新
|
||||
this.notifyDataReload()
|
||||
}
|
||||
|
||||
public searchInsertPosition(creatTime: number): number {
|
||||
for (let index = this.messageData.length - 1; index >= 0; index--) {
|
||||
if (creatTime > this.messageData[index].getCreateTime()) {
|
||||
return index + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public searchPosition(clientId: string): number {
|
||||
for (let index = this.messageData.length - 1; index >= 0; index--) {
|
||||
if (clientId == this.messageData[index].getMessageClientId()) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public clean() {
|
||||
while (this.messageData.length > 0) {
|
||||
this.messageData.pop()
|
||||
}
|
||||
}
|
||||
|
||||
public getMessageList(): NIMMessageInfo[] {
|
||||
return this.messageData;
|
||||
}
|
||||
|
||||
// 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听
|
||||
registerDataChangeListener(listener: DataChangeListener): void {
|
||||
if (this.listeners.indexOf(listener) < 0) {
|
||||
this.listeners.push(listener);
|
||||
}
|
||||
}
|
||||
|
||||
// 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听
|
||||
unregisterDataChangeListener(listener: DataChangeListener): void {
|
||||
const pos = this.listeners.indexOf(listener);
|
||||
if (pos >= 0) {
|
||||
this.listeners.splice(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 通知LazyForEach组件需要重载所有子组件
|
||||
notifyDataReload(): void {
|
||||
this.listeners.forEach(listener => {
|
||||
listener.onDataReloaded();
|
||||
})
|
||||
}
|
||||
|
||||
// 通知LazyForEach组件需要在index对应索引处添加子组件
|
||||
notifyDataAdd(index: number): void {
|
||||
this.listeners.forEach(listener => {
|
||||
listener.onDataAdd(index);
|
||||
})
|
||||
}
|
||||
|
||||
// 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件
|
||||
notifyDataChange(index: number): void {
|
||||
this.listeners.forEach(listener => {
|
||||
listener.onDataChange(index);
|
||||
})
|
||||
}
|
||||
|
||||
// 通知LazyForEach组件需要在index对应索引处删除该子组件
|
||||
notifyDataDelete(index: number): void {
|
||||
this.listeners.forEach(listener => {
|
||||
listener.onDataDelete(index);
|
||||
})
|
||||
}
|
||||
|
||||
// 通知LazyForEach组件将from索引和to索引处的子组件进行交换
|
||||
notifyDataMove(from: number, to: number): void {
|
||||
this.listeners.forEach(listener => {
|
||||
listener.onDataMove(from, to);
|
||||
})
|
||||
}
|
||||
|
||||
// 通知LazyForEach组件将from索引和to索引处的子组件进行交换
|
||||
notifyDatasetChange(operation: DataAddOperation[]): void {
|
||||
this.listeners.forEach(listener => {
|
||||
listener.onDatasetChange(operation);
|
||||
})
|
||||
}
|
||||
// 通知LazyForEach组件将from索引和to索引处的子组件进行交换
|
||||
notifyDatasetReload(operation: DataReloadOperation[]): void {
|
||||
this.listeners.forEach(listener => {
|
||||
listener.onDatasetChange(operation);
|
||||
})
|
||||
}
|
||||
}
|
||||
227
chatkit_ui/src/main/ets/model/ChatTeamInfo.ets
Normal file
227
chatkit_ui/src/main/ets/model/ChatTeamInfo.ets
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { ChatInfo } from './ChatInfo';
|
||||
import { V2NIMFriend, V2NIMMessage, V2NIMTeamMessageReadReceipt, V2NIMUser } from '@nimsdk/base';
|
||||
import { V2NIMTeam, V2NIMTeamMember } from '@nimsdk/base/src/main/ets/nim/sdk/V2NIMTeamService';
|
||||
import { NIMMessageInfo } from './NIMMessageInfo';
|
||||
import { NEFriendUserCache } from '@nimkit/chatkit/src/main/ets/NEFriendUserCache';
|
||||
import { ChatKitClient } from '@nimkit/chatkit';
|
||||
|
||||
@ObservedV2
|
||||
export class ChatTeamInfo extends ChatInfo {
|
||||
@Trace team?: V2NIMTeam | undefined = undefined;
|
||||
@Trace teamMemberMap = new Map<string, V2NIMTeamMember>();
|
||||
@Trace teamUserMap = new Map<string, V2NIMUser>();
|
||||
// 用于动态监听好友信息变更时,更新会话页面好友数据
|
||||
@Trace teamUserFriendMap = new Map<string, V2NIMFriend>();
|
||||
teamMessageReadReceipt = new Map<string, V2NIMTeamMessageReadReceipt>();
|
||||
|
||||
constructor(conversationId: string) {
|
||||
super(conversationId)
|
||||
this.conversationName = conversationId;
|
||||
}
|
||||
|
||||
setConversationId(conversationId: string): void {
|
||||
super.setConversationId(conversationId)
|
||||
this.conversationName = conversationId;
|
||||
}
|
||||
|
||||
setTeam(team: V2NIMTeam | undefined) {
|
||||
this.team = team;
|
||||
if (team !== undefined && team.name != '') {
|
||||
this.conversationName = team.name;
|
||||
}
|
||||
}
|
||||
|
||||
setTeamMember(member: V2NIMTeamMember) {
|
||||
this.teamMemberMap.set(member.accountId, member);
|
||||
}
|
||||
|
||||
setTeamReadReadReceipt(readReceipts: V2NIMTeamMessageReadReceipt[]) {
|
||||
readReceipts.forEach((readReceipt) => {
|
||||
this.teamMessageReadReceipt.set(readReceipt.messageClientId, readReceipt);
|
||||
let msg = this.getMessage(readReceipt.messageClientId);
|
||||
msg?.setReadCount(readReceipt.readCount, readReceipt.unreadCount)
|
||||
})
|
||||
}
|
||||
|
||||
pushDataList(msgList: NIMMessageInfo[]) {
|
||||
msgList.forEach(element => {
|
||||
this.msgList.push(element);
|
||||
});
|
||||
}
|
||||
|
||||
addTeamUser(userList: V2NIMUser[]) {
|
||||
userList.forEach((user) => {
|
||||
this.teamUserMap.set(user.accountId, user);
|
||||
});
|
||||
}
|
||||
|
||||
addTeamFriend(friend: V2NIMFriend) {
|
||||
this.teamUserFriendMap.set(friend.accountId, friend);
|
||||
}
|
||||
|
||||
addTeamMember(member: V2NIMTeamMember[]) {
|
||||
member.forEach((member) => {
|
||||
this.teamMemberMap.set(member.accountId, member);
|
||||
});
|
||||
}
|
||||
|
||||
getTeamMember(accountId: string): V2NIMTeamMember | undefined {
|
||||
if (this.teamMemberMap.has(accountId)) {
|
||||
return this.teamMemberMap.get(accountId);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getChatUserAvatarUrl(param?: string | V2NIMMessage): string {
|
||||
let accountId: string = ""
|
||||
if ((param as V2NIMMessage).senderId) {
|
||||
accountId = (param as V2NIMMessage).senderId
|
||||
} else {
|
||||
accountId = param as string;
|
||||
}
|
||||
|
||||
if (this.teamUserMap.has(accountId)) {
|
||||
return this.teamUserMap.get(accountId)?.avatar ?? '';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
getCurrentUserAvatarUrl(): string {
|
||||
let accountId = ChatKitClient.getLoginUserId()
|
||||
if (this.teamUserMap.has(accountId)) {
|
||||
return this.teamUserMap.get(accountId)?.avatar ?? '';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
getChatUserAvatarName(param?: string | V2NIMMessage): string {
|
||||
let accountId: string = ""
|
||||
if ((param as V2NIMMessage).senderId) {
|
||||
accountId = (param as V2NIMMessage).senderId
|
||||
} else {
|
||||
accountId = param as string;
|
||||
}
|
||||
|
||||
let result = '';
|
||||
if (this.teamUserFriendMap.has(accountId)) {
|
||||
let friend = this.teamUserFriendMap.get(accountId)
|
||||
if (friend && friend.alias !== undefined) {
|
||||
result = friend.alias ?? ''
|
||||
}
|
||||
} else if (NEFriendUserCache.getInstance().isFriend(accountId)) {
|
||||
let friendInfo = NEFriendUserCache.getInstance().getFriendById(accountId)
|
||||
if (friendInfo !== undefined) {
|
||||
result = friendInfo.shortName(true)
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null || result.trim() === '') {
|
||||
result = this.teamUserMap.get(accountId)?.name ?? '';
|
||||
}
|
||||
|
||||
if (result == null || result.trim() === '') {
|
||||
result = accountId;
|
||||
}
|
||||
if (result !== '' && result.length > 2) {
|
||||
result = result.substring(result.length - 2, result.length)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
getCurrentUserAvatarName(): string {
|
||||
let accountId = ChatKitClient.getLoginUserId()
|
||||
let result = '';
|
||||
if (result == null || result.trim() === '') {
|
||||
result = this.teamUserMap.get(accountId)?.name ?? '';
|
||||
}
|
||||
|
||||
if (result == null || result.trim() === '') {
|
||||
result = accountId;
|
||||
}
|
||||
if (result !== '' && result.length > 2) {
|
||||
result = result.substring(result.length - 2, result.length)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
getChatUserShowName(param?: string | V2NIMMessage, alias: boolean = true, teamNick: boolean = true): string {
|
||||
let accountId: string = ""
|
||||
if ((param as V2NIMMessage).senderId) {
|
||||
accountId = (param as V2NIMMessage).senderId
|
||||
} else {
|
||||
accountId = param as string;
|
||||
}
|
||||
|
||||
let result = '';
|
||||
if (this.teamUserFriendMap.has(accountId)) {
|
||||
let friend = this.teamUserFriendMap.get(accountId)
|
||||
if (friend && friend.alias !== undefined) {
|
||||
result = friend.alias ?? ''
|
||||
}
|
||||
}
|
||||
let friendInfo = NEFriendUserCache.getInstance().getFriendById(accountId)
|
||||
//1,备注优先
|
||||
if (alias && (result == null || result.trim() === '') && friendInfo) {
|
||||
result = friendInfo.getAlias() ?? ''
|
||||
}
|
||||
//2,群昵称次之
|
||||
if (teamNick && (result == null || result.trim() === '') && this.teamMemberMap.has(accountId)) {
|
||||
result = this.teamMemberMap.get(accountId)?.teamNick ?? '';
|
||||
}
|
||||
//3,好友名称再次之
|
||||
if ((result == null || result.trim() === '') && NEFriendUserCache.getInstance().isFriend(accountId)) {
|
||||
if (friendInfo !== undefined) {
|
||||
result = friendInfo.showName(alias)
|
||||
}
|
||||
}
|
||||
if (result == null || result.trim() === '') {
|
||||
result = this.teamUserMap.get(accountId)?.name ?? '';
|
||||
}
|
||||
|
||||
if (result == null || result.trim() === '') {
|
||||
result = accountId;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getCurrentUserShowName(): string {
|
||||
let result = '';
|
||||
let accountId = ChatKitClient.getLoginUserId()
|
||||
if (this.teamMemberMap.has(accountId)) {
|
||||
result = this.teamMemberMap.get(accountId)?.teamNick ?? '';
|
||||
}
|
||||
if (result == null || result.trim() === '') {
|
||||
result = this.teamUserMap.get(accountId)?.name ?? '';
|
||||
}
|
||||
|
||||
if (result == null || result.trim() === '') {
|
||||
result = accountId;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用成员用户信息
|
||||
* @param accountId
|
||||
* @returns
|
||||
*/
|
||||
getMemberUserById(accountId: string) {
|
||||
return this.teamUserMap.get(accountId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前成员的群管理员
|
||||
* @returns
|
||||
*/
|
||||
getCurrentUserTeamMember() {
|
||||
let accId = ChatKitClient.getLoginUserId()
|
||||
return this.teamMemberMap.get(accId)
|
||||
}
|
||||
}
|
||||
205
chatkit_ui/src/main/ets/model/ChatUserInfo.ets
Normal file
205
chatkit_ui/src/main/ets/model/ChatUserInfo.ets
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { ChatKitClient, NEFriendUserCache } from '@nimkit/chatkit';
|
||||
import { NIMMessageInfo } from '../model/NIMMessageInfo';
|
||||
import { V2NIMFriend, V2NIMMessage, V2NIMP2PMessageReadReceipt, V2NIMUser } from '@nimsdk/base';
|
||||
import { ChatInfo } from './ChatInfo';
|
||||
|
||||
@ObservedV2
|
||||
export class ChatUserInfo extends ChatInfo {
|
||||
@Trace currentUser: V2NIMUser | undefined = undefined;
|
||||
@Trace chatUser: V2NIMUser | undefined = undefined;
|
||||
@Trace chatFriend: V2NIMFriend | undefined = undefined;
|
||||
readReceipt: V2NIMP2PMessageReadReceipt | undefined = undefined;
|
||||
readReceiptMsg: NIMMessageInfo | undefined = undefined;
|
||||
|
||||
constructor() {
|
||||
super('');
|
||||
}
|
||||
|
||||
setConversationId(conversationId: string): void {
|
||||
super.setConversationId(conversationId)
|
||||
this.conversationName = this.getConversationName();
|
||||
}
|
||||
|
||||
setCurrentUser(user: V2NIMUser | undefined) {
|
||||
this.currentUser = user;
|
||||
}
|
||||
|
||||
setChatUser(user: V2NIMUser | undefined) {
|
||||
this.chatUser = user;
|
||||
this.conversationName = this.getConversationName();
|
||||
}
|
||||
|
||||
setChatFriend(friend: V2NIMFriend | undefined) {
|
||||
this.chatFriend = friend;
|
||||
}
|
||||
|
||||
updateConversationName() {
|
||||
this.conversationName = this.getConversationName();
|
||||
}
|
||||
|
||||
setMessageReadReceipt(readReceipt: V2NIMP2PMessageReadReceipt | undefined) {
|
||||
if (readReceipt == undefined || readReceipt.timestamp == undefined) {
|
||||
return
|
||||
}
|
||||
this.readReceipt = readReceipt
|
||||
let readCount = -1
|
||||
for (let index = super.msgList.totalCount() - 1; index >= 0; index--) {
|
||||
if (super.msgList.messageData[index].readCount > 0) {
|
||||
break
|
||||
} else if (readCount == -1) {
|
||||
let hasRead = readReceipt?.timestamp >= super.msgList.messageData[index].getCreateTime()
|
||||
readCount = hasRead ? 1 : 0;
|
||||
}
|
||||
super.msgList.messageData[index].readCount = readCount
|
||||
}
|
||||
}
|
||||
|
||||
updateMessageReadReceipt(message: NIMMessageInfo) {
|
||||
super.updateMessageReadReceipt(message)
|
||||
if (this.readReceipt == undefined) {
|
||||
return
|
||||
}
|
||||
let hasRead = this.readReceipt?.timestamp >= message.getCreateTime()
|
||||
message.readCount = hasRead ? 1 : 0;
|
||||
}
|
||||
|
||||
getChatUserAvatarName(param?: string | V2NIMMessage): string {
|
||||
let accountId: string = ""
|
||||
if ((param as V2NIMMessage).senderId) {
|
||||
accountId = (param as V2NIMMessage).senderId
|
||||
} else {
|
||||
accountId = param as string;
|
||||
}
|
||||
|
||||
if (accountId === ChatKitClient.getLoginUserId()) {
|
||||
return this.getCurrentUserAvatarName()
|
||||
}
|
||||
|
||||
let name = '';
|
||||
if (this.chatFriend) {
|
||||
name = this.chatFriend?.alias ?? '';
|
||||
}
|
||||
if (name === '' && NEFriendUserCache.getInstance().getFriendById(accountId)) {
|
||||
let friendInfo = NEFriendUserCache.getInstance().getFriendById(accountId)
|
||||
if (friendInfo !== undefined) {
|
||||
name = friendInfo.showName()
|
||||
}
|
||||
}
|
||||
if (name === '' && this.chatUser !== undefined) {
|
||||
name = this.chatUser?.name ?? '';
|
||||
}
|
||||
if (name === '') {
|
||||
name = accountId;
|
||||
}
|
||||
|
||||
if (name !== '' && name.length > 2) {
|
||||
name = name.substring(name.length - 2, name.length)
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
getCurrentUserAvatarName(): string {
|
||||
let name = '';
|
||||
let accountId = ChatKitClient.getLoginUserId();
|
||||
if (this.currentUser !== undefined) {
|
||||
name = this.currentUser?.name ?? '';
|
||||
}
|
||||
if (name === '') {
|
||||
name = accountId;
|
||||
}
|
||||
if (name !== '' && name.length > 2) {
|
||||
name = name.substring(name.length - 2, name.length)
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
getChatUserShowName(param?: string | V2NIMMessage, alias: boolean = true, teamNick: boolean = true): string {
|
||||
let accountId: string = ""
|
||||
if ((param as V2NIMMessage).senderId) {
|
||||
accountId = (param as V2NIMMessage).senderId
|
||||
} else {
|
||||
accountId = param as string;
|
||||
}
|
||||
|
||||
if (accountId === ChatKitClient.getLoginUserId()) {
|
||||
return this.getCurrentUserShowName()
|
||||
}
|
||||
|
||||
let name = '';
|
||||
let friendInfo = NEFriendUserCache.getInstance().getFriendById(accountId)
|
||||
if (friendInfo !== undefined) {
|
||||
name = friendInfo.showName(alias)
|
||||
}
|
||||
if (name === '' && this.chatUser !== undefined) {
|
||||
name = this.chatUser?.name ?? '';
|
||||
}
|
||||
|
||||
if (name === '') {
|
||||
name = accountId;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
getCurrentUserShowName(): string {
|
||||
let name = '';
|
||||
if (name === '' && this.currentUser !== undefined) {
|
||||
name = this.currentUser?.name ?? '';
|
||||
}
|
||||
if (name === '') {
|
||||
name = ChatKitClient.getLoginUserId();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
getChatUserAvatarUrl(param?: string | V2NIMMessage): string {
|
||||
let accountId: string = ""
|
||||
if ((param as V2NIMMessage).senderId) {
|
||||
accountId = (param as V2NIMMessage).senderId
|
||||
} else {
|
||||
accountId = param as string;
|
||||
}
|
||||
|
||||
if (accountId === ChatKitClient.getLoginUserId()) {
|
||||
return this.getCurrentUserAvatarUrl()
|
||||
}
|
||||
|
||||
let result: string = '';
|
||||
if (this.chatUser !== undefined) {
|
||||
result = this.chatUser?.avatar ? this.chatUser?.avatar : '';
|
||||
}
|
||||
if (result == null || result.trim() === '') {
|
||||
result = '';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getCurrentUserAvatarUrl(): string {
|
||||
let result: string = '';
|
||||
if (this.currentUser !== undefined) {
|
||||
result = this.currentUser?.avatar ? this.currentUser?.avatar : '';
|
||||
}
|
||||
if (result == null || result.trim() === '') {
|
||||
result = '';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getConversationName(): string {
|
||||
let result: string = '';
|
||||
let friendInfo = NEFriendUserCache.getInstance().getFriendById(this.targetId)
|
||||
if (friendInfo !== undefined) {
|
||||
result = friendInfo.showName(true)
|
||||
} else if (this.chatUser !== undefined) {
|
||||
result = this.chatUser?.name !== null ? this.chatUser?.name : this.chatUser?.accountId;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
30
chatkit_ui/src/main/ets/model/MergeMessageInfo.ets
Normal file
30
chatkit_ui/src/main/ets/model/MergeMessageInfo.ets
Normal file
@ -0,0 +1,30 @@
|
||||
import { mergedMessageAvatarKey, mergedMessageNickKey } from '@nimkit/chatkit';
|
||||
import { TailString } from '@nimkit/common';
|
||||
import { V2NIMMessage } from '@nimsdk/base';
|
||||
import { ChatInfo } from '../model/ChatInfo';
|
||||
|
||||
@ObservedV2
|
||||
export class MergeMessageInfo extends ChatInfo {
|
||||
getServerExtensionValue(key: string, param?: string | V2NIMMessage | undefined): string {
|
||||
if (param as V2NIMMessage) {
|
||||
const message = param as V2NIMMessage
|
||||
if (message.serverExtension) {
|
||||
const remoteExt = JSON.parse(message.serverExtension) as object
|
||||
return remoteExt[key]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
getChatUserAvatarUrl(param?: string | V2NIMMessage | undefined): string {
|
||||
return this.getServerExtensionValue(mergedMessageAvatarKey, param)
|
||||
}
|
||||
|
||||
getChatUserAvatarName(param?: string | V2NIMMessage | undefined): string {
|
||||
return TailString(this.getChatUserShowName(param))
|
||||
}
|
||||
|
||||
getChatUserShowName(param?: string | V2NIMMessage): string {
|
||||
return this.getServerExtensionValue(mergedMessageNickKey, param)
|
||||
}
|
||||
}
|
||||
36
chatkit_ui/src/main/ets/model/MessageOperationItem.ets
Normal file
36
chatkit_ui/src/main/ets/model/MessageOperationItem.ets
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
export enum MessageOperationType {
|
||||
// 删除
|
||||
Delete = 1,
|
||||
// 复制
|
||||
Copy = 2,
|
||||
// 撤回
|
||||
Undo = 3,
|
||||
// 转发
|
||||
Forward = 4,
|
||||
//标记
|
||||
Pin = 5,
|
||||
//取消标记
|
||||
Unpin = 6,
|
||||
// 多选
|
||||
Select = 7,
|
||||
// 收藏
|
||||
Collection = 8,
|
||||
// 回复
|
||||
Reply = 9
|
||||
}
|
||||
|
||||
export class MessageOperationItem {
|
||||
// 操作提示图片
|
||||
operationImage?: ResourceStr;
|
||||
// 操作提示文字
|
||||
operationText?: ResourceStr;
|
||||
// 操作类型
|
||||
operationType: MessageOperationType = MessageOperationType.Delete;
|
||||
}
|
||||
24
chatkit_ui/src/main/ets/model/NEChatMoreOperationData.ets
Normal file
24
chatkit_ui/src/main/ets/model/NEChatMoreOperationData.ets
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
export enum NEChatMoreOperationType {
|
||||
Video = 1,
|
||||
File = 2,
|
||||
Location = 3,
|
||||
Reply = 4,
|
||||
Teach = 5,
|
||||
Outpatient=6,
|
||||
Shopping=7,
|
||||
Hospital=8,
|
||||
Image=8
|
||||
}
|
||||
|
||||
export class NEChatMoreOperationData {
|
||||
type?: NEChatMoreOperationType;
|
||||
operationTitle?: ResourceStr;
|
||||
imageSource?: string;
|
||||
}
|
||||
17
chatkit_ui/src/main/ets/model/NERectData.ets
Normal file
17
chatkit_ui/src/main/ets/model/NERectData.ets
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
@ObservedV2
|
||||
export class NERectData {
|
||||
@Trace
|
||||
x: number = 0;
|
||||
@Trace
|
||||
y: number = 0;
|
||||
@Trace
|
||||
width: number = 0;
|
||||
@Trace
|
||||
height: number = 0;
|
||||
}
|
||||
358
chatkit_ui/src/main/ets/model/NIMMessageInfo.ets
Normal file
358
chatkit_ui/src/main/ets/model/NIMMessageInfo.ets
Normal file
@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
V2NIMMessage,
|
||||
V2NIMMessageAttachment,
|
||||
V2NIMMessageImageAttachment,
|
||||
V2NIMMessageType
|
||||
} from '@nimsdk/base/src/main/ets/nim/sdk/V2NIMMessageService'
|
||||
import {
|
||||
ChatKitClient,
|
||||
ChatRepo,
|
||||
CustomMessageUtils,
|
||||
keyReplyMsgKey,
|
||||
mergedMessageCustomType,
|
||||
StorageRepo
|
||||
} from '@nimkit/chatkit'
|
||||
import { DateUtils } from '../common/DateUtils'
|
||||
import { fileUri } from '@kit.CoreFileKit'
|
||||
import { ChatConst } from '../constants/ChatConst'
|
||||
import { JSONUtil } from '@nimkit/common'
|
||||
import { BusinessError, systemDateTime } from '@kit.BasicServicesKit'
|
||||
import { V2NIMGetMediaResourceInfoResult, V2NIMMessageAIStreamStatus, V2NIMMessagePin } from '@nimsdk/base'
|
||||
import { ChatKitConfig } from '../ChatKitConfig'
|
||||
import { getMergedMessageContent, measureMessageHeight } from '../common/MessageHelper'
|
||||
import fs from '@ohos.file.fs'
|
||||
|
||||
@ObservedV2
|
||||
export class NIMMessageInfo {
|
||||
// IM SDK 层的消息对象
|
||||
@Trace message: V2NIMMessage
|
||||
// 未读数量
|
||||
@Trace unReadCount: number = 100
|
||||
// 已读数量
|
||||
@Trace readCount: number = -1
|
||||
// 消息下载或上传进度
|
||||
@Trace downloadProgress: number = -1
|
||||
@Trace messageHeight = -1
|
||||
// 消息附件
|
||||
attachment: V2NIMMessageAttachment | null = null
|
||||
// 自定义消息附件
|
||||
customAttachment: object | null = null
|
||||
// 上一条消息的发送时间,用于判断该消息展示时候是否需要展示发送时间
|
||||
@Trace lastMessageTime: number = 0
|
||||
// 是否是接收消息,UI渲染时使用
|
||||
isReceiveMsg: boolean = false
|
||||
// 是否为撤回消息
|
||||
isRevokeMsg: boolean = false
|
||||
// 是否为撤回可编辑
|
||||
@Trace revokeEditMsg: boolean = false
|
||||
// 是否为PIN消息
|
||||
@Trace isPinMsg: boolean = false
|
||||
// 是否为合并转发消息
|
||||
isMergeMsg: boolean = false
|
||||
mergedContent: string = ''
|
||||
// 是否为合并转发详情页中的消息
|
||||
isMergeDetailMsg: boolean = false
|
||||
// 是否为多选选中
|
||||
@Trace isSelectedMsg: boolean = false
|
||||
//是否为回复消息
|
||||
@Trace
|
||||
isReplyMsg: boolean = false
|
||||
// 回复消息
|
||||
@Trace replyMsg: NIMMessageInfo | undefined = undefined
|
||||
// 撤回消息扩展
|
||||
revokeInfo: RevokeInfo | undefined = undefined
|
||||
//pin信息
|
||||
pinInfo: V2NIMMessagePin | undefined = undefined
|
||||
|
||||
constructor(msg: V2NIMMessage) {
|
||||
this.message = msg
|
||||
this.parseMessage(msg)
|
||||
}
|
||||
|
||||
async parseMessage(message: V2NIMMessage) {
|
||||
this.isReceiveMsg = this.message.senderId != ChatKitClient.getLoginUserId()
|
||||
|
||||
// 解析撤回
|
||||
this.revokeInfo = RevokeInfo.parseRevokeInfo(message)
|
||||
if (this.revokeInfo != null) {
|
||||
this.isRevokeMsg = true
|
||||
this.revokeEditMsg = this.revokeInfo?.isEditMsg &&
|
||||
(systemDateTime.getTime() - this.revokeInfo.revokeTime < ChatKitConfig.messageRevokeTimeLimit)
|
||||
}
|
||||
|
||||
// 解析合并转发
|
||||
if (message.messageType == V2NIMMessageType.V2NIM_MESSAGE_TYPE_CUSTOM && message.attachment != null) {
|
||||
this.customAttachment = CustomMessageUtils.dataOfCustomMessage(message.attachment)
|
||||
let customType = CustomMessageUtils.typeOfCustomMessage(message.attachment)
|
||||
if (customType == mergedMessageCustomType) {
|
||||
this.isMergeMsg = true
|
||||
this.mergedContent = getMergedMessageContent(this.customAttachment)
|
||||
}
|
||||
}
|
||||
|
||||
// 解析回复
|
||||
this.parseReply(message)
|
||||
|
||||
this.messageHeight = -1
|
||||
}
|
||||
|
||||
// @Computed
|
||||
// get isReplyMsg():boolean{
|
||||
// return this.replyMsg !== undefined
|
||||
// }
|
||||
|
||||
setIsReply(reply: boolean) {
|
||||
this.isReplyMsg = reply
|
||||
}
|
||||
|
||||
async parseReply(message: V2NIMMessage) {
|
||||
// 优先使用 thread 方案
|
||||
if (message.threadReply) {
|
||||
this.isReplyMsg = true
|
||||
try {
|
||||
const messages = await ChatRepo.getMessageListByRefers([message.threadReply])
|
||||
if (messages.length > 0) {
|
||||
this.replyMsg = new NIMMessageInfo(messages[0])
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 非 thread 方案
|
||||
const remoteExt = message.serverExtension
|
||||
if (remoteExt) {
|
||||
try {
|
||||
const remoteDic = JSON.parse(remoteExt) as object
|
||||
const msgReplyDic = remoteDic[keyReplyMsgKey] as object
|
||||
if (msgReplyDic) {
|
||||
try {
|
||||
this.isReplyMsg = true
|
||||
const messages = await ChatRepo.getMessageListByRefers([{
|
||||
messageClientId: msgReplyDic['idClient'] as string,
|
||||
messageServerId: msgReplyDic['idServer'] as string,
|
||||
senderId: msgReplyDic['from'] as string,
|
||||
createTime: msgReplyDic['time'] as number,
|
||||
conversationId: msgReplyDic['to'] as string,
|
||||
receiverId: msgReplyDic['receiverId'] as string,
|
||||
conversationType: msgReplyDic['scene'] as number,
|
||||
}]).catch((err: BusinessError) => {
|
||||
console.debug('netease parseReply', err.message)
|
||||
})
|
||||
|
||||
if (messages && messages.length > 0) {
|
||||
this.replyMsg = new NIMMessageInfo(messages[0])
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkRevokeEdit(): boolean {
|
||||
if (this.revokeInfo != null) {
|
||||
this.isRevokeMsg = true
|
||||
this.revokeEditMsg = this.revokeInfo?.isEditMsg &&
|
||||
(systemDateTime.getTime() - this.revokeInfo.revokeTime < ChatKitConfig.messageRevokeTimeLimit)
|
||||
return this.revokeEditMsg
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
configReadReceipt(): boolean {
|
||||
return this.message.messageConfig?.readReceiptEnabled ?? false
|
||||
}
|
||||
|
||||
updateMessageStatus(message: V2NIMMessage) {
|
||||
this.message = message
|
||||
}
|
||||
|
||||
setLastMessageTime(time: number) {
|
||||
this.lastMessageTime = time
|
||||
}
|
||||
|
||||
setSelected(isSelected: boolean) {
|
||||
this.isSelectedMsg = isSelected
|
||||
}
|
||||
|
||||
getMessageHeight(context: UIContext): number {
|
||||
if (this.messageHeight < 0) {
|
||||
this.messageHeight = measureMessageHeight(context, this)
|
||||
}
|
||||
return this.messageHeight
|
||||
}
|
||||
|
||||
setPinMessage(pinMsg: V2NIMMessagePin | undefined) {
|
||||
if (pinMsg !== undefined) {
|
||||
this.pinInfo = pinMsg
|
||||
this.isPinMsg = true
|
||||
} else {
|
||||
this.isPinMsg = false
|
||||
this.pinInfo = undefined
|
||||
}
|
||||
this.messageHeight = -1
|
||||
|
||||
}
|
||||
|
||||
setDownloadProgress(progress: number) {
|
||||
this.downloadProgress = progress
|
||||
}
|
||||
|
||||
setReadCount(readCount: number, unreadCount: number) {
|
||||
this.unReadCount = unreadCount
|
||||
this.readCount = readCount
|
||||
}
|
||||
|
||||
getCreateTime(): number {
|
||||
return this.message.createTime
|
||||
}
|
||||
|
||||
// 获取消息时间
|
||||
getMessageFormatTime(): string {
|
||||
return DateUtils.formatTime(this.message.createTime, this.lastMessageTime)
|
||||
}
|
||||
|
||||
// 获取消息时间(根据消息时间展示的间隔)
|
||||
getMessageTime(): string {
|
||||
let result = ''
|
||||
if (this.message.createTime - this.lastMessageTime > ChatKitConfig.messageTimeGap) {
|
||||
result = DateUtils.formatTime(this.message.createTime, this.lastMessageTime)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
getConversationId() {
|
||||
return this.message.conversationId
|
||||
}
|
||||
|
||||
getConversationType() {
|
||||
return ChatKitClient.nim.conversationIdUtil.parseConversationType(this.getConversationId())
|
||||
}
|
||||
|
||||
getMessageClientId(): string {
|
||||
return this.message.messageClientId
|
||||
}
|
||||
|
||||
getAvatarName(): string {
|
||||
let result = ''
|
||||
if (this.message != null && this.message != null) {
|
||||
result = this.message.senderId.substring(this.message.senderId.length - 2, this.message.senderId.length)
|
||||
}
|
||||
if (this.message != null && this.message.senderId != null) {
|
||||
result = this.message.senderId.substring(this.message.senderId.length - 2, this.message.senderId.length)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
getMessageType(): V2NIMMessageType {
|
||||
return this.message.messageType
|
||||
}
|
||||
|
||||
isAiStreamMessage(): boolean {
|
||||
return this.message.aiConfig?.aiStream ?? false
|
||||
}
|
||||
|
||||
isFinishedAiStream(): boolean {
|
||||
return (this.message.aiConfig?.aiStreamStatus ??
|
||||
V2NIMMessageAIStreamStatus.V2NIM_MESSAGE_AI_STREAM_STATUS_GENERATED) >
|
||||
V2NIMMessageAIStreamStatus.V2NIM_MESSAGE_AI_STREAM_STATUS_PLACEHOLDER
|
||||
}
|
||||
|
||||
isReceiveMessage(): boolean {
|
||||
return this.isReceiveMsg
|
||||
}
|
||||
|
||||
isPinMessage(): boolean {
|
||||
return this.isPinMsg
|
||||
}
|
||||
|
||||
isMergeMessage(): boolean {
|
||||
return this.isMergeMsg
|
||||
}
|
||||
|
||||
isMergeDetailMessage(): boolean {
|
||||
return this.isMergeDetailMsg
|
||||
}
|
||||
|
||||
getImageUrl(): string {
|
||||
if (this.message.messageType == V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE) {
|
||||
let iamgeAttachment = this.message.attachment as V2NIMMessageImageAttachment
|
||||
if (iamgeAttachment) {
|
||||
if (iamgeAttachment.path) {
|
||||
if (fs.accessSync(iamgeAttachment.path)) {
|
||||
const uri = fileUri.getUriFromPath(iamgeAttachment.path)
|
||||
return uri
|
||||
}
|
||||
}
|
||||
return iamgeAttachment.url ?? ''
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
async getImageThumbUrl(): Promise<string> {
|
||||
if (this.message.messageType == V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE) {
|
||||
let iamgeAttachment = this.message.attachment as V2NIMMessageImageAttachment
|
||||
if (iamgeAttachment) {
|
||||
if (iamgeAttachment.path) {
|
||||
if (fs.accessSync(iamgeAttachment.path)) {
|
||||
const uri = fileUri.getUriFromPath(iamgeAttachment.path)
|
||||
return uri
|
||||
}
|
||||
}
|
||||
const thumbResult: V2NIMGetMediaResourceInfoResult | undefined =
|
||||
await StorageRepo.getImageThumbUrl(iamgeAttachment, {
|
||||
width: ChatConst.imageMessageWidth
|
||||
})
|
||||
if (!thumbResult) {
|
||||
return iamgeAttachment.url ?? ''
|
||||
} else {
|
||||
return thumbResult.url ?? ''
|
||||
}
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export class RevokeInfo {
|
||||
isLocalRevoke: boolean = false
|
||||
revokeTime: number = 0
|
||||
isEditMsg: boolean = false
|
||||
revokeMsgText: string = ''
|
||||
revokeMsgClientId: string = ''
|
||||
|
||||
static parseRevokeInfo(msg: V2NIMMessage): RevokeInfo | undefined {
|
||||
if (msg == undefined || msg.messageType !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_TEXT ||
|
||||
msg.localExtension == undefined
|
||||
|| !msg.localExtension?.includes(ChatConst.revokeLocalKey)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
let localExtension = msg.localExtension
|
||||
if (JSONUtil.isJSONString(localExtension)) {
|
||||
let localObject = JSON.parse(localExtension) as object
|
||||
let revokeInfo = new RevokeInfo()
|
||||
revokeInfo.revokeMsgText = localObject?.[ChatConst.revokeMsgTextKey]
|
||||
revokeInfo.revokeMsgClientId = localObject?.[ChatConst.revokeMsgClientIdKey]
|
||||
revokeInfo.revokeTime = localObject?.[ChatConst.revokeLocalTimeKey]
|
||||
revokeInfo.isLocalRevoke = localObject?.[ChatConst.revokeLocalKey]
|
||||
revokeInfo.isEditMsg = localObject?.[ChatConst.revokeMsgEditKey]
|
||||
return revokeInfo
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
14
chatkit_ui/src/main/ets/model/TeamDataResult.ets
Normal file
14
chatkit_ui/src/main/ets/model/TeamDataResult.ets
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { V2NIMTeam, V2NIMTeamMember, V2NIMUser } from '@nimsdk/base';
|
||||
|
||||
export class TeamDataResult {
|
||||
team?: V2NIMTeam | undefined = undefined;
|
||||
teamMemberList: V2NIMTeamMember[] = [];
|
||||
teamUserList: V2NIMUser[] = [];
|
||||
}
|
||||
1429
chatkit_ui/src/main/ets/pages/ChatP2PPage.ets
Normal file
1429
chatkit_ui/src/main/ets/pages/ChatP2PPage.ets
Normal file
File diff suppressed because it is too large
Load Diff
164
chatkit_ui/src/main/ets/pages/ChatReadReceiptPage.ets
Normal file
164
chatkit_ui/src/main/ets/pages/ChatReadReceiptPage.ets
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import { ChatKitClient, TeamMemberWithUser } from '@nimkit/chatkit'
|
||||
import { ChatReadReceiptViewModel } from '../viewmodel/ChatReadReceiptViewModel'
|
||||
import { AvatarColorUntil, AvatarItem, CommonAvatar, CommonEmptyResult, NavigationBackBuilder } from '@nimkit/common'
|
||||
import { V2NIMMessage } from '@nimsdk/base'
|
||||
|
||||
@ComponentV2
|
||||
export struct ChatReadReceiptPage {
|
||||
pathStack: NavPathStack = new NavPathStack()
|
||||
//群Id
|
||||
teamId?: string
|
||||
//消息
|
||||
message?: V2NIMMessage
|
||||
@Local viewModel: ChatReadReceiptViewModel = new ChatReadReceiptViewModel()
|
||||
|
||||
build() {
|
||||
NavDestination() {
|
||||
NavigationBackBuilder({
|
||||
title: $r('app.string.chat_read_receipt_title'),
|
||||
backgroundColor: '#ffffffff',
|
||||
leftButtonAction: () => {
|
||||
this.pathStack.pop()
|
||||
}
|
||||
})
|
||||
if (this.viewModel.readReceiptDetail !== null) {
|
||||
Tabs({
|
||||
barPosition: BarPosition.Start
|
||||
}) {
|
||||
|
||||
TabContent() {
|
||||
if (this.viewModel.readReceiptDetail.readAccountList.length <= 0) {
|
||||
CommonEmptyResult({
|
||||
tips: $r('app.string.chat_read_receipt_all_unread')
|
||||
})
|
||||
} else {
|
||||
List() {
|
||||
ForEach(this.viewModel.readMemberList, (member: TeamMemberWithUser) => {
|
||||
ListItem() {
|
||||
Row() {
|
||||
CommonAvatar({
|
||||
item: new AvatarItem(
|
||||
member.getAvatar(),
|
||||
member.getAvatarName(),
|
||||
AvatarColorUntil.getBackgroundColorById(member.getAccId())
|
||||
)
|
||||
})
|
||||
.width(42)
|
||||
.height(42)
|
||||
.margin({
|
||||
right: 14
|
||||
})
|
||||
|
||||
Text(member.getNickname())
|
||||
.fontColor('#ff333333')
|
||||
.fontSize(16)
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
.width(280)
|
||||
}
|
||||
.padding({
|
||||
left: 20,
|
||||
right: 20,
|
||||
top: 10,
|
||||
bottom: 10
|
||||
})
|
||||
.width('100%')
|
||||
}
|
||||
})
|
||||
}
|
||||
.listDirection(Axis.Vertical)
|
||||
.scrollBar(BarState.Off)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor('#ffFFFFFF')
|
||||
}
|
||||
|
||||
}
|
||||
.tabBar($r('app.string.chat_read_receipt_read',
|
||||
this.viewModel.readReceiptDetail.readReceipt.readCount))
|
||||
|
||||
TabContent() {
|
||||
if (this.viewModel.readReceiptDetail.unreadAccountList.length <= 0) {
|
||||
CommonEmptyResult({
|
||||
tips: $r('app.string.chat_read_receipt_all_read')
|
||||
}).margin({
|
||||
top: 80
|
||||
})
|
||||
} else {
|
||||
List() {
|
||||
ForEach(this.viewModel.unreadMemberList, (member: TeamMemberWithUser) => {
|
||||
ListItem() {
|
||||
Row() {
|
||||
CommonAvatar({
|
||||
item: new AvatarItem(
|
||||
member.getAvatar(),
|
||||
member.getAvatarName(),
|
||||
AvatarColorUntil.getBackgroundColorById(member.getAccId())
|
||||
)
|
||||
})
|
||||
.width(42)
|
||||
.height(42)
|
||||
.margin({
|
||||
right: 14
|
||||
})
|
||||
|
||||
Text(member.getNickname())
|
||||
.fontColor('#ff333333')
|
||||
.fontSize(16)
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
.width(280)
|
||||
}
|
||||
.padding({
|
||||
left: 20,
|
||||
right: 20,
|
||||
top: 10,
|
||||
bottom: 10
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
.listDirection(Axis.Vertical)
|
||||
.scrollBar(BarState.Off)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor('#ffFFFFFF')
|
||||
}
|
||||
|
||||
}.tabBar($r('app.string.chat_read_receipt_unread', this.viewModel.readReceiptDetail.readReceipt.unreadCount))
|
||||
.onAttach(() => {
|
||||
if (this.viewModel.readReceiptDetail) {
|
||||
this.viewModel.getMemberList(this.viewModel.readReceiptDetail.unreadAccountList, false)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.hideTitleBar(true)
|
||||
.backgroundColor(Color.White)
|
||||
.onReady((context: NavDestinationContext) => {
|
||||
this.pathStack = context.pathStack
|
||||
this.message = context.pathStack.getParamByName('ChatReadReceiptPage')[0] as V2NIMMessage
|
||||
this.teamId = ChatKitClient.nim.conversationIdUtil.parseConversationTargetId(this.message.conversationId)
|
||||
this.viewModel.init(this.message)
|
||||
this.viewModel.getMessageReadReceipt()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Builder
|
||||
export function ChatReadReceiptPageBuilder() {
|
||||
ChatReadReceiptPage()
|
||||
}
|
||||
|
||||
1432
chatkit_ui/src/main/ets/pages/ChatTeamPage.ets
Normal file
1432
chatkit_ui/src/main/ets/pages/ChatTeamPage.ets
Normal file
File diff suppressed because it is too large
Load Diff
203
chatkit_ui/src/main/ets/pages/MergeMessageDetailPage.ets
Normal file
203
chatkit_ui/src/main/ets/pages/MergeMessageDetailPage.ets
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { NIMMessageInfo } from '../model/NIMMessageInfo';
|
||||
import { MergeDetailMessageComponent } from '../view/MessageComponent';
|
||||
import { NEEmojiManager } from '../manager/NEEmojiManager';
|
||||
import { ImagesIndexModel, ImageViewDialog, NavigationBackBuilder, VideoViewerDialog } from '@nimkit/common';
|
||||
import { DeviceUtils } from '../common/DeviceUtils';
|
||||
import {
|
||||
downLoadAndOpenFile,
|
||||
getMessageImageUrls,
|
||||
getMessageVideoRatio,
|
||||
getMessageVideoUrl
|
||||
} from '../common/MessageHelper';
|
||||
import { V2NIMMessageLocationAttachment, V2NIMMessageType } from '@nimsdk/base';
|
||||
import { CustomMessageUtils, MergedMessageAttachment, mergedMessageCustomType } from '@nimkit/chatkit';
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
import { AudioPlayerManager } from '../manager/AudioPlayerManager';
|
||||
import { common } from '@kit.AbilityKit';
|
||||
import { MergeMessageDetailViewModel } from '../viewmodel/MergeMessageDetailViewModel';
|
||||
import { MergeMessageInfo } from '../model/MergeMessageInfo';
|
||||
import { sceneMap } from '@kit.MapKit';
|
||||
|
||||
@ComponentV2
|
||||
export struct MergeMessageDetailPage {
|
||||
pathStack: NavPathStack = new NavPathStack()
|
||||
@Local mergeMessageInfo: MergeMessageInfo = new MergeMessageInfo('');
|
||||
viewModel: MergeMessageDetailViewModel = new MergeMessageDetailViewModel();
|
||||
conversationId: string = '';
|
||||
@Local imagesIndexModel?: ImagesIndexModel
|
||||
@Local currentImageIndex: number = 0
|
||||
imageViewerDialog: CustomDialogController = new CustomDialogController({
|
||||
builder: ImageViewDialog({
|
||||
imagesIndexModel: this.imagesIndexModel
|
||||
}),
|
||||
cornerRadius: 0,
|
||||
alignment: DialogAlignment.Center,
|
||||
backgroundColor: Color.Black,
|
||||
backgroundBlurStyle: BlurStyle.NONE,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
customStyle: true,
|
||||
})
|
||||
@Local videoFileUrl?: string
|
||||
@Local videoRatio?: number
|
||||
videoViewerDialog: CustomDialogController = new CustomDialogController({
|
||||
builder: VideoViewerDialog({
|
||||
videoUrl: this.videoFileUrl,
|
||||
videoRatio: this.videoRatio
|
||||
}),
|
||||
cornerRadius: 0,
|
||||
alignment: DialogAlignment.Center,
|
||||
backgroundColor: Color.Black,
|
||||
backgroundBlurStyle: BlurStyle.NONE,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
customStyle: true
|
||||
})
|
||||
private listScroller: Scroller = new Scroller()
|
||||
|
||||
aboutToAppear(): void {
|
||||
NEEmojiManager.instance.setup();
|
||||
DeviceUtils.rootDirPath = getContext(this).filesDir
|
||||
}
|
||||
|
||||
async requestData() {
|
||||
let param = this.pathStack.getParamByName("MergeMessageDetailPage") as MergedMessageAttachment[];
|
||||
if (param.length > 0) {
|
||||
this.viewModel.init(this.mergeMessageInfo)
|
||||
this.viewModel.loadMergeMessage(this.pathStack, param[param.length - 1])
|
||||
} else {
|
||||
this.pathStack.removeByName("MergeMessageDetailPage")
|
||||
}
|
||||
}
|
||||
|
||||
async showImageDetail(msg?: NIMMessageInfo) {
|
||||
try {
|
||||
const imageModel = await getMessageImageUrls(msg, this.mergeMessageInfo)
|
||||
this.imagesIndexModel = imageModel
|
||||
this.imageViewerDialog.open()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
showVideoDetail(msg: NIMMessageInfo) {
|
||||
//点击视频消息前关闭所有音频
|
||||
AudioPlayerManager.instance.stopPlayAll()
|
||||
this.videoFileUrl = getMessageVideoUrl(msg, this.mergeMessageInfo)
|
||||
this.videoRatio = getMessageVideoRatio(msg)
|
||||
if (this.videoFileUrl) {
|
||||
this.videoViewerDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
showLocationDetail(msg: NIMMessageInfo) {
|
||||
let attachment = msg.message.attachment as V2NIMMessageLocationAttachment
|
||||
|
||||
let queryLocationOptions: sceneMap.LocationQueryOptions = {
|
||||
location: {
|
||||
latitude: attachment.latitude,
|
||||
longitude: attachment.longitude
|
||||
},
|
||||
name: attachment.address,
|
||||
address: attachment.address,
|
||||
};
|
||||
// 拉起地点详情页
|
||||
sceneMap.queryLocation(getContext(this) as common.UIAbilityContext, queryLocationOptions).then(() => {
|
||||
console.info("netease QueryLocation", "Succeeded in querying location.");
|
||||
}).catch((err: BusinessError) => {
|
||||
console.error("netease QueryLocation", `Failed to query Location, code: ${err.code}, message: ${err.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
@Builder
|
||||
build() {
|
||||
NavDestination() {
|
||||
NavigationBackBuilder({
|
||||
title: $r('app.string.chatHistoryBrief'),
|
||||
backgroundColor: Color.White,
|
||||
leftButtonAction: () => {
|
||||
this.pathStack.pop()
|
||||
},
|
||||
})
|
||||
|
||||
RelativeContainer() {
|
||||
List({ space: 12, scroller: this.listScroller }) {
|
||||
LazyForEach(this.mergeMessageInfo.msgList, (msg: NIMMessageInfo) => {
|
||||
ListItem() {
|
||||
MergeDetailMessageComponent({
|
||||
message: msg,
|
||||
chatUserInfo: this.mergeMessageInfo,
|
||||
onMessageClick: {
|
||||
onAvatarClick: undefined,
|
||||
onItemLongClick: undefined,
|
||||
onItemClick: (event: ClickEvent, msg: NIMMessageInfo | undefined) => {
|
||||
if (msg) {
|
||||
if (msg?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE) {
|
||||
this.showImageDetail(msg)
|
||||
} else if (msg?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_FILE) {
|
||||
downLoadAndOpenFile(msg, getContext(this), this.mergeMessageInfo)
|
||||
} else if (msg?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_VIDEO) {
|
||||
this.showVideoDetail(msg)
|
||||
} else if (msg?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_LOCATION) {
|
||||
this.showLocationDetail(msg)
|
||||
} else if (msg?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_CUSTOM) {
|
||||
const attachment = msg.message.attachment
|
||||
if (attachment) {
|
||||
const type = CustomMessageUtils.typeOfCustomMessage(attachment)
|
||||
if (type === mergedMessageCustomType) {
|
||||
let data = CustomMessageUtils.dataOfCustomMessage(attachment)
|
||||
if (data as MergedMessageAttachment) {
|
||||
this.pathStack.pushPath({
|
||||
name: 'MergeMessageDetailPage',
|
||||
param: data as MergedMessageAttachment
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}, (item: NIMMessageInfo, index?: number) => item.message.messageClientId)
|
||||
}
|
||||
.id("mergeMessageDetailPageListView")
|
||||
.cachedCount(20)
|
||||
.padding({ bottom: 56 })
|
||||
.maintainVisibleContentPosition(true)
|
||||
.alignRules({
|
||||
left: { anchor: "__container__", align: HorizontalAlign.Start },
|
||||
right: { anchor: "__container__", align: HorizontalAlign.End },
|
||||
top: { anchor: "__container__", align: VerticalAlign.Top },
|
||||
})
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
}
|
||||
.hideTitleBar(true)
|
||||
.backgroundColor(Color.White)
|
||||
.onReady((context: NavDestinationContext) => {
|
||||
this.pathStack = context.pathStack
|
||||
this.requestData()
|
||||
})
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
this.viewModel.onDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转页面入口函数
|
||||
@Builder
|
||||
export function MergeMessageDetailPageBuilder() {
|
||||
MergeMessageDetailPage()
|
||||
}
|
||||
|
||||
420
chatkit_ui/src/main/ets/pages/PinMessagePage.ets
Normal file
420
chatkit_ui/src/main/ets/pages/PinMessagePage.ets
Normal file
@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { ChatTeamInfo } from '../model/ChatTeamInfo';
|
||||
import { NIMMessageInfo } from '../model/NIMMessageInfo';
|
||||
import { PinMessageComponent } from '../view/PinMessageComponent';
|
||||
import { NEEmojiManager } from '../manager/NEEmojiManager';
|
||||
import {
|
||||
CommonEmptyResult,
|
||||
ImagesIndexModel,
|
||||
ImageViewDialog,
|
||||
NavigationBackBuilder,
|
||||
NECommonUtils,
|
||||
VideoViewerDialog
|
||||
} from '@nimkit/common';
|
||||
import { DeviceUtils } from '../common/DeviceUtils';
|
||||
import {
|
||||
downLoadAndOpenFile,
|
||||
getMessageImageUrls,
|
||||
getMessageVideoRatio,
|
||||
getMessageVideoUrl
|
||||
} from '../common/MessageHelper';
|
||||
import { V2NIMConversationType, V2NIMErrorCode, V2NIMMessageType } from '@nimsdk/base';
|
||||
import pasteboard from '@ohos.pasteboard';
|
||||
import { ChatKitClient } from '@nimkit/chatkit/src/main/ets/ChatKitClient';
|
||||
import { ChatRepo } from '@nimkit/chatkit/src/main/ets/repo/ChatRepo';
|
||||
import { TextMessageDetailDialog } from '../view/TextMessageDetailDialog';
|
||||
import { ConversationSelectParam } from '@nimkit/chatkit/src/main/ets/model/ConversationSelectParam';
|
||||
import { ConversationSelectModel } from '@nimkit/chatkit/src/main/ets/model/ConversationSelectModel';
|
||||
import { ForwardMessageDialog } from '../view/ForwardMessageDialog';
|
||||
import { ChatPinViewModel } from '../viewmodel/ChatPinViewModel';
|
||||
import {
|
||||
conversationSelectLimitCount,
|
||||
CustomMessageUtils,
|
||||
ErrorUtils,
|
||||
MergedMessageAttachment,
|
||||
mergedMessageCustomType
|
||||
} from '@nimkit/chatkit';
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
import { ChatBaseViewModel } from '../viewmodel/ChatBaseViewModel';
|
||||
import { AudioPlayerManager } from '../manager/AudioPlayerManager';
|
||||
import { common } from '@kit.AbilityKit';
|
||||
import { showLocationDetail } from '../common/ChatUtils';
|
||||
|
||||
@CustomDialog
|
||||
export struct PinMoreActionDialog {
|
||||
@BuilderParam message: NIMMessageInfo
|
||||
@BuilderParam dialogHeight: number
|
||||
@BuilderParam forwardMessageAction?: (message: NIMMessageInfo) => void
|
||||
controller?: CustomDialogController
|
||||
unpinMessage = async () => {
|
||||
if (ErrorUtils.checkNetworkAndToast()) {
|
||||
await ChatRepo.unpinMessage(this.message.message).catch((err: BusinessError) => {
|
||||
if (err.code == V2NIMErrorCode.V2NIM_ERROR_CODE_PIN_NOT_EXIST) {
|
||||
return
|
||||
}
|
||||
ErrorUtils.handleErrorToast(err.code)
|
||||
})
|
||||
NECommonUtils.showToast($r('app.string.pin_list_unpin_success_tips'))
|
||||
this.cancel()
|
||||
}
|
||||
}
|
||||
copyMessage = () => {
|
||||
const copyData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, this.message.message.text)
|
||||
pasteboard.getSystemPasteboard().setData(copyData).then(() => {
|
||||
NECommonUtils.showToast($r('app.string.copy_success'))
|
||||
this.cancel()
|
||||
})
|
||||
}
|
||||
forwardMessage = () => {
|
||||
this.forwardMessageAction?.(this.message)
|
||||
this.cancel()
|
||||
}
|
||||
cancel = () => {
|
||||
this.controller?.close()
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Column() {
|
||||
Text($r("app.string.setting_unpin"))
|
||||
.fontSize(14)
|
||||
.fontColor("#333333")
|
||||
.textAlign(TextAlign.Center)
|
||||
.height(42)
|
||||
.width('100%')
|
||||
.onClick(this.unpinMessage)
|
||||
|
||||
if (this.message.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_TEXT) {
|
||||
Row()
|
||||
.height(1)
|
||||
.width('100%')
|
||||
.backgroundColor("#EFF1F4")
|
||||
|
||||
Text($r("app.string.chat_operation_copy"))
|
||||
.fontSize(14)
|
||||
.fontColor("#333333")
|
||||
.textAlign(TextAlign.Center)
|
||||
.height(42)
|
||||
.width('100%')
|
||||
.onClick(this.copyMessage)
|
||||
}
|
||||
|
||||
if (this.message.message.messageType !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_AUDIO &&
|
||||
this.message.message.messageType !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL) {
|
||||
Row()
|
||||
.height(1)
|
||||
.width('100%')
|
||||
.backgroundColor("#EFF1F4")
|
||||
|
||||
Text($r("app.string.chat_operation_forward"))
|
||||
.fontSize(14)
|
||||
.fontColor("#333333")
|
||||
.textAlign(TextAlign.Center)
|
||||
.height(42)
|
||||
.width('100%')
|
||||
.onClick(this.forwardMessage)
|
||||
}
|
||||
}
|
||||
.height(this.dialogHeight)
|
||||
.width('100%')
|
||||
.backgroundColor(Color.White)
|
||||
.borderRadius(12)
|
||||
.margin({ left: 12, right: 12 })
|
||||
|
||||
Column() {
|
||||
Text($r('app.string.mine_edit_cancel'))
|
||||
.fontSize(14)
|
||||
.fontColor("#333333")
|
||||
.textAlign(TextAlign.Center)
|
||||
.height(42)
|
||||
.width('100%')
|
||||
.onClick(this.cancel)
|
||||
}
|
||||
.height(42)
|
||||
.width('100%')
|
||||
.backgroundColor(Color.White)
|
||||
.borderRadius(12)
|
||||
.margin({ top: 10, left: 12, right: 12 })
|
||||
|
||||
}
|
||||
.height(this.dialogHeight + 52)
|
||||
.backgroundColor(Color.Transparent)
|
||||
}
|
||||
}
|
||||
|
||||
@ComponentV2
|
||||
export struct PinMessagePage {
|
||||
pathStack: NavPathStack = new NavPathStack()
|
||||
@Local chatTeamInfo: ChatTeamInfo = new ChatTeamInfo('');
|
||||
viewModel: ChatPinViewModel = new ChatPinViewModel();
|
||||
conversationId: string = '';
|
||||
@Local imagesIndexModel?: ImagesIndexModel
|
||||
@Local currentImageIndex: number = 0
|
||||
imageViewerDialog: CustomDialogController = new CustomDialogController({
|
||||
builder: ImageViewDialog({
|
||||
imagesIndexModel: this.imagesIndexModel
|
||||
}),
|
||||
cornerRadius: 0,
|
||||
alignment: DialogAlignment.Center,
|
||||
backgroundColor: Color.Black,
|
||||
backgroundBlurStyle: BlurStyle.NONE,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
customStyle: true,
|
||||
})
|
||||
@Local videoFileUrl?: string
|
||||
@Local videoRatio?: number
|
||||
videoViewerDialog: CustomDialogController = new CustomDialogController({
|
||||
builder: VideoViewerDialog({
|
||||
videoUrl: this.videoFileUrl,
|
||||
videoRatio: this.videoRatio
|
||||
}),
|
||||
cornerRadius: 0,
|
||||
alignment: DialogAlignment.Center,
|
||||
backgroundColor: Color.Black,
|
||||
backgroundBlurStyle: BlurStyle.NONE,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
customStyle: true
|
||||
})
|
||||
@Local tapMessage?: NIMMessageInfo
|
||||
@Local pinMoreActionDialogHeight: number = 42
|
||||
@Local forwardMessages: NIMMessageInfo[] = []
|
||||
@Local forwardConversations: ConversationSelectModel[] = []
|
||||
@Local currentConversationName?: string
|
||||
forwardMessageDialog = new CustomDialogController({
|
||||
builder: ForwardMessageDialog({
|
||||
conversationList: this.forwardConversations,
|
||||
currentConversationName: this.currentConversationName,
|
||||
sendForwardMsg: (text: string | undefined) => {
|
||||
this.viewModel.forwardMessage(this.forwardMessages, this.forwardConversations, text)
|
||||
}
|
||||
}),
|
||||
cornerRadius: 14,
|
||||
backgroundColor: Color.White,
|
||||
height: 250,
|
||||
width: 276,
|
||||
})
|
||||
forwardMessageClick = () => {
|
||||
if (ErrorUtils.checkNetworkAndToast()) {
|
||||
this.pathStack.pushPath({
|
||||
name: 'ConversationSelectPage',
|
||||
param: new ConversationSelectParam([], conversationSelectLimitCount, this.forwardMessageAction)
|
||||
})
|
||||
}
|
||||
}
|
||||
pinMoreActionDialog: CustomDialogController = new CustomDialogController({
|
||||
builder: PinMoreActionDialog({
|
||||
message: this.tapMessage,
|
||||
dialogHeight: this.pinMoreActionDialogHeight,
|
||||
forwardMessageAction: this.forwardMessageClick
|
||||
}),
|
||||
cornerRadius: 0,
|
||||
alignment: DialogAlignment.Bottom,
|
||||
backgroundColor: Color.Transparent,
|
||||
backgroundBlurStyle: BlurStyle.NONE,
|
||||
})
|
||||
textMessageDetailDialog: CustomDialogController = new CustomDialogController({
|
||||
builder: TextMessageDetailDialog({
|
||||
message: this.tapMessage
|
||||
}),
|
||||
cornerRadius: 0,
|
||||
alignment: DialogAlignment.Center,
|
||||
backgroundColor: Color.White,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
customStyle: true
|
||||
})
|
||||
private listScroller: Scroller = new Scroller()
|
||||
private scrollIndex: number = 0
|
||||
|
||||
@Monitor("viewModel.topInsert")
|
||||
scrollToTop() {
|
||||
if (this.scrollIndex === 0) {
|
||||
this.listScroller.scrollToIndex(0)
|
||||
}
|
||||
this.viewModel.topInsert = false
|
||||
}
|
||||
|
||||
forwardMessageAction = (selectedList: ConversationSelectModel[]) => {
|
||||
if (this.tapMessage?.message) {
|
||||
this.forwardMessages = [this.tapMessage]
|
||||
this.forwardConversations = selectedList
|
||||
const conversationType = ChatKitClient.nim.conversationIdUtil.parseConversationType(this.conversationId)
|
||||
if (conversationType === V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P) {
|
||||
const targetAccountId = ChatKitClient.nim.conversationIdUtil.parseConversationTargetId(this.conversationId)
|
||||
this.currentConversationName = this.chatTeamInfo.getChatUserShowName(targetAccountId)
|
||||
} else {
|
||||
this.currentConversationName = this.chatTeamInfo.team?.name
|
||||
}
|
||||
this.forwardMessageDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
getPinMoreActionDialogHeight(message?: NIMMessageInfo): number {
|
||||
let height = 42
|
||||
if (message?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_TEXT) {
|
||||
height += 43
|
||||
}
|
||||
if (message?.message.messageType !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_AUDIO) {
|
||||
height += 43
|
||||
}
|
||||
return height
|
||||
}
|
||||
|
||||
aboutToAppear(): void {
|
||||
NEEmojiManager.instance.setup();
|
||||
DeviceUtils.rootDirPath = getContext(this).filesDir
|
||||
}
|
||||
|
||||
async requestChatData() {
|
||||
this.chatTeamInfo.setConversationId(this.conversationId)
|
||||
this.viewModel.init(this.conversationId as string, this.chatTeamInfo)
|
||||
this.viewModel.getPinMessageList()
|
||||
}
|
||||
|
||||
async showImageDetail(msg?: NIMMessageInfo) {
|
||||
try {
|
||||
const imageModel = await getMessageImageUrls(msg, this.chatTeamInfo)
|
||||
this.imagesIndexModel = imageModel
|
||||
this.imageViewerDialog.open()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
showVideoDetail(msg: NIMMessageInfo) {
|
||||
//点击视频消息前关闭所有音频
|
||||
AudioPlayerManager.instance.stopPlayAll()
|
||||
this.videoFileUrl = getMessageVideoUrl(msg, this.chatTeamInfo)
|
||||
this.videoRatio = getMessageVideoRatio(msg)
|
||||
if (this.videoFileUrl) {
|
||||
this.videoViewerDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
@Builder
|
||||
build() {
|
||||
NavDestination() {
|
||||
NavigationBackBuilder({
|
||||
title: $r("app.string.setting_pin"),
|
||||
backgroundColor: '#EFF1F4',
|
||||
leftButtonAction: () => {
|
||||
this.pathStack.pop()
|
||||
},
|
||||
})
|
||||
if (this.viewModel.pinListEmpty) {
|
||||
CommonEmptyResult({
|
||||
tips: $r('app.string.setting_pin_empty')
|
||||
})
|
||||
.margin({
|
||||
top: 56 + 74,
|
||||
})
|
||||
}
|
||||
RelativeContainer() {
|
||||
List({ space: 12, scroller: this.listScroller }) {
|
||||
LazyForEach(this.chatTeamInfo.msgList, (msg: NIMMessageInfo) => {
|
||||
ListItem() {
|
||||
PinMessageComponent({
|
||||
message: msg,
|
||||
chatUserInfo: this.chatTeamInfo,
|
||||
onMessageClick: {
|
||||
onAvatarClick: undefined,
|
||||
onItemLongClick: undefined,
|
||||
onItemClick: (_event: ClickEvent, msg: NIMMessageInfo | undefined) => {
|
||||
this.tapMessage = msg
|
||||
if (msg?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE) {
|
||||
this.showImageDetail(msg)
|
||||
} else if (msg?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_FILE) {
|
||||
downLoadAndOpenFile(msg, getContext(this), this.chatTeamInfo)
|
||||
} else if (msg?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_VIDEO) {
|
||||
this.showVideoDetail(msg)
|
||||
} else if (msg?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_TEXT) {
|
||||
this.textMessageDetailDialog.open()
|
||||
} else if (msg?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_LOCATION) {
|
||||
showLocationDetail(msg, getContext(this) as common.UIAbilityContext)
|
||||
} else if (msg?.message.messageType === V2NIMMessageType.V2NIM_MESSAGE_TYPE_CUSTOM) {
|
||||
const attachment = msg.message.attachment
|
||||
if (attachment) {
|
||||
const type = CustomMessageUtils.typeOfCustomMessage(attachment)
|
||||
if (type === mergedMessageCustomType) {
|
||||
let data = CustomMessageUtils.dataOfCustomMessage(attachment)
|
||||
if (data as MergedMessageAttachment) {
|
||||
this.pathStack.pushPath({
|
||||
name: 'MergeMessageDetailPage',
|
||||
param: data as MergedMessageAttachment
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onMoreButtonClick: (_event: ClickEvent, msg: NIMMessageInfo | undefined) => {
|
||||
this.tapMessage = msg
|
||||
this.pinMoreActionDialogHeight = this.getPinMoreActionDialogHeight(msg)
|
||||
this.pinMoreActionDialog.open()
|
||||
},
|
||||
onPinItemClick: (_event: ClickEvent, msg: NIMMessageInfo | undefined) => {
|
||||
if (msg) {
|
||||
if (ChatBaseViewModel.currentViewModel) {
|
||||
ChatBaseViewModel.currentViewModel.setAnchorMessage(msg)
|
||||
}
|
||||
if (msg.message.conversationType == V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P) {
|
||||
this.pathStack.popToName('ChatP2PPage')
|
||||
} else if (msg.message.conversationType == V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM) {
|
||||
this.pathStack.popToName('ChatTeamPage')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}, (item: NIMMessageInfo) => item.message.messageClientId)
|
||||
}
|
||||
.id("pinPageListView")
|
||||
.cachedCount(20)
|
||||
.padding({ bottom: 56 })
|
||||
.maintainVisibleContentPosition(true)
|
||||
.alignRules({
|
||||
left: { anchor: "__container__", align: HorizontalAlign.Start },
|
||||
right: { anchor: "__container__", align: HorizontalAlign.End },
|
||||
top: { anchor: "__container__", align: VerticalAlign.Top },
|
||||
})
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.onScrollIndex((start: number, _end: number) => {
|
||||
this.scrollIndex = start
|
||||
})
|
||||
}
|
||||
}
|
||||
.hideTitleBar(true)
|
||||
.backgroundColor('#EFF1F4')
|
||||
.onReady((context: NavDestinationContext) => {
|
||||
this.pathStack = context.pathStack
|
||||
let param = this.pathStack.getParamByName("PinMessagePage") as string[];
|
||||
if (param.length > 0) {
|
||||
this.conversationId = param[0];
|
||||
this.requestChatData();
|
||||
} else {
|
||||
this.pathStack.removeByName("PinMessagePage")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
this.viewModel.onDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转页面入口函数
|
||||
@Builder
|
||||
export function PinMessagePageBuilder() {
|
||||
PinMessagePage()
|
||||
}
|
||||
|
||||
249
chatkit_ui/src/main/ets/pages/QuickMessagePage.ets
Normal file
249
chatkit_ui/src/main/ets/pages/QuickMessagePage.ets
Normal file
@ -0,0 +1,249 @@
|
||||
import {
|
||||
NavigationBackBuilder,
|
||||
|
||||
CommonEmptyResult
|
||||
} from '@nimkit/common'
|
||||
import { window } from '@kit.ArkUI';
|
||||
import { hdHttp, HdResponse,BasicConstant, authStore} from '@itcast/basic'
|
||||
import { BusinessError } from '@kit.BasicServicesKit';
|
||||
import { promptAction,LengthMetrics } from '@kit.ArkUI'
|
||||
import { PerfactInputSheet } from '@itcast/basic/src/main/ets/Views/PerfactInputSheet'
|
||||
@ComponentV2
|
||||
export struct QuickMessagePage {
|
||||
@Local delectuuid:string=''
|
||||
@Local statusBarHeight:number=0
|
||||
pathStack: NavPathStack = new NavPathStack()
|
||||
@Local replydata:QuickReplyData[]=[]
|
||||
private InputSheet!:CustomDialogController;
|
||||
dialog = new CustomDialogController({
|
||||
builder: DelectDialog(
|
||||
{
|
||||
CallBack:()=>{
|
||||
|
||||
this.deleteQuickReply(this.delectuuid)
|
||||
|
||||
}
|
||||
}
|
||||
),
|
||||
cornerRadius: 4,
|
||||
width: '70%',
|
||||
})
|
||||
build() {
|
||||
NavDestination() {
|
||||
Column() {
|
||||
NavigationBackBuilder({
|
||||
title:$r('app.string.reply'),
|
||||
top:this.statusBarHeight,
|
||||
leftButtonAction: () => {
|
||||
this.pathStack.pop()
|
||||
}
|
||||
})
|
||||
Row(){
|
||||
Image($r('app.media.add_drawle')).width(30).height(30)
|
||||
Text('添加快捷回复').margin({left:10}).fontSize(20).fontColor($r('app.color.common_gray_03'))
|
||||
}
|
||||
.width('100%')
|
||||
.height(50)
|
||||
.padding({left:10})
|
||||
.onClick(()=>{
|
||||
this.InputSheet.open()
|
||||
})
|
||||
|
||||
Text('').height(5).width('100%')
|
||||
.backgroundColor($r('app.color.common_gray_bg'))
|
||||
if(this.replydata==null||this.replydata.length==0)
|
||||
{
|
||||
CommonEmptyResult({
|
||||
tips: $r('app.string.reply_empty')
|
||||
})
|
||||
}
|
||||
else
|
||||
{
|
||||
List() {
|
||||
ForEach(this.replydata, (item: QuickReplyData) => {
|
||||
ListItem() {
|
||||
Text(item.replystr).fontSize(16).fontColor($r('app.color.common_gray_01')).padding(20).textAlign(TextAlign.Start).width('100%')
|
||||
}
|
||||
.onClick(()=>{
|
||||
this.pathStack.pop(item.replystr)
|
||||
})
|
||||
.gesture(
|
||||
LongPressGesture({
|
||||
duration: 1000, // 设置长按触发时间为1秒
|
||||
repeat: true // 允许连续触发回调
|
||||
})
|
||||
.onAction((event: GestureEvent) => {
|
||||
this.delectuuid=item.uuid
|
||||
this.dialog.open()
|
||||
})
|
||||
)
|
||||
.width('100%')
|
||||
|
||||
})
|
||||
}
|
||||
.divider({
|
||||
strokeWidth: 1, // 线宽
|
||||
color: $r('app.color.common_gray_bg'), // 颜色
|
||||
})
|
||||
.margin({ left: 10, right: 10, top: 0 })
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
.hideTitleBar(true)
|
||||
.backgroundColor(Color.White)
|
||||
.onReady((context: NavDestinationContext) => {
|
||||
this.pathStack = context.pathStack
|
||||
|
||||
})
|
||||
}
|
||||
aboutToAppear(): void {
|
||||
window.getLastWindow(getContext(this)).then(currentWindow => {
|
||||
let avoidArea1 = currentWindow.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
|
||||
// 顶部状态栏高度
|
||||
this.statusBarHeight= px2vp(avoidArea1.topRect.height);
|
||||
|
||||
})
|
||||
this.initInputDialog()
|
||||
this.initList()
|
||||
|
||||
}
|
||||
initList()
|
||||
{
|
||||
hdHttp.post<string>(BasicConstant.QuickReplyList, {
|
||||
user_uuid: authStore.getUser().uuid,
|
||||
} as extraData).then(async (res: HdResponse<string>) => {
|
||||
let json:QuickReplyBean = JSON.parse(res+'') as QuickReplyBean;
|
||||
if(json.code == '1') {
|
||||
this.replydata=json.data
|
||||
} else {
|
||||
|
||||
promptAction.showToast({ message: json.message, duration: 1000 })
|
||||
}
|
||||
}).catch((err: BusinessError) => {
|
||||
console.info(`Response fails: ${err}`);
|
||||
})
|
||||
}
|
||||
initInputDialog() {
|
||||
this.InputSheet = new CustomDialogController({
|
||||
builder:PerfactInputSheet({
|
||||
controller:this.InputSheet,
|
||||
inputTitle:'请输入快捷回复',
|
||||
inputPlaceholder:'',
|
||||
style:'1',
|
||||
inputCallBack:(input: string,title:string)=>{
|
||||
|
||||
this.addQuickReply(input)
|
||||
|
||||
}
|
||||
}),
|
||||
keyboardAvoidDistance: LengthMetrics.vp(0), // 设置弹窗底部与键盘顶部间距(单位:vp)
|
||||
alignment: DialogAlignment.Center,
|
||||
customStyle: true,
|
||||
autoCancel: false,
|
||||
backgroundColor: ('rgba(0,0,0,0.5)'),
|
||||
height: '100%'
|
||||
})
|
||||
}
|
||||
addQuickReply(replystr:string)
|
||||
{
|
||||
hdHttp.post<string>(BasicConstant.addQuickReply, {
|
||||
user_uuid: authStore.getUser().uuid,
|
||||
replystr:replystr
|
||||
} as addextraData).then(async (res: HdResponse<string>) => {
|
||||
let json:QuickReplyBean = JSON.parse(res+'') as QuickReplyBean;
|
||||
this.replydata=json.data
|
||||
promptAction.showToast({ message: '添加成功', duration: 1000 })
|
||||
|
||||
}).catch((err: BusinessError) => {
|
||||
console.info(`Response fails: ${err}`);
|
||||
})
|
||||
}
|
||||
|
||||
deleteQuickReply(uuid:string)
|
||||
{
|
||||
hdHttp.post<string>(BasicConstant.deleteQuickReply, {
|
||||
uuid: uuid,
|
||||
} as delectextraData).then(async (res: HdResponse<string>) => {
|
||||
this.initList()
|
||||
promptAction.showToast({ message: '删除成功', duration: 1000 })
|
||||
|
||||
}).catch((err: BusinessError) => {
|
||||
console.info(`Response fails: ${err}`);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@CustomDialog
|
||||
struct DelectDialog {
|
||||
controller: CustomDialogController
|
||||
CallBack: () => void = () => {};
|
||||
build() {
|
||||
Column() {
|
||||
Text('删除快捷回复')
|
||||
.fontSize(17)
|
||||
.fontColor('#444444')
|
||||
.padding(15)
|
||||
Text('').height(1).width('100%')
|
||||
.backgroundColor($r('app.color.home_gray'))
|
||||
Text('删除')
|
||||
.fontSize(16).fontColor($r('app.color.common_gray_03'))
|
||||
.padding(10).width('100%').textAlign(TextAlign.Start)
|
||||
.onClick(() => {
|
||||
|
||||
if (this.controller != undefined) {
|
||||
this.controller.close()
|
||||
this.CallBack();
|
||||
}
|
||||
})
|
||||
Text('').height(1).width('100%')
|
||||
.backgroundColor($r('app.color.home_gray'))
|
||||
.margin({bottom:10})
|
||||
}
|
||||
// .onClick(() => {
|
||||
//
|
||||
// if (this.controller != undefined) {
|
||||
// this.controller.close()
|
||||
//
|
||||
// }
|
||||
// })
|
||||
|
||||
.backgroundColor($r('app.color.white'))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转页面入口函数
|
||||
@Builder
|
||||
export function QuickMessagePageBuilder() {
|
||||
QuickMessagePage()
|
||||
}
|
||||
|
||||
interface extraData{
|
||||
user_uuid:string
|
||||
}
|
||||
interface delectextraData{
|
||||
uuid:string
|
||||
}
|
||||
interface addextraData{
|
||||
user_uuid:string,
|
||||
replystr:string,
|
||||
}
|
||||
|
||||
interface QuickReplyBean {
|
||||
code:string;
|
||||
message:string;
|
||||
data:QuickReplyData[];
|
||||
|
||||
}
|
||||
interface QuickReplyData{
|
||||
createdate:string;
|
||||
replystr:string;
|
||||
user_uuid:string;
|
||||
uuid:string;
|
||||
sort:number;
|
||||
|
||||
}
|
||||
166
chatkit_ui/src/main/ets/pages/TeamHistoryPage.ets
Normal file
166
chatkit_ui/src/main/ets/pages/TeamHistoryPage.ets
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { ErrorUtils } from '@nimkit/chatkit';
|
||||
import { ChatBaseViewModel } from '../viewmodel/ChatBaseViewModel';
|
||||
import {
|
||||
AvatarColorUntil,
|
||||
AvatarItem,
|
||||
CommonAvatar,
|
||||
CommonEmptyResult,
|
||||
CommonTextInput,
|
||||
MatchSearchText,
|
||||
NavigationBackBuilder,
|
||||
TailString
|
||||
} from '@nimkit/common';
|
||||
import { DateUtil } from '@nimkit/common/src/main/ets/utils/DateUtil';
|
||||
import { TeamHistoryModel, TeamHistoryViewModel } from '../viewmodel/TeamHistoryViewModel';
|
||||
import { V2NIMConversationType } from '@nimsdk/base';
|
||||
import { NIMMessageInfo } from '../model/NIMMessageInfo';
|
||||
|
||||
// 跳转页面入口函数
|
||||
@Builder
|
||||
export function TeamHistoryPageBuilder() {
|
||||
TeamHistoryPage()
|
||||
}
|
||||
|
||||
@ComponentV2
|
||||
export struct TeamHistoryPage {
|
||||
pathStack: NavPathStack = new NavPathStack()
|
||||
@Local viewModel?: TeamHistoryViewModel
|
||||
@Local isEmpty: boolean = false
|
||||
@Local searchText: string = ""
|
||||
onSubmit = async (_enterKey: EnterKeyType, event: SubmitEvent) => {
|
||||
this.searchText = event.text
|
||||
if (ErrorUtils.checkNetworkAndToast()) {
|
||||
if (this.searchText.length > 0) {
|
||||
try {
|
||||
await this.viewModel?.searchHistoryMessages(event.text)
|
||||
this.isEmpty = (this.viewModel?.historyList.length ?? 0) <= 0
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
this.isEmpty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
onTextChange = (value: string) => {
|
||||
if (value.length === 0) {
|
||||
this.viewModel?.clearHistoryMessages()
|
||||
this.isEmpty = false
|
||||
}
|
||||
}
|
||||
|
||||
requestData() {
|
||||
const teamId = this.pathStack.getParamByName('TeamHistoryPage')[0] as string
|
||||
this.viewModel = new TeamHistoryViewModel(teamId)
|
||||
}
|
||||
|
||||
build() {
|
||||
NavDestination() {
|
||||
Column() {
|
||||
NavigationBackBuilder({
|
||||
title: $r('app.string.team_history_record'),
|
||||
leftButtonAction: () => {
|
||||
this.pathStack.pop()
|
||||
}
|
||||
})
|
||||
|
||||
CommonTextInput({
|
||||
placeHolderText: $r('app.string.team_history_record_tip'),
|
||||
onSubmit: this.onSubmit.bind(this),
|
||||
onTextChange: this.onTextChange,
|
||||
keepEditableState: false
|
||||
})
|
||||
.margin({
|
||||
top: 16
|
||||
})
|
||||
|
||||
if (this.isEmpty) {
|
||||
CommonEmptyResult({
|
||||
emptyImage: $r('app.media.no_history'),
|
||||
tips: $r('app.string.team_history_record_empty_tip')
|
||||
})
|
||||
.margin({
|
||||
top: 74
|
||||
})
|
||||
}
|
||||
|
||||
List() {
|
||||
ForEach(this.viewModel?.historyList, (item: TeamHistoryModel) => {
|
||||
ListItem() {
|
||||
Row() {
|
||||
CommonAvatar({
|
||||
item: new AvatarItem(item.teamMember?.getAvatar(),
|
||||
TailString(item.teamMember?.getAvatarName()),
|
||||
AvatarColorUntil.getBackgroundColorById(item.teamMember?.getAccId() ?? "")),
|
||||
roundRadius: 18
|
||||
})
|
||||
.width(36)
|
||||
.height(36)
|
||||
.margin({
|
||||
left: 20
|
||||
})
|
||||
|
||||
Column() {
|
||||
Text(item.teamMember?.getNickname())
|
||||
.fontColor('#333333')
|
||||
|
||||
MatchSearchText({
|
||||
allText: item.message.text,
|
||||
searchText: this.searchText,
|
||||
normalFontColor: "#999999"
|
||||
})
|
||||
.margin({
|
||||
top: 6
|
||||
})
|
||||
}
|
||||
.layoutWeight(1)
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.margin({
|
||||
left: 12,
|
||||
right: 12
|
||||
})
|
||||
|
||||
// 时间
|
||||
Text(DateUtil.formatTimestamp(item.message.createTime))
|
||||
.fontSize(12)
|
||||
.fontColor('#999999')
|
||||
.margin({
|
||||
right: 20
|
||||
})
|
||||
}
|
||||
}
|
||||
.height(62)
|
||||
.onClick(() => {
|
||||
if (ChatBaseViewModel.currentViewModel) {
|
||||
let msgInfo = new NIMMessageInfo(item.message)
|
||||
ChatBaseViewModel.currentViewModel.setAnchorMessage(msgInfo)
|
||||
}
|
||||
if (item.message.conversationType == V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P) {
|
||||
this.pathStack.popToName('ChatP2PPage')
|
||||
} else if (item.message.conversationType == V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM) {
|
||||
this.pathStack.popToName('ChatTeamPage')
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
.divider({
|
||||
strokeWidth: 1,
|
||||
color: "0xDBE0E8",
|
||||
startMargin: 20
|
||||
})
|
||||
}
|
||||
}
|
||||
.hideTitleBar(true)
|
||||
.backgroundColor(Color.White)
|
||||
.onReady((context: NavDestinationContext) => {
|
||||
this.pathStack = context.pathStack
|
||||
this.requestData()
|
||||
})
|
||||
}
|
||||
}
|
||||
251
chatkit_ui/src/main/ets/pages/UserSettingPage.ets
Normal file
251
chatkit_ui/src/main/ets/pages/UserSettingPage.ets
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
ChatKitClient,
|
||||
ContactRepo,
|
||||
ErrorUtils,
|
||||
NEFriendUserCache,
|
||||
NEUserWithFriend,
|
||||
PersonSelectParam,
|
||||
TeamRepo
|
||||
} from '@nimkit/chatkit'
|
||||
import {
|
||||
AvatarColorUntil,
|
||||
AvatarItem,
|
||||
CommonAvatar,
|
||||
NavigationBackBuilder,
|
||||
ViewItemArrow,
|
||||
ViewItemBuilder,
|
||||
ViewItemSwitch
|
||||
} from '@nimkit/common'
|
||||
import { UserSettingViewModel } from '../viewmodel/UserSettingViewModel'
|
||||
|
||||
@ComponentV2
|
||||
export struct UserSettingPage {
|
||||
pathStack: NavPathStack = new NavPathStack()
|
||||
accountId?: string
|
||||
@Local userWithFriend?: NEUserWithFriend
|
||||
viewModel: UserSettingViewModel = new UserSettingViewModel()
|
||||
firstItems: ViewItemArrow[] = [
|
||||
{
|
||||
title: $r("app.string.setting_pin"),
|
||||
itemHeight: 48,
|
||||
routerUrl: "PinMessagePage",
|
||||
rightIcon: $r('app.media.arrow_right')
|
||||
}
|
||||
]
|
||||
// 设置是否开启消息提醒
|
||||
setP2PNotify = async (isOpen: boolean) => {
|
||||
if (ErrorUtils.checkNetworkAndToast()) {
|
||||
try {
|
||||
await this.viewModel?.setP2PNotify(isOpen)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置是否聊天置顶
|
||||
stickTopConversation = async (isOpen: boolean) => {
|
||||
if (ErrorUtils.checkNetworkAndToast()) {
|
||||
try {
|
||||
await this.viewModel?.stickTopConversation(isOpen)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@Local secondItems: ViewItemSwitch[] = []
|
||||
// 创建讨论组
|
||||
createDiscussion = async (selectedList: NEUserWithFriend[]) => {
|
||||
if (ErrorUtils.checkNetworkAndToast()) {
|
||||
const inviteeAccountIds: string[] = []
|
||||
selectedList.forEach((userWithFriend) => {
|
||||
if (userWithFriend.user?.accountId) {
|
||||
inviteeAccountIds.push(userWithFriend.user.accountId)
|
||||
}
|
||||
})
|
||||
if (this.viewModel?.accountId) {
|
||||
inviteeAccountIds.push(this.viewModel?.accountId)
|
||||
}
|
||||
|
||||
try {
|
||||
const createTeamResult = await TeamRepo.createGroupTeam(inviteeAccountIds)
|
||||
|
||||
// 替换聊天页
|
||||
if (createTeamResult?.team) {
|
||||
this.pathStack.popToName('ChatP2PPage')
|
||||
this.pathStack.replacePathByName('ChatTeamPage',
|
||||
ChatKitClient.nim.conversationIdUtil.teamConversationId(createTeamResult.team.teamId))
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 【+】按钮点击事件
|
||||
onAddButtonClick() {
|
||||
this.pathStack.pushPath({
|
||||
name: 'PersonSelectPage',
|
||||
param: new PersonSelectParam(this.createDiscussion,
|
||||
[this.viewModel?.accountId],
|
||||
10)
|
||||
})
|
||||
}
|
||||
|
||||
updateSecondItems() {
|
||||
this.secondItems = [
|
||||
{
|
||||
title: $r('app.string.setting_message_notify'),
|
||||
itemHeight: 48,
|
||||
isOpen: this.viewModel.isMessageNotify,
|
||||
switchChange: this.setP2PNotify
|
||||
},
|
||||
{
|
||||
title: $r('app.string.setting_message_stick_top'),
|
||||
itemHeight: 48,
|
||||
isOpen: this.viewModel.isStickTop,
|
||||
switchChange: this.stickTopConversation
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async requestData() {
|
||||
const conversationId = this.pathStack.getParamByName('UserSettingPage')[0] as string
|
||||
this.viewModel.init(conversationId).then(() => {
|
||||
this.updateSecondItems()
|
||||
})
|
||||
|
||||
const accountId = this.viewModel.accountId
|
||||
if (NEFriendUserCache.getInstance().isFriend(accountId)) {
|
||||
this.userWithFriend = NEFriendUserCache.getInstance().getFriendById(accountId)
|
||||
} else {
|
||||
try {
|
||||
const users = await ContactRepo.getUserWithFriendByIds([accountId])
|
||||
if (users.length > 0) {
|
||||
this.userWithFriend = users[0]
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
NavDestination() {
|
||||
Column() {
|
||||
NavigationBackBuilder({
|
||||
title: $r('app.string.chat_setting'),
|
||||
backgroundColor: '#ffEFF1F4',
|
||||
leftButtonAction: () => {
|
||||
this.pathStack.pop()
|
||||
}
|
||||
})
|
||||
|
||||
Row() {
|
||||
Row() {
|
||||
Column() {
|
||||
CommonAvatar({
|
||||
item: new AvatarItem(this.userWithFriend?.user?.avatar,
|
||||
this.userWithFriend?.shortName(),
|
||||
AvatarColorUntil.getBackgroundColorById(this.userWithFriend?.user?.accountId ?? "")),
|
||||
roundRadius: 21,
|
||||
textSize: 16
|
||||
})
|
||||
.width(42)
|
||||
.height(42)
|
||||
|
||||
Text(this.userWithFriend?.showName())
|
||||
.width(42)
|
||||
.height(14)
|
||||
.fontSize(12)
|
||||
.maxLines(1)
|
||||
.textOverflow({
|
||||
overflow: TextOverflow.Ellipsis
|
||||
})
|
||||
.margin({
|
||||
top: 6
|
||||
})
|
||||
}
|
||||
.width(42)
|
||||
.height(80)
|
||||
|
||||
Image($r('app.media.team_invite'))
|
||||
.width(42)
|
||||
.height(42)
|
||||
.margin({
|
||||
left: 16
|
||||
})
|
||||
.onClick(() => {
|
||||
this.onAddButtonClick()
|
||||
})
|
||||
}
|
||||
.alignSelf(ItemAlign.Start)
|
||||
.alignItems(VerticalAlign.Top)
|
||||
.height(80)
|
||||
.width('100%')
|
||||
}
|
||||
.borderRadius(8)
|
||||
.backgroundColor(Color.White)
|
||||
.margin({
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 12
|
||||
})
|
||||
.padding({
|
||||
left: 20,
|
||||
right: 20,
|
||||
top: 12
|
||||
})
|
||||
|
||||
List() {
|
||||
ForEach(this.firstItems, (item: ViewItemArrow) => {
|
||||
ListItem() {
|
||||
ViewItemBuilder(item)
|
||||
}
|
||||
.width('100%')
|
||||
.height(item.itemHeight)
|
||||
.backgroundColor(item.backgroundColor ?? Color.White)
|
||||
.onClick(() => {
|
||||
this.pathStack.pushPath({
|
||||
name: item.routerUrl,
|
||||
param: this.viewModel?.conversationId
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
ForEach(this.secondItems, (item: ViewItemSwitch) => {
|
||||
ListItem() {
|
||||
ViewItemBuilder(item)
|
||||
}
|
||||
.width('100%')
|
||||
.height(item.itemHeight)
|
||||
.backgroundColor(item.backgroundColor ?? Color.White)
|
||||
})
|
||||
}
|
||||
.borderRadius(8)
|
||||
.backgroundColor(Color.White)
|
||||
.margin({ left: 20, right: 20, top: 0 })
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
.hideTitleBar(true)
|
||||
.backgroundColor('#ffEFF1F4')
|
||||
.onReady((context: NavDestinationContext) => {
|
||||
this.pathStack = context.pathStack
|
||||
this.requestData()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转页面入口函数
|
||||
@Builder
|
||||
export function UserSettingPageBuilder() {
|
||||
UserSettingPage()
|
||||
}
|
||||
209
chatkit_ui/src/main/ets/view/ChatAitDialog.ets
Normal file
209
chatkit_ui/src/main/ets/view/ChatAitDialog.ets
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import {
|
||||
ChatKitClient,
|
||||
keyExtensionAtAll,
|
||||
TeamMemberCache,
|
||||
TeamMemberWithUser,
|
||||
TeamRepo,
|
||||
typeExtensionAllowAll
|
||||
} from '@nimkit/chatkit'
|
||||
import { AvatarColorUntil, AvatarItem, CommonAvatar } from '@nimkit/common'
|
||||
import { V2NIMTeam, V2NIMTeamMemberRole } from '@nimsdk/base'
|
||||
|
||||
/**
|
||||
* @弹窗
|
||||
*/
|
||||
@CustomDialog
|
||||
export struct ChatAitDialog {
|
||||
animationDur: number = 300
|
||||
controller: CustomDialogController
|
||||
@BuilderParam teamId: string
|
||||
@BuilderParam team?: V2NIMTeam
|
||||
onMemberSelected?: (member: TeamMemberWithUser) => void
|
||||
onSelectedAll?: () => void
|
||||
teamMemberCache: TeamMemberCache = TeamMemberCache.getInstance()
|
||||
@State showFlag: Visibility = Visibility.Visible
|
||||
//是否显示@All
|
||||
@State showAll: boolean = false
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
|
||||
RelativeContainer() {
|
||||
Image($r('sys.media.ohos_ic_public_arrow_down'))
|
||||
.fillColor('#999999')
|
||||
.width(16)
|
||||
.height(16)
|
||||
.margin({
|
||||
left: 20
|
||||
})
|
||||
.alignRules({
|
||||
left: { anchor: "__container__", align: HorizontalAlign.Start },
|
||||
center: { anchor: "__container__", align: VerticalAlign.Center },
|
||||
})
|
||||
.onClick(() => {
|
||||
this.closeDialog()
|
||||
})
|
||||
|
||||
Text($r('app.string.chat_message_ait_select_tips'))
|
||||
.fontSize(16)
|
||||
.fontColor('#ff333333')
|
||||
.alignRules({
|
||||
middle: { anchor: "__container__", align: HorizontalAlign.Center },
|
||||
center: { anchor: "__container__", align: VerticalAlign.Center },
|
||||
})
|
||||
}.height(42)
|
||||
|
||||
List() {
|
||||
if (this.showAll) {
|
||||
ListItem() {
|
||||
Row() {
|
||||
Stack() {
|
||||
Image($r('app.media.ic_member_all'))
|
||||
.width(24)
|
||||
.height(24)
|
||||
}
|
||||
.borderRadius(20)
|
||||
.backgroundColor('#337EFF')
|
||||
.width(42)
|
||||
.height(42)
|
||||
.margin({
|
||||
right: 14
|
||||
})
|
||||
|
||||
Text($r('app.string.chat_team_ait_all'))
|
||||
.fontColor('#ff333333')
|
||||
.fontSize(16)
|
||||
.width('70%')
|
||||
|
||||
|
||||
}
|
||||
.padding({
|
||||
left: 20,
|
||||
right: 20,
|
||||
top: 10,
|
||||
bottom: 10
|
||||
})
|
||||
.onClick(() => {
|
||||
this.closeDialog()
|
||||
if (this.onSelectedAll) {
|
||||
this.onSelectedAll()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ForEach(this.teamMemberCache.getAllMembers(), (member: TeamMemberWithUser) => {
|
||||
if (member.getAccId() !== ChatKitClient.getLoginUserId()) {
|
||||
ListItem() {
|
||||
Row() {
|
||||
CommonAvatar({
|
||||
item: new AvatarItem(
|
||||
member.getAvatar(),
|
||||
member.getAvatarName(),
|
||||
AvatarColorUntil.getBackgroundColorById(member.getAccId())
|
||||
)
|
||||
})
|
||||
.width(42)
|
||||
.height(42)
|
||||
.margin({
|
||||
right: 14
|
||||
})
|
||||
|
||||
Text(member.getNickname())
|
||||
.fontColor('#ff333333')
|
||||
.fontSize(16)
|
||||
.width('70%')
|
||||
|
||||
|
||||
}
|
||||
.padding({
|
||||
left: 20,
|
||||
right: 20,
|
||||
top: 10,
|
||||
bottom: 10
|
||||
})
|
||||
.onClick(() => {
|
||||
this.closeDialog()
|
||||
if (this.onMemberSelected) {
|
||||
this.onMemberSelected(member)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ListItem() {
|
||||
Column() {
|
||||
}
|
||||
.height(42)
|
||||
}
|
||||
}
|
||||
.listDirection(Axis.Vertical)
|
||||
.onReachEnd(() => {
|
||||
this.teamMemberCache.getMoreMemberList()
|
||||
})
|
||||
.scrollBar(BarState.Off)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
}
|
||||
.width('100%')
|
||||
.height('60%')
|
||||
.backgroundColor('#ffFFFFFF')
|
||||
.visibility(this.showFlag)
|
||||
.transition(TransitionEffect.OPACITY.animation({ duration: this.animationDur })
|
||||
.combine(TransitionEffect.translate({ y: 100 })))
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.showFlag = Visibility.Hidden
|
||||
setTimeout(() => {
|
||||
this.controller.close()
|
||||
}, this.animationDur)
|
||||
}
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.updateShowAitAllItem()
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新是否要显示@All的操作
|
||||
*/
|
||||
async updateShowAitAllItem() {
|
||||
let team = await this.teamMemberCache.getTeam()
|
||||
if (team) {
|
||||
if (TeamRepo.isGroupTeam(team)) {
|
||||
this.showAll = true
|
||||
return
|
||||
}
|
||||
if (team?.serverExtension) {
|
||||
let obgExt = JSON.parse(team.serverExtension) as object | undefined
|
||||
if (obgExt) {
|
||||
if (obgExt[keyExtensionAtAll] === typeExtensionAllowAll) {
|
||||
this.showAll = true
|
||||
return
|
||||
}
|
||||
} else {
|
||||
this.showAll = true
|
||||
return
|
||||
}
|
||||
} else {
|
||||
this.showAll = true
|
||||
return
|
||||
}
|
||||
}
|
||||
let mineMember = await this.teamMemberCache.getMineMember()
|
||||
if (mineMember
|
||||
&& (mineMember.memberRole === V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_OWNER
|
||||
|| mineMember.memberRole === V2NIMTeamMemberRole.V2NIM_TEAM_MEMBER_ROLE_MANAGER)) {
|
||||
this.showAll = true
|
||||
return
|
||||
}
|
||||
this.showAll = false
|
||||
}
|
||||
}
|
||||
457
chatkit_ui/src/main/ets/view/ChatInputView.ets
Normal file
457
chatkit_ui/src/main/ets/view/ChatInputView.ets
Normal file
@ -0,0 +1,457 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import { NEImageButton } from './NEImageButton';
|
||||
import { NEChatMoreOperationData, NEChatMoreOperationType } from '../model/NEChatMoreOperationData';
|
||||
import { NEEmojiManager, NEEmojiParseResult } from '../manager/NEEmojiManager';
|
||||
import { inputMethod } from '@kit.IMEKit';
|
||||
import { V2NIMTeam } from '@nimsdk/base';
|
||||
import { ChatAitDialog } from './ChatAitDialog';
|
||||
import { TeamMemberCache } from '@nimkit/chatkit/src/main/ets/cache/TeamMemberCache';
|
||||
import { accountAll, ChatKitClient, TeamMemberWithUser } from '@nimkit/chatkit';
|
||||
import { AitManager } from '../manager/ait/AitManager';
|
||||
import { getReplyMessageText, getReplyMessageTitle, parseMessageText, sliceMessageText } from '../common/MessageHelper';
|
||||
import { NIMMessageInfo } from '../model/NIMMessageInfo';
|
||||
import { ChatInfo } from '../model/ChatInfo';
|
||||
|
||||
export enum InputStyleType {
|
||||
// 底部无内容
|
||||
None = 1,
|
||||
// 展示表情输入框
|
||||
Emoji = 2,
|
||||
// 长按录音UI展示
|
||||
Record = 3,
|
||||
// 更多面板展示
|
||||
More = 4,
|
||||
}
|
||||
|
||||
@ComponentV2
|
||||
export struct NEChatInputView {
|
||||
inputSpans: Array<RichEditorImageSpanResult | RichEditorTextSpanResult> = []
|
||||
@Local inputContentLength: number = 0;
|
||||
@Param @Require
|
||||
onSendTextMessage?: () => void;
|
||||
@Local operationMoreDataList: Array<NEChatMoreOperationData> = Array();
|
||||
@Param
|
||||
placeHolder: string = '';
|
||||
@Param @Require
|
||||
onDidClickImage?: () => void;
|
||||
@Param @Require
|
||||
onDidClickAudio?: () => void;
|
||||
@Param @Require
|
||||
onDidClickEmoji?: () => void;
|
||||
@Param @Require
|
||||
onDidClickMore?: () => void;
|
||||
@Param @Require
|
||||
onDidClickCloseReply?: () => void;
|
||||
@Param inputStyle: InputStyleType = InputStyleType.None;
|
||||
@Param @Require
|
||||
controller?: RichEditorController;
|
||||
inputFontSize = 16;
|
||||
@Param
|
||||
mute: boolean = false
|
||||
//群ID
|
||||
@Param
|
||||
teamId?: string = undefined
|
||||
//群
|
||||
@Param
|
||||
team?: V2NIMTeam = undefined
|
||||
//空格符
|
||||
blank: string = ' ';
|
||||
@Param
|
||||
aitManager?: AitManager = undefined
|
||||
//@的Span
|
||||
@Param
|
||||
builderSpans: AitEditorSpan[] = [];
|
||||
@Param
|
||||
replyMsg: NIMMessageInfo | undefined = undefined
|
||||
@Param
|
||||
chatInfo: ChatInfo | undefined = undefined
|
||||
/**
|
||||
* @弹框
|
||||
*/
|
||||
chatAitDialogController: CustomDialogController = new CustomDialogController({
|
||||
builder: ChatAitDialog({
|
||||
teamId: this.teamId,
|
||||
team: this.team,
|
||||
onSelectedAll: this.onSelectedAll.bind(this),
|
||||
onMemberSelected: this.onTeamMemberSelected.bind(this)
|
||||
}),
|
||||
openAnimation: {
|
||||
curve: Curve.Linear,
|
||||
playMode: PlayMode.Alternate
|
||||
},
|
||||
autoCancel: true,
|
||||
alignment: DialogAlignment.Bottom,
|
||||
backgroundColor: Color.White,
|
||||
height: '60%',
|
||||
width: '100%',
|
||||
customStyle: true
|
||||
})
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.initData();
|
||||
}
|
||||
|
||||
@Monitor('mute')
|
||||
onMute() {
|
||||
if (this.mute) {
|
||||
this.controller?.deleteSpans()
|
||||
this.controller?.stopEditing()
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化数据
|
||||
initData() {
|
||||
const videoOperationData = new NEChatMoreOperationData();
|
||||
videoOperationData.operationTitle = "video";
|
||||
videoOperationData.type = NEChatMoreOperationType.Video;
|
||||
videoOperationData.imageSource = "app.media.ic_public_chat_photo";
|
||||
this.operationMoreDataList.push(videoOperationData);
|
||||
const fileOperationData = new NEChatMoreOperationData();
|
||||
fileOperationData.operationTitle = "file";
|
||||
fileOperationData.type = NEChatMoreOperationType.File;
|
||||
fileOperationData.imageSource = "app.media.ic_public_chat_file";
|
||||
this.operationMoreDataList.push(fileOperationData);
|
||||
NEEmojiManager.instance.setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* @所有人
|
||||
*/
|
||||
onSelectedAll() {
|
||||
//输入框添加@信息
|
||||
let aitValue = getContext().resourceManager.getStringByNameSync('chat_team_ait_all') + this.blank
|
||||
|
||||
//输入框添加@信息
|
||||
if (this.controller) {
|
||||
const controller = this.controller;
|
||||
const offset = controller.getCaretOffset();
|
||||
const range: RichEditorRange = { start: offset - 1, end: offset };
|
||||
controller.deleteSpans(range);
|
||||
const spanOffset = offset - 1
|
||||
controller.addBuilderSpan(() => this.AtSpan(aitValue), {
|
||||
offset: spanOffset
|
||||
});
|
||||
let aitSpan: AitEditorSpan = {
|
||||
spanIndex: spanOffset,
|
||||
accountId: accountAll,
|
||||
value: '@' + aitValue
|
||||
}
|
||||
let index = this.builderSpans.findIndex((e) => e.spanIndex >= spanOffset)
|
||||
if (index < 0) {
|
||||
this.builderSpans.push(...[aitSpan])
|
||||
} else {
|
||||
this.builderSpans.splice(index, 0, aitSpan)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @单个人
|
||||
* @param member
|
||||
*/
|
||||
onTeamMemberSelected(member: TeamMemberWithUser) {
|
||||
//输入框添加@信息
|
||||
let aitValue = member.getAitName() + this.blank
|
||||
if (this.controller) {
|
||||
const controller = this.controller;
|
||||
const offset = controller.getCaretOffset();
|
||||
const range: RichEditorRange = { start: offset - 1, end: offset };
|
||||
controller.deleteSpans(range);
|
||||
const spanOffset = offset - 1
|
||||
controller.addBuilderSpan(() => this.AtSpan(aitValue), {
|
||||
offset: spanOffset
|
||||
});
|
||||
let aitSpan: AitEditorSpan = {
|
||||
spanIndex: spanOffset,
|
||||
accountId: member.getAccId(),
|
||||
value: '@' + aitValue,
|
||||
}
|
||||
let index = this.builderSpans.findIndex((e) => e.spanIndex >= spanOffset)
|
||||
if (index < 0) {
|
||||
this.builderSpans.push(...[aitSpan])
|
||||
} else {
|
||||
this.builderSpans.splice(index, 0, aitSpan)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Builder
|
||||
AtSpan(nickname: string) {
|
||||
Text(`@${nickname}`)
|
||||
.fontColor('#FF337EFF');
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示@的弹框
|
||||
*/
|
||||
async showAitDialog() {
|
||||
let teamMemberCache = TeamMemberCache.getInstance()
|
||||
if (teamMemberCache.needFetchMember()) {
|
||||
await teamMemberCache.getMemberList()
|
||||
}
|
||||
this.chatAitDialogController.open()
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是@的span
|
||||
* @param span
|
||||
* @returns
|
||||
*/
|
||||
isAitEditorSpan(span: RichEditorImageSpanResult | RichEditorTextSpanResult, offset?: number): boolean {
|
||||
if (offset !== undefined && offset >= 0) {
|
||||
const buildSpan = this.builderSpans.find(e => e.spanIndex === offset)
|
||||
if (!buildSpan) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return !(span as RichEditorTextSpanResult).value &&
|
||||
!(span as RichEditorImageSpanResult).valueResourceStr?.toString().replaceAll(' ', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有选中
|
||||
* @param controller
|
||||
* @returns
|
||||
*/
|
||||
hasSelection(controller: RichEditorController) {
|
||||
const selection = controller.getSelection().selection;
|
||||
return selection[0] !== selection[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除@Span
|
||||
*/
|
||||
deleteAitEditorSpan() {
|
||||
const controller = this.controller;
|
||||
if (controller) {
|
||||
const range: RichEditorRange = { end: controller.getCaretOffset() };
|
||||
const index = this.getAitEditorSpanCount(controller, range) - 1;
|
||||
this.builderSpans.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取@Span的内容
|
||||
* @param controller
|
||||
* @param range
|
||||
* @returns
|
||||
*/
|
||||
getAitEditorSpanCount(controller: RichEditorController, range: RichEditorRange) {
|
||||
return controller.getSpans(range).reduce((count: number, span) => {
|
||||
return this.isAitEditorSpan(span) ? count + 1 : count;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除操作
|
||||
*/
|
||||
aboutToDelete: (value: RichEditorDeleteValue) => boolean = value => {
|
||||
ChatKitClient.logger?.debug('NEChatInputView', 'aboutToDelete')
|
||||
const controller = this.controller;
|
||||
const span = value.richEditorDeleteSpans[0];
|
||||
if (controller && span && this.isAitEditorSpan(span, value.offset)) {
|
||||
this.deleteAitEditorSpan();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
get hasInput(): boolean {
|
||||
return this.inputContentLength > 0;
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
Column() {
|
||||
// if (!this.mute && this.replyMsg) {
|
||||
// Row() {
|
||||
// Image($r('app.media.ic_chat_reply_close')).height(20).width(20).margin({ right: 12 })
|
||||
// .onClick(() => {
|
||||
// this.onDidClickCloseReply?.()
|
||||
// })
|
||||
// Text() {
|
||||
// if (this.chatInfo) {
|
||||
// Span(getReplyMessageTitle(this.replyMsg, this.chatInfo))
|
||||
// .fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
// .textCase(TextCase.Normal)
|
||||
// .fontColor($r('app.color.color_chat_desc'))
|
||||
// }
|
||||
//
|
||||
// ForEach(sliceMessageText(parseMessageText(getReplyMessageText(this.replyMsg)), 30),
|
||||
// (item: NEEmojiParseResult) => {
|
||||
// if (item.text) {
|
||||
// Span(item.text)
|
||||
// .fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
// .textCase(TextCase.Normal)
|
||||
// .fontColor($r('app.color.color_chat_desc'))
|
||||
// } else if (item.emoji) {
|
||||
// ImageSpan($rawfile(`emoji/${item.emoji.file}`)).width('16')
|
||||
// .height('16')
|
||||
// .objectFit(ImageFit.Fill)
|
||||
// .verticalAlign(ImageSpanAlignment.CENTER)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// .width('90%')
|
||||
// .maxLines(1)
|
||||
// .textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
// .ellipsisMode(EllipsisMode.END)
|
||||
// .fontColor($r('app.color.color_chat_desc'))
|
||||
// .fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
// }
|
||||
// .height(32)
|
||||
// .justifyContent(FlexAlign.Start)
|
||||
// .width('100%')
|
||||
// }
|
||||
|
||||
|
||||
|
||||
Row({ space: 0 }) {
|
||||
NEImageButton({
|
||||
image: $r('app.media.ic_public_chat_speaker'), onDidClick: (): void => {
|
||||
console.log("net ease click speaker");
|
||||
if (this.onDidClickAudio) {
|
||||
inputMethod.getController().stopInputSession();
|
||||
this.onDidClickAudio()
|
||||
}
|
||||
}
|
||||
})
|
||||
.width(27)
|
||||
.height(27)
|
||||
.margin({right:8})
|
||||
RichEditor({ controller: this.controller })
|
||||
.backgroundColor(this.mute ? $r('app.color.color_chat_mute_bg') : $r('app.color.color_chat_page_bg'))
|
||||
.height(40)
|
||||
.layoutWeight(1)
|
||||
.placeholder(this.mute ? $r('app.string.chat_team_all_mute') :
|
||||
$r('app.string.chat_send_tips'))
|
||||
.onWillChange((value: RichEditorChangeValue) => {
|
||||
console.debug(`Response ChatInputView onWillChange before: ${value.rangeBefore}`)
|
||||
return true
|
||||
})
|
||||
.aboutToDelete(this.aboutToDelete)
|
||||
.onDidChange((rangeBefore: TextRange, rangeAfter: TextRange) => {
|
||||
const spans = this.controller?.getSpans() ?? [];
|
||||
this.inputSpans = spans;
|
||||
// 统计所有文本span的总长度
|
||||
this.inputContentLength = spans
|
||||
.map(span => (span as RichEditorTextSpanResult).value?.length ?? 0)
|
||||
.reduce((a, b) => a + b, 0);
|
||||
})
|
||||
.onSelectionChange((range: RichEditorRange) => {
|
||||
console.debug(`Response ChatInputView onSelectionChange ${range.start} ~ ${range.end}`)
|
||||
})
|
||||
.onIMEInputComplete((result: RichEditorTextSpanResult) => {
|
||||
//记录输入内容
|
||||
this.inputSpans = this.controller?.getSpans() ?? []
|
||||
let value = result.value
|
||||
let indexStart: number = result.offsetInSpan[0]
|
||||
//span长度
|
||||
let spanLen = result.offsetInSpan[1] - result.offsetInSpan[0]
|
||||
console.debug(`Response onIMEInputComplete value = ${value} index = ${indexStart} len = ${spanLen}}`)
|
||||
if (this.teamId && this.teamId.length > 0) {
|
||||
//判断span 长度,解决复制粘贴问题
|
||||
if (spanLen === 1 && value.charAt(indexStart) === "@") {
|
||||
//输入@
|
||||
this.showAitDialog()
|
||||
}
|
||||
}
|
||||
})
|
||||
.borderRadius(8)
|
||||
.id('chat_edit_input')
|
||||
.onSubmit((enterKey: EnterKeyType, event: SubmitEvent) => {
|
||||
if (enterKey == EnterKeyType.Send) {
|
||||
if (this.onSendTextMessage) {
|
||||
this.onSendTextMessage();
|
||||
event.keepEditableState()
|
||||
}
|
||||
}
|
||||
})
|
||||
.enterKeyType(EnterKeyType.Send)
|
||||
// 右侧按钮区域
|
||||
if (this.hasInput) {
|
||||
NEImageButton({
|
||||
image: $r("app.media.ic_public_chat_emoji"), onDidClick: (): void => {
|
||||
console.log("net ease click emoji");
|
||||
if (this.onDidClickEmoji) {
|
||||
inputMethod.getController().stopInputSession();
|
||||
this.onDidClickEmoji()
|
||||
}
|
||||
}
|
||||
})
|
||||
.width(27)
|
||||
.height(27)
|
||||
.margin({right:8,left:8})
|
||||
Text("发送")
|
||||
.width(42)
|
||||
.height(30)
|
||||
.margin({right:8,left:8})
|
||||
.fontSize(14)
|
||||
.textAlign(TextAlign.Center)
|
||||
.fontColor(Color.White)
|
||||
.backgroundColor('#08C163')
|
||||
.borderRadius(6)
|
||||
.onClick(() => {
|
||||
if (this.onSendTextMessage) {
|
||||
this.onSendTextMessage();
|
||||
}
|
||||
})
|
||||
} else {
|
||||
NEImageButton({
|
||||
image: $r("app.media.ic_public_chat_emoji"), onDidClick: (): void => {
|
||||
console.log("net ease click emoji");
|
||||
if (this.onDidClickEmoji) {
|
||||
inputMethod.getController().stopInputSession();
|
||||
this.onDidClickEmoji()
|
||||
}
|
||||
}
|
||||
})
|
||||
.width(27)
|
||||
.height(27)
|
||||
.margin({right:8,left:8})
|
||||
NEImageButton({
|
||||
image: $r("app.media.ic_public_chat_more"), onDidClick: (): void => {
|
||||
console.log("net ease click more");
|
||||
if (this.onDidClickMore) {
|
||||
inputMethod.getController().stopInputSession();
|
||||
this.onDidClickMore()
|
||||
}
|
||||
}
|
||||
})
|
||||
.width(27)
|
||||
.height(27)
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.height(60)
|
||||
.id("action_button")
|
||||
}.padding({ left: 8, right: 8 })
|
||||
|
||||
// if (this.mute) {
|
||||
// Column()
|
||||
// .alignRules({
|
||||
// left: { anchor: "__container__", align: HorizontalAlign.Start },
|
||||
// right: { anchor: "__container__", align: HorizontalAlign.End },
|
||||
// top: { anchor: "__container__", align: VerticalAlign.Top },
|
||||
// bottom: { anchor: "__container__", align: VerticalAlign.Bottom }
|
||||
// })
|
||||
// .backgroundColor($r('app.color.color_chat_mute_bg'))
|
||||
// .opacity(0.4)
|
||||
// .width('100%')
|
||||
// .height('100%')
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface AitEditorSpan {
|
||||
spanIndex: number,
|
||||
accountId: string
|
||||
value: string
|
||||
}
|
||||
38
chatkit_ui/src/main/ets/view/ChatMoreOperationLayout.ets
Normal file
38
chatkit_ui/src/main/ets/view/ChatMoreOperationLayout.ets
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { OperationItem } from './OperationItemLayout';
|
||||
import { NEChatMoreOperationData } from '../model/NEChatMoreOperationData';
|
||||
|
||||
@ComponentV2
|
||||
export struct NEChatMoreOperation {
|
||||
// 面板回调
|
||||
@Param @Require dataList: Array<NEChatMoreOperationData>;
|
||||
@Param @Require onDidClick?: (data: NEChatMoreOperationData) => void;
|
||||
|
||||
build() {
|
||||
GridRow({
|
||||
breakpoints: {
|
||||
reference: BreakpointsReference.WindowSize
|
||||
}
|
||||
}) {
|
||||
ForEach(this.dataList, (data: NEChatMoreOperationData) => {
|
||||
GridCol({ span: 3 }) {
|
||||
Row() {
|
||||
OperationItem({
|
||||
operationData: data, onItemClick: (data) => {
|
||||
if (this.onDidClick) {
|
||||
this.onDidClick(data);
|
||||
}
|
||||
}
|
||||
})
|
||||
}.width("100%").height(90)
|
||||
}.backgroundColor(Color.White)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
74
chatkit_ui/src/main/ets/view/ChatMultiSelectView.ets
Normal file
74
chatkit_ui/src/main/ets/view/ChatMultiSelectView.ets
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
@Entry
|
||||
@ComponentV2
|
||||
export struct ChatMultiSelectView {
|
||||
@Param
|
||||
onMultiForward?: (event: ClickEvent) => void = undefined
|
||||
@Param
|
||||
onSingleForward?: (event: ClickEvent) => void = undefined
|
||||
@Param
|
||||
onMultiDelete?: (event: ClickEvent) => void = undefined
|
||||
@Param
|
||||
isEnable?: boolean = true
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
Column() {
|
||||
Image(this.isEnable ? $r('app.media.ic_chat_select_multi_forward') :
|
||||
$r("app.media.ic_chat_select_multi_forward_dis")).width(48).height(48).margin({ top: 20 })
|
||||
Text($r('app.string.chat_operation_multi_forward'))
|
||||
.fontColor($r('app.color.color_chat_sub_title'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.margin({ top: 8 })
|
||||
}.height('100%').alignItems(HorizontalAlign.Center).width(60).onClick((event) => {
|
||||
if (this.isEnable) {
|
||||
this.onMultiForward?.(event)
|
||||
}
|
||||
})
|
||||
|
||||
Column() {
|
||||
Image(this.isEnable ? $r("app.media.ic_chat_select_forward") : $r("app.media.ic_chat_select_forward_dis"))
|
||||
.width(48)
|
||||
.height(48)
|
||||
.margin({ top: 20 })
|
||||
Text($r('app.string.chat_operation_single_forward'))
|
||||
.fontColor($r('app.color.color_chat_sub_title'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.margin({ top: 8 })
|
||||
}.height('100%').alignItems(HorizontalAlign.Center).width(60).onClick((event) => {
|
||||
if (this.isEnable) {
|
||||
this.onSingleForward?.(event)
|
||||
}
|
||||
})
|
||||
|
||||
Column() {
|
||||
Image(this.isEnable ? $r('app.media.ic_chat_select_delete') : $r("app.media.ic_chat_select_delete_dis"))
|
||||
.width(48)
|
||||
.height(48)
|
||||
.margin({ top: 20 })
|
||||
Text($r('app.string.chat_operation_delete'))
|
||||
.fontColor($r('app.color.color_chat_sub_title'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.margin({ top: 8 })
|
||||
}.height('100%').alignItems(HorizontalAlign.Center).width(60).onClick((event) => {
|
||||
if (this.isEnable) {
|
||||
this.onMultiDelete?.(event)
|
||||
}
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.height(100)
|
||||
.backgroundColor($r('app.color.color_chat_op_bg'))
|
||||
.justifyContent(FlexAlign.SpaceAround)
|
||||
.padding({
|
||||
left: 20,
|
||||
right: 20,
|
||||
})
|
||||
}
|
||||
}
|
||||
160
chatkit_ui/src/main/ets/view/ForwardMessageDialog.ets
Normal file
160
chatkit_ui/src/main/ets/view/ForwardMessageDialog.ets
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
import { ConversationSelectModel } from '@nimkit/chatkit/src/main/ets/model/ConversationSelectModel'
|
||||
import { AvatarColorUntil, AvatarItem, CommonAvatar, NECommonUtils } from '@nimkit/common'
|
||||
|
||||
/**
|
||||
* 转发弹框
|
||||
*/
|
||||
@CustomDialog
|
||||
export struct ForwardMessageDialog {
|
||||
controller?: CustomDialogController
|
||||
conversationList: ConversationSelectModel[] = []
|
||||
leaveText?: string // 留言
|
||||
forwardType: ResourceStr = $r('app.string.chat_operation_forward') // 转发类型:转发、合并转发、逐条转发
|
||||
currentConversationName?: string
|
||||
sendForwardMsg?: (leaveText: string | undefined) => void
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Text($r('app.string.chat_send_tips', ""))
|
||||
.fontSize(16)
|
||||
.padding({ top: 16, left: 16 })
|
||||
.height(34)
|
||||
.width('100%')
|
||||
|
||||
Stack({ alignContent: Alignment.Start }) {
|
||||
List({ space: 9 }) {
|
||||
ForEach(this.conversationList, (item: ConversationSelectModel) => {
|
||||
ListItem() {
|
||||
CommonAvatar({
|
||||
item: new AvatarItem(item.avatar, NECommonUtils.shortName(item.name),
|
||||
AvatarColorUntil.getBackgroundColorById(item.conversationId ?? "")),
|
||||
roundRadius: 16
|
||||
})
|
||||
.height(32)
|
||||
.width(32)
|
||||
}
|
||||
.width(32)
|
||||
.height(32)
|
||||
})
|
||||
}
|
||||
.listDirection(Axis.Horizontal)
|
||||
.scrollBar(BarState.Off)
|
||||
.width('100%')
|
||||
.height(48)
|
||||
.backgroundColor(Color.White)
|
||||
.padding({
|
||||
top: 16,
|
||||
left: 16,
|
||||
right: 16
|
||||
})
|
||||
|
||||
if (this.conversationList.length === 1) {
|
||||
Text(this.conversationList[0].name)
|
||||
.fontSize(14)
|
||||
.height(18)
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
.margin({
|
||||
top: 18,
|
||||
left: 56,
|
||||
right: 12
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Column() {
|
||||
Text() {
|
||||
Span('[')
|
||||
Span(this.forwardType)
|
||||
Span(']')
|
||||
Span($r('app.string.chat_operation_forward_desc', this.currentConversationName))
|
||||
}
|
||||
.alignSelf(ItemAlign.Start)
|
||||
.fontSize(14)
|
||||
.height(16)
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.CENTER)
|
||||
.margin({
|
||||
top: 8,
|
||||
left: 12,
|
||||
right: 12
|
||||
})
|
||||
}
|
||||
.width(244)
|
||||
.height(32)
|
||||
.borderRadius(4)
|
||||
.backgroundColor('#F2F4F5')
|
||||
.margin({
|
||||
top: 12,
|
||||
left: 16,
|
||||
right: 16
|
||||
})
|
||||
|
||||
TextInput({ placeholder: $r('app.string.forward_dialog_leave_message') })
|
||||
.fontSize(14)
|
||||
.height(32)
|
||||
.placeholderColor('#A6ADB6')
|
||||
.placeholderFont({ size: 14 })
|
||||
.backgroundColor(Color.White)
|
||||
.borderRadius(4)
|
||||
.borderColor('#E1E6E8')
|
||||
.borderWidth(1)
|
||||
.onChange((value: string, previewText?: PreviewText) => {
|
||||
this.leaveText = value
|
||||
})
|
||||
.margin({
|
||||
top: 12,
|
||||
left: 16,
|
||||
right: 16
|
||||
})
|
||||
|
||||
Row()
|
||||
.height(1)
|
||||
.width('100%')
|
||||
.backgroundColor("#EFF1F4")
|
||||
.margin({ top: 24 })
|
||||
|
||||
Row() {
|
||||
Button($r('app.string.mine_edit_cancel'), { type: ButtonType.Normal })
|
||||
.fontSize(17)
|
||||
.fontColor("#666666")
|
||||
.backgroundColor(Color.White)
|
||||
.width(135)
|
||||
.height(52)
|
||||
.onClick(() => {
|
||||
this.controller?.close()
|
||||
})
|
||||
|
||||
Column()
|
||||
.width(1)
|
||||
.height('100%')
|
||||
.backgroundColor("#EFF1F4")
|
||||
|
||||
Button($r('app.string.chat_send'), { type: ButtonType.Normal })
|
||||
.fontSize(17)
|
||||
.fontColor("#007AFF")
|
||||
.backgroundColor(Color.White)
|
||||
.width(135)
|
||||
.height(52)
|
||||
.onClick(() => {
|
||||
this.sendForwardMsg?.(this.leaveText)
|
||||
this.controller?.close()
|
||||
})
|
||||
}
|
||||
.height(52)
|
||||
.width(270)
|
||||
.alignItems(VerticalAlign.Center)
|
||||
}
|
||||
.height(140)
|
||||
.width(270)
|
||||
.backgroundColor(Color.White)
|
||||
}
|
||||
}
|
||||
113
chatkit_ui/src/main/ets/view/MessageAudioItemView.ets
Normal file
113
chatkit_ui/src/main/ets/view/MessageAudioItemView.ets
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { getAudioMessageText, getAudioMessageWidth } from '../common/MessageHelper'
|
||||
import { V2NIMMessageAudioAttachment } from '@nimsdk/base'
|
||||
import { NIMMessageInfo } from '../model/NIMMessageInfo'
|
||||
import { AudioPlayerManager } from '../manager/AudioPlayerManager'
|
||||
import { ChatConst } from '../constants/ChatConst'
|
||||
import { MessageItemClick } from './MessageItemClick'
|
||||
|
||||
@ComponentV2
|
||||
export struct MessageAudioItemView {
|
||||
@Param
|
||||
@Require
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
@BuilderParam
|
||||
isReceiveStyle: boolean | undefined
|
||||
@Local
|
||||
receiveSoundIndex: number = 2
|
||||
sendSoundImages = [
|
||||
$r('app.media.ic_sound_from_1'),
|
||||
$r('app.media.ic_sound_from_2'),
|
||||
$r('app.media.ic_sound_from_3')
|
||||
]
|
||||
@Local
|
||||
sendSoundIndex: number = 2
|
||||
receiveSoundImages = [
|
||||
$r('app.media.ic_sound_to_1'),
|
||||
$r('app.media.ic_sound_to_2'),
|
||||
$r('app.media.ic_sound_to_3')
|
||||
]
|
||||
//定时器id
|
||||
intervalId?: number
|
||||
|
||||
aboutToDisappear(): void {
|
||||
AudioPlayerManager.instance.stopPlayAll()
|
||||
}
|
||||
|
||||
onPlayStart: () => void = () => {
|
||||
this.startPlayAni()
|
||||
}
|
||||
onPlayFinished: () => void = () => {
|
||||
clearInterval(this.intervalId)
|
||||
this.receiveSoundIndex = 2
|
||||
this.sendSoundIndex = 2
|
||||
}
|
||||
|
||||
showReceiveStyle(): boolean {
|
||||
return this.message.isReceiveMessage() || (this.isReceiveStyle !== undefined && this.isReceiveStyle)
|
||||
}
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
if (this.showReceiveStyle()) {
|
||||
Image(this.sendSoundImages[this.sendSoundIndex]).objectFit(ImageFit.Auto).width(28).height(28)
|
||||
Text(getAudioMessageText(this.message))
|
||||
.margin({ left: 8 }).maxLines(1)
|
||||
.fontColor('#ff333333')
|
||||
.fontSize(14)
|
||||
} else {
|
||||
Text(getAudioMessageText(this.message))
|
||||
.margin({ right: 8 }).maxLines(1)
|
||||
.fontColor('#ff333333')
|
||||
.fontSize(14)
|
||||
Image(this.receiveSoundImages[this.receiveSoundIndex]).objectFit(ImageFit.Auto).width(28).height(28)
|
||||
}
|
||||
}
|
||||
.justifyContent(this.showReceiveStyle() ? FlexAlign.Start : FlexAlign.End)
|
||||
.margin({
|
||||
left: 12,
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
right: 12
|
||||
})
|
||||
.onClick(() => {
|
||||
clearInterval(this.intervalId)
|
||||
this.intervalId = undefined
|
||||
let audioManager = AudioPlayerManager.instance
|
||||
if (this.message.message.attachment) {
|
||||
audioManager.avPlayerLive((this.message.message.attachment as V2NIMMessageAudioAttachment).url ?? '',
|
||||
this.onPlayStart,
|
||||
this.onPlayFinished
|
||||
)
|
||||
}
|
||||
})
|
||||
// .gesture(LongPressGesture({ duration: 300 }).onAction((event: GestureEvent) => {
|
||||
// this.onMessageClick?.onItemLongClick?.(event, this.message)
|
||||
// }))
|
||||
.width(getAudioMessageWidth(this.message))
|
||||
.height(ChatConst.audioMessageWidth)
|
||||
}
|
||||
|
||||
startPlayAni() {
|
||||
this.intervalId = setInterval(() => {
|
||||
if (this.sendSoundIndex === 2) {
|
||||
this.sendSoundIndex = 0
|
||||
} else {
|
||||
this.sendSoundIndex++
|
||||
}
|
||||
if (this.receiveSoundIndex === 2) {
|
||||
this.receiveSoundIndex = 0
|
||||
} else {
|
||||
this.receiveSoundIndex++
|
||||
}
|
||||
}, 600);
|
||||
}
|
||||
}
|
||||
568
chatkit_ui/src/main/ets/view/MessageComponent.ets
Normal file
568
chatkit_ui/src/main/ets/view/MessageComponent.ets
Normal file
@ -0,0 +1,568 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { NIMMessageInfo } from '../model/NIMMessageInfo';
|
||||
import { V2NIMConversationType, V2NIMMessageSendingState, V2NIMMessageType } from '@nimsdk/base';
|
||||
import { ChatInfo } from '../model/ChatInfo';
|
||||
import { getNotificationMessageContent, getPinMessagesTips } from '../common/MessageHelper';
|
||||
import { AvatarColorUntil, AvatarItem, CommonAvatar } from '@nimkit/common';
|
||||
import { messageContent } from './MessageComponentBuilder';
|
||||
import { MessageItemClick } from './MessageItemClick';
|
||||
import { ChatKitConfig } from '../ChatKitConfig';
|
||||
|
||||
/**
|
||||
* 消息组件按照UI样式,划分为发送消息组件、接收消息组件、通知消息组件和提示消息组件
|
||||
*/
|
||||
@ComponentV2
|
||||
export struct MessageComponent {
|
||||
@Require
|
||||
@Param
|
||||
message: NIMMessageInfo | undefined = undefined;
|
||||
@Require
|
||||
@Param
|
||||
chatInfo: ChatInfo
|
||||
@Param
|
||||
onMessageClick: MessageItemClick | undefined = undefined
|
||||
@Param
|
||||
onMoreButtonClick?: (event: ClickEvent, msg: NIMMessageInfo | undefined) => void = undefined
|
||||
@Param
|
||||
onPinItemClick?: (event: ClickEvent, msg: NIMMessageInfo | undefined) => void = undefined
|
||||
@Param
|
||||
showSelect: boolean = false;
|
||||
|
||||
build() {
|
||||
if (this.message?.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_NOTIFICATION) {
|
||||
NotificationMessageComponent({
|
||||
message: this.message,
|
||||
messageContent: getNotificationMessageContent(this.message, this.chatInfo)
|
||||
});
|
||||
} else if (this.message?.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_TIPS) {
|
||||
TipsMessageComponent({ message: this.message });
|
||||
} else if (this.message?.isReceiveMessage()) {
|
||||
ReceiveMessageComponent({
|
||||
message: this.message,
|
||||
chatUserInfo: this.chatInfo,
|
||||
onMessageClick: this.onMessageClick,
|
||||
showRadio: this.showSelect,
|
||||
});
|
||||
} else {
|
||||
SenderMessageComponent({
|
||||
message: this.message,
|
||||
chatUserInfo: this.chatInfo,
|
||||
onMessageClick: this.onMessageClick,
|
||||
showRadio: this.showSelect,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 发送消息组件
|
||||
@ComponentV2
|
||||
export struct SenderMessageComponent {
|
||||
AlignLeft: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = {
|
||||
'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
|
||||
'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start }
|
||||
}
|
||||
AlignRight: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = {
|
||||
'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
|
||||
'right': { 'anchor': '__container__', 'align': HorizontalAlign.End }
|
||||
}
|
||||
@Require
|
||||
@Param
|
||||
message: NIMMessageInfo | undefined = undefined
|
||||
@Require
|
||||
@Param
|
||||
chatUserInfo: ChatInfo | undefined = undefined
|
||||
@Param
|
||||
onMessageClick: MessageItemClick | undefined = undefined
|
||||
@Param
|
||||
showRadio: boolean = false
|
||||
@Local
|
||||
radioOn: boolean = false
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (this.message !== undefined) {
|
||||
if (this.message?.getMessageTime() !== '') {
|
||||
Row() {
|
||||
Text(this.message?.getMessageTime()).fontColor($r('app.color.color_chat_desc'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
}.justifyContent(FlexAlign.Center).width('100%').height(20)
|
||||
}
|
||||
Row() {
|
||||
if (this.showRadio) {
|
||||
if (this.message.isRevokeMsg) {
|
||||
Text().margin({ top: (this.message?.getMessageHeight(this.getUIContext()) - 20) / 2, left: 16 })
|
||||
.height('20')
|
||||
.width('20')
|
||||
} else {
|
||||
Toggle({ type: ToggleType.Checkbox, isOn: this.message.isSelectedMsg })
|
||||
.margin({ top: (this.message?.getMessageHeight(this.getUIContext()) - 20) / 2, left: 16 })
|
||||
.height('20')
|
||||
.width('20')
|
||||
.onChange((value: boolean) => {
|
||||
this.onMessageClick?.onMultiSelect?.(value, this.message)
|
||||
this.radioOn = value;
|
||||
})
|
||||
}
|
||||
}
|
||||
Column() {
|
||||
Row() {
|
||||
if (this.message.message.sendingState === V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_FAILED) {
|
||||
Image($r("app.media.ic_chat_message_status_fail")).width(16).height(16)
|
||||
.alignRules({
|
||||
bottom: { anchor: "msgContainer", align: VerticalAlign.Bottom },
|
||||
right: { anchor: "msgContainer", align: HorizontalAlign.Start }
|
||||
}).margin({ right: 6 })
|
||||
.onClick((event: ClickEvent) => {
|
||||
if (this.onMessageClick) {
|
||||
this.onMessageClick?.onSendFailClick?.(event, this.message)
|
||||
}
|
||||
})
|
||||
} else if (this.message.message.sendingState ===
|
||||
V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_SENDING) {
|
||||
Progress({ value: 0, total: 100, type: ProgressType.Ring })
|
||||
.width(16)
|
||||
.height(16)
|
||||
.color($r('app.color.color_chat_send'))
|
||||
.style({ strokeWidth: 3, status: ProgressStatus.LOADING })
|
||||
.alignRules({
|
||||
bottom: { anchor: "msgContainer", align: VerticalAlign.Bottom },
|
||||
right: { anchor: "msgContainer", align: HorizontalAlign.Start }
|
||||
})
|
||||
.margin({ right: 6 })
|
||||
} else if (this.message.message.sendingState ===
|
||||
V2NIMMessageSendingState.V2NIM_MESSAGE_SENDING_STATE_SUCCEEDED && ChatKitConfig.messageReadState
|
||||
&& this.message.configReadReceipt()) {
|
||||
if (!this.message.isRevokeMsg) {
|
||||
if (this.message.message.conversationType == V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P) {
|
||||
if (this.message.readCount >= 0) {
|
||||
// Image(this.message.readCount == 0 ? $r('app.media.ic_chat_read_status_unread') :
|
||||
// $r('app.media.ic_chat_read_status_read'))
|
||||
// .width(16)
|
||||
// .height(16)
|
||||
// .alignRules({
|
||||
// bottom: { anchor: "msgContainer", align: VerticalAlign.Bottom },
|
||||
// right: { anchor: "msgContainer", align: HorizontalAlign.Start }
|
||||
// })
|
||||
// .margin({ right: 6 })
|
||||
//去掉已读未读
|
||||
}
|
||||
|
||||
} else {
|
||||
if (this.message.readCount !== -1) {
|
||||
if (this.message.unReadCount == 0) {
|
||||
// Image(this.message.readCount == 0 ? $r('app.media.ic_chat_read_status_unread') :
|
||||
// $r('app.media.ic_chat_read_status_read')).width(16).height(16)
|
||||
// .alignRules({
|
||||
// bottom: { anchor: "msgContainer", align: VerticalAlign.Bottom },
|
||||
// right: { anchor: "msgContainer", align: HorizontalAlign.Start }
|
||||
// }).margin({ right: 6 })
|
||||
//去掉已读未读
|
||||
} else {
|
||||
Stack({}) {
|
||||
Circle({ width: 15, height: 15 }).fill($r('app.color.color_chat_send'))
|
||||
Progress({
|
||||
value: this.message.readCount,
|
||||
total: (this.message.readCount + this.message.unReadCount),
|
||||
type: ProgressType.Eclipse
|
||||
})
|
||||
.width(13.8)
|
||||
.height(13.8)
|
||||
.color($r('app.color.color_chat_send'))
|
||||
.backgroundColor($r('app.color.color_chat_page_bg'))
|
||||
.style({
|
||||
strokeWidth: 1
|
||||
})
|
||||
.onClick((event: ClickEvent) => {
|
||||
this.onMessageClick?.onReadReceiptClick?.(event, this.message)
|
||||
})
|
||||
}
|
||||
.alignRules({
|
||||
bottom: { anchor: "msgContainer", align: VerticalAlign.Bottom },
|
||||
right: { anchor: "msgContainer", align: HorizontalAlign.Start }
|
||||
})
|
||||
.margin({ right: 6 })
|
||||
.onClick((event: ClickEvent) => {
|
||||
this.onMessageClick?.onReadReceiptClick?.(event, this.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
messageContent({
|
||||
message: this.message,
|
||||
onMessageClick: this.onMessageClick,
|
||||
chatInfo: this.chatUserInfo
|
||||
})
|
||||
.backgroundColor($r('app.color.color_chat_send_message_bg'))
|
||||
.borderRadius(6)
|
||||
// .backgroundImage($r('app.media.green_bg'))
|
||||
// .backgroundImageSize({ width: '100%', height: '100%' }) // 背景覆盖整个组件区域
|
||||
// .backgroundImageResizable({
|
||||
// slice: { top: 100, left: 50, bottom: 2, right: 60 } // 圆角区域不拉伸
|
||||
//
|
||||
// })
|
||||
.onClick((event: ClickEvent) => {
|
||||
if (this.showRadio) {
|
||||
this.radioOn = !this.radioOn
|
||||
}
|
||||
this.onMessageClick?.onItemClick?.(event, this.message)
|
||||
})
|
||||
.margin({ top: 4, right: 6 })
|
||||
.flexShrink(1)
|
||||
.id("msgContainer")
|
||||
}
|
||||
.justifyContent(FlexAlign.End)
|
||||
.alignItems(VerticalAlign.Bottom)
|
||||
.margin({ right: 8 })
|
||||
.width('100%')
|
||||
//.height(this.message.getMessageHeight(this.getUIContext()))
|
||||
.gesture(LongPressGesture()
|
||||
.onAction((event: GestureEvent) => {
|
||||
this.onMessageClick?.onItemLongClick?.(event, this.message)
|
||||
}))
|
||||
|
||||
if (this.message?.isPinMsg) {
|
||||
Row() {
|
||||
Image($r('app.media.ic_chat_message_pin')).height(12).width(12).margin({ top: 6 })
|
||||
Text(getPinMessagesTips(this.message, this.chatUserInfo))
|
||||
.fontSize(12)
|
||||
.fontColor($r('app.color.color_chat_pin_tips'))
|
||||
.margin({ top: 6, left: 6 })
|
||||
.maxLines(1)
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
}.width('100%').height('26vp').margin({ bottom: 6, right: 12 }).justifyContent(FlexAlign.End)
|
||||
}
|
||||
}.alignItems(HorizontalAlign.End).margin({ left: 8 }).width('75%')
|
||||
|
||||
CommonAvatar({
|
||||
item: new AvatarItem(this.chatUserInfo != null ?
|
||||
this.chatUserInfo?.getCurrentUserAvatarUrl() : '',
|
||||
this.chatUserInfo?.getCurrentUserAvatarName() ?? '',
|
||||
AvatarColorUntil.getBackgroundColorById(this.message.message.senderId),
|
||||
),
|
||||
longPressGesture: () => {
|
||||
this.onMessageClick?.onAvatarLongPress?.( this.message)
|
||||
}
|
||||
})
|
||||
.width(36)
|
||||
.height(36)
|
||||
.borderRadius(20)
|
||||
.margin({ right: 16, top: 6 })
|
||||
.id("mineAvatar")
|
||||
.onClick(() => {
|
||||
this.onMessageClick?.onAvatarClick?.(this.message)
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.backgroundColor(this.message.isPinMsg ? $r('app.color.color_chat_pin_bg') :
|
||||
$r('app.color.color_chat_page_bg'))
|
||||
.justifyContent(FlexAlign.End)
|
||||
.alignItems(VerticalAlign.Top)
|
||||
}
|
||||
}
|
||||
.width('100%').margin({ top: 6, bottom: 6 })
|
||||
}
|
||||
}
|
||||
|
||||
// 接收消息组件
|
||||
@ComponentV2
|
||||
export struct ReceiveMessageComponent {
|
||||
AlignLeft: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = {
|
||||
'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
|
||||
'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start }
|
||||
}
|
||||
@Require
|
||||
@Param
|
||||
message: NIMMessageInfo | undefined = undefined
|
||||
@Require
|
||||
@Param
|
||||
chatUserInfo: ChatInfo | undefined = undefined
|
||||
@Param
|
||||
showRadio: boolean = false
|
||||
@Param
|
||||
onMessageClick: MessageItemClick | undefined = undefined
|
||||
@Local
|
||||
radioOn: boolean = false
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (this.message !== undefined) {
|
||||
if (this.message?.getMessageTime() !== '') {
|
||||
Row() {
|
||||
Text(this.message?.getMessageTime())
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
}.justifyContent(FlexAlign.Center).width('100%').height(20)
|
||||
.margin({ top: 6 })
|
||||
}
|
||||
Row() {
|
||||
if (this.showRadio) {
|
||||
if (this.message.isRevokeMsg) {
|
||||
Text().margin({ top: (this.message?.getMessageHeight(this.getUIContext()) - 20) / 2, left: 16 })
|
||||
.height('20')
|
||||
.width('20')
|
||||
} else {
|
||||
Toggle({ type: ToggleType.Checkbox, isOn: this.message.isSelectedMsg })
|
||||
.margin({ top: (this.message?.getMessageHeight(this.getUIContext()) - 20) / 2, left: 16 })
|
||||
.height('20')
|
||||
.width('20')
|
||||
.onChange((value: boolean) => {
|
||||
this.onMessageClick?.onMultiSelect?.(value, this.message)
|
||||
// this.radioOn = value;
|
||||
})
|
||||
}
|
||||
}
|
||||
CommonAvatar({
|
||||
item: new AvatarItem(this.chatUserInfo != null ?
|
||||
this.chatUserInfo?.getChatUserAvatarUrl(this.message?.message) : '',
|
||||
this.chatUserInfo?.getChatUserAvatarName(this.message?.message) ?? '',
|
||||
AvatarColorUntil.getBackgroundColorById(this.message?.message.senderId ?? '')
|
||||
),
|
||||
longPressGesture: () => {
|
||||
this.onMessageClick?.onAvatarLongPress?.( this.message)
|
||||
}
|
||||
})
|
||||
.width(36)
|
||||
.height(36)
|
||||
.borderRadius(20)
|
||||
.margin({ left: 16, top: 6 })
|
||||
.id("otherAvatar")
|
||||
.onClick(() => {
|
||||
this.onMessageClick?.onAvatarClick?.(this.message)
|
||||
})
|
||||
Column() {
|
||||
if (this.message?.message.conversationType !== V2NIMConversationType.V2NIM_CONVERSATION_TYPE_P2P) {
|
||||
Text(this.chatUserInfo?.getChatUserShowName(this.message?.message))
|
||||
.fontColor($r('app.color.color_chat_sub_title'))
|
||||
.fontSize($r('app.float.chat_subtitle_text_font_size'))
|
||||
.maxLines(1)
|
||||
.width(150)
|
||||
.textAlign(TextAlign.Start)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
.height(25)
|
||||
}
|
||||
|
||||
Column() {
|
||||
messageContent({
|
||||
message: this.message,
|
||||
onMessageClick: this.onMessageClick,
|
||||
chatInfo: this.chatUserInfo
|
||||
})
|
||||
.backgroundColor($r('app.color.color_chat_receive_message_bg'))
|
||||
.borderRadius(6)
|
||||
.id("msgContainer")
|
||||
.alignRules(this.AlignLeft)
|
||||
.onClick((event: ClickEvent) => {
|
||||
if (this.showRadio) {
|
||||
this.radioOn = !this.radioOn
|
||||
}
|
||||
this.onMessageClick?.onItemClick?.(event, this.message)
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
//.height(this.message?.getMessageHeight(this.getUIContext()))
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.gesture(LongPressGesture().onAction((event: GestureEvent) => {
|
||||
this.onMessageClick?.onItemLongClick?.(event, this.message)
|
||||
}))
|
||||
|
||||
if (this.message?.isPinMsg) {
|
||||
Row() {
|
||||
Image($r('app.media.ic_chat_message_pin')).height(12).width(12).margin({ top: 6 })
|
||||
Text(getPinMessagesTips(this.message, this.chatUserInfo))
|
||||
.fontSize(12)
|
||||
.fontColor($r('app.color.color_chat_pin_tips'))
|
||||
.margin({ top: 6, left: 6 })
|
||||
.maxLines(1)
|
||||
.ellipsisMode(EllipsisMode.CENTER)
|
||||
}.width('100%').height('26vp')
|
||||
}
|
||||
}.alignItems(HorizontalAlign.Start).margin({ left: 8, top: 6 }).width('68%')
|
||||
}
|
||||
.width('100%')
|
||||
.alignItems(VerticalAlign.Top)
|
||||
.backgroundColor(this.message?.isPinMsg ? $r('app.color.color_chat_pin_bg') :
|
||||
$r('app.color.color_chat_page_bg'))
|
||||
.id('itemContainer')
|
||||
}
|
||||
}.width('100%').padding({ top: 6, bottom: 6 })
|
||||
}
|
||||
}
|
||||
|
||||
// 通知消息组件
|
||||
@ComponentV2
|
||||
export struct NotificationMessageComponent {
|
||||
@Require
|
||||
@Param
|
||||
message: NIMMessageInfo | null = null;
|
||||
@Require
|
||||
@Param
|
||||
messageContent: string;
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (this.message !== null && this.messageContent !== '') {
|
||||
if (this.message?.getMessageTime() !== '') {
|
||||
Text(this.message?.getMessageTime())
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.textAlign(TextAlign.Center)
|
||||
.width('100%')
|
||||
.height(20)
|
||||
}
|
||||
Row() {
|
||||
Text(this.messageContent ?? $r('app.string.chat_message_empty_notification_text'))
|
||||
.fontColor($r('app.color.color_chat_sub_title'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.padding({
|
||||
left: 6,
|
||||
top: 6,
|
||||
bottom: 6,
|
||||
right: 6
|
||||
})
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
}
|
||||
.width('100%')
|
||||
// .height(this.message?.getMessageHeight(this.getUIContext()))
|
||||
.padding({ top: 6, bottom: 6 })
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.borderRadius(6)
|
||||
.id("msgContainer")
|
||||
}
|
||||
}.margin({ left: 60, right: 60 })
|
||||
}
|
||||
}
|
||||
|
||||
// 提示消息组件
|
||||
@ComponentV2
|
||||
export struct TipsMessageComponent {
|
||||
@Require
|
||||
@Param
|
||||
message: NIMMessageInfo | null = null;
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (this.message !== null && this.message.message.text !== undefined && this.message.message.text !== '') {
|
||||
if (this.message?.getMessageTime() !== '') {
|
||||
Row() {
|
||||
Text(this.message?.getMessageTime()).fontColor($r('app.color.color_chat_desc'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
}.justifyContent(FlexAlign.Center).width('100%').height(20)
|
||||
}
|
||||
Row() {
|
||||
Text(this.message?.message.text ?? $r('app.string.chat_message_empty_tip_text'))
|
||||
.lineHeight(20)
|
||||
.padding({
|
||||
left: 6,
|
||||
top: 6,
|
||||
bottom: 6,
|
||||
right: 6
|
||||
})
|
||||
.fontColor($r('app.color.color_chat_sub_title'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
}
|
||||
.width('100%')
|
||||
// .height(this.message?.getMessageHeight(this.getUIContext()))
|
||||
.padding({ top: 6, bottom: 6 })
|
||||
.margin({ left: 60, right: 60, top: 6 })
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.borderRadius(6)
|
||||
.id("msgContainer")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并转发详情页中的消息组件
|
||||
*/
|
||||
@ComponentV2
|
||||
export struct MergeDetailMessageComponent {
|
||||
AlignLeft: Record<string, Record<string, string | VerticalAlign | HorizontalAlign>> = {
|
||||
'top': { 'anchor': '__container__', 'align': VerticalAlign.Top },
|
||||
'left': { 'anchor': '__container__', 'align': HorizontalAlign.Start }
|
||||
}
|
||||
@Require
|
||||
@Param
|
||||
message: NIMMessageInfo | undefined = undefined
|
||||
@Require
|
||||
@Param
|
||||
chatUserInfo: ChatInfo | undefined = undefined
|
||||
@Param
|
||||
onMessageClick: MessageItemClick | undefined = undefined
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (this.message !== undefined) {
|
||||
if (this.message?.getMessageTime() !== '') {
|
||||
Row() {
|
||||
Text(this.message?.getMessageTime())
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
}.justifyContent(FlexAlign.Center).width('100%').height(20)
|
||||
.margin({ top: 4 })
|
||||
}
|
||||
Row() {
|
||||
CommonAvatar({
|
||||
item: new AvatarItem(this.chatUserInfo != null ?
|
||||
this.chatUserInfo?.getChatUserAvatarUrl(this.message?.message) : '',
|
||||
this.chatUserInfo?.getChatUserAvatarName(this.message?.message) ?? '',
|
||||
AvatarColorUntil.getBackgroundColorById(this.message?.message.senderId ?? '')),
|
||||
longPressGesture: () => {
|
||||
this.onMessageClick?.onAvatarLongPress?.( this.message)
|
||||
}
|
||||
})
|
||||
.width(36)
|
||||
.height(36)
|
||||
.borderRadius(20)
|
||||
.margin({ left: 16, top: 3 })
|
||||
.id("otherAvatar")
|
||||
.onClick(() => {
|
||||
this.onMessageClick?.onAvatarClick?.(this.message)
|
||||
})
|
||||
|
||||
Column() {
|
||||
Text(this.chatUserInfo?.getChatUserShowName(this.message?.message))
|
||||
.fontColor($r('app.color.color_chat_sub_title'))
|
||||
.fontSize($r('app.float.chat_subtitle_text_font_size'))
|
||||
.maxLines(1)
|
||||
.width(150)
|
||||
.textAlign(TextAlign.Start)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
Column() {
|
||||
messageContent({ message: this.message, onMessageClick: this.onMessageClick })
|
||||
.backgroundColor($r('app.color.color_chat_receive_message_bg'))
|
||||
.borderRadius(6)
|
||||
.id("msgContainer")
|
||||
.alignRules(this.AlignLeft)
|
||||
.onClick((event: ClickEvent) => {
|
||||
this.onMessageClick?.onItemClick?.(event, this.message)
|
||||
})
|
||||
} .height(this.message?.getMessageHeight(this.getUIContext()))
|
||||
.margin({ top: 6 })
|
||||
}.alignItems(HorizontalAlign.Start).margin({ left: 8 }).width('68%')
|
||||
.gesture(LongPressGesture().onAction((event: GestureEvent) => {
|
||||
this.onMessageClick?.onItemLongClick?.(event, this.message)
|
||||
}))
|
||||
|
||||
}
|
||||
.width('100%')
|
||||
.alignItems(VerticalAlign.Top)
|
||||
.backgroundColor(this.message?.isPinMsg ? $r('app.color.color_chat_pin_bg') :
|
||||
$r('app.color.color_chat_page_bg'))
|
||||
.id('itemContainer')
|
||||
}
|
||||
}.width('100%').padding({ top: 3})
|
||||
}
|
||||
}
|
||||
972
chatkit_ui/src/main/ets/view/MessageComponentBuilder.ets
Normal file
972
chatkit_ui/src/main/ets/view/MessageComponentBuilder.ets
Normal file
@ -0,0 +1,972 @@
|
||||
/*
|
||||
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
|
||||
* Use of this source code is governed by a MIT license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
*/
|
||||
|
||||
import { NEEmojiParseResult } from '../manager/NEEmojiManager'
|
||||
import { V2NIMMessageFileAttachment, V2NIMMessageLocationAttachment, V2NIMMessageType,V2NIMMessageAttachment } from '@nimsdk/base'
|
||||
import {
|
||||
getAitNodes,
|
||||
getCallMessageIcon,
|
||||
getCallMessageText,
|
||||
getFileMessageIcon,
|
||||
getFileMessageName,
|
||||
getFileMessageSize,
|
||||
getImageHeight,
|
||||
getImageWidth,
|
||||
getReplyMessageText,
|
||||
getVideoMessageThumbnail,
|
||||
getVideoMessageUrl,
|
||||
parseMessageText,
|
||||
parseReplyMessageUserInfo,
|
||||
parseText,
|
||||
sliceMessageText
|
||||
} from '../common/MessageHelper'
|
||||
import { NIMMessageInfo } from '../model/NIMMessageInfo'
|
||||
import { MessageItemClick } from './MessageItemClick'
|
||||
import { MessageAudioItemView } from '../view/MessageAudioItemView'
|
||||
import { image } from '@kit.ImageKit'
|
||||
import { staticMap } from '@kit.MapKit'
|
||||
import { BusinessError } from '@kit.BasicServicesKit'
|
||||
import { ChatAitNode } from '../model/ChatAitNode'
|
||||
import { MergedMessageAttachment } from '@nimkit/chatkit'
|
||||
import { ChatInfo } from '../model/ChatInfo'
|
||||
import { Markdown } from '@nimkit/markdown'
|
||||
import { BasicConstant,DataWebModel } from '@itcast/basic'
|
||||
import { router } from '@kit.ArkUI'
|
||||
@ComponentV2
|
||||
export struct messageContent {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
@BuilderParam
|
||||
chatInfo: ChatInfo | undefined
|
||||
|
||||
build() {
|
||||
if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_TEXT) {
|
||||
if (this.message.isAiStreamMessage() == false) {
|
||||
textBuilder({
|
||||
message: this.message,
|
||||
onMessageClick: this.onMessageClick,
|
||||
chatInfo: this.chatInfo
|
||||
});
|
||||
} else {
|
||||
richTextBuilder({
|
||||
message: this.message,
|
||||
onMessageClick: this.onMessageClick,
|
||||
chatInfo: this.chatInfo
|
||||
});
|
||||
}
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_LOCATION) {
|
||||
locationBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE) {
|
||||
imageBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_VIDEO) {
|
||||
videoBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_FILE) {
|
||||
fileBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_AUDIO) {
|
||||
MessageAudioItemView({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL) {
|
||||
callBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_CUSTOM) {
|
||||
if (this.message.isMergeMsg) {
|
||||
mergedMessageBuilder({ message: this.message, onMessageClick: this.onMessageClick })
|
||||
} else {
|
||||
|
||||
customBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
}
|
||||
} else {
|
||||
unknownBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ComponentV2
|
||||
export struct pinMessageContent {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick?: MessageItemClick
|
||||
|
||||
build() {
|
||||
if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_TEXT) {
|
||||
pinTextBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_LOCATION) {
|
||||
pinLocationBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE) {
|
||||
imageBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_VIDEO) {
|
||||
videoBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_FILE) {
|
||||
fileBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_AUDIO) {
|
||||
MessageAudioItemView({ message: this.message, onMessageClick: this.onMessageClick, isReceiveStyle: true });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_CALL) {
|
||||
callBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
} else if (this.message.getMessageType() == V2NIMMessageType.V2NIM_MESSAGE_TYPE_CUSTOM) {
|
||||
if (this.message.isMergeMsg) {
|
||||
mergedMessageBuilder({ message: this.message, onMessageClick: this.onMessageClick }).width('80%')
|
||||
} else {
|
||||
unknownBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
}
|
||||
} else {
|
||||
unknownBuilder({ message: this.message, onMessageClick: this.onMessageClick });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ComponentV2
|
||||
export struct richTextBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
@BuilderParam
|
||||
chatInfo: ChatInfo | undefined
|
||||
|
||||
private onItemClick(event: ClickEvent) {
|
||||
this.onMessageClick?.onReplyClick?.(event, this.message.replyMsg)
|
||||
}
|
||||
|
||||
private onRevokeEditClick(event: ClickEvent) {
|
||||
this.onMessageClick?.onRevokeEditClick?.(event, this.message)
|
||||
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (!this.message.isRevokeMsg && this.message.isReplyMsg) {
|
||||
if (this.message.replyMsg) {
|
||||
Text() {
|
||||
if (this.chatInfo) {
|
||||
Span(' | ' + parseReplyMessageUserInfo(this.message.replyMsg, this.chatInfo))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.textCase(TextCase.Normal)
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
}
|
||||
ForEach(sliceMessageText(parseMessageText(getReplyMessageText(this.message.replyMsg)), 30),
|
||||
(item: NEEmojiParseResult) => {
|
||||
if (item.text) {
|
||||
Span(item.text)
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.textCase(TextCase.Normal)
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
} else if (item.emoji) {
|
||||
ImageSpan($rawfile(`emoji/${item.emoji.file}`)).width('16')
|
||||
.height('16')
|
||||
.objectFit(ImageFit.Fill)
|
||||
.verticalAlign(ImageSpanAlignment.CENTER)
|
||||
}
|
||||
})
|
||||
}
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.height(20)
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
.margin({ bottom: 4 })
|
||||
.onClick((event) => this.onItemClick(event))
|
||||
} else {
|
||||
// Text(getContext().resourceManager.getStringSync($r('app.string.chat_reply_not_exist').id))
|
||||
// .fontColor($r('app.color.color_chat_desc'))
|
||||
// .fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
// .height(20)
|
||||
// .maxLines(1)
|
||||
// .ellipsisMode(EllipsisMode.END)
|
||||
// .margin({ bottom: 4 })
|
||||
}
|
||||
|
||||
}
|
||||
Column() {
|
||||
if (this.message.isRevokeMsg) {
|
||||
Span(this.message.message.text)
|
||||
if (this.message.revokeEditMsg) {
|
||||
Text($r('app.string.chat_msg_undo_edit_tips'))
|
||||
.fontColor($r('app.color.color_chat_send'))
|
||||
.onClick((event) => {
|
||||
this.onRevokeEditClick(event)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
ForEach(parseMessageText(this.message.message.text?.trimStart()?.trimEnd()), (item: NEEmojiParseResult) => {
|
||||
if (item.text) {
|
||||
ForEach(getAitNodes(item.startIndex, item.text, this.message.message.serverExtension),
|
||||
(node: ChatAitNode) => {
|
||||
if (node.segment) {
|
||||
Span(node.text)
|
||||
.fontSize($r('app.float.chat_message_text_font_size'))
|
||||
.textCase(TextCase.Normal)
|
||||
.fontColor('#337EFF')
|
||||
} else {
|
||||
Markdown({
|
||||
content: node.text,
|
||||
lineSpace: 0,
|
||||
textLineSpace: 6,
|
||||
fontStyle: {
|
||||
fontColor: $r('app.color.color_chat_title'),
|
||||
fontSize: $r('app.float.chat_message_text_font_size')
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
} else if (item.emoji) {
|
||||
ImageSpan($rawfile(`emoji/${item.emoji.file}`))
|
||||
.width(18)
|
||||
.height(18)
|
||||
.objectFit(ImageFit.Fill)
|
||||
.verticalAlign(ImageSpanAlignment.CENTER)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}.alignItems(HorizontalAlign.Start)
|
||||
.padding({
|
||||
left: 12,
|
||||
top: 12,
|
||||
bottom: 12,
|
||||
right: 12
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ComponentV2
|
||||
export struct textBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
@BuilderParam
|
||||
chatInfo: ChatInfo | undefined
|
||||
|
||||
private onItemClick(event: ClickEvent) {
|
||||
this.onMessageClick?.onReplyClick?.(event, this.message.replyMsg)
|
||||
}
|
||||
|
||||
private onRevokeEditClick(event: ClickEvent) {
|
||||
this.onMessageClick?.onRevokeEditClick?.(event, this.message)
|
||||
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (!this.message.isRevokeMsg && this.message.isReplyMsg) {
|
||||
if (this.message.replyMsg) {
|
||||
Text() {
|
||||
if (this.chatInfo) {
|
||||
Span(' | ' + parseReplyMessageUserInfo(this.message.replyMsg, this.chatInfo))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.textCase(TextCase.Normal)
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
}
|
||||
ForEach(sliceMessageText(parseMessageText(getReplyMessageText(this.message.replyMsg)), 30),
|
||||
(item: NEEmojiParseResult) => {
|
||||
if (item.text) {
|
||||
Span(item.text)
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.textCase(TextCase.Normal)
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
} else if (item.emoji) {
|
||||
ImageSpan($rawfile(`emoji/${item.emoji.file}`)).width('16')
|
||||
.height('16')
|
||||
.objectFit(ImageFit.Fill)
|
||||
.verticalAlign(ImageSpanAlignment.CENTER)
|
||||
}
|
||||
})
|
||||
}
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.height(20)
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
.margin({ bottom: 4 })
|
||||
.onClick((event) => this.onItemClick(event))
|
||||
} else {
|
||||
// Text(getContext().resourceManager.getStringSync($r('app.string.chat_reply_not_exist').id))
|
||||
// .fontColor($r('app.color.color_chat_desc'))
|
||||
// .fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
// .height(20)
|
||||
// .maxLines(1)
|
||||
// .ellipsisMode(EllipsisMode.END)
|
||||
// .margin({ bottom: 4 })
|
||||
//去掉消息已撤回
|
||||
}
|
||||
|
||||
}
|
||||
Text() {
|
||||
if (this.message.isRevokeMsg) {
|
||||
Span(this.message.message.text)
|
||||
if (this.message.revokeEditMsg) {
|
||||
Span($r('app.string.chat_msg_undo_edit_tips'))
|
||||
.fontColor($r('app.color.color_chat_send'))
|
||||
.onClick((event) => {
|
||||
this.onRevokeEditClick(event)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
ForEach(parseMessageText(this.message.message.text?.trimStart()?.trimEnd()), (item: NEEmojiParseResult) => {
|
||||
if (item.text) {
|
||||
ForEach(getAitNodes(item.startIndex, item.text, this.message.message.serverExtension),
|
||||
(node: ChatAitNode) => {
|
||||
if (node.segment) {
|
||||
Span(node.text)
|
||||
.fontSize($r('app.float.chat_message_text_font_size'))
|
||||
.textCase(TextCase.Normal)
|
||||
.fontColor('#337EFF')
|
||||
} else {
|
||||
Span(node.text)
|
||||
.fontSize($r('app.float.chat_message_text_font_size'))
|
||||
.textCase(TextCase.Normal)
|
||||
.fontColor($r('app.color.color_chat_title'))
|
||||
}
|
||||
})
|
||||
|
||||
} else if (item.emoji) {
|
||||
ImageSpan($rawfile(`emoji/${item.emoji.file}`))
|
||||
.width(18)
|
||||
.height(18)
|
||||
.objectFit(ImageFit.Fill)
|
||||
.verticalAlign(ImageSpanAlignment.CENTER)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
.lineHeight(20)
|
||||
.fontSize($r('app.float.chat_subtitle_text_font_size'))
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
}.alignItems(HorizontalAlign.Start)
|
||||
.padding({
|
||||
left: 12,
|
||||
top: 12,
|
||||
bottom: 12,
|
||||
right: 12
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ComponentV2
|
||||
export struct pinTextBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Text() {
|
||||
ForEach(parseMessageText(this.message.message.text?.trimStart()?.trimEnd()), (item: NEEmojiParseResult) => {
|
||||
if (item.text) {
|
||||
Span(item.text)
|
||||
.fontSize($r('app.float.chat_message_text_font_size'))
|
||||
.textCase(TextCase.Normal)
|
||||
.fontColor($r('app.color.color_chat_title'))
|
||||
} else if (item.emoji) {
|
||||
ImageSpan($rawfile(`emoji/${item.emoji.file}`)).width('18')
|
||||
.height('18')
|
||||
.objectFit(ImageFit.Fill)
|
||||
.verticalAlign(ImageSpanAlignment.CENTER)
|
||||
}
|
||||
})
|
||||
}
|
||||
.fontColor($r('app.color.color_chat_title'))
|
||||
.lineHeight(20)
|
||||
.fontSize($r('app.float.chat_subtitle_text_font_size'))
|
||||
.maxLines(3)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ComponentV2
|
||||
export struct imageBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
@Local thumbUrl?: string
|
||||
@Local url?: string
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Image(this.thumbUrl ?? this.url)
|
||||
.objectFit(ImageFit.Cover)
|
||||
.width(getImageWidth(this.message))
|
||||
.height(getImageHeight(this.message))
|
||||
.borderRadius(8)
|
||||
.gesture(LongPressGesture().onAction((event: GestureEvent) => {
|
||||
this.onMessageClick?.onItemLongClick?.(event, this.message)
|
||||
}), GestureMask.IgnoreInternal)
|
||||
}
|
||||
}
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.url = this.message.getImageUrl()
|
||||
this.message.getImageThumbUrl().then((thumbUrl) => {
|
||||
this.thumbUrl = thumbUrl
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ComponentV2
|
||||
export struct videoBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
@Local videoMessageThumbnail?: image.PixelMap
|
||||
|
||||
showLoading() {
|
||||
if ((this.message.message.attachment as V2NIMMessageFileAttachment).path === undefined) {
|
||||
if (this.message.downloadProgress > 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
async aboutToAppear(): Promise<void> {
|
||||
this.videoMessageThumbnail = await getVideoMessageThumbnail(this.message)
|
||||
}
|
||||
|
||||
build() {
|
||||
Stack() {
|
||||
Image(this.videoMessageThumbnail ?? getVideoMessageUrl(this.message)).objectFit(ImageFit.Auto)
|
||||
.width(getImageWidth(this.message)).height(getImageHeight(this.message))
|
||||
.borderRadius(8)
|
||||
Image($r('app.media.ic_chat_message_video')).objectFit(ImageFit.Auto)
|
||||
.width(60).height(60)
|
||||
.visibility(!this.showLoading() ? Visibility.Visible : Visibility.Hidden)
|
||||
|
||||
Row() {
|
||||
Column()
|
||||
.width(3)
|
||||
.height(18)
|
||||
.backgroundColor(Color.White)
|
||||
.borderRadius(3)
|
||||
Column()
|
||||
.width(3)
|
||||
.height(18)
|
||||
.backgroundColor(Color.White)
|
||||
.borderRadius(3)
|
||||
.margin({
|
||||
left: 6
|
||||
})
|
||||
}
|
||||
.width(12)
|
||||
.height(18)
|
||||
.backgroundColor(Color.Transparent)
|
||||
.visibility(this.showLoading() ? Visibility.Visible : Visibility.Hidden)
|
||||
|
||||
Progress({ value: 0, total: 100, type: ProgressType.Ring })
|
||||
.width(42)
|
||||
.height(42)
|
||||
.value(this.message.downloadProgress)
|
||||
.color(Color.White)// 进度条前景色为灰色
|
||||
.style({ strokeWidth: 3 })// 设置strokeWidth进度条宽度为15.0vp
|
||||
.visibility(this.showLoading() ? Visibility.Visible : Visibility.Hidden)
|
||||
}.gesture(LongPressGesture().onAction((event: GestureEvent) => {
|
||||
this.onMessageClick?.onItemLongClick?.(event, this.message)
|
||||
}), GestureMask.IgnoreInternal)
|
||||
}
|
||||
}
|
||||
|
||||
@ComponentV2
|
||||
export struct callBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
if (this.message.isReceiveMsg) {
|
||||
Image(getCallMessageIcon(this.message)).width(24).height(24).align(Alignment.Start)
|
||||
Text(getCallMessageText(this.message)).lineHeight(20).margin({ left: 6 })
|
||||
} else {
|
||||
Text(getCallMessageText(this.message)).lineHeight(20)
|
||||
.fontSize($r('app.float.chat_subtitle_text_font_size'))
|
||||
.fontColor($r('app.color.color_chat_title'))
|
||||
Image(getCallMessageIcon(this.message)).width(24).height(24).align(Alignment.Start).margin({ left: 6 })
|
||||
}
|
||||
}
|
||||
.padding({
|
||||
left: 12,
|
||||
top: 12,
|
||||
bottom: 12,
|
||||
right: 12
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ComponentV2
|
||||
export struct fileBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
Stack({}) {
|
||||
Image(getFileMessageIcon(this.message))
|
||||
.objectFit(ImageFit.Auto)
|
||||
.width(35)
|
||||
.height(35)
|
||||
.margin({ left: 12, top: 12, bottom: 12 })
|
||||
Column()
|
||||
.width(35)
|
||||
.height(35)
|
||||
.margin({ left: 12, top: 12, bottom: 12 })
|
||||
.backgroundColor($r('app.color.color_chat_converse_bg'))
|
||||
.opacity(0.8)
|
||||
.borderRadius(6)
|
||||
.visibility(this.message.downloadProgress >= 0 ? Visibility.Visible : Visibility.Hidden)
|
||||
Progress({ value: 0, total: 100, type: ProgressType.Ring })
|
||||
.width(20)
|
||||
.height(20)
|
||||
.value(this.message.downloadProgress)
|
||||
.margin({ left: 16, top: 16, bottom: 12 })
|
||||
.color(Color.White)// 进度条前景色为灰色
|
||||
.style({ strokeWidth: 3 })// 设置strokeWidth进度条宽度为15.0vp
|
||||
.visibility(this.message.downloadProgress >= 0 ? Visibility.Visible : Visibility.Hidden)
|
||||
}
|
||||
|
||||
Column() {
|
||||
Text(getFileMessageName(this.message))
|
||||
.fontColor($r('app.color.color_chat_title'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.textAlign(TextAlign.Start)
|
||||
.width('100%')
|
||||
.height(20)
|
||||
.maxLines(1)
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
Text(getFileMessageSize(this.message))
|
||||
.fontColor('#666666')
|
||||
.fontSize(10)
|
||||
.width('100%')
|
||||
.textAlign(TextAlign.Start)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.maxLines(1)
|
||||
.margin({ top: 2 })
|
||||
}.padding({ left: 12, right: 12 }).width('70%').align(Alignment.Start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ComponentV2
|
||||
export struct locationBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
@Local imagePex: image.PixelMap | undefined
|
||||
|
||||
aboutToAppear(): void {
|
||||
if (this.message) {
|
||||
|
||||
let attachment = this.message.message.attachment as V2NIMMessageLocationAttachment;
|
||||
let staticMapMarker: staticMap.StaticMapMarker = {
|
||||
location: {
|
||||
latitude: attachment.latitude,
|
||||
longitude: attachment.longitude
|
||||
},
|
||||
defaultIconSize: staticMap.IconSize.SMALL
|
||||
};
|
||||
let staticMapOptions: staticMap.StaticMapOptions = {
|
||||
location: {
|
||||
latitude: attachment.latitude,
|
||||
longitude: attachment.longitude
|
||||
},
|
||||
zoom: 15,
|
||||
imageWidth: 300,
|
||||
imageHeight: 100,
|
||||
markers: [staticMapMarker],
|
||||
};
|
||||
|
||||
// 获取静态图
|
||||
staticMap.getMapImage(staticMapOptions).then((value) => {
|
||||
this.imagePex = value;
|
||||
console.info("netease location Succeeded in getting image.");
|
||||
}).catch((error: BusinessError) => {
|
||||
console.info("netease location fail in getting image.", error.code, error.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Text(this.message?.message.text)
|
||||
.fontColor($r('app.color.color_chat_title'))
|
||||
.fontSize($r('app.float.chat_title_text_font_size'))
|
||||
.textAlign(TextAlign.Start)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.padding({ left: 12, top: 12, right: 12 })
|
||||
Text((this.message?.message.attachment as V2NIMMessageLocationAttachment).address)
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.textAlign(TextAlign.Start)
|
||||
.maxLines(1)
|
||||
.margin({ top: 6 })
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.padding({ left: 12, right: 12 })
|
||||
if (this.imagePex) {
|
||||
Image(this.imagePex).width('100%').height(88).margin({ top: 8 })
|
||||
} else {
|
||||
Image($r('app.media.ic_chat_location_default')).width('100%').height(88).margin({ top: 8 })
|
||||
}
|
||||
}.alignItems(HorizontalAlign.Start)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ComponentV2
|
||||
export struct pinLocationBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
@Local imagePex: image.PixelMap | undefined
|
||||
|
||||
aboutToAppear(): void {
|
||||
if (this.message) {
|
||||
|
||||
let attachment = this.message.message.attachment as V2NIMMessageLocationAttachment;
|
||||
let staticMapMarker: staticMap.StaticMapMarker = {
|
||||
location: {
|
||||
latitude: attachment.latitude,
|
||||
longitude: attachment.longitude
|
||||
},
|
||||
defaultIconSize: staticMap.IconSize.SMALL
|
||||
};
|
||||
let staticMapOptions: staticMap.StaticMapOptions = {
|
||||
location: {
|
||||
latitude: attachment.latitude,
|
||||
longitude: attachment.longitude
|
||||
},
|
||||
zoom: 15,
|
||||
imageWidth: 300,
|
||||
imageHeight: 100,
|
||||
markers: [staticMapMarker],
|
||||
};
|
||||
|
||||
// 获取静态图
|
||||
staticMap.getMapImage(staticMapOptions).then((value) => {
|
||||
this.imagePex = value;
|
||||
console.info("netease location Succeeded in getting image.");
|
||||
}).catch((error: BusinessError) => {
|
||||
console.info("netease location fail in getting image.", error.code, error.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Text(this.message?.message.text)
|
||||
.fontColor($r('app.color.color_chat_title'))
|
||||
.fontSize($r('app.float.chat_title_text_font_size'))
|
||||
.textAlign(TextAlign.Start)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.padding({ left: 12, top: 12, right: 12 })
|
||||
Text((this.message?.message.attachment as V2NIMMessageLocationAttachment).address)
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.textAlign(TextAlign.Start)
|
||||
.maxLines(1)
|
||||
.margin({ top: 6 })
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.padding({ left: 12, right: 12 })
|
||||
if (this.imagePex) {
|
||||
Image(this.imagePex).width('100%').height(88).margin({ top: 8 })
|
||||
} else {
|
||||
Image($r('app.media.ic_chat_location_default')).width('100%').height(88).margin({ top: 8 })
|
||||
}
|
||||
}.alignItems(HorizontalAlign.Start)
|
||||
.width('80%')
|
||||
}
|
||||
}
|
||||
|
||||
@ComponentV2
|
||||
export struct unknownBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Text($r('app.string.chat_msg_unknown_type'))
|
||||
.fontColor($r('app.color.color_chat_title'))
|
||||
.lineHeight(20)
|
||||
.fontSize($r('app.float.chat_subtitle_text_font_size'))
|
||||
}
|
||||
.padding({
|
||||
left: 12,
|
||||
top: 12,
|
||||
bottom: 12,
|
||||
right: 12
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ComponentV2
|
||||
export struct mergedMessageBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
mergedAttachment: MergedMessageAttachment | undefined
|
||||
|
||||
aboutToAppear(): void {
|
||||
if (this.message) {
|
||||
this.mergedAttachment = this.message.customAttachment as MergedMessageAttachment
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (this.mergedAttachment) {
|
||||
Text($r('app.string.chat_merged_message_title', this.mergedAttachment.sessionName))
|
||||
.fontColor($r('app.color.color_chat_title'))
|
||||
.fontSize($r('app.float.chat_title_text_font_size'))
|
||||
.height(18)
|
||||
.margin({ top: 6, bottom: 4 })
|
||||
.maxLines(1)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.CENTER)
|
||||
Text() {
|
||||
ForEach(parseText(this.message.mergedContent), (item: NEEmojiParseResult) => {
|
||||
if (item.text) {
|
||||
Span(item.text)
|
||||
.fontSize($r('app.float.chat_message_text_font_size'))
|
||||
.textCase(TextCase.Normal)
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
|
||||
} else if (item.emoji) {
|
||||
ImageSpan($rawfile(`emoji/${item.emoji.file}`)).width('18')
|
||||
.height('18')
|
||||
.objectFit(ImageFit.Fill)
|
||||
.verticalAlign(ImageSpanAlignment.CENTER)
|
||||
}
|
||||
})
|
||||
}
|
||||
.fontSize($r('app.float.chat_subtitle_text_font_size'))
|
||||
.maxLines(3)
|
||||
.lineHeight(20)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.ellipsisMode(EllipsisMode.END)
|
||||
.margin({ bottom: 6 })
|
||||
|
||||
Line().width('100%').height(1).backgroundColor($r('app.color.color_chat_divider_line'))
|
||||
Text($r('app.string.chat_merged_message_desc'))
|
||||
.fontColor($r('app.color.color_chat_desc'))
|
||||
.fontSize($r('app.float.chat_subtitle_text_font_size'))
|
||||
.height(18).margin({ top: 6, bottom: 6 })
|
||||
}
|
||||
}
|
||||
.margin({
|
||||
left: 4,
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
right: 4
|
||||
})
|
||||
.padding({ left: 12, right: 6 })
|
||||
.backgroundColor($r('app.color.color_chat_page_bg'))
|
||||
.borderRadius(12)
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ComponentV2
|
||||
export struct customBuilder {
|
||||
@BuilderParam
|
||||
message: NIMMessageInfo
|
||||
@BuilderParam
|
||||
onMessageClick: MessageItemClick | undefined
|
||||
@Local photoUrl:ResourceStr=$r('app.media.mine_logo')
|
||||
@Local title:string=''
|
||||
@Local content:string=''
|
||||
@Local customType:string=''
|
||||
@Local customUrl:string=''
|
||||
aboutToAppear(): void {
|
||||
if (this.message) {
|
||||
//
|
||||
let attachment = this.message.message.attachment as V2NIMMessageAttachment ;
|
||||
|
||||
let custom=JSON.parse(attachment.raw+'') as customAttachment
|
||||
this.title=custom.gdxz_title
|
||||
this.customType=custom.gdxz_type
|
||||
this.customUrl=custom.gdxz_url
|
||||
if(BasicConstant.CouTeach==custom.gdxz_type)
|
||||
{
|
||||
this.content='点击查看问题详情'
|
||||
this.photoUrl=$r('app.media.icon_chatting_file')
|
||||
}
|
||||
else if(BasicConstant.VisitTeach==custom.gdxz_type)
|
||||
{
|
||||
this.content='肝胆相照®肝胆病在线公共服务平台'
|
||||
this.photoUrl=$r('app.media.mine_logo')
|
||||
}
|
||||
else {
|
||||
if(custom.gdxz_content==null||custom.gdxz_content.trim()=='')
|
||||
{
|
||||
this.content='肝胆相照®肝胆病在线公共服务平台'
|
||||
}
|
||||
else
|
||||
{
|
||||
this.content=custom.gdxz_content
|
||||
}
|
||||
if(custom.gdxz_title!=null&&custom.gdxz_title.includes("互联网医院"))
|
||||
{
|
||||
this.photoUrl=custom.gdxz_img
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
this.photoUrl=$r('app.media.mine_logo')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Text(this.title)
|
||||
.fontColor($r('app.color.color_chat_title'))
|
||||
.fontSize($r('app.float.chat_message_text_font_size'))
|
||||
.textAlign(TextAlign.Start)
|
||||
.maxLines(2)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.padding({ left: 12, top: 12, right: 12 })
|
||||
Row()
|
||||
{
|
||||
Text(this.content)
|
||||
.fontColor($r('app.color.color_chat_sub_title'))
|
||||
.fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
.textAlign(TextAlign.Start)
|
||||
.maxLines(2)
|
||||
.textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.padding({ left: 12, top: 12, right: 12 })
|
||||
.layoutWeight(1)
|
||||
|
||||
Image(this.photoUrl).width(57)
|
||||
.height(57)
|
||||
.margin({ bottom: 12, top: 12, right: 12 })
|
||||
}
|
||||
.alignItems(VerticalAlign.Top)
|
||||
// Text((this.message?.message.attachment as V2NIMMessageLocationAttachment).address)
|
||||
// .fontColor($r('app.color.color_chat_desc'))
|
||||
// .fontSize($r('app.float.chat_desc_text_font_size'))
|
||||
// .textAlign(TextAlign.Start)
|
||||
// .maxLines(1)
|
||||
// .margin({ top: 6 })
|
||||
// .textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
// .padding({ left: 12, right: 12 })
|
||||
// if (this.imagePex) {
|
||||
// Image(this.imagePex).width('100%').height(88).margin({ top: 8 })
|
||||
// } else {
|
||||
// Image($r('app.media.ic_chat_location_default')).width('100%').height(88).margin({ top: 8 })
|
||||
// }
|
||||
}.alignItems(HorizontalAlign.Start)
|
||||
.onClick(()=>{
|
||||
if(BasicConstant.CouTeach==this.customType)
|
||||
{
|
||||
// Intent intent = new Intent(UIUtils.getContext(), PublicServiceDetailsActivity.class);
|
||||
// intent.putExtra("consultUuid", teachAttachment.getMessigeid());
|
||||
// intent.putExtra("isCloseAnswer", true);
|
||||
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
|
||||
// UIUtils.getContext().startActivity(intent);
|
||||
}
|
||||
else if(this.title!=null&&this.title.includes("互联网医院"))
|
||||
{
|
||||
// IWXAPI api = WXAPIFactory.createWXAPI(getActivity(), Constant.WeId);
|
||||
// WXLaunchMiniProgram.Req req = new WXLaunchMiniProgram.Req();
|
||||
// req.userName = "gh_9cd2fd72eb57"; // 填小程序原始id
|
||||
// req.path = "/Pages/yishi/index/index"; //拉起小程序页面的可带参路径,不填默认拉起小程序首页,对于小游戏,可以只传入 query 部分,来实现传参效果,如:传入 "?foo=bar"。
|
||||
// // req.miniprogramType = WXLaunchMiniProgram.Req.MINIPROGRAM_TYPE_PREVIEW;// 可选打开 开发版,体验版和正式版
|
||||
// api.sendReq(req);
|
||||
}
|
||||
else if (this.customUrl!=null)
|
||||
{
|
||||
|
||||
if (this.customUrl.includes("video")){
|
||||
// intent = new Intent(UIUtils.getContext(), VideoDetilActivity.class);
|
||||
// intent.putExtra("uuid", teachAttachment.getMessigeid());
|
||||
|
||||
}else {
|
||||
// intent = new Intent(UIUtils.getContext(), NewsDetailActivity.class);
|
||||
|
||||
}
|
||||
|
||||
if (this.customUrl.includes("outpatient_details")
|
||||
||this.customUrl.includes("wxPatient/index.htm#/outPatient")){
|
||||
// intent.putExtra("title", "门诊详情");
|
||||
ToWeb(this.customUrl,'门诊详情')
|
||||
}else if(this.customUrl.includes(BasicConstant.getNewWa))
|
||||
{
|
||||
ToWeb(this.customUrl,'纽娃复合营养素固体饮料')
|
||||
// intent.putExtra("title", UIUtils.getContext().getResources().getStringArray(R.array.chat_menu_shop)[0]);
|
||||
// intent.putExtra("flag", 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// intent.putExtra("newsTitle", teachAttachment.getTitle()+"");
|
||||
// intent.putExtra("kepuuuid", teachAttachment.getMessigeid());
|
||||
// intent.putExtra("title", "患教详情");
|
||||
// intent.putExtra("need_inside_share", "no");
|
||||
}
|
||||
if(this.customUrl.includes("dcsvip1imapp.cloopen.net:8888"))
|
||||
{
|
||||
// intent.putExtra("url",
|
||||
// ((TeachAttachment) message.getAttachment()).getUrl().split("http://dcsvip1imapp.cloopen.net:8888")[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// intent.putExtra("url", teachAttachment.getUrl());
|
||||
}
|
||||
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
|
||||
// UIUtils.getContext().startActivity(intent);
|
||||
}
|
||||
})
|
||||
.width('80%')
|
||||
}
|
||||
}
|
||||
function ToWeb(u:string,t:string)
|
||||
{
|
||||
let paramsInfo: DataWebModel = {
|
||||
url:u ,
|
||||
title:t
|
||||
|
||||
};
|
||||
router.pushUrl({
|
||||
url: 'pages/WebView/WebPage', // 目标url
|
||||
params: paramsInfo // 添加params属性,传递自定义参数
|
||||
})
|
||||
}
|
||||
|
||||
export interface customAttachment
|
||||
{
|
||||
|
||||
gdxz_title:string;//标题
|
||||
gdxz_url:string;//内容url
|
||||
gdxz_content:string;//内容
|
||||
gdxz_id:string;//id
|
||||
gdxz_img:string;//照片
|
||||
gdxz_type:string;//类型
|
||||
gdxz_ext_data:string;//备用字段,json字符串
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user