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

grain.social/grain-android

1 collaborator
0
1

The app calls grain-serve by its own method names

Merged
Merged chadtmiller.com asked to merge into feature/rust-auth from grain.social/grain-android:feature/rust-calls

What changes

Every call takes a name grain-serve serves. The 22 social.grain.unspecced.* reads and writes take their social.grain.* names, as the "Moving off hatk" map in grain's lexicons/README.md lists them, and leave out the viewer parameter: grain-serve knows the viewer from the service token. getNotifications is social.grain.notification.listNotifications, and the badge asks social.grain.notification.getUnreadCount; a notification carries indexedAt and isRead.

The dev.hatk.* calls go to what replaces each.

  • getFeed is social.grain.feed.getFeed, the feed named by its URI, at://<grain-serve's did:web>/social.grain.feed.generator/<name>.
  • getPreferences and putPreference are social.grain.actor.getPreferences and putPreferences, the typed set. Flipping a photo setting reads the set, changes photoMetadataPref, and puts the whole set back, the theme and pinned feeds with it; a kind grain-serve does not take is left out rather than sent and refused.
  • putPreference of lastSeenNotifications is social.grain.notification.updateSeen.
  • push.registerToken and unregisterToken are social.grain.notification.registerPush and unregisterPush, naming grain-serve's DID, platform android and the package name.
  • createReport is com.atproto.moderation.createReport with the reasonType grain-serve files under the label picked (the web's REPORT_REASONS). It carries atproto-proxy: <did>#grain_appview, so the PDS forwards it to grain-serve rather than to its own moderation service; an account is reported by com.atproto.admin.defs#repoRef, which grain-serve requires.
  • getRecord is com.atproto.repo.getRecord at the account's PDS: both callers read the account's own repo.

Deleting is done at the PDS. grain-serve's deleteGallery refuses a service token, so the app lists its social.grain.gallery.item, photo.exif, favorite and comment records and deletes, children first and the gallery last, what grain-serve's own delete would: the photos only this gallery places, their exif, the gallery's items, and the account's favorites and comments on it, 200 a commit, then has them indexed. Deleting the account empties every social.grain.* collection describeRepo lists, then asks social.grain.server.deleteAccount with the DID, and signs out only once it answers done.

A sign-in seeds a grain profile from the Bluesky one, as grain-serve's web login does (login/profile.rs). In the background, never holding up or failing the sign-in: a grain profile with a displayName is left alone; otherwise app.bsky.actor.profile/self, if there is one, gives it its displayName and description where they are not empty and its avatar blob as it is, keeping the createdAt of the grain profile there is, or now. That one is put over with swapRecord on its CID; else one is created at self. Both are read with com.atproto.repo.getRecord at the PDS, and the write is indexed through social.grain.sync.indexRecords.

The labels come from grain-serve. The report categories and the definitions every gallery card gates on are read once from social.grain.moderation.describeLabels, through the proxy, and kept in memory for the run. Until it answers, or when it fails, the bundled GrainLabels (grain's nine, as grain::labels::DEFINITIONS has them) stand in, so a gallery is never shown ungated offline and the report sheet has no loading state; a failed read is tried again by the next screen that asks.

Nothing names hatk any more: the README's push section, a deep link comment and an instrumented test went with the calls. The test workflow runs on pulls into feature/* branches too, so this one is checked.

Why

grain-serve serves social.grain.* only, so against it the app as it stood loaded nothing. This builds on the sign-in pull, which already writes at the PDS and proxies every other call to grain-serve.

How it was checked

The JVM suite on the runner: the stub servers and fixtures moved to the new names and shapes, with tests added for the feed URI, the preference read-modify-write (theme kept, unknown kind dropped, first preference added), the unread count, updateSeen, push registration's fields, the report's reasonType, proxy header and repoRef, getRecord's parameters, a gallery's deletion (a photo another gallery places stays, with its exif) and the account's (only grain collections, records before the forget); and the profile seed: a named profile left alone, no Bluesky profile does nothing, create at self versus put with swapRecord, createdAt kept, the avatar passed as it is, and a failing seed leaving the sign-in signed in with no error; and the label definitions: the bundled ones before an answer, read once through the proxy, the report sheet listing what the server describes, and a failed read keeping the bundled list and trying again. Not run against a live grain-serve or on a device.

Shortcomings

Every call has a grain-serve method.

Deleting a gallery lists four of the account's collections in full at the PDS, a page of 100 at a time: slow for an account with many favorites or comments, where grain-serve's own delete reads its index.

Review

Start at data/api/Endpoints.kt: the preference, deletion and report sections hold the non-trivial changes; the profile seed is data/api/ProfileSeed.kt, started from MainViewModel.handleOAuthCallback. Stacked on the sign-in pull (feature/rust-auth).

🤖 Generated with Claude Code

Merged as 8aa166c