Switch to disable update checking (#362)
* codex thinks it did it * run format * properly disable on startup * text
This commit is contained in:
parent
ee571b6810
commit
39adf1b455
13 changed files with 187 additions and 96 deletions
|
|
@ -165,8 +165,11 @@ fn initialize_core_logic(app_handle: &AppHandle) {
|
|||
show_main_window(app);
|
||||
}
|
||||
"check_updates" => {
|
||||
show_main_window(app);
|
||||
let _ = app.emit("check-for-updates", ());
|
||||
let settings = settings::get_settings(app);
|
||||
if settings.update_checks_enabled {
|
||||
show_main_window(app);
|
||||
let _ = app.emit("check-for-updates", ());
|
||||
}
|
||||
}
|
||||
"cancel" => {
|
||||
use crate::utils::cancel_current_operation;
|
||||
|
|
@ -205,6 +208,10 @@ fn initialize_core_logic(app_handle: &AppHandle) {
|
|||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
fn trigger_update_check(app: AppHandle) -> Result<(), String> {
|
||||
let settings = settings::get_settings(&app);
|
||||
if !settings.update_checks_enabled {
|
||||
return Ok(());
|
||||
}
|
||||
app.emit("check-for-updates", ())
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
|
|
@ -246,6 +253,7 @@ pub fn run() {
|
|||
shortcut::suspend_binding,
|
||||
shortcut::resume_binding,
|
||||
shortcut::change_mute_while_recording_setting,
|
||||
shortcut::change_update_checks_setting,
|
||||
trigger_update_check,
|
||||
commands::cancel_operation,
|
||||
commands::get_app_dir_path,
|
||||
|
|
@ -300,30 +308,29 @@ pub fn run() {
|
|||
)
|
||||
.expect("Failed to export typescript bindings");
|
||||
|
||||
let mut builder = tauri::Builder::default()
|
||||
.plugin(
|
||||
LogBuilder::new()
|
||||
.level(log::LevelFilter::Trace) // Set to most verbose level globally
|
||||
.max_file_size(500_000)
|
||||
.rotation_strategy(RotationStrategy::KeepOne)
|
||||
.clear_targets()
|
||||
.targets([
|
||||
// Console output respects RUST_LOG environment variable
|
||||
Target::new(TargetKind::Stdout).filter({
|
||||
let console_filter = console_filter.clone();
|
||||
move |metadata| console_filter.enabled(metadata)
|
||||
}),
|
||||
// File logs respect the user's settings (stored in FILE_LOG_LEVEL atomic)
|
||||
Target::new(TargetKind::LogDir {
|
||||
file_name: Some("handy".into()),
|
||||
})
|
||||
.filter(|metadata| {
|
||||
let file_level = FILE_LOG_LEVEL.load(Ordering::Relaxed);
|
||||
metadata.level() <= level_filter_from_u8(file_level)
|
||||
}),
|
||||
])
|
||||
.build(),
|
||||
);
|
||||
let mut builder = tauri::Builder::default().plugin(
|
||||
LogBuilder::new()
|
||||
.level(log::LevelFilter::Trace) // Set to most verbose level globally
|
||||
.max_file_size(500_000)
|
||||
.rotation_strategy(RotationStrategy::KeepOne)
|
||||
.clear_targets()
|
||||
.targets([
|
||||
// Console output respects RUST_LOG environment variable
|
||||
Target::new(TargetKind::Stdout).filter({
|
||||
let console_filter = console_filter.clone();
|
||||
move |metadata| console_filter.enabled(metadata)
|
||||
}),
|
||||
// File logs respect the user's settings (stored in FILE_LOG_LEVEL atomic)
|
||||
Target::new(TargetKind::LogDir {
|
||||
file_name: Some("handy".into()),
|
||||
})
|
||||
.filter(|metadata| {
|
||||
let file_level = FILE_LOG_LEVEL.load(Ordering::Relaxed);
|
||||
metadata.level() <= level_filter_from_u8(file_level)
|
||||
}),
|
||||
])
|
||||
.build(),
|
||||
);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ const OVERLAY_BOTTOM_OFFSET: f64 = 40.0;
|
|||
#[cfg(target_os = "windows")]
|
||||
fn force_overlay_topmost(overlay_window: &tauri::webview::WebviewWindow) {
|
||||
use windows::Win32::UI::WindowsAndMessaging::{
|
||||
SetWindowPos, HWND_TOPMOST, SWP_NOMOVE, SWP_NOSIZE, SWP_NOACTIVATE, SWP_SHOWWINDOW,
|
||||
SetWindowPos, HWND_TOPMOST, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_SHOWWINDOW,
|
||||
};
|
||||
|
||||
// Clone because run_on_main_thread takes 'static
|
||||
|
|
@ -58,7 +58,10 @@ fn force_overlay_topmost(overlay_window: &tauri::webview::WebviewWindow) {
|
|||
let _ = SetWindowPos(
|
||||
hwnd,
|
||||
Some(HWND_TOPMOST),
|
||||
0, 0, 0, 0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW,
|
||||
);
|
||||
}
|
||||
|
|
@ -173,32 +176,26 @@ pub fn create_recording_overlay(app_handle: &AppHandle) {
|
|||
if let Some((x, y)) = calculate_overlay_position(app_handle) {
|
||||
// PanelBuilder creates a Tauri window then converts it to NSPanel.
|
||||
// The window remains registered, so get_webview_window() still works.
|
||||
match PanelBuilder::<_, RecordingOverlayPanel>::new(
|
||||
app_handle,
|
||||
"recording_overlay"
|
||||
)
|
||||
.url(WebviewUrl::App("src/overlay/index.html".into()))
|
||||
.title("Recording")
|
||||
.position(tauri::Position::Logical(tauri::LogicalPosition { x, y }))
|
||||
.level(PanelLevel::Status)
|
||||
.size(tauri::Size::Logical(tauri::LogicalSize {
|
||||
width: OVERLAY_WIDTH,
|
||||
height: OVERLAY_HEIGHT
|
||||
}))
|
||||
.has_shadow(false)
|
||||
.transparent(true)
|
||||
.no_activate(true)
|
||||
.corner_radius(0.0)
|
||||
.with_window(|w| {
|
||||
w.decorations(false)
|
||||
.transparent(true)
|
||||
})
|
||||
.collection_behavior(
|
||||
CollectionBehavior::new()
|
||||
.can_join_all_spaces()
|
||||
.full_screen_auxiliary()
|
||||
)
|
||||
.build()
|
||||
match PanelBuilder::<_, RecordingOverlayPanel>::new(app_handle, "recording_overlay")
|
||||
.url(WebviewUrl::App("src/overlay/index.html".into()))
|
||||
.title("Recording")
|
||||
.position(tauri::Position::Logical(tauri::LogicalPosition { x, y }))
|
||||
.level(PanelLevel::Status)
|
||||
.size(tauri::Size::Logical(tauri::LogicalSize {
|
||||
width: OVERLAY_WIDTH,
|
||||
height: OVERLAY_HEIGHT,
|
||||
}))
|
||||
.has_shadow(false)
|
||||
.transparent(true)
|
||||
.no_activate(true)
|
||||
.corner_radius(0.0)
|
||||
.with_window(|w| w.decorations(false).transparent(true))
|
||||
.collection_behavior(
|
||||
CollectionBehavior::new()
|
||||
.can_join_all_spaces()
|
||||
.full_screen_auxiliary(),
|
||||
)
|
||||
.build()
|
||||
{
|
||||
Ok(panel) => {
|
||||
let _ = panel.hide();
|
||||
|
|
@ -221,7 +218,8 @@ pub fn show_recording_overlay(app_handle: &AppHandle) {
|
|||
if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") {
|
||||
// Update position before showing to prevent flicker from position changes
|
||||
if let Some((x, y)) = calculate_overlay_position(app_handle) {
|
||||
let _ = overlay_window.set_position(tauri::Position::Logical(tauri::LogicalPosition { x, y }));
|
||||
let _ = overlay_window
|
||||
.set_position(tauri::Position::Logical(tauri::LogicalPosition { x, y }));
|
||||
}
|
||||
|
||||
let _ = overlay_window.show();
|
||||
|
|
|
|||
|
|
@ -233,6 +233,8 @@ pub struct AppSettings {
|
|||
pub start_hidden: bool,
|
||||
#[serde(default = "default_autostart_enabled")]
|
||||
pub autostart_enabled: bool,
|
||||
#[serde(default = "default_update_checks_enabled")]
|
||||
pub update_checks_enabled: bool,
|
||||
#[serde(default = "default_model")]
|
||||
pub selected_model: String,
|
||||
#[serde(default = "default_always_on_microphone")]
|
||||
|
|
@ -305,6 +307,10 @@ fn default_autostart_enabled() -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
fn default_update_checks_enabled() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn default_selected_language() -> String {
|
||||
"auto".to_string()
|
||||
}
|
||||
|
|
@ -451,6 +457,7 @@ pub fn get_default_settings() -> AppSettings {
|
|||
sound_theme: default_sound_theme(),
|
||||
start_hidden: default_start_hidden(),
|
||||
autostart_enabled: default_autostart_enabled(),
|
||||
update_checks_enabled: default_update_checks_enabled(),
|
||||
selected_model: "".to_string(),
|
||||
always_on_microphone: false,
|
||||
selected_microphone: None,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ pub fn init_shortcuts(app: &AppHandle) {
|
|||
let default_bindings = settings::get_default_settings().bindings;
|
||||
let user_settings = settings::load_or_create_app_settings(app);
|
||||
|
||||
|
||||
// Register all default shortcuts, applying user customizations
|
||||
for (id, default_binding) in default_bindings {
|
||||
if id == "cancel" {
|
||||
|
|
@ -284,6 +283,24 @@ pub fn change_autostart_setting(app: AppHandle, enabled: bool) -> Result<(), Str
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn change_update_checks_setting(app: AppHandle, enabled: bool) -> Result<(), String> {
|
||||
let mut settings = settings::get_settings(&app);
|
||||
settings.update_checks_enabled = enabled;
|
||||
settings::write_settings(&app, settings);
|
||||
|
||||
let _ = app.emit(
|
||||
"settings-changed",
|
||||
serde_json::json!({
|
||||
"setting": "update_checks_enabled",
|
||||
"value": enabled
|
||||
}),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn update_custom_words(app: AppHandle, words: Vec<String>) -> Result<(), String> {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::settings;
|
||||
use tauri::image::Image;
|
||||
use tauri::menu::{Menu, MenuItem, PredefinedMenuItem};
|
||||
use tauri::tray::TrayIcon;
|
||||
|
|
@ -74,6 +75,8 @@ pub fn change_tray_icon(app: &AppHandle, icon: TrayIconState) {
|
|||
}
|
||||
|
||||
pub fn update_tray_menu(app: &AppHandle, state: &TrayIconState) {
|
||||
let settings = settings::get_settings(app);
|
||||
|
||||
// Platform-specific accelerators
|
||||
#[cfg(target_os = "macos")]
|
||||
let (settings_accelerator, quit_accelerator) = (Some("Cmd+,"), Some("Cmd+Q"));
|
||||
|
|
@ -90,7 +93,7 @@ pub fn update_tray_menu(app: &AppHandle, state: &TrayIconState) {
|
|||
app,
|
||||
"check_updates",
|
||||
"Check for Updates...",
|
||||
true,
|
||||
settings.update_checks_enabled,
|
||||
None::<&str>,
|
||||
)
|
||||
.expect("failed to create check updates item");
|
||||
|
|
|
|||
|
|
@ -244,6 +244,14 @@ async changeMuteWhileRecordingSetting(enabled: boolean) : Promise<Result<null, s
|
|||
else return { status: "error", error: e as any };
|
||||
}
|
||||
},
|
||||
async changeUpdateChecksSetting(enabled: boolean) : Promise<Result<null, string>> {
|
||||
try {
|
||||
return { status: "ok", data: await TAURI_INVOKE("change_update_checks_setting", { enabled }) };
|
||||
} catch (e) {
|
||||
if(e instanceof Error) throw e;
|
||||
else return { status: "error", error: e as any };
|
||||
}
|
||||
},
|
||||
async triggerUpdateCheck() : Promise<Result<null, string>> {
|
||||
try {
|
||||
return { status: "ok", data: await TAURI_INVOKE("trigger_update_check") };
|
||||
|
|
@ -594,7 +602,7 @@ async isLaptop() : Promise<Result<boolean, string>> {
|
|||
|
||||
/** user-defined types **/
|
||||
|
||||
export type AppSettings = { bindings: Partial<{ [key in string]: ShortcutBinding }>; push_to_talk: boolean; audio_feedback: boolean; audio_feedback_volume?: number; sound_theme?: SoundTheme; start_hidden?: boolean; autostart_enabled?: boolean; selected_model?: string; always_on_microphone?: boolean; selected_microphone?: string | null; clamshell_microphone?: string | null; selected_output_device?: string | null; translate_to_english?: boolean; selected_language?: string; overlay_position?: OverlayPosition; debug_mode?: boolean; log_level?: LogLevel; custom_words?: string[]; model_unload_timeout?: ModelUnloadTimeout; word_correction_threshold?: number; history_limit?: string; recording_retention_period?: RecordingRetentionPeriod; paste_method?: PasteMethod; clipboard_handling?: ClipboardHandling; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: Partial<{ [key in string]: string }>; post_process_models?: Partial<{ [key in string]: string }>; post_process_prompts?: LLMPrompt[]; post_process_selected_prompt_id?: string | null; mute_while_recording?: boolean }
|
||||
export type AppSettings = { bindings: Partial<{ [key in string]: ShortcutBinding }>; push_to_talk: boolean; audio_feedback: boolean; audio_feedback_volume?: number; sound_theme?: SoundTheme; start_hidden?: boolean; autostart_enabled?: boolean; update_checks_enabled?: boolean; selected_model?: string; always_on_microphone?: boolean; selected_microphone?: string | null; clamshell_microphone?: string | null; selected_output_device?: string | null; translate_to_english?: boolean; selected_language?: string; overlay_position?: OverlayPosition; debug_mode?: boolean; log_level?: LogLevel; custom_words?: string[]; model_unload_timeout?: ModelUnloadTimeout; word_correction_threshold?: number; history_limit?: string; recording_retention_period?: RecordingRetentionPeriod; paste_method?: PasteMethod; clipboard_handling?: ClipboardHandling; post_process_enabled?: boolean; post_process_provider_id?: string; post_process_providers?: PostProcessProvider[]; post_process_api_keys?: Partial<{ [key in string]: string }>; post_process_models?: Partial<{ [key in string]: string }>; post_process_prompts?: LLMPrompt[]; post_process_selected_prompt_id?: string | null; mute_while_recording?: boolean }
|
||||
export type AudioDevice = { index: string; name: string; is_default: boolean }
|
||||
export type BindingResponse = { success: boolean; binding: ShortcutBinding | null; error: string | null }
|
||||
export type ClipboardHandling = "dont_modify" | "copy_to_clipboard"
|
||||
|
|
|
|||
|
|
@ -84,17 +84,15 @@ export const HandyShortcut: React.FC<HandyShortcutProps> = ({
|
|||
if (editingShortcutId && originalBinding) {
|
||||
try {
|
||||
await updateBinding(editingShortcutId, originalBinding);
|
||||
await commands.resumeBinding(editingShortcutId).catch(
|
||||
console.error,
|
||||
);
|
||||
await commands
|
||||
.resumeBinding(editingShortcutId)
|
||||
.catch(console.error);
|
||||
} catch (error) {
|
||||
console.error("Failed to restore original binding:", error);
|
||||
toast.error("Failed to restore original shortcut");
|
||||
}
|
||||
} else if (editingShortcutId) {
|
||||
await commands.resumeBinding(editingShortcutId).catch(
|
||||
console.error,
|
||||
);
|
||||
await commands.resumeBinding(editingShortcutId).catch(console.error);
|
||||
}
|
||||
setEditingShortcutId(null);
|
||||
setKeyPressed([]);
|
||||
|
|
@ -138,9 +136,9 @@ export const HandyShortcut: React.FC<HandyShortcutProps> = ({
|
|||
try {
|
||||
await updateBinding(editingShortcutId, newShortcut);
|
||||
// Re-register the shortcut now that recording is finished
|
||||
await commands.resumeBinding(editingShortcutId).catch(
|
||||
console.error,
|
||||
);
|
||||
await commands
|
||||
.resumeBinding(editingShortcutId)
|
||||
.catch(console.error);
|
||||
} catch (error) {
|
||||
console.error("Failed to change binding:", error);
|
||||
toast.error(`Failed to set shortcut: ${error}`);
|
||||
|
|
@ -149,9 +147,9 @@ export const HandyShortcut: React.FC<HandyShortcutProps> = ({
|
|||
if (originalBinding) {
|
||||
try {
|
||||
await updateBinding(editingShortcutId, originalBinding);
|
||||
await commands.resumeBinding(editingShortcutId).catch(
|
||||
console.error,
|
||||
);
|
||||
await commands
|
||||
.resumeBinding(editingShortcutId)
|
||||
.catch(console.error);
|
||||
} catch (resetError) {
|
||||
console.error("Failed to reset binding:", resetError);
|
||||
toast.error("Failed to reset shortcut to original value");
|
||||
|
|
@ -177,17 +175,15 @@ export const HandyShortcut: React.FC<HandyShortcutProps> = ({
|
|||
if (editingShortcutId && originalBinding) {
|
||||
try {
|
||||
await updateBinding(editingShortcutId, originalBinding);
|
||||
await commands.resumeBinding(editingShortcutId).catch(
|
||||
console.error,
|
||||
);
|
||||
await commands
|
||||
.resumeBinding(editingShortcutId)
|
||||
.catch(console.error);
|
||||
} catch (error) {
|
||||
console.error("Failed to restore original binding:", error);
|
||||
toast.error("Failed to restore original shortcut");
|
||||
}
|
||||
} else if (editingShortcutId) {
|
||||
commands.resumeBinding(editingShortcutId).catch(
|
||||
console.error,
|
||||
);
|
||||
commands.resumeBinding(editingShortcutId).catch(console.error);
|
||||
}
|
||||
setEditingShortcutId(null);
|
||||
setKeyPressed([]);
|
||||
|
|
|
|||
28
src/components/settings/UpdateChecksToggle.tsx
Normal file
28
src/components/settings/UpdateChecksToggle.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import React from "react";
|
||||
import { ToggleSwitch } from "../ui/ToggleSwitch";
|
||||
import { useSettings } from "../../hooks/useSettings";
|
||||
|
||||
interface UpdateChecksToggleProps {
|
||||
descriptionMode?: "inline" | "tooltip";
|
||||
grouped?: boolean;
|
||||
}
|
||||
|
||||
export const UpdateChecksToggle: React.FC<UpdateChecksToggleProps> = ({
|
||||
descriptionMode = "tooltip",
|
||||
grouped = false,
|
||||
}) => {
|
||||
const { getSetting, updateSetting, isUpdating } = useSettings();
|
||||
const updateChecksEnabled = getSetting("update_checks_enabled") ?? true;
|
||||
|
||||
return (
|
||||
<ToggleSwitch
|
||||
checked={updateChecksEnabled}
|
||||
onChange={(enabled) => updateSetting("update_checks_enabled", enabled)}
|
||||
isUpdating={isUpdating("update_checks_enabled")}
|
||||
label="Check for Updates"
|
||||
description="Allow Handy to automatically check for updates and enable manual checks from the footer or tray menu."
|
||||
descriptionMode={descriptionMode}
|
||||
grouped={grouped}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
@ -11,6 +11,7 @@ import { MuteWhileRecording } from "../MuteWhileRecording";
|
|||
import { RecordingRetentionPeriodSelector } from "../RecordingRetentionPeriod";
|
||||
import { ClamshellMicrophoneSelector } from "../ClamshellMicrophoneSelector";
|
||||
import { HandyShortcut } from "../HandyShortcut";
|
||||
import { UpdateChecksToggle } from "../UpdateChecksToggle";
|
||||
import { useSettings } from "../../../hooks/useSettings";
|
||||
|
||||
export const DebugSettings: React.FC = () => {
|
||||
|
|
@ -22,6 +23,7 @@ export const DebugSettings: React.FC = () => {
|
|||
<SettingsGroup title="Debug">
|
||||
<LogDirectory grouped={true} />
|
||||
<LogLevelSelector grouped={true} />
|
||||
<UpdateChecksToggle descriptionMode="tooltip" grouped={true} />
|
||||
<SoundPicker
|
||||
label="Sound Theme"
|
||||
description="Choose a sound theme for recording start and stop feedback"
|
||||
|
|
|
|||
|
|
@ -26,3 +26,4 @@ export { StartHidden } from "./StartHidden";
|
|||
export { HistoryLimit } from "./HistoryLimit";
|
||||
export { RecordingRetentionPeriodSelector } from "./RecordingRetentionPeriod";
|
||||
export { AutostartToggle } from "./AutostartToggle";
|
||||
export { UpdateChecksToggle } from "./UpdateChecksToggle";
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ const PostProcessingSettingsPromptsComponent: React.FC = () => {
|
|||
try {
|
||||
const result = await commands.addPostProcessPrompt(
|
||||
draftName.trim(),
|
||||
draftText.trim()
|
||||
draftText.trim(),
|
||||
);
|
||||
if (result.status === "ok") {
|
||||
await refreshSettings();
|
||||
|
|
@ -199,7 +199,7 @@ const PostProcessingSettingsPromptsComponent: React.FC = () => {
|
|||
await commands.updatePostProcessPrompt(
|
||||
selectedPromptId,
|
||||
draftName.trim(),
|
||||
draftText.trim()
|
||||
draftText.trim(),
|
||||
);
|
||||
await refreshSettings();
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { check } from "@tauri-apps/plugin-updater";
|
|||
import { relaunch } from "@tauri-apps/plugin-process";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { ProgressBar } from "../shared";
|
||||
import { useSettings } from "../../hooks/useSettings";
|
||||
|
||||
interface UpdateCheckerProps {
|
||||
className?: string;
|
||||
|
|
@ -16,12 +17,29 @@ const UpdateChecker: React.FC<UpdateCheckerProps> = ({ className = "" }) => {
|
|||
const [downloadProgress, setDownloadProgress] = useState(0);
|
||||
const [showUpToDate, setShowUpToDate] = useState(false);
|
||||
|
||||
const { settings, isLoading } = useSettings();
|
||||
const settingsLoaded = !isLoading && settings !== null;
|
||||
const updateChecksEnabled = settings?.update_checks_enabled ?? false;
|
||||
|
||||
const upToDateTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
const isManualCheckRef = useRef(false);
|
||||
const downloadedBytesRef = useRef(0);
|
||||
const contentLengthRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
// Wait for settings to load before doing anything
|
||||
if (!settingsLoaded) return;
|
||||
|
||||
if (!updateChecksEnabled) {
|
||||
if (upToDateTimeoutRef.current) {
|
||||
clearTimeout(upToDateTimeoutRef.current);
|
||||
}
|
||||
setIsChecking(false);
|
||||
setUpdateAvailable(false);
|
||||
setShowUpToDate(false);
|
||||
return;
|
||||
}
|
||||
|
||||
checkForUpdates();
|
||||
|
||||
// Listen for update check events
|
||||
|
|
@ -35,11 +53,11 @@ const UpdateChecker: React.FC<UpdateCheckerProps> = ({ className = "" }) => {
|
|||
}
|
||||
updateUnlisten.then((fn) => fn());
|
||||
};
|
||||
}, []);
|
||||
}, [settingsLoaded, updateChecksEnabled]);
|
||||
|
||||
// Update checking functions
|
||||
const checkForUpdates = async () => {
|
||||
if (isChecking) return;
|
||||
if (!updateChecksEnabled || isChecking) return;
|
||||
|
||||
try {
|
||||
setIsChecking(true);
|
||||
|
|
@ -70,11 +88,13 @@ const UpdateChecker: React.FC<UpdateCheckerProps> = ({ className = "" }) => {
|
|||
};
|
||||
|
||||
const handleManualUpdateCheck = () => {
|
||||
if (!updateChecksEnabled) return;
|
||||
isManualCheckRef.current = true;
|
||||
checkForUpdates();
|
||||
};
|
||||
|
||||
const installUpdate = async () => {
|
||||
if (!updateChecksEnabled) return;
|
||||
try {
|
||||
setIsInstalling(true);
|
||||
setDownloadProgress(0);
|
||||
|
|
@ -119,6 +139,9 @@ const UpdateChecker: React.FC<UpdateCheckerProps> = ({ className = "" }) => {
|
|||
|
||||
// Update status functions
|
||||
const getUpdateStatusText = () => {
|
||||
if (!updateChecksEnabled) {
|
||||
return "Update Checking Disabled";
|
||||
}
|
||||
if (isInstalling) {
|
||||
return downloadProgress > 0 && downloadProgress < 100
|
||||
? `Downloading... ${downloadProgress.toString().padStart(3)}%`
|
||||
|
|
@ -133,13 +156,14 @@ const UpdateChecker: React.FC<UpdateCheckerProps> = ({ className = "" }) => {
|
|||
};
|
||||
|
||||
const getUpdateStatusAction = () => {
|
||||
if (!updateChecksEnabled) return undefined;
|
||||
if (updateAvailable && !isInstalling) return installUpdate;
|
||||
if (!isChecking && !isInstalling && !updateAvailable)
|
||||
return handleManualUpdateCheck;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const isUpdateDisabled = isChecking || isInstalling;
|
||||
const isUpdateDisabled = !updateChecksEnabled || isChecking || isInstalling;
|
||||
const isUpdateClickable =
|
||||
!isUpdateDisabled && (updateAvailable || (!isChecking && !showUpToDate));
|
||||
|
||||
|
|
|
|||
|
|
@ -76,24 +76,28 @@ const settingUpdaters: {
|
|||
commands.changeAudioFeedbackSetting(value as boolean),
|
||||
audio_feedback_volume: (value) =>
|
||||
commands.changeAudioFeedbackVolumeSetting(value as number),
|
||||
sound_theme: (value) =>
|
||||
commands.changeSoundThemeSetting(value as string),
|
||||
start_hidden: (value) =>
|
||||
commands.changeStartHiddenSetting(value as boolean),
|
||||
sound_theme: (value) => commands.changeSoundThemeSetting(value as string),
|
||||
start_hidden: (value) => commands.changeStartHiddenSetting(value as boolean),
|
||||
autostart_enabled: (value) =>
|
||||
commands.changeAutostartSetting(value as boolean),
|
||||
update_checks_enabled: (value) =>
|
||||
commands.changeUpdateChecksSetting(value as boolean),
|
||||
push_to_talk: (value) => commands.changePttSetting(value as boolean),
|
||||
selected_microphone: (value) =>
|
||||
commands.setSelectedMicrophone(
|
||||
(value as string) === "Default" || value === null ? "default" : (value as string)
|
||||
(value as string) === "Default" || value === null
|
||||
? "default"
|
||||
: (value as string),
|
||||
),
|
||||
clamshell_microphone: (value) =>
|
||||
commands.setClamshellMicrophone(
|
||||
(value as string) === "Default" ? "default" : (value as string)
|
||||
(value as string) === "Default" ? "default" : (value as string),
|
||||
),
|
||||
selected_output_device: (value) =>
|
||||
commands.setSelectedOutputDevice(
|
||||
(value as string) === "Default" || value === null ? "default" : (value as string)
|
||||
(value as string) === "Default" || value === null
|
||||
? "default"
|
||||
: (value as string),
|
||||
),
|
||||
recording_retention_period: (value) =>
|
||||
commands.updateRecordingRetentionPeriod(value as string),
|
||||
|
|
@ -103,13 +107,11 @@ const settingUpdaters: {
|
|||
commands.changeSelectedLanguageSetting(value as string),
|
||||
overlay_position: (value) =>
|
||||
commands.changeOverlayPositionSetting(value as string),
|
||||
debug_mode: (value) =>
|
||||
commands.changeDebugModeSetting(value as boolean),
|
||||
debug_mode: (value) => commands.changeDebugModeSetting(value as boolean),
|
||||
custom_words: (value) => commands.updateCustomWords(value as string[]),
|
||||
word_correction_threshold: (value) =>
|
||||
commands.changeWordCorrectionThresholdSetting(value as number),
|
||||
paste_method: (value) =>
|
||||
commands.changePasteMethodSetting(value as string),
|
||||
paste_method: (value) => commands.changePasteMethodSetting(value as string),
|
||||
clipboard_handling: (value) =>
|
||||
commands.changeClipboardHandlingSetting(value as string),
|
||||
history_limit: (value) => commands.updateHistoryLimit(value as string),
|
||||
|
|
@ -158,10 +160,8 @@ export const useSettingsStore = create<SettingsStore>()(
|
|||
const normalizedSettings: Settings = {
|
||||
...settings,
|
||||
always_on_microphone: settings.always_on_microphone ?? false,
|
||||
selected_microphone:
|
||||
settings.selected_microphone ?? "Default",
|
||||
clamshell_microphone:
|
||||
settings.clamshell_microphone ?? "Default",
|
||||
selected_microphone: settings.selected_microphone ?? "Default",
|
||||
clamshell_microphone: settings.clamshell_microphone ?? "Default",
|
||||
selected_output_device:
|
||||
settings.selected_output_device ?? "Default",
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue