Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d45a6b934 | |||
| 13050e52f9 | |||
| 5ef3885f72 | |||
| a103666caf | |||
| 29da5c2788 | |||
| 1880b89b0c | |||
| e769ff1f13 | |||
| 9e7873cdd7 | |||
| f46c2cfb4a | |||
| 9f14061efd | |||
| 851ee7420f | |||
| ef57eb752d | |||
| fcb29ae484 | |||
| 5c4847ae2c | |||
| 3353a17611 |
+13
-10
@@ -12,27 +12,30 @@ jobs:
|
||||
pull-requests: read
|
||||
# Set job outputs to values from filter step
|
||||
outputs:
|
||||
backend: ${{ steps.filter.outputs.backend }}
|
||||
frontend: ${{ steps.filter.outputs.frontend }}
|
||||
scoro: ${{ steps.filter.outputs.scoro }}
|
||||
back: ${{ steps.filter.outputs.back }}
|
||||
front: ${{ steps.filter.outputs.front }}
|
||||
scorometer: ${{ steps.filter.outputs.scorometer }}
|
||||
steps:
|
||||
# For pull requests it's not necessary to checkout the code
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
backend:
|
||||
- 'backend/**'
|
||||
frontend:
|
||||
- 'frontend/**'
|
||||
scoro:
|
||||
back:
|
||||
- 'back/**'
|
||||
- '.github/workflows/back.yml'
|
||||
front:
|
||||
- 'front/**'
|
||||
- '.github/workflows/front.yml'
|
||||
scorometer:
|
||||
- 'scorometer/**'
|
||||
- '.github/workflows/scoro.yml'
|
||||
back_build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
if: ${{ needs.changes.outputs.back == 'true' }}
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./back
|
||||
@@ -47,7 +50,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
needs: [ back_build ]
|
||||
if: ${{ needs.changes.outputs.frontend == 'true' }}
|
||||
if: ${{ needs.changes.outputs.back == 'true' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
+13
-10
@@ -12,28 +12,31 @@ jobs:
|
||||
pull-requests: read
|
||||
# Set job outputs to values from filter step
|
||||
outputs:
|
||||
backend: ${{ steps.filter.outputs.backend }}
|
||||
frontend: ${{ steps.filter.outputs.frontend }}
|
||||
scoro: ${{ steps.filter.outputs.scoro }}
|
||||
back: ${{ steps.filter.outputs.back }}
|
||||
front: ${{ steps.filter.outputs.front }}
|
||||
scorometer: ${{ steps.filter.outputs.scorometer }}
|
||||
steps:
|
||||
# For pull requests it's not necessary to checkout the code
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
backend:
|
||||
- 'backend/**'
|
||||
frontend:
|
||||
- 'frontend/**'
|
||||
scoro:
|
||||
back:
|
||||
- 'back/**'
|
||||
- '.github/workflows/back.yml'
|
||||
front:
|
||||
- 'front/**'
|
||||
- '.github/workflows/front.yml'
|
||||
scorometer:
|
||||
- 'scorometer/**'
|
||||
- '.github/workflows/scoro.yml'
|
||||
front_check:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./front
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.frontend == 'true' }}
|
||||
if: ${{ needs.changes.outputs.front == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
@@ -54,7 +57,7 @@ jobs:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./front
|
||||
if: ${{ needs.changes.outputs.frontend == 'true' }}
|
||||
if: ${{ needs.changes.outputs.front == 'true' }}
|
||||
needs: [ front_check ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -11,25 +11,28 @@ jobs:
|
||||
pull-requests: read
|
||||
# Set job outputs to values from filter step
|
||||
outputs:
|
||||
backend: ${{ steps.filter.outputs.backend }}
|
||||
frontend: ${{ steps.filter.outputs.frontend }}
|
||||
scoro: ${{ steps.filter.outputs.scoro }}
|
||||
back: ${{ steps.filter.outputs.back }}
|
||||
front: ${{ steps.filter.outputs.front }}
|
||||
scorometer: ${{ steps.filter.outputs.scorometer }}
|
||||
steps:
|
||||
# For pull requests it's not necessary to checkout the code
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
backend:
|
||||
- 'backend/**'
|
||||
frontend:
|
||||
- 'frontend/**'
|
||||
scoro:
|
||||
back:
|
||||
- 'back/**'
|
||||
- '.github/workflows/back.yml'
|
||||
front:
|
||||
- 'front/**'
|
||||
- '.github/workflows/front.yml'
|
||||
scorometer:
|
||||
- 'scorometer/**'
|
||||
- '.github/workflows/scoro.yml'
|
||||
scoro_test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.scoro == 'true' }}
|
||||
if: ${{ needs.changes.outputs.scorometer == 'true' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Slider, View, IconButton, Icon } from 'native-base';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
// import { Audio } from 'expo-av';
|
||||
import { Audio } from 'expo-av';
|
||||
import { VolumeHigh, VolumeSlash } from 'iconsax-react-native';
|
||||
import { Translate } from '../i18n/i18n';
|
||||
|
||||
export const MetronomeControls = ({ paused = false, bpm }: { paused?: boolean; bpm: number }) => {
|
||||
const audio = useRef<null>(null);
|
||||
const audio = useRef<Audio.Sound | null>(null);
|
||||
const [enabled, setEnabled] = useState<boolean>(false);
|
||||
const volume = useRef<number>(50);
|
||||
|
||||
@@ -15,12 +15,12 @@ export const MetronomeControls = ({ paused = false, bpm }: { paused?: boolean; b
|
||||
return;
|
||||
} else if (!audio.current) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
// Audio.Sound.createAsync(require('../assets/metronome.mp3')).then((a) => {
|
||||
// audio.current = a.sound;
|
||||
// });
|
||||
Audio.Sound.createAsync(require('../assets/metronome.mp3')).then((a) => {
|
||||
audio.current = a.sound;
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
// audio.current?.unloadAsync();
|
||||
audio.current?.unloadAsync();
|
||||
};
|
||||
}, [enabled]);
|
||||
useEffect(() => {
|
||||
@@ -28,12 +28,12 @@ export const MetronomeControls = ({ paused = false, bpm }: { paused?: boolean; b
|
||||
const int = setInterval(() => {
|
||||
if (!enabled) return;
|
||||
if (!audio.current) return;
|
||||
// audio.current?.playAsync();
|
||||
audio.current?.playAsync();
|
||||
}, 60000 / bpm);
|
||||
return () => clearInterval(int);
|
||||
}, [bpm, paused]);
|
||||
useEffect(() => {
|
||||
// audio.current?.setVolumeAsync(volume.current / 100);
|
||||
audio.current?.setVolumeAsync(volume.current / 100);
|
||||
}, [volume.current]);
|
||||
return (
|
||||
<View flex={1}>
|
||||
|
||||
@@ -5,12 +5,9 @@ import { useQuery } from '../../Queries';
|
||||
import Animated, { useSharedValue, withTiming, Easing } from 'react-native-reanimated';
|
||||
import { CursorInfoItem } from '../../models/SongCursorInfos';
|
||||
import { PianoNotes } from '../../state/SoundPlayerSlice';
|
||||
// import { Audio } from 'expo-av';
|
||||
import { Audio } from 'expo-av';
|
||||
import { SvgContainer } from './SvgContainer';
|
||||
import LoadingComponent from '../Loading';
|
||||
import Sound from 'react-native-sound';
|
||||
|
||||
Sound.setCategory('Playback');
|
||||
|
||||
// note we are also using timestamp in a context
|
||||
export type ParitionMagicProps = {
|
||||
@@ -54,7 +51,7 @@ const PartitionMagic = ({
|
||||
const [endPartitionReached, setEndPartitionReached] = React.useState(false);
|
||||
const [isPartitionSvgLoaded, setIsPartitionSvgLoaded] = React.useState(false);
|
||||
const partitionOffset = useSharedValue(0);
|
||||
const pianoSounds = React.useRef<Record<string, Sound> | null>(null);
|
||||
const pianoSounds = React.useRef<Record<string, Audio.Sound> | null>(null);
|
||||
const cursorPaddingVertical = 10;
|
||||
const cursorPaddingHorizontal = 3;
|
||||
|
||||
@@ -75,40 +72,21 @@ const PartitionMagic = ({
|
||||
React.useEffect(() => {
|
||||
if (!pianoSounds.current) {
|
||||
Promise.all(
|
||||
Object.entries(PianoNotes).map(([midiNumber, noteResource]) => {
|
||||
// Audio.Sound.createAsync(noteResource, {
|
||||
// volume: 1,
|
||||
// progressUpdateIntervalMillis: 100,
|
||||
// }).then((sound) => [midiNumber, sound.sound] as const)
|
||||
return new Promise((resolve, reject) => {
|
||||
const sound = new Sound(noteResource, Sound.MAIN_BUNDLE, (error: any) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve([midiNumber, sound] as const);
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
Object.entries(PianoNotes).map(([midiNumber, noteResource]) =>
|
||||
Audio.Sound.createAsync(noteResource, {
|
||||
volume: 1,
|
||||
progressUpdateIntervalMillis: 100,
|
||||
}).then((sound) => [midiNumber, sound.sound] as const)
|
||||
)
|
||||
).then((res) => {
|
||||
// pianoSounds.current = res.reduce(
|
||||
// (prev, curr) => ({ ...prev, [curr[0]]: curr[1] }),
|
||||
// {}
|
||||
// );
|
||||
pianoSounds.current = {};
|
||||
(res as [string, Sound][]).forEach((curr) => {
|
||||
pianoSounds.current![curr[0]] = curr[1];
|
||||
});
|
||||
pianoSounds.current = res.reduce(
|
||||
(prev, curr) => ({ ...prev, [curr[0]]: curr[1] }),
|
||||
{}
|
||||
);
|
||||
console.log('sound loaded');
|
||||
});
|
||||
}
|
||||
}, [
|
||||
() => {
|
||||
pianoSounds?.current?.forEach((sound) => {
|
||||
sound.release();
|
||||
});
|
||||
},
|
||||
]);
|
||||
}, []);
|
||||
const partitionDims = React.useMemo<[number, number]>(() => {
|
||||
return [data?.pageWidth ?? 0, data?.pageHeight ?? 1];
|
||||
}, [data]);
|
||||
@@ -144,16 +122,12 @@ const PartitionMagic = ({
|
||||
cursor.notes.forEach(({ note, duration }) => {
|
||||
try {
|
||||
const sound = pianoSounds.current![note]!;
|
||||
sound.play((success) => {
|
||||
if (!success) {
|
||||
console.log('Sound did not play');
|
||||
}
|
||||
});
|
||||
sound.playAsync().catch(console.error);
|
||||
setTimeout(() => {
|
||||
sound.stop();
|
||||
sound.stopAsync();
|
||||
}, duration - 10);
|
||||
} catch (e) {
|
||||
console.log('Error key: ', note, e);
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { Animated, StyleProp, StyleSheet, ViewStyle } from 'react-native';
|
||||
|
||||
type StyleObject = Record<string, any>;
|
||||
type InterpolatedStyleObject = Record<string, Animated.AnimatedInterpolation<any>>;
|
||||
|
||||
interface AnimatedBaseProps {
|
||||
style?: StyleObject;
|
||||
defaultStyle: StyleObject;
|
||||
hoverStyle: StyleObject;
|
||||
pressStyle: StyleObject;
|
||||
currentState: number;
|
||||
duration?: number;
|
||||
children?: React.ReactNode;
|
||||
styleContainer?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
const AnimatedBase: React.FC<AnimatedBaseProps> = ({
|
||||
style,
|
||||
defaultStyle,
|
||||
hoverStyle,
|
||||
pressStyle,
|
||||
currentState,
|
||||
children,
|
||||
duration = 250,
|
||||
styleContainer,
|
||||
}) => {
|
||||
const animatedValues = useRef<Record<string, Animated.Value>>({}).current;
|
||||
|
||||
const extractTransformKeys = (styleObject: StyleObject) => {
|
||||
return styleObject.transform
|
||||
? styleObject.transform.map((t: any) => Object.keys(t)[0])
|
||||
: [];
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const allStyleKeys = new Set([
|
||||
...Object.keys(defaultStyle),
|
||||
...Object.keys(hoverStyle),
|
||||
...Object.keys(pressStyle),
|
||||
]);
|
||||
|
||||
allStyleKeys.forEach((key) => {
|
||||
if (!animatedValues[key]) {
|
||||
animatedValues[key] = new Animated.Value(0);
|
||||
console.log('key; ', key);
|
||||
}
|
||||
});
|
||||
|
||||
const allTransformKeys = new Set([
|
||||
...extractTransformKeys(defaultStyle),
|
||||
...extractTransformKeys(hoverStyle),
|
||||
...extractTransformKeys(pressStyle),
|
||||
]);
|
||||
|
||||
allTransformKeys.forEach((key) => {
|
||||
if (!animatedValues[key]) {
|
||||
animatedValues[key] = new Animated.Value(0);
|
||||
console.log('keyxx; ', key);
|
||||
}
|
||||
});
|
||||
}, [defaultStyle, hoverStyle, pressStyle]);
|
||||
|
||||
useEffect(() => {
|
||||
animateToState(currentState);
|
||||
}, [currentState]);
|
||||
|
||||
const getTransformValue = (key: string, style: StyleObject) => {
|
||||
const transformObject = style.transform?.find((t: any) => t.hasOwnProperty(key));
|
||||
return transformObject ? transformObject[key] : 0;
|
||||
};
|
||||
|
||||
const interpolateStyle = (stateStyle: StyleObject): InterpolatedStyleObject => {
|
||||
const interpolatedStyle: InterpolatedStyleObject = {};
|
||||
const transform: any = [];
|
||||
|
||||
Object.keys(animatedValues).forEach((key) => {
|
||||
if (stateStyle.transform?.some((t: any) => t.hasOwnProperty(key))) {
|
||||
const defaultValue = getTransformValue(key, defaultStyle);
|
||||
const hoverValue = getTransformValue(key, hoverStyle);
|
||||
const pressValue = getTransformValue(key, pressStyle);
|
||||
|
||||
const interpolated = animatedValues[key]!.interpolate({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: [defaultValue, hoverValue, pressValue],
|
||||
});
|
||||
|
||||
transform.push({ [key]: interpolated });
|
||||
} else if (stateStyle[key]) {
|
||||
const defaultValue = defaultStyle[key] || 0;
|
||||
const hoverValue = hoverStyle[key] !== undefined ? hoverStyle[key] : defaultValue;
|
||||
const pressValue = pressStyle[key] !== undefined ? pressStyle[key] : defaultValue;
|
||||
|
||||
interpolatedStyle[key] = animatedValues[key]!.interpolate({
|
||||
inputRange: [0, 1, 2],
|
||||
outputRange: [defaultValue, hoverValue, pressValue],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (transform.length > 0) {
|
||||
interpolatedStyle.transform = transform;
|
||||
}
|
||||
|
||||
return interpolatedStyle;
|
||||
};
|
||||
|
||||
const animateToState = (stateValue: number) => {
|
||||
Object.keys(animatedValues).forEach((key) => {
|
||||
Animated.timing(animatedValues[key]!, {
|
||||
toValue: stateValue,
|
||||
duration: duration,
|
||||
useNativeDriver: false,
|
||||
}).start();
|
||||
});
|
||||
};
|
||||
|
||||
const animatedStyle = StyleSheet.flatten([
|
||||
styleContainer,
|
||||
interpolateStyle(defaultStyle),
|
||||
interpolateStyle(hoverStyle),
|
||||
interpolateStyle(pressStyle),
|
||||
]);
|
||||
|
||||
return <Animated.View style={[style, animatedStyle]}>{children && children}</Animated.View>;
|
||||
};
|
||||
|
||||
export default AnimatedBase;
|
||||
@@ -30,7 +30,7 @@ type IconButtonProps = {
|
||||
/**
|
||||
* Callback function triggered when the button is pressed.
|
||||
*/
|
||||
onPress?: () => void | Promise<void>;
|
||||
onPress?: (state: boolean) => void | Promise<void>;
|
||||
|
||||
/**
|
||||
* Size of the icon.
|
||||
@@ -183,7 +183,7 @@ const IconButton: React.FC<IconButtonProps> = ({
|
||||
const toggleState = async () => {
|
||||
// Execute onPress if provided.
|
||||
if (onPress) {
|
||||
await onPress();
|
||||
await onPress(!isActiveState);
|
||||
}
|
||||
|
||||
// Toggle isActiveState.
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import { Pressable } from 'react-native';
|
||||
|
||||
interface InteractiveBaseProps {
|
||||
handleHoverIn: () => void;
|
||||
handleHoverOut: () => void;
|
||||
handlePressIn: () => void;
|
||||
handlePressOut: () => void;
|
||||
children: React.ReactNode;
|
||||
style?: any;
|
||||
}
|
||||
|
||||
const InteractiveBase: React.FC<InteractiveBaseProps> = ({
|
||||
handleHoverIn,
|
||||
handleHoverOut,
|
||||
handlePressIn,
|
||||
handlePressOut,
|
||||
children,
|
||||
style,
|
||||
}) => {
|
||||
return (
|
||||
<Pressable
|
||||
style={style}
|
||||
onHoverIn={handleHoverIn}
|
||||
onPressIn={handlePressIn}
|
||||
onPressOut={handlePressOut}
|
||||
onHoverOut={handleHoverOut}
|
||||
>
|
||||
{children}
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
export default InteractiveBase;
|
||||
@@ -5,19 +5,6 @@ import { Animated, StyleSheet, Pressable, ViewStyle, StyleProp } from 'react-nat
|
||||
type StyleObject = Record<string, any>;
|
||||
type InterpolatedStyleObject = Record<string, Animated.AnimatedInterpolation<any>>;
|
||||
|
||||
const TRANSFORM_WHITELIST = {
|
||||
translateX: true,
|
||||
translateY: true,
|
||||
scale: true,
|
||||
scaleX: true,
|
||||
scaleY: true,
|
||||
rotate: true,
|
||||
rotateX: true,
|
||||
rotateY: true,
|
||||
rotateZ: true,
|
||||
perspective: true,
|
||||
};
|
||||
|
||||
interface InteractiveCCProps {
|
||||
defaultStyle: StyleObject;
|
||||
hoverStyle: StyleObject;
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface MusicItemType {
|
||||
style?: ViewStyle | ViewStyle[];
|
||||
|
||||
/** Callback function triggered when the like button is pressed. */
|
||||
onLike: () => void;
|
||||
onLike: (state: boolean) => void;
|
||||
|
||||
/** Callback function triggered when the song is played. */
|
||||
onPlay: () => void;
|
||||
|
||||
@@ -237,8 +237,6 @@ const styles = StyleSheet.create({
|
||||
// Using `memo` to optimize rendering performance by memorizing the component's output.
|
||||
// This ensures that the component only re-renders when its props change.
|
||||
const MusicList = memo(MusicListComponent, (prev, next) => {
|
||||
console.log('AAAAA');
|
||||
console.log(prev.initialMusics, next.initialMusics);
|
||||
return prev.initialMusics.length == next.initialMusics.length;
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
const InteractionStates = {
|
||||
NORMAL: 0,
|
||||
HOVER: 1,
|
||||
PRESSED: 2,
|
||||
};
|
||||
|
||||
interface InteractionStateProps {
|
||||
onHoverIn?: () => void;
|
||||
onHoverOut?: () => void;
|
||||
onPressIn?: () => void;
|
||||
onPressOut?: () => void;
|
||||
}
|
||||
|
||||
const useInteractionState = ({ onHoverIn, onHoverOut, onPressIn, onPressOut }: InteractionStateProps = {}) => {
|
||||
const [state, setState] = useState(InteractionStates.NORMAL);
|
||||
|
||||
const handleHoverIn = useCallback(() => {
|
||||
setState(InteractionStates.HOVER);
|
||||
if (onHoverIn) onHoverIn();
|
||||
}, [onHoverIn]);
|
||||
|
||||
const handleHoverOut = useCallback(() => {
|
||||
setState(InteractionStates.NORMAL);
|
||||
if (onHoverOut) onHoverOut();
|
||||
}, [onHoverOut]);
|
||||
|
||||
const handlePressIn = useCallback(() => {
|
||||
setState(InteractionStates.PRESSED);
|
||||
if (onPressIn) onPressIn();
|
||||
}, [onPressIn]);
|
||||
|
||||
const handlePressOut = useCallback(() => {
|
||||
setState(InteractionStates.HOVER);
|
||||
if (onPressOut) onPressOut();
|
||||
}, [onPressOut]);
|
||||
|
||||
return {
|
||||
state,
|
||||
handleHoverIn,
|
||||
handleHoverOut,
|
||||
handlePressIn,
|
||||
handlePressOut,
|
||||
};
|
||||
};
|
||||
|
||||
export default useInteractionState;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user