Skip to content

User-configurable bottom navigation + NomadNet browsing polish - #1124

Merged
torlando-tech merged 9 commits into
mainfrom
feature/configurable-bottom-nav
Sep 8, 2026
Merged

User-configurable bottom navigation + NomadNet browsing polish#1124
torlando-tech merged 9 commits into
mainfrom
feature/configurable-bottom-nav

Conversation

@torlando-tech

Copy link
Copy Markdown
Owner

Summary

  • User-configurable bottom navigation bar (up to 5 tabs, Settings pinned last; persisted and sanitized).
  • NomadNet tab is a normal tab: the bar stays visible while browsing; tab taps collapse the browser stack so views can never stack duplicates.
  • Close Site now forgets the persisted last-node binding and resets browser state, so the tab reopens at the address-entry prompt.
  • Empty NomadNet tab: dedicated always-present address field (fixes the one-frame editor flash), no auto-focus (keyboard stays hidden until the user taps the field), and the entry block is biased upward with IME padding so the Go button is never under the keyboard.
  • Node Details keeps the bottom nav bar and pads its content 88.dp above it (removes the jarring bar hide/pop when returning from NomadNet).

Test notes

  • Verified on-device (S21 Ultra): tab config persistence, Close Site flow, no duplicate browser views, keyboard/focus behavior, Node Details nav bar + bottom clearance.
  • Unit suites green (NomadNet screen + viewmodel incl. new closeSite regression test, navigation contract tests); ktlint clean; assembleNoSentryPythonBackendDebug green.

torlando-tech and others added 7 commits September 2, 2026 15:33
Let users choose which shortcuts appear in the bottom bar and in what
order, up to NavTab.MAX_TABS (5) entries. Settings is pinned last and
cannot be removed; the last editable shortcut cannot be removed either.

- NavTab registry with sanitize() as the single normalization authority:
  unknown ids dropped, dedupe, pinned Settings appended, cap enforced,
  fallback to the default layout so a corrupt pref can never render an
  empty bar
- BottomNavigation settings card: add chips, remove, up/down reorder
- Tab selection is now route-prefix based; the index-based selectedTab
  state and its route-sync effect are gone
- The NomadNet tab keeps the bottom bar visible while browsing pages
  (previously the browser hid it). The tab opens the last-browsed node
  via the new nomadnet_home destination, with an address-entry prompt
  on a fresh install. Page content pads 88.dp to clear the bar.
- Layout persists in DataStore (bottom_nav_tabs CSV); the browser
  records nomadnet_last_node on every successful page load
…ar edit

Two device-found bugs in the configurable bottom bar:

- loadSettings()'s combine rebuilds SettingsState from scratch and
  re-passes every collector-owned field. bottomNavTabs and
  nomadNetLastNodeHash were missing from that preserve list, so every
  settings emission reset them to defaults; distinctUntilChanged meant
  the collector never re-emitted, leaving the bar stuck on defaults
  after a restart despite the DataStore value being correct.

- The address-bar BasicTextField's onFocusChanged handler re-fired the
  initial unfocused state when it composed, immediately cancelling the
  edit mode the 'Enter address' home prompt just enabled. Switched to
  onFocusEvent, which only fires on real focus transitions.
The home prompt toggled the top-bar isEditingUrl to reveal the address
bar, but that shared focus/edit state raced with the field's focus
handler and the editor flashed away after a single frame. The home page
now renders its own always-present, auto-focused address field with a
Go button (Chrome new-tab omnibox pattern) that submits directly via
navigateToUrl - no shared edit-state to clobber.
… on tab switch

- Close Site now clears the persisted nomadnet_last_node binding and
  resets browser state, so the bottom-nav tab reopens at the address
  prompt instead of the closed site
- Tab home keeps its position (swaps to the prompt in place); the
  standalone browser pops like Back
- Tapping a different tab while browsing collapses the nomadnet stack
  first, preventing duplicate browser views
…keyboard

- The empty NomadNet tab no longer steals focus, so opening the tab does
  not pop the soft keyboard
- The entry block is biased to the upper third and pads for the IME, so
  the address field and Go button stay clear when the keyboard opens
