Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ pub fn run() {
use crate::compress::compress_files;
use crate::finder::reveal_in_finder;
use crate::menu::{build_menu, set_menu_item_enabled};
use crate::settings::{get_settings, save_settings};
use crate::settings::{
get_settings, load_settings_from_path, save_settings, settings_file_path,
};
use crate::updater::check_for_update;
use tauri::{Emitter, Manager};

Expand All @@ -60,8 +62,13 @@ pub fn run() {
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_notification::init())
.setup(|app| {
let (menu, registry) = build_menu(app.handle())?;
let (menu, registry, auto_update_item) = build_menu(app.handle())?;
app.set_menu(menu)?;

let saved =
load_settings_from_path(&settings_file_path(app.handle())).unwrap_or_default();
auto_update_item.set_checked(saved.auto_update_check).ok();

app.handle().on_menu_event(|app, event| {
let name = match event.id().as_ref() {
"add-files" => "menu:add-files",
Expand All @@ -70,6 +77,7 @@ pub fn run() {
"compress" => "menu:compress",
"reset-selected" => "menu:reset-selected",
"check-for-update" => "menu:check-for-update",
"check-for-update-auto" => "menu:check-for-update-auto",
_ => return,
};
let _ = app.emit(name, ());
Expand Down
26 changes: 21 additions & 5 deletions src-tauri/src/menu.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::sync::Mutex;
use tauri::include_image;
use tauri::menu::{AboutMetadata, Menu, MenuItem, PredefinedMenuItem, Submenu};
use tauri::menu::{AboutMetadata, CheckMenuItem, Menu, MenuItem, PredefinedMenuItem, Submenu};

pub struct MenuRegistry(pub Mutex<HashMap<String, MenuItem<tauri::Wry>>>);

Expand All @@ -14,7 +14,9 @@ pub const MENU_IDS: &[&str] = &[
"check-for-update",
];

pub fn build_menu(app: &tauri::AppHandle) -> tauri::Result<(Menu<tauri::Wry>, MenuRegistry)> {
pub fn build_menu(
app: &tauri::AppHandle,
) -> tauri::Result<(Menu<tauri::Wry>, MenuRegistry, CheckMenuItem<tauri::Wry>)> {
// ── App menu (compress[pdf]) ──────────────────────────────────────────
let about = PredefinedMenuItem::about(
app,
Expand Down Expand Up @@ -115,6 +117,15 @@ pub fn build_menu(app: &tauri::AppHandle) -> tauri::Result<(Menu<tauri::Wry>, Me
)?;

// ── Help menu ─────────────────────────────────────────────────────────
let auto_update = CheckMenuItem::with_id(
app,
"check-for-update-auto",
"Check for Updates Automatically",
true,
false,
None::<&str>,
)?;
let help_sep = PredefinedMenuItem::separator(app)?;
let check_for_updates = MenuItem::with_id(
app,
"check-for-update",
Expand All @@ -123,8 +134,13 @@ pub fn build_menu(app: &tauri::AppHandle) -> tauri::Result<(Menu<tauri::Wry>, Me
None::<&str>,
)?;

let help_menu =
Submenu::with_id_and_items(app, "help-menu", "Help", true, &[&check_for_updates])?;
let help_menu = Submenu::with_id_and_items(
app,
"help-menu",
"Help",
true,
&[&auto_update, &help_sep, &check_for_updates],
)?;

let menu = Menu::with_items(
app,
Expand All @@ -139,7 +155,7 @@ pub fn build_menu(app: &tauri::AppHandle) -> tauri::Result<(Menu<tauri::Wry>, Me
map.insert("reset-selected".to_string(), reset);
map.insert("check-for-update".to_string(), check_for_updates);

Ok((menu, MenuRegistry(Mutex::new(map))))
Ok((menu, MenuRegistry(Mutex::new(map)), auto_update))
}

#[tauri::command]
Expand Down
4 changes: 4 additions & 0 deletions src-tauri/src/path_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mod tests {
output_mode: OutputMode::SameAsSource,
output_folder: None,
naming: NamingMode::Suffix,
auto_update_check: false,
}
}

Expand All @@ -45,6 +46,7 @@ mod tests {
output_mode: OutputMode::CustomFolder,
output_folder: Some("/home/user/output".into()),
naming: NamingMode::Suffix,
auto_update_check: false,
};
let result = resolve_output_path("/home/user/docs/report.pdf", &settings);
assert_eq!(
Expand All @@ -59,6 +61,7 @@ mod tests {
output_mode: OutputMode::SameAsSource,
output_folder: None,
naming: NamingMode::Overwrite,
auto_update_check: false,
};
let result = resolve_output_path("/home/user/docs/report.pdf", &settings);
assert_eq!(result, PathBuf::from("/home/user/docs/report.pdf"));
Expand All @@ -70,6 +73,7 @@ mod tests {
output_mode: OutputMode::CustomFolder,
output_folder: Some("/out".into()),
naming: NamingMode::Overwrite,
auto_update_check: false,
};
let result = resolve_output_path("/home/user/docs/report.pdf", &settings);
assert_eq!(result, PathBuf::from("/out/report.pdf"));
Expand Down
42 changes: 42 additions & 0 deletions src-tauri/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub struct Settings {
pub output_mode: OutputMode,
pub output_folder: Option<String>,
pub naming: NamingMode,
#[serde(default)]
pub auto_update_check: bool,
}

impl Default for Settings {
Expand All @@ -29,6 +31,7 @@ impl Default for Settings {
output_mode: OutputMode::SameAsSource,
output_folder: None,
naming: NamingMode::Suffix,
auto_update_check: false,
}
}
}
Expand Down Expand Up @@ -93,6 +96,7 @@ mod tests {
output_mode: OutputMode::SameAsSource,
output_folder: None,
naming: NamingMode::Suffix,
auto_update_check: false,
};
assert!(validate_settings(&s).is_ok());
}
Expand All @@ -104,6 +108,7 @@ mod tests {
output_mode: OutputMode::CustomFolder,
output_folder: Some(tmp.path().to_string_lossy().into_owned()),
naming: NamingMode::Suffix,
auto_update_check: false,
};
assert!(validate_settings(&s).is_ok());
}
Expand All @@ -114,6 +119,7 @@ mod tests {
output_mode: OutputMode::CustomFolder,
output_folder: Some("relative/path".into()),
naming: NamingMode::Suffix,
auto_update_check: false,
};
assert!(validate_settings(&s).is_err());
}
Expand All @@ -124,6 +130,7 @@ mod tests {
output_mode: OutputMode::CustomFolder,
output_folder: Some("/nonexistent/path/that/should/not/exist/xyz".into()),
naming: NamingMode::Suffix,
auto_update_check: false,
};
assert!(validate_settings(&s).is_err());
}
Expand All @@ -137,6 +144,7 @@ mod tests {
output_mode: OutputMode::CustomFolder,
output_folder: Some(file.to_string_lossy().into_owned()),
naming: NamingMode::Suffix,
auto_update_check: false,
};
assert!(validate_settings(&s).is_err());
}
Expand All @@ -147,6 +155,7 @@ mod tests {
output_mode: OutputMode::CustomFolder,
output_folder: Some("/tmp/has\nnewline".into()),
naming: NamingMode::Suffix,
auto_update_check: false,
};
assert!(validate_settings(&s).is_err());
}
Expand All @@ -160,6 +169,7 @@ mod tests {
output_mode: OutputMode::CustomFolder,
output_folder: Some("/my/folder".into()),
naming: NamingMode::Overwrite,
auto_update_check: false,
};

save_settings_to_path(&original, &path).unwrap();
Expand All @@ -184,4 +194,36 @@ mod tests {
assert!(s.output_folder.is_none());
assert!(matches!(s.naming, NamingMode::Suffix));
}

#[test]
fn auto_update_check_defaults_to_false() {
let s = Settings::default();
assert!(!s.auto_update_check);
}

#[test]
fn auto_update_check_round_trips_as_true() {
let tmp = TempDir::new().unwrap();
let path = tmp.path().join("settings.json");
let original = Settings {
auto_update_check: true,
..Settings::default()
};
save_settings_to_path(&original, &path).unwrap();
let loaded = load_settings_from_path(&path).unwrap();
assert!(loaded.auto_update_check);
}

#[test]
fn auto_update_check_defaults_when_absent_from_json() {
let tmp = TempDir::new().unwrap();
let path = tmp.path().join("settings.json");
std::fs::write(
&path,
r#"{"output_mode":"same_as_source","naming":"suffix"}"#,
)
.unwrap();
let loaded = load_settings_from_path(&path).unwrap();
assert!(!loaded.auto_update_check);
}
}
2 changes: 2 additions & 0 deletions src/lib/stores/settingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ export interface AppSettings {
output_mode: "same_as_source" | "custom_folder";
output_folder: string | null;
naming: "suffix" | "overwrite";
auto_update_check: boolean;
}

const DEFAULT_SETTINGS: AppSettings = {
output_mode: "same_as_source",
output_folder: null,
naming: "suffix",
auto_update_check: false,
};

function createSettingsStore() {
Expand Down
10 changes: 8 additions & 2 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,12 @@
let unlisteners: Array<() => void> = [];

onMount(async () => {
settings.load();
await settings.load();
window.addEventListener("keydown", onKeyDown);

await checkAndShowUpdateToast();
if (get(settings).auto_update_check) {
checkAndShowUpdateToast();
}

unlisteners = await Promise.all([
listen("menu:add-files", () => addFiles()),
Expand All @@ -123,6 +125,10 @@
listen("menu:clear-queue", () => clearAll()),
listen("menu:reset-selected", () => resetSelected()),
listen("menu:check-for-update", () => checkAndShowUpdateToast(true)),
listen("menu:check-for-update-auto", () => {
const current = get(settings);
settings.save({ ...current, auto_update_check: !current.auto_update_check });
}),
]);
});

Expand Down
2 changes: 1 addition & 1 deletion src/test/DetailPanel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe("DetailPanel", () => {
beforeEach(async () => {
queue.clear();
selectedFileId.set(null);
await settings.save({ output_mode: "same_as_source", output_folder: null, naming: "suffix" });
await settings.save({ output_mode: "same_as_source", output_folder: null, naming: "suffix", auto_update_check: false });
vi.clearAllMocks();
});

Expand Down
5 changes: 4 additions & 1 deletion src/test/settingsStore.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { get } from "svelte/store";

// Mock @tauri-apps/api/core BEFORE importing the store
vi.mock("@tauri-apps/api/core", () => ({
invoke: vi.fn().mockResolvedValue({
output_mode: "custom_folder",
output_folder: "/my/folder",
naming: "overwrite",
auto_update_check: true,
}),
}));

Expand All @@ -21,6 +21,7 @@ describe("settingsStore", () => {
expect(s.output_mode).toBe("same_as_source");
expect(s.output_folder).toBeNull();
expect(s.naming).toBe("suffix");
expect(s.auto_update_check).toBe(false);
});

it("load() calls get_settings and updates the store", async () => {
Expand All @@ -30,13 +31,15 @@ describe("settingsStore", () => {
expect(s.output_mode).toBe("custom_folder");
expect(s.output_folder).toBe("/my/folder");
expect(s.naming).toBe("overwrite");
expect(s.auto_update_check).toBe(true);
});

it("save() calls save_settings with current value", async () => {
const newSettings = {
output_mode: "same_as_source" as const,
output_folder: null,
naming: "suffix" as const,
auto_update_check: false,
};
await settings.save(newSettings);
expect(invoke).toHaveBeenCalledWith("save_settings", { settings: newSettings });
Expand Down
Loading
Loading