/** * You (DESIGN.md §12.2, §12.7): Signal with its receipts, First eyes * bylines, your sealed captures and uploads, your posts and holograms, * what you watch, calm-by-default notifications, location precision, the * privacy line we don't cross, and settings. */ import { icon, FAMILY_ICON } from './icons.js'; import { $, $$, esc, ago, haptic, store, applyMotionPref } from './dom.js'; import { all, get } from '../capture/db.js'; import { deviceKey } from '../capture/provenance.js'; import { KIND, badgesFor } from './rooms.js'; import { llShort, utc } from './bands.js'; import { RULES, tierOf, TIERS } from './signal.js'; /** Shown, dimmed and labelled, until the person has receipts of their own. */ const EXAMPLE_LEDGER = [ { delta: 40, text: 'Fulfilled Need eyes: “Is the 7th St underpass flooded?”', m: 1440, }, { delta: 50, text: 'First eyes on Heavy rain cell over Houston', m: 1440 }, { delta: 20, text: 'ADS-B: 4 aircraft within 8 km at the sealed time', m: 1441, }, { delta: 5, text: '@wxdan (Corroborated, ×1.9) corroborated your capture', m: 1420, }, { delta: -25, text: 'A bridged correction: your “Cat 5” claim used the wrong scale', m: 4320, }, ]; const NOTIFY = [ { id: 'severe', label: 'Severe, near me', sub: 'Only things that can hurt people, inside your alert radius', }, { id: 'notable', label: 'Notable', sub: 'Plus wonder: ISS passes, launches, aurora over you', }, { id: 'all', label: 'Everything', sub: 'Every situation inside the radius. Not recommended.', }, ]; export function createYou( view, { bindScroll, getSituations, onOpenSituation, onVerify, onCredits, rooms, rt, onOpenRoom, onLocate, core: coreIn, }, ) { let core = coreIn || null; const getCore = async () => (core = core || (await import('./social.js')).socialCore({ rooms, rt, getSituations })); let urls = []; view.addEventListener( 'scroll', () => view.classList.toggle('is-scrolled', view.scrollTop > 44), { passive: true }, ); bindScroll(view); async function render() { urls.forEach((u) => URL.revokeObjectURL(u)); urls = []; const [captures, key, sits, uploads, posts] = await Promise.all([ all('captures').catch(() => []), deviceKey().catch(() => null), getSituations().catch(() => []), all('uploads').catch(() => []), rooms.myPosts().catch(() => []), ]); const c = await getCore(); const [score, receipts] = await Promise.all([ c.signal.score(), c.signal.receipts(), ]); const tier = tierOf(score); const firsts = receipts.filter((r) => r.rule === 'first'); const exampleLedger = !receipts.length; const ledger = exampleLedger ? EXAMPLE_LEDGER.map((x) => ({ ...x, at: Date.now() - x.m * 60e3, example: true, })) : receipts; const loc = c.myLoc(); const needs = c.needs.near(loc, 60).filter((x) => x.status === 'open'); const notify = store.get('notifyLevel') || 'severe'; const asks = store.get('needAsks') || 'daily'; const quiet = store.get('quietHours') ?? true; const prec = store.get('precisionDefault') || '100m'; const watchSummary = c.ctx.watch?.summary?.() || null; uploads.sort((a, b) => b.addedAt - a.addedAt); posts.sort((a, b) => b.at - a.at); const sitOf = (id) => sits.find((x) => String(x.id) === id); const upThumbs = uploads.slice(0, 12).map((u) => { if (!u.blob) return ''; const url = URL.createObjectURL(u.blob); urls.push(url); return url; }); const holos = posts.filter((p) => p.hologram); const home = rt.me.home; captures.sort((a, b) => b.seq - a.seq); const followed = store.get('followed') || []; const fol = followed .map((id) => sits.find((s) => String(s.id) === id)) .filter(Boolean); const contacts = followed.filter((id) => id.startsWith('contact:')); const thumbs = await Promise.all( captures.slice(0, 30).map(async (c) => { const m = await get('media', c.id).catch(() => null); if (!m?.blob) return ''; const u = URL.createObjectURL(m.blob); urls.push(u); return u; }), ); const q = store.get('quality'); const rm = store.get('reduceMotion'); const radius = store.get('alertRadiusKm'); view.innerHTML = `

You

${icon('you')}
${esc(rt.me.handle)} · this device ${key ? `Signing key ${esc(key.keyId)} · P-256 · ${key.extractable ? 'extractable' : 'non-extractable'}` : 'Signing key unavailable (needs a secure context)'} ${captures.length} sealed capture${captures.length === 1 ? '' : 's'} · ${uploads.length} upload${uploads.length === 1 ? '' : 's'} · ${posts.length} post${posts.length === 1 ? '' : 's'} · ${followed.length} followed
${icon('sparkles')}Signal${esc(tier.name)}
${score.toLocaleString()}votes weigh ×${tier.weight.toFixed(1)}
${TIERS.map((t) => ``).join('')}
${tier.next ? `${(tier.next.min - score).toLocaleString()} to ${esc(tier.next.name)}` : 'Top tier'} · ${receipts.length} receipt${receipts.length === 1 ? '' : 's'} · ${firsts.length} first eyes

Signal rises when your sealed captures are corroborated by people and by sensors, and falls when your claims are corrected. It weighs your votes and notes. No follower counts, ever.

How Signal is calculated

Tiers: ${TIERS.map((t) => `${esc(t.name)} ${t.min}+ (×${t.weight})`).join(' · ')}

${ firsts.length ? `
First eyes permanent bylines on the record
${firsts .map( (r) => ``, ) .join('')}
` : '' }
Receipts ${exampleLedger ? 'example until you earn your own' : 'every change, with its reason'}
${ledger .slice(0, 14) .map( (r) => `
${r.delta > 0 ? '+' : ''}${r.delta}${esc(r.text)}${esc(ago(r.at))}${r.source ? ` · ${esc(r.source)}` : ''}${r.sim ? ' · simulated' : ''}${r.example ? ' · Example' : ''}
`, ) .join('')}
${ exampleLedger ? `
${icon('eyes')}${needs.length ? `${needs.length} Need eyes request${needs.length === 1 ? '' : 's'} near you` : 'Earn your first receipt'}Answer one with a sealed capture: +40 Signal and the First eyes byline.${needs[0] ? `` : ''}
` : '' }
Captures tap to verify
${ captures.length ? `
${captures .slice(0, 30) .map( (c, i) => ``, ) .join('')}
` : `
${icon('seal')}
No sealed captures yet.
Open Capture → Witness and tap the shutter.
` } ${ uploads.length ? `
Uploads unverified · no capture-time seal
${uploads .slice(0, 12) .map( (u, i) => `
${upThumbs[i] ? (/video/.test(u.type) ? `` : ``) : ''}UNVERIFIED
`, ) .join('')}
` : '' }
Your posts ${posts.length ? 'tap to open the room' : ''}
${ posts.length ? `
${posts .slice(0, 20) .map((p) => { const st = sitOf(p.room); const k = KIND[p.kind] || KIND.context; const b = st ? badgesFor(p, st, rt.me) : []; return ``; }) .join('')}
` : '
Nothing posted yet. Every situation has a room: open one and reply with evidence.
' }
Watching ${watchSummary ? esc(watchSummary) : 'places and things you follow'}${c.ctx.watch?.open ? '' : ''}
${ fol.length || contacts.length ? `
${fol .map( (s) => ``, ) .join( '', )}${contacts.map((c) => `
${icon('scope')}${esc(c.slice(8))}Contact
`).join('')}
` : '
Follow a situation from its brief and it appears here and in the tab-bar accessory.
' }
Holograms ${holos.length ? `${holos.length} left in places` : ''}
${ holos.length ? `
${holos.map((p) => ``).join('')}
` : `
${icon('cube')}
Leave something in a placeWhen you post a capture, turn on “Leave as a hologram here”: it stays pinned on the globe at that spot for people who visit with Look.
` }
Home area
Notifications calm by default
${icon('hazards')}Tell me about${esc(NOTIFY.find((x) => x.id === notify).sub)}
${NOTIFY.map((v) => ``).join('')}
${icon('eyes')}Need eyes asksAt most one quiet ask a day, only when you’re inside a request’s radius
${[ ['daily', 'One a day'], ['off', 'Off'], ] .map( ([v, l]) => ``, ) .join('')}
${icon('clock')}Quiet hours22:00–07:00 · severe and local only
${icon('checkmark')}Nothing unusual near youThat’s a valid state. No alarm feed, no upsell.
Location you share default for new captures
${[ ['exact', 'Exact'], ['100m', '100 m'], ['1km', '1 km'], ] .map( ([v, l]) => ``, ) .join('')}
${icon('lock')}The seal still proves the exact position to verifiers without publishing it. You can change it per capture.
The line we don’t cross
${icon('eyes')}
We track things that broadcast, never people.Trackable entities are transponders, AIS, orbital elements, public infrastructure and events. Contributors are pseudonymous. There is no face recognition and no person search, and aircraft on owner-privacy lists (LADD/PIA) are never named.
Settings
${icon('globe')}Globe qualityApplies on next launch
${['auto', 'high', 'battery'].map((v) => ``).join('')}
${icon('sparkles')}Reduce motionCross-fades instead of flights and morphs
${['system', 'on', 'off'].map((v) => ``).join('')}
${icon('pin')}Alert radiusSituations inside it ping you${radius.toLocaleString()} km
${icon('hand')}HapticsRibbon detents and seals only
About
${icon('lock')}ProvenancePrototype uses WebCrypto ECDSA P-256 with a non-extractable key in IndexedDB. Native builds use the Secure Enclave and C2PA.

God’s Eye mobile prototype. The window through which you see the world, and the eye through which the world sees itself.

`; $$('.segmented', view).forEach(placeThumb); } function placeThumb(seg) { const b = $('[aria-pressed="true"]', seg) || $('button', seg); const th = $('.seg-thumb', seg); if (!b || !th) return; th.style.width = `${b.offsetWidth}px`; th.style.transform = `translateX(${b.offsetLeft}px)`; } view.addEventListener('click', async (e) => { const nd = e.target.closest('[data-need-act]'); if (nd) { (await getCore()).needs.act(nd.dataset.needAct, nd.dataset.need); return; } if (e.target.closest('[data-act="watch"]')) { (await getCore()).ctx.watch?.open?.(); return; } const ref = e.target.closest('[data-ref-room]'); if (ref && ref.dataset.refRoom) { const sits = await getSituations(); const st = sits.find((x) => String(x.id) === ref.dataset.refRoom); if (st) onOpenRoom(st, ref.dataset.refPost || undefined); return; } const cap = e.target.closest('[data-cap]'); if (cap) { const c = (await all('captures')).find((x) => x.id === cap.dataset.cap); if (c) onVerify(c); return; } const sit = e.target.closest('[data-sit]'); if (sit) { const s = (await getSituations()).find( (x) => String(x.id) === sit.dataset.sit, ); if (s) onOpenSituation(s); return; } const segBtn = e.target.closest('.segmented[data-set] button'); if (segBtn) { const seg = segBtn.parentElement; $$('button', seg).forEach((b) => b.setAttribute('aria-pressed', String(b === segBtn)), ); placeThumb(seg); store.set(seg.dataset.set, segBtn.dataset.v); if (seg.dataset.set === 'reduceMotion') applyMotionPref(); if (seg.dataset.set === 'notifyLevel') render(); haptic('tick'); return; } const sw = e.target.closest('[data-switch]'); if (sw) { const on = sw.getAttribute('aria-checked') !== 'true'; sw.setAttribute('aria-checked', String(on)); store.set(sw.dataset.switch, on); haptic('tick'); return; } if (e.target.closest('[data-act="credits"]')) onCredits(); const pr = e.target.closest('[data-post-room]'); if (pr) { const s = (await getSituations()).find( (x) => String(x.id) === pr.dataset.postRoom, ); if (s) onOpenRoom(s, pr.dataset.post); return; } if (e.target.closest('[data-act="home"]')) { const loc = await onLocate(); if (loc) { rt.setHome({ lat: Math.round(loc.lat * 10) / 10, lon: Math.round(loc.lon * 10) / 10, }); haptic('success'); render(); } } }); view.addEventListener('input', (e) => { if (!e.target.matches('.slider')) return; const v = Number(e.target.value); e.target.style.setProperty('--p', `${((v - 25) / 1975) * 100}%`); $('[data-radius]', view).textContent = `${v.toLocaleString()} km`; }); view.addEventListener('change', (e) => { if (e.target.matches('.slider')) store.set('alertRadiusKm', Number(e.target.value)); }); new ResizeObserver(() => $$('.segmented', view).forEach(placeThumb)).observe( view, ); // Signal changes (a corroboration, a fulfilled request) refresh You live. getCore() .then((c) => c.signal.on('change', () => { if (view.isConnected && !view.hidden) render(); }), ) .catch(() => {}); return { render }; }