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

Frontend Websocket #40

Merged
merged 22 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d212d68
Create and wrap application in context for state of socket
chiaryan Oct 14, 2024
e6a169e
fix: remove "use client"
chiaryan Oct 14, 2024
10da8f5
Add usewebsocket, basic states for closed channel, open channel, and …
chiaryan Oct 15, 2024
dbafdf7
Implement functionality for finding a match from message
chiaryan Oct 15, 2024
2557048
Move websocket logic into services file, add states for finding match…
chiaryan Oct 16, 2024
82af041
Merge branch 'staging' into frontend-websocket
chiaryan Oct 16, 2024
354f47f
Merge branch 'staging' into frontend-websocket
chiaryan Oct 16, 2024
79b3170
Integrate useMatching interface with UI modals
chiaryan Oct 17, 2024
d2270ba
bugfix: timeout did not direct correct model; hide retry buttons
chiaryan Oct 18, 2024
8668971
revert timeout value
chiaryan Oct 18, 2024
b0d0726
Merge branch 'ben/matching-service' into frontend-websocket
chiaryan Oct 18, 2024
c3714e1
Integrate with new matchmaking changes
chiaryan Oct 18, 2024
404d5ad
remove ValidateUserstub
chiaryan Oct 18, 2024
8782f31
Merge branch 'staging' into frontend-websocket
bensohh Oct 20, 2024
1742ea6
fix: .env.example uses incorrect url
chiaryan Oct 20, 2024
f029e9a
Merge branch 'frontend-websocket' of https://github.com/CS3219-AY2425…
chiaryan Oct 20, 2024
bd4d597
Squashed commit of the following:
chiaryan Oct 20, 2024
6247ae1
Add handling of imporper match_found.
chiaryan Oct 20, 2024
1d3afe7
Merge branch 'staging' into frontend-websocket
chiaryan Oct 20, 2024
7c36043
Remove imports from matching-service (was added during merge)
chiaryan Oct 20, 2024
e94592d
Add captions under matched users
chiaryan Oct 20, 2024
c0e2088
match.go changes
chiaryan Oct 20, 2024
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
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches:
- main
- staging
- frontend-websocket-test
pull_request:
branches:
- main
Expand All @@ -27,6 +28,7 @@ jobs:
env:
QUESTION_SERVICE_URL: ${{ vars.QUESTION_SERVICE_URL }}
USER_SERVICE_URL: ${{ vars.USER_SERVICE_URL }}
MATCHING_SERVICE_URL: ${{ vars.MATCHING_SERVICE_URL }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
FIREBASE_CREDENTIAL_PATH: ${{ vars.QUESTION_SERVICE_FIREBASE_CREDENTIAL_PATH }}
DB_CLOUD_URI: ${{ secrets.USER_SERVICE_DB_CLOUD_URI }}
Expand All @@ -35,6 +37,7 @@ jobs:
cd ./apps/frontend
echo "NEXT_PUBLIC_QUESTION_SERVICE_URL=$QUESTION_SERVICE_URL" >> .env
echo "NEXT_PUBLIC_USER_SERVICE_URL=$USER_SERVICE_URL" >> .env
echo "NEXT_PUBLIC_MATCHING_SERVICE_URL=$MATCHING_SERVICE_URL" >> .env

cd ../question-service
echo "FIREBASE_CREDENTIAL_PATH=$FIREBASE_CREDENTIAL_PATH" >> .env
Expand Down
3 changes: 2 additions & 1 deletion apps/frontend/.env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# URL endpoints of the services
NEXT_PUBLIC_QUESTION_SERVICE_URL="http://localhost:8080/"
NEXT_PUBLIC_USER_SERVICE_URL="http://localhost:3001/"
NEXT_PUBLIC_USER_SERVICE_URL="http://localhost:3001/"
NEXT_PUBLIC_MATCHING_SERVICE_URL="ws://localhost:8081/match"
1 change: 1 addition & 0 deletions apps/frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Then, follow the `.env.example` file and create a `.env` file in the current dir
```bash
NEXT_PUBLIC_QUESTION_SERVICE_URL="http://localhost:8080"
NEXT_PUBLIC_USER_SERVICE_URL="http://localhost:3001/"
NEXT_PUBLIC_MATCHING_SERVICE_URL="wss://localhost:8081"
```

First, run the development server:
Expand Down
2 changes: 2 additions & 0 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"next": "14.2.13",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-timer-hook": "^3.0.7",
"react-use-websocket": "^4.9.0",
"sass": "^1.79.2",
"typeface-montserrat": "^1.1.13"
},
Expand Down
20 changes: 20 additions & 0 deletions apps/frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion apps/frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from "react";
import { AntdRegistry } from "@ant-design/nextjs-registry";
import { ConfigProvider } from "antd";
import WebSocketProvider from "@/components/WebSocketProvider/websocketprovider";

