aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-04 23:40:47 -0700
committerAdam Soltys <[email protected]>2026-10-04 23:40:47 -0700
commit1a2a31365a09a9a5cbd4a43fac6481f3c2f57d72 (patch)
tree01b86f5c3909c1ae524caec659b0c01094d3cdca /src/features
parentf1b0aeb9b3ee2b82808ad5a50e53fcc53f4e820b (diff)
downloadcoinosv3-1a2a31365a09a9a5cbd4a43fac6481f3c2f57d72.tar.gz
coinosv3-1a2a31365a09a9a5cbd4a43fac6481f3c2f57d72.zip
Show popular Concord communities on the Messages screen
Diffstat (limited to 'src/features')
-rw-r--r--src/features/messages.js42
1 files changed, 28 insertions, 14 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 0a0a536..a9b21b0 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -1858,13 +1858,13 @@ export function messagesFeature(ctx) {
}
// ---- community discovery: the Vector Hub's public listing as suggestions
- // under Join. Fetched on demand (it's a megabyte, mostly icons) and kept
+ // on Messages and under Browse. Fetched when Messages opens and kept
// for the session; every listing is a CORD-05 link, which is exactly what
// the paste box takes, so a tap previews it like a pasted invite.
const HUB_URL = 'https://vectorapp.io/api/hub';
const hub = { state: 'idle', list: [] };
async function loadHub() {
- if (hub.state === 'loading' || hub.state === 'ready') return;
+ if (hub.state !== 'idle') return;
hub.state = 'loading'; scheduleRepaint();
try {
const res = await fetch(HUB_URL, { signal: AbortSignal.timeout(20_000) });
@@ -1882,12 +1882,16 @@ export function messagesFeature(ctx) {
} catch { hub.state = 'error'; }
scheduleRepaint();
}
- function hubRows() {
- const q = (ui.msgJoinText || '').trim().toLowerCase();
+ function hubRows({ preview = false } = {}) {
+ const q = preview ? '' : (ui.msgJoinText || '').trim().toLowerCase();
const have = new Set(communities().map((jm) => (rooms.get(jm.community_id)?.folded?.metadata?.name || jm.name || '').toLowerCase()));
- const rows = hub.list.filter((c) => !q || [c.name, c.description, c.category].some((x) => x.toLowerCase().includes(q)));
+ let rows = hub.list.filter((c) => !q || [c.name, c.description, c.category].some((x) => x.toLowerCase().includes(q)));
+ if (preview) rows = rows.filter((c) => !have.has(c.name.toLowerCase())).slice(0, 6);
+ if (!rows.length) return [h('div', { class: 'small muted' }, t(preview ? 'msgHubNoSuggestions' : 'msgNoMatches'))];
return rows.map((c) => h('div', {
- class: 'item chat-thread-row clickable', onClick: () => joinFromText(c.invite),
+ class: 'item chat-thread-row clickable', role: 'button', tabindex: '0',
+ onClick: (e) => { e.stopPropagation(); joinFromText(c.invite); },
+ onKeydown: (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); joinFromText(c.invite); } },
},
c.icon ? h('img', { class: 'chat-avatar hub-icon', src: c.icon, alt: '' }) : h('div', { class: 'chat-avatar fallback' }, c.name.slice(0, 2)),
h('div', { class: 'col grow', style: 'min-width:0;gap:1px' },
@@ -1898,6 +1902,21 @@ export function messagesFeature(ctx) {
(c.members ? t('msgMembers', { n: c.members }) + ' · ' : '') + c.category + (c.description ? ' · ' + c.description : '')))));
}
+ function hubPanel({ preview = false } = {}) {
+ loadHub().catch(() => {});
+ return h('div', { class: 'col community-discovery', style: 'gap:8px' },
+ h('div', { class: 'row between', style: 'align-items:baseline' },
+ h('h3', { style: 'margin:0' }, t(preview ? 'msgPopularCommunities' : 'msgDiscoverTitle')),
+ preview ? h('button', { class: 'btn-sm', onClick: (e) => { e.stopPropagation(); ui.msgJoinText = ''; ui.msgHomePanel = 'join'; render(); } }, t('msgBrowseCommunities')) : null),
+ h('a', { class: 'small muted', href: 'https://vectorapp.io/hub/', target: '_blank', rel: 'noopener noreferrer' }, t('msgHubCredit')),
+ hub.state === 'error' ? h('div', { class: 'row gap6', style: 'align-items:center' },
+ h('span', { class: 'small muted grow' }, t('msgHubFailed')),
+ h('button', { class: 'btn-sm', onClick: (e) => { e.stopPropagation(); hub.state = 'idle'; loadHub().catch(() => {}); } }, t('retry')))
+ : hub.state !== 'ready' ? h('div', { class: 'row gap6', style: 'align-items:center;padding:6px 0' }, h('span', { class: 'spinner sm' }), h('span', { class: 'small muted' }, t('msgHubLoading')))
+ : h('div', { class: 'list' + (preview ? '' : ' hub-list') }, hubRows({ preview })),
+ h('div', { class: 'small muted' }, t('msgWhiteNoiseDiscovery')));
+ }
+
function joinFromText(text) {
const parsed = parseInviteLink(text);
if (!parsed) { toast(t('msgBadInvite')); return; }
@@ -11091,7 +11110,6 @@ export function messagesFeature(ctx) {
h('button', { class: 'btn-sm', onClick: () => { ui.msgHomePanel = ui.msgHomePanel === 'create' ? null : 'create'; render(); } }, t('msgCreate')))));
if (pendingLink && pendingLink.where === 'communities') kids.push(linkInviteCard());
if (ui.msgHomePanel === 'join') {
- loadHub().catch(() => {});
const isLink = !!parseInviteLink(ui.msgJoinText || '');
kids.push(h('div', { class: 'col', style: 'gap:8px' },
h('div', { class: 'row gap6' },
@@ -11101,13 +11119,7 @@ export function messagesFeature(ctx) {
onKeydown: (e) => { if (e.key === 'Enter' && isLink) joinFromText(ui.msgJoinText); },
}),
isLink ? h('button', { class: 'btn-sm btn-primary', onClick: () => joinFromText(ui.msgJoinText) }, t('msgJoin')) : null),
- isLink ? null : h('div', { class: 'row between', style: 'align-items:baseline' },
- h('span', { class: 'small muted' }, t('msgDiscoverTitle')),
- h('a', { class: 'small muted', href: 'https://vectorapp.io/hub/', target: '_blank', rel: 'noopener noreferrer' }, t('msgHubCredit'))),
- isLink ? null
- : hub.state === 'error' ? h('div', { class: 'small muted' }, t('msgHubFailed'))
- : hub.state !== 'ready' ? h('div', { class: 'row gap6', style: 'align-items:center;padding:6px 0' }, h('span', { class: 'spinner sm' }), h('span', { class: 'small muted' }, t('msgHubLoading')))
- : h('div', { class: 'list hub-list' }, hubRows())));
+ isLink ? null : hubPanel()));
}
if (ui.msgHomePanel === 'create')
kids.push(h('div', { class: 'row gap6' },
@@ -11154,6 +11166,8 @@ export function messagesFeature(ctx) {
kids.push(h('button', { class: 'linklike small', onClick: () => { ui.msgAllRooms = false; render(); } },
t('msgShowFewerDms')));
+ if (ui.msgHomePanel !== 'join') kids.push(hubPanel({ preview: true }));
+
return h('div', { class: 'card col chat-page', style: 'gap:10px' }, ...kids);
}