fuzzyworld

nuts / files / src / compat.ts

compat.ts

2.2 KB · history · blame

1export type CompatResult =
2 | { ok: true }
3 | { ok: false; reason: 'no-touch' | 'no-motion' }
4
5/** True if the Vibration API is available (Chrome Android). */
6export function hasVibration(): boolean {
7 return 'vibrate' in navigator && typeof navigator.vibrate === 'function'
8}
9
10/**
11 * Check whether this platform can run the full experience.
12 * Returns immediately (no async, no permissions needed).
13 */
14export function checkCompat(): CompatResult {
15 // Must be a touch device
16 if (!('ontouchstart' in window) && !navigator.maxTouchPoints) {
17 return { ok: false, reason: 'no-touch' }
18 }
19
20 // Must have DeviceMotion (accelerometer / tilt gravity)
21 if (typeof DeviceMotionEvent === 'undefined') {
22 return { ok: false, reason: 'no-motion' }
23 }
24
25 return { ok: true }
26}
27
28const MESSAGES: Record<string, { headline: string; body: string }> = {
29 'no-touch': {
30 headline: 'Phone only',
31 body: 'This app needs a touchscreen to work.\nOpen it on your phone in Chrome.',
32 },
33 'no-motion': {
34 headline: 'No accelerometer',
35 body: 'This app needs a motion sensor to work.\nOpen it on your phone in Chrome.',
36 },
37}
38
39/** Replace the page body with a full-screen "not supported" wall. */
40export function showUnsupportedWall(reason: string): void {
41 const { headline, body } = MESSAGES[reason] ?? {
42 headline: 'Not supported',
43 body: 'Open this on your Android phone in Chrome.',
44 }
45
46 document.body.innerHTML = `
47 <div style="
48 position:fixed;inset:0;
49 background:#111;
50 display:flex;flex-direction:column;align-items:center;justify-content:center;
51 padding:32px;gap:20px;
52 font-family:system-ui,-apple-system,sans-serif;
53 text-align:center;
54 ">
55 <div style="font-size:72px;line-height:1;filter:grayscale(1) opacity(0.4)">🔩</div>
56 <h1 style="color:#fff;font-size:28px;font-weight:700;margin:0">${headline}</h1>
57 <p style="color:#666;font-size:16px;line-height:1.6;margin:0;white-space:pre-line">${body}</p>
58 <a href="https://apps.fuzzyworld.net/nuts/"
59 style="margin-top:8px;color:#c8a84b;font-size:13px;opacity:0.6;text-decoration:none">
60 apps.fuzzyworld.net/nuts
61 </a>
62 </div>`
63}