diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 899263a..c3d4047 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -4,6 +4,7 @@ use crate::{ FriendRemote, FriendRequestResponseDto, FriendshipResponseDto, SendFriendRequestDto, UserBasicDto, }, + remotes::dolls::{DollsRemote, CreateDollDto, UpdateDollDto, DollDto}, services::cursor::start_cursor_tracking, state::{init_app_data, FDOLL}, }; @@ -159,6 +160,38 @@ async fn unfriend(friend_id: String) -> Result<(), String> { .map_err(|e| e.to_string()) } +#[tauri::command] +async fn get_dolls() -> Result, String> { + DollsRemote::new() + .get_dolls() + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn create_doll(dto: CreateDollDto) -> Result { + DollsRemote::new() + .create_doll(dto) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn update_doll(id: String, dto: UpdateDollDto) -> Result { + DollsRemote::new() + .update_doll(&id, dto) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +async fn delete_doll(id: String) -> Result<(), String> { + DollsRemote::new() + .delete_doll(&id) + .await + .map_err(|e| e.to_string()) +} + #[tauri::command] fn quit_app() -> Result<(), String> { let app_handle = get_app_handle(); @@ -184,6 +217,10 @@ pub fn run() { accept_friend_request, deny_friend_request, unfriend, + get_dolls, + create_doll, + update_doll, + delete_doll, quit_app ]) .setup(|app| { diff --git a/src-tauri/src/remotes/dolls.rs b/src-tauri/src/remotes/dolls.rs new file mode 100644 index 0000000..2d673cf --- /dev/null +++ b/src-tauri/src/remotes/dolls.rs @@ -0,0 +1,177 @@ +use reqwest::Client; +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use ts_rs::TS; + +use crate::{lock_r, services::auth::with_auth, state::FDOLL}; + +#[derive(Error, Debug)] +pub enum RemoteError { + #[error("HTTP error: {0}")] + Http(#[from] reqwest::Error), + #[error("JSON parse error: {0}")] + Json(#[from] serde_json::Error), + #[error("{0}")] + Api(String), +} + +#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct DollColorSchemeDto { + pub outline: String, + pub body: String, +} + +#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct DollConfigurationDto { + pub color_scheme: DollColorSchemeDto, +} + +#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct CreateDollDto { + pub name: String, + pub configuration: Option, +} + +#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct UpdateDollDto { + pub name: Option, + pub configuration: Option, +} + +#[derive(Default, Serialize, Deserialize, Clone, Debug, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct DollDto { + pub id: String, + pub name: String, + pub configuration: DollConfigurationDto, + pub created_at: String, + pub updated_at: String, +} + +pub struct DollsRemote { + pub base_url: String, + pub client: Client, +} + +impl DollsRemote { + pub fn new() -> Self { + let guard = lock_r!(FDOLL); + Self { + base_url: guard + .app_config + .api_base_url + .as_ref() + .expect("App configuration error") + .clone(), + client: guard + .clients + .as_ref() + .expect("App configuration error") + .http_client + .clone(), + } + } + + pub async fn get_dolls(&self) -> Result, RemoteError> { + let url = format!("{}/dolls", self.base_url); + tracing::info!("DollsRemote::get_dolls - 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_dolls - HTTP error: {}", e); + e + })?; + + let text = resp.text().await.map_err(|e| { + tracing::error!("DollsRemote::get_dolls - Failed to read response text: {}", e); + e + })?; + + let dolls: Vec = serde_json::from_str(&text).map_err(|e| { + tracing::error!("DollsRemote::get_dolls - Failed to parse JSON: {}", e); + e + })?; + + tracing::info!("DollsRemote::get_dolls - Successfully parsed {} dolls", dolls.len()); + Ok(dolls) + } + + pub async fn create_doll(&self, dto: CreateDollDto) -> Result { + let url = format!("{}/dolls", self.base_url); + tracing::info!("DollsRemote::create_doll - Sending POST request to URL: {}", url); + + let resp = with_auth(self.client.post(url)) + .await + .json(&dto) + .send() + .await?; + + let resp = resp.error_for_status().map_err(|e| { + tracing::error!("DollsRemote::create_doll - HTTP error: {}", e); + e + })?; + + let text = resp.text().await.map_err(|e| { + tracing::error!("DollsRemote::create_doll - Failed to read response text: {}", e); + e + })?; + + let doll: DollDto = serde_json::from_str(&text).map_err(|e| { + tracing::error!("DollsRemote::create_doll - Failed to parse JSON: {}", e); + e + })?; + + Ok(doll) + } + + pub async fn update_doll(&self, id: &str, dto: UpdateDollDto) -> Result { + let url = format!("{}/dolls/{}", self.base_url, id); + tracing::info!("DollsRemote::update_doll - Sending PATCH request to URL: {}", url); + + let resp = with_auth(self.client.patch(url)) + .await + .json(&dto) + .send() + .await?; + + let resp = resp.error_for_status().map_err(|e| { + tracing::error!("DollsRemote::update_doll - HTTP error: {}", e); + e + })?; + + let text = resp.text().await.map_err(|e| { + tracing::error!("DollsRemote::update_doll - Failed to read response text: {}", e); + e + })?; + + let doll: DollDto = serde_json::from_str(&text).map_err(|e| { + tracing::error!("DollsRemote::update_doll - Failed to parse JSON: {}", e); + e + })?; + + Ok(doll) + } + + pub async fn delete_doll(&self, id: &str) -> Result<(), RemoteError> { + let url = format!("{}/dolls/{}", self.base_url, id); + tracing::info!("DollsRemote::delete_doll - Sending DELETE request to URL: {}", url); + + let resp = with_auth(self.client.delete(url)).await.send().await?; + + resp.error_for_status().map_err(|e| { + tracing::error!("DollsRemote::delete_doll - HTTP error: {}", e); + e + })?; + + Ok(()) + } +} diff --git a/src-tauri/src/remotes/mod.rs b/src-tauri/src/remotes/mod.rs index 1f94d0c..88ed21a 100644 --- a/src-tauri/src/remotes/mod.rs +++ b/src-tauri/src/remotes/mod.rs @@ -1,2 +1,3 @@ pub mod friends; pub mod user; +pub mod dolls; diff --git a/src/routes/app-menu/tabs/your-dolls.svelte b/src/routes/app-menu/tabs/your-dolls.svelte index dae3e82..0d94e9c 100644 --- a/src/routes/app-menu/tabs/your-dolls.svelte +++ b/src/routes/app-menu/tabs/your-dolls.svelte @@ -1,7 +1,304 @@ - -
-

{$appData?.user?.name}

+
+
+

Your Dolls

+ +
+ + {#if error} +
+ {error} + +
+ {/if} + + {#if loading} +
+ +
+ {:else if dolls.length === 0} +
+

No dolls found. Create your first doll!

+
+ {:else} +
+ {#each dolls as doll (doll.id)} +
+
+

{doll.name}

+
+
+
+ Body +
+
+
+ Outline +
+
+
+ + +
+
+
+ {/each} +
+ {/if} + + + {#if isCreateModalOpen} + + {/if} + + + {#if isEditModalOpen} + + {/if}
diff --git a/src/types/bindings/CreateDollDto.ts b/src/types/bindings/CreateDollDto.ts new file mode 100644 index 0000000..9631bdf --- /dev/null +++ b/src/types/bindings/CreateDollDto.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DollConfigurationDto } from "./DollConfigurationDto"; + +export type CreateDollDto = { name: string, configuration: DollConfigurationDto | null, }; diff --git a/src/types/bindings/DollColorSchemeDto.ts b/src/types/bindings/DollColorSchemeDto.ts new file mode 100644 index 0000000..e7e304c --- /dev/null +++ b/src/types/bindings/DollColorSchemeDto.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type DollColorSchemeDto = { outline: string, body: string, }; diff --git a/src/types/bindings/DollConfigurationDto.ts b/src/types/bindings/DollConfigurationDto.ts new file mode 100644 index 0000000..f93e2f1 --- /dev/null +++ b/src/types/bindings/DollConfigurationDto.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DollColorSchemeDto } from "./DollColorSchemeDto"; + +export type DollConfigurationDto = { colorScheme: DollColorSchemeDto, }; diff --git a/src/types/bindings/DollDto.ts b/src/types/bindings/DollDto.ts new file mode 100644 index 0000000..c96e617 --- /dev/null +++ b/src/types/bindings/DollDto.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DollConfigurationDto } from "./DollConfigurationDto"; + +export type DollDto = { id: string, name: string, configuration: DollConfigurationDto, createdAt: string, updatedAt: string, }; diff --git a/src/types/bindings/UpdateDollDto.ts b/src/types/bindings/UpdateDollDto.ts new file mode 100644 index 0000000..dcdc2d2 --- /dev/null +++ b/src/types/bindings/UpdateDollDto.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { DollConfigurationDto } from "./DollConfigurationDto"; + +export type UpdateDollDto = { name: string | null, configuration: DollConfigurationDto | null, };