From 24b443fd706b2b351a1b3ced2f22a659fa62352c Mon Sep 17 00:00:00 2001
From: austinried <4966622+austinried@users.noreply.github.com>
Date: Mon, 5 Jul 2021 10:27:15 +0900
Subject: [PATCH] let's try no semicolons
---
.eslintrc.js | 3 +-
.prettierrc.js | 3 +-
App.tsx | 24 +-
__tests__/App-test.tsx | 12 +-
babel.config.js | 2 +-
index.js | 26 +-
metro.config.js | 2 +-
react-native.config.js | 2 +-
src/components/ArtistsList.tsx | 24 +-
src/components/FocusableIcon.tsx | 30 +-
src/components/NowPlayingLayout.tsx | 92 +++---
src/components/Settings.tsx | 52 ++--
src/components/SplashPage.tsx | 46 +--
src/components/TrackPlayerState.tsx | 106 +++----
src/components/common/AlbumArt.tsx | 36 +--
src/components/common/AlbumView.tsx | 88 +++---
src/components/common/ArtistArt.tsx | 86 +++---
src/components/common/ArtistView.tsx | 40 +--
src/components/common/BottomTabBar.tsx | 50 +--
src/components/common/Button.tsx | 20 +-
src/components/common/CoverArt.tsx | 38 +--
src/components/common/GradientBackground.tsx | 28 +-
src/components/common/GradientFlatList.tsx | 14 +-
src/components/common/GradientScrollView.tsx | 18 +-
.../common/ImageGradientBackground.tsx | 62 ++--
.../common/ImageGradientScrollView.tsx | 22 +-
src/components/common/TopTabContainer.tsx | 10 +-
src/components/library/AlbumsTab.tsx | 56 ++--
src/components/library/ArtistsTab.tsx | 48 +--
src/components/library/PlaylistsTab.tsx | 8 +-
.../navigation/BottomTabNavigator.tsx | 22 +-
.../navigation/LibraryTopTabNavigator.tsx | 68 ++---
src/components/navigation/RootNavigator.tsx | 14 +-
src/hooks/subsonic.ts | 16 +-
src/hooks/trackplayer.ts | 44 +--
src/models/music.ts | 130 ++++----
src/models/settings.ts | 14 +-
src/paths.ts | 4 +-
src/playback/service.ts | 24 +-
src/state/music.ts | 152 +++++-----
src/state/settings.ts | 14 +-
src/state/trackplayer.ts | 28 +-
src/storage/asyncstorage.ts | 36 +--
src/storage/atomWithAsyncStorage.ts | 8 +-
src/storage/music.ts | 20 +-
src/styles/colors.ts | 2 +-
src/styles/text.ts | 30 +-
src/subsonic/api.ts | 156 +++++-----
src/subsonic/elements.ts | 284 +++++++++---------
src/subsonic/params.ts | 80 ++---
src/subsonic/responses.ts | 90 +++---
src/util.ts | 12 +-
52 files changed, 1149 insertions(+), 1147 deletions(-)
diff --git a/.eslintrc.js b/.eslintrc.js
index d86d237..4829fca 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -5,5 +5,6 @@ module.exports = {
'react-native/no-inline-styles': 0,
radix: 0,
'@typescript-eslint/no-unused-vars': ['warn'],
+ semi: 0,
},
-};
+}
diff --git a/.prettierrc.js b/.prettierrc.js
index c026d20..ed7187a 100644
--- a/.prettierrc.js
+++ b/.prettierrc.js
@@ -5,4 +5,5 @@ module.exports = {
trailingComma: 'all',
arrowParens: 'avoid',
printWidth: 120,
-};
+ semi: false,
+}
diff --git a/App.tsx b/App.tsx
index fc046c1..caba2ea 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,14 +1,14 @@
-import React from 'react';
-import { DarkTheme, NavigationContainer } from '@react-navigation/native';
-import SplashPage from './src/components/SplashPage';
-import RootNavigator from './src/components/navigation/RootNavigator';
-import { Provider } from 'jotai';
-import { StatusBar, View } from 'react-native';
-import colors from './src/styles/colors';
-import TrackPlayerState from './src/components/TrackPlayerState';
+import React from 'react'
+import { DarkTheme, NavigationContainer } from '@react-navigation/native'
+import SplashPage from './src/components/SplashPage'
+import RootNavigator from './src/components/navigation/RootNavigator'
+import { Provider } from 'jotai'
+import { StatusBar, View } from 'react-native'
+import colors from './src/styles/colors'
+import TrackPlayerState from './src/components/TrackPlayerState'
-const theme = { ...DarkTheme };
-theme.colors.background = colors.gradient.high;
+const theme = { ...DarkTheme }
+theme.colors.background = colors.gradient.high
const App = () => (
@@ -22,6 +22,6 @@ const App = () => (
-);
+)
-export default App;
+export default App
diff --git a/__tests__/App-test.tsx b/__tests__/App-test.tsx
index 1784766..351e770 100644
--- a/__tests__/App-test.tsx
+++ b/__tests__/App-test.tsx
@@ -2,13 +2,13 @@
* @format
*/
-import 'react-native';
-import React from 'react';
-import App from '../App';
+import 'react-native'
+import React from 'react'
+import App from '../App'
// Note: test renderer must be required after react-native.
-import renderer from 'react-test-renderer';
+import renderer from 'react-test-renderer'
it('renders correctly', () => {
- renderer.create();
-});
+ renderer.create()
+})
diff --git a/babel.config.js b/babel.config.js
index d6be578..5d45acd 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -4,4 +4,4 @@ module.exports = {
// reanimated has to be listed last in plugins
'react-native-reanimated/plugin',
],
-};
+}
diff --git a/index.js b/index.js
index a3ee640..5d85b86 100644
--- a/index.js
+++ b/index.js
@@ -1,19 +1,19 @@
-import 'react-native-gesture-handler';
-import 'react-native-get-random-values';
+import 'react-native-gesture-handler'
+import 'react-native-get-random-values'
-import { enableScreens } from 'react-native-screens';
-enableScreens();
+import { enableScreens } from 'react-native-screens'
+enableScreens()
-import { AppRegistry } from 'react-native';
-import App from './App';
-import { name as appName } from './app.json';
-import TrackPlayer, { Capability } from 'react-native-track-player';
+import { AppRegistry } from 'react-native'
+import App from './App'
+import { name as appName } from './app.json'
+import TrackPlayer, { Capability } from 'react-native-track-player'
-AppRegistry.registerComponent(appName, () => App);
-TrackPlayer.registerPlaybackService(() => require('./src/playback/service'));
+AppRegistry.registerComponent(appName, () => App)
+TrackPlayer.registerPlaybackService(() => require('./src/playback/service'))
async function start() {
- await TrackPlayer.setupPlayer();
+ await TrackPlayer.setupPlayer()
await TrackPlayer.updateOptions({
capabilities: [
Capability.Play,
@@ -23,6 +23,6 @@ async function start() {
Capability.SkipToPrevious,
],
compactCapabilities: [Capability.Play, Capability.Pause, Capability.SkipToNext, Capability.SkipToPrevious],
- });
+ })
}
-start();
+start()
diff --git a/metro.config.js b/metro.config.js
index e91aba9..c81b3ca 100644
--- a/metro.config.js
+++ b/metro.config.js
@@ -14,4 +14,4 @@ module.exports = {
},
}),
},
-};
+}
diff --git a/react-native.config.js b/react-native.config.js
index ef09f5b..be7d438 100644
--- a/react-native.config.js
+++ b/react-native.config.js
@@ -4,4 +4,4 @@ module.exports = {
android: {},
},
assets: ['./assets/fonts'],
-};
+}
diff --git a/src/components/ArtistsList.tsx b/src/components/ArtistsList.tsx
index 8715682..7b43c45 100644
--- a/src/components/ArtistsList.tsx
+++ b/src/components/ArtistsList.tsx
@@ -1,8 +1,8 @@
-import React from 'react';
-import { FlatList, Text, View } from 'react-native';
-import { useAtomValue } from 'jotai/utils';
-import { Artist } from '../models/music';
-import { artistsAtom } from '../state/music';
+import React from 'react'
+import { FlatList, Text, View } from 'react-native'
+import { useAtomValue } from 'jotai/utils'
+import { Artist } from '../models/music'
+import { artistsAtom } from '../state/music'
const ArtistItem: React.FC<{ item: Artist }> = ({ item }) => (
@@ -15,15 +15,15 @@ const ArtistItem: React.FC<{ item: Artist }> = ({ item }) => (
{item.name}
-);
+)
const List = () => {
- const artists = useAtomValue(artistsAtom);
+ const artists = useAtomValue(artistsAtom)
- const renderItem: React.FC<{ item: Artist }> = ({ item }) => ;
+ const renderItem: React.FC<{ item: Artist }> = ({ item }) =>
- return item.id} />;
-};
+ return item.id} />
+}
const ArtistsList = () => (
@@ -31,6 +31,6 @@ const ArtistsList = () => (
-);
+)
-export default ArtistsList;
+export default ArtistsList
diff --git a/src/components/FocusableIcon.tsx b/src/components/FocusableIcon.tsx
index 658add3..f65b61a 100644
--- a/src/components/FocusableIcon.tsx
+++ b/src/components/FocusableIcon.tsx
@@ -1,19 +1,19 @@
-import React from 'react';
-import { Image, ImageSourcePropType } from 'react-native';
-import colors from '../styles/colors';
+import React from 'react'
+import { Image, ImageSourcePropType } from 'react-native'
+import colors from '../styles/colors'
export type FocusableIconProps = {
- focused: boolean;
- source: ImageSourcePropType;
- focusedSource?: ImageSourcePropType;
- width?: number;
- height?: number;
-};
+ focused: boolean
+ source: ImageSourcePropType
+ focusedSource?: ImageSourcePropType
+ width?: number
+ height?: number
+}
const FocusableIcon: React.FC = props => {
- props.focusedSource = props.focusedSource || props.source;
- props.width = props.width || 26;
- props.height = props.height || 26;
+ props.focusedSource = props.focusedSource || props.source
+ props.width = props.width || 26
+ props.height = props.height || 26
return (
= props => {
}}
source={props.focused ? props.focusedSource : props.source}
/>
- );
-};
+ )
+}
-export default FocusableIcon;
+export default FocusableIcon
diff --git a/src/components/NowPlayingLayout.tsx b/src/components/NowPlayingLayout.tsx
index ae4e679..f466838 100644
--- a/src/components/NowPlayingLayout.tsx
+++ b/src/components/NowPlayingLayout.tsx
@@ -1,15 +1,15 @@
-import { useAtomValue } from 'jotai/utils';
-import React from 'react';
-import { Pressable, StatusBar, StyleSheet, Text, useWindowDimensions, View } from 'react-native';
-import FastImage from 'react-native-fast-image';
-import TrackPlayer, { State } from 'react-native-track-player';
-import { currentQueueNameAtom, currentTrackAtom, playerStateAtom } from '../state/trackplayer';
-import text from '../styles/text';
-import CoverArt from './common/CoverArt';
-import ImageGradientBackground from './common/ImageGradientBackground';
+import { useAtomValue } from 'jotai/utils'
+import React from 'react'
+import { Pressable, StatusBar, StyleSheet, Text, useWindowDimensions, View } from 'react-native'
+import FastImage from 'react-native-fast-image'
+import TrackPlayer, { State } from 'react-native-track-player'
+import { currentQueueNameAtom, currentTrackAtom, playerStateAtom } from '../state/trackplayer'
+import text from '../styles/text'
+import CoverArt from './common/CoverArt'
+import ImageGradientBackground from './common/ImageGradientBackground'
const NowPlayingHeader = () => {
- const queueName = useAtomValue(currentQueueNameAtom);
+ const queueName = useAtomValue(currentQueueNameAtom)
return (
@@ -19,8 +19,8 @@ const NowPlayingHeader = () => {
- );
-};
+ )
+}
const headerStyles = StyleSheet.create({
container: {
@@ -38,13 +38,13 @@ const headerStyles = StyleSheet.create({
queueName: {
...text.paragraph,
},
-});
+})
const SongCoverArt = () => {
- const track = useAtomValue(currentTrackAtom);
- const layout = useWindowDimensions();
+ const track = useAtomValue(currentTrackAtom)
+ const layout = useWindowDimensions()
- const size = layout.width - layout.width / 7;
+ const size = layout.width - layout.width / 7
return (
@@ -55,8 +55,8 @@ const SongCoverArt = () => {
coverArtUri={track?.artwork as string}
/>
- );
-};
+ )
+}
const coverArtStyles = StyleSheet.create({
container: {
@@ -64,18 +64,18 @@ const coverArtStyles = StyleSheet.create({
alignItems: 'center',
marginTop: 10,
},
-});
+})
const SongInfo = () => {
- const track = useAtomValue(currentTrackAtom);
+ const track = useAtomValue(currentTrackAtom)
return (
{track?.title}
{track?.artist}
- );
-};
+ )
+}
const infoStyles = StyleSheet.create({
container: {
@@ -94,33 +94,33 @@ const infoStyles = StyleSheet.create({
fontSize: 14,
textAlign: 'center',
},
-});
+})
const PlayerControls = () => {
- const state = useAtomValue(playerStateAtom);
+ const state = useAtomValue(playerStateAtom)
- let playPauseIcon: number;
- let playPauseStyle: any;
- let playPauseAction: () => void;
+ let playPauseIcon: number
+ let playPauseStyle: any
+ let playPauseAction: () => void
switch (state) {
case State.Playing:
case State.Buffering:
case State.Connecting:
- playPauseIcon = require('../../res/pause_circle-fill.png');
- playPauseStyle = controlsStyles.enabled;
- playPauseAction = () => TrackPlayer.pause();
- break;
+ playPauseIcon = require('../../res/pause_circle-fill.png')
+ playPauseStyle = controlsStyles.enabled
+ playPauseAction = () => TrackPlayer.pause()
+ break
case State.Paused:
- playPauseIcon = require('../../res/play_circle-fill.png');
- playPauseStyle = controlsStyles.enabled;
- playPauseAction = () => TrackPlayer.play();
- break;
+ playPauseIcon = require('../../res/play_circle-fill.png')
+ playPauseStyle = controlsStyles.enabled
+ playPauseAction = () => TrackPlayer.play()
+ break
default:
- playPauseIcon = require('../../res/play_circle-fill.png');
- playPauseStyle = controlsStyles.disabled;
- playPauseAction = () => {};
- break;
+ playPauseIcon = require('../../res/play_circle-fill.png')
+ playPauseStyle = controlsStyles.disabled
+ playPauseAction = () => {}
+ break
}
return (
@@ -139,8 +139,8 @@ const PlayerControls = () => {
style={{ ...controlsStyles.skip, ...playPauseStyle }}
/>
- );
-};
+ )
+}
const controlsStyles = StyleSheet.create({
container: {
@@ -165,10 +165,10 @@ const controlsStyles = StyleSheet.create({
disabled: {
opacity: 0.35,
},
-});
+})
const NowPlayingLayout = () => {
- const track = useAtomValue(currentTrackAtom);
+ const track = useAtomValue(currentTrackAtom)
return (
{
- );
-};
+ )
+}
-export default NowPlayingLayout;
+export default NowPlayingLayout
diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx
index fb48a25..b157f8c 100644
--- a/src/components/Settings.tsx
+++ b/src/components/Settings.tsx
@@ -1,40 +1,40 @@
-import { useNavigation } from '@react-navigation/core';
-import { useAtom } from 'jotai';
-import md5 from 'md5';
-import React from 'react';
-import { Button, Text, View } from 'react-native';
-import { v4 as uuidv4 } from 'uuid';
-import { appSettingsAtom } from '../state/settings';
-import { getAllKeys, multiRemove } from '../storage/asyncstorage';
-import text from '../styles/text';
+import { useNavigation } from '@react-navigation/core'
+import { useAtom } from 'jotai'
+import md5 from 'md5'
+import React from 'react'
+import { Button, Text, View } from 'react-native'
+import { v4 as uuidv4 } from 'uuid'
+import { appSettingsAtom } from '../state/settings'
+import { getAllKeys, multiRemove } from '../storage/asyncstorage'
+import text from '../styles/text'
const TestControls = () => {
- const navigation = useNavigation();
+ const navigation = useNavigation()
const removeAllKeys = async () => {
- const allKeys = await getAllKeys();
- await multiRemove(allKeys);
- };
+ const allKeys = await getAllKeys()
+ await multiRemove(allKeys)
+ }
return (
- );
-};
+ )
+}
const ServerSettingsView = () => {
- const [appSettings, setAppSettings] = useAtom(appSettingsAtom);
+ const [appSettings, setAppSettings] = useAtom(appSettingsAtom)
const bootstrapServer = () => {
if (appSettings.servers.length !== 0) {
- return;
+ return
}
- const id = uuidv4();
- const salt = uuidv4();
- const address = 'http://demo.subsonic.org';
+ const id = uuidv4()
+ const salt = uuidv4()
+ const address = 'http://demo.subsonic.org'
setAppSettings({
...appSettings,
@@ -49,8 +49,8 @@ const ServerSettingsView = () => {
},
],
activeServer: id,
- });
- };
+ })
+ }
return (
@@ -62,8 +62,8 @@ const ServerSettingsView = () => {
))}
- );
-};
+ )
+}
const SettingsView = () => (
@@ -72,6 +72,6 @@ const SettingsView = () => (
-);
+)
-export default SettingsView;
+export default SettingsView
diff --git a/src/components/SplashPage.tsx b/src/components/SplashPage.tsx
index ec28220..afcb2cd 100644
--- a/src/components/SplashPage.tsx
+++ b/src/components/SplashPage.tsx
@@ -1,45 +1,45 @@
-import React, { useEffect, useState } from 'react';
-import { Text, View } from 'react-native';
-import RNFS from 'react-native-fs';
-import paths from '../paths';
+import React, { useEffect, useState } from 'react'
+import { Text, View } from 'react-native'
+import RNFS from 'react-native-fs'
+import paths from '../paths'
async function mkdir(path: string): Promise {
- const exists = await RNFS.exists(path);
+ const exists = await RNFS.exists(path)
if (exists) {
- const isDir = (await RNFS.stat(path)).isDirectory();
+ const isDir = (await RNFS.stat(path)).isDirectory()
if (!isDir) {
- throw new Error(`path exists and is not a directory: ${path}`);
+ throw new Error(`path exists and is not a directory: ${path}`)
} else {
- return;
+ return
}
}
- return await RNFS.mkdir(path);
+ return await RNFS.mkdir(path)
}
const SplashPage: React.FC<{}> = ({ children }) => {
- const [ready, setReady] = useState(false);
+ const [ready, setReady] = useState(false)
- const minSplashTime = new Promise(resolve => setTimeout(resolve, 1));
+ const minSplashTime = new Promise(resolve => setTimeout(resolve, 1))
const prepare = async () => {
- await mkdir(paths.imageCache);
- await mkdir(paths.songCache);
- await mkdir(paths.songs);
- };
+ await mkdir(paths.imageCache)
+ await mkdir(paths.songCache)
+ await mkdir(paths.songs)
+ }
- const promise = Promise.all([prepare(), minSplashTime]);
+ const promise = Promise.all([prepare(), minSplashTime])
useEffect(() => {
promise.then(() => {
- setReady(true);
- });
- });
+ setReady(true)
+ })
+ })
if (!ready) {
- return Loading THE GOOD SHIT...;
+ return Loading THE GOOD SHIT...
}
- return {children};
-};
+ return {children}
+}
-export default SplashPage;
+export default SplashPage
diff --git a/src/components/TrackPlayerState.tsx b/src/components/TrackPlayerState.tsx
index cfc0999..7ac509f 100644
--- a/src/components/TrackPlayerState.tsx
+++ b/src/components/TrackPlayerState.tsx
@@ -1,27 +1,27 @@
-import React, { useCallback, useEffect } from 'react';
-import TrackPlayer, { Event, State, useTrackPlayerEvents } from 'react-native-track-player';
-import { useAppState } from '@react-native-community/hooks';
-import { useUpdateAtom, useAtomValue } from 'jotai/utils';
-import { currentQueueNameAtom, currentTrackAtom, playerStateAtom } from '../state/trackplayer';
-import { View } from 'react-native';
+import React, { useCallback, useEffect } from 'react'
+import TrackPlayer, { Event, State, useTrackPlayerEvents } from 'react-native-track-player'
+import { useAppState } from '@react-native-community/hooks'
+import { useUpdateAtom, useAtomValue } from 'jotai/utils'
+import { currentQueueNameAtom, currentTrackAtom, playerStateAtom } from '../state/trackplayer'
+import { View } from 'react-native'
const CurrentTrackState = () => {
- const setCurrentTrack = useUpdateAtom(currentTrackAtom);
- const appState = useAppState();
+ const setCurrentTrack = useUpdateAtom(currentTrackAtom)
+ const appState = useAppState()
const update = useCallback(async () => {
- const index = await TrackPlayer.getCurrentTrack();
+ const index = await TrackPlayer.getCurrentTrack()
if (index !== null && index >= 0) {
- const track = await TrackPlayer.getTrack(index);
+ const track = await TrackPlayer.getTrack(index)
if (track !== null) {
- setCurrentTrack(track);
- return;
+ setCurrentTrack(track)
+ return
}
}
- setCurrentTrack(undefined);
- }, [setCurrentTrack]);
+ setCurrentTrack(undefined)
+ }, [setCurrentTrack])
useTrackPlayerEvents(
[
@@ -36,91 +36,91 @@ const CurrentTrackState = () => {
],
event => {
if (event.type === Event.PlaybackQueueEnded && 'track' in event) {
- setCurrentTrack(undefined);
- return;
+ setCurrentTrack(undefined)
+ return
}
- update();
+ update()
},
- );
+ )
useEffect(() => {
if (appState === 'active') {
- update();
+ update()
}
- }, [appState, update]);
+ }, [appState, update])
- return <>>;
-};
+ return <>>
+}
const CurrentQueueName = () => {
- const setCurrentQueueName = useUpdateAtom(currentQueueNameAtom);
- const appState = useAppState();
+ const setCurrentQueueName = useUpdateAtom(currentQueueNameAtom)
+ const appState = useAppState()
const update = useCallback(async () => {
- const queue = await TrackPlayer.getQueue();
+ const queue = await TrackPlayer.getQueue()
if (queue !== null && queue.length > 0) {
- setCurrentQueueName(queue[0].queueName);
- return;
+ setCurrentQueueName(queue[0].queueName)
+ return
}
- setCurrentQueueName(undefined);
- }, [setCurrentQueueName]);
+ setCurrentQueueName(undefined)
+ }, [setCurrentQueueName])
useTrackPlayerEvents(
[Event.PlaybackState, Event.PlaybackQueueEnded, Event.PlaybackMetadataReceived, Event.RemoteDuck, Event.RemoteStop],
event => {
if (event.type === Event.PlaybackState) {
if (event.state === State.Stopped || event.state === State.None) {
- return;
+ return
}
}
- update();
+ update()
},
- );
+ )
useEffect(() => {
if (appState === 'active') {
- update();
+ update()
}
- }, [appState, update]);
+ }, [appState, update])
- return <>>;
-};
+ return <>>
+}
const PlayerState = () => {
- const setPlayerState = useUpdateAtom(playerStateAtom);
- const appState = useAppState();
+ const setPlayerState = useUpdateAtom(playerStateAtom)
+ const appState = useAppState()
const update = useCallback(
async (state?: State) => {
- setPlayerState(state || (await TrackPlayer.getState()));
+ setPlayerState(state || (await TrackPlayer.getState()))
},
[setPlayerState],
- );
+ )
useTrackPlayerEvents([Event.PlaybackState], event => {
- update(event.state);
- });
+ update(event.state)
+ })
useEffect(() => {
if (appState === 'active') {
- update();
+ update()
}
- }, [appState, update]);
+ }, [appState, update])
- return <>>;
-};
+ return <>>
+}
const Debug = () => {
- const value = useAtomValue(currentQueueNameAtom);
+ const value = useAtomValue(currentQueueNameAtom)
useEffect(() => {
- console.log(value);
- }, [value]);
+ console.log(value)
+ }, [value])
- return <>>;
-};
+ return <>>
+}
const TrackPlayerState = () => (
@@ -129,6 +129,6 @@ const TrackPlayerState = () => (
-);
+)
-export default TrackPlayerState;
+export default TrackPlayerState
diff --git a/src/components/common/AlbumArt.tsx b/src/components/common/AlbumArt.tsx
index 4100e45..d5f7bdf 100644
--- a/src/components/common/AlbumArt.tsx
+++ b/src/components/common/AlbumArt.tsx
@@ -1,20 +1,20 @@
-import { useAtomValue } from 'jotai/utils';
-import React from 'react';
-import { ActivityIndicator, View } from 'react-native';
-import FastImage from 'react-native-fast-image';
-import LinearGradient from 'react-native-linear-gradient';
-import { albumArtAtomFamily } from '../../state/music';
-import colors from '../../styles/colors';
-import CoverArt from './CoverArt';
+import { useAtomValue } from 'jotai/utils'
+import React from 'react'
+import { ActivityIndicator, View } from 'react-native'
+import FastImage from 'react-native-fast-image'
+import LinearGradient from 'react-native-linear-gradient'
+import { albumArtAtomFamily } from '../../state/music'
+import colors from '../../styles/colors'
+import CoverArt from './CoverArt'
interface AlbumArtProps {
- id: string;
- height: number;
- width: number;
+ id: string
+ height: number
+ width: number
}
const AlbumArt: React.FC = ({ id, height, width }) => {
- const albumArt = useAtomValue(albumArtAtomFamily(id));
+ const albumArt = useAtomValue(albumArtAtomFamily(id))
const Placeholder = () => (
@@ -24,7 +24,7 @@ const AlbumArt: React.FC = ({ id, height, width }) => {
resizeMode={FastImage.resizeMode.contain}
/>
- );
+ )
return (
= ({ id, height, width }) => {
width={width}
coverArtUri={width > 128 ? albumArt?.uri : albumArt?.thumbUri}
/>
- );
-};
+ )
+}
const AlbumArtFallback: React.FC = ({ height, width }) => (
= ({ height, width }) => (
}}>
-);
+)
const AlbumArtLoader: React.FC = props => (
}>
-);
+)
-export default React.memo(AlbumArtLoader);
+export default React.memo(AlbumArtLoader)
diff --git a/src/components/common/AlbumView.tsx b/src/components/common/AlbumView.tsx
index ba96662..0603eac 100644
--- a/src/components/common/AlbumView.tsx
+++ b/src/components/common/AlbumView.tsx
@@ -1,6 +1,6 @@
-import { useNavigation } from '@react-navigation/native';
-import { useAtomValue } from 'jotai/utils';
-import React, { useEffect, useState } from 'react';
+import { useNavigation } from '@react-navigation/native'
+import { useAtomValue } from 'jotai/utils'
+import React, { useEffect, useState } from 'react'
import {
ActivityIndicator,
GestureResponderEvent,
@@ -9,26 +9,26 @@ import {
Text,
useWindowDimensions,
View,
-} from 'react-native';
-import { useSetQueue } from '../../hooks/trackplayer';
-import { albumAtomFamily } from '../../state/music';
-import { currentTrackAtom } from '../../state/trackplayer';
-import colors from '../../styles/colors';
-import text from '../../styles/text';
-import AlbumArt from './AlbumArt';
-import Button from './Button';
-import GradientBackground from './GradientBackground';
-import ImageGradientScrollView from './ImageGradientScrollView';
+} from 'react-native'
+import { useSetQueue } from '../../hooks/trackplayer'
+import { albumAtomFamily } from '../../state/music'
+import { currentTrackAtom } from '../../state/trackplayer'
+import colors from '../../styles/colors'
+import text from '../../styles/text'
+import AlbumArt from './AlbumArt'
+import Button from './Button'
+import GradientBackground from './GradientBackground'
+import ImageGradientScrollView from './ImageGradientScrollView'
const SongItem: React.FC<{
- id: string;
- title: string;
- artist?: string;
- track?: number;
- onPress: (event: GestureResponderEvent) => void;
+ id: string
+ title: string
+ artist?: string
+ track?: number
+ onPress: (event: GestureResponderEvent) => void
}> = ({ id, title, artist, onPress }) => {
- const [opacity, setOpacity] = useState(1);
- const currentTrack = useAtomValue(currentTrackAtom);
+ const [opacity, setOpacity] = useState(1)
+ const currentTrack = useAtomValue(currentTrackAtom)
return (
- );
-};
+ )
+}
const AlbumDetails: React.FC<{
- id: string;
+ id: string
}> = ({ id }) => {
- const album = useAtomValue(albumAtomFamily(id));
- const layout = useWindowDimensions();
- const setQueue = useSetQueue();
+ const album = useAtomValue(albumAtomFamily(id))
+ const layout = useWindowDimensions()
+ const setQueue = useSetQueue()
- const coverSize = layout.width - layout.width / 2.5;
+ const coverSize = layout.width - layout.width / 2.5
if (!album) {
- return No Album;
+ return No Album
}
return (
@@ -152,9 +152,9 @@ const AlbumDetails: React.FC<{
{album.songs
.sort((a, b) => {
if (b.track && a.track) {
- return a.track - b.track;
+ return a.track - b.track
} else {
- return a.title.localeCompare(b.title);
+ return a.title.localeCompare(b.title)
}
})
.map(s => (
@@ -169,13 +169,13 @@ const AlbumDetails: React.FC<{
))}
- );
-};
+ )
+}
const AlbumViewFallback = () => {
- const layout = useWindowDimensions();
+ const layout = useWindowDimensions()
- const coverSize = layout.width - layout.width / 2.5;
+ const coverSize = layout.width - layout.width / 2.5
return (
{
}}>
- );
-};
+ )
+}
const AlbumView: React.FC<{
- id: string;
- title: string;
+ id: string
+ title: string
}> = ({ id, title }) => {
- const navigation = useNavigation();
+ const navigation = useNavigation()
useEffect(() => {
- navigation.setOptions({ title });
- });
+ navigation.setOptions({ title })
+ })
return (
}>
- );
-};
+ )
+}
-export default React.memo(AlbumView);
+export default React.memo(AlbumView)
diff --git a/src/components/common/ArtistArt.tsx b/src/components/common/ArtistArt.tsx
index 9035c39..5605993 100644
--- a/src/components/common/ArtistArt.tsx
+++ b/src/components/common/ArtistArt.tsx
@@ -1,23 +1,23 @@
-import { useAtomValue } from 'jotai/utils';
-import React from 'react';
-import { ActivityIndicator, View } from 'react-native';
-import FastImage from 'react-native-fast-image';
-import LinearGradient from 'react-native-linear-gradient';
-import { artistArtAtomFamily } from '../../state/music';
-import colors from '../../styles/colors';
-import CoverArt from './CoverArt';
+import { useAtomValue } from 'jotai/utils'
+import React from 'react'
+import { ActivityIndicator, View } from 'react-native'
+import FastImage from 'react-native-fast-image'
+import LinearGradient from 'react-native-linear-gradient'
+import { artistArtAtomFamily } from '../../state/music'
+import colors from '../../styles/colors'
+import CoverArt from './CoverArt'
interface ArtistArtSizeProps {
- height: number;
- width: number;
+ height: number
+ width: number
}
interface ArtistArtXUpProps extends ArtistArtSizeProps {
- coverArtUris: string[];
+ coverArtUris: string[]
}
interface ArtistArtProps extends ArtistArtSizeProps {
- id: string;
+ id: string
}
const PlaceholderContainer: React.FC = ({ height, width, children }) => (
@@ -31,11 +31,11 @@ const PlaceholderContainer: React.FC = ({ height, width, chi
}}>
{children}
-);
+)
const FourUp: React.FC = ({ height, width, coverArtUris }) => {
- const halfHeight = height / 2;
- const halfWidth = width / 2;
+ const halfHeight = height / 2
+ const halfWidth = width / 2
return (
@@ -64,12 +64,12 @@ const FourUp: React.FC = ({ height, width, coverArtUris }) =>
/>
- );
-};
+ )
+}
const ThreeUp: React.FC = ({ height, width, coverArtUris }) => {
- const halfHeight = height / 2;
- const halfWidth = width / 2;
+ const halfHeight = height / 2
+ const halfWidth = width / 2
return (
@@ -93,11 +93,11 @@ const ThreeUp: React.FC = ({ height, width, coverArtUris }) =
/>
- );
-};
+ )
+}
const TwoUp: React.FC = ({ height, width, coverArtUris }) => {
- const halfHeight = height / 2;
+ const halfHeight = height / 2
return (
@@ -116,16 +116,16 @@ const TwoUp: React.FC = ({ height, width, coverArtUris }) =>
/>
- );
-};
+ )
+}
const OneUp: React.FC = ({ height, width, coverArtUris }) => {
return (
- );
-};
+ )
+}
const NoneUp: React.FC = ({ height, width }) => {
return (
@@ -139,35 +139,35 @@ const NoneUp: React.FC = ({ height, width }) => {
resizeMode={FastImage.resizeMode.cover}
/>
- );
-};
+ )
+}
const ArtistArt: React.FC = ({ id, height, width }) => {
- const artistArt = useAtomValue(artistArtAtomFamily(id));
+ const artistArt = useAtomValue(artistArtAtomFamily(id))
const Placeholder = () => {
- const none = ;
+ const none =
if (!artistArt || !artistArt.coverArtUris) {
- return none;
+ return none
}
- const { coverArtUris } = artistArt;
+ const { coverArtUris } = artistArt
if (coverArtUris.length >= 4) {
- return ;
+ return
}
if (coverArtUris.length === 3) {
- return ;
+ return
}
if (coverArtUris.length === 2) {
- return ;
+ return
}
if (coverArtUris.length === 1) {
- return ;
+ return
}
- return none;
- };
+ return none
+ }
return (
= ({ id, height, width }) => {
}}>
- );
-};
+ )
+}
const ArtistArtFallback: React.FC = ({ height, width }) => (
= ({ height, width }) => (
}}>
-);
+)
const ArtistArtLoader: React.FC = props => (
}>
-);
+)
-export default React.memo(ArtistArtLoader);
+export default React.memo(ArtistArtLoader)
diff --git a/src/components/common/ArtistView.tsx b/src/components/common/ArtistView.tsx
index 2a84c23..f7d33cd 100644
--- a/src/components/common/ArtistView.tsx
+++ b/src/components/common/ArtistView.tsx
@@ -1,17 +1,17 @@
-import { useNavigation } from '@react-navigation/native';
-import { useAtomValue } from 'jotai/utils';
-import React, { useEffect } from 'react';
-import { Text } from 'react-native';
-import { artistInfoAtomFamily } from '../../state/music';
-import text from '../../styles/text';
-import ArtistArt from './ArtistArt';
-import GradientScrollView from './GradientScrollView';
+import { useNavigation } from '@react-navigation/native'
+import { useAtomValue } from 'jotai/utils'
+import React, { useEffect } from 'react'
+import { Text } from 'react-native'
+import { artistInfoAtomFamily } from '../../state/music'
+import text from '../../styles/text'
+import ArtistArt from './ArtistArt'
+import GradientScrollView from './GradientScrollView'
const ArtistDetails: React.FC<{ id: string }> = ({ id }) => {
- const artist = useAtomValue(artistInfoAtomFamily(id));
+ const artist = useAtomValue(artistInfoAtomFamily(id))
if (!artist) {
- return <>>;
+ return <>>
}
return (
@@ -26,24 +26,24 @@ const ArtistDetails: React.FC<{ id: string }> = ({ id }) => {
{artist.name}
- );
-};
+ )
+}
const ArtistView: React.FC<{
- id: string;
- title: string;
+ id: string
+ title: string
}> = ({ id, title }) => {
- const navigation = useNavigation();
+ const navigation = useNavigation()
useEffect(() => {
- navigation.setOptions({ title });
- });
+ navigation.setOptions({ title })
+ })
return (
Loading...}>
- );
-};
+ )
+}
-export default React.memo(ArtistView);
+export default React.memo(ArtistView)
diff --git a/src/components/common/BottomTabBar.tsx b/src/components/common/BottomTabBar.tsx
index 70c499d..e187ea6 100644
--- a/src/components/common/BottomTabBar.tsx
+++ b/src/components/common/BottomTabBar.tsx
@@ -1,9 +1,9 @@
-import React, { useState } from 'react';
-import { Text, View, Pressable } from 'react-native';
-import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
-import textStyles from '../../styles/text';
-import colors from '../../styles/colors';
-import FastImage from 'react-native-fast-image';
+import React, { useState } from 'react'
+import { Text, View, Pressable } from 'react-native'
+import { BottomTabBarProps } from '@react-navigation/bottom-tabs'
+import textStyles from '../../styles/text'
+import colors from '../../styles/colors'
+import FastImage from 'react-native-fast-image'
const icons: { [key: string]: any } = {
home: {
@@ -22,29 +22,29 @@ const icons: { [key: string]: any } = {
regular: require('../../../res/settings.png'),
fill: require('../../../res/settings-fill.png'),
},
-};
+}
const BottomTabButton: React.FC<{
- routeKey: string;
- label: string;
- name: string;
- isFocused: boolean;
- img: { regular: number; fill: number };
- navigation: any;
+ routeKey: string
+ label: string
+ name: string
+ isFocused: boolean
+ img: { regular: number; fill: number }
+ navigation: any
}> = ({ routeKey, label, name, isFocused, img, navigation }) => {
- const [opacity, setOpacity] = useState(1);
+ const [opacity, setOpacity] = useState(1)
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: routeKey,
canPreventDefault: true,
- });
+ })
if (!isFocused && !event.defaultPrevented) {
- navigation.navigate(name);
+ navigation.navigate(name)
}
- };
+ }
return (
- );
-};
+ )
+}
const BottomTabBar: React.FC = ({ state, descriptors, navigation }) => {
return (
@@ -87,13 +87,13 @@ const BottomTabBar: React.FC = ({ state, descriptors, navigat
paddingHorizontal: 28,
}}>
{state.routes.map((route, index) => {
- const { options } = descriptors[route.key] as any;
+ const { options } = descriptors[route.key] as any
const label =
options.tabBarLabel !== undefined
? (options.tabBarLabel as string)
: options.title !== undefined
? options.title
- : route.name;
+ : route.name
return (
= ({ state, descriptors, navigat
img={icons[options.icon]}
navigation={navigation}
/>
- );
+ )
})}
- );
-};
+ )
+}
-export default BottomTabBar;
+export default BottomTabBar
diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx
index c16103e..3afadbe 100644
--- a/src/components/common/Button.tsx
+++ b/src/components/common/Button.tsx
@@ -1,13 +1,13 @@
-import React, { useState } from 'react';
-import { GestureResponderEvent, Pressable, Text } from 'react-native';
-import colors from '../../styles/colors';
-import text from '../../styles/text';
+import React, { useState } from 'react'
+import { GestureResponderEvent, Pressable, Text } from 'react-native'
+import colors from '../../styles/colors'
+import text from '../../styles/text'
const Button: React.FC<{
- title: string;
- onPress: (event: GestureResponderEvent) => void;
+ title: string
+ onPress: (event: GestureResponderEvent) => void
}> = ({ title, onPress }) => {
- const [opacity, setOpacity] = useState(1);
+ const [opacity, setOpacity] = useState(1)
return (
{title}
- );
-};
+ )
+}
-export default Button;
+export default Button
diff --git a/src/components/common/CoverArt.tsx b/src/components/common/CoverArt.tsx
index 56fe3ae..acbe84b 100644
--- a/src/components/common/CoverArt.tsx
+++ b/src/components/common/CoverArt.tsx
@@ -1,19 +1,19 @@
-import React, { useState } from 'react';
-import { ActivityIndicator, View } from 'react-native';
-import FastImage from 'react-native-fast-image';
-import colors from '../../styles/colors';
+import React, { useState } from 'react'
+import { ActivityIndicator, View } from 'react-native'
+import FastImage from 'react-native-fast-image'
+import colors from '../../styles/colors'
const CoverArt: React.FC<{
- PlaceholderComponent: () => JSX.Element;
- height: number;
- width: number;
- coverArtUri?: string;
+ PlaceholderComponent: () => JSX.Element
+ height: number
+ width: number
+ coverArtUri?: string
}> = ({ PlaceholderComponent, height, width, coverArtUri }) => {
- const [placeholderVisible, setPlaceholderVisible] = useState(false);
- const [loading, setLoading] = useState(true);
+ const [placeholderVisible, setPlaceholderVisible] = useState(false)
+ const [loading, setLoading] = useState(true)
- const indicatorSize = height > 130 ? 'large' : 'small';
- const halfIndicatorHeight = indicatorSize === 'large' ? 18 : 10;
+ const indicatorSize = height > 130 ? 'large' : 'small'
+ const halfIndicatorHeight = indicatorSize === 'large' ? 18 : 10
const Placeholder: React.FC<{ visible: boolean }> = ({ visible }) => (
- );
+ )
const Art = () => (
<>
@@ -44,15 +44,15 @@ const CoverArt: React.FC<{
}}
resizeMode={FastImage.resizeMode.contain}
onError={() => {
- setLoading(false);
- setPlaceholderVisible(true);
+ setLoading(false)
+ setPlaceholderVisible(true)
}}
onLoadEnd={() => setLoading(false)}
/>
>
- );
+ )
- return {!coverArtUri ? : };
-};
+ return {!coverArtUri ? : }
+}
-export default React.memo(CoverArt);
+export default React.memo(CoverArt)
diff --git a/src/components/common/GradientBackground.tsx b/src/components/common/GradientBackground.tsx
index 00f2ebe..3a72f08 100644
--- a/src/components/common/GradientBackground.tsx
+++ b/src/components/common/GradientBackground.tsx
@@ -1,17 +1,17 @@
-import React from 'react';
-import { useWindowDimensions, ViewStyle } from 'react-native';
-import LinearGradient from 'react-native-linear-gradient';
-import colorStyles from '../../styles/colors';
+import React from 'react'
+import { useWindowDimensions, ViewStyle } from 'react-native'
+import LinearGradient from 'react-native-linear-gradient'
+import colorStyles from '../../styles/colors'
const GradientBackground: React.FC<{
- height?: number | string;
- width?: number | string;
- position?: 'relative' | 'absolute';
- style?: ViewStyle;
- colors?: string[];
- locations?: number[];
+ height?: number | string
+ width?: number | string
+ position?: 'relative' | 'absolute'
+ style?: ViewStyle
+ colors?: string[]
+ locations?: number[]
}> = ({ height, width, position, style, colors, locations, children }) => {
- const layout = useWindowDimensions();
+ const layout = useWindowDimensions()
return (
{children}
- );
-};
+ )
+}
-export default GradientBackground;
+export default GradientBackground
diff --git a/src/components/common/GradientFlatList.tsx b/src/components/common/GradientFlatList.tsx
index 9f94178..77b035c 100644
--- a/src/components/common/GradientFlatList.tsx
+++ b/src/components/common/GradientFlatList.tsx
@@ -1,10 +1,10 @@
-import React from 'react';
-import { FlatList, FlatListProps, useWindowDimensions } from 'react-native';
-import colors from '../../styles/colors';
-import GradientBackground from './GradientBackground';
+import React from 'react'
+import { FlatList, FlatListProps, useWindowDimensions } from 'react-native'
+import colors from '../../styles/colors'
+import GradientBackground from './GradientBackground'
function GradientFlatList(props: FlatListProps) {
- const layout = useWindowDimensions();
+ const layout = useWindowDimensions()
return (
(props: FlatListProps) {
marginBottom: -layout.height,
}}
/>
- );
+ )
}
-export default GradientFlatList;
+export default GradientFlatList
diff --git a/src/components/common/GradientScrollView.tsx b/src/components/common/GradientScrollView.tsx
index 9ac1ae0..dc90bfd 100644
--- a/src/components/common/GradientScrollView.tsx
+++ b/src/components/common/GradientScrollView.tsx
@@ -1,18 +1,18 @@
-import React from 'react';
-import { ScrollView, ScrollViewProps, ViewStyle } from 'react-native';
-import colors from '../../styles/colors';
-import GradientBackground from './GradientBackground';
+import React from 'react'
+import { ScrollView, ScrollViewProps, ViewStyle } from 'react-native'
+import colors from '../../styles/colors'
+import GradientBackground from './GradientBackground'
const GradientScrollView: React.FC = props => {
- props.style = props.style || {};
- (props.style as ViewStyle).backgroundColor = colors.gradient.low;
+ props.style = props.style || {}
+ ;(props.style as ViewStyle).backgroundColor = colors.gradient.low
return (
{props.children}
- );
-};
+ )
+}
-export default GradientScrollView;
+export default GradientScrollView
diff --git a/src/components/common/ImageGradientBackground.tsx b/src/components/common/ImageGradientBackground.tsx
index 745c1b9..8537531 100644
--- a/src/components/common/ImageGradientBackground.tsx
+++ b/src/components/common/ImageGradientBackground.tsx
@@ -1,58 +1,58 @@
-import { useNavigation } from '@react-navigation/native';
-import React, { useEffect, useState } from 'react';
-import { ViewStyle } from 'react-native';
-import FastImage from 'react-native-fast-image';
-import ImageColors from 'react-native-image-colors';
-import { AndroidImageColors } from 'react-native-image-colors/lib/typescript/types';
-import colors from '../../styles/colors';
-import GradientBackground from './GradientBackground';
+import { useNavigation } from '@react-navigation/native'
+import React, { useEffect, useState } from 'react'
+import { ViewStyle } from 'react-native'
+import FastImage from 'react-native-fast-image'
+import ImageColors from 'react-native-image-colors'
+import { AndroidImageColors } from 'react-native-image-colors/lib/typescript/types'
+import colors from '../../styles/colors'
+import GradientBackground from './GradientBackground'
const ImageGradientBackground: React.FC<{
- height?: number | string;
- width?: number | string;
- position?: 'relative' | 'absolute';
- style?: ViewStyle;
- imageUri?: string;
- imageKey?: string;
+ height?: number | string
+ width?: number | string
+ position?: 'relative' | 'absolute'
+ style?: ViewStyle
+ imageUri?: string
+ imageKey?: string
}> = ({ height, width, position, style, imageUri, imageKey, children }) => {
- const [highColor, setHighColor] = useState(colors.gradient.high);
- const navigation = useNavigation();
+ const [highColor, setHighColor] = useState(colors.gradient.high)
+ const navigation = useNavigation()
useEffect(() => {
async function getColors() {
if (imageUri === undefined) {
- return;
+ return
}
- const cachedResult = ImageColors.cache.getItem(imageKey ? imageKey : imageUri);
+ const cachedResult = ImageColors.cache.getItem(imageKey ? imageKey : imageUri)
- let res: AndroidImageColors;
+ let res: AndroidImageColors
if (cachedResult) {
- res = cachedResult as AndroidImageColors;
+ res = cachedResult as AndroidImageColors
} else {
- const path = await FastImage.getCachePath({ uri: imageUri });
+ const path = await FastImage.getCachePath({ uri: imageUri })
res = (await ImageColors.getColors(path ? `file://${path}` : imageUri, {
cache: true,
key: imageKey ? imageKey : imageUri,
- })) as AndroidImageColors;
+ })) as AndroidImageColors
}
if (res.muted && res.muted !== '#000000') {
- setHighColor(res.muted);
+ setHighColor(res.muted)
} else if (res.darkMuted && res.darkMuted !== '#000000') {
- setHighColor(res.darkMuted);
+ setHighColor(res.darkMuted)
}
}
- getColors();
- }, [imageUri, imageKey]);
+ getColors()
+ }, [imageUri, imageKey])
useEffect(() => {
navigation.setOptions({
headerStyle: {
backgroundColor: highColor,
},
- });
- }, [navigation, highColor]);
+ })
+ }, [navigation, highColor])
return (
{children}
- );
-};
+ )
+}
-export default ImageGradientBackground;
+export default ImageGradientBackground
diff --git a/src/components/common/ImageGradientScrollView.tsx b/src/components/common/ImageGradientScrollView.tsx
index ebe8ab7..d34dbf6 100644
--- a/src/components/common/ImageGradientScrollView.tsx
+++ b/src/components/common/ImageGradientScrollView.tsx
@@ -1,17 +1,17 @@
-import React, { useState } from 'react';
-import { LayoutRectangle, ScrollView, ScrollViewProps } from 'react-native';
-import colors from '../../styles/colors';
-import ImageGradientBackground from './ImageGradientBackground';
+import React, { useState } from 'react'
+import { LayoutRectangle, ScrollView, ScrollViewProps } from 'react-native'
+import colors from '../../styles/colors'
+import ImageGradientBackground from './ImageGradientBackground'
const ImageGradientScrollView: React.FC = props => {
- const [layout, setLayout] = useState(undefined);
+ const [layout, setLayout] = useState(undefined)
- props.style = props.style || {};
+ props.style = props.style || {}
if (typeof props.style === 'object' && props.style !== null) {
props.style = {
...props.style,
backgroundColor: colors.gradient.low,
- };
+ }
}
return (
@@ -19,12 +19,12 @@ const ImageGradientScrollView: React.FC {
- setLayout(event.nativeEvent.layout);
+ setLayout(event.nativeEvent.layout)
}}>
{props.children}
- );
-};
+ )
+}
-export default ImageGradientScrollView;
+export default ImageGradientScrollView
diff --git a/src/components/common/TopTabContainer.tsx b/src/components/common/TopTabContainer.tsx
index b13c553..45d7c26 100644
--- a/src/components/common/TopTabContainer.tsx
+++ b/src/components/common/TopTabContainer.tsx
@@ -1,6 +1,6 @@
-import React from 'react';
-import LinearGradient from 'react-native-linear-gradient';
-import colors from '../../styles/colors';
+import React from 'react'
+import LinearGradient from 'react-native-linear-gradient'
+import colors from '../../styles/colors'
const TopTabContainer: React.FC<{}> = ({ children }) => (
= ({ children }) => (
}}>
{children}
-);
+)
-export default TopTabContainer;
+export default TopTabContainer
diff --git a/src/components/library/AlbumsTab.tsx b/src/components/library/AlbumsTab.tsx
index 0d9fdd5..e168d4c 100644
--- a/src/components/library/AlbumsTab.tsx
+++ b/src/components/library/AlbumsTab.tsx
@@ -1,21 +1,21 @@
-import { useNavigation } from '@react-navigation/native';
-import { useAtomValue } from 'jotai/utils';
-import React, { useEffect } from 'react';
-import { Pressable, Text, View } from 'react-native';
-import { Album } from '../../models/music';
-import { albumsAtom, albumsUpdatingAtom, useUpdateAlbums } from '../../state/music';
-import textStyles from '../../styles/text';
-import AlbumArt from '../common/AlbumArt';
-import GradientFlatList from '../common/GradientFlatList';
+import { useNavigation } from '@react-navigation/native'
+import { useAtomValue } from 'jotai/utils'
+import React, { useEffect } from 'react'
+import { Pressable, Text, View } from 'react-native'
+import { Album } from '../../models/music'
+import { albumsAtom, albumsUpdatingAtom, useUpdateAlbums } from '../../state/music'
+import textStyles from '../../styles/text'
+import AlbumArt from '../common/AlbumArt'
+import GradientFlatList from '../common/GradientFlatList'
const AlbumItem: React.FC<{
- id: string;
- name: string;
- artist?: string;
+ id: string
+ name: string
+ artist?: string
}> = ({ id, name, artist }) => {
- const navigation = useNavigation();
+ const navigation = useNavigation()
- const size = 125;
+ const size = 125
return (
- );
-};
-const MemoAlbumItem = React.memo(AlbumItem);
+ )
+}
+const MemoAlbumItem = React.memo(AlbumItem)
const AlbumListRenderItem: React.FC<{ item: Album }> = ({ item }) => (
-);
+)
const AlbumsList = () => {
- const albums = useAtomValue(albumsAtom);
- const updating = useAtomValue(albumsUpdatingAtom);
- const updateAlbums = useUpdateAlbums();
+ const albums = useAtomValue(albumsAtom)
+ const updating = useAtomValue(albumsUpdatingAtom)
+ const updateAlbums = useUpdateAlbums()
- const albumsList = Object.values(albums);
+ const albumsList = Object.values(albums)
useEffect(() => {
if (albumsList.length === 0) {
- updateAlbums();
+ updateAlbums()
}
- });
+ })
return (
@@ -78,13 +78,13 @@ const AlbumsList = () => {
overScrollMode="never"
/>
- );
-};
+ )
+}
const AlbumsTab = () => (
Loading...}>
-);
+)
-export default React.memo(AlbumsTab);
+export default React.memo(AlbumsTab)
diff --git a/src/components/library/ArtistsTab.tsx b/src/components/library/ArtistsTab.tsx
index b07f84b..322e1fc 100644
--- a/src/components/library/ArtistsTab.tsx
+++ b/src/components/library/ArtistsTab.tsx
@@ -1,16 +1,16 @@
-import { useNavigation } from '@react-navigation/native';
-import { useAtomValue } from 'jotai/utils';
-import React, { useEffect } from 'react';
-import { Pressable } from 'react-native';
-import { Text } from 'react-native';
-import { Artist } from '../../models/music';
-import { artistsAtom, artistsUpdatingAtom, useUpdateArtists } from '../../state/music';
-import textStyles from '../../styles/text';
-import ArtistArt from '../common/ArtistArt';
-import GradientFlatList from '../common/GradientFlatList';
+import { useNavigation } from '@react-navigation/native'
+import { useAtomValue } from 'jotai/utils'
+import React, { useEffect } from 'react'
+import { Pressable } from 'react-native'
+import { Text } from 'react-native'
+import { Artist } from '../../models/music'
+import { artistsAtom, artistsUpdatingAtom, useUpdateArtists } from '../../state/music'
+import textStyles from '../../styles/text'
+import ArtistArt from '../common/ArtistArt'
+import GradientFlatList from '../common/GradientFlatList'
const ArtistItem: React.FC<{ item: Artist }> = ({ item }) => {
- const navigation = useNavigation();
+ const navigation = useNavigation()
return (
= ({ item }) => {
{item.name}
- );
-};
+ )
+}
const ArtistItemLoader: React.FC<{ item: Artist }> = props => (
Loading...}>
-);
+)
const ArtistsList = () => {
- const artists = useAtomValue(artistsAtom);
- const updating = useAtomValue(artistsUpdatingAtom);
- const updateArtists = useUpdateArtists();
+ const artists = useAtomValue(artistsAtom)
+ const updating = useAtomValue(artistsUpdatingAtom)
+ const updateArtists = useUpdateArtists()
useEffect(() => {
if (artists.length === 0) {
- updateArtists();
+ updateArtists()
}
- });
+ })
- const renderItem: React.FC<{ item: Artist }> = ({ item }) => ;
+ const renderItem: React.FC<{ item: Artist }> = ({ item }) =>
return (
{
refreshing={updating}
overScrollMode="never"
/>
- );
-};
+ )
+}
-const ArtistsTab = () => ;
+const ArtistsTab = () =>
-export default ArtistsTab;
+export default ArtistsTab
diff --git a/src/components/library/PlaylistsTab.tsx b/src/components/library/PlaylistsTab.tsx
index 6b8a3cf..6d668c7 100644
--- a/src/components/library/PlaylistsTab.tsx
+++ b/src/components/library/PlaylistsTab.tsx
@@ -1,6 +1,6 @@
-import React from 'react';
-import GradientBackground from '../common/GradientBackground';
+import React from 'react'
+import GradientBackground from '../common/GradientBackground'
-const PlaylistsTab = () => ;
+const PlaylistsTab = () =>
-export default PlaylistsTab;
+export default PlaylistsTab
diff --git a/src/components/navigation/BottomTabNavigator.tsx b/src/components/navigation/BottomTabNavigator.tsx
index f98ba32..9326990 100644
--- a/src/components/navigation/BottomTabNavigator.tsx
+++ b/src/components/navigation/BottomTabNavigator.tsx
@@ -1,12 +1,12 @@
-import React from 'react';
-import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
-import SettingsView from '../Settings';
-import NowPlayingLayout from '../NowPlayingLayout';
-import ArtistsList from '../ArtistsList';
-import LibraryTopTabNavigator from './LibraryTopTabNavigator';
-import BottomTabBar from '../common/BottomTabBar';
+import React from 'react'
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
+import SettingsView from '../Settings'
+import NowPlayingLayout from '../NowPlayingLayout'
+import ArtistsList from '../ArtistsList'
+import LibraryTopTabNavigator from './LibraryTopTabNavigator'
+import BottomTabBar from '../common/BottomTabBar'
-const Tab = createBottomTabNavigator();
+const Tab = createBottomTabNavigator()
const BottomTabNavigator = () => {
return (
@@ -16,7 +16,7 @@ const BottomTabNavigator = () => {
- );
-};
+ )
+}
-export default BottomTabNavigator;
+export default BottomTabNavigator
diff --git a/src/components/navigation/LibraryTopTabNavigator.tsx b/src/components/navigation/LibraryTopTabNavigator.tsx
index ec47503..9b49943 100644
--- a/src/components/navigation/LibraryTopTabNavigator.tsx
+++ b/src/components/navigation/LibraryTopTabNavigator.tsx
@@ -1,17 +1,17 @@
-import React from 'react';
-import { StatusBar, View } from 'react-native';
-import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
-import AlbumsTab from '../library/AlbumsTab';
-import ArtistsTab from '../library/ArtistsTab';
-import PlaylistsTab from '../library/PlaylistsTab';
-import { createNativeStackNavigator, NativeStackNavigationProp } from 'react-native-screens/native-stack';
-import AlbumView from '../common/AlbumView';
-import { RouteProp } from '@react-navigation/native';
-import text from '../../styles/text';
-import colors from '../../styles/colors';
-import ArtistView from '../common/ArtistView';
+import React from 'react'
+import { StatusBar, View } from 'react-native'
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs'
+import AlbumsTab from '../library/AlbumsTab'
+import ArtistsTab from '../library/ArtistsTab'
+import PlaylistsTab from '../library/PlaylistsTab'
+import { createNativeStackNavigator, NativeStackNavigationProp } from 'react-native-screens/native-stack'
+import AlbumView from '../common/AlbumView'
+import { RouteProp } from '@react-navigation/native'
+import text from '../../styles/text'
+import colors from '../../styles/colors'
+import ArtistView from '../common/ArtistView'
-const Tab = createMaterialTopTabNavigator();
+const Tab = createMaterialTopTabNavigator()
const LibraryTopTabNavigator = () => (
(
-);
+)
type LibraryStackParamList = {
- LibraryTopTabs: undefined;
- AlbumView: { id: string; title: string };
- ArtistView: { id: string; title: string };
-};
+ LibraryTopTabs: undefined
+ AlbumView: { id: string; title: string }
+ ArtistView: { id: string; title: string }
+}
-type AlbumScreenNavigationProp = NativeStackNavigationProp;
-type AlbumScreenRouteProp = RouteProp;
+type AlbumScreenNavigationProp = NativeStackNavigationProp
+type AlbumScreenRouteProp = RouteProp
type AlbumScreenProps = {
- route: AlbumScreenRouteProp;
- navigation: AlbumScreenNavigationProp;
-};
+ route: AlbumScreenRouteProp
+ navigation: AlbumScreenNavigationProp
+}
const AlbumScreen: React.FC = ({ route }) => (
-);
+)
-type ArtistScreenNavigationProp = NativeStackNavigationProp;
-type ArtistScreenRouteProp = RouteProp;
+type ArtistScreenNavigationProp = NativeStackNavigationProp
+type ArtistScreenRouteProp = RouteProp
type ArtistScreenProps = {
- route: ArtistScreenRouteProp;
- navigation: ArtistScreenNavigationProp;
-};
+ route: ArtistScreenRouteProp
+ navigation: ArtistScreenNavigationProp
+}
const ArtistScreen: React.FC = ({ route }) => (
-);
+)
-const Stack = createNativeStackNavigator();
+const Stack = createNativeStackNavigator()
const itemScreenOptions = {
title: '',
@@ -78,7 +78,7 @@ const itemScreenOptions = {
headerTitleStyle: {
...text.header,
} as any,
-};
+}
const LibraryStackNavigator = () => (
@@ -88,6 +88,6 @@ const LibraryStackNavigator = () => (
-);
+)
-export default LibraryStackNavigator;
+export default LibraryStackNavigator
diff --git a/src/components/navigation/RootNavigator.tsx b/src/components/navigation/RootNavigator.tsx
index be61c7e..8aee1ff 100644
--- a/src/components/navigation/RootNavigator.tsx
+++ b/src/components/navigation/RootNavigator.tsx
@@ -1,9 +1,9 @@
-import React from 'react';
-import { createNativeStackNavigator } from 'react-native-screens/native-stack';
-import NowPlayingLayout from '../NowPlayingLayout';
-import BottomTabNavigator from './BottomTabNavigator';
+import React from 'react'
+import { createNativeStackNavigator } from 'react-native-screens/native-stack'
+import NowPlayingLayout from '../NowPlayingLayout'
+import BottomTabNavigator from './BottomTabNavigator'
-const RootStack = createNativeStackNavigator();
+const RootStack = createNativeStackNavigator()
const RootNavigator = () => (
(
-);
+)
-export default RootNavigator;
+export default RootNavigator
diff --git a/src/hooks/subsonic.ts b/src/hooks/subsonic.ts
index fccbec7..296354b 100644
--- a/src/hooks/subsonic.ts
+++ b/src/hooks/subsonic.ts
@@ -1,14 +1,14 @@
-import { useAtomValue } from 'jotai/utils';
-import { activeServerAtom } from '../state/settings';
-import { SubsonicApiClient } from '../subsonic/api';
+import { useAtomValue } from 'jotai/utils'
+import { activeServerAtom } from '../state/settings'
+import { SubsonicApiClient } from '../subsonic/api'
export const useSubsonicApi = () => {
- const activeServer = useAtomValue(activeServerAtom);
+ const activeServer = useAtomValue(activeServerAtom)
return () => {
if (!activeServer) {
- return undefined;
+ return undefined
}
- return new SubsonicApiClient(activeServer);
- };
-};
+ return new SubsonicApiClient(activeServer)
+ }
+}
diff --git a/src/hooks/trackplayer.ts b/src/hooks/trackplayer.ts
index 059b247..74d7d72 100644
--- a/src/hooks/trackplayer.ts
+++ b/src/hooks/trackplayer.ts
@@ -1,7 +1,7 @@
-import { useUpdateAtom } from 'jotai/utils';
-import TrackPlayer, { Track } from 'react-native-track-player';
-import { Song } from '../models/music';
-import { currentQueueNameAtom, currentTrackAtom } from '../state/trackplayer';
+import { useUpdateAtom } from 'jotai/utils'
+import TrackPlayer, { Track } from 'react-native-track-player'
+import { Song } from '../models/music'
+import { currentQueueNameAtom, currentTrackAtom } from '../state/trackplayer'
function mapSongToTrack(song: Song, queueName: string): Track {
return {
@@ -13,39 +13,39 @@ function mapSongToTrack(song: Song, queueName: string): Track {
artwork: song.coverArtUri,
artworkThumb: song.coverArtThumbUri,
duration: song.duration,
- };
+ }
}
export const useSetQueue = () => {
- const setCurrentTrack = useUpdateAtom(currentTrackAtom);
- const setCurrentQueueName = useUpdateAtom(currentQueueNameAtom);
+ const setCurrentTrack = useUpdateAtom(currentTrackAtom)
+ const setCurrentQueueName = useUpdateAtom(currentQueueNameAtom)
return async (songs: Song[], name: string, playId?: string) => {
- await TrackPlayer.reset();
- const tracks = songs.map(s => mapSongToTrack(s, name));
+ await TrackPlayer.reset()
+ const tracks = songs.map(s => mapSongToTrack(s, name))
- setCurrentQueueName(name);
+ setCurrentQueueName(name)
if (playId) {
- setCurrentTrack(tracks.find(t => t.id === playId));
+ setCurrentTrack(tracks.find(t => t.id === playId))
}
if (!playId) {
- await TrackPlayer.add(tracks);
+ await TrackPlayer.add(tracks)
} else if (playId === tracks[0].id) {
- await TrackPlayer.add(tracks);
- await TrackPlayer.play();
+ await TrackPlayer.add(tracks)
+ await TrackPlayer.play()
} else {
- const playIndex = tracks.findIndex(t => t.id === playId);
- const tracks1 = tracks.slice(0, playIndex);
- const tracks2 = tracks.slice(playIndex);
+ const playIndex = tracks.findIndex(t => t.id === playId)
+ const tracks1 = tracks.slice(0, playIndex)
+ const tracks2 = tracks.slice(playIndex)
- await TrackPlayer.add(tracks2);
- await TrackPlayer.play();
+ await TrackPlayer.add(tracks2)
+ await TrackPlayer.play()
- await TrackPlayer.add(tracks1, 0);
+ await TrackPlayer.add(tracks1, 0)
// const queue = await TrackPlayer.getQueue();
// console.log(`queue: ${JSON.stringify(queue.map(x => x.title))}`);
}
- };
-};
+ }
+}
diff --git a/src/models/music.ts b/src/models/music.ts
index 9d18c1f..9d9d258 100644
--- a/src/models/music.ts
+++ b/src/models/music.ts
@@ -1,95 +1,95 @@
export interface Artist {
- id: string;
- name: string;
- starred?: Date;
+ id: string
+ name: string
+ starred?: Date
}
export interface ArtistInfo extends Artist {
- albums: Album[];
+ albums: Album[]
- mediumImageUrl?: string;
- largeImageUrl?: string;
- coverArtUris: string[];
+ mediumImageUrl?: string
+ largeImageUrl?: string
+ coverArtUris: string[]
}
export interface ArtistArt {
- uri?: string;
- coverArtUris: string[];
+ uri?: string
+ coverArtUris: string[]
}
export interface Album {
- id: string;
- artistId?: string;
- artist?: string;
- name: string;
- starred?: Date;
- coverArt?: string;
- coverArtUri?: string;
- coverArtThumbUri?: string;
- year?: number;
+ id: string
+ artistId?: string
+ artist?: string
+ name: string
+ starred?: Date
+ coverArt?: string
+ coverArtUri?: string
+ coverArtThumbUri?: string
+ year?: number
}
export interface AlbumArt {
- uri?: string;
- thumbUri?: string;
+ uri?: string
+ thumbUri?: string
}
export interface AlbumWithSongs extends Album {
- songs: Song[];
+ songs: Song[]
}
export interface Song {
- id: string;
- album?: string;
- artist?: string;
- title: string;
- track?: number;
- year?: number;
- genre?: string;
- coverArt?: string;
- size?: number;
- contentType?: string;
- suffix?: string;
- duration?: number;
- bitRate?: number;
- userRating?: number;
- averageRating?: number;
- playCount?: number;
- discNumber?: number;
- created?: Date;
- starred?: Date;
+ id: string
+ album?: string
+ artist?: string
+ title: string
+ track?: number
+ year?: number
+ genre?: string
+ coverArt?: string
+ size?: number
+ contentType?: string
+ suffix?: string
+ duration?: number
+ bitRate?: number
+ userRating?: number
+ averageRating?: number
+ playCount?: number
+ discNumber?: number
+ created?: Date
+ starred?: Date
- streamUri: string;
- coverArtUri?: string;
- coverArtThumbUri?: string;
+ streamUri: string
+ coverArtUri?: string
+ coverArtThumbUri?: string
}
export type DownloadedSong = {
- id: string;
- type: 'song';
- name: string;
- album: string;
- artist: string;
-};
+ id: string
+ type: 'song'
+ name: string
+ album: string
+ artist: string
+}
export type DownloadedAlbum = {
- id: string;
- type: 'album';
- songs: string[];
- name: string;
- artist: string;
-};
+ id: string
+ type: 'album'
+ songs: string[]
+ name: string
+ artist: string
+}
export type DownloadedArtist = {
- id: string;
- type: 'artist';
- songs: string[];
- name: string;
-};
+ id: string
+ type: 'artist'
+ songs: string[]
+ name: string
+}
export type DownloadedPlaylist = {
- id: string;
- type: 'playlist';
- songs: string[];
- name: string;
-};
+ id: string
+ type: 'playlist'
+ songs: string[]
+ name: string
+}
diff --git a/src/models/settings.ts b/src/models/settings.ts
index a8a5b42..3904ad7 100644
--- a/src/models/settings.ts
+++ b/src/models/settings.ts
@@ -1,12 +1,12 @@
export interface Server {
- id: string;
- address: string;
- username: string;
- token: string;
- salt: string;
+ id: string
+ address: string
+ username: string
+ token: string
+ salt: string
}
export interface AppSettings {
- servers: Server[];
- activeServer?: string;
+ servers: Server[]
+ activeServer?: string
}
diff --git a/src/paths.ts b/src/paths.ts
index 2b15207..a9066ca 100644
--- a/src/paths.ts
+++ b/src/paths.ts
@@ -1,7 +1,7 @@
-import RNFS from 'react-native-fs';
+import RNFS from 'react-native-fs'
export default {
imageCache: `${RNFS.DocumentDirectoryPath}/image_cache`,
songCache: `${RNFS.DocumentDirectoryPath}/song_cache`,
songs: `${RNFS.DocumentDirectoryPath}/songs`,
-};
+}
diff --git a/src/playback/service.ts b/src/playback/service.ts
index e313eeb..5bc3603 100644
--- a/src/playback/service.ts
+++ b/src/playback/service.ts
@@ -1,23 +1,23 @@
-import TrackPlayer, { Event } from 'react-native-track-player';
+import TrackPlayer, { Event } from 'react-native-track-player'
module.exports = async function () {
- TrackPlayer.addEventListener(Event.RemotePlay, () => TrackPlayer.play());
- TrackPlayer.addEventListener(Event.RemotePause, () => TrackPlayer.pause());
- TrackPlayer.addEventListener(Event.RemoteStop, () => TrackPlayer.destroy());
+ TrackPlayer.addEventListener(Event.RemotePlay, () => TrackPlayer.play())
+ TrackPlayer.addEventListener(Event.RemotePause, () => TrackPlayer.pause())
+ TrackPlayer.addEventListener(Event.RemoteStop, () => TrackPlayer.destroy())
TrackPlayer.addEventListener(Event.RemoteDuck, data => {
if (data.permanent) {
- TrackPlayer.stop();
- return;
+ TrackPlayer.stop()
+ return
}
if (data.paused) {
- TrackPlayer.pause();
+ TrackPlayer.pause()
} else {
- TrackPlayer.play();
+ TrackPlayer.play()
}
- });
+ })
- TrackPlayer.addEventListener(Event.RemoteNext, () => TrackPlayer.skipToNext().catch(() => {}));
- TrackPlayer.addEventListener(Event.RemotePrevious, () => TrackPlayer.skipToPrevious().catch(() => {}));
-};
+ TrackPlayer.addEventListener(Event.RemoteNext, () => TrackPlayer.skipToNext().catch(() => {}))
+ TrackPlayer.addEventListener(Event.RemotePrevious, () => TrackPlayer.skipToPrevious().catch(() => {}))
+}
diff --git a/src/state/music.ts b/src/state/music.ts
index 4a98ad3..864ea3e 100644
--- a/src/state/music.ts
+++ b/src/state/music.ts
@@ -1,31 +1,31 @@
-import { atom, useAtom } from 'jotai';
-import { atomFamily, useAtomValue, useUpdateAtom } from 'jotai/utils';
-import { Album, AlbumArt, AlbumWithSongs, Artist, ArtistArt, ArtistInfo, Song } from '../models/music';
-import { SubsonicApiClient } from '../subsonic/api';
-import { AlbumID3Element, ArtistInfo2Element, ChildElement } from '../subsonic/elements';
-import { GetArtistResponse } from '../subsonic/responses';
-import { activeServerAtom } from './settings';
+import { atom, useAtom } from 'jotai'
+import { atomFamily, useAtomValue, useUpdateAtom } from 'jotai/utils'
+import { Album, AlbumArt, AlbumWithSongs, Artist, ArtistArt, ArtistInfo, Song } from '../models/music'
+import { SubsonicApiClient } from '../subsonic/api'
+import { AlbumID3Element, ArtistInfo2Element, ChildElement } from '../subsonic/elements'
+import { GetArtistResponse } from '../subsonic/responses'
+import { activeServerAtom } from './settings'
-export const artistsAtom = atom([]);
-export const artistsUpdatingAtom = atom(false);
+export const artistsAtom = atom([])
+export const artistsUpdatingAtom = atom(false)
export const useUpdateArtists = () => {
- const server = useAtomValue(activeServerAtom);
- const [updating, setUpdating] = useAtom(artistsUpdatingAtom);
- const setArtists = useUpdateAtom(artistsAtom);
+ const server = useAtomValue(activeServerAtom)
+ const [updating, setUpdating] = useAtom(artistsUpdatingAtom)
+ const setArtists = useUpdateAtom(artistsAtom)
if (!server) {
- return () => Promise.resolve();
+ return () => Promise.resolve()
}
return async () => {
if (updating) {
- return;
+ return
}
- setUpdating(true);
+ setUpdating(true)
- const client = new SubsonicApiClient(server);
- const response = await client.getArtists();
+ const client = new SubsonicApiClient(server)
+ const response = await client.getArtists()
setArtists(
response.data.artists.map(x => ({
@@ -33,146 +33,146 @@ export const useUpdateArtists = () => {
name: x.name,
starred: x.starred,
})),
- );
- setUpdating(false);
- };
-};
+ )
+ setUpdating(false)
+ }
+}
-export const albumsAtom = atom>({});
-export const albumsUpdatingAtom = atom(false);
+export const albumsAtom = atom>({})
+export const albumsUpdatingAtom = atom(false)
export const useUpdateAlbums = () => {
- const server = useAtomValue(activeServerAtom);
- const [updating, setUpdating] = useAtom(albumsUpdatingAtom);
- const setAlbums = useUpdateAtom(albumsAtom);
+ const server = useAtomValue(activeServerAtom)
+ const [updating, setUpdating] = useAtom(albumsUpdatingAtom)
+ const setAlbums = useUpdateAtom(albumsAtom)
if (!server) {
- return () => Promise.resolve();
+ return () => Promise.resolve()
}
return async () => {
if (updating) {
- return;
+ return
}
- setUpdating(true);
+ setUpdating(true)
- const client = new SubsonicApiClient(server);
- const response = await client.getAlbumList2({ type: 'alphabeticalByArtist', size: 500 });
+ const client = new SubsonicApiClient(server)
+ const response = await client.getAlbumList2({ type: 'alphabeticalByArtist', size: 500 })
setAlbums(
response.data.albums.reduce((acc, next) => {
- const album = mapAlbumID3(next, client);
- acc[album.id] = album;
- return acc;
+ const album = mapAlbumID3(next, client)
+ acc[album.id] = album
+ return acc
}, {} as Record),
- );
- setUpdating(false);
- };
-};
+ )
+ setUpdating(false)
+ }
+}
export const albumAtomFamily = atomFamily((id: string) =>
atom(async get => {
- const server = get(activeServerAtom);
+ const server = get(activeServerAtom)
if (!server) {
- return undefined;
+ return undefined
}
- const client = new SubsonicApiClient(server);
- const response = await client.getAlbum({ id });
- return mapAlbumID3WithSongs(response.data.album, response.data.songs, client);
+ const client = new SubsonicApiClient(server)
+ const response = await client.getAlbum({ id })
+ return mapAlbumID3WithSongs(response.data.album, response.data.songs, client)
}),
-);
+)
export const albumArtAtomFamily = atomFamily((id: string) =>
atom(async get => {
- const server = get(activeServerAtom);
+ const server = get(activeServerAtom)
if (!server) {
- return undefined;
+ return undefined
}
- const albums = get(albumsAtom);
- const album = id in albums ? albums[id] : undefined;
+ const albums = get(albumsAtom)
+ const album = id in albums ? albums[id] : undefined
if (!album) {
- return undefined;
+ return undefined
}
- const client = new SubsonicApiClient(server);
+ const client = new SubsonicApiClient(server)
return {
uri: album.coverArt ? client.getCoverArtUri({ id: album.coverArt }) : undefined,
thumbUri: album.coverArt ? client.getCoverArtUri({ id: album.coverArt, size: '256' }) : undefined,
- };
+ }
}),
-);
+)
export const artistInfoAtomFamily = atomFamily((id: string) =>
atom(async get => {
- const server = get(activeServerAtom);
+ const server = get(activeServerAtom)
if (!server) {
- return undefined;
+ return undefined
}
- const client = new SubsonicApiClient(server);
+ const client = new SubsonicApiClient(server)
const [artistResponse, artistInfoResponse] = await Promise.all([
client.getArtist({ id }),
client.getArtistInfo2({ id }),
- ]);
- return mapArtistInfo(artistResponse.data, artistInfoResponse.data.artistInfo, client);
+ ])
+ return mapArtistInfo(artistResponse.data, artistInfoResponse.data.artistInfo, client)
}),
-);
+)
export const artistArtAtomFamily = atomFamily((id: string) =>
atom(async get => {
- const artistInfo = get(artistInfoAtomFamily(id));
+ const artistInfo = get(artistInfoAtomFamily(id))
if (!artistInfo) {
- return undefined;
+ return undefined
}
const coverArtUris = artistInfo.albums
.filter(a => a.coverArtThumbUri !== undefined)
.sort((a, b) => {
if (b.year && a.year) {
- return b.year - a.year;
+ return b.year - a.year
} else {
- return a.name.localeCompare(b.name);
+ return a.name.localeCompare(b.name)
}
})
- .map(a => a.coverArtThumbUri) as string[];
+ .map(a => a.coverArtThumbUri) as string[]
return {
coverArtUris,
uri: artistInfo.mediumImageUrl,
- };
+ }
}),
-);
+)
function mapArtistInfo(
artistResponse: GetArtistResponse,
artistInfo: ArtistInfo2Element,
client: SubsonicApiClient,
): ArtistInfo {
- const info = { ...artistInfo } as any;
- delete info.similarArtists;
+ const info = { ...artistInfo } as any
+ delete info.similarArtists
- const { artist, albums } = artistResponse;
+ const { artist, albums } = artistResponse
- const mappedAlbums = albums.map(a => mapAlbumID3(a, client));
+ const mappedAlbums = albums.map(a => mapAlbumID3(a, client))
const coverArtUris = mappedAlbums
.sort((a, b) => {
if (a.year && b.year) {
- return a.year - b.year;
+ return a.year - b.year
} else {
- return a.name.localeCompare(b.name) - 9000;
+ return a.name.localeCompare(b.name) - 9000
}
})
- .map(a => a.coverArtThumbUri);
+ .map(a => a.coverArtThumbUri)
return {
...artist,
...info,
albums: mappedAlbums,
coverArtUris,
- };
+ }
}
function mapAlbumID3(album: AlbumID3Element, client: SubsonicApiClient): Album {
@@ -180,7 +180,7 @@ function mapAlbumID3(album: AlbumID3Element, client: SubsonicApiClient): Album {
...album,
coverArtUri: album.coverArt ? client.getCoverArtUri({ id: album.coverArt }) : undefined,
coverArtThumbUri: album.coverArt ? client.getCoverArtUri({ id: album.coverArt, size: '256' }) : undefined,
- };
+ }
}
function mapChildToSong(child: ChildElement, client: SubsonicApiClient): Song {
@@ -189,7 +189,7 @@ function mapChildToSong(child: ChildElement, client: SubsonicApiClient): Song {
streamUri: client.streamUri({ id: child.id }),
coverArtUri: child.coverArt ? client.getCoverArtUri({ id: child.coverArt }) : undefined,
coverArtThumbUri: child.coverArt ? client.getCoverArtUri({ id: child.coverArt, size: '256' }) : undefined,
- };
+ }
}
function mapAlbumID3WithSongs(
@@ -200,5 +200,5 @@ function mapAlbumID3WithSongs(
return {
...mapAlbumID3(album, client),
songs: songs.map(s => mapChildToSong(s, client)),
- };
+ }
}
diff --git a/src/state/settings.ts b/src/state/settings.ts
index 38f7195..2f0c474 100644
--- a/src/state/settings.ts
+++ b/src/state/settings.ts
@@ -1,12 +1,12 @@
-import { atom } from 'jotai';
-import { AppSettings } from '../models/settings';
-import atomWithAsyncStorage from '../storage/atomWithAsyncStorage';
+import { atom } from 'jotai'
+import { AppSettings } from '../models/settings'
+import atomWithAsyncStorage from '../storage/atomWithAsyncStorage'
export const appSettingsAtom = atomWithAsyncStorage('@appSettings', {
servers: [],
-});
+})
export const activeServerAtom = atom(get => {
- const appSettings = get(appSettingsAtom);
- return appSettings.servers.find(x => x.id === appSettings.activeServer);
-});
+ const appSettings = get(appSettingsAtom)
+ return appSettings.servers.find(x => x.id === appSettings.activeServer)
+})
diff --git a/src/state/trackplayer.ts b/src/state/trackplayer.ts
index 236c4cd..fd59fa4 100644
--- a/src/state/trackplayer.ts
+++ b/src/state/trackplayer.ts
@@ -1,37 +1,37 @@
-import { atom } from 'jotai';
-import { State, Track } from 'react-native-track-player';
-import equal from 'fast-deep-equal';
+import { atom } from 'jotai'
+import { State, Track } from 'react-native-track-player'
+import equal from 'fast-deep-equal'
-type OptionalTrack = Track | undefined;
+type OptionalTrack = Track | undefined
-const currentTrack = atom(undefined);
+const currentTrack = atom(undefined)
export const currentTrackAtom = atom(
get => get(currentTrack),
(get, set, value) => {
if (!equal(get(currentTrack), value)) {
- set(currentTrack, value);
+ set(currentTrack, value)
}
},
-);
+)
-type OptionalString = string | undefined;
+type OptionalString = string | undefined
-const currentQueueName = atom(undefined);
+const currentQueueName = atom(undefined)
export const currentQueueNameAtom = atom(
get => get(currentQueueName),
(get, set, value) => {
if (get(currentQueueName) !== value) {
- set(currentQueueName, value);
+ set(currentQueueName, value)
}
},
-);
+)
-const playerState = atom(State.None);
+const playerState = atom(State.None)
export const playerStateAtom = atom(
get => get(playerState),
(get, set, value) => {
if (get(playerState) !== value) {
- set(playerState, value);
+ set(playerState, value)
}
},
-);
+)
diff --git a/src/storage/asyncstorage.ts b/src/storage/asyncstorage.ts
index 15908e8..66a8899 100644
--- a/src/storage/asyncstorage.ts
+++ b/src/storage/asyncstorage.ts
@@ -1,54 +1,54 @@
-import AsyncStorage from '@react-native-async-storage/async-storage';
+import AsyncStorage from '@react-native-async-storage/async-storage'
export async function getItem(key: string): Promise {
try {
- const item = await AsyncStorage.getItem(key);
- return item ? JSON.parse(item) : null;
+ const item = await AsyncStorage.getItem(key)
+ return item ? JSON.parse(item) : null
} catch (e) {
- console.error(`getItem error (key: ${key})`, e);
- return null;
+ console.error(`getItem error (key: ${key})`, e)
+ return null
}
}
export async function multiGet(keys: string[]): Promise<[string, any | null][]> {
try {
- const items = await AsyncStorage.multiGet(keys);
- return items.map(x => [x[0], x[1] ? JSON.parse(x[1]) : null]);
+ const items = await AsyncStorage.multiGet(keys)
+ return items.map(x => [x[0], x[1] ? JSON.parse(x[1]) : null])
} catch (e) {
- console.error('multiGet error', e);
- return [];
+ console.error('multiGet error', e)
+ return []
}
}
export async function setItem(key: string, item: any): Promise {
try {
- await AsyncStorage.setItem(key, JSON.stringify(item));
+ await AsyncStorage.setItem(key, JSON.stringify(item))
} catch (e) {
- console.error(`setItem error (key: ${key})`, e);
+ console.error(`setItem error (key: ${key})`, e)
}
}
export async function multiSet(items: string[][]): Promise {
try {
- await AsyncStorage.multiSet(items.map(x => [x[0], JSON.stringify(x[1])]));
+ await AsyncStorage.multiSet(items.map(x => [x[0], JSON.stringify(x[1])]))
} catch (e) {
- console.error('multiSet error', e);
+ console.error('multiSet error', e)
}
}
export async function getAllKeys(): Promise {
try {
- return await AsyncStorage.getAllKeys();
+ return await AsyncStorage.getAllKeys()
} catch (e) {
- console.error('getAllKeys error', e);
- return [];
+ console.error('getAllKeys error', e)
+ return []
}
}
export async function multiRemove(keys: string[]): Promise {
try {
- await AsyncStorage.multiRemove(keys);
+ await AsyncStorage.multiRemove(keys)
} catch (e) {
- console.error('multiRemove error', e);
+ console.error('multiRemove error', e)
}
}
diff --git a/src/storage/atomWithAsyncStorage.ts b/src/storage/atomWithAsyncStorage.ts
index 7dcfa22..dc1d496 100644
--- a/src/storage/atomWithAsyncStorage.ts
+++ b/src/storage/atomWithAsyncStorage.ts
@@ -1,10 +1,10 @@
-import { atomWithStorage } from 'jotai/utils';
-import { getItem, setItem } from './asyncstorage';
+import { atomWithStorage } from 'jotai/utils'
+import { getItem, setItem } from './asyncstorage'
export default (key: string, defaultValue: T) => {
return atomWithStorage(key, defaultValue, {
getItem: async () => (await getItem(key)) || defaultValue,
setItem: setItem,
delayInit: true,
- });
-};
+ })
+}
diff --git a/src/storage/music.ts b/src/storage/music.ts
index 7eb6150..9b8ebd6 100644
--- a/src/storage/music.ts
+++ b/src/storage/music.ts
@@ -1,26 +1,26 @@
-import { DownloadedSong } from '../models/music';
-import { getItem, multiGet, multiSet } from './asyncstorage';
+import { DownloadedSong } from '../models/music'
+import { getItem, multiGet, multiSet } from './asyncstorage'
const key = {
downloadedSongKeys: '@downloadedSongKeys',
downloadedAlbumKeys: '@downloadedAlbumKeys',
downloadedArtistKeys: '@downloadedArtistKeys',
downloadedPlaylistKeys: '@downloadedPlaylistKeys',
-};
+}
export async function getDownloadedSongs(): Promise {
- const keysItem = await getItem(key.downloadedSongKeys);
- const keys: string[] = keysItem ? JSON.parse(keysItem) : [];
+ const keysItem = await getItem(key.downloadedSongKeys)
+ const keys: string[] = keysItem ? JSON.parse(keysItem) : []
- const items = await multiGet(keys);
+ const items = await multiGet(keys)
return items.map(x => {
- const parsed = JSON.parse(x[1] as string);
+ const parsed = JSON.parse(x[1] as string)
return {
id: x[0],
type: 'song',
...parsed,
- };
- });
+ }
+ })
}
export async function setDownloadedSongs(items: DownloadedSong[]): Promise {
@@ -34,5 +34,5 @@ export async function setDownloadedSongs(items: DownloadedSong[]): Promise
artist: x.artist,
}),
]),
- ]);
+ ])
}
diff --git a/src/styles/colors.ts b/src/styles/colors.ts
index 926bfa9..50bd00d 100644
--- a/src/styles/colors.ts
+++ b/src/styles/colors.ts
@@ -10,4 +10,4 @@ export default {
},
accent: '#b134db',
accentLow: '#511c63',
-};
+}
diff --git a/src/styles/text.ts b/src/styles/text.ts
index cefc3a1..bf88119 100644
--- a/src/styles/text.ts
+++ b/src/styles/text.ts
@@ -1,62 +1,62 @@
-import { TextStyle } from 'react-native';
-import colors from './colors';
+import { TextStyle } from 'react-native'
+import colors from './colors'
-const fontRegular = 'Metropolis-Regular';
-const fontSemiBold = 'Metropolis-SemiBold';
-const fontBold = 'Metropolis-Bold';
+const fontRegular = 'Metropolis-Regular'
+const fontSemiBold = 'Metropolis-SemiBold'
+const fontBold = 'Metropolis-Bold'
const paragraph: TextStyle = {
fontFamily: fontRegular,
fontSize: 16,
color: colors.text.primary,
-};
+}
const header: TextStyle = {
...paragraph,
fontSize: 18,
fontFamily: fontSemiBold,
-};
+}
const title: TextStyle = {
...paragraph,
fontSize: 24,
fontFamily: fontBold,
-};
+}
const itemTitle: TextStyle = {
...paragraph,
fontSize: 13,
fontFamily: fontSemiBold,
-};
+}
const itemSubtitle: TextStyle = {
...paragraph,
fontSize: 12,
color: colors.text.secondary,
-};
+}
const songListTitle: TextStyle = {
...paragraph,
fontSize: 16,
fontFamily: fontSemiBold,
-};
+}
const songListSubtitle: TextStyle = {
...paragraph,
fontSize: 14,
color: colors.text.secondary,
-};
+}
const xsmall: TextStyle = {
...paragraph,
fontSize: 10,
-};
+}
const button: TextStyle = {
...paragraph,
fontSize: 15,
fontFamily: fontBold,
-};
+}
export default {
paragraph,
@@ -68,4 +68,4 @@ export default {
songListSubtitle,
xsmall,
button,
-};
+}
diff --git a/src/subsonic/api.ts b/src/subsonic/api.ts
index cc15e64..bb209ec 100644
--- a/src/subsonic/api.ts
+++ b/src/subsonic/api.ts
@@ -1,5 +1,5 @@
-import { DOMParser } from 'xmldom';
-import RNFS from 'react-native-fs';
+import { DOMParser } from 'xmldom'
+import RNFS from 'react-native-fs'
import {
GetAlbumList2Params,
GetAlbumListParams,
@@ -11,7 +11,7 @@ import {
GetIndexesParams,
GetMusicDirectoryParams,
StreamParams,
-} from './params';
+} from './params'
import {
GetAlbumList2Response,
GetAlbumListResponse,
@@ -23,127 +23,127 @@ import {
GetIndexesResponse,
GetMusicDirectoryResponse,
SubsonicResponse,
-} from './responses';
-import { Server } from '../models/settings';
-import paths from '../paths';
+} from './responses'
+import { Server } from '../models/settings'
+import paths from '../paths'
export class SubsonicApiError extends Error {
- method: string;
- code: string;
+ method: string
+ code: string
constructor(method: string, xml: Document) {
- const errorElement = xml.getElementsByTagName('error')[0];
+ const errorElement = xml.getElementsByTagName('error')[0]
- super(errorElement.getAttribute('message') as string);
+ super(errorElement.getAttribute('message') as string)
- this.name = method;
- this.method = method;
- this.code = errorElement.getAttribute('code') as string;
+ this.name = method
+ this.method = method
+ this.code = errorElement.getAttribute('code') as string
}
}
-type QueuePromise = () => Promise;
+type QueuePromise = () => Promise
class Queue {
- maxSimultaneously: number;
+ maxSimultaneously: number
- private active = 0;
- private queue: QueuePromise[] = [];
+ private active = 0
+ private queue: QueuePromise[] = []
constructor(maxSimultaneously = 1) {
- this.maxSimultaneously = maxSimultaneously;
+ this.maxSimultaneously = maxSimultaneously
}
async enqueue(func: QueuePromise) {
if (++this.active > this.maxSimultaneously) {
- await new Promise(resolve => this.queue.push(resolve as QueuePromise));
+ await new Promise(resolve => this.queue.push(resolve as QueuePromise))
}
try {
- return await func();
+ return await func()
} catch (err) {
- throw err;
+ throw err
} finally {
- this.active--;
+ this.active--
if (this.queue.length) {
- (this.queue.shift() as QueuePromise)();
+ this.queue.shift()?.()
}
}
}
}
-const downloadQueue = new Queue(1);
+const downloadQueue = new Queue(1)
export class SubsonicApiClient {
- address: string;
- username: string;
+ address: string
+ username: string
- private params: URLSearchParams;
+ private params: URLSearchParams
constructor(server: Server) {
- this.address = server.address;
- this.username = server.username;
+ this.address = server.address
+ this.username = server.username
- this.params = new URLSearchParams();
- this.params.append('u', server.username);
- this.params.append('t', server.token);
- this.params.append('s', server.salt);
- this.params.append('v', '1.15.0');
- this.params.append('c', 'subsonify-cool-unique-app-string');
+ this.params = new URLSearchParams()
+ this.params.append('u', server.username)
+ this.params.append('t', server.token)
+ this.params.append('s', server.salt)
+ this.params.append('v', '1.15.0')
+ this.params.append('c', 'subsonify-cool-unique-app-string')
}
private buildUrl(method: string, params?: { [key: string]: any }): string {
- let query = this.params.toString();
+ let query = this.params.toString()
if (params) {
- const urlParams = this.obj2Params(params);
+ const urlParams = this.obj2Params(params)
if (urlParams) {
- query += '&' + urlParams.toString();
+ query += '&' + urlParams.toString()
}
}
- const url = `${this.address}/rest/${method}?${query}`;
+ const url = `${this.address}/rest/${method}?${query}`
// console.log(url);
- return url;
+ return url
}
private async apiDownload(method: string, path: string, params?: { [key: string]: any }): Promise {
const download = RNFS.downloadFile({
fromUrl: this.buildUrl(method, params),
toFile: path,
- }).promise;
+ }).promise
- await downloadQueue.enqueue(() => download);
- await downloadQueue.enqueue(() => new Promise(resolve => setTimeout(resolve, 100)));
+ await downloadQueue.enqueue(() => download)
+ await downloadQueue.enqueue(() => new Promise(resolve => setTimeout(resolve, 100)))
- return path;
+ return path
}
private async apiGetXml(method: string, params?: { [key: string]: any }): Promise {
- const response = await fetch(this.buildUrl(method, params));
- const text = await response.text();
+ const response = await fetch(this.buildUrl(method, params))
+ const text = await response.text()
// console.log(text);
- const xml = new DOMParser().parseFromString(text);
+ const xml = new DOMParser().parseFromString(text)
if (xml.documentElement.getAttribute('status') !== 'ok') {
- throw new SubsonicApiError(method, xml);
+ throw new SubsonicApiError(method, xml)
}
- return xml;
+ return xml
}
private obj2Params(obj: { [key: string]: any }): URLSearchParams | undefined {
- const keys = Object.keys(obj);
+ const keys = Object.keys(obj)
if (keys.length === 0) {
- return undefined;
+ return undefined
}
- const params = new URLSearchParams();
+ const params = new URLSearchParams()
for (const key of keys) {
- params.append(key, String(obj[key]));
+ params.append(key, String(obj[key]))
}
- return params;
+ return params
}
//
@@ -151,8 +151,8 @@ export class SubsonicApiClient {
//
async ping(): Promise> {
- const xml = await this.apiGetXml('ping');
- return new SubsonicResponse(xml, null);
+ const xml = await this.apiGetXml('ping')
+ return new SubsonicResponse(xml, null)
}
//
@@ -160,38 +160,38 @@ export class SubsonicApiClient {
//
async getArtists(): Promise> {
- const xml = await this.apiGetXml('getArtists');
- return new SubsonicResponse(xml, new GetArtistsResponse(xml));
+ const xml = await this.apiGetXml('getArtists')
+ return new SubsonicResponse(xml, new GetArtistsResponse(xml))
}
async getIndexes(params?: GetIndexesParams): Promise> {
- const xml = await this.apiGetXml('getIndexes', params);
- return new SubsonicResponse(xml, new GetIndexesResponse(xml));
+ const xml = await this.apiGetXml('getIndexes', params)
+ return new SubsonicResponse(xml, new GetIndexesResponse(xml))
}
async getMusicDirectory(params: GetMusicDirectoryParams): Promise> {
- const xml = await this.apiGetXml('getMusicDirectory', params);
- return new SubsonicResponse(xml, new GetMusicDirectoryResponse(xml));
+ const xml = await this.apiGetXml('getMusicDirectory', params)
+ return new SubsonicResponse(xml, new GetMusicDirectoryResponse(xml))
}
async getAlbum(params: GetAlbumParams): Promise> {
- const xml = await this.apiGetXml('getAlbum', params);
- return new SubsonicResponse(xml, new GetAlbumResponse(xml));
+ const xml = await this.apiGetXml('getAlbum', params)
+ return new SubsonicResponse(xml, new GetAlbumResponse(xml))
}
async getArtistInfo(params: GetArtistInfoParams): Promise> {
- const xml = await this.apiGetXml('getArtistInfo', params);
- return new SubsonicResponse(xml, new GetArtistInfoResponse(xml));
+ const xml = await this.apiGetXml('getArtistInfo', params)
+ return new SubsonicResponse(xml, new GetArtistInfoResponse(xml))
}
async getArtistInfo2(params: GetArtistInfo2Params): Promise> {
- const xml = await this.apiGetXml('getArtistInfo2', params);
- return new SubsonicResponse(xml, new GetArtistInfo2Response(xml));
+ const xml = await this.apiGetXml('getArtistInfo2', params)
+ return new SubsonicResponse(xml, new GetArtistInfo2Response(xml))
}
async getArtist(params: GetArtistParams): Promise> {
- const xml = await this.apiGetXml('getArtist', params);
- return new SubsonicResponse(xml, new GetArtistResponse(xml));
+ const xml = await this.apiGetXml('getArtist', params)
+ return new SubsonicResponse(xml, new GetArtistResponse(xml))
}
//
@@ -199,13 +199,13 @@ export class SubsonicApiClient {
//
async getAlbumList(params: GetAlbumListParams): Promise> {
- const xml = await this.apiGetXml('getAlbumList', params);
- return new SubsonicResponse(xml, new GetAlbumListResponse(xml));
+ const xml = await this.apiGetXml('getAlbumList', params)
+ return new SubsonicResponse(xml, new GetAlbumListResponse(xml))
}
async getAlbumList2(params: GetAlbumList2Params): Promise> {
- const xml = await this.apiGetXml('getAlbumList2', params);
- return new SubsonicResponse(xml, new GetAlbumList2Response(xml));
+ const xml = await this.apiGetXml('getAlbumList2', params)
+ return new SubsonicResponse(xml, new GetAlbumList2Response(xml))
}
//
@@ -213,15 +213,15 @@ export class SubsonicApiClient {
//
async getCoverArt(params: GetCoverArtParams): Promise {
- const path = `${paths.songCache}/${params.id}`;
- return await this.apiDownload('getCoverArt', path, params);
+ const path = `${paths.songCache}/${params.id}`
+ return await this.apiDownload('getCoverArt', path, params)
}
getCoverArtUri(params: GetCoverArtParams): string {
- return this.buildUrl('getCoverArt', params);
+ return this.buildUrl('getCoverArt', params)
}
streamUri(params: StreamParams): string {
- return this.buildUrl('stream', params);
+ return this.buildUrl('stream', params)
}
}
diff --git a/src/subsonic/elements.ts b/src/subsonic/elements.ts
index a112216..f321575 100644
--- a/src/subsonic/elements.ts
+++ b/src/subsonic/elements.ts
@@ -1,237 +1,237 @@
function requiredString(e: Element, name: string): string {
- return e.getAttribute(name) as string;
+ return e.getAttribute(name) as string
}
function optionalString(e: Element, name: string): string | undefined {
- return e.hasAttribute(name) ? requiredString(e, name) : undefined;
+ return e.hasAttribute(name) ? requiredString(e, name) : undefined
}
function requiredBoolean(e: Element, name: string): boolean {
- return (e.getAttribute(name) as string).toLowerCase() === 'true';
+ return (e.getAttribute(name) as string).toLowerCase() === 'true'
}
function optionalBoolean(e: Element, name: string): boolean | undefined {
- return e.hasAttribute(name) ? requiredBoolean(e, name) : undefined;
+ return e.hasAttribute(name) ? requiredBoolean(e, name) : undefined
}
function requiredInt(e: Element, name: string): number {
- return parseInt(e.getAttribute(name) as string);
+ return parseInt(e.getAttribute(name) as string)
}
function optionalInt(e: Element, name: string): number | undefined {
- return e.hasAttribute(name) ? requiredInt(e, name) : undefined;
+ return e.hasAttribute(name) ? requiredInt(e, name) : undefined
}
function requiredFloat(e: Element, name: string): number {
- return parseFloat(e.getAttribute(name) as string);
+ return parseFloat(e.getAttribute(name) as string)
}
function optionalFloat(e: Element, name: string): number | undefined {
- return e.hasAttribute(name) ? requiredFloat(e, name) : undefined;
+ return e.hasAttribute(name) ? requiredFloat(e, name) : undefined
}
function requiredDate(e: Element, name: string): Date {
- return new Date(e.getAttribute(name) as string);
+ return new Date(e.getAttribute(name) as string)
}
function optionalDate(e: Element, name: string): Date | undefined {
- return e.hasAttribute(name) ? requiredDate(e, name) : undefined;
+ return e.hasAttribute(name) ? requiredDate(e, name) : undefined
}
export class BaseArtistElement {
- id: string;
- name: string;
- starred?: Date;
+ id: string
+ name: string
+ starred?: Date
constructor(e: Element) {
- this.id = requiredString(e, 'id');
- this.name = requiredString(e, 'name');
- this.starred = optionalDate(e, 'starred');
+ this.id = requiredString(e, 'id')
+ this.name = requiredString(e, 'name')
+ this.starred = optionalDate(e, 'starred')
}
}
export class ArtistID3Element extends BaseArtistElement {
- coverArt?: string;
- albumCount?: number;
+ coverArt?: string
+ albumCount?: number
constructor(e: Element) {
- super(e);
- this.coverArt = optionalString(e, 'coverArt');
- this.albumCount = optionalInt(e, 'albumCount');
+ super(e)
+ this.coverArt = optionalString(e, 'coverArt')
+ this.albumCount = optionalInt(e, 'albumCount')
}
}
export class ArtistElement extends BaseArtistElement {
- userRating?: number;
- averageRating?: number;
+ userRating?: number
+ averageRating?: number
constructor(e: Element) {
- super(e);
- this.userRating = optionalInt(e, 'userRating');
- this.averageRating = optionalFloat(e, 'averageRating');
+ super(e)
+ this.userRating = optionalInt(e, 'userRating')
+ this.averageRating = optionalFloat(e, 'averageRating')
}
}
export class BaseArtistInfoElement {
- similarArtists: T[] = [];
- biography?: string;
- musicBrainzId?: string;
- lastFmUrl?: string;
- smallImageUrl?: string;
- mediumImageUrl?: string;
- largeImageUrl?: string;
+ similarArtists: T[] = []
+ biography?: string
+ musicBrainzId?: string
+ lastFmUrl?: string
+ smallImageUrl?: string
+ mediumImageUrl?: string
+ largeImageUrl?: string
constructor(e: Element, artistType: new (e: Element) => T) {
if (e.getElementsByTagName('biography').length > 0) {
- this.biography = e.getElementsByTagName('biography')[0].textContent as string;
+ this.biography = e.getElementsByTagName('biography')[0].textContent as string
}
if (e.getElementsByTagName('musicBrainzId').length > 0) {
- this.musicBrainzId = e.getElementsByTagName('musicBrainzId')[0].textContent as string;
+ this.musicBrainzId = e.getElementsByTagName('musicBrainzId')[0].textContent as string
}
if (e.getElementsByTagName('lastFmUrl').length > 0) {
- this.lastFmUrl = e.getElementsByTagName('lastFmUrl')[0].textContent as string;
+ this.lastFmUrl = e.getElementsByTagName('lastFmUrl')[0].textContent as string
}
if (e.getElementsByTagName('smallImageUrl').length > 0) {
- this.smallImageUrl = e.getElementsByTagName('smallImageUrl')[0].textContent as string;
+ this.smallImageUrl = e.getElementsByTagName('smallImageUrl')[0].textContent as string
}
if (e.getElementsByTagName('mediumImageUrl').length > 0) {
- this.mediumImageUrl = e.getElementsByTagName('mediumImageUrl')[0].textContent as string;
+ this.mediumImageUrl = e.getElementsByTagName('mediumImageUrl')[0].textContent as string
}
if (e.getElementsByTagName('largeImageUrl').length > 0) {
- this.largeImageUrl = e.getElementsByTagName('largeImageUrl')[0].textContent as string;
+ this.largeImageUrl = e.getElementsByTagName('largeImageUrl')[0].textContent as string
}
- const similarArtistElements = e.getElementsByTagName('similarArtist');
+ const similarArtistElements = e.getElementsByTagName('similarArtist')
for (let i = 0; i < similarArtistElements.length; i++) {
- this.similarArtists.push(new artistType(similarArtistElements[i]));
+ this.similarArtists.push(new artistType(similarArtistElements[i]))
}
}
}
export class ArtistInfoElement extends BaseArtistInfoElement {
constructor(e: Element) {
- super(e, ArtistElement);
+ super(e, ArtistElement)
}
}
export class ArtistInfo2Element extends BaseArtistInfoElement {
constructor(e: Element) {
- super(e, ArtistID3Element);
+ super(e, ArtistID3Element)
}
}
export class DirectoryElement {
- id: string;
- parent?: string;
- name: string;
- starred?: Date;
- userRating?: number;
- averageRating?: number;
- playCount?: number;
+ id: string
+ parent?: string
+ name: string
+ starred?: Date
+ userRating?: number
+ averageRating?: number
+ playCount?: number
constructor(e: Element) {
- this.id = requiredString(e, 'id');
- this.parent = optionalString(e, 'parent');
- this.name = requiredString(e, 'name');
- this.starred = optionalDate(e, 'starred');
- this.userRating = optionalInt(e, 'userRating');
- this.averageRating = optionalFloat(e, 'averageRating');
+ this.id = requiredString(e, 'id')
+ this.parent = optionalString(e, 'parent')
+ this.name = requiredString(e, 'name')
+ this.starred = optionalDate(e, 'starred')
+ this.userRating = optionalInt(e, 'userRating')
+ this.averageRating = optionalFloat(e, 'averageRating')
}
}
export class ChildElement {
- id: string;
- parent?: string;
- isDir: boolean;
- title: string;
- album?: string;
- artist?: string;
- track?: number;
- year?: number;
- genre?: string;
- coverArt?: string;
- size?: number;
- contentType?: string;
- suffix?: string;
- transcodedContentType?: string;
- transcodedSuffix?: string;
- duration?: number;
- bitRate?: number;
- path?: string;
- isVideo?: boolean;
- userRating?: number;
- averageRating?: number;
- playCount?: number;
- discNumber?: number;
- created?: Date;
- starred?: Date;
- albumId?: string;
- artistId?: string;
- type?: string;
- bookmarkPosition?: number;
- originalWidth?: number;
- originalHeight?: number;
+ id: string
+ parent?: string
+ isDir: boolean
+ title: string
+ album?: string
+ artist?: string
+ track?: number
+ year?: number
+ genre?: string
+ coverArt?: string
+ size?: number
+ contentType?: string
+ suffix?: string
+ transcodedContentType?: string
+ transcodedSuffix?: string
+ duration?: number
+ bitRate?: number
+ path?: string
+ isVideo?: boolean
+ userRating?: number
+ averageRating?: number
+ playCount?: number
+ discNumber?: number
+ created?: Date
+ starred?: Date
+ albumId?: string
+ artistId?: string
+ type?: string
+ bookmarkPosition?: number
+ originalWidth?: number
+ originalHeight?: number
constructor(e: Element) {
- this.id = requiredString(e, 'id');
- this.parent = optionalString(e, 'parent');
- this.isDir = requiredBoolean(e, 'isDir');
- this.title = requiredString(e, 'title');
- this.album = optionalString(e, 'album');
- this.artist = optionalString(e, 'artist');
- this.track = optionalInt(e, 'track');
- this.year = optionalInt(e, 'year');
- this.genre = optionalString(e, 'genre');
- this.coverArt = optionalString(e, 'coverArt');
- this.size = optionalInt(e, 'size');
- this.contentType = optionalString(e, 'contentType');
- this.suffix = optionalString(e, 'suffix');
- this.transcodedContentType = optionalString(e, 'transcodedContentType');
- this.transcodedSuffix = optionalString(e, 'transcodedSuffix');
- this.duration = optionalInt(e, 'duration');
- this.bitRate = optionalInt(e, 'bitRate');
- this.path = optionalString(e, 'path');
- this.isVideo = optionalBoolean(e, 'isVideo');
- this.userRating = optionalInt(e, 'userRating');
- this.averageRating = optionalFloat(e, 'averageRating');
- this.playCount = optionalInt(e, 'playCount');
- this.discNumber = optionalInt(e, 'discNumber');
- this.created = optionalDate(e, 'created');
- this.starred = optionalDate(e, 'starred');
- this.albumId = optionalString(e, 'albumId');
- this.artistId = optionalString(e, 'artistId');
- this.type = optionalString(e, 'type');
- this.bookmarkPosition = optionalInt(e, 'bookmarkPosition');
- this.originalWidth = optionalInt(e, 'originalWidth');
- this.originalHeight = optionalInt(e, 'originalHeight');
+ this.id = requiredString(e, 'id')
+ this.parent = optionalString(e, 'parent')
+ this.isDir = requiredBoolean(e, 'isDir')
+ this.title = requiredString(e, 'title')
+ this.album = optionalString(e, 'album')
+ this.artist = optionalString(e, 'artist')
+ this.track = optionalInt(e, 'track')
+ this.year = optionalInt(e, 'year')
+ this.genre = optionalString(e, 'genre')
+ this.coverArt = optionalString(e, 'coverArt')
+ this.size = optionalInt(e, 'size')
+ this.contentType = optionalString(e, 'contentType')
+ this.suffix = optionalString(e, 'suffix')
+ this.transcodedContentType = optionalString(e, 'transcodedContentType')
+ this.transcodedSuffix = optionalString(e, 'transcodedSuffix')
+ this.duration = optionalInt(e, 'duration')
+ this.bitRate = optionalInt(e, 'bitRate')
+ this.path = optionalString(e, 'path')
+ this.isVideo = optionalBoolean(e, 'isVideo')
+ this.userRating = optionalInt(e, 'userRating')
+ this.averageRating = optionalFloat(e, 'averageRating')
+ this.playCount = optionalInt(e, 'playCount')
+ this.discNumber = optionalInt(e, 'discNumber')
+ this.created = optionalDate(e, 'created')
+ this.starred = optionalDate(e, 'starred')
+ this.albumId = optionalString(e, 'albumId')
+ this.artistId = optionalString(e, 'artistId')
+ this.type = optionalString(e, 'type')
+ this.bookmarkPosition = optionalInt(e, 'bookmarkPosition')
+ this.originalWidth = optionalInt(e, 'originalWidth')
+ this.originalHeight = optionalInt(e, 'originalHeight')
}
}
export class AlbumID3Element {
- id: string;
- name: string;
- artist?: string;
- artistId?: string;
- coverArt?: string;
- songCount: number;
- duration: number;
- playCount?: number;
- created: Date;
- starred?: Date;
- year?: number;
- genre?: string;
+ id: string
+ name: string
+ artist?: string
+ artistId?: string
+ coverArt?: string
+ songCount: number
+ duration: number
+ playCount?: number
+ created: Date
+ starred?: Date
+ year?: number
+ genre?: string
constructor(e: Element) {
- this.id = requiredString(e, 'id');
- this.name = requiredString(e, 'name');
- this.artist = optionalString(e, 'artist');
- this.artistId = optionalString(e, 'artistId');
- this.coverArt = optionalString(e, 'coverArt');
- this.songCount = requiredInt(e, 'songCount');
- this.duration = requiredInt(e, 'duration');
- this.playCount = optionalInt(e, 'playCount');
- this.created = requiredDate(e, 'created');
- this.starred = optionalDate(e, 'starred');
- this.year = optionalInt(e, 'year');
- this.genre = optionalString(e, 'genre');
+ this.id = requiredString(e, 'id')
+ this.name = requiredString(e, 'name')
+ this.artist = optionalString(e, 'artist')
+ this.artistId = optionalString(e, 'artistId')
+ this.coverArt = optionalString(e, 'coverArt')
+ this.songCount = requiredInt(e, 'songCount')
+ this.duration = requiredInt(e, 'duration')
+ this.playCount = optionalInt(e, 'playCount')
+ this.created = requiredDate(e, 'created')
+ this.starred = optionalDate(e, 'starred')
+ this.year = optionalInt(e, 'year')
+ this.genre = optionalString(e, 'genre')
}
}
diff --git a/src/subsonic/params.ts b/src/subsonic/params.ts
index dda8bfb..854eb13 100644
--- a/src/subsonic/params.ts
+++ b/src/subsonic/params.ts
@@ -3,29 +3,29 @@
//
export type GetIndexesParams = {
- musicFolderId?: string;
- ifModifiedSince?: number;
-};
+ musicFolderId?: string
+ ifModifiedSince?: number
+}
export type GetArtistInfoParams = {
- id: string;
- count?: number;
- includeNotPresent?: boolean;
-};
+ id: string
+ count?: number
+ includeNotPresent?: boolean
+}
-export type GetArtistInfo2Params = GetArtistInfoParams;
+export type GetArtistInfo2Params = GetArtistInfoParams
export type GetMusicDirectoryParams = {
- id: string;
-};
+ id: string
+}
export type GetAlbumParams = {
- id: string;
-};
+ id: string
+}
export type GetArtistParams = {
- id: string;
-};
+ id: string
+}
//
// Album/song lists
@@ -38,47 +38,47 @@ export type GetAlbumList2Type =
| 'recent'
| 'starred'
| 'alphabeticalByName'
- | 'alphabeticalByArtist';
-export type GetAlbumListType = GetAlbumList2Type | ' highest';
+ | 'alphabeticalByArtist'
+export type GetAlbumListType = GetAlbumList2Type | ' highest'
export type GetAlbumList2TypeByYear = {
- type: 'byYear';
- fromYear: string;
- toYear: string;
-};
+ type: 'byYear'
+ fromYear: string
+ toYear: string
+}
export type GetAlbumList2TypeByGenre = {
- type: 'byGenre';
- genre: string;
-};
+ type: 'byGenre'
+ genre: string
+}
export type GetAlbumList2Params =
| {
- type: GetAlbumList2Type;
- size?: number;
- offset?: number;
- fromYear?: string;
- toYear?: string;
- genre?: string;
- musicFolderId?: string;
+ type: GetAlbumList2Type
+ size?: number
+ offset?: number
+ fromYear?: string
+ toYear?: string
+ genre?: string
+ musicFolderId?: string
}
| GetAlbumList2TypeByYear
- | GetAlbumList2TypeByGenre;
+ | GetAlbumList2TypeByGenre
-export type GetAlbumListParams = GetAlbumList2Params;
+export type GetAlbumListParams = GetAlbumList2Params
//
// Media retrieval
//
export type GetCoverArtParams = {
- id: string;
- size?: string;
-};
+ id: string
+ size?: string
+}
export type StreamParams = {
- id: string;
- maxBitRate?: number;
- format?: string;
- estimateContentLength?: boolean;
-};
+ id: string
+ maxBitRate?: number
+ format?: string
+ estimateContentLength?: boolean
+}
diff --git a/src/subsonic/responses.ts b/src/subsonic/responses.ts
index 7dc35f3..32cd795 100644
--- a/src/subsonic/responses.ts
+++ b/src/subsonic/responses.ts
@@ -6,19 +6,19 @@ import {
ArtistInfoElement,
ChildElement,
DirectoryElement,
-} from './elements';
+} from './elements'
-export type ResponseStatus = 'ok' | 'failed';
+export type ResponseStatus = 'ok' | 'failed'
export class SubsonicResponse {
- status: ResponseStatus;
- version: string;
- data: T;
+ status: ResponseStatus
+ version: string
+ data: T
constructor(xml: Document, data: T) {
- this.data = data;
- this.status = xml.documentElement.getAttribute('status') as ResponseStatus;
- this.version = xml.documentElement.getAttribute('version') as string;
+ this.data = data
+ this.status = xml.documentElement.getAttribute('status') as ResponseStatus
+ this.version = xml.documentElement.getAttribute('version') as string
}
}
@@ -27,91 +27,91 @@ export class SubsonicResponse {
//
export class GetArtistsResponse {
- ignoredArticles: string;
- artists: ArtistID3Element[] = [];
+ ignoredArticles: string
+ artists: ArtistID3Element[] = []
constructor(xml: Document) {
- this.ignoredArticles = xml.getElementsByTagName('artists')[0].getAttribute('ignoredArticles') as string;
+ this.ignoredArticles = xml.getElementsByTagName('artists')[0].getAttribute('ignoredArticles') as string
- const artistElements = xml.getElementsByTagName('artist');
+ const artistElements = xml.getElementsByTagName('artist')
for (let i = 0; i < artistElements.length; i++) {
- this.artists.push(new ArtistID3Element(artistElements[i]));
+ this.artists.push(new ArtistID3Element(artistElements[i]))
}
}
}
export class GetArtistResponse {
- artist: ArtistID3Element;
- albums: AlbumID3Element[] = [];
+ artist: ArtistID3Element
+ albums: AlbumID3Element[] = []
constructor(xml: Document) {
- this.artist = new ArtistID3Element(xml.getElementsByTagName('artist')[0]);
+ this.artist = new ArtistID3Element(xml.getElementsByTagName('artist')[0])
- const albumElements = xml.getElementsByTagName('album');
+ const albumElements = xml.getElementsByTagName('album')
for (let i = 0; i < albumElements.length; i++) {
- this.albums.push(new AlbumID3Element(albumElements[i]));
+ this.albums.push(new AlbumID3Element(albumElements[i]))
}
}
}
export class GetIndexesResponse {
- ignoredArticles: string;
- lastModified: number;
- artists: ArtistElement[] = [];
+ ignoredArticles: string
+ lastModified: number
+ artists: ArtistElement[] = []
constructor(xml: Document) {
- const indexesElement = xml.getElementsByTagName('indexes')[0];
+ const indexesElement = xml.getElementsByTagName('indexes')[0]
- this.ignoredArticles = indexesElement.getAttribute('ignoredArticles') as string;
- this.lastModified = parseInt(indexesElement.getAttribute('lastModified') as string);
+ this.ignoredArticles = indexesElement.getAttribute('ignoredArticles') as string
+ this.lastModified = parseInt(indexesElement.getAttribute('lastModified') as string)
- const artistElements = xml.getElementsByTagName('artist');
+ const artistElements = xml.getElementsByTagName('artist')
for (let i = 0; i < artistElements.length; i++) {
- this.artists.push(new ArtistElement(artistElements[i]));
+ this.artists.push(new ArtistElement(artistElements[i]))
}
}
}
export class GetArtistInfoResponse {
- artistInfo: ArtistInfoElement;
+ artistInfo: ArtistInfoElement
constructor(xml: Document) {
- this.artistInfo = new ArtistInfoElement(xml.getElementsByTagName('artistInfo')[0]);
+ this.artistInfo = new ArtistInfoElement(xml.getElementsByTagName('artistInfo')[0])
}
}
export class GetArtistInfo2Response {
- artistInfo: ArtistInfo2Element;
+ artistInfo: ArtistInfo2Element
constructor(xml: Document) {
- this.artistInfo = new ArtistInfo2Element(xml.getElementsByTagName('artistInfo2')[0]);
+ this.artistInfo = new ArtistInfo2Element(xml.getElementsByTagName('artistInfo2')[0])
}
}
export class GetMusicDirectoryResponse {
- directory: DirectoryElement;
- children: ChildElement[] = [];
+ directory: DirectoryElement
+ children: ChildElement[] = []
constructor(xml: Document) {
- this.directory = new DirectoryElement(xml.getElementsByTagName('directory')[0]);
+ this.directory = new DirectoryElement(xml.getElementsByTagName('directory')[0])
- const childElements = xml.getElementsByTagName('child');
+ const childElements = xml.getElementsByTagName('child')
for (let i = 0; i < childElements.length; i++) {
- this.children.push(new ChildElement(childElements[i]));
+ this.children.push(new ChildElement(childElements[i]))
}
}
}
export class GetAlbumResponse {
- album: AlbumID3Element;
- songs: ChildElement[] = [];
+ album: AlbumID3Element
+ songs: ChildElement[] = []
constructor(xml: Document) {
- this.album = new AlbumID3Element(xml.getElementsByTagName('album')[0]);
+ this.album = new AlbumID3Element(xml.getElementsByTagName('album')[0])
- const childElements = xml.getElementsByTagName('song');
+ const childElements = xml.getElementsByTagName('song')
for (let i = 0; i < childElements.length; i++) {
- this.songs.push(new ChildElement(childElements[i]));
+ this.songs.push(new ChildElement(childElements[i]))
}
}
}
@@ -121,24 +121,24 @@ export class GetAlbumResponse {
//
class BaseGetAlbumListResponse {
- albums: T[] = [];
+ albums: T[] = []
constructor(xml: Document, albumType: new (e: Element) => T) {
- const albumElements = xml.getElementsByTagName('album');
+ const albumElements = xml.getElementsByTagName('album')
for (let i = 0; i < albumElements.length; i++) {
- this.albums.push(new albumType(albumElements[i]));
+ this.albums.push(new albumType(albumElements[i]))
}
}
}
export class GetAlbumListResponse extends BaseGetAlbumListResponse {
constructor(xml: Document) {
- super(xml, ChildElement);
+ super(xml, ChildElement)
}
}
export class GetAlbumList2Response extends BaseGetAlbumListResponse {
constructor(xml: Document) {
- super(xml, AlbumID3Element);
+ super(xml, AlbumID3Element)
}
}
diff --git a/src/util.ts b/src/util.ts
index 72d9897..fe2a803 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -1,11 +1,11 @@
export function formatDuration(seconds: number): string {
- const s = seconds % 60;
- const m = Math.floor(seconds / 60) % 60;
- const h = Math.floor(seconds / 60 / 60);
+ const s = seconds % 60
+ const m = Math.floor(seconds / 60) % 60
+ const h = Math.floor(seconds / 60 / 60)
- let time = `${m.toString().padStart(1, '0')}:${s.toString().padStart(2, '0')}`;
+ let time = `${m.toString().padStart(1, '0')}:${s.toString().padStart(2, '0')}`
if (h > 0) {
- time = `${h}:${time}`;
+ time = `${h}:${time}`
}
- return time;
+ return time
}