diff --git a/app/components/PressableOpacity.tsx b/app/components/PressableOpacity.tsx index cdd0045..8c1ef80 100644 --- a/app/components/PressableOpacity.tsx +++ b/app/components/PressableOpacity.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useState } from 'react' -import { LayoutRectangle, Pressable, PressableProps } from 'react-native' +import React, { useCallback, useEffect, useState } from 'react' +import { GestureResponderEvent, LayoutRectangle, Pressable, PressableProps, ViewStyle } from 'react-native' type PressableOpacityProps = PressableProps & { ripple?: boolean @@ -9,10 +9,11 @@ type PressableOpacityProps = PressableProps & { const PressableOpacity: React.FC = props => { const [opacity, setOpacity] = useState(1) + const [disabledStyle, setDisabledStyle] = useState({}) const [dimensions, setDimensions] = useState(undefined) useEffect(() => { - props.disabled === true ? setOpacity(0.3) : setOpacity(1) + props.disabled === true ? setDisabledStyle({ opacity: 0.3 }) : setDisabledStyle({}) }, [props.disabled]) props = { @@ -20,10 +21,41 @@ const PressableOpacity: React.FC = props => { unstable_pressDelay: props.unstable_pressDelay === undefined ? 60 : props.unstable_pressDelay, } + const onPressIn = useCallback<(event: GestureResponderEvent) => void>( + data => { + if (props.disabled) { + return + } + setOpacity(0.4) + props.onPressIn ? props.onPressIn(data) : null + }, + [props], + ) + const onPressOut = useCallback<(event: GestureResponderEvent) => void>( + data => { + if (props.disabled) { + return + } + setOpacity(1) + props.onPressOut ? props.onPressOut(data) : null + }, + [props], + ) + const onLongPress = useCallback<(event: GestureResponderEvent) => void>( + data => { + if (props.disabled) { + return + } + setOpacity(1) + props.onLongPress ? props.onLongPress(data) : null + }, + [props], + ) + return ( = props => { : undefined } onLayout={event => setDimensions(event.nativeEvent.layout)} - onPressIn={() => { - if (!props.disabled) { - setOpacity(0.4) - } - }} - onPressOut={() => { - if (!props.disabled) { - setOpacity(1) - } - }} - onLongPress={data => { - if (!props.disabled) { - setOpacity(1) - props.onLongPress ? props.onLongPress(data) : null - } - }}> + onPressIn={onPressIn} + onPressOut={onPressOut} + onLongPress={onLongPress}> {props.children} ) diff --git a/app/components/SettingsItem.tsx b/app/components/SettingsItem.tsx index aaccfd4..788350d 100644 --- a/app/components/SettingsItem.tsx +++ b/app/components/SettingsItem.tsx @@ -42,7 +42,7 @@ const styles = StyleSheet.create({ itemSubtitle: { fontFamily: font.regular, color: colors.text.secondary, - fontSize: 15, + fontSize: 14, }, }) diff --git a/app/models/music.ts b/app/models/music.ts index f058150..d41e55e 100644 --- a/app/models/music.ts +++ b/app/models/music.ts @@ -72,9 +72,9 @@ export type HomeLists = { [key: string]: AlbumListItem[] } export type StarrableItemType = 'song' | 'album' | 'artist' export enum CacheItemType { - coverArt, - artistArt, - song, + coverArt = 'coverArt', + artistArt = 'artistArt', + song = 'song', } export type CacheItemTypeKey = keyof typeof CacheItemType diff --git a/app/screens/ServerView.tsx b/app/screens/ServerView.tsx index 020e533..ee895cb 100644 --- a/app/screens/ServerView.tsx +++ b/app/screens/ServerView.tsx @@ -8,28 +8,27 @@ import font from '@app/styles/font' import { useNavigation } from '@react-navigation/native' import md5 from 'md5' import React, { useCallback, useState } from 'react' -import { StyleSheet, Text, TextInput, View } from 'react-native' +import { StyleSheet, Text, TextInput, ToastAndroid, View } from 'react-native' import { v4 as uuidv4 } from 'uuid' -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] -} - const ServerView: React.FC<{ id?: string }> = ({ id }) => { const navigation = useNavigation() const activeServer = useStore(selectSettings.activeServer) - const setActiveServer = useStore(selectSettings.setActiveServer) const servers = useStore(selectSettings.servers) - const setServers = useStore(selectSettings.setServers) + const addServer = useStore(selectSettings.addServer) + const updateServer = useStore(selectSettings.updateServer) + const removeServer = useStore(selectSettings.removeServer) const server = id ? servers.find(s => s.id === id) : undefined + const pingServer = useStore(selectSettings.pingServer) const [address, setAddress] = useState(server?.address || '') const [username, setUsername] = useState(server?.username || '') const [password, setPassword] = useState(server?.token ? 'password' : '') + const [testing, setTesting] = useState(false) + const [removing, setRemoving] = useState(false) + const [saving, setSaving] = useState(false) const validate = useCallback(() => { return !!address && !!username && !!password @@ -47,11 +46,7 @@ const ServerView: React.FC<{ } }, [navigation]) - const save = useCallback(() => { - if (!validate()) { - return - } - + const createServer = useCallback<() => Server>(() => { const salt = server?.salt || uuidv4() let token: string if (password === 'password' && server?.token) { @@ -60,47 +55,76 @@ const ServerView: React.FC<{ token = md5(password + salt) } - const update: Server = { + return { id: server?.id || uuidv4(), address, username, salt, token, } + }, [address, password, server?.id, server?.salt, server?.token, username]) - if (server) { - setServers( - replaceIndex( - servers, - servers.findIndex(s => s.id === id), - update, - ), - ) - } else { - setServers([...servers, update]) + const save = useCallback(() => { + if (!validate()) { + return } - if (!activeServer) { - setActiveServer(update.id) - } + setSaving(true) + const update = createServer() - exit() - }, [activeServer, address, exit, id, password, server, servers, setActiveServer, setServers, username, validate]) + const waitForSave = async () => { + try { + if (id) { + updateServer(update) + } else { + await addServer(update) + } + exit() + } catch (err) { + console.error(err) + setSaving(false) + } + } + waitForSave() + }, [addServer, createServer, exit, id, updateServer, validate]) const remove = useCallback(() => { if (!canRemove()) { return } - const update = [...servers] - update.splice( - update.findIndex(s => s.id === id), - 1, - ) + setRemoving(true) + const waitForRemove = async () => { + try { + await removeServer(id as string) + exit() + } catch (err) { + console.error(err) + setRemoving(false) + } + } + waitForRemove() + }, [canRemove, exit, id, removeServer]) - setServers(update) - exit() - }, [canRemove, exit, id, servers, setServers]) + const test = useCallback(() => { + setTesting(true) + const potential = createServer() + + const ping = async () => { + const res = await pingServer(potential) + if (res) { + ToastAndroid.show(`Connection to ${potential.address} OK!`, ToastAndroid.SHORT) + } else { + ToastAndroid.show(`Connection to ${potential.address} failed, check settings or server`, ToastAndroid.SHORT) + } + setTesting(false) + } + ping() + }, [createServer, pingServer, setTesting]) + + const disableControls = useCallback(() => { + return !validate() || testing || removing || saving + }, [validate, testing, removing, saving]) return ( @@ -139,18 +163,19 @@ const ServerView: React.FC<{ onChangeText={setPassword} />