[WEB-2717] chore: implemented issue attachment upload progress (#5901)
* chore: added attachment upload progress * chore: add debounce while updating the upload status * chore: update percentage calc logic * chore: update debounce interval
This commit is contained in:
parent
538e78f135
commit
c423d7d9df
17 changed files with 314 additions and 194 deletions
|
|
@ -1,9 +1,12 @@
|
|||
import concat from "lodash/concat";
|
||||
import debounce from "lodash/debounce";
|
||||
import pull from "lodash/pull";
|
||||
import set from "lodash/set";
|
||||
import uniq from "lodash/uniq";
|
||||
import update from "lodash/update";
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
// types
|
||||
import { TIssueAttachment, TIssueAttachmentMap, TIssueAttachmentIdMap } from "@plane/types";
|
||||
// services
|
||||
|
|
@ -11,7 +14,16 @@ import { IssueAttachmentService } from "@/services/issue";
|
|||
import { IIssueRootStore } from "../root.store";
|
||||
import { IIssueDetail } from "./root.store";
|
||||
|
||||
export type TAttachmentUploadStatus = {
|
||||
id: string;
|
||||
name: string;
|
||||
progress: number;
|
||||
size: number;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export interface IIssueAttachmentStoreActions {
|
||||
// actions
|
||||
addAttachments: (issueId: string, attachments: TIssueAttachment[]) => void;
|
||||
fetchAttachments: (workspaceSlug: string, projectId: string, issueId: string) => Promise<TIssueAttachment[]>;
|
||||
createAttachment: (
|
||||
|
|
@ -32,9 +44,11 @@ export interface IIssueAttachmentStore extends IIssueAttachmentStoreActions {
|
|||
// observables
|
||||
attachments: TIssueAttachmentIdMap;
|
||||
attachmentMap: TIssueAttachmentMap;
|
||||
attachmentsUploadStatusMap: Record<string, Record<string, TAttachmentUploadStatus>>;
|
||||
// computed
|
||||
issueAttachments: string[] | undefined;
|
||||
// helper methods
|
||||
getAttachmentsUploadStatusByIssueId: (issueId: string) => TAttachmentUploadStatus[] | undefined;
|
||||
getAttachmentsByIssueId: (issueId: string) => string[] | undefined;
|
||||
getAttachmentById: (attachmentId: string) => TIssueAttachment | undefined;
|
||||
}
|
||||
|
|
@ -43,6 +57,7 @@ export class IssueAttachmentStore implements IIssueAttachmentStore {
|
|||
// observables
|
||||
attachments: TIssueAttachmentIdMap = {};
|
||||
attachmentMap: TIssueAttachmentMap = {};
|
||||
attachmentsUploadStatusMap: Record<string, Record<string, TAttachmentUploadStatus>> = {};
|
||||
// root store
|
||||
rootIssueStore: IIssueRootStore;
|
||||
rootIssueDetailStore: IIssueDetail;
|
||||
|
|
@ -54,6 +69,7 @@ export class IssueAttachmentStore implements IIssueAttachmentStore {
|
|||
// observables
|
||||
attachments: observable,
|
||||
attachmentMap: observable,
|
||||
attachmentsUploadStatusMap: observable,
|
||||
// computed
|
||||
issueAttachments: computed,
|
||||
// actions
|
||||
|
|
@ -77,6 +93,12 @@ export class IssueAttachmentStore implements IIssueAttachmentStore {
|
|||
}
|
||||
|
||||
// helper methods
|
||||
getAttachmentsUploadStatusByIssueId = computedFn((issueId: string) => {
|
||||
if (!issueId) return undefined;
|
||||
const attachmentsUploadStatus = Object.values(this.attachmentsUploadStatusMap[issueId] ?? {});
|
||||
return attachmentsUploadStatus ?? undefined;
|
||||
});
|
||||
|
||||
getAttachmentsByIssueId = (issueId: string) => {
|
||||
if (!issueId) return undefined;
|
||||
return this.attachments[issueId] ?? undefined;
|
||||
|
|
@ -104,21 +126,56 @@ export class IssueAttachmentStore implements IIssueAttachmentStore {
|
|||
return response;
|
||||
};
|
||||
|
||||
createAttachment = async (workspaceSlug: string, projectId: string, issueId: string, file: File) => {
|
||||
const response = await this.issueAttachmentService.uploadIssueAttachment(workspaceSlug, projectId, issueId, file);
|
||||
const issueAttachmentsCount = this.getAttachmentsByIssueId(issueId)?.length ?? 0;
|
||||
debouncedUpdateProgress = debounce((issueId: string, tempId: string, progress: number) => {
|
||||
runInAction(() => {
|
||||
set(this.attachmentsUploadStatusMap, [issueId, tempId, "progress"], progress);
|
||||
});
|
||||
}, 16);
|
||||
|
||||
if (response && response.id) {
|
||||
createAttachment = async (workspaceSlug: string, projectId: string, issueId: string, file: File) => {
|
||||
const tempId = uuidv4();
|
||||
try {
|
||||
// update attachment upload status
|
||||
runInAction(() => {
|
||||
update(this.attachments, [issueId], (attachmentIds = []) => uniq(concat(attachmentIds, [response.id])));
|
||||
set(this.attachmentMap, response.id, response);
|
||||
this.rootIssueStore.issues.updateIssue(issueId, {
|
||||
attachment_count: issueAttachmentsCount + 1, // increment attachment count
|
||||
set(this.attachmentsUploadStatusMap, [issueId, tempId], {
|
||||
id: tempId,
|
||||
name: file.name,
|
||||
progress: 0,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
});
|
||||
});
|
||||
}
|
||||
const response = await this.issueAttachmentService.uploadIssueAttachment(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
issueId,
|
||||
file,
|
||||
(progressEvent) => {
|
||||
const progressPercentage = Math.round((progressEvent.progress ?? 0) * 100);
|
||||
this.debouncedUpdateProgress(issueId, tempId, progressPercentage);
|
||||
}
|
||||
);
|
||||
const issueAttachmentsCount = this.getAttachmentsByIssueId(issueId)?.length ?? 0;
|
||||
|
||||
return response;
|
||||
if (response && response.id) {
|
||||
runInAction(() => {
|
||||
update(this.attachments, [issueId], (attachmentIds = []) => uniq(concat(attachmentIds, [response.id])));
|
||||
set(this.attachmentMap, response.id, response);
|
||||
this.rootIssueStore.issues.updateIssue(issueId, {
|
||||
attachment_count: issueAttachmentsCount + 1, // increment attachment count
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("Error in uploading issue attachment:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
runInAction(() => {
|
||||
delete this.attachmentsUploadStatusMap[issueId][tempId];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
removeAttachment = async (workspaceSlug: string, projectId: string, issueId: string, attachmentId: string) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue