Skip to content

Commit d32a601

Browse files
committed
Lift form state to Modal, add retry button
1 parent 74baa54 commit d32a601

File tree

6 files changed

+81
-111
lines changed

6 files changed

+81
-111
lines changed

apps/frontend/src/app/matching/MatchingModal.tsx

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useState, useEffect } from 'react';
22
import {
3+
Form,
34
Modal,
45
} from 'antd';
56
import 'typeface-montserrat';
@@ -11,12 +12,18 @@ import JoinedMatchContent from './modalContent/JoinedMatchContent';
1112
import MatchNotFoundContent from './modalContent/MatchNotFoundContent';
1213
import MatchCancelledContent from './modalContent/MatchCancelledContent';
1314
import useMatching from '../services/use-matching';
15+
import { ValidateUser } from '../services/user';
1416

1517
interface MatchingModalProps {
1618
isOpen: boolean;
1719
close: () => void;
1820
}
1921

22+
export interface MatchParams {
23+
topics: string[],
24+
difficulties: string[],
25+
}
26+
2027
const MatchingModal: React.FC<MatchingModalProps> = ({ isOpen, close: _close }) => {
2128
const matchingState = useMatching();
2229
const [closedType, setClosedType] = useState<"finding" | "cancelled" | "joined">("finding");
@@ -32,6 +39,16 @@ const MatchingModal: React.FC<MatchingModalProps> = ({ isOpen, close: _close })
3239
_close();
3340
}
3441

