aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/features/messages.js23
-rw-r--r--src/style.css12
2 files changed, 30 insertions, 5 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index e9e238a..1fef866 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -4208,10 +4208,21 @@ export function messagesFeature(ctx) {
}
// Zap a chat message or DM: same one-tap flow as a post (ark first,
// Lightning fallback, the amount remembered from the first time).
- function zapMessage(pk, id) {
- zapNote(pk, { id });
+ function zapMessage(pk, id, origin) {
+ zapNote(pk, { id }, origin);
recheckZap(id);
}
+ // The one-tap ⚡ beside someone else's bubble: zap without opening the
+ // message sheet first (tap pays the default, hold sets the amount).
+ function msgZapBtn(pk, id) {
+ if (!canZapPk(pk) || isMe(pk)) return null;
+ if (zapSoundOn()) warmZapSound();
+ return h('button', {
+ class: 'chat-zap', title: t('msgZap'), 'aria-label': t('msgZap'),
+ ...holdable((el) => openZapSettings(pk, id, el.getBoundingClientRect()),
+ (e) => { e.stopPropagation(); zapMessage(pk, id, e.currentTarget.getBoundingClientRect()); }),
+ }, h('span', { style: 'display:flex', html: BOLT_SVG }));
+ }
const notesCache = new Map(); // pk -> { status: 'loading'|'ready', notes: [kind-1 events] }
// NIP-65: where this author actually writes. Their notes and threads live
// there first — our default relays are just the common ground.
@@ -12055,7 +12066,7 @@ export function messagesFeature(ctx) {
displayName(m.author),
m.author === room.jm.owner ? h('span', { class: 'chat-badge' }, t('msgAdmin')) : null),
h('span', { class: 'chat-time' }, timeLabel(tms))),
- h('div', {
+ h('div', { class: 'chat-line' }, h('div', {
class: 'chat-bubble clickable' + (emojiJumbo(text, em) ? ' jumbo' : ''),
// Telegram-style: a tap on the message opens its action sheet
// (quick reactions, reply, copy, delete). Links, images and the
@@ -12089,7 +12100,8 @@ export function messagesFeature(ctx) {
title: t('msgReact'),
onClick: (e) => { e.stopPropagation(); sendReaction(room, chId, m, emoji); },
}, reactNode(emoji), n > 1 ? ' ' + n : '')))
- : null)(zapChip(m.rumor.id, { cls: 'chat-react', onClick: canZapPk(m.author) ? () => zapMessage(m.author, m.rumor.id) : null, onHold: () => openZapSettings(m.author, m.rumor.id) }))))
+ : null)(zapChip(m.rumor.id, { cls: 'chat-react', onClick: canZapPk(m.author) ? () => zapMessage(m.author, m.rumor.id) : null, onHold: () => openZapSettings(m.author, m.rumor.id) }))),
+ msgZapBtn(m.author, m.rumor.id)))
);
});
}
@@ -12642,7 +12654,7 @@ export function messagesFeature(ctx) {
? msgs.map((m) =>
h('div', { class: 'chat-row dm' + (m.mine ? ' mine' : '') },
h('div', { class: 'chat-body' },
- h('div', {
+ h('div', { class: 'chat-line' }, h('div', {
class: 'chat-bubble clickable' + (m.mine ? ' me' : '') + (emojiJumbo(m.rumor.content, emojiTagMap(m.rumor.tags)) ? ' jumbo' : ''),
// same tap-for-actions as community bubbles
onClick: (e) => {
@@ -12653,6 +12665,7 @@ export function messagesFeature(ctx) {
render();
},
}, dmQuote(m), ...(m.rumor.kind === 15 ? [] : noteBody(m.rumor.content, 0, emojiTagMap(m.rumor.tags))), ...attachmentNodes(m.rumor, m.wn), dmChips(m)),
+ m.mine ? null : msgZapBtn(m.rumor.pubkey, m.rumor.id)),
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/style.css b/src/style.css
index 3b715ec..7a8d3c0 100644
--- a/src/style.css
+++ b/src/style.css
@@ -580,6 +580,18 @@ html:has(#app .chat-card), body:has(#app .chat-card) { overflow: hidden; }
.zap-tally { display: inline-flex; align-items: center; gap: 2px; font-size: 11px; font-weight: 650; color: #e6a800; white-space: nowrap; flex-shrink: 0; }
.zap-tally.chat-react { font-size: 12px; padding: 1px 7px 1px 5px; }
.zap-tally.clickable { cursor: pointer; }
+/* the one-tap ⚡ beside someone else's chat bubble */
+.chat-line { display: flex; align-items: center; gap: 4px; }
+.chat-row.dm.mine .chat-line { justify-content: flex-end; }
+.chat-line > .chat-bubble { min-width: 0; }
+.chat-zap {
+ flex-shrink: 0; width: 28px; height: 28px; padding: 0; border: none; border-radius: 50%;
+ display: flex; align-items: center; justify-content: center;
+ background: none; color: var(--muted); cursor: pointer;
+ -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
+}
+.chat-zap:hover { color: #e6a800; background: var(--paper); }
+.chat-zap svg { width: 17px; height: 17px; }
/* A tap-local lightning strike, followed by a floating amount and embers. */
.zap-fx { position: fixed; inset: 0; z-index: 10000; pointer-events: none; overflow: hidden; contain: strict; }
.zap-fx-impact { position: absolute; left: var(--zap-x); top: var(--zap-y); width: 0; height: 0; color: #ffc83d; }