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