diff --git a/app/frontend/src/components/Chat.vue b/app/frontend/src/components/Chat.vue index 82c6ca4..cecfa3d 100644 --- a/app/frontend/src/components/Chat.vue +++ b/app/frontend/src/components/Chat.vue @@ -17,12 +17,16 @@ const accountStore = useAccountStore();
- - -

{{ bufferStore.activeBufferName }}

- {{ bufferStore.activeBuffer?.topic }} -
-
+ + + + {{ bufferStore.activeBufferName }} + + + {{ bufferStore.activeBuffer?.topic }} + + + diff --git a/app/frontend/src/components/MessageList.vue b/app/frontend/src/components/MessageList.vue index dc98a9b..47f1210 100644 --- a/app/frontend/src/components/MessageList.vue +++ b/app/frontend/src/components/MessageList.vue @@ -61,7 +61,7 @@ watch( >mdi-eyeOnly visible to you - {{ msg.message }} +
{{ msg.message }}
diff --git a/app/frontend/src/stores/irc.ts b/app/frontend/src/stores/irc.ts index 5b7d59c..43c9c5b 100644 --- a/app/frontend/src/stores/irc.ts +++ b/app/frontend/src/stores/irc.ts @@ -12,12 +12,19 @@ export enum HookStatus { HOOK_EAT, } +interface Batch { + type: string; + target: string; + messages: any[]; + params: string[]; +} + export const useIRCStore = defineStore("ircStore", () => { const bufferStore = useBufferStore(); const accountStore = useAccountStore(); const connected = ref(false); const { authError } = storeToRefs(accountStore); - + const batches = new Map(); const hooks = {} as Record; function registerHook(event: string, f: HookFunction) { @@ -103,6 +110,7 @@ export const useIRCStore = defineStore("ircStore", () => { client.requestCap("draft/metadata-2"); client.requestCap("echo-message"); client.requestCap("chathistory"); + client.requestCap("draft/multiline"); const tls = location.protocol === "https:"; const connectParams = { host: location.hostname, @@ -127,9 +135,27 @@ export const useIRCStore = defineStore("ircStore", () => { if (!bufferStore.activeBuffer) { return; } - if (bufferStore.activeBuffer.channel) - bufferStore.activeBuffer.channel.say(message); - else client.say(bufferStore.activeBuffer.name, message); + send(bufferStore.activeBuffer?.name, message); + } + + function genBatchId(): string { + return Math.random().toString(36).substring(2, 10); + } + + function send(target: string, message: string) { + if (!message.includes("\n")) { + client.say(target, message); + return; + } + + const split = message.split("\n"); + + const batchId = genBatchId(); + client.raw(`BATCH +${batchId} draft/multiline ${target}`); + split.forEach((line) => { + client.raw(`@batch=${batchId} PRIVMSG ${target} :${line}`); + }); + client.raw(`BATCH -${batchId}`); } function isMe(target: string) { @@ -143,6 +169,7 @@ export const useIRCStore = defineStore("ircStore", () => { } client.on("socket close", () => { + batches.clear(); connected.value = false; }); @@ -242,7 +269,7 @@ export const useIRCStore = defineStore("ircStore", () => { }, ); - client.on("message", function (message: { nick: string; target: string }) { + function handleMessage(message: any) { let buffer; const retVal = runHook("message", message); @@ -270,7 +297,25 @@ export const useIRCStore = defineStore("ircStore", () => { ) { buffer.resetLastSeen(); } - }); + } + + client.on( + "message", + function (message: { + nick: string; + target: string; + tags: Record; + }) { + const batchId = message.tags["batch"]; + + if (batchId && batches.has(batchId)) { + batches.get(batchId)?.messages.push(message); + return; + } + + handleMessage(message); + }, + ); client.on("notice", function (message) { const retVal = runHook("notice", message); @@ -335,6 +380,45 @@ export const useIRCStore = defineStore("ircStore", () => { buffer.users = ev.users; }); + client.on("batch start", (event: any) => { + batches.set(event.id, { + type: event.type, + target: event.params[0], + messages: [], + params: event.params, + }); + }); + + function handleChatHistory(batch: Batch) { + for (const message of batch.messages) { + handleMessage(message); + } + } + + function handleMultiline(batch: Batch) { + console.log(batch); + let m = ""; + for (const message of batch.messages) { + m += `${message.message}\n`; + } + handleMessage({ ...batch.messages[0], message: m }); + } + + client.on("batch end draft/multiline", (event: any) => { + const batch = batches.get(event.id); + if (!batch) return; + + handleMultiline(batch); + batches.delete(event.id); + }); + + client.on("batch end chathistory", (event: any) => { + const batch = batches.get(event.id); + if (!batch) return; + handleChatHistory(batch); + batches.delete(event.id); + }); + return { connect, client,