aboutsummaryrefslogtreecommitdiffstats
path: root/src/public-chat.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-04 09:26:00 -0700
committerAdam Soltys <[email protected]>2026-09-04 09:26:00 -0700
commitd92dfd42e7b01fd2a4806175136d10872474ca5c (patch)
tree113b754ed455dbe0b0f5a7e3a0b0aa9b6ec17018 /src/public-chat.js
parent760efa2b404d559c6b2f369ddeb1b543b931b93d (diff)
downloadcoinosv3-d92dfd42e7b01fd2a4806175136d10872474ca5c.tar.gz
coinosv3-d92dfd42e7b01fd2a4806175136d10872474ca5c.zip
Telegram-style message action sheet + replies in community chat. Tapping any bubble opens a centered sheet: a quick row of the six reaction emojis (your current one highlighted; picking fires immediately) above Reply / Copy / Delete (delete on own messages only) — replacing the hover-only 🙂 picker, which was invisible on the phones that need it most. Links, images and the hover × keep their own clicks, and a desktop text-selection drag never pops the sheet over the copy. REPLIES are new: Reply arms a composer bar showing who/what you're answering (× to cancel, cleared on channel/room switch), the sent kind-9 e-tags the quoted message, and rows carrying an e-tag render the quoted context above their text — author + one-line snippet, silent when the original is deleted or not held. The public /chat page renders the same quotes. Verified against the built stylesheet: 300px sheet, six even emoji buttons, quote ellipsizes, circular cancel button
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
Diffstat (limited to 'src/public-chat.js')
-rw-r--r--src/public-chat.js10
1 files changed, 9 insertions, 1 deletions
diff --git a/src/public-chat.js b/src/public-chat.js
index 5732f4a..d3dd042 100644
--- a/src/public-chat.js
+++ b/src/public-chat.js
@@ -237,6 +237,14 @@ export function mountPublicChat() {
const edit = edits.get(m.rumor.id);
const content = edit && edit.author === m.author ? edit.rumor.content : m.rumor.content;
const reacts = reactions.get(m.rumor.id);
+ // a reply e-tags its quoted message — render the context when we hold it
+ const replyId = (m.rumor.tags || []).find((x) => x[0] === 'e')?.[1];
+ const replySrc = replyId && !(deletes.get(replyId) || new Set()).size
+ ? (byChannel.get(activeChannel) || new Map()).get(replyId) : null;
+ const quote = replySrc ? h('div', { class: 'chat-quote' },
+ h('span', { class: 'chat-quote-name' }, nameOf(replySrc.author)),
+ h('span', { class: 'chat-quote-text' },
+ String(replySrc.rumor.content || '').replace(/\s+/g, ' ').slice(0, 90))) : null;
log.append(h('div', { class: 'chat-row' + (grouped ? ' grouped' : '') },
grouped ? h('div', { class: 'chat-avatar spacer' }) : avatar(m.author),
h('div', { class: 'chat-body' },
@@ -244,7 +252,7 @@ export function mountPublicChat() {
h('span', { class: 'chat-name' + (m.author === COMMUNITY.owner ? ' owner' : '') }, nameOf(m.author)),
m.author === COMMUNITY.owner ? h('span', { class: 'chat-badge' }, t('msgAdmin')) : null,
h('span', { class: 'chat-time' }, timeAgo(ms / 1000))),
- h('div', { class: 'chat-bubble' }, ...linkedBody(content),
+ h('div', { class: 'chat-bubble' }, quote, ...linkedBody(content),
edit && edit.author === m.author ? h('span', { class: 'chat-edited' }, ' ' + t('msgEdited')) : null),
reacts && reacts.size ? h('div', { class: 'chat-reacts' },
...[...[...reacts.values()].reduce((m2, e) => m2.set(e, (m2.get(e) || 0) + 1), new Map()).entries()]