Contextual/DuckAi: Make sheet initial state transparent - #9535
Contextual/DuckAi: Make sheet initial state transparent #9535karlenDimla wants to merge 8 commits into
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
f96ee93 to
6ee7b9b
Compare
6ee7b9b to
59be903
Compare
|
|
||
| private fun requestWriteStoragePermission() { | ||
| requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) | ||
| } |
There was a problem hiding this comment.
Download permission result is ignored
Medium Severity
requestFileDownload can call requestWriteStoragePermission from the download listener’s IO coroutine, and this fragment never implements onRequestPermissionsResult. On API 29 and below the grant is never observed, downloadFile never runs, and requestPermissions is invoked off the main thread.
Reviewed by Cursor Bugbot for commit 59be903. Configure here.
There was a problem hiding this comment.
This is existing in the old fragment, will not fix for now.
59be903 to
a289f69
Compare
a289f69 to
4d75b22
Compare
4d75b22 to
729ad71
Compare
| * fragment) work inside a dialog, just as they do inside a [DuckDuckGoFragment]. | ||
| */ | ||
| @HasMemberInjections | ||
| abstract class DuckDuckGoBottomSheetDialogFragment : BottomSheetDialogFragment(), HasDaggerInjector { |
There was a problem hiding this comment.
This necessary since we are using custom views that also inject dependencies.
| sourceTabId: String, | ||
| anchor: View?, | ||
| onAskAboutPage: () -> Unit, | ||
| showChatSurface: () -> Unit, |
There was a problem hiding this comment.
Renamed this API just because it was not reflective of what they actually do. Will update the original API Proposal. This does not change anything functionally.
| * from the retained tab id, so it survives configuration changes. | ||
| */ | ||
| @InjectWith(FragmentScope::class) | ||
| class DuckChatContextualEntryDialog : DuckDuckGoBottomSheetDialogFragment() { |
There was a problem hiding this comment.
This the dialog that represent the new contextual entry - shows with transparent background.
| * keyboard-driven sheet sizing. | ||
| */ | ||
| @InjectWith(FragmentScope::class) | ||
| class DuckChatContextualWebViewFragment : |
There was a problem hiding this comment.
I ended up creating a separate WEBVIEW only sheet. I wanted to reuse the existing one but there is a lot of logic that resets the sheet to the old entry screen (INPUT mode). TO avoid breaking things there, I exxtracted the WEBVIEW only logic into this sheet.
| * always in the WebView state and carries only the follow-up composer alongside the chat. | ||
| */ | ||
| @ContributesViewModel(FragmentScope::class) | ||
| class DuckChatContextualWebViewViewModel @Inject constructor( |
There was a problem hiding this comment.
I avoided more refactors/improvements than necessary for now since this is already a big change.
be6106a to
b0d1731
Compare
| } | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Voice launcher overwritten after New Chat
High Severity
VoiceSearchLauncher is Activity-scoped and keeps a single registerForActivityResult launcher. The entry dialog and the webview sheet both call registerResultsCallback and can be alive together after New Chat. The dialog’s later registration replaces the sheet’s launcher, so after the dialog is dismissed, voice in the running chat can crash or deliver results nowhere.
Please tell me if this was useful or not with a 👍 or 👎.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit b0d1731. Configure here.
There was a problem hiding this comment.
This is a false positive. Tested several scenarios and I don’t see the voice launcher not working.
So there is no shared voiceSearchActivityLaucher field to overwrite. Each surface’s caller.registerForActivityResult(...) registers against its own fragment and stores the launcher in its own wrapper instance. The entry dialog's registration can't clobber the sheet's, because they're different wrapper objects entirely.
Behind the contextualSheetRedesign flag, present the entry (INPUT) stage as a dedicated DuckChatContextualEntryDialog: a scrimmed, transparent bottom-sheet dialog showing only the suggested prompts and native input over the dimmed page, dismissed by tapping outside or the sheet's empty area. The running chat stays the existing embedded sheet. - DuckChatContextualEntryViewModel owns page-context attach/validity and the prompt hand-off; ContextualEntryPromptStore (per-tab, cleared on tab deletion/fire) parks the prompt and the sheet auto-submits it on web-app ready. - Full-height expanded sheet with a weighted/scrollable prompts area and pinned input so the composer stays above the keyboard in any orientation. - Surface-coloured suggestion + Summarize chips (and loading view) for legibility. - Camera/file attach wired via the shared native input manager. - DuckDuckGoBottomSheetDialogFragment base supplies HasDaggerInjector so self-injecting child views resolve inside a dialog. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When the redesign flag is on, New Chat from the WEBVIEW chats popup now dismisses and clears the current chat and opens the transparent entry dialog, handing the composed prompt back to start a fresh chat. The flag check lives in the ViewModel so the fragment stays flag-agnostic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drive the entry dialog's suggestion chips from the entry view model's retained, validated attachedContext (a StateFlow) instead of the one-shot PageContextAttached command. The context delivered right after open can be blank/late; re-collecting a StateFlow guarantees the latest valid context still reaches the suggestions after the initial load times out, so the prompts populate on first open rather than only on relaunch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Names the chat-in-progress surface it builds, reserving "sheet" for the umbrella contextual surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
createChatSurface now returns a webview-only fragment when the contextual sheet redesign is enabled; the INPUT/entry stage stays in the entry dialog. The new fragment and ViewModel carry only WEBVIEW-mode concerns (the chat plus the follow-up composer), while the redesign-OFF path keeps the existing INPUT/WEBVIEW fragment untouched. New Chat routes its entry-dialog hand-off through the host via a PresentSheet signal so the sheet container is re-shown before the reopened chat loads (previously it loaded into a hidden container), and the entry dialog re-asserts the tab's contextual input state so its composer keeps its affordances. Removes entry-dialog plumbing that moved to the webview path from the OFF-path ViewModel/fragment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Returning from the file/camera picker dropped the entry composer's focus, so the keyboard did not come back. Re-focus after each picker result, requesting soft-keyboard show only when no hardware keyboard is connected to avoid popping the on-screen keyboard over an external/Chromebook keyboard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
b0d1731 to
048b037
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 048b037. Configure here.
| You probably forgot to annotate your class with @InjectWith(Scope::class). | ||
| """.trimIndent(), | ||
| ) | ||
| } |
There was a problem hiding this comment.
Entry dialog view DI crashes
High Severity
DuckDuckGoBottomSheetDialogFragment implements HasDaggerInjector so child views like NativeInputModeWidget and ContextualSuggestionsView can self-inject, but findHasDaggerInjectorForView only accepts DaggerFragment. The entry dialog extends BottomSheetDialogFragment, so attach-time injection does a reified findFragment<DaggerFragment>() cast that throws ClassCastException (only IllegalStateException is caught). Opening Ask about page with the redesign on crashes when those views attach.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 048b037. Configure here.



Task/Issue URL: https://app.asana.com/1/137249556945/project/1208671677432066/task/1217146291865568?focus=true
Tech Design URL (if applicable): https://app.asana.com/1/137249556945/project/481882893211075/task/1217235340374866
API Proposals URL(s) (if applicable): N/A
Description
DuckChatContextualEntryDialog+DuckChatContextualEntryViewModel) as the initial input state of the contextual sheet, shown when tapping "Ask about page" from the entry menu. It lets the user compose a prompt, pick a suggested prompt, or run the Summarize quick action before any chat opens.DuckChatContextualWebViewFragment+DuckChatContextualWebViewViewModel) that the redesign opens into directly, auto-submitting the parked prompt instead of reopening the input state.ContextualEntryPromptStore, keyed per tab (cleared on tab delete/clear-all) so concurrent tabs don't clobber each other.All new behavior is gated behind the existing
isContextualSheetRedesignEnabled()flag; when off, the previousDuckChatContextualFragmentflow is unchanged.NOTE: Pixel related fixes are NOT YET included in this PR.
Steps to test this PR
Testing should be done on the top stack.
Note
Medium Risk
Large new UI/WebView flow and fragment hand-off (including file/camera pickers and JS prompt submit), gated by an existing flag. Not auth or data-store schema, but easy to get lifecycle/handoff races wrong.
Overview
When the contextual sheet redesign is on, Ask about page now opens a transparent entry dialog first so users compose a prompt (or pick Summarize/suggestions) over the page before any chat WebView appears.
The composed prompt and attached page context are parked in a per-tab
ContextualEntryPromptStore, then the host shows a new webview-only chat surface that auto-submits once the web app is ready. New Chat from the chats popup reuses that same dialog. Shared VM signaling is split intoShowSheet(host unhides the container) andReloadChat(sheet loads).The
DuckChatContextualAPI is renamed (createSheet→createChatSurface,onAskAboutPage→showChatSurface). Flag off keeps the existing combined sheet. Pending prompts are cleared with tab chat state.Reviewed by Cursor Bugbot for commit 048b037. Bugbot is set up for automated code reviews on this repo. Configure here.