import concat from "lodash/concat"; import pull from "lodash/pull"; import set from "lodash/set"; import uniq from "lodash/uniq"; import update from "lodash/update"; import { action, makeObservable, observable, runInAction } from "mobx"; // services import { IssueCommentService } from "services/issue"; // types import { TIssueComment, TIssueCommentMap, TIssueCommentIdMap } from "@plane/types"; import { IIssueDetail } from "./root.store"; export type TCommentLoader = "fetch" | "create" | "update" | "delete" | "mutate" | undefined; export interface IIssueCommentStoreActions { fetchComments: ( workspaceSlug: string, projectId: string, issueId: string, loaderType?: TCommentLoader ) => Promise; createComment: ( workspaceSlug: string, projectId: string, issueId: string, data: Partial ) => Promise; updateComment: ( workspaceSlug: string, projectId: string, issueId: string, commentId: string, data: Partial ) => Promise; removeComment: (workspaceSlug: string, projectId: string, issueId: string, commentId: string) => Promise; } export interface IIssueCommentStore extends IIssueCommentStoreActions { // observables loader: TCommentLoader; comments: TIssueCommentIdMap; commentMap: TIssueCommentMap; // helper methods getCommentsByIssueId: (issueId: string) => string[] | undefined; getCommentById: (activityId: string) => TIssueComment | undefined; } export class IssueCommentStore implements IIssueCommentStore { // observables loader: TCommentLoader = "fetch"; comments: TIssueCommentIdMap = {}; commentMap: TIssueCommentMap = {}; // root store rootIssueDetail: IIssueDetail; // services issueCommentService; constructor(rootStore: IIssueDetail) { makeObservable(this, { // observables loader: observable.ref, comments: observable, commentMap: observable, // actions fetchComments: action, createComment: action, updateComment: action, removeComment: action, }); // root store this.rootIssueDetail = rootStore; // services this.issueCommentService = new IssueCommentService(); } // helper methods getCommentsByIssueId = (issueId: string) => { if (!issueId) return undefined; return this.comments[issueId] ?? undefined; }; getCommentById = (commentId: string) => { if (!commentId) return undefined; return this.commentMap[commentId] ?? undefined; }; fetchComments = async ( workspaceSlug: string, projectId: string, issueId: string, loaderType: TCommentLoader = "fetch" ) => { try { this.loader = loaderType; let props = {}; const _commentIds = this.getCommentsByIssueId(issueId); if (_commentIds && _commentIds.length > 0) { const _comment = this.getCommentById(_commentIds[_commentIds.length - 1]); if (_comment) props = { created_at__gt: _comment.created_at }; } const comments = await this.issueCommentService.getIssueComments(workspaceSlug, projectId, issueId, props); const commentIds = comments.map((comment) => comment.id); runInAction(() => { update(this.comments, issueId, (_commentIds) => { if (!_commentIds) return commentIds; return uniq(concat(_commentIds, commentIds)); }); comments.forEach((comment) => { this.rootIssueDetail.commentReaction.applyCommentReactions(comment.id, comment?.comment_reactions || []); set(this.commentMap, comment.id, comment); }); this.loader = undefined; }); return comments; } catch (error) { throw error; } }; createComment = async (workspaceSlug: string, projectId: string, issueId: string, data: Partial) => { try { const response = await this.issueCommentService.createIssueComment(workspaceSlug, projectId, issueId, data); runInAction(() => { update(this.comments, issueId, (_commentIds) => { if (!_commentIds) return [response.id]; return uniq(concat(_commentIds, [response.id])); }); set(this.commentMap, response.id, response); }); return response; } catch (error) { throw error; } }; updateComment = async ( workspaceSlug: string, projectId: string, issueId: string, commentId: string, data: Partial ) => { try { runInAction(() => { Object.keys(data).forEach((key) => { set(this.commentMap, [commentId, key], data[key as keyof TIssueComment]); }); }); const response = await this.issueCommentService.patchIssueComment( workspaceSlug, projectId, issueId, commentId, data ); return response; } catch (error) { this.rootIssueDetail.activity.fetchActivities(workspaceSlug, projectId, issueId); throw error; } }; removeComment = async (workspaceSlug: string, projectId: string, issueId: string, commentId: string) => { try { const response = await this.issueCommentService.deleteIssueComment(workspaceSlug, projectId, issueId, commentId); runInAction(() => { pull(this.comments[issueId], commentId); delete this.commentMap[commentId]; }); return response; } catch (error) { throw error; } }; }