2026-07-27 21:54:31 +07:00
|
|
|
import { useCallback, useState } from "react";
|
2026-08-01 09:19:39 +07:00
|
|
|
import useSWR, { useSWRConfig } from "swr";
|
|
|
|
|
import { useAction } from "@/hooks/use-action";
|
2026-07-26 16:14:32 +07:00
|
|
|
import { voiceApi } from "@/lib/api";
|
2026-07-27 21:54:31 +07:00
|
|
|
import type { ActiveSpeaker, Channel, VoiceStatus } from "@/lib/types";
|
|
|
|
|
import type { WsHook } from "@/lib/ws-hook";
|
2026-07-26 16:14:32 +07:00
|
|
|
|
2026-08-01 09:19:39 +07:00
|
|
|
const STATUS_KEY = ["voice-status"] as const;
|
|
|
|
|
|
2026-07-26 16:55:20 +07:00
|
|
|
export function useVoiceStatus() {
|
2026-08-01 09:19:39 +07:00
|
|
|
return useSWR<VoiceStatus>(STATUS_KEY, () => voiceApi.getStatus(), {
|
|
|
|
|
shouldRetryOnError: false,
|
2026-07-26 16:55:20 +07:00
|
|
|
});
|
2026-07-26 16:14:32 +07:00
|
|
|
}
|
|
|
|
|
|
2026-07-27 21:54:31 +07:00
|
|
|
export function useVoiceChannels(guildId: string) {
|
2026-08-01 09:19:39 +07:00
|
|
|
return useSWR<Channel[]>(guildId ? ["voice-channels", guildId] : null, () =>
|
|
|
|
|
voiceApi.getVoiceChannels(guildId),
|
|
|
|
|
);
|
2026-07-26 16:14:32 +07:00
|
|
|
}
|
|
|
|
|
|
2026-07-26 16:55:20 +07:00
|
|
|
export function useSpeakers() {
|
2026-07-26 16:14:32 +07:00
|
|
|
const [speakers, setSpeakers] = useState<ActiveSpeaker[]>([]);
|
|
|
|
|
|
|
|
|
|
const subscribe = useCallback((ws: WsHook) => {
|
|
|
|
|
const unsub = ws.on("voice_active_user", (data) => {
|
|
|
|
|
const speaker = data as ActiveSpeaker;
|
|
|
|
|
setSpeakers((prev) => {
|
|
|
|
|
const idx = prev.findIndex((s) => s.userId === speaker.userId);
|
|
|
|
|
if (idx >= 0) {
|
|
|
|
|
const next = [...prev];
|
|
|
|
|
next[idx] = speaker;
|
|
|
|
|
return next;
|
|
|
|
|
}
|
|
|
|
|
return [...prev, speaker];
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
return () => {
|
|
|
|
|
unsub();
|
|
|
|
|
setSpeakers([]);
|
|
|
|
|
};
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
return { speakers, subscribe };
|
|
|
|
|
}
|
2026-07-26 16:55:20 +07:00
|
|
|
|
2026-08-01 09:19:39 +07:00
|
|
|
function useStatusInvalidator() {
|
|
|
|
|
const { mutate } = useSWRConfig();
|
|
|
|
|
return () => {
|
|
|
|
|
void mutate(STATUS_KEY);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-26 16:55:20 +07:00
|
|
|
export function useVoiceConnect() {
|
2026-08-01 09:19:39 +07:00
|
|
|
const invalidate = useStatusInvalidator();
|
|
|
|
|
return useAction(
|
|
|
|
|
({ guildId, channelId }: { guildId: string; channelId: string }) =>
|
|
|
|
|
voiceApi.connect(guildId, channelId),
|
|
|
|
|
{ onSuccess: invalidate },
|
|
|
|
|
);
|
2026-07-26 16:55:20 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useVoiceDisconnect() {
|
2026-08-01 09:19:39 +07:00
|
|
|
const invalidate = useStatusInvalidator();
|
|
|
|
|
return useAction(() => voiceApi.disconnect(), { onSuccess: invalidate });
|
2026-07-26 16:55:20 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useMicTransmit() {
|
2026-08-01 09:19:39 +07:00
|
|
|
return useAction((active: boolean) =>
|
|
|
|
|
voiceApi.sendCommand(
|
|
|
|
|
active ? "voice:transmit:start" : "voice:transmit:stop",
|
|
|
|
|
),
|
|
|
|
|
);
|
2026-07-26 16:55:20 +07:00
|
|
|
}
|