~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2

grain-serve keeps preferences, notification settings among them

Merged
Merged chadtmiller.com asked to merge into feature/rust from grain.social/grain:web/preferences

What changes

Preferences. grain-serve serves social.grain.actor.getPreferences and putPreferences (lexicons already in the tree), shaped after app.bsky.actor.*Preferences: an array of social.grain.actor.defs#preferences items, each kind at most once, and a put replaces the whole set.

  • pinnedFeedsPref → preference key pinnedFeeds (its items), 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_pref rows, one per kind set (favorites, follows, comments, mentions), from: "follows" as from_follows. That is the table listNotifications / getUnreadCount and arising already 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.
  • A put deletes only the four keys it owns (and every notification_pref row) before inserting, in one transaction, so any other preference key an account has is left alone.

Who may call them. The viewer as viewer::viewer finds it: a service token (the apps) or the web's cookie; a put by cookie needs the site's Origin (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_pref rather 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; lastSeenNotifications is updateSeen's, not a preference.

How it was checked

  • Unit tests: the Index::preferences / put_preferences store (whole-set replace, other keys untouched); the mapping both ways; refusals; stored values that no longer parse read as nothing.
  • tests/notifications.rs preferences_put_are_the_ones_notifications_follow (service token, through common): an empty get, a put of all four kinds, the same set read back exactly, favorites turned off for in-app dropping them from listNotifications, an empty put unsetting everything (14 listed again), the refusals, no auth, no index.

The rest is left to the checks.

Shortcomings

  • No per-key merge: a client must send the whole set, as Bluesky's does.
  • pinnedFeeds items are kept as their four declared fields; anything extra a client sends is dropped.

Review

crates/grain-serve/src/preferences.rs (the mapping) and index/preferences.rs. Stacked on web/login (pull 3mz7oyx7oiu3c) for viewer::writer; its own diff is the last commit.

🤖 Generated with Claude Code

Approved

This 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.

Approved

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.

Approved

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.

Approved

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.

Approved

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.

Merged as 0b1acfa
Part of a stack of 2
  1. grain-serve keeps preferences, notification settings among them Landed as 0b1acfa
  2. main