Skip to content

Release candidate (tablet adaptive layout + UI polish)#69

Merged
NateEaton merged 15 commits into
mainfrom
release-candidate
Feb 20, 2026
Merged

Release candidate (tablet adaptive layout + UI polish)#69
NateEaton merged 15 commits into
mainfrom
release-candidate

Conversation

@NateEaton

Copy link
Copy Markdown
Owner

Phase 5: Tablet & Adaptive Layout + UI Polish

Implements a fully adaptive layout supporting compact phones, medium screens (phone landscape, tablet portrait), and expanded screens (tablet landscape), replacing the previous single-column phone-only layout. Also includes a set of UI polish changes merged from the feature branch.

Navigation

  • Compact (W < 600dp): ModalNavigationDrawer (unchanged)
  • Medium (W ≥ 600dp): NavigationRail — persistent during list browsing, hidden during reading; scrollable independently of the list in landscape
  • Expanded (W ≥ 840dp, landscape, tablet height): PermanentNavigationDrawer — visible during list browsing, hidden during reading

Breakpoint detection uses WindowWidthSizeClass + orientation + WindowHeightSizeClass to prevent phone-in-landscape from hitting the Expanded tier.

Settings and About screens suppress the back button on medium/expanded (user navigates via rail/drawer). Rail/drawer taps pop back to the list if currently on Settings/About.

Multi-Column Grid

LazyVerticalGrid with GridCells.Adaptive on medium/expanded screens:

  • Grid mode: 250dp min cell width
  • Mosaic mode: 180dp min cell width
  • Compact mode: always single column regardless of screen width

Grid cards use a fixed 300dp height with a 140dp image, weight(1f) content column, and Spacer(weight(1f)) before actions — all cards in a row are the same height with action icons anchored at the bottom. Labels are a single-line horizontally scrollable row.

Card Variants

New wide-layout variants for medium/expanded screens, selected via LocalIsWideLayout:

  • BookmarkGridCardWide — image above, title/site/labels/actions below
  • BookmarkCompactCardWide — thumbnail left, content right
  • BookmarkMosaicCard — image-only with overlay title

Reading Mode

Reading always fills the full screen on all device types (no split-pane). Navigation rail/drawer is hidden during reading. Content width is fillMaxWidth(0.9f) for Wide text setting and fillMaxWidth(0.8f) for Narrow — consistent margins across all screen sizes and orientations.

Full-screen loading spinner is shown while article content is being fetched on-demand, replacing the previous small inline spinner.

Favorites Icon

Replaced Icons.Filled.Grade (star) with Icons.Filled.Favorite / Icons.Outlined.FavoriteBorder (heart) across all surfaces: bookmark cards, detail top bar, navigation drawer, and navigation rail.

Delete Snackbar

New TimedDeleteSnackbar component replaces the plain snackbar for bookmark deletion. A colored progress bar drawn along the bottom edge of the snackbar counts down the 5-second undo window before auto-dismissal.

Add Bookmark Sheet

  • Smooth animated progress bar during auto-save
  • Favorite toggle button added alongside the label input field
  • isFavorite state threaded through BookmarkListViewModel, CreateBookmarkWorker, and ShareActivity so bookmarks saved via the share sheet or add dialog are created with the correct favorite flag

Settings Screens

  • HorizontalDividers added between items in SettingsScreen
  • Card/shaded backgrounds removed from SyncSettingsScreen sections; replaced with HorizontalDividers between Bookmark Sync, Content Sync, and Sync Status sections

Navigation Drawer Polish

  • App title row aligned with the topbar height (removed excess top spacer)
  • Vertical padding added around dividers so selected-item highlights don't touch separator lines
  • Divider between Archive and Favorites removed

Other

  • VerticalScrollbar component added for grid/list views on larger screens
  • Animated transitions between list and detail screens
  • theme_system string shortened to "System" in English, Spanish, and Chinese locales

Step 1: Dependency updates
- Replace monolithic material3-adaptive:1.0.0-alpha02 with split artifacts at 1.1.0
  (adaptive, adaptive-layout, adaptive-navigation)
- Remove material3-window-size-class entry (replaced by currentWindowAdaptiveInfo())

Step 2: BookmarkDetailViewModel refactor
- Change bookmarkId from String? to MutableStateFlow<String?>
- Make uiState reactive via filterNotNull().flatMapLatest
- Extract init block logic into private initializeBookmark(id)
- Add public loadBookmark() for expanded pane reuse
- Update all internal references to use flow value
- SavedStateHandle path unchanged for compact/medium

Step 3: Extract BookmarkDetailHost
- Extract outer BookmarkDetailScreen body into BookmarkDetailHost composable
- Takes viewModel, showOriginal, onNavigateBack parameters
- Outer composable becomes thin wrapper passing popBackStack()
- All behavior identical after refactor

Phone behavior (compact size class) is unchanged.
- Step 4: Add AppNavigationRailContent with icons-only NavigationRail,
  Settings/About pushed to bottom, same selected-state logic as drawer
- Step 5: Refactor AppDrawerContent to extract DrawerColumnContent,
  add usePermanentSheet param (ModalDrawerSheet vs PermanentDrawerSheet)
- Step 8 (medium): Extract CompactAppShell (pure extraction, zero behavior
  change), add MediumAppShell (Row + NavigationRail + NavHost), branch
  AppShell on WindowWidthSizeClass (Compact → modal drawer, else → rail)
