@@ -12,31 +12,19 @@ import * as Y from 'yjs';
1212import { NodeService } from '../node/node.service' ;
1313import { PageService } from '../page/page.service' ;
1414import { NodeCacheService } from '../node-cache/node-cache.service' ;
15- import { yXmlFragmentToProsemirrorJSON } from 'y-prosemirror' ;
15+ import {
16+ yXmlFragmentToProsemirrorJSON ,
17+ prosemirrorJSONToYXmlFragment ,
18+ prosemirrorJSONToYDoc ,
19+ yDocToProsemirrorJSON ,
20+ } from 'y-prosemirror' ;
21+ import { novelEditorSchema } from './yjs.schema' ;
22+ import { Schema } from 'prosemirror-model' ;
1623import { EdgeService } from '../edge/edge.service' ;
17- // yMap에 저장되는 Node 형태
18- type YMapNode = {
19- id : string ; // 노드 아이디
20- type : string ; // 노드의 유형
21- data : {
22- title : string ; // 제목
23- id : number ; // 페이지 아이디
24- } ;
25- position : {
26- x : number ; // X 좌표
27- y : number ; // Y 좌표
28- } ;
29- selected : boolean ;
30- } ;
31-
32- // yMap에 저장되는 edge 형태
33- type YMapEdge = {
34- id : string ; // Edge 아이디
35- source : string ; // 출발 노드 아이디
36- target : string ; // 도착 노드 아이디
37- sourceHandle : string ;
38- targetHandle : string ;
39- } ;
24+ import { Node } from 'src/node/node.entity' ;
25+ import { Edge } from 'src/edge/edge.entity' ;
26+ import { YMapEdge } from './yjs.type' ;
27+ import { YMapNode } from './yjs.type' ;
4028
4129// Y.Doc에는 name 컬럼이 없어서 생성했습니다.
4230class CustomDoc extends Y . Doc {
@@ -47,6 +35,7 @@ class CustomDoc extends Y.Doc {
4735 this . name = name ;
4836 }
4937}
38+
5039@WebSocketGateway ( 1234 )
5140export class YjsService
5241 implements OnGatewayInit , OnGatewayConnection , OnGatewayDisconnect
@@ -62,7 +51,27 @@ export class YjsService
6251 ) { }
6352 @WebSocketServer ( )
6453 server : Server ;
54+ insertProseMirrorDataToXmlFragment ( xmlFragment : Y . XmlFragment , data : any [ ] ) {
55+ // XML Fragment 초기화
56+ xmlFragment . delete ( 0 , xmlFragment . length ) ;
57+
58+ // 데이터를 순회하면서 추가
59+ data . forEach ( ( nodeData ) => {
60+ const yNode = new Y . XmlElement ( nodeData . type ) ;
61+
62+ if ( nodeData . content ) {
63+ nodeData . content . forEach ( ( child ) => {
64+ if ( child . type === 'text' ) {
65+ const yText = new Y . XmlText ( ) ;
66+ yText . insert ( 0 , child . text ) ;
67+ yNode . push ( [ yText ] ) ;
68+ }
69+ } ) ;
70+ }
6571
72+ xmlFragment . push ( [ yNode ] ) ;
73+ } ) ;
74+ }
6675 afterInit ( ) {
6776 if ( ! this . server ) {
6877 this . logger . error ( '서버 초기화 안됨..!' ) ;
@@ -75,23 +84,47 @@ export class YjsService
7584
7685 this . ysocketio . initialize ( ) ;
7786
78- this . ysocketio . on ( 'document-loaded' , ( doc : Y . Doc ) => {
79- const nodes = doc . getMap ( 'nodes' ) ;
80- const edges = doc . getMap ( 'edges' ) ;
87+ this . ysocketio . on ( 'document-loaded' , async ( doc : Y . Doc ) => {
88+ // Y.Doc에 name이 없어서 새로 만든 CustomDoc
8189 const editorDoc = doc . getXmlFragment ( 'default' ) ;
90+ const customDoc = editorDoc . doc as CustomDoc ;
8291
83- // page content의 변경 사항을 감지한다.
84- editorDoc . observeDeep ( ( ) => {
85- const document = editorDoc . doc as CustomDoc ;
86- const pageId = parseInt ( document . name . split ( '-' ) [ 1 ] ) ;
87- this . pageService . updatePage (
88- pageId ,
89- JSON . parse ( JSON . stringify ( yXmlFragmentToProsemirrorJSON ( editorDoc ) ) ) ,
90- ) ;
91- } ) ;
92+ // document name이 flow-room이라면 모든 노드들을 볼 수 있는 화면입니다.
93+ // 노드를 클릭해 페이지를 열었을 때만 해당 페이지 값을 가져와서 초기 데이터로 세팅해줍니다.
94+ if ( customDoc . name !== 'flow-room' ) {
95+ const pageId = parseInt ( customDoc . name . split ( '-' ) [ 1 ] ) ;
96+ const content = await this . pageService . findPageById ( pageId ) ;
97+
98+ // content가 비어있다면 내부 구조가 novel editor schema를 따르지 않기 때문에 오류가 납니다.
99+ // type이라는 key가 있을 때만 초기 데이터를 세팅해줍니다.
100+ 'type' in content &&
101+ this . initializePageContent ( content . content , editorDoc ) ;
102+
103+ // 페이지 내용 변경 사항을 감지해서 데이터베이스에 갱신합니다.
104+ editorDoc . observeDeep ( ( ) => {
105+ const document = editorDoc . doc as CustomDoc ;
106+ const pageId = parseInt ( document . name . split ( '-' ) [ 1 ] ) ;
107+ this . pageService . updatePage (
108+ pageId ,
109+ JSON . parse (
110+ JSON . stringify ( yXmlFragmentToProsemirrorJSON ( editorDoc ) ) ,
111+ ) ,
112+ ) ;
113+ } ) ;
114+ return ;
115+ }
116+
117+ // 만약 페이지가 아닌 모든 노드들을 볼 수 있는 document라면 node, edge 초기 데이터를 세팅해줍니다.
118+ // node, edge, page content 가져오기
119+ const nodes = await this . nodeService . findNodes ( ) ;
120+ const edges = await this . edgeService . findEdges ( ) ;
121+ const nodesMap = doc . getMap ( 'nodes' ) ;
122+ const edgesMap = doc . getMap ( 'edges' ) ;
123+
124+ this . initializeYNodeMap ( nodes , nodesMap ) ;
92125
93126 // node의 변경 사항을 감지한다.
94- nodes . observe ( ( ) => {
127+ nodesMap . observe ( ( ) => {
95128 const nodes = Object . values ( doc . getMap ( 'nodes' ) . toJSON ( ) ) ;
96129
97130 // 모든 노드에 대해 검사한다.
@@ -115,9 +148,10 @@ export class YjsService
115148 } ) ;
116149 } ) ;
117150 // edge의 변경 사항을 감지한다.
118- edges . observe ( ( ) => {
151+ edgesMap . observe ( ( ) => {
119152 const edges = Object . values ( doc . getMap ( 'edges' ) . toJSON ( ) ) ;
120153 edges . forEach ( async ( edge : YMapEdge ) => {
154+ console . log ( edge ) ;
121155 console . log ( edge ) ;
122156 const findEdge = await this . edgeService . findEdgeByFromNodeAndToNode (
123157 parseInt ( edge . source ) ,
@@ -135,6 +169,34 @@ export class YjsService
135169 } ) ;
136170 }
137171
172+ // YMap에 노드 정보를 넣어준다.
173+ initializeYNodeMap ( nodes : Node [ ] , yMap : Y . Map < Object > ) : void {
174+ nodes . forEach ( ( node ) => {
175+ console . log ( node ) ;
176+ const nodeId = node . id . toString ( ) ; // id를 string으로 변환
177+
178+ // Y.Map에 데이터를 삽입
179+ yMap . set ( nodeId , {
180+ id : nodeId ,
181+ type : 'note' ,
182+ data : {
183+ title : node . page . title ,
184+ id : node . page . id ,
185+ } ,
186+ position : {
187+ x : node . x ,
188+ y : node . y ,
189+ } ,
190+ selected : false , // 기본적으로 선택되지 않음
191+ } ) ;
192+ } ) ;
193+ }
194+
195+
196+ // yXmlFragment에 content를 넣어준다.
197+ initializePageContent ( content : JSON , yXmlFragment : Y . XmlFragment ) {
198+ prosemirrorJSONToYXmlFragment ( novelEditorSchema , content , yXmlFragment ) ;
199+ }
138200 handleConnection ( ) {
139201 this . logger . log ( '접속' ) ;
140202 }
0 commit comments