@ericchen1990/pano-view 3.1.1 → 3.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/dist/index.cjs CHANGED
@@ -2352,6 +2352,25 @@ function cn(...values) {
2352
2352
 
2353
2353
  // src/audio/howl.ts
2354
2354
  var import_howler = __toESM(require_howler());
2355
+
2356
+ // src/media-activation-event.ts
2357
+ function isMediaActivationEvent(event) {
2358
+ switch (event.type) {
2359
+ case "keydown":
2360
+ return event instanceof KeyboardEvent && event.key !== "Escape";
2361
+ case "pointerdown":
2362
+ return event instanceof PointerEvent && event.pointerType === "mouse";
2363
+ case "pointerup":
2364
+ return event instanceof PointerEvent && event.pointerType !== "mouse";
2365
+ default:
2366
+ return false;
2367
+ }
2368
+ }
2369
+ function isAutoplayPolicyError(error) {
2370
+ return typeof error === "object" && error !== null && "name" in error && error.name === "NotAllowedError";
2371
+ }
2372
+
2373
+ // src/audio/howl.ts
2355
2374
  import_howler.Howler.autoUnlock = true;
2356
2375
  function resumePanoAudio() {
2357
2376
  if (!import_howler.Howler.ctx || import_howler.Howler.ctx.state === "running") {
@@ -2359,6 +2378,9 @@ function resumePanoAudio() {
2359
2378
  }
2360
2379
  return import_howler.Howler.ctx.resume();
2361
2380
  }
2381
+ function isPanoAudioUnlocked() {
2382
+ return !import_howler.Howler.ctx || import_howler.Howler.ctx.state === "running";
2383
+ }
2362
2384
  function sourceList(src) {
2363
2385
  if (src == null) {
2364
2386
  return [];
@@ -2424,7 +2446,10 @@ function createUnlockingHowl({
2424
2446
  blockedNotified = true;
2425
2447
  onPlayError?.(error);
2426
2448
  }
2427
- const retry = () => {
2449
+ const retry = (event) => {
2450
+ if (!isMediaActivationEvent(event)) {
2451
+ return;
2452
+ }
2428
2453
  removeUnlockListeners();
2429
2454
  if (!disposed && isActive() && shouldRetryPlay()) {
2430
2455
  void resumePanoAudio();
@@ -2433,8 +2458,10 @@ function createUnlockingHowl({
2433
2458
  };
2434
2459
  removeUnlockListeners();
2435
2460
  unlockListeners.push({ type: "pointerdown", listener: retry });
2461
+ unlockListeners.push({ type: "pointerup", listener: retry });
2436
2462
  unlockListeners.push({ type: "keydown", listener: retry });
2437
2463
  window.addEventListener("pointerdown", retry, { capture: true });
2464
+ window.addEventListener("pointerup", retry, { capture: true });
2438
2465
  window.addEventListener("keydown", retry, { capture: true });
2439
2466
  }
2440
2467
  });
@@ -3873,7 +3900,7 @@ function hasPanoMediaActivationIntent(host) {
3873
3900
  return host.intents.size > 0;
3874
3901
  }
3875
3902
  function needsMediaGesture() {
3876
- if (typeof navigator !== "undefined" && navigator.userActivation?.hasBeenActive) {
3903
+ if (typeof navigator !== "undefined" && navigator.userActivation?.isActive) {
3877
3904
  return false;
3878
3905
  }
3879
3906
  if (import_howler2.Howler.ctx?.state === "running") {
@@ -7165,34 +7192,91 @@ var PanoViewer = (0, import_react26.forwardRef)(
7165
7192
  }),
7166
7193
  [bgmHost, videoHost]
7167
7194
  );
7168
- const activateMedia = (0, import_react26.useCallback)(() => {
7169
- setMediaActivated(true);
7195
+ const runMediaActivation = (0, import_react26.useCallback)(() => {
7170
7196
  const resume = mediaActivationControls.resumeAudio();
7171
7197
  mediaActivationOptions?.onActivate?.(mediaActivationControls);
7172
7198
  return resume;
7173
7199
  }, [mediaActivationControls, mediaActivationOptions]);
7200
+ const activateMedia = (0, import_react26.useCallback)(() => {
7201
+ setMediaActivated(true);
7202
+ return runMediaActivation();
7203
+ }, [runMediaActivation]);
7174
7204
  const hasMediaActivationIntent = mediaActivationRevision >= 0 && hasPanoMediaActivationIntent(mediaActivationHost);
7175
7205
  const shouldActivateMedia = mediaActivationOptions !== null && hasMediaActivationIntent && !mediaActivated;
7176
7206
  (0, import_react26.useEffect)(() => {
7177
7207
  if (!shouldActivateMedia) {
7178
7208
  return;
7179
7209
  }
7180
- if (!needsMediaGesture()) {
7181
- void activateMedia();
7182
- return;
7183
- }
7184
- const handleGesture = () => {
7185
- removeListeners();
7186
- void activateMedia();
7187
- };
7210
+ let cancelled = false;
7211
+ let activating = false;
7188
7212
  const removeListeners = () => {
7189
7213
  window.removeEventListener("pointerdown", handleGesture, true);
7214
+ window.removeEventListener("pointerup", handleGesture, true);
7190
7215
  window.removeEventListener("keydown", handleGesture, true);
7191
7216
  };
7217
+ const completeIfUnlocked = (requestedVideo, videoOk) => {
7218
+ if (cancelled || !isPanoAudioUnlocked()) {
7219
+ return;
7220
+ }
7221
+ if (requestedVideo && videoHost.controller && !videoOk) {
7222
+ return;
7223
+ }
7224
+ removeListeners();
7225
+ setMediaActivated(true);
7226
+ };
7227
+ const tryActivate = () => {
7228
+ if (activating || cancelled) {
7229
+ return;
7230
+ }
7231
+ activating = true;
7232
+ let videoPlay;
7233
+ const controls2 = {
7234
+ resumeAudio: mediaActivationControls.resumeAudio,
7235
+ playVideo: (options) => {
7236
+ const result = mediaActivationControls.playVideo(options);
7237
+ videoPlay = result;
7238
+ return result;
7239
+ },
7240
+ playBackgroundAudio: mediaActivationControls.playBackgroundAudio
7241
+ };
7242
+ try {
7243
+ const resume = controls2.resumeAudio();
7244
+ mediaActivationOptions?.onActivate?.(controls2);
7245
+ const requestedVideo = videoPlay !== void 0;
7246
+ void Promise.all([resume, videoPlay ?? Promise.resolve(true)]).then(
7247
+ ([, videoOk]) => {
7248
+ completeIfUnlocked(requestedVideo, videoOk);
7249
+ },
7250
+ () => {
7251
+ completeIfUnlocked(requestedVideo, false);
7252
+ }
7253
+ );
7254
+ } finally {
7255
+ activating = false;
7256
+ }
7257
+ };
7258
+ const handleGesture = (event) => {
7259
+ if (!isMediaActivationEvent(event)) {
7260
+ return;
7261
+ }
7262
+ tryActivate();
7263
+ };
7192
7264
  window.addEventListener("pointerdown", handleGesture, { capture: true });
7265
+ window.addEventListener("pointerup", handleGesture, { capture: true });
7193
7266
  window.addEventListener("keydown", handleGesture, { capture: true });
7194
- return removeListeners;
7195
- }, [activateMedia, shouldActivateMedia]);
7267
+ if (!needsMediaGesture()) {
7268
+ tryActivate();
7269
+ }
7270
+ return () => {
7271
+ cancelled = true;
7272
+ removeListeners();
7273
+ };
7274
+ }, [
7275
+ mediaActivationControls,
7276
+ mediaActivationOptions,
7277
+ shouldActivateMedia,
7278
+ videoHost
7279
+ ]);
7196
7280
  const fallbackViewRef = (0, import_react26.useRef)(DEFAULT_VIEW);
7197
7281
  const normalizedMinFov = Math.max(1, Math.min(minFov, maxFov - 1));
7198
7282
  const normalizedMaxFov = Math.min(
@@ -10483,8 +10567,10 @@ function PanoVideo({
10483
10567
  try {
10484
10568
  await video.play();
10485
10569
  } catch (error) {
10486
- blockedRef.current = true;
10487
- onPlaybackStateChangeRef.current?.("blocked");
10570
+ if (isAutoplayPolicyError(error)) {
10571
+ blockedRef.current = true;
10572
+ onPlaybackStateChangeRef.current?.("blocked");
10573
+ }
10488
10574
  onErrorRef.current?.({ source: "video", error });
10489
10575
  emitSnapshot();
10490
10576
  }