@netless/forge-slide 0.1.1-alpha.8 → 1.0.0-alpha.1

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,13 +1,12 @@
1
- import {Whiteboard, WhiteboardApplication, WhiteboardPermissionFlag} from "@netless/forge-whiteboard";
2
- import {ISlideConfig, Slide, SLIDE_EVENTS} from "@netless/slide";
3
- import {AbstractApplication} from "@netless/forge-room";
4
- import {SlideForge} from "./Slide";
5
- import {ForgeSlidePermissionFlag, ForgeSlidePermissions} from "./ForgeSlidePermession";
6
- import {FooterView} from "./FooterView";
7
- import * as Y from "yjs";
8
- import {SideBarView} from "./SiderBarView";
9
- import {deepEqual, delay} from "./utils";
10
- import { kvStore } from "@netless/forge-room";
1
+ import { Whiteboard, WhiteboardApplication, WhiteboardPermissionFlag } from '@netless/forge-whiteboard';
2
+ import { ISlideConfig, Slide, SLIDE_EVENTS } from '@netless/slide';
3
+ import { AbstractApplication, kvStore } from '@netless/forge-room';
4
+ import { SlideForge } from './Slide';
5
+ import { ForgeSlidePermissionFlag, ForgeSlidePermissions } from './ForgeSlidePermession';
6
+ import { FooterView } from './FooterView';
7
+ import * as Y from 'yjs';
8
+ import { SideBarView } from './SiderBarView';
9
+ import { deepEqual } from './utils';
11
10
 
