Merge branch 'feat/inda-347-host-update' into 'main'
[ProtonMail-WebClient.git] / packages / docs-core / lib / Services / Comments / LocalCommentsState.ts
blob12de4dd5387c15d069d913654bd2aa6eb8e1173a
1 import type {
2   CommentInterface,
3   CommentThreadInterface,
4   CommentsChangedData,
5   EditCommentData,
6   InternalEventBusInterface,
7 } from '@proton/docs-shared'
8 import { CommentThreadState, CommentsEvent } from '@proton/docs-shared'
10 export class LocalCommentsState {
11   private threads: CommentThreadInterface[] = []
12   private unreadThreadIDs = new Set<string>()
14   constructor(private eventBus: InternalEventBusInterface) {}
16   getAllThreads(): CommentThreadInterface[] {
17     return this.threads
18   }
20   hasUnreadThreads(): boolean {
21     return this.unreadThreadIDs.size > 0
22   }
24   markThreadAsRead(id: string): void {
25     if (this.unreadThreadIDs.has(id)) {
26       this.unreadThreadIDs.delete(id)
27       this.notifyLocalListeners()
28     }
29   }
31   findThreadById(threadId: string): CommentThreadInterface | undefined {
32     return this.threads.find((thread) => thread.id === threadId)
33   }
35   addThread(thread: CommentThreadInterface, markUnread = false) {
36     this.threads = this.threads.filter((existing) => existing.id !== thread.id)
38     this.threads.push(thread)
40     if (markUnread) {
41       this.unreadThreadIDs.add(thread.id)
42     }
44     this.notifyLocalListeners()
45   }
47   deleteThread(id: string): void {
48     this.threads = this.threads.filter((thread) => thread.id !== id)
50     if (this.unreadThreadIDs.has(id)) {
51       this.unreadThreadIDs.delete(id)
52     }
54     this.notifyLocalListeners()
55   }
57   resolveThread(threadId: string) {
58     const thread = this.findThreadById(threadId)
59     if (!thread) {
60       return
61     }
62     thread.state = CommentThreadState.Resolved
63     this.sortThreadsAndNotify()
64     return thread
65   }
67   unresolveThread(threadId: string) {
68     const thread = this.findThreadById(threadId)
69     if (!thread) {
70       return
71     }
72     thread.state = CommentThreadState.Active
73     this.sortThreadsAndNotify()
74     return thread
75   }
77   changeThreadState(threadId: string, state: CommentThreadState) {
78     const thread = this.findThreadById(threadId)
79     if (!thread) {
80       return
81     }
82     thread.state = state
83     this.sortThreadsAndNotify()
84     return thread
85   }
87   addComment(comment: CommentInterface, threadID: string, markUnread = false): void {
88     const thread = this.findThreadById(threadID)
89     if (!thread) {
90       return
91     }
92     thread.comments.push(comment)
93     if (markUnread) {
94       this.unreadThreadIDs.add(thread.id)
95     }
96     this.notifyLocalListeners()
97   }
99   editComment({ commentID, threadID, content, markThreadUnread = false }: EditCommentData): void {
100     const thread = this.findThreadById(threadID)
101     if (!thread) {
102       return
103     }
105     const comment = thread.comments.find((comment) => comment.id === commentID)
106     if (!comment) {
107       return
108     }
110     comment.content = content
112     if (markThreadUnread) {
113       this.unreadThreadIDs.add(thread.id)
114     }
116     this.notifyLocalListeners()
117   }
119   deleteComment({ commentID, threadID }: { commentID: string; threadID: string }): void {
120     const thread = this.findThreadById(threadID)
121     if (!thread) {
122       return
123     }
124     thread.comments = thread.comments.filter((comment) => comment.id !== commentID)
125     this.notifyLocalListeners()
126   }
128   replacePlaceholderThread(placeholderID: string, thread: CommentThreadInterface) {
129     const index = this.threads.findIndex((thread) => thread.id === placeholderID)
130     if (index === -1) {
131       return
132     }
134     this.threads[index] = {
135       ...thread,
136       isPlaceholder: false,
137     }
139     this.notifyLocalListeners()
140   }
142   replacePlaceholderComment(placeholderID: string, comment: CommentInterface) {
143     this.threads.forEach((thread) => {
144       const index = thread.comments.findIndex((comment) => comment.id === placeholderID)
145       if (index !== -1) {
146         thread.comments[index] = {
147           ...comment,
148           isPlaceholder: false,
149         }
150       }
151     })
152     this.notifyLocalListeners()
153   }
155   sortThreadsAndNotify(): void {
156     this.threads.sort((a, b) => {
157       if (a.state === CommentThreadState.Resolved && b.state !== CommentThreadState.Resolved) {
158         return 1
159       } else if (a.state !== CommentThreadState.Resolved && b.state === CommentThreadState.Resolved) {
160         return -1
161       } else {
162         return a.createTime.milliseconds - b.createTime.milliseconds
163       }
164     })
165     this.notifyLocalListeners()
166   }
168   private notifyLocalListeners(): void {
169     this.eventBus.publish<CommentsChangedData>({
170       type: CommentsEvent.CommentsChanged,
171       payload: {
172         hasUnreadThreads: this.hasUnreadThreads(),
173       },
174     })
175   }