refined doll editor windowing logic
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
"$schema": "../gen/schemas/desktop-schema.json",
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
"identifier": "default",
|
"identifier": "default",
|
||||||
"description": "Capability for the main window",
|
"description": "Capability for the main window",
|
||||||
"windows": ["main", "scene", "app_menu", "doll_editor"],
|
"windows": ["main", "scene", "app_menu", "doll_editor*"],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"core:default",
|
"core:default",
|
||||||
"opener:default",
|
"opener:default",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use crate::{
|
|||||||
state::{init_app_data, FDOLL},
|
state::{init_app_data, FDOLL},
|
||||||
};
|
};
|
||||||
use tauri::async_runtime;
|
use tauri::async_runtime;
|
||||||
use tauri::Manager;
|
use tauri::{Emitter, Manager};
|
||||||
use tracing_subscriber::{self, util::SubscriberInitExt};
|
use tracing_subscriber::{self, util::SubscriberInitExt};
|
||||||
|
|
||||||
static APP_HANDLE: std::sync::OnceLock<tauri::AppHandle<tauri::Wry>> = std::sync::OnceLock::new();
|
static APP_HANDLE: std::sync::OnceLock<tauri::AppHandle<tauri::Wry>> = std::sync::OnceLock::new();
|
||||||
@@ -199,18 +199,33 @@ async fn get_doll(id: String) -> Result<DollDto, String> {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn create_doll(dto: CreateDollDto) -> Result<DollDto, String> {
|
async fn create_doll(dto: CreateDollDto) -> Result<DollDto, String> {
|
||||||
DollsRemote::new()
|
let result = DollsRemote::new()
|
||||||
.create_doll(dto)
|
.create_doll(dto)
|
||||||
.await
|
.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]
|
#[tauri::command]
|
||||||
async fn update_doll(id: String, dto: UpdateDollDto) -> Result<DollDto, String> {
|
async fn update_doll(id: String, dto: UpdateDollDto) -> Result<DollDto, String> {
|
||||||
DollsRemote::new()
|
let result = DollsRemote::new()
|
||||||
.update_doll(&id, dto)
|
.update_doll(&id, dto)
|
||||||
.await
|
.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]
|
#[tauri::command]
|
||||||
@@ -218,7 +233,24 @@ async fn delete_doll(id: String) -> Result<(), String> {
|
|||||||
DollsRemote::new()
|
DollsRemote::new()
|
||||||
.delete_doll(&id)
|
.delete_doll(&id)
|
||||||
.await
|
.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]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ pub fn open_app_menu_window() {
|
|||||||
.inner_size(600.0, 500.0)
|
.inner_size(600.0, 500.0)
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
.decorations(true)
|
.decorations(true)
|
||||||
.transparent(true)
|
.transparent(false)
|
||||||
.shadow(true)
|
.shadow(true)
|
||||||
.visible(true)
|
.visible(true)
|
||||||
.skip_taskbar(false)
|
.skip_taskbar(false)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use tracing::{error, info};
|
|||||||
|
|
||||||
use crate::get_app_handle;
|
use crate::get_app_handle;
|
||||||
|
|
||||||
static DOLL_EDITOR_WINDOW_LABEL: &str = "doll_editor";
|
|
||||||
static APP_MENU_WINDOW_LABEL: &str = "app_menu";
|
static APP_MENU_WINDOW_LABEL: &str = "app_menu";
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -14,8 +13,15 @@ pub async fn open_doll_editor_window(doll_id: Option<String>) {
|
|||||||
let _ = app_handle.run_on_main_thread(move || {
|
let _ = app_handle.run_on_main_thread(move || {
|
||||||
let app_handle = get_app_handle();
|
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
|
// 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 let Some(window) = existing_window {
|
||||||
// If it exists, we might want to reload it with new params or just focus it
|
// If it exists, we might want to reload it with new params or just focus it
|
||||||
if let Err(e) = window.set_focus() {
|
if let Err(e) = window.set_focus() {
|
||||||
@@ -44,14 +50,14 @@ pub async fn open_doll_editor_window(doll_id: Option<String>) {
|
|||||||
|
|
||||||
let mut builder = tauri::WebviewWindowBuilder::new(
|
let mut builder = tauri::WebviewWindowBuilder::new(
|
||||||
app_handle,
|
app_handle,
|
||||||
DOLL_EDITOR_WINDOW_LABEL,
|
&window_label,
|
||||||
tauri::WebviewUrl::App(url_path.into()),
|
tauri::WebviewUrl::App(url_path.into()),
|
||||||
)
|
)
|
||||||
.title("Doll Editor")
|
.title("Doll Editor")
|
||||||
.inner_size(300.0, 400.0)
|
.inner_size(300.0, 400.0)
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.decorations(true)
|
.decorations(true)
|
||||||
.transparent(true)
|
.transparent(false)
|
||||||
.shadow(true)
|
.shadow(true)
|
||||||
.visible(true)
|
.visible(true)
|
||||||
.skip_taskbar(false)
|
.skip_taskbar(false)
|
||||||
@@ -73,12 +79,12 @@ pub async fn open_doll_editor_window(doll_id: Option<String>) {
|
|||||||
|
|
||||||
match builder.build() {
|
match builder.build() {
|
||||||
Ok(window) => {
|
Ok(window) => {
|
||||||
info!("{} window builder succeeded", DOLL_EDITOR_WINDOW_LABEL);
|
info!("{} window builder succeeded", window_label);
|
||||||
#[cfg(debug_assertions)]
|
// #[cfg(debug_assertions)]
|
||||||
window.open_devtools();
|
// window.open_devtools();
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to build {} window: {}", DOLL_EDITOR_WINDOW_LABEL, e);
|
error!("Failed to build {} window: {}", window_label, e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ impl WS_EVENT {
|
|||||||
pub const FRIEND_DOLL_CREATED: &str = "friend-doll-created";
|
pub const FRIEND_DOLL_CREATED: &str = "friend-doll-created";
|
||||||
pub const FRIEND_DOLL_UPDATED: &str = "friend-doll-updated";
|
pub const FRIEND_DOLL_UPDATED: &str = "friend-doll-updated";
|
||||||
pub const FRIEND_DOLL_DELETED: &str = "friend-doll-deleted";
|
pub const FRIEND_DOLL_DELETED: &str = "friend-doll-deleted";
|
||||||
pub const DOLL_CREATED: &str = "doll.created";
|
pub const DOLL_CREATED: &str = "doll_created";
|
||||||
pub const DOLL_UPDATED: &str = "doll.updated";
|
pub const DOLL_UPDATED: &str = "doll_updated";
|
||||||
pub const DOLL_DELETED: &str = "doll.deleted";
|
pub const DOLL_DELETED: &str = "doll_deleted";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_friend_request_received(payload: Payload, _socket: RawClient) {
|
fn on_friend_request_received(payload: Payload, _socket: RawClient) {
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { page } from "$app/stores";
|
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||||
import DollEditor from "./doll-editor.svelte";
|
|
||||||
import type { DollDto } from "../../../../types/bindings/DollDto";
|
import type { DollDto } from "../../../../types/bindings/DollDto";
|
||||||
import type { CreateDollDto } from "../../../../types/bindings/CreateDollDto";
|
import type { CreateDollDto } from "../../../../types/bindings/CreateDollDto";
|
||||||
import type { UpdateDollDto } from "../../../../types/bindings/UpdateDollDto";
|
import type { UpdateDollDto } from "../../../../types/bindings/UpdateDollDto";
|
||||||
|
import DollPreview from "../DollPreview.svelte";
|
||||||
|
|
||||||
let mode: "create" | "edit" = "create";
|
let mode: "create" | "edit" = "create";
|
||||||
let dollId: string | null = null;
|
let dollId: string | null = null;
|
||||||
let loading = true;
|
let loading = true;
|
||||||
let error: string | null = null;
|
let error: string | null = null;
|
||||||
|
|
||||||
let initialName = "";
|
let name = "";
|
||||||
let initialBodyColor = "#FFFFFF";
|
let bodyColor = "#FFFFFF";
|
||||||
let initialOutlineColor = "#000000";
|
let outlineColor = "#000000";
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// Check URL search params for ID
|
// Check URL search params for ID
|
||||||
@@ -36,9 +35,9 @@
|
|||||||
loading = true;
|
loading = true;
|
||||||
try {
|
try {
|
||||||
const doll: DollDto = await invoke("get_doll", { id });
|
const doll: DollDto = await invoke("get_doll", { id });
|
||||||
initialName = doll.name;
|
name = doll.name;
|
||||||
initialBodyColor = doll.configuration.colorScheme.body;
|
bodyColor = doll.configuration.colorScheme.body;
|
||||||
initialOutlineColor = doll.configuration.colorScheme.outline;
|
outlineColor = doll.configuration.colorScheme.outline;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = (e as Error)?.message ?? String(e);
|
error = (e as Error)?.message ?? String(e);
|
||||||
console.error("Failed to fetch doll:", e);
|
console.error("Failed to fetch doll:", e);
|
||||||
@@ -47,11 +46,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSave(
|
async function handleSave() {
|
||||||
name: string,
|
if (!name.trim()) return;
|
||||||
bodyColor: string,
|
|
||||||
outlineColor: string,
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
if (mode === "create") {
|
if (mode === "create") {
|
||||||
const dto: CreateDollDto = {
|
const dto: CreateDollDto = {
|
||||||
@@ -90,7 +87,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-screen h-screen bg-base-100">
|
<div class="w-screen h-screen bg-base-100 flex flex-col">
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<div class="flex h-full items-center justify-center">
|
<div class="flex h-full items-center justify-center">
|
||||||
<span class="loading loading-spinner loading-lg"></span>
|
<span class="loading loading-spinner loading-lg"></span>
|
||||||
@@ -103,15 +100,69 @@
|
|||||||
<button class="btn btn-sm mt-4" on:click={handleCancel}>Close</button>
|
<button class="btn btn-sm mt-4" on:click={handleCancel}>Close</button>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<DollEditor
|
<div class="h-full w-full p-4 flex flex-col">
|
||||||
isOpen={true}
|
<div class="form-control w-full">
|
||||||
standalone={true}
|
<label class="label">
|
||||||
{mode}
|
<span class="label-text">Name</span>
|
||||||
{initialName}
|
</label>
|
||||||
{initialBodyColor}
|
<input
|
||||||
{initialOutlineColor}
|
type="text"
|
||||||
onSave={handleSave}
|
placeholder="Doll Name"
|
||||||
onCancel={handleCancel}
|
class="input input-bordered w-full"
|
||||||
/>
|
bind:value={name}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center mt-4">
|
||||||
|
<DollPreview {bodyColor} {outlineColor} />
|
||||||
|
</div>
|
||||||
|
<div class="form-control w-full mt-2">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">Body Color</span>
|
||||||
|
</label>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
class="input input-bordered w-12 p-1 h-10"
|
||||||
|
bind:value={bodyColor}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
bind:value={bodyColor}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-control w-full mt-2">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">Outline Color</span>
|
||||||
|
</label>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
class="input input-bordered w-12 p-1 h-10"
|
||||||
|
bind:value={outlineColor}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="input input-bordered w-full"
|
||||||
|
bind:value={outlineColor}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-auto pt-4 flex justify-end gap-2">
|
||||||
|
<button class="btn" on:click={handleCancel}>Cancel</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-primary"
|
||||||
|
on:click={handleSave}
|
||||||
|
disabled={!name.trim()}
|
||||||
|
>
|
||||||
|
{#if mode === "create"}
|
||||||
|
Create
|
||||||
|
{:else}
|
||||||
|
Save
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import DollPreview from "../DollPreview.svelte";
|
|
||||||
|
|
||||||
export let isOpen: boolean;
|
|
||||||
export let mode: "create" | "edit";
|
|
||||||
export let initialName = "";
|
|
||||||
export let initialBodyColor = "#FFFFFF";
|
|
||||||
export let initialOutlineColor = "#000000";
|
|
||||||
export let onSave: (
|
|
||||||
name: string,
|
|
||||||
bodyColor: string,
|
|
||||||
outlineColor: string,
|
|
||||||
) => void;
|
|
||||||
export let onCancel: () => void;
|
|
||||||
export let standalone = false;
|
|
||||||
|
|
||||||
let name = initialName;
|
|
||||||
let bodyColor = initialBodyColor;
|
|
||||||
let outlineColor = initialOutlineColor;
|
|
||||||
|
|
||||||
$: if (isOpen) {
|
|
||||||
name = initialName;
|
|
||||||
bodyColor = initialBodyColor;
|
|
||||||
outlineColor = initialOutlineColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSave() {
|
|
||||||
if (!name.trim()) return;
|
|
||||||
onSave(name, bodyColor, outlineColor);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if !standalone}
|
|
||||||
{#if isOpen}
|
|
||||||
<div class="modal modal-open">
|
|
||||||
<div class="modal-box">
|
|
||||||
<h3 class="font-bold text-lg">
|
|
||||||
{#if mode === "create"}
|
|
||||||
Create New Doll
|
|
||||||
{:else}
|
|
||||||
Edit Doll
|
|
||||||
{/if}
|
|
||||||
</h3>
|
|
||||||
<div class="form-control w-full mt-4">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Name</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Doll Name"
|
|
||||||
class="input input-bordered w-full"
|
|
||||||
bind:value={name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-center mt-4">
|
|
||||||
<DollPreview {bodyColor} {outlineColor} />
|
|
||||||
</div>
|
|
||||||
<div class="form-control w-full mt-2">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Body Color</span>
|
|
||||||
</label>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
class="input input-bordered w-12 p-1 h-10"
|
|
||||||
bind:value={bodyColor}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="input input-bordered w-full"
|
|
||||||
bind:value={bodyColor}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-control w-full mt-2">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Outline Color</span>
|
|
||||||
</label>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
class="input input-bordered w-12 p-1 h-10"
|
|
||||||
bind:value={outlineColor}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="input input-bordered w-full"
|
|
||||||
bind:value={outlineColor}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-action">
|
|
||||||
<button class="btn" on:click={onCancel}>Cancel</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-primary"
|
|
||||||
on:click={handleSave}
|
|
||||||
disabled={!name.trim()}
|
|
||||||
>
|
|
||||||
{#if mode === "create"}
|
|
||||||
Create
|
|
||||||
{:else}
|
|
||||||
Save
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<div class="h-full w-full bg-base-100 p-4 flex flex-col">
|
|
||||||
<h3 class="font-bold text-lg">
|
|
||||||
{#if mode === "create"}
|
|
||||||
Create New Doll
|
|
||||||
{:else}
|
|
||||||
Edit Doll
|
|
||||||
{/if}
|
|
||||||
</h3>
|
|
||||||
<div class="form-control w-full mt-4">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Name</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Doll Name"
|
|
||||||
class="input input-bordered w-full"
|
|
||||||
bind:value={name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-center mt-4">
|
|
||||||
<DollPreview {bodyColor} {outlineColor} />
|
|
||||||
</div>
|
|
||||||
<div class="form-control w-full mt-2">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Body Color</span>
|
|
||||||
</label>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
class="input input-bordered w-12 p-1 h-10"
|
|
||||||
bind:value={bodyColor}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="input input-bordered w-full"
|
|
||||||
bind:value={bodyColor}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-control w-full mt-2">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Outline Color</span>
|
|
||||||
</label>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
class="input input-bordered w-12 p-1 h-10"
|
|
||||||
bind:value={outlineColor}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="input input-bordered w-full"
|
|
||||||
bind:value={outlineColor}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mt-auto pt-4 flex justify-end gap-2">
|
|
||||||
<button class="btn" on:click={onCancel}>Cancel</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-primary"
|
|
||||||
on:click={handleSave}
|
|
||||||
disabled={!name.trim()}
|
|
||||||
>
|
|
||||||
{#if mode === "create"}
|
|
||||||
Create
|
|
||||||
{:else}
|
|
||||||
Save
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
@@ -15,46 +15,56 @@
|
|||||||
// We still keep the focus listener as a fallback, but the websocket events should handle most updates
|
// We still keep the focus listener as a fallback, but the websocket events should handle most updates
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
refreshDolls();
|
refreshDolls();
|
||||||
|
|
||||||
// Set up listeners
|
// Set up listeners
|
||||||
const unlistenCreated = listen("doll.created", (event) => {
|
const unlistenCreated = listen("doll_created", (event) => {
|
||||||
console.log("Received doll.created event", event);
|
console.log("Received doll_created event", event);
|
||||||
refreshDolls();
|
refreshDolls();
|
||||||
});
|
});
|
||||||
|
|
||||||
const unlistenUpdated = listen("doll.updated", (event) => {
|
const unlistenUpdated = listen("doll_updated", (event) => {
|
||||||
console.log("Received doll.updated event", event);
|
console.log("Received doll_updated event", event);
|
||||||
refreshDolls();
|
refreshDolls();
|
||||||
});
|
});
|
||||||
|
|
||||||
const unlistenDeleted = listen("doll.deleted", (event) => {
|
const unlistenDeleted = listen("doll_deleted", (event) => {
|
||||||
console.log("Received doll.deleted event", event);
|
console.log("Received doll_deleted event", event);
|
||||||
refreshDolls();
|
refreshDolls();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for focus events to refresh data when returning from editor window
|
|
||||||
window.addEventListener("focus", refreshDolls);
|
|
||||||
|
|
||||||
return async () => {
|
return async () => {
|
||||||
window.removeEventListener("focus", refreshDolls);
|
|
||||||
(await unlistenCreated)();
|
(await unlistenCreated)();
|
||||||
(await unlistenUpdated)();
|
(await unlistenUpdated)();
|
||||||
(await unlistenDeleted)();
|
(await unlistenDeleted)();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let isRefreshing = false;
|
||||||
|
let refreshQueued = false;
|
||||||
|
|
||||||
async function refreshDolls() {
|
async function refreshDolls() {
|
||||||
|
if (isRefreshing) {
|
||||||
|
refreshQueued = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRefreshing = true;
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
dolls = await invoke("get_dolls");
|
do {
|
||||||
// Use refresh_app_data to ensure we get the latest user state (including activeDollId)
|
refreshQueued = false;
|
||||||
// from the server, as the local state might be stale after updates.
|
try {
|
||||||
const appData: AppData = await invoke("refresh_app_data");
|
dolls = await invoke("get_dolls");
|
||||||
user = appData.user;
|
const appData: AppData = await invoke("refresh_app_data");
|
||||||
} catch (e) {
|
user = appData.user;
|
||||||
error = (e as Error)?.message ?? String(e);
|
} catch (e) {
|
||||||
|
error = (e as Error)?.message ?? String(e);
|
||||||
|
}
|
||||||
|
} while (refreshQueued);
|
||||||
} finally {
|
} finally {
|
||||||
loading = false;
|
loading = false;
|
||||||
|
isRefreshing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user