From 8d3ea5bb3ebed71723159297b838cc8f32858e4d Mon Sep 17 00:00:00 2001 From: "M. Palanikannan" <73993394+Palanikannan1437@users.noreply.github.com> Date: Wed, 24 Jan 2024 18:56:19 +0530 Subject: [PATCH] =?UTF-8?q?fix:=20delete=20and=20restore=20for=20dynamic?= =?UTF-8?q?=20urls=20for=20minio=20hosted=20images=20fix=E2=80=A6=20(#3452?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: delete and restore for dynamic urls for minio hosted images fixed in spaces * feat: delete and restore images calls fixed for web --- .../peek-overview/comment/add-comment.tsx | 8 +++-- .../comment/comment-detail-card.tsx | 8 +++-- space/services/file.service.ts | 33 +++++++++++++++++++ .../inbox/modals/create-issue-modal.tsx | 7 ++-- web/components/issues/comment/add-comment.tsx | 9 +++-- .../issues/comment/comment-card.tsx | 9 +++-- web/components/issues/description-form.tsx | 11 ++++--- web/components/issues/draft-issue-form.tsx | 9 +++-- .../issue-activity/comments/comment-card.tsx | 8 +++-- .../comments/comment-create.tsx | 8 +++-- web/components/issues/issue-modal/form.tsx | 9 +++-- .../peek-overview/activity/comment-card.tsx | 9 +++-- .../peek-overview/activity/comment-editor.tsx | 9 +++-- .../projects/[projectId]/pages/[pageId].tsx | 9 +++-- web/services/file.service.ts | 33 +++++++++++++++++++ 15 files changed, 143 insertions(+), 36 deletions(-) diff --git a/space/components/issues/peek-overview/comment/add-comment.tsx b/space/components/issues/peek-overview/comment/add-comment.tsx index d6c3ce4e6..ef1a115d2 100644 --- a/space/components/issues/peek-overview/comment/add-comment.tsx +++ b/space/components/issues/peek-overview/comment/add-comment.tsx @@ -14,6 +14,7 @@ import { Comment } from "types/issue"; import { LiteTextEditorWithRef } from "@plane/lite-text-editor"; // service import fileService from "services/file.service"; +import { RootStore } from "store/root"; const defaultValues: Partial = { comment_html: "", @@ -35,6 +36,9 @@ export const AddComment: React.FC = observer((props) => { } = useForm({ defaultValues }); const router = useRouter(); + const { project }: RootStore = useMobxStore(); + const workspaceId = project.workspace?.id; + const { workspace_slug, project_slug } = router.query as { workspace_slug: string; project_slug: string }; const { user: userStore, issueDetails: issueDetailStore } = useMobxStore(); @@ -78,8 +82,8 @@ export const AddComment: React.FC = observer((props) => { }} cancelUploadImage={fileService.cancelUpload} uploadFile={fileService.getUploadFileFunction(workspace_slug as string)} - deleteFile={fileService.deleteImage} - restoreFile={fileService.restoreImage} + deleteFile={fileService.getDeleteImageFunction(workspaceId as string)} + restoreFile={fileService.getRestoreImageFunction(workspaceId as string)} ref={editorRef} value={ !value || value === "" || (typeof value === "object" && Object.keys(value).length === 0) diff --git a/space/components/issues/peek-overview/comment/comment-detail-card.tsx b/space/components/issues/peek-overview/comment/comment-detail-card.tsx index a82165140..7c6abe199 100644 --- a/space/components/issues/peek-overview/comment/comment-detail-card.tsx +++ b/space/components/issues/peek-overview/comment/comment-detail-card.tsx @@ -17,6 +17,7 @@ import { Comment } from "types/issue"; import fileService from "services/file.service"; import useEditorSuggestions from "hooks/use-editor-suggestions"; +import { RootStore } from "store/root"; type Props = { workspaceSlug: string; comment: Comment; @@ -24,6 +25,9 @@ type Props = { export const CommentCard: React.FC = observer((props) => { const { comment, workspaceSlug } = props; + const { project }: RootStore = useMobxStore(); + const workspaceId = project.workspace?.id; + // store const { user: userStore, issueDetails: issueDetailStore } = useMobxStore(); // states @@ -105,8 +109,8 @@ export const CommentCard: React.FC = observer((props) => { onEnterKeyPress={handleSubmit(handleCommentUpdate)} cancelUploadImage={fileService.cancelUpload} uploadFile={fileService.getUploadFileFunction(workspaceSlug)} - deleteFile={fileService.deleteImage} - restoreFile={fileService.restoreImage} + deleteFile={fileService.getDeleteImageFunction(workspaceId as string)} + restoreFile={fileService.getRestoreImageFunction(workspaceId as string)} ref={editorRef} value={value} debouncedUpdatesEnabled={false} diff --git a/space/services/file.service.ts b/space/services/file.service.ts index b2d1f6ccd..ecebf92b7 100644 --- a/space/services/file.service.ts +++ b/space/services/file.service.ts @@ -74,6 +74,39 @@ class FileService extends APIService { }; } + getDeleteImageFunction(workspaceId: string) { + return async (src: string) => { + try { + const assetUrlWithWorkspaceId = `${workspaceId}/${this.extractAssetIdFromUrl(src, workspaceId)}`; + const data = await this.deleteImage(assetUrlWithWorkspaceId); + return data; + } catch (e) { + console.error(e); + } + }; + } + + getRestoreImageFunction(workspaceId: string) { + return async (src: string) => { + try { + const assetUrlWithWorkspaceId = `${workspaceId}/${this.extractAssetIdFromUrl(src, workspaceId)}`; + const data = await this.restoreImage(assetUrlWithWorkspaceId); + return data; + } catch (e) { + console.error(e); + } + }; + } + + extractAssetIdFromUrl(src: string, workspaceId: string): string { + const indexWhereAssetIdStarts = src.indexOf(workspaceId) + workspaceId.length + 1; + if (indexWhereAssetIdStarts === -1) { + throw new Error("Workspace ID not found in source string"); + } + const assetUrl = src.substring(indexWhereAssetIdStarts); + return assetUrl; + } + async deleteImage(assetUrlWithWorkspaceId: string): Promise { return this.delete(`/api/workspaces/file-assets/${assetUrlWithWorkspaceId}/`) .then((response) => response?.status) diff --git a/web/components/inbox/modals/create-issue-modal.tsx b/web/components/inbox/modals/create-issue-modal.tsx index 3e56f575a..358a6f1ec 100644 --- a/web/components/inbox/modals/create-issue-modal.tsx +++ b/web/components/inbox/modals/create-issue-modal.tsx @@ -54,6 +54,9 @@ export const CreateInboxIssueModal: React.FC = observer((props) => { projectId: string; inboxId: string; }; + const workspaceStore = useWorkspace(); + const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string; + // store hooks const { createIssue } = useInboxIssues(); const { @@ -277,8 +280,8 @@ export const CreateInboxIssueModal: React.FC = observer((props) => {

" : value} diff --git a/web/components/issues/comment/add-comment.tsx b/web/components/issues/comment/add-comment.tsx index 1bd2c83d6..e9396eccc 100644 --- a/web/components/issues/comment/add-comment.tsx +++ b/web/components/issues/comment/add-comment.tsx @@ -2,7 +2,7 @@ import React from "react"; import { useRouter } from "next/router"; import { useForm, Controller } from "react-hook-form"; // hooks -import { useMention } from "hooks/store"; +import { useMention, useWorkspace } from "hooks/store"; // services import { FileService } from "services/file.service"; // components @@ -51,6 +51,9 @@ export const AddComment: React.FC = ({ disabled = false, onSubmit, showAc // router const router = useRouter(); const { workspaceSlug } = router.query; + const workspaceStore = useWorkspace(); + const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string; + // store hooks const { mentionHighlights, mentionSuggestions } = useMention(); // form info @@ -86,8 +89,8 @@ export const AddComment: React.FC = ({ disabled = false, onSubmit, showAc onEnterKeyPress={handleSubmit(handleAddComment)} cancelUploadImage={fileService.cancelUpload} uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)} - deleteFile={fileService.deleteImage} - restoreFile={fileService.restoreImage} + deleteFile={fileService.getDeleteImageFunction(workspaceId)} + restoreFile={fileService.getRestoreImageFunction(workspaceId)} ref={editorRef} value={!commentValue || commentValue === "" ? "

" : commentValue} customClassName="p-2 h-full" diff --git a/web/components/issues/comment/comment-card.tsx b/web/components/issues/comment/comment-card.tsx index 05c7081ec..1ee90f99f 100644 --- a/web/components/issues/comment/comment-card.tsx +++ b/web/components/issues/comment/comment-card.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { observer } from "mobx-react-lite"; // hooks -import { useMention, useUser } from "hooks/store"; +import { useMention, useUser, useWorkspace } from "hooks/store"; // services import { FileService } from "services/file.service"; // icons @@ -29,6 +29,9 @@ type Props = { export const CommentCard: React.FC = observer((props) => { const { comment, handleCommentDeletion, onSubmit, showAccessSpecifier = false, workspaceSlug } = props; + const workspaceStore = useWorkspace(); + const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug)?.id as string; + // states const [isEditing, setIsEditing] = useState(false); // refs @@ -102,8 +105,8 @@ export const CommentCard: React.FC = observer((props) => { onEnterKeyPress={handleSubmit(onEnter)} cancelUploadImage={fileService.cancelUpload} uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)} - deleteFile={fileService.deleteImage} - restoreFile={fileService.restoreImage} + deleteFile={fileService.getDeleteImageFunction(workspaceId)} + restoreFile={fileService.getRestoreImageFunction(workspaceId)} ref={editorRef} value={watch("comment_html") ?? ""} debouncedUpdatesEnabled={false} diff --git a/web/components/issues/description-form.tsx b/web/components/issues/description-form.tsx index f020672d2..458fe443a 100644 --- a/web/components/issues/description-form.tsx +++ b/web/components/issues/description-form.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, FC, useCallback, useEffect, useState } from "react"; +import { ChangeEvent, FC, useCallback, useContext, useEffect, useState } from "react"; import { Controller, useForm } from "react-hook-form"; // hooks import useReloadConfirmations from "hooks/use-reload-confirmation"; @@ -11,7 +11,7 @@ import { TIssue } from "@plane/types"; import { TIssueOperations } from "./issue-detail"; // services import { FileService } from "services/file.service"; -import { useMention } from "hooks/store"; +import { useMention, useWorkspace } from "hooks/store"; export interface IssueDescriptionFormValues { name: string; @@ -38,6 +38,9 @@ const fileService = new FileService(); export const IssueDescriptionForm: FC = (props) => { const { workspaceSlug, projectId, issueId, issue, issueOperations, disabled, isSubmitting, setIsSubmitting } = props; + const workspaceStore = useWorkspace(); + const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug)?.id as string; + // states const [characterLimit, setCharacterLimit] = useState(false); @@ -172,8 +175,8 @@ export const IssueDescriptionForm: FC = (props) => { = observer((props) => { // router const router = useRouter(); const { workspaceSlug } = router.query; + const workspaceStore = useWorkspace(); + const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string; + // store const { config: { envConfig }, @@ -434,8 +437,8 @@ export const DraftIssueForm: FC = observer((props) => { = (props) => { const [isEditing, setIsEditing] = useState(false); const comment = getCommentById(commentId); + const workspaceStore = useWorkspace(); + const workspaceId = workspaceStore.getWorkspaceBySlug(comment?.workspace_detail?.slug as string)?.id as string; const { formState: { isSubmitting }, @@ -118,8 +120,8 @@ export const IssueCommentCard: FC = (props) => { onEnterKeyPress={handleSubmit(onEnter)} cancelUploadImage={fileService.cancelUpload} uploadFile={fileService.getUploadFileFunction(comment?.workspace_detail?.slug as string)} - deleteFile={fileService.deleteImage} - restoreFile={fileService.restoreImage} + deleteFile={fileService.getDeleteImageFunction(workspaceId)} + restoreFile={fileService.getRestoreImageFunction(workspaceId)} ref={editorRef} value={watch("comment_html") ?? ""} debouncedUpdatesEnabled={false} diff --git a/web/components/issues/issue-detail/issue-activity/comments/comment-create.tsx b/web/components/issues/issue-detail/issue-activity/comments/comment-create.tsx index af439e9f6..28bd00b1f 100644 --- a/web/components/issues/issue-detail/issue-activity/comments/comment-create.tsx +++ b/web/components/issues/issue-detail/issue-activity/comments/comment-create.tsx @@ -10,6 +10,7 @@ import { TActivityOperations } from "../root"; import { TIssueComment } from "@plane/types"; // icons import { Globe2, Lock } from "lucide-react"; +import { useWorkspace } from "hooks/store"; const fileService = new FileService(); @@ -40,6 +41,9 @@ const commentAccess: commentAccessType[] = [ export const IssueCommentCreate: FC = (props) => { const { workspaceSlug, activityOperations, disabled, showAccessSpecifier = false } = props; + const workspaceStore = useWorkspace(); + const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string; + // refs const editorRef = useRef(null); // react hook form @@ -73,8 +77,8 @@ export const IssueCommentCreate: FC = (props) => { }} cancelUploadImage={fileService.cancelUpload} uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)} - deleteFile={fileService.deleteImage} - restoreFile={fileService.restoreImage} + deleteFile={fileService.getDeleteImageFunction(workspaceId)} + restoreFile={fileService.getRestoreImageFunction(workspaceId)} ref={editorRef} value={!value ? "

" : value} customClassName="p-2" diff --git a/web/components/issues/issue-modal/form.tsx b/web/components/issues/issue-modal/form.tsx index 537ca0161..dbae7e2d5 100644 --- a/web/components/issues/issue-modal/form.tsx +++ b/web/components/issues/issue-modal/form.tsx @@ -6,7 +6,7 @@ import { LayoutPanelTop, Sparkle, X } from "lucide-react"; // editor import { RichTextEditorWithRef } from "@plane/rich-text-editor"; // hooks -import { useApplication, useEstimate, useIssueDetail, useMention, useProject } from "hooks/store"; +import { useApplication, useEstimate, useIssueDetail, useMention, useProject, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // services import { AIService } from "services/ai.service"; @@ -85,6 +85,9 @@ export const IssueFormRoot: FC = observer((props) => { // router const router = useRouter(); const { workspaceSlug } = router.query; + const workspaceStore = useWorkspace(); + const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string; + // store hooks const { config: { envConfig }, @@ -384,8 +387,8 @@ export const IssueFormRoot: FC = observer((props) => { = (props) => { issueCommentReactionCreate, issueCommentReactionRemove, } = props; + const workspaceStore = useWorkspace(); + const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug)?.id as string; + // states const [isEditing, setIsEditing] = useState(false); // refs @@ -117,8 +120,8 @@ export const IssueCommentCard: React.FC = (props) => { onEnterKeyPress={handleSubmit(formSubmit)} cancelUploadImage={fileService.cancelUpload} uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)} - deleteFile={fileService.deleteImage} - restoreFile={fileService.restoreImage} + deleteFile={fileService.getDeleteImageFunction(workspaceId)} + restoreFile={fileService.getRestoreImageFunction(workspaceId)} ref={editorRef} value={watch("comment_html") ?? ""} debouncedUpdatesEnabled={false} diff --git a/web/components/issues/peek-overview/activity/comment-editor.tsx b/web/components/issues/peek-overview/activity/comment-editor.tsx index a2b66443c..45119e932 100644 --- a/web/components/issues/peek-overview/activity/comment-editor.tsx +++ b/web/components/issues/peek-overview/activity/comment-editor.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; import { useForm, Controller } from "react-hook-form"; import { Globe2, Lock } from "lucide-react"; // hooks -import { useMention } from "hooks/store"; +import { useMention, useWorkspace } from "hooks/store"; // services import { FileService } from "services/file.service"; // components @@ -52,6 +52,9 @@ export const IssueCommentEditor: React.FC = (props) => { // router const router = useRouter(); const { workspaceSlug } = router.query; + const workspaceStore = useWorkspace(); + const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string; + // store hooks const { mentionHighlights, mentionSuggestions } = useMention(); // form info @@ -87,8 +90,8 @@ export const IssueCommentEditor: React.FC = (props) => { onEnterKeyPress={handleSubmit(handleAddComment)} cancelUploadImage={fileService.cancelUpload} uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)} - deleteFile={fileService.deleteImage} - restoreFile={fileService.restoreImage} + deleteFile={fileService.getDeleteImageFunction(workspaceId)} + restoreFile={fileService.getRestoreImageFunction(workspaceId)} ref={editorRef} value={!commentValue || commentValue === "" ? "

" : commentValue} customClassName="p-2 h-full" diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx index a141455b7..4d335f3b8 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx @@ -5,7 +5,7 @@ import { useRouter } from "next/router"; import { ReactElement, useEffect, useRef, useState } from "react"; import { Controller, useForm } from "react-hook-form"; // hooks -import { useApplication, useIssues, usePage, useUser } from "hooks/store"; +import { useApplication, useIssues, usePage, useUser, useWorkspace } from "hooks/store"; import useReloadConfirmations from "hooks/use-reload-confirmation"; import useToast from "hooks/use-toast"; // services @@ -46,6 +46,9 @@ const PageDetailsPage: NextPageWithLayout = observer(() => { const router = useRouter(); const { workspaceSlug, projectId, pageId } = router.query; + const workspaceStore = useWorkspace(); + const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string; + // store hooks const { config: { envConfig }, @@ -312,10 +315,10 @@ const PageDetailsPage: NextPageWithLayout = observer(() => { last_updated_by: updated_by, }} uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)} + deleteFile={fileService.getDeleteImageFunction(workspaceId)} + restoreFile={fileService.getRestoreImageFunction(workspaceId)} value={pageDescription} setShouldShowAlert={setShowAlert} - deleteFile={fileService.deleteImage} - restoreFile={fileService.restoreImage} cancelUploadImage={fileService.cancelUpload} ref={editorRef} debouncedUpdatesEnabled={false} diff --git a/web/services/file.service.ts b/web/services/file.service.ts index 4085a7309..d5e80dd53 100644 --- a/web/services/file.service.ts +++ b/web/services/file.service.ts @@ -78,6 +78,39 @@ export class FileService extends APIService { }; } + getDeleteImageFunction(workspaceId: string) { + return async (src: string) => { + try { + const assetUrlWithWorkspaceId = `${workspaceId}/${this.extractAssetIdFromUrl(src, workspaceId)}`; + const data = await this.deleteImage(assetUrlWithWorkspaceId); + return data; + } catch (e) { + console.error(e); + } + }; + } + + getRestoreImageFunction(workspaceId: string) { + return async (src: string) => { + try { + const assetUrlWithWorkspaceId = `${workspaceId}/${this.extractAssetIdFromUrl(src, workspaceId)}`; + const data = await this.restoreImage(assetUrlWithWorkspaceId); + return data; + } catch (e) { + console.error(e); + } + }; + } + + extractAssetIdFromUrl(src: string, workspaceId: string): string { + const indexWhereAssetIdStarts = src.indexOf(workspaceId) + workspaceId.length + 1; + if (indexWhereAssetIdStarts === -1) { + throw new Error("Workspace ID not found in source string"); + } + const assetUrl = src.substring(indexWhereAssetIdStarts); + return assetUrl; + } + async deleteImage(assetUrlWithWorkspaceId: string): Promise { return this.delete(`/api/workspaces/file-assets/${assetUrlWithWorkspaceId}/`) .then((response) => response?.status)