working on images
This commit is contained in:
parent
1b3fd93150
commit
be4a3e928c
1 changed files with 91 additions and 19 deletions
110
xmpp.view.ts
110
xmpp.view.ts
|
|
@ -14,6 +14,13 @@
|
|||
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_room = {
|
||||
|
|
@ -22,6 +29,8 @@
|
|||
nick: string // my nickname in this room
|
||||
}
|
||||
|
||||
type Media_type = 'image' | 'audio' | 'link' | null
|
||||
|
||||
// $mol_db schema for persisted messages.
|
||||
type Xmpp_db_schema = {
|
||||
Messages: {
|
||||
|
|
@ -35,13 +44,18 @@
|
|||
body: string
|
||||
time: number
|
||||
nick?: string
|
||||
media_uri?: string
|
||||
media_mime?: string
|
||||
media_name?: string
|
||||
media_kind?: Media_type
|
||||
media_size?: number
|
||||
media_hash?: string
|
||||
media_hash_algo?: string
|
||||
}
|
||||
Indexes: {}
|
||||
}
|
||||
}
|
||||
|
||||
type Media_type = 'image' | 'audio' | 'link' | null
|
||||
|
||||
function media_type(url: string): Media_type {
|
||||
const s = url.trim()
|
||||
if (!/^https?:\/\/\S+$/.test(s)) return null
|
||||
|
|
@ -51,6 +65,13 @@
|
|||
return 'link'
|
||||
}
|
||||
|
||||
function media_type_from_mime(mime: string): Media_type {
|
||||
const s = mime.trim().toLowerCase()
|
||||
if (s.startsWith('image/')) return 'image'
|
||||
if (s.startsWith('audio/')) return 'audio'
|
||||
return null
|
||||
}
|
||||
|
||||
// ─── XMPP over WebSocket (RFC 7590) ──────────────────────────────────────
|
||||
|
||||
class Xmpp_conn {
|
||||
|
|
@ -342,6 +363,27 @@ private _getBody(el: Element): string | null {
|
|||
return new DOMParser().parseFromString(data, 'text/xml')
|
||||
}
|
||||
|
||||
private _parse_media_sharing(el: Element) {
|
||||
const reference = el.getElementsByTagNameNS('urn:xmpp:reference:0', 'reference')[0]
|
||||
if (!reference || reference.getAttribute('type') !== 'data') return null
|
||||
const media = reference.getElementsByTagNameNS('urn:xmpp:sims:1', 'media-sharing')[0]
|
||||
if (!media) return null
|
||||
const file = media.getElementsByTagNameNS('urn:xmpp:jingle:apps:file-transfer:5', 'file')[0]
|
||||
if (!file) return null
|
||||
const name = file.getElementsByTagNameNS('urn:xmpp:jingle:apps:file-transfer:5', 'name')[0]?.textContent || undefined
|
||||
const mime = file.getElementsByTagNameNS('urn:xmpp:jingle:apps:file-transfer:5', 'media-type')[0]?.textContent || undefined
|
||||
const sizeText = file.getElementsByTagNameNS('urn:xmpp:jingle:apps:file-transfer:5', 'size')[0]?.textContent
|
||||
const hashElem = file.getElementsByTagNameNS('urn:xmpp:hashes:2', 'hash')[0]
|
||||
const sourceRef = Array.from(media.getElementsByTagNameNS('urn:xmpp:reference:0', 'reference'))
|
||||
.find(r => r.getAttribute('type') === 'data')
|
||||
const uri = sourceRef?.getAttribute('uri') || undefined
|
||||
const size = sizeText ? Number(sizeText) : undefined
|
||||
const hash = hashElem?.textContent || undefined
|
||||
const hash_algo = hashElem?.getAttribute('algo') || undefined
|
||||
const kind = mime ? media_type_from_mime(mime) : uri ? media_type(uri) : null
|
||||
return { media_uri: uri, media_mime: mime, media_name: name, media_kind: kind, media_size: size, media_hash: hash, media_hash_algo: hash_algo }
|
||||
}
|
||||
|
||||
private _handle(data: string) {
|
||||
const doc = this._parse(data)
|
||||
const root = doc.documentElement
|
||||
|
|
@ -552,16 +594,10 @@ private _handle_message(el: Element) {
|
|||
|
||||
const type = el.getAttribute('type') || 'chat';
|
||||
let body = this._getBody(el);
|
||||
const media = this._parse_media_sharing(el)
|
||||
if (!body) {
|
||||
const reference = this._find(el, 'urn:xmpp:reference:0', 'reference')
|
||||
if (reference?.getAttribute('type') === 'data') {
|
||||
const media = reference.querySelector('media-sharing')
|
||||
const file = media?.querySelector('file')
|
||||
const name = file?.querySelector('name')?.textContent
|
||||
const source = media?.querySelector('sources reference')?.getAttribute('uri')
|
||||
if (name) body = `Shared file: ${ name }`
|
||||
else if (source) body = source
|
||||
}
|
||||
if (media?.media_name) body = `Shared file: ${ media.media_name }`
|
||||
else if (media?.media_uri) body = media.media_uri
|
||||
}
|
||||
if (!body) return;
|
||||
|
||||
|
|
@ -589,6 +625,7 @@ private _handle_message(el: Element) {
|
|||
from: room_jid,
|
||||
to: (el.getAttribute('to') || '').split('/')[0],
|
||||
body, time, nick,
|
||||
...(media || {}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
@ -599,6 +636,7 @@ private _handle_message(el: Element) {
|
|||
from: (el.getAttribute('from') || '').split('/')[0],
|
||||
to: (el.getAttribute('to') || '').split('/')[0],
|
||||
body, time,
|
||||
...(media || {}),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -607,7 +645,12 @@ private _handle_message(el: Element) {
|
|||
if (!fwd) return
|
||||
const msg = fwd.querySelector('message')
|
||||
if (!msg) return
|
||||
const body = msg.querySelector('body')?.textContent
|
||||
let body = msg.querySelector('body')?.textContent
|
||||
const media = this._parse_media_sharing(msg)
|
||||
if (!body) {
|
||||
if (media?.media_name) body = `Shared file: ${ media.media_name }`
|
||||
else if (media?.media_uri) body = media.media_uri
|
||||
}
|
||||
if (!body) return
|
||||
const type = msg.getAttribute('type') || 'chat'
|
||||
if (type !== 'chat' && type !== 'normal' && type !== 'groupchat') return
|
||||
|
|
@ -625,6 +668,7 @@ private _handle_message(el: Element) {
|
|||
to: (msg.getAttribute('to') || '').split('/')[0],
|
||||
body, time,
|
||||
...(nick !== undefined ? { nick } : {}),
|
||||
...(media || {}),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1473,14 +1517,19 @@ private _handle_message(el: Element) {
|
|||
|
||||
@ $mol_mem_key // ← FIX: make reactive
|
||||
msg_body(id: string) {
|
||||
const body = this._msg_by_id.get(id)?.body ?? ''
|
||||
const msg = this._msg_by_id.get(id)
|
||||
if (!msg) return ''
|
||||
if (msg.media_uri || msg.media_kind) return ''
|
||||
const body = msg.body ?? ''
|
||||
return media_type(body) ? '' : body
|
||||
}
|
||||
|
||||
@ $mol_mem_key // ← FIX: make reactive
|
||||
msg_media(id: string): $mol_view[] {
|
||||
const body = this._msg_by_id.get(id)?.body.trim() ?? ''
|
||||
const type = media_type(body)
|
||||
const msg = this._msg_by_id.get(id)
|
||||
if (!msg) return []
|
||||
const uri = msg.media_uri || msg.body.trim()
|
||||
const type = msg.media_kind ?? (msg.media_uri ? media_type(uri) : media_type(msg.body.trim()))
|
||||
if (type === 'image') return [this.Msg_image(id)]
|
||||
if (type === 'audio') return [this.Msg_audio(id)]
|
||||
if (type === 'link') return [this.Msg_link(id)]
|
||||
|
|
@ -1488,13 +1537,13 @@ private _handle_message(el: Element) {
|
|||
}
|
||||
|
||||
@ $mol_mem_key // ← FIX: make reactive
|
||||
msg_image_uri(id: string) { return this._msg_by_id.get(id)?.body.trim() ?? '' }
|
||||
msg_image_uri(id: string) { const msg = this._msg_by_id.get(id); return msg?.media_uri || (msg?.body.trim() ?? '') }
|
||||
|
||||
@ $mol_mem_key // ← FIX: make reactive
|
||||
msg_audio_src(id: string) { return this._msg_by_id.get(id)?.body.trim() ?? '' }
|
||||
msg_audio_src(id: string) { const msg = this._msg_by_id.get(id); return msg?.media_uri || (msg?.body.trim() ?? '') }
|
||||
|
||||
@ $mol_mem_key // ← FIX: make reactive
|
||||
msg_link_uri(id: string) { return this._msg_by_id.get(id)?.body.trim() ?? '' }
|
||||
msg_link_uri(id: string) { const msg = this._msg_by_id.get(id); return msg?.media_uri || (msg?.body.trim() ?? '') }
|
||||
|
||||
@ $mol_mem_key
|
||||
msg_time(id: string) {
|
||||
|
|
@ -1881,6 +1930,13 @@ private _handle_message(el: Element) {
|
|||
account, peer,
|
||||
id: msg.id, from: msg.from, to: msg.to, body: msg.body, time: msg.time,
|
||||
...(msg.nick !== undefined ? { nick: msg.nick } : {}),
|
||||
...(msg.media_uri !== undefined ? { media_uri: msg.media_uri } : {}),
|
||||
...(msg.media_mime !== undefined ? { media_mime: msg.media_mime } : {}),
|
||||
...(msg.media_name !== undefined ? { media_name: msg.media_name } : {}),
|
||||
...(msg.media_kind !== undefined ? { media_kind: msg.media_kind } : {}),
|
||||
...(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 } : {}),
|
||||
}
|
||||
await Messages.put(doc, [account, msg.id])
|
||||
} catch (e) { console.warn('[xmpp] persist failed', e) }
|
||||
|
|
@ -1901,6 +1957,13 @@ private _handle_message(el: Element) {
|
|||
id: doc.id, from: doc.from, to: doc.to,
|
||||
body: doc.body, time: doc.time,
|
||||
...(doc.nick !== undefined ? { nick: doc.nick } : {}),
|
||||
...(doc.media_uri !== undefined ? { media_uri: doc.media_uri } : {}),
|
||||
...(doc.media_mime !== undefined ? { media_mime: doc.media_mime } : {}),
|
||||
...(doc.media_name !== undefined ? { media_name: doc.media_name } : {}),
|
||||
...(doc.media_kind !== undefined ? { media_kind: doc.media_kind } : {}),
|
||||
...(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 } : {}),
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
|
|
@ -2000,7 +2063,16 @@ private _handle_message(el: Element) {
|
|||
const id = `l${ Date.now() }_${ Math.random().toString(36).slice(2) }`
|
||||
const type = this._rooms.has(jid) ? 'groupchat' : 'chat'
|
||||
this._conn.send_media_sharing(jid, body, file.name, file.size, file.type || 'application/octet-stream', 'sha-256', hash, get, type, id)
|
||||
this._add_message({ id, from: this.my_jid().split('/')[0], to: jid, body, time: Date.now() })
|
||||
this._add_message({
|
||||
id, from: this.my_jid().split('/')[0], to: jid, body, time: Date.now(),
|
||||
media_uri: get,
|
||||
media_mime: file.type || 'application/octet-stream',
|
||||
media_name: file.name,
|
||||
media_kind: media_type_from_mime(file.type) ?? media_type(get),
|
||||
media_size: file.size,
|
||||
media_hash: hash,
|
||||
media_hash_algo: 'sha-256',
|
||||
})
|
||||
this._scroll_to_bottom(jid)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue