chore: upgrade all dependencies

This commit is contained in:
Lei Nelissen
2024-02-08 22:11:43 +01:00
parent 0489e1a86d
commit fb4af1b7c6
23 changed files with 797 additions and 1161 deletions

View File

@@ -5,13 +5,12 @@ import CloudDownArrow from '@/assets/icons/cloud-down-arrow.svg';
import CloudExclamationMarkIcon from '@/assets/icons/cloud-exclamation-mark.svg';
import InternalDriveIcon from '@/assets/icons/internal-drive.svg';
import useDefaultStyles from './Colors';
import { EntityId } from '@reduxjs/toolkit';
import Svg, { Circle, CircleProps } from 'react-native-svg';
import { Animated, Easing } from 'react-native';
import styled from 'styled-components/native';
interface DownloadIconProps {
trackId: EntityId;
trackId: string;
size?: number;
fill?: string;
}

View File

@@ -1,4 +1,3 @@
import { EntityId } from '@reduxjs/toolkit';
import { xor } from 'lodash';
import { useEffect, useRef, useState } from 'react';
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
// have actually been pushed out to react-native-fs).
const [hasRehydratedOrphans, setHasRehydratedOrphans] = useState(false);
const activeDownloads = useRef(new Set<EntityId>());
const activeDownloads = useRef(new Set<string>());
useEffect(() => {
// GUARD: Check if the queue is empty

View File

@@ -1,7 +1,9 @@
import i18n from 'i18n-js';
import { findBestAvailableLanguage } from 'react-native-localize';
import { I18n } from 'i18n-js';
import { findBestLanguageTag } from 'react-native-localize';
import { LocaleKeys } from './types';
const i18n = new I18n();
// Lazy loaders for locale
const localeGetters: Record<string, () => object> = {
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
let locale = findBestAvailableLanguage(Object.keys(localeGetters));
let locale = findBestLanguageTag(Object.keys(localeGetters));
// Check if the locale is correctly picked
if (!locale || !locale.languageTag) {
@@ -46,7 +48,7 @@ if (locale.languageTag !== 'en') {
i18n.locale = locale.languageTag;
// 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

View File

@@ -51,6 +51,7 @@ export type LocaleKeys = 'play-next'
| 'delete-album'
| 'delete-playlist'
| 'delete-track'
| 'delete-all-tracks'
| 'total-download-size'
| 'no-downloads'
| 'retry-failed-downloads'

View File

@@ -6,10 +6,8 @@ import { useAppDispatch, useTypedSelector } from '@/store';
import formatBytes from '@/utility/formatBytes';
import TrashIcon from '@/assets/icons/trash.svg';
import ArrowClockwise from '@/assets/icons/arrow-clockwise.svg';
import { EntityId } from '@reduxjs/toolkit';
import { queueTrackForDownload, removeDownloadedTrack } from '@/store/downloads/actions';
import Button from '@/components/Button';
import { t } from 'i18n-js';
import DownloadIcon from '@/components/DownloadIcon';
import styled from 'styled-components/native';
import { Text } from '@/components/Typography';
@@ -18,6 +16,7 @@ import { useGetImage } from '@/utility/JellyfinApi';
import { ShadowWrapper } from '@/components/Shadow';
import { SafeFlatList } from '@/components/SafeNavigatorView';
import { THEME_COLOR } from '@/CONSTANTS';
import { t } from '@/localisation';
const DownloadedTrack = styled.View`
flex: 1 0 auto;
@@ -55,7 +54,7 @@ function Downloads() {
*/
// Delete a single downloaded track
const handleDelete = useCallback((id: EntityId) => {
const handleDelete = useCallback((id: string) => {
dispatch(removeDownloadedTrack(id));
}, [dispatch]);
@@ -63,7 +62,7 @@ function Downloads() {
const handleDeleteAllTracks = useCallback(() => ids.forEach(handleDelete), [handleDelete, ids]);
// Retry a single failed track
const retryTrack = useCallback((id: EntityId) => {
const retryTrack = useCallback((id: string) => {
dispatch(queueTrackForDownload(id));
}, [dispatch]);
@@ -110,7 +109,7 @@ function Downloads() {
</View>
), [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>
<View style={{ marginRight: 12 }}>

View File

@@ -10,7 +10,6 @@ import TouchableHandler from '@/components/TouchableHandler';
import AlbumImage, { AlbumHeight, AlbumItem } from './components/AlbumImage';
import { selectAlbumsByAlphabet, SectionedId } from '@/store/music/selectors';
import AlphabetScroller from '@/components/AlphabetScroller';
import { EntityId } from '@reduxjs/toolkit';
import styled from 'styled-components/native';
import useDefaultStyles, { ColoredBlurView } from '@/components/Colors';
import { Album } from '@/store/music/types';
@@ -90,7 +89,7 @@ const Albums: React.FC = () => {
const dispatch = useAppDispatch();
const navigation = useNavigation<NavigationProp>();
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
// 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) => {
listRef.current?.scrollToLocation({ sectionIndex, itemIndex: 0, animated: false, });
}, [listRef]);
const generateItem = useCallback(({ item }: { item: EntityId[] }) => {
const generateItem = useCallback(({ item }: { item: string[] }) => {
return (
<View style={{ flexDirection: 'row', marginLeft: 10, marginRight: 10 }} key={item.join('-')}>
{item.map((id) => (

View File

@@ -8,7 +8,6 @@ import { fetchAllAlbums } from '@/store/music/actions';
import { ALBUM_CACHE_AMOUNT_OF_DAYS } from '@/CONSTANTS';
import TouchableHandler from '@/components/TouchableHandler';
import AlbumImage, { AlbumItem } from './components/AlbumImage';
import { EntityId } from '@reduxjs/toolkit';
import styled from 'styled-components/native';
import useDefaultStyles from '@/components/Colors';
import { Album } from '@/store/music/types';
@@ -62,7 +61,7 @@ const Artist: React.FC = () => {
// Set callbacks
const retrieveData = useCallback(() => dispatch(fetchAllAlbums()), [dispatch]);
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 (
<View style={{ flexDirection: 'row', marginLeft: 10, marginRight: 10 }} key={item.join('-')}>
{item.map((id) => (

View File

@@ -8,7 +8,6 @@ import { fetchAllPlaylists } from '@/store/music/actions';
import { PLAYLIST_CACHE_AMOUNT_OF_DAYS } from '@/CONSTANTS';
import TouchableHandler from '@/components/TouchableHandler';
import AlbumImage, { AlbumItem } from './components/AlbumImage';
import { EntityId } from '@reduxjs/toolkit';
import useDefaultStyles from '@/components/Colors';
import { NavigationProp } from '@/screens/types';
import { SafeFlatList, useNavigationOffsets } from '@/components/SafeNavigatorView';
@@ -46,9 +45,9 @@ const Playlists: React.FC = () => {
const dispatch = useAppDispatch();
const navigation = useNavigation<NavigationProp>();
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 offset = length * index;
return { index, length, offset };
@@ -59,7 +58,7 @@ const Playlists: React.FC = () => {
const selectAlbum = useCallback((id: string) => {
navigation.navigate('Playlist', { id });
}, [navigation]);
const generateItem: ListRenderItem<EntityId> = useCallback(({ item, index }) => {
const generateItem: ListRenderItem<string> = useCallback(({ item, index }) => {
if (index % 2 === 1) {
return <View key={item} />;
}

View File

@@ -12,7 +12,6 @@ import Play from '@/assets/icons/play.svg';
import Shuffle from '@/assets/icons/shuffle.svg';
import useDefaultStyles from '@/components/Colors';
import usePlayTracks from '@/utility/usePlayTracks';
import { EntityId } from '@reduxjs/toolkit';
import { WrappableButtonRow, WrappableButton } from '@/components/WrappableButtonRow';
import { NavigationProp } from '@/screens/types';
import DownloadIcon from '@/components/DownloadIcon';
@@ -63,7 +62,7 @@ const TrackContainer = styled.View<{ isPlaying: boolean, small?: boolean }>`
export interface TrackListViewProps extends PropsWithChildren<{}> {
title?: string;
artist?: string;
trackIds: EntityId[];
trackIds: string[];
entityId: string;
refresh: () => void;
playButtonText: string;

View File

@@ -3,7 +3,7 @@ import Input from '@/components/Input';
import { ActivityIndicator, Animated, SafeAreaView, View } from 'react-native';
import styled from 'styled-components/native';
import { useAppDispatch, useTypedSelector } from '@/store';
import Fuse from 'fuse.js';
import Fuse, { IFuseOptions } from 'fuse.js';
import { Album, AlbumTrack } from '@/store/music/types';
import { FlatList } from 'react-native-gesture-handler';
import TouchableHandler from '@/components/TouchableHandler';
@@ -73,7 +73,7 @@ const SearchResult = styled.View`
height: 54px;
`;
const fuseOptions: Fuse.IFuseOptions<Album> = {
const fuseOptions: IFuseOptions<Album> = {
keys: ['Name', 'AlbumArtist', 'AlbumArtists', 'Artists'],
threshold: 0.1,
includeScore: true,

View File

@@ -1,23 +1,21 @@
import { createAction, createAsyncThunk, createEntityAdapter, EntityId } from '@reduxjs/toolkit';
import { createAction, createAsyncThunk, createEntityAdapter } from '@reduxjs/toolkit';
import { AppState } from '@/store';
import { generateTrackUrl } from '@/utility/JellyfinApi';
import { downloadFile, unlink, DocumentDirectoryPath, exists } from 'react-native-fs';
import { DownloadEntity } from './types';
import MimeTypes from '@/utility/MimeTypes';
export const downloadAdapter = createEntityAdapter<DownloadEntity>({
selectId: (entity) => entity.id,
});
export const downloadAdapter = createEntityAdapter<DownloadEntity>();
export const queueTrackForDownload = createAction<EntityId>('download/queue');
export const initializeDownload = createAction<{ id: EntityId, size?: number, jobId?: number, location: string }>('download/initialize');
export const progressDownload = createAction<{ id: EntityId, progress: number, jobId?: number }>('download/progress');
export const completeDownload = createAction<{ id: EntityId, location: string, size?: number }>('download/complete');
export const failDownload = createAction<{ id: EntityId }>('download/fail');
export const queueTrackForDownload = createAction<string>('download/queue');
export const initializeDownload = createAction<{ id: string, size?: number, jobId?: number, location: string }>('download/initialize');
export const progressDownload = createAction<{ id: string, progress: number, jobId?: number }>('download/progress');
export const completeDownload = createAction<{ id: string, location: string, size?: number }>('download/complete');
export const failDownload = createAction<{ id: string }>('download/fail');
export const downloadTrack = createAsyncThunk(
'/downloads/track',
async (id: EntityId, { dispatch, getState }) => {
async (id: string, { dispatch, getState }) => {
// Get the credentials from the store
const { settings: { jellyfin: credentials } } = (getState() as AppState);
@@ -63,7 +61,7 @@ export const downloadTrack = createAsyncThunk(
export const removeDownloadedTrack = createAsyncThunk(
'/downloads/remove/track',
async(id: EntityId, { getState }) => {
async(id: string, { getState }) => {
// Retrieve the state
const { downloads: { entities }} = getState() as AppState;

View File

@@ -1,4 +1,4 @@
import { createSlice, Dictionary, EntityId } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit';
import {
completeDownload,
downloadAdapter,
@@ -12,9 +12,9 @@ import {
import { DownloadEntity } from './types';
interface State {
entities: Dictionary<DownloadEntity>;
ids: EntityId[];
queued: EntityId[];
entities: Record<string, DownloadEntity>;
ids: string[];
queued: string[];
}
export const initialState: State = {

View File

@@ -1,4 +1,4 @@
import { createSelector, EntityId } from '@reduxjs/toolkit';
import { createSelector } from '@reduxjs/toolkit';
import { intersection } from 'lodash';
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
*/
export const selectDownloadedTracks = (trackIds: EntityId[]) => (
export const selectDownloadedTracks = (trackIds: string[]) => (
createSelector(
selectAllDownloads,
({ entities, ids }) => {

View File

@@ -1,7 +1,5 @@
import { EntityId } from '@reduxjs/toolkit';
export interface DownloadEntity {
id: EntityId;
id: string;
progress: number;
isFailed: boolean;
isComplete: boolean;

View File

@@ -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 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 settings from './settings';
import music, { initialState as musicInitialState } from './music';
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 { ColorScheme } from './settings/types';
const persistConfig: PersistConfig<Omit<AppState, '_persist'>> = {
key: 'root',
@@ -77,16 +76,11 @@ const reducers = combineReducers({
const persistedReducer = persistReducer(persistConfig, reducers);
const middlewares = [];
if (__DEV__) {
middlewares.push(require('redux-flipper').default());
}
const store = configureStore({
reducer: persistedReducer,
middleware: getDefaultMiddleware({ serializableCheck: false, immutableCheck: false }).concat(
// logger,
...middlewares,
middleware: (getDefaultMiddleware) => (
getDefaultMiddleware({ serializableCheck: false, immutableCheck: false })
.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 AsyncThunkAPI = { state: AppState, dispatch: AppDispatch };
export const useTypedSelector: TypedUseSelectorHook<AppState> = useSelector;
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppDispatch: () => AppDispatch = useDispatch;
export const persistedStore = persistStore(store);

View File

@@ -3,7 +3,7 @@ import { Album, AlbumTrack, Playlist } from './types';
import { AsyncThunkAPI } from '..';
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,
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,
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,
sortComparer: (a, b) => a.Name.localeCompare(b.Name),
});

View File

@@ -10,28 +10,28 @@ import {
fetchTracksByPlaylist,
fetchAlbum
} from './actions';
import { createSlice, Dictionary, EntityId } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit';
import { Album, AlbumTrack, Playlist } from './types';
import { setJellyfinCredentials } from '@/store/settings/actions';
export interface State {
albums: {
isLoading: boolean;
entities: Dictionary<Album>;
ids: EntityId[];
entities: Record<string, Album>;
ids: string[];
lastRefreshed?: number,
},
tracks: {
isLoading: boolean;
entities: Dictionary<AlbumTrack>;
ids: EntityId[];
byAlbum: Dictionary<EntityId[]>;
byPlaylist: Dictionary<EntityId[]>;
entities: Record<string, AlbumTrack>;
ids: string[];
byAlbum: Record<string, string[]>;
byPlaylist: Record<string, string[]>;
},
playlists: {
isLoading: boolean;
entities: Dictionary<Playlist>;
ids: EntityId[];
entities: Record<string, Playlist>;
ids: string[];
lastRefreshed?: number,
}
}

View File

@@ -1,7 +1,7 @@
import { useTypedSelector, AppState } from '@/store';
import { parseISO } from 'date-fns';
import { ALPHABET_LETTERS } from '@/CONSTANTS';
import { createSelector, EntityId } from '@reduxjs/toolkit';
import { createSelector } from '@reduxjs/toolkit';
import { SectionListData } from 'react-native';
import { ArtistItem } from './types';
@@ -51,7 +51,7 @@ export const selectAlbumsByArtist = createSelector(
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
@@ -77,7 +77,7 @@ function splitAlbumsByAlphabet(state: AppState['music']['albums']): SectionedId[
// GUARD: Check if the row is overflowing. If so, add a new row.
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,
);
export type SectionArtistItem = ArtistItem & { albumIds: EntityId[] };
export type SectionArtistItem = ArtistItem & { albumIds: string[] };
/**
* Retrieve all artists based on the available albums
@@ -107,7 +107,7 @@ export function artistsFromAlbums(state: AppState['music']['albums']) {
album?.ArtistItems.forEach((artist) => {
// GUARD: Check that an array already exists for this artist
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

View File

@@ -1,5 +1,3 @@
import { Dictionary } from '@reduxjs/toolkit';
export interface UserData {
PlaybackPositionTicks: number;
PlayCount: number;
@@ -73,7 +71,7 @@ export interface AlbumTrack {
export interface State {
albums: {
ids: string[];
entities: Dictionary<Album>;
entities: Record<string, Album>;
isLoading: boolean;
}
}

View File

@@ -2,7 +2,6 @@ import { useTypedSelector } from '@/store';
import { useCallback } from 'react';
import TrackPlayer, { Track } from 'react-native-track-player';
import { generateTrack } from './JellyfinApi';
import { EntityId } from '@reduxjs/toolkit';
import { shuffle as shuffleArray } from 'lodash';
interface PlayOptions {
@@ -27,7 +26,7 @@ export default function usePlayTracks() {
const downloads = useTypedSelector(state => state.downloads.entities);
return useCallback(async function playTracks(
trackIds: EntityId[] | undefined,
trackIds: string[] | undefined,
options: Partial<PlayOptions> = {},
): Promise<Track[] | undefined> {
if (!trackIds) {