Repository navigation
feat(storybook): post categories, clearer wording and timed changes for Plus in Feed Explain and Tune - #6960
Merged
Merged
Conversation
The Personalization center was a page of its own, so most people would never find it. This design rebuilds Feed settings around it instead, in a new "Features/Feed Settings" story group that uses the real settings menu pieces, the webapp's AccountPageContainer and the phone ShellBlock. Feed settings becomes: - Overview (new, first): learn from my reading, the AI agent, what shapes For You at a glance, your next page, and Start over. - Topics (was Tags): follows and topics from reading with their boosts, then the real tag directory. - Sources and people (was Content sources): follows with boosts and a search for more. - Ranking (new): every ranking setting. - Filters: the real Content preferences and Blocked sections on one page. It is the only place to unblock; Topics and Sources link to it. - AI superpowers and General, unchanged. The separate Personalization page is gone. The feed's Feed settings button, the dock and card links open the matching settings page. msw handlers answer the real blocked lists and feed preferences from the personalization store, so a block or an Undo from either side shows on the other. On a phone the changes bar shows once there is something to undo, above the page's search field.
The page lists only what you follow and its boosts, so the Follow a source chips and their search go. With no search field on the page, the phone changes bar no longer moves up for one.
Both sections render their parts as siblings and take their spacing from the page around them. The real settings pages wrap every section in a flex column with gap-4; the design rendered these two bare, so the banner, dividers and fields sat flush against each other.
Topics, Sources and people, Ranking and the Overview used their own card style, so next to Filters, AI superpowers and General they looked like a separate product. They now use the feed settings sections' language: a bold title over a tertiary description, plain list rows, dividers between blocks, and secondary buttons. Changes confirm in the app's toast with Undo instead of a floating bar, and the prototype goes back to the feed from the page heading. Your next page and the Left out list are gone. They belonged to the single Personalization page and don't make sense in settings pages; the Overview keeps learning, what shapes your feed, your AI agent and Start over.
The agent is the quickest way to change everything on these pages, so it now sits right under the page intro, above Learn from your reading.
…trols Moving personalization into Feed settings lost the single page where a change showed in the feed right away. Tune brings that back on For You: the topic, source and ranking controls sit in a panel beside the feed (a bottom sheet on phones), the page re-ranks as you change them, and cards that moved show New or how many places they moved. The panel's footer counts what changed and offers Undo; Done keeps the new page. Tune opens from the Explain bar or from a settings page's Live preview, and links back to the matching settings page. Settings and Tune render from one list, rankingControls, which names each control's settings page and Tune tab. A control added to one of its sections, or a new entry, shows up in both, so neither can fall behind. The learning switch moved into its own section so both can share it.
Free viewers choose what's in their feed; Plus viewers set exact numbers. Free gets follow, block, Fewer like this and a level per topic (Off, Less or Auto) and per source (Less or Auto). Plus swaps the levels for the exact boost sliders and unlocks the Ranking page, which free viewers see read only behind the feed settings Plus banner. Every screen story has a Free and Plus switch that updates the signed-in user, so the real Plus checks (usePlusSubscription, the settings menu, AI superpowers) follow it. The new Features/Feed Free vs Plus stories show the split, "Too much AI" on each plan, a card topic, and the Topics and Ranking pages on desktop and phone. "Too much AI" on a free account has to work, but lowering a topic's boost barely moves the page: its posts also come in through other topics and follows. So in the story's policy, Less and Off push down every post that carries the topic, and Less halves instead of the config's 0.8, which left the page unchanged. Show less AI sets the whole AI family to Less on every plan, and the Topics page lists turned-down topics that aren't in the profile. What's off? suggestions that set a number show a Plus tag and Get Plus for free viewers. The popular fill-in switch moves from Ranking to the Overview, next to Learn from your reading, so Ranking is all Plus.
Off read like Block, but it only stopped a topic counting as an interest. Not interested is the phrase people know from other feeds for "stop recommending this", so the free level is now Not interested, Less or Auto. Picking it on a topic or source you follow also unfollows it, since you can't be not interested in something you follow; following again clears it. Sources get the same three levels. A source set to Not interested has its posts pushed down like Less. The card row reads "Not interested" with Undo, What's off? offers "Not interested in AI", and the Plus slider's bottom reads "No boost" so Off never shows next to Block.
Brings Tune, the live feed beside the personalization controls, onto the branch with the free vs Plus split, so one branch has all of it. The Popular posts switch becomes its own section in rankingControls, so it shows on the Overview and on Tune's Ranking tab above the locked ranking settings, and a page's controls get a divider between them.
Shared pieces for the cards, the complaint box, Feed settings and Tune.
- policy/glossary.ts: one verb, effect and undo line per feed action
(Follow, Show more, Show less, Stop learning, Fewer like this, Hide a
category, Block, plus Auto, Unfollow, Unblock and Just this post), with
describeAction for tooltips.
- policy/timed.ts: durations (3 days, 1 week, until I change it),
expiry keys for topic and source levels and for blocks, formatUntil
("until Sun", "until Thu, Oct 22"), getActiveState, which the policy
now applies so an ended change is ignored, and the messages for the
notice when one ends.
- State gains expiries. The store takes a clock (now), clears changes that
already ended and says so, takes an optional duration on Show less,
Show more, Stop learning and blocks, and adds fewerLikeThis, which
removes the post and, with a duration, shows less of its main topic.
- Not interested is retired. Stop learning replaces it on topics: reading
stops counting but a follow keeps its base weight, and nothing is
unfollowed. Sources keep Show less and Auto. The level pickers use the
glossary verbs, and Segmented options take a description shown as a
tooltip.
- Fix: the Sources page used isDemoted without defining it.
…x card
Cards
- Every icon button and level has a tooltip from the glossary, so Follow,
Block and Fewer like this say what they do.
- The topic boost popover lists Show more, Auto, Show less and Stop
learning with what each does, plus how long (3 days, 1 week, until I
change it). Rows and leave notices say "Showing less until Sun" or
"Blocked until Thu, Oct 22".
- Fewer like this asks how far to go: just this post, or show less of its
main topic for 3 days or a week.
Complaint box
- Each suggestion says what changes, what stays, how to undo it, and its
effect on the next page from the policy preview ("AI goes from 6 posts
to 4 on your next page").
- Complaints that sound temporary ("a break from Fireship for a few
days") offer Show less and Block for 3 days, a week or until changed.
After a downvote
- In place of "Don't show me posts from...", the card shows why the post
was there and the likeliest fixes (Show less of its topic, Show less from
its source, or just remove it) with a duration, plus Report, Don't ask
again and Explain my feed, which turns Explain on at that card.
Also: tall popovers stay on screen and scroll; the What it needs story
notes that downvotes aren't evidence in the inferred profile and that
changes can't expire today; stories 15 and 16 show the fix card and a
break that ended; Free vs Plus copy uses the glossary verbs.
The user wants categories kept in Feed settings > Content preferences and also in Tune. Tune gets a Categories tab whose section reads and writes through the same useFeedSettings and useAdvancedSettings hooks and the same list helper as Content preferences, so a category turned off in one shows off in the other. rankingControls entries can now name the existing settings section that already shows them, so the Filters page doesn't render the control twice while Tune still gets it from the shared list. The settings mocks serve the 14 categories daily-api seeds, and keep advanced settings across refetches.
…and the fix card The 14 post categories (content curation) from daily-api's seeds, wired end to end and waiting on the real per-post values, which the public Post type doesn't expose. Until they arrive, no post has a category and nothing below shows. - policy/curation.ts: the categories (id, setting title, one-post label) and postCurations, the map the real values go into. - State gains blockedCurations. A hidden category removes a post unless it came from a follow, like $allowed_cc() in challenger/5.0 (blocked_curation). Hiding can be timed like blocks, and its end shows in the notice. The store adds setCurationHidden. - Cards: a post with categories shows them in place of its least telling topic row, as chips with a one-tap Hide each, so the card keeps its height. Leave notices say "You hid Drama" with Show again. - Complaint box: "too much drama", "no memes", "only tutorials" and "a break from hot takes" map to Hide (timed when temporary) or Hide everything but; "more tutorials" says the feed can only hide kinds. - Downvote fix card: Hide its category, for a post not from a follow. - What it needs: categories on the Post type, and a setting for every kind the feed assigns (the seeds have 14, the captured production settings list 8).
… and Tune Topic and source rows take the level pickers' new glossary verbs and add the card's "How long" choice once a level is set (3 days, 1 week, until I change it), with "Show less until Thu, then back to Auto." while it runs. A new section lists every change that ends on its own, blocks set from cards included, each with End now. It sits at the top of the Overview and of Tune's Topics tab, and hides while nothing is timed, so rankingControls entries can now say when they show. Follow, Unfollow, the turned-down topics and the Tune categories carry the glossary line as a tooltip, and the categories say that sources you follow still come through. Tune's tabs scroll on a phone and the desktop panel is wide enough for all four.
…timing Tune's Categories tab and Content preferences now read and write the store's blockedCurations through the settings API mocks, so a kind of post hidden on a card, in the complaint box, in Tune or in Content preferences shows hidden everywhere and changes the feed. The mocks take the category list from policy/curation.ts and keep only the API's descriptions. A hidden category in Tune offers the same How long choice as the other levels and says when it shows again; timed hides join the changes that end on their own. Each category will say how many posts of its kind are on your next page once posts carry categories, like the cards. The Overview's Filters summary counts hidden kinds too. Settings and Tune share one hook that binds the mocks to the store and refetches blocks and feed settings when they change; feed settings never refetch on mount, so the inactive ones refetch too.
Fills the story's per-post categories with post.contentCuration for exactly the 64 story posts, read from the production database with one read-only query on the replica. Every post had one category: Opinion 29, Story 20, News 5, Tutorial 4, Release 4, Hot take 2. A post with none would stay out rather than be guessed. Cards now show each post's kind with Hide, the complaint box counts them on the page, and Tune's category counts can read the same map.
Content preferences called every switch a hard filter that stops a post from showing at all. For categories that isn't so: daily-api sends hidden categories as allowed_content_curations, and both For You arms apply them only to posts that didn't come from a follow. lofn's default filter wraps $allowed_cc() in ($follow() || ...), and challenger/5.0 does the same. So the copy now says categories you turn off still come through from sources and squads you follow, which matches the Hide wording in Feed Explain, and keeps the rest of the section as it is.
…nge them The user found Tune hard to follow when a click moved rows: following a topic from reading jumped it into Topics you follow, a new boost re-sorted the list, and a timed change made a section appear at the top. Lists now keep the order and grouping they had when their page or tab opened (useFrozenGroups). A row shows its new state where it is: a topic followed from reading stays under Topics from your reading with Unfollow, an unfollowed source or person stays with Follow, and a change ended from the timed list stays, marked ended. New rows join the end of their list, and which sections show is decided when the page or tab opens. Opening Tune or the page again sorts afresh.
Review feedback on the Explain cards. - Categories: each of a post's categories is its own row, styled like the topic and source rows (label and a meta line, no icon, text lined up with theirs) with the same Block-style button, which hides it. They always come last, after the topics and the source, and the card keeps three rows so nothing moves. - Timed changes are Plus. plus.tsx gains isTimedLocked and PlusDurationPicker: for free viewers 3 days and a week show the Plus mark and can't be picked, with a Plus line, and Until I change it stays free. It gates the topic popover, Fewer like this (which gains a free Show less until changed choice), the complaint box suggestions and the downvote fix card. Segmented options can be locked. bootAsPlus moves to plus.tsx, and Card states and A break that ended boot as Plus. - The complaint box rebuilds its suggestions on every complaint, so a repeated one picks up the viewer's current plan.
The user wants "for a few days" to be a Plus feature. Topic, source and category timing in settings and Tune now use the cards session's PlusDurationPicker: free viewers keep "Until I change it", while 3 days and 1 week are locked with the Plus line, and a locked choice never reaches the store. "Changes that end on their own" shows only for Plus; rankingControls' isShown takes the plan, and the shown sections are decided again when the plan changes. Also drops a duplicate Duration import in plus.tsx that the merge of 58d4bd2 left behind and that stopped the stories from compiling.
The notice's message stretched to fill the panel, so the icon sat on the divider and the button on the card's bottom border. Now icon, title, message and button are one group with 12px gaps, padded like the rest of the panel and centered below the divider, on grid and list cards. Also: "for a week" counts as a temporary complaint, and the leave note after Fewer like this names the lowered topic even when it lasts until changed.
…f Plus icons Leave notice (follows the spacing fix in 572fddc, which was already shared with the settings branch, so this is a new commit rather than an amend): - Title: "You won't see this after a refresh". - The message says only what you did: "You hid Tutorial posts.", "You blocked DEV.", "You stopped learning from #career.". The follow exception for hidden kinds moves to a tooltip. - The button names the exact reverse: "Unhide Tutorials", "Unblock DEV", "Unblock #rust", "Learn #career again", "Set #career back to Auto", "Follow Fireship", and Undo for Fewer like this. All fit on one line at 375. Plus section: - plus.tsx PlusSection: the Plus badge on a divider, with one Get Plus link for free viewers; everything under it is Plus, locked for free viewers and unlocked for Plus, with no Plus icon on each control. - The topic popover keeps the free levels on top and puts the exact boost slider (a dimmed, disabled bar with its value for free viewers) and How long under the section, replacing the pink upsell sentence. - Fewer like this puts its timed choices under the section. - PlusDurationPicker renders as a section, so the complaint box and the downvote fix card match; locked duration chips are dimmed without icons.
Topic and source rows now follow the card's topic popover: the free levels come first for every viewer, then the boost as a chip that opens a PlusSection with the exact boost and, once a level is set, How long. Free viewers see the slider dimmed with its value and 3 days and 1 week locked, under one Get Plus link; Plus viewers get the same controls unlocked. The section opens on its own when a level is set, so How long is never hidden, and the chip keeps a long list from carrying a Plus section on every row. The Plus banners at the top of Topics and Sources go, since each row now names what Plus adds, and the two sections share one description for both plans.
A downvote now opens the same overlay as Explain mode instead of a separate fix card: the card head on the real card's pixels, the divider, the reason line led by "You downvoted this.", and the same topic, source and category rows. On these rows the Follow button becomes Show less, which asks how long (until changed, or 3 days and a week in a Plus section). Block and Hide stay as they are, so a change that drops the post shows the same leave notice with its fix. The footer keeps the Explain layout with Report, Don't ask again and Explain my feed, and a close button takes the place of the position number. Report shows a centered thank-you notice built from the same piece as the leave notice. Stop popular fill-ins and Just remove this post are gone from the downvote card; Explain mode never offered them either.
…lus label
The downvote card's line read as two unrelated sentences ("You downvoted
this. You've read a few posts about #docker."). It now gives the policy's
reason as a cause, "We showed it because you read about #docker.", with
"You downvoted it. Change what brought it here:" under it, so the rows
read as the answer. Each case has its own cause: a followed topic or
source, a learned or boosted topic, and a popular fill-in ("We added it
because your topics ran short.").
A popular fill-in now leads its rows with a Popular fill-ins row whose
block button stops fill-ins, so that reason can be acted on too, in
Explain mode as well.
To fit the prompt line, the downvote card's rows sit one step closer and
its padding is a little tighter. A two-line reason still fits the
3-line-title grid cards and the phone list.
The Plus section now centers the Plus label on its rule, and free viewers
get a Get Plus button under the last Plus control instead of a link on
the rule. Plus viewers see the label only.
…nalization-303eed
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes
Feed Explain, Feed settings and Tune in Storybook, after the personalization demo. One change reaches production: the Content preferences copy, described at the end.
Demo fixes
$allowed_cc(). Cards show a post's category as a row with Hide. The complaint box understands "too much drama" or "a break from hot takes". Tune gets a Categories tab that uses the sameuseFeedSettings/useAdvancedSettingshooks as Content preferences, with how many posts of each kind your next page has. Content preferences stays where it is.policy/glossary.tsholds one verb, effect and undo line per action (Follow, Show more, Show less, Stop learning, Fewer like this, Hide, Block). Cards, the complaint box, Feed settings and Tune all use it, with a one-line tooltip on each control. "Not interested" and a bare "Less" are retired.PlusSection. Plus viewers also get a "Changes that end on their own" list with End now.Review fixes since the demo
Data
The per-post categories in
policy/postCurations.tscome from a read-only production query for the 64 story posts. The public Post type doesn't expose them.Production change
FeedSettingsContentPreferencesSectionused to call every switch a hard filter that "won't show up at all". For categories that isn't true. daily-api sends hidden categories asallowed_content_curations, and both For You arms apply that only to posts that didn't come from a follow: lofn's default filter wraps$allowed_cc()in($follow() || …), and challenger/5.0 does the same. The copy now says that categories you turn off still come through from sources and squads you follow. The rest of the section is unchanged.Events
No new tracking events.
Experiment
No new experiments.
Manual Testing
pnpm --filter webapp test, the full webapptsc, and a local Storybook build, after syncing with main.Preview domain
https://claude-settings-personalization.preview.app.daily.dev