@supernova-studio/client 0.47.43 → 0.47.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,29 @@
1
+ import {
2
+ DocumentationItemConfigurationV2,
3
+ DocumentationPageV2,
4
+ ElementGroup,
5
+ mapByUnique,
6
+ } from "@supernova-studio/model";
7
+ import deepEqual from "deep-equal";
1
8
  import * as Y from "yjs";
2
- import { DTODocumentationHierarchyV2, documentationElementsToHierarchyDto } from "../../api";
9
+ import {
10
+ DTODocumentationDraftState,
11
+ DTODocumentationDraftStateUpdated,
12
+ DTODocumentationHierarchyV2,
13
+ documentationItemConfigurationToDTOV2,
14
+ documentationPagesToDTOV2,
15
+ elementGroupsToDocumentationGroupDTOV2,
16
+ } from "../../api";
3
17
  import { generateHash } from "../../utils";
4
18
  import { DocumentationPageEditorModel } from "../docs-editor";
5
19
  import { VersionRoomBaseYDoc } from "./base";
6
20
 
21
+ type ItemState = {
22
+ title: string;
23
+ configuration: DocumentationItemConfigurationV2 | undefined;
24
+ contentHash: string;
25
+ };
26
+
7
27
  export class FrontendVersionRoomYDoc {
8
28
  private readonly yDoc: Y.Doc;
9
29
 
@@ -11,19 +31,201 @@ export class FrontendVersionRoomYDoc {
11
31
  this.yDoc = yDoc;
12
32
  }
13
33
 
34
+ //
35
+ // Hierarchy
36
+ //
37
+
14
38
  getDocumentationHierarchy(): DTODocumentationHierarchyV2 {
15
39
  const doc = new VersionRoomBaseYDoc(this.yDoc);
16
40
 
17
- const pages = doc.getDocumentationPages();
18
- const groups = doc.getDocumentationGroups();
41
+ // Read current room data
42
+ const pages = doc.getPages();
43
+ const groups = doc.getGroups();
44
+
19
45
  const settings = doc.getDocumentationInternalSettings();
20
46
 
21
- // Map model onto client DTO
22
- const hierarchy = documentationElementsToHierarchyDto(pages, groups, settings.routingVersion);
47
+ // Convert pages to DTOs with draft states
48
+ const pageDTOs = documentationPagesToDTOV2(pages, groups, settings.routingVersion);
49
+ const pageDraftStates = this.buildPageDraftStates(pages);
50
+ pageDTOs.forEach(p => {
51
+ const draftState = pageDraftStates.get(p.id);
52
+ draftState && (p.draftState = draftState);
53
+ });
54
+
55
+ // Convert groups to DTOs with draft states
56
+ const groupDTOs = elementGroupsToDocumentationGroupDTOV2(groups, pages);
57
+ const groupDraftStates = this.buildGroupDraftStates(groups);
58
+ groupDTOs.forEach(g => {
59
+ const draftState = groupDraftStates.get(g.id);
60
+ draftState && (g.draftState = draftState);
61
+ });
62
+
63
+ // Read deleted room data
64
+ const deletedGroups = doc.getGroupDeletedSnapshots().map(s => s.group);
65
+ const deletedPages = doc.getPageDeletedSnapshots().map(s => s.page);
66
+
67
+ // Convert deleted pages to DTOs with draft states
68
+ const deletedPageDTOs = documentationPagesToDTOV2(
69
+ deletedPages,
70
+ [...groups, ...deletedGroups],
71
+ settings.routingVersion
72
+ ).map(p => {
73
+ return { ...p, draftState: { changeType: "Deleted" } } as const;
74
+ });
75
+
76
+ // Convert deleted groups to DTOs with draft states
77
+ const deletedGroupDTOs = elementGroupsToDocumentationGroupDTOV2(deletedGroups, deletedPages).map(g => {
78
+ return { ...g, draftState: { changeType: "Deleted" } } as const;
79
+ });
80
+
81
+ return {
82
+ pages: pageDTOs,
83
+ groups: groupDTOs,
84
+
85
+ deletedPages: deletedPageDTOs,
86
+ deletedGroups: deletedGroupDTOs,
87
+ };
88
+ }
89
+
90
+ //
91
+ // Drafts - Pages
92
+ //
93
+
94
+ private buildPageDraftStates(pages: DocumentationPageV2[]): Map<string, DTODocumentationDraftState> {
95
+ const doc = new VersionRoomBaseYDoc(this.yDoc);
96
+
97
+ // Read room data
98
+ const pageHashes = doc.getDocumentationPageContentHashes();
99
+ const publishedSnapshots = doc.getPagePublishedSnapshots();
100
+
101
+ const publishedSnapshotsByPageId = mapByUnique(publishedSnapshots, s => s.page.id);
102
+ const publishedPagesById = mapByUnique(
103
+ publishedSnapshots.map(s => s.page),
104
+ p => p.id
105
+ );
106
+
107
+ const result = new Map<string, DTODocumentationDraftState>();
108
+
109
+ pages.forEach(page => {
110
+ // Current state
111
+ const currentPageContentHash = pageHashes[page.persistentId] ?? "";
112
+ const currentState: ItemState = this.itemStateFromPage(page, currentPageContentHash);
23
113
 
24
- return hierarchy;
114
+ // Published state
115
+ const snapshot = publishedSnapshotsByPageId.get(page.id);
116
+ let publishedState: ItemState | undefined;
117
+ if (snapshot) {
118
+ const publishedPage = publishedPagesById.get(snapshot.page.id)!;
119
+ publishedState = this.itemStateFromPage(publishedPage, snapshot.pageContentHash);
120
+ }
121
+
122
+ // Calculate draft
123
+ const draftState = this.createDraftState(currentState, publishedState);
124
+ if (draftState) result.set(page.id, draftState);
125
+ });
126
+
127
+ return result;
25
128
  }
26
129
 
130
+ private itemStateFromPage(page: DocumentationPageV2, pageContentHash: string): ItemState {
131
+ return {
132
+ title: page.meta.name,
133
+ configuration: page.data.configuration,
134
+ contentHash: pageContentHash,
135
+ };
136
+ }
137
+
138
+ //
139
+ // Drafts - Groups
140
+ //
141
+
142
+ private buildGroupDraftStates(groups: ElementGroup[]): Map<string, DTODocumentationDraftState> {
143
+ const doc = new VersionRoomBaseYDoc(this.yDoc);
144
+
145
+ // Read room data
146
+ const publishedSnapshots = doc.getGroupPublishedSnapshots();
147
+
148
+ const publishedSnapshotsByGroupId = mapByUnique(publishedSnapshots, s => s.group.id);
149
+ const publishedGroupsById = mapByUnique(
150
+ publishedSnapshots.map(s => s.group),
151
+ g => g.id
152
+ );
153
+
154
+ const result = new Map<string, DTODocumentationDraftState>();
155
+
156
+ groups.forEach(group => {
157
+ // Current state
158
+ const currentState: ItemState = this.itemStateFromGroup(group);
159
+
160
+ // Published state
161
+ const snapshot = publishedSnapshotsByGroupId.get(group.id);
162
+ let publishedState: ItemState | undefined;
163
+ if (snapshot) {
164
+ const publishedGroup = publishedGroupsById.get(snapshot.group.id)!;
165
+ publishedState = this.itemStateFromGroup(publishedGroup);
166
+ }
167
+
168
+ // Calculate draft
169
+ const draftState = this.createDraftState(currentState, publishedState);
170
+ if (draftState) result.set(group.id, draftState);
171
+ });
172
+
173
+ return result;
174
+ }
175
+
176
+ private itemStateFromGroup(group: ElementGroup): ItemState {
177
+ return {
178
+ title: group.meta.name,
179
+ configuration: group.data?.configuration,
180
+ contentHash: "-",
181
+ };
182
+ }
183
+
184
+ //
185
+ // Drafts - Shared
186
+ //
187
+
188
+ private createDraftState(
189
+ currentState: ItemState,
190
+ publishedState: ItemState | undefined
191
+ ): DTODocumentationDraftState | undefined {
192
+ if (!publishedState) {
193
+ // New item (hasn't been published yet)
194
+ return { changeType: "Created" };
195
+ }
196
+
197
+ // Compare current item state to the published state
198
+ const updatedDraftState: DTODocumentationDraftStateUpdated = {
199
+ changeType: "Updated",
200
+ changes: {},
201
+ };
202
+
203
+ if (currentState.title !== publishedState.title) {
204
+ updatedDraftState.changes.previousTitle = publishedState.title;
205
+ }
206
+
207
+ if (!deepEqual(currentState.configuration, publishedState.configuration)) {
208
+ const configurationDto = documentationItemConfigurationToDTOV2(publishedState.configuration);
209
+ updatedDraftState.changes.previousConfiguration = configurationDto;
210
+ }
211
+
212
+ if (currentState.contentHash !== publishedState.contentHash) {
213
+ updatedDraftState.changes.previousContentHash = publishedState.contentHash;
214
+ }
215
+
216
+ if (Object.keys(updatedDraftState).length) {
217
+ // Item has at least one of the draft changes
218
+ return updatedDraftState;
219
+ }
220
+
221
+ // Item has no draft changes compared to the published item
222
+ return undefined;
223
+ }
224
+
225
+ //
226
+ // Update page content hash
227
+ //
228
+
27
229
  notifyDocumentationPageContentUpdated(pageId: string, content: DocumentationPageEditorModel) {
28
230
  const pageContentHash = generateHash(content);
29
231
 
@@ -31,4 +233,14 @@ export class FrontendVersionRoomYDoc {
31
233
  [pageId]: pageContentHash,
32
234
  });
33
235
  }
