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 { check, PERMISSIONS, request, RESULTS } from "react-native-permissions"; import AppInput from "./AppInput"; import ConfirmDialog from "./ConfirmDialog"; import { PADDING } from "./constants"; import { AppDataSource } from "./data-source"; import { setRepo, settingsRepo } from "./db"; import { DrawerParams } from "./drawer-params"; import DrawerHeader from "./DrawerHeader"; import { darkOptions, lightOptions, themeOptions } from "./options"; import Page from "./Page"; import Select from "./Select"; import Settings from "./settings"; import Switch from "./Switch"; import { toast } from "./toast"; import { useAppTheme } 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, } = useAppTheme(); 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 backupString = useMemo(() => { if (!settings.backupDir) return null; console.log(settings.backupDir); const split = decodeURIComponent(settings.backupDir).split(":"); return split.pop(); }, [settings.backupDir]); 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); await update("backup", false); reset({ index: 0, routes: [{ name: "Settings" }] }); }, [reset, update]); const today = new Date(); const data: Item[] = [ { name: "Start up page", renderItem: (name: string) => ( { setValue("theme", value); setTheme(value); await update("theme", value); 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."); }} /> ), }, { name: "Dark color", renderItem: (name: string) => ( { setValue("lightColor", value); setLightColor(value); await update("lightColor", value); toast("Set primary color for light mode."); }} /> ), }, { name: "Date format", renderItem: (name: string) => ( { setValue("autoConvert", value); await update("autoConvert", value); if (value) toast(`Sets now automatically convert to ${value}`); else toast("Stopped automatically converting sets."); }} /> ), }, { 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(); setValue("backupDir", result.uri); await update("backupDir", result.uri); console.log(`${SettingsPage.name}.backup:`, { result }); toast("Backup database daily."); NativeModules.BackupModule.start(result.uri); } else { toast("Stopped backing up daily"); NativeModules.BackupModule.stop(); } }} title={name} /> ), }, { name: `Backup directory: ${backupString || "Downloads"}`, renderItem: (name: string) => ( ), }, { 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! ); }