mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-05 12:47:43 +00:00
nodejs: add tests to library (#7694)
* nodejs: add tests to library * new tests * remove message splitting * fix race * docs * replace extract-zip with yauzl that it wraps (high severity issue) * fix ci tests * skip server tests on macos and win unless SMP_SERVER is set * fix win test * fix win test --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
This commit is contained in:
1 parent
ba9269a3ad
commit
46b2cf9f85
73 files changed
+957
-265
No files matched your search
@@ -273,9 +273,11 @@ export class ChatApi {
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let done = false
|
||||
let timer: NodeJS.Timeout | undefined = undefined
|
||||
const cleanup = () => {
|
||||
done = true
|
||||
this.off(event, subscriber)
|
||||
clearTimeout(timer)
|
||||
this.off(event, subscriber)
|
||||
}
|
||||
const subscriber: EventSubscriberFunc<K> = async (evt: ChatEvent & {type: K}) => {
|
||||
if (done) return
|
||||
@@ -288,7 +290,7 @@ export class ChatApi {
|
||||
}
|
||||
this.on(event, subscriber)
|
||||
if (timeout > 0) {
|
||||
setTimeout(() => { if (!done) { cleanup(); resolve(undefined) } }, timeout)
|
||||
timer = setTimeout(() => { if (!done) { cleanup(); resolve(undefined) } }, timeout)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -476,9 +478,10 @@ export class ChatApi {
|
||||
|
||||
/**
|
||||
* Update message.
|
||||
* A live message stays live with `liveMessage = true` and is finalized with `liveMessage = false`.
|
||||
* Network usage: background.
|
||||
*/
|
||||
async apiUpdateChatItem(chatType: T.ChatType, chatId: number, chatItemId: number, msgContent: T.MsgContent, liveMessage: false): Promise<T.ChatItem> {
|
||||
async apiUpdateChatItem(chatType: T.ChatType, chatId: number, chatItemId: number, msgContent: T.MsgContent, liveMessage = false): Promise<T.ChatItem> {
|
||||
const r = await this.sendChatCmd(
|
||||
CC.APIUpdateChatItem.cmdString({
|
||||
chatRef: {chatType, chatId},
|
||||
@@ -995,3 +998,88 @@ export class ChatApi {
|
||||
throw new ChatCommandError("error sending member contact invitation", r)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Text shown as a live message while it is written, e.g. a streamed reply.
|
||||
*/
|
||||
export class LiveMessage {
|
||||
private tasks: Promise<void> = Promise.resolve()
|
||||
private text = ""
|
||||
private writing = false
|
||||
private timer: NodeJS.Timeout | undefined = undefined
|
||||
private item: {itemId: number, text: string} | undefined = undefined
|
||||
|
||||
/**
|
||||
* @param chat - Chat API, or an object with its methods to send, update and delete messages.
|
||||
* @param {T.ChatType} chatType - Chat type.
|
||||
* @param {number} chatId - Contact or group ID.
|
||||
* @param {number} [interval=3000] - Minimum interval between updates of the live message, in milliseconds.
|
||||
* @param onError - Error handler for sending, updating and deleting messages. Errors are logged when it is omitted.
|
||||
*/
|
||||
constructor(
|
||||
private readonly chat: Pick<ChatApi, "apiSendMessages" | "apiUpdateChatItem" | "apiDeleteChatItems">,
|
||||
private readonly chatType: T.ChatType,
|
||||
private readonly chatId: number,
|
||||
private readonly interval = 3000,
|
||||
private readonly onError: (error: unknown) => void = e => console.log("live message error", e)
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Set the text of the live message.
|
||||
* The first text is sent at once; later texts update the message at most once per interval.
|
||||
*/
|
||||
update(text: string): void {
|
||||
this.text = text
|
||||
if (!this.writing) {
|
||||
this.writing = true
|
||||
this.queueText(text, true)
|
||||
} else if (this.timer === undefined) {
|
||||
this.timer = setTimeout(() => {
|
||||
this.timer = undefined
|
||||
this.queueText(this.text, true)
|
||||
}, this.interval)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize the live message with the text, or with the last text when omitted.
|
||||
* A message without text is deleted. The next update starts a new live message.
|
||||
* The returned promise resolves after all queued commands complete.
|
||||
*/
|
||||
end(text = this.text): Promise<void> {
|
||||
clearTimeout(this.timer)
|
||||
this.timer = undefined
|
||||
this.text = ""
|
||||
this.writing = false
|
||||
this.queueText(text, false)
|
||||
return this.tasks
|
||||
}
|
||||
|
||||
private queueText(text: string, live: boolean): void {
|
||||
this.tasks = this.tasks.then(() => this.showText(text, live)).catch(this.onError)
|
||||
}
|
||||
|
||||
private async showText(text: string, live: boolean): Promise<void> {
|
||||
try {
|
||||
await this.showMessage(text, live)
|
||||
} finally {
|
||||
if (!live) this.item = undefined
|
||||
}
|
||||
}
|
||||
|
||||
private async showMessage(text: string, live: boolean): Promise<void> {
|
||||
const item = this.item
|
||||
if (item === undefined) {
|
||||
if (live || text !== "") {
|
||||
const [{chatItem}] = await this.chat.apiSendMessages([this.chatType, this.chatId], [{msgContent: {type: "text", text}, mentions: {}}], live)
|
||||
this.item = live ? {itemId: chatItem.meta.itemId, text} : undefined
|
||||
}
|
||||
} else if (!live && text === "") {
|
||||
await this.chat.apiDeleteChatItems(this.chatType, this.chatId, [item.itemId], T.CIDeleteMode.Broadcast)
|
||||
this.item = undefined
|
||||
} else if (!live || text !== item.text) {
|
||||
await this.chat.apiUpdateChatItem(this.chatType, this.chatId, item.itemId, {type: "text", text}, live)
|
||||
this.item = live ? {itemId: item.itemId, text} : undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user