Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename jadi hardware websocket dan mengubah useEffect menjadi subscribe method #67

Merged
merged 3 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/clients/chooser/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import superjson from "superjson";

import { ClientNotFound } from "@sora-vp/ui/client-not-found";

import { KeyboardWebsocketProvider } from "./context/keyboard-websocket";
import { HardwareWebsocketProvider } from "./context/hardware-websocket";
import { env } from "./env";

const router = createBrowserRouter([
Expand Down Expand Up @@ -78,13 +78,13 @@ export default function App() {
return (
<api.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<KeyboardWebsocketProvider>
<HardwareWebsocketProvider>
<ParticipantProvider>
<ServerSettingProvider>
<RouterProvider router={router} />
</ServerSettingProvider>
</ParticipantProvider>
</KeyboardWebsocketProvider>
</HardwareWebsocketProvider>
</QueryClientProvider>
</api.Provider>
);
Expand Down
21 changes: 11 additions & 10 deletions apps/clients/chooser/src/components/scanner/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from "react";
import { useKeyboardWebsocket } from "@/context/keyboard-websocket";
import { useHardwareWebsocket } from "@/context/hardware-websocket";
import { useParticipant } from "@/context/participant-context";
import { api } from "@/utils/api";
import { Navigate } from "react-router-dom";
Expand All @@ -10,24 +10,21 @@ import { MainScanner } from "./main-scanner";

export function ScannerComponent() {
const { qrId, setQRCode } = useParticipant();
const { wsEnabled, lastMessage, setLastMessage } = useKeyboardWebsocket();
const { subscribe } = useHardwareWebsocket();

const [isQrInvalid, setInvalidQr] = useState(false);

const participantAttended =
api.clientConsumer.checkParticipantAttended.useMutation({
onSuccess() {
setLastMessage(null);

setQRCode(participantAttended.variables!);
},
});

useEffect(() => {
if (wsEnabled && lastMessage) {
// Precheck before consuming command
if (lastMessage.startsWith("SORA-KEYBIND-")) {
const actualCommand = lastMessage.replace("SORA-KEYBIND-", "");
const unsubHardware = subscribe((message) => {
if (message.startsWith("SORA-KEYBIND-")) {
const actualCommand = message.replace("SORA-KEYBIND-", "");

switch (actualCommand) {
case "RELOAD": {
Expand All @@ -37,8 +34,12 @@ export function ScannerComponent() {
}
}
}
}
}, [isQrInvalid, participantAttended.isError, wsEnabled, lastMessage]);
});

return () => {
unsubHardware();
};
}, [isQrInvalid, participantAttended.isError]);

const setIsQrValid = useCallback(
(invalid: boolean) => setInvalidQr(invalid),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,39 @@ import {
useCallback,
useContext,
useEffect,
useMemo,
useState,
useRef,
} from "react";
import { defaultWSPortAtom, enableWSConnectionAtom } from "@/utils/atom";
import { useAtomValue } from "jotai";
import useWebSocket, { ReadyState } from "react-use-websocket";

import { toast } from "@sora-vp/ui/toast";

export interface IKeyboardWebsocket {
export type THardwareWebsocketCallback = (message: string) => void;

export interface IHardwareWebsocket {
wsEnabled: boolean;
lastMessage: string | null;
setLastMessage: (msg: string | null) => void;
subscribe(callbacK: THardwareWebsocketCallback): () => void;
}

export const KeyboardWebsocketContext = createContext<IKeyboardWebsocket>(
{} as IKeyboardWebsocket,
export const HardwareWebsocketContext = createContext<IHardwareWebsocket>(
{} as IHardwareWebsocket,
);

export const KeyboardWebsocketProvider = ({
export const HardwareWebsocketProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const [lastMessage, setLastMessageState] =
useState<IKeyboardWebsocket["lastMessage"]>(null);
const currentSubscriberIdRef = useRef<number>(0);
const subscribersRef = useRef<Map<number, THardwareWebsocketCallback>>(
new Map(),
);

const wsPortNumber = useAtomValue(defaultWSPortAtom);
const wsEnabled = useAtomValue(enableWSConnectionAtom);

const { lastMessage: libLastMessage, readyState } = useWebSocket<string>(
const { lastMessage, readyState } = useWebSocket<string>(
wsEnabled ? `ws://127.0.0.1:${wsPortNumber}/ws` : null,
{
share: true,
Expand All @@ -47,23 +49,23 @@ export const KeyboardWebsocketProvider = ({
},
);

const setLastMessage = useCallback(
(msg: IKeyboardWebsocket["lastMessage"]) => setLastMessageState(msg),
[],
);
const subscribe = useCallback((callback: THardwareWebsocketCallback) => {
const id = currentSubscriberIdRef.current;
subscribersRef.current.set(id, callback);
currentSubscriberIdRef.current++;

const contextValue = useMemo(
() => ({
wsEnabled,
lastMessage,
setLastMessage,
}),
[wsEnabled, lastMessage],
);
return () => {
subscribersRef.current.delete(id);
};
}, []);

useEffect(() => {
if (libLastMessage) setLastMessageState(libLastMessage.data);
}, [libLastMessage]);
if (lastMessage) {
Array.from(subscribersRef.current).forEach(([, callback]) => {
callback(lastMessage.data);
});
}
}, [lastMessage]);

useEffect(() => {
if (wsEnabled) {
Expand Down Expand Up @@ -96,11 +98,16 @@ export const KeyboardWebsocketProvider = ({
}, [readyState, wsEnabled]);

return (
<KeyboardWebsocketContext.Provider value={contextValue}>
<HardwareWebsocketContext.Provider
value={{
wsEnabled,
subscribe,
}}
>
{children}
</KeyboardWebsocketContext.Provider>
</HardwareWebsocketContext.Provider>
);
};

export const useKeyboardWebsocket = () =>
useContext(KeyboardWebsocketContext) as IKeyboardWebsocket;
export const useHardwareWebsocket = () =>
useContext(HardwareWebsocketContext) as IHardwareWebsocket;
25 changes: 11 additions & 14 deletions apps/clients/chooser/src/context/participant-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { api } from "@/utils/api";
import { motion } from "framer-motion";
import { Navigate } from "react-router-dom";

import { useKeyboardWebsocket } from "./keyboard-websocket";
import { useHardwareWebsocket } from "./hardware-websocket";

export interface IParticipantContext {
name: string | null;
Expand All @@ -30,7 +30,7 @@ export const ParticipantProvider = ({
}: {
children: React.ReactNode;
}) => {
const { wsEnabled, lastMessage } = useKeyboardWebsocket();
const { subscribe } = useHardwareWebsocket();

const [qrId, setQrId] = useState<string | null>(null);
const [votedSuccessfully, setVoted] = useState(false);
Expand All @@ -50,10 +50,9 @@ export const ParticipantProvider = ({
);

useEffect(() => {
if (wsEnabled && lastMessage) {
// Precheck before consuming command
if (lastMessage.startsWith("SORA-KEYBIND-")) {
const actualCommand = lastMessage.replace("SORA-KEYBIND-", "");
const unsubHardware = subscribe((message) => {
if (message.startsWith("SORA-KEYBIND-")) {
const actualCommand = message.replace("SORA-KEYBIND-", "");

switch (actualCommand) {
case "RELOAD": {
Expand All @@ -69,14 +68,12 @@ export const ParticipantProvider = ({
}
}
}
}
}, [
qrId,
participantQuery.isFetched,
participantQuery.data,
wsEnabled,
lastMessage,
]);

return () => {
unsubHardware();
};
});
}, [qrId, participantQuery.isFetched, participantQuery.data]);

const propsValue = useMemo(() => {
if (!qrId)
Expand Down
19 changes: 11 additions & 8 deletions apps/clients/chooser/src/context/server-setting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { api } from "@/utils/api";
import { motion } from "framer-motion";
import { Loader } from "lucide-react";

import { useKeyboardWebsocket } from "./keyboard-websocket";
import { useHardwareWebsocket } from "./hardware-websocket";
import { useParticipant } from "./participant-context";

interface ISettingContext {
Expand All @@ -20,7 +20,7 @@ export const ServerSettingProvider = ({
}: {
children: React.ReactNode;
}) => {
const { wsEnabled, lastMessage } = useKeyboardWebsocket();
const { subscribe } = useHardwareWebsocket();
const { qrId, setQRCode } = useParticipant();

const [errorMessage, setErrorMessage] = useState("");
Expand All @@ -31,10 +31,9 @@ export const ServerSettingProvider = ({
});

useEffect(() => {
if (wsEnabled && lastMessage) {
// Precheck before consuming command
if (lastMessage.startsWith("SORA-KEYBIND-")) {
const actualCommand = lastMessage.replace("SORA-KEYBIND-", "");
const unsubHardware = subscribe((message) => {
if (message.startsWith("SORA-KEYBIND-")) {
const actualCommand = message.replace("SORA-KEYBIND-", "");

switch (actualCommand) {
case "RELOAD": {
Expand All @@ -44,8 +43,12 @@ export const ServerSettingProvider = ({
}
}
}
}
}, [settingsQuery.errorUpdateCount, wsEnabled, lastMessage]);
});

return () => {
unsubHardware();
};
}, [settingsQuery.errorUpdateCount]);

useEffect(() => {
if (settingsQuery.error) setErrorMessage(settingsQuery.error.message);
Expand Down
27 changes: 10 additions & 17 deletions apps/clients/chooser/src/routes/vote-page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { UniversalError } from "@/components/universal-error";
import { useKeyboardWebsocket } from "@/context/keyboard-websocket";
import { useHardwareWebsocket } from "@/context/hardware-websocket";
import { ensureQRIDExist, useParticipant } from "@/context/participant-context";
import { env } from "@/env";
import { api } from "@/utils/api";
Expand Down Expand Up @@ -91,7 +91,7 @@ const CurrentParticipantInfo = (props: { isSuccess?: boolean }) => {

function VotePage() {
const { qrId, setQRCode, setVotedSuccessfully } = useParticipant();
const { wsEnabled, lastMessage, setLastMessage } = useKeyboardWebsocket();
const { subscribe } = useHardwareWebsocket();

const successTimeout = useAtomValue(successTimeoutAtom);

Expand All @@ -116,9 +116,6 @@ function VotePage() {
setAlertOpen(false);
setID(null);
},
onSettled() {
setLastMessage(null);
},
});

const candidateName = useMemo(
Expand All @@ -141,8 +138,6 @@ function VotePage() {
const chooseCandidate = useCallback(() => {
if (!cannotPushKey) {
if (qrId && currentID && alertOpen) {
setLastMessage(null);

upvoteCandidate.mutate({
id: currentID,
qrId,
Expand Down Expand Up @@ -176,7 +171,6 @@ function VotePage() {
if (!upvoteCandidate.isPending) {
setID(null);
setAlertOpen(false);
setLastMessage(null);
}

break;
Expand Down Expand Up @@ -228,17 +222,15 @@ function VotePage() {
}, [upvoteCandidate.isPending, alertOpen, triggerOpen, chooseCandidate]);

useEffect(() => {
if (wsEnabled && lastMessage) {
// Precheck before consuming command
if (lastMessage.startsWith("SORA-KEYBIND-")) {
const actualCommand = lastMessage.replace("SORA-KEYBIND-", "");
const unsubHardware = subscribe((message) => {
if (message.startsWith("SORA-KEYBIND-")) {
const actualCommand = message.replace("SORA-KEYBIND-", "");

switch (actualCommand) {
case "ESC": {
if (!upvoteCandidate.isPending) {
setID(null);
setAlertOpen(false);
setLastMessage(null);
}

break;
Expand Down Expand Up @@ -288,15 +280,17 @@ function VotePage() {
}
}
}
}
});

return () => {
unsubHardware();
};
}, [
upvoteCandidate.isPending,
candidateList.errorUpdateCount,
alertOpen,
triggerOpen,
chooseCandidate,
wsEnabled,
lastMessage,
]);

useEffect(() => {
Expand Down Expand Up @@ -443,7 +437,6 @@ function VotePage() {
open={alertOpen || (upvoteCandidate.isPending && !!qrId)}
onOpenChange={() => {
setAlertOpen((prev) => !prev);
setLastMessage(null);
}}
>
<AlertDialogContent
Expand Down