From 1c288a9b470e5f9c8123fe944d1b8ca9c65cda3f Mon Sep 17 00:00:00 2001 From: koplenov Date: Sat, 9 May 2026 19:51:06 +0300 Subject: [PATCH] working on messages sync --- xmpp.view.ts | 74 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/xmpp.view.ts b/xmpp.view.ts index 87e34a6..440f080 100644 --- a/xmpp.view.ts +++ b/xmpp.view.ts @@ -7,21 +7,22 @@ status: string } - type Xmpp_message = { - id: string - from: string - to: string - body: string - time: number - nick?: string // sender nick for MUC groupchat messages - media_uri?: string - media_mime?: string - media_name?: string - media_kind?: Media_type - media_size?: number - media_hash?: string - media_hash_algo?: string - } + type Xmpp_message = { + id: string + from: string + to: string + body: string + time: number + nick?: string // sender nick for MUC groupchat messages + mam_id?: string // archive result id for MAM pagination + media_uri?: string + media_mime?: string + media_name?: string + media_kind?: Media_type + media_size?: number + media_hash?: string + media_hash_algo?: string + } type Xmpp_room = { jid: string @@ -44,6 +45,7 @@ body: string time: number nick?: string + mam_id?: string media_uri?: string media_mime?: string media_name?: string @@ -317,6 +319,7 @@ private _getBody(el: Element): string | null { // XEP-0313 for MUC: query sent TO the room, no "with" filter, optional end-timestamp to avoid duplicating join-history request_mam_room(room_jid: string, max = 50, before_id?: string, before_time?: number): void { if (!this._ws) return + room_jid = room_jid.split('/')[0] const qid = this._id() const id = this._id() this._mam_iqs.set(id, room_jid) @@ -341,6 +344,7 @@ private _getBody(el: Element): string | null { // XEP-0313: request last `max` messages with a given peer; pass `before_id` for pagination request_mam(with_jid: string, max = 50, before_id?: string): void { if (!this._ws) return + with_jid = with_jid.split('/')[0] const qid = this._id() const id = this._id() this._mam_iqs.set(id, with_jid) @@ -662,11 +666,14 @@ private _handle_message(el: Element) { const from = slash >= 0 ? from_full.slice(0, slash) : from_full // Only groupchat carries a meaningful nick after the slash; for 1:1 the resource is just a device id. const nick = type === 'groupchat' && slash >= 0 ? from_full.slice(slash + 1) : undefined + const stanza_id = msg.getAttribute('id') || result.getAttribute('id') || this._id() + const mam_id = result.getAttribute('id') || stanza_id this.on_mam_message?.({ - id: result.getAttribute('id') || this._id(), + id: stanza_id, from, to: (msg.getAttribute('to') || '').split('/')[0], body, time, + mam_id, ...(nick !== undefined ? { nick } : {}), ...(media || {}), }) @@ -705,6 +712,7 @@ private _handle_message(el: Element) { private _rec = new Map() private _history_loaded = new Set() private _mam_oldest = new Map() // jid → oldest MAM result id + private _mam_oldest_time = new Map() // jid → oldest MAM result time private _loading_more = new Set() private _fin_count = new Map() private _scroll_setup = new Set() // scroll listener installed @@ -1281,9 +1289,12 @@ private _handle_message(el: Element) { const bare = this.my_jid().split('/')[0] const peer = msg.from === bare ? msg.to : msg.from if (peer) { - const cur = this._mam_oldest.get(peer) - const cur_time = cur ? (this._msg_by_id.get(cur)?.time ?? Infinity) : Infinity - if (msg.time < cur_time) this._mam_oldest.set(peer, msg.id) + const oldest_id = msg.mam_id || msg.id + const cur_time = this._mam_oldest_time.get(peer) ?? Infinity + if (msg.time < cur_time) { + this._mam_oldest.set(peer, oldest_id) + this._mam_oldest_time.set(peer, msg.time) + } } // MAM-on-delivery: server may wrap live messages in ; notify if recent if (Date.now() - msg.time < 30_000 && msg.from !== bare) { @@ -1471,6 +1482,8 @@ private _handle_message(el: Element) { this._scroll_setup.delete(jid) this._oldest_time.delete(jid) this._mam_oldest.delete(jid) + this._mam_oldest_time.delete(jid) + this._history_loaded.delete(jid) this._fin_count.delete(jid) this.messages_ver(this.messages_ver() + 1) this.$.$mol_state_arg.value('chat', null) @@ -1852,7 +1865,13 @@ private _handle_message(el: Element) { private _load_more_history(jid: string) { if (this._loading_more.has(jid) || !this._conn) return - const oldest_id = this._mam_oldest.get(jid) + let oldest_id = this._mam_oldest.get(jid) + if (!oldest_id) { + const oldest_msg = this._msgs + .filter(m => m.from === jid || m.to === jid || (m.from === this.my_jid().split('/')[0] && m.to === jid) || (m.from === jid && m.to === this.my_jid().split('/')[0])) + .sort((a, b) => a.time - b.time)[0] + if (oldest_msg?.mam_id) oldest_id = oldest_msg.mam_id + } this._loading_more.add(jid) if (this._rooms.has(jid)) { // cursor available after first MAM batch; otherwise use timestamp to avoid duplicating join-history @@ -1937,6 +1956,7 @@ private _handle_message(el: Element) { ...(msg.media_size !== undefined ? { media_size: msg.media_size } : {}), ...(msg.media_hash !== undefined ? { media_hash: msg.media_hash } : {}), ...(msg.media_hash_algo !== undefined ? { media_hash_algo: msg.media_hash_algo } : {}), + ...(msg.mam_id !== undefined ? { mam_id: msg.mam_id } : {}), } await Messages.put(doc, [account, msg.id]) } catch (e) { console.warn('[xmpp] persist failed', e) } @@ -1953,7 +1973,7 @@ private _handle_message(el: Element) { this._loading_persisted = true try { for (const doc of docs) { - this._add_message({ + const msg: Xmpp_message = { id: doc.id, from: doc.from, to: doc.to, body: doc.body, time: doc.time, ...(doc.nick !== undefined ? { nick: doc.nick } : {}), @@ -1964,7 +1984,17 @@ private _handle_message(el: Element) { ...(doc.media_size !== undefined ? { media_size: doc.media_size } : {}), ...(doc.media_hash !== undefined ? { media_hash: doc.media_hash } : {}), ...(doc.media_hash_algo !== undefined ? { media_hash_algo: doc.media_hash_algo } : {}), - }) + ...(doc.mam_id !== undefined ? { mam_id: doc.mam_id } : {}), + } + this._add_message(msg) + if (msg.mam_id) { + const peer = this._peer_of(msg, account) + const cur_time = this._mam_oldest_time.get(peer) ?? Infinity + if (msg.time < cur_time) { + this._mam_oldest.set(peer, msg.mam_id) + this._mam_oldest_time.set(peer, msg.time) + } + } } } finally { this._loading_persisted = false