Knowledge Base
  • Welcome to Huddle01 SYNC
  • Web-SDK
    • Client
    • Demo App
  • Flutter SDK
    • Client
    • Demo App
  • Android-Native
    • Client
    • Demo App
  • React-Native
    • Client
    • Demo App
Powered by GitBook
On this page
  • GitHub Link:
  • Codes

Was this helpful?

  1. React-Native

Demo App

A demo application consuming the ReactNative Huddle Client SDK

PreviousClient

Last updated 3 years ago

Was this helpful?

GitHub Link:

Codes

import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import LoginScreen from './screens/LoginScreen';
import RoomScreen from './screens/RoomScreen';
import WaitingScreen from './screens/WaitingScreen';

const Stack = createStackNavigator();

const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen
          name="Login"
          component={LoginScreen}
          options={{headerShown: false}}
        />
        <Stack.Screen name="Room" component={RoomScreen} />
        <Stack.Screen name="Waiting" component={WaitingScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

export default App;
import React, {useState} from 'react';
import {View, StyleSheet} from 'react-native';
import {TextInput} from 'react-native-paper';
import AsyncStorage from '@react-native-community/async-storage';
import {Button} from 'react-native-paper';

export default function LoginScreen(props) {
  const [roomId, setRoomId] = useState('');
  const [userName, setUserName] = useState('');
  const [loading, setLoading] = useState(false);

  const onLogin = async () => {
    setLoading(true);
    try {
      await AsyncStorage.setItem('roomId', roomId);
      await AsyncStorage.setItem('userName', userName);
      setLoading(false);
      props.navigation.push('Room', {roomId, userName});
    } catch (err) {
      console.log('Error', err);
      setLoading(false);
    }
  };

  return (
    <View style={styles.root}>
      <View style={styles.content}>
        <TextInput
          label="Room ID"
          onChangeText={text => setRoomId(text)}
          mode="outlined"
          style={styles.input}
        />
        <TextInput
          label="Your Name"
          onChangeText={text => setUserName(text)}
          mode="outlined"
          style={styles.input}
        />

        <Button
          mode="contained"
          onPress={onLogin}
          loading={loading}
          style={styles.btn}
          contentStyle={styles.btnContent}
          disabled={userName.length === 0}>
          Join
        </Button>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    backgroundColor: '#fff',
    flex: 1,
    justifyContent: 'center',
  },
  content: {
    paddingHorizontal: 20,
    justifyContent: 'center',
  },
  heading: {
    fontSize: 18,
    marginBottom: 10,
    fontWeight: '600',
  },
  input: {
    height: 60,
    marginBottom: 10,
  },
  btn: {
    height: 60,
    alignItems: 'stretch',
    justifyContent: 'center',
    fontSize: 18,
  },
  btnContent: {
    alignItems: 'center',
    justifyContent: 'center',
    height: 60,
  },
});
import React, {useEffect, useState} from 'react';
import { useNavigation } from '@react-navigation/core';
import {View, StyleSheet, Button, Alert} from 'react-native';
import {Text, IconButton} from 'react-native-paper';
import InCallManager from 'react-native-incall-manager';
import HuddleClient, { emitter } from 'react-native-huddle-client';
import {
  RTCView,
  MediaStream,
} from 'react-native-webrtc';

