From ceaa1257bf767dcd2c4d3cd2a89309f1511e7801 Mon Sep 17 00:00:00 2001 From: Wind-Explorer Date: Mon, 9 Mar 2026 19:34:57 +0800 Subject: [PATCH] move retrieval of sprite url from svelte to rust --- src-tauri/src/commands/app_state.rs | 26 ++++------------------ src-tauri/src/commands/mod.rs | 2 +- src-tauri/src/lib.rs | 19 ++++++++-------- src-tauri/src/services/app_events.rs | 4 ++++ src-tauri/src/services/mod.rs | 1 + src-tauri/src/services/sprite.rs | 33 ++++++++++++++++++++++++++++ src-tauri/src/state/ui.rs | 27 +++++++++++++++++++++-- src/events/active-doll-sprite.ts | 25 +++++++++++++++++++++ src/lib/bindings.ts | 7 ++++-- src/routes/+layout.svelte | 6 +++++ src/routes/scene/+page.svelte | 16 ++------------ 11 files changed, 116 insertions(+), 50 deletions(-) create mode 100644 src-tauri/src/services/sprite.rs create mode 100644 src/events/active-doll-sprite.ts diff --git a/src-tauri/src/commands/app_state.rs b/src-tauri/src/commands/app_state.rs index 56539e9..3466ec6 100644 --- a/src-tauri/src/commands/app_state.rs +++ b/src-tauri/src/commands/app_state.rs @@ -1,7 +1,7 @@ use crate::{ lock_r, - models::{app_data::UserData, dolls::DollColorSchemeDto}, - services::presence_modules::models::ModuleMetadata, + models::app_data::UserData, + services::{presence_modules::models::ModuleMetadata, sprite}, state::{init_app_data_scoped, AppDataRefreshScope, FDOLL}, }; @@ -29,24 +29,6 @@ pub fn get_modules() -> Result, String> { #[tauri::command] #[specta::specta] -pub fn get_active_doll_color_scheme() -> Result, String> { - let guard = lock_r!(FDOLL); - let active_doll_id = guard - .user_data - .user - .as_ref() - .and_then(|u| u.active_doll_id.as_deref()); - - match active_doll_id { - Some(active_doll_id) => { - let color_scheme = guard - .user_data - .dolls - .as_ref() - .and_then(|dolls| dolls.iter().find(|d| d.id == active_doll_id)) - .map(|d| d.configuration.color_scheme.clone()); - Ok(color_scheme) - } - None => Ok(None), - } +pub fn get_active_doll_sprite_base64() -> Result, String> { + sprite::get_active_doll_sprite_base64() } diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index 6cf9b25..32bad26 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -5,8 +5,8 @@ pub mod config; pub mod dolls; pub mod friends; pub mod interaction; -pub mod sprite; pub mod petpet; +pub mod sprite; use crate::lock_r; use crate::state::{init_app_data_scoped, AppDataRefreshScope, FDOLL}; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ee3596c..eaf24cb 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -6,7 +6,7 @@ use crate::{ }, }; use commands::app::{quit_app, restart_app, retry_connection}; -use commands::app_state::{get_app_data, get_active_doll_color_scheme, refresh_app_data}; +use commands::app_state::{get_active_doll_sprite_base64, get_app_data, refresh_app_data}; use commands::auth::{change_password, login, logout_and_restart, register, reset_password}; use commands::config::{get_client_config, open_client_config_manager, save_client_config}; use commands::dolls::{ @@ -17,18 +17,18 @@ use commands::friends::{ search_users, send_friend_request, sent_friend_requests, unfriend, }; use commands::interaction::send_interaction_cmd; -use commands::sprite::recolor_gif_base64; use commands::petpet::encode_pet_doll_gif_base64; +use commands::sprite::recolor_gif_base64; use specta_typescript::Typescript; use tauri::async_runtime; -use tauri_specta::{Builder as SpectaBuilder, ErrorHandlingMode, collect_commands, collect_events}; +use tauri_specta::{collect_commands, collect_events, Builder as SpectaBuilder, ErrorHandlingMode}; use crate::services::app_events::{ - AppDataRefreshed, CreateDoll, CursorMoved, EditDoll, FriendActiveDollChanged, - FriendCursorPositionsUpdated, FriendDisconnected, - FriendRequestAccepted, FriendRequestDenied, FriendRequestReceived, - FriendUserStatusChanged, InteractionDeliveryFailed, InteractionReceived, - SceneInteractiveChanged, SetInteractionOverlay, Unfriended, UserStatusChanged, + ActiveDollSpriteChanged, AppDataRefreshed, CreateDoll, CursorMoved, EditDoll, + FriendActiveDollChanged, FriendCursorPositionsUpdated, FriendDisconnected, + FriendRequestAccepted, FriendRequestDenied, FriendRequestReceived, FriendUserStatusChanged, + InteractionDeliveryFailed, InteractionReceived, SceneInteractiveChanged, SetInteractionOverlay, + Unfriended, UserStatusChanged, }; static APP_HANDLE: std::sync::OnceLock> = std::sync::OnceLock::new(); @@ -65,7 +65,7 @@ pub fn run() { .error_handling(ErrorHandlingMode::Throw) .commands(collect_commands![ get_app_data, - get_active_doll_color_scheme, + get_active_doll_sprite_base64, refresh_app_data, list_friends, search_users, @@ -106,6 +106,7 @@ pub fn run() { CursorMoved, SceneInteractiveChanged, AppDataRefreshed, + ActiveDollSpriteChanged, SetInteractionOverlay, EditDoll, CreateDoll, diff --git a/src-tauri/src/services/app_events.rs b/src-tauri/src/services/app_events.rs index 6903636..617ae68 100644 --- a/src-tauri/src/services/app_events.rs +++ b/src-tauri/src/services/app_events.rs @@ -30,6 +30,10 @@ pub struct SceneInteractiveChanged(pub bool); #[tauri_specta(event_name = "app-data-refreshed")] pub struct AppDataRefreshed(pub UserData); +#[derive(Debug, Clone, Serialize, Deserialize, Type, Event)] +#[tauri_specta(event_name = "active-doll-sprite-changed")] +pub struct ActiveDollSpriteChanged(pub Option); + #[derive(Debug, Clone, Serialize, Deserialize, Type, Event)] #[tauri_specta(event_name = "set-interaction-overlay")] pub struct SetInteractionOverlay(pub bool); diff --git a/src-tauri/src/services/mod.rs b/src-tauri/src/services/mod.rs index 7e6b30c..bb8e9ff 100644 --- a/src-tauri/src/services/mod.rs +++ b/src-tauri/src/services/mod.rs @@ -15,6 +15,7 @@ pub mod interaction; pub mod petpet; pub mod presence_modules; pub mod scene; +pub mod sprite; pub mod sprite_recolor; pub mod welcome; pub mod ws; diff --git a/src-tauri/src/services/sprite.rs b/src-tauri/src/services/sprite.rs new file mode 100644 index 0000000..c90c5cc --- /dev/null +++ b/src-tauri/src/services/sprite.rs @@ -0,0 +1,33 @@ +use crate::{lock_r, models::dolls::DollDto, state::FDOLL}; + +const APPLY_TEXTURE: bool = true; + +pub fn get_active_doll() -> Option { + let guard = lock_r!(FDOLL); + let active_doll_id = guard + .user_data + .user + .as_ref() + .and_then(|user| user.active_doll_id.as_deref())?; + + guard + .user_data + .dolls + .as_ref() + .and_then(|dolls| dolls.iter().find(|doll| doll.id == active_doll_id)) + .cloned() +} + +pub fn get_active_doll_sprite_base64() -> Result, String> { + get_active_doll() + .map(|doll| { + let color_scheme = doll.configuration.color_scheme; + super::sprite_recolor::recolor_gif_base64( + &color_scheme.body, + &color_scheme.outline, + APPLY_TEXTURE, + ) + .map_err(|err| err.to_string()) + }) + .transpose() +} diff --git a/src-tauri/src/state/ui.rs b/src-tauri/src/state/ui.rs index 264c42a..b2cb7c6 100644 --- a/src-tauri/src/state/ui.rs +++ b/src-tauri/src/state/ui.rs @@ -1,12 +1,15 @@ use crate::{ get_app_handle, lock_r, lock_w, remotes::{dolls::DollsRemote, friends::FriendRemote, user::UserRemote}, - services::{app_events::AppDataRefreshed, friend_cursor}, + services::{ + app_events::{ActiveDollSpriteChanged, AppDataRefreshed}, + friend_cursor, sprite, + }, state::FDOLL, }; use std::{collections::HashSet, sync::LazyLock}; -use tokio::sync::Mutex; use tauri_specta::Event as _; +use tokio::sync::Mutex; use tracing::{info, warn}; pub fn update_display_dimensions_for_scene_state() { @@ -227,6 +230,26 @@ pub async fn init_app_data_scoped(scope: AppDataRefreshScope) { .kind(MessageDialogKind::Error) .show(|_| {}); } + + if matches!( + scope, + AppDataRefreshScope::All + | AppDataRefreshScope::User + | AppDataRefreshScope::Dolls + ) { + match sprite::get_active_doll_sprite_base64() { + Ok(sprite_b64) => { + if let Err(e) = + ActiveDollSpriteChanged(sprite_b64).emit(get_app_handle()) + { + warn!("Failed to emit active-doll-sprite-changed event: {}", e); + } + } + Err(e) => { + warn!("Failed to generate active doll sprite: {}", e); + } + } + } } Ok(()) diff --git a/src/events/active-doll-sprite.ts b/src/events/active-doll-sprite.ts new file mode 100644 index 0000000..fa108ad --- /dev/null +++ b/src/events/active-doll-sprite.ts @@ -0,0 +1,25 @@ +import { writable } from "svelte/store"; +import onekoGif from "../assets/oneko/oneko.gif"; +import { commands, events } from "$lib/bindings"; +import { createEventSource } from "./listener-utils"; + +export const activeDollSpriteUrl = writable(onekoGif); + +function toSpriteUrl(spriteBase64: string | null): string { + return spriteBase64 ? `data:image/gif;base64,${spriteBase64}` : onekoGif; +} + +export const { + start: startActiveDollSprite, + stop: stopActiveDollSprite, +} = createEventSource(async (addEventListener) => { + activeDollSpriteUrl.set( + toSpriteUrl(await commands.getActiveDollSpriteBase64()), + ); + + addEventListener( + await events.activeDollSpriteChanged.listen((event) => { + activeDollSpriteUrl.set(toSpriteUrl(event.payload)); + }), + ); +}); diff --git a/src/lib/bindings.ts b/src/lib/bindings.ts index d5e2063..dc1f93a 100644 --- a/src/lib/bindings.ts +++ b/src/lib/bindings.ts @@ -8,8 +8,8 @@ export const commands = { async getAppData() : Promise { return await TAURI_INVOKE("get_app_data"); }, -async getActiveDollColorScheme() : Promise { - return await TAURI_INVOKE("get_active_doll_color_scheme"); +async getActiveDollSpriteBase64() : Promise { + return await TAURI_INVOKE("get_active_doll_sprite_base64"); }, async refreshAppData() : Promise { return await TAURI_INVOKE("refresh_app_data"); @@ -128,6 +128,7 @@ async getModules() : Promise { export const events = __makeEvents__<{ +activeDollSpriteChanged: ActiveDollSpriteChanged, appDataRefreshed: AppDataRefreshed, createDoll: CreateDoll, cursorMoved: CursorMoved, @@ -146,6 +147,7 @@ setInteractionOverlay: SetInteractionOverlay, unfriended: Unfriended, userStatusChanged: UserStatusChanged }>({ +activeDollSpriteChanged: "active-doll-sprite-changed", appDataRefreshed: "app-data-refreshed", createDoll: "create-doll", cursorMoved: "cursor-moved", @@ -171,6 +173,7 @@ userStatusChanged: "user-status-changed" /** user-defined types **/ +export type ActiveDollSpriteChanged = string | null export type AppConfig = { api_base_url: string | null } export type AppDataRefreshed = UserData export type CreateDoll = null diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index de48d52..b0d98aa 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -6,6 +6,10 @@ startFriendCursorTracking, stopFriendCursorTracking, } from "../events/friend-cursor"; + import { + startActiveDollSprite, + stopActiveDollSprite, + } from "../events/active-doll-sprite"; import { startAppData } from "../events/app-data"; import { startInteraction, stopInteraction } from "../events/interaction"; import { @@ -19,6 +23,7 @@ onMount(async () => { try { await startAppData(); + await startActiveDollSprite(); await startCursorTracking(); await startFriendCursorTracking(); await startSceneInteractive(); @@ -32,6 +37,7 @@ onDestroy(() => { stopCursorTracking(); stopFriendCursorTracking(); + stopActiveDollSprite(); stopSceneInteractive(); stopInteraction(); stopUserStatus(); diff --git a/src/routes/scene/+page.svelte b/src/routes/scene/+page.svelte index 925b249..8923026 100644 --- a/src/routes/scene/+page.svelte +++ b/src/routes/scene/+page.svelte @@ -2,27 +2,15 @@ import { cursorPositionOnScreen } from "../../events/cursor"; import { friendsCursorPositions } from "../../events/friend-cursor"; import { appData } from "../../events/app-data"; + import { activeDollSpriteUrl } from "../../events/active-doll-sprite"; import { sceneInteractive } from "../../events/scene-interactive"; import { friendsPresenceStates, currentPresenceState, } from "../../events/user-status"; import { commands } from "$lib/bindings"; - import { getSpriteSheetUrl } from "$lib/utils/sprite-utils"; import DebugBar from "./components/debug-bar.svelte"; import Neko from "./components/neko/neko.svelte"; - - let spriteUrl = $state(""); - - $effect(() => { - $appData; - if (!$appData) return; - commands.getActiveDollColorScheme().then((colorScheme) => { - getSpriteSheetUrl(colorScheme ?? undefined).then((url) => { - spriteUrl = url; - }); - }); - });
@@ -36,7 +24,7 @@