All files / src/pages/flowsheet-page/economics/settings-panel/hooks useServerSyncedDraft.ts

65.75% Statements 48/73
51.06% Branches 24/47
33.33% Functions 3/9
79.16% Lines 38/48

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131                253x                         253x 253x 253x 759x   253x 759x 253x   253x 253x   13x   82x     143x       28x                     28x                   28x 28x 28x 28x     28x 253x             2024x   253x 82x 82x 82x 253x   253x 10x                     10x 10x           10x 10x 10x 307x       147x                 988x               15x    
import { useCallback, useEffect, useRef, useState } from "react";
import { isNewerServerData } from "../model/settingsDrafts";
import type {
  FieldMessages,
  SaveState,
  ServerDataStamp,
} from "../model/settingsTypes";
 
export function useServerSyncedDraft<TDraft extends object>({
  studyId,
  serverDraft,
  serverStamp,
  draftMatchesSavedDraft,
  fieldMessagesForDraft,
}: {
  studyId: number;
  serverDraft: TDraft;
  serverStamp: ServerDataStamp;
  draftMatchesSavedDraft: (nextDraft: TDraft, savedDraft: TDraft) => boolean;
  fieldMessagesForDraft: (draft: TDraft) => FieldMessages<TDraft>;
}) {
  const [draft, setDraft] = useState<TDraft>(serverDraft);
  const [dirty, setDirty] = useState(false);
  const [fieldMessages, setFieldMessages] = useState<FieldMessages<TDraft>>(
    () => fieldMessagesForDraft(serverDraft),
  );
  const [saveState, setSaveState] = useState<SaveState>({ kind: "idle" });
  const acceptedServerRef = useRef({ studyId, stamp: serverStamp });
  const unconfirmedSavedDraftRef = useRef<TDraft | null>(null);
 
  useEffect(() => {
    const studyChanged = acceptedServerRef.current.studyId !== studyId;
    if (studyChanged) {
      unconfirmedSavedDraftRef.current = null;
    }
    Iif (!studyChanged && dirty) return;
    if (
      !studyChanged &&
      !isNewerServerData(serverStamp, acceptedServerRef.current.stamp)
    ) {
      return;
    }
    const unconfirmedSavedDraft = unconfirmedSavedDraftRef.current;
    if (
      !studyChanged &&
      shouldKeepUnconfirmedSavedDraft(
        serverDraft,
        unconfirmedSavedDraft,
        draftMatchesSavedDraft,
      )
    ) {
      return;
    }
    unconfirmedSavedDraftRef.current = null;
    if (unconfirmedSavedDraft) {
      setDirty(false);
      setFieldMessages(fieldMessagesForDraft(unconfirmedSavedDraft));
      setSaveState((current) =>
        current.kind === "saved" ? current : { kind: "idle" },
      );
      acceptedServerRef.current = { studyId, stamp: serverStamp };
      return;
    }
    setDraft(serverDraft);
    setDirty(false);
    setFieldMessages(fieldMessagesForDraft(serverDraft));
    setSaveState((current) =>
      current.kind === "saved" ? current : { kind: "idle" },
    );
    acceptedServerRef.current = { studyId, stamp: serverStamp };
  }, [
    dirty,
    draftMatchesSavedDraft,
    fieldMessagesForDraft,
    serverDraft,
    serverStamp,
    studyId,
  ]);
 
  const updateDraft = useCallback((patch: Partial<TDraft>) => {
    setDraft((current) => ({ ...current, ...patch }));
    setDirty(true);
    setSaveState({ kind: "idle" });
  }, []);
 
  const confirmSavedDraft = useCallback(
    ({
      draft: savedDraft,
      hasFreshResponse,
      responseStamp,
      message,
    }: {
      draft: TDraft;
      hasFreshResponse: boolean;
      responseStamp: ServerDataStamp;
      message: string;
    }) => {
      unconfirmedSavedDraftRef.current = hasFreshResponse ? null : savedDraft;
      acceptedServerRef.current = {
        studyId,
        stamp: hasFreshResponse
          ? responseStamp
          : acceptedServerRef.current.stamp,
      };
      setDirty(false);
      setFieldMessages(fieldMessagesForDraft(savedDraft));
      setSaveState({ kind: "saved", message });
    },
    [fieldMessagesForDraft, studyId],
  );
 
  return {
    draft,
    dirty,
    fieldMessages,
    saveState,
    setFieldMessages,
    setSaveState,
    updateDraft,
    confirmSavedDraft,
  };
}
 
function shouldKeepUnconfirmedSavedDraft<TDraft>(
  nextDraft: TDraft,
  savedDraft: TDraft | null,
  matchesSavedDraft: (nextDraft: TDraft, savedDraft: TDraft) => boolean,
) {
  return savedDraft != null && !matchesSavedDraft(nextDraft, savedDraft);
}