Improved tauri events type safety
This commit is contained in:
@@ -41,4 +41,4 @@ pub struct DollDto {
|
|||||||
pub configuration: DollConfigurationDto,
|
pub configuration: DollConfigurationDto,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
pub updated_at: String,
|
pub updated_at: String,
|
||||||
}
|
}
|
||||||
|
|||||||
79
src-tauri/src/models/event_payloads.rs
Normal file
79
src-tauri/src/models/event_payloads.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
use super::dolls::DollDto;
|
||||||
|
use super::friends::UserBasicDto;
|
||||||
|
use crate::services::presence_modules::models::PresenceStatus;
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, TS)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
#[ts(export)]
|
||||||
|
pub enum UserStatusState {
|
||||||
|
Idle,
|
||||||
|
Resting,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct UserStatusPayload {
|
||||||
|
pub presence_status: PresenceStatus,
|
||||||
|
pub state: UserStatusState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct FriendUserStatusPayload {
|
||||||
|
pub user_id: String,
|
||||||
|
pub status: UserStatusPayload,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct FriendDisconnectedPayload {
|
||||||
|
pub user_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct FriendActiveDollChangedPayload {
|
||||||
|
pub friend_id: String,
|
||||||
|
pub doll: Option<DollDto>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct FriendRequestReceivedPayload {
|
||||||
|
pub id: String,
|
||||||
|
pub sender: UserBasicDto,
|
||||||
|
pub created_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct FriendRequestAcceptedPayload {
|
||||||
|
pub id: String,
|
||||||
|
pub friend: UserBasicDto,
|
||||||
|
pub accepted_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct FriendRequestDeniedPayload {
|
||||||
|
pub id: String,
|
||||||
|
pub denier: UserBasicDto,
|
||||||
|
pub denied_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize, Debug, TS)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct UnfriendedPayload {
|
||||||
|
pub friend_id: String,
|
||||||
|
}
|
||||||
@@ -25,4 +25,4 @@ pub enum HealthError {
|
|||||||
NonOkStatus(String),
|
NonOkStatus(String),
|
||||||
#[error("health response decode failed: {0}")]
|
#[error("health response decode failed: {0}")]
|
||||||
Decode(reqwest::Error),
|
Decode(reqwest::Error),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
pub mod app_data;
|
pub mod app_data;
|
||||||
pub mod remote_error;
|
|
||||||
pub mod dolls;
|
pub mod dolls;
|
||||||
|
pub mod event_payloads;
|
||||||
pub mod friends;
|
pub mod friends;
|
||||||
pub mod health;
|
pub mod health;
|
||||||
pub mod interaction;
|
pub mod interaction;
|
||||||
|
pub mod remote_error;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ use std::{path::Path, thread, time::Duration};
|
|||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
use crate::services::ws::user_status::{report_user_status, UserStatusPayload};
|
use crate::models::event_payloads::{UserStatusPayload, UserStatusState};
|
||||||
|
use crate::services::ws::user_status::report_user_status;
|
||||||
use crate::services::ws::{ws_emit_soft, WS_EVENT};
|
use crate::services::ws::{ws_emit_soft, WS_EVENT};
|
||||||
|
|
||||||
use super::models::PresenceStatus;
|
use super::models::PresenceStatus;
|
||||||
@@ -45,7 +46,7 @@ impl UserData for Engine {
|
|||||||
async fn update_status(status: PresenceStatus) {
|
async fn update_status(status: PresenceStatus) {
|
||||||
let user_status = UserStatusPayload {
|
let user_status = UserStatusPayload {
|
||||||
presence_status: status,
|
presence_status: status,
|
||||||
state: String::from("idle"),
|
state: UserStatusState::Idle,
|
||||||
};
|
};
|
||||||
report_user_status(user_status).await;
|
report_user_status(user_status).await;
|
||||||
}
|
}
|
||||||
@@ -53,7 +54,7 @@ async fn update_status(status: PresenceStatus) {
|
|||||||
async fn update_status_async(status: PresenceStatus) {
|
async fn update_status_async(status: PresenceStatus) {
|
||||||
let payload = UserStatusPayload {
|
let payload = UserStatusPayload {
|
||||||
presence_status: status,
|
presence_status: status,
|
||||||
state: String::from("idle"),
|
state: UserStatusState::Idle,
|
||||||
};
|
};
|
||||||
if let Err(e) = ws_emit_soft(WS_EVENT::CLIENT_REPORT_USER_STATUS, payload).await {
|
if let Err(e) = ws_emit_soft(WS_EVENT::CLIENT_REPORT_USER_STATUS, payload).await {
|
||||||
warn!("User status report failed: {}", e);
|
warn!("User status report failed: {}", e);
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
use rust_socketio::{Payload, RawClient};
|
use rust_socketio::{Payload, RawClient};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
|
use crate::models::event_payloads::{
|
||||||
|
FriendActiveDollChangedPayload, FriendDisconnectedPayload, FriendRequestAcceptedPayload,
|
||||||
|
FriendRequestDeniedPayload, FriendRequestReceivedPayload, FriendUserStatusPayload,
|
||||||
|
UnfriendedPayload,
|
||||||
|
};
|
||||||
use crate::services::app_events::AppEvents;
|
use crate::services::app_events::AppEvents;
|
||||||
use crate::services::cursor::{normalized_to_absolute, CursorPositions};
|
use crate::services::cursor::{normalized_to_absolute, CursorPositions};
|
||||||
use crate::state::AppDataRefreshScope;
|
use crate::state::AppDataRefreshScope;
|
||||||
@@ -13,30 +18,36 @@ use super::{
|
|||||||
|
|
||||||
/// Handler for friend-request-received event
|
/// Handler for friend-request-received event
|
||||||
pub fn on_friend_request_received(payload: Payload, _socket: RawClient) {
|
pub fn on_friend_request_received(payload: Payload, _socket: RawClient) {
|
||||||
if let Ok(value) = utils::extract_text_value(payload, "friend-request-received") {
|
if let Ok(data) =
|
||||||
emitter::emit_to_frontend(AppEvents::FriendRequestReceived.as_str(), value);
|
utils::extract_and_parse::<FriendRequestReceivedPayload>(payload, "friend-request-received")
|
||||||
|
{
|
||||||
|
emitter::emit_to_frontend(AppEvents::FriendRequestReceived.as_str(), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for friend-request-accepted event
|
/// Handler for friend-request-accepted event
|
||||||
pub fn on_friend_request_accepted(payload: Payload, _socket: RawClient) {
|
pub fn on_friend_request_accepted(payload: Payload, _socket: RawClient) {
|
||||||
if let Ok(value) = utils::extract_text_value(payload, "friend-request-accepted") {
|
if let Ok(data) =
|
||||||
emitter::emit_to_frontend(AppEvents::FriendRequestAccepted.as_str(), value);
|
utils::extract_and_parse::<FriendRequestAcceptedPayload>(payload, "friend-request-accepted")
|
||||||
|
{
|
||||||
|
emitter::emit_to_frontend(AppEvents::FriendRequestAccepted.as_str(), data);
|
||||||
refresh::refresh_app_data(AppDataRefreshScope::Friends);
|
refresh::refresh_app_data(AppDataRefreshScope::Friends);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for friend-request-denied event
|
/// Handler for friend-request-denied event
|
||||||
pub fn on_friend_request_denied(payload: Payload, _socket: RawClient) {
|
pub fn on_friend_request_denied(payload: Payload, _socket: RawClient) {
|
||||||
if let Ok(value) = utils::extract_text_value(payload, "friend-request-denied") {
|
if let Ok(data) =
|
||||||
emitter::emit_to_frontend(AppEvents::FriendRequestDenied.as_str(), value);
|
utils::extract_and_parse::<FriendRequestDeniedPayload>(payload, "friend-request-denied")
|
||||||
|
{
|
||||||
|
emitter::emit_to_frontend(AppEvents::FriendRequestDenied.as_str(), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for unfriended event
|
/// Handler for unfriended event
|
||||||
pub fn on_unfriended(payload: Payload, _socket: RawClient) {
|
pub fn on_unfriended(payload: Payload, _socket: RawClient) {
|
||||||
if let Ok(value) = utils::extract_text_value(payload, "unfriended") {
|
if let Ok(data) = utils::extract_and_parse::<UnfriendedPayload>(payload, "unfriended") {
|
||||||
emitter::emit_to_frontend(AppEvents::Unfriended.as_str(), value);
|
emitter::emit_to_frontend(AppEvents::Unfriended.as_str(), data);
|
||||||
refresh::refresh_app_data(AppDataRefreshScope::Friends);
|
refresh::refresh_app_data(AppDataRefreshScope::Friends);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,8 +74,10 @@ pub fn on_friend_cursor_position(payload: Payload, _socket: RawClient) {
|
|||||||
|
|
||||||
/// Handler for friend-disconnected event
|
/// Handler for friend-disconnected event
|
||||||
pub fn on_friend_disconnected(payload: Payload, _socket: RawClient) {
|
pub fn on_friend_disconnected(payload: Payload, _socket: RawClient) {
|
||||||
if let Ok(value) = utils::extract_text_value(payload, "friend-disconnected") {
|
if let Ok(data) =
|
||||||
emitter::emit_to_frontend(AppEvents::FriendDisconnected.as_str(), value);
|
utils::extract_and_parse::<FriendDisconnectedPayload>(payload, "friend-disconnected")
|
||||||
|
{
|
||||||
|
emitter::emit_to_frontend(AppEvents::FriendDisconnected.as_str(), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,15 +106,20 @@ fn handle_friend_doll_change(event_name: &str, payload: Payload) {
|
|||||||
|
|
||||||
/// Handler for friend-active-doll-changed event
|
/// Handler for friend-active-doll-changed event
|
||||||
pub fn on_friend_active_doll_changed(payload: Payload, _socket: RawClient) {
|
pub fn on_friend_active_doll_changed(payload: Payload, _socket: RawClient) {
|
||||||
if let Ok(value) = utils::extract_text_value(payload, "friend-active-doll-changed") {
|
if let Ok(data) = utils::extract_and_parse::<FriendActiveDollChangedPayload>(
|
||||||
emitter::emit_to_frontend(AppEvents::FriendActiveDollChanged.as_str(), value);
|
payload,
|
||||||
|
"friend-active-doll-changed",
|
||||||
|
) {
|
||||||
|
emitter::emit_to_frontend(AppEvents::FriendActiveDollChanged.as_str(), data);
|
||||||
refresh::refresh_app_data(AppDataRefreshScope::Friends);
|
refresh::refresh_app_data(AppDataRefreshScope::Friends);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handler for friend-user-status event
|
/// Handler for friend-user-status event
|
||||||
pub fn on_friend_user_status(payload: Payload, _socket: RawClient) {
|
pub fn on_friend_user_status(payload: Payload, _socket: RawClient) {
|
||||||
if let Ok(value) = utils::extract_text_value(payload, "friend-user-status") {
|
if let Ok(data) =
|
||||||
emitter::emit_to_frontend(AppEvents::FriendUserStatus.as_str(), value);
|
utils::extract_and_parse::<FriendUserStatusPayload>(payload, "friend-user-status")
|
||||||
|
{
|
||||||
|
emitter::emit_to_frontend(AppEvents::FriendUserStatus.as_str(), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,15 @@
|
|||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use serde::Serialize;
|
|
||||||
use tauri::async_runtime::{self, JoinHandle};
|
use tauri::async_runtime::{self, JoinHandle};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio::time::Duration;
|
use tokio::time::Duration;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::services::presence_modules::models::PresenceStatus;
|
use crate::models::event_payloads::UserStatusPayload;
|
||||||
|
|
||||||
use crate::services::app_events::AppEvents;
|
use crate::services::app_events::AppEvents;
|
||||||
|
|
||||||
use super::{emitter, types::WS_EVENT};
|
use super::{emitter, types::WS_EVENT};
|
||||||
|
|
||||||
/// User status payload sent to WebSocket server
|
|
||||||
#[derive(Clone, Serialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct UserStatusPayload {
|
|
||||||
pub presence_status: PresenceStatus,
|
|
||||||
pub state: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Debouncer for user status reports
|
/// Debouncer for user status reports
|
||||||
static USER_STATUS_REPORT_DEBOUNCE: Lazy<Mutex<Option<JoinHandle<()>>>> =
|
static USER_STATUS_REPORT_DEBOUNCE: Lazy<Mutex<Option<JoinHandle<()>>>> =
|
||||||
Lazy::new(|| Mutex::new(None));
|
Lazy::new(|| Mutex::new(None));
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import { listen } from "@tauri-apps/api/event";
|
|||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
import type { CursorPositions } from "../types/bindings/CursorPositions";
|
import type { CursorPositions } from "../types/bindings/CursorPositions";
|
||||||
import type { DollDto } from "../types/bindings/DollDto";
|
import type { DollDto } from "../types/bindings/DollDto";
|
||||||
|
import type { FriendDisconnectedPayload } from "../types/bindings/FriendDisconnectedPayload";
|
||||||
|
import type { FriendActiveDollChangedPayload } from "../types/bindings/FriendActiveDollChangedPayload";
|
||||||
import { AppEvents } from "../types/bindings/AppEventsConstants";
|
import { AppEvents } from "../types/bindings/AppEventsConstants";
|
||||||
import {
|
import {
|
||||||
createMultiListenerSubscription,
|
createMultiListenerSubscription,
|
||||||
parseEventPayload,
|
|
||||||
removeFromStore,
|
removeFromStore,
|
||||||
setupHmrCleanup,
|
setupHmrCleanup,
|
||||||
} from "./listener-utils";
|
} from "./listener-utils";
|
||||||
@@ -59,63 +60,51 @@ export async function startFriendCursorTracking() {
|
|||||||
);
|
);
|
||||||
subscription.addUnlisten(unlistenFriendCursor);
|
subscription.addUnlisten(unlistenFriendCursor);
|
||||||
|
|
||||||
const unlistenFriendDisconnected = await listen<
|
const unlistenFriendDisconnected = await listen<FriendDisconnectedPayload>(
|
||||||
[{ userId: string }] | { userId: string } | string
|
AppEvents.FriendDisconnected,
|
||||||
>(AppEvents.FriendDisconnected, (event) => {
|
(event) => {
|
||||||
const payload = parseEventPayload<
|
const data = event.payload;
|
||||||
[{ userId: string }] | { userId: string }
|
|
||||||
>(event.payload, AppEvents.FriendDisconnected);
|
|
||||||
if (!payload) return;
|
|
||||||
|
|
||||||
const data = Array.isArray(payload) ? payload[0] : payload;
|
if (friendCursorState[data.userId]) {
|
||||||
|
delete friendCursorState[data.userId];
|
||||||
|
}
|
||||||
|
|
||||||
if (friendCursorState[data.userId]) {
|
friendsCursorPositions.update((current) =>
|
||||||
delete friendCursorState[data.userId];
|
removeFromStore(current, data.userId),
|
||||||
}
|
);
|
||||||
|
},
|
||||||
friendsCursorPositions.update((current) =>
|
|
||||||
removeFromStore(current, data.userId),
|
|
||||||
);
|
);
|
||||||
});
|
subscription.addUnlisten(unlistenFriendDisconnected);
|
||||||
subscription.addUnlisten(unlistenFriendDisconnected);
|
|
||||||
|
|
||||||
const unlistenFriendActiveDollChanged = await listen<
|
const unlistenFriendActiveDollChanged =
|
||||||
| string
|
await listen<FriendActiveDollChangedPayload>(
|
||||||
| {
|
AppEvents.FriendActiveDollChanged,
|
||||||
friendId: string;
|
(event) => {
|
||||||
doll: DollDto | null;
|
const payload = event.payload;
|
||||||
}
|
|
||||||
>(AppEvents.FriendActiveDollChanged, (event) => {
|
|
||||||
const data = parseEventPayload<{
|
|
||||||
friendId: string;
|
|
||||||
doll: DollDto | null;
|
|
||||||
}>(event.payload, AppEvents.FriendActiveDollChanged);
|
|
||||||
if (!data) return;
|
|
||||||
|
|
||||||
const payload = data as { friendId: string; doll: DollDto | null };
|
if (!payload.doll) {
|
||||||
|
friendsActiveDolls.update((current) => {
|
||||||
|
const next = { ...current };
|
||||||
|
next[payload.friendId] = null;
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
|
||||||
if (!payload.doll) {
|
friendsCursorPositions.update((current) =>
|
||||||
friendsActiveDolls.update((current) => {
|
removeFromStore(current, payload.friendId),
|
||||||
const next = { ...current };
|
);
|
||||||
next[payload.friendId] = null;
|
} else {
|
||||||
return next;
|
friendsActiveDolls.update((current) => {
|
||||||
});
|
return {
|
||||||
|
...current,
|
||||||
friendsCursorPositions.update((current) =>
|
[payload.friendId]: payload.doll,
|
||||||
removeFromStore(current, payload.friendId),
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
} else {
|
subscription.addUnlisten(unlistenFriendActiveDollChanged);
|
||||||
friendsActiveDolls.update((current) => {
|
|
||||||
return {
|
|
||||||
...current,
|
|
||||||
[payload.friendId]: payload.doll!,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
subscription.addUnlisten(unlistenFriendActiveDollChanged);
|
|
||||||
|
|
||||||
subscription.setListening(true);
|
subscription.setListening(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to initialize friend cursor tracking:", err);
|
console.error("Failed to initialize friend cursor tracking:", err);
|
||||||
throw err;
|
throw err;
|
||||||
|
|||||||
@@ -74,22 +74,6 @@ export function setupHmrCleanup(cleanup: () => void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseEventPayload<T>(
|
|
||||||
payload: unknown,
|
|
||||||
errorLabel: string,
|
|
||||||
): T | null {
|
|
||||||
if (typeof payload === "string") {
|
|
||||||
try {
|
|
||||||
return JSON.parse(payload) as T;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to parse ${errorLabel} payload`, error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return payload as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeFromStore<T>(
|
export function removeFromStore<T>(
|
||||||
current: Record<string, T>,
|
current: Record<string, T>,
|
||||||
key: string,
|
key: string,
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
import type { PresenceStatus } from "../types/bindings/PresenceStatus";
|
import type { FriendDisconnectedPayload } from "../types/bindings/FriendDisconnectedPayload";
|
||||||
|
import type { FriendUserStatusPayload } from "../types/bindings/FriendUserStatusPayload";
|
||||||
|
import type { UserStatusPayload } from "../types/bindings/UserStatusPayload";
|
||||||
import { AppEvents } from "../types/bindings/AppEventsConstants";
|
import { AppEvents } from "../types/bindings/AppEventsConstants";
|
||||||
import {
|
import {
|
||||||
createMultiListenerSubscription,
|
createMultiListenerSubscription,
|
||||||
parseEventPayload,
|
|
||||||
removeFromStore,
|
removeFromStore,
|
||||||
setupHmrCleanup,
|
setupHmrCleanup,
|
||||||
} from "./listener-utils";
|
} from "./listener-utils";
|
||||||
|
|
||||||
export type PresenceState = {
|
export const friendsPresenceStates = writable<
|
||||||
presenceStatus: PresenceStatus;
|
Record<string, UserStatusPayload>
|
||||||
state: "idle" | "resting";
|
>({});
|
||||||
};
|
export const currentPresenceState = writable<UserStatusPayload | null>(null);
|
||||||
|
|
||||||
export const friendsPresenceStates = writable<Record<string, PresenceState>>({});
|
|
||||||
export const currentPresenceState = writable<PresenceState | null>(null);
|
|
||||||
|
|
||||||
const subscription = createMultiListenerSubscription();
|
const subscription = createMultiListenerSubscription();
|
||||||
|
|
||||||
@@ -26,22 +24,11 @@ export async function startUserStatus() {
|
|||||||
if (subscription.isListening()) return;
|
if (subscription.isListening()) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const unlistenStatus = await listen<unknown>(
|
const unlistenStatus = await listen<FriendUserStatusPayload>(
|
||||||
AppEvents.FriendUserStatus,
|
AppEvents.FriendUserStatus,
|
||||||
(event) => {
|
(event) => {
|
||||||
const payload = parseEventPayload<{
|
const { userId, status } = event.payload;
|
||||||
userId?: string;
|
|
||||||
status?: PresenceState;
|
|
||||||
}>(event.payload, AppEvents.FriendUserStatus);
|
|
||||||
if (!payload) return;
|
|
||||||
|
|
||||||
const userId = payload.userId;
|
|
||||||
const status = payload.status;
|
|
||||||
|
|
||||||
if (!userId || !status) return;
|
|
||||||
if (!status.presenceStatus) return;
|
|
||||||
|
|
||||||
// Validate that appMetadata has at least one valid name
|
|
||||||
const hasValidName =
|
const hasValidName =
|
||||||
(typeof status.presenceStatus.title === "string" &&
|
(typeof status.presenceStatus.title === "string" &&
|
||||||
status.presenceStatus.title.trim() !== "") ||
|
status.presenceStatus.title.trim() !== "") ||
|
||||||
@@ -49,20 +36,15 @@ export async function startUserStatus() {
|
|||||||
status.presenceStatus.subtitle.trim() !== "");
|
status.presenceStatus.subtitle.trim() !== "");
|
||||||
if (!hasValidName) return;
|
if (!hasValidName) return;
|
||||||
|
|
||||||
if (status.state !== "idle" && status.state !== "resting") return;
|
|
||||||
|
|
||||||
friendsPresenceStates.update((current) => ({
|
friendsPresenceStates.update((current) => ({
|
||||||
...current,
|
...current,
|
||||||
[userId]: {
|
[userId]: status,
|
||||||
presenceStatus: status.presenceStatus,
|
|
||||||
state: status.state,
|
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
subscription.addUnlisten(unlistenStatus);
|
subscription.addUnlisten(unlistenStatus);
|
||||||
|
|
||||||
const unlistenUserStatusChanged = await listen<PresenceState>(
|
const unlistenUserStatusChanged = await listen<UserStatusPayload>(
|
||||||
AppEvents.UserStatusChanged,
|
AppEvents.UserStatusChanged,
|
||||||
(event) => {
|
(event) => {
|
||||||
currentPresenceState.set(event.payload);
|
currentPresenceState.set(event.payload);
|
||||||
@@ -70,20 +52,15 @@ export async function startUserStatus() {
|
|||||||
);
|
);
|
||||||
subscription.addUnlisten(unlistenUserStatusChanged);
|
subscription.addUnlisten(unlistenUserStatusChanged);
|
||||||
|
|
||||||
const unlistenFriendDisconnected = await listen<
|
const unlistenFriendDisconnected = await listen<FriendDisconnectedPayload>(
|
||||||
[{ userId: string }] | { userId: string } | string
|
AppEvents.FriendDisconnected,
|
||||||
>(AppEvents.FriendDisconnected, (event) => {
|
(event) => {
|
||||||
const payload = parseEventPayload<
|
const { userId } = event.payload;
|
||||||
[{ userId: string }] | { userId: string }
|
friendsPresenceStates.update((current) =>
|
||||||
>(event.payload, AppEvents.FriendDisconnected);
|
removeFromStore(current, userId),
|
||||||
if (!payload) return;
|
);
|
||||||
|
},
|
||||||
const data = Array.isArray(payload) ? payload[0] : payload;
|
);
|
||||||
const userId = data?.userId as string | undefined;
|
|
||||||
if (!userId) return;
|
|
||||||
|
|
||||||
friendsPresenceStates.update((current) => removeFromStore(current, userId));
|
|
||||||
});
|
|
||||||
subscription.addUnlisten(unlistenFriendDisconnected);
|
subscription.addUnlisten(unlistenFriendDisconnected);
|
||||||
|
|
||||||
subscription.setListening(true);
|
subscription.setListening(true);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PresenceStatus } from "../../../types/bindings/PresenceStatus";
|
import type { PresenceStatus } from "../../../types/bindings/PresenceStatus";
|
||||||
import type { PresenceState } from "../../../events/user-status";
|
import type { UserStatusPayload } from "../../../types/bindings/UserStatusPayload";
|
||||||
|
|
||||||
interface Friend {
|
interface Friend {
|
||||||
friend?: {
|
friend?: {
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
presenceStatus: PresenceStatus | null;
|
presenceStatus: PresenceStatus | null;
|
||||||
friendsCursorPositions: Record<string, { mapped: { x: number; y: number } }>;
|
friendsCursorPositions: Record<string, { mapped: { x: number; y: number } }>;
|
||||||
friends: Friend[];
|
friends: Friend[];
|
||||||
friendsPresenceStates: Record<string, PresenceState>;
|
friendsPresenceStates: Record<string, UserStatusPayload>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
|||||||
4
src/types/bindings/FriendActiveDollChangedPayload.ts
Normal file
4
src/types/bindings/FriendActiveDollChangedPayload.ts
Normal file
@@ -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 { DollDto } from "./DollDto";
|
||||||
|
|
||||||
|
export type FriendActiveDollChangedPayload = { friendId: string, doll: DollDto | null, };
|
||||||
3
src/types/bindings/FriendDisconnectedPayload.ts
Normal file
3
src/types/bindings/FriendDisconnectedPayload.ts
Normal file
@@ -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 FriendDisconnectedPayload = { userId: string, };
|
||||||
4
src/types/bindings/FriendRequestAcceptedPayload.ts
Normal file
4
src/types/bindings/FriendRequestAcceptedPayload.ts
Normal file
@@ -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 { UserBasicDto } from "./UserBasicDto";
|
||||||
|
|
||||||
|
export type FriendRequestAcceptedPayload = { id: string, friend: UserBasicDto, acceptedAt: string, };
|
||||||
4
src/types/bindings/FriendRequestDeniedPayload.ts
Normal file
4
src/types/bindings/FriendRequestDeniedPayload.ts
Normal file
@@ -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 { UserBasicDto } from "./UserBasicDto";
|
||||||
|
|
||||||
|
export type FriendRequestDeniedPayload = { id: string, denier: UserBasicDto, deniedAt: string, };
|
||||||
4
src/types/bindings/FriendRequestReceivedPayload.ts
Normal file
4
src/types/bindings/FriendRequestReceivedPayload.ts
Normal file
@@ -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 { UserBasicDto } from "./UserBasicDto";
|
||||||
|
|
||||||
|
export type FriendRequestReceivedPayload = { id: string, sender: UserBasicDto, createdAt: string, };
|
||||||
4
src/types/bindings/FriendUserStatusPayload.ts
Normal file
4
src/types/bindings/FriendUserStatusPayload.ts
Normal file
@@ -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 { UserStatusPayload } from "./UserStatusPayload";
|
||||||
|
|
||||||
|
export type FriendUserStatusPayload = { userId: string, status: UserStatusPayload, };
|
||||||
3
src/types/bindings/UnfriendedPayload.ts
Normal file
3
src/types/bindings/UnfriendedPayload.ts
Normal file
@@ -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 UnfriendedPayload = { friendId: string, };
|
||||||
5
src/types/bindings/UserStatusPayload.ts
Normal file
5
src/types/bindings/UserStatusPayload.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { PresenceStatus } from "./PresenceStatus";
|
||||||
|
import type { UserStatusState } from "./UserStatusState";
|
||||||
|
|
||||||
|
export type UserStatusPayload = { presenceStatus: PresenceStatus, state: UserStatusState, };
|
||||||
3
src/types/bindings/UserStatusState.ts
Normal file
3
src/types/bindings/UserStatusState.ts
Normal file
@@ -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 UserStatusState = "idle" | "resting";
|
||||||
Reference in New Issue
Block a user