announce_detail was in the hide-the-bar prefix list, which made the bar
vanish on Node Details and pop back on return - jarring next to the new
always-visible browsing flow.
The scroll column ended at 16.dp, so the last card sat behind the
nav bar now that the bar is visible on this screen. Use the same
88.dp bottom clearance as the other nav-visible screens.
@greptile-apps

greptile-apps Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds a persisted, sanitized, user-configurable bottom navigation bar and integrates NomadNet as a normal top-level tab while polishing its browsing, empty-entry, and Close Site behavior.

  • Adds configurable tab selection and ordering with Settings pinned last.
  • Persists the selected tabs and last visited NomadNet node through DataStore-backed settings.
  • Keeps bottom navigation visible through NomadNet and Node Details flows.
  • Makes Close Site reset browser state and reliably remove the persisted node binding.
  • Adds navigation, settings, and NomadNet regression tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
app/src/main/java/network/columba/app/MainActivity.kt Replaces fixed tab-index navigation with route-derived configurable tabs and adds the NomadNet top-level flow.
app/src/main/java/network/columba/app/navigation/NavTab.kt Defines the configurable tab registry and sanitizes persisted layouts by deduplicating, bounding, and pinning Settings.
app/src/main/java/network/columba/app/repository/SettingsRepository.kt Adds durable tab and NomadNet-node preferences, with the save guard evaluated atomically inside the DataStore edit.
app/src/main/java/network/columba/app/ui/screens/NomadNetBrowserScreen.kt Adds the stable empty-tab address entry, explicit Close Site handling, focus polish, IME padding, and bottom-bar clearance.
app/src/main/java/network/columba/app/ui/screens/settings/cards/BottomNavigationCard.kt Provides tab add, remove, and arrow-based ordering controls whose instructions now match the available interaction.
app/src/main/java/network/columba/app/viewmodel/NomadNetBrowserViewModel.kt Adds Close Site state cleanup and guarded last-node persistence without leaving the previously reported save-after-clear race reachable.
app/src/main/java/network/columba/app/viewmodel/SettingsViewModel.kt Collects persisted navigation state and exposes sanitized tab updates to the application shell.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[User taps NomadNet tab] --> B{Saved node exists?}
  B -->|No| C[Show address-entry prompt]
  B -->|Yes| D[Load saved node]
  C -->|Submit address| E[Browse NomadNet page]
  D --> E
  E --> F[Persist active node]
  E -->|Close Site| G[Set browser state to Initial]
  G --> H[Clear persisted node]
  H --> C
  E -->|Tap another tab| I[Collapse NomadNet stack]
  I --> J[Navigate to selected tab]
Loading

Reviews (3): Last reviewed commit: "fix(nomadnet): evaluate the last-node sa..." | Re-trigger Greptile

@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

- emitPageLoaded only persists the node hash while the view still shows
  that page, so a page that landed just before Close Site can no longer
  re-save the binding behind closeSite's clear (greptile P1)
- Bottom Navigation card copy now describes the arrow controls instead
  of drag-to-reorder, which the UI does not implement (greptile P2)
@torlando-tech

Copy link
Copy Markdown
Owner Author

Both greptile findings addressed in 3805272:

  • P1 Close Site races persistence - valid. The save in emitPageLoaded is now guarded: it only persists while the view state is still that page, so a page that completed just before Close Site can no longer re-save the binding behind closeSite's clear. (DataStore serializes edits, so clear-then-guarded-save ordering is stable.)
  • P2 Copy advertises unsupported dragging - valid, applied the suggested copy (arrow controls, not drag).

… transaction

Closes the remaining check-then-write window greptile flagged: the
PageLoaded predicate now runs under the DataStore edit mutex, so a save
either loses to Close Site's clear (predicate false, nothing written) or
commits before it and is properly erased. No interleaving can leave a
just-closed site persisted.
@torlando-tech

Copy link
Copy Markdown
Owner Author

Follow-up P1 (3928581672) also addressed in the next push: the guard moved inside the DataStore edit transaction via a new whileActive parameter on saveNomadNetLastNodeHash, so the check and the write are serialized against clearNomadNetLastNodeHash by the DataStore mutex. The resurrect interleaving is now structurally impossible.

@torlando-tech
torlando-tech merged commit a3392a3 into main Sep 8, 2026
13 checks passed
@torlando-tech
torlando-tech deleted the feature/configurable-bottom-nav branch September 8, 2026 05:13
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