From 38fa01620d75a1f1754b77e3bae784434d4d8f61 Mon Sep 17 00:00:00 2001 From: Lei Nelissen Date: Sun, 21 Jun 2020 10:30:41 +0200 Subject: [PATCH] Refactor some generic components --- src/components/App.tsx | 2 +- src/components/TouchableHandler.tsx | 25 +++++++ src/screens/Albums/components/Album.tsx | 72 +++++++------------ src/screens/Albums/components/Albums.tsx | 42 ++++------- .../Player/components/MediaControls.tsx | 2 +- src/screens/Player/components/NowPlaying.tsx | 2 +- src/screens/Player/components/Queue.tsx | 4 +- src/screens/Settings/index.tsx | 2 +- .../components/CredentialGenerator.tsx | 2 +- .../modals/SetJellyfinServer/index.tsx | 6 +- src/store/music/actions.ts | 2 +- src/utility/JellyfinApi.ts | 4 +- src/utility/usePlayAlbum.ts | 40 +++++++++++ src/utility/usePlayTrack.ts | 36 ++++++++++ tsconfig.json | 2 +- 15 files changed, 153 insertions(+), 90 deletions(-) create mode 100644 src/components/TouchableHandler.tsx create mode 100644 src/utility/usePlayAlbum.ts create mode 100644 src/utility/usePlayTrack.ts diff --git a/src/components/App.tsx b/src/components/App.tsx index 0cef05e..6184670 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -4,7 +4,7 @@ import TrackPlayer from 'react-native-track-player'; import { PersistGate } from 'redux-persist/integration/react'; import { NavigationContainer } from '@react-navigation/native'; import Routes from '../screens'; -import store, { persistedStore } from '../store'; +import store, { persistedStore } from 'store'; interface State { isReady: boolean; diff --git a/src/components/TouchableHandler.tsx b/src/components/TouchableHandler.tsx new file mode 100644 index 0000000..5689ee3 --- /dev/null +++ b/src/components/TouchableHandler.tsx @@ -0,0 +1,25 @@ +import React, { useCallback } from 'react'; +import { TouchableOpacity } from 'react-native'; + +interface TouchableHandlerProps { + id: string; + onPress: (id: string) => void; +} + +/** + * This is a generic handler that accepts id as a prop, and return it when it is + * pressed. This comes in handy with lists in which albums / tracks need to be selected. + */ +const TouchableHandler: React.FC = ({ id, onPress, children }) => { + const handlePress = useCallback(() => { + return onPress(id); + }, [id]); + + return ( + + {children} + + ); +}; + +export default TouchableHandler; \ No newline at end of file diff --git a/src/screens/Albums/components/Album.tsx b/src/screens/Albums/components/Album.tsx index 40ed2fa..6fd5e65 100644 --- a/src/screens/Albums/components/Album.tsx +++ b/src/screens/Albums/components/Album.tsx @@ -1,16 +1,18 @@ import React, { useCallback, useEffect } from 'react'; -import TrackPlayer from 'react-native-track-player'; import { StackParams } from '../types'; -import { Text, ScrollView, Dimensions, Button, TouchableOpacity, RefreshControl } from 'react-native'; -import { generateTrack, useGetImage } from '../../../utility/JellyfinApi'; +import { Text, ScrollView, Dimensions, Button, RefreshControl } from 'react-native'; +import { useGetImage } from 'utility/JellyfinApi'; import styled from 'styled-components/native'; import { useRoute, RouteProp } from '@react-navigation/native'; import FastImage from 'react-native-fast-image'; import { useDispatch } from 'react-redux'; import { differenceInDays } from 'date-fns'; -import { useTypedSelector } from '../../../store'; -import { fetchTracksByAlbum } from '../../../store/music/actions'; -import { ALBUM_CACHE_AMOUNT_OF_DAYS } from '../../../CONSTANTS'; +import { useTypedSelector } from 'store'; +import { fetchTracksByAlbum } from 'store/music/actions'; +import { ALBUM_CACHE_AMOUNT_OF_DAYS } from 'CONSTANTS'; +import usePlayAlbum from 'utility/usePlayAlbum'; +import usePlayTrack from 'utility/usePlayTrack'; +import TouchableHandler from 'components/TouchableHandler'; type Route = RouteProp; @@ -30,52 +32,21 @@ const TrackContainer = styled.View` flex-direction: row; `; -interface TouchableTrackProps { - id: string; - onPress: (id: string) => void; -} - -const TouchableTrack: React.FC = ({ id, onPress, children }) => { - const handlePress = useCallback(() => { - return onPress(id); - }, [id]); - - return ( - - - {children} - - - ); -}; - const Album: React.FC = () => { // Retrieve state const { params: { id } } = useRoute(); const tracks = useTypedSelector((state) => state.music.tracks.entities); const album = useTypedSelector((state) => state.music.albums.entities[id]); const isLoading = useTypedSelector((state) => state.music.tracks.isLoading); - const credentials = useTypedSelector((state) => state.settings.jellyfin); // Retrieve helpers const dispatch = useDispatch(); const getImage = useGetImage(); + const playAlbum = usePlayAlbum(); - // Set callbacks - const selectTrack = useCallback(async (trackId) => { - const newTrack = generateTrack(tracks[trackId], credentials); - console.log(newTrack); - await TrackPlayer.add([ newTrack ]); - await TrackPlayer.skip(trackId); - TrackPlayer.play(); - }, [tracks, credentials]); - const playAlbum = useCallback(async () => { - const newTracks = album.Tracks.map((trackId) => generateTrack(tracks[trackId], credentials)); - await TrackPlayer.removeUpcomingTracks(); - await TrackPlayer.add(newTracks); - await TrackPlayer.skip(album.Tracks[0]); - TrackPlayer.play(); - }, [tracks, credentials]); + // Setup callbacks + const selectAlbum = useCallback(() => { playAlbum(id); }, [playAlbum]); + const selectTrack = usePlayTrack(); const refresh = useCallback(() => { dispatch(fetchTracksByAlbum(id)); }, [id]); // Retrieve album tracks on load @@ -85,6 +56,11 @@ const Album: React.FC = () => { } }, []); + // GUARD: If there is no album, we cannot render a thing + if (!album) { + return null; + } + return ( { } > - + {album?.Name} {album?.AlbumArtist} -