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)