Skip to content

Add chatIdOrNull to duckchat api module and drop chatID string duplication#8631

Open
GerardPaligot wants to merge 3 commits into
developfrom
feature/gerard/duckchat-extract-chatid
Open

Add chatIdOrNull to duckchat api module and drop chatID string duplication#8631
GerardPaligot wants to merge 3 commits into
developfrom
feature/gerard/duckchat-extract-chatid

Conversation

@GerardPaligot
Copy link
Copy Markdown
Contributor

@GerardPaligot GerardPaligot commented May 20, 2026

Task/Issue URL: https://app.asana.com/1/137249556945/task/1214962354449266

Description

Promotes the "chatID" query parameter handling out of three impl-private helpers into one function on the public DuckChat interface: chatIdOrNull(uri: Uri): String?. As a side effect, DuckChatContextualViewModel.hasChatId now correctly gates on isDuckChatUrl — previously a crafted URL like https://evil.com?chatID=abc was treated as a Duck.ai chat.

Steps to test this PR

Chat-history clears (DuckChatDataClearingPlugin + DuckAiTabsCleanupPlugin)

  • Open the Duck.ai chat-history screen → long-press to enter select mode → select 2 chats → tap fire → tap Delete all → confirm the chats clear and any open Duck.ai tabs pointing at those chats close.
  • Open the same chat in two browser tabs → from chat-history, delete that chat via the row overflow → confirm both browser tabs close.
  • In chat-history with at least one pinned chat → tap fire (default mode, no selection) → tap Delete all → confirm every chat, pinned included, clears.

Chat resume from input suggestions

  • On the input screen, tap a recent chat suggestion → confirm the chat resumes in Duck.ai with the correct chatID in the URL.
  • If your build surfaces the NTP widget input mode, tap a recent chat suggestion there → same result.

UI changes

n/a


Note

Medium Risk
Touches Duck.ai URL generation and chat-history clearing/tab-closing flows; a mistake could prevent chat resume or cause incorrect tab closures, though changes are mostly consolidations with updated tests.

Overview
Introduces a public Uri.toChatIdOrNull(duckChat) helper in duckchat-api and migrates multiple call sites to use it, removing duplicated chatID extraction logic and the impl-private CHAT_ID_PARAM constant.

Standardizes “resume chat” URL creation by routing recent-chat selection through DuckChatInternal.buildChatUrl(chatId) and updates RealDuckChat.buildChatUrl to include native-input parameters when enabled.

Tightens “has chat” checks to require isDuckChatUrl (via toChatIdOrNull), avoiding treating non-Duck.ai URLs with a chatID parameter as valid chats, and updates/extends unit tests accordingly.

Reviewed by Cursor Bugbot for commit 5a6e8c1. Bugbot is set up for automated code reviews on this repo. Configure here.

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

Comment thread duckchat/duckchat-api/src/main/java/com/duckduckgo/duckchat/api/DuckChat.kt Outdated
The "chatID" query parameter name was duplicated across three call sites
extracting the chatID from a Duck.ai URL, each implementing the lookup
differently and only two of them gating on isDuckChatUrl. Lift the
operation onto the public DuckChat interface as chatIdOrNull(uri) so the
literal lives in one place and the safety guard is enforced for every
consumer — closing a latent foot-gun in DuckChatContextualViewModel.hasChatId
that previously trusted any URL carrying a chatID query param.

DuckChatConstants.CHAT_ID_PARAM is removed; the raw "chatID" string now
only lives in RealDuckChat's private const. URL building stays on the
impl-only DuckChatInternal since every builder is impl-internal.
…lication

The "chatID" query parameter name was extracted in three different shapes
across modules — a custom Uri extension in :app, a private helper in
:duckchat-impl, and an inline lookup in DuckChatContextualViewModel — only
two of which gated on isDuckChatUrl. Lift the operation onto a top-level
Uri.toChatIdOrNull(DuckChat) extension in :duckchat-api so the literal lives
in one place and the safety guard is enforced for every consumer, closing a
latent foot-gun in DuckChatContextualViewModel.hasChatId that previously
trusted any URL carrying a chatID query param.

URL building stays on the impl-only DuckChatInternal since every builder is
impl-internal and never crosses the module boundary.
@GerardPaligot GerardPaligot force-pushed the feature/gerard/duckchat-extract-chatid branch from 09514e4 to 48d4109 Compare May 21, 2026 16:27
@GerardPaligot GerardPaligot marked this pull request as ready for review May 21, 2026 16:28
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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 48d4109. Configure here.

@GerardPaligot GerardPaligot changed the title Add chatIdOrNull to DuckChat api and drop chatID string duplication Add chatIdOrNull to duckchat api module and drop chatID string duplication May 22, 2026
buildChatUrl was the only Duck.ai URL builder that did not call
nativeInputParameters(), so callers that previously composed their URL
via getDuckChatUrl("", false) + manual appendQueryParameter("chatID", …)
silently lost the native-input=true param after migrating onto
buildChatUrl(chatId). Pull nativeInputParameters() into buildChatUrl so
the "single source of truth for the Duck.ai chat URL shape" actually
produces the same URL shape as every other builder.

Adds two RealDuckChatTest cases that pin the contract: with the native
input setting on, buildChatUrl emits ?chatID=…&native-input=true&duckai=5;
with it off, native-input is absent.
@malmstein malmstein self-assigned this May 22, 2026
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.

3 participants