From 29a01f92346681802181be47fdd0c3aab38dc066 Mon Sep 17 00:00:00 2001 From: Wind-Explorer Date: Mon, 22 Dec 2025 12:53:13 +0800 Subject: [PATCH] refined doll editor windowing logic --- src-tauri/capabilities/default.json | 2 +- src-tauri/src/lib.rs | 44 ++++- src-tauri/src/services/app_menu.rs | 2 +- src-tauri/src/services/doll_editor.rs | 22 ++- src-tauri/src/services/ws.rs | 6 +- .../tabs/your-dolls/doll-editor-window.svelte | 101 +++++++--- .../tabs/your-dolls/doll-editor.svelte | 181 ------------------ .../app-menu/tabs/your-dolls/index.svelte | 52 +++-- 8 files changed, 164 insertions(+), 246 deletions(-) delete mode 100644 src/routes/app-menu/tabs/your-dolls/doll-editor.svelte diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index dd3e158..ac7e0ad 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -2,7 +2,7 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Capability for the main window", - "windows": ["main", "scene", "app_menu", "doll_editor"], + "windows": ["main", "scene", "app_menu", "doll_editor*"], "permissions": [ "core:default", "opener:default", diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 1aafc05..3dc96af 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -11,7 +11,7 @@ use crate::{ state::{init_app_data, FDOLL}, }; use tauri::async_runtime; -use tauri::Manager; +use tauri::{Emitter, Manager}; use tracing_subscriber::{self, util::SubscriberInitExt}; static APP_HANDLE: std::sync::OnceLock> = std::sync::OnceLock::new(); @@ -199,18 +199,33 @@ async fn get_doll(id: String) -> Result { #[tauri::command] async fn create_doll(dto: CreateDollDto) -> Result { - DollsRemote::new() + let result = DollsRemote::new() .create_doll(dto) .await - .map_err(|e| e.to_string()) + .map_err(|e| e.to_string())?; + + // Emit event locally so the app knows about the update immediately + // The websocket event will also come in, but this makes the UI snappy + if let Err(e) = get_app_handle().emit("doll_created", &result) { + tracing::error!("Failed to emit local doll.created event: {}", e); + } + + Ok(result) } #[tauri::command] async fn update_doll(id: String, dto: UpdateDollDto) -> Result { - DollsRemote::new() + let result = DollsRemote::new() .update_doll(&id, dto) .await - .map_err(|e| e.to_string()) + .map_err(|e| e.to_string())?; + + // Emit event locally + if let Err(e) = get_app_handle().emit("doll_updated", &result) { + tracing::error!("Failed to emit local doll.updated event: {}", e); + } + + Ok(result) } #[tauri::command] @@ -218,7 +233,24 @@ async fn delete_doll(id: String) -> Result<(), String> { DollsRemote::new() .delete_doll(&id) .await - .map_err(|e| e.to_string()) + .map_err(|e| e.to_string())?; + + // Emit event locally + // We need to send something that matches the structure expected by the frontend + // The WS event sends the full object, but for deletion usually ID is enough or the object itself + // Let's send a dummy object with just the ID if we can, or just the ID if the frontend handles it. + // Looking at WS code: on_doll_deleted emits the payload it gets. + // Ideally we'd return the deleted doll from the backend but delete usually returns empty. + // Let's just emit the ID wrapped in an object or similar if needed. + // Actually, let's check what the frontend expects. + // src/routes/app-menu/tabs/your-dolls/index.svelte just listens and calls refreshDolls(), + // it doesn't use the payload. So any payload is fine. + + if let Err(e) = get_app_handle().emit("doll_deleted", ()) { + tracing::error!("Failed to emit local doll.deleted event: {}", e); + } + + Ok(()) } #[tauri::command] diff --git a/src-tauri/src/services/app_menu.rs b/src-tauri/src/services/app_menu.rs index 6ccfb92..4b04324 100644 --- a/src-tauri/src/services/app_menu.rs +++ b/src-tauri/src/services/app_menu.rs @@ -23,7 +23,7 @@ pub fn open_app_menu_window() { .inner_size(600.0, 500.0) .resizable(true) .decorations(true) - .transparent(true) + .transparent(false) .shadow(true) .visible(true) .skip_taskbar(false) diff --git a/src-tauri/src/services/doll_editor.rs b/src-tauri/src/services/doll_editor.rs index da78d69..98b4625 100644 --- a/src-tauri/src/services/doll_editor.rs +++ b/src-tauri/src/services/doll_editor.rs @@ -3,7 +3,6 @@ use tracing::{error, info}; use crate::get_app_handle; -static DOLL_EDITOR_WINDOW_LABEL: &str = "doll_editor"; static APP_MENU_WINDOW_LABEL: &str = "app_menu"; #[tauri::command] @@ -14,8 +13,15 @@ pub async fn open_doll_editor_window(doll_id: Option) { let _ = app_handle.run_on_main_thread(move || { let app_handle = get_app_handle(); + // Construct a unique window label + let window_label = if let Some(ref id) = doll_id { + format!("doll_editor_{}", id) + } else { + "doll_editor_create".to_string() + }; + // Check if the window already exists - let existing_window = app_handle.get_webview_window(DOLL_EDITOR_WINDOW_LABEL); + let existing_window = app_handle.get_webview_window(&window_label); if let Some(window) = existing_window { // If it exists, we might want to reload it with new params or just focus it if let Err(e) = window.set_focus() { @@ -44,14 +50,14 @@ pub async fn open_doll_editor_window(doll_id: Option) { let mut builder = tauri::WebviewWindowBuilder::new( app_handle, - DOLL_EDITOR_WINDOW_LABEL, + &window_label, tauri::WebviewUrl::App(url_path.into()), ) .title("Doll Editor") .inner_size(300.0, 400.0) .resizable(false) .decorations(true) - .transparent(true) + .transparent(false) .shadow(true) .visible(true) .skip_taskbar(false) @@ -73,12 +79,12 @@ pub async fn open_doll_editor_window(doll_id: Option) { match builder.build() { Ok(window) => { - info!("{} window builder succeeded", DOLL_EDITOR_WINDOW_LABEL); - #[cfg(debug_assertions)] - window.open_devtools(); + info!("{} window builder succeeded", window_label); + // #[cfg(debug_assertions)] + // window.open_devtools(); } Err(e) => { - error!("Failed to build {} window: {}", DOLL_EDITOR_WINDOW_LABEL, e); + error!("Failed to build {} window: {}", window_label, e); } }; }); diff --git a/src-tauri/src/services/ws.rs b/src-tauri/src/services/ws.rs index 74f8576..db18a03 100644 --- a/src-tauri/src/services/ws.rs +++ b/src-tauri/src/services/ws.rs @@ -39,9 +39,9 @@ impl WS_EVENT { pub const FRIEND_DOLL_CREATED: &str = "friend-doll-created"; pub const FRIEND_DOLL_UPDATED: &str = "friend-doll-updated"; pub const FRIEND_DOLL_DELETED: &str = "friend-doll-deleted"; - pub const DOLL_CREATED: &str = "doll.created"; - pub const DOLL_UPDATED: &str = "doll.updated"; - pub const DOLL_DELETED: &str = "doll.deleted"; + pub const DOLL_CREATED: &str = "doll_created"; + pub const DOLL_UPDATED: &str = "doll_updated"; + pub const DOLL_DELETED: &str = "doll_deleted"; } fn on_friend_request_received(payload: Payload, _socket: RawClient) { diff --git a/src/routes/app-menu/tabs/your-dolls/doll-editor-window.svelte b/src/routes/app-menu/tabs/your-dolls/doll-editor-window.svelte index 6a55a92..95c71c8 100644 --- a/src/routes/app-menu/tabs/your-dolls/doll-editor-window.svelte +++ b/src/routes/app-menu/tabs/your-dolls/doll-editor-window.svelte @@ -1,21 +1,20 @@ -
+
{#if loading}
@@ -103,15 +100,69 @@
{:else} - +
+
+ + +
+
+ +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+ + +
+
{/if} -
+
\ No newline at end of file diff --git a/src/routes/app-menu/tabs/your-dolls/doll-editor.svelte b/src/routes/app-menu/tabs/your-dolls/doll-editor.svelte deleted file mode 100644 index 78f78a6..0000000 --- a/src/routes/app-menu/tabs/your-dolls/doll-editor.svelte +++ /dev/null @@ -1,181 +0,0 @@ - - - {#if !standalone} - {#if isOpen} - - {/if} - {:else} -
-

- {#if mode === "create"} - Create New Doll - {:else} - Edit Doll - {/if} -

-
- - -
-
- -
-
- -
- - -
-
-
- -
- - -
-
-
- - -
-
- {/if} diff --git a/src/routes/app-menu/tabs/your-dolls/index.svelte b/src/routes/app-menu/tabs/your-dolls/index.svelte index d7f8357..a499742 100644 --- a/src/routes/app-menu/tabs/your-dolls/index.svelte +++ b/src/routes/app-menu/tabs/your-dolls/index.svelte @@ -15,46 +15,56 @@ // We still keep the focus listener as a fallback, but the websocket events should handle most updates onMount(() => { refreshDolls(); - + // Set up listeners - const unlistenCreated = listen("doll.created", (event) => { - console.log("Received doll.created event", event); - refreshDolls(); + const unlistenCreated = listen("doll_created", (event) => { + console.log("Received doll_created event", event); + refreshDolls(); }); - const unlistenUpdated = listen("doll.updated", (event) => { - console.log("Received doll.updated event", event); - refreshDolls(); + const unlistenUpdated = listen("doll_updated", (event) => { + console.log("Received doll_updated event", event); + refreshDolls(); }); - const unlistenDeleted = listen("doll.deleted", (event) => { - console.log("Received doll.deleted event", event); - refreshDolls(); + const unlistenDeleted = listen("doll_deleted", (event) => { + console.log("Received doll_deleted event", event); + refreshDolls(); }); - // Listen for focus events to refresh data when returning from editor window - window.addEventListener("focus", refreshDolls); - return async () => { - window.removeEventListener("focus", refreshDolls); (await unlistenCreated)(); (await unlistenUpdated)(); (await unlistenDeleted)(); }; }); + let isRefreshing = false; + let refreshQueued = false; + async function refreshDolls() { + if (isRefreshing) { + refreshQueued = true; + return; + } + + isRefreshing = true; loading = true; + try { - dolls = await invoke("get_dolls"); - // Use refresh_app_data to ensure we get the latest user state (including activeDollId) - // from the server, as the local state might be stale after updates. - const appData: AppData = await invoke("refresh_app_data"); - user = appData.user; - } catch (e) { - error = (e as Error)?.message ?? String(e); + do { + refreshQueued = false; + try { + dolls = await invoke("get_dolls"); + const appData: AppData = await invoke("refresh_app_data"); + user = appData.user; + } catch (e) { + error = (e as Error)?.message ?? String(e); + } + } while (refreshQueued); } finally { loading = false; + isRefreshing = false; } }