* fix comments
* fix page history
* fix aside width on smaller screens
This commit is contained in:
Philipinho
2024-04-23 22:07:00 +01:00
parent 2af1fe3c40
commit b91c3ede1e
15 changed files with 611 additions and 464 deletions

View File

@ -24,6 +24,12 @@
}
}
@media (max-width: 48em) {
.aside {
width: 350px;
}
}
@media (max-width: 48em) {
.navbar {
width: 300px;

View File

@ -33,8 +33,8 @@ export default function Shell({ children }: { children: React.ReactNode }) {
collapsed: { mobile: !mobileOpened, desktop: !desktopOpened },
}}
aside={{
width: 300,
breakpoint: "md",
width: 350,
breakpoint: "sm",
collapsed: { mobile: !isAsideOpen, desktop: !isAsideOpen },
}}
padding="md"

View File

@ -57,8 +57,8 @@ function CommentDialog({ editor, pageId }: CommentDialogProps) {
await createCommentMutation.mutateAsync(commentData);
editor
.chain()
.setContent(createdComment.id)
// @ts-ignore
.setComment(createdComment.id)
.unsetCommentDecoration()
.run();
setActiveCommentId(createdComment.id);
@ -75,7 +75,7 @@ function CommentDialog({ editor, pageId }: CommentDialogProps) {
}
};
const handleCommentEditorChange = (newContent) => {
const handleCommentEditorChange = (newContent: any) => {
setComment(newContent);
};
@ -93,7 +93,7 @@ function CommentDialog({ editor, pageId }: CommentDialogProps) {
>
<Stack gap={2}>
<Group>
<Avatar size="sm" c="blue">
<Avatar size="sm" color="blue">
{currentUser.user.name.charAt(0)}
</Avatar>
<div style={{ flex: 1 }}>

View File

@ -1,16 +1,24 @@
import React, { useState, useRef } from 'react';
import { useParams } from 'react-router-dom';
import { Divider, Paper } from '@mantine/core';
import CommentListItem from '@/features/comment/components/comment-list-item';
import { useCommentsQuery, useCreateCommentMutation } from '@/features/comment/queries/comment-query';
import React, { useState, useRef } from "react";
import { useParams } from "react-router-dom";
import { Divider, Paper } from "@mantine/core";
import CommentListItem from "@/features/comment/components/comment-list-item";
import {
useCommentsQuery,
useCreateCommentMutation,
} from "@/features/comment/queries/comment-query";
import CommentEditor from '@/features/comment/components/comment-editor';
import CommentActions from '@/features/comment/components/comment-actions';
import { useFocusWithin } from '@mantine/hooks';
import CommentEditor from "@/features/comment/components/comment-editor";
import CommentActions from "@/features/comment/components/comment-actions";
import { useFocusWithin } from "@mantine/hooks";
import { IComment } from "@/features/comment/types/comment.types.ts";
function CommentList() {
const { pageId } = useParams();
const { data: comments, isLoading: isCommentsLoading, isError } = useCommentsQuery(pageId);
const {
data: comments,
isLoading: isCommentsLoading,
isError,
} = useCommentsQuery(pageId);
const [isLoading, setIsLoading] = useState(false);
const createCommentMutation = useCreateCommentMutation();
@ -22,12 +30,12 @@ function CommentList() {
return <div>Error loading comments.</div>;
}
if (!comments || comments.length === 0) {
if (!comments || comments.items.length === 0) {
return <>No comments yet.</>;
}
const renderComments = (comment) => {
const handleAddReply = async (commentId, content) => {
const renderComments = (comment: IComment) => {
const handleAddReply = async (commentId: string, content: string) => {
try {
setIsLoading(true);
const commentData = {
@ -38,14 +46,22 @@ function CommentList() {
await createCommentMutation.mutateAsync(commentData);
} catch (error) {
console.error('Failed to post comment:', error);
console.error("Failed to post comment:", error);
} finally {
setIsLoading(false);
}
};
return (
<Paper shadow="sm" radius="md" p="sm" mb="sm" withBorder key={comment.id} data-comment-id={comment.id}>
<Paper
shadow="sm"
radius="md"
p="sm"
mb="sm"
withBorder
key={comment.id}
data-comment-id={comment.id}
>
<div>
<CommentListItem comment={comment} />
<ChildComments comments={comments} parentId={comment.id} />
@ -53,26 +69,34 @@ function CommentList() {
<Divider my={4} />
<CommentEditorWithActions commentId={comment.id} onSave={handleAddReply} isLoading={isLoading} />
<CommentEditorWithActions
commentId={comment.id}
onSave={handleAddReply}
isLoading={isLoading}
/>
</Paper>
);
};
return (
<>
{comments.filter(comment => comment.parentCommentId === null).map(renderComments)}
{comments.items
.filter((comment) => comment.parentCommentId === null)
.map(renderComments)}
</>
);
}
const ChildComments = ({ comments, parentId }) => {
const getChildComments = (parentId) => {
return comments.filter(comment => comment.parentCommentId === parentId);
const getChildComments = (parentId: string) => {
return comments.items.filter(
(comment: IComment) => comment.parentCommentId === parentId,
);
};
return (
<div>
{getChildComments(parentId).map(childComment => (
{getChildComments(parentId).map((childComment) => (
<div key={childComment.id}>
<CommentListItem comment={childComment} />
<ChildComments comments={comments} parentId={childComment.id} />
@ -83,23 +107,26 @@ const ChildComments = ({ comments, parentId }) => {
};
const CommentEditorWithActions = ({ commentId, onSave, isLoading }) => {
const [content, setContent] = useState('');
const [content, setContent] = useState("");
const { ref, focused } = useFocusWithin();
const commentEditorRef = useRef(null);
const handleSave = () => {
onSave(commentId, content);
setContent('');
setContent("");
commentEditorRef.current?.clearContent();
};
return (
<div ref={ref}>
<CommentEditor ref={commentEditorRef} onUpdate={setContent} editable={true} />
<CommentEditor
ref={commentEditorRef}
onUpdate={setContent}
editable={true}
/>
{focused && <CommentActions onSave={handleSave} isLoading={isLoading} />}
</div>
);
};
export default CommentList;

View File

@ -1,16 +1,28 @@
import { useMutation, useQuery, useQueryClient, UseQueryResult } from '@tanstack/react-query';
import {
useMutation,
useQuery,
useQueryClient,
UseQueryResult,
} from "@tanstack/react-query";
import {
createComment,
deleteComment, getPageComments,
deleteComment,
getPageComments,
resolveComment,
updateComment,
} from '@/features/comment/services/comment-service';
import { IComment, IResolveComment } from '@/features/comment/types/comment.types';
import { notifications } from '@mantine/notifications';
} from "@/features/comment/services/comment-service";
import {
IComment,
IResolveComment,
} from "@/features/comment/types/comment.types";
import { notifications } from "@mantine/notifications";
import { IPagination } from "@/lib/types.ts";
export const RQ_KEY = (pageId: string) => ['comments', pageId];
export const RQ_KEY = (pageId: string) => ["comments", pageId];
export function useCommentsQuery(pageId: string): UseQueryResult<IComment[], Error> {
export function useCommentsQuery(
pageId: string,
): UseQueryResult<IPagination<IComment>, Error> {
return useQuery({
queryKey: RQ_KEY(pageId),
queryFn: () => getPageComments(pageId),
@ -27,14 +39,14 @@ export function useCreateCommentMutation() {
const newComment = data;
let comments = queryClient.getQueryData(RQ_KEY(data.pageId));
if (comments) {
comments = prevComments => [...prevComments, newComment];
queryClient.setQueryData(RQ_KEY(data.pageId), comments);
// comments = prevComments => [...prevComments, newComment];
//queryClient.setQueryData(RQ_KEY(data.pageId), comments);
}
notifications.show({ message: 'Comment created successfully' });
notifications.show({ message: "Comment created successfully" });
},
onError: (error) => {
notifications.show({ message: 'Error creating comment', color: 'red' });
notifications.show({ message: "Error creating comment", color: "red" });
},
});
}
@ -43,10 +55,10 @@ export function useUpdateCommentMutation() {
return useMutation<IComment, Error, Partial<IComment>>({
mutationFn: (data) => updateComment(data),
onSuccess: (data) => {
notifications.show({ message: 'Comment updated successfully' });
notifications.show({ message: "Comment updated successfully" });
},
onError: (error) => {
notifications.show({ message: 'Failed to update comment', color: 'red' });
notifications.show({ message: "Failed to update comment", color: "red" });
},
});
}
@ -59,13 +71,13 @@ export function useDeleteCommentMutation(pageId?: string) {
onSuccess: (data, variables) => {
let comments = queryClient.getQueryData(RQ_KEY(pageId)) as IComment[];
if (comments) {
comments = comments.filter(comment => comment.id !== variables);
queryClient.setQueryData(RQ_KEY(pageId), comments);
// comments = comments.filter(comment => comment.id !== variables);
// queryClient.setQueryData(RQ_KEY(pageId), comments);
}
notifications.show({ message: 'Comment deleted successfully' });
notifications.show({ message: "Comment deleted successfully" });
},
onError: (error) => {
notifications.show({ message: 'Failed to delete comment', color: 'red' });
notifications.show({ message: "Failed to delete comment", color: "red" });
},
});
}
@ -76,21 +88,26 @@ export function useResolveCommentMutation() {
return useMutation({
mutationFn: (data: IResolveComment) => resolveComment(data),
onSuccess: (data: IComment, variables) => {
const currentComments = queryClient.getQueryData(RQ_KEY(data.pageId)) as IComment[];
const currentComments = queryClient.getQueryData(
RQ_KEY(data.pageId),
) as IComment[];
if (currentComments) {
const updatedComments = currentComments.map((comment) =>
comment.id === variables.commentId ? { ...comment, ...data } : comment,
comment.id === variables.commentId
? { ...comment, ...data }
: comment,
);
queryClient.setQueryData(RQ_KEY(data.pageId), updatedComments);
}
notifications.show({ message: 'Comment resolved successfully' });
notifications.show({ message: "Comment resolved successfully" });
},
onError: (error) => {
notifications.show({ message: 'Failed to resolve comment', color: 'red' });
notifications.show({
message: "Failed to resolve comment",
color: "red",
});
},
});
}

View File

@ -1,8 +1,14 @@
import api from '@/lib/api-client';
import { IComment, IResolveComment } from '@/features/comment/types/comment.types';
import api from "@/lib/api-client";
import {
IComment,
IResolveComment,
} from "@/features/comment/types/comment.types";
import { IPagination } from "@/lib/types.ts";
export async function createComment(data: Partial<IComment>): Promise<IComment> {
const req = await api.post<IComment>('/comments/create', data);
export async function createComment(
data: Partial<IComment>,
): Promise<IComment> {
const req = await api.post<IComment>("/comments/create", data);
return req.data as IComment;
}
@ -11,21 +17,25 @@ export async function resolveComment(data: IResolveComment): Promise<IComment> {
return req.data as IComment;
}
export async function updateComment(data: Partial<IComment>): Promise<IComment> {
export async function updateComment(
data: Partial<IComment>,
): Promise<IComment> {
const req = await api.post<IComment>(`/comments/update`, data);
return req.data as IComment;
}
export async function getCommentById(id: string): Promise<IComment> {
const req = await api.post<IComment>('/comments/view', { id });
export async function getCommentById(commentId: string): Promise<IComment> {
const req = await api.post<IComment>("/comments/info", { commentId });
return req.data as IComment;
}
export async function getPageComments(pageId: string): Promise<IComment[]> {
const req = await api.post<IComment[]>('/comments', { pageId });
return req.data as IComment[];
export async function getPageComments(
pageId: string,
): Promise<IPagination<IComment>> {
const req = await api.post("/comments", { pageId });
return req.data;
}
export async function deleteComment(id: string): Promise<void> {
await api.post('/comments/delete', { id });
export async function deleteComment(commentId: string): Promise<void> {
await api.post("/comments/delete", { commentId });
}

View File

@ -1,50 +1,76 @@
import { usePageHistoryListQuery, usePageHistoryQuery } from '@/features/page-history/queries/page-history-query';
import { useParams } from 'react-router-dom';
import HistoryItem from '@/features/page-history/components/history-item';
import { activeHistoryIdAtom, historyAtoms } from '@/features/page-history/atoms/history-atoms';
import { useAtom } from 'jotai';
import { useCallback, useEffect } from 'react';
import { Button, ScrollArea, Group, Divider, Text } from '@mantine/core';
import { pageEditorAtom, titleEditorAtom } from '@/features/editor/atoms/editor-atoms';
import { modals } from '@mantine/modals';
import { notifications } from '@mantine/notifications';
import {
usePageHistoryListQuery,
usePageHistoryQuery,
} from "@/features/page-history/queries/page-history-query";
import { useParams } from "react-router-dom";
import HistoryItem from "@/features/page-history/components/history-item";
import {
activeHistoryIdAtom,
historyAtoms,
} from "@/features/page-history/atoms/history-atoms";
import { useAtom } from "jotai";
import { useCallback, useEffect } from "react";
import { Button, ScrollArea, Group, Divider, Text } from "@mantine/core";
import {
pageEditorAtom,
titleEditorAtom,
} from "@/features/editor/atoms/editor-atoms";
import { modals } from "@mantine/modals";
import { notifications } from "@mantine/notifications";
function HistoryList() {
const [activeHistoryId, setActiveHistoryId] = useAtom(activeHistoryIdAtom);
const { pageId } = useParams();
const { data, isLoading, isError } = usePageHistoryListQuery(pageId);
const {
data: pageHistoryList,
isLoading,
isError,
} = usePageHistoryListQuery(pageId);
const { data: activeHistoryData } = usePageHistoryQuery(activeHistoryId);
const [mainEditor] = useAtom(pageEditorAtom);
const [mainEditorTitle] = useAtom(titleEditorAtom);
const [, setHistoryModalOpen] = useAtom(historyAtoms);
const confirmModal = () => modals.openConfirmModal({
title: 'Please confirm your action',
const confirmModal = () =>
modals.openConfirmModal({
title: "Please confirm your action",
children: (
<Text size="sm">
Are you sure you want to restore this version? Any changes not versioned will be lost.
Are you sure you want to restore this version? Any changes not
versioned will be lost.
</Text>
),
labels: { confirm: 'Confirm', cancel: 'Cancel' },
labels: { confirm: "Confirm", cancel: "Cancel" },
onConfirm: handleRestore,
});
const handleRestore = useCallback(() => {
if (activeHistoryData) {
mainEditorTitle.chain().clearContent().setContent(activeHistoryData.title, true).run();
mainEditor.chain().clearContent().setContent(activeHistoryData.content).run();
mainEditorTitle
.chain()
.clearContent()
.setContent(activeHistoryData.title, true)
.run();
mainEditor
.chain()
.clearContent()
.setContent(activeHistoryData.content)
.run();
setHistoryModalOpen(false);
notifications.show({ message: 'Successfully restored' });
notifications.show({ message: "Successfully restored" });
}
}, [activeHistoryData]);
useEffect(() => {
if (data && data.length > 0 && !activeHistoryId) {
setActiveHistoryId(data[0].id);
if (
pageHistoryList &&
pageHistoryList.items.length > 0 &&
!activeHistoryId
) {
setActiveHistoryId(pageHistoryList.items[0].id);
}
}, [data]);
}, [pageHistoryList]);
if (isLoading) {
return <></>;
@ -54,14 +80,15 @@ function HistoryList() {
return <div>Error loading page history.</div>;
}
if (!data || data.length === 0) {
if (!pageHistoryList || pageHistoryList.items.length === 0) {
return <>No page history saved yet.</>;
}
return (
<div>
<ScrollArea h={620} w="100%" type="scroll" scrollbarSize={5}>
{data && data.map((historyItem, index) => (
{pageHistoryList &&
pageHistoryList.items.map((historyItem, index) => (
<HistoryItem
key={index}
historyItem={historyItem}
@ -74,11 +101,18 @@ function HistoryList() {
<Divider />
<Group p="xs" wrap="nowrap">
<Button size="compact-md" onClick={confirmModal}>Restore</Button>
<Button variant="default" size="compact-md" onClick={() => setHistoryModalOpen(false)}>Cancel</Button>
<Button size="compact-md" onClick={confirmModal}>
Restore
</Button>
<Button
variant="default"
size="compact-md"
onClick={() => setHistoryModalOpen(false)}
>
Cancel
</Button>
</Group>
</div>
);
}

View File

@ -1,18 +1,26 @@
import { useQuery, UseQueryResult } from '@tanstack/react-query';
import { getPageHistoryById, getPageHistoryList } from '@/features/page-history/services/page-history-service';
import { IPageHistory } from '@/features/page-history/types/page.types';
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import {
getPageHistoryById,
getPageHistoryList,
} from "@/features/page-history/services/page-history-service";
import { IPageHistory } from "@/features/page-history/types/page.types";
import { IPagination } from "@/lib/types.ts";
export function usePageHistoryListQuery(pageId: string): UseQueryResult<IPageHistory[], Error> {
export function usePageHistoryListQuery(
pageId: string,
): UseQueryResult<IPagination<IPageHistory>, Error> {
return useQuery({
queryKey: ['page-history-list', pageId],
queryKey: ["page-history-list", pageId],
queryFn: () => getPageHistoryList(pageId),
enabled: !!pageId,
});
}
export function usePageHistoryQuery(historyId: string): UseQueryResult<IPageHistory, Error> {
export function usePageHistoryQuery(
historyId: string,
): UseQueryResult<IPageHistory, Error> {
return useQuery({
queryKey: ['page-history', historyId],
queryKey: ["page-history", historyId],
queryFn: () => getPageHistoryById(historyId),
enabled: !!historyId,
staleTime: 10 * 60 * 1000,

View File

@ -1,12 +1,20 @@
import api from '@/lib/api-client';
import { IPageHistory } from '@/features/page-history/types/page.types';
import api from "@/lib/api-client";
import { IPageHistory } from "@/features/page-history/types/page.types";
export async function getPageHistoryList(pageId: string): Promise<IPageHistory[]> {
const req = await api.post<IPageHistory[]>('/pages/history', { pageId });
return req.data as IPageHistory[];
export async function getPageHistoryList(
pageId: string,
): Promise<IPageHistory[]> {
const req = await api.post("/pages/history", {
pageId,
});
return req.data;
}
export async function getPageHistoryById(id: string): Promise<IPageHistory> {
const req = await api.post<IPageHistory>('/pages/history/details', { id });
return req.data as IPageHistory;
export async function getPageHistoryById(
historyId: string,
): Promise<IPageHistory> {
const req = await api.post<IPageHistory>("/pages/history/info", {
historyId,
});
return req.data;
}

View File

@ -5,6 +5,8 @@ import {
HttpCode,
HttpStatus,
UseGuards,
NotFoundException,
ForbiddenException,
} from '@nestjs/common';
import { CommentService } from './comment.service';
import { CreateCommentDto } from './dto/create-comment.dto';
@ -15,43 +17,90 @@ import { AuthWorkspace } from '../../decorators/auth-workspace.decorator';
import { JwtAuthGuard } from '../../guards/jwt-auth.guard';
import { PaginationOptions } from '@docmost/db/pagination/pagination-options';
import { User, Workspace } from '@docmost/db/types/entity.types';
import SpaceAbilityFactory from '../casl/abilities/space-ability.factory';
import { PageRepo } from '@docmost/db/repos/page/page.repo';
import {
SpaceCaslAction,
SpaceCaslSubject,
} from '../casl/interfaces/space-ability.type';
import { CommentRepo } from '@docmost/db/repos/comment/comment.repo';
@UseGuards(JwtAuthGuard)
@Controller('comments')
export class CommentController {
constructor(private readonly commentService: CommentService) {}
constructor(
private readonly commentService: CommentService,
private readonly commentRepo: CommentRepo,
private readonly pageRepo: PageRepo,
private readonly spaceAbility: SpaceAbilityFactory,
) {}
@HttpCode(HttpStatus.CREATED)
@HttpCode(HttpStatus.OK)
@Post('create')
async create(
@Body() createCommentDto: CreateCommentDto,
@AuthUser() user: User,
@AuthWorkspace() workspace: Workspace,
) {
const page = await this.pageRepo.findById(createCommentDto.pageId);
if (!page) {
throw new NotFoundException('Page not found');
}
const ability = await this.spaceAbility.createForUser(user, page.spaceId);
if (ability.cannot(SpaceCaslAction.Create, SpaceCaslSubject.Page)) {
throw new ForbiddenException();
}
return this.commentService.create(user.id, workspace.id, createCommentDto);
}
@HttpCode(HttpStatus.OK)
@Post()
findPageComments(
@Post('/')
async findPageComments(
@Body() input: PageIdDto,
@Body()
pagination: PaginationOptions,
//@AuthUser() user: User,
@AuthUser() user: User,
// @AuthWorkspace() workspace: Workspace,
) {
const page = await this.pageRepo.findById(input.pageId);
if (!page) {
throw new NotFoundException('Page not found');
}
const ability = await this.spaceAbility.createForUser(user, page.spaceId);
if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) {
throw new ForbiddenException();
}
return this.commentService.findByPageId(input.pageId, pagination);
}
@HttpCode(HttpStatus.OK)
@Post('info')
findOne(@Body() input: CommentIdDto) {
return this.commentService.findById(input.commentId);
async findOne(@Body() input: CommentIdDto, @AuthUser() user: User) {
const comment = await this.commentRepo.findById(input.commentId);
if (!comment) {
throw new NotFoundException('Comment not found');
}
// TODO: add spaceId to comment entity.
const page = await this.pageRepo.findById(comment.pageId);
if (!page) {
throw new NotFoundException('Page not found');
}
const ability = await this.spaceAbility.createForUser(user, page.spaceId);
if (ability.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) {
throw new ForbiddenException();
}
return comment;
}
@HttpCode(HttpStatus.OK)
@Post('update')
update(@Body() updateCommentDto: UpdateCommentDto) {
update(@Body() updateCommentDto: UpdateCommentDto, @AuthUser() user: User) {
//TODO: only comment creators can update their comments
return this.commentService.update(
updateCommentDto.commentId,
updateCommentDto,
@ -60,7 +109,8 @@ export class CommentController {
@HttpCode(HttpStatus.OK)
@Post('delete')
remove(@Body() input: CommentIdDto) {
remove(@Body() input: CommentIdDto, @AuthUser() user: User) {
// TODO: only comment creators and admins can delete their comments
return this.commentService.remove(input.commentId);
}
}

View File

@ -1,10 +1,9 @@
import { Module } from '@nestjs/common';
import { CommentService } from './comment.service';
import { CommentController } from './comment.controller';
import { PageModule } from '../page/page.module';
@Module({
imports: [PageModule],
imports: [],
controllers: [CommentController],
providers: [CommentService],
exports: [CommentService],

View File

@ -5,17 +5,17 @@ import {
} from '@nestjs/common';
import { CreateCommentDto } from './dto/create-comment.dto';
import { UpdateCommentDto } from './dto/update-comment.dto';
import { PageService } from '../page/services/page.service';
import { CommentRepo } from '@docmost/db/repos/comment/comment.repo';
import { Comment } from '@docmost/db/types/entity.types';
import { PaginationOptions } from '@docmost/db/pagination/pagination-options';
import { PaginationResult } from '@docmost/db/pagination/pagination';
import { PageRepo } from '@docmost/db/repos/page/page.repo';
@Injectable()
export class CommentService {
constructor(
private commentRepo: CommentRepo,
private pageService: PageService,
private pageRepo: PageRepo,
) {}
async findById(commentId: string) {
@ -35,7 +35,7 @@ export class CommentService {
) {
const commentContent = JSON.parse(createCommentDto.content);
const page = await this.pageService.findById(createCommentDto.pageId);
const page = await this.pageRepo.findById(createCommentDto.pageId);
// const spaceId = null; // todo, get from page
if (!page) {
@ -59,7 +59,7 @@ export class CommentService {
const createdComment = await this.commentRepo.insertComment({
pageId: createCommentDto.pageId,
content: commentContent,
selection: createCommentDto?.selection.substring(0, 250),
selection: createCommentDto?.selection?.substring(0, 250),
type: 'inline', // for now
parentCommentId: createCommentDto?.parentCommentId,
creatorId: userId,
@ -74,7 +74,7 @@ export class CommentService {
pageId: string,
pagination: PaginationOptions,
): Promise<PaginationResult<Comment>> {
const page = await this.pageService.findById(pageId);
const page = await this.pageRepo.findById(pageId);
if (!page) {
throw new BadRequestException('Page not found');

View File

@ -18,24 +18,11 @@ import { DB } from '@docmost/db/types/db';
export class PageHistoryRepo {
constructor(@InjectKysely() private readonly db: KyselyDB) {}
private baseFields: Array<keyof PageHistory> = [
'id',
'pageId',
'title',
'slug',
'icon',
'coverPhoto',
'version',
'lastUpdatedById',
'workspaceId',
'createdAt',
'updatedAt',
];
async findById(pageHistoryId: string): Promise<PageHistory> {
return await this.db
.selectFrom('pageHistory')
.select((eb) => [...this.baseFields, this.withLastUpdatedBy(eb)])
.selectAll()
.select((eb) => this.withLastUpdatedBy(eb))
.where('id', '=', pageHistoryId)
.executeTakeFirst();
}
@ -83,7 +70,8 @@ export class PageHistoryRepo {
async findPageHistoryByPageId(pageId: string, pagination: PaginationOptions) {
const query = this.db
.selectFrom('pageHistory')
.select((eb) => [...this.baseFields, this.withLastUpdatedBy(eb)])
.selectAll()
.select((eb) => this.withLastUpdatedBy(eb))
.where('pageId', '=', pageId)
.orderBy('createdAt', 'desc');
@ -101,6 +89,6 @@ export class PageHistoryRepo {
.selectFrom('users')
.select(['users.id', 'users.name', 'users.avatarUrl'])
.whereRef('users.id', '=', 'pageHistory.lastUpdatedById'),
).as('withLastUpdatedBy');
).as('lastUpdatedBy');
}
}

View File

@ -10,40 +10,40 @@
"@hocuspocus/provider": "^2.11.3",
"@hocuspocus/server": "^2.11.3",
"@hocuspocus/transformer": "^2.11.3",
"@tiptap/extension-code-block": "^2.2.4",
"@tiptap/extension-collaboration": "^2.2.4",
"@tiptap/extension-collaboration-cursor": "^2.2.4",
"@tiptap/extension-color": "^2.2.4",
"@tiptap/extension-document": "^2.2.4",
"@tiptap/extension-heading": "^2.2.4",
"@tiptap/extension-highlight": "^2.2.4",
"@tiptap/extension-link": "^2.2.4",
"@tiptap/extension-list-item": "^2.2.4",
"@tiptap/extension-list-keymap": "^2.2.4",
"@tiptap/extension-mention": "^2.2.4",
"@tiptap/extension-placeholder": "^2.2.4",
"@tiptap/extension-subscript": "^2.2.4",
"@tiptap/extension-superscript": "^2.2.4",
"@tiptap/extension-task-item": "^2.2.4",
"@tiptap/extension-task-list": "^2.2.4",
"@tiptap/extension-text": "^2.2.4",
"@tiptap/extension-text-align": "^2.2.4",
"@tiptap/extension-text-style": "^2.2.4",
"@tiptap/extension-typography": "^2.2.4",
"@tiptap/extension-underline": "^2.2.4",
"@tiptap/html": "^2.2.4",
"@tiptap/pm": "^2.2.4",
"@tiptap/react": "^2.2.4",
"@tiptap/starter-kit": "^2.2.4",
"@tiptap/suggestion": "^2.2.4",
"@tiptap/extension-code-block": "^2.3.0",
"@tiptap/extension-collaboration": "^2.3.0",
"@tiptap/extension-collaboration-cursor": "^2.3.0",
"@tiptap/extension-color": "^2.3.0",
"@tiptap/extension-document": "^2.3.0",
"@tiptap/extension-heading": "^2.3.0",
"@tiptap/extension-highlight": "^2.3.0",
"@tiptap/extension-link": "^2.3.0",
"@tiptap/extension-list-item": "^2.3.0",
"@tiptap/extension-list-keymap": "^2.3.0",
"@tiptap/extension-mention": "^2.3.0",
"@tiptap/extension-placeholder": "^2.3.0",
"@tiptap/extension-subscript": "^2.3.0",
"@tiptap/extension-superscript": "^2.3.0",
"@tiptap/extension-task-item": "^2.3.0",
"@tiptap/extension-task-list": "^2.3.0",
"@tiptap/extension-text": "^2.3.0",
"@tiptap/extension-text-align": "^2.3.0",
"@tiptap/extension-text-style": "^2.3.0",
"@tiptap/extension-typography": "^2.3.0",
"@tiptap/extension-underline": "^2.3.0",
"@tiptap/html": "^2.3.0",
"@tiptap/pm": "^2.3.0",
"@tiptap/react": "^2.3.0",
"@tiptap/starter-kit": "^2.3.0",
"@tiptap/suggestion": "^2.3.0",
"fractional-indexing-jittered": "^0.9.1",
"tiptap-extension-global-drag-handle": "^0.1.6",
"y-indexeddb": "^9.0.12",
"yjs": "^13.6.14"
},
"devDependencies": {
"@nx/js": "18.2.2",
"nx": "18.2.2"
"@nx/js": "18.3.3",
"nx": "18.3.3"
},
"workspaces": {
"packages": [

570
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff