All files / client/src/utils useTimeNow.ts

100% Statements 13/13
100% Branches 5/5
100% Functions 5/5
100% Lines 12/12

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                    520x 11869x 11869x         11869x 6399x                 520x 11807x 23306x 11807x 11807x 5442x       11807x                  
import dayjs, { Dayjs } from "dayjs";
import { useMemo, useSyncExternalStore } from "react";
import { useTranslation } from "react-i18next";
import { config } from "shared/config";
import { getTimeNowSnapshot, subscribeToClock } from "client/utils/clock";
import { useAppSelector } from "client/utils/hooks";
 
// Real wall-clock time, never the mocked test time. Use this over useTimeNow
// only where the value is compared against something the mock doesn't move
// either, such as a timestamp the server has already written
export const useRealTimeNow = (): Dayjs => {
  const { i18n } = useTranslation();
  const timeNowMs = useSyncExternalStore(subscribeToClock, getTimeNowSnapshot);
 
  // A Dayjs instance captures the active locale when it is constructed, so it
  // has to be rebuilt on a language change or anything formatted from it keeps
  // coming out in the language it was first read in
  return useMemo(
    () => dayjs(timeNowMs).locale(i18n.language),
    [timeNowMs, i18n.language],
  );
};
 
// The current time, mocked time included. A hook rather than a plain getter so
// a component re-renders as the clock advances and when a data poll brings in a
// new mocked time. Pass the value down to pure helpers instead of letting them
// reach for the store
export const useTimeNow = (): Dayjs => {
  const { i18n } = useTranslation();
  const testTime = useAppSelector((state) => state.testSettings.testTime);
  const realTimeNow = useRealTimeNow();
  const mockedTimeNow = useMemo(
    () => dayjs(testTime).locale(i18n.language),
    [testTime, i18n.language],
  );
 
  return config.client().loadedSettings !== "production" &&
    config.client().showTestValues &&
    // Fall back to the real time while no mocked time is set: dayjs("") is an
    // invalid date, which silently makes every time comparison false instead
    // of failing loudly
    testTime
    ? mockedTimeNow
    : realTimeNow;
};