1
- import * as React from ' react' ;
1
+ import * as React from " react" ;
2
2
import { Box } from "@mui/material" ;
3
- import Button from '@mui/material/Button' ;
4
- import InputLabel from '@mui/material/InputLabel' ;
5
- import MenuItem from '@mui/material/MenuItem' ;
6
- import FormControl from '@mui/material/FormControl' ;
7
- import Select , { SelectChangeEvent } from '@mui/material/Select' ;
8
- import { useNavigate } from 'react-router-dom' ; // Import useNavigate for redirection
9
- import socket from './socket' ;
3
+ import Button from "@mui/material/Button" ;
4
+ import Snackbar from "@mui/material/Snackbar" ;
5
+ import Alert from "@mui/material/Alert" ;
6
+ import { useNavigate } from "react-router-dom" ;
7
+ import socket from "./socket" ;
8
+ import { getAllQuestions } from "../../api/questions/data" ;
9
+ import { useAuth } from "../../auth/auth.context" ;
10
+ import { sha256 } from "js-sha256" ;
11
+ import Question from "../Questions/Question" ;
10
12
11
13
const style = {
12
- position : ' absolute' as ' absolute' ,
13
- top : ' 50%' ,
14
- left : ' 50%' ,
15
- transform : ' translate(-50%, -50%)' ,
16
- width : ' 50%' ,
17
- display : ' flex-wrap' ,
18
- maxHeight : ' 60%' ,
19
- justifyContent : "center" ,
20
- textAlign : "center" ,
21
- bgcolor : ' background.paper' ,
22
- border : ' 2px solid #000' ,
23
- boxShadow : 24 ,
24
- overflow : ' auto' ,
25
- p : 4 ,
14
+ position : " absolute" as " absolute" ,
15
+ top : " 50%" ,
16
+ left : " 50%" ,
17
+ transform : " translate(-50%, -50%)" ,
18
+ width : " 50%" ,
19
+ display : " flex-wrap" ,
20
+ maxHeight : " 60%" ,
21
+ justifyContent : "center" ,
22
+ textAlign : "center" ,
23
+ bgcolor : " background.paper" ,
24
+ border : " 2px solid #000" ,
25
+ boxShadow : 24 ,
26
+ overflow : " auto" ,
27
+ p : 4 ,
26
28
} ;
27
29
30
+
31
+
32
+ const titleStyle = {
33
+ fontSize : "2rem" // Increase the title size
34
+ } ;
35
+
36
+ const subtitleStyle = {
37
+ fontSize : "1.5rem" ,
38
+ padding :'10px' ,
39
+
40
+ // Increase the subtitle size
41
+ } ;
42
+
43
+
44
+ const dropdownStyle = {
45
+ fontSize : "1.5rem" , // Increase the font size
46
+ padding : "10px" , // Add padding for a bigger and more clickable area
47
+ margin : "10px 0" // Space out the dropdowns a bit more
48
+ } ;
49
+
50
+
28
51
const MatchingForm = React . forwardRef ( function MatchingForm ( ) {
29
- const [ difficulty , setDifficulty ] = React . useState ( '' ) ;
30
- const [ category , setCategory ] = React . useState ( '' ) ;
31
- const [ isMatching , setIsMatching ] = React . useState ( false ) ; // Track matching state
32
- const navigate = useNavigate ( ) ; // Get navigate for redirection
52
+ const [ difficulty , setDifficulty ] = React . useState ( "Easy" ) ;
53
+ const [ category , setCategory ] = React . useState ( "Strings" ) ;
54
+ const [ categoryList , setCategoryList ] = React . useState < string [ ] > ( [ ] ) ;
55
+ const [ isMatching , setIsMatching ] = React . useState ( false ) ;
56
+ const navigate = useNavigate ( ) ;
57
+ const { user } = useAuth ( ) ;
58
+ const userEmail = user ?. email ;
59
+ const [ openSnackbar , setOpenSnackbar ] = React . useState ( false ) ;
33
60
34
- const handleDiffChange = ( event : SelectChangeEvent ) => {
35
- setDifficulty ( event . target . value ) ;
61
+ const handleConnect = async ( ) => {
62
+ const preferences = {
63
+ userEmail,
64
+ difficulty,
65
+ category,
36
66
} ;
67
+ // if no such question with difficulty and category, show a pop up saying no question
68
+ // matching the requirements
37
69
38
- const handleCatChange = ( event : SelectChangeEvent ) => {
39
- setCategory ( event . target . value ) ;
40
- } ;
70
+ const questions = await getAllQuestions ( ) ;
71
+ const filteredQuestions = questions . data . filter ( ( q : any ) => {
72
+ return q . categories . includes ( category ) && q . difficulty === difficulty ;
73
+ } ) ;
41
74
42
- const handleConnect = ( ) => {
43
- const preferences = {
44
- difficulty,
45
- category,
46
- } ;
75
+ if ( filteredQuestions . length === 0 ) {
76
+ setOpenSnackbar ( true ) ;
77
+ return ;
78
+ }
47
79
48
- // Emit the startMatching event to the server with user preferences
49
- socket . emit ( 'startMatching' , preferences ) ;
80
+ socket . emit ( "startMatching" , preferences ) ;
81
+ setIsMatching ( true ) ;
82
+ } ;
50
83
51
- // Set matching state to true when attempting to match
52
- setIsMatching ( true ) ;
53
- } ;
84
+ const generateConsistentRandomIndex = ( seed : any , arrayLength : number ) => {
85
+ return seed % arrayLength ;
86
+ } ;
87
+
88
+ const getQuestions = async ( seed : any ) => {
89
+ const questions = await getAllQuestions ( ) ;
90
+ const filteredQuestions = questions . data . filter ( ( q : any ) => {
91
+ return q . categories . includes ( category ) && q . difficulty === difficulty ;
92
+ } ) ;
54
93
55
- // Handle the "matchFound" event from the server
56
- React . useEffect ( ( ) => {
57
- socket . on ( 'matchFound' , ( matchedUserPreferences ) => {
58
- // Handle the matched user's preferences here
59
- console . log ( 'Match Found:' , matchedUserPreferences ) ;
60
- setIsMatching ( false ) ; // Set matching state to false
61
- // Redirect to the question page with the code editor
62
- navigate ( '/question' ) ; // Update the route as needed
63
- } ) ;
64
-
65
- // Clean up the event listener when the component unmounts
66
- return ( ) => {
67
- socket . off ( 'matchFound' ) ;
68
- } ;
69
- } , [ navigate ] ) ;
70
-
71
- return (
72
- < Box sx = { style } >
73
- < h2 > < center > Please select a difficulty and question category.</ center > </ h2 >
74
- < h4 > < center > We will attempt to connect you with a user who chose the same options as you within 30 seconds.</ center > </ h4 >
75
- < FormControl sx = { { mt : 1 , width : '100%' } } >
76
- < InputLabel id = "demo-simple-select-helper-label" > Difficulty</ InputLabel >
77
- < Select
78
- labelId = "demo-simple-select-helper-label"
79
- id = "demo-simple-select-helper"
80
- value = { difficulty }
81
- label = "Difficulty"
82
- onChange = { handleDiffChange }
83
- >
84
- < MenuItem value = "" >
85
- < em > None</ em >
86
- </ MenuItem >
87
- < MenuItem value = { 'Easy' } > Easy</ MenuItem >
88
- < MenuItem value = { 'Medium' } > Medium</ MenuItem >
89
- < MenuItem value = { 'Hard' } > Hard</ MenuItem >
90
- </ Select >
91
- </ FormControl >
92
- < FormControl sx = { { mt : 1 , mb : 1 , width : '100%' } } >
93
- < InputLabel id = "demo-simple-select-helper-label" > Category</ InputLabel >
94
- < Select
95
- labelId = "demo-simple-select-helper-label"
96
- id = "demo-simple-select-helper"
97
- value = { category }
98
- label = "Category"
99
- onChange = { handleCatChange }
100
- >
101
- < MenuItem value = "" >
102
- < em > None</ em >
103
- </ MenuItem >
104
- < MenuItem value = { 'Algo' } > Algo</ MenuItem >
105
- < MenuItem value = { 'ML' } > ML</ MenuItem >
106
- </ Select >
107
- </ FormControl >
108
- { isMatching ? (
109
- < div > Loading...</ div > // Show loading spinner
110
- ) : (
111
- < Button sx = { { mt : '5%' } } variant = "contained" onClick = { handleConnect } >
112
- Connect
113
- </ Button >
114
- ) }
115
- </ Box >
94
+ const randomIndex = generateConsistentRandomIndex (
95
+ seed ,
96
+ filteredQuestions . length
116
97
) ;
98
+ const selectedQuestion = filteredQuestions [ randomIndex ] ;
99
+ if ( ! selectedQuestion ) {
100
+ return 1 ;
101
+ }
102
+ const selectedId = selectedQuestion . id ;
103
+ return selectedId ;
104
+ } ;
105
+
106
+ React . useEffect ( ( ) => {
107
+ async function getCategories ( ) {
108
+ const questionsData = ( await getAllQuestions ( ) ) as { data : Question [ ] } ;
109
+ const allCategories = questionsData . data . reduce (
110
+ ( acc : string [ ] , question : Question ) => {
111
+ return [ ...acc , ...question . categories ] ;
112
+ } ,
113
+ [ ]
114
+ ) ;
115
+ const uniqueCategories = Array . from ( new Set ( allCategories ) ) ;
116
+ setCategoryList ( uniqueCategories ) ;
117
+ }
118
+ getCategories ( ) ;
119
+ } , [ ] ) ;
120
+
121
+ React . useEffect ( ( ) => {
122
+ socket . on ( "matchFound" , async ( matchedUserPreferences ) => {
123
+ console . log ( "Match Found:" , matchedUserPreferences ) ;
124
+ const seed = matchedUserPreferences . seed ;
125
+ const matchedUser = matchedUserPreferences . matchedUserPreferences ;
126
+ setIsMatching ( false ) ;
127
+ const qId = await getQuestions ( seed ) ;
128
+ const hashedEmailOne = sha256 ( userEmail || "" ) ;
129
+ const hashedEmailTwo = sha256 ( matchedUser . userEmail ) ;
130
+ navigate ( `/collab/question/${ qId } /${ hashedEmailOne } /${ hashedEmailTwo } ` ) ;
131
+ } ) ;
132
+
133
+ return ( ) => {
134
+ socket . off ( "matchFound" ) ;
135
+ } ;
136
+ } , [ difficulty , category , userEmail , navigate ] ) ;
137
+
138
+ return (
139
+ < Box sx = { style } >
140
+ < h2 style = { titleStyle } >
141
+ < center > Please select a difficulty and question category.</ center >
142
+ </ h2 >
143
+ < h4 style = { subtitleStyle } >
144
+ < center >
145
+ We will attempt to connect you with a user who chose the same options
146
+ as you within 30 seconds.
147
+ </ center >
148
+ </ h4 >
149
+ < div >
150
+ < label htmlFor = "difficulty" style = { subtitleStyle } > Difficulty:</ label >
151
+ < select
152
+ id = "difficulty"
153
+ value = { difficulty }
154
+ onChange = { ( e ) => setDifficulty ( e . target . value ) }
155
+ style = { dropdownStyle }
156
+ >
157
+ < option value = "" > None</ option >
158
+ < option value = "Easy" > Easy</ option >
159
+ < option value = "Medium" > Medium</ option >
160
+ < option value = "Hard" > Hard</ option >
161
+ </ select >
162
+ </ div >
163
+ < div >
164
+ < label htmlFor = "category" style = { subtitleStyle } > Category:</ label >
165
+ < select
166
+ id = "category"
167
+ value = { category }
168
+ onChange = { ( e ) => setCategory ( e . target . value ) }
169
+ style = { dropdownStyle }
170
+ >
171
+ < option value = "" > None</ option >
172
+ { categoryList . map ( ( cat ) => (
173
+ < option key = { cat } value = { cat } >
174
+ { cat }
175
+ </ option >
176
+ ) ) }
177
+ </ select >
178
+ </ div >
179
+ { isMatching ? (
180
+ < div > Loading...</ div >
181
+ ) : (
182
+ < Button sx = { { mt : "5%" } } variant = "contained" onClick = { handleConnect } >
183
+ Connect
184
+ </ Button >
185
+ ) }
186
+
187
+ < Snackbar
188
+ open = { openSnackbar }
189
+ autoHideDuration = { 6000 }
190
+ onClose = { ( ) => setOpenSnackbar ( false ) }
191
+ anchorOrigin = { { vertical : "top" , horizontal : "center" } }
192
+ >
193
+ < Alert
194
+ onClose = { ( ) => setOpenSnackbar ( false ) }
195
+ severity = "warning"
196
+ sx = { { width : "100%" } }
197
+ >
198
+ No questions match the selected difficulty and category.
199
+ </ Alert >
200
+ </ Snackbar >
201
+ </ Box >
202
+ ) ;
117
203
} ) ;
118
204
119
- export default MatchingForm ;
205
+ export default MatchingForm ;
0 commit comments