Skip to content

Commit

Permalink
Error handling for about page image uploads
Browse files Browse the repository at this point in the history
  • Loading branch information
underbluewaters committed Jan 17, 2025
1 parent 9ee9f46 commit 4855aa5
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 60 deletions.
4 changes: 3 additions & 1 deletion packages/api/src/plugins/fileUploadPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ const FileUploadPlugin = makeExtendSchemaPlugin((build) => {
const isCloudflareImagesSupported =
cloudflareImagesSupported.includes(contentType) &&
fileSizeBytes < bytes("10mb");
if (isCloudflareImagesSupported) {
if (usage === "about_page" && fileSizeBytes >= bytes("10mb")) {
throw new Error("File size must be less than 10MB");
} else if (isCloudflareImagesSupported) {
const img = await getDirectCreatorUploadUrl();

const { rows } = await context.adminPool.query(
Expand Down
4 changes: 3 additions & 1 deletion packages/client/src/admin/AboutPageSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import "prosemirror-image-plugin/dist/styles/common.css";
import "./prosemirror-image.css";
import { useApolloClient } from "@apollo/client";
import { startImageUpload } from "prosemirror-image-plugin";
import { useGlobalErrorHandler } from "../components/GlobalErrorHandler";

export default function AboutPageSettings({
projectId,
Expand All @@ -34,9 +35,10 @@ export default function AboutPageSettings({
});

const client = useApolloClient();
const onError = useGlobalErrorHandler();

const { schema, plugins, imageSettings } = useMemo(() => {
return createAboutPageEditorConfig(client, projectId);
return createAboutPageEditorConfig(client, projectId, onError);
}, [client, projectId]);

const [mutate, mutationState] = useUpdateAboutPageContentsMutation();
Expand Down
16 changes: 14 additions & 2 deletions packages/client/src/editor/EditorMenuBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,7 @@ function ImageModal({
}) {
const { t } = useTranslation();
const [saving, setSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
return (
<Modal onRequestClose={onRequestClose} title={t("Upload Image")}>
<p className="text-sm text-gray-500">
Expand Down Expand Up @@ -990,17 +991,28 @@ function ImageModal({
type="file"
name="image"
accept="image/*"
onChange={(e) => {
const file = e.target.files?.[0];
if (file && file.size >= 10 * 1024 * 1024) {
setError("File must be less than 10MB");
} else {
setError(null);
}
}}
/>
<input
className="block w-128 border-gray-300 rounded-md focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50 text-black"
type="text"
name="alt-text"
placeholder="Alt text"
/>
{error && <p className="text-red-500 text-sm py-2">{error}</p>}
<div className="space-x-2 pt-4">
<button
className="border rounded px-2 py-0.5 border-gray-500 bg-gray-200 "
disabled={saving}
className={`border rounded px-2 py-0.5 border-gray-500 bg-gray-200 ${
error || saving ? "opacity-50" : ""
}`}
disabled={saving || Boolean(error)}
type="submit"
>
{t("Submit")}
Expand Down
122 changes: 66 additions & 56 deletions packages/client/src/editor/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,66 +261,76 @@ const aboutPageSchema = new Schema({

export function createAboutPageEditorConfig(
client: ApolloClient<any>,
projectId: number
projectId: number,
onError: (e: Error) => void
) {
const uploadFile: (file: File) => Promise<string> = async (file) => {
const response = await client.mutate({
mutation: CreateFileUploadForAboutPageDocument,
variables: {
contentType: file.type,
filename: file.name,
fileSizeBytes: file.size,
projectId,
},
});
const uploadUrl =
response?.data?.createFileUpload?.cloudflareImagesUploadUrl;
if (!uploadUrl) {
throw new Error("Failed to get upload URL");
}
const formData = new FormData();
formData.append("file", file);
const res = await axios({
url: uploadUrl,
method: "POST",
data: formData,
});
if (
"data" in res &&
res.data &&
"result" in res.data &&
res.data["result"] &&
"variants" in res.data["result"] &&
res.data["result"]["variants"] &&
Array.isArray(res.data["result"]["variants"])
) {
const variants = res.data["result"]["variants"] as string[];
const prosemirrorEmbed = variants.find((variant: any) =>
/prosemirrorEmbed/.test(variant)
);
if (!prosemirrorEmbed) {
throw new Error("Could not find prosemirrorEmbed variant");
try {
const response = await client
.mutate({
mutation: CreateFileUploadForAboutPageDocument,
variables: {
contentType: file.type,
filename: file.name,
fileSizeBytes: file.size,
projectId,
},
})
.catch((e) => {
throw new Error(`Failed to create file upload. ${e.message}`);
});
const uploadUrl =
response?.data?.createFileUpload?.cloudflareImagesUploadUrl;
if (!uploadUrl) {
throw new Error("Failed to get upload URL");
}
// This rediculous hack is necessary to avoid having the image
// flash with a broken icon (in chrome, others?) for a second
// before it finally loads. The image is preloaded in a hidden
// position before being finally inserted into the prosemirror
// document.
return new Promise((resolve) => {
const img = document.createElement("img");
img.src = prosemirrorEmbed;
img.setAttribute(
"style",
"position: absolute; top: -10000px; left: -10000px;"
);
img.onload = () => {
resolve(prosemirrorEmbed);
document.body.removeChild(img);
};
document.body.append(img);
const formData = new FormData();
formData.append("file", file);
const res = await axios({
url: uploadUrl,
method: "POST",
data: formData,
});
} else {
throw new Error("Could not get variants from upload response");
if (
"data" in res &&
res.data &&
"result" in res.data &&
res.data["result"] &&
"variants" in res.data["result"] &&
res.data["result"]["variants"] &&
Array.isArray(res.data["result"]["variants"])
) {
const variants = res.data["result"]["variants"] as string[];
const prosemirrorEmbed = variants.find((variant: any) =>
/prosemirrorEmbed/.test(variant)
);
if (!prosemirrorEmbed) {
throw new Error("Could not find prosemirrorEmbed variant");
}
// This rediculous hack is necessary to avoid having the image
// flash with a broken icon (in chrome, others?) for a second
// before it finally loads. The image is preloaded in a hidden
// position before being finally inserted into the prosemirror
// document.
return new Promise((resolve) => {
const img = document.createElement("img");
img.src = prosemirrorEmbed;
img.setAttribute(
"style",
"position: absolute; top: -10000px; left: -10000px;"
);
img.onload = () => {
resolve(prosemirrorEmbed);
document.body.removeChild(img);
};
document.body.append(img);
});
} else {
throw new Error("Could not get variants from upload response");
}
} catch (e) {
onError(e);
throw e;
}
};
return {
Expand Down

0 comments on commit 4855aa5

Please sign in to comment.