feat: autoupdate toggle

This commit is contained in:
2025-04-04 19:41:38 +00:00
parent 8c3f9762a4
commit ca871f069f
8 changed files with 107 additions and 33 deletions

View File

@ -3,6 +3,7 @@ use std::collections::HashMap;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tokio::fs; use tokio::fs;
use tauri::{AppHandle, Manager, State}; use tauri::{AppHandle, Manager, State};
use crate::model::config::GlobalConfigField;
use crate::model::misc::Game; use crate::model::misc::Game;
use crate::pkg::{Package, PkgKey}; use crate::pkg::{Package, PkgKey};
use crate::pkg_store::{InstallResult, PackageStore}; use crate::pkg_store::{InstallResult, PackageStore};
@ -356,19 +357,25 @@ pub async fn list_platform_capabilities() -> Result<Vec<String>, ()> {
} }
#[tauri::command] #[tauri::command]
pub async fn is_offline(state: State<'_, Mutex<AppData>>) -> Result<bool, ()> { pub async fn get_global_config(state: State<'_, Mutex<AppData>>, field: GlobalConfigField) -> Result<bool, ()> {
log::debug!("invoke: is_offline"); log::debug!("invoke: get_global_config({field:?})");
let appd = state.lock().await; let appd = state.lock().await;
Ok(appd.cfg.offline_mode) match field {
GlobalConfigField::OfflineMode => Ok(appd.cfg.offline_mode),
GlobalConfigField::EnableAutoupdates => Ok(appd.cfg.enable_autoupdates)
}
} }
#[tauri::command] #[tauri::command]
pub async fn set_offline(state: State<'_, Mutex<AppData>>, value: bool) -> Result<(), String> { pub async fn set_global_config(state: State<'_, Mutex<AppData>>, field: GlobalConfigField, value: bool) -> Result<(), String> {
log::debug!("invoke: set_offline({value})"); log::debug!("invoke: set_global_config({field:?}, {value})");
let mut appd = state.lock().await; let mut appd = state.lock().await;
appd.cfg.offline_mode = value; match field {
GlobalConfigField::OfflineMode => appd.cfg.offline_mode = value,
GlobalConfigField::EnableAutoupdates => appd.cfg.enable_autoupdates = value
};
appd.write().map_err(|e| e.to_string()) appd.write().map_err(|e| e.to_string())
} }

View File