236
+
237
+ //
238
+ // Misc
239
+ //
240
+
241
+ isDraftFeatureAdopted() {
242
+ const doc = new VersionRoomBaseYDoc(this.yDoc);
243
+ const settings = doc.getDocumentationInternalSettings();
244
+ return settings.isDraftFeatureAdopted;
245
+ }
34
246
  }
@@ -1,25 +0,0 @@
1
- import { DocumentationPageV2, ElementGroup } from "@supernova-studio/model";
2
- import { DTODocumentationHierarchyV2 } from "../../dto";
3
- import { elementGroupsToDocumentationGroupStructureDTOV2 } from "./documentation-group-v2-to-dto";
4
- import { documentationPagesToStructureDTOV2 } from "./documentation-page-v2-to-dto";
5
-
6
- // The fact that DTO conversion is located here instead of main backend code is due to the fact
7
- // that we store page and group data in YJS documents in the same way as they are stored in the database.
8
- // Therefore, we need to expose this conversion to the client so that it can consume data from YJS documents.
9
- //
10
- // Please do not put more DTO conversion here unless you know what you're doing.
11
-
12
- export function documentationElementsToHierarchyDto(
13
- docPages: DocumentationPageV2[],
14
- docGroups: ElementGroup[],
15
- routingVersion: string
16
- ): DTODocumentationHierarchyV2 {
17
- return {
18
- pages: documentationPagesToStructureDTOV2(docPages, docGroups, routingVersion),
19
- groups: elementGroupsToDocumentationGroupStructureDTOV2(docGroups, docPages),
20
-
21
- // TODO Artem
22
- deletedGroups: [],
23
- deletedPages: [],
24
- };
25
- }