Skip to content

Wire per-row Download in chat history overflow menu#8610

Draft
GerardPaligot wants to merge 5 commits into
developfrom
feature/gerard/chat-history-download
Draft

Wire per-row Download in chat history overflow menu#8610
GerardPaligot wants to merge 5 commits into
developfrom
feature/gerard/chat-history-download

Conversation

@GerardPaligot
Copy link
Copy Markdown
Contributor

Task/Issue URL: https://app.asana.com/1/137249556945/task/1214820120386826?focus=true

Description

Wires the Download action on the Duck.ai chat history screen so a chat is exported as a plain-text .txt file saved to the device's Downloads folder and registered with the in-app Downloads screen. A snackbar confirms the save with the resulting filename and offers a Show action that opens the Downloads screen.

Steps to test this PR

Note

Prerequisites:

  • Install Internal Debug.
  • In Settings → Developer Settings → Feature Flags, confirm duckAiChatHistory (self, historyScreen) is ON and duckChat → useNativeStorageChatData is ON.
  • Create at least one Duck.ai chat with a few back-and-forth turns.

Happy path

  • Open the Chats screen → tap the 3-dot on any row → tap Download → confirm a snackbar appears reading "Download complete for <filename>.txt" with a Show action.
  • Tap Show → confirm the in-app Downloads screen opens with the new .txt file listed.
  • Open the saved file from Downloads (or via a file manager in the public Downloads folder) → confirm it contains a Duck.ai/model header, a separator line, and each turn in order (user prompt + model response).

Guards

  • Trigger Download twice on the same chat → confirm the second save lands as &lt;title&gt;-1.txt, not an overwrite.
  • Download a chat whose title contains slashes, colons, or other unsafe characters → confirm the filename sanitises them and the snackbar shows the sanitised name.
  • Trigger Download from inside select mode → confirm the export still runs and the snackbar appears.

UI changes

Before After
!(Upload before screenshot) (Upload after screenshot)

Copy link
Copy Markdown
Contributor Author

GerardPaligot commented May 19, 2026

@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-pin branch from 13f65fe to 7814890 Compare May 19, 2026 09:21
@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-download branch from c9c0069 to 3ade15f Compare May 19, 2026 09:21
@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-pin branch from 7814890 to 8ccddb3 Compare May 19, 2026 09:38
@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-download branch 3 times, most recently from c1b6b95 to d95ddfc Compare May 19, 2026 12:38
@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-pin branch 2 times, most recently from 0bdc467 to f77064b Compare May 19, 2026 21:51
@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-download branch 2 times, most recently from 44f76c2 to c1a8944 Compare May 20, 2026 08:25
@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-pin branch from f77064b to 6530893 Compare May 20, 2026 08:25
@GerardPaligot GerardPaligot changed the base branch from feature/gerard/chat-history-pin to graphite-base/8610 May 20, 2026 09:31
@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-download branch from c1a8944 to 160544c Compare May 20, 2026 09:33
@GerardPaligot GerardPaligot changed the base branch from graphite-base/8610 to feature/gerard/chat-history-rename May 20, 2026 09:33
@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-download branch from 160544c to 50b6ef6 Compare May 20, 2026 14:27
@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-rename branch from 65337eb to c62b930 Compare May 20, 2026 14:27
Base automatically changed from feature/gerard/chat-history-rename to develop May 20, 2026 14:41
@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-download branch from 50b6ef6 to aeaf1b7 Compare May 20, 2026 15:12
Surfaces "Download complete for <filename>" with a Show action that
opens the Downloads screen, matching the design. The ViewModel already
emits the filename via ShowDownloadComplete — only the Fragment copy
and string format needed updating.

Also removes a duplicate setPinned override accidentally added to
RecordingRenameRepository, which was breaking compileDebugUnitTestKotlin.
- spotlessApply on three :app files to keep DownloadsScreenNoParams
  alphabetically after AppScope / DI imports.
- Add an instruction attribute to duck_ai_chat_history_download_complete
  so the MissingInstruction lint rule passes for the %1$s filename arg.
- Drop the now-stale setPinned override in RecordingRenameRepository;
  setPinned is no longer on ChatHistoryRepository after the rebase.
Brings Android's chat-history export in line with the macOS/Windows
reference for every chat type. ChatExporter branches by ChatType and
returns a sealed ExportResult so the writer can produce either a `.txt`
(discussion/voice) or a `.zip` (image-generation, bundling chat.txt
with a UTF-8 BOM and the resolved image bytes).

Filename pattern switches from the sanitized chat title to the
cross-platform `duck.ai_yyyy-MM-dd_HH-mm-ss.<txt|zip>` shape. Turns are
now separated with the reference's `--------------------` divider.
Voice turns with no model response omit the assistant block entirely.

DuckAiChatStore gains openFileRef(uuid) so the impl module can stream
image bytes for the zip without leaking the chat-files dir; the same
path-traversal guard used by deleteChat applies. Positional fileRef ↔
turn association is a documented known assumption pending confirmation
on the FE message schema for image-generation chats.
The on-disk file at <chat-files-dir>/<uuid> is a JSON envelope written
by the FE bridge via writeText(params.toString()), with the image bytes
base64-encoded inside a "data" field — not raw image bytes. Reading the
file as a stream gave back JSON, which is why the image-N.jpeg entries
exported for image-generation chats were unreadable.

Replace openFileRef(uuid): InputStream? with
readFileRef(uuid): FileRefContent? which parses the envelope, decodes
the base64 data, and exposes the FE-provided fileName + mimeType. Tests
cover plain base64, the data:<mime>;base64,<payload> URL prefix path,
missing data field, malformed JSON, and the path-traversal guard.

Use java.util.Base64 instead of android.util.Base64 — it's available
from Java 8 / API 26, which matches the project's minSdk, and works in
pure-JVM unit tests without a Robolectric runner.
@GerardPaligot GerardPaligot force-pushed the feature/gerard/chat-history-download branch from 3f4fae7 to 936406b Compare May 22, 2026 13:40
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