Public landing, owner-list links, owner-conditional semi, PWA + mobile
Four related UX/privacy/install changes.
1. **Logged-out lands on the public list.** The root route now shows the
same Home view as a logged-in user, minus their own private rows and
the "Ny aktivitet" button. The nav exposes a "Logg inn" button when
no session is present. Login becomes one click away, not the forced
landing — anyone can browse the public + semi list anonymously.
2. **Public activities link to /<owner_username>/list.** When a public
activity's owner has opted into a public list, the "Lagt til av X"
line renders X as a link to /<username>/list. Server populates
`owner_username` on every public-row serialisation (null when the
owner hasn't opted in, so the client just renders plain text).
3. **Conditional owner_id on semi rows.** The server now serialises
`owner_id` on a semi row ONLY when the viewer IS the owner. The
wire type's `ActivitySemi.owner_id` is therefore optional. This
solves the semi-delete UX without leaking attribution: owners see
Edit/Delete buttons on their own semi rows; non-owners get the same
bare row they got before. The privacy property is enforced at the
API boundary, not in client-side render logic.
4. **Mobile-friendly + installable PWA.**
- `manifest.webmanifest` with name, theme color, standalone display,
and a maskable SVG icon (icon.svg).
- Service worker (sw.js): cache-first for the bundled shell;
network-only for /api/* (we never cache session-dependent or
ciphertext data — see the comment in sw.js for the rationale).
Falls back to the SPA shell for navigation requests when offline.
- SW registered in main.ts only in production builds (import.meta.env.PROD).
- viewport-fit=cover + env(safe-area-inset-*) padding so content
doesn't slip under iOS notches when installed.
- WCAG 2.5.5 touch-target sizing: min-height: 44px on buttons,
with an explicit opt-out for tag-close buttons (24×24 still meets
the 2.5.8 minimum).
- 16px font on form inputs below 480px so iOS doesn't auto-zoom.
Server-side: server/index.ts now serves manifest, icon, and sw.js
from frontend/dist alongside /assets/*. The catch-all still serves
index.html so the SPA's /<username>/list path routing keeps working.
Smoke-tested with a production-mode server: manifest returns the
correct application/manifest+json MIME, SVG renders, sw.js is
loadable, and unknown paths fall through to index.html as expected.
26 tests still pass; both tsconfigs typecheck (frontend now pulls
vite/client types for import.meta.env.PROD); Vite build succeeds.
This commit is contained in:
parent
6f4c11c7a6
commit
f0b4d735b5
15 changed files with 317 additions and 54 deletions
|
|
@ -115,8 +115,11 @@ export interface FeedbackEntry {
|
|||
export interface ActivityPublic {
|
||||
id: string;
|
||||
visibility: 'public';
|
||||
owner_id: string; // serialized for public
|
||||
owner_display: string; // display_name OR derived handle (email prefix)
|
||||
owner_id: string; // serialized for public
|
||||
owner_display: string; // display_name OR derived handle (email prefix)
|
||||
// Owner's URL slug, if they've opted into a public list. When non-null, the
|
||||
// client renders the owner attribution as a link to /<owner_username>/list.
|
||||
owner_username: string | null;
|
||||
title: string;
|
||||
tags: string[];
|
||||
loc_label: string | null;
|
||||
|
|
@ -130,7 +133,10 @@ export interface ActivityPublic {
|
|||
export interface ActivitySemi {
|
||||
id: string;
|
||||
visibility: 'semi';
|
||||
// owner_id deliberately omitted — see SECURITY.md
|
||||
// Set ONLY when the viewer is the owner. Lets the client surface
|
||||
// Edit/Delete on the user's own semi rows without leaking attribution to
|
||||
// anyone else. Stripped server-side for any other viewer; see SECURITY.md.
|
||||
owner_id?: string;
|
||||
title: string;
|
||||
tags: string[];
|
||||
loc_label: string | null;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue