* messages times
* sending message on Enter key * multiline message * new chat on contact * paste file from buffer on ctrl+v
This commit is contained in:
parent
596f2a1210
commit
3b55ecc693
3 changed files with 111 additions and 2 deletions
|
|
@ -40,6 +40,26 @@
|
|||
|
||||
[xmpp_Compose_input] {
|
||||
flex: 1;
|
||||
min-height: 2.5rem;
|
||||
max-height: 8rem;
|
||||
resize: vertical;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
[xmpp_Msg_head] {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
gap: .5rem;
|
||||
}
|
||||
|
||||
[xmpp_Msg_time] {
|
||||
font-size: .7rem;
|
||||
opacity: .5;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* media */
|
||||
|
|
|
|||
|
|
@ -54,6 +54,17 @@ $xmpp $mol_book2
|
|||
body /
|
||||
<= Roster_list $mol_list
|
||||
rows <= roster_rows /
|
||||
<= New_chat_form $mol_form
|
||||
form_fields /
|
||||
<= New_chat_field $mol_form_field
|
||||
name @ \New chat with
|
||||
control <= New_chat_input $mol_string
|
||||
hint \user@example.com
|
||||
value? <=> new_chat_jid? \
|
||||
buttons /
|
||||
<= New_chat_button $mol_button_major
|
||||
title @ \Start chat
|
||||
click? <=> do_new_chat? null
|
||||
<= Room_join_form $mol_form
|
||||
form_fields /
|
||||
<= Room_jid_field $mol_form_field
|
||||
|
|
@ -96,8 +107,13 @@ $xmpp $mol_book2
|
|||
rows <= message_rows* /
|
||||
foot /
|
||||
<= Compose_input* $mol_string
|
||||
dom_name \textarea
|
||||
hint @ \Type a message…
|
||||
value? <=> compose*? \
|
||||
event *
|
||||
^
|
||||
keydown? <=> compose_keydown*? null
|
||||
paste? <=> compose_paste*? null
|
||||
<= Attach_button* $mol_button_minor
|
||||
title @ \Attach
|
||||
click? <=> do_attach*? null
|
||||
|
|
@ -131,9 +147,14 @@ $xmpp $mol_book2
|
|||
alt \
|
||||
<= Msg_content* $mol_view
|
||||
sub /
|
||||
<= Msg_from* $mol_view
|
||||
<= Msg_head* $mol_view
|
||||
sub /
|
||||
<= msg_from* \
|
||||
<= Msg_from* $mol_view
|
||||
sub /
|
||||
<= msg_from* \
|
||||
<= Msg_time* $mol_view
|
||||
sub /
|
||||
<= msg_time* \
|
||||
<= Msg_body* $mol_view
|
||||
sub /
|
||||
<= msg_body* \
|
||||
|
|
|
|||
68
xmpp.view.ts
68
xmpp.view.ts
|
|
@ -702,6 +702,9 @@ private _handle_message(el: Element) {
|
|||
@ $mol_mem
|
||||
room_nick(next?: string) { return next ?? '' }
|
||||
|
||||
@ $mol_mem
|
||||
new_chat_jid(next?: string) { return next ?? '' }
|
||||
|
||||
@ $mol_mem_key
|
||||
compose(_jid: string, next?: string) { return next ?? '' }
|
||||
|
||||
|
|
@ -905,6 +908,16 @@ private _handle_message(el: Element) {
|
|||
|
||||
open_chat(jid: string) { this.$.$mol_state_arg.value('chat', jid) }
|
||||
|
||||
do_new_chat() {
|
||||
const jid = this.new_chat_jid().trim()
|
||||
if (!jid || !jid.includes('@')) {
|
||||
this.error_text('Enter a valid JID like user@server')
|
||||
return
|
||||
}
|
||||
this.new_chat_jid('')
|
||||
this.$.$mol_state_arg.value('chat', jid)
|
||||
}
|
||||
|
||||
// ── Rooms ─────────────────────────────────────────────────────────────
|
||||
|
||||
do_join_room() {
|
||||
|
|
@ -999,6 +1012,23 @@ private _handle_message(el: Element) {
|
|||
@ $mol_mem_key // ← FIX: make reactive
|
||||
msg_link_uri(id: string) { return this._msg_by_id.get(id)?.body.trim() ?? '' }
|
||||
|
||||
@ $mol_mem_key
|
||||
msg_time(id: string) {
|
||||
const msg = this._msg_by_id.get(id)
|
||||
if (!msg) return ''
|
||||
const d = new Date(msg.time)
|
||||
const same_day = (() => {
|
||||
const now = new Date()
|
||||
return d.getFullYear() === now.getFullYear()
|
||||
&& d.getMonth() === now.getMonth()
|
||||
&& d.getDate() === now.getDate()
|
||||
})()
|
||||
const time = d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
||||
if (same_day) return time
|
||||
const date = d.toLocaleDateString([], { day: '2-digit', month: '2-digit' })
|
||||
return `${ date } ${ time }`
|
||||
}
|
||||
|
||||
// XEP-0333 status for outgoing messages.
|
||||
// ✓ — sent locally; ✓✓ — peer received; ✓✓ (filled) — peer displayed.
|
||||
@ $mol_mem_key
|
||||
|
|
@ -1014,6 +1044,37 @@ private _handle_message(el: Element) {
|
|||
return '✓'
|
||||
}
|
||||
|
||||
// ── Compose input event handlers ──────────────────────────────────────
|
||||
|
||||
// Enter sends; Shift+Enter inserts a newline (browser default).
|
||||
compose_keydown(jid: string, e?: Event | null) {
|
||||
const ke = e as KeyboardEvent | null | undefined
|
||||
if (!ke) return null
|
||||
if (ke.key === 'Enter' && !ke.shiftKey && !ke.ctrlKey && !ke.altKey && !ke.metaKey) {
|
||||
ke.preventDefault()
|
||||
this.do_send(jid)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Paste image / file from clipboard → upload via XEP-0363 and send the link.
|
||||
compose_paste(jid: string, e?: Event | null) {
|
||||
const ce = e as ClipboardEvent | null | undefined
|
||||
const items = ce?.clipboardData?.items
|
||||
if (!items) return null
|
||||
for (const item of Array.from(items)) {
|
||||
if (item.kind === 'file') {
|
||||
const file = item.getAsFile()
|
||||
if (file) {
|
||||
ce!.preventDefault()
|
||||
void this._upload_and_send(jid, file).catch(err => this.error_text(String(err)))
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// ── Send text ─────────────────────────────────────────────────────────
|
||||
|
||||
do_send(jid: string) {
|
||||
|
|
@ -1240,6 +1301,12 @@ private _handle_message(el: Element) {
|
|||
el.addEventListener('scroll', () => {
|
||||
if (el.scrollTop < 200) this._load_more_history(jid)
|
||||
}, { passive: true })
|
||||
// Late-loading images push content; if user is near the bottom, follow.
|
||||
el.addEventListener('load', e => {
|
||||
if (!(e.target instanceof HTMLImageElement)) return
|
||||
const near = el.scrollHeight - el.scrollTop - el.clientHeight < 200
|
||||
if (near) el.scrollTop = el.scrollHeight
|
||||
}, true)
|
||||
} catch {
|
||||
this._scroll_setup.delete(jid)
|
||||
}
|
||||
|
|
@ -1299,6 +1366,7 @@ private _handle_message(el: Element) {
|
|||
const id = `l${ Date.now() }_${ Math.random().toString(36).slice(2) }`
|
||||
this._conn.send_message(jid, get, id)
|
||||
this._add_message({ id, from: this.my_jid().split('/')[0], to: jid, body: get, time: Date.now() })
|
||||
this._scroll_to_bottom(jid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue