Skip to content

Add focus requesters for search bar and TV navigation rail#1

Merged
sang765 merged 3 commits into
settings/internetfrom
dev
Apr 22, 2026
Merged

Add focus requesters for search bar and TV navigation rail#1
sang765 merged 3 commits into
settings/internetfrom
dev

Conversation

@sang765

@sang765 sang765 commented Apr 22, 2026

Copy link
Copy Markdown
Owner

No description provided.

@coderabbitai

coderabbitai Bot commented Apr 22, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ce34722-9d17-4314-b128-fce765be8021

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Add bidirectional focus navigation between search and TV rail

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add focus navigation support between search bar and TV navigation rail
• Implement left focus requester in search input field for TV navigation
• Add content focus requester to TV navigation rail for right navigation
• Initialize TV rail focus on component mount using LaunchedEffect
Diagram
flowchart LR
  TVRail["TV Navigation Rail"]
  SearchBar["Search Bar"]
  MainActivity["MainActivity"]
  
  MainActivity -- "firstItemFocusRequester" --> TVRail
  MainActivity -- "contentFocusRequester" --> SearchBar
  TVRail -- "focusProperties right" --> SearchBar
  SearchBar -- "focusProperties left" --> TVRail
  TVRail -- "LaunchedEffect requestFocus" --> TVRail
Loading

Grey Divider

File Changes

1. app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt ✨ Enhancement +2/-0

Connect focus requesters between navigation components

• Pass searchBarFocusRequester as contentFocusRequester to TvNavigationRail
• Pass tvRailFocusRequester as leftFocusRequester to TopSearch component
• Establishes bidirectional focus navigation between rail and search bar

app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt


2. app/src/main/kotlin/moe/koiverse/archivetune/ui/component/SearchBar.kt ✨ Enhancement +13/-0

Add left focus navigation to search input field

• Add leftFocusRequester optional parameter to TopSearch function
• Pass leftFocusRequester to SearchBarInputField component
• Apply focusProperties modifier to BasicTextField to set left focus target
• Import focusProperties from Compose UI focus module

app/src/main/kotlin/moe/koiverse/archivetune/ui/component/SearchBar.kt


3. app/src/main/kotlin/moe/koiverse/archivetune/ui/component/TvNavigationRail.kt ✨ Enhancement +24/-5

Add right focus navigation and initial focus handling

• Add contentFocusRequester optional parameter for right focus navigation
• Implement LaunchedEffect to request focus on firstItemFocusRequester on mount
• Apply focusProperties modifier to set right focus target to contentFocusRequester
• Import LaunchedEffect and focusProperties from Compose runtime and focus modules

app/src/main/kotlin/moe/koiverse/archivetune/ui/component/TvNavigationRail.kt


Grey Divider

Qodo Logo

@sang765 sang765 merged commit c69dc3b into settings/internet Apr 22, 2026
2 checks passed
@qodo-code-review

qodo-code-review Bot commented Apr 22, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Rail right focus dead-end 🐞 Bug ☼ Reliability
Description
TvNavigationRail forces DPAD-right to contentFocusRequester, but MainActivity wires it to
searchBarFocusRequester while TopSearch is not composed when active is false. This can trap
focus on the rail or trigger FocusRequester errors on TV when navigating right from the rail on
non-search screens.
Code

app/src/main/kotlin/moe/koiverse/archivetune/ui/component/TvNavigationRail.kt[R78-94]

+                    modifier = Modifier
+                        .then(
+                            if (index == 0 && firstItemFocusRequester != null) {
+                                Modifier.focusRequester(firstItemFocusRequester)
+                            } else {
+                                Modifier
+                            }
+                        )
+                        .then(
+                            if (contentFocusRequester != null) {
+                                Modifier.focusProperties {
+                                    right = contentFocusRequester
+                                }
+                            } else {
+                                Modifier
+                            }
+                        ),
Evidence
When the rail is visible, search is inactive (!active), and TopSearch is not composed; however the
rail still routes DPAD-right to searchBarFocusRequester, which therefore has no attached focus
node. The codebase already shows requestFocus() can throw and is guarded in other places, making
this focus route a real stability risk.

app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt[762-767]
app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt[1169-1186]
app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt[1354-1359]
app/src/main/kotlin/moe/koiverse/archivetune/ui/component/TvNavigationRail.kt[74-96]
app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt[1803-1810]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`TvNavigationRail` routes DPAD-right to a `contentFocusRequester` that is currently wired to `searchBarFocusRequester`, but `TopSearch` is not composed in the common state where the rail is visible (`!active`). This can cause focus to get stuck on the rail or crash when focus traversal attempts to focus an unattached FocusRequester.

## Issue Context
- Rail is visible only when `shouldShowNavigationBar` which requires `!active`.
- `TopSearch` is only composed when `active` (or when route starts with `search/`).
- So when the rail is visible, `searchBarFocusRequester` often has no attached node.

## Fix Focus Areas
- app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt[762-767]
- app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt[1169-1186]
- app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt[1354-1359]
- app/src/main/kotlin/moe/koiverse/archivetune/ui/component/TvNavigationRail.kt[74-96]

