From 2369ef68aea0fe844b66db5c632f76bb813ee046 Mon Sep 17 00:00:00 2001 From: Lei Nelissen Date: Wed, 17 Jun 2020 14:58:04 +0200 Subject: [PATCH] Port to Redux and add settings --- .eslintrc.js | 1 + ios/Podfile.lock | 18 +++ package-lock.json | 132 ++++++++++++++++++ package.json | 10 ++ src/components/App.tsx | 13 +- src/components/Input.tsx | 10 ++ src/components/Modal.tsx | 29 ++++ src/screens/Albums/components/Album.tsx | 99 +++++++------ src/screens/Albums/components/Albums.tsx | 99 ++++++------- src/screens/Albums/types.ts | 5 +- src/screens/Settings/index.tsx | 28 +++- src/screens/index.tsx | 42 +++++- .../components/CredentialGenerator.tsx | 63 +++++++++ .../modals/SetJellyfinServer/index.tsx | 48 +++++++ src/screens/types.ts | 0 src/store/index.ts | 34 +++++ src/store/music/actions.ts | 32 +++++ src/store/music/index.ts | 41 ++++++ src/store/music/types.ts | 75 ++++++++++ src/store/settings/actions.ts | 4 + src/store/settings/index.ts | 29 ++++ src/utility/JellyfinApi.ts | 76 +++++----- 22 files changed, 730 insertions(+), 158 deletions(-) create mode 100644 src/components/Input.tsx create mode 100644 src/components/Modal.tsx create mode 100644 src/screens/modals/SetJellyfinServer/components/CredentialGenerator.tsx create mode 100644 src/screens/modals/SetJellyfinServer/index.tsx create mode 100644 src/screens/types.ts create mode 100644 src/store/index.ts create mode 100644 src/store/music/actions.ts create mode 100644 src/store/music/index.ts create mode 100644 src/store/music/types.ts create mode 100644 src/store/settings/actions.ts create mode 100644 src/store/settings/index.ts diff --git a/.eslintrc.js b/.eslintrc.js index 5ac168b..69b9353 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -46,6 +46,7 @@ module.exports = { 'always' ], "no-unused-vars": "off", + "react/prop-types": "off", "@typescript-eslint/no-unused-vars": [ "error" ] diff --git a/ios/Podfile.lock b/ios/Podfile.lock index bf760f8..f46e7d2 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -237,6 +237,8 @@ PODS: - React - react-native-track-player (1.2.3): - React + - react-native-webview (10.3.1): + - React - React-RCTActionSheet (0.62.2): - React-Core/RCTActionSheetHeaders (= 0.62.2) - React-RCTAnimation (0.62.2): @@ -296,8 +298,12 @@ PODS: - React-cxxreact (= 0.62.2) - React-jsi (= 0.62.2) - ReactCommon/callinvoker (= 0.62.2) + - RNCAsyncStorage (1.11.0): + - React - RNCMaskedView (0.1.10): - React + - RNCPicker (1.6.3): + - React - RNGestureHandler (1.6.1): - React - RNReanimated (1.9.0): @@ -348,6 +354,7 @@ DEPENDENCIES: - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - react-native-track-player (from `../node_modules/react-native-track-player`) + - react-native-webview (from `../node_modules/react-native-webview`) - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) @@ -359,7 +366,9 @@ DEPENDENCIES: - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) - ReactCommon/callinvoker (from `../node_modules/react-native/ReactCommon`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)" - "RNCMaskedView (from `../node_modules/@react-native-community/masked-view`)" + - "RNCPicker (from `../node_modules/@react-native-community/picker`)" - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) @@ -414,6 +423,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-safe-area-context" react-native-track-player: :path: "../node_modules/react-native-track-player" + react-native-webview: + :path: "../node_modules/react-native-webview" React-RCTActionSheet: :path: "../node_modules/react-native/Libraries/ActionSheetIOS" React-RCTAnimation: @@ -434,8 +445,12 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/Libraries/Vibration" ReactCommon: :path: "../node_modules/react-native/ReactCommon" + RNCAsyncStorage: + :path: "../node_modules/@react-native-community/async-storage" RNCMaskedView: :path: "../node_modules/@react-native-community/masked-view" + RNCPicker: + :path: "../node_modules/@react-native-community/picker" RNGestureHandler: :path: "../node_modules/react-native-gesture-handler" RNReanimated: @@ -475,6 +490,7 @@ SPEC CHECKSUMS: React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493 react-native-safe-area-context: e9a863c2dbbc0d42d2b12209d63fa79dfbc9662c react-native-track-player: ba2416753b58f3cdf9db5a07daa65876d659f925 + react-native-webview: 938d49de66f0d5c95134f19682243cbe53294f51 React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0 React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71 @@ -485,7 +501,9 @@ SPEC CHECKSUMS: React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256 ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3 + RNCAsyncStorage: db711e29e5e0500d9bd21aa0c2e397efa45302b1 RNCMaskedView: f5c7d14d6847b7b44853f7acb6284c1da30a3459 + RNCPicker: 1c63b084bcbcd33d159a83144543f3bda4cd1793 RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38 RNReanimated: b5ccb50650ba06f6e749c7c329a1bc3ae0c88b43 RNScreens: 62211832af51e0aebcf6e8c36bcf7dd65592f244 diff --git a/package-lock.json b/package-lock.json index 8930ba7..c80652a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1512,6 +1512,14 @@ } } }, + "@react-native-community/async-storage": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@react-native-community/async-storage/-/async-storage-1.11.0.tgz", + "integrity": "sha512-Pq9LlmvtCEKAGdkyrgTcRxNh2fnHFykEj2qnRYijOl1pDIl2MkD5IxaXu5eOL0wgOtAl4U//ff4z40Td6XR5rw==", + "requires": { + "deep-assign": "^3.0.0" + } + }, "@react-native-community/cli-debugger-ui": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-4.9.0.tgz", @@ -1643,6 +1651,11 @@ "resolved": "https://registry.npmjs.org/@react-native-community/masked-view/-/masked-view-0.1.10.tgz", "integrity": "sha512-rk4sWFsmtOw8oyx8SD3KSvawwaK7gRBSEIy2TAwURyGt+3TizssXP1r8nx3zY+R7v2vYYHXZ+k2/GULAT/bcaQ==" }, + "@react-native-community/picker": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@react-native-community/picker/-/picker-1.6.3.tgz", + "integrity": "sha512-8oPcwimsz14BDu0a8smuqw52uuaXgWrUKAVhv1mXMFvZq3Spt23UVH8Q0Zogi4t7WNlwx4dNXB08iUUP8ltKzg==" + }, "@react-navigation/bottom-tabs": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-5.5.2.tgz", @@ -1698,6 +1711,17 @@ "react-native-iphone-x-helper": "^1.2.1" } }, + "@reduxjs/toolkit": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.3.6.tgz", + "integrity": "sha512-eNYURfoJa6mRNU5YtBVbmE5+nDoc4lpjZ181PBwRC6nIFYZdNR3GcoQ4uomFt8eHpXAUAdpCdxBlDsmwyXOt9Q==", + "requires": { + "immer": "^6.0.1", + "redux": "^4.0.0", + "redux-thunk": "^2.3.0", + "reselect": "^4.0.0" + } + }, "@types/babel__core": { "version": "7.1.8", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.8.tgz", @@ -1828,6 +1852,18 @@ "@types/react": "*" } }, + "@types/react-redux": { + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.9.tgz", + "integrity": "sha512-mpC0jqxhP4mhmOl3P4ipRsgTgbNofMRXJb08Ms6gekViLj61v1hOZEKWDCyWsdONr6EjEA6ZHXC446wdywDe0w==", + "dev": true, + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "@types/react-test-renderer": { "version": "16.9.2", "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.9.2.tgz", @@ -1837,6 +1873,14 @@ "@types/react": "*" } }, + "@types/redux-logger": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/redux-logger/-/redux-logger-3.0.8.tgz", + "integrity": "sha512-zM+cxiSw6nZtRbxpVp9SE3x/X77Z7e7YAfHD1NkxJyJbAGSXJGF0E9aqajZfPOa/sTYnuwutmlCldveExuCeLw==", + "requires": { + "redux": "^4.0.0" + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -3154,6 +3198,19 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, + "deep-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-3.0.0.tgz", + "integrity": "sha512-YX2i9XjJ7h5q/aQ/IM9PEwEnDqETAIYbggmdDB3HLTlSgo1CxPsj6pvhPG68rq6SVE0+p+6Ywsm5fTYNrYtBWw==", + "requires": { + "is-obj": "^1.0.0" + } + }, + "deep-diff": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", + "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=" + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -4709,6 +4766,11 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz", "integrity": "sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA==" }, + "immer": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/immer/-/immer-6.0.9.tgz", + "integrity": "sha512-SyCYnAuiRf67Lvk0VkwFvwtDoEiCMjeamnHvRfnVDyc7re1/rQrNxuL+jJ7lA3WvdC4uznrvbmm+clJ9+XXatg==" + }, "import-fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", @@ -4983,6 +5045,11 @@ } } }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -8686,6 +8753,34 @@ "version": "github:react-native-kit/react-native-track-player#d6e26a4b27f4bb6a5a1dbfb67407d165a809439a", "from": "github:react-native-kit/react-native-track-player" }, + "react-native-webview": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-10.3.1.tgz", + "integrity": "sha512-Ac0v4lFaJl5P/haLook+UIKMW4Ru5xg0+AdxD2oJRB2TPgdqoATziYNU0LBMjntlxDp+DVzdr3fVDuLMdox0tg==", + "requires": { + "escape-string-regexp": "2.0.0", + "invariant": "2.2.4" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + } + } + }, + "react-redux": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz", + "integrity": "sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==", + "requires": { + "@babel/runtime": "^7.5.5", + "hoist-non-react-statics": "^3.3.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.9.0" + } + }, "react-refresh": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz", @@ -8783,6 +8878,33 @@ "util.promisify": "^1.0.0" } }, + "redux": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", + "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", + "requires": { + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" + } + }, + "redux-logger": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/redux-logger/-/redux-logger-3.0.6.tgz", + "integrity": "sha1-91VZZvMJjzyIYExEnPC69XeCdL8=", + "requires": { + "deep-diff": "^0.3.5" + } + }, + "redux-persist": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", + "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==" + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, "regenerate": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", @@ -8941,6 +9063,11 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, + "reselect": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz", + "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==" + }, "resolve": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", @@ -9702,6 +9829,11 @@ "has-flag": "^4.0.0" } }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", diff --git a/package.json b/package.json index d9815a1..37fe316 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,15 @@ "@fortawesome/fontawesome-svg-core": "^1.2.28", "@fortawesome/free-solid-svg-icons": "^5.13.0", "@fortawesome/react-native-fontawesome": "^0.2.5", + "@react-native-community/async-storage": "^1.11.0", "@react-native-community/masked-view": "^0.1.10", + "@react-native-community/picker": "^1.6.3", "@react-navigation/bottom-tabs": "^5.5.2", "@react-navigation/native": "^5.5.1", "@react-navigation/stack": "^5.5.1", + "@reduxjs/toolkit": "^1.3.6", "@types/lodash": "^4.14.155", + "@types/redux-logger": "^3.0.8", "@types/styled-components": "^5.1.0", "lodash": "^4.17.15", "react": "16.11.0", @@ -30,6 +34,11 @@ "react-native-svg": "^12.1.0", "react-native-swift": "^1.2.3", "react-native-track-player": "github:react-native-kit/react-native-track-player", + "react-native-webview": "^10.3.1", + "react-redux": "^7.2.0", + "redux": "^4.0.5", + "redux-logger": "^3.0.6", + "redux-persist": "^6.0.0", "styled-components": "^5.1.1" }, "devDependencies": { @@ -38,6 +47,7 @@ "@react-native-community/eslint-config": "^1.0.0", "@types/jest": "^24.0.24", "@types/react-native": "^0.62.0", + "@types/react-redux": "^7.1.9", "@types/react-test-renderer": "16.9.2", "@typescript-eslint/eslint-plugin": "^2.27.0", "@typescript-eslint/parser": "^2.27.0", diff --git a/src/components/App.tsx b/src/components/App.tsx index f336983..e573b67 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,7 +1,10 @@ import React, { Component } from 'react'; +import { Provider } from 'react-redux'; 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'; interface State { isReady: boolean; @@ -34,9 +37,13 @@ export default class App extends Component { } return ( - - - + + + + + + + ); } } \ No newline at end of file diff --git a/src/components/Input.tsx b/src/components/Input.tsx new file mode 100644 index 0000000..02cc820 --- /dev/null +++ b/src/components/Input.tsx @@ -0,0 +1,10 @@ +import styled from 'styled-components/native'; + +const Input = styled.TextInput` + margin: 10px 0; + background-color: #f6f6f6; + border-radius: 5px; + padding: 15px; +`; + +export default Input; \ No newline at end of file diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx new file mode 100644 index 0000000..16c5842 --- /dev/null +++ b/src/components/Modal.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import styled from 'styled-components/native'; +import { SafeAreaView } from 'react-native'; + +const Background = styled.View` + background-color: #eeeeeeee; + padding: 100px 25px; + flex: 1; +`; + +const Container = styled.View` + background-color: white; + border-radius: 20px; + flex: 1; +`; + +const Modal: React.FC = ({ children }) => { + return ( + + + + {children} + + + + ); +}; + +export default Modal; \ No newline at end of file diff --git a/src/screens/Albums/components/Album.tsx b/src/screens/Albums/components/Album.tsx index 9e52c9b..98ab8ae 100644 --- a/src/screens/Albums/components/Album.tsx +++ b/src/screens/Albums/components/Album.tsx @@ -1,18 +1,15 @@ -import React, { Component, useCallback } from 'react'; +import React, { useCallback, useEffect } from 'react'; import TrackPlayer from 'react-native-track-player'; -import { StackScreenProps } from '@react-navigation/stack'; -import { RootStackParamList, AlbumTrack } from '../types'; -import { Text, ScrollView, Dimensions, FlatList, Button, TouchableOpacity } from 'react-native'; -import { retrieveAlbumTracks, getImage, generateTrack } from '../../../utility/JellyfinApi'; +import { StackParams } from '../types'; +import { Text, ScrollView, Dimensions, Button, TouchableOpacity } from 'react-native'; +import { generateTrack, useGetImage } from '../../../utility/JellyfinApi'; import styled from 'styled-components/native'; +import { useRoute, RouteProp } from '@react-navigation/native'; +import { useDispatch } from 'react-redux'; +import { useTypedSelector } from '../../../store'; +import { fetchTracksByAlbum } from '../../../store/music/actions'; -interface Props extends StackScreenProps { - // -} - -interface State { - tracks: AlbumTrack[]; -} +type Route = RouteProp; const Screen = Dimensions.get('screen'); @@ -49,48 +46,50 @@ const TouchableTrack: React.FC = ({ id, onPress, children ); }; -export default class Album extends Component { - state: State = { - tracks: [], - } +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 credentials = useTypedSelector((state) => state.settings.jellyfin); - async componentDidMount() { - const tracks = await retrieveAlbumTracks(this.props.route.params.id); - this.setState({ tracks }); - } + // Retrieve helpers + const dispatch = useDispatch(); + const getImage = useGetImage(); - selectTrack = async (id: string) => { - const track = await generateTrack(id); - await TrackPlayer.add([ track ]); - await TrackPlayer.skip(id); + // 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(); - } - - playAlbum = async () => { - const tracks = await Promise.all(this.state.tracks.map(track => generateTrack(track.Id))); + }, [tracks, credentials]); + const playAlbum = useCallback(async () => { + const newTracks = album.Tracks.map((trackId) => generateTrack(tracks[trackId], credentials)); await TrackPlayer.removeUpcomingTracks(); - await TrackPlayer.add(tracks); - await TrackPlayer.skip(tracks[0].id); + await TrackPlayer.add(newTracks); + await TrackPlayer.skip(album.Tracks[0]); TrackPlayer.play(); - } + }, [tracks, credentials]); - render() { - const { tracks } = this.state; - const { album } = this.props.route.params; + // Retrieve album tracks on load + useEffect(() => { dispatch(fetchTracksByAlbum(id)); }, []); - return ( - - - {album.Name} - {album.AlbumArtist} -