@thewhateverapp/tile-sdk 0.12.1 → 0.12.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.
@@ -59,24 +59,14 @@ export interface CuePoint {
59
59
  data?: unknown;
60
60
  }
61
61
  export interface VideoPlayerProps {
62
- /** HLS playlist URL (m3u8) */
63
- src: string;
64
- /** Auto-start playback (default: true) */
65
- autoplay?: boolean;
66
- /** Loop video (default: false, but true in preview mode) */
67
- loop?: boolean;
68
- /** Start muted (default: true for autoplay compliance) */
69
- muted?: boolean;
70
- /** Poster image URL */
71
- poster?: string;
72
- /** Show native controls (default: false) */
73
- controls?: boolean;
74
62
  /** Children rendered as overlay */
75
63
  children?: ReactNode;
76
64
  /** Additional class names */
77
65
  className?: string;
78
66
  /** Video wrapper class names */
79
67
  videoClassName?: string;
68
+ /** Show native controls (default: false) */
69
+ controls?: boolean;
80
70
  /** Cue points for time-based triggers */
81
71
  cuePoints?: CuePoint[];
82
72
  /** Callback when a cue point is reached */
@@ -89,12 +79,20 @@ export interface VideoPlayerProps {
89
79
  * Provides video state and controls to child overlays via context.
90
80
  *
91
81
  * Features:
82
+ * - Auto-fetches video URL from tile metadata (no src prop needed!)
92
83
  * - HLS streaming with automatic quality adaptation
93
84
  * - Time-based cue points for triggering overlays
94
85
  * - Visibility-aware playback (plays when visible, pauses when hidden)
95
- * - Auto-loops in preview mode for testing
86
+ * - Auto-loops by default (can be disabled via tile metadata)
87
+ *
88
+ * Usage:
89
+ * ```tsx
90
+ * <VideoPlayer className="w-full h-full">
91
+ * <YourOverlay />
92
+ * </VideoPlayer>
93
+ * ```
96
94
  */
97
- export declare function VideoPlayer({ src, autoplay, loop: loopProp, muted, poster, controls, children, className, videoClassName, cuePoints, onCuePoint, onTimeUpdate, }: VideoPlayerProps): React.JSX.Element;
95
+ export declare function VideoPlayer({ controls, children, className, videoClassName, cuePoints, onCuePoint, onTimeUpdate, }: VideoPlayerProps): React.JSX.Element;
98
96
  /**
99
97
  * Hook to access video state and controls from within VideoPlayer children.
100
98
  */
@@ -1 +1 @@
1
- {"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/VideoPlayer.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/VideoPlayer.tsx"],"names":[],"mappings":"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"}
@@ -1,30 +1,97 @@
1
1
  'use client';
2
2
  import React, { createContext, useContext, useEffect, useRef, useState, useCallback, } from 'react';
3
3
  import { getTileBridge } from '../../bridge/TileBridge';
4
- const VideoContext = createContext(null);
5
- // Detect if we're in preview mode (tile-preview sets this global)
6
- function isPreviewMode() {
7
- if (typeof window === 'undefined')
8
- return false;
9
- return !!window.__PREVIEW_SESSION_ID__;
4
+ // Cache for video metadata to avoid repeated fetches
5
+ const videoMetadataCache = new Map();
6
+ /**
7
+ * Get tileId from environment variable (set at build time)
8
+ */
9
+ function getTileId() {
10
+ if (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_TILE_ID) {
11
+ return process.env.NEXT_PUBLIC_TILE_ID;
12
+ }
13
+ return null;
14
+ }
15
+ /**
16
+ * Fetch video metadata for a tile from the platform API
17
+ */
18
+ async function fetchVideoMetadata(tileId) {
19
+ // Check cache first
20
+ if (videoMetadataCache.has(tileId)) {
21
+ return videoMetadataCache.get(tileId);
22
+ }
23
+ try {
24
+ // Use production API URL
25
+ const apiBase = 'https://api.thewhatever.app';
26
+ const response = await fetch(`${apiBase}/platform/tiles/${tileId}/video-metadata`);
27
+ if (!response.ok) {
28
+ console.error(`[VideoPlayer] Failed to fetch video metadata: ${response.status}`);
29
+ return null;
30
+ }
31
+ const metadata = await response.json();
32
+ videoMetadataCache.set(tileId, metadata);
33
+ return metadata;
34
+ }
35
+ catch (error) {
36
+ console.error('[VideoPlayer] Error fetching video metadata:', error);
37
+ return null;
38
+ }
10
39
  }
40
+ const VideoContext = createContext(null);
11
41
  /**
12
42
  * VideoPlayer component with HLS streaming support.
13
43
  * Provides video state and controls to child overlays via context.
14
44
  *
15
45
  * Features:
46
+ * - Auto-fetches video URL from tile metadata (no src prop needed!)
16
47
  * - HLS streaming with automatic quality adaptation
17
48
  * - Time-based cue points for triggering overlays
18
49
  * - Visibility-aware playback (plays when visible, pauses when hidden)
19
- * - Auto-loops in preview mode for testing
50
+ * - Auto-loops by default (can be disabled via tile metadata)
51
+ *
52
+ * Usage:
53
+ * ```tsx
54
+ * <VideoPlayer className="w-full h-full">
55
+ * <YourOverlay />
56
+ * </VideoPlayer>
57
+ * ```
20
58
  */
21
- export function VideoPlayer({ src, autoplay = true, loop: loopProp, muted = true, poster, controls = false, children, className = '', videoClassName = '', cuePoints = [], onCuePoint, onTimeUpdate, }) {
59
+ export function VideoPlayer({ controls = false, children, className = '', videoClassName = '', cuePoints = [], onCuePoint, onTimeUpdate, }) {
22
60
  const videoRef = useRef(null);
23
61
  const hlsRef = useRef(null);
24
62
  const triggeredCuePointsRef = useRef(new Set());
25
63
  const lastTimeRef = useRef(0);
26
- // Auto-enable loop in preview mode unless explicitly set to false
27
- const loop = loopProp ?? isPreviewMode();
64
+ // State for auto-fetched metadata
65
+ const [metadata, setMetadata] = useState(null);
66
+ const [metadataError, setMetadataError] = useState(null);
67
+ // Auto-fetch video metadata from tile
68
+ useEffect(() => {
69
+ const tileId = getTileId();
70
+ if (!tileId) {
71
+ setMetadataError('NEXT_PUBLIC_TILE_ID not set');
72
+ return;
73
+ }
74
+ fetchVideoMetadata(tileId).then((data) => {
75
+ if (data) {
76
+ if (data.videoUrl) {
77
+ setMetadata(data);
78
+ }
79
+ else {
80
+ setMetadataError('No video URL found in tile metadata');
81
+ }
82
+ }
83
+ else {
84
+ setMetadataError('Failed to fetch video metadata');
85
+ }
86
+ });
87
+ }, []);
88
+ // Get values from metadata
89
+ const src = metadata?.videoUrl || null;
90
+ const autoplay = metadata?.autoplay ?? true;
91
+ const muted = metadata?.muted ?? false;
92
+ const poster = metadata?.thumbnail || undefined;
93
+ // Default to loop enabled (videos almost always loop)
94
+ const loop = metadata?.loop ?? true;
28
95
  const [state, setState] = useState({
29
96
  isPlaying: false,
30
97
  currentTime: 0,
@@ -207,8 +274,9 @@ export function VideoPlayer({ src, autoplay = true, loop: loopProp, muted = true
207
274
  video.removeEventListener('loadeddata', handleLoadedData);
208
275
  };
209
276
  }, [onTimeUpdate, onCuePoint, cuePoints]);
210
- // Visibility handling - play/pause based on tile visibility
277
+ // Visibility handling - play/pause AND mute/unmute based on tile visibility
211
278
  // Enables TikTok-style preloaded video tiles that only play when visible
279
+ // Audio is controlled here to ensure only the visible tile has sound
212
280
  useEffect(() => {
213
281
  const video = videoRef.current;
214
282
  if (!video || !autoplay)
@@ -218,17 +286,25 @@ export function VideoPlayer({ src, autoplay = true, loop: loopProp, muted = true
218
286
  // Handle visibility changes from parent (mobile app)
219
287
  const unsubscribe = bridge.onVisibilityChange((visibilityState) => {
220
288
  if (visibilityState.visible) {
221
- // Tile became visible - play the video
222
- video.play().catch(() => { });
289
+ // Tile became visible - unmute and play
290
+ // Unmute first so audio starts with the video
291
+ video.muted = false;
292
+ video.play().catch(() => {
293
+ // If play fails (e.g., autoplay policy), keep muted and retry
294
+ video.muted = true;
295
+ video.play().catch(() => { });
296
+ });
223
297
  }
224
298
  else {
225
- // Tile became hidden - pause the video
299
+ // Tile became hidden - mute first (immediate audio stop), then pause
300
+ video.muted = true;
226
301
  video.pause();
227
302
  }
228
303
  });
229
304
  // Check initial visibility state
230
305
  if (!bridge.isVisible()) {
231
- // If not visible on mount, pause any autoplay
306
+ // If not visible on mount, ensure muted and paused
307
+ video.muted = true;
232
308
  video.pause();
233
309
  }
234
310
  return () => {
@@ -237,7 +313,7 @@ export function VideoPlayer({ src, autoplay = true, loop: loopProp, muted = true
237
313
  }
238
314
  catch {
239
315
  // Bridge not available (e.g., in SSR or outside TileProvider)
240
- // Video will just use autoplay behavior
316
+ // Video will just use autoplay behavior (muted)
241
317
  }
242
318
  }, [autoplay]);
243
319
  const contextValue = {
@@ -265,10 +341,10 @@ export function VideoPlayer({ src, autoplay = true, loop: loopProp, muted = true
265
341
  display: none !important;
266
342
  }
267
343
  `),
268
- state.isLoading && (React.createElement("div", { className: "absolute inset-0 flex items-center justify-center" },
344
+ (state.isLoading || (!src && !metadataError)) && (React.createElement("div", { className: "absolute inset-0 flex items-center justify-center" },
269
345
  React.createElement("div", { className: "w-10 h-10 border-3 border-white/30 border-t-white rounded-full animate-spin" }))),
270
- state.error && (React.createElement("div", { className: "absolute inset-0 flex items-center justify-center text-white/80" },
271
- React.createElement("p", null, state.error))),
346
+ (state.error || metadataError) && (React.createElement("div", { className: "absolute inset-0 flex items-center justify-center text-white/80" },
347
+ React.createElement("p", null, state.error || metadataError))),
272
348
  children)));
273
349
  }
274
350
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thewhateverapp/tile-sdk",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "description": "SDK for building interactive tiles on The Whatever App platform",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",