## Suggested fix
- Only set `right = contentFocusRequester` when the target is guaranteed to be composed (otherwise pass `null` / skip the focusProperties override).
- Alternatively, pass a real "content" FocusRequester that is always attached when the rail is visible (e.g., the first focusable item in the current screen content), instead of using `searchBarFocusRequester`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@kilo-code-bot

kilo-code-bot Bot commented Apr 22, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (3 files)
  • app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt - +2 lines
  • app/src/main/kotlin/moe/koiverse/archivetune/ui/component/SearchBar.kt - +13 lines
  • app/src/main/kotlin/moe/koiverse/archivetune/ui/component/TvNavigationRail.kt - +24 lines

The implementation adds bidirectional focus navigation between the TV navigation rail and search bar. The code:

  • Uses proper null-safe patterns with default null values for backwards compatibility
  • Correctly chains Compose modifiers using .then()
  • Properly uses LaunchedEffect to request focus on component mount
  • Follows existing codebase patterns for focus requesters

No runtime errors, logic bugs, or security issues identified.


Reviewed by minimax-m2.5-20260211 · 68,897 tokens

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements explicit focus navigation between the TV navigation rail and the search bar by passing focus requesters and utilizing focusProperties. Feedback indicates that a redundant LaunchedEffect in TvNavigationRail should be removed to avoid focus management conflicts with existing logic in MainActivity. Additionally, the modifier chains in both SearchBar and TvNavigationRail should be refactored to use conditional logic within the focusProperties block for more idiomatic and readable Compose code.

Comment on lines 57 to +61
) {
LaunchedEffect(firstItemFocusRequester) {
firstItemFocusRequester?.requestFocus()
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

This LaunchedEffect should be removed. MainActivity.kt already manages the initial focus for the navigation rail (lines 990-1001) with specific logic to ensure it doesn't steal focus when the search bar is active. Removing this ensures that focus is managed centrally and avoids potential race conditions or focus-stealing during recomposition.

) {

Comment on lines +287 to +295
.then(
if (leftFocusRequester != null) {
Modifier.focusProperties {
left = leftFocusRequester
}
} else {
Modifier
}
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The modifier chain can be simplified by using focusProperties directly with a conditional check inside the block, rather than using .then(if (...)). This is cleaner and more idiomatic in Compose.

                .focusProperties {
                    leftFocusRequester?.let { left = it }
                }

Comment on lines +78 to +94
modifier = Modifier
.then(
if (index == 0 && firstItemFocusRequester != null) {
Modifier.focusRequester(firstItemFocusRequester)
} else {
Modifier
}
)
.then(
if (contentFocusRequester != null) {
Modifier.focusProperties {
right = contentFocusRequester
}
} else {
Modifier
}
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The modifier chain for TvNavigationRailItem can be simplified. Instead of using multiple .then(if (...)) blocks, you can call focusProperties directly and use a conditional check inside the block. This improves readability and maintainability.

                    modifier = Modifier
                        .then(
                            if (index == 0 && firstItemFocusRequester != null) {
                                Modifier.focusRequester(firstItemFocusRequester)
                            } else {
                                Modifier
                            }
                        )
                        .focusProperties {
                            contentFocusRequester?.let { right = it }
                        },

Comment on lines +78 to +94
modifier = Modifier
.then(
if (index == 0 && firstItemFocusRequester != null) {
Modifier.focusRequester(firstItemFocusRequester)
} else {
Modifier
}
)
.then(
if (contentFocusRequester != null) {
Modifier.focusProperties {
right = contentFocusRequester
}
} else {
Modifier
}
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Rail right focus dead-end 🐞 Bug ☼ Reliability

TvNavigationRail forces DPAD-right to contentFocusRequester, but MainActivity wires it to
searchBarFocusRequester while TopSearch is not composed when active is false. This can trap
focus on the rail or trigger FocusRequester errors on TV when navigating right from the rail on
non-search screens.
Agent Prompt
## Issue description
`TvNavigationRail` routes DPAD-right to a `contentFocusRequester` that is currently wired to `searchBarFocusRequester`, but `TopSearch` is not composed in the common state where the rail is visible (`!active`). This can cause focus to get stuck on the rail or crash when focus traversal attempts to focus an unattached FocusRequester.

## Issue Context
- Rail is visible only when `shouldShowNavigationBar` which requires `!active`.
- `TopSearch` is only composed when `active` (or when route starts with `search/`).
- So when the rail is visible, `searchBarFocusRequester` often has no attached node.

## Fix Focus Areas
- app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt[762-767]
- app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt[1169-1186]
- app/src/main/kotlin/moe/koiverse/archivetune/MainActivity.kt[1354-1359]
- app/src/main/kotlin/moe/koiverse/archivetune/ui/component/TvNavigationRail.kt[74-96]

## Suggested fix
- Only set `right = contentFocusRequester` when the target is guaranteed to be composed (otherwise pass `null` / skip the focusProperties override).
- Alternatively, pass a real "content" FocusRequester that is always attached when the rail is visible (e.g., the first focusable item in the current screen content), instead of using `searchBarFocusRequester`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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.

2 participants