Skip to content

Commit 221b2bf

Browse files
Merge branch 'develop' into frontend/fixes
2 parents 4273fc9 + 1526354 commit 221b2bf

File tree

14 files changed

+129
-123
lines changed

14 files changed

+129
-123
lines changed
File renamed without changes.

client/public/audio/Dan_incorrect/Dan-incorrect-3..wav renamed to client/public/audio/Dan_incorrect/Dan-incorrect-3.wav

File renamed without changes.

client/public/audio/Dan_incorrect/Dan-firstincorrect.wav renamed to client/public/audio/Dan_incorrect/Dan-incorrect-5.wav

File renamed without changes.
2.39 MB
Loading
2.36 MB
Loading
Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,56 @@
1-
import { useEffect, useState } from 'react';
2-
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '../components/ui/dialog';
3-
import SoundPlayer from './SoundPlayer';
1+
import { useEffect, useState } from "react";
2+
import SoundPlayer from "./SoundPlayer";
3+
import ReactDOM from "react-dom";
44

55
interface AnswerResultModalProps {
66
isOpen: boolean;
77
onClose: () => void;
88
isCorrect: boolean;
9-
drDanQuote: string;
109
audioUrl?: string;
1110
}
1211

1312
const AnswerResultModal = ({
1413
isOpen,
1514
onClose,
1615
isCorrect,
17-
drDanQuote,
1816
audioUrl,
1917
}: AnswerResultModalProps) => {
2018
const [playAudio, setPlayAudio] = useState(false);
2119

22-
// ✅ Start or stop audio based on modal open state
2320
useEffect(() => {
2421
if (isOpen) {
2522
setPlayAudio(true);
26-
} else {
27-
setPlayAudio(false);
2823
}
2924
}, [isOpen]);
3025

31-
return (
32-
<Dialog open={isOpen} onOpenChange={(open) => { if (!open) onClose(); }}>
33-
<DialogContent className="rounded-2xl shadow-xl text-center">
34-
<DialogHeader>
35-
<DialogTitle className={`text-2xl font-bold ${isCorrect ? 'text-green-500' : 'text-red-500'}`}>
36-
{isCorrect ? "Correct!" : "Wrong!"}
37-
</DialogTitle>
38-
<DialogDescription className="text-lg mt-2">
39-
{drDanQuote}
40-
</DialogDescription>
41-
</DialogHeader>
42-
43-
{/* ✅ Use Howler to play full clip, then close modal */}
44-
{audioUrl && (
45-
<SoundPlayer
46-
src={audioUrl}
47-
playing={playAudio}
48-
onEnd={() => {
49-
setPlayAudio(false);
50-
onClose();
51-
}}
52-
/>
53-
)}
54-
</DialogContent>
55-
</Dialog>
26+
if (!isOpen) return null;
27+
28+
const drDanImage = isCorrect
29+
? "/avatars/DrDanCorrect.png"
30+
: "/avatars/DrDanWrong.png";
31+
32+
const modalContent = (
33+
<div className="fixed top-1/2 right-4 transform -translate-y-1/2 z-[9999] w-[200px] flex flex-col items-center gap-2 animate-fadeIn pointer-events-none">
34+
<img
35+
src={drDanImage}
36+
alt="Dr. Dan"
37+
className="w-[160px] h-auto object-contain"
38+
/>
39+
{audioUrl && (
40+
<SoundPlayer
41+
src={audioUrl}
42+
playing={playAudio}
43+
onEnd={() => {
44+
setPlayAudio(false);
45+
onClose();
46+
}}
47+
/>
48+
)}
49+
</div>
5650
);
51+
52+
return ReactDOM.createPortal(modalContent, document.body);
5753
};
5854

5955
export default AnswerResultModal;
56+
//commiting"

client/src/components/DanismEvent.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ const DanismEvent = () => {
1818
'/audio/Dan_correct/Dan-correct-2.wav',
1919
'/audio/Dan_correct/Dan-correct-3.wav',
2020
'/audio/Dan_correct/Dan-correct-4.wav',
21+
'/audio/Dan_correct/Dan-5inarow.wav',
2122
'/audio/Dan_incorrect/Dan-incorrect-1.wav',
2223
'/audio/Dan_incorrect/Dan-incorrect-2.wav',
2324
'/audio/Dan_incorrect/Dan-incorrect-3.wav',
2425
'/audio/Dan_incorrect/Dan-incorrect-4.wav',
25-
'/audio/drdan_fallback.mp3'
26+
'/audio/Dan_incorrect/Dan-incorrect-5.wav'
2627
]);
2728
}, []);
2829

client/src/components/screens/Questions.tsx

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { useParams, useNavigate } from 'react-router-dom';
33
import AnswerResultModal from '../AnswerResultModal';
44
import ReactMarkdown from 'react-markdown';
55
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
6-
import vscDarkPlus from "react-syntax-highlighter/dist/esm/styles/prism/vsc-dark-plus";
6+
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';
7+
import { preloadSounds } from '../../utils/preloadSounds';
78

89
interface Question {
910
snippet?: string;
@@ -22,20 +23,25 @@ const Questions: React.FC = () => {
2223
const [userWasCorrect, setUserWasCorrect] = useState(false);
2324
const [audioUrl, setAudioUrl] = useState('');
2425

26+
useEffect(() => {
27+
preloadSounds([
28+
'/audio/Dan_correct/Dan-correct-1.wav',
29+
'/audio/Dan_correct/Dan-correct-2.wav',
30+
'/audio/Dan_correct/Dan-correct-3.wav',
31+
'/audio/Dan_correct/correctStar.wav',
32+
'/audio/Dan_incorrect/Dan-incorrect-1.wav',
33+
'/audio/Dan_incorrect/Dan-incorrect-2.wav',
34+
'/audio/Dan_incorrect/Dan-incorrect-3.wav',
35+
'/audio/Dan_incorrect/Dan-incorrect-4.wav',
36+
'/audio/Dan_incorrect/firstincorrect.wav'
37+
]);
38+
}, []);
39+
2540
const minionMap: Record<string, string> = {
26-
q1: 'NullByte',
27-
q2: 'Dbug',
28-
q3: 'Typerrorasaurus',
29-
q4: 'PieThon',
30-
q5: 'Codezilla',
41+
q1: 'NullByte', q2: 'Dbug', q3: 'Typerrorasaurus', q4: 'PieThon', q5: 'Codezilla',
3142
};
32-
3343
const difficultyMap: Record<string, string> = {
34-
NullByte: 'easy',
35-
Dbug: 'medium',
36-
Typerrorasaurus: 'medium-hard',
37-
PieThon: 'hard',
38-
Codezilla: 'boss',
44+
NullByte: 'easy', Dbug: 'medium', Typerrorasaurus: 'medium-hard', PieThon: 'hard', Codezilla: 'boss',
3945
};
4046

4147
useEffect(() => {
@@ -67,15 +73,11 @@ const Questions: React.FC = () => {
6773
value: choice.replace(/^[A-Da-d]\)\s*/, ''),
6874
}));
6975

70-
const correctFromLetter =
71-
raw.answer?.length === 1
72-
? raw.choices[raw.answer.charCodeAt(0) - 65]?.replace(/^[A-Da-d]\)\s*/, '')
73-
: raw.answer;
76+
const correctFromLetter = raw.answer?.length === 1
77+
? raw.choices[raw.answer.charCodeAt(0) - 65]?.replace(/^[A-Da-d]\)\s*/, '')
78+
: raw.answer;
7479

75-
const correctFromIndex =
76-
typeof raw.correctIndex === 'number'
77-
? raw.choices[raw.correctIndex]
78-
: null;
80+
const correctFromIndex = typeof raw.correctIndex === 'number' ? raw.choices[raw.correctIndex] : null;
7981

8082
setQuestion({
8183
snippet: raw.snippet,
@@ -89,24 +91,22 @@ const Questions: React.FC = () => {
8991
};
9092

9193
fetchQuestion();
92-
return () => {
93-
didCancel = true;
94-
};
94+
return () => { didCancel = true; };
9595
}, [id]);
9696