12
11
  export interface SlideApplicationOption {
13
12
  prefix: string;
@@ -26,482 +25,555 @@ interface PreviewImage {
26
25
  height: number;
27
26
  }
28
27
 
29
- export const Slide_APP_NAME = "forge_slide";
28
+ export const Slide_APP_NAME = 'forge_slide';
30
29
 
31
30
  export class SlideApplication extends AbstractApplication<SlideApplicationOption, SlideForge> {
32
31
 
33
- static applicationName = Slide_APP_NAME;
34
-
35
- public readonly name: string = Slide_APP_NAME;
36
- public readonly emitter: SlideForge = new SlideForge();
37
-
38
- private whiteboardApp!: WhiteboardApplication;
39
- private whiteboard!: Whiteboard;
40
- private rootView: HTMLDivElement = document.createElement("div");
41
- private contentContainer: HTMLDivElement = document.createElement("div");
42
- private whiteboardContainer: HTMLDivElement = document.createElement("div");
43
- private slideContainer: HTMLDivElement = document.createElement("div");
44
- private permissions!: ForgeSlidePermissions;
45
- private footer: FooterView;
46
- private sideBar: SideBarView;
47
- private slide!: Slide;
48
- private currentSlideIndex: number = 0;
49
- private taskId: string = "";
50
- private prefix: string = "";
51
- private slideCount: number = 0;
52
-
53
- constructor() {
54
- super();
55
- (window as any).emitter = this.emitter;
56
- this.rootView.setAttribute("data-forge-app", Slide_APP_NAME);
57
- this.rootView.style.background = "#f0f0f0f0";
58
- this.rootView.style.overflow = "hidden";
59
-
60
- this.contentContainer.style.width = "100%";
61
- this.contentContainer.style.height = "100%";
62
- this.contentContainer.style.display = "flex";
63
- this.contentContainer.style.flexDirection = "column";
64
-
65
- this.slideContainer.style.width = "100%";
66
- this.slideContainer.style.height = "100%";
67
-
68
- this.footer = new FooterView();
69
- this.sideBar = new SideBarView();
70
- this.sideBar.on("pageChange", index => {
71
- if (index > 0 && index <= this.slideCount) {
72
- this.slide.renderSlide(index);
73
- }
74
- });
75
- this.footer.on("prevStep", () => {
76
- if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changeStep)) {
77
- return;
78
- }
79
- if (this.slide.mainSeqStep > 0 || this.slide.mainSeqState !== "idle") {
80
- this.slide.prevStep();
81
- } else if (this.currentSlideIndex > 1){
82
- if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changePage)) {
83
- return;
84
- }
85
- this.slide.renderSlide(this.currentSlideIndex - 1)
86
- }
87
- });
88
- this.footer.on("nextStep", () => {
89
- if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changeStep)) {
90
- return;
91
- }
92
- if (this.slide.mainSeqStep < this.slide.mainSeqLength) {
93
- this.slide.nextStep()
94
- } else {
95
- if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changePage)) {
96
- return;
97
- }
98
- this.slide.renderSlide(this.currentSlideIndex + 1)
99
- }
100
- });
101
- this.footer.on("prevPage", () => {
102
- if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changePage)) {
103
- return;
104
- }
105
- if (this.currentSlideIndex > 1) {
106
- this.slide.renderSlide(this.currentSlideIndex - 1);
107
- }
108
- });
109
- this.footer.on("nextPage", () => {
110
- if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changePage)) {
111
- return;
112
- }
113
- if (this.currentSlideIndex < this.slideCount) {
114
- this.slide.renderSlide(this.currentSlideIndex + 1);
115
- }
116
- });
117
-
118
- this.footer.on("sideBarToggle", () => {
119
- if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changePage)) {
120
- return;
121
- }
122
- if (this.sideBar.isShowSideBar) {
123
- this.sideBar.hidden();
124
- } else {
125
- this.sideBar.show();
126
- }
127
- })
128
-
129
- this.rootView.appendChild(this.contentContainer);
130
-
131
- this.emitter.on("renderStart", pageIndex => {
132
- this.footer.prevPageState(pageIndex !== 0);
133
- });
134
- const propertyConfig = {
135
- configurable: false,
136
- enumerable: false,
137
- writable: false
32
+ static applicationName = Slide_APP_NAME;
33
+
34
+ public readonly name: string = Slide_APP_NAME;
35
+ public readonly emitter: SlideForge = new SlideForge();
36
+
37
+ private whiteboardApp!: WhiteboardApplication;
38
+ private whiteboard!: Whiteboard;
39
+ private rootView: HTMLDivElement = document.createElement('div');
40
+ private contentContainer: HTMLDivElement = document.createElement('div');
41
+ private whiteboardContainer: HTMLDivElement = document.createElement('div');
42
+ private slideContainer: HTMLDivElement = document.createElement('div');
43
+ private permissions!: ForgeSlidePermissions;
44
+ private footer: FooterView;
45
+ private sideBar: SideBarView;
46
+ private slide!: Slide;
47
+ private currentSlideIndex: number = 0;
48
+ private taskId: string = '';
49
+ private prefix: string = '';
50
+ private slideCount: number = 0;
51
+ private lastDispatchUuid: string = '';
52
+ private syncMessageQueue: { state: any, dispatch: any }[] = [];
53
+ private isSyncing: boolean = false;
54
+
55
+ constructor() {
56
+ super();
57
+ (window as any).emitter = this.emitter;
58
+ this.rootView.setAttribute('data-forge-app', Slide_APP_NAME);
59
+ this.rootView.style.background = '#f9f9fc';
60
+ this.rootView.style.overflow = 'hidden';
61
+
62
+ this.contentContainer.style.width = '100%';
63
+ this.contentContainer.style.height = '100%';
64
+ this.contentContainer.style.display = 'flex';
65
+ this.contentContainer.style.flexDirection = 'column';
66
+
67
+ this.slideContainer.style.width = '100%';
68
+ this.slideContainer.style.height = '100%';
69
+
70
+ this.footer = new FooterView();
71
+ this.sideBar = new SideBarView();
72
+ this.sideBar.on('pageChange', index => {
73
+ if (index > 0 && index <= this.slideCount) {
74
+ this.slide.renderSlide(index);
75
+ }
76
+ });
77
+ this.footer.on('prevStep', () => {
78
+ if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changeStep)) {
79
+ return;
80
+ }
81
+ if (this.slide.mainSeqStep > 0 || this.slide.mainSeqState !== 'idle') {
82
+ this.slide.prevStep();
83
+ } else if (this.currentSlideIndex > 1){
84
+ if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changePage)) {
85
+ return;
138
86
  }
139
- const that = this;
140
- Object.defineProperties(this.emitter, {
141
- view: {
142
- get(): HTMLDivElement { return that.rootView }
143
- },
144
- permissions: {
145
- get(): ForgeSlidePermissions { return that.permissions }
146
- },
147
- footView: {
148
- get(): HTMLDivElement { return that.footer.root }
149
- },
150
- sidebarView: {
151
- get(): HTMLDivElement { return that.sideBar.root }
152
- },
153
- whiteboardView: {
154
- get(): HTMLDivElement { return that.whiteboard.view }
155
- },
156
- slideView: {
157
- get(): HTMLDivElement { return that.slideContainer }
158
- },
159
- pageIndex: {
160
- get(): number { return that.currentSlideIndex }
161
- },
162
- pageCount: {
163
- get(): number { return that.slideCount }
164
- },
165
- goto: {
166
- ...propertyConfig,
167
- value: (pageIndex: number) => {
168
- this.sideBar.emit("pageChange", pageIndex);
169
- }
170
- },
171
- nextStep: {
172
- ...propertyConfig,
173
- value: () => {
174
- this.footer.emit("nextStep");
175
- }
176
- },
177
- prevStep: {
178
- ...propertyConfig,
179
- value: () => {
180
- this.footer.emit("prevStep");
181
- }
182
- },
183
- nextPage: {
184
- ...propertyConfig,
185
- value: () => {
186
- this.footer.emit("nextPage");
187
- }
188
- },
189
- prevPage: {
190
- ...propertyConfig,
191
- value: () => {
192
- this.footer.emit("prevPage");
193
- }
194
- },
195
- sideBarToggle: {
196
- ...propertyConfig,
197
- value: () => {
198
- this.footer.emit("sideBarToggle");
199
- }
200
- },
201
- imgContent: {
202
- ...propertyConfig,
203
- value: (pageIndex: number) => {
204
- return this.getImageContent(pageIndex);
205
- }
206
- },
207
- imgUrl: {
208
- ...propertyConfig,
209
- value: (pageIndex: number) => {
210
- return this.getImageUrl(pageIndex);
211
- }
212
- },
213
- imgSize: {
214
- ...propertyConfig,
215
- value: (pageIndex: number) => {
216
- return this.getImageSize(pageIndex);
217
- }
218
- },
219
- })
220
- }
221
-
222
- private getPreviewImageUrl (pageIndex: number) {
223
- if (pageIndex < 1 || pageIndex > this.slideCount) {
224
- throw new Error("pageIndex out of range");
87
+ this.slide.renderSlide(this.currentSlideIndex - 1);
88
+ }
89
+ });
90
+ this.footer.on('nextStep', () => {
91
+ if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changeStep)) {
92
+ return;
93
+ }
94
+ if (this.slide.mainSeqStep < this.slide.mainSeqLength) {
95
+ this.slide.nextStep();
96
+ } else {
97
+ if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changePage)) {
98
+ return;
225
99
  }
