diff --git a/src/common/api/mutations/index.ts b/src/common/api/mutations/index.ts index b7ded518e31..5613bf84dad 100644 --- a/src/common/api/mutations/index.ts +++ b/src/common/api/mutations/index.ts @@ -3,3 +3,4 @@ export * from "./create-reply"; export * from "./update-reply"; export * from "./user-activity"; export * from "./pin-reply"; +export * from "./upload-post-image"; diff --git a/src/common/api/mutations/upload-post-image.ts b/src/common/api/mutations/upload-post-image.ts new file mode 100644 index 00000000000..4b76db386dc --- /dev/null +++ b/src/common/api/mutations/upload-post-image.ts @@ -0,0 +1,75 @@ +import { useMutation } from "@tanstack/react-query"; +import { getAccessToken } from "../../helper/user-token"; +import { uploadImage } from "../misc"; +import { addImage } from "../private-api"; +import { error } from "../../components/feedback"; +import { _t } from "../../i18n"; +import axios from "axios"; +import { useMappedStore } from "../../store/use-mapped-store"; + +export function useUploadPostImage() { + const { activeUser, global } = useMappedStore(); + const { mutateAsync: upload } = useMutation({ + mutationKey: ["uploadPostImage"], + mutationFn: async ({ file }: { file: File }) => { + const username = activeUser?.username!; + let token = getAccessToken(username); + + if (!token) { + error(_t("editor-toolbar.image-error-cache")); + throw new Error("Token missed"); + } + + return uploadImage(file, token); + }, + onError: (e: Error) => { + if (axios.isAxiosError(e) && e.response?.status === 413) { + error(_t("editor-toolbar.image-error-size")); + } else if (e.message === "Token missed") { + error(_t("g.image-error-cache")); + } else { + error(_t("editor-toolbar.image-error")); + } + } + }); + + const { mutateAsync: add } = useMutation({ + mutationKey: ["addPostImage"], + mutationFn: async ({ url }: { url: string }) => { + const username = activeUser?.username!; + let token = getAccessToken(username); + + if (!token) { + error(_t("editor-toolbar.image-error-cache")); + throw new Error("Token missed"); + } + + if (global.usePrivate && url.length > 0) { + await addImage(username, url); + return; + } + + throw new Error("URL missed"); + }, + onError: (e: Error) => { + if (axios.isAxiosError(e)) { + error(_t("editor-toolbar.image-error-network")); + } else if (e.message === "Token missed") { + error(_t("g.image-error-cache")); + } else if (e.message === "URL missed") { + error(_t("editor-toolbar.image-error-url-missed")); + } else { + error(_t("editor-toolbar.image-error")); + } + } + }); + + return useMutation({ + mutationKey: ["uploadAndAddPostImage"], + mutationFn: async ({ file }: { file: File }) => { + const response = await upload({ file }); + await add(response); + return response; + } + }); +} diff --git a/src/common/components/editor-toolbar/index.tsx b/src/common/components/editor-toolbar/index.tsx index 3837afac597..9eb90201a52 100644 --- a/src/common/components/editor-toolbar/index.tsx +++ b/src/common/components/editor-toolbar/index.tsx @@ -29,11 +29,6 @@ import GifPicker from "../gif-picker"; import AddImageMobile from "../add-image-mobile"; import { insertOrReplace, replace } from "../../util/input-util"; import Gallery from "../gallery"; -import { getAccessToken } from "../../helper/user-token"; -import { uploadImage } from "../../api/misc"; -import { addImage } from "../../api/private-api"; -import { error } from "../feedback"; -import axios from "axios"; import Fragments from "../fragments"; import AddImage from "../add-image"; import AddLink from "../add-link"; @@ -41,6 +36,7 @@ import "./_index.scss"; import { PollsCreation, PollSnapshot } from "../../features/polls"; import { UilPanelAdd } from "@iconscout/react-unicons"; import { classNameObject } from "../../helper/class-name-object"; +import { useUploadPostImage } from "../../api/mutations"; interface Props { sm?: boolean; @@ -93,6 +89,7 @@ export function EditorToolbar({ const toolbarId = useMemo(() => v4(), []); const headers = useMemo(() => [...Array(3).keys()], []); + const uploadImage = useUploadPostImage(); const isMounted = useMountedState(); useEffect(() => { @@ -191,36 +188,12 @@ export function EditorToolbar({ }; const upload = async (file: File) => { - const username = activeUser?.username!; - const tempImgTag = `![Uploading ${file.name} #${Math.floor(Math.random() * 99)}]()\n\n`; insertText(tempImgTag); - let imageUrl: string; - try { - let token = getAccessToken(username); - if (token) { - const resp = await uploadImage(file, token); - imageUrl = resp.url; - - if (global.usePrivate && imageUrl.length > 0) { - addImage(username, imageUrl).then(); - } - - const imgTag = imageUrl.length > 0 && `![](${imageUrl})\n\n`; - - imgTag && replaceText(tempImgTag, imgTag); - } else { - error(_t("editor-toolbar.image-error-cache")); - } - } catch (e) { - if (axios.isAxiosError(e) && e.response?.status === 413) { - error(_t("editor-toolbar.image-error-size")); - } else { - error(_t("editor-toolbar.image-error")); - } - return; - } + const { url } = await uploadImage.mutateAsync({ file }); + const imgTag = url.length > 0 && `![](${url})\n\n`; + imgTag && replaceText(tempImgTag, imgTag); }; const checkFile = (filename: string) => diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 7b56ddbee43..05477387bb5 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1428,6 +1428,8 @@ "image-error-cache": "Couldn't upload image. You seem to be signed out. Try signing in again.", "image-error-size": "Image size is too large, try smaller size image again.", "image-error-conflict-name": "You have image with this filename already. Select it from gallery instead.", + "image-error-network": "Couldn't upload image. Network issue with status {{0}}.", + "image-error-url-missed": "Image uploading caused error. Please, try again.", "fragments": "Snippets", "link-image": "Link", "polls": "Polls"