feat: page content update and output

This commit is contained in:
Philipinho
2026-02-11 23:27:22 -08:00
parent 19806eb060
commit 623182c447
3 changed files with 62 additions and 38 deletions
@@ -1,5 +1,12 @@
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { Hocuspocus, Document } from '@hocuspocus/server'; import { Hocuspocus, Document } from '@hocuspocus/server';
import { TiptapTransformer } from '@hocuspocus/transformer';
import {
prosemirrorNodeToYElement,
tiptapExtensions,
} from './collaboration.util';
import * as Y from 'yjs';
import { User } from '@docmost/db/types/entity.types';
export type CollabEventHandlers = ReturnType< export type CollabEventHandlers = ReturnType<
CollaborationHandler['getHandlers'] CollaborationHandler['getHandlers']
@@ -20,6 +27,47 @@ export class CollaborationHandler {
// const fragment = doc.getXmlFragment('default'); // const fragment = doc.getXmlFragment('default');
//}); //});
}, },
updatePageContent: async (
documentName: string,
payload: {
pageId: string;
prosemirrorJson: any;
operation: string;
user: User;
},
) => {
const { pageId, prosemirrorJson, operation, user } = payload;
this.logger.debug(
'Updating page content via yjs',
documentName,
payload,
);
await this.withYdocConnection(
hocuspocus,
documentName,
{ user },
(doc) => {
const fragment = doc.getXmlFragment('default');
if (operation === 'replace') {
if (fragment.length > 0) {
fragment.delete(0, fragment.length);
}
const newDoc = TiptapTransformer.toYdoc(
prosemirrorJson,
'default',
tiptapExtensions,
);
Y.applyUpdate(doc, Y.encodeStateAsUpdate(newDoc));
} else {
const newContent = prosemirrorJson.content || [];
const yElements = newContent.map(prosemirrorNodeToYElement);
fragment.insert(fragment.length, yElements);
}
},
);
},
}; };
} }
+1 -1
View File
@@ -116,7 +116,7 @@ export class PageController {
throw new ForbiddenException(); throw new ForbiddenException();
} }
return this.pageService.update(page, updatePageDto, user.id); return this.pageService.update(page, updatePageDto, user);
} }
@HttpCode(HttpStatus.OK) @HttpCode(HttpStatus.OK)
@@ -32,8 +32,6 @@ import {
htmlToJson, htmlToJson,
jsonToNode, jsonToNode,
jsonToText, jsonToText,
prosemirrorNodeToYElement,
tiptapExtensions,
} from 'src/collaboration/collaboration.util'; } from 'src/collaboration/collaboration.util';
import { import {
CopyPageMapEntry, CopyPageMapEntry,
@@ -48,8 +46,6 @@ import { EventName } from '../../../common/events/event.contants';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { CollaborationGateway } from '../../../collaboration/collaboration.gateway'; import { CollaborationGateway } from '../../../collaboration/collaboration.gateway';
import { markdownToHtml } from '@docmost/editor-ext'; import { markdownToHtml } from '@docmost/editor-ext';
import { TiptapTransformer } from '@hocuspocus/transformer';
import * as Y from 'yjs';
@Injectable() @Injectable()
export class PageService { export class PageService {
@@ -197,17 +193,17 @@ export class PageService {
async update( async update(
page: Page, page: Page,
updatePageDto: UpdatePageDto, updatePageDto: UpdatePageDto,
userId: string, user: User,
): Promise<Page> { ): Promise<Page> {
const contributors = new Set<string>(page.contributorIds); const contributors = new Set<string>(page.contributorIds);
contributors.add(userId); contributors.add(user.id);
const contributorIds = Array.from(contributors); const contributorIds = Array.from(contributors);
await this.pageRepo.updatePage( await this.pageRepo.updatePage(
{ {
title: updatePageDto.title, title: updatePageDto.title,
icon: updatePageDto.icon, icon: updatePageDto.icon,
lastUpdatedById: userId, lastUpdatedById: user.id,
updatedAt: new Date(), updatedAt: new Date(),
contributorIds: contributorIds, contributorIds: contributorIds,
}, },
@@ -224,7 +220,7 @@ export class PageService {
updatePageDto.content, updatePageDto.content,
updatePageDto.operation, updatePageDto.operation,
updatePageDto.input, updatePageDto.input,
userId, user,
); );
} }
@@ -242,7 +238,7 @@ export class PageService {
content: string | object, content: string | object,
operation: ContentOperation, operation: ContentOperation,
input: InputFormat, input: InputFormat,
userId: string, user: User,
): Promise<void> { ): Promise<void> {
let prosemirrorJson: any; let prosemirrorJson: any;
@@ -270,35 +266,11 @@ export class PageService {
} }
const documentName = `page.${pageId}`; const documentName = `page.${pageId}`;
const connection = await this.collaborationGateway.openDirectConnection( await this.collaborationGateway.handleYjsEvent(
'updatePageContent',
documentName, documentName,
{ user: { id: userId } }, { pageId, operation, prosemirrorJson, user },
); );
try {
await connection.transact((doc) => {
const fragment = doc.getXmlFragment('default');
if (operation === 'replace') {
if (fragment.length > 0) {
fragment.delete(0, fragment.length);
}
const newDoc = TiptapTransformer.toYdoc(
prosemirrorJson,
'default',
tiptapExtensions,
);
Y.applyUpdate(doc, Y.encodeStateAsUpdate(newDoc));
} else {
const newContent = prosemirrorJson.content || [];
const yElements = newContent.map(prosemirrorNodeToYElement);
fragment.insert(fragment.length, yElements);
}
});
} finally {
await connection.disconnect();
}
} }
async getSidebarPages( async getSidebarPages(
@@ -334,7 +306,11 @@ export class PageService {
cursor: pagination.cursor, cursor: pagination.cursor,
beforeCursor: pagination.beforeCursor, beforeCursor: pagination.beforeCursor,
fields: [ fields: [
{ expression: 'position', direction: 'asc', orderModifier: (ob) => ob.collate('C').asc() }, {
expression: 'position',
direction: 'asc',
orderModifier: (ob) => ob.collate('C').asc(),
},
{ expression: 'id', direction: 'asc' }, { expression: 'id', direction: 'asc' },
], ],
parseCursor: (cursor) => ({ parseCursor: (cursor) => ({