feat: add QR share button to each pub row in invites view
- Import details, summary from hyperaxe - renderPubTable converted to async, generates QR SVG per pub key - Each row: details/summary collapsible QR panel below the key link (same pattern as profile and tribe invite QR, no JS required) - All three renderPubTable calls updated with await - QR falls back silently if key is missing or generation fails Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
16f9189e61
commit
12acd6fd20
1 changed files with 29 additions and 14 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
const { form, button, div, h2, h3, p, section, ul, li, a, br, hr, input, span, table, tr, td } = require("../server/node_modules/hyperaxe");
|
const { form, button, div, h2, h3, p, section, ul, li, a, br, hr, input, span, table, tr, td, details, summary } = require("../server/node_modules/hyperaxe");
|
||||||
const QRCode = require('../server/node_modules/qrcode');
|
const QRCode = require('../server/node_modules/qrcode');
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
@ -75,16 +75,31 @@ const invitesView = async ({ invitesEnabled }) => {
|
||||||
const activePubs = filteredPubs.filter(pubItem => !hasError(pubItem));
|
const activePubs = filteredPubs.filter(pubItem => !hasError(pubItem));
|
||||||
const unreachablePubs = pubs.filter(hasError);
|
const unreachablePubs = pubs.filter(hasError);
|
||||||
|
|
||||||
const renderPubTable = (items, actionFn) => table({ class: 'block-info-table' },
|
const renderPubTable = async (items, actionFn) => {
|
||||||
pubTableHeader(),
|
const rows = await Promise.all(items.map(async pubItem => {
|
||||||
items.map(pubItem => tr(
|
let qrSvg = '';
|
||||||
td(pubItem.host || '—'),
|
try {
|
||||||
td(String(pubItem.port || 8008)),
|
if (pubItem.key) qrSvg = await QRCode.toString(pubItem.key, { type: 'svg' });
|
||||||
td(String(pubItem.announcers || 0)),
|
} catch {}
|
||||||
td(a({ href: encodePubLink(pubItem.key), class: 'user-link' }, pubItem.key)),
|
return tr(
|
||||||
td(actionFn(pubItem))
|
td(pubItem.host || '—'),
|
||||||
))
|
td(String(pubItem.port || 8008)),
|
||||||
);
|
td(String(pubItem.announcers || 0)),
|
||||||
|
td(
|
||||||
|
a({ href: encodePubLink(pubItem.key), class: 'user-link' }, pubItem.key),
|
||||||
|
qrSvg ? details({ class: 'qr-share-details' },
|
||||||
|
summary({ class: 'qr-share-btn' }, '⬡ QR'),
|
||||||
|
div({ class: 'qr-share-panel' },
|
||||||
|
div({ class: 'qr-code', innerHTML: qrSvg }),
|
||||||
|
p({ class: 'qr-share-id' }, pubItem.key)
|
||||||
|
)
|
||||||
|
) : null
|
||||||
|
),
|
||||||
|
td(actionFn(pubItem))
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
return table({ class: 'block-info-table' }, pubTableHeader(), ...rows);
|
||||||
|
};
|
||||||
|
|
||||||
const title = i18n.invites;
|
const title = i18n.invites;
|
||||||
const description = i18n.invitesDescription;
|
const description = i18n.invitesDescription;
|
||||||
|
|
@ -130,7 +145,7 @@ const invitesView = async ({ invitesEnabled }) => {
|
||||||
hr(),
|
hr(),
|
||||||
h2(`${i18n.invitesAcceptedInvites} (${activePubs.length})`),
|
h2(`${i18n.invitesAcceptedInvites} (${activePubs.length})`),
|
||||||
activePubs.length
|
activePubs.length
|
||||||
? renderPubTable(activePubs, pubItem =>
|
? await renderPubTable(activePubs, pubItem =>
|
||||||
form({ action: '/settings/invite/unfollow', method: 'post' },
|
form({ action: '/settings/invite/unfollow', method: 'post' },
|
||||||
input({ type: 'hidden', name: 'key', value: pubItem.key }),
|
input({ type: 'hidden', name: 'key', value: pubItem.key }),
|
||||||
button({ type: 'submit' }, i18n.invitesUnfollow)
|
button({ type: 'submit' }, i18n.invitesUnfollow)
|
||||||
|
|
@ -140,7 +155,7 @@ const invitesView = async ({ invitesEnabled }) => {
|
||||||
hr(),
|
hr(),
|
||||||
h2(`${i18n.invitesUnfollowedInvites} (${unfollowed.length})`),
|
h2(`${i18n.invitesUnfollowedInvites} (${unfollowed.length})`),
|
||||||
unfollowed.length
|
unfollowed.length
|
||||||
? renderPubTable(unfollowed, pubItem =>
|
? await renderPubTable(unfollowed, pubItem =>
|
||||||
form({ action: '/settings/invite/follow', method: 'post' },
|
form({ action: '/settings/invite/follow', method: 'post' },
|
||||||
input({ type: 'hidden', name: 'key', value: pubItem.key }),
|
input({ type: 'hidden', name: 'key', value: pubItem.key }),
|
||||||
input({ type: 'hidden', name: 'host', value: pubItem.host || '' }),
|
input({ type: 'hidden', name: 'host', value: pubItem.host || '' }),
|
||||||
|
|
@ -152,7 +167,7 @@ const invitesView = async ({ invitesEnabled }) => {
|
||||||
hr(),
|
hr(),
|
||||||
h2(`${i18n.invitesUnreachablePubs} (${unreachablePubs.length})`),
|
h2(`${i18n.invitesUnreachablePubs} (${unreachablePubs.length})`),
|
||||||
unreachablePubs.length
|
unreachablePubs.length
|
||||||
? renderPubTable(unreachablePubs, pubItem =>
|
? await renderPubTable(unreachablePubs, pubItem =>
|
||||||
div({ class: 'error-box' },
|
div({ class: 'error-box' },
|
||||||
p({ class: 'error-title' }, i18n.errorDetails),
|
p({ class: 'error-title' }, i18n.errorDetails),
|
||||||
p({ class: 'error-pre' }, String(pubItem.error || i18n.genericError))
|
p({ class: 'error-pre' }, String(pubItem.error || i18n.genericError))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue