aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-13 05:31:05 +0000
committerAdam Soltys <[email protected]>2026-10-13 05:31:05 +0000
commita6ed8151a08f540a68fd5362884e4f9b888bca69 (patch)
tree00d22960c57765246e96750ecb780a356ac6d4ce
parentc423cf3b79b088e542709267002b7db27b1a83ff (diff)
downloadcoinosv3-a6ed8151a08f540a68fd5362884e4f9b888bca69.tar.gz
coinosv3-a6ed8151a08f540a68fd5362884e4f9b888bca69.zip
Chat header: Add person is an icon. Adam's screenshot of the Bitcoin Community header: "Add person" wrapped onto two lines in a box twice the height of the bell and the door beside it. It is now a person with a plus, the same size and stroke as those (and lit while its panel is open), with "Add person" as its label and tooltip — in a community's header and a private group's. The form's own Add person button keeps its words. chat-layout-test finds the button by its label too; SHOT_PHONE=path saves the phone-width header.
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
-rw-r--r--src/features/messages.js15
-rw-r--r--tools/chat-layout-test.js7
2 files changed, 15 insertions, 7 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index b09aa17..f4e01b7 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -9707,6 +9707,8 @@ export function messagesFeature(ctx) {
const I_BOOST = ICON('<path d="M17 2.5l3.5 3.5-3.5 3.5"/><path d="M3.5 11.5v-1a4 4 0 0 1 4-4h13"/><path d="M7 21.5L3.5 18 7 14.5"/><path d="M20.5 12.5v1a4 4 0 0 1-4 4h-13"/>');
const I_QUOTE = ICON('<path stroke="none" d="M7.2 17c.5 0 1-.3 1.2-.7l1.4-2.9c.1-.3.2-.6.2-.9V8c0-.6-.4-1-1-1H6c-.6 0-1 .4-1 1v4c0 .6.4 1 1 1h2l-1 2.1c-.5.9.2 1.9 1.2 1.9zm10 0c.5 0 1-.3 1.2-.7l1.4-2.9c.1-.3.2-.6.2-.9V8c0-.6-.4-1-1-1H16c-.6 0-1 .4-1 1v4c0 .6.4 1 1 1h2l-1 2.1c-.5.9.2 1.9 1.2 1.9z"/>', true);
const I_HEART = (on) => ICON('<path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.7l-1-1.1a5.5 5.5 0 1 0-7.8 7.8l1.1 1L12 21l7.7-7.7 1.1-1a5.5 5.5 0 0 0 0-7.7z"/>', on);
+ // a person with a plus: add someone to a group or community
+ const I_ADD_PERSON = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M15 21v-1.5a4.5 4.5 0 0 0-4.5-4.5h-5A4.5 4.5 0 0 0 1 19.5V21"/><circle cx="8" cy="7.5" r="4"/><path d="M19.5 8v6"/><path d="M22.5 11h-6"/></svg>';
const I_ZAP = ICON('<path d="M13 2L4 14h7l-1 8 9-12h-7l1-8z"/>', true);
function noteActions(pk, ev, { canZap }) {
@@ -13710,10 +13712,13 @@ export function messagesFeature(ctx) {
? '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0" fill="none"/></svg>'
: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M13.7 21a2 2 0 0 1-3.4 0"/><path d="M18 8a6 6 0 0 0-9.3-5"/><path d="M6.3 6.3A6 6 0 0 0 6 8c0 7-3 9-3 9h15"/><path d="m2 2 20 20"/></svg>',
}),
+ // a person with a plus, like the bell and the door beside it: the
+ // words wrapped onto two lines in a narrow header
h('button', {
- class: 'btn-sm', title: t('msgInviteTitle'),
+ class: 'btn-sm' + (ui.msgInvitePanel ? ' on' : ''), title: t('msgInviteTitle'), 'aria-label': t('msgAddPerson'),
onClick: () => { ui.msgInvitePanel = !ui.msgInvitePanel; render(); },
- }, t('msgAddPerson')),
+ html: I_ADD_PERSON,
+ }),
// the door out — in the header where anyone looks for it, not in
// the invite panel; one tap arms it, the second leaves
jm.community_id === COMMUNITY.community_id ? null : h('button', {
@@ -14062,8 +14067,10 @@ export function messagesFeature(ctx) {
h('div', { class: 'muted small' }, g.removed ? t('msgGroupGone') : t('msgMembers', { n: accounts.length })))),
h('div', { class: 'row gap6', style: 'align-items:center' },
admin ? h('button', {
- class: 'btn-sm', onClick: () => { ui.msgMembers = !ui.msgMembers; dmSearcher.clear(false); render(); },
- }, t('msgAddPerson')) : null,
+ class: 'btn-sm' + (ui.msgMembers ? ' on' : ''), title: t('msgAddPerson'), 'aria-label': t('msgAddPerson'),
+ onClick: () => { ui.msgMembers = !ui.msgMembers; dmSearcher.clear(false); render(); },
+ html: I_ADD_PERSON,
+ }) : null,
// the door out: one tap arms it, the second leaves (same as a community)
h('button', {
class: 'btn-sm' + (ui.msgLeaveArm ? ' btn-danger' : ''), title: t('msgGroupLeave'), 'aria-label': t('msgGroupLeave'), disabled: ui.msgBusy,
diff --git a/tools/chat-layout-test.js b/tools/chat-layout-test.js
index a6f4162..7c79858 100644
--- a/tools/chat-layout-test.js
+++ b/tools/chat-layout-test.js
@@ -70,7 +70,7 @@ try {
check('composer is on screen', r.found && r.fits, `bottom ${r.bottom} of ${r.vh}`);
// the state from the report: the invite panel open above the log
- await page.evaluate(() => { const b = [...document.querySelectorAll('.chat-head button')].find((e) => /add person/i.test(e.textContent)); if (b) b.click(); });
+ await page.evaluate(() => { const b = [...document.querySelectorAll('.chat-head button')].find((e) => /add person/i.test(e.textContent + ' ' + (e.getAttribute('aria-label') || ''))); if (b) b.click(); });
await sleep(600);
const panelOpen = await page.$('.chat-invite');
r = await composerFits();
@@ -96,7 +96,7 @@ try {
// put it back for the next viewport
await page.evaluate(() => { const e = [...document.querySelectorAll('.chat-head .col')].find((n) => n.className.includes('clickable')); if (e) e.click(); });
await sleep(300);
- await page.evaluate(() => { const b = [...document.querySelectorAll('.chat-head button')].find((e) => /add person/i.test(e.textContent)); if (b) b.click(); });
+ await page.evaluate(() => { const b = [...document.querySelectorAll('.chat-head button')].find((e) => /add person/i.test(e.textContent + ' ' + (e.getAttribute('aria-label') || ''))); if (b) b.click(); });
await sleep(300);
const pinned = await page.evaluate(() => {
@@ -117,6 +117,7 @@ try {
});
check('a long message history scrolls with the name and composer pinned', pinned);
}
+ if (process.env.SHOT_PHONE) await page.screenshot({ path: process.env.SHOT_PHONE }); // the phone-width header
await page.setViewport({ width: 1280, height: 800 });
await sleep(500);
await page.screenshot({ path: shotPath });
@@ -131,7 +132,7 @@ try {
check('and the composer says just "Message"', /^Message/.test(one.placeholder || '') && !/#/.test(one.placeholder || ''), one.placeholder);
console.log('\n[adding a channel]');
- await page.evaluate(() => { const b = [...document.querySelectorAll('.chat-head button')].find((e) => /add person/i.test(e.textContent)); if (b) b.click(); });
+ await page.evaluate(() => { const b = [...document.querySelectorAll('.chat-head button')].find((e) => /add person/i.test(e.textContent + ' ' + (e.getAttribute('aria-label') || ''))); if (b) b.click(); });
await sleep(600);
const gated = await page.evaluate(() => ({
field: !!document.querySelector('input[placeholder*="channel" i]'),