aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend/src/pages/Settings/utilities/hooks.ts
blob: 00c8b9bef892db110c88c22e4ba0846c19fd4445 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { useCallback, useMemo, useRef } from "react";
import { get, isNull, isUndefined, uniqBy } from "lodash";
import {
  HookType,
  useFormActions,
  useStagedValues,
} from "@/pages/Settings/utilities/FormValues";
import { useSettings } from "@/pages/Settings/utilities/SettingsProvider";
import { LOG } from "@/utilities/console";

export interface BaseInput<T> {
  disabled?: boolean;
  settingKey: string;
  settingOptions?: SettingValueOptions<T>;
}

export type SettingValueOptions<T> = {
  original?: boolean;
  defaultValue?: T;
  onLoaded?: (settings: Settings) => T;
  onSaved?: (value: T) => unknown;
  onSubmit?: (value: T) => unknown;
};

export function useBaseInput<T, V>(props: T & BaseInput<V>) {
  const { settingKey, settingOptions, ...rest } = props;
  // TODO: Opti options
  const value = useSettingValue<V>(settingKey, settingOptions);

  const { setValue } = useFormActions();

  const update = useCallback(
    (newValue: V | null) => {
      const moddedValue =
        (newValue && settingOptions?.onSaved?.(newValue)) ?? newValue;

      setValue(moddedValue, settingKey, settingOptions?.onSubmit);
    },
    [settingOptions, setValue, settingKey],
  );

  return { value, update, rest };
}

export function useSettingValue<T>(
  key: string,
  options?: SettingValueOptions<T>,
): Readonly<Nullable<T>> {
  const settings = useSettings();

  const optionsRef = useRef(options);
  optionsRef.current = options;

  const originalValue = useMemo(() => {
    const onLoaded = optionsRef.current?.onLoaded;
    const defaultValue = optionsRef.current?.defaultValue;
    if (onLoaded && settings) {
      LOG("info", `${key} is using custom loader`);

      return onLoaded(settings);
    }

    const path = key.replaceAll("-", ".");

    const value = get({ settings }, path, null) as Nullable<T>;

    if (defaultValue && (isNull(value) || isUndefined(value))) {
      LOG("info", `${key} is falling back to`, defaultValue);

      return defaultValue;
    }

    return value;
  }, [key, settings]);

  const stagedValue = useStagedValues();

  if (key in stagedValue && optionsRef.current?.original !== true) {
    return stagedValue[key] as T;
  } else {
    return originalValue;
  }
}

export function useUpdateArray<T>(
  key: string,
  current: Readonly<T[]>,
  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 current;
    }
  }, [key, stagedValue, current]);

  return useCallback(
    (v: T, hook?: HookType) => {
      const newArray = uniqBy([v, ...staged], compareRef.current);
      setValue(newArray, key, hook);
    },
    [staged, setValue, key],
  );
}