Skip to content

fix: restore "don't show again", and put Sharing in the drawer - #87

Merged
SiteRelEnby merged 1 commit into
mainfrom
fix/announcement-dont-show-again
Sep 20, 2026
Merged

SiteRelEnby merged 1 commit into
mainfrom
fix/announcement-dont-show-again

Conversation

@SiteRelEnby

Copy link
Copy Markdown
Collaborator

Two bugs found in 1.4.0 before it went to Play. 1.4.0 is already tagged and its GitHub release is published with signed open APKs, so these want a 1.4.1 rather than a re-cut: anyone who has installed sheaf-1.4.0-open.apk holds versionCode 104000, and re-issuing different bytes under that same code leaves them with no upgrade path.

"Don't show again" never survived a restart

ClientSettingsResponse names the field clientId; the server sends client_id; Moshi does not convert snake_case. Non-null with no default, so the missing field failed the whole response, and both callers wrap the call in runCatching - so every read of client settings threw, silently, for the life of the feature.

The write path was fine throughout. The dismissal reached the server and was stored; it just could never be read back, which is why this looked like a persistence bug rather than a parse one.

JsonDataException: Required value 'clientId' missing at $

A contract test now decodes the shape the server actually sends. It uses the app's own Moshi rather than a bare one, because this type is handled by KotlinJsonAdapterFactory and a plain builder cannot read it at all - which is also why the existing model tests never caught it.

I swept every model for the same mistake. The only other camelCase-without-@Json properties are on FrontUpdate, whose hand-written adapter writes the snake_case names itself, and on the watch's local-storage models, which never touch the wire.

Anything dismissed permanently before this stays dismissed - the data was always there.

Sharing was reachable only through Settings

It's where you go to take something down, which is not a thing to go hunting through Settings for, and web has had it as a top-level destination all along. It now joins the drawer with the rest of the system, and can be pinned to the bottom bar.

  • The entry appears when the instance serves public profiles, or when it doesn't but this account still has grants on file. Grants outlive the instance switch, so hiding the entry in that case would leave somebody published with no way to reach the button that unpublishes them.
  • Availability is re-asked whenever the drawer opens, so publishing a first link doesn't need a relaunch to reveal the entry.
  • A pinned Sharing slot on a server that later turns sharing off keeps its place, dimmed, and says why when tapped, rather than disappearing from somebody's bar over a setting they don't control.
  • The Settings > System row stays. A second way in costs nothing, and feat(settings): fold the Safety category into System #79 put it there deliberately.

Two things that should not ship as they are.

**"Don't show again" never survived a restart.** ClientSettingsResponse names
the field clientId while the server sends client_id, and Moshi does not
convert snake_case. Being non-null with no default, the missing field failed
the WHOLE response, so every read of client settings threw - and both callers
wrap the call in runCatching, so it failed silently. The write worked
throughout: the dismissal was saved server-side and simply never read back,
which is why the announcement returned on the next launch and why the bug
looked like a persistence failure rather than a parse one.

A contract test now decodes the shape the server actually sends, through the
app's own Moshi rather than a bare one, since this type is handled by
KotlinJsonAdapterFactory and a plain builder cannot read it at all. Sweeping
the rest of the models for the same mistake found only FrontUpdate, whose
hand-written adapter writes the snake_case names itself, and the watch's
models, which are local storage with their own serialisation.

**Sharing was reachable only through Settings.** It is where you go to take
something down, which is not a thing to go hunting through settings for, and
web has had it as a top-level destination all along. It joins the drawer with
the rest of the system, and can be pinned. The entry appears when the instance
serves public profiles, or when it does not but this account still has grants
on file - grants outlive the instance switch, and hiding the entry then would
leave somebody published with no way to reach the button that unpublishes
them. Availability is re-asked when the drawer opens, so publishing a first
link does not need a relaunch to show the entry. The Settings > System row
stays, since a second way in costs nothing.

A pinned Sharing slot on a server that has since turned sharing off keeps its
place, dimmed, and says so when tapped, rather than vanishing from somebody's
bar because of a setting they do not control.
@SiteRelEnby
SiteRelEnby merged commit 34f7056 into main Sep 20, 2026
1 check passed
@SiteRelEnby
SiteRelEnby deleted the fix/announcement-dont-show-again branch September 20, 2026 20:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant