import { NavigationProp, useNavigation } from '@react-navigation/native' import { format } from 'date-fns' import { useCallback, useEffect, useMemo, useState } from 'react' import { useForm } from 'react-hook-form' import { NativeModules, ScrollView } from 'react-native' import DocumentPicker from 'react-native-document-picker' import { Dirs, FileSystem } from 'react-native-file-access' import ConfirmDialog from './ConfirmDialog' import { MARGIN } from './constants' import { AppDataSource } from './data-source' import { setRepo, settingsRepo } from './db' import { DrawerParamList } from './drawer-param-list' import DrawerHeader from './DrawerHeader' import Input from './input' import { darkOptions, lightOptions, themeOptions } from './options' import Page from './Page' import Select from './Select' import SettingButton from './SettingButton' import Settings from './settings' import Switch from './Switch' import { toast } from './toast' import { useTheme } from './use-theme' const twelveHours = [ 'dd/LL/yyyy', 'dd/LL/yyyy, p', 'ccc p', 'p', 'yyyy-MM-d', 'yyyy-MM-d, p', 'yyyy.MM.d', ] const twentyFours = [ 'dd/LL/yyyy', 'dd/LL/yyyy, k:m', 'ccc k:m', 'k:m', 'yyyy-MM-d', 'yyyy-MM-d, k:m', 'yyyy.MM.d', ] export default function SettingsPage() { const [ignoring, setIgnoring] = useState(false) const [term, setTerm] = useState('') const [formatOptions, setFormatOptions] = useState(twelveHours) const [importing, setImporting] = useState(false) const [deleting, setDeleting] = useState(false) const { reset } = useNavigation>() const { watch, setValue } = useForm({ defaultValues: () => settingsRepo.findOne({ where: {} }), }) const settings = watch() const { theme, setTheme, lightColor, setLightColor, darkColor, setDarkColor, } = useTheme() useEffect(() => { NativeModules.SettingsModule.ignoringBattery(setIgnoring) NativeModules.SettingsModule.is24().then((is24: boolean) => { console.log(`${SettingsPage.name}.focus:`, { is24 }) if (is24) setFormatOptions(twentyFours) else setFormatOptions(twelveHours) }) }, []) const update = useCallback((key: keyof Settings, value: unknown) => { return settingsRepo .createQueryBuilder() .update() .set({ [key]: value }) .printSql() .execute() }, []) const soundString = useMemo(() => { if (!settings.sound) return null const split = settings.sound.split('/') return split.pop() }, [settings.sound]) const changeSound = useCallback(async () => { const { fileCopyUri } = await DocumentPicker.pickSingle({ type: DocumentPicker.types.audio, copyTo: 'documentDirectory', }) if (!fileCopyUri) return setValue('sound', fileCopyUri) await update('sound', fileCopyUri) toast('Sound will play after rest timers.') }, [setValue, update]) const switches: Input[] = useMemo( () => [ { name: 'Rest timers', value: settings.alarm, key: 'alarm' }, { name: 'Vibrate', value: settings.vibrate, key: 'vibrate' }, { name: 'Disable sound', value: settings.noSound, key: 'noSound' }, { name: 'Notifications', value: settings.notify, key: 'notify' }, { name: 'Show images', value: settings.images, key: 'images' }, { name: 'Show unit', value: settings.showUnit, key: 'showUnit' }, { name: 'Show steps', value: settings.steps, key: 'steps' }, { name: 'Show date', value: settings.showDate, key: 'showDate' }, { name: 'Automatic backup', value: settings.backup, key: 'backup' }, ], [settings], ) const filter = useCallback( ({ name }) => name.toLowerCase().includes(term.toLowerCase()), [term], ) const changeBoolean = useCallback( async (key: keyof Settings, value: boolean) => { setValue(key, value) await update(key, value) switch (key) { case 'alarm': if (value) toast('Timers will now run after each set.') else toast('Stopped timers running after each set.') if (value && !ignoring) NativeModules.SettingsModule.ignoreBattery() return case 'vibrate': if (value) toast('Alarms will now vibrate.') else toast('Alarms will no longer vibrate.') return case 'notify': if (value) toast('Show notifications for new records.') else toast('Stopped notifications for new records.') return case 'images': if (value) toast('Show images for sets.') else toast('Hid images for sets.') return case 'showUnit': if (value) toast('Show option to select unit for sets.') else toast('Hid unit option for sets.') return case 'steps': if (value) toast('Show steps for a workout.') else toast('Hid steps for workouts.') return case 'showDate': if (value) toast('Show date for sets.') else toast('Hid date on sets.') return case 'noSound': if (value) toast('Disable sound on rest timer alarms.') else toast('Enabled sound for rest timer alarms.') return case 'backup': if (value) { const result = await DocumentPicker.pickDirectory() toast('Backup database daily.') NativeModules.BackupModule.start(result.uri) } else { toast('Stopped backing up daily') NativeModules.BackupModule.stop() } return } }, [ignoring, setValue, update], ) const renderSwitch = useCallback( (item: Input) => ( changeBoolean(item.key, value)} title={item.name} /> ), [changeBoolean], ) const switchesMarkup = useMemo( () => switches.filter(filter).map((s) => renderSwitch(s)), [filter, switches, renderSwitch], ) const changeString = useCallback( async (key: keyof Settings, value: string) => { setValue(key, value) await update(key, value) switch (key) { case 'date': return toast('Changed date format') case 'darkColor': setDarkColor(value) return toast('Set primary color for dark mode.') case 'lightColor': setLightColor(value) return toast('Set primary color for light mode.') case 'vibrate': return toast('Set primary color for light mode.') case 'sound': return toast('Sound will play after rest timers.') case 'theme': setTheme(value as string) if (value === 'dark') toast('Theme will always be dark.') else if (value === 'light') toast('Theme will always be light.') else if (value === 'system') toast('Theme will follow system.') return } }, [update, setTheme, setDarkColor, setLightColor, setValue], ) const selects: Input[] = useMemo(() => { const today = new Date() return [ { name: 'Theme', value: theme, items: themeOptions, key: 'theme' }, { name: 'Dark color', value: darkColor, items: lightOptions, key: 'darkColor', }, { name: 'Light color', value: lightColor, items: darkOptions, key: 'lightColor', }, { name: 'Date format', value: settings.date, items: formatOptions.map((option) => ({ label: format(today, option), value: option, })), key: 'date', }, ] }, [settings, darkColor, formatOptions, theme, lightColor]) const renderSelect = useCallback( (item: Input) => (