diff options
Diffstat (limited to 'web/src/siteFooter.js')
| -rw-r--r-- | web/src/siteFooter.js | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/web/src/siteFooter.js b/web/src/siteFooter.js new file mode 100644 index 0000000..1d5139e --- /dev/null +++ b/web/src/siteFooter.js @@ -0,0 +1,66 @@ +import { attachTooltips } from './tooltip.js'; + +// The links every site is expected to carry: where the source lives, who made +// it, and what it does with your data. Kept to one line at the foot of the +// menu rather than a page of its own, because there is very little to say and +// a whole screen would overstate it. +// +// Edit these to point at the real destinations. +export const LINKS = { + github: 'https://github.com/srdusr/typerpunk', + privacy: '#privacy', +}; + +// Share is a Web Share invocation where the browser has one (every mobile +// browser, Safari, and Edge), and a clipboard copy everywhere else. Neither +// needs a network call or a third-party button, so there is no tracking +// script here and nothing to consent to. +async function share(result) { + const url = window.location.origin; + const text = result + ? `I just typed ${Math.round(result.wpm)} wpm at ${Math.round(result.accuracy)}% accuracy on TyperPunk.` + : 'TyperPunk - competitive typing in the terminal and the browser.'; + + if (navigator.share) { + try { + await navigator.share({ title: 'TyperPunk', text, url }); + return 'shared'; + } catch { + // A cancelled share sheet is not a failure; fall through to copy. + } + } + try { + await navigator.clipboard.writeText(`${text} ${url}`); + return 'copied'; + } catch { + return 'failed'; + } +} + +/// `result` is optional: with one, Share offers the score just achieved; +/// without, it offers the site. +export function renderSiteFooter(root, { result, onShowPrivacy } = {}) { + const wrap = document.createElement('div'); + wrap.className = 'site-footer'; + wrap.innerHTML = ` + <button class="site-footer-link" data-action="share" data-tooltip="${result ? 'Share this result' : 'Share TyperPunk'}">Share</button> + <span class="site-footer-sep">·</span> + <a class="site-footer-link" href="${LINKS.github}" target="_blank" rel="noopener noreferrer" data-tooltip="Source on GitHub">GitHub</a> + <span class="site-footer-sep">·</span> + <button class="site-footer-link" data-action="privacy" data-tooltip="What this site stores">Privacy</button> + <span class="site-footer-sep">·</span> + <span class="site-footer-version">v0.1.0</span> + `; + root.appendChild(wrap); + attachTooltips(wrap); + + const shareBtn = wrap.querySelector('[data-action="share"]'); + shareBtn.addEventListener('click', async () => { + const outcome = await share(result); + shareBtn.textContent = outcome === 'copied' ? 'Copied' : outcome === 'failed' ? 'Copy failed' : 'Shared'; + setTimeout(() => { shareBtn.textContent = 'Share'; }, 1800); + }); + wrap.querySelector('[data-action="privacy"]').addEventListener('click', () => onShowPrivacy?.()); + + return () => wrap.remove(); +} |