mirror of
https://github.com/outline/outline.git
synced 2026-06-13 03:14:59 +03:00
0139b91b5d
* chore: Replace lodash with es-toolkit Migrate all direct lodash imports to es-toolkit/compat for a smaller, faster, lodash-compatible utility library. Transitive lodash usage from other packages remains unchanged. * fix: Restore isPlainObject semantics in CanCan policy The lodash migration aliased `isObject` to `lodash/isPlainObject` and the codemod incorrectly mapped the local name to es-toolkit's `isObject`, which also returns true for arrays and functions. This caused condition objects in policy definitions to be skipped, breaking authorization checks across the codebase. * fix: Restore unicode-aware length counting in validators es-toolkit/compat's size() returns string.length, while lodash's _.size() counts unicode code points. Switch to [...value].length to preserve the previous behavior so multi-byte characters like emoji count as one.
154 lines
4.4 KiB
TypeScript
154 lines
4.4 KiB
TypeScript
import invariant from "invariant";
|
|
import { compact, differenceBy, keyBy, orderBy, uniq } from "es-toolkit/compat";
|
|
import { action, computed } from "mobx";
|
|
import Comment from "~/models/Comment";
|
|
import { type CommentSortOption, CommentSortType } from "~/types";
|
|
import { client } from "~/utils/ApiClient";
|
|
import type RootStore from "./RootStore";
|
|
import Store from "./base/Store";
|
|
|
|
export default class CommentsStore extends Store<Comment> {
|
|
constructor(rootStore: RootStore) {
|
|
super(rootStore, Comment);
|
|
}
|
|
|
|
/**
|
|
* 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 this.filter((comment: Comment) => comment.documentId === documentId);
|
|
}
|
|
|
|
/**
|
|
* 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,
|
|
options: CommentSortOption = { type: CommentSortType.MostRecent }
|
|
) {
|
|
const comments = this.filter(
|
|
(comment: Comment) =>
|
|
comment.documentId === documentId &&
|
|
!comment.parentCommentId &&
|
|
(!comment.isNew ||
|
|
comment.createdById === this.rootStore.auth.currentUserId)
|
|
);
|
|
|
|
if (options.type === CommentSortType.MostRecent) {
|
|
return comments;
|
|
}
|
|
|
|
const commentsById = keyBy(comments, "id");
|
|
const referencedComments = compact(
|
|
uniq(options.referencedCommentIds.map((id) => commentsById[id]))
|
|
);
|
|
const directComments = differenceBy(comments, referencedComments, "id");
|
|
|
|
return [...referencedComments, ...directComments];
|
|
}
|
|
|
|
/**
|
|
* Returns a list of resolved 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
|
|
*/
|
|
resolvedThreadsInDocument(
|
|
documentId: string,
|
|
options: CommentSortOption = { type: CommentSortType.MostRecent }
|
|
): Comment[] {
|
|
return this.threadsInDocument(documentId, options).filter(
|
|
(comment: Comment) => comment.isResolved === true
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
unresolvedThreadsInDocument(
|
|
documentId: string,
|
|
options: CommentSortOption = { type: CommentSortType.MostRecent }
|
|
): Comment[] {
|
|
return this.threadsInDocument(documentId, options).filter(
|
|
(comment: Comment) => comment.isResolved !== true
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns the total number of unresolved comments in the given document.
|
|
*
|
|
* @param documentId ID of the document to get comments for
|
|
* @returns A number of comments
|
|
*/
|
|
unresolvedCommentsInDocumentCount(documentId: string): number {
|
|
return this.unresolvedThreadsInDocument(documentId).reduce(
|
|
(memo, thread) => memo + this.inThread(thread.id).length,
|
|
0
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns a list of comments that includes the given thread ID and any of it's replies.
|
|
*
|
|
* @param commentId ID of the comment to get replies for
|
|
* @returns Array of comments
|
|
*/
|
|
inThread(threadId: string): Comment[] {
|
|
return this.filter(
|
|
(comment: Comment) =>
|
|
comment.parentCommentId === threadId || comment.id === threadId
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Resolve a comment thread with the given ID.
|
|
*
|
|
* @param id ID of the comment to resolve
|
|
* @returns Resolved comment
|
|
*/
|
|
@action
|
|
resolve = async (id: string): Promise<Comment> => {
|
|
const res = await client.post("/comments.resolve", {
|
|
id,
|
|
});
|
|
invariant(res?.data, "Comment not available");
|
|
this.addPolicies(res.policies);
|
|
this.add(res.data);
|
|
return this.data.get(res.data.id) as Comment;
|
|
};
|
|
|
|
/**
|
|
* Unresolve a comment thread with the given ID.
|
|
*
|
|
* @param id ID of the comment to unresolve
|
|
* @returns Unresolved comment
|
|
*/
|
|
@action
|
|
unresolve = async (id: string): Promise<Comment> => {
|
|
const res = await client.post("/comments.unresolve", {
|
|
id,
|
|
});
|
|
invariant(res?.data, "Comment not available");
|
|
this.addPolicies(res.policies);
|
|
this.add(res.data);
|
|
return this.data.get(res.data.id) as Comment;
|
|
};
|
|
|
|
@computed
|
|
get orderedData(): Comment[] {
|
|
return orderBy(Array.from(this.data.values()), "createdAt", "asc");
|
|
}
|
|
}
|