Source: lib/helper/form/generic.js

import {distinctUntilChanged, startWith, map, tap, share} from "rxjs/operators";
import { bind } from "@react-rxjs/core";
import { createSignal } from "@react-rxjs/utils";
import {combineLatest, ReplaySubject} from "rxjs";
import GENERAL_STORE_STATES from "@/shared/enum/general";
import getStore from "@/shared/store/index";
import GenericUserHelper from "@/shared/helper/user/generic";

const genericFormHelperInstances = {
  meeting: new Map(),
  task: new Map(),
  persons: new Map(),
  questionnaire: new Map(),
  investigation: new Map(),
};

const formInfoDefault = {
  status: GENERAL_STORE_STATES.INIT,
};

const createFormInfo = ({ status, signature, signedAt, deadline, category } = {}) => {
  let issuedAt = "";
  if (signature && status !== "EDIT") {
    //https://stackoverflow.com/a/38552302
    const base64 = signature.split(".")[1].replace(/-/g, "+").replace(/_/g, "/");
    const jsonPayload = decodeURIComponent(
      window
        .atob(base64)
        .split("")
        .map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2))
        .join("")
    );
    issuedAt = JSON.parse(jsonPayload).iat * 1000;
  }
  const returnObject = { status: status ?? formInfoDefault.status, signedAt, issuedAt, category };
  if (typeof deadline !== "undefined") {
    returnObject.deadline = deadline;
  }
  return returnObject;
};

const GenericFormHelper = (type, id) => {
  let storeId = id;
  const storeType = type;
  // TODO: Mobx seems like a better solution
  let lastKnownFormInfo = formInfoDefault;
  const { storeId$ } = getStore(storeType, storeId).getStore();
  storeId$.subscribe((currentStoreId) => {
    if (storeId !== currentStoreId) {
      // Current workaround to add 'this' also as the other identifier.
      const originalStore = genericFormHelperInstances[storeType].get(id);
      if (originalStore) {
        genericFormHelperInstances[storeType].set(currentStoreId, originalStore);
      }
    }
    storeId = currentStoreId;
  });
  const [formInfoPipe$, updateFormInfo] = createSignal(createFormInfo);
  formInfoPipe$.subscribe((newValue) => {
    lastKnownFormInfo = newValue;
  });
  const generalFormInfo$ = formInfoPipe$.pipe(
    startWith(null),
    map((v) => v ?? lastKnownFormInfo),
    distinctUntilChanged((prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)),
    share({
      connector: () => new ReplaySubject(1),
      resetOnRefCountZero: false, // Do not reset if ref count becomes zero
    })
  );

  const isSubmitted$ = generalFormInfo$.pipe(
    // In the future add more constraints here.
    map(({ status }) => {
      if (storeType === "meeting") {
        return [GENERAL_STORE_STATES.SUBMITTED, "ASUB"].includes(status);
      }
      return status === GENERAL_STORE_STATES.SUBMITTED;
    }),
    distinctUntilChanged(),
  );

  const isFormReadOnly$ = combineLatest(
      isSubmitted$,
      GenericUserHelper.isMunicipalityStaff$
  ).pipe(
      // Staff is not allowed to write changes to the questionnaire
      map(([formReadyOnly, isStaff]) => formReadyOnly || (isStaff && type === "questionnaire"))
  );
  return {
    get info$() {
      return generalFormInfo$;
    },
    /**
     *
     * @param rawDynamoDBObject
     * @param {boolean} [awaitResult=false] If true, wait for the result to return
     * @returns {Promise<Object>|void}
     */
    updateInfo: async function (rawDynamoDBObject, awaitResult = false) {
      const fn = () => {
        updateFormInfo({
          ...lastKnownFormInfo,
          ...rawDynamoDBObject
        });
      }
      if (awaitResult) {
        let subscription;
        return new Promise((resolve) => {
          subscription = formInfoPipe$.subscribe((value) => {
            resolve(value);
          });
          fn();
        }).then((v) => {
          subscription.unsubscribe();
          return v;
        })
      }
      return void fn();
    },
    get isFormReadOnly$() {
      return isFormReadOnly$;
    },
    get isSubmitted$() {
      return isSubmitted$;
    }
  };
};
function getGenericFormHelper(type, id) {
  // Presumably change Store to Factory design pattern
  let store = genericFormHelperInstances[type].get(id);
  if (!store) {
    store = GenericFormHelper(type, id);
    genericFormHelperInstances[type].set(id, store);
  }
  return store;
}

export const useGenericFormInfo = bind((type, id) => getGenericFormHelper(type, id).info$, formInfoDefault)[0];
export const useIsSubmitted = bind(
  (type, id) => getGenericFormHelper(type, id).isSubmitted$,
  null
)[0];
export const useIsFormReadOnly = bind((type, id) => getGenericFormHelper(type, id).isFormReadOnly$, false)[0];

export default getGenericFormHelper;