Skip to content

Commit

Permalink
Combine methods to fetch default schema and files from query into one
Browse files Browse the repository at this point in the history
  • Loading branch information
graphemecluster committed Oct 4, 2024
1 parent fc0f631 commit 8ea830c
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 70 deletions.
46 changes: 4 additions & 42 deletions src/Components/CreateSchemaDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

import ExplorerFolder from "./ExplorerFolder";
import Spinner from "./Spinner";
import actions from "../actions";
import Swal from "../Classes/SwalReact";
import { invalidCharsRegex, newFileTemplate, tshetUinhExamplesURLPrefix } from "../consts";
import samples from "../samples";
import { fetchFile, normalizeFileName } from "../utils";

import type { Folder, Sample, SchemaState, UseMainState } from "../consts";
import type { Folder, MainState, Sample, SchemaState } from "../consts";
import type { ChangeEventHandler, RefObject } from "react";

const Container = styled.dialog`
Expand Down Expand Up @@ -169,19 +167,16 @@ const Loading = styled.div`
background-color: rgba(249, 250, 251, 0.6);
margin: -2rem;
`;
const LoadModal = styled.div`
margin-top: 3rem;
color: #bbb;
`;

interface CreateSchemaDialogProps extends UseMainState {
interface CreateSchemaDialogProps {
state: MainState;
getDefaultFileName(sample: string): string;
schemaLoaded(schema: Omit<SchemaState, "parameters">): void;
hasSchemaName(name: string): boolean;
}

const CreateSchemaDialog = forwardRef<HTMLDialogElement, CreateSchemaDialogProps>(function CreateSchemaDialog(
{ state: { schemas }, setState, getDefaultFileName, schemaLoaded, hasSchemaName },
{ state: { schemas }, getDefaultFileName, schemaLoaded, hasSchemaName },
ref,
) {
const [createSchemaName, setCreateSchemaName] = useState(() => getDefaultFileName("") + ".js");
Expand Down Expand Up @@ -249,39 +244,6 @@ const CreateSchemaDialog = forwardRef<HTMLDialogElement, CreateSchemaDialogProps
}
}, [createSchemaName, createSchemaSample, schemaLoaded, ref]);

useEffect(() => {
if (schemas.length) return;
Swal.fire({
html: (
<LoadModal>
<Spinner />
<h2>載入中……</h2>
</LoadModal>
),
allowOutsideClick: false,
allowEscapeKey: false,
showConfirmButton: false,
});
(async () => {
try {
setState(
actions.addSchema({
name: "tupa.js",
input: await fetchFile(tshetUinhExamplesURLPrefix + "tupa.js"),
}),
);
Swal.close();
} catch {
setState(
actions.addSchema({
name: "untitled.js",
input: newFileTemplate,
}),
);
}
})();
}, [schemas, setState]);

const inputChange: ChangeEventHandler<HTMLInputElement> = useCallback(
event => setCreateSchemaName(event.target.value),
[],
Expand Down
41 changes: 31 additions & 10 deletions src/Components/SchemaEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import CreateSchemaDialog from "./CreateSchemaDialog";
import Spinner from "./Spinner";
import actions from "../actions";
import Swal from "../Classes/SwalReact";
import { codeFontFamily, invalidCharsRegex, noop } from "../consts";
import { codeFontFamily, invalidCharsRegex, newFileTemplate, noop, tshetUinhExamplesURLPrefix } from "../consts";
import "../editor/setup";
import { memoize, normalizeFileName, notifyError, showLoadingDialog } from "../utils";
import { fetchFile, memoize, normalizeFileName, notifyError, showLoadingModal } from "../utils";

import type { UseMainState, ReactNode } from "../consts";
import type { MouseEvent, MutableRefObject } from "react";
Expand Down Expand Up @@ -282,14 +282,33 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH
);

useEffect(() => {
async function fetchQueryFiles() {
async function loadSchemas() {
const query = new URLSearchParams(location.search);
history.replaceState(null, document.title, location.pathname); // Remove query
const hrefs = query.getAll("script");
if (!hrefs.length) return;
if (!hrefs.length && schemas.length) return;
const abortController = new AbortController();
const { signal } = abortController;
showLoadingDialog("正在載入檔案,請稍候……", abortController);
showLoadingModal(abortController);
if (!hrefs.length) {
try {
setState(
actions.addSchema({
name: "tupa.js",
input: await fetchFile(tshetUinhExamplesURLPrefix + "tupa.js", signal),
}),
);
} catch {
setState(
actions.addSchema({
name: "untitled.js",
input: newFileTemplate,
}),
);
}
Swal.close();
return;
}
const names = query.getAll("name");
let i = 0;
const fetchResults = await Promise.allSettled(
Expand Down Expand Up @@ -329,18 +348,21 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH
errors.push(result.reason);
}
}
// The file names may be incorrect in strict mode, but are fine in production build
await addFilesToSchema(files);
// Add `tupa.js` if all fetches failed and no schemas present
if (errors.length === fetchResults.length && !schemas.length) await loadSchemas();
Swal.close();
if (!signal.aborted) {
if (errors.length > 1) {
notifyError(`${errors.length} 個檔案無法載入`, new AggregateError(errors));
notifyError(`${errors.length} 個方案無法載入`, new AggregateError(errors));
} else if (errors.length === 1) {
notifyError(fetchResults.length === 1 ? "無法載入檔案" : "1 個檔案無法載入", errors[0]);
notifyError(fetchResults.length === 1 ? "無法載入方案" : "1 個方案無法載入", errors[0]);
}
}
}
fetchQueryFiles();
}, [addFilesToSchema]);
loadSchemas();
}, [schemas, setState, addFilesToSchema]);

const deleteSchema = useCallback(
async (name: string) => {
Expand Down Expand Up @@ -759,7 +781,6 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH
<CreateSchemaDialog
ref={dialogRef}
state={state}
setState={setState}
schemaLoaded={useCallback(schema => setState(actions.addSchema(schema)), [setState])}
getDefaultFileName={getDefaultFileName}
hasSchemaName={useCallback(name => !!schemas.find(schema => schema.name === name), [schemas])}
Expand Down
30 changes: 12 additions & 18 deletions src/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,26 @@
import { css as stylesheet } from "@emotion/css";
import styled from "@emotion/styled";

import Swal from "./Classes/SwalReact";
import Spinner from "./Components/Spinner";

import type { SweetAlertOptions } from "sweetalert2";

const loadingModal = stylesheet`
display: flex !important;
flex-direction: column;
align-items: center;
gap: 2rem;
margin: 1rem;
overflow: unset;
const LoadModal = styled.div`
margin-top: 3rem;
color: #bbb;
`;

export function showLoadingDialog(msg: string, abortController: AbortController) {
export function showLoadingModal(abortController: AbortController) {
Swal.fire({
title: "載入中",
html: (
<>
<div>{msg}</div>
<LoadModal>
<Spinner />
</>
<h2>正在載入方案……</h2>
</LoadModal>
),
customClass: {
htmlContainer: loadingModal,
},
allowOutsideClick: false,
allowEscapeKey: false,
showConfirmButton: false,
showCancelButton: true,
cancelButtonText: "取消",
Expand Down Expand Up @@ -82,14 +76,14 @@ export function notifyError(msg: string, err?: unknown) {
return new Error(msg, err instanceof Error ? { cause: err } : {});
}

export async function fetchFile(input: string) {
export async function fetchFile(href: string | URL, signal: AbortSignal | null = null) {
try {
const response = await fetch(input, { cache: "no-cache" });
const response = await fetch(href, { cache: "no-cache", signal });
const text = await response.text();
if (!response.ok) throw new Error(text);
return text;
} catch (err) {
throw notifyError("載入檔案失敗", err);
throw signal?.aborted ? err : notifyError("載入檔案失敗", err);
}
}

Expand Down

0 comments on commit 8ea830c

Please sign in to comment.