Skip to content

Public profiles: settings page, sections, identity, favourites and comparison - #240

Open
alistairjcbrown wants to merge 9 commits into
mainfrom
claude/public-profiles-planning-tl53z1
Open

alistairjcbrown wants to merge 9 commits into
mainfrom
claude/public-profiles-planning-tl53z1

Conversation

@alistairjcbrown

Copy link
Copy Markdown
Member

The site's half of the public profile work. The Worker's half is clusterflick/api-profiles on the same branch name. Deploy order: Worker, then Firestore rules, then this site. If the rules and site go out first, a reader could hide a section the old Worker still sends.

Settings: /personalise/profile/

  • One draft, one Save. Address, about you, favourite films and what it shows sit on one page, saved with one button in a bar stuck to the bottom of the screen. A new handle is claimed in the same write. The bar says what stops a save, asks before a rename frees the old address, and leaving with unsaved changes asks first.
  • Live preview. It sits beside the settings from 1100px. Narrower, it's a full-screen dialog opened from the save bar. It draws the profile page's own hero and sections, so the two can't differ.
  • Handle checked as it's typed, and again on saving.
  • Publishing is a moment, not a switch. It first lists what goes public and what stays hidden, then saves the draft and public: true in one write. The card then lights up with the address, Copy link and Share. Making it private never waits for anything.
  • Account bar. Its Public profile panel now only says where the profile stands and links to the settings page.

Profile page: /u/<handle>/

  • Favourites as the hero. Up to four, fanned over a blurred wash of their posters. A favourite that's showing links to its film page.

  • Taste line under the name, e.g. "Mostly drama · Big on the 2000s · 2 Wong Kar-wai films". It only appears with the stats, and each part only when it's true enough to say.

  • Owner: Edit profile and Copy link.

  • Watchlist: leads with what's showing this week and links to the planner. Its buttons speak of the owner by name.

  • Comparison, offered not shown. A signed-in visitor gets a card ("You have 4 films in common with Siobhán") with Compare, which opens groups as poster stacks:

    • both want to see, with "Plan together";
    • both seen;
    • each one's films the other has seen.

    Signed out, the card invites the visitor to start their own lists. Either card can be dismissed for the day. Only lists the owner shows are compared.

Data and rules

  • New fields on users/{uid}/meta/profile:
    • sections, required before public: true;
    • displayName, stripped of HTML and JSON-significant, control and bidirectional characters;
    • links;
    • favourites.
  • Validation in three places. Each field is checked in @/lib/user-profile, again in firestore.rules (held to the module by rules-copy.test.ts), and again by the Worker.
  • Revoked capability. Without it, switching the profile off is the only change allowed.

Testing

  • npm run lint is clean.
  • Unit tests: pass under src/lib, covering the draft, comparison, taste line, favourites, identity and day-long dismissals.
  • Firestore rules: npm run test:rules passes all 101 tests against the emulator.
  • Storybook: the profile, settings and personalise page stories pass locally, but only against stand-in data, because the real releases couldn't be downloaded in my session. Two existing Import stories on /personalise fail the same way on main with that stand-in data. CI runs them against the real data.
  • Not yet seen with real data:
    • the poster wash behind the hero;
    • "Plan together" and the "showing this week" line;
    • a long comparison group closed to a pile.

Generated by Claude Code

A profile's settings move to their own page, /personalise/profile/: the
handle, then a switch for each section (Seen stats, watchlist, favourite
venues, Seen list), then the public switch, over a preview drawn with the
profile page's own sections. Every section starts on, but the rules refuse
`public: true` until a choice has been saved, so nobody publishes without
seeing it. Without the capability, switching off is now the only change
allowed. The account bar's panel says where the profile stands and links
to the page.

The profile page draws only the sections the Worker says are shown, and a
profile public from before sections shows everything, as it did. Showing
the stats without the Seen list still sends the list, since the stats are
worked out in the browser; the settings say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBhsxV5yDVkwkbmmrioZpp
A new "About you" step on /personalise/profile/ takes an optional display
name and links to the reader's Letterboxd, IMDb and website. The name heads
the profile in place of the handle, which leads the line beneath it; links
are stored as identifiers (a username, an IMDb user id) and the page builds
the addresses, bar an https website. The preview shows both as they're
typed.

A name is stripped rather than escaped: the characters HTML and JSON give
meaning to, control and bidirectional characters, and a leading `$` are
dropped as it's typed, refused by the rules, and stripped again when the page
reads it. Apostrophes stay. The rules hold every field to the module's
patterns, and a test holds the rules' copy to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBhsxV5yDVkwkbmmrioZpp
A new "Favourite films" step on /personalise/profile/ picks up to four films
to head the profile, in the reader's order. The Seen list is searched as the
reader types, and TheMovieDB on "Search all films", as the film search on
/personalise does. Each favourite is its own snapshot rather than a reference
into the Seen list, so editing Seen doesn't undo it, a hidden Seen list stays
wholly hidden, and a reader with no Seen list can still pick. The profile
draws them four across or two by two, linked to the film's page while it's
showing, and the preview shows picks before they're saved.

The rules hold the count and each film's fields to the module, and a test
holds the rules' copy to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBhsxV5yDVkwkbmmrioZpp
Settings: one draft saved with one Save, in a bar stuck to the bottom of the
screen, beside a live preview where there's room and in a full-screen dialog
from the bar where there isn't. The steps lose their numbers, the handle is
checked as it's typed, and publishing becomes a moment: it lists what goes
public and what stays hidden, saves and publishes in one write, then lights
up with the address, Copy link and Share. Favourites sit in four places that
can be dragged into order, an empty one opening the search.

Profiles: the favourites head the page, fanned over a blurred wash of their
posters, with a line of the reader's taste from their stats under the name,
and Copy link beside Edit profile for the owner. On someone's profile the
watchlist leads with what's showing this week, linking to the planner, and
its buttons speak of them by name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBhsxV5yDVkwkbmmrioZpp
A signed-in visitor sees, above the owner's lists, the films they both want
to see (with "Plan together" opening the planner on those with a showing to
book), the films they've both seen, and each one's films the other has
seen. It is worked out in the visitor's browser from their own lists
against what the profile sends, and only from lists the owner shows: the
Seen list sent for the stats while hidden is not compared, since that would
name films from it. Signed out, it is an invitation to start lists instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBhsxV5yDVkwkbmmrioZpp
The comparison no longer leads someone else's profile: a small card offers
it instead. Signed in, it says how many films the visitor shares with the
owner and opens the groups on Compare, each now a poster stack as the
watchlist's groups are, with Hide to put the card back. Signed out, it
invites the visitor to start lists of their own. Either card can be
dismissed: the sign-up one for every profile, the compare one for that
profile, remembered in the browser.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBhsxV5yDVkwkbmmrioZpp
A dismissed card now comes back the next day rather than staying gone: out
of the way for the visit, without being hidden for good. The sign-up card
still goes for every profile, the compare card for one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBhsxV5yDVkwkbmmrioZpp
Letterboxd usernames are letters, numbers and underscores, so a hyphen is
refused as it's typed, by the rules and by the Worker's copy of the pattern.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBhsxV5yDVkwkbmmrioZpp

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants