@netless/app-slide 0.1.0 → 0.1.3

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/app-slide",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "main": "dist/main.cjs.js",
5
5
  "module": "dist/main.es.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,11 +11,11 @@
11
11
  ],
12
12
  "devDependencies": {
13
13
  "@netless/app-shared": "^0.1.1",
14
- "@netless/slide": "^0.2.7",
14
+ "@netless/slide": "^0.2.10",
15
15
  "@types/color-string": "^1.5.2",
16
16
  "color-string": "^1.9.0",
17
17
  "side-effect-manager": "^0.1.5",
18
- "vanilla-lazyload": "^17.5.0"
18
+ "vanilla-lazyload": "^17.6.1"
19
19
  },
20
20
  "scripts": {
21
21
  "types": "cross-env NODE_ENV=production tsc --declaration --emitDeclarationOnly --outDir dist",
@@ -14,7 +14,7 @@ import type { Attributes, MagixEvents, MagixPayload, SlideState } from "../typin
14
14
 
15
15
  import { SideEffectManager } from "side-effect-manager";
16
16
  import { Slide, SLIDE_EVENTS } from "@netless/slide";
17
- import { clamp, deepClone } from "../utils/helpers";
17
+ import { clamp } from "../utils/helpers";
18
18
  import { cachedGetBgColor } from "../utils/bgcolor";
19
19
  import { logger, log, verbose } from "../utils/logger";
20
20
  export { syncSceneWithSlide, createDocsViewerPages } from "./helpers";
@@ -55,6 +55,9 @@ export class SlideController {
55
55
 
56
56
  private syncStateOnceFlag: boolean;
57
57
 
58
+ private visible: boolean;
59
+ private savedIsFrozen: boolean;
60
+
58
61
  public constructor({
59
62
  context,
60
63
  anchor,
@@ -73,6 +76,8 @@ export class SlideController {
73
76
  this.slide = this.createSlide(anchor);
74
77
  // the adder does not need to sync state
75
78
  this.syncStateOnceFlag = !this.context.isAddApp;
79
+ this.visible = document.visibilityState === "visible";
80
+ this.savedIsFrozen = false;
76
81
  this.initialize();
77
82
  }
78
83
 
@@ -100,13 +105,16 @@ export class SlideController {
100
105
 
101
106
  private kickStart() {
102
107
  const { context, slide } = this;
103
- const { taskId, url, state } = { ...EmptyAttributes, ...this.context.getAttributes() };
108
+ if (context.getIsWritable()) {
109
+ context.storage.ensureState(EmptyAttributes);
110
+ }
111
+ const { taskId, url, state } = context.storage.state;
104
112
  slide.setResource(taskId, url || DefaultUrl);
105
113
  if (state) {
106
114
  // if we already have state, try restore from it
107
- log("[Slide] init with state", deepClone(state));
115
+ log("[Slide] init with state", state);
108
116
  this.syncStateOnceFlag = false;
109
- slide.setSlideState(deepClone(state));
117
+ slide.setSlideState(state);
110
118
  } else if (context.isAddApp) {
111
119
  // otherwise, maybe this slide is just added, let the adder kick start first render
112
120
  log("[Slide] init by renderSlide", 1);
@@ -124,19 +132,14 @@ export class SlideController {
124
132
  // it is possible that we miss the first `renderSlide(1)` event
125
133
  // and the attributes has no value yet, so we need to sync state
126
134
  // when there's state change. we only have to do it once
127
- this.sideEffect.add(() => {
128
- const disposer = context.mobxUtils.reaction(
129
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
130
- () => context.getAttributes()!.state,
131
- () => {
135
+ const disposerId = this.sideEffect.addDisposer(
136
+ context.storage.addStateChangedListener(() => {
137
+ if (context.storage.state.state) {
132
138
  this.syncStateOnce();
133
- disposer();
139
+ this.sideEffect.flush(disposerId);
134
140
  }
135
- );
136
- // mobx already has a "disposed" flag in reaction,
137
- // so we don't need to check it here
138
- return disposer;
139
- });
141
+ })
142
+ );
140
143
 
141
144
  this.sideEffect.add(() =>
142
145
  context.addMagixEventListener(SLIDE_EVENTS.syncDispatch, this.magixEventListener, {
@@ -154,6 +157,11 @@ export class SlideController {
154
157
  slide.on(SLIDE_EVENTS.syncDispatch, this.onSyncDispatch);
155
158
 
156
159
  slide.on(SLIDE_EVENTS.renderEnd, this.resolveReady);
160
+
161
+ this.sideEffect.add(() => {
162
+ document.addEventListener("visibilitychange", this.onVisibilityChange);
163
+ return () => document.removeEventListener("visibilitychange", this.onVisibilityChange);
164
+ });
157
165
  }
158
166
 
159
167
  private onSyncDispatch = (event: SyncEvent) => {
@@ -179,10 +187,13 @@ export class SlideController {
179
187
  private syncStateOnce() {
180
188
  // sync state before the first event, so that they can be in the correct order
181
189
  if (this.syncStateOnceFlag) {
182
- const { state } = { ...EmptyAttributes, ...this.context.storage.state };
190
+ if (this.context.getIsWritable()) {
191
+ this.context.storage.ensureState(EmptyAttributes);
192
+ }
193
+ const { state } = this.context.storage.state;
183
194
  if (state) {
184
- log("[Slide] sync with state (once)", deepClone(state));
185
- this.slide.setSlideState(deepClone(state));
195
+ log("[Slide] sync with state (once)", state);
196
+ this.slide.setSlideState(state);
186
197
  this.syncStateOnceFlag = false;
187
198
  }
188
199
  }
@@ -307,6 +318,7 @@ export class SlideController {
307
318
  };
308
319
 
309
320
  public unfreeze = async () => {
321
+ if (!this.visible) return;
310
322
  if (this.ready) {
311
323
  log("[Slide] unfreeze", this.context.appId);
312
324
  if (this.freezePromise) {
@@ -319,4 +331,17 @@ export class SlideController {
319
331
  this._toFreeze = -1;
320
332
  }
321
333
  };
334
+
335
+ private onVisibilityChange = async () => {
336
+ if (!(this.visible = document.visibilityState === "visible")) {
337
+ this.savedIsFrozen = this.freezePromise ? this._toFreeze === 1 : this.isFrozen;
338
+ log("[Slide] freeze because tab becomes invisible");
339
+ this.freeze();
340
+ } else {
341
+ log("[Slide] unfreeze because tab becomes visible", { savedIsFrozen: this.savedIsFrozen });
342
+ if (!this.savedIsFrozen) {
343
+ this.unfreeze();
344
+ }
345
+ }
346
+ };
322
347
  }
@@ -169,6 +169,15 @@ export class SlideDocsViewer {
169
169
  height,
170
170
  animationMode: "immediately" as AnimationMode.Immediately,
171
171
  });
172
+ this.whiteboardView.setCameraBound({
173
+ damping: 1,
174
+ maxContentMode: () => this.whiteboardView.camera.scale,
175
+ minContentMode: () => this.whiteboardView.camera.scale,
176
+ centerX: 0,
177
+ centerY: 0,
178
+ width,
179
+ height,
180
+ });
172
181
  if (!this.isViewMounted) {
173
182
  this.isViewMounted = true;
174
183
  console.log("[Slide] mount whiteboard view");