Skip to content

Allow custom folder for downloaded attachments - #68

Open
birdbox219 wants to merge 2 commits into
crmne:mainfrom
birdbox219:custom-media-path
Open

birdbox219 wants to merge 2 commits into
crmne:mainfrom
birdbox219:custom-media-path

Conversation

@birdbox219

Copy link
Copy Markdown

Add an option in Settings under Files to select a custom directory for downloaded attachments.

  • Add custom_media_dir setting serialized to settings.json.
  • Support custom attachment path resolution in AppDirs::media_dir, falling back to the default cache directory.
  • Update backend worker to direct downloads and outgoing media to the configured media directory.
  • Add Change, Reset, and Open folder buttons in Settings UI.
  • Relocate matching media files in archive.db when the folder changes.
  • Preserve custom user folders on logout by only clearing media_cache_dir.

Testing:

  • Unit tests for settings defaults and round trip (settings::tests::custom_media_dir_defaults_to_none, settings::tests::round_trips_through_disk).

  • Unit test for path fallback and custom override (paths::tests::media_dir_uses_custom_path_when_configured).

  • Unit test for live directory update and in-memory message path repair (app::tests::media_dir_changed_event_updates_settings_and_dirs).

  • Manual verification with --demo --demo-page settings testing folder selection, path display, open folder,and reset.

    Checks run on Windows: fmt, clippy (default and all features), tests (default and all features), and cargo doc, all clean. Other platforms compile through the same code paths with no new platform-specific code.

zapfast-custom-media-path-showcase 2026-09-18

Add an option in Settings under Files to select a custom directory for downloaded attachments.

- Add custom_media_dir setting serialized to settings.json.
- Support custom attachment path resolution in AppDirs::media_dir, falling back to the default cache directory.
- Update backend worker to direct downloads and outgoing media to the configured media directory.
- Add Change, Reset, and Open folder buttons in Settings UI.
- Relocate matching media files in archive.db when the folder changes.
- Preserve custom user folders on logout by only clearing media_cache_dir.

Copilot AI 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.

🟡 Changes recommended

Unresolved findings affect attachment relocation, cache cleanup, UI blocking, and in-memory path consistency.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds persistent custom attachment storage with folder selection, reset, and live path updates.

Changes:

  • Persists and resolves a configurable media directory.
  • Routes downloads and outgoing media through it.
  • Adds Settings controls and backend path updates.
File summaries
File Description
src/ui/settings.rs Adds attachment folder controls.
src/settings.rs Persists the custom directory.
src/paths.rs Resolves the effective media directory.
src/model.rs Adds media directory actions.
src/main.rs Initializes the configured directory.
src/backend/worker.rs Handles relocation, cleanup, and media routing.
src/backend.rs Adds media directory commands and events.
src/app.rs Applies changes and synchronizes loaded media paths.
Review details

Suppressed comments (4)

src/app.rs:1226

  • This handler performs up to two blocking metadata checks for every loaded message on the UI thread. A custom directory may be slow, and this duplicates the worker's relocate_media scan. Have the worker return the repointed message paths and let the UI apply those results instead of probing the filesystem here.
                    for conversation in self.conversations.values_mut() {
                        for message in &mut conversation.messages {
                            let Some(media) = message.content.media_mut() else {
                                continue;
                            };

src/app.rs:1237

  • relocate_media clears this row from the archive when neither the old path nor a file with the same name in the new directory exists, but this branch leaves the loaded message's media.path unchanged. An open conversation can therefore keep rendering the attachment as downloaded and try to open a nonexistent file until the chat is reloaded. Clear the in-memory path when no candidate is found so it stays consistent with the archive.
                            if !path.exists() {
                                let candidate = dir.join(name);
                                if candidate.exists() {
                                    media.path = Some(candidate);
                                }

src/app.rs:2428

  • The advertised --demo --demo-page settings verification cannot exercise these actions: demo startup uses App::headless, whose detached backend drops commands, and no demo responder handles PickMediaDir or ResetMediaDir. The Change and Reset flows therefore do nothing in that mode; verify them with a real backend or add a demo response path.
            Action::PickMediaDir => self.backend.send(Command::PickMediaDir),
            Action::ResetMediaDir => self.backend.send(Command::ResetMediaDir),

src/backend/worker.rs:2589

  • Changing the setting does not actually relocate existing attachments. relocate_media skips every row whose recorded old path still exists (src/backend/worker.rs:526-527), so selecting a new folder leaves the file and archive row in the old directory; Reset has the symmetric problem. Move existing files into the new directory and update their archive paths as part of the change so the configured folder applies to existing downloads.
                self.relocate_media();
  • Files reviewed: 8/8 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/backend/worker.rs
Comment thread src/ui/settings.rs
Comment thread src/app.rs
Comment thread src/settings.rs
- Reject custom attachment paths inside the cache directory and treat selecting the default cache folder as a reset.
- Make logout media cache cleanup skip configured custom subtrees.
- Remove blocking directory creation from UI click handler.
- Add test fixture asserting in-memory media path repair when the directory changes.
- Update README to document custom media directories and preserved folders on logout.

Copilot AI 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.

Comment thread src/backend/worker.rs
/// repairs moved attachment paths or clears missing files for redownload.
fn relocate_media(&mut self) {
let dir = self.dirs.media_cache_dir();
let dir = self.dirs.media_dir();
Comment thread src/ui/settings.rs
Comment on lines 200 to 201
if theme::soft_button(ui, &palette, Some(Icon::ExternalLink), "Open folder", false).clicked() {
let _ = std::fs::create_dir_all(&media);
app.actions.push(Action::OpenFile(media.clone()));
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