Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / docs-core / lib / UseCase / CreateComment.ts
blobfa680276565f9d4b6ccf20346ba967acd59c2347
1 import { Comment } from '../Models'
2 import { GenerateUUID } from '../Util/GenerateUuid'
3 import type { DocumentEntitlements, PublicDocumentEntitlements } from '../Types/DocumentEntitlements'
4 import { isPrivateDocumentKeys } from '../Types/DocumentEntitlements'
5 import { Result } from '@proton/docs-shared'
6 import { ServerTime } from '@proton/docs-shared'
7 import metrics from '@proton/metrics'
8 import type { CommentInterface, CommentType } from '@proton/docs-shared'
9 import type { DocsApi } from '../Api/DocsApi'
10 import type { EncryptComment } from './EncryptComment'
11 import type { LocalCommentsState } from '../Services/Comments/LocalCommentsState'
12 import type { UseCaseInterface } from '../Domain/UseCase/UseCaseInterface'
14 /**
15  * Creates and encrypts a new comment in a thread with the API.
16  */
17 export class CreateComment implements UseCaseInterface<CommentInterface> {
18   constructor(
19     private api: DocsApi,
20     private encryptComment: EncryptComment,
21   ) {}
23   async execute(dto: {
24     text: string
25     threadID: string
26     entitlements: PublicDocumentEntitlements | DocumentEntitlements
27     commentsState: LocalCommentsState
28     type: CommentType
29     decryptedDocumentName: string | null
30   }): Promise<Result<CommentInterface>> {
31     const thread = dto.commentsState.findThreadById(dto.threadID)
32     if (!thread) {
33       return Result.fail('Thread not found')
34     }
36     const localComment = new Comment(
37       GenerateUUID(),
38       ServerTime.now(),
39       ServerTime.now(),
40       dto.text,
41       null,
42       isPrivateDocumentKeys(dto.entitlements.keys) ? dto.entitlements.keys.userOwnAddress : undefined,
43       [],
44       true,
45       dto.type,
46     )
48     dto.commentsState.addComment(localComment, dto.threadID)
50     const encryptionResult = await this.encryptComment.execute(dto.text, thread.markID, dto.entitlements.keys)
52     if (encryptionResult.isFailed()) {
53       dto.commentsState.deleteComment({ commentID: localComment.id, threadID: dto.threadID })
54       return Result.fail(encryptionResult.getError())
55     }
57     const encryptedValue = encryptionResult.getValue()
59     const result = await this.api.addCommentToThread(
60       {
61         threadId: dto.threadID,
62         encryptedContent: encryptedValue,
63         parentCommentId: null,
64         type: dto.type,
65         decryptedDocumentName: dto.decryptedDocumentName,
66       },
67       dto.entitlements,
68     )
70     if (result.isFailed()) {
71       metrics.docs_comments_error_total.increment({
72         reason: 'server_error',
73       })
75       dto.commentsState.deleteComment({ commentID: localComment.id, threadID: dto.threadID })
76       return Result.fail(result.getError().message)
77     }
79     const { Comment: commentFromResponse } = result.getValue()
81     const emailToUse = commentFromResponse.AuthorEmail || commentFromResponse.Author
83     const comment = new Comment(
84       commentFromResponse.CommentID,
85       new ServerTime(commentFromResponse.CreateTime),
86       new ServerTime(commentFromResponse.ModifyTime),
87       dto.text,
88       commentFromResponse.ParentCommentID,
89       emailToUse,
90       [],
91       false,
92       commentFromResponse.Type,
93     )
94     dto.commentsState.replacePlaceholderComment(localComment.id, comment)
96     return Result.ok(comment)
97   }