update sound / add random feature
Browse files- .DS_Store +0 -0
- client/public/sounds/dice-1.mp3 +0 -0
- client/public/sounds/dice-2.mp3 +0 -0
- client/public/sounds/dice-3.mp3 +0 -0
- client/public/sounds/lock-1.mp3 +0 -0
- client/public/sounds/tick-1.mp3 +0 -0
- client/public/sounds/tick-2.mp3 +0 -0
- client/public/sounds/tick-3.mp3 +0 -0
- client/public/sounds/tick-4.mp3 +0 -0
- client/src/components/StoryChoices.jsx +77 -5
- client/src/components/UniverseSlotMachine.jsx +33 -2
- client/src/contexts/SoundContext.jsx +31 -2
- client/src/pages/Tutorial.jsx +45 -21
.DS_Store
CHANGED
|
Binary files a/.DS_Store and b/.DS_Store differ
|
|
|
client/public/sounds/dice-1.mp3
ADDED
|
Binary file (31.8 kB). View file
|
|
|
client/public/sounds/dice-2.mp3
ADDED
|
Binary file (31.8 kB). View file
|
|
|
client/public/sounds/dice-3.mp3
ADDED
|
Binary file (31.8 kB). View file
|
|
|
client/public/sounds/lock-1.mp3
ADDED
|
Binary file (5.42 kB). View file
|
|
|
client/public/sounds/tick-1.mp3
ADDED
|
Binary file (2.93 kB). View file
|
|
|
client/public/sounds/tick-2.mp3
ADDED
|
Binary file (3.41 kB). View file
|
|
|
client/public/sounds/tick-3.mp3
ADDED
|
Binary file (4.37 kB). View file
|
|
|
client/public/sounds/tick-4.mp3
ADDED
|
Binary file (4.37 kB). View file
|
|
|
client/src/components/StoryChoices.jsx
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
| 13 |
useMediaQuery,
|
| 14 |
useTheme,
|
| 15 |
IconButton,
|
|
|
|
| 16 |
} from "@mui/material";
|
| 17 |
import { useNavigate } from "react-router-dom";
|
| 18 |
import { TalkWithSarah } from "./TalkWithSarah";
|
|
@@ -21,9 +22,24 @@ import { useGame } from "../contexts/GameContext";
|
|
| 21 |
import { storyApi } from "../utils/api";
|
| 22 |
import { useSoundEffect } from "../hooks/useSoundEffect";
|
| 23 |
import CloseIcon from "@mui/icons-material/Close";
|
|
|
|
| 24 |
|
| 25 |
const { initAudioContext } = storyApi;
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
// Function to convert text with ** to Chip elements
|
| 28 |
const formatTextWithBold = (text) => {
|
| 29 |
if (!text) return "";
|
|
@@ -54,6 +70,12 @@ export function StoryChoices() {
|
|
| 54 |
const [sarahRecommendation, setSarahRecommendation] = useState(null);
|
| 55 |
const [showCustomDialog, setShowCustomDialog] = useState(false);
|
| 56 |
const [customChoice, setCustomChoice] = useState("");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
const {
|
| 58 |
choices,
|
| 59 |
onChoice,
|
|
@@ -76,12 +98,26 @@ export function StoryChoices() {
|
|
| 76 |
volume: 0.5,
|
| 77 |
});
|
| 78 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
const lastSegment = getLastSegment();
|
| 80 |
const isLastStep = lastSegment?.is_last_step;
|
| 81 |
const isDeath = lastSegment?.isDeath;
|
| 82 |
const isVictory = lastSegment?.isVictory;
|
| 83 |
const storyText = lastSegment?.rawText || "";
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
if (isGameOver()) {
|
| 86 |
return (
|
| 87 |
<Box
|
|
@@ -193,13 +229,26 @@ export function StoryChoices() {
|
|
| 193 |
</Button>
|
| 194 |
</Box>
|
| 195 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
<Box
|
| 197 |
sx={{
|
| 198 |
display: "flex",
|
| 199 |
flexDirection: "column",
|
| 200 |
alignItems: "center",
|
| 201 |
gap: 1,
|
| 202 |
-
ml: isMobile ? 0 : 4,
|
| 203 |
minWidth: "fit-content",
|
| 204 |
maxWidth: "30%",
|
| 205 |
}}
|
|
@@ -216,7 +265,7 @@ export function StoryChoices() {
|
|
| 216 |
textTransform: "none",
|
| 217 |
}}
|
| 218 |
>
|
| 219 |
-
Write your own
|
| 220 |
</Button>
|
| 221 |
</Box>
|
| 222 |
</>
|
|
@@ -279,7 +328,7 @@ export function StoryChoices() {
|
|
| 279 |
rows={isMobile ? 5 : 4}
|
| 280 |
fullWidth
|
| 281 |
variant="outlined"
|
| 282 |
-
placeholder=
|
| 283 |
value={customChoice}
|
| 284 |
onChange={(e) => setCustomChoice(e.target.value)}
|
| 285 |
sx={{
|
|
@@ -302,7 +351,31 @@ export function StoryChoices() {
|
|
| 302 |
},
|
| 303 |
}}
|
| 304 |
/>
|
| 305 |
-
<Box
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
<Button
|
| 307 |
onClick={() => {
|
| 308 |
if (customChoice.trim()) {
|
|
@@ -317,7 +390,6 @@ export function StoryChoices() {
|
|
| 317 |
disabled={!customChoice.trim()}
|
| 318 |
variant="contained"
|
| 319 |
sx={{
|
| 320 |
-
mt: 1,
|
| 321 |
py: 1.5,
|
| 322 |
px: 4,
|
| 323 |
fontWeight: "bold",
|
|
|
|
| 13 |
useMediaQuery,
|
| 14 |
useTheme,
|
| 15 |
IconButton,
|
| 16 |
+
Tooltip,
|
| 17 |
} from "@mui/material";
|
| 18 |
import { useNavigate } from "react-router-dom";
|
| 19 |
import { TalkWithSarah } from "./TalkWithSarah";
|
|
|
|
| 22 |
import { storyApi } from "../utils/api";
|
| 23 |
import { useSoundEffect } from "../hooks/useSoundEffect";
|
| 24 |
import CloseIcon from "@mui/icons-material/Close";
|
| 25 |
+
import CasinoOutlinedIcon from "@mui/icons-material/CasinoOutlined";
|
| 26 |
|
| 27 |
const { initAudioContext } = storyApi;
|
| 28 |
|
| 29 |
+
// Phrases aléatoires WTF pour le placeholder
|
| 30 |
+
const RANDOM_PLACEHOLDERS = [
|
| 31 |
+
"A dragon appears right above the hero...",
|
| 32 |
+
"Suddenly, all the trees start dancing the macarena...",
|
| 33 |
+
"A time-traveling pizza delivery guy shows up with a mysterious package...",
|
| 34 |
+
"The ground turns into jello and starts wobbling menacingly...",
|
| 35 |
+
"A choir of singing cats descends from the sky...",
|
| 36 |
+
"The hero's shadow detaches itself and starts doing stand-up comedy...",
|
| 37 |
+
"All the nearby rocks transform into vintage toasters...",
|
| 38 |
+
"A portal opens, and out steps the hero's evil twin made entirely of cheese...",
|
| 39 |
+
"The moon starts beatboxing an ominous rhythm...",
|
| 40 |
+
"Every nearby plant suddenly develops a British accent and starts having tea...",
|
| 41 |
+
];
|
| 42 |
+
|
| 43 |
// Function to convert text with ** to Chip elements
|
| 44 |
const formatTextWithBold = (text) => {
|
| 45 |
if (!text) return "";
|
|
|
|
| 70 |
const [sarahRecommendation, setSarahRecommendation] = useState(null);
|
| 71 |
const [showCustomDialog, setShowCustomDialog] = useState(false);
|
| 72 |
const [customChoice, setCustomChoice] = useState("");
|
| 73 |
+
const [currentPlaceholder] = useState(
|
| 74 |
+
() =>
|
| 75 |
+
RANDOM_PLACEHOLDERS[
|
| 76 |
+
Math.floor(Math.random() * RANDOM_PLACEHOLDERS.length)
|
| 77 |
+
]
|
| 78 |
+
);
|
| 79 |
const {
|
| 80 |
choices,
|
| 81 |
onChoice,
|
|
|
|
| 98 |
volume: 0.5,
|
| 99 |
});
|
| 100 |
|
| 101 |
+
// Son de dé
|
| 102 |
+
const playDiceSound = useSoundEffect({
|
| 103 |
+
basePath: "/sounds/dice-",
|
| 104 |
+
numSounds: 3,
|
| 105 |
+
volume: 0.1,
|
| 106 |
+
enabled: true,
|
| 107 |
+
});
|
| 108 |
+
|
| 109 |
const lastSegment = getLastSegment();
|
| 110 |
const isLastStep = lastSegment?.is_last_step;
|
| 111 |
const isDeath = lastSegment?.isDeath;
|
| 112 |
const isVictory = lastSegment?.isVictory;
|
| 113 |
const storyText = lastSegment?.rawText || "";
|
| 114 |
|
| 115 |
+
const getRandomPlaceholder = () => {
|
| 116 |
+
return RANDOM_PLACEHOLDERS[
|
| 117 |
+
Math.floor(Math.random() * RANDOM_PLACEHOLDERS.length)
|
| 118 |
+
];
|
| 119 |
+
};
|
| 120 |
+
|
| 121 |
if (isGameOver()) {
|
| 122 |
return (
|
| 123 |
<Box
|
|
|
|
| 229 |
</Button>
|
| 230 |
</Box>
|
| 231 |
|
| 232 |
+
<Typography
|
| 233 |
+
variant="h6"
|
| 234 |
+
sx={{
|
| 235 |
+
display: { xs: "none", sm: "block" },
|
| 236 |
+
color: "rgba(255,255,255,0.5)",
|
| 237 |
+
fontWeight: "bold",
|
| 238 |
+
fontSize: "1.2rem",
|
| 239 |
+
mx: 2,
|
| 240 |
+
}}
|
| 241 |
+
>
|
| 242 |
+
OR
|
| 243 |
+
</Typography>
|
| 244 |
+
|
| 245 |
<Box
|
| 246 |
sx={{
|
| 247 |
display: "flex",
|
| 248 |
flexDirection: "column",
|
| 249 |
alignItems: "center",
|
| 250 |
gap: 1,
|
| 251 |
+
// ml: isMobile ? 0 : 4,
|
| 252 |
minWidth: "fit-content",
|
| 253 |
maxWidth: "30%",
|
| 254 |
}}
|
|
|
|
| 265 |
textTransform: "none",
|
| 266 |
}}
|
| 267 |
>
|
| 268 |
+
Write your own choice...
|
| 269 |
</Button>
|
| 270 |
</Box>
|
| 271 |
</>
|
|
|
|
| 328 |
rows={isMobile ? 5 : 4}
|
| 329 |
fullWidth
|
| 330 |
variant="outlined"
|
| 331 |
+
placeholder={currentPlaceholder}
|
| 332 |
value={customChoice}
|
| 333 |
onChange={(e) => setCustomChoice(e.target.value)}
|
| 334 |
sx={{
|
|
|
|
| 351 |
},
|
| 352 |
}}
|
| 353 |
/>
|
| 354 |
+
<Box
|
| 355 |
+
sx={{ display: "flex", justifyContent: "flex-end", gap: 1, mt: 1 }}
|
| 356 |
+
>
|
| 357 |
+
<Button
|
| 358 |
+
onClick={() => {
|
| 359 |
+
const randomChoice = getRandomPlaceholder();
|
| 360 |
+
setCustomChoice(randomChoice.slice(0, -3));
|
| 361 |
+
playDiceSound();
|
| 362 |
+
}}
|
| 363 |
+
variant="outlined"
|
| 364 |
+
sx={{
|
| 365 |
+
minWidth: "48px",
|
| 366 |
+
width: "48px",
|
| 367 |
+
height: "48px",
|
| 368 |
+
p: 0,
|
| 369 |
+
borderColor: "rgba(255, 255, 255, 0.23)",
|
| 370 |
+
color: "white",
|
| 371 |
+
"&:hover": {
|
| 372 |
+
borderColor: "white",
|
| 373 |
+
backgroundColor: "rgba(255, 255, 255, 0.08)",
|
| 374 |
+
},
|
| 375 |
+
}}
|
| 376 |
+
>
|
| 377 |
+
<CasinoOutlinedIcon />
|
| 378 |
+
</Button>
|
| 379 |
<Button
|
| 380 |
onClick={() => {
|
| 381 |
if (customChoice.trim()) {
|
|
|
|
| 390 |
disabled={!customChoice.trim()}
|
| 391 |
variant="contained"
|
| 392 |
sx={{
|
|
|
|
| 393 |
py: 1.5,
|
| 394 |
px: 4,
|
| 395 |
fontWeight: "bold",
|
client/src/components/UniverseSlotMachine.jsx
CHANGED
|
@@ -1,10 +1,11 @@
|
|
| 1 |
import React, { useEffect, useRef, useState } from "react";
|
| 2 |
import { Box, Typography, useTheme, useMediaQuery } from "@mui/material";
|
| 3 |
import { motion, useAnimation } from "framer-motion";
|
|
|
|
| 4 |
|
| 5 |
// Animation timing configuration
|
| 6 |
const SLOT_ANIMATION_DURATION = 2; // Duration of each slot animation
|
| 7 |
-
const SLOT_SPEED =
|
| 8 |
const TOTAL_ANIMATION_DURATION = 1; // Total duration for each slot reel in seconds
|
| 9 |
const SLOT_START_DELAY = 2; // Delay between each slot start in seconds
|
| 10 |
|
|
@@ -26,6 +27,9 @@ const SlotReel = ({ words, isActive, finalValue, onComplete, delay = 0 }) => {
|
|
| 26 |
const [isVisible, setIsVisible] = useState(false);
|
| 27 |
const theme = useTheme();
|
| 28 |
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
useEffect(() => {
|
| 31 |
if (isActive) {
|
|
@@ -38,6 +42,7 @@ const SlotReel = ({ words, isActive, finalValue, onComplete, delay = 0 }) => {
|
|
| 38 |
setReelItems(repeatedWords);
|
| 39 |
|
| 40 |
const itemHeight = isMobile ? 60 : 80;
|
|
|
|
| 41 |
const totalHeight = repeatedWords.length * itemHeight;
|
| 42 |
|
| 43 |
setTimeout(() => {
|
|
@@ -49,6 +54,28 @@ const SlotReel = ({ words, isActive, finalValue, onComplete, delay = 0 }) => {
|
|
| 49 |
duration: TOTAL_ANIMATION_DURATION / SLOT_SPEED,
|
| 50 |
ease: [0.25, 0.1, 0.25, 1.0],
|
| 51 |
times: [0, 1],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
},
|
| 53 |
})
|
| 54 |
.then(() => {
|
|
@@ -56,7 +83,11 @@ const SlotReel = ({ words, isActive, finalValue, onComplete, delay = 0 }) => {
|
|
| 56 |
});
|
| 57 |
}, delay * SLOT_START_DELAY * 1000);
|
| 58 |
}
|
| 59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
return (
|
| 62 |
<Box
|
|
|
|
| 1 |
import React, { useEffect, useRef, useState } from "react";
|
| 2 |
import { Box, Typography, useTheme, useMediaQuery } from "@mui/material";
|
| 3 |
import { motion, useAnimation } from "framer-motion";
|
| 4 |
+
import { useSoundSystem } from "../contexts/SoundContext";
|
| 5 |
|
| 6 |
// Animation timing configuration
|
| 7 |
const SLOT_ANIMATION_DURATION = 2; // Duration of each slot animation
|
| 8 |
+
const SLOT_SPEED = 1; // Base speed of the slot animation (higher = faster)
|
| 9 |
const TOTAL_ANIMATION_DURATION = 1; // Total duration for each slot reel in seconds
|
| 10 |
const SLOT_START_DELAY = 2; // Delay between each slot start in seconds
|
| 11 |
|
|
|
|
| 27 |
const [isVisible, setIsVisible] = useState(false);
|
| 28 |
const theme = useTheme();
|
| 29 |
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
| 30 |
+
const { playSound } = useSoundSystem();
|
| 31 |
+
const lastPositionRef = useRef(0);
|
| 32 |
+
const itemHeightRef = useRef(0);
|
| 33 |
|
| 34 |
useEffect(() => {
|
| 35 |
if (isActive) {
|
|
|
|
| 42 |
setReelItems(repeatedWords);
|
| 43 |
|
| 44 |
const itemHeight = isMobile ? 60 : 80;
|
| 45 |
+
itemHeightRef.current = itemHeight;
|
| 46 |
const totalHeight = repeatedWords.length * itemHeight;
|
| 47 |
|
| 48 |
setTimeout(() => {
|
|
|
|
| 54 |
duration: TOTAL_ANIMATION_DURATION / SLOT_SPEED,
|
| 55 |
ease: [0.25, 0.1, 0.25, 1.0],
|
| 56 |
times: [0, 1],
|
| 57 |
+
onUpdate: (latest) => {
|
| 58 |
+
// Calculer l'index du mot actuel basé sur la position
|
| 59 |
+
const currentPosition = Math.abs(latest);
|
| 60 |
+
const currentIndex = Math.floor(currentPosition / itemHeight);
|
| 61 |
+
|
| 62 |
+
// Si on a changé de mot, jouer le son
|
| 63 |
+
if (
|
| 64 |
+
Math.floor(lastPositionRef.current / itemHeight) !==
|
| 65 |
+
currentIndex
|
| 66 |
+
) {
|
| 67 |
+
// Vérifier si c'est le dernier mot (final)
|
| 68 |
+
const isFinalWord = currentIndex === repeatedWords.length - 1;
|
| 69 |
+
// Jouer le son approprié
|
| 70 |
+
if (isFinalWord) {
|
| 71 |
+
playSound("lock");
|
| 72 |
+
} else {
|
| 73 |
+
playSound("tick", "normal");
|
| 74 |
+
}
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
lastPositionRef.current = currentPosition;
|
| 78 |
+
},
|
| 79 |
},
|
| 80 |
})
|
| 81 |
.then(() => {
|
|
|
|
| 83 |
});
|
| 84 |
}, delay * SLOT_START_DELAY * 1000);
|
| 85 |
}
|
| 86 |
+
|
| 87 |
+
return () => {
|
| 88 |
+
lastPositionRef.current = 0;
|
| 89 |
+
};
|
| 90 |
+
}, [isActive, finalValue, words, delay, isMobile, playSound]);
|
| 91 |
|
| 92 |
return (
|
| 93 |
<Box
|
client/src/contexts/SoundContext.jsx
CHANGED
|
@@ -35,6 +35,17 @@ const SOUNDS = {
|
|
| 35 |
off: "/sounds/talky-walky-off.mp3",
|
| 36 |
volume: 0.5,
|
| 37 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
};
|
| 39 |
|
| 40 |
const SoundContext = createContext(null);
|
|
@@ -69,7 +80,19 @@ export function SoundProvider({ children }) {
|
|
| 69 |
// Initialiser tous les sons
|
| 70 |
const soundInstances = {};
|
| 71 |
Object.entries(SOUNDS).forEach(([category, config]) => {
|
| 72 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
// Pour les sons avec plusieurs variations
|
| 74 |
soundInstances[category] = config.files.map((file) => {
|
| 75 |
const [play] = useSound(file, { volume: config.volume });
|
|
@@ -102,7 +125,13 @@ export function SoundProvider({ children }) {
|
|
| 102 |
if (!isSoundEnabled) return;
|
| 103 |
|
| 104 |
try {
|
| 105 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
// Pour les sons avec sous-catégories (comme talkySarah.on)
|
| 107 |
soundInstances[category][subCategory]?.();
|
| 108 |
} else if (Array.isArray(soundInstances[category])) {
|
|
|
|
| 35 |
off: "/sounds/talky-walky-off.mp3",
|
| 36 |
volume: 0.5,
|
| 37 |
},
|
| 38 |
+
tick: {
|
| 39 |
+
files: Array.from({ length: 4 }, (_, i) => `/sounds/tick-${i + 1}.mp3`),
|
| 40 |
+
volume: {
|
| 41 |
+
normal: 0.05, // Volume normal à 50% du volume final
|
| 42 |
+
final: 0.4, // Volume final (comme avant)
|
| 43 |
+
},
|
| 44 |
+
},
|
| 45 |
+
lock: {
|
| 46 |
+
files: ["/sounds/lock-1.mp3"],
|
| 47 |
+
volume: 0.025,
|
| 48 |
+
},
|
| 49 |
};
|
| 50 |
|
| 51 |
const SoundContext = createContext(null);
|
|
|
|
| 80 |
// Initialiser tous les sons
|
| 81 |
const soundInstances = {};
|
| 82 |
Object.entries(SOUNDS).forEach(([category, config]) => {
|
| 83 |
+
if (category === "tick") {
|
| 84 |
+
// Initialisation spéciale pour les ticks avec volumes différents
|
| 85 |
+
soundInstances[category] = {
|
| 86 |
+
normal: config.files.map((file) => {
|
| 87 |
+
const [play] = useSound(file, { volume: config.volume.normal });
|
| 88 |
+
return play;
|
| 89 |
+
}),
|
| 90 |
+
final: config.files.map((file) => {
|
| 91 |
+
const [play] = useSound(file, { volume: config.volume.final });
|
| 92 |
+
return play;
|
| 93 |
+
}),
|
| 94 |
+
};
|
| 95 |
+
} else if (Array.isArray(config.files)) {
|
| 96 |
// Pour les sons avec plusieurs variations
|
| 97 |
soundInstances[category] = config.files.map((file) => {
|
| 98 |
const [play] = useSound(file, { volume: config.volume });
|
|
|
|
| 125 |
if (!isSoundEnabled) return;
|
| 126 |
|
| 127 |
try {
|
| 128 |
+
if (category === "tick") {
|
| 129 |
+
// Pour les ticks avec volumes différents
|
| 130 |
+
const type = subCategory || "normal";
|
| 131 |
+
const sounds = soundInstances[category][type];
|
| 132 |
+
const randomIndex = Math.floor(Math.random() * sounds.length);
|
| 133 |
+
sounds[randomIndex]?.();
|
| 134 |
+
} else if (subCategory) {
|
| 135 |
// Pour les sons avec sous-catégories (comme talkySarah.on)
|
| 136 |
soundInstances[category][subCategory]?.();
|
| 137 |
} else if (Array.isArray(soundInstances[category])) {
|
client/src/pages/Tutorial.jsx
CHANGED
|
@@ -93,17 +93,20 @@ export function Tutorial() {
|
|
| 93 |
<Box
|
| 94 |
sx={{
|
| 95 |
display: "flex",
|
| 96 |
-
gap: 4,
|
| 97 |
justifyContent: "center",
|
| 98 |
-
mb: 2,
|
| 99 |
alignItems: "center",
|
|
|
|
|
|
|
| 100 |
}}
|
| 101 |
>
|
| 102 |
<Box
|
| 103 |
sx={{
|
| 104 |
position: "relative",
|
| 105 |
-
flex: 1,
|
| 106 |
-
|
|
|
|
| 107 |
"&::before": {
|
| 108 |
content: '""',
|
| 109 |
position: "absolute",
|
|
@@ -113,32 +116,39 @@ export function Tutorial() {
|
|
| 113 |
bottom: 0,
|
| 114 |
background: "rgba(255, 255, 255, 0.05)",
|
| 115 |
backdropFilter: "blur(10px)",
|
| 116 |
-
WebkitBackdropFilter: "blur(10px)",
|
| 117 |
borderRadius: "8px",
|
| 118 |
-
border: "1px solid
|
|
|
|
| 119 |
},
|
| 120 |
}}
|
| 121 |
>
|
| 122 |
<Box
|
| 123 |
sx={{
|
| 124 |
position: "relative",
|
| 125 |
-
p: 2,
|
| 126 |
display: "flex",
|
| 127 |
flexDirection: "column",
|
| 128 |
alignItems: "center",
|
| 129 |
-
gap:
|
| 130 |
zIndex: 1,
|
| 131 |
}}
|
| 132 |
>
|
| 133 |
<CallSplitOutlinedIcon
|
| 134 |
sx={{
|
| 135 |
-
fontSize: 40,
|
| 136 |
-
color: "primary.
|
| 137 |
-
mb:
|
| 138 |
-
transform: "rotate(90deg)",
|
| 139 |
}}
|
| 140 |
/>
|
| 141 |
-
<Typography
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
Make a choice
|
| 143 |
</Typography>
|
| 144 |
</Box>
|
|
@@ -149,6 +159,8 @@ export function Tutorial() {
|
|
| 149 |
sx={{
|
| 150 |
color: "rgba(255,255,255,0.5)",
|
| 151 |
fontWeight: "bold",
|
|
|
|
|
|
|
| 152 |
}}
|
| 153 |
>
|
| 154 |
OR
|
|
@@ -157,8 +169,9 @@ export function Tutorial() {
|
|
| 157 |
<Box
|
| 158 |
sx={{
|
| 159 |
position: "relative",
|
| 160 |
-
flex: 1,
|
| 161 |
-
|
|
|
|
| 162 |
"&::before": {
|
| 163 |
content: '""',
|
| 164 |
position: "absolute",
|
|
@@ -168,27 +181,38 @@ export function Tutorial() {
|
|
| 168 |
bottom: 0,
|
| 169 |
background: "rgba(255, 255, 255, 0.05)",
|
| 170 |
backdropFilter: "blur(10px)",
|
| 171 |
-
WebkitBackdropFilter: "blur(10px)",
|
| 172 |
borderRadius: "8px",
|
| 173 |
-
border: "1px solid
|
|
|
|
| 174 |
},
|
| 175 |
}}
|
| 176 |
>
|
| 177 |
<Box
|
| 178 |
sx={{
|
| 179 |
position: "relative",
|
| 180 |
-
p: 2,
|
| 181 |
display: "flex",
|
| 182 |
flexDirection: "column",
|
| 183 |
alignItems: "center",
|
| 184 |
-
gap:
|
| 185 |
zIndex: 1,
|
| 186 |
}}
|
| 187 |
>
|
| 188 |
<CreateOutlinedIcon
|
| 189 |
-
sx={{
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
/>
|
| 191 |
-
<Typography
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
Write your own
|
| 193 |
</Typography>
|
| 194 |
</Box>
|
|
|
|
| 93 |
<Box
|
| 94 |
sx={{
|
| 95 |
display: "flex",
|
| 96 |
+
gap: { xs: 1, sm: 4 },
|
| 97 |
justifyContent: "center",
|
| 98 |
+
mb: { xs: 1, sm: 2 },
|
| 99 |
alignItems: "center",
|
| 100 |
+
flexDirection: { xs: "column", sm: "row" },
|
| 101 |
+
width: "100%",
|
| 102 |
}}
|
| 103 |
>
|
| 104 |
<Box
|
| 105 |
sx={{
|
| 106 |
position: "relative",
|
| 107 |
+
flex: { xs: "none", sm: 1 },
|
| 108 |
+
width: { xs: "50%", sm: "auto" },
|
| 109 |
+
maxWidth: { xs: "160px", sm: "200px" },
|
| 110 |
"&::before": {
|
| 111 |
content: '""',
|
| 112 |
position: "absolute",
|
|
|
|
| 116 |
bottom: 0,
|
| 117 |
background: "rgba(255, 255, 255, 0.05)",
|
| 118 |
backdropFilter: "blur(10px)",
|
| 119 |
+
WebkitBackdropFilter: "blur(10px)",
|
| 120 |
borderRadius: "8px",
|
| 121 |
+
border: "1px solid",
|
| 122 |
+
borderColor: "primary.main",
|
| 123 |
},
|
| 124 |
}}
|
| 125 |
>
|
| 126 |
<Box
|
| 127 |
sx={{
|
| 128 |
position: "relative",
|
| 129 |
+
p: { xs: 1.5, sm: 2 },
|
| 130 |
display: "flex",
|
| 131 |
flexDirection: "column",
|
| 132 |
alignItems: "center",
|
| 133 |
+
gap: 0.5,
|
| 134 |
zIndex: 1,
|
| 135 |
}}
|
| 136 |
>
|
| 137 |
<CallSplitOutlinedIcon
|
| 138 |
sx={{
|
| 139 |
+
fontSize: { xs: 28, sm: 40 },
|
| 140 |
+
color: "primary.main",
|
| 141 |
+
mb: 0.5,
|
| 142 |
+
transform: "rotate(90deg)",
|
| 143 |
}}
|
| 144 |
/>
|
| 145 |
+
<Typography
|
| 146 |
+
variant="subtitle1"
|
| 147 |
+
sx={{
|
| 148 |
+
color: "primary.main",
|
| 149 |
+
fontSize: { xs: "0.875rem", sm: "1rem" },
|
| 150 |
+
}}
|
| 151 |
+
>
|
| 152 |
Make a choice
|
| 153 |
</Typography>
|
| 154 |
</Box>
|
|
|
|
| 159 |
sx={{
|
| 160 |
color: "rgba(255,255,255,0.5)",
|
| 161 |
fontWeight: "bold",
|
| 162 |
+
fontSize: { xs: "1rem", sm: "1.2rem" },
|
| 163 |
+
my: { xs: 0.25, sm: 0 },
|
| 164 |
}}
|
| 165 |
>
|
| 166 |
OR
|
|
|
|
| 169 |
<Box
|
| 170 |
sx={{
|
| 171 |
position: "relative",
|
| 172 |
+
flex: { xs: "none", sm: 1 },
|
| 173 |
+
width: { xs: "50%", sm: "auto" },
|
| 174 |
+
maxWidth: { xs: "160px", sm: "200px" },
|
| 175 |
"&::before": {
|
| 176 |
content: '""',
|
| 177 |
position: "absolute",
|
|
|
|
| 181 |
bottom: 0,
|
| 182 |
background: "rgba(255, 255, 255, 0.05)",
|
| 183 |
backdropFilter: "blur(10px)",
|
| 184 |
+
WebkitBackdropFilter: "blur(10px)",
|
| 185 |
borderRadius: "8px",
|
| 186 |
+
border: "1px solid",
|
| 187 |
+
borderColor: "secondary.main",
|
| 188 |
},
|
| 189 |
}}
|
| 190 |
>
|
| 191 |
<Box
|
| 192 |
sx={{
|
| 193 |
position: "relative",
|
| 194 |
+
p: { xs: 1.5, sm: 2 },
|
| 195 |
display: "flex",
|
| 196 |
flexDirection: "column",
|
| 197 |
alignItems: "center",
|
| 198 |
+
gap: 0.5,
|
| 199 |
zIndex: 1,
|
| 200 |
}}
|
| 201 |
>
|
| 202 |
<CreateOutlinedIcon
|
| 203 |
+
sx={{
|
| 204 |
+
fontSize: { xs: 28, sm: 40 },
|
| 205 |
+
color: "secondary.main",
|
| 206 |
+
mb: 0.5,
|
| 207 |
+
}}
|
| 208 |
/>
|
| 209 |
+
<Typography
|
| 210 |
+
variant="subtitle1"
|
| 211 |
+
sx={{
|
| 212 |
+
color: "secondary.main",
|
| 213 |
+
fontSize: { xs: "0.875rem", sm: "1rem" },
|
| 214 |
+
}}
|
| 215 |
+
>
|
| 216 |
Write your own
|
| 217 |
</Typography>
|
| 218 |
</Box>
|