226
- return `${this.prefix}/${this.taskId}/preview/${pageIndex}.png`;
100
+ this.slide.renderSlide(this.currentSlideIndex + 1);
101
+ }
102
+ });
103
+ this.footer.on('prevPage', () => {
104
+ if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changePage)) {
105
+ return;
106
+ }
107
+ if (this.currentSlideIndex > 1) {
108
+ this.slide.renderSlide(this.currentSlideIndex - 1);
109
+ }
110
+ });
111
+ this.footer.on('nextPage', () => {
112
+ if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changePage)) {
113
+ return;
114
+ }
115
+ if (this.currentSlideIndex < this.slideCount) {
116
+ this.slide.renderSlide(this.currentSlideIndex + 1);
117
+ }
118
+ });
119
+ this.footer.on('sideBarToggle', () => {
120
+ if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changePage)) {
121
+ return;
122
+ }
123
+ if (this.sideBar.isShowSideBar) {
124
+ this.sideBar.hidden();
125
+ } else {
126
+ this.sideBar.show();
127
+ }
128
+ });
129
+
130
+ this.rootView.appendChild(this.contentContainer);
131
+
132
+ this.emitter.on('renderStart', pageIndex => {
133
+ this.footer.prevPageState(pageIndex !== 0);
134
+ });
135
+
136
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
137
+ const that = this;
138
+ Object.defineProperty(this.emitter, 'view', {
139
+ get(): any {
140
+ return that.rootView;
141
+ },
142
+ });
143
+ Object.defineProperty(this.emitter, 'permissions', {
144
+ get(): any {
145
+ return that.permissions;
146
+ },
147
+ });
148
+ Object.defineProperty(this.emitter, 'footView', {
149
+ get(): any {
150
+ return that.footer.root;
151
+ },
152
+ });
153
+ Object.defineProperty(this.emitter, 'sidebarView', {
154
+ get(): any {
155
+ return that.sideBar.root;
156
+ },
157
+ });
158
+ Object.defineProperty(this.emitter, 'pageIndex', {
159
+ get(): any {
160
+ return that.currentSlideIndex;
161
+ },
162
+ });
163
+ Object.defineProperty(this.emitter, 'pageCount', {
164
+ get(): any {
165
+ return that.slideCount;
166
+ },
167
+ });
168
+ Object.defineProperty(this.emitter, 'goto', {
169
+ writable: false,
170
+ enumerable: false,
171
+ value: (pageIndex: number) => {
172
+ this.sideBar.emit('pageChange', pageIndex);
173
+ },
174
+ });
175
+ Object.defineProperty(this.emitter, 'nextStep', {
176
+ writable: false,
177
+ enumerable: false,
178
+ value: () => {
179
+ this.footer.emit('nextStep');
180
+ },
181
+ });
182
+ Object.defineProperty(this.emitter, 'prevStep', {
183
+ writable: false,
184
+ enumerable: false,
185
+ value: () => {
186
+ this.footer.emit('prevStep');
187
+ },
188
+ });
189
+ Object.defineProperty(this.emitter, 'nextPage', {
190
+ writable: false,
191
+ enumerable: false,
192
+ value: () => {
193
+ this.footer.emit('nextPage');
194
+ },
195
+ });
196
+ Object.defineProperty(this.emitter, 'prevPage', {
197
+ writable: false,
198
+ enumerable: false,
199
+ value: () => {
200
+ this.footer.emit('prevPage');
201
+ },
202
+ });
203
+ Object.defineProperty(this.emitter, 'sideBarToggle', {
204
+ writable: false,
205
+ enumerable: false,
206
+ value: () => {
207
+ this.footer.emit('sideBarToggle');
208
+ },
209
+ });
210
+ Object.defineProperty(this.emitter, 'imgContent', {
211
+ writable: false,
212
+ enumerable: false,
213
+ value: (pageIndex: number) => {
214
+ return this.getImageContent(pageIndex);
215
+ },
216
+ });
217
+ Object.defineProperty(this.emitter, 'imgUrl', {
218
+ writable: false,
219
+ enumerable: false,
220
+ value: (pageIndex: number) => {
221
+ return this.getImageUrl(pageIndex);
222
+ },
223
+ });
224
+ Object.defineProperty(this.emitter, 'imgSize', {
225
+ writable: false,
226
+ enumerable: false,
227
+ value: (pageIndex: number) => {
228
+ return this.getImageSize(pageIndex);
229
+ },
230
+ });
231
+ this.applySlideState();
232
+ }
233
+
234
+ private getPreviewImageUrl (pageIndex: number) {
235
+ if (pageIndex < 1 || pageIndex > this.slideCount) {
236
+ throw new Error('pageIndex out of range');
227
237
  }
228
-
229
- private async getPreviewImage(imageUrl: string) {
230
- const image = fetch(imageUrl);
231
- return await image.then(res => res.blob()).then(blob => {
232
- return new Promise<PreviewImage>(resolve => {
233
- const reader = new FileReader();
234
- reader.onloadend = () => {
235
- const base64Data = reader.result as string;
236
- const img = document.createElement("img");
237
- img.src = base64Data;
238
- img.onload = () => {
239
- resolve({
240
- url: imageUrl,
241
- src: base64Data,
242
- width: img.width,
243
- height: img.height,
244
- });
245
- }
246
- }
247
- reader.readAsDataURL(blob);
248
- })
249
- });
238
+ return `${this.prefix}/${this.taskId}/preview/${pageIndex}.png`;
239
+ }
240
+
241
+ private async getImageUrl(pageIndex: number) {
242
+ return this.getPreviewImageUrl(pageIndex);
243
+ }
244
+
245
+ private async getImageSize(pageIndex: number) {
246
+ const imageUrl = this.getPreviewImageUrl(pageIndex);
247
+ let preview: PreviewImage | null = null;
248
+ try {
249
+ const result = await kvStore.getItem(imageUrl);
250
+ preview = result ? JSON.parse(result) : null;
251
+ } catch (e) {
252
+ console.warn('kvStore getItem error', e);
250
253
  }
251
-
252
- private async getImageUrl(pageIndex: number) {
253
- return this.getPreviewImageUrl(pageIndex);
254
+ if (preview) {
255
+ return { width: preview.width, height: preview.height };
254
256
  }
255
-
256
- private async getImageSize(pageIndex: number) {
257
- const imageUrl = this.getPreviewImageUrl(pageIndex);
258
- let preview: PreviewImage | null = null;
259
- try {
260
- const result = await kvStore.getItem(imageUrl);
261
- preview = result ? JSON.parse(result) : null;
262
- } catch (e) {
263
- console.warn("kvStore getItem error", e);
264
- }
265
- if (preview) {
266
- return { width: preview.width, height: preview.height };
267
- }
268
- preview = await this.getPreviewImage(imageUrl);
269
- await kvStore.setItem(imageUrl, JSON.stringify(preview));
270
- return { width: preview.width, height: preview.height };
257
+ preview = await this.sideBar.getPreviewImage(imageUrl);
258
+ await kvStore.setItem(imageUrl, JSON.stringify(preview));
259
+ return { width: preview.width, height: preview.height };
260
+ }
261
+
262
+ private async getImageContent(pageIndex: number) {
263
+ const imageUrl = this.getPreviewImageUrl(pageIndex);
264
+ let preview: PreviewImage | null = null;
265
+ try {
266
+ const result = await kvStore.getItem(imageUrl);
267
+ preview = result ? JSON.parse(result) : null;
268
+ } catch (e) {
269
+ console.warn('kvStore getItem error', e);
271
270
  }
272
-
273
- private async getImageContent(pageIndex: number) {
274
- const imageUrl = this.getPreviewImageUrl(pageIndex);
275
- let preview: PreviewImage | null = null;
276
- try {
277
- const result = await kvStore.getItem(imageUrl);
278
- preview = result ? JSON.parse(result) : null;
279
- } catch (e) {
280
- console.warn("kvStore getItem error", e);
281
- }
282
- if (preview) {
283
- return preview.src;
284
- }
285
- preview = await this.getPreviewImage(imageUrl);
286
- await kvStore.setItem(imageUrl, JSON.stringify(preview));
287
- return preview.src;
271
+ if (preview) {
272
+ return preview.src;
288
273
  }
289
-
290
- private applySlideState = async (slideState: any, lastDispatch: any) => {
291
- if (this.slide.slideState.currentSlideIndex < 0) {
292
- this.slide.emit(SLIDE_EVENTS.syncReceive, lastDispatch);
293
- return;
294
- }
295
- if (this.slide.slideState.currentSlideIndex > 0 && deepEqual(this.slide.slideState, slideState)) {
296
- this.slide.emit(SLIDE_EVENTS.syncReceive, lastDispatch);
297
- } else {
298
- await this.slide.setSlideState(slideState);
299
- await delay(200);
300
- this.slide.emit(SLIDE_EVENTS.syncReceive, lastDispatch);
301
- }
274
+ preview = await this.sideBar.getPreviewImage(imageUrl);
275
+ await kvStore.setItem(imageUrl, JSON.stringify(preview));
276
+ return preview.src;
277
+ }
278
+
279
+ private nextTick = () => {
280
+ this.isSyncing = false;
281
+ requestAnimationFrame(() => {
282
+ this.applySlideState().catch((error) => {
283
+ console.error('Error in applySlideState:', error);
284
+ });
285
+ });
286
+ };
287
+
288
+ private applySlideState = async () => {
289
+ if (this.isSyncing) {
290
+ return;
302
291
  }
303
292
 
304
- private onSlideEventHandler = async (event: Y.YMapEvent<any>) => {
305
- for (const [key, value] of event.changes.keys.entries()) {
306
- if (key === "syncSlide") {
307
- if (value.action === "add" || value.action === "update") {
308
- const { slideState: slideStateFromServer, dispatch: dispatchFromServer } = this.getMap(this.name).get("syncSlide");
309
- this.applySlideState(slideStateFromServer, dispatchFromServer);
310
- }
311
- }
312
- }
293
+ const lastSyncMessage = this.syncMessageQueue.pop();
294
+ if (!lastSyncMessage) {
295
+ return this.nextTick();
313
296
  }
314
297
 
315
- private keyBoardEvents = (event: KeyboardEvent) => {
316
- if (event.key === "ArrowLeft") {
317
- this.footer.emit("prevStep");
318
- } else if (event.key === "ArrowRight") {
319
- this.footer.emit("nextStep");
320
- } else if (event.key === "ArrowUp") {
321
- this.footer.emit("prevPage");
322
- } else if (event.key === "ArrowDown") {
323
- this.footer.emit("nextPage");
324
- }
298
+ this.syncMessageQueue = [];
299
+ this.isSyncing = true;
300
+ const { state, dispatch } = lastSyncMessage;
301
+ let ignoreKeys: string[] | undefined = undefined;
302
+ if ( dispatch.type === 'mediaPlay' || dispatch.type === 'mediaPause' || dispatch.type === 'mediaFullscreen') {
303
+ ignoreKeys = [dispatch.id];
325
304
  }
326
-
327
- private bindKeyBoardEvent() {
328
- document.addEventListener("keydown", this.keyBoardEvents);
305
+ if (this.slide.slideState.currentSlideIndex < 0 || state.currentSlideIndex < 0) {
306
+ // @ts-ignore
307
+ await this.slide.receiveSyncHandler(dispatch);
308
+ return this.nextTick();
309
+ } else if (!deepEqual(this.slide.slideState, state, ignoreKeys)) {
310
+ await this.slide.setSlideState(state);
311
+ // @ts-ignore
312
+ await this.slide.receiveSyncHandler(dispatch);
313
+ } else {
314
+ this.slide.emit(SLIDE_EVENTS.syncReceive, dispatch);
329
315
  }
316
+ return this.nextTick();
330
317
 
331
- private unbindKeyBoarEvent() {
332
- document.removeEventListener("keydown", this.keyBoardEvents);
333
- }
318
+ };
334
319
 
335
- public async initialize(option: SlideApplicationOption): Promise<void> {
336
- this.prefix = option.prefix;
337
- this.taskId = option.taskId;
338
- const whiteboardApp = new WhiteboardApplication();
339
- // @ts-ignore
340
- whiteboardApp.roomDoc = this.roomDoc;
341
- // @ts-ignore
342
- whiteboardApp.appId = `${option.taskId}_wb`;
343
- // @ts-ignore
344
- whiteboardApp.userId = this.userId;
345
- // @ts-ignore
346
- whiteboardApp.userManager = this.userManager;
347
- // @ts-ignore
348
- whiteboardApp.applicationManager = this.applicationManager;
349
-
350
- const json = await fetch(`${option.prefix}/${option.taskId}/jsonOutput/slide-1.json`).then(res => res.json())
351
- this.slideCount = json.slideCount;
352
- await whiteboardApp.initialize({
353
- width: json.width,
354
- height: json.height,
355
- });
356
- this.whiteboardApp = whiteboardApp;
357
- this.whiteboard = whiteboardApp.emitter;
358
- this.whiteboard.enableCameraByMouse = false;
359
- this.whiteboard.enableCameraByTouch = false;
360
- this.whiteboard.view.style.width = "100%";
361
- this.whiteboard.view.style.height = "100%";
362
- this.whiteboard.view.style.position = "absolute";
363
- this.whiteboard.view.style.top = "0";
364
- this.whiteboard.view.style.left = "0";
365
- this.whiteboard.view.style.zIndex = "4";
366
- this.whiteboard.view.classList.add("slide-whiteboard");
367
- this.whiteboard.permissions.addPermission(WhiteboardPermissionFlag.all);
368
- this.whiteboard.setCanvasBackgroundColor("#f0f0f000");
369
- this.whiteboardContainer.style.position = "relative";
370
- this.whiteboardContainer.style.flex = "0 0 auto";
371
- this.whiteboardContainer.style.height = "calc(100% - 24px)";
372
- this.whiteboardContainer.classList.add("forge-slide-whiteboard-container");
373
- this.whiteboardContainer.appendChild(this.whiteboard.view);
374
- this.whiteboardContainer.appendChild(this.slideContainer);
375
-
376
- this.contentContainer.appendChild(this.whiteboardContainer);
377
- this.contentContainer.appendChild(this.footer.root);
378
- this.contentContainer.appendChild(this.sideBar.root);
379
-
380
- if (option.inheritWhiteboardId) {
381
- whiteboardApp.linkToWhiteboard(option.inheritWhiteboardId);
320
+ private onSlideEventHandler = async (event: Y.YMapEvent<any>) => {
321
+ for (const [key, value] of event.changes.keys.entries()) {
322
+ if (key === 'syncSlide') {
323
+ if (value.action === 'add' || value.action === 'update') {
324
+ const { slideState: slideStateFromServer, dispatch: dispatchFromServer } = this.getMap(this.name).get('syncSlide');
325
+ if (this.lastDispatchUuid === dispatchFromServer.uuid) {
326
+ return;
327
+ }
328
+ this.lastDispatchUuid = dispatchFromServer.uuid;
329
+ this.syncMessageQueue.push({
330
+ state: slideStateFromServer,
331
+ dispatch: dispatchFromServer,
332
+ });
333
+ this.applySlideState();
382
334
  }
335
+ }
336
+ }
337
+ };
338
+
339
+ private keyBoardEvents = (event: KeyboardEvent) => {
340
+ if (event.key === 'ArrowLeft') {
341
+ this.footer.emit('prevStep');
342
+ } else if (event.key === 'ArrowRight') {
343
+ this.footer.emit('nextStep');
344
+ } else if (event.key === 'ArrowUp') {
345
+ this.footer.emit('prevPage');
346
+ } else if (event.key === 'ArrowDown') {
347
+ this.footer.emit('nextPage');
348
+ }
349
+ };
350
+
351
+ private bindKeyBoardEvent() {
352
+ document.addEventListener('keydown', this.keyBoardEvents);
353
+ }
354
+
355
+ private unbindKeyBoardEvent() {
356
+ document.removeEventListener('keydown', this.keyBoardEvents);
357
+ }
358
+
359
+ private async onFocusInstance() {
360
+ this.bindKeyBoardEvent();
361
+ // await slidePool.active(this.appId, this.slide);
362
+ }
363
+
364
+ private onRefocusInstance() {
365
+ this.unbindKeyBoardEvent();
366
+ }
367
+
368
+
369
+ public async initialize(option: SlideApplicationOption): Promise<void> {
370
+ this.prefix = option.prefix;
371
+ this.taskId = option.taskId;
372
+ const whiteboardApp = new WhiteboardApplication();
373
+ // @ts-ignore
374
+ whiteboardApp.appDoc = this.appDoc;
375
+ // @ts-ignore
376
+ whiteboardApp.appId = `${this.appId}_wb`;
377
+ // @ts-ignore
378
+ whiteboardApp.userId = this.userId;
379
+ // @ts-ignore
380
+ whiteboardApp.userManager = this.userManager;
381
+ // @ts-ignore
382
+ whiteboardApp.deleteSubDoc = this.deleteSubDoc;
383
+
384
+ const json = await fetch(`${option.prefix}/${option.taskId}/jsonOutput/slide-1.json`).then(res => res.json());
385
+ this.slideCount = json.slideCount;
386
+ this.footer.setTotalPageIndex(this.slideCount);
387
+ await whiteboardApp.initialize({
388
+ width: json.width,
389
+ height: json.height,
390
+ maxScaleRatio: 1,
391
+ });
392
+ this.whiteboardApp = whiteboardApp;
393
+ this.whiteboard = whiteboardApp.emitter;
394
+ this.whiteboard.enableCameraBoundaryHighlight = false;
395
+ this.whiteboard.enableCameraByMouse = false;
396
+ this.whiteboard.enableCameraByTouch = false;
397
+ this.whiteboard.view.style.width = '100%';
398
+ this.whiteboard.view.style.height = '100%';
399
+ this.whiteboard.view.style.position = 'absolute';
400
+ this.whiteboard.view.style.top = '0';
401
+ this.whiteboard.view.style.left = '0';
402
+ this.whiteboard.view.style.zIndex = '4';
403
+ this.whiteboard.view.classList.add('slide-whiteboard');
404
+ this.whiteboard.permissions.addPermission(WhiteboardPermissionFlag.all);
405
+ this.whiteboard.setCanvasBackgroundColor('#f0f0f000');
406
+ this.whiteboardContainer.style.position = 'relative';
407
+ this.whiteboardContainer.style.flex = '0 0 auto';
408
+ this.whiteboardContainer.style.height = 'calc(100% - 24px)';
409
+ this.whiteboardContainer.classList.add('forge-slide-whiteboard-container');
410
+ this.whiteboardContainer.appendChild(this.whiteboard.view);
411
+ this.whiteboardContainer.appendChild(this.slideContainer);
412
+
413
+ this.contentContainer.appendChild(this.whiteboardContainer);
414
+ this.contentContainer.appendChild(this.footer.root);
415
+ this.contentContainer.appendChild(this.sideBar.root);
416
+
417
+ if (option.inheritWhiteboardId) {
418
+ whiteboardApp.linkToWhiteboard(option.inheritWhiteboardId);
419
+ }
383
420
 
384
- this.whiteboard.setViewModeToMain();
385
-
386
-
387
- this.slideContainer.setAttribute("builder", "slide-builder")
388
- this.slide = new Slide({
389
- ...option.options,
390
- interactive: true,
391
- anchor: this.slideContainer,
392
- mode: "interactive",
393
- // logger: {
394
- // info: console.log,
395
- // warn: console.warn,
396
- // error: console.error,
397
- // },
398
- })
399
- this.slide.setResource(option.taskId, option.prefix);
400
- this.sideBar.initialize(json.slideCount, option);
401
-
402
- this.slide.on(SLIDE_EVENTS.syncDispatch, event => {
403
- this.getMap(this.name).set("syncSlide", {
404
- slideState: this.slide.slideState,
405
- dispatch: event,
406
- })
407
- })
408
-
409
- this.slide.on(SLIDE_EVENTS.mainSeqStepStart, (animateIndex: number) => {
410
- this.emitter.emit("mainSeqStepStart", animateIndex);
411
- })
412
-
413
- this.slide.on(SLIDE_EVENTS.mainSeqStepEnd, (animateIndex: number) => {
414
- this.emitter.emit("mainSeqStepEnd", animateIndex);
415
- })
416
-
417
- this.slide.on(SLIDE_EVENTS.animateStart, () => {
418
- this.sideBar.hidden();
419
- })
420
-
421
- this.slide.on(SLIDE_EVENTS.renderStart, (slideIndex) => {
422
- this.whiteboardApp.emitter.view.style.opacity = "0";
423
- this.whiteboardApp.emitter.addPage(`${slideIndex}`);
424
- this.whiteboardApp.emitter.gotoPage(`${slideIndex}`);
425
- this.emitter.emit("renderStart", slideIndex);
426
- this.sideBar.hidden();
427
- })
428
-
429
- this.slide.on(SLIDE_EVENTS.renderEnd, (slideIndex) => {
430
- this.currentSlideIndex = slideIndex;
431
- this.whiteboardApp.emitter.view.style.opacity = "1";
432
- this.emitter.emit("renderEnd", slideIndex);
433
- });
434
-
435
- this.slide.on(SLIDE_EVENTS.stateChange, (state) => {
436
- this.getMap(this.name).set("slideState", state)
437
- })
438
-
439
- this.getMap(this.name).observe(this.onSlideEventHandler);
440
-
441
- (window as any).slide = this.slide;
442
- (window as any).slideWhiteboard = this.whiteboardApp;
443
- (window as any).forgeSlide = this;
444
-
445
- const syncSlide = this.getMap(this.name).get("slideState");
446
- if (syncSlide && syncSlide.taskId === option.taskId) {
447
- this.slide.setSlideState(syncSlide)
421
+ this.whiteboard.setViewModeToMain();
422
+
423
+
424
+ this.slideContainer.setAttribute('builder', 'slide-builder');
425
+
426
+ // await slidePool.waitUntilReady(this.appId);
427
+ this.slide = new Slide({
428
+ ...option.options,
429
+ interactive: true,
430
+ anchor: this.slideContainer,
431
+ mode: 'interactive',
432
+ clientId: Math.random().toString(36).substring(2, 15),
433
+ timestamp: () => {
434
+ console.log('timestamp');
435
+ console.log(Date.now, this.calibrationTimestamp, Date.now() - this.calibrationTimestamp);
436
+ return this.calibrationTimestamp;
437
+ },
438
+ // logger: {
439
+ // info: console.log,
440
+ // warn: console.warn,
441
+ // error: console.error,
442
+ // },
443
+ });
444
+ this.slide.setResource(option.taskId, option.prefix);
445
+ this.sideBar.initialize(json.slideCount, option);
446
+
447
+ this.slide.on(SLIDE_EVENTS.syncDispatch, event => {
448
+ this.getMap(this.name).set('syncSlide', {
449
+ slideState: this.slide.slideState,
450
+ dispatch: event,
451
+ });
452
+ });
453
+
454
+ this.slide.on(SLIDE_EVENTS.mainSeqStepStart, (animateIndex: number) => {
455
+ this.emitter.emit('mainSeqStepStart', animateIndex);
456
+ this.footer.changeIconToPause();
457
+ });
458
+
459
+ this.slide.on(SLIDE_EVENTS.mainSeqStepEnd, (animateIndex: number) => {
460
+ this.emitter.emit('mainSeqStepEnd', animateIndex);
461
+ this.footer.changeIconToNextStep();
462
+ });
463
+
464
+ this.slide.on(SLIDE_EVENTS.renderError, ({ error, index }) => {
465
+ if (error.errorType === 'CANVAS_CRASH') {
466
+ this.slide.renderSlide(index);
467
+ }
468
+ });
469
+
470
+ this.slide.on(SLIDE_EVENTS.renderStart, (slideIndex) => {
471
+ if (slideIndex >= 0) {
472
+ this.sideBar.pauseGetPreviewImageSchedule();
473
+ this.whiteboardApp.emitter.view.style.opacity = '0';
474
+ this.whiteboardApp.emitter.addPage(`${slideIndex}`);
475
+ this.whiteboardApp.emitter.gotoPage(`${slideIndex}`);
476
+ this.sideBar.hidden();
477
+ this.footer.changeIconToPause();
478
+ this.emitter.emit('renderStart', slideIndex);
479
+ }
480
+ });
481
+
482
+ this.slide.on(SLIDE_EVENTS.renderEnd, (slideIndex) => {
483
+ if (slideIndex >= 0) {
484
+ this.sideBar.startGetPreviewImageSchedule();
485
+ this.currentSlideIndex = slideIndex;
486
+ this.whiteboardApp.emitter.view.style.opacity = '1';
487
+ this.footer.setCurrentPageIndex(slideIndex);
488
+ // slidePool.active(this.appId, this.slide);
489
+ // slidePool.onRenderEnd(this.appId, this.window?.focused ?? false);
490
+ this.footer.changeIconToNextStep();
491
+ this.emitter.emit('renderEnd', slideIndex);
492
+ }
493
+ });
494
+
495
+ this.slide.on(SLIDE_EVENTS.stateChange, (state) => {
496
+ this.getMap(this.name).set('slideState', state);
497
+ });
498
+
499
+ this.getMap(this.name).observe(this.onSlideEventHandler);
500
+
501
+ (window as any).slide = this.slide;
502
+ (window as any).slideWhiteboard = this.whiteboardApp;
503
+ (window as any).forgeSlide = this;
504
+
505
+ this.whiteboardContainer.addEventListener('click', () => {
506
+ this.sideBar.hidden();
507
+ }, false);
508
+
509
+ const syncSlide = this.getMap(this.name).get('slideState');
510
+ if (syncSlide && syncSlide.taskId === option.taskId) {
511
+ this.slide.setSlideState(syncSlide);
512
+ } else {
513
+ this.slide.renderSlide(1);
514
+ }
515
+ this.permissions = new ForgeSlidePermissions(this.userManager, (userId: string) => {
516
+ return this.userMap(userId);
517
+ });
518
+ this.permissions.on('change', (userId, flags, value) => {
519
+ this.emitter.emit('permissionChange', userId, flags, value);
520
+ if (this.userId === userId) {
521
+ if (flags.includes(ForgeSlidePermissionFlag.clickAnim)) {
522
+ this.slideContainer.style.pointerEvents = 'auto';
448
523
  } else {
449
- this.slide.renderSlide(1);
524
+ this.slideContainer.style.pointerEvents = 'none';
450
525
  }
451
- this.permissions = new ForgeSlidePermissions(this.userManager, (userId: string) => {
452
- return this.userMap(userId);
453
- });
454
- this.permissions.on("change", (userId, flags, value) => {
455
- this.emitter.emit("permissionChange", userId, flags, value);
456
- if (this.userId === userId) {
457
- if (flags.includes(ForgeSlidePermissionFlag.clickAnim)) {
458
- this.slideContainer.style.pointerEvents = "auto";
459
- } else {
460
- this.slideContainer.style.pointerEvents = "none";
461
- }
462
- }
463
- });
464
- this.whiteboardApp.disableViewModel();
465
- if (this.window) {
466
- let prevStatus = "normal";
467
- this.window.on("statusChange", (status) => {
468
- if (prevStatus === "minimized") {
469
- this.bindKeyBoardEvent();
470
- prevStatus = status;
471
- return;
472
- }
473
- prevStatus = status;
474
- if (status === "normal") {
475
- this.bindKeyBoardEvent();
476
- } else if (status === "minimized") {
477
- this.unbindKeyBoarEvent();
478
- }
479
- })
480
- this.window.on("focusedChange", (status) => {
481
- if (status) {
482
- this.bindKeyBoardEvent();
483
- } else {
484
- this.unbindKeyBoarEvent();
485
- }
486
- })
526
+ }
527
+ });
528
+ this.permissions.setPermission(ForgeSlidePermissionFlag.all);
529
+ this.whiteboardApp.disableViewModel();
530
+ if (this.window) {
531
+ let prevStatus = 'normal';
532
+ this.window.on('statusChange', (status) => {
533
+ if (prevStatus === 'minimized') {
534
+ this.onFocusInstance();
535
+ prevStatus = status;
536
+ return;
487
537
  }
488
- // @ts-ignore
489
- window.__forge_slide = this;
490
- // @ts-ignore
491
- window.slidePermissions = this.permissions;
492
- return Promise.resolve(undefined);
493
- }
494
-
495
- private userMap(userId: string): Y.Map<any> {
496
- return this.getMap(`user/${userId}`);
538
+ prevStatus = status;
539
+ if (status === 'normal') {
540
+ this.onFocusInstance();
541
+ } else if (status === 'minimized') {
542
+ this.onRefocusInstance();
543
+ }
544
+ });
545
+ this.window.on('focusedChange', (status) => {
546
+ if (status) {
547
+ this.onFocusInstance();
548
+ } else {
549
+ this.onRefocusInstance();
550
+ }
551
+ });
552
+ this.bindKeyBoardEvent();
497
553
  }
498
-
499
- public async dispose(): Promise<void> {
500
- await this.whiteboardApp.dispose();
501
- this.rootView.parentElement?.removeChild(this.rootView);
502
- this.slide.destroy();
503
- this.sideBar.dispose();
504
- this.getMap(this.name).unobserve(this.onSlideEventHandler)
554
+ // @ts-ignore
555
+ window.__forge_slide = this;
556
+ // @ts-ignore
557
+ window.slidePermissions = this.permissions;
558
+ return Promise.resolve(undefined);
559
+ }
560
+
561
+ private userMap(userId: string): Y.Map<any> {
562
+ return this.getMap(`user/${userId}`);
563
+ }
564
+
565
+ public async dispose(removeSubDoc: boolean): Promise<void> {
566
+ if (removeSubDoc) {
567
+ this.deleteSubDoc(this.appId);
505
568
  }
569
+ await this.whiteboardApp.dispose(removeSubDoc);
570
+ this.rootView.parentElement?.removeChild(this.rootView);
571
+ this.slide.destroy();
572
+ this.sideBar.dispose();
573
+ this.getMap(this.name).unobserve(this.onSlideEventHandler);
574
+ this.permissions.dispose();
575
+ this.footer.dispose();
576
+ // slidePool.remove(this.appId);
577
+ }
506
578
 
507
- }
579
+ }