chore: Automatically display errors as toast if uncaught in actions (#6482)
Reduces plumbing
This commit is contained in:
@@ -86,19 +86,14 @@ export const clearIndexedDB = createAction({
|
||||
});
|
||||
|
||||
export const createTestUsers = createAction({
|
||||
name: "Create test users",
|
||||
name: "Create 10 test users",
|
||||
icon: <UserIcon />,
|
||||
section: DeveloperSection,
|
||||
visible: () => env.ENVIRONMENT === "development",
|
||||
perform: async () => {
|
||||
const count = 10;
|
||||
|
||||
try {
|
||||
await client.post("/developer.create_test_users", { count });
|
||||
toast.message(`${count} test users created`);
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -251,7 +251,6 @@ export const unpublishDocument = createAction({
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await document.unpublish();
|
||||
|
||||
toast.success(
|
||||
@@ -259,9 +258,6 @@ export const unpublishDocument = createAction({
|
||||
documentName: document.noun,
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -288,9 +284,7 @@ export const subscribeDocument = createAction({
|
||||
}
|
||||
|
||||
const document = stores.documents.get(activeDocumentId);
|
||||
|
||||
await document?.subscribe();
|
||||
|
||||
toast.success(t("Subscribed to document notifications"));
|
||||
},
|
||||
});
|
||||
@@ -540,7 +534,6 @@ export const pinDocumentToCollection = createAction({
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const document = stores.documents.get(activeDocumentId);
|
||||
await document?.pin(document.collectionId);
|
||||
|
||||
@@ -549,9 +542,6 @@ export const pinDocumentToCollection = createAction({
|
||||
if (!collection || !location.pathname.startsWith(collection?.url)) {
|
||||
toast.success(t("Pinned to collection"));
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -583,15 +573,11 @@ export const pinDocumentToHome = createAction({
|
||||
}
|
||||
const document = stores.documents.get(activeDocumentId);
|
||||
|
||||
try {
|
||||
await document?.pin();
|
||||
|
||||
if (location.pathname !== homePath()) {
|
||||
toast.success(t("Pinned to home"));
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -641,7 +627,6 @@ export const importDocument = createAction({
|
||||
input.onchange = async (ev) => {
|
||||
const files = getEventFiles(ev);
|
||||
|
||||
try {
|
||||
const file = files[0];
|
||||
const document = await documents.import(
|
||||
file,
|
||||
@@ -652,10 +637,6 @@ export const importDocument = createAction({
|
||||
}
|
||||
);
|
||||
history.push(document.url);
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
input.click();
|
||||
|
||||
@@ -74,13 +74,7 @@ export function actionToMenuItem(
|
||||
icon,
|
||||
visible,
|
||||
dangerous: action.dangerous,
|
||||
onClick: () => {
|
||||
try {
|
||||
action.perform?.(context);
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
},
|
||||
onClick: () => performAction(action, context),
|
||||
selected: action.selected?.(context),
|
||||
};
|
||||
}
|
||||
@@ -114,10 +108,18 @@ export function actionToKBar(
|
||||
keywords: action.keywords ?? "",
|
||||
shortcut: action.shortcut || [],
|
||||
icon: resolvedIcon,
|
||||
perform: action.perform ? () => action.perform?.(context) : undefined,
|
||||
perform: action.perform
|
||||
? () => performAction(action, context)
|
||||
: undefined,
|
||||
},
|
||||
].concat(
|
||||
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||
children.map((child) => ({ ...child, parent: child.parent ?? action.id }))
|
||||
);
|
||||
}
|
||||
|
||||
export async function performAction(action: Action, context: ActionContext) {
|
||||
return action.perform?.(context).catch((err: Error) => {
|
||||
toast.error(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import * as React from "react";
|
||||
import Tooltip, { Props as TooltipProps } from "~/components/Tooltip";
|
||||
import { performAction } from "~/actions";
|
||||
import { Action, ActionContext } from "~/types";
|
||||
|
||||
export type Props = React.HTMLAttributes<HTMLButtonElement> & {
|
||||
@@ -60,10 +61,10 @@ const ActionButton = React.forwardRef<HTMLButtonElement, Props>(
|
||||
? (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const response = action.perform?.(actionContext);
|
||||
const response = performAction(action, actionContext);
|
||||
if (response?.finally) {
|
||||
setExecuting(true);
|
||||
response.finally(() => setExecuting(false));
|
||||
void response.finally(() => setExecuting(false));
|
||||
}
|
||||
}
|
||||
: rest.onClick
|
||||
|
||||
@@ -107,6 +107,10 @@ export type Action = {
|
||||
placeholder?: ((context: ActionContext) => string) | string;
|
||||
selected?: (context: ActionContext) => boolean;
|
||||
visible?: (context: ActionContext) => boolean;
|
||||
/**
|
||||
* Perform the action – note this should generally not be called directly, use `performAction`
|
||||
* instead. Errors will be caught and displayed to the user as a toast message.
|
||||
*/
|
||||
perform?: (context: ActionContext) => Promise<any> | any;
|
||||
children?: ((context: ActionContext) => Action[]) | Action[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user