feat(mobile): page viewer and ContentImage (SUPER-2079)

Closed
SA
2mo ago
+168-5

Links

Summary

  • The mobile page viewer: /(authenticated)/page/<id> loads viewUrl top-level in a WebView — the page's origin is first-party, so storage, CSP and video playback work exactly as on the web, cached by WebKit. Plus ContentImage (disk cache keyed by storage key), a 512 MB boot-time image-cache bound, and sign-out clearing.

How It Works

  • page.get → viewUrl carries the ticket in its path, so the WebView needs no headers or cookies; the query's staleTime (55 min) holds the URL inside the ticket's window so reopening reuses WebKit's cache instead of minting a fresh URL.
  • onShouldStartLoadWithRequest keeps navigation on the page's own origin and hands anything else to the system browser. allowsInlineMediaPlayback + mediaPlaybackRequiresUserAction={false} are what let <video> inside a page play inline.
  • ContentImage wraps expo-image with cacheKey = storageKey (page.list returns thumbnailStorageKey; the files layer returns storageKey), so a rotated ticket URL is never a fresh download on cellular. OrganizationAvatar adopts it as the first consumer; the pages grid and attachments use it when they land.
  • Error copy reuses the desktop viewer's already-translated dashboard.pageViewer.* messages — zero new catalog strings.

Manual QA Checklist

End-to-end viewing is blocked on the deployed origin (SUPER-2080). Verify in the simulator once it's up:

  • Deep link to /(authenticated)/page/<id> renders a page; a bogus id renders the error state
  • A page with <video> plays inline and seeks
  • External links in a page open in Safari, not the WebView
  • Reopening a page within the hour does not refetch its assets (Network inspector)

Testing

  • bun run typecheck in apps/mobile — the one error is pre-existing on the base branch (useCloudWorkspaceItems / tags, from main's workspace-tags work; untouched here)
  • packages/i18n check clean (reused ids)
  • Not sim-verified yet: the viewer has nothing real to load until the Worker deploys — flagged rather than skipped silently

Known Limitations

  • No discovery UX on purpose — where pages appear on mobile is being designed separately; this screen is the deep-link target it will push to.
  • expo-video intentionally absent: page video plays natively in the WebView; the dependency lands with the attachments UI (decision 10).

Summary by cubic

Implements the mobile page viewer at /(authenticated)/page/<id>, loading published pages top-level in a WebView so the page's origin is first-party and storage, CSP, and inline video playback work as on the web. Also adds ContentImage, which caches remote images on disk by storage key instead of URL (SUPER-2079).

Page viewer

  • The ticket rides the URL path, so the WebView needs no headers or cookies; a 55-minute stale time keeps the URL inside the ticket's window so reopening reuses WebKit's cache.
  • Navigation stays on the page's origin; anything else opens in the system browser.
  • Error copy reuses the desktop viewer's already-translated messages, and no discovery UX is included.
  • End-to-end viewing is blocked until the deployed origin is updated (SUPER-2080), so the viewer was not sim-verified.

ContentImage

  • Wraps expo-image with cacheKey = storage key, so a rotated ticket URL is never a fresh download.
  • Bounds the image disk cache at 512 MB at boot and clears it on sign-out.
  • OrganizationAvatar is the first consumer; the pages grid and attachments will use it when they land.
  • Locale catalogs were regenerated to record the viewer's references to the reused error message ids.