mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 09:09:29 +01:00
some progress on now playing
image colors gradient working for now playing fixed track player init losing handle to notification
This commit is contained in:
parent
28ab092402
commit
0a31597111
14
App.tsx
14
App.tsx
@ -3,7 +3,7 @@ import { DarkTheme, NavigationContainer } from '@react-navigation/native';
|
||||
import SplashPage from './src/components/SplashPage';
|
||||
import RootNavigator from './src/components/navigation/RootNavigator';
|
||||
import { Provider } from 'jotai';
|
||||
import { StatusBar } from 'react-native';
|
||||
import { StatusBar, View } from 'react-native';
|
||||
import colors from './src/styles/colors';
|
||||
import TrackPlayerState from './src/components/TrackPlayerState';
|
||||
|
||||
@ -14,11 +14,13 @@ const App = () => (
|
||||
<Provider>
|
||||
<StatusBar animated={true} backgroundColor={'rgba(0, 0, 0, 0.4)'} barStyle={'light-content'} translucent={true} />
|
||||
<TrackPlayerState />
|
||||
<SplashPage>
|
||||
<NavigationContainer theme={theme}>
|
||||
<RootNavigator />
|
||||
</NavigationContainer>
|
||||
</SplashPage>
|
||||
<View style={{ flex: 1, backgroundColor: colors.gradient.high }}>
|
||||
<SplashPage>
|
||||
<NavigationContainer theme={theme}>
|
||||
<RootNavigator />
|
||||
</NavigationContainer>
|
||||
</SplashPage>
|
||||
</View>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
|
||||
17
index.js
17
index.js
@ -7,7 +7,22 @@ enableScreens();
|
||||
import { AppRegistry } from 'react-native';
|
||||
import App from './App';
|
||||
import { name as appName } from './app.json';
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import TrackPlayer, { Capability } from 'react-native-track-player';
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
TrackPlayer.registerPlaybackService(() => require('./src/playback/service'));
|
||||
|
||||
async function start() {
|
||||
await TrackPlayer.setupPlayer();
|
||||
await TrackPlayer.updateOptions({
|
||||
capabilities: [
|
||||
Capability.Play,
|
||||
Capability.Pause,
|
||||
Capability.Stop,
|
||||
Capability.SkipToNext,
|
||||
Capability.SkipToPrevious,
|
||||
],
|
||||
compactCapabilities: [Capability.Play, Capability.Pause, Capability.SkipToNext, Capability.SkipToPrevious],
|
||||
});
|
||||
}
|
||||
start();
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"react-native-fs": "^2.18.0",
|
||||
"react-native-gesture-handler": "^1.10.3",
|
||||
"react-native-get-random-values": "^1.7.0",
|
||||
"react-native-image-colors": "^1.3.0",
|
||||
"react-native-linear-gradient": "^2.5.6",
|
||||
"react-native-reanimated": "^2.2.0",
|
||||
"react-native-safe-area-context": "^3.2.0",
|
||||
|
||||
@ -1,134 +1,182 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { useAtomValue } from 'jotai/utils';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { StyleSheet, Text, View, StatusBar, useWindowDimensions } from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { currentQueueNameAtom, currentTrackAtom } from '../state/trackplayer';
|
||||
import colors from '../styles/colors';
|
||||
import text from '../styles/text';
|
||||
import CoverArt from './common/CoverArt';
|
||||
import GradientBackground from './common/GradientBackground';
|
||||
import ImageColors from 'react-native-image-colors';
|
||||
|
||||
const NowPlayingHeader = () => {
|
||||
const queueName = useAtomValue(currentQueueNameAtom);
|
||||
|
||||
const NowPlayingLayout = () => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
// background
|
||||
backgroundColor: 'darkblue',
|
||||
flex: 1,
|
||||
}}>
|
||||
{/* top bar */}
|
||||
<View
|
||||
style={{
|
||||
height: 70,
|
||||
flexDirection: 'row',
|
||||
}}>
|
||||
<View style={{ width: 70, height: 70, backgroundColor: 'grey' }} />
|
||||
<View style={{ flex: 1, alignItems: 'center', height: 70 }}>
|
||||
<View style={{ flex: 1 }} />
|
||||
<Text style={styles.text}>Playing from Your Library</Text>
|
||||
<Text style={styles.text}>Songs</Text>
|
||||
<View style={{ flex: 1 }} />
|
||||
</View>
|
||||
<View style={{ width: 70, height: 70, backgroundColor: 'grey' }} />
|
||||
</View>
|
||||
|
||||
{/* album art */}
|
||||
<View
|
||||
style={{
|
||||
flex: 5,
|
||||
// backgroundColor: 'darkorange',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<View style={{ flex: 1 }} />
|
||||
<View
|
||||
style={{
|
||||
width: 320,
|
||||
height: 320,
|
||||
backgroundColor: 'grey',
|
||||
}}
|
||||
/>
|
||||
<View style={{ flex: 1 }} />
|
||||
</View>
|
||||
|
||||
{/* song/album/artist title */}
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
// backgroundColor: 'green',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Text style={{ ...styles.text, fontSize: 26 }}>Name of the Song</Text>
|
||||
<Text style={{ ...styles.text, fontSize: 20, fontWeight: 'normal' }}>Cool Artist</Text>
|
||||
</View>
|
||||
|
||||
{/* seek bar */}
|
||||
<View
|
||||
style={{
|
||||
flex: 0.7,
|
||||
// backgroundColor: 'red',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<View style={{ width: 20 }} />
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
}}>
|
||||
<View>
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'grey',
|
||||
height: 3,
|
||||
marginBottom: 3,
|
||||
// flex: 1,
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
}}>
|
||||
<Text style={{ ...styles.text, fontWeight: 'normal' }}>00:00</Text>
|
||||
<View style={{ flex: 1 }} />
|
||||
<Text style={{ ...styles.text, fontWeight: 'normal' }}>00:00</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<View style={{ width: 20 }} />
|
||||
</View>
|
||||
|
||||
{/* main player controls */}
|
||||
<View
|
||||
style={{
|
||||
height: 90,
|
||||
// backgroundColor: 'darkorange',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
<View style={{ width: 14 }} />
|
||||
<View style={{ width: 60, height: 60, backgroundColor: 'grey' }} />
|
||||
<View style={{ width: 60, height: 60, backgroundColor: 'grey' }} />
|
||||
<View style={{ width: 90, height: 90, backgroundColor: 'grey' }} />
|
||||
<View style={{ width: 60, height: 60, backgroundColor: 'grey' }} />
|
||||
<View style={{ width: 60, height: 60, backgroundColor: 'grey' }} />
|
||||
<View style={{ width: 14 }} />
|
||||
</View>
|
||||
|
||||
{/* extra controls */}
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
// backgroundColor: 'green',
|
||||
flexDirection: 'row',
|
||||
}}>
|
||||
<View style={{ width: 14 }} />
|
||||
<View style={{ width: 60, height: 60, backgroundColor: 'grey' }} />
|
||||
<View style={{ flex: 1 }} />
|
||||
<View style={{ width: 60, height: 60, backgroundColor: 'grey' }} />
|
||||
<View style={{ width: 14 }} />
|
||||
</View>
|
||||
<View style={headerStyles.container}>
|
||||
<FastImage source={require('../../res/arrow_left-fill.png')} style={headerStyles.backArrow} tintColor="white" />
|
||||
<Text numberOfLines={2} style={headerStyles.queueName}>
|
||||
{queueName}
|
||||
</Text>
|
||||
<FastImage source={require('../../res/more_vertical.png')} style={headerStyles.more} tintColor="white" />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
text: {
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
const headerStyles = StyleSheet.create({
|
||||
container: {
|
||||
height: 60,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
// backgroundColor: 'green',
|
||||
},
|
||||
backArrow: {
|
||||
height: 24,
|
||||
width: 24,
|
||||
margin: 20,
|
||||
},
|
||||
queueName: {
|
||||
...text.paragraph,
|
||||
},
|
||||
more: {
|
||||
height: 24,
|
||||
width: 24,
|
||||
margin: 20,
|
||||
},
|
||||
});
|
||||
|
||||
const SongCoverArt = () => {
|
||||
const track = useAtomValue(currentTrackAtom);
|
||||
const layout = useWindowDimensions();
|
||||
|
||||
const size = layout.width - layout.width / 6;
|
||||
|
||||
return (
|
||||
<View style={coverArtStyles.container}>
|
||||
<CoverArt
|
||||
PlaceholderComponent={() => (
|
||||
<View style={{ height: size, width: size }}>
|
||||
<Text>Failed</Text>
|
||||
</View>
|
||||
)}
|
||||
height={size}
|
||||
width={size}
|
||||
coverArtUri={track?.artwork as string}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const coverArtStyles = StyleSheet.create({
|
||||
container: {
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
marginTop: 20,
|
||||
},
|
||||
});
|
||||
|
||||
const SongInfo = () => {
|
||||
const track = useAtomValue(currentTrackAtom);
|
||||
|
||||
return (
|
||||
<View style={infoStyles.container}>
|
||||
<Text style={infoStyles.title}>{track?.title}</Text>
|
||||
<Text style={infoStyles.artist}>{track?.artist}</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const infoStyles = StyleSheet.create({
|
||||
container: {
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
marginTop: 20,
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
title: {
|
||||
...text.songListTitle,
|
||||
fontSize: 22,
|
||||
textAlign: 'center',
|
||||
},
|
||||
artist: {
|
||||
...text.songListSubtitle,
|
||||
fontSize: 14,
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
interface AndroidImageColors {
|
||||
dominant?: string;
|
||||
average?: string;
|
||||
vibrant?: string;
|
||||
darkVibrant?: string;
|
||||
lightVibrant?: string;
|
||||
darkMuted?: string;
|
||||
lightMuted?: string;
|
||||
muted?: string;
|
||||
platform: 'android';
|
||||
}
|
||||
|
||||
interface IOSImageColors {
|
||||
background: string;
|
||||
primary: string;
|
||||
secondary: string;
|
||||
detail: string;
|
||||
quality: Config['quality'];
|
||||
platform: 'ios';
|
||||
}
|
||||
|
||||
interface Config {
|
||||
fallback?: string;
|
||||
pixelSpacing?: number;
|
||||
quality?: 'lowest' | 'low' | 'high' | 'highest';
|
||||
cache?: boolean;
|
||||
key?: string;
|
||||
}
|
||||
|
||||
declare type ImageColorsResult = AndroidImageColors | IOSImageColors;
|
||||
|
||||
const NowPlayingLayout = () => {
|
||||
const track = useAtomValue(currentTrackAtom);
|
||||
const [imageColors, setImageColors] = useState<ImageColorsResult | undefined>(undefined);
|
||||
const ica = imageColors as AndroidImageColors;
|
||||
|
||||
useEffect(() => {
|
||||
async function getColors() {
|
||||
if (track?.artwork === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cachedResult = ImageColors.cache.getItem(track.artwork as string);
|
||||
if (cachedResult) {
|
||||
setImageColors(cachedResult);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await ImageColors.getColors(track.artwork as string, {
|
||||
cache: true,
|
||||
});
|
||||
setImageColors(result);
|
||||
}
|
||||
getColors();
|
||||
}, [track]);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
paddingTop: StatusBar.currentHeight,
|
||||
}}>
|
||||
<GradientBackground
|
||||
colors={[ica ? (ica.muted as string) : colors.gradient.high, colors.gradient.low]}
|
||||
locations={[0.1, 1.0]}
|
||||
/>
|
||||
<NowPlayingHeader />
|
||||
<SongCoverArt />
|
||||
<SongInfo />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default NowPlayingLayout;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import RNFS from 'react-native-fs';
|
||||
import TrackPlayer, { Capability, Track } from 'react-native-track-player';
|
||||
import paths from '../paths';
|
||||
|
||||
async function mkdir(path: string): Promise<void> {
|
||||
@ -27,32 +26,6 @@ const SplashPage: React.FC<{}> = ({ children }) => {
|
||||
await mkdir(paths.imageCache);
|
||||
await mkdir(paths.songCache);
|
||||
await mkdir(paths.songs);
|
||||
|
||||
await TrackPlayer.setupPlayer();
|
||||
TrackPlayer.updateOptions({
|
||||
capabilities: [
|
||||
Capability.Play,
|
||||
Capability.Pause,
|
||||
Capability.Stop,
|
||||
Capability.SkipToNext,
|
||||
Capability.SkipToPrevious,
|
||||
],
|
||||
compactCapabilities: [Capability.Play, Capability.Pause, Capability.SkipToNext, Capability.SkipToPrevious],
|
||||
});
|
||||
|
||||
const castlevania: Track = {
|
||||
id: 'castlevania',
|
||||
url: 'http://www.vgmuseum.com/mrp/cv1/music/03.mp3',
|
||||
title: 'Stage 1: Castle Entrance',
|
||||
artist: 'Kinuyo Yamashita and S.Terishima',
|
||||
duration: 110,
|
||||
artwork: 'https://webgames.host/uploads/2017/03/castlevania-3-draculas-curse.jpg',
|
||||
genre: 'BGM',
|
||||
date: new Date(1989, 1).toISOString(),
|
||||
};
|
||||
|
||||
await TrackPlayer.add([castlevania]);
|
||||
// TrackPlayer.play();
|
||||
};
|
||||
|
||||
const promise = Promise.all([prepare(), minSplashTime]);
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import TrackPlayer, { Event, State, Track, useTrackPlayerEvents } from 'react-native-track-player';
|
||||
import TrackPlayer, { Event, useTrackPlayerEvents } from 'react-native-track-player';
|
||||
import { useAppState } from '@react-native-community/hooks';
|
||||
import { useUpdateAtom, useAtomValue } from 'jotai/utils';
|
||||
import { currentTrackAtom } from '../state/trackplayer';
|
||||
import { currentQueueNameAtom, currentTrackAtom } from '../state/trackplayer';
|
||||
import { View } from 'react-native';
|
||||
|
||||
const TrackPlayerState = () => {
|
||||
const CurrentTrackState = () => {
|
||||
const setCurrentTrack = useUpdateAtom(currentTrackAtom);
|
||||
const appState = useAppState();
|
||||
|
||||
const updateCurrentTrack = useCallback(async () => {
|
||||
const update = useCallback(async () => {
|
||||
const index = await TrackPlayer.getCurrentTrack();
|
||||
|
||||
if (index !== null && index >= 0) {
|
||||
@ -39,34 +39,59 @@ const TrackPlayerState = () => {
|
||||
setCurrentTrack(undefined);
|
||||
return;
|
||||
}
|
||||
updateCurrentTrack();
|
||||
update();
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (appState === 'active') {
|
||||
updateCurrentTrack();
|
||||
update();
|
||||
}
|
||||
}, [appState, updateCurrentTrack]);
|
||||
}, [appState, update]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
const CurrentTrack = () => {
|
||||
const currentTrack = useAtomValue(currentTrackAtom);
|
||||
const CurrentQueueName = () => {
|
||||
const setCurrentQueueName = useUpdateAtom(currentQueueNameAtom);
|
||||
const appState = useAppState();
|
||||
|
||||
const update = useCallback(async () => {
|
||||
const queue = await TrackPlayer.getQueue();
|
||||
|
||||
if (queue !== null && queue.length > 0) {
|
||||
setCurrentQueueName(queue[0].queueName);
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentQueueName(undefined);
|
||||
}, [setCurrentQueueName]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(currentTrack?.title);
|
||||
}, [currentTrack]);
|
||||
if (appState === 'active') {
|
||||
update();
|
||||
}
|
||||
}, [appState, update]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
const ASDFSADFSAF = () => (
|
||||
const Debug = () => {
|
||||
const value = useAtomValue(currentQueueNameAtom);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(value);
|
||||
}, [value]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
const TrackPlayerState = () => (
|
||||
<View>
|
||||
<TrackPlayerState />
|
||||
<CurrentTrack />
|
||||
<CurrentTrackState />
|
||||
<CurrentQueueName />
|
||||
<Debug />
|
||||
</View>
|
||||
);
|
||||
|
||||
export default ASDFSADFSAF;
|
||||
export default TrackPlayerState;
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
useWindowDimensions,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { useSetQueue } from '../../hooks/player';
|
||||
import { useSetQueue } from '../../hooks/trackplayer';
|
||||
import { albumAtomFamily } from '../../state/music';
|
||||
import { currentTrackAtom } from '../../state/trackplayer';
|
||||
import colors from '../../styles/colors';
|
||||
@ -138,7 +138,7 @@ const AlbumDetails: React.FC<{
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
}}>
|
||||
<Button title="Play Album" onPress={() => setQueue(album.songs, album.songs[0].id)} />
|
||||
<Button title="Play Album" onPress={() => setQueue(album.songs, album.name, album.songs[0].id)} />
|
||||
</View>
|
||||
|
||||
<View
|
||||
@ -162,7 +162,7 @@ const AlbumDetails: React.FC<{
|
||||
title={s.title}
|
||||
artist={s.artist}
|
||||
track={s.track}
|
||||
onPress={() => setQueue(album.songs, s.id)}
|
||||
onPress={() => setQueue(album.songs, album.name, s.id)}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
import React from 'react';
|
||||
import { useWindowDimensions, ViewStyle } from 'react-native';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import colors from '../../styles/colors';
|
||||
import colorStyles from '../../styles/colors';
|
||||
|
||||
const GradientBackground: React.FC<{
|
||||
height?: number | string;
|
||||
width?: number | string;
|
||||
position?: 'relative' | 'absolute';
|
||||
style?: ViewStyle;
|
||||
}> = ({ height, width, position, style, children }) => {
|
||||
colors?: string[];
|
||||
locations?: number[];
|
||||
}> = ({ height, width, position, style, colors, locations, children }) => {
|
||||
const layout = useWindowDimensions();
|
||||
|
||||
return (
|
||||
<LinearGradient
|
||||
colors={[colors.gradient.high, colors.gradient.low]}
|
||||
locations={[0.01, 0.7]}
|
||||
colors={colors || [colorStyles.gradient.high, colorStyles.gradient.low]}
|
||||
locations={locations || [0.01, 0.7]}
|
||||
style={{
|
||||
...style,
|
||||
width: width || '100%',
|
||||
|
||||
@ -6,9 +6,12 @@ import BottomTabNavigator from './BottomTabNavigator';
|
||||
const RootStack = createNativeStackNavigator();
|
||||
|
||||
const RootNavigator = () => (
|
||||
<RootStack.Navigator>
|
||||
<RootStack.Screen name="Main" component={BottomTabNavigator} options={{ headerShown: false }} />
|
||||
<RootStack.Screen name="Now Playing" component={NowPlayingLayout} options={{ headerShown: false }} />
|
||||
<RootStack.Navigator
|
||||
screenOptions={{
|
||||
headerShown: false,
|
||||
}}>
|
||||
<RootStack.Screen name="Main" component={BottomTabNavigator} />
|
||||
<RootStack.Screen name="Now Playing" component={NowPlayingLayout} />
|
||||
</RootStack.Navigator>
|
||||
);
|
||||
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { useUpdateAtom } from 'jotai/utils';
|
||||
import TrackPlayer, { Track } from 'react-native-track-player';
|
||||
import { Song } from '../models/music';
|
||||
import { currentTrackAtom } from '../state/trackplayer';
|
||||
import { currentQueueNameAtom, currentTrackAtom } from '../state/trackplayer';
|
||||
|
||||
function mapSongToTrack(song: Song): Track {
|
||||
function mapSongToTrack(song: Song, queueName: string): Track {
|
||||
return {
|
||||
id: song.id,
|
||||
queueName,
|
||||
title: song.title,
|
||||
artist: song.artist || 'Unknown Artist',
|
||||
url: song.streamUri,
|
||||
@ -16,11 +17,13 @@ function mapSongToTrack(song: Song): Track {
|
||||
|
||||
export const useSetQueue = () => {
|
||||
const setCurrentTrack = useUpdateAtom(currentTrackAtom);
|
||||
const setCurrentQueueName = useUpdateAtom(currentQueueNameAtom);
|
||||
|
||||
return async (songs: Song[], playId?: string) => {
|
||||
return async (songs: Song[], name: string, playId?: string) => {
|
||||
await TrackPlayer.reset();
|
||||
const tracks = songs.map(mapSongToTrack);
|
||||
const tracks = songs.map(s => mapSongToTrack(s, name));
|
||||
|
||||
setCurrentQueueName(name);
|
||||
if (playId) {
|
||||
setCurrentTrack(tracks.find(t => t.id === playId));
|
||||
}
|
||||
@ -8,9 +8,20 @@ const currentTrack = atom<OptionalTrack>(undefined);
|
||||
export const currentTrackAtom = atom<OptionalTrack, OptionalTrack>(
|
||||
get => get(currentTrack),
|
||||
(get, set, value) => {
|
||||
if (equal(get(currentTrack), value)) {
|
||||
return;
|
||||
if (!equal(get(currentTrack), value)) {
|
||||
set(currentTrack, value);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
type OptionalString = string | undefined;
|
||||
|
||||
const currentQueueName = atom<OptionalString>(undefined);
|
||||
export const currentQueueNameAtom = atom<OptionalString, OptionalString>(
|
||||
get => get(currentQueueName),
|
||||
(get, set, value) => {
|
||||
if (get(currentQueueName) !== value) {
|
||||
set(currentQueueName, value);
|
||||
}
|
||||
set(currentTrack, value);
|
||||
},
|
||||
);
|
||||
|
||||
@ -5429,6 +5429,11 @@ react-native-get-random-values@^1.7.0:
|
||||
dependencies:
|
||||
fast-base64-decode "^1.0.0"
|
||||
|
||||
react-native-image-colors@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-image-colors/-/react-native-image-colors-1.3.0.tgz#3e499730618540fddb779b3f73defe81d2d91801"
|
||||
integrity sha512-1k+4wXWqm+sFA3H01Xri9HQ4UCWi0dvuUID4rrnzu6VlR/Oa525Scot0li36IOTOl0x/Ar883bh0bcfHOhi+vg==
|
||||
|
||||
react-native-iphone-x-helper@^1.3.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz#20c603e9a0e765fd6f97396638bdeb0e5a60b010"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user