167 lines
8.3 KiB
JavaScript
167 lines
8.3 KiB
JavaScript
// Player page rendering and bootstrap script assembly.
|
|
|
|
const Handlebars = require('handlebars');
|
|
const path = require('path');
|
|
const packageMetadata = require('../../package.json');
|
|
const { mediaKind, safeJsonForScript, getPlayerPageTemplate, getPlayerPageAnnouncementsScript, getPlayerPageScript, getPlayerOnboardingLandingScript, getPlayerOnboardingFormScript, getPlayerRuntimeScripts } = require('./render-helpers');
|
|
const { createPageAuthBundle, createPageFetchAuthScript } = require('#src/request-auth');
|
|
const { getFontStylesheetHref } = require('#src/web/lib/media/font-library');
|
|
|
|
const PLAYER_MEDIA_DIR = path.join(__dirname, '..', '..', 'media');
|
|
|
|
function renderPage(template, options) {
|
|
const stylesheetLinks = Array.isArray(options && options.stylesheets) ? options.stylesheets : [];
|
|
return template({
|
|
TITLE: options.title,
|
|
BODY_CLASS: options.bodyClass || '',
|
|
BODY: new Handlebars.SafeString(options.body || ''),
|
|
STYLESHEETS: new Handlebars.SafeString(stylesheetLinks.map(function (href) {
|
|
return `<link rel="stylesheet" href="${Handlebars.escapeExpression(href)}" />`;
|
|
}).join('')),
|
|
SCRIPT_BLOCK: new Handlebars.SafeString(options.script || '')
|
|
});
|
|
}
|
|
|
|
function getPlayerServiceWorkerRegistrationScript() {
|
|
const releaseVersion = encodeURIComponent(String(packageMetadata.version || 'development'));
|
|
return [
|
|
'<script>',
|
|
' if ("serviceWorker" in navigator) {',
|
|
' window.addEventListener("load", function () {',
|
|
' navigator.serviceWorker.register("/sw.js?v=' + releaseVersion + '").catch(function () {',
|
|
' return null;',
|
|
' });',
|
|
' });',
|
|
' }',
|
|
'</script>'
|
|
].join('');
|
|
}
|
|
|
|
function renderOnboardingLandingBody(options) {
|
|
const onboardingCode = String(options && options.pairingCode || '').trim();
|
|
const deviceId = String(options && options.deviceId || '').trim();
|
|
return [
|
|
' <main id="onboarding-shell" class="onboarding-shell">',
|
|
' <section class="onboarding-stage onboarding-stage--landing">',
|
|
' <div class="onboarding-layout">',
|
|
' <div class="onboarding-qr-pane">',
|
|
' <div class="onboarding-qr-frame">',
|
|
' <img id="onboarding-qr" alt="Onboarding QR code" src="data:image/svg+xml;charset=utf-8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 320 320%22%3E%3Crect width=%22320%22 height=%22320%22 rx=%2224%22 fill=%22%23ffffff%22/%3E%3Crect x=%2230%22 y=%2230%22 width=%22260%22 height=%22260%22 rx=%2218%22 fill=%22%23f8fafc%22 stroke=%22%23cbd5e1%22 stroke-width=%223%22 stroke-dasharray=%2212 10%22/%3E%3Cpath d=%22M106 118h108M106 156h108M106 194h72%22 stroke=%22%2394a3b8%22 stroke-width=%2214%22 stroke-linecap=%22round%22/%3E%3Ccircle cx=%22128%22 cy=%22248%22 r=%2212%22 fill=%22%2394a3b8%22/%3E%3Ctext x=%22160%22 y=%2278%22 text-anchor=%22middle%22 fill=%22%230f172a%22 font-family=%22Arial,sans-serif%22 font-size=%2224%22 font-weight=%22700%22%3EQR code loading%3C/text%3E%3Ctext x=%22160%22 y=%22266%22 text-anchor=%22middle%22 fill=%22%234b5563%22 font-family=%22Arial,sans-serif%22 font-size=%2214%22%3EPlease wait%3C/text%3E%3C/svg%3E" />',
|
|
' </div>',
|
|
' <p class="onboarding-qr-caption">Scan this QR code with your phone</p>',
|
|
' </div>',
|
|
' <div class="onboarding-copy-panel">',
|
|
' <p class="onboarding-kicker onboarding-brand-title">Pulse Signage</p>',
|
|
' <h1>Quickly set up with your phone</h1>',
|
|
' <ol class="onboarding-instructions">',
|
|
' <li>Open the camera and scan the QR code.</li>',
|
|
' <li>Log in to Pulse Signage and choose a screen.</li>',
|
|
' </ol>',
|
|
' <div class="onboarding-pin-block">',
|
|
' <span class="onboarding-pin-label">Pairing Code</span>',
|
|
' <strong id="onboarding-pairing-code" class="onboarding-pin">' + Handlebars.escapeExpression(onboardingCode || 'Loading...') + '</strong>',
|
|
' </div>',
|
|
' </div>',
|
|
' </div>',
|
|
' </section>',
|
|
'</main>'
|
|
].join('');
|
|
}
|
|
|
|
function renderOnboardingFormBody(deviceId) {
|
|
return [
|
|
'<main class="onboarding-shell">',
|
|
' <section class="onboarding-card onboarding-card--form">',
|
|
' <p class="onboarding-kicker">Pulse Signage</p>',
|
|
' <h1>Name this client</h1>',
|
|
' <p class="onboarding-copy">Pick an existing screen and give this player a friendly name that will persist after refreshes.</p>',
|
|
' <form id="onboarding-form" class="onboarding-form">',
|
|
' <label>',
|
|
' <span>Pairing code</span>',
|
|
' <input name="pairingCode" type="text" inputmode="numeric" pattern="[0-9]{6}" maxlength="6" required autocomplete="one-time-code" placeholder="Enter the code shown on the kiosk" />',
|
|
' </label>',
|
|
' <label>',
|
|
' <span>Client name</span>',
|
|
' <input name="clientName" type="text" maxlength="255" required placeholder="Lobby player" />',
|
|
' </label>',
|
|
' <label>',
|
|
' <span>Screen</span>',
|
|
' <select name="screenSlug" id="onboarding-screen-select" required>',
|
|
' <option value="">Loading screens...</option>',
|
|
' </select>',
|
|
' </label>',
|
|
' <input type="hidden" name="deviceId" value="' + Handlebars.escapeExpression(deviceId || '') + '" />',
|
|
' <button type="submit">Save client</button>',
|
|
' <div id="onboarding-message" class="onboarding-status"></div>',
|
|
' </form>',
|
|
' </section>',
|
|
'</main>'
|
|
].join('');
|
|
}
|
|
|
|
|
|
function renderOnboardingLandingScript() {
|
|
return getPlayerOnboardingLandingScript()();
|
|
}
|
|
|
|
function renderOnboardingFormScript(deviceId) {
|
|
return getPlayerOnboardingFormScript()({
|
|
DEVICE_ID_JSON: new Handlebars.SafeString(JSON.stringify(deviceId || ''))
|
|
});
|
|
}
|
|
|
|
function renderPlayerPage(slug, initialData) {
|
|
const serviceWorkerScript = getPlayerServiceWorkerRegistrationScript();
|
|
const template = getPlayerPageTemplate();
|
|
const hlsScriptTag = '<script src="/assets/vendor/hls.min.js"></script>';
|
|
const pageAuthToken = createPageAuthBundle({ scope: 'player', slug: String(slug || '').trim() });
|
|
const fontStylesheetHref = getFontStylesheetHref(PLAYER_MEDIA_DIR);
|
|
const bootstrapScript = getPlayerPageScript()({
|
|
SLUG_JSON: new Handlebars.SafeString(JSON.stringify(slug)),
|
|
INITIAL_DATA_JSON: new Handlebars.SafeString(safeJsonForScript(initialData || null)),
|
|
REGION_SCRIPTS: ''
|
|
});
|
|
const runtimeScriptTags = getPlayerRuntimeScripts().map(function (entry) {
|
|
return '<script src="/assets/player-script/' + encodeURIComponent(entry[0]) + '.js?v=' + encodeURIComponent(String(packageMetadata.version || 'development')) + '"></script>';
|
|
}).join('');
|
|
|
|
return renderPage(template, {
|
|
title: 'Screen ' + slug,
|
|
bodyClass: '',
|
|
body: '<div id="app"><div class="empty">Loading screen...</div></div>',
|
|
stylesheets: fontStylesheetHref ? [fontStylesheetHref] : [],
|
|
script: createPageFetchAuthScript(pageAuthToken, '/ws/screens/' + encodeURIComponent(slug || '')) + hlsScriptTag + serviceWorkerScript + runtimeScriptTags + bootstrapScript + '<script>' + getPlayerPageAnnouncementsScript()() + '</script>'
|
|
});
|
|
}
|
|
|
|
function renderPlayerOnboardingLandingPage(options) {
|
|
const pageAuthToken = createPageAuthBundle({ scope: 'onboarding' });
|
|
const fontStylesheetHref = getFontStylesheetHref(PLAYER_MEDIA_DIR);
|
|
return renderPage(getPlayerPageTemplate(), {
|
|
title: 'Onboard player',
|
|
bodyClass: 'onboarding-page',
|
|
body: renderOnboardingLandingBody(options),
|
|
stylesheets: fontStylesheetHref ? [fontStylesheetHref] : [],
|
|
script: createPageFetchAuthScript(pageAuthToken) + getPlayerServiceWorkerRegistrationScript() + renderOnboardingLandingScript()
|
|
});
|
|
}
|
|
|
|
function renderPlayerOnboardingFormPage(deviceId) {
|
|
const pageAuthToken = createPageAuthBundle({ scope: 'onboarding', deviceId: String(deviceId || '').trim() });
|
|
const fontStylesheetHref = getFontStylesheetHref(PLAYER_MEDIA_DIR);
|
|
return renderPage(getPlayerPageTemplate(), {
|
|
title: 'Onboard screen',
|
|
bodyClass: 'onboarding-page',
|
|
body: renderOnboardingFormBody(deviceId),
|
|
stylesheets: fontStylesheetHref ? [fontStylesheetHref] : [],
|
|
script: createPageFetchAuthScript(pageAuthToken) + getPlayerServiceWorkerRegistrationScript() + renderOnboardingFormScript(deviceId)
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
mediaKind,
|
|
renderPlayerPage,
|
|
renderPlayerOnboardingLandingPage,
|
|
renderPlayerOnboardingFormPage
|
|
};
|