import { AppSettings, Server } from '@app/models/settings' import { Store } from '@app/state/store' import { SubsonicApiClient } from '@app/subsonic/api' import produce from 'immer' import { GetState, SetState } from 'zustand' export type SettingsSlice = { settings: AppSettings client?: SubsonicApiClient setActiveServer: (id: string | undefined, force?: boolean) => Promise getActiveServer: () => Server | undefined addServer: (server: Server) => Promise removeServer: (id: string) => Promise updateServer: (server: Server) => void setScrobble: (scrobble: boolean) => void setEstimateContentLength: (estimateContentLength: boolean) => void setMaxBitrateWifi: (maxBitrateWifi: number) => void setMaxBitrateMobile: (maxBitrateMobile: number) => void pingServer: (server?: Server) => Promise } export const selectSettings = { client: (state: SettingsSlice) => state.client, activeServer: (state: SettingsSlice) => state.settings.servers.find(s => s.id === state.settings.activeServer), setActiveServer: (state: SettingsSlice) => state.setActiveServer, servers: (state: SettingsSlice) => state.settings.servers, addServer: (state: SettingsSlice) => state.addServer, removeServer: (state: SettingsSlice) => state.removeServer, updateServer: (state: SettingsSlice) => state.updateServer, homeLists: (state: SettingsSlice) => state.settings.home.lists, scrobble: (state: SettingsSlice) => state.settings.scrobble, setScrobble: (state: SettingsSlice) => state.setScrobble, estimateContentLength: (state: SettingsSlice) => state.settings.estimateContentLength, setEstimateContentLength: (state: SettingsSlice) => state.setEstimateContentLength, maxBitrateWifi: (state: SettingsSlice) => state.settings.maxBitrateWifi, setMaxBitrateWifi: (state: SettingsSlice) => state.setMaxBitrateWifi, maxBitrateMobile: (state: SettingsSlice) => state.settings.maxBitrateMobile, setMaxBitrateMobile: (state: SettingsSlice) => state.setMaxBitrateMobile, pingServer: (state: SettingsSlice) => state.pingServer, } export const createSettingsSlice = (set: SetState, get: GetState): SettingsSlice => ({ settings: { servers: [], home: { lists: ['recent', 'random', 'frequent', 'starred'], }, scrobble: false, estimateContentLength: true, maxBitrateWifi: 0, maxBitrateMobile: 192, }, setActiveServer: async (id, force) => { const servers = get().settings.servers const currentActiveServerId = get().settings.activeServer const newActiveServer = servers.find(s => s.id === id) if (!newActiveServer) { set({ client: undefined, }) return } if (currentActiveServerId === id && !force) { return } get().prepareCache(newActiveServer.id) set( produce(state => { state.settings.activeServer = newActiveServer.id state.client = new SubsonicApiClient(newActiveServer) }), ) }, getActiveServer: () => get().settings.servers.find(s => s.id === get().settings.activeServer), addServer: async server => { await get().createCache(server.id) set( produce(state => { state.settings.servers.push(server) }), ) if (get().settings.servers.length === 1) { get().setActiveServer(server.id) } }, removeServer: async id => { await get().removeCache(id) set( produce(state => { state.settings.servers = state.settings.servers.filter(s => s.id !== id) }), ) }, updateServer: server => { set( produce(state => { state.settings.servers = replaceIndex( state.settings.servers, state.settings.servers.findIndex(s => s.id === server.id), server, ) }), ) if (get().settings.activeServer === server.id) { get().setActiveServer(server.id) } }, setScrobble: scrobble => { set( produce(state => { state.settings.scrobble = scrobble }), ) }, setEstimateContentLength: estimateContentLength => { set( produce(state => { state.settings.estimateContentLength = estimateContentLength }), ) get().rebuildQueue() }, setMaxBitrateWifi: maxBitrateWifi => { set( produce(state => { state.settings.maxBitrateWifi = maxBitrateWifi }), ) if (get().netState === 'wifi') { get().rebuildQueue() } }, setMaxBitrateMobile: maxBitrateMobile => { set( produce(state => { state.settings.maxBitrateMobile = maxBitrateMobile }), ) if (get().netState === 'mobile') { get().rebuildQueue() } }, pingServer: async server => { let client: SubsonicApiClient if (server) { client = new SubsonicApiClient(server) } else { const currentClient = get().client if (!currentClient) { return false } client = currentClient } try { await client.ping() return true } catch { return false } }, }) function replaceIndex(array: T[], index: number, replacement: T): T[] { const start = array.slice(0, index) const end = array.slice(index + 1) return [...start, replacement, ...end] }