const RootLayout = ({
children,
Expand All @@ -23,7 +24,11 @@ const RootLayout = ({
},
}}
>
<AntdRegistry>{children}</AntdRegistry>
<AntdRegistry>
<WebSocketProvider>
{children}
</WebSocketProvider>
</AntdRegistry>
</ConfigProvider>
</body>
</html>
Expand Down
100 changes: 66 additions & 34 deletions apps/frontend/src/app/matching/MatchingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,66 +6,98 @@ import 'typeface-montserrat';
import './styles.scss';
import FindMatchContent from './modalContent/FindMatchContent';
import MatchingInProgressContent from './modalContent/MatchingInProgressContent';
import MatchFound from './modalContent/MatchFoundContent';
import MatchFoundContent from './modalContent/MatchFoundContent';
import JoinedMatchContent from './modalContent/JoinedMatchContent';
import MatchNotFoundContent from './modalContent/MatchNotFoundContent';
import MatchCancelledContent from './modalContent/MatchCancelledContent';
import useMatching from '../services/use-matching';

interface MatchingModalProps {
isOpen: boolean;
onClose: () => void;
close: () => void;
}

const MatchingModal: React.FC<MatchingModalProps> = ({ isOpen, onClose }) => {
// TODO: placehoder for now, to be replaced my useContext
const [matchingState, setMatchingState] = useState('finding');
const MatchingModal: React.FC<MatchingModalProps> = ({ isOpen, close: _close }) => {
const matchingState = useMatching();
const [closedType, setClosedType] = useState<"finding" | "cancelled" | "joined">("finding");
const [timeoutAfter, setTimeoutAfter] = useState<number>(9999);
const isClosable = ["timeout", "closed"].includes(matchingState.state);

// TODO: remove this after testing
useEffect(() => {
// Uncomment the following lines to test the different matching states
// setMatchingState('finding');
// setMatchingState('matching');
// setMatchingState('found');
// setMatchingState('joined');
// setMatchingState('notFound');
// setMatchingState('cancelled');
}, []);

// TODO: modify by using matchingState via useContext
const isClosableMatchingState = () => {
return matchingState === 'finding' || matchingState === 'notFound' || matchingState === 'cancelled';
};
function close() {
// clean up matching and closedType State
if (matchingState.state === "timeout") {
matchingState.ok();
}
setClosedType("finding");
_close();
}

const renderModalContent = () => {
switch (matchingState) {
case 'finding':
return <FindMatchContent/>;
switch (matchingState.state) {
case 'closed':
switch (closedType) {
case "finding":
return <FindMatchContent beginMatch={matchingState.start}/>;
case "cancelled":
return <MatchCancelledContent
reselect={() => {
setClosedType("finding");
}}
retry={() => {}}
canceledIn={timeoutAfter}
/>;
case "joined":
return <JoinedMatchContent cancel={() => {
setClosedType("cancelled");
}}/>;
}
case 'matching':
return <MatchingInProgressContent />;
return <MatchingInProgressContent
cancelMatch={(timeoutAfter: number) => {
setClosedType("cancelled");
setTimeoutAfter(timeoutAfter);
matchingState.cancel();
}}
timeout={(timeoutAfter: number) => {
matchingState.timeout()
setTimeoutAfter(timeoutAfter);
}}
/>;
case 'cancelling':
return <MatchingInProgressContent cancelMatch={() => {}} timeout={() => {}}/>;
case 'starting':
return <FindMatchContent beginMatch={() => {}}/>
case 'found':
return <MatchFound />;
case 'joined':
return <JoinedMatchContent />;
case 'notFound':
return <MatchNotFoundContent />;
case 'cancelled':
return <MatchCancelledContent />;
return <MatchFoundContent
cancel={() => {
matchingState.ok();
setClosedType("cancelled");
}}
join={() => {
matchingState.ok();
setClosedType("joined");
}}
name1={matchingState.info.myName}
name2={matchingState.info.partnerName}
/>
case 'timeout':
return <MatchNotFoundContent reselect={matchingState.ok} retry={() => {}} timedOutIn={10}/>;
default:
throw new Error('Invalid matching state.');
}
};

return (
<Modal open={isOpen}
onCancel={onClose}
onCancel={close}
footer={null}
closable={false}
maskClosable={false}
className="modal"
>
{renderModalContent()}
{isClosableMatchingState() && (
<button className="close-button" onClick={onClose}>Close</button>
{isClosable && (
<button className="close-button" onClick={close}>Close</button>
)}
</Modal>
)
Expand Down
34 changes: 24 additions & 10 deletions apps/frontend/src/app/matching/modalContent/FindMatchContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
import type { SelectProps } from 'antd';
import 'typeface-montserrat';
import './styles.scss';
import { handleFindMatch } from '../handlers';
import { ValidateUser } from "@/app/services/user"
import { type MatchRequestParams } from '@/app/services/use-matching';

interface DifficultySelectorProps {
className?: string;
Expand All @@ -25,9 +26,14 @@ interface TopicSelectorProps {
onChange: (topics: string[]) => void;
}

const FindMatchContent: React.FC = () => {
interface Props {
beginMatch(request: MatchRequestParams): void
}

const FindMatchContent: React.FC<Props> = ({ beginMatch }) => {
const [selectedDifficulties, setSelectedDifficulties] = useState<string[]>([]);
const [selectedTopics, setSelectedTopics] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState(false);

const handleDifficultyChange = (difficulties: string[]) => {
setSelectedDifficulties(difficulties);
Expand Down Expand Up @@ -55,7 +61,18 @@ const FindMatchContent: React.FC = () => {
/>
</div>
<button className="find-match-button"
onClick={handleFindMatch}
onClick={async () => {
setIsLoading(true);
const user = await ValidateUser();
beginMatch({
email: user.data.email,
username: user.data.username,
type: "match_request",
difficulties: selectedDifficulties,
topics: selectedTopics,
})
}}
disabled={isLoading}
>
Find Match
</button>
Expand All @@ -77,8 +94,8 @@ const DifficultySelector: React.FC<DifficultySelectorProps> = ({ selectedDifficu
<Tag.CheckableTag
className={`difficulty-tag ${difficultyOption.value}-tag`}
key={difficultyOption.value}
checked={selectedDifficulties.includes(difficultyOption.value)}
onChange={() => handleChange(difficultyOption.value)}
checked={selectedDifficulties.includes(difficultyOption.label)}
onChange={() => handleChange(difficultyOption.label)}
>
{difficultyOption.label}
</Tag.CheckableTag>
Expand All @@ -90,11 +107,8 @@ const DifficultySelector: React.FC<DifficultySelectorProps> = ({ selectedDifficu
const TopicSelector: React.FC<TopicSelectorProps> = ({ selectedTopics, onChange}) => {
const topicOptions: SelectProps[] = CategoriesOption;

const handleChange = (topic: string) => {
const newSelectedTopics = selectedTopics.includes(topic)
? selectedTopics.filter(selectedTopic => selectedTopic !== topic)
: [...selectedTopics, topic];
onChange(newSelectedTopics);
const handleChange = (topics: string[]) => {
onChange(topics);
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import './styles.scss';
import { handleCancelMatch } from '../handlers';
import { formatTime } from '@/utils/DateTime';

const JoinedMatchContent: React.FC = () => {

interface Props {
cancel(): void
}

const JoinedMatchContent: React.FC<Props> = ({cancel}) => {
const matchAlreadyJoined = () => {
throw new Error('Match already joined.');
}
Expand All @@ -36,12 +41,11 @@ const JoinedMatchContent: React.FC = () => {
</div>
<button className="joined-match-deactivated-button"
disabled
onClick={() => matchAlreadyJoined()}
>
Joined
</button>
<button className="cancel-match-button"
onClick={() => handleCancelMatch()}
onClick={cancel}
>
Cancel
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import './styles.scss';
import { handleReselectMatchOptions, handleRetryMatch } from '../handlers';
import { formatTime } from '@/utils/DateTime';

const MatchCancelledContent: React.FC = () => {
interface Props {
retry(): void,
reselect(): void,
canceledIn: number,
}

const MatchCancelledContent: React.FC<Props> = ({retry, reselect, canceledIn}) => {
return (
<div className="match-cancelled-content">
<div className="cancel-icon-container">
Expand All @@ -20,15 +26,15 @@ const MatchCancelledContent: React.FC = () => {
</div>
<div className="match-status-label">Match Cancelled!</div>
<div className="match-status-message">
Your match request has been cancelled after waiting {formatTime(83)}
Your match request has been cancelled after waiting {formatTime(canceledIn)}
</div>
<button className="retry-match-button"
onClick={() => handleRetryMatch()}
{/* <button className="retry-match-button"
onClick={retry}
>
Retry
</button>
</button> */}
<button className="reselect-match-options-button"
onClick={() => handleReselectMatchOptions()}
onClick={reselect}
>
Reselect Options
</button>
Expand Down
Loading