event broadcasting & nuking foreground app listener
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { writable } from "svelte/store";
|
||||
import type { AppMetadata } from "../types/bindings/AppMetadata";
|
||||
import type { PresenceStatus } from "../types/bindings/PresenceStatus";
|
||||
|
||||
export type FriendUserStatus = {
|
||||
appMetadata: AppMetadata;
|
||||
export type UserStatus = {
|
||||
presenceStatus: PresenceStatus;
|
||||
state: "idle" | "resting";
|
||||
};
|
||||
|
||||
export const friendsUserStatuses = writable<Record<string, FriendUserStatus>>({});
|
||||
export const friendsUserStatuses = writable<Record<string, UserStatus>>({});
|
||||
|
||||
let unlistenStatus: UnlistenFn | null = null;
|
||||
let unlistenFriendDisconnected: UnlistenFn | null = null;
|
||||
@@ -29,52 +29,53 @@ export async function initUserStatusListeners() {
|
||||
}
|
||||
|
||||
const userId = payload?.userId as string | undefined;
|
||||
const status = payload?.status as FriendUserStatus | undefined;
|
||||
const status = payload?.status as UserStatus | undefined;
|
||||
|
||||
if (!userId || !status) return;
|
||||
if (!status.appMetadata) return;
|
||||
|
||||
if (!status.presenceStatus) return;
|
||||
|
||||
// Validate that appMetadata has at least one valid name
|
||||
const hasValidName =
|
||||
(typeof status.appMetadata.localized === "string" && status.appMetadata.localized.trim() !== "") ||
|
||||
(typeof status.appMetadata.unlocalized === "string" && status.appMetadata.unlocalized.trim() !== "");
|
||||
const hasValidName =
|
||||
(typeof status.presenceStatus.title === "string" &&
|
||||
status.presenceStatus.title.trim() !== "") ||
|
||||
(typeof status.presenceStatus.subtitle === "string" &&
|
||||
status.presenceStatus.subtitle.trim() !== "");
|
||||
if (!hasValidName) return;
|
||||
|
||||
|
||||
if (status.state !== "idle" && status.state !== "resting") return;
|
||||
|
||||
friendsUserStatuses.update((current) => ({
|
||||
...current,
|
||||
[userId]: {
|
||||
appMetadata: status.appMetadata,
|
||||
presenceStatus: status.presenceStatus,
|
||||
state: status.state,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
unlistenFriendDisconnected = await listen<[{ userId: string }] | { userId: string } | string>(
|
||||
"friend-disconnected",
|
||||
(event) => {
|
||||
let payload = event.payload as any;
|
||||
if (typeof payload === "string") {
|
||||
try {
|
||||
payload = JSON.parse(payload);
|
||||
} catch (error) {
|
||||
console.error("Failed to parse friend-disconnected payload", error);
|
||||
return;
|
||||
}
|
||||
unlistenFriendDisconnected = await listen<
|
||||
[{ userId: string }] | { userId: string } | string
|
||||
>("friend-disconnected", (event) => {
|
||||
let payload = event.payload as any;
|
||||
if (typeof payload === "string") {
|
||||
try {
|
||||
payload = JSON.parse(payload);
|
||||
} catch (error) {
|
||||
console.error("Failed to parse friend-disconnected payload", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const data = Array.isArray(payload) ? payload[0] : payload;
|
||||
const userId = data?.userId as string | undefined;
|
||||
if (!userId) return;
|
||||
const data = Array.isArray(payload) ? payload[0] : payload;
|
||||
const userId = data?.userId as string | undefined;
|
||||
if (!userId) return;
|
||||
|
||||
friendsUserStatuses.update((current) => {
|
||||
const next = { ...current };
|
||||
delete next[userId];
|
||||
return next;
|
||||
});
|
||||
},
|
||||
);
|
||||
friendsUserStatuses.update((current) => {
|
||||
const next = { ...current };
|
||||
delete next[userId];
|
||||
return next;
|
||||
});
|
||||
});
|
||||
|
||||
isListening = true;
|
||||
} catch (error) {
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
} from "../../events/cursor";
|
||||
import { appData } from "../../events/app-data";
|
||||
import { sceneInteractive } from "../../events/scene-interactive";
|
||||
import { friendsUserStatuses } from "../../events/user-status";
|
||||
import {
|
||||
friendsUserStatuses,
|
||||
type UserStatus,
|
||||
} from "../../events/user-status";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import DesktopPet from "./components/DesktopPet.svelte";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { onMount } from "svelte";
|
||||
import type { AppMetadata } from "../../types/bindings/AppMetadata";
|
||||
import type { PresenceStatus } from "../../types/bindings/PresenceStatus";
|
||||
import type { DollDto } from "../../types/bindings/DollDto";
|
||||
|
||||
let innerWidth = $state(0);
|
||||
@@ -43,11 +46,12 @@
|
||||
return $appData?.dolls?.find((d) => d.id === user.activeDollId);
|
||||
}
|
||||
|
||||
let appMetadata: AppMetadata | null = $state(null);
|
||||
let presenceStatus: PresenceStatus | null = $state(null);
|
||||
|
||||
onMount(() => {
|
||||
const unlisten = listen<AppMetadata>("active-app-changed", (event) => {
|
||||
appMetadata = event.payload;
|
||||
const unlisten = listen<UserStatus>("user-status-changed", (event) => {
|
||||
console.log("event received");
|
||||
presenceStatus = event.payload.presenceStatus;
|
||||
});
|
||||
|
||||
return () => {
|
||||
@@ -89,14 +93,14 @@
|
||||
</span>
|
||||
|
||||
<span class="font-mono text-xs badge py-3 flex items-center gap-2">
|
||||
{#if appMetadata?.appIconB64}
|
||||
{#if presenceStatus?.graphicsB64}
|
||||
<img
|
||||
src={`data:image/png;base64,${appMetadata.appIconB64}`}
|
||||
src={`data:image/png;base64,${presenceStatus.graphicsB64}`}
|
||||
alt="Active app icon"
|
||||
class="size-4"
|
||||
/>
|
||||
{/if}
|
||||
{appMetadata?.localized}
|
||||
{presenceStatus?.title}
|
||||
</span>
|
||||
|
||||
{#if Object.keys($friendsCursorPositions).length > 0}
|
||||
@@ -115,15 +119,15 @@
|
||||
{#if status}
|
||||
<span class="flex items-center gap-1">
|
||||
{status.state} in
|
||||
{#if status.appMetadata.appIconB64}
|
||||
{#if status.presenceStatus.graphicsB64}
|
||||
<img
|
||||
src={`data:image/png;base64,${status.appMetadata.appIconB64}`}
|
||||
src={`data:image/png;base64,${status.presenceStatus.graphicsB64}`}
|
||||
alt="Friend's active app icon"
|
||||
class="size-4"
|
||||
/>
|
||||
{/if}
|
||||
{status.appMetadata.localized ||
|
||||
status.appMetadata.unlocalized}
|
||||
{status.presenceStatus.title ||
|
||||
status.presenceStatus.subtitle}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -163,8 +167,8 @@
|
||||
username: $appData.user.username,
|
||||
activeDoll: getUserDoll() ?? null,
|
||||
}}
|
||||
userStatus={appMetadata
|
||||
? { appMetadata: appMetadata, state: "idle" }
|
||||
userStatus={presenceStatus
|
||||
? { presenceStatus: presenceStatus, state: "idle" }
|
||||
: undefined}
|
||||
doll={getUserDoll()}
|
||||
isInteractive={false}
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
import PetMenu from "./PetMenu.svelte";
|
||||
import type { DollDto } from "../../../types/bindings/DollDto";
|
||||
import type { UserBasicDto } from "../../../types/bindings/UserBasicDto";
|
||||
import type { AppMetadata } from "../../../types/bindings/AppMetadata";
|
||||
import type { FriendUserStatus } from "../../../events/user-status";
|
||||
import type { PresenceStatus } from "../../../types/bindings/PresenceStatus";
|
||||
import type { UserStatus } from "../../../events/user-status";
|
||||
|
||||
export let id = "";
|
||||
export let targetX = 0;
|
||||
export let targetY = 0;
|
||||
export let user: UserBasicDto;
|
||||
export let userStatus: FriendUserStatus | undefined = undefined;
|
||||
export let userStatus: UserStatus | undefined = undefined;
|
||||
export let doll: DollDto | undefined = undefined;
|
||||
export let isInteractive = false;
|
||||
|
||||
@@ -160,9 +160,9 @@
|
||||
{/if}
|
||||
{#if userStatus}
|
||||
<div class="absolute -top-5 left-0 right-0 w-max mx-auto">
|
||||
{#if userStatus.appMetadata.appIconB64}
|
||||
{#if userStatus.presenceStatus.graphicsB64}
|
||||
<img
|
||||
src={`data:image/png;base64,${userStatus.appMetadata.appIconB64}`}
|
||||
src={`data:image/png;base64,${userStatus.presenceStatus.graphicsB64}`}
|
||||
alt="Friend's active app icon"
|
||||
class="size-4"
|
||||
/>
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
import { type DollDto } from "../../../types/bindings/DollDto";
|
||||
import type { UserBasicDto } from "../../../types/bindings/UserBasicDto";
|
||||
import type { SendInteractionDto } from "../../../types/bindings/SendInteractionDto";
|
||||
import type { FriendUserStatus } from "../../../events/user-status";
|
||||
import type { UserStatus } from "../../../events/user-status";
|
||||
|
||||
export let doll: DollDto;
|
||||
export let user: UserBasicDto;
|
||||
export let userStatus: FriendUserStatus | undefined = undefined;
|
||||
export let userStatus: UserStatus | undefined = undefined;
|
||||
export let receivedMessage: string | undefined = undefined;
|
||||
|
||||
let showMessageInput = false;
|
||||
@@ -49,15 +49,15 @@
|
||||
</div>
|
||||
{#if userStatus}
|
||||
<div class="card bg-base-200 px-2 py-1 flex flex-row gap-2 items-center">
|
||||
{#if userStatus.appMetadata.appIconB64}
|
||||
{#if userStatus.presenceStatus.graphicsB64}
|
||||
<img
|
||||
src={`data:image/png;base64,${userStatus.appMetadata.appIconB64}`}
|
||||
src={`data:image/png;base64,${userStatus.presenceStatus.graphicsB64}`}
|
||||
alt="Friend's active app icon"
|
||||
class="size-3"
|
||||
/>
|
||||
{/if}
|
||||
<p class="text-[0.6rem] font-mono text-ellipsis line-clamp-1">
|
||||
{userStatus.appMetadata.localized}
|
||||
{userStatus.presenceStatus.title}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Metadata for the currently active application, including localized and unlocalized names, and an optional base64-encoded icon.
|
||||
*/
|
||||
export type AppMetadata = { localized: string | null, unlocalized: string | null, appIconB64: string | null, };
|
||||
@@ -1,3 +1,3 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type UpdateUserDto = Record<string, never>;
|
||||
export type PresenceStatus = { title: string | null, subtitle: string | null, graphicsB64: string | null, };
|
||||
Reference in New Issue
Block a user