API Reference

Methods, types etc.


Interfaces and Enums


StreamLayerDemoEvent

Represents a demo event in the StreamLayer system.

  1. id: string – Unique identifier for the event.
  2. title?: string – (Optional) Title of the event.
  3. subtitle?: string – (Optional) Subtitle or additional description.
  4. previewUrl?: string – (Optional) URL of a preview image or video.
  5. videoUrl?: string – (Optional) URL of the main event video.
export interface StreamLayerDemoEvent {
  id: string,
  title?: string,
  subtitle?: string,
  previewUrl?: string,
  videoUrl?: string
}

StreamLayerInviteGroupType

Defines the type of group invitation in StreamLayer.

  1. WatchParty – Represents an invitation to a Watch Party.
  2. Chat – Represents an invitation to a Chat group.
export enum StreamLayerInviteGroupType {
  WatchParty = "WatchParty",
  Chat = "Chat"
}

StreamLayerInviteUser

Represents an invited user in the StreamLayer system.

  1. id?: string – (Optional) Unique user identifier.
  2. tinodeUserId?: string – (Optional) Tinode-specific user identifier (if applicable).
  3. name?: string – (Optional) Full name of the user.
  4. username?: string – (Optional) Username of the user.
  5. avatar?: string – (Optional) URL to the user’s avatar image.
export interface StreamLayerInviteUser {
  id?: string,
  tinodeUserId?: string,
  name?: string,
  username?: string,
  avatar?: string,
}

StreamLayerInvite

Represents an invitation to a StreamLayer event or group.

  1. linkId?: string – (Optional) Unique identifier for the invitation link.
  2. eventId?: string – (Optional) Identifier for the event related to this invite.
  3. externalEventId?: string – (Optional) External identifier of the event (for integration purposes).
  4. groupId?: string – (Optional) Identifier of the group the invite is associated with.
  5. externalGroupId?: string – (Optional) External identifier for the group.
  6. gamification?: boolean – (Optional) Whether gamification features are enabled for this invite.
  7. groupType?: StreamLayerInviteGroupType – (Optional) Type of the invite (Watch Party or Chat).
  8. user?: StreamLayerInviteUser – (Optional) User details associated with the invitation.
export interface StreamLayerInvite {
  linkId?: string,
  eventId?: string,
  externalEventId?: string,
  groupId?: string,
  externalGroupId?: string,
  gamification?: boolean,
  groupType?: StreamLayerInviteGroupType,
  user?: StreamLayerInviteUser
}

StreamLayerTheme (Enum)

Defines the available UI themes for the StreamLayer SDK.

  1. Blue – Blue theme.
  2. Green – Green theme.
export enum StreamLayerTheme {
  Blue = "Blue",
  Green = "Green"
}

StreamLayerConfiguration

Defines the configuration settings for initializing the StreamLayer SDK.

  1. sdkKey: string – Required API key to authenticate the SDK.
  2. theme?: StreamLayerTheme – (Optional) Defines the UI theme (Blue or Green).
  3. isLoggingEnabled?: boolean – (Optional) Enables or disables logging for debugging.
  4. isGlobalLeaderboardEnabled?: boolean – (Optional) Enables global leaderboard functionality.
  5. isGamesInviteEnabled?: boolean – (Optional) Enables the ability to send game invites.
export interface StreamLayerConfiguration {
  sdkKey: string,
  theme?: StreamLayerTheme,
  isLoggingEnabled?: boolean,
  isGlobalLeaderboardEnabled?: boolean,
  isGamesInviteEnabled?: boolean
}

DeepLinkParams

A dynamic object that holds key-value pairs for handling deep links.

  1. [key: string]: any; – Allows storing various deep link parameters.
export interface DeepLinkParams {
  [key: string]: any;
}

Class: StreamLayer

Provides methods for interacting with the StreamLayer SDK.

export class StreamLayer {

  static isInitialized(): Promise<boolean> {
      return StreamLayerModule.isInitialized();
  }

  static authorizationBypass(schema: string, token: string): Promise<void> {
    return StreamLayerModule.authorizationBypass(schema, token)
  }

  static useAnonymousAuth(): Promise<void> {
    return StreamLayerModule.useAnonymousAuth()
  }

  static isUserAuthorized(): Promise<boolean> {
    return StreamLayerModule.isUserAuthorized()
  }

  static logout(): Promise<void> {
    return StreamLayerModule.logout() 
  }

  static removeOverlay(): Promise<void> {
    return StreamLayerModule.removeOverlay() 
  }

  static createEventSession(id: string): Promise<void> {
    return StreamLayerModule.createEventSession(id)
  }

  static releaseEventSession(): void {
    StreamLayerModule.releaseEventSession()
  }

  static getInvite(json: Object): Promise<StreamLayerInvite> {
    return StreamLayerModule.getInvite(json);
  }

  static getDemoEvents(date: string): Promise<Array<StreamLayerDemoEvent>> {
    return StreamLayerModule.getDemoEvents(date)
  }

  static initSdk(config: StreamLayerConfiguration): Promise<void> {
    return StreamLayerModule.initSdk({...config, sdkKey: Config.SL_SDK_API_KEY })
  }

  static handleDeepLink(params: DeepLinkParams): Promise<boolean> {
    return StreamLayerModule.handleDeepLink(params);
  }

}

useAnonymousAuth(): Promise<void>

Enables anonymous authentication mode.
Returns: A Promise<void> indicating the completion of the operation.


isUserAuthorized(): Promise<boolean>

Checks whether the current user is authorized.
Returns: A Promise<boolean> resolving to true if the user is authorized, otherwise false.


logout(): Promise<void>

Logs out the current user from the SDK.
Returns: A Promise<void> indicating the completion of the operation.


removeOverlay(): Promise<void>

Removes any active overlay from the StreamLayer UI.
Returns: A Promise<void> indicating the completion of the operation.


createEventSession(id: string): Promise<void>

Creates a new event session with the specified ID.
Parameters: id: string – The unique identifier for the event session. Returns: A Promise<void> indicating the completion of the operation.


releaseEventSession(): void

Releases the current event session, if active.
Returns: void.


getInvite(json: Object): Promise<StreamLayerInvite>

Retrieves invite details based on the provided JSON parameters.
Parameters: json: Object – Object containing the invite request parameters. Returns: A Promise<StreamLayerInvite> resolving to the invite details.


getDemoEvents(date: string): Promise<Array<StreamLayerDemoEvent>>

Fetches a list of demo events for a specific date.
Parameters: date: string – The date for which demo events should be retrieved. Returns: A Promise<Array<StreamLayerDemoEvent>> resolving to an array of demo events.


initSdk(config: StreamLayerConfiguration): Promise<void>

Initializes the StreamLayer SDK with the provided configuration.
Parameters:

config: StreamLayerConfiguration – The SDK configuration settings.

Returns: A Promise<void> indicating the completion of the initialization process.


handleDeepLink(params: DeepLinkParams): Promise<boolean>

Processes a deep link and handles navigation accordingly.
Parameters: params: DeepLinkParams – Key-value parameters from the deep link. Returns: A Promise<boolean> resolving to true if the deep link was handled successfully, otherwise false.


Class StreamLayerView


StreamLayerViewOverlay

Defines different overlay types available in the StreamLayer view.

  1. Games: Overlay for interactive games.
  2. WatchParty: Overlay for watch parties where users can watch content together.
  3. Twitter: Overlay that integrates Twitter feed.
  4. Statistics: Overlay displaying statistical data.
export enum StreamLayerViewOverlay {
  Games = "Games",
  WatchParty = "WatchParty",
  Twitter = "Twitter",
  Statistics = "Statistics",
}

StreamLayerViewOverlayLandscapeMode

Specifies the placement of overlays in landscape mode.

  1. Start: Overlay positioned at the beginning of the screen.
  2. End: Overlay positioned at the end of the screen.
  3. Lbar: Overlay positioned in an L-bar layout.
export enum StreamLayerViewOverlayLandscapeMode {
  Start = "Start",
  End = "End",
  Lbar = "Lbar",
}

StreamLayerViewNotificationFeature

Defines the notification features available in the StreamLayer view.

  1. WatchParty: Notifications related to watch parties.
  2. Games: Notifications about game events.
  3. Chat: Notifications for chat interactions.
  4. Twitter: Notifications for Twitter-related content.
export enum StreamLayerViewNotificationFeature {
  WatchParty = "WatchParty",
  Games = "Games",
  Chat = "Chat",
  Twitter = "Twitter",
}

StreamLayerViewConfiguration

Configuration options for the StreamLayer view.

  1. viewNotificationFeatures: List of enabled notification features.
  2. isGamesPointsEnabled: Enables/disables game points display.
  3. isGamesPointsStartSide: Determines whether game points appear on the starting side.
  4. isLaunchButtonEnabled: Enables/disables the launch button.
  5. isMenuAlwaysOpened: Keeps the menu always open if true.
  6. isMenuLabelsVisible: Shows or hides menu labels.
  7. isMenuProfileEnabled: Enables/disables the profile section in the menu.
  8. isTooltipsEnabled: Enables/disables tooltips.
  9. isWatchPartyReturnButtonEnabled: Enables/disables the "return to watch party" button.
  10. isWhoIsWatchingViewEnabled: Enables/disables the "Who is watching?" view.
  11. isOverlayExpandable: Allows the overlay to be expanded.
  12. overlayHeightSpace: Sets additional height space for the overlay.
  13. overlayWidth: Defines the width of the overlay.
  14. overlayLandscapeMode: Defines how the overlay is displayed in landscape mode.
export interface StreamLayerViewConfiguration {
  viewNotificationFeatures?: StreamLayerViewNotificationFeature[]
  isGamesPointsEnabled?: boolean,
  isGamesPointsStartSide?: boolean,
  isLaunchButtonEnabled?: boolean,
  isMenuAlwaysOpened?: boolean,
  isMenuLabelsVisible?: boolean,
  isMenuProfileEnabled?: boolean,
  isTooltipsEnabled?: boolean,
  isWatchPartyReturnButtonEnabled?: boolean,
  isWhoIsWatchingViewEnabled?: boolean,
  isOverlayExpandable?: boolean,
  overlayHeightSpace?: number,
  overlayWidth?: number,
  overlayLandscapeMode?: StreamLayerViewOverlayLandscapeMode
}

StreamLayerViewProps

Props for the StreamLayerView React component.

  1. style: Custom styles for the view.
  2. config: Configuration options for the view.
  3. applyWindowInsets: If true, applies window insets for better layout adaptation.
  4. playerView: A React node representing the video player inside the overlay.
  5. player: The player object controlling volume and settings.
  6. onRequestStream: Callback when a stream is requested (receives id).
  7. onLBarStateChanged: Callback triggered when the L-bar position changes (receives slideX, slideY).
  8. onRequestAudioDucking: Callback for requesting audio ducking (receives level).
  9. onDisableAudioDucking: Callback when audio ducking should be disabled.

export interface StreamLayerViewProps {
  style?: StyleProp<ViewStyle>;
  config?: StreamLayerViewConfiguration;
  applyWindowInsets?: Boolean;
  playerView?: ReactNode;
  player?: StreamLayerViewPlayer;
  onRequestStream?: (id: string) => void;
  onLBarStateChanged?: (slideX: number, slideY: number) => void;
  onRequestAudioDucking?: (level: number) => void;
  onDisableAudioDucking?: () => void;
}

StreamLayerViewPlayer
Represents a player inside the StreamLayer view.

volume: The volume level of the player.

export interface StreamLayerViewPlayer {
  volume: number;
}

Integration usage:


Variables.

State Class for LBar

This class represents the state of an LBar with two properties: slideX and slideY, which define its position.

class LBarState {
    slideX: number;
    slideY: number;

    constructor(slideX: number, slideY: number) {
        this.slideX = slideX
        this.slideY = slideY
    }
}

Player Configuration and Source

These constants define the player's configuration and source URL for playback.

const playerConfig: PlayerConfiguration = {
    license: undefined,
};

const source: SourceDescription = {
    sources: [
        {
            src: "https://cdn.theoplayer.com/video/elephants-dream/playlist-single-audio.m3u8",
            type: "application/x-mpegurl"
        },
    ],
};

const playerHeight = isScreenPortrait() ? 300 : Dimensions.get('screen').height;

const streamLayerViewPlayer: StreamLayerViewPlayer = {
    get volume() {
        return 0.0
    },
    set volume(value) {

    },
}
View Configuration and Insets

These variables store the safe area insets and the view configuration.

const viewConfig = getViewConfig()
const insets = useSafeAreaInsets()
Event List and Current Event

This block initializes an array of event items and finds the currently selected event.

volumeBeforeDucking (number | undefined) – Stores the player's volume level before it is temporarily reduced (ducked), ensuring it can be restored later.

isPortrait (boolean | undefined) – Tracks whether the device screen is in portrait mode, likely affecting UI layout and player positioning.

player (THEOplayer | undefined) – Holds an instance of THEOplayer, managing video playback. If undefined, the player is not yet initialized.

lbarState (LBarState) – Manages the position of the LBar UI component, storing slideX and slideY values for animations or positioning.

events (Array<StreamLayerDemoEvent> | undefined) – Holds a list of available events for selection. If undefined, no events have been loaded yet.

currentEventId (String | undefined) – Stores the ID of the currently selected event, helping to determine which event details to display.

isInitialized (boolean) – Indicates whether the component has completed its setup process, ensuring dependent logic executes only when ready.

viewRef (useRef<StreamLayerView>) – A reference to the StreamLayerView component, allowing direct interaction and modifications.

const [volumeBeforeDucking, setVolumeBeforeDucking] = useState<number | undefined>(undefined)
const [isPortrait, setPortrait] = useState<boolean>();
const [player, setPlayer] = useState<THEOplayer | undefined>(undefined);
const [lbarState, setLbarState] = useState(new LBarState(0, 0));
const [events, setEvents] = useState<Array<StreamLayerDemoEvent>>()
const [currentEventId, setCurrentEventId] = useState<String>()
const [isInitialized, setInitialized] = useState(false);
const viewRef = useRef<StreamLayerView>(null);

Event List and Selection
  1. scrollItems (Array<ReactElement>) – Holds a list of rendered event items for display.
  2. Looping through events – Each event is converted into a Pressable component, allowing user interaction.
  3. currentEvent (StreamLayerDemoEvent | undefined) – Finds the currently selected event based on currentEventId.
var scrollItems = new Array<ReactElement>();
if (events !== undefined) {
  events.forEach((event) => {
      scrollItems.push(
          <Pressable key={event.id} onPress={() => createEventSession(event.id)}>
              <View style={styles.eventRow}>
                  {event.previewUrl !== undefined && (
                      <Image source={{ uri: event.previewUrl }}
                          style={styles.eventRowImage} />
                  )}
                  <Text style={styles.eventRowTitle} numberOfLines={1} ellipsizeMode='tail'>{event.title}</Text>
              </View>
          </Pressable>
      )
  })
}

var currentEvent: StreamLayerDemoEvent | undefined;
if (events !== undefined && currentEventId !== undefined) {
  currentEvent = events.find((event) => {
      return event.id == currentEventId
  })
}

Effect Hook for Initialization and Orientation Handling
  1. useEffect – Runs once on mount ([] dependency array).
  2. setPortrait(isScreenPortrait()) – Determines initial screen orientation.
  3. initialize() – Calls checkInitialized() inside a try-catch block.
  4. Dimensions.addEventListener('change', ...) – Listens for screen rotation and updates isPortrait.
