Refactor voice page and hooks to use React Query for data fetching
Deploy to VPS / deploy (push) Successful in 2m57s

- Updated VoicePage component to utilize useVoiceConnect, useVoiceDisconnect, and useMicTransmit mutations.
- Replaced local state management with React Query's useQuery for voice status, guilds, and channels.
- Removed custom useAsync hook and replaced it with useQuery in useConfig, useStats, useUsers, useChannels, and useMessages hooks.
- Simplified useRecordings and useSpeakers hooks to leverage React Query for data fetching and mutations.
- Removed deprecated use-async hook and related code.
- Enhanced error handling and loading states across various hooks.
This commit is contained in:
asepharyana
2026-07-26 16:55:20 +07:00
parent 323fa207af
commit 05bbae66da
20 changed files with 980 additions and 1582 deletions
+48 -62
View File
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { voiceApi } from "@/lib/api";
import type { MediaState } from "@/lib/types";
@@ -11,74 +11,60 @@ type WsHook = {
) => () => void;
};
interface UseMediaStateReturn {
mediaState: MediaState | null;
refresh: () => void;
queue: (url: string) => void;
skip: () => void;
stop: () => void;
setVolume: (value: number | readonly number[]) => void;
export function useMediaState() {
return useQuery<MediaState>({
queryKey: ["media-state"],
queryFn: () => voiceApi.getMediaStatus(),
retry: false,
refetchInterval: 10_000,
});
}
export function useMediaState(): UseMediaStateReturn {
const [mediaState, setMediaState] = useState<MediaState | null>(null);
export function useMediaQueue() {
const qc = useQueryClient();
return useMutation({
mutationFn: (url: string) => voiceApi.mediaQueue(url, "music"),
onSuccess: (data) => qc.setQueryData(["media-state"], data),
});
}
const refresh = useCallback(async () => {
try {
const state = await voiceApi.getMediaStatus();
setMediaState(state);
} catch (err) {
console.error("useMediaState/refresh:", err);
}
}, []);
export function useMediaSkip() {
const qc = useQueryClient();
return useMutation({
mutationFn: () => voiceApi.mediaSkip(),
onSuccess: (data) => qc.setQueryData(["media-state"], data),
});
}
const queue = useCallback(async (url: string) => {
try {
const state = await voiceApi.mediaQueue(url, "music");
setMediaState(state);
} catch (err) {
console.error("useMediaState/queue:", err);
}
}, []);
export function useMediaStop() {
const qc = useQueryClient();
return useMutation({
mutationFn: () => voiceApi.mediaStop(),
onSuccess: (data) => qc.setQueryData(["media-state"], data),
});
}
const skip = useCallback(async () => {
try {
const state = await voiceApi.mediaSkip();
setMediaState(state);
} catch (err) {
console.error("useMediaState/skip:", err);
}
}, []);
export function useMediaVolume() {
const qc = useQueryClient();
return useMutation({
mutationFn: (volume: number) => voiceApi.mediaVolume(volume),
onSuccess: (data) => qc.setQueryData(["media-state"], data),
});
}
const stop = useCallback(async () => {
try {
const state = await voiceApi.mediaStop();
setMediaState(state);
} catch (err) {
console.error("useMediaState/stop:", err);
}
}, []);
/** Subscribe to WS media_state events to keep cache fresh */
export function useMediaWsSync(ws: WsHook) {
const qc = useQueryClient();
useEffectFn(ws, qc);
}
const setVolume = useCallback(async (value: number | readonly number[]) => {
const vol = Array.isArray(value) ? value[0] : value;
try {
const state = await voiceApi.mediaVolume(vol);
setMediaState(state);
} catch (err) {
console.error("useMediaState/setVolume:", err);
}
}, []);
import { useEffect } from "react";
function useEffectFn(ws: WsHook, qc: ReturnType<typeof useQueryClient>) {
useEffect(() => {
refresh();
}, [refresh]);
return { mediaState, refresh, queue, skip, stop, setVolume };
}
export function useMediaWsSubscription(
ws: WsHook,
onState: (state: MediaState) => void,
) {
return ws.on("media_state", (data) => onState(data as MediaState));
const unsub = ws.on("media_state", (data) => {
qc.setQueryData(["media-state"], data as MediaState);
});
return unsub;
}, [ws, qc]);
}