@netless/forge-slide 0.1.0 → 0.1.1-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.
package/package.json CHANGED
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "name": "@netless/forge-slide",
3
- "version": "0.1.0",
3
+ "version": "0.1.1-alpha.1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "dependencies": {
9
- "@netless/slide": "^1.4.15"
9
+ "@netless/slide": "^1.4.15",
10
+ "uuid": "^11.0.5",
11
+ "@netless/forge-room": "0.1.8",
12
+ "@netless/forge-whiteboard": "0.1.14"
10
13
  },
11
14
  "peerDependencies": {
12
15
  "eventemitter3": "^5.0.1",
13
16
  "yjs": "^13.6.18",
14
- "uuid": "^11.0.5",
15
- "@netless/forge-room": "0.1.8",
16
- "@netless/forge-whiteboard": "0.1.14"
17
+ "@netless/forge-room": "0.1.8"
17
18
  },
18
19
  "keywords": [],
19
20
  "author": "",
package/src/Slide.ts CHANGED
@@ -22,6 +22,18 @@ export interface SlideEvents {
22
22
  * @param {number} pageIndex 页面索引
23
23
  */
24
24
  renderEnd: (pageIndex: number) => void;
25
+
26
+ /**
27
+ * 主序列动画完成时触发
28
+ * @param {number} animateIndex 主动画索引
29
+ */
30
+ mainSeqStepEnd: (animateIndex: number) => void;
31
+
32
+ /**
33
+ * 主序列动画开始时触发
34
+ * @param {number} animateIndex 主动画索引
35
+ */
36
+ mainSeqStepStart: (animateIndex: number) => void;
25
37
  }
26
38
 
27
39
 
@@ -29,11 +41,54 @@ export class SlideForge extends EventEmitter<SlideEvents> implements Application
29
41
 
30
42
  public readonly view!: HTMLDivElement;
31
43
  public readonly permissions!: ForgeSlidePermissions;
44
+ public readonly footView!: HTMLDivElement;
45
+ /**
46
+ * 当前页面索引, 从 0 开始
47
+ */
32
48
  public readonly pageIndex!: number;
33
-
49
+ /**
50
+ * 总页数
51
+ */
52
+ public readonly pageCount!: number;
34
53
  /**
35
54
  * 切换到参数指定页面, index 从 0 开始
36
55
  * @param {number} index 页面索引
37
56
  */
38
- public goto!: (index: number) => Promise<void>
57
+ public goto!: (index: number) => void
58
+ /**
59
+ * 下一步, 如果已经是本页的最后一步, 则切换到下一页
60
+ */
61
+ public nextStep!: () => void
62
+ /**
63
+ * 上一步, 如果已经是本页的第一步, 则切换到上一页
64
+ */
65
+ public prevStep!: () => void
66
+ /**
67
+ * 下一页, 如果是最后一页, 则不执行任何操作
68
+ */
69
+ public nextPage!: () => void
70
+ /**
71
+ * 上一页, 如果是第一页, 则不执行任何操作
72
+ */
73
+ public prevPage!: () => void
74
+ /**
75
+ * 切换侧栏显示状态
76
+ */
77
+ public sideBarToggle!: () => void
78
+
79
+ /**
80
+ * 获取预览图图片内容, base64 编码
81
+ * @param {number} index 页面索引
82
+ */
83
+ public imgContent!: (index: number) => Promise<string>;
84
+ /**
85
+ * 获取预览图图片链接
86
+ * @param {number} index 页面索引
87
+ */
88
+ public imgUrl!: (index: number) => Promise<string>;
89
+ /**
90
+ * 获取预览图图片尺寸
91
+ * @param {number} index 页面索引
92
+ */
93
+ public imgSize!: (index: number) => Promise<{width: number, height: number}>;
39
94
  }
@@ -7,6 +7,7 @@ import {FooterView} from "./FoorerView";
7
7
  import * as Y from "yjs";
8
8
  import {SideBarView} from "./SiderBarView";
