From 58609bd7ec9dc7813d8b35d45a1172f0556147b2 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Fri, 28 Aug 2026 02:25:06 +0200 Subject: [PATCH] Refactored status card --- src/app/app.ts | 40 ++++++++ src/app/server.ts | 3 + src/components/page/channel/VoiceChannel.tsx | 72 ++----------- src/components/view/AccountCard.tsx | 60 ----------- src/components/view/Sidebar.tsx | 4 +- src/components/view/StatusCard.tsx | 102 +++++++++++++++++++ 6 files changed, 157 insertions(+), 124 deletions(-) delete mode 100644 src/components/view/AccountCard.tsx create mode 100644 src/components/view/StatusCard.tsx diff --git a/src/app/app.ts b/src/app/app.ts index 58924f1..39b1115 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -7,6 +7,8 @@ import { base58 } from "@scure/base"; import axios from "axios"; import { Account, AccountsFile, getAccounts } from "@/lib/accounts"; import { Page } from "@/components/page/PageView"; +import { Channel } from "@/lib/types"; +import { invoke } from "@tauri-apps/api/core"; import { ClientMethod } from "./protocol"; import { Config, getConfig } from "@/lib/config"; @@ -152,6 +154,44 @@ export default class Enclave

{ this.forceRender(); } + public joinVoice(channel: Channel) { + const server = this.server; + + if (!server || channel.kind !== "voice") return; + + server.voiceJoin = (pin, channelId) => { + invoke("connect_to_vc", { hostname: server.hostname, pin, channelId }) + .then(() => { + server.isInVoiceChat = true; + server.voiceChannelId = channelId; + this.forceRender(); + }) + .catch(console.error); + }; + + server.isInVoiceChat = true; + server.voiceChannelId = channel.id; + + server.websocket?.send({ method: "JoinVoice", channel_id: channel.id }); + + this.forceRender(); + } + + public leaveVoice() { + const server = this.server; + + if (!server || !server.isInVoiceChat) return; + + server.websocket?.send({ method: "LeaveVoice" }); + invoke("disconnect_from_vc").catch(console.error); + + server.isInVoiceChat = false; + server.voiceChannelId = undefined; + server.voiceChatSpeakers = {}; + + this.forceRender(); + } + public async onMessage(msg: ClientMethod) { const server = this.server; diff --git a/src/app/server.ts b/src/app/server.ts index a3e56d1..c4e20f5 100644 --- a/src/app/server.ts +++ b/src/app/server.ts @@ -36,6 +36,8 @@ export default class EnclaveServer { public voiceJoin?: (pin: bigint, channelId: string) => void; public voiceChatUsers: Record; public voiceChatSpeakers: Record; + public isInVoiceChat: boolean; + public voiceChannelId?: string; public constructor(hostname: string, isSecure: boolean) { this.hostname = hostname; @@ -44,6 +46,7 @@ export default class EnclaveServer { this.users = {}; this.voiceChatUsers = {}; this.voiceChatSpeakers = {}; + this.isInVoiceChat = false; } public disconnect() { diff --git a/src/components/page/channel/VoiceChannel.tsx b/src/components/page/channel/VoiceChannel.tsx index 52a7ed0..25e2366 100644 --- a/src/components/page/channel/VoiceChannel.tsx +++ b/src/components/page/channel/VoiceChannel.tsx @@ -1,11 +1,9 @@ import Enclave from "@/app/app"; import { ChannelPageProps } from "../PageView"; -import { useEffect, useRef, useState } from "react"; -import { invoke } from "@tauri-apps/api/core"; import { cn } from "@/lib/utils"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; -import { Phone, PhoneOff } from "lucide-react"; +import { Phone } from "lucide-react"; export default function VoiceChannel({ appRef, @@ -15,50 +13,9 @@ export default function VoiceChannel({ const channel = appRef.current?.page?.channel; if (!channel) return null; - const [isConnected, setIsConnected] = useState(false); - const lastChannelId = useRef(null); - - // Leave current channel setup - const handleLeave = () => { - appRef.current?.server?.websocket?.send({ method: "LeaveVoice" }); - invoke("disconnect_from_vc").catch(console.error); - setIsConnected(false); - }; - - // Explicit action to join the voice channel - const handleJoin = () => { - const hostname = appRef.current?.server?.hostname; - if (!hostname || channel.kind !== "voice" || !appRef.current?.server) - return; - - appRef.current.server.voiceJoin = (pin, channelId) => { - invoke("connect_to_vc", { hostname, pin, channelId }) - .then(() => setIsConnected(true)) - .catch(console.error); - }; - - appRef.current.server.websocket?.send({ - method: "JoinVoice", - channel_id: channel.id, - }); - - setIsConnected(true); - }; - - // Clean up when channel changes or component unmounts - useEffect(() => { - if (lastChannelId.current !== channel.id) { - if (isConnected) { - invoke("disconnect_from_vc").catch(console.error); - setIsConnected(false); - } - lastChannelId.current = channel.id; - } - - return () => { - invoke("disconnect_from_vc").catch(console.error); - }; - }, [channel.id]); + const isConnected = + appRef.current?.server?.isInVoiceChat && + appRef.current.server.voiceChannelId === channel.id; const speakers = appRef.current?.server?.voiceChatSpeakers && @@ -141,27 +98,18 @@ export default function VoiceChannel({ {/* Bottom Control Bar */} -

+ )} ); -} +} \ No newline at end of file diff --git a/src/components/view/AccountCard.tsx b/src/components/view/AccountCard.tsx deleted file mode 100644 index d5d63e4..0000000 --- a/src/components/view/AccountCard.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { Card } from "../ui/card"; -import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; -import { CopyIcon, Settings2Icon } from "lucide-react"; -import { Button } from "../ui/button"; -import Enclave from "@/app/app"; -import { base58 } from "@scure/base"; -import * as ed from "@noble/ed25519"; - -export default function AccountCard({ - appRef, -}: { - appRef: React.RefObject; -}) { - const account = appRef.current?.getAccount(); - - if (!account) return null; - - return ( -
- -
- - - {account.meta.displayName[0]} - -
- { - navigator.clipboard.writeText( - base58.encode( - ed.getPublicKey(base58.decode(account.privateKey)), - ), - ); - }} - > -

{account.meta.displayName}

- -
- Online -
-
-
- -
-
-
- ); -} diff --git a/src/components/view/Sidebar.tsx b/src/components/view/Sidebar.tsx index 1fe61fa..5113fb0 100644 --- a/src/components/view/Sidebar.tsx +++ b/src/components/view/Sidebar.tsx @@ -2,7 +2,7 @@ import Enclave from "@/app/app"; import { Channel, ChannelKind } from "@/lib/types"; import { ChevronDown, ChevronUp, HashIcon, Volume2Icon } from "lucide-react"; import { useState } from "react"; -import AccountCard from "./AccountCard"; +import StatusCard from "./StatusCard"; import { cn } from "@/lib/utils"; export function ChannelIcon({ kind }: { kind: ChannelKind["kind"] }) { @@ -106,7 +106,7 @@ export default function Sidebar({ )} - + ); } diff --git a/src/components/view/StatusCard.tsx b/src/components/view/StatusCard.tsx new file mode 100644 index 0000000..a9fa701 --- /dev/null +++ b/src/components/view/StatusCard.tsx @@ -0,0 +1,102 @@ +import { Card } from "../ui/card"; +import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; +import { CopyIcon, PhoneOff, Settings2Icon, Signal } from "lucide-react"; +import { Button } from "../ui/button"; +import Enclave from "@/app/app"; +import { base58 } from "@scure/base"; +import * as ed from "@noble/ed25519"; +import { Channel } from "@/lib/types"; + +function findChannel(channels: Channel[], id: string): Channel | undefined { + for (const channel of channels) { + if (channel.id === id) return channel; + + if (channel.kind === "category") { + const found = findChannel(channel.channels, id); + if (found) return found; + } + } + + return undefined; +} + +export default function StatusCard({ + appRef, +}: { + appRef: React.RefObject; +}) { + const account = appRef.current?.getAccount(); + const server = appRef.current?.server; + + const voiceChannelId = server?.voiceChannelId; + const voiceChannel = + voiceChannelId && server?.meta + ? findChannel(server.meta.channels, voiceChannelId) + : undefined; + + if (!account) return null; + + return ( +
+ + {server?.isInVoiceChat && voiceChannel && ( +
+ +
+ Voice Connected + + {voiceChannel.name} + +
+ +
+ )} +
+
+ + + {account.meta.displayName[0]} + +
+ { + navigator.clipboard.writeText( + base58.encode( + ed.getPublicKey(base58.decode(account.privateKey)), + ), + ); + }} + > +

{account.meta.displayName}

+ +
+ Online +
+
+
+ +
+
+
+
+ ); +} \ No newline at end of file