diff --git a/xmpp.view.css b/xmpp.view.css index 125c33f..4baf28e 100644 --- a/xmpp.view.css +++ b/xmpp.view.css @@ -153,6 +153,36 @@ gap: .5rem; padding: .5rem .75rem; color: inherit; + justify-content: space-between; +} + +[xmpp_Contact_info] { + display: flex; + flex-direction: column; + flex: 1; + min-width: 0; +} + +[xmpp_Contact_name] { + font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +[xmpp_Last_message] { + font-size: .875rem; + color: var(--mol_theme_shade, #666); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-top: .125rem; +} + +[xmpp_Unread_dot] { + font-size: 1.2rem; + color: var(--mol_theme_focus, #4a9eff); + flex-shrink: 0; } [xmpp_Roster_contact_link]:hover { diff --git a/xmpp.view.tree b/xmpp.view.tree index d813998..2ff7697 100644 --- a/xmpp.view.tree +++ b/xmpp.view.tree @@ -284,9 +284,17 @@ $xmpp $mol_view ^ src <= contact_avatar_uri* \ alt \ - <= Contact_label* $mol_view + <= Contact_info* $mol_view sub / - <= contact_display* \ + <= Contact_name* $mol_view + sub / + <= contact_name* \ + <= Last_message* $mol_view + sub / + <= last_message* \ + <= Unread_dot* $mol_view + sub / + <= unread_dot* \ <= Pin_button* $mol_button_minor click? <=> toggle_pin*? null sub / diff --git a/xmpp.view.ts b/xmpp.view.ts index 53285bc..03e5018 100644 --- a/xmpp.view.ts +++ b/xmpp.view.ts @@ -1042,6 +1042,8 @@ private _handle_message(el: Element) { folder() { return (this.$.$mol_state_arg.value('folder') as string | null) ?? '' } + chat() { return (this.$.$mol_state_arg.value('chat') as string | null) ?? '' } + // ── User folders (drag chat → drop on +zone or existing folder) ────── @ $mol_mem @@ -1483,6 +1485,44 @@ private _handle_message(el: Element) { return name + dot } + contact_name(jid: string) { + const room = this._rooms.get(jid) + if (room) return `# ${ room.name || jid.split('@')[0] }` + const c = this.contacts().find(x => x.jid === jid) + return c?.name && c.name !== jid ? c.name : jid + } + + @ $mol_mem_key + last_message(jid: string) { + this.messages_ver() + const bare = this.my_jid().split('/')[0] + const msgs = this._msgs + .filter(m => m.from === jid || m.to === jid + || (m.from === bare && m.to === jid) + || (m.from === jid && m.to === bare)) + .sort((a, b) => a.time - b.time) + const last = msgs[msgs.length - 1] + return last ? (last.body || '[media]') : '' + } + + @ $mol_mem_key + unread_indicator(jid: string) { + this.messages_ver() + const current_chat = this.chat() + if (current_chat === jid && this.scroll_at_bottom(jid)) return false + const bare = this.my_jid().split('/')[0] + const msgs = this._msgs + .filter(m => m.from === jid || m.to === jid + || (m.from === bare && m.to === jid) + || (m.from === jid && m.to === bare)) + return msgs.length > 0 + } + + @ $mol_mem_key + unread_dot(jid: string) { + return this.unread_indicator(jid) ? '●' : '' + } + open_chat(jid: string) { this.$.$mol_state_arg.value('chat', jid) } do_new_chat() {