9
9
  import {deepEqual, delay} from "./utils";
10
+ import { kvStore } from "@netless/forge-room";
10
11
 
11
12
  export interface SlideApplicationOption {
12
13
  prefix: string;
@@ -18,6 +19,13 @@ export interface SlideApplicationOption {
18
19
  inheritWhiteboardId?: string;
19
20
  }
20
21
 
22
+ interface PreviewImage {
23
+ url: string;
24
+ src: string;
25
+ width: number;
26
+ height: number;
27
+ }
28
+
21
29
  export const Slide_APP_NAME = "forge_slide";
22
30
 
23
31
  export class SlideApplication extends AbstractApplication<SlideApplicationOption, SlideForge> {
@@ -25,6 +33,7 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
25
33
  static applicationName = Slide_APP_NAME;
26
34
 
27
35
  public readonly name: string = Slide_APP_NAME;
36
+ public readonly emitter: SlideForge = new SlideForge();
28
37
 
29
38
  private whiteboardApp!: WhiteboardApplication;
30
39
  private whiteboard!: Whiteboard;
@@ -37,9 +46,12 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
37
46
  private sideBar: SideBarView;
38
47
  private slide!: Slide;
39
48
  private currentSlideIndex: number = 0;
49
+ private taskId: string = "";
50
+ private prefix: string = "";
40
51
 
41
52
  constructor() {
42
53
  super();
54
+ (window as any).emitter = this.emitter;
43
55
  this.rootView.setAttribute("data-forge-app", Slide_APP_NAME);
44
56
  this.rootView.style.background = "#f0f0f0f0";
45
57
  this.rootView.style.overflow = "hidden";
@@ -56,7 +68,9 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
56
68
  this.footer = new FooterView();
57
69
  this.sideBar = new SideBarView();
58
70
  this.sideBar.on("pageChange", index => {
59
- this.slide.renderSlide(index);
71
+ if (index > 0 && index <= this.slide.slideCount) {
72
+ this.slide.renderSlide(index);
73
+ }
60
74
  });
61
75
  this.footer.on("prevStep", () => {
62
76
  if (!this.permissions.hasPermission(ForgeSlidePermissionFlag.changeStep)) {
@@ -129,34 +143,154 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
129
143
  return that.permissions;
130
144
  }
131
145
  });
146
+ Object.defineProperty(this.emitter, "footView", {
147
+ get(): any {
148
+ return that.footer.root;
149
+ }
150
+ })
132
151
  Object.defineProperty(this.emitter, "pageIndex", {
133
152
  get(): any {
134
153
  return that.currentSlideIndex;
135
154
  }
136
155
  });
137
- Object.defineProperty(this.emitter, "goto", {
156
+ Object.defineProperty(this.emitter, "pageCount", {
138
157
  get(): any {
139
- return (index: number) => {
140
- if (index > 0 && index < this.slide.slideCount) {
141
- this.slide.renderSlide(index);
142
- }
143
- };
158
+ return that.slide.slideCount;
159
+ }
160
+ });
161
+ Object.defineProperty(this.emitter, "goto", {
162
+ writable: false,
163
+ enumerable: false,
164
+ value: (pageIndex: number) => {
165
+ this.sideBar.emit("pageChange", pageIndex);
144
166
  }
145
167
  });
168
+ Object.defineProperty(this.emitter, "nextStep", {
169
+ writable: false,
170
+ enumerable: false,
171
+ value: () => {
172
+ this.footer.emit("nextStep");
173
+ }
174
+ })
175
+ Object.defineProperty(this.emitter, "prevStep", {
176
+ writable: false,
177
+ enumerable: false,
178
+ value: () => {
179
+ this.footer.emit("prevStep");
180
+ }
181
+ })
182
+ Object.defineProperty(this.emitter, "nextPage", {
183
+ writable: false,
184
+ enumerable: false,
185
+ value: () => {
186
+ this.footer.emit("nextPage");
187
+ }
188
+ })
189
+ Object.defineProperty(this.emitter, "prevPage", {
190
+ writable: false,
191
+ enumerable: false,
192
+ value: () => {
193
+ this.footer.emit("prevPage");
194
+ }
195
+ })
196
+ Object.defineProperty(this.emitter, "sideBarToggle", {
197
+ writable: false,
198
+ enumerable: false,
199
+ value: () => {
200
+ this.footer.emit("sideBarToggle");
201
+ }
202
+ })
203
+ Object.defineProperty(this.emitter, "imgContent", {
204
+ writable: false,
205
+ enumerable: false,
206
+ value: (pageIndex: number) => {
207
+ return this.getImageContent(pageIndex);
208
+ }
209
+ })
210
+
211
+ Object.defineProperty(this.emitter, "imgUrl", {
212
+ writable: false,
213
+ enumerable: false,
214
+ value: (pageIndex: number) => {
215
+ return this.getImageUrl(pageIndex);
216
+ }
217
+ })
218
+
219
+ Object.defineProperty(this.emitter, "imgSize", {
220
+ writable: false,
221
+ enumerable: false,
222
+ value: (pageIndex: number) => {
223
+ return this.getImageSize(pageIndex);
224
+ }
225
+ })
226
+ }
227
+
228
+ private async getPreviewImage(imageUrl: string) {
229
+ const image = fetch(imageUrl);
230
+ return await image.then(res => res.blob()).then(blob => {
231
+ return new Promise<PreviewImage>(resolve => {
232
+ const reader = new FileReader();
233
+ reader.onloadend = () => {
234
+ const base64Data = reader.result as string;
235
+ const img = document.createElement("img");
236
+ img.src = base64Data;
237
+ img.onload = () => {
238
+ resolve({
239
+ url: imageUrl,
240
+ src: base64Data,
241
+ width: img.width,
242
+ height: img.height,
243
+ });
244
+ }
245
+ }
246
+ reader.readAsDataURL(blob);
247
+ })
248
+ });
249
+ }
250
+
251
+ private async getImageUrl(pageIndex: number) {
252
+ return `${this.prefix}/${this.taskId}/preview/${pageIndex + 1}.png`;
146
253
  }
147
254
 
148
- emitter: SlideForge = new SlideForge();
255
+ private async getImageSize(pageIndex: number) {
256
+ const imageUrl = `${this.prefix}/${this.taskId}/preview/${pageIndex + 1}.png`;
257
+ let preview: PreviewImage | null = null;
258
+ try {
259
+ const result = await kvStore.getItem(imageUrl);
260
+ preview = result ? JSON.parse(result) : null;
261
+ } catch (e) {
262
+ console.warn("kvStore getItem error", e);
263
+ }
264
+ if (preview) {
265
+ return { width: preview.width, height: preview.height };
266
+ }
267
+ preview = await this.getPreviewImage(imageUrl);
268
+ await kvStore.setItem(imageUrl, JSON.stringify(preview));
269
+ return { width: preview.width, height: preview.height };
270
+ }
271
+
272
+ private async getImageContent(pageIndex: number) {
273
+ const imageUrl = `${this.prefix}/${this.taskId}/preview/${pageIndex + 1}.png`;
274
+ let preview: PreviewImage | null = null;
275
+ try {
276
+ const result = await kvStore.getItem(imageUrl);
277
+ preview = result ? JSON.parse(result) : null;
278
+ } catch (e) {
279
+ console.warn("kvStore getItem error", e);
280
+ }
281
+ if (preview) {
282
+ return preview.src;
283
+ }
284
+ preview = await this.getPreviewImage(imageUrl);
285
+ await kvStore.setItem(imageUrl, JSON.stringify(preview));
286
+ return preview.src;
287
+ }
149
288
 
150
289
  private applySlideState = async (slideState: any, lastDispatch: any) => {
151
- // console.log('%cstate', 'color: blue; font-size: 20px;');
152
- // console.log(slideStateFromServer);
153
- // console.log('%cevent', 'color: blue; font-size: 20px;');
154
- // console.log(dispatchFromServer);
155
290
  if (this.slide.slideState.currentSlideIndex < 0) {
156
291
  this.slide.emit(SLIDE_EVENTS.syncReceive, lastDispatch);
157
292
  return;
158
293
  }
159
- console.log('%cisEqual', 'color: blue; font-size: 20px;', deepEqual(this.slide.slideState, slideState));
160
294
  if (this.slide.slideState.currentSlideIndex > 0 && deepEqual(this.slide.slideState, slideState)) {
161
295
  this.slide.emit(SLIDE_EVENTS.syncReceive, lastDispatch);
162
296
  } else {
@@ -178,6 +312,8 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
178
312
  }
179
313
 
180
314
  public async initialize(option: SlideApplicationOption): Promise<void> {
315
+ this.prefix = option.prefix;
316
+ this.taskId = option.taskId;
181
317
  const whiteboardApp = new WhiteboardApplication();
182
318
  // @ts-ignore
183
319
  whiteboardApp.roomDoc = this.roomDoc;
@@ -220,7 +356,6 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
220
356
 
221
357
  if (option.inheritWhiteboardId) {
222
358
  whiteboardApp.linkToWhiteboard(option.inheritWhiteboardId);
223
- console.log("inheritWhiteboardId", option.inheritWhiteboardId);
224
359
  }
225
360
 
226
361
  this.whiteboard.setViewModeToMain();
@@ -248,25 +383,36 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
248
383
  })
249
384
  })
250
385
 
386
+ this.slide.on("mainSeqStepStart", (animateIndex: number) => {
387
+ this.emitter.emit("mainSeqStepStart", animateIndex);
388
+ })
389
+
390
+ this.slide.on("mainSeqStepEnd", (animateIndex: number) => {
391
+ this.emitter.emit("mainSeqStepEnd", animateIndex);
392
+ })
393
+
251
394
  this.slide.on("renderStart", (slideIndex) => {
252
395
  this.whiteboardApp.emitter.view.style.opacity = "0";
253
396
  this.whiteboardApp.emitter.addPage(`${slideIndex}`);
254
397
  this.whiteboardApp.emitter.gotoPage(`${slideIndex}`);
398
+ this.emitter.emit("renderStart", slideIndex);
255
399
  })
256
400
 
257
401
  this.slide.on("renderEnd", (slideIndex) => {
258
402
  this.currentSlideIndex = slideIndex;
259
403
  this.whiteboardApp.emitter.view.style.opacity = "1";
404
+ this.emitter.emit("renderEnd", slideIndex);
260
405
  });
261
406
 
262
- this.slide.on("stateChange", (state) => {
263
- this.getMap(this.name).set("slideState", state)
264
- })
407
+ this.slide.on("stateChange", (state) => {
408
+ this.getMap(this.name).set("slideState", state)
409
+ })
265
410
 
266
411
  this.getMap(this.name).observe(this.onSlideEventHandler);
267
412
 
268
413
  (window as any).slide = this.slide;
269
414
  (window as any).slideWhiteboard = this.whiteboardApp;
415
+ (window as any).forgeSlide = this;
270
416
 
271
417
  const syncSlide = this.getMap(this.name).get("slideState");
272
418
  if (syncSlide && syncSlide.taskId === option.taskId) {
@@ -279,7 +425,7 @@ export class SlideApplication extends AbstractApplication<SlideApplicationOption
279
425
  return this.userMap(userId);
280
426
  });
281
427
  this.permissions.on("change", (userId, flags, value) => {
282
- console.log(userId, flags, value);
428
+ this.emitter.emit("permissionChange", userId, flags, value);
283
429
  if (this.userId === userId) {
284
430
  if (flags.includes(ForgeSlidePermissionFlag.clickAnim)) {
285
431
  this.slideContainer.style.pointerEvents = "auto";