diff --git a/app/components/ContextMenu.tsx b/app/components/ContextMenu.tsx index 913780b..b089317 100644 --- a/app/components/ContextMenu.tsx +++ b/app/components/ContextMenu.tsx @@ -1,6 +1,6 @@ import PressableOpacity from '@app/components/PressableOpacity' import { useStar } from '@app/hooks/music' -import { AlbumListItem, Artist, Song, StarrableItemType } from '@app/models/music' +import { StarrableItemType, Song, Artist, Album } from '@app/models/library' import colors from '@app/styles/colors' import font from '@app/styles/font' import { NavigationProp, useNavigation } from '@react-navigation/native' @@ -199,7 +199,7 @@ const OptionViewAlbum = React.memo<{ // )) export type AlbumContextPressableProps = ContextMenuProps & { - album: AlbumListItem + album: Album } export const AlbumContextPressable: React.FC = props => { diff --git a/app/components/HeaderBar.tsx b/app/components/HeaderBar.tsx index 202875c..c391d47 100644 --- a/app/components/HeaderBar.tsx +++ b/app/components/HeaderBar.tsx @@ -8,10 +8,10 @@ import Animated from 'react-native-reanimated' import PressableOpacity from './PressableOpacity' import IconMat from 'react-native-vector-icons/MaterialIcons' import { ReactComponentLike } from 'prop-types' -import { AlbumListItem, Song } from '@app/models/music' import { AlbumContextPressable, NowPlayingContextPressable } from './ContextMenu' +import { Album, Song } from '@app/models/library' -export type HeaderContextItem = Song | AlbumListItem +export type HeaderContextItem = Song | Album const More = React.memo<{ contextItem?: HeaderContextItem }>(({ contextItem }) => { const moreIcon = diff --git a/app/components/ListItem.tsx b/app/components/ListItem.tsx index 329feef..6b72a81 100644 --- a/app/components/ListItem.tsx +++ b/app/components/ListItem.tsx @@ -1,5 +1,5 @@ import { useIsPlaying } from '@app/hooks/trackplayer' -import { AlbumListItem, Artist, ListableItem, Song } from '@app/models/music' +import { Album, Artist, ListableItem, Song } from '@app/models/library' import colors from '@app/styles/colors' import font from '@app/styles/font' import { useNavigation } from '@react-navigation/native' @@ -97,7 +97,7 @@ const ListItem: React.FC<{ ) const albumPressable = useCallback( ({ children }) => ( - + {children} ), diff --git a/app/components/ListPlayerControls.tsx b/app/components/ListPlayerControls.tsx index 3e0e6a3..6f206fe 100644 --- a/app/components/ListPlayerControls.tsx +++ b/app/components/ListPlayerControls.tsx @@ -1,5 +1,5 @@ import Button from '@app/components/Button' -import { Song } from '@app/models/music' +import { Song } from '@app/models/library' import { useStore } from '@app/state/store' import { QueueContextType, selectTrackPlayer } from '@app/state/trackplayer' import colors from '@app/styles/colors' diff --git a/app/models/cache.ts b/app/models/cache.ts index cff273e..5bd23cd 100644 --- a/app/models/cache.ts +++ b/app/models/cache.ts @@ -1,4 +1,4 @@ -import { Album, PlaylistListItem, Artist, Song } from './music' +import { Album, Playlist, Artist, Song } from './library' export enum CacheItemType { coverArt = 'coverArt', @@ -27,7 +27,7 @@ export type DownloadedAlbum = Album & { songs: string[] } -export type DownloadedPlaylist = PlaylistListItem & { +export type DownloadedPlaylist = Playlist & { songs: string[] } diff --git a/app/models/music.ts b/app/models/library.ts similarity index 64% rename from app/models/music.ts rename to app/models/library.ts index 10d5b65..ddc02e4 100644 --- a/app/models/music.ts +++ b/app/models/library.ts @@ -6,7 +6,12 @@ export interface Artist { coverArt?: string } -export interface AlbumListItem { +export interface ArtistInfo { + id: string + largeImageUrl?: string +} + +export interface Album { itemType: 'album' id: string name: string @@ -14,14 +19,10 @@ export interface AlbumListItem { artistId?: string starred?: Date coverArt?: string -} - -export interface Album extends AlbumListItem { - coverArt?: string year?: number } -export interface PlaylistListItem { +export interface Playlist { itemType: 'playlist' id: string name: string @@ -41,11 +42,15 @@ export interface Song { discNumber?: number duration?: number starred?: Date - - // streamUri: string coverArt?: string } -export type ListableItem = Song | AlbumListItem | Artist | PlaylistListItem +export interface SearchResults { + artists: string[] + albums: string[] + songs: string[] +} -export type StarrableItemType = 'song' | 'album' | 'artist' +export type StarrableItemType = 'album' | 'song' | 'artist' + +export type ListableItem = Album | Song | Artist | Playlist diff --git a/app/models/state.ts b/app/models/state.ts new file mode 100644 index 0000000..3c68c17 --- /dev/null +++ b/app/models/state.ts @@ -0,0 +1,14 @@ +export interface ById { + [id: string]: T +} + +export type OneToMany = ById + +export interface OrderedById { + byId: ById + allIds: string[] +} + +export interface PaginatedList { + [offset: number]: string[] +} diff --git a/app/screens/ArtistView.tsx b/app/screens/ArtistView.tsx index 7b3d537..9e21a55 100644 --- a/app/screens/ArtistView.tsx +++ b/app/screens/ArtistView.tsx @@ -5,13 +5,13 @@ import GradientScrollView from '@app/components/GradientScrollView' import Header from '@app/components/Header' import HeaderBar from '@app/components/HeaderBar' import ListItem from '@app/components/ListItem' -import { Album, Song } from '@app/models/music' -import { mapById } from '@app/state/library' +import { Album, Song } from '@app/models/library' import { useStore, useStoreDeep } from '@app/state/store' import { selectTrackPlayer } from '@app/state/trackplayer' import colors from '@app/styles/colors' import dimensions from '@app/styles/dimensions' import font from '@app/styles/font' +import { mapById } from '@app/util/state' import { useLayout } from '@react-native-community/hooks' import { useNavigation } from '@react-navigation/native' import React, { useCallback, useEffect } from 'react' diff --git a/app/screens/Home.tsx b/app/screens/Home.tsx index 2cdf3b1..01e0961 100644 --- a/app/screens/Home.tsx +++ b/app/screens/Home.tsx @@ -4,13 +4,13 @@ import GradientScrollView from '@app/components/GradientScrollView' import Header from '@app/components/Header' import NothingHere from '@app/components/NothingHere' import { useActiveServerRefresh } from '@app/hooks/server' -import { AlbumListItem } from '@app/models/music' -import { mapById } from '@app/state/library' +import { Album } from '@app/models/library' import { selectSettings } from '@app/state/settings' import { useStore, useStoreDeep } from '@app/state/store' import colors from '@app/styles/colors' import font from '@app/styles/font' import { GetAlbumList2TypeBase, GetAlbumListType } from '@app/subsonic/params' +import { mapById } from '@app/util/state' import { useNavigation } from '@react-navigation/native' import equal from 'fast-deep-equal/es6/react' import produce from 'immer' @@ -26,7 +26,7 @@ const titles: { [key in GetAlbumListType]?: string } = { } const AlbumItem = React.memo<{ - album: AlbumListItem + album: Album }>(({ album }) => { const navigation = useNavigation() diff --git a/app/screens/LibraryAlbums.tsx b/app/screens/LibraryAlbums.tsx index f0f8eed..7d5cfe6 100644 --- a/app/screens/LibraryAlbums.tsx +++ b/app/screens/LibraryAlbums.tsx @@ -3,19 +3,19 @@ import CoverArt from '@app/components/CoverArt' import FilterButton, { OptionData } from '@app/components/FilterButton' import GradientFlatList from '@app/components/GradientFlatList' import { useFetchPaginatedList } from '@app/hooks/list' -import { Album, AlbumListItem } from '@app/models/music' -import { mapById } from '@app/state/library' +import { Album } from '@app/models/library' import { selectSettings } from '@app/state/settings' import { useStore, useStoreDeep } from '@app/state/store' import colors from '@app/styles/colors' import font from '@app/styles/font' import { GetAlbumList2Params, GetAlbumList2Type } from '@app/subsonic/params' +import { mapById } from '@app/util/state' import { useNavigation } from '@react-navigation/native' import React, { useCallback } from 'react' import { StyleSheet, Text, useWindowDimensions, View } from 'react-native' const AlbumItem = React.memo<{ - album: AlbumListItem + album: Album size: number height: number }>(({ album, size, height }) => { diff --git a/app/screens/LibraryArtists.tsx b/app/screens/LibraryArtists.tsx index 939c846..780f31a 100644 --- a/app/screens/LibraryArtists.tsx +++ b/app/screens/LibraryArtists.tsx @@ -2,7 +2,7 @@ import FilterButton, { OptionData } from '@app/components/FilterButton' import GradientFlatList from '@app/components/GradientFlatList' import ListItem from '@app/components/ListItem' import { useFetchList2 } from '@app/hooks/list' -import { Artist } from '@app/models/music' +import { Artist } from '@app/models/library' import { ArtistFilterType } from '@app/models/settings' import { selectSettings } from '@app/state/settings' import { useStore, useStoreDeep } from '@app/state/store' diff --git a/app/screens/LibraryPlaylists.tsx b/app/screens/LibraryPlaylists.tsx index 6c9920b..9a1a074 100644 --- a/app/screens/LibraryPlaylists.tsx +++ b/app/screens/LibraryPlaylists.tsx @@ -1,12 +1,12 @@ import GradientFlatList from '@app/components/GradientFlatList' import ListItem from '@app/components/ListItem' import { useFetchList2 } from '@app/hooks/list' -import { PlaylistListItem } from '@app/models/music' +import { Playlist } from '@app/models/library' import { useStore, useStoreDeep } from '@app/state/store' import React from 'react' import { StyleSheet } from 'react-native' -const PlaylistRenderItem: React.FC<{ item: PlaylistListItem }> = ({ item }) => ( +const PlaylistRenderItem: React.FC<{ item: Playlist }> = ({ item }) => ( ) diff --git a/app/screens/NowPlayingQueue.tsx b/app/screens/NowPlayingQueue.tsx index 247f0a7..e09441e 100644 --- a/app/screens/NowPlayingQueue.tsx +++ b/app/screens/NowPlayingQueue.tsx @@ -2,7 +2,7 @@ import GradientFlatList from '@app/components/GradientFlatList' import ListItem from '@app/components/ListItem' import NowPlayingBar from '@app/components/NowPlayingBar' import { useSkipTo } from '@app/hooks/trackplayer' -import { Song } from '@app/models/music' +import { Song } from '@app/models/library' import { useStore } from '@app/state/store' import { selectTrackPlayer } from '@app/state/trackplayer' import { selectTrackPlayerMap } from '@app/state/trackplayermap' diff --git a/app/screens/Search.tsx b/app/screens/Search.tsx index 26ba23b..02b5a64 100644 --- a/app/screens/Search.tsx +++ b/app/screens/Search.tsx @@ -5,11 +5,12 @@ import ListItem from '@app/components/ListItem' import NothingHere from '@app/components/NothingHere' import TextInput from '@app/components/TextInput' import { useActiveServerRefresh } from '@app/hooks/server' -import { Album, Artist, mapById, SearchResults, Song } from '@app/state/library' +import { Song, Album, Artist, SearchResults } from '@app/models/library' import { useStore, useStoreDeep } from '@app/state/store' import { selectTrackPlayer } from '@app/state/trackplayer' import colors from '@app/styles/colors' import font from '@app/styles/font' +import { mapById } from '@app/util/state' import { useFocusEffect, useNavigation } from '@react-navigation/native' import debounce from 'lodash.debounce' import React, { useCallback, useMemo, useRef, useState } from 'react' diff --git a/app/screens/SearchResultsView.tsx b/app/screens/SearchResultsView.tsx index d93d480..5f6f9a7 100644 --- a/app/screens/SearchResultsView.tsx +++ b/app/screens/SearchResultsView.tsx @@ -1,10 +1,11 @@ import GradientFlatList from '@app/components/GradientFlatList' import ListItem from '@app/components/ListItem' import { useFetchPaginatedList } from '@app/hooks/list' -import { Album, Artist, Song, mapById } from '@app/state/library' +import { Album, Artist, Song } from '@app/models/library' import { useStore, useStoreDeep } from '@app/state/store' import { selectTrackPlayer } from '@app/state/trackplayer' import { Search3Params } from '@app/subsonic/params' +import { mapById } from '@app/util/state' import { useNavigation } from '@react-navigation/native' import React, { useCallback, useEffect } from 'react' import { StyleSheet } from 'react-native' diff --git a/app/screens/SongListView.tsx b/app/screens/SongListView.tsx index 38de9da..05529c0 100644 --- a/app/screens/SongListView.tsx +++ b/app/screens/SongListView.tsx @@ -6,7 +6,7 @@ import ListItem from '@app/components/ListItem' import ListPlayerControls from '@app/components/ListPlayerControls' import NothingHere from '@app/components/NothingHere' import { useCoverArtFile } from '@app/hooks/cache' -import { Album, PlaylistListItem, Song } from '@app/models/music' +import { Song, Album, Playlist } from '@app/models/library' import { useStore, useStoreDeep } from '@app/state/store' import { selectTrackPlayer } from '@app/state/trackplayer' import colors from '@app/styles/colors' @@ -46,7 +46,7 @@ const SongRenderItem: React.FC<{ const SongListDetails = React.memo<{ title: string type: SongListType - songList?: Album | PlaylistListItem + songList?: Album | Playlist songs?: Song[] subtitle?: string }>(({ title, songList, songs, subtitle, type }) => { diff --git a/app/state/library.ts b/app/state/library.ts index 582fd21..8cd9e1e 100644 --- a/app/state/library.ts +++ b/app/state/library.ts @@ -1,3 +1,5 @@ +import { Album, Artist, ArtistInfo, Playlist, SearchResults, Song } from '@app/models/library' +import { ById, OneToMany } from '@app/models/state' import { Store } from '@app/state/store' import { AlbumID3Element, @@ -20,141 +22,12 @@ import { Search3Response, SubsonicResponse, } from '@app/subsonic/responses' +import { reduceById, mergeById } from '@app/util/state' import produce from 'immer' import { WritableDraft } from 'immer/dist/types/types-external' -import merge from 'lodash.merge' import pick from 'lodash.pick' import { GetState, SetState } from 'zustand' -export interface ById { - [id: string]: T -} - -export type OneToMany = ById - -export interface OrderedById { - byId: ById - allIds: string[] -} - -export interface PaginatedList { - [offset: number]: string[] -} - -export interface Artist { - itemType: 'artist' - id: string - name: string - starred?: Date - coverArt?: string -} - -export interface ArtistInfo { - id: string - largeImageUrl?: string -} - -export interface Album { - itemType: 'album' - id: string - name: string - artist?: string - artistId?: string - starred?: Date - coverArt?: string - year?: number -} - -export interface Playlist { - itemType: 'playlist' - id: string - name: string - comment?: string - coverArt?: string -} - -export interface Song { - itemType: 'song' - id: string - album?: string - albumId?: string - artist?: string - artistId?: string - title: string - track?: number - discNumber?: number - duration?: number - starred?: Date - coverArt?: string -} - -export interface SearchResults { - artists: string[] - albums: string[] - songs: string[] -} - -function mapArtist(artist: ArtistID3Element): Artist { - return { - itemType: 'artist', - id: artist.id, - name: artist.name, - starred: artist.starred, - coverArt: artist.coverArt, - } -} - -function mapArtistInfo(id: string, info: ArtistInfo2Element): ArtistInfo { - return { - id, - largeImageUrl: info.largeImageUrl, - } -} - -function mapAlbum(album: AlbumID3Element): Album { - return { - itemType: 'album', - id: album.id, - name: album.name, - artist: album.artist, - artistId: album.artistId, - starred: album.starred, - coverArt: album.coverArt, - year: album.year, - } -} - -function mapPlaylist(playlist: PlaylistElement): Playlist { - return { - itemType: 'playlist', - id: playlist.id, - name: playlist.name, - comment: playlist.comment, - coverArt: playlist.coverArt, - } -} - -function mapSong(song: ChildElement): Song { - return { - itemType: 'song', - id: song.id, - album: song.album, - albumId: song.albumId, - artist: song.artist, - artistId: song.artistId, - title: song.title, - track: song.track, - discNumber: song.discNumber, - duration: song.duration, - starred: song.starred, - coverArt: song.coverArt, - } -} - -function mapId(entities: { id: string }[]): string[] { - return entities.map(e => e.id) -} - export type LibrarySlice = { entities: { artists: ById @@ -191,21 +64,6 @@ export type LibrarySlice = { unstar: (params: StarParams) => Promise } -function reduceById(collection: T[]): ById { - return collection.reduce((acc, value) => { - acc[value.id] = value - return acc - }, {} as ById) -} - -function mergeById(object: T, source: T): void { - merge(object, source) -} - -export function mapById(object: ById, ids: string[]): T[] { - return ids.map(id => object[id]).filter(a => a !== undefined) -} - const defaultEntities = () => ({ artists: {}, artistAlbums: {}, @@ -576,3 +434,64 @@ export const createLibrarySlice = (set: SetState, get: GetState): } }, }) + +function mapArtist(artist: ArtistID3Element): Artist { + return { + itemType: 'artist', + id: artist.id, + name: artist.name, + starred: artist.starred, + coverArt: artist.coverArt, + } +} + +function mapArtistInfo(id: string, info: ArtistInfo2Element): ArtistInfo { + return { + id, + largeImageUrl: info.largeImageUrl, + } +} + +function mapAlbum(album: AlbumID3Element): Album { + return { + itemType: 'album', + id: album.id, + name: album.name, + artist: album.artist, + artistId: album.artistId, + starred: album.starred, + coverArt: album.coverArt, + year: album.year, + } +} + +function mapPlaylist(playlist: PlaylistElement): Playlist { + return { + itemType: 'playlist', + id: playlist.id, + name: playlist.name, + comment: playlist.comment, + coverArt: playlist.coverArt, + } +} + +function mapSong(song: ChildElement): Song { + return { + itemType: 'song', + id: song.id, + album: song.album, + albumId: song.albumId, + artist: song.artist, + artistId: song.artistId, + title: song.title, + track: song.track, + discNumber: song.discNumber, + duration: song.duration, + starred: song.starred, + coverArt: song.coverArt, + } +} + +function mapId(entities: { id: string }[]): string[] { + return entities.map(e => e.id) +} diff --git a/app/state/music.ts b/app/state/music.ts deleted file mode 100644 index d2cc658..0000000 --- a/app/state/music.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Store } from '@app/state/store' -import produce from 'immer' -import { GetState, SetState } from 'zustand' - -export type MusicSlice = { - albumIdCoverArt: { [id: string]: string | undefined } - albumIdCoverArtRequests: { [id: string]: Promise } - fetchAlbumCoverArt: (id: string) => Promise - getAlbumCoverArt: (id: string | undefined) => Promise -} - -export const createMusicSlice = (set: SetState, get: GetState): MusicSlice => ({ - albumIdCoverArt: {}, - albumIdCoverArtRequests: {}, - - fetchAlbumCoverArt: async id => { - const client = get().client - if (!client) { - return - } - - const inProgress = get().albumIdCoverArtRequests[id] - if (inProgress !== undefined) { - return await inProgress - } - - const promise = new Promise(async resolve => { - try { - const response = await client.getAlbum({ id }) - set( - produce(state => { - state.albumIdCoverArt[id] = response.data.album.coverArt - }), - ) - } finally { - resolve() - } - }).then(() => { - set( - produce(state => { - delete state.albumIdCoverArtRequests[id] - }), - ) - }) - set( - produce(state => { - state.albumIdCoverArtRequests[id] = promise - }), - ) - - return await promise - }, - - getAlbumCoverArt: async id => { - if (!id) { - return - } - - const existing = get().albumIdCoverArt[id] - if (existing) { - return existing - } - - await get().fetchAlbumCoverArt(id) - return get().albumIdCoverArt[id] - }, -}) diff --git a/app/state/musicmap.ts b/app/state/musicmap.ts deleted file mode 100644 index e086b1e..0000000 --- a/app/state/musicmap.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { AlbumListItem, Artist, PlaylistListItem, Song } from '@app/models/music' -import { AlbumID3Element, ArtistID3Element, ChildElement, PlaylistElement } from '@app/subsonic/elements' -import { GetState, SetState } from 'zustand' -import { Store } from './store' - -export type MusicMapSlice = { - mapChildToSong: (child: ChildElement, coverArt?: string) => Promise - mapChildrenToSongs: (children: ChildElement[], coverArt?: string) => Promise - mapArtistID3toArtist: (artist: ArtistID3Element) => Artist - mapAlbumID3toAlbumListItem: (album: AlbumID3Element) => AlbumListItem - mapAlbumID3toAlbum: (album: AlbumID3Element) => AlbumListItem - mapPlaylistListItem: (playlist: PlaylistElement) => PlaylistListItem -} - -export const createMusicMapSlice = (set: SetState, get: GetState): MusicMapSlice => ({ - mapChildToSong: async (child, coverArt) => { - return { - itemType: 'song', - id: child.id, - album: child.album, - albumId: child.albumId, - artist: child.artist, - artistId: child.artistId, - title: child.title, - track: child.track, - discNumber: child.discNumber, - duration: child.duration, - starred: child.starred, - coverArt: coverArt || (await get().getAlbumCoverArt(child.albumId)), - streamUri: get().buildStreamUri(child.id), - } - }, - - mapChildrenToSongs: async (children, coverArt) => { - const albumIds = children.reduce((acc, val) => { - if (val.albumId && !(val.albumId in acc)) { - acc[val.albumId] = get().getAlbumCoverArt(val.albumId) - } - return acc - }, {} as Record>) - - await Promise.all(Object.values(albumIds)) - - const songs: Song[] = [] - for (const child of children) { - songs.push(await get().mapChildToSong(child, coverArt || (await get().getAlbumCoverArt(child.albumId)))) - } - return songs - }, - - mapArtistID3toArtist: artist => { - return { - itemType: 'artist', - id: artist.id, - name: artist.name, - starred: artist.starred, - coverArt: artist.coverArt, - } - }, - - mapAlbumID3toAlbumListItem: album => { - return { - itemType: 'album', - id: album.id, - name: album.name, - artist: album.artist, - artistId: album.artistId, - starred: album.starred, - coverArt: album.coverArt, - } - }, - - mapAlbumID3toAlbum: album => { - return { - ...get().mapAlbumID3toAlbumListItem(album), - coverArt: album.coverArt, - year: album.year, - } - }, - - mapPlaylistListItem: playlist => { - return { - itemType: 'playlist', - id: playlist.id, - name: playlist.name, - comment: playlist.comment, - coverArt: playlist.coverArt, - } - }, -}) diff --git a/app/state/store.ts b/app/state/store.ts index c000496..20be9ac 100644 --- a/app/state/store.ts +++ b/app/state/store.ts @@ -1,4 +1,3 @@ -import { createMusicSlice, MusicSlice } from '@app/state/music' import { createSettingsSlice, SettingsSlice } from '@app/state/settings' import AsyncStorage from '@react-native-async-storage/async-storage' import equal from 'fast-deep-equal/es6/react' @@ -7,16 +6,13 @@ import { persist, subscribeWithSelector } from 'zustand/middleware' import { CacheSlice, createCacheSlice } from './cache' import { createLibrarySlice, LibrarySlice } from './library' import migrations from './migrations' -import { createMusicMapSlice, MusicMapSlice } from './musicmap' import { createTrackPlayerSlice, TrackPlayerSlice } from './trackplayer' import { createTrackPlayerMapSlice, TrackPlayerMapSlice } from './trackplayermap' const DB_VERSION = migrations.length export type Store = SettingsSlice & - MusicSlice & LibrarySlice & - MusicMapSlice & TrackPlayerSlice & TrackPlayerMapSlice & CacheSlice & { @@ -34,9 +30,7 @@ export const useStore = create< persist( (set, get) => ({ ...createSettingsSlice(set, get), - ...createMusicSlice(set, get), ...createLibrarySlice(set, get), - ...createMusicMapSlice(set, get), ...createTrackPlayerSlice(set, get), ...createTrackPlayerMapSlice(set, get), ...createCacheSlice(set, get), diff --git a/app/state/trackplayer.ts b/app/state/trackplayer.ts index 09a20c0..d61b653 100644 --- a/app/state/trackplayer.ts +++ b/app/state/trackplayer.ts @@ -1,5 +1,5 @@ import { NoClientError } from '@app/models/error' -import { Song } from '@app/models/music' +import { Song } from '@app/models/library' import PromiseQueue from '@app/util/PromiseQueue' import produce from 'immer' import TrackPlayer, { PlayerOptions, RepeatMode, State, Track } from 'react-native-track-player' diff --git a/app/state/trackplayermap.ts b/app/state/trackplayermap.ts index 0ba5725..3c80ff4 100644 --- a/app/state/trackplayermap.ts +++ b/app/state/trackplayermap.ts @@ -1,4 +1,4 @@ -import { Song } from '@app/models/music' +import { Song } from '@app/models/library' import userAgent from '@app/util/userAgent' import { GetState, SetState } from 'zustand' import { Store } from './store' diff --git a/app/util/state.ts b/app/util/state.ts new file mode 100644 index 0000000..379e5f6 --- /dev/null +++ b/app/util/state.ts @@ -0,0 +1,17 @@ +import { ById } from '@app/models/state' +import merge from 'lodash.merge' + +export function reduceById(collection: T[]): ById { + return collection.reduce((acc, value) => { + acc[value.id] = value + return acc + }, {} as ById) +} + +export function mergeById(object: T, source: T): void { + merge(object, source) +} + +export function mapById(object: ById, ids: string[]): T[] { + return ids.map(id => object[id]).filter(a => a !== undefined) +}