screentinker/frontend
ScreenTinker 399af54839 feat(workspaces): accept-invite URL handler (slice 2C) + email URL path fix
Slice 2C: hash route #/accept-invite/{id} with full flow support across
all six auth entry points (login/register/Google/Microsoft/support/setup)
via app-boot consumer pattern rather than per-handler hooks. Stash
mechanism uses localStorage with timestamp + staleness check
(INVITE_EXPIRY_DAYS_FRONTEND = 7, mirrors backend default). On success:
switch workspace, reload, show toast post-reload via scoped
pending_invite_toast key. On error: showToast directly, no reload.
Non-reentrant guard prevents double-consume across the synthetic
hashchange that fires before reload completes.

Two bugs surfaced during Playwright-driven verification (slice 1 left
two latent issues that only manifested when the full accept-invite
flow ran end-to-end):

1. Email URL path: workspaces.js constructed
   ${publicBase}/#/accept-invite/X which lands on the marketing landing
   page (the SPA is at /app). Fixed to use
   ${publicBase}/app#/accept-invite/X. Any invite email sent before
   this fix would have produced an unfollowable link.

2. Synchronous hashchange race: location.hash = '#/' followed by
   reload() fires hashchange BEFORE the reload unloads the page. The
   intermediate route() call would consume the toast key against a DOM
   about to be destroyed, so the post-reload page had no toast. Fixed
   with history.replaceState which mutates hash without firing
   hashchange.

Files:
- server/routes/workspaces.js (+4/-1, /app path fix + comment)
- frontend/js/api.js (+3 LOC, acceptInvite helper)
- frontend/js/app.js (+154 LOC, accept-invite plumbing)
- frontend/js/i18n/en.js (+9 LOC, accept.* keys)

Browser verification: 11/11 assertions PASS via Playwright suite
covering all 5 D-cases (unauthed flow, authed direct, wrong account,
stale stash, already-member). Script stashed at
~/Documents/screentinker-2c-playwright-2026-05.py.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 13:50:23 -05:00
..
assets Landing: replace iframe mock with dashboard screenshot 2026-04-21 19:47:13 -05:00
compare chore(discord): update Discord invite link 2026-05-14 12:26:20 -05:00
css feat(workspaces): members page read-only view (slice 2A) 2026-05-16 13:00:51 -05:00
guides chore(discord): update Discord invite link 2026-05-14 12:26:20 -05:00
js feat(workspaces): accept-invite URL handler (slice 2C) + email URL path fix 2026-05-16 13:50:23 -05:00
legal docs(privacy): disclose error and diagnostic telemetry from players 2026-05-15 15:31:21 -05:00
index.html feat(teams): temporarily disable Teams API while feature is redesigned 2026-05-12 13:30:55 -05:00
landing.html fix(landing): replace broken Custom pricing card with enterprise contact form 2026-05-14 13:52:24 -05:00
manifest.json Initial open source release 2026-04-08 12:14:53 -05:00
robots.txt SEO: add meta tags, sitemap, robots.txt, comparison pages, guides, internal linking 2026-04-28 20:54:32 -05:00
sitemap.xml SEO: add meta tags, sitemap, robots.txt, comparison pages, guides, internal linking 2026-04-28 20:54:32 -05:00
sw-admin.js fix(frontend): workspace switcher (Phase 3 MVP) + SW network-first migration + platform_admin accessible_workspaces expansion + static render CSS cleanup. The switcher adds a sidebar dropdown for users who are members of multiple workspaces, renders as static text with a 'Workspace' label for single-workspace users, and muted 'No workspace' for zero. Uses existing /api/auth/me's accessible_workspaces and POST /api/auth/switch-workspace endpoints. Platform admin / superadmin users now see all workspaces in accessible_workspaces (closing the known regression from 88d91b1) via a LEFT JOIN that preserves workspace_role semantics (null = acting-as, role string = direct member). No cap on the list - deliberate for now, revisit at 50+ workspaces. SW fix bumps rd-admin-v1 -> rd-admin-v2 and switches fetch strategy from cache-first to network-first so the server's existing Cache-Control: no-cache + ETag headers actually get respected; preserves offline fallback. Static render CSS drops the bordered-box chrome that was making single-workspace users think the static text was clickable. Includes test fixture user switcher-test@local.test (credentials in fixture SQL header). Surfaced by semetra22 / Discord report about 'screens jumbled up' post-migration; root cause was the missing workspace switcher UI making devices in non-active workspaces appear missing. 2026-05-12 10:55:09 -05:00