chore: upgrade all dependencies
This commit is contained in:
@@ -958,22 +958,24 @@ PODS:
|
|||||||
- React-Mapbuffer (0.73.3):
|
- React-Mapbuffer (0.73.3):
|
||||||
- glog
|
- glog
|
||||||
- React-debug
|
- React-debug
|
||||||
- react-native-blur (4.3.0):
|
- react-native-blur (4.4.0):
|
||||||
|
- glog
|
||||||
|
- RCT-Folly (= 2022.05.16.00)
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-netinfo (11.2.1):
|
- react-native-netinfo (11.2.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-safe-area-context (4.8.2):
|
- react-native-safe-area-context (4.9.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-skia (0.1.237):
|
- react-native-skia (0.1.238):
|
||||||
- glog
|
- glog
|
||||||
- RCT-Folly (= 2022.05.16.00)
|
- RCT-Folly (= 2022.05.16.00)
|
||||||
- React
|
- React
|
||||||
- React-callinvoker
|
- React-callinvoker
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-track-player (3.2.0):
|
- react-native-track-player (4.0.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- SwiftAudioEx (= 0.15.3)
|
- SwiftAudioEx (= 1.0.0)
|
||||||
- react-native-webview (13.6.4):
|
- react-native-webview (13.7.1):
|
||||||
- glog
|
- glog
|
||||||
- RCT-Folly (= 2022.05.16.00)
|
- RCT-Folly (= 2022.05.16.00)
|
||||||
- React-Core
|
- React-Core
|
||||||
@@ -1154,13 +1156,13 @@ PODS:
|
|||||||
- SDWebImageWebPCoder (~> 0.8.4)
|
- SDWebImageWebPCoder (~> 0.8.4)
|
||||||
- RNFS (2.20.0):
|
- RNFS (2.20.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNGestureHandler (2.14.1):
|
- RNGestureHandler (2.15.0):
|
||||||
- glog
|
- glog
|
||||||
- RCT-Folly (= 2022.05.16.00)
|
- RCT-Folly (= 2022.05.16.00)
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNLocalize (2.2.4):
|
- RNLocalize (3.0.6):
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNReanimated (3.6.2):
|
- RNReanimated (3.7.0):
|
||||||
- glog
|
- glog
|
||||||
- RCT-Folly (= 2022.05.16.00)
|
- RCT-Folly (= 2022.05.16.00)
|
||||||
- React-Core
|
- React-Core
|
||||||
@@ -1169,7 +1171,7 @@ PODS:
|
|||||||
- glog
|
- glog
|
||||||
- RCT-Folly (= 2022.05.16.00)
|
- RCT-Folly (= 2022.05.16.00)
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNSentry (5.17.0):
|
- RNSentry (5.18.0):
|
||||||
- hermes-engine
|
- hermes-engine
|
||||||
- React-Core
|
- React-Core
|
||||||
- React-hermes
|
- React-hermes
|
||||||
@@ -1186,7 +1188,7 @@ PODS:
|
|||||||
- SentryPrivate (= 8.17.1)
|
- SentryPrivate (= 8.17.1)
|
||||||
- SentryPrivate (8.17.1)
|
- SentryPrivate (8.17.1)
|
||||||
- SocketRocket (0.6.1)
|
- SocketRocket (0.6.1)
|
||||||
- SwiftAudioEx (0.15.3)
|
- SwiftAudioEx (1.0.0)
|
||||||
- Yoga (1.14.0)
|
- Yoga (1.14.0)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
@@ -1479,12 +1481,12 @@ SPEC CHECKSUMS:
|
|||||||
React-jsinspector: 6fad0fe14882fb6b1c32e5cc8a4bd3d33a8b6790
|
React-jsinspector: 6fad0fe14882fb6b1c32e5cc8a4bd3d33a8b6790
|
||||||
React-logger: cb0dd15ac67b00e7b771ef15203edcb29d4a3f8e
|
React-logger: cb0dd15ac67b00e7b771ef15203edcb29d4a3f8e
|
||||||
React-Mapbuffer: d59258be3b0d2280c6ba8964ab6e36ec69211871
|
React-Mapbuffer: d59258be3b0d2280c6ba8964ab6e36ec69211871
|
||||||
react-native-blur: 50c9feabacbc5f49b61337ebc32192c6be7ec3c3
|
react-native-blur: 27113acc008facbc8accae5fb3a78b8424f64cfd
|
||||||
react-native-netinfo: 8a7fd3f7130ef4ad2fb4276d5c9f8d3f28d2df3d
|
react-native-netinfo: 8a7fd3f7130ef4ad2fb4276d5c9f8d3f28d2df3d
|
||||||
react-native-safe-area-context: 0ee144a6170530ccc37a0fd9388e28d06f516a89
|
react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b
|
||||||
react-native-skia: c7c1d05ed3dd10f1bd37c4d37fcfce45aba9644b
|
react-native-skia: 88bc2430278842f46935d77888059bc1a2f564b6
|
||||||
react-native-track-player: 0c26d981b5097910486cbbeb6d8f5352d41be069
|
react-native-track-player: 97d76dbbd35f27cc709e5f04540615e54264b3f9
|
||||||
react-native-webview: f95eb7d4d6a7ca45d04d861d99f628241b2aff83
|
react-native-webview: d35380fcc2e1385cebc5b90fb96eebcdd1f2548e
|
||||||
React-nativeconfig: 4d3076dc3dc498ec49819e4e4225b55d3507f902
|
React-nativeconfig: 4d3076dc3dc498ec49819e4e4225b55d3507f902
|
||||||
React-NativeModulesApple: 46f14baf36010b22ffd84fd89d5586e4148edfb3
|
React-NativeModulesApple: 46f14baf36010b22ffd84fd89d5586e4148edfb3
|
||||||
React-perflogger: 27ccacf853ba725524ef2b4e444f14e34d0837b0
|
React-perflogger: 27ccacf853ba725524ef2b4e444f14e34d0837b0
|
||||||
@@ -1509,18 +1511,18 @@ SPEC CHECKSUMS:
|
|||||||
RNDateTimePicker: fc2e4f2795877d45e84d85659bebe627eba5c931
|
RNDateTimePicker: fc2e4f2795877d45e84d85659bebe627eba5c931
|
||||||
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
|
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
|
||||||
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
|
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
|
||||||
RNGestureHandler: 25b969a1ffc806b9f9ad2e170d4a3b049c6af85e
|
RNGestureHandler: deda62b8339496ba721a45e0f3e2d7a319932cee
|
||||||
RNLocalize: 0df7970cfc60389f00eb62fd7c097dc75af3fb4f
|
RNLocalize: 4222a3756cdbe2dc9a5bdf445765a4d2572107cb
|
||||||
RNReanimated: 5589be82dc26b3f94738eb7c6b1f942787532b25
|
RNReanimated: 7d6d32f238f914f13d9d6fb45c0aef557f7f901e
|
||||||
RNScreens: b582cb834dc4133307562e930e8fa914b8c04ef2
|
RNScreens: b582cb834dc4133307562e930e8fa914b8c04ef2
|
||||||
RNSentry: ed42b1443bef1fec1fc9273b5193a79b0cab1a01
|
RNSentry: eefa12fa65b37dd71f54371720f9fcddcd17be2f
|
||||||
RNSVG: ba3e7232f45e34b7b47e74472386cf4e1a676d0a
|
RNSVG: ba3e7232f45e34b7b47e74472386cf4e1a676d0a
|
||||||
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
|
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
|
||||||
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
|
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
|
||||||
Sentry: d9f99f9cc13777c5d938650c1e1c85047bb4f0d1
|
Sentry: d9f99f9cc13777c5d938650c1e1c85047bb4f0d1
|
||||||
SentryPrivate: 839b1e58addf58624087a80b2628e543193fa8ef
|
SentryPrivate: 839b1e58addf58624087a80b2628e543193fa8ef
|
||||||
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
|
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
|
||||||
SwiftAudioEx: 83eabba2940924fc1c0d5cb0896049921365229c
|
SwiftAudioEx: 6f511018b7a0fdfd14ed1bb4081f953588245cc0
|
||||||
Yoga: ff0382b894475dba0b4d2a5fda860bfee5a9afad
|
Yoga: ff0382b894475dba0b4d2a5fda860bfee5a9afad
|
||||||
|
|
||||||
PODFILE CHECKSUM: 0061941757351ef20fb9ad4bc98e51b1212f87d3
|
PODFILE CHECKSUM: 0061941757351ef20fb9ad4bc98e51b1212f87d3
|
||||||
|
|||||||
1685
package-lock.json
generated
1685
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
84
package.json
84
package.json
@@ -13,40 +13,40 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-native-async-storage/async-storage": "^1.21.0",
|
"@react-native-async-storage/async-storage": "^1.21.0",
|
||||||
"@react-native-community/blur": "^4.3.0",
|
"@react-native-community/blur": "^4.4.0",
|
||||||
"@react-native-community/datetimepicker": "^7.6.2",
|
"@react-native-community/datetimepicker": "^7.6.2",
|
||||||
"@react-native-community/netinfo": "^11.0.1",
|
"@react-native-community/netinfo": "^11.2.1",
|
||||||
"@react-navigation/bottom-tabs": "^6.4.0",
|
"@react-navigation/bottom-tabs": "^6.5.12",
|
||||||
"@react-navigation/elements": "^1.3.17",
|
"@react-navigation/elements": "^1.3.22",
|
||||||
"@react-navigation/native": "^6.0.8",
|
"@react-navigation/native": "^6.1.10",
|
||||||
"@react-navigation/native-stack": "^6.9.1",
|
"@react-navigation/native-stack": "^6.9.18",
|
||||||
"@react-navigation/stack": "^6.2.0",
|
"@react-navigation/stack": "^6.3.21",
|
||||||
"@reduxjs/toolkit": "^1.9.0",
|
"@reduxjs/toolkit": "^2.1.0",
|
||||||
"@shopify/react-native-skia": "^0.1.237",
|
"@shopify/react-native-skia": "^0.1.238",
|
||||||
"date-fns": "^2.29.3",
|
"date-fns": "^3.3.1",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^7.0.0",
|
||||||
"i18n-js": "^3.9.2",
|
"i18n-js": "^4.3.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"react": "18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-airplay": "^1.2.0",
|
"react-airplay": "^1.2.0",
|
||||||
"react-native": "^0.73.0",
|
"react-native": "^0.73.4",
|
||||||
"react-native-collapsible": "^1.6.0",
|
"react-native-collapsible": "^1.6.1",
|
||||||
"react-native-dotenv": "^3.4.2",
|
"react-native-dotenv": "^3.4.9",
|
||||||
"react-native-fast-image": "^8.6.3",
|
"react-native-fast-image": "^8.6.3",
|
||||||
"react-native-fs": "^2.18.0",
|
"react-native-fs": "^2.20.0",
|
||||||
"react-native-gesture-handler": "^2.14.0",
|
"react-native-gesture-handler": "^2.15.0",
|
||||||
"react-native-localize": "^2.2.4",
|
"react-native-localize": "^3.0.6",
|
||||||
"react-native-modal-datetime-picker": "^17.0.0",
|
"react-native-modal-datetime-picker": "^17.1.0",
|
||||||
"react-native-reanimated": "^3.6.0",
|
"react-native-reanimated": "^3.7.0",
|
||||||
"react-native-safe-area-context": "^4.8.2",
|
"react-native-safe-area-context": "^4.9.0",
|
||||||
"react-native-screens": "^3.21.0",
|
"react-native-screens": "^3.29.0",
|
||||||
"react-native-shadow-2": "^7.0.6",
|
"react-native-shadow-2": "^7.0.8",
|
||||||
"react-native-svg": "^14.0.0",
|
"react-native-svg": "^14.1.0",
|
||||||
"react-native-track-player": "^3.2.0",
|
"react-native-track-player": "^4.0.1",
|
||||||
"react-native-webview": "^13.6.1",
|
"react-native-webview": "^13.7.1",
|
||||||
"react-redux": "^8.0.5",
|
"react-redux": "^9.1.0",
|
||||||
"redux": "^4.2.0",
|
"redux": "^5.0.1",
|
||||||
"redux-flipper": "^2.0.2",
|
"redux-flipper": "^2.0.2",
|
||||||
"redux-logger": "^3.0.6",
|
"redux-logger": "^3.0.6",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
@@ -55,23 +55,23 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.2",
|
"@babel/core": "^7.20.2",
|
||||||
"@babel/runtime": "^7.20.1",
|
"@babel/runtime": "^7.20.1",
|
||||||
"@react-native/metro-config": "^0.73.4",
|
"@react-native/babel-preset": "^0.73.21",
|
||||||
|
"@react-native/metro-config": "^0.73.5",
|
||||||
"@react-native/typescript-config": "^0.74.0",
|
"@react-native/typescript-config": "^0.74.0",
|
||||||
"@sentry/cli": "^2.27.0",
|
"@sentry/cli": "^2.28.0",
|
||||||
"@sentry/react-native": "^5.17.0",
|
"@sentry/react-native": "^5.18.0",
|
||||||
"@types/i18n-js": "^3.8.3",
|
"@types/i18n-js": "^3.8.9",
|
||||||
"@types/lodash": "^4.14.202",
|
"@types/lodash": "^4.14.202",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.11.17",
|
||||||
"@types/redux-logger": "^3.0.9",
|
"@types/react": "^18.2.55",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
"@types/redux-logger": "^3.0.13",
|
||||||
"@typescript-eslint/parser": "^6.19.1",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"babel-plugin-module-resolver": "^4.1.0",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
"eslint": "^8.27.0",
|
"babel-plugin-module-resolver": "^5.0.0",
|
||||||
|
"eslint": "^8.56.0",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"metro-config": "^0.80.0",
|
"metro-react-native-babel-transformer": "^0.77.0",
|
||||||
"metro-react-native-babel-preset": "^0.73.10",
|
|
||||||
"metro-react-native-babel-transformer": "^0.73.3",
|
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"react-native-flipper": "^0.212.0",
|
"react-native-flipper": "^0.212.0",
|
||||||
"react-native-svg-transformer": "^1.3.0",
|
"react-native-svg-transformer": "^1.3.0",
|
||||||
|
|||||||
@@ -5,13 +5,12 @@ import CloudDownArrow from '@/assets/icons/cloud-down-arrow.svg';
|
|||||||
import CloudExclamationMarkIcon from '@/assets/icons/cloud-exclamation-mark.svg';
|
import CloudExclamationMarkIcon from '@/assets/icons/cloud-exclamation-mark.svg';
|
||||||
import InternalDriveIcon from '@/assets/icons/internal-drive.svg';
|
import InternalDriveIcon from '@/assets/icons/internal-drive.svg';
|
||||||
import useDefaultStyles from './Colors';
|
import useDefaultStyles from './Colors';
|
||||||
import { EntityId } from '@reduxjs/toolkit';
|
|
||||||
import Svg, { Circle, CircleProps } from 'react-native-svg';
|
import Svg, { Circle, CircleProps } from 'react-native-svg';
|
||||||
import { Animated, Easing } from 'react-native';
|
import { Animated, Easing } from 'react-native';
|
||||||
import styled from 'styled-components/native';
|
import styled from 'styled-components/native';
|
||||||
|
|
||||||
interface DownloadIconProps {
|
interface DownloadIconProps {
|
||||||
trackId: EntityId;
|
trackId: string;
|
||||||
size?: number;
|
size?: number;
|
||||||
fill?: string;
|
fill?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { EntityId } from '@reduxjs/toolkit';
|
|
||||||
import { xor } from 'lodash';
|
import { xor } from 'lodash';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { DocumentDirectoryPath, readDir } from 'react-native-fs';
|
import { DocumentDirectoryPath, readDir } from 'react-native-fs';
|
||||||
@@ -24,7 +23,7 @@ function DownloadManager () {
|
|||||||
// Keep state for the currently active downloads (i.e. the downloads that
|
// Keep state for the currently active downloads (i.e. the downloads that
|
||||||
// have actually been pushed out to react-native-fs).
|
// have actually been pushed out to react-native-fs).
|
||||||
const [hasRehydratedOrphans, setHasRehydratedOrphans] = useState(false);
|
const [hasRehydratedOrphans, setHasRehydratedOrphans] = useState(false);
|
||||||
const activeDownloads = useRef(new Set<EntityId>());
|
const activeDownloads = useRef(new Set<string>());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// GUARD: Check if the queue is empty
|
// GUARD: Check if the queue is empty
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import i18n from 'i18n-js';
|
import { I18n } from 'i18n-js';
|
||||||
import { findBestAvailableLanguage } from 'react-native-localize';
|
import { findBestLanguageTag } from 'react-native-localize';
|
||||||
import { LocaleKeys } from './types';
|
import { LocaleKeys } from './types';
|
||||||
|
|
||||||
|
const i18n = new I18n();
|
||||||
|
|
||||||
// Lazy loaders for locale
|
// Lazy loaders for locale
|
||||||
const localeGetters: Record<string, () => object> = {
|
const localeGetters: Record<string, () => object> = {
|
||||||
de: () => require('./lang/de/locale.json'),
|
de: () => require('./lang/de/locale.json'),
|
||||||
@@ -20,7 +22,7 @@ const localeGetters: Record<string, () => object> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Have RNLocalize pick the best locale from the languages on offer
|
// Have RNLocalize pick the best locale from the languages on offer
|
||||||
let locale = findBestAvailableLanguage(Object.keys(localeGetters));
|
let locale = findBestLanguageTag(Object.keys(localeGetters));
|
||||||
|
|
||||||
// Check if the locale is correctly picked
|
// Check if the locale is correctly picked
|
||||||
if (!locale || !locale.languageTag) {
|
if (!locale || !locale.languageTag) {
|
||||||
@@ -46,7 +48,7 @@ if (locale.languageTag !== 'en') {
|
|||||||
i18n.locale = locale.languageTag;
|
i18n.locale = locale.languageTag;
|
||||||
|
|
||||||
// Fallback to the default language for missing translation strings
|
// Fallback to the default language for missing translation strings
|
||||||
i18n.fallbacks = true;
|
i18n.enableFallback = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An i18n Typescript helper with autocomplete for the key argument
|
* An i18n Typescript helper with autocomplete for the key argument
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export type LocaleKeys = 'play-next'
|
|||||||
| 'delete-album'
|
| 'delete-album'
|
||||||
| 'delete-playlist'
|
| 'delete-playlist'
|
||||||
| 'delete-track'
|
| 'delete-track'
|
||||||
|
| 'delete-all-tracks'
|
||||||
| 'total-download-size'
|
| 'total-download-size'
|
||||||
| 'no-downloads'
|
| 'no-downloads'
|
||||||
| 'retry-failed-downloads'
|
| 'retry-failed-downloads'
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ import { useAppDispatch, useTypedSelector } from '@/store';
|
|||||||
import formatBytes from '@/utility/formatBytes';
|
import formatBytes from '@/utility/formatBytes';
|
||||||
import TrashIcon from '@/assets/icons/trash.svg';
|
import TrashIcon from '@/assets/icons/trash.svg';
|
||||||
import ArrowClockwise from '@/assets/icons/arrow-clockwise.svg';
|
import ArrowClockwise from '@/assets/icons/arrow-clockwise.svg';
|
||||||
import { EntityId } from '@reduxjs/toolkit';
|
|
||||||
import { queueTrackForDownload, removeDownloadedTrack } from '@/store/downloads/actions';
|
import { queueTrackForDownload, removeDownloadedTrack } from '@/store/downloads/actions';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import { t } from 'i18n-js';
|
|
||||||
import DownloadIcon from '@/components/DownloadIcon';
|
import DownloadIcon from '@/components/DownloadIcon';
|
||||||
import styled from 'styled-components/native';
|
import styled from 'styled-components/native';
|
||||||
import { Text } from '@/components/Typography';
|
import { Text } from '@/components/Typography';
|
||||||
@@ -18,6 +16,7 @@ import { useGetImage } from '@/utility/JellyfinApi';
|
|||||||
import { ShadowWrapper } from '@/components/Shadow';
|
import { ShadowWrapper } from '@/components/Shadow';
|
||||||
import { SafeFlatList } from '@/components/SafeNavigatorView';
|
import { SafeFlatList } from '@/components/SafeNavigatorView';
|
||||||
import { THEME_COLOR } from '@/CONSTANTS';
|
import { THEME_COLOR } from '@/CONSTANTS';
|
||||||
|
import { t } from '@/localisation';
|
||||||
|
|
||||||
const DownloadedTrack = styled.View`
|
const DownloadedTrack = styled.View`
|
||||||
flex: 1 0 auto;
|
flex: 1 0 auto;
|
||||||
@@ -55,7 +54,7 @@ function Downloads() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Delete a single downloaded track
|
// Delete a single downloaded track
|
||||||
const handleDelete = useCallback((id: EntityId) => {
|
const handleDelete = useCallback((id: string) => {
|
||||||
dispatch(removeDownloadedTrack(id));
|
dispatch(removeDownloadedTrack(id));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
@@ -63,7 +62,7 @@ function Downloads() {
|
|||||||
const handleDeleteAllTracks = useCallback(() => ids.forEach(handleDelete), [handleDelete, ids]);
|
const handleDeleteAllTracks = useCallback(() => ids.forEach(handleDelete), [handleDelete, ids]);
|
||||||
|
|
||||||
// Retry a single failed track
|
// Retry a single failed track
|
||||||
const retryTrack = useCallback((id: EntityId) => {
|
const retryTrack = useCallback((id: string) => {
|
||||||
dispatch(queueTrackForDownload(id));
|
dispatch(queueTrackForDownload(id));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
@@ -110,7 +109,7 @@ function Downloads() {
|
|||||||
</View>
|
</View>
|
||||||
), [totalDownloadSize, defaultStyles, failedIds.length, handleRetryFailed, handleDeleteAllTracks, ids.length]);
|
), [totalDownloadSize, defaultStyles, failedIds.length, handleRetryFailed, handleDeleteAllTracks, ids.length]);
|
||||||
|
|
||||||
const renderItem = useCallback<NonNullable<FlatListProps<EntityId>['renderItem']>>(({ item }) => (
|
const renderItem = useCallback<NonNullable<FlatListProps<string>['renderItem']>>(({ item }) => (
|
||||||
<>
|
<>
|
||||||
<DownloadedTrack>
|
<DownloadedTrack>
|
||||||
<View style={{ marginRight: 12 }}>
|
<View style={{ marginRight: 12 }}>
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import TouchableHandler from '@/components/TouchableHandler';
|
|||||||
import AlbumImage, { AlbumHeight, AlbumItem } from './components/AlbumImage';
|
import AlbumImage, { AlbumHeight, AlbumItem } from './components/AlbumImage';
|
||||||
import { selectAlbumsByAlphabet, SectionedId } from '@/store/music/selectors';
|
import { selectAlbumsByAlphabet, SectionedId } from '@/store/music/selectors';
|
||||||
import AlphabetScroller from '@/components/AlphabetScroller';
|
import AlphabetScroller from '@/components/AlphabetScroller';
|
||||||
import { EntityId } from '@reduxjs/toolkit';
|
|
||||||
import styled from 'styled-components/native';
|
import styled from 'styled-components/native';
|
||||||
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
|
||||||
import { Album } from '@/store/music/types';
|
import { Album } from '@/store/music/types';
|
||||||
@@ -90,7 +89,7 @@ const Albums: React.FC = () => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const navigation = useNavigation<NavigationProp>();
|
const navigation = useNavigation<NavigationProp>();
|
||||||
const getImage = useGetImage();
|
const getImage = useGetImage();
|
||||||
const listRef = useRef<SectionList<EntityId[]>>(null);
|
const listRef = useRef<SectionList<string[]>>(null);
|
||||||
|
|
||||||
// Create an array that computes all the height data for the entire list in
|
// Create an array that computes all the height data for the entire list in
|
||||||
// advance. We can then use this pre-computed data to respond to
|
// advance. We can then use this pre-computed data to respond to
|
||||||
@@ -143,7 +142,7 @@ const Albums: React.FC = () => {
|
|||||||
const selectLetter = useCallback((sectionIndex: number) => {
|
const selectLetter = useCallback((sectionIndex: number) => {
|
||||||
listRef.current?.scrollToLocation({ sectionIndex, itemIndex: 0, animated: false, });
|
listRef.current?.scrollToLocation({ sectionIndex, itemIndex: 0, animated: false, });
|
||||||
}, [listRef]);
|
}, [listRef]);
|
||||||
const generateItem = useCallback(({ item }: { item: EntityId[] }) => {
|
const generateItem = useCallback(({ item }: { item: string[] }) => {
|
||||||
return (
|
return (
|
||||||
<View style={{ flexDirection: 'row', marginLeft: 10, marginRight: 10 }} key={item.join('-')}>
|
<View style={{ flexDirection: 'row', marginLeft: 10, marginRight: 10 }} key={item.join('-')}>
|
||||||
{item.map((id) => (
|
{item.map((id) => (
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { fetchAllAlbums } from '@/store/music/actions';
|
|||||||
import { ALBUM_CACHE_AMOUNT_OF_DAYS } from '@/CONSTANTS';
|
import { ALBUM_CACHE_AMOUNT_OF_DAYS } from '@/CONSTANTS';
|
||||||
import TouchableHandler from '@/components/TouchableHandler';
|
import TouchableHandler from '@/components/TouchableHandler';
|
||||||
import AlbumImage, { AlbumItem } from './components/AlbumImage';
|
import AlbumImage, { AlbumItem } from './components/AlbumImage';
|
||||||
import { EntityId } from '@reduxjs/toolkit';
|
|
||||||
import styled from 'styled-components/native';
|
import styled from 'styled-components/native';
|
||||||
import useDefaultStyles from '@/components/Colors';
|
import useDefaultStyles from '@/components/Colors';
|
||||||
import { Album } from '@/store/music/types';
|
import { Album } from '@/store/music/types';
|
||||||
@@ -62,7 +61,7 @@ const Artist: React.FC = () => {
|
|||||||
// Set callbacks
|
// Set callbacks
|
||||||
const retrieveData = useCallback(() => dispatch(fetchAllAlbums()), [dispatch]);
|
const retrieveData = useCallback(() => dispatch(fetchAllAlbums()), [dispatch]);
|
||||||
const selectAlbum = useCallback((id: string) => navigation.navigate('Album', { id, album: albums[id] as Album }), [navigation, albums]);
|
const selectAlbum = useCallback((id: string) => navigation.navigate('Album', { id, album: albums[id] as Album }), [navigation, albums]);
|
||||||
const generateItem = useCallback(({ item }: { item: EntityId[] }) => {
|
const generateItem = useCallback(({ item }: { item: string[] }) => {
|
||||||
return (
|
return (
|
||||||
<View style={{ flexDirection: 'row', marginLeft: 10, marginRight: 10 }} key={item.join('-')}>
|
<View style={{ flexDirection: 'row', marginLeft: 10, marginRight: 10 }} key={item.join('-')}>
|
||||||
{item.map((id) => (
|
{item.map((id) => (
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { fetchAllPlaylists } from '@/store/music/actions';
|
|||||||
import { PLAYLIST_CACHE_AMOUNT_OF_DAYS } from '@/CONSTANTS';
|
import { PLAYLIST_CACHE_AMOUNT_OF_DAYS } from '@/CONSTANTS';
|
||||||
import TouchableHandler from '@/components/TouchableHandler';
|
import TouchableHandler from '@/components/TouchableHandler';
|
||||||
import AlbumImage, { AlbumItem } from './components/AlbumImage';
|
import AlbumImage, { AlbumItem } from './components/AlbumImage';
|
||||||
import { EntityId } from '@reduxjs/toolkit';
|
|
||||||
import useDefaultStyles from '@/components/Colors';
|
import useDefaultStyles from '@/components/Colors';
|
||||||
import { NavigationProp } from '@/screens/types';
|
import { NavigationProp } from '@/screens/types';
|
||||||
import { SafeFlatList, useNavigationOffsets } from '@/components/SafeNavigatorView';
|
import { SafeFlatList, useNavigationOffsets } from '@/components/SafeNavigatorView';
|
||||||
@@ -46,9 +45,9 @@ const Playlists: React.FC = () => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const navigation = useNavigation<NavigationProp>();
|
const navigation = useNavigation<NavigationProp>();
|
||||||
const getImage = useGetImage();
|
const getImage = useGetImage();
|
||||||
const listRef = useRef<FlatList<EntityId>>(null);
|
const listRef = useRef<FlatList<string>>(null);
|
||||||
|
|
||||||
const getItemLayout = useCallback((data: ArrayLike<EntityId> | null | undefined, index: number): { offset: number, length: number, index: number } => {
|
const getItemLayout = useCallback((data: ArrayLike<string> | null | undefined, index: number): { offset: number, length: number, index: number } => {
|
||||||
const length = 220;
|
const length = 220;
|
||||||
const offset = length * index;
|
const offset = length * index;
|
||||||
return { index, length, offset };
|
return { index, length, offset };
|
||||||
@@ -59,7 +58,7 @@ const Playlists: React.FC = () => {
|
|||||||
const selectAlbum = useCallback((id: string) => {
|
const selectAlbum = useCallback((id: string) => {
|
||||||
navigation.navigate('Playlist', { id });
|
navigation.navigate('Playlist', { id });
|
||||||
}, [navigation]);
|
}, [navigation]);
|
||||||
const generateItem: ListRenderItem<EntityId> = useCallback(({ item, index }) => {
|
const generateItem: ListRenderItem<string> = useCallback(({ item, index }) => {
|
||||||
if (index % 2 === 1) {
|
if (index % 2 === 1) {
|
||||||
return <View key={item} />;
|
return <View key={item} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import Play from '@/assets/icons/play.svg';
|
|||||||
import Shuffle from '@/assets/icons/shuffle.svg';
|
import Shuffle from '@/assets/icons/shuffle.svg';
|
||||||
import useDefaultStyles from '@/components/Colors';
|
import useDefaultStyles from '@/components/Colors';
|
||||||
import usePlayTracks from '@/utility/usePlayTracks';
|
import usePlayTracks from '@/utility/usePlayTracks';
|
||||||
import { EntityId } from '@reduxjs/toolkit';
|
|
||||||
import { WrappableButtonRow, WrappableButton } from '@/components/WrappableButtonRow';
|
import { WrappableButtonRow, WrappableButton } from '@/components/WrappableButtonRow';
|
||||||
import { NavigationProp } from '@/screens/types';
|
import { NavigationProp } from '@/screens/types';
|
||||||
import DownloadIcon from '@/components/DownloadIcon';
|
import DownloadIcon from '@/components/DownloadIcon';
|
||||||
@@ -63,7 +62,7 @@ const TrackContainer = styled.View<{ isPlaying: boolean, small?: boolean }>`
|
|||||||
export interface TrackListViewProps extends PropsWithChildren<{}> {
|
export interface TrackListViewProps extends PropsWithChildren<{}> {
|
||||||
title?: string;
|
title?: string;
|
||||||
artist?: string;
|
artist?: string;
|
||||||
trackIds: EntityId[];
|
trackIds: string[];
|
||||||
entityId: string;
|
entityId: string;
|
||||||
refresh: () => void;
|
refresh: () => void;
|
||||||
playButtonText: string;
|
playButtonText: string;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Input from '@/components/Input';
|
|||||||
import { ActivityIndicator, Animated, SafeAreaView, View } from 'react-native';
|
import { ActivityIndicator, Animated, SafeAreaView, View } from 'react-native';
|
||||||
import styled from 'styled-components/native';
|
import styled from 'styled-components/native';
|
||||||
import { useAppDispatch, useTypedSelector } from '@/store';
|
import { useAppDispatch, useTypedSelector } from '@/store';
|
||||||
import Fuse from 'fuse.js';
|
import Fuse, { IFuseOptions } from 'fuse.js';
|
||||||
import { Album, AlbumTrack } from '@/store/music/types';
|
import { Album, AlbumTrack } from '@/store/music/types';
|
||||||
import { FlatList } from 'react-native-gesture-handler';
|
import { FlatList } from 'react-native-gesture-handler';
|
||||||
import TouchableHandler from '@/components/TouchableHandler';
|
import TouchableHandler from '@/components/TouchableHandler';
|
||||||
@@ -73,7 +73,7 @@ const SearchResult = styled.View`
|
|||||||
height: 54px;
|
height: 54px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const fuseOptions: Fuse.IFuseOptions<Album> = {
|
const fuseOptions: IFuseOptions<Album> = {
|
||||||
keys: ['Name', 'AlbumArtist', 'AlbumArtists', 'Artists'],
|
keys: ['Name', 'AlbumArtist', 'AlbumArtists', 'Artists'],
|
||||||
threshold: 0.1,
|
threshold: 0.1,
|
||||||
includeScore: true,
|
includeScore: true,
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
import { createAction, createAsyncThunk, createEntityAdapter, EntityId } from '@reduxjs/toolkit';
|
import { createAction, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit';
|
||||||
import { AppState } from '@/store';
|
import { AppState } from '@/store';
|
||||||
import { generateTrackUrl } from '@/utility/JellyfinApi';
|
import { generateTrackUrl } from '@/utility/JellyfinApi';
|
||||||
import { downloadFile, unlink, DocumentDirectoryPath, exists } from 'react-native-fs';
|
import { downloadFile, unlink, DocumentDirectoryPath, exists } from 'react-native-fs';
|
||||||
import { DownloadEntity } from './types';
|
import { DownloadEntity } from './types';
|
||||||
import MimeTypes from '@/utility/MimeTypes';
|
import MimeTypes from '@/utility/MimeTypes';
|
||||||
|
|
||||||
export const downloadAdapter = createEntityAdapter<DownloadEntity>({
|
export const downloadAdapter = createEntityAdapter<DownloadEntity>();
|
||||||
selectId: (entity) => entity.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const queueTrackForDownload = createAction<EntityId>('download/queue');
|
export const queueTrackForDownload = createAction<string>('download/queue');
|
||||||
export const initializeDownload = createAction<{ id: EntityId, size?: number, jobId?: number, location: string }>('download/initialize');
|
export const initializeDownload = createAction<{ id: string, size?: number, jobId?: number, location: string }>('download/initialize');
|
||||||
export const progressDownload = createAction<{ id: EntityId, progress: number, jobId?: number }>('download/progress');
|
export const progressDownload = createAction<{ id: string, progress: number, jobId?: number }>('download/progress');
|
||||||
export const completeDownload = createAction<{ id: EntityId, location: string, size?: number }>('download/complete');
|
export const completeDownload = createAction<{ id: string, location: string, size?: number }>('download/complete');
|
||||||
export const failDownload = createAction<{ id: EntityId }>('download/fail');
|
export const failDownload = createAction<{ id: string }>('download/fail');
|
||||||
|
|
||||||
export const downloadTrack = createAsyncThunk(
|
export const downloadTrack = createAsyncThunk(
|
||||||
'/downloads/track',
|
'/downloads/track',
|
||||||
async (id: EntityId, { dispatch, getState }) => {
|
async (id: string, { dispatch, getState }) => {
|
||||||
// Get the credentials from the store
|
// Get the credentials from the store
|
||||||
const { settings: { jellyfin: credentials } } = (getState() as AppState);
|
const { settings: { jellyfin: credentials } } = (getState() as AppState);
|
||||||
|
|
||||||
@@ -63,7 +61,7 @@ export const downloadTrack = createAsyncThunk(
|
|||||||
|
|
||||||
export const removeDownloadedTrack = createAsyncThunk(
|
export const removeDownloadedTrack = createAsyncThunk(
|
||||||
'/downloads/remove/track',
|
'/downloads/remove/track',
|
||||||
async(id: EntityId, { getState }) => {
|
async(id: string, { getState }) => {
|
||||||
// Retrieve the state
|
// Retrieve the state
|
||||||
const { downloads: { entities }} = getState() as AppState;
|
const { downloads: { entities }} = getState() as AppState;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createSlice, Dictionary, EntityId } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import {
|
import {
|
||||||
completeDownload,
|
completeDownload,
|
||||||
downloadAdapter,
|
downloadAdapter,
|
||||||
@@ -12,9 +12,9 @@ import {
|
|||||||
import { DownloadEntity } from './types';
|
import { DownloadEntity } from './types';
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
entities: Dictionary<DownloadEntity>;
|
entities: Record<string, DownloadEntity>;
|
||||||
ids: EntityId[];
|
ids: string[];
|
||||||
queued: EntityId[];
|
queued: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialState: State = {
|
export const initialState: State = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createSelector, EntityId } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { intersection } from 'lodash';
|
import { intersection } from 'lodash';
|
||||||
import { AppState } from '@/store';
|
import { AppState } from '@/store';
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ export const selectDownloadedEntities = (state: AppState) => state.downloads.ent
|
|||||||
/**
|
/**
|
||||||
* Only retain the supplied trackIds that have successfully been downloaded
|
* Only retain the supplied trackIds that have successfully been downloaded
|
||||||
*/
|
*/
|
||||||
export const selectDownloadedTracks = (trackIds: EntityId[]) => (
|
export const selectDownloadedTracks = (trackIds: string[]) => (
|
||||||
createSelector(
|
createSelector(
|
||||||
selectAllDownloads,
|
selectAllDownloads,
|
||||||
({ entities, ids }) => {
|
({ entities, ids }) => {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { EntityId } from '@reduxjs/toolkit';
|
|
||||||
|
|
||||||
export interface DownloadEntity {
|
export interface DownloadEntity {
|
||||||
id: EntityId;
|
id: string;
|
||||||
progress: number;
|
progress: number;
|
||||||
isFailed: boolean;
|
isFailed: boolean;
|
||||||
isComplete: boolean;
|
isComplete: boolean;
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import { configureStore, getDefaultMiddleware, combineReducers } from '@reduxjs/toolkit';
|
import { configureStore, combineReducers } from '@reduxjs/toolkit';
|
||||||
import { useSelector, TypedUseSelectorHook, useDispatch } from 'react-redux';
|
import { useSelector, TypedUseSelectorHook, useDispatch } from 'react-redux';
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
import { persistStore, persistReducer, PersistConfig, createMigrate } from 'redux-persist';
|
import { persistStore, persistReducer, PersistConfig, createMigrate, PersistState } from 'redux-persist';
|
||||||
import autoMergeLevel2 from 'redux-persist/es/stateReconciler/autoMergeLevel2';
|
import autoMergeLevel2 from 'redux-persist/es/stateReconciler/autoMergeLevel2';
|
||||||
|
|
||||||
import settings from './settings';
|
import settings from './settings';
|
||||||
import music, { initialState as musicInitialState } from './music';
|
import music, { initialState as musicInitialState } from './music';
|
||||||
import downloads, { initialState as downloadsInitialState } from './downloads';
|
import downloads, { initialState as downloadsInitialState } from './downloads';
|
||||||
import { PersistState } from 'redux-persist/es/types';
|
|
||||||
import { ColorScheme } from './settings/types';
|
|
||||||
import sleepTimer from './sleep-timer';
|
import sleepTimer from './sleep-timer';
|
||||||
|
import { ColorScheme } from './settings/types';
|
||||||
|
|
||||||
const persistConfig: PersistConfig<Omit<AppState, '_persist'>> = {
|
const persistConfig: PersistConfig<Omit<AppState, '_persist'>> = {
|
||||||
key: 'root',
|
key: 'root',
|
||||||
@@ -77,16 +76,11 @@ const reducers = combineReducers({
|
|||||||
|
|
||||||
const persistedReducer = persistReducer(persistConfig, reducers);
|
const persistedReducer = persistReducer(persistConfig, reducers);
|
||||||
|
|
||||||
const middlewares = [];
|
|
||||||
if (__DEV__) {
|
|
||||||
middlewares.push(require('redux-flipper').default());
|
|
||||||
}
|
|
||||||
|
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
reducer: persistedReducer,
|
reducer: persistedReducer,
|
||||||
middleware: getDefaultMiddleware({ serializableCheck: false, immutableCheck: false }).concat(
|
middleware: (getDefaultMiddleware) => (
|
||||||
// logger,
|
getDefaultMiddleware({ serializableCheck: false, immutableCheck: false })
|
||||||
...middlewares,
|
.concat(__DEV__ ? [require('redux-flipper').default()] : [])
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -94,7 +88,7 @@ export type AppState = ReturnType<typeof reducers> & { _persist: PersistState };
|
|||||||
export type AppDispatch = typeof store.dispatch;
|
export type AppDispatch = typeof store.dispatch;
|
||||||
export type AsyncThunkAPI = { state: AppState, dispatch: AppDispatch };
|
export type AsyncThunkAPI = { state: AppState, dispatch: AppDispatch };
|
||||||
export const useTypedSelector: TypedUseSelectorHook<AppState> = useSelector;
|
export const useTypedSelector: TypedUseSelectorHook<AppState> = useSelector;
|
||||||
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
export const useAppDispatch: () => AppDispatch = useDispatch;
|
||||||
|
|
||||||
export const persistedStore = persistStore(store);
|
export const persistedStore = persistStore(store);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Album, AlbumTrack, Playlist } from './types';
|
|||||||
import { AsyncThunkAPI } from '..';
|
import { AsyncThunkAPI } from '..';
|
||||||
import { retrieveAllAlbums, retrieveAlbumTracks, retrieveRecentAlbums, searchItem, retrieveAlbum, retrieveAllPlaylists, retrievePlaylistTracks } from '@/utility/JellyfinApi';
|
import { retrieveAllAlbums, retrieveAlbumTracks, retrieveRecentAlbums, searchItem, retrieveAlbum, retrieveAllPlaylists, retrievePlaylistTracks } from '@/utility/JellyfinApi';
|
||||||
|
|
||||||
export const albumAdapter = createEntityAdapter<Album>({
|
export const albumAdapter = createEntityAdapter<Album, string>({
|
||||||
selectId: album => album.Id,
|
selectId: album => album.Id,
|
||||||
sortComparer: (a, b) => a.Name.localeCompare(b.Name),
|
sortComparer: (a, b) => a.Name.localeCompare(b.Name),
|
||||||
});
|
});
|
||||||
@@ -30,7 +30,7 @@ export const fetchRecentAlbums = createAsyncThunk<Album[], number | undefined, A
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const trackAdapter = createEntityAdapter<AlbumTrack>({
|
export const trackAdapter = createEntityAdapter<AlbumTrack, string>({
|
||||||
selectId: track => track.Id,
|
selectId: track => track.Id,
|
||||||
sortComparer: (a, b) => a.IndexNumber - b.IndexNumber,
|
sortComparer: (a, b) => a.IndexNumber - b.IndexNumber,
|
||||||
});
|
});
|
||||||
@@ -86,7 +86,7 @@ AsyncThunkAPI
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const playlistAdapter = createEntityAdapter<Playlist>({
|
export const playlistAdapter = createEntityAdapter<Playlist, string>({
|
||||||
selectId: (playlist) => playlist.Id,
|
selectId: (playlist) => playlist.Id,
|
||||||
sortComparer: (a, b) => a.Name.localeCompare(b.Name),
|
sortComparer: (a, b) => a.Name.localeCompare(b.Name),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,28 +10,28 @@ import {
|
|||||||
fetchTracksByPlaylist,
|
fetchTracksByPlaylist,
|
||||||
fetchAlbum
|
fetchAlbum
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import { createSlice, Dictionary, EntityId } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import { Album, AlbumTrack, Playlist } from './types';
|
import { Album, AlbumTrack, Playlist } from './types';
|
||||||
import { setJellyfinCredentials } from '@/store/settings/actions';
|
import { setJellyfinCredentials } from '@/store/settings/actions';
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
albums: {
|
albums: {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
entities: Dictionary<Album>;
|
entities: Record<string, Album>;
|
||||||
ids: EntityId[];
|
ids: string[];
|
||||||
lastRefreshed?: number,
|
lastRefreshed?: number,
|
||||||
},
|
},
|
||||||
tracks: {
|
tracks: {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
entities: Dictionary<AlbumTrack>;
|
entities: Record<string, AlbumTrack>;
|
||||||
ids: EntityId[];
|
ids: string[];
|
||||||
byAlbum: Dictionary<EntityId[]>;
|
byAlbum: Record<string, string[]>;
|
||||||
byPlaylist: Dictionary<EntityId[]>;
|
byPlaylist: Record<string, string[]>;
|
||||||
},
|
},
|
||||||
playlists: {
|
playlists: {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
entities: Dictionary<Playlist>;
|
entities: Record<string, Playlist>;
|
||||||
ids: EntityId[];
|
ids: string[];
|
||||||
lastRefreshed?: number,
|
lastRefreshed?: number,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useTypedSelector, AppState } from '@/store';
|
import { useTypedSelector, AppState } from '@/store';
|
||||||
import { parseISO } from 'date-fns';
|
import { parseISO } from 'date-fns';
|
||||||
import { ALPHABET_LETTERS } from '@/CONSTANTS';
|
import { ALPHABET_LETTERS } from '@/CONSTANTS';
|
||||||
import { createSelector, EntityId } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { SectionListData } from 'react-native';
|
import { SectionListData } from 'react-native';
|
||||||
import { ArtistItem } from './types';
|
import { ArtistItem } from './types';
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ export const selectAlbumsByArtist = createSelector(
|
|||||||
albumsByArtist,
|
albumsByArtist,
|
||||||
);
|
);
|
||||||
|
|
||||||
export type SectionedId = SectionListData<EntityId[]>;
|
export type SectionedId = SectionListData<string[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits a set of albums into a list that is split by alphabet letters
|
* Splits a set of albums into a list that is split by alphabet letters
|
||||||
@@ -77,7 +77,7 @@ function splitAlbumsByAlphabet(state: AppState['music']['albums']): SectionedId[
|
|||||||
|
|
||||||
// GUARD: Check if the row is overflowing. If so, add a new row.
|
// GUARD: Check if the row is overflowing. If so, add a new row.
|
||||||
if (section.data[row].length >= 2) {
|
if (section.data[row].length >= 2) {
|
||||||
(section.data as EntityId[][]).push([]);
|
(section.data as string[][]).push([]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ export const selectAlbumsByAlphabet = createSelector(
|
|||||||
splitAlbumsByAlphabet,
|
splitAlbumsByAlphabet,
|
||||||
);
|
);
|
||||||
|
|
||||||
export type SectionArtistItem = ArtistItem & { albumIds: EntityId[] };
|
export type SectionArtistItem = ArtistItem & { albumIds: string[] };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve all artists based on the available albums
|
* Retrieve all artists based on the available albums
|
||||||
@@ -107,7 +107,7 @@ export function artistsFromAlbums(state: AppState['music']['albums']) {
|
|||||||
album?.ArtistItems.forEach((artist) => {
|
album?.ArtistItems.forEach((artist) => {
|
||||||
// GUARD: Check that an array already exists for this artist
|
// GUARD: Check that an array already exists for this artist
|
||||||
if (!(artist.Name in sum)) {
|
if (!(artist.Name in sum)) {
|
||||||
sum[artist.Name] = { albumIds: [] as EntityId[], ...artist };
|
sum[artist.Name] = { albumIds: [] as string[], ...artist };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the album id to the artist in the object
|
// Add the album id to the artist in the object
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { Dictionary } from '@reduxjs/toolkit';
|
|
||||||
|
|
||||||
export interface UserData {
|
export interface UserData {
|
||||||
PlaybackPositionTicks: number;
|
PlaybackPositionTicks: number;
|
||||||
PlayCount: number;
|
PlayCount: number;
|
||||||
@@ -73,7 +71,7 @@ export interface AlbumTrack {
|
|||||||
export interface State {
|
export interface State {
|
||||||
albums: {
|
albums: {
|
||||||
ids: string[];
|
ids: string[];
|
||||||
entities: Dictionary<Album>;
|
entities: Record<string, Album>;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { useTypedSelector } from '@/store';
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import TrackPlayer, { Track } from 'react-native-track-player';
|
import TrackPlayer, { Track } from 'react-native-track-player';
|
||||||
import { generateTrack } from './JellyfinApi';
|
import { generateTrack } from './JellyfinApi';
|
||||||
import { EntityId } from '@reduxjs/toolkit';
|
|
||||||
import { shuffle as shuffleArray } from 'lodash';
|
import { shuffle as shuffleArray } from 'lodash';
|
||||||
|
|
||||||
interface PlayOptions {
|
interface PlayOptions {
|
||||||
@@ -27,7 +26,7 @@ export default function usePlayTracks() {
|
|||||||
const downloads = useTypedSelector(state => state.downloads.entities);
|
const downloads = useTypedSelector(state => state.downloads.entities);
|
||||||
|
|
||||||
return useCallback(async function playTracks(
|
return useCallback(async function playTracks(
|
||||||
trackIds: EntityId[] | undefined,
|
trackIds: string[] | undefined,
|
||||||
options: Partial<PlayOptions> = {},
|
options: Partial<PlayOptions> = {},
|
||||||
): Promise<Track[] | undefined> {
|
): Promise<Track[] | undefined> {
|
||||||
if (!trackIds) {
|
if (!trackIds) {
|
||||||
|
|||||||
Reference in New Issue
Block a user