aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-06 19:48:19 -0700
committerAdam Soltys <[email protected]>2026-09-06 19:48:19 -0700
commitad496c0fb53706f221241dd48440c68d3333937d (patch)
tree0adff8309cee22c52389010a9c9095a883c87f9b /src
parent966ef24380bc25fb0f0a064358c802347cb965a3 (diff)
downloadcoinosv3-ad496c0fb53706f221241dd48440c68d3333937d.tar.gz
coinosv3-ad496c0fb53706f221241dd48440c68d3333937d.zip
Chat reactions render inside the bubble (Telegram-style), not floating below it.
Reactions were a sibling of the bubble (a separate row under it), so they hung outside the message. Moved the reaction pills inside .chat-bubble, tucked under the text, across all three renderers: community channel messages, DMs, and the read-only public /chat page. The pill background and the "your reaction" highlight border now derive from the bubble's own text colour (color-mix on currentColor), so they read correctly on both the plain bubble and the accent "me" bubble. Tapping a pill stops propagation so it toggles the reaction without also opening the message action sheet. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
Diffstat (limited to 'src')
-rw-r--r--src/features/messages.js26
-rw-r--r--src/public-chat.js8
-rw-r--r--src/style.css12
3 files changed, 25 insertions, 21 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 66a1270..4455711 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -2984,16 +2984,17 @@ export function messagesFeature(ctx) {
edit ? h('span', { class: 'chat-edited' }, ' ', t('msgEdited')) : null,
mine
? h('button', { class: 'chat-del', title: t('msgDelete'), onClick: () => deleteMessage(room, chId, m) }, '×')
- : null),
- counts.size
- ? h('div', { class: 'chat-reacts' },
- [...counts.entries()].map(([emoji, n]) =>
- h('span', {
- class: 'chat-react clickable' + (emoji === myReact ? ' on' : ''),
- title: t('msgReact'),
- onClick: () => sendReaction(room, chId, m, emoji),
- }, emoji, n > 1 ? ' ' + n : '')))
- : null)
+ : null,
+ // reactions tuck inside the bubble, under the text (Telegram-style)
+ counts.size
+ ? h('div', { class: 'chat-reacts' },
+ [...counts.entries()].map(([emoji, n]) =>
+ h('span', {
+ class: 'chat-react clickable' + (emoji === myReact ? ' on' : ''),
+ title: t('msgReact'),
+ onClick: (e) => { e.stopPropagation(); sendReaction(room, chId, m, emoji); },
+ }, emoji, n > 1 ? ' ' + n : '')))
+ : null))
);
});
}
@@ -3206,7 +3207,7 @@ export function messagesFeature(ctx) {
return h('div', { class: 'chat-reacts' },
[...counts.entries()].map(([emoji, n]) => h('span', {
class: 'chat-react clickable' + (emoji === myReact ? ' on' : ''),
- onClick: () => sendDmReaction(peer, m, emoji),
+ onClick: (e) => { e.stopPropagation(); sendDmReaction(peer, m, emoji); },
}, emoji, n > 1 ? ' ' + n : '')));
};
const dmReplyBar = () => {
@@ -3246,8 +3247,7 @@ export function messagesFeature(ctx) {
ui.msgSheet = ui.msgSheet === m.rumor.id ? null : m.rumor.id;
render();
},
- }, dmQuote(m), ...noteBody(m.rumor.content)),
- dmChips(m),
+ }, dmQuote(m), ...noteBody(m.rumor.content), dmChips(m)),
h('div', { class: 'chat-time' }, timeLabel(m.rumor.created_at * 1000)))))
: [h('div', { class: 'muted small', style: 'text-align:center;padding:24px 0' }, t('msgNoDmsYet'))])),
dmReplyBar(),
diff --git a/src/public-chat.js b/src/public-chat.js
index d3dd042..0cdadc4 100644
--- a/src/public-chat.js
+++ b/src/public-chat.js
@@ -253,10 +253,10 @@ export function mountPublicChat() {
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' }, 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()]
- .map(([emoji, n]) => h('span', { class: 'chat-react' }, emoji + (n > 1 ? ' ' + n : '')))) : null)));
+ 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()]
+ .map(([emoji, n]) => h('span', { class: 'chat-react' }, emoji + (n > 1 ? ' ' + n : '')))) : null))));
}
if (stick) log.scrollTop = log.scrollHeight;
}
diff --git a/src/style.css b/src/style.css
index a99212d..6779579 100644
--- a/src/style.css
+++ b/src/style.css
@@ -469,13 +469,17 @@ hr.divider { margin: 16px 0; }
display: flex; align-items: center; gap: 8px; padding: 7px 12px;
border-top: 1px solid var(--line); background: var(--surface2); flex: 0 0 auto;
}
-.chat-reacts { display: flex; gap: 5px; margin-top: 3px; flex-wrap: wrap; }
+/* Reactions sit INSIDE the bubble, tucked under the text (Telegram-style).
+ The pill tint/border derive from the bubble's own text colour, so they read
+ correctly on both the plain bubble and the accent "me" bubble. */
+.chat-reacts { display: flex; gap: 5px; margin-top: 5px; flex-wrap: wrap; }
.chat-react {
- font-size: 12px; background: var(--paper); border: 1px solid var(--line);
- border-radius: 9px; padding: 1px 7px;
+ font-size: 12px; border-radius: 10px; padding: 1px 7px;
+ background: color-mix(in srgb, currentColor 13%, transparent);
+ border: 1px solid transparent;
}
.chat-react.clickable { cursor: pointer; }
-.chat-react.on { border-color: var(--accent); color: var(--accent); }
+.chat-react.on { border-color: color-mix(in srgb, currentColor 55%, transparent); font-weight: 600; }
.chat-compose {
display: flex; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--line);
align-items: center; flex: 0 0 auto;