Skip to content

Add iOS 26 Liquid Glass tab bar enhancements#1

Open
JamesLautner wants to merge 1 commit into
mainfrom
agents/apollo-tabbar-plan-update
Open

Add iOS 26 Liquid Glass tab bar enhancements#1
JamesLautner wants to merge 1 commit into
mainfrom
agents/apollo-tabbar-plan-update

Conversation

@JamesLautner

Copy link
Copy Markdown
Owner

Introduce configurable toggles to replicate Apple Music's iOS 26 tab bar behavior, allowing users to move the Search tab to the right and control the tab bar's minimization on scroll. Both features default to OFF and require a restart to apply.

Adds two independent toggles to replicate Apple Music's iOS 26 tab bar behavior:

- Move Search Tab to Right: Reorders viewControllers so Search is always last
- Shrink Tab Bar on Scroll: Controls minimizationBehavior (0=automatic collapse, 1=always expanded)

Changes:
- UserDefaultConstants.h: Add UDKeySearchTabRight and UDKeyTabBarShrinkOnScroll
- Tweak.xm: Extend ApolloTabBarController hook with viewDidLoad logic and two helper methods
- CustomAPIViewController.m: Add two toggle rows to General section and their handler methods

Both toggles default to OFF and require app restart to apply.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 44aef1ce3d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Tweak.xm
Comment thread src/Tweak.xm
Comment on lines +1001 to +1005
SEL setter = NSSelectorFromString(@"setMinimizationBehavior:");
if ([self.tabBar respondsToSelector:setter]) {
// 0 = automatic (shrink on scroll), 1 = never (always expanded)
NSInteger value = shrinkOnScroll ? 0 : 1;
[self.tabBar setValue:@(value) forKey:@"minimizationBehavior"];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply the native minimize API to the tab controller

When the new “Shrink Tab Bar on Scroll” setting is enabled on iOS 26, this code checks self.tabBar for setMinimizationBehavior: and then sets minimizationBehavior on the UITabBar; however the existing iOS 26 implementation in src/ApolloAutoHideTabBar.xm detects setTabBarMinimizeBehavior: on UITabBarController and uses raw value 2 for on-scroll-down. In this form the selector check never reaches the real API, so the setting is a no-op for the scenario it is meant to control.

Useful? React with 👍 / 👎.

Comment thread src/Tweak.xm
Comment on lines +1019 to +1021
- (void)dealloc {
%orig;
[[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:UDKeyApolloPostCommentsSnapshots];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Unregister KVO before calling original dealloc

If an ApolloTabBarController instance is deallocated after this observer was added, calling %orig first lets the original dealloc tear down/super-deallocate the object before it is passed back to removeObserver:forKeyPath:. KVO cleanup should happen before %orig; otherwise teardown can use a deallocated observer or leave the registration active, causing a crash during controller destruction.

Useful? React with 👍 / 👎.

JamesLautner pushed a commit that referenced this pull request Jul 2, 2026
…pollo-Reborn#442)

* Add Web JSON spike: dormant OAuth-free escape hatch (flag-gated)

Spike proving Apollo can browse via cookie-authenticated
www.reddit.com/...json instead of oauth.reddit.com + bearer tokens, as
a recovery path for Reddit API-key revocation. Dormant by default
behind the "Web JSON Mode" flag; flag-off behavior is unchanged.

- ApolloWebJSON.{h,m}: whitelist-gated request rewrite (subreddit/front
  listings, GET only) spliced into the __NSCFLocalSessionTask chokepoint
  in Tweak.xm — strips Authorization, attaches harvested session cookie.
- ApolloWebSessionLoginViewController.{h,m}: WKWebView login that detects
  auth state via an /api/me.json probe (not cookie presence) and prompts
  Keep/Re-authenticate/Cancel on an existing session.
- Settings: "Web JSON Mode" switch + "Web Session Login" row.
- Flag + cookie-header globals (spike-grade NSUserDefaults storage).

Transport verified in the iOS 26 simulator (cookie-authed feed renders
with zero response transformation). Identity integration for keyless
cold start is deferred — see docs/web-json-spike-findings.md.

* Web JSON: full OAuth-free migration (reads, writes, identity, lifecycle)

Builds out the deferred work from docs/web-json-spike-findings.md so the
cookie-session escape hatch is a usable read+write client that works without
Reddit API keys. Flag-gated and dormant by default (Web JSON Mode off ->
byte-for-byte stock behavior).

Item 1 - Full read coverage: ApolloWebJSONClassifyReadPath routes listings,
comments, user pages, search, subscriptions, inbox, prefs, about/wiki, and
every /api/* GET (no .json suffix for those) to www.reddit.com with cookie
auth; unrecognized paths fall through to oauth untouched.

Item 2 - Writes via modhash: routable /api/* POST/PUT/DELETE re-point to
www.reddit.com with the session cookie + X-Modhash (harvested from
/api/me.json at login). Token/media-upload endpoints excluded.

Item 3 - Identity (ApolloWebJSONIdentity.xm): RDKClient reports authenticated
and gets a synthetic credential; the token mint/refresh methods are
short-circuited to an instant success (completion confirmed in Hopper to be
void(^)(id, NSError*)) so a keyless cold start doesn't stall on the failing
access_token POST. For the no-account case, a signed-in account is synthesized
from the cookie identity by writing the on-disk blobs AccountManager's loader
reads (RedditAccounts2 [RDKClient], 2RedditAccounts2 Valet [[String:String]],
CurrentRedditAccountIndex) - the exact gate the vote/comment UI and account tab
check. Runs at login harvest (restart prompt) and in %ctor before AccountManager
loads. Verified end-to-end in the iOS 26 simulator: account tab shows the user,
personalized reads load, upvote/downvote route to /api/vote with cookie+modhash
and register (no "Sign In to Upvote").

Item 4 - Lifecycle + keychain: cookie/modhash/username moved to the keychain
(migrated out of NSUserDefaults). A 403 text/html block page on a cookie-authed
request posts ApolloWebJSONSessionExpiredNotification -> one-shot "sign in
again" prompt.

Remaining: on-device validation of the real keychain/Valet account write and
live write round-trips (sim uses Tweak.xm's virtualized Valet).

* Apply suggestions from code review

Co-authored-by: JeffreyCA <jeffreyca16@gmail.com>

* Web JSON: address review feedback

- Session-expiry detection: replace the latch-on-first-block-page heuristic
  with a consecutive-streak counter (threshold 3). Any non-block response on a
  cookie-authed request resets the streak, so a transient Cloudflare /
  rate-limit / captcha 403 HTML page no longer fires a spurious "session
  expired" prompt; only a sustained run of block pages declares the cookie
  dead. Counter also resets on a fresh harvest.
- Log unclassified reads that fall through to oauth, so a stray 401 in the
  keyless case (synthetic dummy bearer) is traceable to an unrouted path.
- Centralize the API Keys row-index remapping into ApolloAPIKeyCanonicalRow()
  + an ApolloAPIKeyRow enum (mirrors the existing ApolloMediaLogicalRow
  pattern), replacing the four inlined copies across the cell builder,
  didSelect, shouldHighlight, viewWillAppear, the toggle, and the row count.
- Refresh the stale "spike-grade storage" note: the cookie now lives in the
  keychain; the NSUserDefaults key is retained only for one-time migration.

* Web JSON: fix profile listing, comment edit/post, and upload fallback

Address PR Apollo-Reborn#442 tester feedback for keyless Web JSON mode.

Profile posts/comments empty (Apollo-Reborn#3): the account loaded with a nil
currentUser.username (NSKeyedArchiver omits nil values, so the RDKMe was
archived with no username key), so /user/<name>/overview could never fire
and the profile tab just spun. Backfill the username onto the live
currentUser from the harvested session identity at -[RDKClient
setCurrentUser:] (the on-disk blob can't be repaired -- RDKMe's MTLModel
encoding drops a re-set username), with a belt-and-suspenders backfill in
the synthetic-credential install.

Comment editing (Apollo-Reborn#4): the Edit affordance returns once the username is
non-nil (same root cause as Apollo-Reborn#3). Also fix the empty re-render after an
edit/new comment: www.reddit.com's /api/editusertext and /api/comment
return each thing in the legacy old-reddit {parent, content:"<html>"}
shape instead of the modern comment JSON, so Apollo rendered an empty
comment with 0 upvotes. ApolloWebJSONFixupWriteResponseObject (hooked at
RDKResponseSerializer) detects the legacy shape, pulls the fullname from
the content HTML, re-fetches the modern object via info.json, and swaps it
into the things envelope.

Image uploads (Apollo-Reborn#5): native keyless Reddit upload isn't viable -- a spike
confirmed www.reddit.com/api/media/asset.json returns Reddit's 403 block
page for cookie+modhash auth (every modhash placement), it needs a real
OAuth bearer. Keep the media lease on the oauth path always (routing it to
www would also break uploads for a real-account user with Web JSON on), and
in a keyless session short-circuit the upload hooks before the doomed lease
to fall back to Apollo's Imgur path, with accurate guidance when no Imgur
key is set (the old copy wrongly suggested switching the provider to Reddit
to upload without keys). Inert for non-Web-JSON users.

Also includes the earlier fixes: the spurious session-expired prompt (#1,
verify via /api/me.json probe before announcing) and the post-login restart
UX polish (Apollo-Reborn#2).

---------

Co-authored-by: JeffreyCA <jeffreyca16@gmail.com>
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