fix: Image restoration fixed (marks/unmarks an image to be deleted after a week) (#2859)

* image restoration fixed (marks an image to be deleted after a week)

* removed clgs

* added image constraints

* formatted editor-core package using yarn format

* lite-text-editor nothing to format

* rich-text-editor nothing to format

* formatted document-editor with prettier

* modified file service to follow api change

* fixed more formatting in document editor

* fixed all instances of types with that from the package

* fixed delete to work consistently (minor optimizations turned off)

* stop duplicate images inside editor

* restore image on editor creation

say if user A deletes image number 2, user B was also in the same issue and in their screen the image was there, if user B makes certain changes and that gets saved in backend, according to user B image 2 should exist but since user A deleted it, it'll not get restored and get deleted in 7 days, hence I've added a check such that whenever a issue loads we restore all images by default

* added restore image function with types

* replaced all instances to have restore image logic

* fixed issue detail for peek view

* disabled option to insert table inside a table
This commit is contained in:
M. Palanikannan 2023-11-28 11:34:20 +05:30 committed by sriram veeraghanta
parent 0fcadca53a
commit e01ca97fc9
63 changed files with 471 additions and 225 deletions

View file

@ -155,6 +155,7 @@ export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
debouncedUpdatesEnabled={false}
value={!value || value === "" ? "<p></p>" : value}

View file

@ -87,6 +87,7 @@ export const AddComment: React.FC<Props> = ({ disabled = false, onSubmit, showAc
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
value={!commentValue || commentValue === "" ? "<p></p>" : commentValue}
customClassName="p-2 h-full"

View file

@ -108,6 +108,7 @@ export const CommentCard: React.FC<Props> = ({
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
value={watch("comment_html")}
debouncedUpdatesEnabled={false}

View file

@ -148,6 +148,7 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = (props) => {
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
value={value}
setShouldShowAlert={setShowAlert}
setIsSubmitting={setIsSubmitting}

View file

@ -425,6 +425,7 @@ export const DraftIssueForm: FC<IssueFormProps> = observer((props) => {
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
debouncedUpdatesEnabled={false}
value={

View file

@ -379,6 +379,7 @@ export const IssueForm: FC<IssueFormProps> = observer((props) => {
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
debouncedUpdatesEnabled={false}
value={

View file

@ -117,6 +117,7 @@ export const IssueCommentCard: React.FC<IIssueCommentCard> = (props) => {
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
value={watch("comment_html")}
debouncedUpdatesEnabled={false}

View file

@ -88,6 +88,7 @@ export const IssueCommentEditor: React.FC<IIssueCommentEditor> = (props) => {
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
value={!commentValue || commentValue === "" ? "<p></p>" : commentValue}
customClassName="p-2 h-full"

View file

@ -66,11 +66,15 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
[issue, issueUpdate]
);
const debouncedIssueDescription = useDebouncedCallback(async (_data: any) => {
issueUpdate({ ...issue, description_html: _data });
}, 1500);
const [localTitleValue, setLocalTitleValue] = useState("");
const issueTitleCurrentValue = watch("name");
useEffect(() => {
if (localTitleValue === "" && issueTitleCurrentValue !== "") {
setLocalTitleValue(issueTitleCurrentValue);
}
}, [issueTitleCurrentValue, localTitleValue]);
const debouncedTitleSave = useDebouncedCallback(async () => {
const debouncedFormSave = useDebouncedCallback(async () => {
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
}, 1500);
@ -105,18 +109,19 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
<Controller
name="name"
control={control}
render={({ field: { value, onChange } }) => (
render={({ field: { onChange } }) => (
<TextArea
id="name"
name="name"
value={value}
value={localTitleValue}
placeholder="Enter issue name"
onFocus={() => setCharacterLimit(true)}
onChange={(e: ChangeEvent<HTMLTextAreaElement>) => {
setCharacterLimit(false);
setIsSubmitting("submitting");
debouncedTitleSave();
setLocalTitleValue(e.target.value);
onChange(e.target.value);
debouncedFormSave();
}}
required={true}
className="min-h-10 block w-full resize-none overflow-hidden rounded border-none bg-transparent text-xl outline-none ring-0 focus:ring-1 focus:ring-custom-primary !p-0 focus:!px-3 focus:!py-2"
@ -139,20 +144,41 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
)}
</div>
<span>{errors.name ? errors.name.message : null}</span>
<RichTextEditor
dragDropEnabled
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug)}
deleteFile={fileService.deleteImage}
value={issue?.description_html}
debouncedUpdatesEnabled={false}
onChange={(description: Object, description_html: string) => {
debouncedIssueDescription(description_html);
}}
customClassName="mt-0"
mentionSuggestions={editorSuggestions.mentionSuggestions}
mentionHighlights={editorSuggestions.mentionHighlights}
/>
<div className="relative">
<Controller
name="description_html"
control={control}
render={({ field: { value, onChange } }) => (
<RichTextEditor
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
value={value}
setShouldShowAlert={setShowAlert}
setIsSubmitting={setIsSubmitting}
dragDropEnabled
customClassName={isAllowed ? "min-h-[150px] shadow-sm" : "!p-0 !pt-2 text-custom-text-200"}
noBorder={!isAllowed}
onChange={(description: Object, description_html: string) => {
setShowAlert(true);
setIsSubmitting("submitting");
onChange(description_html);
debouncedFormSave();
}}
mentionSuggestions={editorSuggestions.mentionSuggestions}
mentionHighlights={editorSuggestions.mentionHighlights}
/>
)}
/>
<div
className={`absolute right-5 bottom-5 text-xs text-custom-text-200 border border-custom-border-400 rounded-xl w-[6.5rem] py-1 z-10 flex items-center justify-center ${
isSubmitting === "saved" ? "fadeOut" : "fadeIn"
}`}
>
{isSubmitting === "submitting" ? "Saving..." : "Saved"}
</div>
</div>
<IssueReaction
issueReactions={issueReactions}
user={user}

View file

@ -271,6 +271,7 @@ export const CreateUpdateBlockInline: FC<Props> = (props) => {
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
value={"<p></p>"}
debouncedUpdatesEnabled={false}
@ -293,6 +294,7 @@ export const CreateUpdateBlockInline: FC<Props> = (props) => {
cancelUploadImage={fileService.cancelUpload}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
value={
value && value !== "" && Object.keys(value).length > 0

View file

@ -267,6 +267,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
}}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage}
ref={editorRef}
debouncedUpdatesEnabled={false}
setIsSubmitting={setIsSubmitting}

View file

@ -35,6 +35,7 @@ export class FileService extends APIService {
super(API_BASE_URL);
this.uploadFile = this.uploadFile.bind(this);
this.deleteImage = this.deleteImage.bind(this);
this.restoreImage = this.restoreImage.bind(this);
this.cancelUpload = this.cancelUpload.bind(this);
}
@ -81,6 +82,17 @@ export class FileService extends APIService {
});
}
async restoreImage(assetUrlWithWorkspaceId: string): Promise<any> {
return this.post(`/api/workspaces/file-assets/${assetUrlWithWorkspaceId}/restore/`, {
headers: this.getHeaders(),
"Content-Type": "application/json",
})
.then((response) => response?.status)
.catch((error) => {
throw error?.response?.data;
});
}
async deleteFile(workspaceId: string, assetUrl: string): Promise<any> {
const lastIndex = assetUrl.lastIndexOf("/");
const assetId = assetUrl.substring(lastIndex + 1);

View file

@ -23,6 +23,8 @@
/* Custom image styles */
.ProseMirror img {
min-width: 100px;
min-height: 100px;
transition: filter 0.1s ease-in-out;
&:hover {