Skip to content

Commit

Permalink
Link Matching Service (#90)
Browse files Browse the repository at this point in the history
Fixes #69
  • Loading branch information
ong6 authored Oct 15, 2023
1 parent 1833e41 commit a12bda4
Show file tree
Hide file tree
Showing 14 changed files with 593 additions and 296 deletions.
148 changes: 148 additions & 0 deletions frontend/providers/MatchmakingProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import React, {
createContext,
useContext,
useState,
useEffect,
useCallback,
} from "react";
import { io, Socket } from "socket.io-client";
import { Match } from "@prisma/client";
import { AuthContext } from "@/contexts/AuthContext";

const SERVER_URL = "http://localhost:5002";

interface MatchmakingContextValue {
socket: Socket | null;
match: Match | null;
message: string;
error: string;
joinQueue: (difficulties: string[], programmingLang: string) => void;
sendMessage: (message: string) => void;
leaveMatch: () => void;
cancelLooking: () => void;
}

export const MatchmakingContext = createContext<
MatchmakingContextValue | undefined
>(undefined);

interface MatchmakingProviderProps {
children: React.ReactNode;
}

export const MatchmakingProvider: React.FC<MatchmakingProviderProps> = ({
children,
}) => {
const [socket, setSocket] = useState<Socket | null>(null);
const [match, setMatch] = useState<Match | null>(null);
const [message, setMessage] = useState<string>("");
const [error, setError] = useState<string>("");

const { user: currentUser, authIsReady } = useContext(AuthContext);

const generateRandomNumber = () => {
// Return a random number either 0 or 1 as a string
return Math.floor(Math.random() * 2).toString();
};

// Initialize socket connection
useEffect(() => {
if (currentUser) {
const newSocket = io(SERVER_URL, {
autoConnect: false,
// query: { username: currentUser?.email },
query: { username: generateRandomNumber() },
});
setSocket(newSocket);
newSocket.connect();

console.log("Socket connected");

return () => {
newSocket.close();
};
}
}, [currentUser]);

useEffect(() => {
if (!socket) return;

socket.on("connect", () => {
console.log("Connected to server");
});

socket.on("matchFound", (match: Match) => {
console.log("Match found:", match);
setMatch(match);
});

socket.on("receiveMessage", (message: string) => {
console.log("Message received:", message);
setMessage(message);
});

socket.on("error", (error: string) => {
console.error("An error occurred:", error);
setError(error);
});

socket.on("disconnect", () => {
console.log("Disconnected from server");
});

return () => {
socket.off("connect");
socket.off("matchFound");
socket.off("receiveMessage");
socket.off("error");
socket.off("disconnect");
};
}, [socket]);

const joinQueue = useCallback(
(difficulties: string[], programmingLang: string) => {
if (!socket) return;

socket.emit("lookingForMatch", difficulties, programmingLang);
},
[socket]
);

const sendMessage = useCallback(
(message: string) => {
if (!socket) return;

socket.emit("sendMessage", message);
},
[socket]
);

const leaveMatch = useCallback(() => {
if (!socket) return;

socket.emit("leaveMatch");
}, [socket]);

const cancelLooking = useCallback(() => {
if (!socket) return;

socket.emit("cancelLooking");
}, [socket]);

const value = {
socket,
match,
message,
error,
joinQueue,
sendMessage,
leaveMatch,
cancelLooking,
};

return (
<MatchmakingContext.Provider value={value}>
{children}
</MatchmakingContext.Provider>
);
};
12 changes: 12 additions & 0 deletions frontend/src/components/interviews/InterviewsLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react";
import { MatchmakingProvider } from "../../../providers/MatchmakingProvider";

interface InterviewsLayoutProps {
children: React.ReactNode;
}

const InterviewsLayout: React.FC<InterviewsLayoutProps> = ({ children }) => {
return <MatchmakingProvider>{children}</MatchmakingProvider>;
};

export default InterviewsLayout;
10 changes: 10 additions & 0 deletions frontend/src/hooks/useMatchmaking.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useContext } from "react";
import { MatchmakingContext } from "../../providers/MatchmakingProvider";

export function useMatchmaking() {
const context = useContext(MatchmakingContext);
if (!context) {
throw new Error("useMatchmaking must be used within a MatchmakingProvider");
}
return context;
}
27 changes: 15 additions & 12 deletions frontend/src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import '@/styles/globals.scss'
import type { AppProps } from 'next/app'
import Layout from '../components/common/layout'
import { Noto_Sans } from 'next/font/google'
import "@/styles/globals.scss";
import type { AppProps } from "next/app";
import Layout from "../components/common/layout";
import { Noto_Sans } from "next/font/google";
import AuthContextProvider from "@/contexts/AuthContext";
import AuthChecker from '@/components/common/auth-checker';
import { MatchmakingProvider } from "../../providers/MatchmakingProvider";
import AuthChecker from "@/components/common/auth-checker";

const notoSans = Noto_Sans({
weight: ['400', '500', '600', '700', '800', '900'],
preload: false
})
weight: ["400", "500", "600", "700", "800", "900"],
preload: false,
});

export default function App({ Component, pageProps }: AppProps) {
return (
Expand All @@ -21,12 +22,14 @@ export default function App({ Component, pageProps }: AppProps) {
<main>
<AuthContextProvider>
<AuthChecker>
<Layout>
<Component {...pageProps} />
</Layout>
<MatchmakingProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</MatchmakingProvider>
</AuthChecker>
</AuthContextProvider>
</main>
</>
)
);
}
37 changes: 0 additions & 37 deletions frontend/src/pages/interviews/[id]/find-match.tsx

This file was deleted.

52 changes: 0 additions & 52 deletions frontend/src/pages/interviews/[id]/match-found.tsx

This file was deleted.

40 changes: 40 additions & 0 deletions frontend/src/pages/interviews/find-match.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Loader from "@/components/interviews/loader";
import { Button } from "@/components/ui/button";
import { TypographyBody, TypographyH2 } from "@/components/ui/typography";
import Link from "next/link";
import { useRouter } from "next/router";
import { useEffect } from "react";
import InterviewsLayout from "@/components/interviews/InterviewsLayout";
import { useMatchmaking } from "@/hooks/useMatchmaking";

export default function FindMatch() {
const router = useRouter();
const { match, cancelLooking } = useMatchmaking();

const onClickCancel = () => {
cancelLooking();
router.push("/interviews");
};

useEffect(() => {
if (match) {
router.push("/interviews/match-found");
}
}, [match, router]);

return (
<div className="min-h-screen p-12 mx-auto max-w-7xl flex flex-col justify-evenly items-center">
<div className="gap-y-6 flex flex-col justify-center items-center">
<TypographyH2>Finding a match for your interview prep...</TypographyH2>

<TypographyBody>Estimated time: 25 secs</TypographyBody>
</div>

<Loader />

<Button variant="secondary" onClick={onClickCancel}>
Cancel Search
</Button>
</div>
);
}
Loading

0 comments on commit a12bda4

Please sign in to comment.