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, View } 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 { DrawerParams } 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, { settingsUpdated } 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(async (key: keyof Settings, value: unknown) => { await settingsRepo .createQueryBuilder() .update() .set({ [key]: value }) .printSql() .execute(); settingsUpdated(); }, []); 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("Alarms will no longer make a sound."); else toast("Enabled sound for 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( (input: Input) => (