export default function RoomScreen({...props}) {
  const navigation = useNavigation();
  const params = props.route.params;

  [huddle, setHuddle] = useState(null);
  [remoteStream, setRemoteStream] = useState({toURL: () => null});
  [localStream, setLocalStream] = useState({toURL: () => null});
  [peer, setPeer] = useState(null);
  [isMute, setMute] = useState(true);
  [isCameraOn, setCameraOn] = useState(false);
  [isOpenWaitingRoom, setOpenWaitingRoom] = useState(false);
  [lobbyPeers, setLobbyPeers] = useState([]);

  React.useLayoutEffect(() => {
    navigation.setOptions({
      headerRight: () => (
        <Button onPress={() => {
          navigation.push('Waiting', {huddle, lobbyPeers});
        }} title="Waiting Room" />
      ),
    })
  }, [navigation]);

  useEffect(() => {
    let roomId = params.roomId;
    const userName = params.userName;
    const userId = generateId(8);

    if (!roomId) {
      roomId = generateId(8);
    }

    const config = {
      apiKey: 'abcd',
      hostname: 'alpha.huddle01.com:4443',
      roomId: roomId,
      peerId: userId,
      displayName: userName,
      isBot: false,
    };

    const h = new HuddleClient(config);
    setHuddle(h);

    navigation.setOptions({ title: roomId });

    InCallManager.start({media: 'audio'});
    InCallManager.setForceSpeakerphoneOn(true);
    InCallManager.setSpeakerphoneOn(true);

    return async () => {
      await leaveRoom();
    }
  }, []);

  useEffect(() => {
    joinRoom();
  }, [huddle]);

  generateId = (length) => {
    let result = '';
    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    const charactersLength = characters.length;
    for ( var i = 0; i < length; i++ ) {
      result += characters.charAt(Math.floor(Math.random() * charactersLength));
    }
    return result;
  };

  const enableDataProducers = async () => {
    await huddle.enableChatDataProducer();
    await huddle.enableBotDataProducer();
    await huddle.enableMiscDataProducer();
    await huddle.enableRaiseHandDataProducer();
    await huddle.enableReactionsDataProducer();
    await huddle.enableMiroDataProducer();
  };

  const setupEventListeners = async () => {
    emitter.on("roomState", async (state) => {
      console.log('emitter::roomState: ', state);
      if (state === 'connected') {
        await enableDataProducers();
        await huddle.enableMic();
        await disableMic();
      }
    });
  
    emitter.on("error", (error) => {
      console.log('emitter::error: ', JSON.stringify(error));
    });
  
    emitter.on("addPeer", (peer) => {
      setPeer(peer);
      console.log('emitter::addPeer: ', JSON.stringify(peer));
    });
  
    emitter.on("addProducer", (producer) => {
      console.log('emitter::addProducer: ', JSON.stringify(producer));

      if (producer.type === 'camera') {
        const track = producer.track;
        const stream = new MediaStream();
        stream.addTrack(track);
        setLocalStream(stream);
      }
    });
  
    emitter.on("addConsumer", (consumer) => {
      console.log('emitter::addConsumer: ', JSON.stringify(consumer));
      const track = consumer.track;
      const stream = new MediaStream();
      stream.addTrack(track);
      setRemoteStream(stream);
    });
  
    emitter.on("removePeer", (peer) => {
      setPeer(null);
      console.log('emitter::removePeer: ', JSON.stringify(peer));
    });
  
    emitter.on("removeProducer", (producer) => {
      setLocalStream({toURL: () => null});
      console.log('emitter::removeProducer: ', JSON.stringify(producer));
    });
   
    emitter.on("removeConsumer", (consumer) => {
      setRemoteStream({toURL: () => null});
      console.log('emitter::removeConsumer: ', JSON.stringify(consumer));
    });

    emitter.on("newLobbyPeer", (peers) => {
      const lobbyPeers = peers.peers;
      setLobbyPeers(lobbyPeers);
    });

    emitter.on("updatedPeersArray", (peers) => {
      const lobbyPeers = peers.peers;
      setLobbyPeers(lobbyPeers);
    })
  };

  const joinRoom = async () => {
    if (!huddle) return;
    try {
      setupEventListeners();
      await huddle.join();
    } catch (error) {
      console.log(error);
    }
  };

  const leaveRoom = async () => {
    if (!huddle) return;
    try {
      await huddle.close();
      // setRoomState(false);
    } catch (error) {
      console.log(error);
    }
  };

  const enableCamera = async () => {
    if (!huddle) return;
    try {
      await huddle.enableCamera();
    } catch (error) {
      console.log(error);
    }
  };

  const disableCamera = async () => {
    if (!huddle) return;
    try {
      await huddle.disableCamera();
    } catch (error) {
      console.log(error);
    }
  };

  const enableMic = async () => {
    if (!huddle) return;
    try {
      // await huddle.enableMic();
      await huddle.unmuteMic();
    } catch (error) {
      console.log(error);
    }
  };

  const disableMic = async () => {
    if (!huddle) return;
    try {
      // await huddle.disableMic();
      await huddle.muteMic();
    } catch (error) {
      console.log(error);
    }
  };

  const sendMsg = async () => {
    if (!huddle) return;
    try {
      await huddle.sendReaction("😂");
    } catch (error) {
      console.log(error);
    }
  };

  const changeCamera = async () => {
    if (!huddle) return;
    try {
      await huddle.changeCamera();
    } catch (error) {
      console.log(error);
    }
  };

  const changeCameraResolution = async () => {
    if (!huddle) return;
    try {
      await huddle.changeCameraResolution();
    } catch (error) {
      console.log(error);
    }
  };

  const onToggleCamera = () => {
    if (isCameraOn) {
      disableCamera();
      setCameraOn(false);
    } else {
      enableCamera();
      setCameraOn(true);
    }
  };

  const onToggleAudio = () => {
    if (isMute) {
      enableMic();
      setMute(false);
    } else {
      disableMic();
      setMute(true);
    }
  };

  const onSwitchCamera = () => {
    changeCamera();
  };

  const onAction = async () => {
    if (!huddle) return;
    try {
      await huddle.sendReaction("😂");
    } catch (error) {
      console.log(error);
    }
  }

  const onRaiseHand = async () => {
    if (!huddle) return;
    try {
      await huddle.raiseHand("true");
    } catch (error) {
      console.log(error);
    }
  }

  useEffect(() => {
    
  }, []);

  return (
    <>
      <View style={styles.root}>
        <View style={[styles.videoContainer]}>
          <Text>My Video</Text>
          <RTCView streamURL={localStream.toURL()} style={styles.localVideo} />
          <View style={{position: 'absolute', top: 20, right: 5}}>
            <IconButton
              style={styles.controlIcon} 
              icon={ isCameraOn ? 'camera' : 'camera-off' }
              color='#777' 
              size={25}
              onPress={onToggleCamera} />
            <IconButton
              style={styles.controlIcon} 
              icon={isMute ? 'microphone-off' : 'microphone'}
              color='#777' 
              size={25}
              onPress={onToggleAudio} />
            <IconButton
              style={styles.controlIcon} 
              icon='camera-party-mode'
              color='#777' 
              size={25}
              onPress={onSwitchCamera} />
            <IconButton
              style={styles.controlIcon} 
              icon='emoticon'
              color='#777' 
              size={25}
              onPress={onAction} />
            <IconButton
              style={styles.controlIcon} 
              icon='hand'
              color='#777' 
              size={25}
              onPress={onRaiseHand} />
          </View>
        </View>
        <View style={[styles.videoContainer]}>
          <Text>{(peer ?? {}).displayName ?? 'Friends Video'}</Text>
          <RTCView
            streamURL={remoteStream.toURL()}
            style={styles.remoteVideo}
          />
        </View>
      </View>
    </>
  );
}

const styles = StyleSheet.create({
  root: {
    backgroundColor: '#fff',
    flex: 1,
    paddingHorizontal: 20,
  },
  videoContainer: {
    flex: 1,
    overflow: 'hidden',
    borderRadius: 6,
    marginVertical: 10,
  },
  localVideo: {
    backgroundColor: '#f2f2f2',
    flex: 1, 
    alignItems: 'flex-end',
  },
  remoteVideo: {
    backgroundColor: '#f2f2f2',
    flex: 1, 
  },
  controlIcon: {
    backgroundColor: '#d0d0d0',
    marginHorizontal: 10,
    marginTop: 10,
  }
});
import React, {useEffect} from 'react';
import { useNavigation } from '@react-navigation/core';
import {View, StyleSheet, FlatList, Alert, TouchableOpacity} from 'react-native';
import {Text} from 'react-native-paper';

export default function WaitingScreen({...props}) {
  const navigation = useNavigation();
  const params = props.route.params;

  onDeny = (peerId) => {
    const huddleClient = params.huddle;
    huddleClient.disallowLobbyPeerFromJoiningRoom(peerId);
  }

  onAllow = (peerId) => {
    const huddleClient = params.huddle;
    huddleClient.allowLobbyPeerToJoinRoom(peerId);
  }

  renderItem =({item, index}) => {
    return (
      <View style={{flexDirection: 'row', alignItems: 'center', marginVertical: 8}}>
        <Text style={{flex: 1, fontSize: 16, fontWeight: '500', marginVertical: 8}}>{item.displayName}</Text>
        <TouchableOpacity style={{...styles.button, borderColor: 'red'}} onPress={() => onDeny(item.peerId)}>
          <Text style={{color: 'red'}}>Deny</Text>
        </TouchableOpacity>
        <TouchableOpacity style={{...styles.button, borderColor: 'green'}} onPress={() => onAllow(item.peerId)}>
          <Text style={{color: 'green'}}>Allow</Text>
        </TouchableOpacity>
      </View>
    )
  }

  return (
    <View style={styles.root}>
      <FlatList 
        data={params.lobbyPeers}
        renderItem={renderItem}
        keyExtractor={(item, index) => index.toString()}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    backgroundColor: '#fff',
    flex: 1,
    paddingHorizontal: 20,
  },
  button: {
    height: 30, 
    paddingVertical: 5, 
    paddingHorizontal: 10,
    margin: 5,
    borderRadius: 5, 
    borderWidth: 1, 
    justifyContent: 'center', 
    alignItems: 'center'
  }
});

For any help, reach out to us on Slack. We are available 24*7 at: .

https://github.com/Huddle-01/react-native-sdk
Huddle01 Community