Skip to content
Snippets Groups Projects
utils.ts 2.74 KiB
Newer Older
import type { DialogMap, DialogOption, DialogSet } from "./types";

/**
 * Returns true, if input is a valid name of a dialogSet.
 * Intended to protect from loading anything other than dialogSet json.
 * @param input input to check
 */
function isValidDialogSetName(input: unknown) {
  if (typeof input !== "string") {
    return false;
  }
  if (input.length === 0) {
    return false;
  }
  if (input.length > 50) {
    return false;
  }
  return !/[^a-zA-Z0-9\-\_]/i.test(input);
}

function getParamValue(paramName: string): string | null {
  return new URLSearchParams(window.location.search).get(paramName);
}

/**
 * Fetch a dialogSet based on the hash value
 */
export async function fetchDialogSet(): Promise<DialogSet> {
  const setName = getParamValue("dialogSet");
  if (!isValidDialogSetName(setName))
    throw new Error("Name is not valid dialogSet name");

  const dialogSetUrl = "./dialogs/" + setName + ".json";

  try {
    const dialogSet: DialogSet = await (await fetch(dialogSetUrl)).json();
    return dialogSet;
  } catch (error) {
    throw error;
  }
}

export function findDialogSetProblems(dialogSet: DialogSet) {
  const dialogReferenceProblems = checkForInvalidDialogReferences(dialogSet);
  return [...dialogReferenceProblems];
}

interface DialogReferenceProblem {
  type: "DialogReferenceProblem";
  sourceDialog: string;
  text: string;
}

function checkForInvalidDialogReferences(
  dialogSet: DialogSet
): DialogReferenceProblem[] {
  const dialogs = dialogSet.dialogs;
  const problems: DialogReferenceProblem[] = [];
  const allDialogKeys = Object.keys(dialogs);
  const allLinkedToDialogKeys: Set<string> = new Set();
  for (const dialogName of allDialogKeys) {
    // TODO check all buttons for invalid references
    for (const { text, linksToDialog } of dialogs[dialogName].options) {
      allLinkedToDialogKeys.add(linksToDialog);
      if (!allDialogKeys.includes(linksToDialog)) {
        problems.push({
          type: "DialogReferenceProblem",
          sourceDialog: dialogName,
          text: `Dialog ${dialogName}'s option "${text}" links to unknown dialog '${linksToDialog}'`,
        });
      }

      if (linksToDialog === dialogName) {
        problems.push({
          type: "DialogReferenceProblem",
          sourceDialog: dialogName,
          text: `Dialog ${dialogName}'s option "${text}" links to itself!`,
        });
      }
    }
  }

  for (const dialogName of allDialogKeys) {
    if (
      !allLinkedToDialogKeys.has(dialogName) &&
      dialogSet.startDialogName !== dialogName
    ) {
      problems.push({
        type: "DialogReferenceProblem",
        sourceDialog: dialogName,
        text: `Dialog ${dialogName} is unreachable: Neither linked to from any other dialog, nor startDialog`,
      });
    }
  }
  return problems;
}