mirror of
https://github.com/docmost/docmost.git
synced 2026-08-31 02:46:27 +08:00
refactor naming
* support prepend
This commit is contained in:
@@ -30,18 +30,13 @@ export class CollaborationHandler {
|
|||||||
updatePageContent: async (
|
updatePageContent: async (
|
||||||
documentName: string,
|
documentName: string,
|
||||||
payload: {
|
payload: {
|
||||||
pageId: string;
|
|
||||||
prosemirrorJson: any;
|
prosemirrorJson: any;
|
||||||
operation: string;
|
contentOperation: string;
|
||||||
user: User;
|
user: User;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const { pageId, prosemirrorJson, operation, user } = payload;
|
const { prosemirrorJson, contentOperation, user } = payload;
|
||||||
this.logger.debug(
|
this.logger.debug('Updating page content via yjs', documentName);
|
||||||
'Updating page content via yjs',
|
|
||||||
documentName,
|
|
||||||
payload,
|
|
||||||
);
|
|
||||||
await this.withYdocConnection(
|
await this.withYdocConnection(
|
||||||
hocuspocus,
|
hocuspocus,
|
||||||
documentName,
|
documentName,
|
||||||
@@ -49,7 +44,7 @@ export class CollaborationHandler {
|
|||||||
(doc) => {
|
(doc) => {
|
||||||
const fragment = doc.getXmlFragment('default');
|
const fragment = doc.getXmlFragment('default');
|
||||||
|
|
||||||
if (operation === 'replace') {
|
if (contentOperation === 'replace') {
|
||||||
if (fragment.length > 0) {
|
if (fragment.length > 0) {
|
||||||
fragment.delete(0, fragment.length);
|
fragment.delete(0, fragment.length);
|
||||||
}
|
}
|
||||||
@@ -63,7 +58,9 @@ export class CollaborationHandler {
|
|||||||
} else {
|
} else {
|
||||||
const newContent = prosemirrorJson.content || [];
|
const newContent = prosemirrorJson.content || [];
|
||||||
const yElements = newContent.map(prosemirrorNodeToYElement);
|
const yElements = newContent.map(prosemirrorNodeToYElement);
|
||||||
fragment.insert(fragment.length, yElements);
|
const position =
|
||||||
|
contentOperation === 'prepend' ? 0 : fragment.length;
|
||||||
|
fragment.insert(position, yElements);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { LoggerExtension } from './extensions/logger.extension';
|
|||||||
import { CollaborationHandler } from './collaboration.handler';
|
import { CollaborationHandler } from './collaboration.handler';
|
||||||
import { CollabHistoryService } from './services/collab-history.service';
|
import { CollabHistoryService } from './services/collab-history.service';
|
||||||
|
|
||||||
@Global()
|
|
||||||
@Module({
|
@Module({
|
||||||
providers: [
|
providers: [
|
||||||
CollaborationGateway,
|
CollaborationGateway,
|
||||||
|
|||||||
@@ -181,7 +181,8 @@ export class PersistenceExtension implements Extension {
|
|||||||
|
|
||||||
async onChange(data: onChangePayload) {
|
async onChange(data: onChangePayload) {
|
||||||
const documentName = data.documentName;
|
const documentName = data.documentName;
|
||||||
const userId = data.context?.user.id;
|
const userId = data.context?.user?.id;
|
||||||
|
|
||||||
if (!userId) return;
|
if (!userId) return;
|
||||||
|
|
||||||
if (!this.contributors.has(documentName)) {
|
if (!this.contributors.has(documentName)) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { Transform } from 'class-transformer';
|
import { Transform } from 'class-transformer';
|
||||||
|
|
||||||
export type InputFormat = 'json' | 'markdown' | 'html';
|
export type ContentFormat = 'json' | 'markdown' | 'html';
|
||||||
|
|
||||||
export class CreatePageDto {
|
export class CreatePageDto {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -29,7 +29,7 @@ export class CreatePageDto {
|
|||||||
content?: string | object;
|
content?: string | object;
|
||||||
|
|
||||||
@ValidateIf((o) => o.content !== undefined)
|
@ValidateIf((o) => o.content !== undefined)
|
||||||
@Transform(({ value }) => value?.toLowerCase())
|
@Transform(({ value }) => value?.toLowerCase() ?? 'json')
|
||||||
@IsIn(['json', 'markdown', 'html'])
|
@IsIn(['json', 'markdown', 'html'])
|
||||||
input?: InputFormat;
|
format?: ContentFormat;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { Transform } from 'class-transformer';
|
import { Transform } from 'class-transformer';
|
||||||
|
|
||||||
export type OutputFormat = 'json' | 'markdown' | 'html';
|
import { ContentFormat } from './create-page.dto';
|
||||||
|
|
||||||
export class PageIdDto {
|
export class PageIdDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@@ -38,7 +38,7 @@ export class PageInfoDto extends PageIdDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Transform(({ value }) => value?.toLowerCase())
|
@Transform(({ value }) => value?.toLowerCase())
|
||||||
@IsIn(['json', 'markdown', 'html'])
|
@IsIn(['json', 'markdown', 'html'])
|
||||||
output?: OutputFormat;
|
format?: ContentFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DeletePageDto extends PageIdDto {
|
export class DeletePageDto extends PageIdDto {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { PartialType } from '@nestjs/mapped-types';
|
import { PartialType } from '@nestjs/mapped-types';
|
||||||
import { CreatePageDto, InputFormat } from './create-page.dto';
|
import { CreatePageDto, ContentFormat } from './create-page.dto';
|
||||||
import { IsIn, IsOptional, IsString, ValidateIf } from 'class-validator';
|
import { IsIn, IsOptional, IsString, ValidateIf } from 'class-validator';
|
||||||
import { Transform } from 'class-transformer';
|
import { Transform } from 'class-transformer';
|
||||||
|
|
||||||
export type ContentOperation = 'append' | 'replace';
|
export type ContentOperation = 'append' | 'prepend' | 'replace';
|
||||||
|
|
||||||
export class UpdatePageDto extends PartialType(CreatePageDto) {
|
export class UpdatePageDto extends PartialType(CreatePageDto) {
|
||||||
@IsString()
|
@IsString()
|
||||||
@@ -14,11 +14,11 @@ export class UpdatePageDto extends PartialType(CreatePageDto) {
|
|||||||
|
|
||||||
@ValidateIf((o) => o.content !== undefined)
|
@ValidateIf((o) => o.content !== undefined)
|
||||||
@Transform(({ value }) => value?.toLowerCase())
|
@Transform(({ value }) => value?.toLowerCase())
|
||||||
@IsIn(['append', 'replace'])
|
@IsIn(['append', 'prepend', 'replace'])
|
||||||
operation?: ContentOperation;
|
contentOperation?: ContentOperation;
|
||||||
|
|
||||||
@ValidateIf((o) => o.content !== undefined)
|
@ValidateIf((o) => o.content !== undefined)
|
||||||
@Transform(({ value }) => value?.toLowerCase())
|
@Transform(({ value }) => value?.toLowerCase() ?? 'json')
|
||||||
@IsIn(['json', 'markdown', 'html'])
|
@IsIn(['json', 'markdown', 'html'])
|
||||||
input?: InputFormat;
|
format?: ContentFormat;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,9 +70,9 @@ export class PageController {
|
|||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dto.output && dto.output !== 'json' && page.content) {
|
if (dto.format && dto.format !== 'json' && page.content) {
|
||||||
const contentOutput =
|
const contentOutput =
|
||||||
dto.output === 'markdown'
|
dto.format === 'markdown'
|
||||||
? jsonToMarkdown(page.content)
|
? jsonToMarkdown(page.content)
|
||||||
: jsonToHtml(page.content);
|
: jsonToHtml(page.content);
|
||||||
return {
|
return {
|
||||||
@@ -99,7 +99,25 @@ export class PageController {
|
|||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.pageService.create(user.id, workspace.id, createPageDto);
|
const page = await this.pageService.create(
|
||||||
|
user.id,
|
||||||
|
workspace.id,
|
||||||
|
createPageDto,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
createPageDto.format &&
|
||||||
|
createPageDto.format !== 'json' &&
|
||||||
|
page.content
|
||||||
|
) {
|
||||||
|
const contentOutput =
|
||||||
|
createPageDto.format === 'markdown'
|
||||||
|
? jsonToMarkdown(page.content)
|
||||||
|
: jsonToHtml(page.content);
|
||||||
|
return { ...page, content: contentOutput };
|
||||||
|
}
|
||||||
|
|
||||||
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@@ -116,7 +134,25 @@ export class PageController {
|
|||||||
throw new ForbiddenException();
|
throw new ForbiddenException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.pageService.update(page, updatePageDto, user);
|
const updatedPage = await this.pageService.update(
|
||||||
|
page,
|
||||||
|
updatePageDto,
|
||||||
|
user,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
updatePageDto.format &&
|
||||||
|
updatePageDto.format !== 'json' &&
|
||||||
|
updatedPage.content
|
||||||
|
) {
|
||||||
|
const contentOutput =
|
||||||
|
updatePageDto.format === 'markdown'
|
||||||
|
? jsonToMarkdown(updatedPage.content)
|
||||||
|
: jsonToHtml(updatedPage.content);
|
||||||
|
return { ...updatedPage, content: contentOutput };
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
Logger,
|
Logger,
|
||||||
NotFoundException,
|
NotFoundException,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { CreatePageDto, InputFormat } from '../dto/create-page.dto';
|
import { CreatePageDto, ContentFormat } from '../dto/create-page.dto';
|
||||||
import { ContentOperation, UpdatePageDto } from '../dto/update-page.dto';
|
import { ContentOperation, UpdatePageDto } from '../dto/update-page.dto';
|
||||||
import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
||||||
import { InsertablePage, Page, User } from '@docmost/db/types/entity.types';
|
import { InsertablePage, Page, User } from '@docmost/db/types/entity.types';
|
||||||
@@ -99,10 +99,10 @@ export class PageService {
|
|||||||
let textContent = undefined;
|
let textContent = undefined;
|
||||||
let ydoc = undefined;
|
let ydoc = undefined;
|
||||||
|
|
||||||
if (createPageDto?.content && createPageDto?.input) {
|
if (createPageDto?.content && createPageDto?.format) {
|
||||||
let prosemirrorJson: any;
|
let prosemirrorJson: any;
|
||||||
|
|
||||||
switch (createPageDto.input) {
|
switch (createPageDto.format) {
|
||||||
case 'markdown': {
|
case 'markdown': {
|
||||||
const html = await markdownToHtml(createPageDto.content as string);
|
const html = await markdownToHtml(createPageDto.content as string);
|
||||||
prosemirrorJson = htmlToJson(html as string);
|
prosemirrorJson = htmlToJson(html as string);
|
||||||
@@ -212,14 +212,14 @@ export class PageService {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
updatePageDto.content &&
|
updatePageDto.content &&
|
||||||
updatePageDto.operation &&
|
updatePageDto.contentOperation &&
|
||||||
updatePageDto.input
|
updatePageDto.format
|
||||||
) {
|
) {
|
||||||
await this.updatePageContent(
|
await this.updatePageContent(
|
||||||
page.id,
|
page.id,
|
||||||
updatePageDto.content,
|
updatePageDto.content,
|
||||||
updatePageDto.operation,
|
updatePageDto.contentOperation,
|
||||||
updatePageDto.input,
|
updatePageDto.format,
|
||||||
user,
|
user,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -236,13 +236,13 @@ export class PageService {
|
|||||||
async updatePageContent(
|
async updatePageContent(
|
||||||
pageId: string,
|
pageId: string,
|
||||||
content: string | object,
|
content: string | object,
|
||||||
operation: ContentOperation,
|
contentOperation: ContentOperation,
|
||||||
input: InputFormat,
|
format: ContentFormat,
|
||||||
user: User,
|
user: User,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
let prosemirrorJson: any;
|
let prosemirrorJson: any;
|
||||||
|
|
||||||
switch (input) {
|
switch (format) {
|
||||||
case 'markdown': {
|
case 'markdown': {
|
||||||
const html = await markdownToHtml(content as string);
|
const html = await markdownToHtml(content as string);
|
||||||
prosemirrorJson = htmlToJson(html as string);
|
prosemirrorJson = htmlToJson(html as string);
|
||||||
@@ -262,14 +262,14 @@ export class PageService {
|
|||||||
try {
|
try {
|
||||||
jsonToNode(prosemirrorJson);
|
jsonToNode(prosemirrorJson);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new BadRequestException('Invalid content format');
|
throw new BadRequestException('Invalid content');
|
||||||
}
|
}
|
||||||
|
|
||||||
const documentName = `page.${pageId}`;
|
const documentName = `page.${pageId}`;
|
||||||
await this.collaborationGateway.handleYjsEvent(
|
await this.collaborationGateway.handleYjsEvent(
|
||||||
'updatePageContent',
|
'updatePageContent',
|
||||||
documentName,
|
documentName,
|
||||||
{ pageId, operation, prosemirrorJson, user },
|
{ contentOperation, prosemirrorJson, user },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,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) => ({
|
||||||
|
|||||||
Reference in New Issue
Block a user