9797
const getRandomAudio = (isCorrect: boolean): string => {
9898
const correctClips = [
9999
'/audio/Dan_correct/Dan-correct-1.wav',
100100
'/audio/Dan_correct/Dan-correct-2.wav',
101101
'/audio/Dan_correct/Dan-correct-3.wav',
102-
'/audio/Dan_correct/correctStar.wav',
102+
'/audio/Dan_correct/Dan-correct-4.wav'
103103
];
104104
const incorrectClips = [
105105
'/audio/Dan_incorrect/Dan-incorrect-1.wav',
106106
'/audio/Dan_incorrect/Dan-incorrect-2.wav',
107107
'/audio/Dan_incorrect/Dan-incorrect-3.wav',
108108
'/audio/Dan_incorrect/Dan-incorrect-4.wav',
109-
'/audio/Dan_incorrect/firstincorrect.wav',
109+
'/audio/Dan_incorrect/Dan-incorrect-5.wav'
110110
];
111111
const pool = isCorrect ? correctClips : incorrectClips;
112112
return pool[Math.floor(Math.random() * pool.length)];
@@ -125,48 +125,46 @@ const Questions: React.FC = () => {
125125
const handleBack = () => navigate('/map');
126126

127127
return (
128-
<div className="question-screen max-h-screen overflow-y-auto p-6 max-w-xl mx-auto text-center">
129-
<h1 className="text-xl font-semibold mb-2">
130-
Question {id?.replace('q', '') || ''}
131-
</h1>
128+
<div className="relative question-screen max-h-screen overflow-y-auto p-6 max-w-xl mx-auto text-center">
129+
<h1 className="text-xl font-semibold mb-2">Question {id?.replace('q', '') || ''}</h1>
132130

133131
{!question ? (
134132
<p>Loading question...</p>
135133
) : (
136134
<>
137135
<div className="mb-4 text-white text-base text-left whitespace-pre-wrap">
138-
{question.snippet?.trim() ? (
136+
{question.snippet?.trim() && (
139137
<SyntaxHighlighter
140138
language="javascript"
141139
style={vscDarkPlus}
142140
showLineNumbers
143141
customStyle={{
144-
border: '2px solid red',
145-
borderRadius: '0.5rem',
146-
marginBottom: '1rem',
147-
maxHeight: '220px',
148-
overflowY: 'auto',
149-
paddingRight: '1rem',
150-
fontSize: '0.75rem',
142+
border: '2px solid red', borderRadius: '0.5rem',
143+
marginBottom: '1rem', maxHeight: '220px',
144+
overflowY: 'auto', paddingRight: '1rem', fontSize: '0.75rem'
151145
}}
152146
>
153147
{question.snippet}
154148
</SyntaxHighlighter>
155-
) : null}
149+
)}
156150

157151
<ReactMarkdown
158152
components={{
159-
code({ inline, children, ...props }: { inline?: boolean; children?: React.ReactNode }) {
153+
code({ inline, className, children, ...props }: { inline?: boolean; className?: string; children?: React.ReactNode }) {
160154
return inline ? (
161-
<code className="bg-gray-700 px-1 rounded text-sm" {...props}>
162-
{children}
163-
</code>
155+
<code className="bg-gray-700 px-1 rounded text-sm" {...props}>{children}</code>
164156
) : (
165-
<pre className="bg-gray-800 p-4 rounded-md text-sm font-mono shadow-lg mb-4">
157+
<pre className={`bg-gray-800 p-4 rounded-md text-sm font-mono shadow-lg ${className ?? ''}`}>
166158
<code {...props}>{children}</code>
167159
</pre>
168160
);
169161
},
162+
p({ node, children, ...props }) {
163+
if (Array.isArray(children) && children.length === 1 && typeof children[0] === 'object' && (children[0] as any).type === 'pre') {
164+
return <>{children}</>;
165+
}
166+
return <p {...props}>{children}</p>;
167+
},
170168
}}
171169
>
172170
{question.question}
@@ -209,9 +207,6 @@ const Questions: React.FC = () => {
209207
isOpen={showResult}
210208
onClose={() => setShowResult(false)}
211209
isCorrect={userWasCorrect}
212-
drDanQuote={
213-
userWasCorrect ? "You're getting it, junior dev!" : "Nope — that ain't it!"
214-
}
215210
audioUrl={audioUrl}
216211
/>
217212
</div>

client/src/components/ui/dialog.tsx

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,61 @@
22

33
import * as React from "react";
44
import * as DialogPrimitive from "@radix-ui/react-dialog";
5-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
6-
//import { X } from "lucide-react";
7-
85
import { cn } from "@/utils/utils";
96

7+
// Root dialog elements
108
const Dialog = DialogPrimitive.Root;
119
const DialogTrigger = DialogPrimitive.Trigger;
1210

11+
// Portal container
1312
const DialogPortal = ({ ...props }: DialogPrimitive.DialogPortalProps) => (
14-
< DialogPrimitive.Portal {...props} />
13+
<DialogPrimitive.Portal {...props} />
1514
);
1615

16+
// Optional overlay
1717
const DialogOverlay = React.forwardRef<
1818
React.ComponentRef<typeof DialogPrimitive.Overlay>,
1919
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
2020
>(({ className, ...props }, ref) => (
2121
<DialogPrimitive.Overlay
2222
ref={ref}
23-
className={cn(
24-
"fixed inset-0 z-[60] bg-black/80 backdrop-blur-sm",
25-
className
26-
)}
23+
className={cn("fixed inset-0 z-[60] bg-black/60 backdrop-blur-sm", className)}
2724
{...props}
2825
/>
2926
));
3027
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
3128

29+
// Clean DialogContent (NO red border, NO forced width, NO test box)
3230
const DialogContent = React.forwardRef<
3331
React.ComponentRef<typeof DialogPrimitive.Content>,
3432
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
3533
>(({ className, children, ...props }, ref) => (
3634
<DialogPortal>
3735
<DialogOverlay />
3836
<DialogPrimitive.Content
39-
ref={ref}
40-
className={cn(
41-
"fixed z-[70] bottom-[15%] right-[5%] w-[300px] rounded-xl bg-white/90 dark:bg-gray-900/90 text-black dark:text-white p-4 shadow-xl ring-2 ring-blue-400/40 animate-fadeIn",
42-
className
43-
)}
44-
{...props}
45-
>
46-
47-
<div className="space-y-4">{children}</div>
37+
ref={ref}
38+
className={cn(
39+
"fixed z-[9999] p-0 bg-transparent border-none shadow-none w-fit",
40+
className
41+
)}
42+
{...props}
43+
>
44+
{children}
4845
</DialogPrimitive.Content>
4946
</DialogPortal>
5047
));
5148
DialogContent.displayName = DialogPrimitive.Content.displayName;
5249

50+
// Optional header wrapper
5351
const DialogHeader = ({
5452
className,
5553
...props
5654
}: React.HTMLAttributes<HTMLDivElement>) => (
57-
<div
58-
className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)}
59-
{...props}
60-
/>
55+
<div className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...props} />
6156
);
6257
DialogHeader.displayName = "DialogHeader";
6358

59+
// Optional title
6460
const DialogTitle = React.forwardRef<
6561
React.ComponentRef<typeof DialogPrimitive.Title>,
6662
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
@@ -73,18 +69,20 @@ const DialogTitle = React.forwardRef<
7369
));
7470
DialogTitle.displayName = DialogPrimitive.Title.displayName;
7571

72+
// Optional description
7673
const DialogDescription = React.forwardRef<
7774
React.ComponentRef<typeof DialogPrimitive.Description>,
7875
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
7976
>(({ className, ...props }, ref) => (
8077
<DialogPrimitive.Description
81-
ref={ref}
82-
className={cn("text-sm text-muted-foreground", className)}
83-
{...props}
84-
/>
78+
ref={ref}
79+
className={cn("text-sm text-muted-foreground", className)}
80+
{...props}
81+
/>
8582
));
8683
DialogDescription.displayName = DialogPrimitive.Description.displayName;
8784

85+
// Exports
8886
export {
8987
Dialog,
9088
DialogTrigger,

0 commit comments

Comments
 (0)