grain-serve keeps preferences, notification settings among them
MergedThis adds social.grain.actor.getPreferences and putPreferences. A put replaces the whole set in one transaction. It touches only the four owned preference keys, plus all of that account's notification_pref rows. I read preferences.rs and index/preferences.rs but not the stacked login commit. The mapping both ways, the duplicate and unknown-kind refusals, and the delete-then-insert scoping all look right. I did not run the tests, and the four checks (check, rust, test, harness) were still queued when I looked, so confirm they pass, including the coverage gate.
One small thing, which I don't think should block the merge: Reach.from is any string, and anything other than "follows" is stored as from_follows = false. A typo like "follow" is therefore silently treated as "from anyone". If the lexicon limits from to all or follows, refuse other values with 400, as you do for unknown kinds.
My earlier request is done. The new commit refuses any notification from other than all or follows with a 400, and it has a test for the "follow" typo. I read only that follow-up diff this round. The four checks (check, rust, test, harness) were still queued when I looked, so confirm they pass, including the coverage gate. I found nothing else.
Carried forward: the change this pull makes at 0861f5f9d3a4bd42d19609a29a0c2adde3611f63 is the one reviewed at 9a83847b6f518b646b0336ed7f0a6f016f173c1f, so that review stands. What is new comes from its base.
Nothing in the preferences work has changed since my last round. The two new commits only merge feature/rust and web/login into the branch. The login side now includes a separate cookie signing key. I didn't review that, because it belongs to the stacked login pull. My earlier request, to refuse any from other than all or follows, was done and still stands. The four checks (check, rust, test, harness) were still queued on a80bdd4 when I looked, so confirm they pass, including the coverage gate, before merging.
Carried forward: the change this pull makes at 819507fa7f6d39703c21be474c2a7d8fb647a0ac is the one reviewed at a80bdd4f8815c091876dfe357572d85effdc9397, so that review stands. What is new comes from its base.
- grain-serve keeps preferences, notification settings among them
- main
What changes
Preferences. grain-serve serves
social.grain.actor.getPreferencesandputPreferences(lexicons already in the tree), shaped afterapp.bsky.actor.*Preferences: an array ofsocial.grain.actor.defs#preferencesitems, each kind at most once, and a put replaces the whole set.pinnedFeedsPref→preferencekeypinnedFeeds(itsitems),photoMetadataPref→includeExif/includeLocation(each only if set),themePref→theme. These are the same keys and value shapes the import brings over from_preferences, so imported preferences read back typed.notificationsPref→notification_prefrows, one per kind set (favorites,follows,comments,mentions),from: "follows"asfrom_follows. That is the tablelistNotifications/getUnreadCountandarisingalready read, so a put changes what is listed straight away. A kind left out has no row: the default, push and in-app from anyone.notification_prefrow) before inserting, in one transaction, so any otherpreferencekey an account has is left alone.Who may call them. The viewer as
viewer::viewerfinds it: a service token (the apps) or the web's cookie; a put by cookie needs the site'sOrigin(viewer::writer). No one → 401, no index → 501.Why
The web keeps pinned feeds, photo metadata, theme and notification settings through these calls. Putting notification preferences into
notification_prefrather than a JSON blob means the notification queries filter on it in SQL, as they already do.How it differs from hatk's
dev.hatk.getPreferences/putPreference: typed and whole-set instead of free key/value one at a time; unknown$types, unknown notification kinds and duplicates are refused (400) instead of stored;lastSeenNotificationsisupdateSeen's, not a preference.How it was checked
Index::preferences/put_preferencesstore (whole-set replace, other keys untouched); the mapping both ways; refusals; stored values that no longer parse read as nothing.tests/notifications.rspreferences_put_are_the_ones_notifications_follow(service token, throughcommon): an empty get, a put of all four kinds, the same set read back exactly, favorites turned off for in-app dropping them fromlistNotifications, an empty put unsetting everything (14 listed again), the refusals, no auth, no index.The rest is left to the checks.
Shortcomings
pinnedFeedsitems are kept as their four declared fields; anything extra a client sends is dropped.Review
crates/grain-serve/src/preferences.rs(the mapping) andindex/preferences.rs. Stacked onweb/login(pull 3mz7oyx7oiu3c) forviewer::writer; its own diff is the last commit.🤖 Generated with Claude Code