@thewhateverapp/tile-sdk 0.12.13 → 0.12.15
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.
|
@@ -59,6 +59,7 @@ export declare class TileBridge {
|
|
|
59
59
|
private tokenExpiresAt;
|
|
60
60
|
private keyboardState;
|
|
61
61
|
private visibilityState;
|
|
62
|
+
private receivedVisibilityMessage;
|
|
62
63
|
private mediaOriginalMutedState;
|
|
63
64
|
private trackedAudioContexts;
|
|
64
65
|
private audioContextPatched;
|
|
@@ -122,6 +123,15 @@ export declare class TileBridge {
|
|
|
122
123
|
* Auto-mutes any <video> or <audio> elements added while tile is offscreen
|
|
123
124
|
*/
|
|
124
125
|
private setupMediaObserver;
|
|
126
|
+
/**
|
|
127
|
+
* SELF-DETECTION: Use Page Visibility API as fallback for detecting when
|
|
128
|
+
* the WebView/tab is hidden (app backgrounded, tab switched, screen locked)
|
|
129
|
+
*
|
|
130
|
+
* This works independently of the parent sending visibility messages.
|
|
131
|
+
* NOTE: This does NOT detect when the WebView is scrolled offscreen in a
|
|
132
|
+
* TikTok-style feed - that requires the native app to send visibility messages.
|
|
133
|
+
*/
|
|
134
|
+
private setupPageVisibilityFallback;
|
|
125
135
|
/**
|
|
126
136
|
* MANDATORY: Mute all audio sources when tile is offscreen
|
|
127
137
|
* - Mutes all <video> and <audio> elements
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TileBridge.d.ts","sourceRoot":"","sources":["../../src/bridge/TileBridge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,iGAAiG;IACjG,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAGD,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAEF,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,gBAAgB,CAAmD;IAC3E,OAAO,CAAC,aAAa,CAAoD;IACzE,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAA2B;IAGzC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,cAAc,CAAqB;IAG3C,OAAO,CAAC,aAAa,CAAgD;
|
|
1
|
+
{"version":3,"file":"TileBridge.d.ts","sourceRoot":"","sources":["../../src/bridge/TileBridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,7 +17,11 @@ export class TileBridge {
|
|
|
17
17
|
this.keyboardState = { visible: false, height: 0 };
|
|
18
18
|
// Visibility state from parent (for TikTok-style feeds)
|
|
19
19
|
// Includes muted state for atomic updates to prevent race conditions
|
|
20
|
-
|
|
20
|
+
// FAIL-SAFE: Default to muted=true until parent explicitly says otherwise
|
|
21
|
+
// This prevents audio leakage if visibility message is delayed or never arrives
|
|
22
|
+
this.visibilityState = { visible: true, muted: true };
|
|
23
|
+
// Track whether we've received at least one visibility message from parent
|
|
24
|
+
this.receivedVisibilityMessage = false;
|
|
21
25
|
// Store original muted state of media elements to restore when unmuting
|
|
22
26
|
this.mediaOriginalMutedState = new WeakMap();
|
|
23
27
|
// Track all AudioContext instances for suspension (catches Emscripten/WASM audio)
|
|
@@ -89,6 +93,9 @@ export class TileBridge {
|
|
|
89
93
|
// ROBUST AUDIO MUTING: Auto-intercept all audio sources
|
|
90
94
|
this.patchAudioContext();
|
|
91
95
|
this.setupMediaObserver();
|
|
96
|
+
// SELF-DETECTION: Use Page Visibility API as fallback for detecting when
|
|
97
|
+
// the WebView/tab is hidden (app backgrounded, tab switched, screen locked)
|
|
98
|
+
this.setupPageVisibilityFallback();
|
|
92
99
|
}
|
|
93
100
|
/**
|
|
94
101
|
* Initialize keyboard detection using VisualViewport API and input focus tracking
|
|
@@ -405,6 +412,9 @@ export class TileBridge {
|
|
|
405
412
|
console.warn('[TileBridge] Invalid visibility payload received');
|
|
406
413
|
return;
|
|
407
414
|
}
|
|
415
|
+
// Mark that we've received at least one visibility message from parent
|
|
416
|
+
// This means parent is controlling visibility, so we defer to their mute state
|
|
417
|
+
this.receivedVisibilityMessage = true;
|
|
408
418
|
const wasVisible = this.visibilityState.visible;
|
|
409
419
|
const wasMuted = this.visibilityState.muted;
|
|
410
420
|
// Update visibility and muted state atomically
|
|
@@ -511,6 +521,95 @@ export class TileBridge {
|
|
|
511
521
|
});
|
|
512
522
|
console.log('[TileBridge] 👁️ MutationObserver active for dynamic media elements');
|
|
513
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* SELF-DETECTION: Use Page Visibility API as fallback for detecting when
|
|
526
|
+
* the WebView/tab is hidden (app backgrounded, tab switched, screen locked)
|
|
527
|
+
*
|
|
528
|
+
* This works independently of the parent sending visibility messages.
|
|
529
|
+
* NOTE: This does NOT detect when the WebView is scrolled offscreen in a
|
|
530
|
+
* TikTok-style feed - that requires the native app to send visibility messages.
|
|
531
|
+
*/
|
|
532
|
+
setupPageVisibilityFallback() {
|
|
533
|
+
if (typeof document === 'undefined')
|
|
534
|
+
return;
|
|
535
|
+
// Check initial state - if page is hidden at startup, ensure we're muted
|
|
536
|
+
if (document.hidden) {
|
|
537
|
+
console.log('[TileBridge] 📱 Page hidden at startup - ensuring muted');
|
|
538
|
+
this.visibilityState.muted = true;
|
|
539
|
+
this.muteAllMedia();
|
|
540
|
+
}
|
|
541
|
+
// STANDALONE TILE DETECTION:
|
|
542
|
+
// If NOT in an iframe, this is a standalone tile (direct browser access).
|
|
543
|
+
// Standalone tiles should manage their own audio based on Page Visibility only.
|
|
544
|
+
// We unmute after a short delay if page is visible and we're not in iframe.
|
|
545
|
+
if (!this.isInIframe()) {
|
|
546
|
+
console.log('[TileBridge] 📱 Standalone tile detected (not in iframe)');
|
|
547
|
+
if (!document.hidden) {
|
|
548
|
+
// Page is visible and we're standalone - unmute after brief delay
|
|
549
|
+
// to ensure initialization is complete
|
|
550
|
+
setTimeout(() => {
|
|
551
|
+
if (!this.receivedVisibilityMessage && !document.hidden) {
|
|
552
|
+
console.log('[TileBridge] 📱 Standalone tile visible - unmuting');
|
|
553
|
+
this.visibilityState.muted = false;
|
|
554
|
+
this.unmuteAllMedia();
|
|
555
|
+
}
|
|
556
|
+
}, 100);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
// IN IFRAME: Start muted and wait for parent's visibility message
|
|
561
|
+
// If parent doesn't send within 3 seconds AND page is visible, assume
|
|
562
|
+
// parent doesn't implement visibility protocol (web parent, not mobile)
|
|
563
|
+
setTimeout(() => {
|
|
564
|
+
if (!this.receivedVisibilityMessage && !document.hidden) {
|
|
565
|
+
console.log('[TileBridge] 📱 No visibility message received, page visible - unmuting (web parent?)');
|
|
566
|
+
this.visibilityState.muted = false;
|
|
567
|
+
this.unmuteAllMedia();
|
|
568
|
+
}
|
|
569
|
+
}, 3000);
|
|
570
|
+
}
|
|
571
|
+
// Listen for visibility changes
|
|
572
|
+
document.addEventListener('visibilitychange', () => {
|
|
573
|
+
const isHidden = document.hidden;
|
|
574
|
+
console.log(`[TileBridge] 📱 Page visibility changed: ${isHidden ? 'hidden' : 'visible'}`);
|
|
575
|
+
if (isHidden) {
|
|
576
|
+
// Page is hidden (app backgrounded, tab switched, screen locked)
|
|
577
|
+
// ALWAYS mute - this is a hard requirement
|
|
578
|
+
this.visibilityState.muted = true;
|
|
579
|
+
this.muteAllMedia();
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
// Page is visible again
|
|
583
|
+
// Only unmute if parent has explicitly said we're not muted
|
|
584
|
+
// OR if we've never received a visibility message (standalone tile)
|
|
585
|
+
if (!this.receivedVisibilityMessage || !this.visibilityState.muted) {
|
|
586
|
+
// For standalone tiles (no parent visibility control), restore audio
|
|
587
|
+
console.log('[TileBridge] 📱 Page visible - restoring audio');
|
|
588
|
+
this.visibilityState.muted = false;
|
|
589
|
+
this.unmuteAllMedia();
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
// Parent is controlling visibility - respect their muted state
|
|
593
|
+
console.log('[TileBridge] 📱 Page visible but parent controls mute state');
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
// Also handle window blur/focus as additional fallback
|
|
598
|
+
window.addEventListener('blur', () => {
|
|
599
|
+
console.log('[TileBridge] 📱 Window blur - muting audio');
|
|
600
|
+
this.visibilityState.muted = true;
|
|
601
|
+
this.muteAllMedia();
|
|
602
|
+
});
|
|
603
|
+
window.addEventListener('focus', () => {
|
|
604
|
+
// Only restore if page is visible and parent hasn't explicitly muted
|
|
605
|
+
if (!document.hidden && (!this.receivedVisibilityMessage || !this.visibilityState.muted)) {
|
|
606
|
+
console.log('[TileBridge] 📱 Window focus - restoring audio');
|
|
607
|
+
this.visibilityState.muted = false;
|
|
608
|
+
this.unmuteAllMedia();
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
console.log('[TileBridge] 📱 Page Visibility fallback active');
|
|
612
|
+
}
|
|
514
613
|
/**
|
|
515
614
|
* MANDATORY: Mute all audio sources when tile is offscreen
|
|
516
615
|
* - Mutes all <video> and <audio> elements
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/VideoPlayer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/VideoPlayer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -46,10 +46,12 @@ function getVideoMetadataFromEnv() {
|
|
|
46
46
|
? parseFloat(process.env.NEXT_PUBLIC_VIDEO_DURATION)
|
|
47
47
|
: null,
|
|
48
48
|
title: process.env.NEXT_PUBLIC_VIDEO_TITLE || null,
|
|
49
|
-
// Default to
|
|
50
|
-
|
|
49
|
+
// Default to false for autoplay - wait for visibility message from parent
|
|
50
|
+
// This prevents offscreen tiles from playing in TikTok-style feeds
|
|
51
|
+
autoplay: process.env.NEXT_PUBLIC_VIDEO_AUTOPLAY === 'true',
|
|
51
52
|
// Default to true for loop (most video tiles loop)
|
|
52
53
|
loop: process.env.NEXT_PUBLIC_VIDEO_LOOP !== 'false',
|
|
54
|
+
// Default to true for muted - start muted, unmute on visibility
|
|
53
55
|
muted: process.env.NEXT_PUBLIC_VIDEO_MUTED !== 'false',
|
|
54
56
|
};
|
|
55
57
|
}
|
|
@@ -109,8 +111,10 @@ export function VideoPlayer({ controls = false, children, className = '', videoC
|
|
|
109
111
|
}, []);
|
|
110
112
|
// Get values from metadata
|
|
111
113
|
const src = metadata?.videoUrl || null;
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
+
// Default to false for autoplay - videos wait for visibility message from parent
|
|
115
|
+
// This prevents offscreen tiles from playing in TikTok-style feeds
|
|
116
|
+
const autoplay = metadata?.autoplay ?? false;
|
|
117
|
+
// Default to true for muted - videos start muted until visibility says otherwise
|
|
114
118
|
const muted = metadata?.muted ?? true;
|
|
115
119
|
const poster = metadata?.thumbnail || undefined;
|
|
116
120
|
// Default to loop enabled (videos almost always loop)
|
|
@@ -343,72 +347,64 @@ export function VideoPlayer({ controls = false, children, className = '', videoC
|
|
|
343
347
|
};
|
|
344
348
|
}, [onTimeUpdate, onCuePoint, cuePoints]);
|
|
345
349
|
// Visibility handling - play/pause AND mute/unmute based on tile visibility.
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
// IMPORTANT:
|
|
349
|
-
//
|
|
350
|
+
// Videos start paused and only play when parent sends visible: true.
|
|
351
|
+
// This enables TikTok-style feeds where only the active tile plays.
|
|
352
|
+
// IMPORTANT: Videos default to autoplay=false, muted=true.
|
|
353
|
+
// Parent controls playback via visibility messages.
|
|
350
354
|
useEffect(() => {
|
|
351
355
|
const video = videoRef.current;
|
|
352
|
-
if (!video
|
|
356
|
+
if (!video)
|
|
353
357
|
return;
|
|
354
358
|
try {
|
|
355
359
|
const bridge = getTileBridge();
|
|
356
360
|
// Handle visibility changes from parent (mobile app)
|
|
357
|
-
// Now includes muted state for atomic updates (no race conditions)
|
|
358
361
|
const unsubscribe = bridge.onVisibilityChange((visibilityState) => {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
const shouldMute = visibilityState.muted ?? !visibilityState.visible;
|
|
362
|
-
if (visibilityState.visible && !shouldMute) {
|
|
363
|
-
// Tile became visible and should be unmuted - unmute and play
|
|
364
|
-
// Unmute first so audio starts with the video
|
|
362
|
+
if (visibilityState.visible) {
|
|
363
|
+
// Tile is visible - play video, unmute audio
|
|
365
364
|
video.muted = false;
|
|
366
365
|
video.play().catch(() => {
|
|
367
|
-
// If play fails (
|
|
366
|
+
// If unmuted play fails (browser policy), retry muted
|
|
368
367
|
video.muted = true;
|
|
369
368
|
video.play().catch(() => { });
|
|
370
369
|
});
|
|
371
370
|
}
|
|
372
|
-
else if (visibilityState.visible && shouldMute) {
|
|
373
|
-
// Tile is visible but should be muted (e.g., transitioning)
|
|
374
|
-
video.muted = true;
|
|
375
|
-
video.play().catch(() => { });
|
|
376
|
-
}
|
|
377
371
|
else {
|
|
378
|
-
// Tile
|
|
372
|
+
// Tile is hidden - pause video, mute audio
|
|
379
373
|
video.muted = true;
|
|
380
374
|
video.pause();
|
|
381
375
|
}
|
|
382
376
|
});
|
|
383
|
-
// Check initial visibility state
|
|
377
|
+
// Check initial visibility state
|
|
384
378
|
const initialState = bridge.getVisibilityState();
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
// If visible and unmuted on mount, unmute so sound plays
|
|
388
|
-
// This handles the race condition where the first tile loads visible
|
|
379
|
+
if (initialState.visible) {
|
|
380
|
+
// Visible on mount - start playing
|
|
389
381
|
video.muted = false;
|
|
390
|
-
// If unmuted autoplay fails, fall back to muted
|
|
391
382
|
video.play().catch(() => {
|
|
392
383
|
video.muted = true;
|
|
393
384
|
video.play().catch(() => { });
|
|
394
385
|
});
|
|
395
386
|
}
|
|
396
387
|
else {
|
|
397
|
-
//
|
|
388
|
+
// Not visible on mount - ensure paused and muted
|
|
398
389
|
video.muted = true;
|
|
399
|
-
|
|
400
|
-
video.pause();
|
|
401
|
-
}
|
|
390
|
+
video.pause();
|
|
402
391
|
}
|
|
403
392
|
return () => {
|
|
404
393
|
unsubscribe();
|
|
405
394
|
};
|
|
406
395
|
}
|
|
407
396
|
catch {
|
|
408
|
-
// Bridge not available (
|
|
409
|
-
//
|
|
397
|
+
// Bridge not available (standalone tile, not in feed)
|
|
398
|
+
// For standalone tiles, play with audio if visible
|
|
399
|
+
if (!document.hidden) {
|
|
400
|
+
video.muted = false;
|
|
401
|
+
video.play().catch(() => {
|
|
402
|
+
video.muted = true;
|
|
403
|
+
video.play().catch(() => { });
|
|
404
|
+
});
|
|
405
|
+
}
|
|
410
406
|
}
|
|
411
|
-
}, [
|
|
407
|
+
}, []);
|
|
412
408
|
const contextValue = {
|
|
413
409
|
state,
|
|
414
410
|
controls: { play, pause, toggle, seek, setVolume, setMuted },
|