比较提交
没有共同的提交。de1c7e77e78667e22e0f8875093c5be10acfd176 和 930c8f36aeefc2d07f0e91ee4f91d70075a8a6ea 的历史完全不同。
de1c7e77e7
...
930c8f36ae
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,4 +8,3 @@ build/
|
|||||||
*.iml
|
*.iml
|
||||||
.idea/
|
.idea/
|
||||||
*.log
|
*.log
|
||||||
.hvigor/
|
|
||||||
|
|||||||
1
.hvigor/cache/file-cache.json
vendored
1
.hvigor/cache/file-cache.json
vendored
文件差异因一行或多行过长而隐藏
1
.hvigor/cache/meta.json
vendored
1
.hvigor/cache/meta.json
vendored
@ -1 +0,0 @@
|
|||||||
{"compileSdkVersion":"6.0.2(22)","hvigorVersion":"6.22.4","toolChainsVersion":"6.0.2.130"}
|
|
||||||
1
.hvigor/cache/task-cache.json
vendored
1
.hvigor/cache/task-cache.json
vendored
文件差异因一行或多行过长而隐藏
@ -1 +0,0 @@
|
|||||||
{"basePath":"/Users/xuqinmin/Projects/XuqmGroup/XuqmGroup-HarmonySDK/.hvigor/dependencyMap/dependencyMap.json5","rootDependency":"./oh-package.json5","dependencyMap":{"xuqmSdk":"./xuqmSdk/oh-package.json5","entry":"./entry/oh-package.json5"},"modules":[{"name":"xuqmSdk","srcPath":"../../../xuqm-sdk"},{"name":"entry","srcPath":"../../../entry"}]}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
{"name":"entry","version":"1.0.0","description":"SDK sample app","main":"","author":"","license":"MIT","dependencies":{"@xuqm/harmony-sdk":"file:../xuqm-sdk"}}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
{"name":"xuqm-harmony-sdk-workspace","version":"0.1.0","modelVersion":"5.0.0","description":"XuqmGroup HarmonyOS SDK workspace","author":"xuqm","license":"MIT"}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
{"name":"@xuqm/harmony-sdk","version":"0.1.0","description":"XuqmGroup HarmonyOS SDK — IM, Push, Version Management","main":"Index.ets","author":"xuqm","license":"MIT","publishConfig":{"registry":"https://ohpm.openharmony.cn/ohpm/"},"dependencies":{}}
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
{
|
|
||||||
"HVIGOR_OHOS_PLUGIN": {
|
|
||||||
"MODULES": [
|
|
||||||
{
|
|
||||||
"MODULE_NAME": "77aabe6c19463543339f337db9c84e4d10fd2f56ea0aedaf85a0214d59e93ec4",
|
|
||||||
"API_TYPE": "stageMode",
|
|
||||||
"INCLUDE_IN_BUILD": true,
|
|
||||||
"IS_COMMAND_LINE_ENTRY_MODULE": false,
|
|
||||||
"MODULE_TYPE": "har",
|
|
||||||
"IS_INCREMENTAL_MODULE": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"MODULE_NAME": "923fe53966c6cd9343e11af776cd4b05be315ea4b200b02e4d5dfb0f929b73bf",
|
|
||||||
"API_TYPE": "stageMode",
|
|
||||||
"INCLUDE_IN_BUILD": true,
|
|
||||||
"IS_COMMAND_LINE_ENTRY_MODULE": false,
|
|
||||||
"MODULE_TYPE": "entry",
|
|
||||||
"INCREMENTAL_TASKS": {
|
|
||||||
"COMPILE_ARKTS": true
|
|
||||||
},
|
|
||||||
"IS_INCREMENTAL_MODULE": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"NATIVE_COMPILER": "Default",
|
|
||||||
"IS_FULL_BUILD": false,
|
|
||||||
"BUILD_MODE": "debug"
|
|
||||||
},
|
|
||||||
"HVIGOR": {
|
|
||||||
"IS_INCREMENTAL": true,
|
|
||||||
"IS_DAEMON": false,
|
|
||||||
"IS_PARALLEL": true,
|
|
||||||
"IS_HVIGORFILE_TYPE_CHECK": false,
|
|
||||||
"TASK_TIME": {
|
|
||||||
"923fe53966c6cd9343e11af776cd4b05be315ea4b200b02e4d5dfb0f929b73bf": {
|
|
||||||
"CreateModuleInfo": 314458,
|
|
||||||
"PreCheckSyscap": 126708,
|
|
||||||
"ProcessIntegratedHsp": 218542,
|
|
||||||
"SyscapTransform": 12138250,
|
|
||||||
"ProcessStartupConfig": 988208,
|
|
||||||
"ConfigureCmake": 73375,
|
|
||||||
"BuildNativeWithCmake": 72625,
|
|
||||||
"BuildNativeWithNinja": 129375,
|
|
||||||
"BuildJS": 920292,
|
|
||||||
"CompileArkTS": 2834291708,
|
|
||||||
"ProcessCompiledResources": 207917,
|
|
||||||
"PackageHap": 234451500,
|
|
||||||
"PackingCheck": 2285000,
|
|
||||||
"SignHap": 493042,
|
|
||||||
"CollectDebugSymbol": 273250,
|
|
||||||
"assembleHap": 56166
|
|
||||||
},
|
|
||||||
"77aabe6c19463543339f337db9c84e4d10fd2f56ea0aedaf85a0214d59e93ec4": {
|
|
||||||
"ConfigureCmake": 64000,
|
|
||||||
"BuildNativeWithCmake": 77000,
|
|
||||||
"BuildNativeWithNinja": 510500
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"APIS": [
|
|
||||||
"getProperty"
|
|
||||||
],
|
|
||||||
"CONFIG_EXPERIMENT": {
|
|
||||||
"ENABLE_MODULE_SKIP": false,
|
|
||||||
"ENABLE_CPP_FUNCTION_LEVEL_INCREMENTAL": false
|
|
||||||
},
|
|
||||||
"CONFIG_PROPERTIES": {},
|
|
||||||
"BUILD_ID": "202604282229113860",
|
|
||||||
"TOTAL_TIME": 3981273167
|
|
||||||
}
|
|
||||||
}
|
|
||||||
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
文件差异内容过多而无法显示
加载差异
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"app": {
|
|
||||||
"bundleName": "com.xuqmgroup.harmony.sdk",
|
|
||||||
"vendor": "xuqm",
|
|
||||||
"versionCode": 1000000,
|
|
||||||
"versionName": "1.0.0",
|
|
||||||
"icon": "$media:app_icon",
|
|
||||||
"label": "$string:EntryAbility_label"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
# XuqmGroup HarmonyOS SDK 文档
|
# XuqmGroup HarmonyOS SDK 文档
|
||||||
|
|
||||||
> ArkTS · HarmonyOS 5 (API 12) · 发布至 ohpm
|
> ArkTS · HarmonyOS 5 (API 12) · 发布至 ohpm
|
||||||
> 当前工程已可成功执行 `hvigorw assembleHap`
|
|
||||||
|
|
||||||
## 模块结构
|
## 模块结构
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,6 @@
|
|||||||
{
|
{
|
||||||
"app": {
|
"app": {
|
||||||
"signingConfigs": [
|
"signingConfigs": [],
|
||||||
{
|
|
||||||
"name": "default",
|
|
||||||
"type": "HarmonyOS",
|
|
||||||
"material": {
|
|
||||||
"certpath": "/Users/xuqinmin/.ohos/config/default_XuqmGroup-HarmonySDK_xvtPiZRWbWuJ1guqtszanEOmoD8f2kda5ume6x7cDEg=.cer",
|
|
||||||
"keyAlias": "debugKey",
|
|
||||||
"keyPassword": "0000001BAEFFFD913AED357759CBA7F92502E226B07F444BBB101F06934FC8D4A84C1B5561BC0253532FCC",
|
|
||||||
"profile": "/Users/xuqinmin/.ohos/config/default_XuqmGroup-HarmonySDK_xvtPiZRWbWuJ1guqtszanEOmoD8f2kda5ume6x7cDEg=.p7b",
|
|
||||||
"signAlg": "SHA256withECDSA",
|
|
||||||
"storeFile": "/Users/xuqinmin/.ohos/config/default_XuqmGroup-HarmonySDK_xvtPiZRWbWuJ1guqtszanEOmoD8f2kda5ume6x7cDEg=.p12",
|
|
||||||
"storePassword": "0000001B822DC06852C049293C0D7A3A5ED4C040F995F0DE8E832EA7C75D5C0B9A063C501AE3B14CE6B900"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"products": [
|
"products": [
|
||||||
{
|
{
|
||||||
"name": "default",
|
"name": "default",
|
||||||
@ -26,42 +12,27 @@
|
|||||||
"caseSensitiveCheck": true,
|
"caseSensitiveCheck": true,
|
||||||
"useNormalizedOHMUrl": true
|
"useNormalizedOHMUrl": true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"targetSdkVersion": "6.0.2(22)"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"buildModeSet": [
|
"buildModeSet": [
|
||||||
{
|
{ "name": "debug" },
|
||||||
"name": "debug"
|
{ "name": "release" }
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "release"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"modules": [
|
"modules": [
|
||||||
{
|
{
|
||||||
"name": "xuqmSdk",
|
"name": "xuqm-sdk",
|
||||||
"srcPath": "./xuqm-sdk",
|
"srcPath": "./xuqm-sdk",
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{ "name": "default", "applyToProducts": ["default"] }
|
||||||
"name": "default",
|
|
||||||
"applyToProducts": [
|
|
||||||
"default"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "entry",
|
"name": "entry",
|
||||||
"srcPath": "./entry",
|
"srcPath": "./entry",
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{ "name": "default", "applyToProducts": ["default"] }
|
||||||
"name": "default",
|
|
||||||
"applyToProducts": [
|
|
||||||
"default"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"meta": {
|
|
||||||
"stableOrder": true,
|
|
||||||
"enableUnifiedLockfile": false
|
|
||||||
},
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
|
|
||||||
"specifiers": {
|
|
||||||
"@xuqm/harmony-sdk@../xuqm-sdk": "@xuqm/harmony-sdk@../xuqm-sdk"
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@xuqm/harmony-sdk@../xuqm-sdk": {
|
|
||||||
"name": "@xuqm/harmony-sdk",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"resolved": "../xuqm-sdk",
|
|
||||||
"registryType": "local"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../../../xuqm-sdk
|
|
||||||
@ -1,42 +1,6 @@
|
|||||||
import { XuqmSDK, ImMessage } from '@xuqm/harmony-sdk'
|
import { XuqmSDK, ImMessage } from '@xuqm/harmony-sdk'
|
||||||
import type { ImEventDelegate } from '@xuqm/harmony-sdk'
|
|
||||||
import promptAction from '@ohos.promptAction'
|
import promptAction from '@ohos.promptAction'
|
||||||
|
|
||||||
class DemoImDelegate implements ImEventDelegate {
|
|
||||||
private readonly onConnectedAction: () => void
|
|
||||||
private readonly onDisconnectedAction: (code: number, reason: string) => void
|
|
||||||
private readonly onMessageAction: (msg: ImMessage) => void
|
|
||||||
private readonly onErrorAction: (message: string) => void
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
onConnectedAction: () => void,
|
|
||||||
onDisconnectedAction: (code: number, reason: string) => void,
|
|
||||||
onMessageAction: (msg: ImMessage) => void,
|
|
||||||
onErrorAction: (message: string) => void,
|
|
||||||
) {
|
|
||||||
this.onConnectedAction = onConnectedAction
|
|
||||||
this.onDisconnectedAction = onDisconnectedAction
|
|
||||||
this.onMessageAction = onMessageAction
|
|
||||||
this.onErrorAction = onErrorAction
|
|
||||||
}
|
|
||||||
|
|
||||||
onConnected(): void {
|
|
||||||
this.onConnectedAction()
|
|
||||||
}
|
|
||||||
|
|
||||||
onDisconnected(code: number, reason: string): void {
|
|
||||||
this.onDisconnectedAction(code, reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMessage(msg: ImMessage): void {
|
|
||||||
this.onMessageAction(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
onError(message: string): void {
|
|
||||||
this.onErrorAction(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entry
|
@Entry
|
||||||
@Component
|
@Component
|
||||||
struct Index {
|
struct Index {
|
||||||
@ -47,23 +11,22 @@ struct Index {
|
|||||||
|
|
||||||
aboutToAppear(): void {
|
aboutToAppear(): void {
|
||||||
const im = XuqmSDK.im
|
const im = XuqmSDK.im
|
||||||
const delegate = new DemoImDelegate(
|
im.delegate = {
|
||||||
() => {
|
onConnected: () => {
|
||||||
this.connected = true
|
this.connected = true
|
||||||
promptAction.showToast({ message: 'IM 已连接' })
|
promptAction.showToast({ message: 'IM 已连接' })
|
||||||
},
|
},
|
||||||
(code: number, reason: string) => {
|
onDisconnected: (code, reason) => {
|
||||||
this.connected = false
|
this.connected = false
|
||||||
console.log('IM disconnected: ' + code + ' ' + reason)
|
console.log(`IM disconnected: ${code} ${reason}`)
|
||||||
},
|
},
|
||||||
(msg: ImMessage) => {
|
onMessage: (msg) => {
|
||||||
this.messages = [...this.messages, msg]
|
this.messages = [...this.messages, msg]
|
||||||
},
|
},
|
||||||
(err: string) => {
|
onError: (err) => {
|
||||||
promptAction.showToast({ message: 'IM 错误: ' + err })
|
promptAction.showToast({ message: 'IM 错误: ' + err })
|
||||||
},
|
},
|
||||||
)
|
}
|
||||||
im.delegate = delegate
|
|
||||||
im.connect()
|
im.connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
二进制文件未显示。
|
之前 宽度: | 高度: | 大小: 68 B |
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"modelVersion": "5.0.0",
|
|
||||||
"dependencies": {},
|
|
||||||
"execution": {
|
|
||||||
"daemon": false,
|
|
||||||
"incremental": true,
|
|
||||||
"parallel": true,
|
|
||||||
"typeCheck": false
|
|
||||||
},
|
|
||||||
"logging": {
|
|
||||||
"level": "info"
|
|
||||||
},
|
|
||||||
"debugging": {
|
|
||||||
"stacktrace": false
|
|
||||||
},
|
|
||||||
"nodeOptions": {
|
|
||||||
"maxOldSpaceSize": 4096
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "xuqm-harmony-sdk-workspace",
|
"name": "xuqm-harmony-sdk-workspace",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"modelVersion": "5.0.0",
|
|
||||||
"description": "XuqmGroup HarmonyOS SDK workspace",
|
"description": "XuqmGroup HarmonyOS SDK workspace",
|
||||||
"author": "xuqm",
|
"author": "xuqm",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
|||||||
@ -1,48 +0,0 @@
|
|||||||
{
|
|
||||||
"lockVersion": "1.0",
|
|
||||||
"settings": {
|
|
||||||
"resolveConflict": true,
|
|
||||||
"resolveConflictStrict": false,
|
|
||||||
"installAll": true
|
|
||||||
},
|
|
||||||
"overrides": {},
|
|
||||||
"overrideDependencyMap": {},
|
|
||||||
"modules": {
|
|
||||||
".": {
|
|
||||||
"name": "",
|
|
||||||
"dependencies": {},
|
|
||||||
"devDependencies": {},
|
|
||||||
"dynamicDependencies": {},
|
|
||||||
"maskedByOverrideDependencyMap": false
|
|
||||||
},
|
|
||||||
"entry": {
|
|
||||||
"name": "entry",
|
|
||||||
"dependencies": {
|
|
||||||
"@xuqm/harmony-sdk": {
|
|
||||||
"specifier": "file:xuqm-sdk",
|
|
||||||
"version": "file:xuqm-sdk"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"devDependencies": {},
|
|
||||||
"dynamicDependencies": {},
|
|
||||||
"maskedByOverrideDependencyMap": false
|
|
||||||
},
|
|
||||||
"xuqm-sdk": {
|
|
||||||
"name": "xuqmSdk",
|
|
||||||
"dependencies": {},
|
|
||||||
"devDependencies": {},
|
|
||||||
"dynamicDependencies": {},
|
|
||||||
"maskedByOverrideDependencyMap": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"packages": {
|
|
||||||
"@xuqm/harmony-sdk@file:xuqm-sdk": {
|
|
||||||
"storePath": "xuqm-sdk",
|
|
||||||
"dependencies": {},
|
|
||||||
"dynamicDependencies": {},
|
|
||||||
"dev": false,
|
|
||||||
"dynamic": false,
|
|
||||||
"maskedByOverrideDependencyMap": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* Use these variables when you tailor your ArkTS code. They must be of the const type.
|
|
||||||
*/
|
|
||||||
export const HAR_VERSION = '0.1.0';
|
|
||||||
export const BUILD_MODE_NAME = 'debug';
|
|
||||||
export const DEBUG = true;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@ -15,9 +15,6 @@ export type {
|
|||||||
ChatType,
|
ChatType,
|
||||||
MsgStatus,
|
MsgStatus,
|
||||||
ConversationData,
|
ConversationData,
|
||||||
FriendRequest,
|
|
||||||
GroupJoinRequest,
|
|
||||||
ImGroup,
|
|
||||||
HistoryQuery,
|
HistoryQuery,
|
||||||
PageResult,
|
PageResult,
|
||||||
UserProfile,
|
UserProfile,
|
||||||
|
|||||||
@ -1,182 +0,0 @@
|
|||||||
/**
|
|
||||||
* XuqmGroup Update Service — HarmonyOS hvigorw Release Task
|
|
||||||
*
|
|
||||||
* HarmonyOS projects use hvigorw (Huawei's Gradle wrapper).
|
|
||||||
* This script registers a task compatible with both hvigorw and standard Gradle.
|
|
||||||
*
|
|
||||||
* Copy to your HarmonyOS module directory and apply:
|
|
||||||
* apply(from = "xuqm_release.gradle.kts") // in build.gradle.kts
|
|
||||||
*
|
|
||||||
* Run:
|
|
||||||
* ./hvigorw xuqmRelease --mode project
|
|
||||||
*
|
|
||||||
* Config: xuqm.properties in the module or project root
|
|
||||||
* ---
|
|
||||||
* xuqm.serverUrl=https://update.dev.xuqinmin.com
|
|
||||||
* xuqm.appId=your-app-id
|
|
||||||
* xuqm.apiToken=your-api-token
|
|
||||||
* xuqm.storeTargets=HUAWEI # optional: HUAWEI,HONOR,...
|
|
||||||
* xuqm.autoPublishAfterReview=false
|
|
||||||
* xuqm.scheduledPublishAt= # optional ISO datetime
|
|
||||||
* xuqm.webhookUrl= # optional
|
|
||||||
* ---
|
|
||||||
*
|
|
||||||
* Version is read from AppScope/app.json5 (standard HarmonyOS project layout).
|
|
||||||
*/
|
|
||||||
|
|
||||||
import org.gradle.api.GradleException
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URI
|
|
||||||
import java.net.http.HttpClient
|
|
||||||
import java.net.http.HttpRequest
|
|
||||||
import java.net.http.HttpResponse
|
|
||||||
import java.util.Properties
|
|
||||||
import java.util.UUID
|
|
||||||
import java.util.regex.Pattern
|
|
||||||
|
|
||||||
// ── Config ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
fun loadXuqmCfg(projectDir: File): Properties {
|
|
||||||
val props = Properties()
|
|
||||||
listOf(File(projectDir, "xuqm.properties"), File(projectDir.parentFile, "xuqm.properties"))
|
|
||||||
.firstOrNull { it.exists() }?.inputStream()?.use(props::load)
|
|
||||||
?: throw GradleException("xuqm.properties not found")
|
|
||||||
return props
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Version from app.json5 ───────────────────────────────────────────────
|
|
||||||
|
|
||||||
data class HarmonyVersion(val name: String, val code: Int)
|
|
||||||
|
|
||||||
fun readHarmonyVersion(projectDir: File): HarmonyVersion {
|
|
||||||
val json5 = listOf(
|
|
||||||
File(projectDir, "AppScope/app.json5"),
|
|
||||||
File(projectDir.parentFile, "AppScope/app.json5"),
|
|
||||||
).firstOrNull { it.exists() } ?: throw GradleException("AppScope/app.json5 not found")
|
|
||||||
|
|
||||||
val content = json5.readText()
|
|
||||||
val name = Regex(""""versionName"\s*:\s*"([^"]+)"""").find(content)?.groupValues?.get(1)
|
|
||||||
?: throw GradleException("versionName not found in app.json5")
|
|
||||||
val code = Regex(""""versionCode"\s*:\s*(\d+)""").find(content)?.groupValues?.get(1)?.toInt()
|
|
||||||
?: throw GradleException("versionCode not found in app.json5")
|
|
||||||
return HarmonyVersion(name, code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── HTTP helpers ──────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
fun httpGet(url: String, token: String): String {
|
|
||||||
val client = HttpClient.newHttpClient()
|
|
||||||
val req = HttpRequest.newBuilder(URI.create(url)).header("Authorization", "Bearer $token").GET().build()
|
|
||||||
return client.send(req, HttpResponse.BodyHandlers.ofString()).body()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun httpMultipartPost(url: String, token: String, parts: Map<String, Any>): String {
|
|
||||||
val boundary = UUID.randomUUID().toString()
|
|
||||||
val baos = java.io.ByteArrayOutputStream()
|
|
||||||
fun writeln(s: String) = baos.write("$s\r\n".toByteArray())
|
|
||||||
for ((name, value) in parts) {
|
|
||||||
writeln("--$boundary")
|
|
||||||
when (value) {
|
|
||||||
is File -> {
|
|
||||||
writeln("""Content-Disposition: form-data; name="$name"; filename="${value.name}"""")
|
|
||||||
writeln("Content-Type: application/octet-stream"); writeln("")
|
|
||||||
baos.write(value.readBytes()); writeln("")
|
|
||||||
}
|
|
||||||
else -> { writeln("""Content-Disposition: form-data; name="$name""""); writeln(""); writeln(value.toString()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeln("--$boundary--")
|
|
||||||
val client = HttpClient.newHttpClient()
|
|
||||||
val req = HttpRequest.newBuilder(URI.create(url))
|
|
||||||
.header("Authorization", "Bearer $token")
|
|
||||||
.header("Content-Type", "multipart/form-data; boundary=$boundary")
|
|
||||||
.POST(HttpRequest.BodyPublishers.ofByteArray(baos.toByteArray()))
|
|
||||||
.build()
|
|
||||||
return client.send(req, HttpResponse.BodyHandlers.ofString()).body()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseJson(json: String, key: String) =
|
|
||||||
Regex(""""$key"\s*:\s*"([^"]+)"""").find(json)?.groupValues?.get(1)
|
|
||||||
|
|
||||||
// ── Task ──────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
tasks.register("xuqmRelease") {
|
|
||||||
group = "xuqm"
|
|
||||||
description = "Build HAP and upload to XuqmGroup Update Service"
|
|
||||||
|
|
||||||
// hvigorw uses 'assembleApp' by default; adjust if your task is named differently
|
|
||||||
dependsOn(tasks.findByName("assembleApp") ?: tasks.findByName("default") ?: return@register)
|
|
||||||
|
|
||||||
doLast {
|
|
||||||
val cfg = loadXuqmCfg(projectDir)
|
|
||||||
val serverUrl = cfg.getProperty("xuqm.serverUrl") ?: throw GradleException("xuqm.serverUrl missing")
|
|
||||||
val appId = cfg.getProperty("xuqm.appId") ?: throw GradleException("xuqm.appId missing")
|
|
||||||
val apiToken = cfg.getProperty("xuqm.apiToken") ?: throw GradleException("xuqm.apiToken missing")
|
|
||||||
val storeTargets = cfg.getProperty("xuqm.storeTargets", "")
|
|
||||||
val autoPublish = cfg.getProperty("xuqm.autoPublishAfterReview", "false").toBoolean()
|
|
||||||
val scheduledAt = cfg.getProperty("xuqm.scheduledPublishAt", "")
|
|
||||||
val webhookUrl = cfg.getProperty("xuqm.webhookUrl", "")
|
|
||||||
|
|
||||||
// ── 1. Read local version ──────────────────────────────────────────
|
|
||||||
val (versionName, versionCode) = readHarmonyVersion(projectDir)
|
|
||||||
println("[xuqm] Local version: $versionName ($versionCode)")
|
|
||||||
|
|
||||||
// ── 2. Check server ────────────────────────────────────────────────
|
|
||||||
val listResp = httpGet("$serverUrl/api/v1/updates/app/list?appId=$appId&platform=ANDROID", apiToken)
|
|
||||||
val serverCode = Regex(""""versionCode"\s*:\s*(\d+)""").findAll(listResp)
|
|
||||||
.mapNotNull { it.groupValues[1].toIntOrNull() }.maxOrNull() ?: 0
|
|
||||||
println("[xuqm] Server latest versionCode: $serverCode")
|
|
||||||
|
|
||||||
if (versionCode <= serverCode) {
|
|
||||||
throw GradleException(
|
|
||||||
"[xuqm] Local versionCode ($versionCode) ≤ server ($serverCode). " +
|
|
||||||
"Bump versionCode in AppScope/app.json5 first."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 3. Locate HAP ──────────────────────────────────────────────────
|
|
||||||
val hapDir = File(projectDir, "entry/build/default/outputs/default")
|
|
||||||
val hapFile = hapDir.listFiles { f -> f.extension == "hap" }?.firstOrNull()
|
|
||||||
?: throw GradleException("HAP not found in ${hapDir.absolutePath}")
|
|
||||||
println("[xuqm] HAP: ${hapFile.absolutePath}")
|
|
||||||
|
|
||||||
// ── 4. Upload ──────────────────────────────────────────────────────
|
|
||||||
val parts = mutableMapOf<String, Any>(
|
|
||||||
"appId" to appId, "platform" to "ANDROID",
|
|
||||||
"versionName" to versionName, "versionCode" to versionCode,
|
|
||||||
"forceUpdate" to "false", "autoPublishAfterReview" to autoPublish.toString(),
|
|
||||||
"apkFile" to hapFile,
|
|
||||||
)
|
|
||||||
if (storeTargets.isNotBlank()) parts["storeSubmitTargets"] = "[\"${storeTargets.split(",").joinToString("\",\"")}\"]"
|
|
||||||
if (scheduledAt.isNotBlank()) parts["scheduledPublishAt"] = scheduledAt
|
|
||||||
if (webhookUrl.isNotBlank()) parts["webhookUrl"] = webhookUrl
|
|
||||||
|
|
||||||
println("[xuqm] Uploading HAP...")
|
|
||||||
val uploadResp = httpMultipartPost("$serverUrl/api/v1/updates/app/upload", apiToken, parts)
|
|
||||||
val versionId = parseJson(uploadResp, "id")
|
|
||||||
?: throw GradleException("[xuqm] Upload failed:\n$uploadResp")
|
|
||||||
println("[xuqm] Uploaded, version ID: $versionId")
|
|
||||||
|
|
||||||
// ── 5. Trigger store submission ────────────────────────────────────
|
|
||||||
if (storeTargets.isNotBlank()) {
|
|
||||||
println("[xuqm] Triggering store submission: $storeTargets")
|
|
||||||
val body = """{"storeTypes":[${storeTargets.split(",").joinToString(",") { "\"$it\"" }}]}"""
|
|
||||||
val client = HttpClient.newHttpClient()
|
|
||||||
val req = HttpRequest.newBuilder(URI.create("$serverUrl/api/v1/updates/store/app/$versionId/execute-submit"))
|
|
||||||
.header("Authorization", "Bearer $apiToken")
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(body)).build()
|
|
||||||
val storeResp = client.send(req, HttpResponse.BodyHandlers.ofString())
|
|
||||||
println("[xuqm] Store submission HTTP ${storeResp.statusCode()}")
|
|
||||||
}
|
|
||||||
|
|
||||||
println("[xuqm] Done.")
|
|
||||||
if (scheduledAt.isNotBlank()) {
|
|
||||||
println("[xuqm] Will publish at: $scheduledAt")
|
|
||||||
} else if (autoPublish) {
|
|
||||||
println("[xuqm] Will auto-publish after store reviews pass.")
|
|
||||||
} else {
|
|
||||||
println("[xuqm] Publish manually: POST $serverUrl/api/v1/updates/app/$versionId/publish")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,6 +2,8 @@ import common from '@ohos.app.ability.common'
|
|||||||
import type { SDKConfig } from './core/Types'
|
import type { SDKConfig } from './core/Types'
|
||||||
import { SDKContext } from './core/SDKContext'
|
import { SDKContext } from './core/SDKContext'
|
||||||
import { ImClient } from './im/ImClient'
|
import { ImClient } from './im/ImClient'
|
||||||
|
import { PushSDK } from './push/PushSDK'
|
||||||
|
import { UpdateSDK } from './update/UpdateSDK'
|
||||||
|
|
||||||
export class XuqmSDK {
|
export class XuqmSDK {
|
||||||
private static _imClient: ImClient | null = null
|
private static _imClient: ImClient | null = null
|
||||||
@ -33,4 +35,12 @@ export class XuqmSDK {
|
|||||||
}
|
}
|
||||||
return XuqmSDK._imClient
|
return XuqmSDK._imClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get push(): typeof PushSDK {
|
||||||
|
return PushSDK
|
||||||
|
}
|
||||||
|
|
||||||
|
static get update(): typeof UpdateSDK {
|
||||||
|
return UpdateSDK
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,30 +1,35 @@
|
|||||||
import http from '@ohos.net.http'
|
import http from '@ohos.net.http'
|
||||||
import type { ApiResponse, HttpHeaders } from './Types'
|
import type { ApiResponse } from './Types'
|
||||||
import { SDKContext } from './SDKContext'
|
import { SDKContext } from './SDKContext'
|
||||||
|
|
||||||
export class HttpClient {
|
export class HttpClient {
|
||||||
static async request<T>(
|
static async request<T>(
|
||||||
method: http.RequestMethod,
|
method: http.RequestMethod,
|
||||||
path: string,
|
path: string,
|
||||||
body?: Object,
|
body?: object,
|
||||||
query?: string,
|
query?: Record<string, string | number | boolean | Date | null | undefined>,
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const config = SDKContext.getConfig()
|
const config = SDKContext.getConfig()
|
||||||
const token = SDKContext.getToken()
|
const token = SDKContext.getToken()
|
||||||
const url = config.apiBaseUrl.replace(/\/$/, '') + path + (query ? '?' + query : '')
|
const queryPairs: string[] = []
|
||||||
|
if (query) {
|
||||||
|
for (const key of Object.keys(query)) {
|
||||||
|
const value = query[key]
|
||||||
|
if (value === undefined || value === null || value === '') continue
|
||||||
|
queryPairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(value instanceof Date ? value.toISOString() : String(value))}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const url = config.apiBaseUrl.replace(/\/$/, '') + path + (queryPairs.length > 0 ? `?${queryPairs.join('&')}` : '')
|
||||||
|
|
||||||
const client = http.createHttp()
|
const client = http.createHttp()
|
||||||
try {
|
try {
|
||||||
const header: HttpHeaders = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
}
|
|
||||||
if (token) {
|
|
||||||
header.Authorization = 'Bearer ' + token
|
|
||||||
}
|
|
||||||
const options: http.HttpRequestOptions = {
|
const options: http.HttpRequestOptions = {
|
||||||
method,
|
method,
|
||||||
header,
|
header: {
|
||||||
extraData: body ? JSON.stringify(body) : '',
|
'Content-Type': 'application/json',
|
||||||
|
...(token ? { 'Authorization': `Bearer ${token}` } : {}),
|
||||||
|
},
|
||||||
|
extraData: body ? JSON.stringify(body) : undefined,
|
||||||
connectTimeout: 15000,
|
connectTimeout: 15000,
|
||||||
readTimeout: 15000,
|
readTimeout: 15000,
|
||||||
}
|
}
|
||||||
@ -38,19 +43,19 @@ export class HttpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get<T>(path: string, query?: string): Promise<T> {
|
static get<T>(path: string, query?: Record<string, string | number | boolean | Date | null | undefined>): Promise<T> {
|
||||||
return HttpClient.request<T>(http.RequestMethod.GET, path, undefined, query)
|
return HttpClient.request<T>(http.RequestMethod.GET, path, undefined, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
static post<T>(path: string, body?: Object, query?: string): Promise<T> {
|
static post<T>(path: string, body?: object, query?: Record<string, string | number | boolean | Date | null | undefined>): Promise<T> {
|
||||||
return HttpClient.request<T>(http.RequestMethod.POST, path, body, query)
|
return HttpClient.request<T>(http.RequestMethod.POST, path, body, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
static put<T>(path: string, body?: Object, query?: string): Promise<T> {
|
static put<T>(path: string, body?: object, query?: Record<string, string | number | boolean | Date | null | undefined>): Promise<T> {
|
||||||
return HttpClient.request<T>(http.RequestMethod.PUT, path, body, query)
|
return HttpClient.request<T>(http.RequestMethod.PUT, path, body, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
static delete<T>(path: string, query?: string): Promise<T> {
|
static delete<T>(path: string, query?: Record<string, string | number | boolean | Date | null | undefined>): Promise<T> {
|
||||||
return HttpClient.request<T>(http.RequestMethod.DELETE, path, undefined, query)
|
return HttpClient.request<T>(http.RequestMethod.DELETE, path, undefined, query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,11 +13,6 @@ export interface ApiResponse<T> {
|
|||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HttpHeaders {
|
|
||||||
'Content-Type': string
|
|
||||||
Authorization?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MsgType =
|
export type MsgType =
|
||||||
| 'TEXT'
|
| 'TEXT'
|
||||||
| 'IMAGE'
|
| 'IMAGE'
|
||||||
@ -98,40 +93,6 @@ export interface UserProfile {
|
|||||||
createdAt?: number | null
|
createdAt?: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ImGroup {
|
|
||||||
id: string
|
|
||||||
appId?: string
|
|
||||||
name: string
|
|
||||||
groupType?: string
|
|
||||||
creatorId: string
|
|
||||||
memberIds: string
|
|
||||||
adminIds: string
|
|
||||||
announcement?: string | null
|
|
||||||
createdAt?: number | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FriendRequest {
|
|
||||||
id: string
|
|
||||||
appId?: string
|
|
||||||
fromUserId: string
|
|
||||||
toUserId: string
|
|
||||||
remark?: string | null
|
|
||||||
status: 'PENDING' | 'ACCEPTED' | 'REJECTED'
|
|
||||||
createdAt: number
|
|
||||||
reviewedAt?: number | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GroupJoinRequest {
|
|
||||||
id: string
|
|
||||||
appId?: string
|
|
||||||
groupId: string
|
|
||||||
requesterId: string
|
|
||||||
remark?: string | null
|
|
||||||
status: 'PENDING' | 'ACCEPTED' | 'REJECTED'
|
|
||||||
createdAt: number
|
|
||||||
reviewedAt?: number | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SendMessageParams {
|
export interface SendMessageParams {
|
||||||
messageId?: string
|
messageId?: string
|
||||||
toId: string
|
toId: string
|
||||||
|
|||||||
@ -1,25 +1,12 @@
|
|||||||
import webSocket from '@ohos.net.webSocket'
|
import webSocket from '@ohos.net.webSocket'
|
||||||
import { HttpClient } from '../core/HttpClient'
|
import { HttpClient } from '../core/HttpClient'
|
||||||
import { SDKContext } from '../core/SDKContext'
|
import { SDKContext } from '../core/SDKContext'
|
||||||
import type {
|
import type { ChatType, ConversationData, HistoryQuery, ImMessage, MsgType, PageResult, SendMessageParams, UserProfile } from '../core/Types'
|
||||||
ChatType,
|
|
||||||
ConversationData,
|
|
||||||
FriendRequest,
|
|
||||||
GroupJoinRequest,
|
|
||||||
HistoryQuery,
|
|
||||||
ImGroup,
|
|
||||||
ImMessage,
|
|
||||||
MsgType,
|
|
||||||
PageResult,
|
|
||||||
SendMessageParams,
|
|
||||||
UserProfile,
|
|
||||||
} from '../core/Types'
|
|
||||||
|
|
||||||
export interface ImEventDelegate {
|
export interface ImEventDelegate {
|
||||||
onConnected?(): void
|
onConnected?(): void
|
||||||
onDisconnected?(code: number, reason: string): void
|
onDisconnected?(code: number, reason: string): void
|
||||||
onMessage?(msg: ImMessage): void
|
onMessage?(msg: ImMessage): void
|
||||||
onRead?(msg: ImMessage): void
|
|
||||||
onRevoke?(data: RevokeData): void
|
onRevoke?(data: RevokeData): void
|
||||||
onError?(message: string): void
|
onError?(message: string): void
|
||||||
}
|
}
|
||||||
@ -29,93 +16,6 @@ export interface RevokeData {
|
|||||||
operatorId: string
|
operatorId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebSocketFrame {
|
|
||||||
type: string = ''
|
|
||||||
payload: Object = new Object()
|
|
||||||
}
|
|
||||||
|
|
||||||
class WebSocketEnvelope {
|
|
||||||
destination: string = ''
|
|
||||||
payload: Object = new Object()
|
|
||||||
}
|
|
||||||
|
|
||||||
class AppBody {
|
|
||||||
appId: string = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
class HistoryQueryParams {
|
|
||||||
appId: string = ''
|
|
||||||
page: number = 0
|
|
||||||
size: number = 0
|
|
||||||
msgType: MsgType = 'TEXT'
|
|
||||||
keyword: string = ''
|
|
||||||
startTime: string = ''
|
|
||||||
endTime: string = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConversationActionBody {
|
|
||||||
appId: string = ''
|
|
||||||
chatType: ChatType = 'SINGLE'
|
|
||||||
}
|
|
||||||
|
|
||||||
class FriendRequestQueryBody {
|
|
||||||
appId: string = ''
|
|
||||||
direction: 'incoming' | 'outgoing' = 'incoming'
|
|
||||||
}
|
|
||||||
|
|
||||||
class FriendRequestBody {
|
|
||||||
appId: string = ''
|
|
||||||
toUserId: string = ''
|
|
||||||
remark: string = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
class GroupJoinRequestBody {
|
|
||||||
appId: string = ''
|
|
||||||
remark: string = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
class EditMessageBody {
|
|
||||||
content: string = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
class UpdateProfileBody {
|
|
||||||
appId: string = ''
|
|
||||||
nickname: string = ''
|
|
||||||
avatar: string = ''
|
|
||||||
gender: string = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
class DraftBody {
|
|
||||||
appId: string = ''
|
|
||||||
chatType: ChatType = 'SINGLE'
|
|
||||||
draft: string = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
class PinBody {
|
|
||||||
appId: string = ''
|
|
||||||
chatType: ChatType = 'SINGLE'
|
|
||||||
pinned: boolean = false
|
|
||||||
}
|
|
||||||
|
|
||||||
class MuteBody {
|
|
||||||
appId: string = ''
|
|
||||||
chatType: ChatType = 'SINGLE'
|
|
||||||
muted: boolean = false
|
|
||||||
}
|
|
||||||
|
|
||||||
class SendEnvelopePayload {
|
|
||||||
messageId: string = ''
|
|
||||||
toId: string = ''
|
|
||||||
chatType: ChatType = 'SINGLE'
|
|
||||||
msgType: MsgType = 'TEXT'
|
|
||||||
content: string = ''
|
|
||||||
mentionedUserIds: string = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
class RevokeEnvelopePayload {
|
|
||||||
msgId: string = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const MAX_RECONNECT_DELAY = 30_000
|
const MAX_RECONNECT_DELAY = 30_000
|
||||||
|
|
||||||
export class ImClient {
|
export class ImClient {
|
||||||
@ -129,7 +29,7 @@ export class ImClient {
|
|||||||
if (this.destroyed) return
|
if (this.destroyed) return
|
||||||
const config = SDKContext.getConfig()
|
const config = SDKContext.getConfig()
|
||||||
const token = SDKContext.getToken() ?? ''
|
const token = SDKContext.getToken() ?? ''
|
||||||
const url = config.imBaseUrl.replace(/\/$/, '') + '/ws/im?token=' + encodeURIComponent(token)
|
const url = `${config.imBaseUrl}/ws/im?token=${token}`
|
||||||
|
|
||||||
this.ws = webSocket.createWebSocket()
|
this.ws = webSocket.createWebSocket()
|
||||||
|
|
||||||
@ -141,19 +41,10 @@ export class ImClient {
|
|||||||
|
|
||||||
this.ws.on('message', (_err: Error, value: string | ArrayBuffer) => {
|
this.ws.on('message', (_err: Error, value: string | ArrayBuffer) => {
|
||||||
try {
|
try {
|
||||||
if (typeof value !== 'string') {
|
const text = typeof value === 'string' ? value : new TextDecoder().decode(value)
|
||||||
return
|
const frame = JSON.parse(text) as { type: string; payload: unknown }
|
||||||
}
|
|
||||||
const frame = JSON.parse(value) as WebSocketFrame
|
|
||||||
if (frame.type === 'MESSAGE') {
|
if (frame.type === 'MESSAGE') {
|
||||||
const message = this.normalizeMessage(frame.payload as ImMessage)
|
this.delegate?.onMessage?.(this.normalizeMessage(frame.payload as ImMessage))
|
||||||
if (message.status === 'READ') {
|
|
||||||
this.delegate?.onRead?.(message)
|
|
||||||
}
|
|
||||||
if (message.revoked || message.status === 'REVOKED' || message.msgType === 'REVOKED') {
|
|
||||||
this.delegate?.onRevoke?.({ msgId: message.id, operatorId: message.fromId })
|
|
||||||
}
|
|
||||||
this.delegate?.onMessage?.(message)
|
|
||||||
} else if (frame.type === 'REVOKE') {
|
} else if (frame.type === 'REVOKE') {
|
||||||
this.delegate?.onRevoke?.(frame.payload as RevokeData)
|
this.delegate?.onRevoke?.(frame.payload as RevokeData)
|
||||||
}
|
}
|
||||||
@ -177,27 +68,23 @@ export class ImClient {
|
|||||||
send(params: SendMessageParams): ImMessage {
|
send(params: SendMessageParams): ImMessage {
|
||||||
const outgoing = this.buildOutgoingMessage(params)
|
const outgoing = this.buildOutgoingMessage(params)
|
||||||
if (!this.ws) {
|
if (!this.ws) {
|
||||||
return this.markFailed(outgoing)
|
return { ...outgoing, status: 'FAILED' }
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = new SendEnvelopePayload()
|
this.ws.send(
|
||||||
payload.messageId = outgoing.id
|
JSON.stringify({
|
||||||
payload.toId = params.toId
|
destination: '/app/chat.send',
|
||||||
payload.chatType = params.chatType
|
payload: {
|
||||||
payload.msgType = params.msgType
|
...params,
|
||||||
payload.content = params.content
|
messageId: outgoing.id,
|
||||||
if (params.mentionedUserIds !== undefined) {
|
},
|
||||||
payload.mentionedUserIds = params.mentionedUserIds
|
}),
|
||||||
}
|
(_err: Error) => {
|
||||||
const envelope = new WebSocketEnvelope()
|
|
||||||
envelope.destination = '/app/chat.send'
|
|
||||||
envelope.payload = payload
|
|
||||||
|
|
||||||
this.ws.send(JSON.stringify(envelope), (_err: Error) => {
|
|
||||||
if (_err) {
|
if (_err) {
|
||||||
this.delegate?.onError?.(_err.message)
|
this.delegate?.onError?.(_err.message)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
return outgoing
|
return outgoing
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,14 +92,15 @@ export class ImClient {
|
|||||||
if (!this.ws) {
|
if (!this.ws) {
|
||||||
throw new Error('WebSocket not connected')
|
throw new Error('WebSocket not connected')
|
||||||
}
|
}
|
||||||
const payload = new RevokeEnvelopePayload()
|
this.ws.send(
|
||||||
payload.msgId = msgId
|
JSON.stringify({
|
||||||
const envelope = new WebSocketEnvelope()
|
destination: '/app/chat.revoke',
|
||||||
envelope.destination = '/app/chat.revoke'
|
payload: { msgId },
|
||||||
envelope.payload = payload
|
}),
|
||||||
this.ws.send(JSON.stringify(envelope), (_err: Error) => {
|
(_err: Error) => {
|
||||||
if (_err) this.delegate?.onError?.(_err.message)
|
if (_err) this.delegate?.onError?.(_err.message)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchHistory(
|
async fetchHistory(
|
||||||
@ -221,8 +109,19 @@ export class ImClient {
|
|||||||
size: number = 20,
|
size: number = 20,
|
||||||
query: HistoryQuery = {},
|
query: HistoryQuery = {},
|
||||||
): Promise<PageResult<ImMessage>> {
|
): Promise<PageResult<ImMessage>> {
|
||||||
const queryString = this.buildHistoryQuery(page, size, query)
|
return HttpClient.get<PageResult<ImMessage>>(`/api/im/messages/history/${encodeURIComponent(toId)}`, {
|
||||||
return HttpClient.get<PageResult<ImMessage>>('/api/im/messages/history/' + encodeURIComponent(toId), queryString)
|
appId: SDKContext.getConfig().appKey,
|
||||||
|
page,
|
||||||
|
size,
|
||||||
|
msgType: query.msgType,
|
||||||
|
keyword: query.keyword,
|
||||||
|
startTime: query.startTime instanceof Date
|
||||||
|
? this.formatDateTime(query.startTime)
|
||||||
|
: query.startTime,
|
||||||
|
endTime: query.endTime instanceof Date
|
||||||
|
? this.formatDateTime(query.endTime)
|
||||||
|
: query.endTime,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchGroupHistory(
|
async fetchGroupHistory(
|
||||||
@ -231,208 +130,55 @@ export class ImClient {
|
|||||||
size: number = 50,
|
size: number = 50,
|
||||||
query: HistoryQuery = {},
|
query: HistoryQuery = {},
|
||||||
): Promise<PageResult<ImMessage>> {
|
): Promise<PageResult<ImMessage>> {
|
||||||
const queryString = this.buildHistoryQuery(page, size, query)
|
return HttpClient.get<PageResult<ImMessage>>(`/api/im/messages/group-history/${encodeURIComponent(groupId)}`, {
|
||||||
return HttpClient.get<PageResult<ImMessage>>('/api/im/messages/group-history/' + encodeURIComponent(groupId), queryString)
|
appId: SDKContext.getConfig().appKey,
|
||||||
}
|
page,
|
||||||
|
size,
|
||||||
async locateHistoryPage(
|
msgType: query.msgType,
|
||||||
toId: string,
|
keyword: query.keyword,
|
||||||
messageId: string,
|
startTime: query.startTime instanceof Date
|
||||||
pageSize: number = 20,
|
? this.formatDateTime(query.startTime)
|
||||||
maxPages: number = 20,
|
: query.startTime,
|
||||||
): Promise<ImMessage[] | null> {
|
endTime: query.endTime instanceof Date
|
||||||
const pageCount = Math.max(maxPages, 1)
|
? this.formatDateTime(query.endTime)
|
||||||
for (let page = 0; page < pageCount; page += 1) {
|
: query.endTime,
|
||||||
const result = await this.fetchHistory(toId, page, pageSize)
|
})
|
||||||
const messages = result.content ?? []
|
|
||||||
if (messages.some(item => item.id === messageId)) {
|
|
||||||
return messages
|
|
||||||
}
|
|
||||||
if (messages.length < pageSize) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
async locateGroupHistoryPage(
|
|
||||||
groupId: string,
|
|
||||||
messageId: string,
|
|
||||||
pageSize: number = 50,
|
|
||||||
maxPages: number = 20,
|
|
||||||
): Promise<ImMessage[] | null> {
|
|
||||||
const pageCount = Math.max(maxPages, 1)
|
|
||||||
for (let page = 0; page < pageCount; page += 1) {
|
|
||||||
const result = await this.fetchGroupHistory(groupId, page, pageSize)
|
|
||||||
const messages = result.content ?? []
|
|
||||||
if (messages.some(item => item.id === messageId)) {
|
|
||||||
return messages
|
|
||||||
}
|
|
||||||
if (messages.length < pageSize) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async listConversations(size: number = 20): Promise<ConversationData[]> {
|
async listConversations(size: number = 20): Promise<ConversationData[]> {
|
||||||
return HttpClient.get<ConversationData[]>('/api/im/conversations', this.buildConversationQuery(size))
|
return HttpClient.get<ConversationData[]>('/api/im/conversations', {
|
||||||
|
appId: SDKContext.getConfig().appKey,
|
||||||
|
page: 0,
|
||||||
|
size,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async markRead(targetId: string, chatType: ChatType = 'SINGLE'): Promise<void> {
|
async markRead(targetId: string, chatType: ChatType = 'SINGLE'): Promise<void> {
|
||||||
await HttpClient.put<void>('/api/im/conversations/' + encodeURIComponent(targetId) + '/read', undefined, this.buildConversationActionQuery(chatType))
|
await HttpClient.put<void>(`/api/im/conversations/${encodeURIComponent(targetId)}/read`, undefined, {
|
||||||
|
appId: SDKContext.getConfig().appKey,
|
||||||
|
chatType,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async setDraft(targetId: string, chatType: ChatType, draft: string): Promise<void> {
|
async setDraft(targetId: string, chatType: ChatType, draft: string): Promise<void> {
|
||||||
const params = new DraftBody()
|
await HttpClient.put<void>(`/api/im/conversations/${encodeURIComponent(targetId)}/draft`, undefined, {
|
||||||
params.appId = SDKContext.getConfig().appKey
|
appId: SDKContext.getConfig().appKey,
|
||||||
params.chatType = chatType
|
chatType,
|
||||||
params.draft = draft
|
draft,
|
||||||
await HttpClient.put<void>('/api/im/conversations/' + encodeURIComponent(targetId) + '/draft', params)
|
})
|
||||||
}
|
|
||||||
|
|
||||||
async setConversationPinned(targetId: string, chatType: ChatType, pinned: boolean): Promise<void> {
|
|
||||||
const params = new PinBody()
|
|
||||||
params.appId = SDKContext.getConfig().appKey
|
|
||||||
params.chatType = chatType
|
|
||||||
params.pinned = pinned
|
|
||||||
await HttpClient.put<void>('/api/im/conversations/' + encodeURIComponent(targetId) + '/pinned', params)
|
|
||||||
}
|
|
||||||
|
|
||||||
async setConversationMuted(targetId: string, chatType: ChatType, muted: boolean): Promise<void> {
|
|
||||||
const params = new MuteBody()
|
|
||||||
params.appId = SDKContext.getConfig().appKey
|
|
||||||
params.chatType = chatType
|
|
||||||
params.muted = muted
|
|
||||||
await HttpClient.put<void>('/api/im/conversations/' + encodeURIComponent(targetId) + '/muted', params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteConversation(targetId: string, chatType: ChatType): Promise<void> {
|
async deleteConversation(targetId: string, chatType: ChatType): Promise<void> {
|
||||||
await HttpClient.delete<void>('/api/im/conversations/' + encodeURIComponent(targetId), this.buildConversationActionQuery(chatType))
|
await HttpClient.delete<void>(`/api/im/conversations/${encodeURIComponent(targetId)}`, {
|
||||||
}
|
appId: SDKContext.getConfig().appKey,
|
||||||
|
chatType,
|
||||||
async listFriends(): Promise<string[]> {
|
})
|
||||||
return HttpClient.get<string[]>('/api/im/friends', this.buildAppQuery())
|
|
||||||
}
|
|
||||||
|
|
||||||
async listGroups(): Promise<ImGroup[]> {
|
|
||||||
return HttpClient.get<ImGroup[]>('/api/im/groups', this.buildAppQuery())
|
|
||||||
}
|
|
||||||
|
|
||||||
async getGroupInfo(groupId: string): Promise<ImGroup> {
|
|
||||||
return HttpClient.get<ImGroup>('/api/im/groups/' + encodeURIComponent(groupId), this.buildAppQuery())
|
|
||||||
}
|
|
||||||
|
|
||||||
async listGroupMembers(groupId: string): Promise<UserProfile[]> {
|
|
||||||
return HttpClient.get<UserProfile[]>(
|
|
||||||
'/api/im/groups/' + encodeURIComponent(groupId) + '/members',
|
|
||||||
this.buildAppQuery(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async searchGroupMembers(groupId: string, keyword: string, size: number = 20): Promise<UserProfile[]> {
|
|
||||||
return HttpClient.get<UserProfile[]>(
|
|
||||||
'/api/im/groups/' + encodeURIComponent(groupId) + '/members/search',
|
|
||||||
this.buildSearchQuery(keyword, size),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async searchUsers(keyword: string, size: number = 20): Promise<UserProfile[]> {
|
|
||||||
return HttpClient.get<UserProfile[]>(
|
|
||||||
'/api/im/admin/users/search',
|
|
||||||
this.buildSearchQuery(keyword, size),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async searchGroups(keyword: string, size: number = 20): Promise<ImGroup[]> {
|
|
||||||
return HttpClient.get<ImGroup[]>(
|
|
||||||
'/api/im/admin/groups/search',
|
|
||||||
this.buildSearchQuery(keyword, size),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async searchMessages(
|
|
||||||
keyword: string = '',
|
|
||||||
chatType: ChatType | '' = '',
|
|
||||||
msgType: MsgType | '' = '',
|
|
||||||
page: number = 0,
|
|
||||||
size: number = 20,
|
|
||||||
): Promise<PageResult<ImMessage>> {
|
|
||||||
return HttpClient.get<PageResult<ImMessage>>(
|
|
||||||
'/api/im/admin/messages/search',
|
|
||||||
this.buildMessageSearchQuery(keyword, chatType, msgType, page, size),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async editMessage(messageId: string, content: string): Promise<ImMessage> {
|
|
||||||
const body = new EditMessageBody()
|
|
||||||
body.content = content
|
|
||||||
return HttpClient.put<ImMessage>(
|
|
||||||
'/api/im/messages/' + encodeURIComponent(messageId),
|
|
||||||
body,
|
|
||||||
this.buildAppQuery(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async revokeMessage(messageId: string): Promise<ImMessage> {
|
|
||||||
return HttpClient.post<ImMessage>(
|
|
||||||
'/api/im/messages/' + encodeURIComponent(messageId) + '/revoke',
|
|
||||||
this.buildAppBody(),
|
|
||||||
this.buildAppQuery(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async sendFriendRequest(toUserId: string, remark: string | null = null): Promise<FriendRequest> {
|
|
||||||
const params = new FriendRequestBody()
|
|
||||||
params.appId = SDKContext.getConfig().appKey
|
|
||||||
params.toUserId = toUserId
|
|
||||||
if (remark !== null && remark !== '') {
|
|
||||||
params.remark = remark
|
|
||||||
}
|
|
||||||
return HttpClient.post<FriendRequest>('/api/im/friend-requests', params)
|
|
||||||
}
|
|
||||||
|
|
||||||
async listFriendRequests(direction: 'incoming' | 'outgoing' = 'incoming'): Promise<FriendRequest[]> {
|
|
||||||
return HttpClient.get<FriendRequest[]>('/api/im/friend-requests', this.buildFriendRequestQuery(direction))
|
|
||||||
}
|
|
||||||
|
|
||||||
async acceptFriendRequest(requestId: string): Promise<FriendRequest> {
|
|
||||||
return HttpClient.post<FriendRequest>('/api/im/friend-requests/' + encodeURIComponent(requestId) + '/accept', this.buildAppBody())
|
|
||||||
}
|
|
||||||
|
|
||||||
async rejectFriendRequest(requestId: string): Promise<FriendRequest> {
|
|
||||||
return HttpClient.post<FriendRequest>('/api/im/friend-requests/' + encodeURIComponent(requestId) + '/reject', this.buildAppBody())
|
|
||||||
}
|
|
||||||
|
|
||||||
async sendGroupJoinRequest(groupId: string, remark: string | null = null): Promise<GroupJoinRequest> {
|
|
||||||
const params = new GroupJoinRequestBody()
|
|
||||||
params.appId = SDKContext.getConfig().appKey
|
|
||||||
if (remark !== null && remark !== '') {
|
|
||||||
params.remark = remark
|
|
||||||
}
|
|
||||||
return HttpClient.post<GroupJoinRequest>('/api/im/groups/' + encodeURIComponent(groupId) + '/join-requests', params)
|
|
||||||
}
|
|
||||||
|
|
||||||
async listGroupJoinRequests(groupId: string): Promise<GroupJoinRequest[]> {
|
|
||||||
return HttpClient.get<GroupJoinRequest[]>('/api/im/groups/' + encodeURIComponent(groupId) + '/join-requests', this.buildAppQuery())
|
|
||||||
}
|
|
||||||
|
|
||||||
async acceptGroupJoinRequest(groupId: string, requestId: string): Promise<GroupJoinRequest> {
|
|
||||||
return HttpClient.post<GroupJoinRequest>(
|
|
||||||
'/api/im/groups/' + encodeURIComponent(groupId) + '/join-requests/' + encodeURIComponent(requestId) + '/accept',
|
|
||||||
this.buildAppBody(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async rejectGroupJoinRequest(groupId: string, requestId: string): Promise<GroupJoinRequest> {
|
|
||||||
return HttpClient.post<GroupJoinRequest>(
|
|
||||||
'/api/im/groups/' + encodeURIComponent(groupId) + '/join-requests/' + encodeURIComponent(requestId) + '/reject',
|
|
||||||
this.buildAppBody(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProfile(userId: string): Promise<UserProfile> {
|
async getProfile(userId: string): Promise<UserProfile> {
|
||||||
return HttpClient.get<UserProfile>('/api/im/accounts/' + encodeURIComponent(userId), this.buildAppQuery())
|
return HttpClient.get<UserProfile>(`/api/im/accounts/${encodeURIComponent(userId)}`, {
|
||||||
|
appId: SDKContext.getConfig().appKey,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProfile(
|
async updateProfile(
|
||||||
@ -441,91 +187,12 @@ export class ImClient {
|
|||||||
avatar: string | null = null,
|
avatar: string | null = null,
|
||||||
gender: string | null = null,
|
gender: string | null = null,
|
||||||
): Promise<UserProfile> {
|
): Promise<UserProfile> {
|
||||||
const params = new UpdateProfileBody()
|
return HttpClient.put<UserProfile>(`/api/im/accounts/${encodeURIComponent(userId)}`, undefined, {
|
||||||
params.appId = SDKContext.getConfig().appKey
|
appId: SDKContext.getConfig().appKey,
|
||||||
if (nickname !== null) {
|
...(nickname !== null ? { nickname } : {}),
|
||||||
params.nickname = nickname
|
...(avatar !== null ? { avatar } : {}),
|
||||||
}
|
...(gender !== null ? { gender } : {}),
|
||||||
if (avatar !== null) {
|
})
|
||||||
params.avatar = avatar
|
|
||||||
}
|
|
||||||
if (gender !== null) {
|
|
||||||
params.gender = gender
|
|
||||||
}
|
|
||||||
return HttpClient.put<UserProfile>('/api/im/accounts/' + encodeURIComponent(userId), params)
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildAppBody(): AppBody {
|
|
||||||
const body = new AppBody()
|
|
||||||
body.appId = SDKContext.getConfig().appKey
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildAppQuery(): string {
|
|
||||||
return 'appId=' + encodeURIComponent(SDKContext.getConfig().appKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildConversationActionQuery(chatType: ChatType): string {
|
|
||||||
return 'appId=' + encodeURIComponent(SDKContext.getConfig().appKey) + '&chatType=' + encodeURIComponent(chatType)
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildConversationQuery(size: number): string {
|
|
||||||
return 'appId=' + encodeURIComponent(SDKContext.getConfig().appKey) + '&page=0&size=' + encodeURIComponent(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildFriendRequestQuery(direction: 'incoming' | 'outgoing'): string {
|
|
||||||
return 'appId=' + encodeURIComponent(SDKContext.getConfig().appKey) + '&direction=' + encodeURIComponent(direction)
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildSearchQuery(keyword: string, size: number): string {
|
|
||||||
return 'appId=' + encodeURIComponent(SDKContext.getConfig().appKey) +
|
|
||||||
'&keyword=' + encodeURIComponent(keyword) +
|
|
||||||
'&size=' + encodeURIComponent(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildMessageSearchQuery(
|
|
||||||
keyword: string,
|
|
||||||
chatType: ChatType | '',
|
|
||||||
msgType: MsgType | '',
|
|
||||||
page: number,
|
|
||||||
size: number,
|
|
||||||
): string {
|
|
||||||
const parts: string[] = []
|
|
||||||
parts.push('appId=' + encodeURIComponent(SDKContext.getConfig().appKey))
|
|
||||||
if (keyword !== '') {
|
|
||||||
parts.push('keyword=' + encodeURIComponent(keyword))
|
|
||||||
}
|
|
||||||
if (chatType !== '') {
|
|
||||||
parts.push('chatType=' + encodeURIComponent(chatType))
|
|
||||||
}
|
|
||||||
if (msgType !== '') {
|
|
||||||
parts.push('msgType=' + encodeURIComponent(msgType))
|
|
||||||
}
|
|
||||||
parts.push('page=' + encodeURIComponent(page))
|
|
||||||
parts.push('size=' + encodeURIComponent(size))
|
|
||||||
return parts.join('&')
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildHistoryQuery(page: number, size: number, query: HistoryQuery): string {
|
|
||||||
const parts: string[] = []
|
|
||||||
parts.push('appId=' + encodeURIComponent(SDKContext.getConfig().appKey))
|
|
||||||
parts.push('page=' + encodeURIComponent(page))
|
|
||||||
parts.push('size=' + encodeURIComponent(size))
|
|
||||||
if (query.msgType !== undefined) {
|
|
||||||
parts.push('msgType=' + encodeURIComponent(query.msgType))
|
|
||||||
}
|
|
||||||
if (query.keyword !== undefined && query.keyword !== '') {
|
|
||||||
parts.push('keyword=' + encodeURIComponent(query.keyword))
|
|
||||||
}
|
|
||||||
if (query.startTime !== undefined) {
|
|
||||||
const startValue = query.startTime instanceof Date ? this.formatDateTime(query.startTime) : String(query.startTime)
|
|
||||||
parts.push('startTime=' + encodeURIComponent(startValue))
|
|
||||||
}
|
|
||||||
if (query.endTime !== undefined) {
|
|
||||||
const endValue = query.endTime instanceof Date ? this.formatDateTime(query.endTime) : String(query.endTime)
|
|
||||||
parts.push('endTime=' + encodeURIComponent(endValue))
|
|
||||||
}
|
|
||||||
return parts.join('&')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect(): void {
|
disconnect(): void {
|
||||||
@ -541,7 +208,7 @@ export class ImClient {
|
|||||||
private scheduleReconnect(): void {
|
private scheduleReconnect(): void {
|
||||||
if (this.destroyed) return
|
if (this.destroyed) return
|
||||||
const delay = this.reconnectDelay
|
const delay = this.reconnectDelay
|
||||||
if (SDKContext.getConfig().debug) console.log('[ImClient] reconnect in ' + delay + 'ms')
|
if (SDKContext.getConfig().debug) console.log(`[ImClient] reconnect in ${delay}ms`)
|
||||||
this.reconnectTimer = setTimeout(() => {
|
this.reconnectTimer = setTimeout(() => {
|
||||||
this.connect()
|
this.connect()
|
||||||
this.reconnectDelay = Math.min(this.reconnectDelay * 2, MAX_RECONNECT_DELAY)
|
this.reconnectDelay = Math.min(this.reconnectDelay * 2, MAX_RECONNECT_DELAY)
|
||||||
@ -552,7 +219,7 @@ export class ImClient {
|
|||||||
const messageId = params.messageId ?? this.generateMessageId()
|
const messageId = params.messageId ?? this.generateMessageId()
|
||||||
const userId = SDKContext.getUserId() ?? ''
|
const userId = SDKContext.getUserId() ?? ''
|
||||||
const appId = SDKContext.getConfig().appKey
|
const appId = SDKContext.getConfig().appKey
|
||||||
const message: ImMessage = {
|
return {
|
||||||
id: messageId,
|
id: messageId,
|
||||||
appId,
|
appId,
|
||||||
fromUserId: userId,
|
fromUserId: userId,
|
||||||
@ -567,55 +234,25 @@ export class ImClient {
|
|||||||
revoked: false,
|
revoked: false,
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
}
|
}
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
private markFailed(message: ImMessage): ImMessage {
|
|
||||||
const failed: ImMessage = {
|
|
||||||
id: message.id,
|
|
||||||
appId: message.appId,
|
|
||||||
fromUserId: message.fromUserId,
|
|
||||||
fromId: message.fromId,
|
|
||||||
toId: message.toId,
|
|
||||||
chatType: message.chatType,
|
|
||||||
msgType: message.msgType,
|
|
||||||
content: message.content,
|
|
||||||
status: 'FAILED',
|
|
||||||
mentionedUserIds: message.mentionedUserIds,
|
|
||||||
groupReadCount: message.groupReadCount,
|
|
||||||
revoked: message.revoked,
|
|
||||||
createdAt: message.createdAt,
|
|
||||||
}
|
|
||||||
return failed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private normalizeMessage(message: ImMessage): ImMessage {
|
private normalizeMessage(message: ImMessage): ImMessage {
|
||||||
const normalized: ImMessage = {
|
return {
|
||||||
id: message.id,
|
...message,
|
||||||
appId: message.appId || SDKContext.getConfig().appKey,
|
fromId: message.fromId ?? message.fromUserId,
|
||||||
fromUserId: message.fromUserId,
|
revoked: message.revoked ?? message.status === 'REVOKED',
|
||||||
fromId: message.fromId || message.fromUserId,
|
appId: message.appId ?? SDKContext.getConfig().appKey,
|
||||||
toId: message.toId,
|
|
||||||
chatType: message.chatType,
|
|
||||||
msgType: message.msgType,
|
|
||||||
content: message.content,
|
|
||||||
status: message.status,
|
|
||||||
mentionedUserIds: message.mentionedUserIds,
|
|
||||||
groupReadCount: message.groupReadCount,
|
|
||||||
revoked: message.revoked || message.status === 'REVOKED',
|
|
||||||
createdAt: message.createdAt,
|
|
||||||
}
|
}
|
||||||
return normalized
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateMessageId(): string {
|
private generateMessageId(): string {
|
||||||
return 'msg_' + Date.now() + '_' + Math.floor(Math.random() * 1000000).toString(16)
|
const cryptoId = globalThis.crypto?.randomUUID?.()
|
||||||
|
if (cryptoId) return cryptoId
|
||||||
|
return `msg_${Date.now()}_${Math.random().toString(16).slice(2)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
private formatDateTime(value: Date): string {
|
private formatDateTime(value: Date): string {
|
||||||
const pad = (n: number): string => {
|
const pad = (n: number) => String(n).padStart(2, '0')
|
||||||
return n < 10 ? '0' + n : String(n)
|
|
||||||
}
|
|
||||||
return [
|
return [
|
||||||
value.getFullYear(),
|
value.getFullYear(),
|
||||||
'-',
|
'-',
|
||||||
@ -630,14 +267,4 @@ export class ImClient {
|
|||||||
pad(value.getSeconds()),
|
pad(value.getSeconds()),
|
||||||
].join('')
|
].join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
private toStringValue(value: Date | string | number | undefined): string | undefined {
|
|
||||||
if (value === undefined) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
if (value instanceof Date) {
|
|
||||||
return this.formatDateTime(value)
|
|
||||||
}
|
|
||||||
return String(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,5 @@
|
|||||||
import { HttpClient } from '../core/HttpClient'
|
import { HttpClient } from '../core/HttpClient'
|
||||||
|
import type { PushTokenInfo } from '../core/Types'
|
||||||
class PushRegisterBody {
|
|
||||||
vendor: string = 'HARMONY'
|
|
||||||
token: string = ''
|
|
||||||
platform: string = 'harmony'
|
|
||||||
imUserId: string | null = null
|
|
||||||
}
|
|
||||||
|
|
||||||
class PushUnregisterBody {
|
|
||||||
vendor: string = 'HARMONY'
|
|
||||||
token: string = ''
|
|
||||||
platform: string = 'harmony'
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PushSDK {
|
export class PushSDK {
|
||||||
/**
|
/**
|
||||||
@ -20,20 +8,21 @@ export class PushSDK {
|
|||||||
* vendor should be 'HARMONY' for HarmonyOS devices.
|
* vendor should be 'HARMONY' for HarmonyOS devices.
|
||||||
*/
|
*/
|
||||||
static async registerToken(token: string, imUserId?: string): Promise<void> {
|
static async registerToken(token: string, imUserId?: string): Promise<void> {
|
||||||
const body = new PushRegisterBody()
|
const body: PushTokenInfo = {
|
||||||
body.token = token
|
vendor: 'HARMONY',
|
||||||
if (imUserId !== undefined) {
|
token,
|
||||||
body.imUserId = imUserId
|
platform: 'harmony',
|
||||||
}
|
}
|
||||||
await HttpClient.post<void>('/api/v1/push/register', body)
|
await HttpClient.post<void>('/api/v1/push/register', {
|
||||||
|
...body,
|
||||||
|
imUserId: imUserId ?? null,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister push token on logout.
|
* Unregister push token on logout.
|
||||||
*/
|
*/
|
||||||
static async unregisterToken(token: string): Promise<void> {
|
static async unregisterToken(token: string): Promise<void> {
|
||||||
const body = new PushUnregisterBody()
|
await HttpClient.post<void>('/api/v1/push/unregister', { token, platform: 'harmony' })
|
||||||
body.token = token
|
|
||||||
await HttpClient.post<void>('/api/v1/push/unregister', body)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"module": {
|
"module": {
|
||||||
"name": "xuqmSdk",
|
"name": "xuqm-sdk",
|
||||||
"type": "har",
|
"type": "har",
|
||||||
"deviceTypes": ["phone", "tablet"]
|
"deviceTypes": ["phone", "tablet"]
|
||||||
}
|
}
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户