mirror of
https://github.com/austinried/subtracks.git
synced 2026-02-10 06:52:43 +01:00
reorg, remove old music slice files
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import PressableOpacity from '@app/components/PressableOpacity'
|
import PressableOpacity from '@app/components/PressableOpacity'
|
||||||
import { useStar } from '@app/hooks/music'
|
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 colors from '@app/styles/colors'
|
||||||
import font from '@app/styles/font'
|
import font from '@app/styles/font'
|
||||||
import { NavigationProp, useNavigation } from '@react-navigation/native'
|
import { NavigationProp, useNavigation } from '@react-navigation/native'
|
||||||
@@ -199,7 +199,7 @@ const OptionViewAlbum = React.memo<{
|
|||||||
// ))
|
// ))
|
||||||
|
|
||||||
export type AlbumContextPressableProps = ContextMenuProps & {
|
export type AlbumContextPressableProps = ContextMenuProps & {
|
||||||
album: AlbumListItem
|
album: Album
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AlbumContextPressable: React.FC<AlbumContextPressableProps> = props => {
|
export const AlbumContextPressable: React.FC<AlbumContextPressableProps> = props => {
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import Animated from 'react-native-reanimated'
|
|||||||
import PressableOpacity from './PressableOpacity'
|
import PressableOpacity from './PressableOpacity'
|
||||||
import IconMat from 'react-native-vector-icons/MaterialIcons'
|
import IconMat from 'react-native-vector-icons/MaterialIcons'
|
||||||
import { ReactComponentLike } from 'prop-types'
|
import { ReactComponentLike } from 'prop-types'
|
||||||
import { AlbumListItem, Song } from '@app/models/music'
|
|
||||||
import { AlbumContextPressable, NowPlayingContextPressable } from './ContextMenu'
|
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 More = React.memo<{ contextItem?: HeaderContextItem }>(({ contextItem }) => {
|
||||||
const moreIcon = <IconMat name="more-vert" color="white" size={25} />
|
const moreIcon = <IconMat name="more-vert" color="white" size={25} />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useIsPlaying } from '@app/hooks/trackplayer'
|
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 colors from '@app/styles/colors'
|
||||||
import font from '@app/styles/font'
|
import font from '@app/styles/font'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
@@ -97,7 +97,7 @@ const ListItem: React.FC<{
|
|||||||
)
|
)
|
||||||
const albumPressable = useCallback(
|
const albumPressable = useCallback(
|
||||||
({ children }) => (
|
({ children }) => (
|
||||||
<AlbumContextPressable album={item as AlbumListItem} onPress={onPress} triggerWrapperStyle={styles.item}>
|
<AlbumContextPressable album={item as Album} onPress={onPress} triggerWrapperStyle={styles.item}>
|
||||||
{children}
|
{children}
|
||||||
</AlbumContextPressable>
|
</AlbumContextPressable>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Button from '@app/components/Button'
|
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 { useStore } from '@app/state/store'
|
||||||
import { QueueContextType, selectTrackPlayer } from '@app/state/trackplayer'
|
import { QueueContextType, selectTrackPlayer } from '@app/state/trackplayer'
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Album, PlaylistListItem, Artist, Song } from './music'
|
import { Album, Playlist, Artist, Song } from './library'
|
||||||
|
|
||||||
export enum CacheItemType {
|
export enum CacheItemType {
|
||||||
coverArt = 'coverArt',
|
coverArt = 'coverArt',
|
||||||
@@ -27,7 +27,7 @@ export type DownloadedAlbum = Album & {
|
|||||||
songs: string[]
|
songs: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DownloadedPlaylist = PlaylistListItem & {
|
export type DownloadedPlaylist = Playlist & {
|
||||||
songs: string[]
|
songs: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,12 @@ export interface Artist {
|
|||||||
coverArt?: string
|
coverArt?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AlbumListItem {
|
export interface ArtistInfo {
|
||||||
|
id: string
|
||||||
|
largeImageUrl?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Album {
|
||||||
itemType: 'album'
|
itemType: 'album'
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@@ -14,14 +19,10 @@ export interface AlbumListItem {
|
|||||||
artistId?: string
|
artistId?: string
|
||||||
starred?: Date
|
starred?: Date
|
||||||
coverArt?: string
|
coverArt?: string
|
||||||
}
|
|
||||||
|
|
||||||
export interface Album extends AlbumListItem {
|
|
||||||
coverArt?: string
|
|
||||||
year?: number
|
year?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlaylistListItem {
|
export interface Playlist {
|
||||||
itemType: 'playlist'
|
itemType: 'playlist'
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@@ -41,11 +42,15 @@ export interface Song {
|
|||||||
discNumber?: number
|
discNumber?: number
|
||||||
duration?: number
|
duration?: number
|
||||||
starred?: Date
|
starred?: Date
|
||||||
|
|
||||||
// streamUri: string
|
|
||||||
coverArt?: 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
|
||||||
14
app/models/state.ts
Normal file
14
app/models/state.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export interface ById<T> {
|
||||||
|
[id: string]: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OneToMany = ById<string[]>
|
||||||
|
|
||||||
|
export interface OrderedById<T> {
|
||||||
|
byId: ById<T>
|
||||||
|
allIds: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PaginatedList {
|
||||||
|
[offset: number]: string[]
|
||||||
|
}
|
||||||
@@ -5,13 +5,13 @@ import GradientScrollView from '@app/components/GradientScrollView'
|
|||||||
import Header from '@app/components/Header'
|
import Header from '@app/components/Header'
|
||||||
import HeaderBar from '@app/components/HeaderBar'
|
import HeaderBar from '@app/components/HeaderBar'
|
||||||
import ListItem from '@app/components/ListItem'
|
import ListItem from '@app/components/ListItem'
|
||||||
import { Album, Song } from '@app/models/music'
|
import { Album, Song } from '@app/models/library'
|
||||||
import { mapById } from '@app/state/library'
|
|
||||||
import { useStore, useStoreDeep } from '@app/state/store'
|
import { useStore, useStoreDeep } from '@app/state/store'
|
||||||
import { selectTrackPlayer } from '@app/state/trackplayer'
|
import { selectTrackPlayer } from '@app/state/trackplayer'
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
import dimensions from '@app/styles/dimensions'
|
import dimensions from '@app/styles/dimensions'
|
||||||
import font from '@app/styles/font'
|
import font from '@app/styles/font'
|
||||||
|
import { mapById } from '@app/util/state'
|
||||||
import { useLayout } from '@react-native-community/hooks'
|
import { useLayout } from '@react-native-community/hooks'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import React, { useCallback, useEffect } from 'react'
|
import React, { useCallback, useEffect } from 'react'
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import GradientScrollView from '@app/components/GradientScrollView'
|
|||||||
import Header from '@app/components/Header'
|
import Header from '@app/components/Header'
|
||||||
import NothingHere from '@app/components/NothingHere'
|
import NothingHere from '@app/components/NothingHere'
|
||||||
import { useActiveServerRefresh } from '@app/hooks/server'
|
import { useActiveServerRefresh } from '@app/hooks/server'
|
||||||
import { AlbumListItem } from '@app/models/music'
|
import { Album } from '@app/models/library'
|
||||||
import { mapById } from '@app/state/library'
|
|
||||||
import { selectSettings } from '@app/state/settings'
|
import { selectSettings } from '@app/state/settings'
|
||||||
import { useStore, useStoreDeep } from '@app/state/store'
|
import { useStore, useStoreDeep } from '@app/state/store'
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
import font from '@app/styles/font'
|
import font from '@app/styles/font'
|
||||||
import { GetAlbumList2TypeBase, GetAlbumListType } from '@app/subsonic/params'
|
import { GetAlbumList2TypeBase, GetAlbumListType } from '@app/subsonic/params'
|
||||||
|
import { mapById } from '@app/util/state'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import equal from 'fast-deep-equal/es6/react'
|
import equal from 'fast-deep-equal/es6/react'
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
@@ -26,7 +26,7 @@ const titles: { [key in GetAlbumListType]?: string } = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AlbumItem = React.memo<{
|
const AlbumItem = React.memo<{
|
||||||
album: AlbumListItem
|
album: Album
|
||||||
}>(({ album }) => {
|
}>(({ album }) => {
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
|
|
||||||
|
|||||||
@@ -3,19 +3,19 @@ import CoverArt from '@app/components/CoverArt'
|
|||||||
import FilterButton, { OptionData } from '@app/components/FilterButton'
|
import FilterButton, { OptionData } from '@app/components/FilterButton'
|
||||||
import GradientFlatList from '@app/components/GradientFlatList'
|
import GradientFlatList from '@app/components/GradientFlatList'
|
||||||
import { useFetchPaginatedList } from '@app/hooks/list'
|
import { useFetchPaginatedList } from '@app/hooks/list'
|
||||||
import { Album, AlbumListItem } from '@app/models/music'
|
import { Album } from '@app/models/library'
|
||||||
import { mapById } from '@app/state/library'
|
|
||||||
import { selectSettings } from '@app/state/settings'
|
import { selectSettings } from '@app/state/settings'
|
||||||
import { useStore, useStoreDeep } from '@app/state/store'
|
import { useStore, useStoreDeep } from '@app/state/store'
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
import font from '@app/styles/font'
|
import font from '@app/styles/font'
|
||||||
import { GetAlbumList2Params, GetAlbumList2Type } from '@app/subsonic/params'
|
import { GetAlbumList2Params, GetAlbumList2Type } from '@app/subsonic/params'
|
||||||
|
import { mapById } from '@app/util/state'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import { StyleSheet, Text, useWindowDimensions, View } from 'react-native'
|
import { StyleSheet, Text, useWindowDimensions, View } from 'react-native'
|
||||||
|
|
||||||
const AlbumItem = React.memo<{
|
const AlbumItem = React.memo<{
|
||||||
album: AlbumListItem
|
album: Album
|
||||||
size: number
|
size: number
|
||||||
height: number
|
height: number
|
||||||
}>(({ album, size, height }) => {
|
}>(({ album, size, height }) => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import FilterButton, { OptionData } from '@app/components/FilterButton'
|
|||||||
import GradientFlatList from '@app/components/GradientFlatList'
|
import GradientFlatList from '@app/components/GradientFlatList'
|
||||||
import ListItem from '@app/components/ListItem'
|
import ListItem from '@app/components/ListItem'
|
||||||
import { useFetchList2 } from '@app/hooks/list'
|
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 { ArtistFilterType } from '@app/models/settings'
|
||||||
import { selectSettings } from '@app/state/settings'
|
import { selectSettings } from '@app/state/settings'
|
||||||
import { useStore, useStoreDeep } from '@app/state/store'
|
import { useStore, useStoreDeep } from '@app/state/store'
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import GradientFlatList from '@app/components/GradientFlatList'
|
import GradientFlatList from '@app/components/GradientFlatList'
|
||||||
import ListItem from '@app/components/ListItem'
|
import ListItem from '@app/components/ListItem'
|
||||||
import { useFetchList2 } from '@app/hooks/list'
|
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 { useStore, useStoreDeep } from '@app/state/store'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { StyleSheet } from 'react-native'
|
import { StyleSheet } from 'react-native'
|
||||||
|
|
||||||
const PlaylistRenderItem: React.FC<{ item: PlaylistListItem }> = ({ item }) => (
|
const PlaylistRenderItem: React.FC<{ item: Playlist }> = ({ item }) => (
|
||||||
<ListItem item={item} showArt={true} showStar={false} listStyle="big" style={styles.listItem} />
|
<ListItem item={item} showArt={true} showStar={false} listStyle="big" style={styles.listItem} />
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import GradientFlatList from '@app/components/GradientFlatList'
|
|||||||
import ListItem from '@app/components/ListItem'
|
import ListItem from '@app/components/ListItem'
|
||||||
import NowPlayingBar from '@app/components/NowPlayingBar'
|
import NowPlayingBar from '@app/components/NowPlayingBar'
|
||||||
import { useSkipTo } from '@app/hooks/trackplayer'
|
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 { useStore } from '@app/state/store'
|
||||||
import { selectTrackPlayer } from '@app/state/trackplayer'
|
import { selectTrackPlayer } from '@app/state/trackplayer'
|
||||||
import { selectTrackPlayerMap } from '@app/state/trackplayermap'
|
import { selectTrackPlayerMap } from '@app/state/trackplayermap'
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ import ListItem from '@app/components/ListItem'
|
|||||||
import NothingHere from '@app/components/NothingHere'
|
import NothingHere from '@app/components/NothingHere'
|
||||||
import TextInput from '@app/components/TextInput'
|
import TextInput from '@app/components/TextInput'
|
||||||
import { useActiveServerRefresh } from '@app/hooks/server'
|
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 { useStore, useStoreDeep } from '@app/state/store'
|
||||||
import { selectTrackPlayer } from '@app/state/trackplayer'
|
import { selectTrackPlayer } from '@app/state/trackplayer'
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
import font from '@app/styles/font'
|
import font from '@app/styles/font'
|
||||||
|
import { mapById } from '@app/util/state'
|
||||||
import { useFocusEffect, useNavigation } from '@react-navigation/native'
|
import { useFocusEffect, useNavigation } from '@react-navigation/native'
|
||||||
import debounce from 'lodash.debounce'
|
import debounce from 'lodash.debounce'
|
||||||
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import GradientFlatList from '@app/components/GradientFlatList'
|
import GradientFlatList from '@app/components/GradientFlatList'
|
||||||
import ListItem from '@app/components/ListItem'
|
import ListItem from '@app/components/ListItem'
|
||||||
import { useFetchPaginatedList } from '@app/hooks/list'
|
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 { useStore, useStoreDeep } from '@app/state/store'
|
||||||
import { selectTrackPlayer } from '@app/state/trackplayer'
|
import { selectTrackPlayer } from '@app/state/trackplayer'
|
||||||
import { Search3Params } from '@app/subsonic/params'
|
import { Search3Params } from '@app/subsonic/params'
|
||||||
|
import { mapById } from '@app/util/state'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import React, { useCallback, useEffect } from 'react'
|
import React, { useCallback, useEffect } from 'react'
|
||||||
import { StyleSheet } from 'react-native'
|
import { StyleSheet } from 'react-native'
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import ListItem from '@app/components/ListItem'
|
|||||||
import ListPlayerControls from '@app/components/ListPlayerControls'
|
import ListPlayerControls from '@app/components/ListPlayerControls'
|
||||||
import NothingHere from '@app/components/NothingHere'
|
import NothingHere from '@app/components/NothingHere'
|
||||||
import { useCoverArtFile } from '@app/hooks/cache'
|
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 { useStore, useStoreDeep } from '@app/state/store'
|
||||||
import { selectTrackPlayer } from '@app/state/trackplayer'
|
import { selectTrackPlayer } from '@app/state/trackplayer'
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
@@ -46,7 +46,7 @@ const SongRenderItem: React.FC<{
|
|||||||
const SongListDetails = React.memo<{
|
const SongListDetails = React.memo<{
|
||||||
title: string
|
title: string
|
||||||
type: SongListType
|
type: SongListType
|
||||||
songList?: Album | PlaylistListItem
|
songList?: Album | Playlist
|
||||||
songs?: Song[]
|
songs?: Song[]
|
||||||
subtitle?: string
|
subtitle?: string
|
||||||
}>(({ title, songList, songs, subtitle, type }) => {
|
}>(({ title, songList, songs, subtitle, type }) => {
|
||||||
|
|||||||
@@ -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 { Store } from '@app/state/store'
|
||||||
import {
|
import {
|
||||||
AlbumID3Element,
|
AlbumID3Element,
|
||||||
@@ -20,141 +22,12 @@ import {
|
|||||||
Search3Response,
|
Search3Response,
|
||||||
SubsonicResponse,
|
SubsonicResponse,
|
||||||
} from '@app/subsonic/responses'
|
} from '@app/subsonic/responses'
|
||||||
|
import { reduceById, mergeById } from '@app/util/state'
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
import { WritableDraft } from 'immer/dist/types/types-external'
|
import { WritableDraft } from 'immer/dist/types/types-external'
|
||||||
import merge from 'lodash.merge'
|
|
||||||
import pick from 'lodash.pick'
|
import pick from 'lodash.pick'
|
||||||
import { GetState, SetState } from 'zustand'
|
import { GetState, SetState } from 'zustand'
|
||||||
|
|
||||||
export interface ById<T> {
|
|
||||||
[id: string]: T
|
|
||||||
}
|
|
||||||
|
|
||||||
export type OneToMany = ById<string[]>
|
|
||||||
|
|
||||||
export interface OrderedById<T> {
|
|
||||||
byId: ById<T>
|
|
||||||
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 = {
|
export type LibrarySlice = {
|
||||||
entities: {
|
entities: {
|
||||||
artists: ById<Artist>
|
artists: ById<Artist>
|
||||||
@@ -191,21 +64,6 @@ export type LibrarySlice = {
|
|||||||
unstar: (params: StarParams) => Promise<void>
|
unstar: (params: StarParams) => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
function reduceById<T extends { id: string }>(collection: T[]): ById<T> {
|
|
||||||
return collection.reduce((acc, value) => {
|
|
||||||
acc[value.id] = value
|
|
||||||
return acc
|
|
||||||
}, {} as ById<T>)
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergeById<T extends { [id: string]: unknown }>(object: T, source: T): void {
|
|
||||||
merge(object, source)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mapById<T>(object: ById<T>, ids: string[]): T[] {
|
|
||||||
return ids.map(id => object[id]).filter(a => a !== undefined)
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultEntities = () => ({
|
const defaultEntities = () => ({
|
||||||
artists: {},
|
artists: {},
|
||||||
artistAlbums: {},
|
artistAlbums: {},
|
||||||
@@ -576,3 +434,64 @@ export const createLibrarySlice = (set: SetState<Store>, get: GetState<Store>):
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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<void> }
|
|
||||||
fetchAlbumCoverArt: (id: string) => Promise<void>
|
|
||||||
getAlbumCoverArt: (id: string | undefined) => Promise<string | undefined>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createMusicSlice = (set: SetState<Store>, get: GetState<Store>): 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<void>(async resolve => {
|
|
||||||
try {
|
|
||||||
const response = await client.getAlbum({ id })
|
|
||||||
set(
|
|
||||||
produce<MusicSlice>(state => {
|
|
||||||
state.albumIdCoverArt[id] = response.data.album.coverArt
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
} finally {
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
}).then(() => {
|
|
||||||
set(
|
|
||||||
produce<MusicSlice>(state => {
|
|
||||||
delete state.albumIdCoverArtRequests[id]
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
set(
|
|
||||||
produce<MusicSlice>(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]
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -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<Song>
|
|
||||||
mapChildrenToSongs: (children: ChildElement[], coverArt?: string) => Promise<Song[]>
|
|
||||||
mapArtistID3toArtist: (artist: ArtistID3Element) => Artist
|
|
||||||
mapAlbumID3toAlbumListItem: (album: AlbumID3Element) => AlbumListItem
|
|
||||||
mapAlbumID3toAlbum: (album: AlbumID3Element) => AlbumListItem
|
|
||||||
mapPlaylistListItem: (playlist: PlaylistElement) => PlaylistListItem
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createMusicMapSlice = (set: SetState<Store>, get: GetState<Store>): 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<string, Promise<string | undefined>>)
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { createMusicSlice, MusicSlice } from '@app/state/music'
|
|
||||||
import { createSettingsSlice, SettingsSlice } from '@app/state/settings'
|
import { createSettingsSlice, SettingsSlice } from '@app/state/settings'
|
||||||
import AsyncStorage from '@react-native-async-storage/async-storage'
|
import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||||
import equal from 'fast-deep-equal/es6/react'
|
import equal from 'fast-deep-equal/es6/react'
|
||||||
@@ -7,16 +6,13 @@ import { persist, subscribeWithSelector } from 'zustand/middleware'
|
|||||||
import { CacheSlice, createCacheSlice } from './cache'
|
import { CacheSlice, createCacheSlice } from './cache'
|
||||||
import { createLibrarySlice, LibrarySlice } from './library'
|
import { createLibrarySlice, LibrarySlice } from './library'
|
||||||
import migrations from './migrations'
|
import migrations from './migrations'
|
||||||
import { createMusicMapSlice, MusicMapSlice } from './musicmap'
|
|
||||||
import { createTrackPlayerSlice, TrackPlayerSlice } from './trackplayer'
|
import { createTrackPlayerSlice, TrackPlayerSlice } from './trackplayer'
|
||||||
import { createTrackPlayerMapSlice, TrackPlayerMapSlice } from './trackplayermap'
|
import { createTrackPlayerMapSlice, TrackPlayerMapSlice } from './trackplayermap'
|
||||||
|
|
||||||
const DB_VERSION = migrations.length
|
const DB_VERSION = migrations.length
|
||||||
|
|
||||||
export type Store = SettingsSlice &
|
export type Store = SettingsSlice &
|
||||||
MusicSlice &
|
|
||||||
LibrarySlice &
|
LibrarySlice &
|
||||||
MusicMapSlice &
|
|
||||||
TrackPlayerSlice &
|
TrackPlayerSlice &
|
||||||
TrackPlayerMapSlice &
|
TrackPlayerMapSlice &
|
||||||
CacheSlice & {
|
CacheSlice & {
|
||||||
@@ -34,9 +30,7 @@ export const useStore = create<
|
|||||||
persist(
|
persist(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
...createSettingsSlice(set, get),
|
...createSettingsSlice(set, get),
|
||||||
...createMusicSlice(set, get),
|
|
||||||
...createLibrarySlice(set, get),
|
...createLibrarySlice(set, get),
|
||||||
...createMusicMapSlice(set, get),
|
|
||||||
...createTrackPlayerSlice(set, get),
|
...createTrackPlayerSlice(set, get),
|
||||||
...createTrackPlayerMapSlice(set, get),
|
...createTrackPlayerMapSlice(set, get),
|
||||||
...createCacheSlice(set, get),
|
...createCacheSlice(set, get),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NoClientError } from '@app/models/error'
|
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 PromiseQueue from '@app/util/PromiseQueue'
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
import TrackPlayer, { PlayerOptions, RepeatMode, State, Track } from 'react-native-track-player'
|
import TrackPlayer, { PlayerOptions, RepeatMode, State, Track } from 'react-native-track-player'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Song } from '@app/models/music'
|
import { Song } from '@app/models/library'
|
||||||
import userAgent from '@app/util/userAgent'
|
import userAgent from '@app/util/userAgent'
|
||||||
import { GetState, SetState } from 'zustand'
|
import { GetState, SetState } from 'zustand'
|
||||||
import { Store } from './store'
|
import { Store } from './store'
|
||||||
|
|||||||
17
app/util/state.ts
Normal file
17
app/util/state.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { ById } from '@app/models/state'
|
||||||
|
import merge from 'lodash.merge'
|
||||||
|
|
||||||
|
export function reduceById<T extends { id: string }>(collection: T[]): ById<T> {
|
||||||
|
return collection.reduce((acc, value) => {
|
||||||
|
acc[value.id] = value
|
||||||
|
return acc
|
||||||
|
}, {} as ById<T>)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mergeById<T extends { [id: string]: unknown }>(object: T, source: T): void {
|
||||||
|
merge(object, source)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapById<T>(object: ById<T>, ids: string[]): T[] {
|
||||||
|
return ids.map(id => object[id]).filter(a => a !== undefined)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user