- Step 9: Add showNavigationIcon param to BookmarkListScreen (default true);
  MediumAppShell passes false to hide hamburger when rail is visible
- Add BookmarkDetailPaneHost.kt: thin wrapper creating a keyed
  BookmarkDetailViewModel that reloads on bookmark ID changes
- Add ListDetailLayout.kt: ExpandedListDetailLayout wrapping
  ListDetailPaneScaffold with list/detail panes and BackHandler
- Add ExpandedAppShell to AppShell.kt with PermanentNavigationDrawer,
  navigation event interception (bookmark detail opens in pane instead
  of push nav), and deep link redirect to list+pane
- Add select_bookmark string resource to all 10 strings.xml files
- Add LocalIsWideLayout CompositionLocal (default false)
- BookmarkMosaicCard: reduce padding to 1dp when wide
- BookmarkGridCard: wide variant shows full-width 16:9 image above content
- BookmarkCompactCard: wide variant moves actions to title row, labels inline with site name
- AppShell: provide LocalIsWideLayout=true in MediumAppShell and ExpandedAppShell
- Add LocalReaderMaxWidth CompositionLocal and ReaderMaxWidthMedium/
  ReaderMaxWidthExpanded constants to Dimens.kt
- Provide LocalReaderMaxWidth in AppShell based on window size class:
  Compact → Dp.Unspecified, Medium → 720.dp, Expanded → 840.dp
- Wrap BookmarkDetailScreen Scaffold content in centering Box with
  widthIn(max) constraint when a max width is set
- Replace width-only breakpoint with layout tier using width + orientation + height
  (phone landscape → Medium rail, not Expanded)
- Remove hoisted selectedBookmarkId/selectedShowOriginal pane state
- Add route-aware hideNavigation (hides rail/drawer during reading and welcome)
- Rewrite MediumAppShell: wrap rail in AnimatedVisibility, weighted Surface
- Rewrite ExpandedAppShell: replace PermanentNavigationDrawer + ListDetailPaneScaffold
  with Row + AnimatedVisibility around AppDrawerContent + standard NavHost push nav
- Delete ListDetailLayout.kt and BookmarkDetailPaneHost.kt
- Remove adaptive-layout and adaptive-navigation dependencies
…ck button

- Add launchSingleTop=true to NavigateToSettings/NavigateToAbout in all
  three shells (Compact, Medium, Expanded) to prevent back stack stacking
- Add navigateToListAndApply helper in MediumAppShell and ExpandedAppShell;
  wrap all 7 list-filter click handlers so clicking rail/drawer items while
  on Settings/About navigates back to BookmarkListRoute first
- Add showBackButton: Boolean = true param to SettingsScreen/SettingScreenView
  and AboutScreen/AboutScreenContent; wrap navigationIcon in if(showBackButton)
- Pass showBackButton=false in Medium/ExpandedAppShell NavHost for Settings
  and About routes (back button redundant when rail/drawer is always visible)
- Add isMultiColumn param to BookmarkListView (defaults to LocalIsWideLayout)
- Use LazyVerticalGrid with GridCells.Adaptive on wide layouts:
  GRID=250dp, COMPACT=350dp, MOSAIC=180dp columns
- Add VerticalScrollbar overload for LazyGridState
- Keep LazyColumn path completely unchanged for compact layouts
… rail scroll, reader width

- Grid layout (landscape/tablet): cards now use fixed 300dp height when in
  LazyVerticalGrid (isInGrid=true), with a fixed 140dp image, weight(1f) on
  the content column, and Spacer(weight(1f)) before actions so all cards in
  a row are the same height and action icons are anchored at the bottom.
  Labels row changed from FlowRow (multi-line wrap) to a single-line
  horizontally scrollable Row to prevent variable card heights.
  Portrait single-column path is unchanged (aspectRatio image, natural height).

- Compact layout mode: always forces single column regardless of device width
  by excluding LayoutMode.COMPACT from the LazyVerticalGrid path.

- Navigation rail (mobile landscape): wrapped AppNavigationRailContent in a
  verticalScroll Column so the rail scrolls independently of the list.

- Reader content width: replaced fixed-dp LocalReaderMaxWidth cap with
  fillMaxWidth(fraction) applied directly to the reader Column — 0.9f for
  WIDE, 0.8f for NARROW — so margins are consistent across all screen sizes
  and orientations.
- Replace Grade icon with Favorite/FavoriteBorder across all nav surfaces
  (BookmarkCard, BookmarkDetailTopBar, AppDrawerContent, AppNavigationRailContent)
- Add TimedDeleteSnackbar with animated progress bar drawn on snackbar surface
- Wire TimedDeleteSnackbar into BookmarkDetailScreen and BookmarkListScreen
- Add isFavorite toggle to AddBookmarkSheet, BookmarkListScreen, BookmarkListViewModel
- Thread isFavorite through ShareActivity and CreateBookmarkWorker
- Add HorizontalDividers between settings items in SettingsScreen
- Replace Card wrappers with plain Columns + dividers in SyncSettingsScreen
- Adjust AppDrawerContent: align title with topbar, add divider spacing, remove Archive/Favorites divider
- Show full-screen loading spinner in reader view while article content is fetching
- Shorten theme_system string in en/es/zh locale files
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@NateEaton NateEaton merged commit 05fd35f into main Feb 20, 2026
2 checks passed
@NateEaton NateEaton deleted the release-candidate branch February 20, 2026 02:04
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