added persist:
* auto login if credentials exists * storing messages in indexdb ($mol_db)
This commit is contained in:
parent
3b55ecc693
commit
e90ae849f3
1 changed files with 111 additions and 9 deletions
120
xmpp.view.ts
120
xmpp.view.ts
|
|
@ -22,6 +22,24 @@ namespace $.$$ {
|
|||
nick: string // my nickname in this room
|
||||
}
|
||||
|
||||
// $mol_db schema for persisted messages.
|
||||
type Xmpp_db_schema = {
|
||||
Messages: {
|
||||
Key: [ string, string ] // [account_bare_jid, msg_id]
|
||||
Doc: {
|
||||
account: string
|
||||
peer: string
|
||||
id: string
|
||||
from: string
|
||||
to: string
|
||||
body: string
|
||||
time: number
|
||||
nick?: string
|
||||
}
|
||||
Indexes: {}
|
||||
}
|
||||
}
|
||||
|
||||
type Media_type = 'image' | 'audio' | 'link' | null
|
||||
|
||||
function media_type(url: string): Media_type {
|
||||
|
|
@ -616,20 +634,29 @@ private _handle_message(el: Element) {
|
|||
// — covers servers that replay offline messages without proper <delay>.
|
||||
private _connect_at = 0
|
||||
|
||||
// IndexedDB for persistent message history (via $mol_db).
|
||||
private _db: $mol_db_database<Xmpp_db_schema> | null = null
|
||||
private _db_init: Promise<$mol_db_database<Xmpp_db_schema>> | null = null
|
||||
private _loading_persisted = false
|
||||
|
||||
private _auto_connect_tried = false
|
||||
|
||||
@ $mol_mem
|
||||
status(next?: 'disconnected' | 'connecting' | 'connected') { return next ?? 'disconnected' }
|
||||
|
||||
@ $mol_mem
|
||||
my_jid(next?: string) { return next ?? '' }
|
||||
|
||||
@ $mol_mem
|
||||
server(next?: string) { return next ?? '' }
|
||||
|
||||
@ $mol_mem
|
||||
jid(next?: string) { return next ?? '' }
|
||||
|
||||
@ $mol_mem
|
||||
password(next?: string) { return next ?? '' }
|
||||
// Persisted in localStorage so the user doesn't have to re-enter on reload.
|
||||
server(next?: string) {
|
||||
return (this.$.$mol_state_local.value('xmpp_server', next) as string | null) ?? ''
|
||||
}
|
||||
jid(next?: string) {
|
||||
return (this.$.$mol_state_local.value('xmpp_jid', next) as string | null) ?? ''
|
||||
}
|
||||
password(next?: string) {
|
||||
return (this.$.$mol_state_local.value('xmpp_password', next) as string | null) ?? ''
|
||||
}
|
||||
|
||||
@ $mol_mem
|
||||
error_text(next?: string) { return next ?? '' }
|
||||
|
|
@ -714,7 +741,10 @@ private _handle_message(el: Element) {
|
|||
// ── Pages ─────────────────────────────────────────────────────────────
|
||||
|
||||
pages() {
|
||||
if (this.status() !== 'connected') return [this.Login_page()]
|
||||
if (this.status() !== 'connected') {
|
||||
this._maybe_auto_connect()
|
||||
return [this.Login_page()]
|
||||
}
|
||||
const pages: $mol_view[] = [this.Roster_page()]
|
||||
const peer = this.$.$mol_state_arg.value('chat')
|
||||
if (peer) {
|
||||
|
|
@ -725,6 +755,21 @@ private _handle_message(el: Element) {
|
|||
return pages
|
||||
}
|
||||
|
||||
// On first render, if we have saved credentials and aren't connecting/connected, kick off auto-connect.
|
||||
private _maybe_auto_connect() {
|
||||
if (this._auto_connect_tried) return
|
||||
if (this.status() !== 'disconnected') return
|
||||
const url = this.server().trim()
|
||||
const jid = this.jid().trim()
|
||||
const pw = this.password()
|
||||
if (!url || !jid || !pw) return
|
||||
this._auto_connect_tried = true
|
||||
// Defer to escape the current reactive fiber — do_connect mutates memos.
|
||||
Promise.resolve().then(() => {
|
||||
if (this.status() === 'disconnected') this.do_connect()
|
||||
})
|
||||
}
|
||||
|
||||
// ── Login ─────────────────────────────────────────────────────────────
|
||||
|
||||
connecting() { return this.status() === 'connecting' }
|
||||
|
|
@ -751,6 +796,7 @@ private _handle_message(el: Element) {
|
|||
this.my_jid(bound_jid)
|
||||
this.status('connected')
|
||||
this._connect_at = Date.now()
|
||||
void this._load_persisted()
|
||||
}
|
||||
conn.on_bookmarks = bookmarks => {
|
||||
const my_nick = this.my_jid().split('@')[0]
|
||||
|
|
@ -1322,6 +1368,62 @@ private _handle_message(el: Element) {
|
|||
if (msg.time < cur) this._oldest_time.set(msg.from, msg.time)
|
||||
}
|
||||
this.messages_ver(this.messages_ver() + 1)
|
||||
if (!this._loading_persisted) void this._persist_msg(msg)
|
||||
}
|
||||
|
||||
// ── Persistence (IndexedDB via $mol_db) ──────────────────────────────
|
||||
|
||||
private _ensure_db(): Promise<$mol_db_database<Xmpp_db_schema>> {
|
||||
if (this._db) return Promise.resolve(this._db)
|
||||
if (this._db_init) return this._db_init
|
||||
this._db_init = this.$.$mol_db<Xmpp_db_schema>('xmpp',
|
||||
mig => { mig.store_make('Messages') },
|
||||
).then(db => { this._db = db; return db })
|
||||
return this._db_init
|
||||
}
|
||||
|
||||
private _peer_of(msg: Xmpp_message, account: string): string {
|
||||
if (msg.nick !== undefined) return msg.from
|
||||
return msg.from === account ? msg.to : msg.from
|
||||
}
|
||||
|
||||
private async _persist_msg(msg: Xmpp_message) {
|
||||
const account = this.my_jid().split('/')[0]
|
||||
if (!account) return
|
||||
try {
|
||||
const db = await this._ensure_db()
|
||||
const peer = this._peer_of(msg, account)
|
||||
const { Messages } = db.change('Messages').stores
|
||||
const doc: Xmpp_db_schema['Messages']['Doc'] = {
|
||||
account, peer,
|
||||
id: msg.id, from: msg.from, to: msg.to, body: msg.body, time: msg.time,
|
||||
...(msg.nick !== undefined ? { nick: msg.nick } : {}),
|
||||
}
|
||||
await Messages.put(doc, [account, msg.id])
|
||||
} catch (e) { console.warn('[xmpp] persist failed', e) }
|
||||
}
|
||||
|
||||
private async _load_persisted() {
|
||||
const account = this.my_jid().split('/')[0]
|
||||
if (!account) return
|
||||
try {
|
||||
const db = await this._ensure_db()
|
||||
const range = this.$.$mol_dom_context.IDBKeyRange.bound([account, ''], [account, ''])
|
||||
const docs = await db.read('Messages').Messages.select(range, 100_000)
|
||||
if (!docs?.length) return
|
||||
this._loading_persisted = true
|
||||
try {
|
||||
for (const doc of docs) {
|
||||
this._add_message({
|
||||
id: doc.id, from: doc.from, to: doc.to,
|
||||
body: doc.body, time: doc.time,
|
||||
...(doc.nick !== undefined ? { nick: doc.nick } : {}),
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
this._loading_persisted = false
|
||||
}
|
||||
} catch (e) { console.warn('[xmpp] load persisted failed', e) }
|
||||
}
|
||||
|
||||
private _maybe_load_history(jid: string) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue