diff --git a/nodejs-project/nodejs-project/src/views/invites_view.js b/nodejs-project/nodejs-project/src/views/invites_view.js index 299db11e..ba3e03a2 100644 --- a/nodejs-project/nodejs-project/src/views/invites_view.js +++ b/nodejs-project/nodejs-project/src/views/invites_view.js @@ -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 path = require("path"); const fs = require('fs'); @@ -75,16 +75,31 @@ const invitesView = async ({ invitesEnabled }) => { const activePubs = filteredPubs.filter(pubItem => !hasError(pubItem)); const unreachablePubs = pubs.filter(hasError); - const renderPubTable = (items, actionFn) => table({ class: 'block-info-table' }, - pubTableHeader(), - items.map(pubItem => tr( - td(pubItem.host || '—'), - td(String(pubItem.port || 8008)), - td(String(pubItem.announcers || 0)), - td(a({ href: encodePubLink(pubItem.key), class: 'user-link' }, pubItem.key)), - td(actionFn(pubItem)) - )) - ); + const renderPubTable = async (items, actionFn) => { + const rows = await Promise.all(items.map(async pubItem => { + let qrSvg = ''; + try { + if (pubItem.key) qrSvg = await QRCode.toString(pubItem.key, { type: 'svg' }); + } catch {} + return tr( + 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 description = i18n.invitesDescription; @@ -130,7 +145,7 @@ const invitesView = async ({ invitesEnabled }) => { hr(), h2(`${i18n.invitesAcceptedInvites} (${activePubs.length})`), activePubs.length - ? renderPubTable(activePubs, pubItem => + ? await renderPubTable(activePubs, pubItem => form({ action: '/settings/invite/unfollow', method: 'post' }, input({ type: 'hidden', name: 'key', value: pubItem.key }), button({ type: 'submit' }, i18n.invitesUnfollow) @@ -140,7 +155,7 @@ const invitesView = async ({ invitesEnabled }) => { hr(), h2(`${i18n.invitesUnfollowedInvites} (${unfollowed.length})`), unfollowed.length - ? renderPubTable(unfollowed, pubItem => + ? await renderPubTable(unfollowed, pubItem => form({ action: '/settings/invite/follow', method: 'post' }, input({ type: 'hidden', name: 'key', value: pubItem.key }), input({ type: 'hidden', name: 'host', value: pubItem.host || '' }), @@ -152,7 +167,7 @@ const invitesView = async ({ invitesEnabled }) => { hr(), h2(`${i18n.invitesUnreachablePubs} (${unreachablePubs.length})`), unreachablePubs.length - ? renderPubTable(unreachablePubs, pubItem => + ? await renderPubTable(unreachablePubs, pubItem => div({ class: 'error-box' }, p({ class: 'error-title' }, i18n.errorDetails), p({ class: 'error-pre' }, String(pubItem.error || i18n.genericError))