42+
const startMatch = matchingState.state == "closed" || matchingState.state == "timeout" ? async (params: MatchParams): Promise<void> => {
43+
const user = await ValidateUser();
44+
matchingState.start({
45+
email: user.data.email,
46+
username: user.data.username,
47+
type: "match_request",
48+
...params
49+
});
50+
} : undefined;
51+
3552
const renderModalContent = () => {
3653
switch (matchingState.state) {
3754
case 'closed':
@@ -43,7 +60,6 @@ const MatchingModal: React.FC<MatchingModalProps> = ({ isOpen, close: _close })
4360
reselect={() => {
4461
setClosedType("finding");
4562
}}
46-
retry={() => {}}
4763
canceledIn={timeoutAfter}
4864
/>;
4965
case "joined":
@@ -72,7 +88,7 @@ const MatchingModal: React.FC<MatchingModalProps> = ({ isOpen, close: _close })
7288
case 'starting':
7389
return <FindMatchContent beginMatch={() => {}}/>
7490
case 'found':
75-
return <MatchFoundContent
91+
return <MatchFoundContent
7692
cancel={() => {
7793
matchingState.ok();
7894
setClosedType("cancelled");
@@ -85,7 +101,7 @@ const MatchingModal: React.FC<MatchingModalProps> = ({ isOpen, close: _close })
85101
name2={matchingState.info.partnerName}
86102
/>
87103
case 'timeout':
88-
return <MatchNotFoundContent reselect={matchingState.ok} retry={() => {}} timedOutIn={10}/>;
104+
return <MatchNotFoundContent reselect={matchingState.ok} timedOutIn={10}/>;
89105
default:
90106
throw new Error('Invalid matching state.');
91107
}
@@ -99,7 +115,15 @@ const MatchingModal: React.FC<MatchingModalProps> = ({ isOpen, close: _close })
99115
maskClosable={false}
100116
className="modal"
101117
>
102-
{renderModalContent()}
118+
<Form<MatchParams>
119+
name="match"
120+
onFinish={startMatch}
121+
initialValues={{
122+
topics: [],
123+
difficulties: [],
124+
}}>
125+
{renderModalContent()}
126+
</Form>
103127
{isClosable && (
104128
<button className="close-button" onClick={close}>Close</button>
105129
)}

apps/frontend/src/app/matching/modalContent/FindMatchContent.tsx

Lines changed: 36 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {
33
Tag,
44
Select,
55
Space,
6+
Form,
7+
Button,
68
} from 'antd';
79
import {
810
DifficultyOption,
@@ -13,119 +15,60 @@ import 'typeface-montserrat';
1315
import './styles.scss';
1416
import { ValidateUser } from "@/app/services/user"
1517
import { type MatchRequestParams } from '@/app/services/use-matching';
16-
17-
interface DifficultySelectorProps {
18-
className?: string;
19-
selectedDifficulties: string[];
20-
onChange: (difficulties: string[]) => void;
21-
}
22-
23-
interface TopicSelectorProps {
24-
className?: string;
25-
selectedTopics: string[];
26-
onChange: (topics: string[]) => void;
27-
}
18+
import { MatchParams } from '../MatchingModal';
2819

2920
interface Props {
3021
beginMatch(request: MatchRequestParams): void
3122
}
3223

3324
const FindMatchContent: React.FC<Props> = ({ beginMatch }) => {
34-
const [selectedDifficulties, setSelectedDifficulties] = useState<string[]>([]);
35-
const [selectedTopics, setSelectedTopics] = useState<string[]>([]);
36-
const [isLoading, setIsLoading] = useState(false);
37-
38-
const handleDifficultyChange = (difficulties: string[]) => {
39-
setSelectedDifficulties(difficulties);
40-
};
41-
42-
const handleTopicChange = (topics: string[]) => {
43-
setSelectedTopics(topics);
44-
};
45-
4625
return (
4726
<div>
4827
<div className="find-match-title">Find Match</div>
4928
<div className="difficulty-label">Difficulty</div>
5029
<div className="difficulty-selector">
51-
<DifficultySelector
52-
selectedDifficulties={selectedDifficulties}
53-
onChange={handleDifficultyChange}
54-
/>
30+
<Form.Item<MatchParams> name="difficulties">
31+
{/* @ts-ignore Not required to pass value and onChange as since this is handled by Form.Item wrapper*/}
32+
<TagInput/>
33+
</Form.Item>
5534
</div>
5635
<div className="topic-label">Topic</div>
5736
<div className="topic-selector">
58-
<TopicSelector
59-
selectedTopics={selectedTopics}
60-
onChange={handleTopicChange}
61-
/>
37+
<Form.Item<MatchParams>
38+
name="topics"
39+
>
40+
<Select mode="multiple" allowClear placeholder="Select Topic(s)" options={CategoriesOption}/>
41+
</Form.Item>
6242
</div>
63-
<button className="find-match-button"
64-
onClick={async () => {
65-
setIsLoading(true);
66-
const user = await ValidateUser();
67-
beginMatch({
68-
email: user.data.email,
69-
username: user.data.username,
70-
type: "match_request",
71-
difficulties: selectedDifficulties,
72-
topics: selectedTopics,
73-
})
74-
}}
75-
disabled={isLoading}
76-
>
77-
Find Match
43+
<button className="find-match-button" type="submit">
44+
FIND
7845
</button>
7946
</div>
8047
)
8148
}
8249

83-
const DifficultySelector: React.FC<DifficultySelectorProps> = ({ selectedDifficulties, onChange}) => {
84-
const handleChange = (difficulty: string) => {
85-
const newSelectedDifficulties = selectedDifficulties.includes(difficulty)
86-
? selectedDifficulties.filter(selectedDifficulty => selectedDifficulty !== difficulty)
87-
: [...selectedDifficulties, difficulty];
88-
onChange(newSelectedDifficulties);
89-
}
90-
91-
return (
92-
<div>
93-
{DifficultyOption.map(difficultyOption => (
94-
<Tag.CheckableTag
95-
className={`difficulty-tag ${difficultyOption.value}-tag`}
96-
key={difficultyOption.value}
97-
checked={selectedDifficulties.includes(difficultyOption.label)}
98-
onChange={() => handleChange(difficultyOption.label)}
99-
>
100-
{difficultyOption.label}
101-
</Tag.CheckableTag>
102-
))}
103-
</div>
104-
)
105-
}
106-
107-
const TopicSelector: React.FC<TopicSelectorProps> = ({ selectedTopics, onChange}) => {
108-
const topicOptions: SelectProps[] = CategoriesOption;
109-
110-
const handleChange = (topics: string[]) => {
111-
onChange(topics);
112-
}
113-
114-
return (
115-
<div className="find-match-content">
116-
<Space className="select-space" direction="vertical">
117-
<Select
118-
className="select-topic"
119-
mode="multiple"
120-
allowClear
121-
style={{ width: '100%' }}
122-
placeholder="Select Topic(s)"
123-
onChange={handleChange}
124-
options={topicOptions}
125-
/>
126-
</Space>
127-
</div>
128-
)
50+
const TagInput: React.FC<{
51+
value: string[],
52+
onChange(value: string[]): void,
53+
}> = ({ value, onChange }) => {
54+
return <>
55+
{DifficultyOption.map(difficultyOption => (
56+
<Tag.CheckableTag
57+
className={`difficulty-tag ${difficultyOption.value}-tag`}
58+
key={difficultyOption.value}
59+
checked={value.includes(difficultyOption.label)}
60+
onChange={(enabled) => {
61+
onChange(
62+
enabled
63+
? [...value, difficultyOption.label]
64+
: value.filter(diff => diff !== difficultyOption.label)
65+
)
66+
}}
67+
>
68+
{difficultyOption.label}
69+
</Tag.CheckableTag>
70+
))}
71+
</>
12972
}
13073

13174
export default FindMatchContent;

apps/frontend/src/app/matching/modalContent/MatchCancelledContent.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import 'typeface-montserrat';
33
import './styles.scss';
44
import { handleReselectMatchOptions, handleRetryMatch } from '../handlers';
55
import { formatTime } from '@/utils/DateTime';
6+
import { Button, Form } from 'antd';
67

78
interface Props {
8-
retry(): void,
99
reselect(): void,
1010
canceledIn: number,
1111
}
1212

13-
const MatchCancelledContent: React.FC<Props> = ({retry, reselect, canceledIn}) => {
13+
const MatchCancelledContent: React.FC<Props> = ({reselect, canceledIn}) => {
1414
return (
1515
<div className="match-cancelled-content">
1616
<div className="cancel-icon-container">
@@ -28,11 +28,11 @@ const MatchCancelledContent: React.FC<Props> = ({retry, reselect, canceledIn}) =
2828
<div className="match-status-message">
2929
Your match request has been cancelled after waiting {formatTime(canceledIn)}
3030
</div>
31-
{/* <button className="retry-match-button"
32-
onClick={retry}
33-
>
34-
Retry
35-
</button> */}
31+
<Form.Item noStyle>
32+
<button className="retry-match-button" type="submit">
33+
Retry
34+
</button>
35+
</Form.Item>
3636
<button className="reselect-match-options-button"
3737
onClick={reselect}
3838
>

apps/frontend/src/app/matching/modalContent/MatchNotFoundContent.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import 'typeface-montserrat';
33
import './styles.scss';
44
import { handleReselectMatchOptions, handleRetryMatch } from '../handlers';
55
import { formatTime } from '@/utils/DateTime';
6+
import { Button, Form } from 'antd';
7+
import { MatchParams } from '../MatchingModal';
68

79
const MatchNotFoundContent: React.FC<{
8-
retry(): void,
910
reselect(): void,
1011
timedOutIn: number,
1112
}> = ({
12-
retry, reselect, timedOutIn
13+
reselect, timedOutIn
1314
}) => {
1415
return (
1516
<div className="joined-match-content">
@@ -28,11 +29,11 @@ const MatchNotFoundContent: React.FC<{
2829
<div className="match-status-message">
2930
Sorry, we could not find a match after {formatTime(timedOutIn)}
3031
</div>
31-
{/* <button className="retry-match-button"
32-
onClick={retry}
33-
>
34-
Retry
35-
</button> */}
32+
<Form.Item<MatchParams> noStyle>
33+
<button className="retry-match-button" type="submit">
34+
Retry
35+
</button>
36+
</Form.Item>
3637
<button className="reselect-match-options-button"
3738
onClick={reselect}
3839
>

apps/frontend/src/app/services/use-matching.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export default function useMatching(): MatchState {
9898
setSte({
9999
state: "timeout",
100100
ok: cancel,
101+
start
101102
});
102103
}
103104

apps/frontend/src/contexts/websocketcontext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type MatchState = SocketState | {
2626
} | {
2727
state: "timeout";
2828
ok(): void;
29+
start(req: MatchRequestParams): void;
2930
};
3031

3132
export const WebSocketContext = createContext<MatchState | null>(null);

0 commit comments

Comments
 (0)