Skip to content

Unlock themes and app icons from the Tip Jar - #1648

Merged
GianniCarlo merged 1 commit into
developfrom
feat/settings-unlock-tip-jar
Oct 15, 2026
Merged

GianniCarlo merged 1 commit into
developfrom
feat/settings-unlock-tip-jar

Conversation

@GianniCarlo

Copy link
Copy Markdown
Collaborator

Problem

Themes and App Icon show the BookPlayer Pro card, the same one at the top of the Settings list. With Settings as a split view (#1647), both are on screen at once on iPad and the open iPhone Duo. And the card pitches Pro, while what unlocks the extra themes and icons is a tip (or any paid tier).

Changes

  • Themes and App Icon (SettingsThemesView, SettingsAppIconsView): while the account is free, the toolbar's Restore becomes Unlock, which opens the Tip Jar in a sheet with a close button (unlocksWithTipJar(), in SettingsTipJarView.swift). The Pro card is gone from both, and showPro with it; the card stays at the top of the Settings list. Accounts with access see neither.
    • Restore stays one tap away, in the Tip Jar's own toolbar.
    • The Tip Jar's thank-you (and restored) alert closes the sheet with its existing dismiss(); the screens read accountService.accessLevel, which a tip updates, so they unlock where you are.
  • Strings: new unlock_button, translated in every locale.
  • Hardcover settings (nearby fixes):
    • "You can find your Hardcover API access at…" (was "Your can find…") in Base and English.
    • The Progress Tracking footer renders its Markdown: Text only reads Markdown in a literal, so "**Currently Reading**" showed its asterisks. It now goes through AttributedString(markdown:), like the API Access footer, in every locale.

Testing

  • iPhone 17 Pro simulator (iOS 27.0), free account:
    • Themes and App Icon: no Pro card, Unlock in the toolbar, locked items shown locked. Unlock opens the Tip Jar sheet (X, Restore, the three prices); X closes it.
    • With a local patch that fakes a successful tip (not in this PR): confetti and the thank-you alert; OK closes the sheet back to Themes. The patch skipped the account update, so the unlock itself after a real tip wasn't exercised.
    • The Settings list still shows the Pro card.
    • Hardcover: "Currently Reading" in bold without asterisks; the API footer reads "You can find…" with its link.
  • iOS 26.5 simulator: the release check's testFreshLaunch, testImport and testPlayback pass.
  • Unit tests: all 972 pass.

Themes and App Icon showed the BookPlayer Pro card, the same one at the top of the
Settings list, which beside the list on a wide screen meant two of them on screen. A tip
is what unlocks the extra themes and icons, so those screens now offer it directly:

- While the account is free, their toolbar's Restore becomes "Unlock", which opens the Tip
  Jar in a sheet (unlocksWithTipJar(), with a close button). The Tip Jar keeps its own
  Restore, and its thank-you closes the sheet; the screens follow the account's access
  level, so they unlock as it updates. Accounts with access see neither.
- The Pro card is gone from both; showPro with it. The card stays at the top of the list.
- New string unlock_button in every locale.

Hardcover settings, nearby:
- "You can find your Hardcover API access at…" (was "Your can find…").
- The Progress Tracking footer renders its Markdown bold: Text only reads Markdown in a
  literal, so "**Currently Reading**" showed its asterisks.
@github-actions

Copy link
Copy Markdown

✅ Claude PR Review — PASS

The PR swaps the Pro card and Restore button on Themes and App Icon for an Unlock toolbar button, shown only to free accounts. It opens the Tip Jar in a sheet (unlocksWithTipJar()). It also adds the unlock_button string in every locale and fixes two Hardcover footers (a typo, and Markdown that showed its asterisks).

Entitlement: nothing new is trusted. Tipping and restoring still go through AccountService.updateAccount(donationMade:), and the screens read the observable accountService.accessLevel, so they unlock in place. Restore is still in the Tip Jar's own toolbar.

Concurrency/lifecycle: no Combine, player, CoreData/SwiftData or threading changes. SettingsThemesView dropped its loadingState, but ThemesView never used it. SettingsAppIconsView keeps its own for AppIconView errors. The sheet passes theme down, and \.accountService reaches it as an ordinary environment value. The Tip Jar's existing dismiss() closes the sheet.

Older-iOS check: the new .toolbar { if … { ToolbarItem } } sits directly in the toolbar closure, not inside a ForEach. Other screens already use the same pattern (ChaptersView, MediaServersView, JellyfinRootView). The release check never opens Themes or App Icon, so look at both once on an iOS 26 simulator. The new string key is in Base.lproj, and the close button has a VoiceOver label.

Findings: no findings

Converged: nothing new this round, and no earlier finding is open.

Model claude-opus-5-5 · run log · 0 new · 0 carried over · 0 resolved · advisory (a human should still review). Findings are de-duplicated across pushes; an earlier finding closes only when the verification pass judges it against the current code — fixed, no longer applicable, accepted by a maintainer, or a duplicate of a finding reported on this push.

@GianniCarlo
GianniCarlo merged commit 77599b5 into develop Oct 15, 2026
3 checks passed
@GianniCarlo
GianniCarlo deleted the feat/settings-unlock-tip-jar branch October 15, 2026 03:26

This branch was successfully deployed

1 active deployment
reviewer — 93a8c782 Deployed Oct 15, 2026 by GianniCarlo via review #270
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