Host: 256 feature flags, the onboarding mode (mode_get/mode_set, shell-only), and the one-time move of replaced plugin files to feature flags
- settings: MAX_FEATURES 64 -> 256 (an older app's whole-map backup still validates)
- modes: ModeState {mode, onboarded, existing} in modes.json; existing is decided natively on the first start
- plugins::migrate: PluginHost.superseded (file -> flag); enabled and as shipped -> flag on and the file goes; edited -> kept, flag left off; marker written last, every step repeatable; map_backup for imported enabled lists
- a superseded file is never written again by install_bundled
- create-waybetter: the host command list and PluginHost.superseded
This commit is contained in:
parent
f31b00eec7
commit
365f55f604
9 changed files with 477 additions and 6 deletions
|
|
@ -22,6 +22,7 @@ use crate::{
|
|||
host::{self, emit_shell, emit_tab_state, read_json, shell_only, site_tab_only, tab_only, tab_webview, write_json},
|
||||
i18n::{self, NativeStrings},
|
||||
layout,
|
||||
modes::{ModeState, MODES_FILE},
|
||||
plugins::{self, PluginHost, PluginStore, ScriptPlugin},
|
||||
policy,
|
||||
profiles::{self, AccountSeen, ProfileSettings},
|
||||
|
|
@ -203,6 +204,8 @@ pub struct PageState {
|
|||
/// A JSON object of booleans ([`settings::validate_features`]).
|
||||
pub features: serde_json::Value,
|
||||
pub settings: ShellSettings,
|
||||
/// The onboarding mode the owner started from and whether the first-run screen was answered.
|
||||
pub modes: ModeState,
|
||||
}
|
||||
|
||||
struct Profiles(Mutex<ProfileState>);
|
||||
|
|
@ -715,6 +718,25 @@ async fn features_set(webview: Webview, app: AppHandle, features: serde_json::Ma
|
|||
set_features(&app, features)
|
||||
}
|
||||
|
||||
/// The onboarding mode: what was chosen, whether the first-run screen was answered, and whether
|
||||
/// this app was set up before modes existed.
|
||||
#[tauri::command]
|
||||
async fn mode_get(webview: Webview, app: AppHandle) -> Result<ModeState, String> {
|
||||
shell_only(&webview)?;
|
||||
Ok(page(&app)?.modes.clone())
|
||||
}
|
||||
|
||||
/// Remembers the chosen mode (or none) and that the first-run screen was answered. The preset's
|
||||
/// flags are the shell's to apply (`features_set`); this only records the choice.
|
||||
#[tauri::command]
|
||||
async fn mode_set(webview: Webview, app: AppHandle, mode: Option<String>, onboarded: bool) -> Result<ModeState, String> {
|
||||
shell_only(&webview)?;
|
||||
let next = page(&app)?.modes.chosen(mode, onboarded)?;
|
||||
write_json(&app, MODES_FILE, &next)?;
|
||||
page(&app)?.modes = next.clone();
|
||||
Ok(next)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn settings_get(webview: Webview, app: AppHandle) -> Result<ShellSettings, String> {
|
||||
shell_only(&webview)?;
|
||||
|
|
@ -952,6 +974,8 @@ fn host_handler() -> impl Fn(tauri::ipc::Invoke<Wry>) -> bool + Send + Sync + 's
|
|||
custom_css_set,
|
||||
features_get,
|
||||
features_set,
|
||||
mode_get,
|
||||
mode_set,
|
||||
settings_get,
|
||||
settings_set,
|
||||
tab_shortcut,
|
||||
|
|
@ -1019,6 +1043,8 @@ impl App {
|
|||
}
|
||||
let directory = tauri_app.path().app_data_dir()?;
|
||||
std::fs::create_dir_all(&directory)?;
|
||||
// Someone set this app up before (read before anything below writes a file).
|
||||
let existing = [PROFILES_FILE, SETTINGS_FILE, FEATURES_FILE, PLUGINS_DIR].iter().any(|name| directory.join(name).exists());
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
|
@ -1042,10 +1068,28 @@ impl App {
|
|||
let settings = read_json::<serde_json::Value>(&handle, SETTINGS_FILE)
|
||||
.and_then(|value| ShellSettings::from_value(value, &home).ok())
|
||||
.unwrap_or_else(|| ShellSettings::defaults(&home));
|
||||
let features = read_json::<serde_json::Value>(&handle, FEATURES_FILE)
|
||||
let mut features = read_json::<serde_json::Value>(&handle, FEATURES_FILE)
|
||||
.filter(serde_json::Value::is_object)
|
||||
.unwrap_or_else(|| serde_json::json!({}));
|
||||
tauri_app.manage(Page(Mutex::new(PageState { plugins, features, settings })));
|
||||
// Plugin files the app turned into features: what was enabled becomes its flag, once
|
||||
// (plugins::migrate). The flags are saved before any file is touched.
|
||||
let migrated = plugins.migrate_superseded(config.plugins.superseded, |flags| {
|
||||
let Some(map) = features.as_object_mut().filter(|_| !flags.is_empty()) else { return Ok(()) };
|
||||
for flag in flags {
|
||||
map.insert(flag.clone(), serde_json::Value::Bool(true));
|
||||
}
|
||||
settings::validate_features(map)?;
|
||||
write_json(&handle, FEATURES_FILE, &*map)
|
||||
});
|
||||
if let Err(error) = migrated {
|
||||
eprintln!("{}: {error}", config.title);
|
||||
}
|
||||
let modes = read_json::<ModeState>(&handle, MODES_FILE).and_then(|state| state.validated().ok()).unwrap_or_else(|| {
|
||||
let state = ModeState::first_run(existing);
|
||||
let _ = write_json(&handle, MODES_FILE, &state);
|
||||
state
|
||||
});
|
||||
tauri_app.manage(Page(Mutex::new(PageState { plugins, features, settings, modes })));
|
||||
tauri_app.manage(Host { config, hooks });
|
||||
if let Some(setup) = setup {
|
||||
setup(&handle)?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue