bananas-commerce-admin
Version:
What's this, an admin for apes?
71 lines • 3.03 kB
JavaScript
import React, { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Suspense } from "react";
import { useApi } from "../contexts/ApiContext";
import { useUser } from "../contexts/UserContext";
import useAsyncError from "../hooks/useAsyncError";
import { hasAccess } from "../util/has_access";
import LoadingScreen from "./LoadingScreen";
export class DataLoadFailedError extends Error {
response;
constructor(response, message) {
super(message);
this.response = response;
}
}
export const ComponentLoader = ({ operation, ...rest }) => {
const api = useApi();
const { user } = useUser();
if (typeof operation === "string") {
operation = api.operations[operation];
}
if (operation == null) {
console.error(`[COMPONENT_LOADER] Could not find operation ${operation}`);
return null;
}
if (operation.component?.component == null) {
console.error(`[COMPONENT_LOADER] Could not find component for operation ${operation.id}`);
return null;
}
if (!hasAccess(user, operation.component.permission, operation.component.group)) {
return null;
}
rest.body ??= operation.component.body;
// NOTE: Consider merging with the rest properties instead of
// using the operation component's properties as defaults.
rest.params ??= operation.component.params;
rest.query ??= operation.component.query;
rest.headers ??= operation.component.headers;
return (React.createElement(ComponentLoaderInner, { operation: operation, ...rest }));
};
const ComponentLoaderInner = ({ operation, ...rest }) => {
const throwError = useAsyncError();
// TODO: Find a suitable type
const [data, setData] = useState();
const initialLoad = useRef(true);
const component = operation.component.component ?? (() => null);
const Component = useMemo(() => lazy(async () => ({ default: await Promise.resolve(component) })), [component]);
const refresh = useCallback(async () => {
const response = await operation.call(rest);
if (response.ok) {
setData(await response.json());
}
else {
// TODO: Report to Sentry
// TODO: Handle in the component, this will just unmount it.
// Things that suddenly dissapear are bit confusing, don't you think?
console.error(`[COMPONENT_LOADER] Data load failed with ${response.status} ${response.statusText}`);
setData(null);
}
}, [operation, rest, throwError]);
useEffect(() => {
if (initialLoad.current) {
initialLoad.current = false;
}
else {
refresh();
}
}, [rest.query, rest.params]);
return (React.createElement(Suspense, { fallback: React.createElement(LoadingScreen, null) }, data == null ? null : React.createElement(Component, { data: data, refresh: refresh, ...rest })));
};
export default ComponentLoader;
//# sourceMappingURL=ComponentLoader.js.map