separate window for doll editor
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"],
|
"windows": ["main", "scene", "app_menu", "doll_editor"],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"core:default",
|
"core:default",
|
||||||
"opener:default",
|
"opener:default",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
remotes::user::UserRemote,
|
remotes::user::UserRemote,
|
||||||
services::cursor::start_cursor_tracking,
|
services::cursor::start_cursor_tracking,
|
||||||
|
services::doll_editor::open_doll_editor_window,
|
||||||
state::{init_app_data, FDOLL},
|
state::{init_app_data, FDOLL},
|
||||||
};
|
};
|
||||||
use tauri::async_runtime;
|
use tauri::async_runtime;
|
||||||
@@ -188,6 +189,14 @@ async fn get_dolls() -> Result<Vec<DollDto>, String> {
|
|||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn get_doll(id: String) -> Result<DollDto, String> {
|
||||||
|
DollsRemote::new()
|
||||||
|
.get_doll(&id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_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()
|
DollsRemote::new()
|
||||||
@@ -268,13 +277,15 @@ pub fn run() {
|
|||||||
deny_friend_request,
|
deny_friend_request,
|
||||||
unfriend,
|
unfriend,
|
||||||
get_dolls,
|
get_dolls,
|
||||||
|
get_doll,
|
||||||
create_doll,
|
create_doll,
|
||||||
update_doll,
|
update_doll,
|
||||||
delete_doll,
|
delete_doll,
|
||||||
set_active_doll,
|
set_active_doll,
|
||||||
remove_active_doll,
|
remove_active_doll,
|
||||||
recolor_gif_base64,
|
recolor_gif_base64,
|
||||||
quit_app
|
quit_app,
|
||||||
|
open_doll_editor_window
|
||||||
])
|
])
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
APP_HANDLE
|
APP_HANDLE
|
||||||
|
|||||||
@@ -105,6 +105,30 @@ impl DollsRemote {
|
|||||||
Ok(dolls)
|
Ok(dolls)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_doll(&self, id: &str) -> Result<DollDto, RemoteError> {
|
||||||
|
let url = format!("{}/dolls/{}", self.base_url, id);
|
||||||
|
tracing::info!("DollsRemote::get_doll - Sending GET request to URL: {}", url);
|
||||||
|
|
||||||
|
let resp = with_auth(self.client.get(url)).await.send().await?;
|
||||||
|
|
||||||
|
let resp = resp.error_for_status().map_err(|e| {
|
||||||
|
tracing::error!("DollsRemote::get_doll - HTTP error: {}", e);
|
||||||
|
e
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let text = resp.text().await.map_err(|e| {
|
||||||
|
tracing::error!("DollsRemote::get_doll - Failed to read response text: {}", e);
|
||||||
|
e
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let doll: DollDto = serde_json::from_str(&text).map_err(|e| {
|
||||||
|
tracing::error!("DollsRemote::get_doll - Failed to parse JSON: {}", e);
|
||||||
|
e
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(doll)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn create_doll(&self, dto: CreateDollDto) -> Result<DollDto, RemoteError> {
|
pub async fn create_doll(&self, dto: CreateDollDto) -> Result<DollDto, RemoteError> {
|
||||||
let url = format!("{}/dolls", self.base_url);
|
let url = format!("{}/dolls", self.base_url);
|
||||||
tracing::info!("DollsRemote::create_doll - Sending POST request to URL: {}", url);
|
tracing::info!("DollsRemote::create_doll - Sending POST request to URL: {}", url);
|
||||||
|
|||||||
85
src-tauri/src/services/doll_editor.rs
Normal file
85
src-tauri/src/services/doll_editor.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use tauri::{Emitter, Manager};
|
||||||
|
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]
|
||||||
|
pub async fn open_doll_editor_window(doll_id: Option<String>) {
|
||||||
|
let app_handle = get_app_handle().clone();
|
||||||
|
|
||||||
|
// Dispatch to main thread to avoid potential deadlocks on Windows when setting parent window
|
||||||
|
let _ = app_handle.run_on_main_thread(move || {
|
||||||
|
let app_handle = get_app_handle();
|
||||||
|
|
||||||
|
// Check if the window already exists
|
||||||
|
let existing_window = app_handle.get_webview_window(DOLL_EDITOR_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() {
|
||||||
|
error!("Failed to focus existing doll editor window: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit event to update context
|
||||||
|
if let Some(id) = doll_id {
|
||||||
|
if let Err(e) = window.emit("edit-doll", id) {
|
||||||
|
error!("Failed to emit edit-doll event: {}", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Err(e) = window.emit("create-doll", ()) {
|
||||||
|
error!("Failed to emit create-doll event: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let url_path = if let Some(id) = doll_id {
|
||||||
|
format!("/app-menu/tabs/your-dolls/doll-editor?id={}", id)
|
||||||
|
} else {
|
||||||
|
"/app-menu/tabs/your-dolls/doll-editor".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut builder = tauri::WebviewWindowBuilder::new(
|
||||||
|
app_handle,
|
||||||
|
DOLL_EDITOR_WINDOW_LABEL,
|
||||||
|
tauri::WebviewUrl::App(url_path.into()),
|
||||||
|
)
|
||||||
|
.title("Doll Editor")
|
||||||
|
.inner_size(300.0, 400.0)
|
||||||
|
.resizable(false)
|
||||||
|
.decorations(true)
|
||||||
|
.transparent(true)
|
||||||
|
.shadow(true)
|
||||||
|
.visible(true)
|
||||||
|
.skip_taskbar(false)
|
||||||
|
.always_on_top(true) // Helper window, nice to stay on top
|
||||||
|
.visible_on_all_workspaces(false);
|
||||||
|
|
||||||
|
// Set parent if app menu exists
|
||||||
|
if let Some(parent) = app_handle.get_webview_window(APP_MENU_WINDOW_LABEL) {
|
||||||
|
match builder.parent(&parent) {
|
||||||
|
Ok(b) => builder = b,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to set parent for doll editor window: {}", e);
|
||||||
|
// If we fail to set parent, we effectively lost the builder because .parent() consumes it.
|
||||||
|
// We must return here to avoid using moved value.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
match builder.build() {
|
||||||
|
Ok(window) => {
|
||||||
|
info!("{} window builder succeeded", DOLL_EDITOR_WINDOW_LABEL);
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
window.open_devtools();
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to build {} window: {}", DOLL_EDITOR_WINDOW_LABEL, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
pub mod app_menu;
|
pub mod app_menu;
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
|
pub mod doll_editor;
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
pub mod sprite_recolor;
|
pub mod sprite_recolor;
|
||||||
pub mod ws;
|
pub mod ws;
|
||||||
|
|||||||
@@ -39,6 +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_UPDATED: &str = "doll.updated";
|
||||||
|
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) {
|
||||||
@@ -184,6 +187,54 @@ fn on_friend_doll_deleted(payload: Payload, _socket: RawClient) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_doll_created(payload: Payload, _socket: RawClient) {
|
||||||
|
match payload {
|
||||||
|
Payload::Text(values) => {
|
||||||
|
if let Some(first_value) = values.first() {
|
||||||
|
info!("Received doll.created event: {:?}", first_value);
|
||||||
|
if let Err(e) = get_app_handle().emit(WS_EVENT::DOLL_CREATED, first_value) {
|
||||||
|
error!("Failed to emit doll.created event: {:?}", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("Received doll.created event with empty payload");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => error!("Received unexpected payload format for doll.created"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_doll_updated(payload: Payload, _socket: RawClient) {
|
||||||
|
match payload {
|
||||||
|
Payload::Text(values) => {
|
||||||
|
if let Some(first_value) = values.first() {
|
||||||
|
info!("Received doll.updated event: {:?}", first_value);
|
||||||
|
if let Err(e) = get_app_handle().emit(WS_EVENT::DOLL_UPDATED, first_value) {
|
||||||
|
error!("Failed to emit doll.updated event: {:?}", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("Received doll.updated event with empty payload");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => error!("Received unexpected payload format for doll.updated"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_doll_deleted(payload: Payload, _socket: RawClient) {
|
||||||
|
match payload {
|
||||||
|
Payload::Text(values) => {
|
||||||
|
if let Some(first_value) = values.first() {
|
||||||
|
info!("Received doll.deleted event: {:?}", first_value);
|
||||||
|
if let Err(e) = get_app_handle().emit(WS_EVENT::DOLL_DELETED, first_value) {
|
||||||
|
error!("Failed to emit doll.deleted event: {:?}", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("Received doll.deleted event with empty payload");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => error!("Received unexpected payload format for doll.deleted"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn report_cursor_data(cursor_position: CursorPosition) {
|
pub async fn report_cursor_data(cursor_position: CursorPosition) {
|
||||||
// Only attempt to get clients if lock_r succeeds (it should, but safety first)
|
// Only attempt to get clients if lock_r succeeds (it should, but safety first)
|
||||||
// and if clients are actually initialized.
|
// and if clients are actually initialized.
|
||||||
@@ -272,6 +323,9 @@ pub async fn build_ws_client(
|
|||||||
.on(WS_EVENT::FRIEND_DOLL_CREATED, on_friend_doll_created)
|
.on(WS_EVENT::FRIEND_DOLL_CREATED, on_friend_doll_created)
|
||||||
.on(WS_EVENT::FRIEND_DOLL_UPDATED, on_friend_doll_updated)
|
.on(WS_EVENT::FRIEND_DOLL_UPDATED, on_friend_doll_updated)
|
||||||
.on(WS_EVENT::FRIEND_DOLL_DELETED, on_friend_doll_deleted)
|
.on(WS_EVENT::FRIEND_DOLL_DELETED, on_friend_doll_deleted)
|
||||||
|
.on(WS_EVENT::DOLL_CREATED, on_doll_created)
|
||||||
|
.on(WS_EVENT::DOLL_UPDATED, on_doll_updated)
|
||||||
|
.on(WS_EVENT::DOLL_DELETED, on_doll_deleted)
|
||||||
.auth(json!({ "token": token }))
|
.auth(json!({ "token": token }))
|
||||||
.connect()
|
.connect()
|
||||||
})
|
})
|
||||||
|
|||||||
117
src/routes/app-menu/tabs/your-dolls/doll-editor-window.svelte
Normal file
117
src/routes/app-menu/tabs/your-dolls/doll-editor-window.svelte
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { page } from "$app/stores";
|
||||||
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||||
|
import DollEditor from "./doll-editor.svelte";
|
||||||
|
import type { DollDto } from "../../../../types/bindings/DollDto";
|
||||||
|
import type { CreateDollDto } from "../../../../types/bindings/CreateDollDto";
|
||||||
|
import type { UpdateDollDto } from "../../../../types/bindings/UpdateDollDto";
|
||||||
|
|
||||||
|
let mode: "create" | "edit" = "create";
|
||||||
|
let dollId: string | null = null;
|
||||||
|
let loading = true;
|
||||||
|
let error: string | null = null;
|
||||||
|
|
||||||
|
let initialName = "";
|
||||||
|
let initialBodyColor = "#FFFFFF";
|
||||||
|
let initialOutlineColor = "#000000";
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
// Check URL search params for ID
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const id = urlParams.get("id");
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
mode = "edit";
|
||||||
|
dollId = id;
|
||||||
|
await fetchDoll(id);
|
||||||
|
} else {
|
||||||
|
mode = "create";
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchDoll(id: string) {
|
||||||
|
loading = true;
|
||||||
|
try {
|
||||||
|
const doll: DollDto = await invoke("get_doll", { id });
|
||||||
|
initialName = doll.name;
|
||||||
|
initialBodyColor = doll.configuration.colorScheme.body;
|
||||||
|
initialOutlineColor = doll.configuration.colorScheme.outline;
|
||||||
|
} catch (e) {
|
||||||
|
error = (e as Error)?.message ?? String(e);
|
||||||
|
console.error("Failed to fetch doll:", e);
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSave(
|
||||||
|
name: string,
|
||||||
|
bodyColor: string,
|
||||||
|
outlineColor: string,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
if (mode === "create") {
|
||||||
|
const dto: CreateDollDto = {
|
||||||
|
name,
|
||||||
|
configuration: {
|
||||||
|
colorScheme: {
|
||||||
|
body: bodyColor,
|
||||||
|
outline: outlineColor,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await invoke("create_doll", { dto });
|
||||||
|
} else if (dollId) {
|
||||||
|
const dto: UpdateDollDto = {
|
||||||
|
name,
|
||||||
|
configuration: {
|
||||||
|
colorScheme: {
|
||||||
|
body: bodyColor,
|
||||||
|
outline: outlineColor,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await invoke("update_doll", { id: dollId, dto });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close window on success
|
||||||
|
await getCurrentWebviewWindow().close();
|
||||||
|
} catch (e) {
|
||||||
|
error = (e as Error)?.message ?? String(e);
|
||||||
|
console.error("Failed to save doll:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCancel() {
|
||||||
|
await getCurrentWebviewWindow().close();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="w-screen h-screen bg-base-100">
|
||||||
|
{#if loading}
|
||||||
|
<div class="flex h-full items-center justify-center">
|
||||||
|
<span class="loading loading-spinner loading-lg"></span>
|
||||||
|
</div>
|
||||||
|
{:else if error}
|
||||||
|
<div class="flex h-full flex-col items-center justify-center p-4">
|
||||||
|
<div class="alert alert-error">
|
||||||
|
<span>{error}</span>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-sm mt-4" on:click={handleCancel}>Close</button>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<DollEditor
|
||||||
|
isOpen={true}
|
||||||
|
standalone={true}
|
||||||
|
{mode}
|
||||||
|
{initialName}
|
||||||
|
{initialBodyColor}
|
||||||
|
{initialOutlineColor}
|
||||||
|
onSave={handleSave}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
outlineColor: string,
|
outlineColor: string,
|
||||||
) => void;
|
) => void;
|
||||||
export let onCancel: () => void;
|
export let onCancel: () => void;
|
||||||
|
export let standalone = false;
|
||||||
|
|
||||||
let name = initialName;
|
let name = initialName;
|
||||||
let bodyColor = initialBodyColor;
|
let bodyColor = initialBodyColor;
|
||||||
@@ -29,7 +30,8 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isOpen}
|
{#if !standalone}
|
||||||
|
{#if isOpen}
|
||||||
<div class="modal modal-open">
|
<div class="modal modal-open">
|
||||||
<div class="modal-box">
|
<div class="modal-box">
|
||||||
<h3 class="font-bold text-lg">
|
<h3 class="font-bold text-lg">
|
||||||
@@ -103,4 +105,77 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/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}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import DollEditorWindow from "../doll-editor-window.svelte";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DollEditorWindow />
|
||||||
@@ -1,27 +1,46 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
|
import { listen } from "@tauri-apps/api/event";
|
||||||
import type { DollDto } from "../../../../types/bindings/DollDto";
|
import type { DollDto } from "../../../../types/bindings/DollDto";
|
||||||
import type { UserProfile } from "../../../../types/bindings/UserProfile";
|
import type { UserProfile } from "../../../../types/bindings/UserProfile";
|
||||||
import type { AppData } from "../../../../types/bindings/AppData";
|
import type { AppData } from "../../../../types/bindings/AppData";
|
||||||
import type { CreateDollDto } from "../../../../types/bindings/CreateDollDto";
|
|
||||||
import type { UpdateDollDto } from "../../../../types/bindings/UpdateDollDto";
|
|
||||||
import DollsList from "./dolls-list.svelte";
|
import DollsList from "./dolls-list.svelte";
|
||||||
import DollEditor from "./doll-editor.svelte";
|
|
||||||
|
|
||||||
let dolls: DollDto[] = [];
|
let dolls: DollDto[] = [];
|
||||||
let user: UserProfile | null = null;
|
let user: UserProfile | null = null;
|
||||||
let loading = false;
|
let loading = false;
|
||||||
let error: string | null = null;
|
let error: string | null = null;
|
||||||
let isEditorOpen = false;
|
|
||||||
let editorMode: "create" | "edit" = "create";
|
|
||||||
let editingDollId: string | null = null;
|
|
||||||
let editorInitialName = "";
|
|
||||||
let editorInitialBodyColor = "#FFFFFF";
|
|
||||||
let editorInitialOutlineColor = "#000000";
|
|
||||||
|
|
||||||
|
// We still keep the focus listener as a fallback, but the websocket events should handle most updates
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
refreshDolls();
|
refreshDolls();
|
||||||
|
|
||||||
|
// Set up listeners
|
||||||
|
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 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)();
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
async function refreshDolls() {
|
async function refreshDolls() {
|
||||||
@@ -39,86 +58,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCreateModal() {
|
async function openCreateModal() {
|
||||||
editorMode = "create";
|
await invoke("open_doll_editor_window", { dollId: null });
|
||||||
editorInitialName = "";
|
|
||||||
editorInitialBodyColor = "#FFFFFF";
|
|
||||||
editorInitialOutlineColor = "#000000";
|
|
||||||
isEditorOpen = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEditModal(doll: DollDto) {
|
async function openEditModal(doll: DollDto) {
|
||||||
editorMode = "edit";
|
await invoke("open_doll_editor_window", { dollId: doll.id });
|
||||||
editingDollId = doll.id;
|
|
||||||
editorInitialName = doll.name;
|
|
||||||
editorInitialBodyColor = doll.configuration.colorScheme.body;
|
|
||||||
editorInitialOutlineColor = doll.configuration.colorScheme.outline;
|
|
||||||
isEditorOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeEditor() {
|
|
||||||
isEditorOpen = false;
|
|
||||||
editingDollId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleSave(
|
|
||||||
name: string,
|
|
||||||
bodyColor: string,
|
|
||||||
outlineColor: string,
|
|
||||||
) {
|
|
||||||
if (editorMode === "create") {
|
|
||||||
await handleCreateDoll(name, bodyColor, outlineColor);
|
|
||||||
} else {
|
|
||||||
await handleUpdateDoll(name, bodyColor, outlineColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleCreateDoll(
|
|
||||||
name: string,
|
|
||||||
bodyColor: string,
|
|
||||||
outlineColor: string,
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const dto: CreateDollDto = {
|
|
||||||
name,
|
|
||||||
configuration: {
|
|
||||||
colorScheme: {
|
|
||||||
body: bodyColor,
|
|
||||||
outline: outlineColor,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
await invoke("create_doll", { dto });
|
|
||||||
closeEditor();
|
|
||||||
await refreshDolls();
|
|
||||||
} catch (e) {
|
|
||||||
error = (e as Error)?.message ?? String(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleUpdateDoll(
|
|
||||||
name: string,
|
|
||||||
bodyColor: string,
|
|
||||||
outlineColor: string,
|
|
||||||
) {
|
|
||||||
if (!editingDollId) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const dto: UpdateDollDto = {
|
|
||||||
name,
|
|
||||||
configuration: {
|
|
||||||
colorScheme: {
|
|
||||||
body: bodyColor,
|
|
||||||
outline: outlineColor,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
await invoke("update_doll", { id: editingDollId, dto });
|
|
||||||
closeEditor();
|
|
||||||
await refreshDolls();
|
|
||||||
} catch (e) {
|
|
||||||
error = (e as Error)?.message ?? String(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSetActiveDoll(dollId: string) {
|
async function handleSetActiveDoll(dollId: string) {
|
||||||
@@ -157,14 +102,4 @@
|
|||||||
onSetActiveDoll={handleSetActiveDoll}
|
onSetActiveDoll={handleSetActiveDoll}
|
||||||
onRemoveActiveDoll={handleRemoveActiveDoll}
|
onRemoveActiveDoll={handleRemoveActiveDoll}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DollEditor
|
|
||||||
isOpen={isEditorOpen}
|
|
||||||
mode={editorMode}
|
|
||||||
initialName={editorInitialName}
|
|
||||||
initialBodyColor={editorInitialBodyColor}
|
|
||||||
initialOutlineColor={editorInitialOutlineColor}
|
|
||||||
onSave={handleSave}
|
|
||||||
onCancel={closeEditor}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user