useEffect(() => {
        
  setPortrait(isScreenPortrait())
  const initialize = async () => {

      try {
          checkInitialized();
      } catch (error) {
          console.error("Error initializing:", error);
      }
  };

  initialize();

  const subscription = Dimensions.addEventListener('change', ({ window, screen }) => {
      setPortrait(window.height > window.width)
  });


  return () => subscription?.remove()


}, []);

View Configuration: getViewConfig()

This function is used to return a configuration object for the StreamLayerView, which defines various UI and functional settings for the StreamLayer integration in the app.

  1. viewNotificationFeatures (StreamLayerViewNotificationFeature[]):
    An array of features that should be enabled for notifications in the view. The array includes:
    1. StreamLayerViewNotificationFeature.Games: Enables the "Games" notification feature.
    2. StreamLayerViewNotificationFeature.Chat: Enables the "Chat" feature, allowing users to communicate within the stream.
    3. StreamLayerViewNotificationFeature.WatchParty: Enables "Watch Party" functionality, which allows users to watch together.
    4. StreamLayerViewNotificationFeature.Twitter: Enables Twitter integration for the event or stream.
      isGamesPointsEnabled (boolean): This boolean value indicates whether game points are enabled or not. It's set to true, meaning points are enabled for the game or stream.
  2. isGamesPointsStartSide (boolean):
    Determines whether the points start from the side or not. It's set to false, meaning the points will not start from the side.
  3. isLaunchButtonEnabled (boolean):
    This value enables or disables the "Launch" button in the UI. It's set to true, meaning the button will be shown.
  4. isMenuAlwaysOpened (boolean):
    Controls whether the menu should always remain open. It's set to false, meaning the menu can be collapsed.
  5. isMenuLabelsVisible (boolean):
    This option controls whether labels in the menu are visible. It's set to true, meaning labels will be displayed.
  6. isMenuProfileEnabled (boolean):
    Controls whether the user profile option is enabled in the menu. It's set to true, meaning the profile will be enabled.
  7. isTooltipsEnabled (boolean):
    This option enables or disables tooltips in the app. It's set to true, meaning tooltips are enabled.
  8. isWatchPartyReturnButtonEnabled (boolean):
    This controls the visibility of the "Watch Party" return button. It's set to true, meaning this button will be enabled.
  9. isWhoIsWatchingViewEnabled (boolean):
    If set to true, this enables a view that shows who is watching the stream.
  10. isOverlayExpandable (boolean):
    This option allows the overlay to be expandable. It's set to true, meaning the overlay can expand.
  11. overlayHeightSpace (number):
    Specifies the height space for the overlay. It’s set to 300, indicating the overlay’s height.
  12. overlayWidth (number):
    Specifies the width of the overlay. It's set to 0, meaning the overlay has no width initially.
  13. overlayLandscapeMode (StreamLayerViewOverlayLandscapeMode):
    Defines the behavior of the overlay in landscape mode. It’s set to
  14. StreamLayerViewOverlayLandscapeMode.Start, meaning the overlay behavior will be defined at the start of the landscape mode.
function getViewConfig(): StreamLayerViewConfiguration {
  return {
      viewNotificationFeatures: new Array(
          StreamLayerViewNotificationFeature.Games,
          StreamLayerViewNotificationFeature.Chat,
          StreamLayerViewNotificationFeature.WatchParty,
          StreamLayerViewNotificationFeature.Twitter
      ),
      isGamesPointsEnabled: true,
      isGamesPointsStartSide: false,
      isLaunchButtonEnabled: true,
      isMenuAlwaysOpened: false,
      isMenuLabelsVisible: true,
      isMenuProfileEnabled: true,
      isTooltipsEnabled: true,
      isWatchPartyReturnButtonEnabled: true,
      isWhoIsWatchingViewEnabled: true,
      isOverlayExpandable: true,
      overlayHeightSpace: 300,
      overlayWidth: 0,
      overlayLandscapeMode: StreamLayerViewOverlayLandscapeMode.Start
  }
}
isScreenPortrait()
function isScreenPortrait(): boolean {
  return Dimensions.get('window').height > Dimensions.get('window').width
}
onReady(player: THEOplayer)

This function is called when the player is ready:

  1. setPlayer(player):
    Sets the player instance to the state.
  2. player.autoplay = true:
    Automatically plays the video when it’s loaded.
  3. player.source = source:
    Sets the video source, where source is assumed to be a predefined video URL or stream source.
  4. player.addEventListener(PlayerEventType.ERROR, console.log):
    Adds an error event listener to the player. If an error occurs during playback, it will be logged to the console.
const onReady = (player: THEOplayer) => {
  setPlayer(player);
  player.autoplay = true
  player.source = source;
  player.addEventListener(PlayerEventType.ERROR, console.log);
}

checkInitialized()

This asynchronous function initializes the StreamLayer SDK:

  1. StreamLayer.initSdk({ isLoggingEnabled: true, theme: StreamLayerTheme.Green }, false):
    Initializes the SDK with logging enabled and sets the theme to green.
  2. checkAuth():
    Calls checkAuth() to verify the user's authentication status.
  3. loadDemoEvents():
    Loads demo events from the StreamLayer API.
  4. StreamLayer.isInitialized():
    Checks if the SDK is initialized and stores the result in inited. It then updates the state setInitialized(inited).
const checkInitialized = async () => {
  try {
          await StreamLayer.initSdk({
              isLoggingEnabled: true,
              theme: StreamLayerTheme.Green
            },false)
          checkAuth()
          loadDemoEvents()
          const inited = await StreamLayer.isInitialized()
          setInitialized(inited)

  } catch (e) {
      console.error(e);
  }
}
loadDemoEvents()

This function loads demo events:

  1. It fetches events from the StreamLayer API using StreamLayer.getDemoEvents("2022-01-01").
  2. If events are successfully fetched, it updates the state with the event list using setEvents(events).
  3. If there are events, it automatically creates a session for the first event by calling createEventSession(events[0].id).
const loadDemoEvents = async () => {
  try {
      const events = await StreamLayer.getDemoEvents("2022-01-01")
      setEvents(events)
      if (events !== undefined && events !== null && events.length > 0) {
          createEventSession(events[0].id)
      }
  } catch (e) {
      console.error("PlayerScreen loadDemoEvents error", e);
  }
}

checkAuth()

This function checks if the user is authenticated:

  1. StreamLayer.isUserAuthorized():
    Checks if the user is authorized.
  2. StreamLayer.useAnonymousAuth():
    If the user is not authorized, it switches to anonymous authentication.
const checkAuth = async () => {
  try {
      const isUserAuthorized = await StreamLayer.isUserAuthorized()
      if (!isUserAuthorized) {
          await StreamLayer.useAnonymousAuth();
      }
  } catch (e) {
      console.error(e);
  }
}
createEventSession(id: string)

This function creates a new event session

  1. StreamLayer.createEventSession(id):
    Calls StreamLayer to create an event session with the given id.
  2. setCurrentEventId(id):
    Updates the currentEventId state to the newly created event's ID.
const createEventSession = async (id: string) => {
  try {
      await StreamLayer.createEventSession(id);
      console.log(`Created a new event with id ${id}`);
      setCurrentEventId(id)
  } catch (e) {
      console.error(e);
  }
};

Event Listeners for UI and Audio

  1. onRequestStream(id: string) – Handles stream request events.
  2. onLBarStateChanged(slideX: number, slideY: number) – Updates LBar state when its position changes.
  3. onRequestAudioDucking(level: number) – Handles audio ducking requests.
  4. onDisableAudioDucking() – Disables audio ducking when needed.
    const onRequestStream = (id: string) => {
        console.log("onRequestStream id=" + id)
        createEventSession(id)
    }

    const onLBarStateChanged = (slideX: number, slideY: number) => {
        console.log("onLBarStateChanged slideX=" + slideX + " slideY=" + slideY)
        setLbarState(new LBarState(slideX, slideY));
    }

    const onRequestAudioDucking = (level: number) => {
        console.log("onRequestAudioDucking level=" + level)
    }

    const onDisableAudioDucking = () => {
        console.log("onDisableAudioDucking")
    }

Fully tuned example:

function App(): React.JSX.Element {

  class LBarState {
    slideX: number;
    slideY: number;

    constructor(slideX: number, slideY: number) {
        this.slideX = slideX
        this.slideY = slideY
    }
}

const playerConfig: PlayerConfiguration = {
    license: undefined,
};

const source: SourceDescription = {
    sources: [
        {
            src: "https://cdn.theoplayer.com/video/elephants-dream/playlist-single-audio.m3u8",
            type: "application/x-mpegurl"
        },
    ],
};

const insets = useSafeAreaInsets()

const playerHeight = isScreenPortrait() ? 300 : Dimensions.get('screen').height;

const streamLayerViewPlayer: StreamLayerViewPlayer = {
    get volume() {
        return 0.0
    },
    set volume(value) {

    },
}

const viewConfig = getViewConfig()

const [volumeBeforeDucking, setVolumeBeforeDucking] = useState<number | undefined>(undefined)
const [isPortrait, setPortrait] = useState<boolean>();
const [player, setPlayer] = useState<THEOplayer | undefined>(undefined);
const [lbarState, setLbarState] = useState(new LBarState(0, 0));
const [events, setEvents] = useState<Array<StreamLayerDemoEvent>>()
const [currentEventId, setCurrentEventId] = useState<String>()
const [isInitialized, setInitialized] = useState(false);
const viewRef = useRef<StreamLayerView>(null);

var scrollItems = new Array<ReactElement>();
if (events !== undefined) {
  events.forEach((event) => {
      scrollItems.push(
          <Pressable key={event.id} onPress={() => createEventSession(event.id)}>
              <View style={styles.eventRow}>
                  {event.previewUrl !== undefined && (
                      <Image source={{ uri: event.previewUrl }}
                          style={styles.eventRowImage} />
                  )}
                  <Text style={styles.eventRowTitle} numberOfLines={1} ellipsizeMode='tail'>{event.title}</Text>
              </View>
          </Pressable>
      )
  })
}

var currentEvent: StreamLayerDemoEvent | undefined;
if (events !== undefined && currentEventId !== undefined) {
  currentEvent = events.find((event) => {
      return event.id == currentEventId
  })
}

useEffect(() => {
        
  setPortrait(isScreenPortrait())
  const initialize = async () => {

      try {
          checkInitialized();
      } catch (error) {
          console.error("Error initializing:", error);
      }
  };

  initialize();

  const subscription = Dimensions.addEventListener('change', ({ window, screen }) => {
      setPortrait(window.height > window.width)
  });


  return () => subscription?.remove()


}, []);


function getViewConfig(): StreamLayerViewConfiguration {
  return {
      viewNotificationFeatures: new Array(
          StreamLayerViewNotificationFeature.Games,
          StreamLayerViewNotificationFeature.Chat,
          StreamLayerViewNotificationFeature.WatchParty,
          StreamLayerViewNotificationFeature.Twitter
      ),
      isGamesPointsEnabled: true,
      isGamesPointsStartSide: false,
      isLaunchButtonEnabled: true,
      isMenuAlwaysOpened: false,
      isMenuLabelsVisible: true,
      isMenuProfileEnabled: true,
      isTooltipsEnabled: true,
      isWatchPartyReturnButtonEnabled: true,
      isWhoIsWatchingViewEnabled: true,
      isOverlayExpandable: true,
      overlayHeightSpace: 300,
      overlayWidth: 0,
      overlayLandscapeMode: StreamLayerViewOverlayLandscapeMode.Start
  }
}

function isScreenPortrait(): boolean {
  return Dimensions.get('window').height > Dimensions.get('window').width
}


const onReady = (player: THEOplayer) => {
  setPlayer(player);
  player.autoplay = true
  player.source = source;
  player.addEventListener(PlayerEventType.ERROR, console.log);
}


const checkInitialized = async () => {
  try {
          await StreamLayer.initSdk({
              isLoggingEnabled: true,
              theme: StreamLayerTheme.Green
            },false)
          checkAuth()
          loadDemoEvents()
          const inited = await StreamLayer.isInitialized()
          setInitialized(inited)

  } catch (e) {
      console.error(e);
  }
}

const loadDemoEvents = async () => {
  try {
      const events = await StreamLayer.getDemoEvents("2022-01-01")
      setEvents(events)
      if (events !== undefined && events !== null && events.length > 0) {
          createEventSession(events[0].id)
      }
  } catch (e) {
      console.error("PlayerScreen loadDemoEvents error", e);
  }
}

const checkAuth = async () => {
  try {
      const isUserAuthorized = await StreamLayer.isUserAuthorized()
      if (!isUserAuthorized) {
          await StreamLayer.useAnonymousAuth();
      }
  } catch (e) {
      console.error(e);
  }
}

const createEventSession = async (id: string) => {
  try {
      await StreamLayer.createEventSession(id);
      console.log(`Created a new event with id ${id}`);
      setCurrentEventId(id)
  } catch (e) {
      console.error(e);
  }
};

const onRequestStream = (id: string) => {
  console.log("onRequestStream id=" + id)
  createEventSession(id)
}

const onLBarStateChanged = (slideX: number, slideY: number) => {
  console.log("onLBarStateChanged slideX=" + slideX + " slideY=" + slideY)
  setLbarState(new LBarState(slideX, slideY));
}

const onRequestAudioDucking = (level: number) => {
  console.log("onRequestAudioDucking level=" + level)
}

const onDisableAudioDucking = () => {
  console.log("onDisableAudioDucking")
}
   


 return (
        <SafeAreaView style={{...styles.container, marginTop: insets.top }}  edges={['top']}>
            {(isPortrait) &&
                <View style={{ flex: 1, marginTop: playerHeight  - insets.top }}>
                    {currentEvent !== undefined && (
                        <Text style={styles.eventTitle}>{currentEvent.title}</Text>
                    )}
                    <ScrollView style={{ flex: 1 }}>
                        {scrollItems}
                    </ScrollView>
                </View>
            }
            
            {isInitialized && 
                <StreamLayerView
                    style={StyleSheet.absoluteFillObject}
                    ref={viewRef}
                    config={viewConfig}
                    applyWindowInsets={false}
                    onRequestStream={onRequestStream}
                    onLBarStateChanged={onLBarStateChanged}
                    onRequestAudioDucking={onRequestAudioDucking}
                    onDisableAudioDucking={onDisableAudioDucking}
                    player={streamLayerViewPlayer}
                    playerView={
                        <THEOplayerView config={playerConfig} onPlayerReady={onReady}
                            style={{
                                width: Dimensions.get('screen').width - lbarState.slideX,
                                height: playerHeight-lbarState.slideY,
                                paddingTop: 0,
                            }}>
                            {player !== undefined && (
                                <UiContainer
                                    theme={DEFAULT_THEOPLAYER_THEME}
                                    player={player}
                                    center={
                                        <CenteredControlBar
                                            left={<SkipButton skip={-10} />}
                                            middle={<PlayButton />}
                                            right={<SkipButton skip={10} />}
                                        />
                                    }
                                />
                            )}
                        </THEOplayerView>
                    }
                />
            }

        </SafeAreaView>
    )


const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: 'gray',
    },
    eventTitle: {
        color: 'white',
        fontSize: 24,
        margin: 4
    },
    eventRowTitle: {
        flex: 1,
        color: 'white',
        fontSize: 16,
        margin: 4
    },
    eventRow: {
        flexDirection: 'row',
        margin: 4,
        padding: 4,
        height: 58,
        justifyContent: 'flex-start',
        alignItems: 'center',
        borderWidth: 1,
        borderColor: 'black',
    },
    eventRowImage: {
        width: 100,
        height: 50
    },
    overlay: {
        paddingTop: 500,
        backgroundColor: '#00000000',
        flex: 1,
    }
});