feat: add WorkspaceMode (local/webdav) and per-workspace theme to config
Introduces WorkspaceMode enum with local and webdav variants, plus a theme field on WorkspaceConfig. Adds set_workspace_theme and add_webdav_workspace Tauri commands. WebDAV workspaces auto-manage local files in app data dir. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4483a6450f
commit
a60b1a997b
|
|
@ -11,7 +11,7 @@ use tauri::{Emitter, Manager, State};
|
|||
use uuid::Uuid;
|
||||
|
||||
use onyx_core::{
|
||||
config::{AppConfig, WorkspaceConfig},
|
||||
config::{AppConfig, WorkspaceConfig, WorkspaceMode},
|
||||
models::{Task, TaskList, TaskStatus},
|
||||
repository::TaskRepository,
|
||||
sync::{self, SyncMode, SyncResult as CoreSyncResult},
|
||||
|
|
@ -31,6 +31,7 @@ static LAST_WRITE: Mutex<Option<Instant>> = Mutex::new(None);
|
|||
struct AppState {
|
||||
config: AppConfig,
|
||||
config_path: PathBuf,
|
||||
app_data_dir: PathBuf,
|
||||
repo: Option<TaskRepository>,
|
||||
}
|
||||
|
||||
|
|
@ -417,6 +418,53 @@ fn set_webdav_config(
|
|||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn set_workspace_theme(
|
||||
workspace_name: String,
|
||||
theme: Option<String>,
|
||||
state: State<'_, Mutex<AppState>>,
|
||||
) -> Result<(), String> {
|
||||
let mut s = lock_state(&state)?;
|
||||
if let Some(ws) = s.config.workspaces.get_mut(&workspace_name) {
|
||||
ws.theme = theme;
|
||||
}
|
||||
s.config.save_to_file(&s.config_path.clone()).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn add_webdav_workspace(
|
||||
name: String,
|
||||
webdav_url: String,
|
||||
username: String,
|
||||
password: String,
|
||||
state: State<'_, Mutex<AppState>>,
|
||||
) -> Result<(), String> {
|
||||
let mut s = lock_state(&state)?;
|
||||
let managed_dir = s.app_data_dir.join("workspaces").join(&name);
|
||||
std::fs::create_dir_all(&managed_dir).map_err(|e| e.to_string())?;
|
||||
TaskRepository::init(managed_dir.clone()).map(|_| ()).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut ws = WorkspaceConfig::new(managed_dir);
|
||||
ws.mode = WorkspaceMode::Webdav;
|
||||
ws.webdav_url = Some(webdav_url.clone());
|
||||
|
||||
s.config.add_workspace(name.clone(), ws);
|
||||
s.config.set_current_workspace(name).map_err(|e| e.to_string())?;
|
||||
s.repo = None;
|
||||
|
||||
// Store credentials keyed by hostname
|
||||
let domain = webdav_url
|
||||
.split("://")
|
||||
.nth(1)
|
||||
.and_then(|rest| rest.split('/').next())
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
s.config.save_to_file(&s.config_path.clone()).map_err(|e| e.to_string())?;
|
||||
drop(s);
|
||||
webdav::store_credentials(&domain, &username, &password).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn store_credentials(
|
||||
domain: String,
|
||||
|
|
@ -545,23 +593,18 @@ pub fn run() {
|
|||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.setup(|app| {
|
||||
// Resolve config path: Tauri's app_data_dir on Android, directories crate on desktop
|
||||
// Resolve app data dir and config path
|
||||
let app_data_dir = app.path().app_data_dir()
|
||||
.map_err(|e| format!("Failed to get app data dir: {}", e))?;
|
||||
let config_path = {
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
use tauri::Manager;
|
||||
app.path().app_data_dir()
|
||||
.map_err(|e| format!("Failed to get app data dir: {}", e))?
|
||||
.join("config.json")
|
||||
}
|
||||
{ app_data_dir.join("config.json") }
|
||||
#[cfg(not(target_os = "android"))]
|
||||
{
|
||||
AppConfig::get_config_path()
|
||||
}
|
||||
{ AppConfig::get_config_path() }
|
||||
};
|
||||
let config = AppConfig::load_from_file(&config_path).unwrap_or_default();
|
||||
let workspace_path = config.get_current_workspace().ok().map(|(_, ws)| ws.path.clone());
|
||||
app.manage(Mutex::new(AppState { config, config_path, repo: None }));
|
||||
app.manage(Mutex::new(AppState { config, config_path, app_data_dir, repo: None }));
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
if let Some(path) = workspace_path {
|
||||
|
|
@ -591,6 +634,8 @@ pub fn run() {
|
|||
set_group_by_due_date,
|
||||
get_group_by_due_date,
|
||||
set_webdav_config,
|
||||
set_workspace_theme,
|
||||
add_webdav_workspace,
|
||||
store_credentials,
|
||||
load_credentials,
|
||||
test_webdav_connection,
|
||||
|
|
|
|||
|
|
@ -19,10 +19,14 @@ export interface TaskList {
|
|||
group_by_due_date: boolean;
|
||||
}
|
||||
|
||||
export type WorkspaceMode = "local" | "webdav";
|
||||
|
||||
export interface WorkspaceConfig {
|
||||
path: string;
|
||||
mode: WorkspaceMode;
|
||||
webdav_url: string | null;
|
||||
last_sync: string | null;
|
||||
theme: string | null;
|
||||
}
|
||||
|
||||
export interface AppConfig {
|
||||
|
|
|
|||
|
|
@ -3,18 +3,35 @@ use std::path::PathBuf;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum WorkspaceMode {
|
||||
Local,
|
||||
Webdav,
|
||||
}
|
||||
|
||||
impl Default for WorkspaceMode {
|
||||
fn default() -> Self {
|
||||
Self::Local
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WorkspaceConfig {
|
||||
pub path: PathBuf,
|
||||
#[serde(default)]
|
||||
pub mode: WorkspaceMode,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub webdav_url: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub last_sync: Option<chrono::DateTime<chrono::Utc>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none", default)]
|
||||
pub theme: Option<String>,
|
||||
}
|
||||
|
||||
impl WorkspaceConfig {
|
||||
pub fn new(path: PathBuf) -> Self {
|
||||
Self { path, webdav_url: None, last_sync: None }
|
||||
Self { path, mode: WorkspaceMode::Local, webdav_url: None, last_sync: None, theme: None }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +246,7 @@ mod tests {
|
|||
let temp_dir = TempDir::new().unwrap();
|
||||
let config_path = temp_dir.path().join("config.json");
|
||||
|
||||
// Write old-format JSON without webdav_url or last_sync fields
|
||||
// Write old-format JSON without webdav_url, last_sync, mode, or theme fields
|
||||
let old_json = r#"{
|
||||
"workspaces": {
|
||||
"personal": { "path": "/home/user/tasks" }
|
||||
|
|
@ -243,5 +260,7 @@ mod tests {
|
|||
assert_eq!(ws.path, PathBuf::from("/home/user/tasks"));
|
||||
assert!(ws.webdav_url.is_none());
|
||||
assert!(ws.last_sync.is_none());
|
||||
assert_eq!(ws.mode, WorkspaceMode::Local);
|
||||
assert!(ws.theme.is_none());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue