/** * Blue Billywig Channel SDK Types */ /** * Fullscreen player mode options */ export type FullscreenMode = 'fullscreen' | 'landscape'; /** * Safe area edge options for automatic padding * Controls which edges receive safe area inset padding. * * @example * // Apply safe area padding to top and bottom * * * // Apply to all edges (default when safeAreaEdges is true) * * * // Disable safe area handling (use with caution) * */ export type SafeAreaEdge = 'top' | 'bottom' | 'left' | 'right'; /** * Safe area insets values * Used when manually providing inset values instead of using react-native-safe-area-context */ export interface SafeAreaInsets { top?: number; bottom?: number; left?: number; right?: number; } /** * Player event names * These mirror the native SDK event names for consistency across platforms. */ export type BBPlayerEventName = 'play' | 'pause' | 'ended' | 'canplay' | 'waiting' | 'seeking' | 'seeked' | 'timeupdate' | 'durationchange' | 'progress' | 'error' | 'volumechange' | 'fullscreenchange' | 'loadstart' | 'loadedmetadata' | 'loadeddata' | 'cliploaded' | 'clipfailed' | 'cliplistloaded' | 'adstart' | 'adend' | 'adskip' | 'aderror'; /** * Base player event data */ export interface BBPlayerEventBase { /** Event timestamp */ timestamp: number; /** Current clip ID if available */ clipId?: string; /** Current clip title if available */ title?: string; } /** * Time update event data */ export interface BBTimeUpdateEvent extends BBPlayerEventBase { /** Current playback time in seconds */ currentTime: number; /** Total duration in seconds */ duration: number; /** Playback progress as percentage (0-100) */ progress: number; } /** * Volume change event data */ export interface BBVolumeChangeEvent extends BBPlayerEventBase { /** Current volume (0-1) */ volume: number; /** Whether player is muted */ muted: boolean; } /** * Seek event data */ export interface BBSeekEvent extends BBPlayerEventBase { /** Position seeked to in seconds */ position: number; /** Previous position in seconds */ previousPosition?: number; } /** * Fullscreen change event data */ export interface BBFullscreenChangeEvent extends BBPlayerEventBase { /** Whether player is in fullscreen */ isFullscreen: boolean; } /** * Clip loaded event data */ export interface BBClipLoadedEvent extends BBPlayerEventBase { /** Clip ID */ clipId: string; /** Clip title */ title: string; /** Clip duration in seconds */ duration: number; /** Thumbnail URL */ thumbnailUrl?: string; /** Whether it's a live stream */ isLive?: boolean; } /** * Ad event data */ export interface BBAdEvent extends BBPlayerEventBase { /** Ad position (pre, mid, post) */ position?: 'pre' | 'mid' | 'post'; /** Ad duration in seconds */ duration?: number; /** Remaining time in seconds */ remainingTime?: number; /** Whether ad is skippable */ skippable?: boolean; /** Seconds until skip is available */ skipOffset?: number; } /** * Player error event data */ export interface BBPlayerErrorEvent extends BBPlayerEventBase { /** Error code */ code: string; /** Error message */ message: string; /** Additional error details */ details?: unknown; } /** * Union type for all player events */ export type BBPlayerEvent = { type: 'play'; data: BBPlayerEventBase; } | { type: 'pause'; data: BBPlayerEventBase; } | { type: 'ended'; data: BBPlayerEventBase; } | { type: 'canplay'; data: BBPlayerEventBase; } | { type: 'waiting'; data: BBPlayerEventBase; } | { type: 'seeking'; data: BBSeekEvent; } | { type: 'seeked'; data: BBSeekEvent; } | { type: 'timeupdate'; data: BBTimeUpdateEvent; } | { type: 'durationchange'; data: { duration: number; } & BBPlayerEventBase; } | { type: 'progress'; data: { buffered: number; } & BBPlayerEventBase; } | { type: 'volumechange'; data: BBVolumeChangeEvent; } | { type: 'fullscreenchange'; data: BBFullscreenChangeEvent; } | { type: 'loadstart'; data: BBPlayerEventBase; } | { type: 'loadedmetadata'; data: BBPlayerEventBase; } | { type: 'loadeddata'; data: BBPlayerEventBase; } | { type: 'cliploaded'; data: BBClipLoadedEvent; } | { type: 'clipfailed'; data: BBPlayerErrorEvent; } | { type: 'cliplistloaded'; data: { cliplistId: string; clipCount: number; } & BBPlayerEventBase; } | { type: 'adstart'; data: BBAdEvent; } | { type: 'adend'; data: BBAdEvent; } | { type: 'adskip'; data: BBAdEvent; } | { type: 'aderror'; data: BBAdEvent & { error: string; }; } | { type: 'error'; data: BBPlayerErrorEvent; }; /** * Configuration options for the BBChannel component */ export interface BBChannelOptions { /** * Enable/disable autoplay for videos * @default true */ autoPlay?: boolean; /** * Show/hide the search bar * @default true */ searchBar?: boolean; /** * Disable analytics/statistics * @default false */ noStats?: boolean; /** * JWT token for authenticated content */ jwt?: string; /** * RPC token for authenticated content */ rpcToken?: string; /** * Custom playout configuration name */ playout?: string; /** * Initial content to play (clip ID) */ contentId?: string; /** * Initial content type */ contentType?: 'mediaclip' | 'mediacliplist' | 'project'; /** * Custom bundle URL for channel-native.js * Must be a *.bluebillywig.com URL for security. * @default "https://cdn.bluebillywig.com/apps/channel/latest/channel-native.js" */ bundleUrl?: string; /** * Fullscreen player behavior when a video is played * - 'fullscreen': Standard fullscreen (maintains current orientation) * - 'landscape': Auto-rotate to landscape for landscape videos * @default 'landscape' */ fullscreenMode?: FullscreenMode; /** * Enable native player mode (used internally by BBChannelWithPlayer) * When true, uses NativePlayerStrategy to send MEDIA_PLAY messages * instead of navigating to the web detail page * @internal */ nativePlayerMode?: boolean; } /** * Media clip entity information * * In native mode, when a user clicks a thumbnail, the SDK sends this info * so the app can launch a native fullscreen player. */ export interface BBMediaInfo { /** * JSON URL for loading the media in a native player * Format: https://[publication].bbvms.com/p/[playout]/[c|l]/[id].json */ jsonUrl: string; /** * Playout configuration ID used for this media */ playoutId: string; /** * Whether the media should autoplay when loaded */ autoplay: boolean; /** * Thumbnail URL for the media (used for placeholder display) */ thumbnailUrl?: string; /** * Tiny thumbnail URL for blurred background (natural blur from low resolution) */ blurredThumbnailUrl?: string; /** * Content entity ID (numeric string) */ entityId?: string; /** * Content entity type (e.g. 'MediaClip', 'MediaClipList', 'Project') */ entityType?: string; /** * Media title */ title?: string; /** * Media type - video or audio */ mediaType?: 'video' | 'audio'; /** * Type of the entity being played (e.g., 'MediaClip') */ contextEntityType?: string; /** * ID of the entity being played */ contextEntityId?: string; /** * Type of the collection containing the entity (e.g., 'MediaClipList') */ contextCollectionType?: string; /** * ID of the collection (playlist) containing the entity */ contextCollectionId?: string; /** * Target player block ID (e.g. 'player', 'detailPage', 'overviewPage') * Used to determine how/whether to render a native player * @internal */ playerBlockId?: string; /** * Whether this media is from an inline player block (not the main player) * When true, BBChannelWithPlayer opens a modal player instead of the top inline player * @internal */ isInlinePlayer?: boolean; /** * Block ID of the inline player that triggered this play request * @internal */ blockId?: string; /** * Index of the item in the playlist when playing from a cliplist inline player * @internal */ listIndex?: number; /** * Background color for native container (from channel CSS vars) */ backgroundColor?: string; /** * Text color for native container (contrast-aware, from channel CSS vars) */ textColor?: string; /** * Whether to show blurred thumbnail as background (from channel detail page config) */ showThumbnailAsBackground?: boolean; } /** * Page type for navigation */ export type BBPageType = 'main' | 'detailPage' | 'overviewPage' | 'searchPage'; /** * Full navigation state that can be passed between native app and WebView. * Use for deep linking, state persistence, and navigation control. */ export interface BBNavigationState { /** Current page type */ pageType: BBPageType; /** Entity ID when viewing detail/overview page */ contentId?: string; /** Entity type: 'mediaclip' or 'mediacliplist' */ contentType?: 'mediaclip' | 'mediacliplist'; /** Search query when on search page */ searchQuery?: string; /** Whether navigated from clicking a search result */ clickedSearchResult?: boolean; /** Block ID that initiated the navigation */ blockId?: string; /** Context identifier for player context */ contextId?: string; /** Playout name for the detail page player */ playout?: string; /** Collection ID when viewing playlist collection item */ collectionId?: string; /** Sub-channel ID in multi-channel mode */ subChannelId?: string; } /** * Navigation event data (enhanced with full state) */ export interface BBNavigationEvent { /** Current page type */ pageType: BBPageType; /** Entity info (for detail pages) */ entity?: BBMediaInfo; /** Full navigation state for state persistence */ state: BBNavigationState; /** Whether back navigation is possible */ canGoBack: boolean; /** Navigation stack depth (for debugging/UI) */ stackDepth: number; } /** * Player state information */ export interface BBPlayerState { isPlaying: boolean; currentTime?: number; duration?: number; volume?: number; isMuted?: boolean; isFullscreen?: boolean; /** Player ID (when returned from getPlayerState with specific player) */ playerId?: string; } /** * Registered player information */ export interface BBRegisteredPlayer { /** Unique player ID (e.g., 'detailPage', 'player', or block ID) */ id: string; /** Whether this player is currently active */ isActive: boolean; /** Current player state (null if player not ready) */ state: BBPlayerState | null; } /** * Search event data */ export interface BBSearchEvent { query: string; resultCount: number; } /** * Error event data */ export interface BBErrorEvent { code: string; message: string; details?: unknown; } /** * Analytics event data * * Emitted when the embedded player triggers a statistics event. * Common event codes: * - 'st': Start - playback started * - 'pg': Progress - playback progress (quartile: 0%, 25%, 50%, 75%, 100%) * - 'fn': Finish - playback completed * - 'pa': Pause - playback paused * - 'rs': Resume - playback resumed * - 'se': Seek - seek completed * - 'it': Init - clip initialized * - 'xst': Extension start - entity tracking (Session, View, Project, etc.) * - 'xit': Extension init - entity initialized * - 'xcl': External click - user interaction * * @example * ```tsx * onAnalyticsEvent={(event) => { * // Forward to your analytics system * myAnalytics.track(event.eventCode, event.properties); * * // Or handle specific events * if (event.eventCode === 'pg') { * console.log(`Progress: ${event.properties.pct}%`); * } * }} * ``` */ export interface BBAnalyticsEvent { /** Player session ID */ sessionId: string; /** * Event code indicating the type of event * Common codes: 'st', 'pg', 'fn', 'pa', 'rs', 'se', 'it', 'xst', 'xit', 'xcl' */ eventCode: string; /** * Full event properties - structure varies by event type * Common properties: ev (event code), id (entity id), ts (timestamp), * pct (percentage), du (duration), ct (clip title) */ properties: Record; } /** * Props for the BBChannel component */ export interface BBChannelProps { /** * URL to the channel JSON configuration * @required * @example "https://demo.bbvms.com/ch/123.json" */ channelUrl: string; /** * Optional configuration options */ options?: BBChannelOptions; /** * Custom styles for the container */ style?: object; /** * Which edges should receive safe area inset padding. * Prevents content from being hidden under the status bar, notch, or navigation bar. * * - `['top']` - Only top edge (recommended for fullscreen channels) * - `['top', 'bottom']` - Top and bottom edges * - `['top', 'bottom', 'left', 'right']` - All edges * - `[]` - Disable safe area handling (use with caution) * * Requires `react-native-safe-area-context` and wrapping your app in ``. * * @default ['top'] - Applies top padding by default for Android status bar / iOS notch * @example * // Default behavior - top padding only * * * // All edges (for edge-to-edge layouts) * * * // Disable safe area handling (parent handles it) * */ safeAreaEdges?: SafeAreaEdge[]; /** * Manual safe area inset values (in pixels). * Use this when you want to provide your own inset values instead of * using react-native-safe-area-context. * * When provided, these values override the automatic insets from SafeAreaContext. * Only the specified edges (in `safeAreaEdges`) will be applied. * * @example * // Manual insets (e.g., from StatusBar.currentHeight on Android) * */ safeAreaInsets?: SafeAreaInsets; /** * Called when the channel is fully loaded and ready */ onReady?: () => void; /** * Called when an error occurs */ onError?: (error: BBErrorEvent) => void; /** * Called when navigation occurs within the channel */ onNavigate?: (event: BBNavigationEvent) => void; /** * Called when navigation state changes. * Use to persist navigation state for app state restoration. */ onNavigationStateChange?: (state: BBNavigationState) => void; /** * Called when back navigation availability changes. * Use to update native back button UI or handle Android hardware back. */ onCanGoBackChange?: (canGoBack: boolean) => void; /** * Initial navigation state to restore on mount. * Use for deep linking or restoring app state after backgrounding. * @example { pageType: 'detailPage', contentId: '12345', contentType: 'mediaclip' } */ initialNavigationState?: BBNavigationState; /** * Automatically handle Android hardware back button. * When enabled, the SDK registers a BackHandler that: * - Calls goBack() when canGoBack is true * - Returns false (lets parent handle) when at root * * Uses refs internally to prevent race conditions with rapid back presses. * * @default false * @example * // Let SDK handle back button automatically * * * // Or handle manually with onCanGoBackChange * { * // Custom back handling logic * }} * /> */ handleBackButton?: boolean; /** * Called when a search is performed */ onSearch?: (event: BBSearchEvent) => void; /** * Called when media starts playing */ onMediaPlay?: (media: BBMediaInfo) => void; /** * Called when media is paused */ onMediaPause?: (media: BBMediaInfo) => void; /** * Called when media ends */ onMediaEnd?: (media: BBMediaInfo) => void; /** * Called when player state changes (play/pause/seeking/etc.) */ onPlayerStateChange?: (state: BBPlayerState) => void; /** * Called when the first player media is identified. * In native mode, the first Player block is skipped in the WebView. * This callback provides the media info so the app can render a native player. */ onFirstPlayerMedia?: (media: BBMediaInfo) => void; /** * Called on every player event (play, pause, timeupdate, etc.) * Use for fine-grained player event handling. * @example * ```tsx * onPlayerEvent={(event) => { * if (event.type === 'timeupdate') { * console.log(`Progress: ${event.data.progress}%`); * } * }} * ``` */ onPlayerEvent?: (event: BBPlayerEvent) => void; /** * Called when playback time updates (fires frequently during playback) * Use for progress bars, time displays, etc. */ onTimeUpdate?: (data: BBTimeUpdateEvent) => void; /** * Called when a clip is loaded */ onClipLoaded?: (data: BBClipLoadedEvent) => void; /** * Called when an ad starts playing */ onAdStart?: (data: BBAdEvent) => void; /** * Called when an ad finishes */ onAdEnd?: (data: BBAdEvent) => void; /** * Called when the list of registered players changes. * Fires when players are added or removed from the channel. * @example * ```tsx * onPlayersChange={(players) => { * console.log(`${players.length} players registered`); * const activePlayer = players.find(p => p.isActive); * console.log(`Active player: ${activePlayer?.id}`); * }} * ``` */ onPlayersChange?: (players: BBRegisteredPlayer[]) => void; /** * Called when the active player changes. * @example * ```tsx * onActivePlayerChange={(playerId) => { * console.log(`Active player is now: ${playerId}`); * }} * ``` */ onActivePlayerChange?: (playerId: string | null) => void; /** * Called when an analytics/statistics event is triggered by the player. * Use this to forward player analytics to your own analytics system * or to track custom metrics. * * @example * ```tsx * onAnalyticsEvent={(event) => { * // Forward to your analytics system * if (event.eventCode === 'st') { * analytics.track('video_started', { clipId: event.properties.id }); * } else if (event.eventCode === 'pg') { * analytics.track('video_progress', { percent: event.properties.pct }); * } else if (event.eventCode === 'fn') { * analytics.track('video_completed', { clipId: event.properties.id }); * } * }} * ``` */ onAnalyticsEvent?: (event: BBAnalyticsEvent) => void; /** * Called when an external link is clicked within the channel. * External links are URLs that don't belong to Blue Billywig domains. * Use this to handle navigation to external websites (e.g., webshop links). * * If not provided, external links are silently blocked. * * @example * ```tsx * import { Linking } from 'react-native'; * * { * // Open in system browser * Linking.openURL(url); * }} * /> * ``` */ onExternalLink?: (url: string) => void; } /** * Options for navigateToEntity method */ export interface NavigateToEntityOptions { /** Playout configuration name */ playout?: string; /** Source block ID */ blockId?: string; /** Whether to autoplay the video */ autoPlay?: boolean; } /** * Methods available on the BBChannel ref */ export interface BBChannelRef { /** * Update the JWT token for authenticated content. * Use this when the token needs to be refreshed or when switching users. * @param jwt - The new JWT token, or null to clear */ setJwt: (jwt: string | null) => void; /** * Update the RPC token for authenticated content. * @param rpcToken - The new RPC token, or null to clear */ setRpcToken: (rpcToken: string | null) => void; /** * Navigate to and play a specific video (same as clicking a thumbnail). * * This navigates to the video's detail page AND starts playback with autoPlay. * Shorthand for `navigateToEntity(videoId, 'mediaclip')`. * * @param videoId - The mediaclip ID * @param type - Content type (default: 'mediaclip') */ playVideo: (videoId: string, type?: 'mediaclip' | 'mediacliplist') => void; /** * Navigate to a specific page */ navigateTo: (page: 'main' | 'search') => void; /** * Navigate back to previous page */ goBack: () => void; /** * Perform a search */ search: (query: string) => void; /** * Reload the channel */ reload: () => void; /** * Navigate to a specific state (for deep linking / state restoration) * @param state - The navigation state to restore */ setNavigationState: (state: BBNavigationState) => void; /** * Get the current navigation state * @returns Promise resolving to current navigation state */ getNavigationState: () => Promise; /** * Check if back navigation is possible * @returns Promise resolving to true if there's navigation history */ canGoBack: () => Promise; /** * Navigate to and play an entity (same as clicking a thumbnail). * * This method navigates to the entity's detail/overview page AND starts playback. * It's equivalent to the user clicking on a video thumbnail in the channel. * * For mediaclip: navigates to detail page and plays the video * For mediacliplist: navigates to overview page and plays the first item * * @param entityId - The mediaclip or mediacliplist ID * @param entityType - 'mediaclip' or 'mediacliplist' * @param options - Optional: playout, blockId, autoPlay (default: true) */ navigateToEntity: (entityId: string, entityType: 'mediaclip' | 'mediacliplist', options?: NavigateToEntityOptions) => void; /** * Update the detail page entity without triggering player reload. * Used for auto-advance: player already has the content, just update metadata. * @param entityId - The mediaclip ID */ updateDetailPageEntity: (entityId: string) => void; /** * Navigate to a sub-channel (multi-channel mode) * @param subChannelId - The sub-channel ID */ navigateToSubChannel: (subChannelId: string) => void; /** * Clear navigation history and go to main page */ resetNavigation: () => void; /** * Start or resume playback * @param playerId - Optional player ID to target. If omitted, targets active player. */ play: (playerId?: string) => void; /** * Pause playback * @param playerId - Optional player ID to target. If omitted, targets active player. */ pause: (playerId?: string) => void; /** * Seek to a specific time in the video * @param seconds - Position to seek to in seconds * @param playerId - Optional player ID to target. If omitted, targets active player. */ seek: (seconds: number, playerId?: string) => void; /** * Set the volume level * @param volume - Volume level from 0.0 (muted) to 1.0 (max) * @param playerId - Optional player ID to target. If omitted, targets active player. */ setVolume: (volume: number, playerId?: string) => void; /** * Mute or unmute the player * @param muted - Whether to mute * @param playerId - Optional player ID to target. If omitted, targets active player. */ setMuted: (muted: boolean, playerId?: string) => void; /** * Enter fullscreen mode * @param playerId - Optional player ID to target. If omitted, targets active player. */ enterFullscreen: (playerId?: string) => void; /** * Exit fullscreen mode * @param playerId - Optional player ID to target. If omitted, targets active player. */ exitFullscreen: (playerId?: string) => void; /** * Get player state for a specific player or the active player * @param playerId - Optional player ID to query. If omitted, targets active player. * @returns Promise resolving to player state */ getPlayerState: (playerId?: string) => Promise; /** * Get all registered players in the channel * @returns Promise resolving to array of registered players with their states * @example * ```tsx * const players = await channelRef.current?.getPlayers(); * players?.forEach(p => console.log(`${p.id}: ${p.isActive ? 'active' : 'inactive'}`)); * ``` */ getPlayers: () => Promise; /** * Set the active player * Subsequent player control calls without a playerId will target this player. * @param playerId - The player ID to make active * @example * ```tsx * // Make the detail page player active * channelRef.current?.setActivePlayer('detailPage'); * // Now play() will control the detail page player * channelRef.current?.play(); * ``` */ setActivePlayer: (playerId: string) => void; } /** * Message types sent from WebView to React Native */ export type WebViewMessageType = 'READY' | 'ERROR' | 'RENDER_ERROR' | 'LOAD_ERROR' | 'NAVIGATION' | 'NAVIGATION_STATE_CHANGE' | 'CAN_GO_BACK_CHANGE' | 'NAVIGATION_STATE_RESPONSE' | 'SEARCH' | 'MEDIA_PLAY' | 'MEDIA_PAUSE' | 'MEDIA_END' | 'PLAYER_STATE' | 'FIRST_PLAYER_MEDIA' | 'INLINE_PLAYER_PLAY' | 'PLAYER_EVENT' | 'TIME_UPDATE' | 'CLIP_LOADED' | 'AD_START' | 'AD_END' | 'PLAYERS_CHANGE' | 'ACTIVE_PLAYER_CHANGE' | 'PLAYERS_RESPONSE' | 'ANALYTICS' | 'PLAYER_STATE_RESPONSE' | 'EXTERNAL_LINK' | 'DEBUG'; /** * Message structure from WebView */ export interface WebViewMessage { type: WebViewMessageType; payload: unknown; /** Request ID for async responses (e.g., getNavigationState) */ requestId?: string; } /** * Payload for NAVIGATION_STATE_CHANGE message */ export interface NavigationStateChangePayload { state: BBNavigationState; canGoBack: boolean; stackDepth: number; } /** * Payload for CAN_GO_BACK_CHANGE message */ export interface CanGoBackChangePayload { canGoBack: boolean; } /** * Payload for NAVIGATION_STATE_RESPONSE message */ export interface NavigationStateResponsePayload { state: BBNavigationState; canGoBack: boolean; requestId: string; } export type { PlayerType, PlayerConfig, PlayerEvents, PlayerControls, PlayerComponentProps, PlayerComponentRef, PlayerProvider, } from './player'; export { DEFAULT_PLAYER_PROVIDER } from './player'; //# sourceMappingURL=index.d.ts.map