feat: Comments (#4911)

* Comment model

* Framework, model, policy, presenter, api endpoint etc

* Iteration, first pass of UI

* fixes, refactors

* Comment commands

* comment socket support

* typing indicators

* comment component, styling

* wip

* right sidebar resize

* fix: CMD+Enter submit

* Add usePersistedState
fix: Main page scrolling on comment highlight

* drafts

* Typing indicator

* refactor

* policies

* Click thread to highlight
Improve comment timestamps

* padding

* Comment menu v1

* Change comments to use editor

* Basic comment editing

* fix: Hide commenting button when disabled at team level

* Enable opening sidebar without mark

* Move selected comment to location state

* Add comment delete confirmation

* Add comment count to document meta

* fix: Comment sidebar togglable
Add copy link to comment

* stash

* Restore History changes

* Refactor right sidebar to allow for comment animation

* Update to new router best practices

* stash

* Various improvements

* stash

* Handle click outside

* Fix incorrect placeholder in input
fix: Input box appearing on other sessions erroneously

* stash

* fix: Don't leave orphaned child comments

* styling

* stash

* Enable comment toggling again

* Edit styling, merge conflicts

* fix: Cannot navigate from insights to comments

* Remove draft comment mark on click outside

* Fix: Empty comment sidebar, tsc

* Remove public toggle

* fix: All comments are recessed
fix: Comments should not be printed

* fix: Associated mark should be removed on comment delete

* Revert unused changes

* Empty state, basic RTL support

* Create dont toggle comment mark

* Make it feel more snappy

* Highlight active comment in text

* fix animation

* RTL support

* Add reply CTA

* Translations
This commit is contained in:
Tom Moor
2023-02-25 15:03:05 -05:00
committed by GitHub
parent 59e25a0ef0
commit fc8c20149f
89 changed files with 2909 additions and 315 deletions

101
app/stores/CommentsStore.ts Normal file
View File

@@ -0,0 +1,101 @@
import invariant from "invariant";
import { filter, orderBy } from "lodash";
import { action, runInAction, computed } from "mobx";
import Comment from "~/models/Comment";
import Document from "~/models/Document";
import { PaginationParams } from "~/types";
import { client } from "~/utils/ApiClient";
import BaseStore from "./BaseStore";
import RootStore from "./RootStore";
export default class CommentsStore extends BaseStore<Comment> {
apiEndpoint = "comments";
constructor(rootStore: RootStore) {
super(rootStore, Comment);
}
/**
* Returns a list of comments in a document that are not replies to other
* comments.
*
* @param documentId ID of the document to get comments for
* @returns Array of comments
*/
threadsInDocument(documentId: string): Comment[] {
return this.inDocument(documentId).filter(
(comment) => !comment.parentCommentId
);
}
/**
* Returns a list of comments that are replies to the given comment.
*
* @param commentId ID of the comment to get replies for
* @returns Array of comments
*/
inThread(threadId: string): Comment[] {
return filter(
this.orderedData,
(comment) =>
comment.parentCommentId === threadId ||
(comment.id === threadId && !comment.isNew)
);
}
/**
* Returns a list of comments in a document.
*
* @param documentId ID of the document to get comments for
* @returns Array of comments
*/
inDocument(documentId: string): Comment[] {
return filter(
this.orderedData,
(comment) => comment.documentId === documentId
);
}
@action
setTyping({
commentId,
userId,
}: {
commentId: string;
userId: string;
}): void {
const comment = this.get(commentId);
if (comment) {
comment.typingUsers.set(userId, new Date());
}
}
@action
fetchDocumentComments = async (
documentId: string,
options?: PaginationParams | undefined
): Promise<Document[]> => {
this.isFetching = true;
try {
const res = await client.post(`/comments.list`, {
documentId,
...options,
});
invariant(res && res.data, "Comment list not available");
runInAction("CommentsStore#fetchDocumentComments", () => {
res.data.forEach(this.add);
this.addPolicies(res.policies);
});
return res.data;
} finally {
this.isFetching = false;
}
};
@computed
get orderedData(): Comment[] {
return orderBy(Array.from(this.data.values()), "createdAt", "asc");
}
}