Repository navigation
Bring across five recent web changes: system banner, custom-field privacy, and three unavailable-option fixes - #96
Merged
Conversation
Two additions the web client picked up this week. The system profile gains a banner, the exact twin of a member's: same 3:1 shape, same cropper, same upload endpoint and purpose tag, sitting above the avatar in the same place the member editor puts it. The control itself moves into a shared BannerPicker rather than being written twice, since two copies of a thing this fiddly drift. Custom fields now say who can see them where their values are edited and shown. The level lives on the definition and applies to every member, which is exactly why the member editor could not answer "wait, is that one public?" on its own: you had to leave the member, open Settings, find the field and come back, which is a poor thing to have to do before typing something sensitive into a box. The tag appears under each input in the editor, beside each value on the member profile, and on the field list in Settings, from one component so the three cannot disagree. Display only, as on web: changing a level stays in Settings, where the raise runs the step-up and grace-period flow. Where a raise is staged, all three surfaces now say what it will become and when, which needed pending_privacy and privacy_activates_at adding to the field model. Rendered as a tinted pill rather than web's tinted word: bare coloured text has to stay legible across seventeen palettes in light and dark, and the pill is what this app already uses for the pending-delete badge. Public borrows that badge's warning colours, the app's existing "visible, or about to be" tone.
An instance with public profiles switched off cannot publish anything, and the API refuses a raise to public with a 403 saying so. All five privacy selects went on offering it anyway, so choosing Public was a guaranteed dead end that surfaced as a bare permissions error with no hint as to why. Members, groups, custom fields, the system profile and relationship edges now render it disabled with one line underneath. Disabled rather than removed: dropping the option would make the feature look absent, which is a different untruth from the one being fixed. Two cases keep it offered, and both matter more than the tidiness of the rule. A record already stored as public keeps it, because that is how its own value displays and, above all, how somebody lowers it again - nothing may stand between a user and reducing their own exposure. And relationship edges between groups are not gated at all, because the public projection never queries them, so the server stores public there as asked and disabling it would invent a restriction that does not exist. The predicate and the wording live next to the existing raise gate, so the five surfaces cannot drift, and the note opens with the same clause as the sharing screen's card rather than inventing a sixth description of one state.
…nnot work Two states the app had no way to show, both landing in server 1.6.0. A channel whose destination kept failing is switched off by the server after a day. Nothing said so: notifications simply stopped, which is indistinguishable from nothing having happened, and is a bad failure mode for the feature whose job is telling you things. Worse, the recipient-facing label read "Unsubscribed", which tells an owner their recipient opted out when in fact their endpoint died. disabled_reason now reaches both sides. The owner's channel list carries a banner naming the stopped channels and what to check, and the row says "Stopped: deliveries failing" rather than "Disabled", which would read as a choice somebody made. The Receiving tab gets the same distinction. Only server-stopped channels warn: one the owner paused needs no warning, they know, they did it. The channel-type picker offered Mobile push on every instance. On a self-hosted one without credentials you filled the form, pressed Create, and got back a message that read like a setting someone forgot. It is not one: a push credential is paired to an app build rather than to a server, so no amount of configuring reaches the store builds. The option is now disabled where the instance cannot offer it, with the reason folded away behind a question, and the form moves off it so you cannot submit into a refusal. The reason is the server's own words from /v1/notifications/server-config, which exists so every client explains this identically rather than each inventing its own wording. Both features default to today's behaviour on a server too old to be asked. WarningCard moves to the components package, since a second screen now needs it and the warning tone should mean one thing across the app.
…d-field-privacy # Conflicts: # CHANGELOG.md # sheaf/app/src/main/java/systems/lupine/sheaf/ui/fields/CustomFieldsScreen.kt
SiteRelEnby
enabled auto-merge
September 30, 2026 07:58
SiteRelEnby
disabled auto-merge
September 30, 2026 08:00
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.
Five UI changes the web client picked up recently, brought across.
A banner for the system
System.banner_urllanded server-side as the exact twin ofMember.banner_url, and Android already had member banners end to end. Settings > Profile gains the control above the avatar, in the same place the member editor puts it, using the same 3:1 cropper and the same upload endpoint and purpose tag.The control moved into a shared
BannerPickerrather than being written a second time.banner_urlis registered as clearing inSystemUpdateJsonAdapter, which exists precisely because Moshi drops nulls and there would otherwise be no way to remove one.Needs a server running 1.6.0 or later; older ones simply never send the field.
Custom fields say who can see them
The member editor listed custom fields as a label and an input, so "wait, is that one public?" meant leaving the member, opening Settings, finding the field and coming back - which is a poor thing to have to do before typing something sensitive into a box. The level lives on the definition and applies to every member, which is exactly why the editor could not answer it on its own.
The tag now appears under each input in the editor, beside each value on the member profile, and on the field list in Settings, from one component so the three cannot disagree. Display only: changing a level stays in Settings, where the raise runs the step-up and grace-period flow.
Where a raise is waiting out a grace period, all three surfaces say what it will become and when. That needed
pending_privacyandprivacy_activates_atadding toCustomFieldRead- the server sends both and the app was dropping them, so without it that half would have silently rendered nothing.Rendered as a tinted pill rather than web's tinted word. Bare coloured text has to stay legible across seventeen palettes in light and dark, and the pill is what this app already uses for the pending-delete badge; Public borrows that badge's warning colours, the existing "visible, or about to be" tone. Same vocabulary, same order. The Settings list said "Friends" before and now says "Friends only" like everywhere else.
Public is no longer offered where the instance cannot publish
An instance with public profiles switched off refuses a raise with a 403. All five privacy selects went on offering it, so choosing Public was a guaranteed dead end surfacing as a bare permissions error. Members, groups, custom fields, the system profile and relationship edges now render it disabled with one line underneath, which opens with the same clause as the sharing screen's card rather than inventing a sixth description of one state.
Disabled rather than removed: dropping the option would make the feature look absent, which is a different untruth from the one being fixed.
Two exceptions, both load-bearing:
Both are pinned by tests, since they are the cases a mechanical sweep gets wrong.
A channel that stopped sending says so
A channel whose destination keeps failing is switched off by the server after a day. Nothing said so anywhere the owner looks: notifications simply stopped, which is indistinguishable from nothing having happened, and is a bad failure mode for the feature whose job is telling you things. Worse, the recipient-facing label read "Unsubscribed" - telling an owner someone opted out when in fact their endpoint died.
disabled_reasonnow reaches both sides: a banner naming the stopped channels and what to check, and a "Stopped: deliveries failing" row state instead of "Disabled", which reads as a choice somebody made. Only server-stopped channels warn; one the owner paused needs no warning.Mobile push is no longer offered where it cannot work
The picker offered it on every instance. On a self-hosted one without credentials you filled in the whole form, pressed Create, and got back a message that read like a setting somebody forgot. It is not one: a push credential is paired to an app build rather than to a server, so no amount of configuring reaches the store builds.
The option is disabled where the instance cannot offer it, with the reason folded behind a question, and the form moves off it so you cannot submit into a refusal. The reason comes from
GET /v1/notifications/server-config, which exists so every client explains this in the same words.Notes
Both notification changes need server 1.6.0 and default to today's behaviour on anything older.
WarningCardmoved to the components package now that a second screen needs it.Two things for a reviewer rather than decisions taken here:
Touches
CustomFieldsScreen.kt, which #94 also changes; expect a small conflict there, resolved by keeping the tag in #94's supporting-content position.Checked against web and found already done, so not included: group description editing, markdown formatting help (Android had both first), explicit null on custom-field clear, relationship types and per-relationship privacy, journal pinning, BerryTree import, the share-view public rule, staged member privacy raises, link preview modes and member created date.