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 { FlatList, NativeModules } from "react-native"; import DocumentPicker from "react-native-document-picker"; import { Dirs, FileSystem } from "react-native-file-access"; import { Button } from "react-native-paper"; import { PERMISSIONS, RESULTS, check, request } from "react-native-permissions"; import AppInput from "./AppInput"; import ConfirmDialog from "./ConfirmDialog"; import DrawerHeader from "./DrawerHeader"; import Page from "./Page"; import Select from "./Select"; import Switch from "./Switch"; import { PADDING } from "./constants"; import { AppDataSource } from "./data-source"; import { setRepo, settingsRepo } from "./db"; import { DrawerParams } from "./drawer-param-list"; import { darkOptions, lightOptions, themeOptions } from "./options"; import Settings from "./settings"; 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", ]; interface Item { name: string; renderItem: (name: string) => React.JSX.Element; } 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(); }, []); const soundString = useMemo(() => { if (!settings.sound) return null; const split = settings.sound.split("/"); return split.pop(); }, [settings.sound]); const confirmDelete = useCallback(async () => { setDeleting(false); await AppDataSource.dropDatabase(); await AppDataSource.destroy(); await AppDataSource.initialize(); toast("Database deleted."); }, []); const confirmImport = useCallback(async () => { setImporting(false); await AppDataSource.destroy(); const file = await DocumentPicker.pickSingle(); await FileSystem.cp(file.uri, Dirs.DatabaseDir + "/massive.db"); await AppDataSource.initialize(); await setRepo.createQueryBuilder().update().set({ image: null }).execute(); await update("sound", null); const { alarm, backup } = await settingsRepo.findOne({ where: {} }); console.log({ backup }); const directory = await DocumentPicker.pickDirectory(); if (backup) NativeModules.BackupModule.start(directory.uri); else NativeModules.BackupModule.stop(); NativeModules.SettingsModule.ignoringBattery((isIgnoring: boolean) => { if (alarm && !isIgnoring) NativeModules.SettingsModule.ignoreBattery(); reset({ index: 0, routes: [{ name: "Settings" }] }); }); }, [reset, update]); const today = new Date(); const data: Item[] = [ { name: "Theme", renderItem: (name: string) => ( { setValue("darkColor", value); setDarkColor(value); await update("darkColor", value); toast("Set primary color for dark mode."); }} /> ), }, { name: "Light color", renderItem: (name: string) => ( ({ label: format(today, option), value: option, }))} value={settings.date} onChange={async (value) => { setValue("date", value); await update("date", value); toast("Changed date format."); }} /> ), }, { name: "Vibration duration (ms)", renderItem: (name: string) => ( setValue("duration", Number(value))} onSubmitEditing={async (e) => { setValue("duration", Number(e.nativeEvent.text)); await update("duration", e.nativeEvent.text); toast("Changed duration of alarm vibrations."); }} keyboardType="numeric" blurOnSubmit /> ), }, { name: "Rest timers", renderItem: (name: string) => ( { setValue("alarm", value); if (value && !ignoring) NativeModules.SettingsModule.ignoreBattery(); await update("alarm", value); if (value) toast("Timers will now run after each set."); else toast("Stopped timers running after each set."); }} title={name} /> ), }, { name: "Vibrate", renderItem: (name: string) => ( { setValue("vibrate", value); await update("vibrate", value); if (value) toast("Timers will now run after each set."); else toast("Stopped timers running after each set."); }} title={name} /> ), }, { name: "Disable sound", renderItem: (name: string) => ( { setValue("noSound", value); await update("noSound", value); if (value) toast("Alarms will no longer make a sound."); else toast("Enabled sound for alarms."); }} title={name} /> ), }, { name: "Notifications", renderItem: (name: string) => ( { setValue("notify", value); await update("notify", value); if (value) toast("Show notifications for new records."); else toast("Stopped notifications for new records."); }} title={name} /> ), }, { name: "Show images", renderItem: (name: string) => ( { setValue("images", value); await update("images", value); if (value) toast("Show images for sets."); else toast("Hid images for sets."); }} title={name} /> ), }, { name: "Show unit", renderItem: (name: string) => ( { setValue("showUnit", value); await update("showUnit", value); if (value) toast("Show option to select unit for sets."); else toast("Hid unit option for sets."); }} title={name} /> ), }, { name: "Show steps", renderItem: (name: string) => ( { setValue("steps", value); await update("steps", value); if (value) toast("Show steps for exercises."); else toast("Hid steps for exercises."); }} title={name} /> ), }, { name: "Show date", renderItem: (name: string) => ( { setValue("showDate", value); await update("showDate", value); if (value) toast("Show date for sets."); else toast("Hid date on sets."); }} title={name} /> ), }, { name: "Automatic backup", renderItem: (name: string) => ( { setValue("backup", value); await update("backup", value); 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(); } }} title={name} /> ), }, { name: `Alarm sound: ${soundString || "Default"}`, renderItem: (name: string) => ( ), }, { name: "Export database", renderItem: (name: string) => ( ), }, { name: "Import database", renderItem: (name: string) => ( ), }, { name: "Delete database", renderItem: (name: string) => ( ), }, ]; return ( <> item.name.toLowerCase().includes(term.toLowerCase()) )} renderItem={({ item }) => item.renderItem(item.name)} style={{ flex: 1, paddingTop: PADDING }} /> Importing a database overwrites your current data. This action cannot be reversed! Deleting your database wipes your current data. This action cannot be reversed! ); }