import { ASSERT, LOG } from "@/utilities/console"; import { get, isNull, isUndefined, uniqBy } from "lodash"; import { useCallback, useMemo, useRef } from "react"; import { FormKey, useFormActions, useStagedValues, } from "../utilities/FormValues"; import { useSettings } from "../utilities/SettingsProvider"; import { useSubmitHookWith } from "./HooksProvider"; export interface BaseInput { disabled?: boolean; settingKey: string; location?: FormKey; settingOptions?: SettingValueOptions; } export type SettingValueOptions = { original?: boolean; defaultValue?: T; onLoaded?: (settings: Settings, storage: Storage) => T; onSaved?: (value: T) => unknown; onSubmit?: (value: T) => unknown; }; export function useBaseInput(props: T & BaseInput) { const { settingKey, settingOptions, location, ...rest } = props; // TODO: Opti options const value = useSettingValue(settingKey, settingOptions); const { setValue } = useFormActions(); const update = useCallback( (newValue: V | null) => { const moddedValue = (newValue && settingOptions?.onSaved?.(newValue)) ?? newValue; setValue(moddedValue, settingKey, location); }, [settingOptions, setValue, settingKey, location] ); return { value, update, rest }; } export function useSettingValue( key: string, options?: SettingValueOptions ): Readonly> { const settings = useSettings(); const storage = window.localStorage; const isSettingsKey = useMemo(() => key.startsWith("settings"), [key]); ASSERT(isSettingsKey === false && key.startsWith("storage")); const optionsRef = useRef(options); optionsRef.current = options; useSubmitHookWith(key, options?.onSubmit); const originalValue = useMemo(() => { const onLoaded = optionsRef.current?.onLoaded; const defaultValue = optionsRef.current?.defaultValue; if (onLoaded) { LOG("info", `${key} is using custom loader`); return onLoaded(settings, storage); } let value: Nullable = null; if (isSettingsKey) { const path = key.replaceAll("-", "."); value = get({ settings }, path, null) as Nullable; } else { const storageValue = storage.getItem(key); if (storageValue !== null) { value = JSON.parse(storageValue); } } if (defaultValue && (isNull(value) || isUndefined(value))) { LOG("info", `${key} is falling back to`, defaultValue); return defaultValue; } return value; }, [isSettingsKey, key, settings, storage]); const stagedValue = useStagedValues(); if (key in stagedValue && optionsRef.current?.original !== true) { return stagedValue[key] as T; } else { return originalValue; } } export function useUpdateArray(key: string, compare: keyof T) { const { setValue } = useFormActions(); const stagedValue = useStagedValues(); const compareRef = useRef(compare); compareRef.current = compare; const staged: T[] = useMemo(() => { if (key in stagedValue) { return stagedValue[key]; } else { return []; } }, [key, stagedValue]); return useCallback( (v: T) => { const newArray = uniqBy([v, ...staged], compareRef.current); setValue(newArray, key); }, [staged, setValue, key] ); }