@ -200,8 +200,8 @@ pub async fn run(_args: Vec<String>) {
cmd::sync_current_profile, cmd::sync_current_profile,
cmd::save_current_profile, cmd::save_current_profile,
cmd::is_offline, cmd::get_global_config,
cmd::set_offline, cmd::set_global_config,
cmd::list_displays, cmd::list_displays,
cmd::list_platform_capabilities, cmd::list_platform_capabilities,
@ -265,10 +265,16 @@ fn deep_link(app: AppHandle, args: Vec<String>) {
} }
async fn update(app: tauri::AppHandle) -> tauri_plugin_updater::Result<()> { async fn update(app: tauri::AppHandle) -> tauri_plugin_updater::Result<()> {
let mutex = app.state::<Mutex<AppData>>();
let appd = mutex.lock().await;
if !appd.cfg.enable_autoupdates {
log::info!("skipping autoupdate");
return Ok(());
}
if let Some(update) = app.updater()?.check().await? { if let Some(update) = app.updater()?.check().await? {
let mut downloaded = 0; let mut downloaded = 0;
update update.download_and_install(
.download_and_install(
|chunk_length, content_length| { |chunk_length, content_length| {
downloaded += chunk_length; downloaded += chunk_length;
log::debug!("downloaded {downloaded} from {content_length:?}"); log::debug!("downloaded {downloaded} from {content_length:?}");

View File

@ -1,10 +1,25 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::misc::Game; use super::misc::Game;
#[derive(Serialize, Deserialize, Clone, Default)] #[derive(Serialize, Deserialize, Clone)]
pub struct GlobalConfig { pub struct GlobalConfig {
pub recent_profile: Option<(Game, String)>, pub recent_profile: Option<(Game, String)>,
#[serde(default)]
pub offline_mode: bool, pub offline_mode: bool,
pub enable_autoupdates: bool,
}
impl Default for GlobalConfig {
fn default() -> Self {
Self {
recent_profile: Default::default(),
offline_mode: false,
enable_autoupdates: true
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum GlobalConfigField {
OfflineMode,
EnableAutoupdates
} }

View File

@ -48,7 +48,10 @@ const aimeCodePaste = (ev: ClipboardEvent) => {
<template> <template>
<OptionCategory title="Aime"> <OptionCategory title="Aime">
<OptionRow title="Aime emulation"> <OptionRow
title="Aime emulation"
tooltip="Aime plugins can be downloaded from the package store."
>
<Select <Select
v-model="prf.current!.data.sgt.aime" v-model="prf.current!.data.sgt.aime"
:options="[ :options="[

View File

@ -13,7 +13,10 @@ const prf = usePrfStore();
<OptionRow title="OpenSSL bug workaround for Intel ≥10th gen"> <OptionRow title="OpenSSL bug workaround for Intel ≥10th gen">
<ToggleSwitch v-model="prf.current!.data.sgt.intel" /> <ToggleSwitch v-model="prf.current!.data.sgt.intel" />
</OptionRow> </OptionRow>
<OptionRow title="More segatools options"> <OptionRow
title="More segatools options"
tooltip="Advanced options not covered by STARTLINER"
>
<FileEditor filename="segatools-base.ini" /> <FileEditor filename="segatools-base.ini" />
</OptionRow> </OptionRow>
</OptionCategory> </OptionCategory>

View File

@ -79,7 +79,10 @@ const names = computed(() => {
" "
></FilePicker> ></FilePicker>
</OptionRow> </OptionRow>
<OptionRow :title="names.hook"> <OptionRow
:title="names.hook"
tooltip="Hooks can be downloaded from the package store."
>
<Select <Select
v-model="prf.current!.data.sgt.hook" v-model="prf.current!.data.sgt.hook"
:options=" :options="
@ -97,7 +100,11 @@ const names = computed(() => {
option-value="value" option-value="value"
></Select> ></Select>
</OptionRow> </OptionRow>
<OptionRow :title="names.io" v-if="prf.current?.meta.game === 'ongeki'"> <OptionRow
:title="names.io"
v-if="prf.current?.meta.game === 'ongeki'"
tooltip="IO plugins can be downloaded from the package store."
>
<Select <Select
v-model="prf.current!.data.sgt.io" v-model="prf.current!.data.sgt.io"
placeholder="segatools built-in" placeholder="segatools built-in"

View File

@ -16,10 +16,19 @@ const offlineModel = computed({
await client.setOfflineMode(value); await client.setOfflineMode(value);
}, },
}); });
const updatesModel = computed({
get() {
return client.enableAutoupdates;
},
async set(value: boolean) {
await client.setAutoupdates(value);
},
});
</script> </script>
<template> <template>
<OptionCategory title="Startliner"> <OptionCategory title="STARTLINER">
<OptionRow title="UI scaling"> <OptionRow title="UI scaling">
<SelectButton <SelectButton
v-model="client.scaleModel" v-model="client.scaleModel"
@ -34,8 +43,14 @@ const offlineModel = computed({
option-value="value" option-value="value"
/> />
</OptionRow> </OptionRow>
<OptionRow title="Offline mode" tooltip="Applies after a restart"> <OptionRow
title="Offline mode"
tooltip="Disables the package store. Applies after a restart"
>
<ToggleSwitch v-model="offlineModel" /> <ToggleSwitch v-model="offlineModel" />
</OptionRow> </OptionRow>
<OptionRow title="Enable automatic updates">
<ToggleSwitch v-model="updatesModel" />
</OptionRow>
</OptionCategory> </OptionCategory>
</template> </template>

View File

@ -336,7 +336,9 @@ export const useClientStore = defineStore('client', () => {
type ScaleType = 's' | 'm' | 'l' | 'xl'; type ScaleType = 's' | 'm' | 'l' | 'xl';
const scaleFactor: Ref<ScaleType> = ref('s'); const scaleFactor: Ref<ScaleType> = ref('s');
const timeout: Ref<NodeJS.Timeout | null> = ref(null); const timeout: Ref<NodeJS.Timeout | null> = ref(null);
const offlineMode = ref(true);
const offlineMode = ref(false);
const enableAutoupdates = ref(true);
const scaleValue = (value: ScaleType) => const scaleValue = (value: ScaleType) =>
value === 's' ? 1 : value === 'm' ? 1.25 : value === 'l' ? 1.5 : 2; value === 's' ? 1 : value === 'm' ? 1.25 : value === 'l' ? 1.5 : 2;
@ -387,11 +389,17 @@ export const useClientStore = defineStore('client', () => {
if (input.scaleFactor) { if (input.scaleFactor) {
await setScaleFactor(input.scaleFactor); await setScaleFactor(input.scaleFactor);
} }
offlineMode.value = await invoke('is_offline');
} catch (e) { } catch (e) {
console.error(`Error reading client options: ${e}`); console.error(`Error reading client options: ${e}`);
} }
offlineMode.value = await invoke('get_global_config', {
field: 'OfflineMode',
});
enableAutoupdates.value = await invoke('get_global_config', {
field: 'EnableAutoupdates',
});
}; };
const save = async () => { const save = async () => {
@ -423,7 +431,15 @@ export const useClientStore = defineStore('client', () => {
const setOfflineMode = async (value: boolean) => { const setOfflineMode = async (value: boolean) => {
offlineMode.value = value; offlineMode.value = value;
await invoke('set_offline', { value }); await invoke('set_global_config', { field: 'OfflineMode', value });
};
const setAutoupdates = async (value: boolean) => {
enableAutoupdates.value = value;
await invoke('set_global_config', {
field: 'EnableAutoupdates',
value,
});
}; };
getCurrentWindow().onResized(async ({ payload }) => { getCurrentWindow().onResized(async ({ payload }) => {
@ -436,11 +452,13 @@ export const useClientStore = defineStore('client', () => {
return { return {
scaleFactor, scaleFactor,
offlineMode, offlineMode,
enableAutoupdates,
timeout, timeout,
scaleModel, scaleModel,
load, load,
save, save,
queueSave, queueSave,
setOfflineMode, setOfflineMode,
setAutoupdates,
}; };
}); });