Play works on web websocket not reimplemented

This commit is contained in:
Clément Le Bihan
2024-01-13 17:31:56 +01:00
parent 00ee5cd531
commit f632ed42a3
4 changed files with 97 additions and 52 deletions
+31 -19
View File
@@ -9,6 +9,7 @@ import { SvgContainer } from './SvgContainer';
import LoadingComponent from '../Loading';
import { SplendidGrandPiano } from 'smplr';
import { atom, useAtom } from 'jotai';
import { useStopwatch } from 'react-use-precision-timer';
export const timestampAtom = atom(0);
export const shouldPlayAtom = atom(false);
@@ -37,20 +38,21 @@ const getCursorToPlay = (
}
};
const transitionDuration = 50;
const transitionDuration = 200;
const PartitionMagic = ({ songID }: ParitionMagicProps) => {
const { data, isLoading, isError } = useQuery(API.getSongCursorInfos(songID));
const currentCurIdx = React.useRef(-1);
const stopwatch = useStopwatch();
const [endPartitionReached, setEndPartitionReached] = React.useState(false);
const [isPartitionSvgLoaded, setIsPartitionSvgLoaded] = React.useState(false);
const partitionOffset = useSharedValue(0);
const melodySound = React.useRef<Audio.Sound | null>(null);
const piano = React.useRef<SplendidGrandPiano | null>(null);
const [isPianoLoaded, setIsPianoLoaded] = React.useState(false);
const timestamp = useAtom(timestampAtom)[0];
const [timestamp, setTimestamp] = useAtom(timestampAtom);
const shouldPlay = useAtom(shouldPlayAtom)[0];
const [, setPartitionState] = useAtom(partitionStateAtom);
const [partitionState, setPartitionState] = useAtom(partitionStateAtom);
const cursorPaddingVertical = 10;
const cursorPaddingHorizontal = 3;
@@ -62,13 +64,28 @@ const PartitionMagic = ({ songID }: ParitionMagicProps) => {
const cursorTop = (data?.cursors[cursorDisplayIdx]?.y ?? 0) - cursorPaddingVertical;
const cursorLeft = (data?.cursors[0]?.x ?? 0) - cursorPaddingHorizontal;
console.log('state', partitionState);
if (!endPartitionReached && currentCurIdx.current + 1 === data?.cursors.length) {
// weird contraption but the mobile don't want classic functions to be called
// with the withTiming function :(
melodySound.current?.pauseAsync();
piano.current?.stop();
setPartitionState('ended');
}
React.useEffect(() => {
if (isError) {
setPartitionState('error');
}
}, [isError]);
React.useEffect(() => {
if (isPartitionSvgLoaded && !isLoading && (melodySound.current?._loaded || isPianoLoaded)) {
setPartitionState('ready');
}
}, [isPartitionSvgLoaded, isLoading, melodySound.current?._loaded, isPianoLoaded]);
React.useEffect(() => {
if (Platform.OS === 'web' && !piano.current) {
const audio = new AudioContext();
@@ -106,28 +123,23 @@ const PartitionMagic = ({ songID }: ParitionMagicProps) => {
}, [data]);
React.useEffect(() => {
if (isError) {
setPartitionState('error');
}
}, [isError]);
React.useEffect(() => {
if (isPartitionSvgLoaded && !isLoading && (melodySound.current?._loaded || isPianoLoaded)) {
setPartitionState('ready');
}
}, [isPartitionSvgLoaded, isLoading, melodySound.current?._loaded, isPianoLoaded]);
const interval = setInterval(() => {
setTimestamp(stopwatch.getElapsedRunningTime());
}, 200);
return () => {
clearInterval(interval);
};
}, []);
React.useEffect(() => {
if (Platform.OS === 'web') {
if (!piano.current || !isPianoLoaded) {
return;
}
if (!piano.current || !isPianoLoaded) return;
shouldPlay ? stopwatch.start() : stopwatch.pause();
setPartitionState(shouldPlay ? 'playing' : 'paused');
return;
}
if (!melodySound.current || !melodySound.current._loaded) {
return;
}
if (!melodySound.current || !melodySound.current._loaded) return;
shouldPlay ? stopwatch.start() : stopwatch.pause();
if (shouldPlay) {
melodySound.current
.playAsync()
+31
View File
@@ -0,0 +1,31 @@
import PopupCC from '../UI/PopupCC';
import { shouldEndAtom } from './PlayViewControlBar';
import { partitionStateAtom } from './PartitionMagic';
import ScoreModal from '../ScoreModal';
import { useAtom } from 'jotai';
export const PlayEndModal = () => {
const [shouldEnd] = useAtom(shouldEndAtom);
const [partitionState] = useAtom(partitionStateAtom);
const isEnd = shouldEnd || partitionState === 'ended';
return (
<PopupCC isVisible={isEnd}>
<ScoreModal
songId={0}
overallScore={0}
precision={0}
score={{
max_score: 0,
missed: 0,
wrong: 0,
good: 0,
great: 0,
perfect: 0,
current_streak: 0,
max_streak: 0,
}}
/>
</PopupCC>
);
};
+4 -4
View File
@@ -25,7 +25,7 @@ const PlayViewControlBar = ({ song }: PlayViewControlBarProps) => {
const bpm = React.useRef<number>(60);
const { colors } = useTheme();
const textColor = colors.text;
const paused = partitionState === 'paused';
const isPlaying = partitionState === 'playing';
const disabled = partitionState === 'loading' || partitionState === 'error';
return (
<Row
@@ -108,9 +108,9 @@ const PlayViewControlBar = ({ song }: PlayViewControlBarProps) => {
_icon={{
as: Ionicons,
color: colors.coolGray[900],
name: paused ? 'play' : 'pause',
name: isPlaying ? 'pause' : 'play',
}}
onPress={() => setShouldPlay(paused)}
onPress={() => setShouldPlay(!isPlaying)}
/>
<IconButton
size="sm"
@@ -144,7 +144,7 @@ const PlayViewControlBar = ({ song }: PlayViewControlBarProps) => {
minWidth: 120,
}}
>
<MetronomeControls paused={paused} bpm={bpm.current} />
<MetronomeControls paused={isPlaying} bpm={bpm.current} />
</View>
</Row>
);
+31 -29
View File
@@ -25,6 +25,7 @@ import { Clock, Cup } from 'iconsax-react-native';
import PlayViewControlBar from '../components/Play/PlayViewControlBar';
import ScoreModal from '../components/ScoreModal';
import { PlayScore, ScoreMessage } from '../components/Play/PlayScore';
import { PlayEndModal } from '../components/Play/PlayEndModal';
type PlayViewProps = {
songId: number;
@@ -63,22 +64,22 @@ const PlayView = ({ songId }: PlayViewProps) => {
const isPhone = screenSize === 'small';
const song = useQuery(API.getSong(songId, ['artist']), { staleTime: Infinity });
const toast = useToast();
const [lastScoreMessage, setLastScoreMessage] = useState<ScoreMessage>();
// const [lastScoreMessage, setLastScoreMessage] = useState<ScoreMessage>();
const webSocket = useRef<WebSocket>();
const [paused, setPause] = useState<boolean>(true);
const stopwatch = useStopwatch();
const [time, setTime] = useState(0);
// const [paused, setPause] = useState<boolean>(true);
// const stopwatch = useStopwatch();
// const [time, setTime] = useState(0);
const [endResult, setEndResult] = useState<unknown>();
const [shouldPlay, setShouldPlay] = useState(false);
// const [shouldPlay, setShouldPlay] = useState(false);
const songHistory = useQuery(API.getSongHistory(songId));
const [score, setScore] = useState(0); // Between 0 and 100
const getElapsedTime = () => stopwatch.getElapsedRunningTime();
const [readyToPlay, setReadyToPlay] = useState(false);
// const [score, setScore] = useState(0); // Between 0 and 100
// const getElapsedTime = () => stopwatch.getElapsedRunningTime();
// const [readyToPlay, setReadyToPlay] = useState(false);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [midiKeyboardFound, setMidiKeyboardFound] = useState<boolean>();
// first number is the note, the other is the time when pressed on release the key is removed
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [streak, setStreak] = useState(0);
// const [streak, setStreak] = useState(0);
const colorScheme = useColorScheme();
const { colors } = useTheme();
const statColor = colors.lightText;
@@ -131,7 +132,7 @@ const PlayView = ({ songId }: PlayViewProps) => {
console.log('MIDI inputs', inputs);
let endMsgReceived = false; // Used to know if to go to error screen when websocket closes
if (inputs.size <= 0) {
if (inputs.size <= 10) {
toast.show({ description: 'No MIDI Keyboard found' });
return;
}
@@ -238,14 +239,14 @@ const PlayView = ({ songId }: PlayViewProps) => {
useEffect(() => {
ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE).catch(() => {});
const interval = setInterval(() => {
setTime(() => getElapsedTime()); // Countdown
}, 200);
// const interval = setInterval(() => {
// setTime(() => getElapsedTime()); // Countdown
// }, 200);
return () => {
ScreenOrientation.unlockAsync().catch(() => {});
stopwatch.stop();
clearInterval(interval);
// stopwatch.stop();
// clearInterval(interval);
};
}, []);
@@ -290,12 +291,13 @@ const PlayView = ({ songId }: PlayViewProps) => {
zIndex: 100,
}}
>
<PopupCC isVisible={endResult != undefined}>
<PlayEndModal />
{/* <PopupCC isVisible={endResult != undefined}>
{
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(() => (endResult ? <ScoreModal {...(endResult as any)} /> : <></>))()
endResult ? <ScoreModal {...(endResult as any)} /> : <></>
}
</PopupCC>
</PopupCC> */}
<PopupCC
title={translate('selectPlayMode')}
description={translate('selectPlayModeExplaination')}
@@ -404,18 +406,18 @@ const PlayView = ({ songId }: PlayViewProps) => {
/>
</View>
<PlayViewControlBar
score={score}
time={time}
paused={paused}
disabled={playType == null || !readyToPlay}
// score={score}
// time={time}
// paused={paused}
// disabled={playType == null || !readyToPlay}
song={song.data}
onEnd={onEnd}
onPause={() => {
setShouldPlay(false);
}}
onResume={() => {
setShouldPlay(true);
}}
// onEnd={onEnd}
// onPause={() => {
// setShouldPlay(false);
// }}
// onResume={() => {
// setShouldPlay(true);
// }}
/>
</SafeAreaView>
{colorScheme === 'dark' && (