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

feat: multi voices race #12

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
84 changes: 50 additions & 34 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ function App() {
// ? 414
// : window.innerWidth
// : 414;
const [primaryVoiceInfo, setPrimaryVoiceInfo] = useState<VoiceV1Cover | null>(
null
);
const [primaryVoiceInfo, setPrimaryVoiceInfo] = useState<
VoiceV1Cover[] | null
>(null);
const [secondaryVoiceInfo, setSecondaryVoiceInfo] = useState<
VoiceV1Cover[] | null
>(null);
Expand All @@ -103,6 +103,7 @@ function App() {
const [showGameOverButtons, setShowGameOverButtons] = useState(false);
const [isPlayingGame, setIsPlayingGame] = useState(false);
const [showIosNotice, setShowIosNotice] = useState(false);
const [noOfVoices, setNoOfVoices] = useState(1);
const [coversSnapshot, cssLoading, cssError] = useCollection(
query(
collection(db, "tunedash_covers"),
Expand All @@ -119,7 +120,10 @@ function App() {
if (primaryVoiceInfo && secondaryVoiceInfo) {
const urls = [
`https://voxaudio.nusic.fm/covers/${selectedCoverDocId}/instrumental.mp3`,
`https://voxaudio.nusic.fm/covers/${selectedCoverDocId}/${primaryVoiceInfo.id}.mp3`,
...primaryVoiceInfo.map(
(info) =>
`https://voxaudio.nusic.fm/covers/${selectedCoverDocId}/${info.id}.mp3`
),
...secondaryVoiceInfo.map(
(info) =>
`https://voxaudio.nusic.fm/covers/${selectedCoverDocId}/${info.id}.mp3`
Expand Down Expand Up @@ -209,9 +213,10 @@ function App() {
isWinner ? 2500 : 1800
);
if (userDoc?.id) {
// TODO
logFirebaseEvent("race_result", {
track_id: selectedCoverDocId,
primary_voice_id: primaryVoiceInfo?.id,
// primary_voice_id: primaryVoiceInfo?.id,
// secondary_voice_id: secondaryVoiceInfo?.[0]?.id,
winning_voice_id: winningVoiceId,
is_user_win: isWinner,
Expand Down Expand Up @@ -300,7 +305,7 @@ function App() {
!!primaryVoiceInfo &&
marbleRacePlayVocals(
selectedCoverDocId,
primaryVoiceInfo.id
primaryVoiceInfo[0].id
);
setSecondaryVoiceInfo(null);
setScreenName("voices-clash");
Expand Down Expand Up @@ -328,7 +333,7 @@ function App() {
logFirebaseEvent("race_again", {
track_id: selectedCoverDocId,
track_title: coverDoc?.title,
primary_voice_id: primaryVoiceInfo?.id,
primary_voice_id: primaryVoiceInfo?.[0]?.id,
});
setScreenName("voices-clash");
setShowGameOverButtons(false);
Expand All @@ -341,7 +346,7 @@ function App() {
logFirebaseEvent("new_race", {
track_id: selectedCoverDocId,
track_title: coverDoc?.title,
primary_voice_id: primaryVoiceInfo?.id,
primary_voice_id: primaryVoiceInfo?.[0]?.id,
});
setScreenName("select-track");
setShowGameOverButtons(false);
Expand Down Expand Up @@ -427,56 +432,63 @@ function App() {
onTrackSelected={(
coverDoc: CoverV1,
coverId: string,
voiceInfo: VoiceV1Cover | null
voiceInfo: VoiceV1Cover[] | null
) => {
setCoverDoc(coverDoc);
setSelectedCoverDocId(coverId);
setPrimaryVoiceInfo(voiceInfo);
// TODO:
logFirebaseEvent("track_playback", {
track_id: coverId,
track_title: coverDoc?.title,
voice_id: voiceInfo?.id,
voice_id: voiceInfo?.[0]?.id,
});
}}
onNextPageClick={() => {
setScreenName("choose-primary-voice");
// TODO:
logFirebaseEvent("track_selection", {
track_id: selectedCoverDocId,
track_title: coverDoc?.title,
voice_id: primaryVoiceInfo?.id,
voice_id: primaryVoiceInfo?.[0]?.id,
});
}}
/>
)}
{coverDoc && screenName === "choose-primary-voice" && (
<ChoosePrimaryVoice
selectedCoverId={selectedCoverDocId}
voices={coverDoc.voices}
primaryVoiceInfo={primaryVoiceInfo}
onPrimaryVoiceSelected={(voiceInfo) => {
setPrimaryVoiceInfo(voiceInfo);
// setScreenName("voices-clash");
setScreenName("game-ready");
logFirebaseEvent("voice_selection", {
track_id: selectedCoverDocId,
track_title: coverDoc?.title,
voice_id: voiceInfo.id,
voice_name: voiceInfo.name,
});
}}
coverTitle={coverDoc.title}
userDoc={userDoc}
/>
)}
{primaryVoiceInfo &&
{coverDoc &&
screenName === "choose-primary-voice" &&
primaryVoiceInfo?.length && (
<ChoosePrimaryVoice
selectedCoverId={selectedCoverDocId}
voices={coverDoc.voices}
primaryVoiceInfo={primaryVoiceInfo[0]}
onPrimaryVoiceSelected={(voiceInfo) => {
setPrimaryVoiceInfo(voiceInfo);
// setScreenName("voices-clash");
setScreenName("game-ready");
// TODO:
logFirebaseEvent("voice_selection", {
track_id: selectedCoverDocId,
track_title: coverDoc?.title,
voice_id: voiceInfo[0].id,
voice_name: voiceInfo[0].name,
});
}}
coverTitle={coverDoc.title}
userDoc={userDoc}
noOfVoices={noOfVoices}
setNoOfVoices={setNoOfVoices}
/>
)}
{primaryVoiceInfo?.length &&
coverDoc &&
(screenName === "voices-clash" ||
screenName === "game-ready") && (
<VoicesClash
voices={coverDoc.voices}
selectedCoverDocId={selectedCoverDocId}
primaryVoiceInfo={primaryVoiceInfo}
secondaryVoiceInfo={secondaryVoiceInfo?.at(0) || null}
secondaryVoiceInfo={secondaryVoiceInfo}
onChooseOpponent={(voiceInfo) => {
setSecondaryVoiceInfo(voiceInfo);
}}
Expand All @@ -491,6 +503,7 @@ function App() {
setShowOpponentVoiceSelection={
setShowOpponentVoiceSelection
}
noOfVoices={noOfVoices}
/>
)}
{primaryVoiceInfo &&
Expand All @@ -499,7 +512,7 @@ function App() {
coverDoc && (
<PhaserGame
ref={phaserRef}
voices={[primaryVoiceInfo, ...secondaryVoiceInfo].map(
voices={[...primaryVoiceInfo, ...secondaryVoiceInfo].map(
(v) => ({
id: v.id,
name: v.name,
Expand All @@ -521,6 +534,9 @@ function App() {
width={canvasElemWidth}
trailPath={getTrailPath(selectedTrailPath)}
dpr={window.devicePixelRatio || 2}
userMarbleIndexes={new Array(noOfVoices)
.fill(0)
.map((_, i) => i)}
/>
)}
</Stack>
Expand Down
141 changes: 105 additions & 36 deletions src/components/ChoosePrimaryVoice.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Stack, Box, Typography, Chip } from "@mui/material";
import { Stack, Box, Typography, Chip, Slider } from "@mui/material";
import { VoiceV1Cover } from "../services/db/coversV1.service";
import { getVoiceAvatarPath } from "../helpers";
import { createRandomNumber, getVoiceAvatarPath } from "../helpers";
import { useState } from "react";
import LongImageMotionButton from "./Buttons/LongImageMotionButton";
import { switchVocalsByDownloading } from "../hooks/useTonejs";
Expand All @@ -9,12 +9,14 @@ import { UserDoc } from "../services/db/user.service";
import { motion } from "framer-motion";

type Props = {
onPrimaryVoiceSelected: (voiceInfo: VoiceV1Cover) => void;
onPrimaryVoiceSelected: (voiceInfo: VoiceV1Cover[]) => void;
voices: VoiceV1Cover[];
primaryVoiceInfo: VoiceV1Cover | null;
selectedCoverId: string;
coverTitle: string;
userDoc: UserDoc | null;
noOfVoices: number;
setNoOfVoices: (noOfVoices: number) => void;
};

const ChoosePrimaryVoice = ({
Expand All @@ -24,6 +26,8 @@ const ChoosePrimaryVoice = ({
selectedCoverId,
coverTitle,
userDoc,
noOfVoices,
setNoOfVoices,
}: Props) => {
const [selectedVoiceInfo, setSelectedVoiceInfo] = useState<VoiceV1Cover>(
primaryVoiceInfo || voices[0]
Expand All @@ -39,41 +43,91 @@ const ChoosePrimaryVoice = ({
alignItems={"center"}
position={"relative"}
>
<Stack alignItems={"center"} gap={0.5}>
<img
src={getVoiceAvatarPath(selectedVoiceInfo.id)}
width={105}
height={105}
style={{
borderRadius: "12px",
cursor: "pointer",
}}
/>
<Box
px={2}
// width={100}
height={20}
sx={{
background: `url(/assets/tunedash/track-rect.png)`,
backgroundSize: "cover",
backgroundPosition: "center",
}}
display={"flex"}
<Stack
direction={"row"}
justifyContent={"center"}
alignItems={"center"}
gap={0.5}
>
<Box height={100}>
<Slider
value={noOfVoices}
onChange={(_, value) => {
setNoOfVoices(value as number);
}}
color="secondary"
min={1}
max={5}
marks
orientation="vertical"
/>
</Box>
<Stack
direction={"row"}
justifyContent={"center"}
alignItems={"center"}
width={"100%"}
>
<Typography
variant="caption"
fontWeight={600}
sx={{
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
{selectedVoiceInfo.name}
</Typography>
</Box>
<Stack>
<img
src={getVoiceAvatarPath(selectedVoiceInfo.id)}
width={105}
height={105}
style={{
borderRadius: "12px",
cursor: "pointer",
}}
/>
<Box
px={2}
// width={100}
height={20}
sx={{
background: `url(/assets/tunedash/track-rect.png)`,
backgroundSize: "cover",
backgroundPosition: "center",
}}
display={"flex"}
justifyContent={"center"}
alignItems={"center"}
>
<Typography
variant="caption"
fontWeight={600}
sx={{
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
{selectedVoiceInfo.name}
</Typography>
</Box>
</Stack>
{noOfVoices > 1 && (
<Stack
direction={"row"}
alignItems={"center"}
flexWrap={"wrap"}
width={105}
height={105}
>
{new Array(noOfVoices - 1).fill(0).map((_, idx) => (
<img
key={idx}
src="/assets/tunedash/question-mark.png"
width={50}
height={50}
style={{
objectFit: "contain",
borderRadius: "12px",
cursor: "pointer",
}}
/>
))}
</Stack>
)}
</Stack>
</Stack>
<Box
width={window.innerWidth > 350 ? 350 : window.innerWidth}
Expand Down Expand Up @@ -212,7 +266,22 @@ const ChoosePrimaryVoice = ({
<Box position={"absolute"} bottom={20} zIndex={100}>
<LongImageMotionButton
onClick={() => {
onPrimaryVoiceSelected(selectedVoiceInfo);
const selectedVoices = [selectedVoiceInfo];
const currentIdx = voices.findIndex(
(voice) => voice.id === selectedVoiceInfo.id
);
const usedIndexes = [currentIdx];
for (let i = 1; i < noOfVoices; i++) {
let randomIdx = createRandomNumber(
0,
voices.length - 1,
usedIndexes
);
const randomNextVoice = voices[randomIdx];
selectedVoices.push(randomNextVoice);
usedIndexes.push(randomIdx);
}
onPrimaryVoiceSelected(selectedVoices);
}}
name="Proceed"
width={230}
Expand Down
13 changes: 7 additions & 6 deletions src/components/SelectTrack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Props = {
onTrackSelected: (
coverDoc: CoverV1,
coverId: string,
voiceId: VoiceV1Cover | null
voiceId: VoiceV1Cover[] | null
) => void;
selectedCoverDocId: string;
onNextPageClick: () => void;
Expand Down Expand Up @@ -116,12 +116,13 @@ const SelectTrack = ({
zIndex={9}
onClick={async () => {
if (doc.id !== selectedCoverDocId) {
const randomVoice =
coverDoc.voices[
createRandomNumber(0, coverDoc.voices.length - 1)
];
const randomIdx = createRandomNumber(
0,
coverDoc.voices.length - 1
);
const randomVoice = coverDoc.voices[randomIdx];
await downloadInstrumental(doc.id, coverDoc, randomVoice);
onTrackSelected(coverDoc, doc.id, randomVoice);
onTrackSelected(coverDoc, doc.id, [randomVoice]);
}
}}
>
Expand Down
Loading