├── .eslintrc.js
├── .github
└── ISSUE_TEMPLATE
│ └── issue-report.md
├── .gitignore
├── .prettierrc.js
├── Components.ts
├── Contexts.ts
├── LICENSE
├── README.md
├── UI Kit.png
├── docs
├── .nojekyll
├── UI Kit.png
├── assets
│ ├── highlight.css
│ ├── icons.css
│ ├── icons.png
│ ├── icons@2x.png
│ ├── main.js
│ ├── search.js
│ ├── style.css
│ ├── widgets.png
│ └── widgets@2x.png
├── classes
│ ├── Agora_UIKit._internal_.Component.html
│ ├── Agora_UIKit._internal_.RtcConnection.html
│ ├── Agora_UIKit._internal_.RtmAttribute.html
│ ├── Agora_UIKit._internal_.RtmChannelAttribute.html
│ ├── Agora_UIKit._internal_.RtmMessage.html
│ ├── React_Contexts._internal_.EventEmitter.html
│ ├── React_Contexts._internal_.EventSubscriptionVendor.html
│ ├── React_Contexts._internal_.RtmAttribute.html
│ ├── React_Contexts._internal_.RtmChannelAttribute.html
│ ├── React_Contexts._internal_.RtmEngine.html
│ └── React_Contexts._internal_.RtmMessage.html
├── enums
│ ├── Agora_UIKit.ChannelProfileType.html
│ ├── Agora_UIKit.ClientRoleType.html
│ ├── Agora_UIKit.DualStreamMode.html
│ ├── Agora_UIKit.Layout.html
│ ├── Agora_UIKit.RenderModeType.html
│ ├── Agora_UIKit.ToggleState.html
│ ├── Agora_UIKit._internal_.ConnectionChangeReason.html
│ ├── Agora_UIKit._internal_.LocalInvitationError.html
│ ├── Agora_UIKit._internal_.LocalInvitationState.html
│ ├── Agora_UIKit._internal_.PeerOnlineState.html
│ ├── Agora_UIKit._internal_.RemoteAudioState.html
│ ├── Agora_UIKit._internal_.RemoteAudioStateReason.html
│ ├── Agora_UIKit._internal_.RemoteInvitationError.html
│ ├── Agora_UIKit._internal_.RemoteInvitationState.html
│ ├── Agora_UIKit._internal_.RemoteVideoState.html
│ ├── Agora_UIKit._internal_.RemoteVideoStateReason.html
│ ├── Agora_UIKit._internal_.RtmConnectionState.html
│ ├── Agora_UIKit._internal_.UserOfflineReasonType.html
│ ├── React_Contexts._internal_.ConnectionChangeReason.html
│ ├── React_Contexts._internal_.LocalInvitationError.html
│ ├── React_Contexts._internal_.LocalInvitationState.html
│ ├── React_Contexts._internal_.LogLevel.html
│ ├── React_Contexts._internal_.PeerOnlineState.html
│ ├── React_Contexts._internal_.RemoteInvitationError.html
│ ├── React_Contexts._internal_.RemoteInvitationState.html
│ ├── React_Contexts._internal_.RtmConnectionState.html
│ ├── React_Contexts._internal_.clientRoleRaw.html
│ ├── React_Contexts._internal_.mutingDevice.html
│ ├── React_Contexts._internal_.popUpStateEnum.html
│ └── React_Contexts._internal_.rtmStatusEnum.html
├── index.html
├── interfaces
│ ├── Agora_UIKit.AgoraUIKitProps.html
│ ├── Agora_UIKit.ConnectionData.html
│ ├── Agora_UIKit.IconsInterface.html
│ ├── Agora_UIKit.Settings.html
│ ├── Agora_UIKit.StylePropInterface.html
│ ├── Agora_UIKit.UidInterface.html
│ ├── Agora_UIKit._internal_.CallbacksInterface.html
│ ├── Agora_UIKit._internal_.ComponentLifecycle.html
│ ├── Agora_UIKit._internal_.ConcatArray.html
│ ├── Agora_UIKit._internal_.ConsumerProps.html
│ ├── Agora_UIKit._internal_.Context.html
│ ├── Agora_UIKit._internal_.DeprecatedLifecycle.html
│ ├── Agora_UIKit._internal_.Element.html
│ ├── Agora_UIKit._internal_.Error.html
│ ├── Agora_UIKit._internal_.ErrorInfo.html
│ ├── Agora_UIKit._internal_.ExoticComponent.html
│ ├── Agora_UIKit._internal_.FlexStyle.html
│ ├── Agora_UIKit._internal_.FunctionComponent.html
│ ├── Agora_UIKit._internal_.Iterable.html
│ ├── Agora_UIKit._internal_.IterableIterator.html
│ ├── Agora_UIKit._internal_.Iterator.html
│ ├── Agora_UIKit._internal_.IteratorReturnResult.html
│ ├── Agora_UIKit._internal_.IteratorYieldResult.html
│ ├── Agora_UIKit._internal_.LocalInvitation.html
│ ├── Agora_UIKit._internal_.MatrixTransform.html
│ ├── Agora_UIKit._internal_.NewLifecycle.html
│ ├── Agora_UIKit._internal_.PeersOnlineStatus.html
│ ├── Agora_UIKit._internal_.PerpectiveTransform.html
│ ├── Agora_UIKit._internal_.ProviderExoticComponent.html
│ ├── Agora_UIKit._internal_.ProviderProps.html
│ ├── Agora_UIKit._internal_.ReactElement.html
│ ├── Agora_UIKit._internal_.ReactPortal.html
│ ├── Agora_UIKit._internal_.RecursiveArray.html
│ ├── Agora_UIKit._internal_.RemoteInvitation.html
│ ├── Agora_UIKit._internal_.RotateTransform.html
│ ├── Agora_UIKit._internal_.RotateXTransform.html
│ ├── Agora_UIKit._internal_.RotateYTransform.html
│ ├── Agora_UIKit._internal_.RotateZTransform.html
│ ├── Agora_UIKit._internal_.RtcConnectionData.html
│ ├── Agora_UIKit._internal_.RtcSettings.html
│ ├── Agora_UIKit._internal_.RtmChannelMember.html
│ ├── Agora_UIKit._internal_.RtmClientEvents.html
│ ├── Agora_UIKit._internal_.RtmConnectionData.html
│ ├── Agora_UIKit._internal_.RtmSettings.html
│ ├── Agora_UIKit._internal_.ScaleTransform.html
│ ├── Agora_UIKit._internal_.ScaleXTransform.html
│ ├── Agora_UIKit._internal_.ScaleYTransform.html
│ ├── Agora_UIKit._internal_.ShadowStyleIOS.html
│ ├── Agora_UIKit._internal_.SkewXTransform.html
│ ├── Agora_UIKit._internal_.SkewYTransform.html
│ ├── Agora_UIKit._internal_.TextStyle.html
│ ├── Agora_UIKit._internal_.TextStyleAndroid.html
│ ├── Agora_UIKit._internal_.TextStyleIOS.html
│ ├── Agora_UIKit._internal_.TransformsStyle.html
│ ├── Agora_UIKit._internal_.TranslateXTransform.html
│ ├── Agora_UIKit._internal_.TranslateYTransform.html
│ ├── Agora_UIKit._internal_.Validator.html
│ ├── Agora_UIKit._internal_.ViewStyle.html
│ ├── Agora_UIKit._internal_.localBtnStylesInterface.html
│ ├── Agora_UIKit._internal_.remoteBtnStylesInterface.html
│ ├── Built_in_Components._internal_.AbstractView.html
│ ├── Built_in_Components._internal_.AnchorHTMLAttributes.html
│ ├── Built_in_Components._internal_.AnimationEvent-1.html
│ ├── Built_in_Components._internal_.AnimationEvent.html
│ ├── Built_in_Components._internal_.AreaHTMLAttributes.html
│ ├── Built_in_Components._internal_.AriaAttributes.html
│ ├── Built_in_Components._internal_.Attributes.html
│ ├── Built_in_Components._internal_.AudioHTMLAttributes.html
│ ├── Built_in_Components._internal_.BaseHTMLAttributes.html
│ ├── Built_in_Components._internal_.BaseInterface.html
│ ├── Built_in_Components._internal_.BaseInterfaceWithIcon.html
│ ├── Built_in_Components._internal_.BaseInterfaceWithName.html
│ ├── Built_in_Components._internal_.BaseSyntheticEvent.html
│ ├── Built_in_Components._internal_.BlockquoteHTMLAttributes.html
│ ├── Built_in_Components._internal_.BtnTemplateBasicInterface.html
│ ├── Built_in_Components._internal_.BtnTemplateInterfaceWithIcon.html
│ ├── Built_in_Components._internal_.BtnTemplateInterfaceWithName.html
│ ├── Built_in_Components._internal_.ButtonHTMLAttributes.html
│ ├── Built_in_Components._internal_.CSSProperties.html
│ ├── Built_in_Components._internal_.CanvasHTMLAttributes.html
│ ├── Built_in_Components._internal_.ChangeEvent.html
│ ├── Built_in_Components._internal_.ClassAttributes.html
│ ├── Built_in_Components._internal_.ClipboardEvent-1.html
│ ├── Built_in_Components._internal_.ClipboardEvent.html
│ ├── Built_in_Components._internal_.ColHTMLAttributes.html
│ ├── Built_in_Components._internal_.ColgroupHTMLAttributes.html
│ ├── Built_in_Components._internal_.ComponentClass.html
│ ├── Built_in_Components._internal_.CompositionEvent-1.html
│ ├── Built_in_Components._internal_.CompositionEvent.html
│ ├── Built_in_Components._internal_.ControlsPropsInterface.html
│ ├── Built_in_Components._internal_.DOMAttributes.html
│ ├── Built_in_Components._internal_.DataHTMLAttributes.html
│ ├── Built_in_Components._internal_.DataTransfer.html
│ ├── Built_in_Components._internal_.DelHTMLAttributes.html
│ ├── Built_in_Components._internal_.DetailsHTMLAttributes.html
│ ├── Built_in_Components._internal_.DialogHTMLAttributes.html
│ ├── Built_in_Components._internal_.Document.html
│ ├── Built_in_Components._internal_.DragEvent-1.html
│ ├── Built_in_Components._internal_.DragEvent.html
│ ├── Built_in_Components._internal_.EmbedHTMLAttributes.html
│ ├── Built_in_Components._internal_.Event.html
│ ├── Built_in_Components._internal_.EventTarget.html
│ ├── Built_in_Components._internal_.FieldsetHTMLAttributes.html
│ ├── Built_in_Components._internal_.FocusEvent-1.html
│ ├── Built_in_Components._internal_.FocusEvent.html
│ ├── Built_in_Components._internal_.FormEvent.html
│ ├── Built_in_Components._internal_.FormHTMLAttributes.html
│ ├── Built_in_Components._internal_.ForwardRefExoticComponent.html
│ ├── Built_in_Components._internal_.GestureResponderEvent.html
│ ├── Built_in_Components._internal_.HTMLAnchorElement.html
│ ├── Built_in_Components._internal_.HTMLAreaElement.html
│ ├── Built_in_Components._internal_.HTMLAttributes.html
│ ├── Built_in_Components._internal_.HTMLAudioElement.html
│ ├── Built_in_Components._internal_.HTMLBRElement.html
│ ├── Built_in_Components._internal_.HTMLBaseElement.html
│ ├── Built_in_Components._internal_.HTMLBodyElement.html
│ ├── Built_in_Components._internal_.HTMLButtonElement.html
│ ├── Built_in_Components._internal_.HTMLCanvasElement.html
│ ├── Built_in_Components._internal_.HTMLDListElement.html
│ ├── Built_in_Components._internal_.HTMLDataElement.html
│ ├── Built_in_Components._internal_.HTMLDataListElement.html
│ ├── Built_in_Components._internal_.HTMLDetailsElement.html
│ ├── Built_in_Components._internal_.HTMLDialogElement.html
│ ├── Built_in_Components._internal_.HTMLDivElement.html
│ ├── Built_in_Components._internal_.HTMLElement.html
│ ├── Built_in_Components._internal_.HTMLEmbedElement.html
│ ├── Built_in_Components._internal_.HTMLFieldSetElement.html
│ ├── Built_in_Components._internal_.HTMLFormElement.html
│ ├── Built_in_Components._internal_.HTMLHRElement.html
│ ├── Built_in_Components._internal_.HTMLHeadElement.html
│ ├── Built_in_Components._internal_.HTMLHeadingElement.html
│ ├── Built_in_Components._internal_.HTMLHtmlElement.html
│ ├── Built_in_Components._internal_.HTMLIFrameElement.html
│ ├── Built_in_Components._internal_.HTMLImageElement.html
│ ├── Built_in_Components._internal_.HTMLInputElement.html
│ ├── Built_in_Components._internal_.HTMLLIElement.html
│ ├── Built_in_Components._internal_.HTMLLabelElement.html
│ ├── Built_in_Components._internal_.HTMLLegendElement.html
│ ├── Built_in_Components._internal_.HTMLLinkElement.html
│ ├── Built_in_Components._internal_.HTMLMapElement.html
│ ├── Built_in_Components._internal_.HTMLMetaElement.html
│ ├── Built_in_Components._internal_.HTMLMeterElement.html
│ ├── Built_in_Components._internal_.HTMLModElement.html
│ ├── Built_in_Components._internal_.HTMLOListElement.html
│ ├── Built_in_Components._internal_.HTMLObjectElement.html
│ ├── Built_in_Components._internal_.HTMLOptGroupElement.html
│ ├── Built_in_Components._internal_.HTMLOptionElement.html
│ ├── Built_in_Components._internal_.HTMLOutputElement.html
│ ├── Built_in_Components._internal_.HTMLParagraphElement.html
│ ├── Built_in_Components._internal_.HTMLParamElement.html
│ ├── Built_in_Components._internal_.HTMLPreElement.html
│ ├── Built_in_Components._internal_.HTMLProgressElement.html
│ ├── Built_in_Components._internal_.HTMLQuoteElement.html
│ ├── Built_in_Components._internal_.HTMLScriptElement.html
│ ├── Built_in_Components._internal_.HTMLSelectElement.html
│ ├── Built_in_Components._internal_.HTMLSlotElement.html
│ ├── Built_in_Components._internal_.HTMLSourceElement.html
│ ├── Built_in_Components._internal_.HTMLSpanElement.html
│ ├── Built_in_Components._internal_.HTMLStyleElement.html
│ ├── Built_in_Components._internal_.HTMLTableColElement.html
│ ├── Built_in_Components._internal_.HTMLTableDataCellElement.html
│ ├── Built_in_Components._internal_.HTMLTableElement.html
│ ├── Built_in_Components._internal_.HTMLTableHeaderCellElement.html
│ ├── Built_in_Components._internal_.HTMLTableRowElement.html
│ ├── Built_in_Components._internal_.HTMLTableSectionElement.html
│ ├── Built_in_Components._internal_.HTMLTemplateElement.html
│ ├── Built_in_Components._internal_.HTMLTextAreaElement.html
│ ├── Built_in_Components._internal_.HTMLTimeElement.html
│ ├── Built_in_Components._internal_.HTMLTitleElement.html
│ ├── Built_in_Components._internal_.HTMLTrackElement.html
│ ├── Built_in_Components._internal_.HTMLUListElement.html
│ ├── Built_in_Components._internal_.HTMLVideoElement.html
│ ├── Built_in_Components._internal_.HTMLWebViewElement.html
│ ├── Built_in_Components._internal_.HostComponent.html
│ ├── Built_in_Components._internal_.HtmlHTMLAttributes.html
│ ├── Built_in_Components._internal_.IframeHTMLAttributes.html
│ ├── Built_in_Components._internal_.ImgHTMLAttributes.html
│ ├── Built_in_Components._internal_.InputHTMLAttributes.html
│ ├── Built_in_Components._internal_.InsHTMLAttributes.html
│ ├── Built_in_Components._internal_.IntrinsicElements.html
│ ├── Built_in_Components._internal_.KeyboardEvent-1.html
│ ├── Built_in_Components._internal_.KeyboardEvent.html
│ ├── Built_in_Components._internal_.KeygenHTMLAttributes.html
│ ├── Built_in_Components._internal_.LabelHTMLAttributes.html
│ ├── Built_in_Components._internal_.LiHTMLAttributes.html
│ ├── Built_in_Components._internal_.LinkHTMLAttributes.html
│ ├── Built_in_Components._internal_.LocalAudioMuteProps.html
│ ├── Built_in_Components._internal_.LocalUserContextInterface.html
│ ├── Built_in_Components._internal_.LocalVideoMuteProps.html
│ ├── Built_in_Components._internal_.MapHTMLAttributes.html
│ ├── Built_in_Components._internal_.MaxViewInterface.html
│ ├── Built_in_Components._internal_.MediaHTMLAttributes.html
│ ├── Built_in_Components._internal_.MenuHTMLAttributes.html
│ ├── Built_in_Components._internal_.MetaHTMLAttributes.html
│ ├── Built_in_Components._internal_.MeterHTMLAttributes.html
│ ├── Built_in_Components._internal_.MinViewInterface.html
│ ├── Built_in_Components._internal_.MouseEvent-1.html
│ ├── Built_in_Components._internal_.MouseEvent.html
│ ├── Built_in_Components._internal_.MutableRefObject.html
│ ├── Built_in_Components._internal_.NamedExoticComponent.html
│ ├── Built_in_Components._internal_.NativeMethods.html
│ ├── Built_in_Components._internal_.NativeSyntheticEvent.html
│ ├── Built_in_Components._internal_.NativeTouchEvent.html
│ ├── Built_in_Components._internal_.ObjectHTMLAttributes.html
│ ├── Built_in_Components._internal_.ObsoleteProperties.html
│ ├── Built_in_Components._internal_.OlHTMLAttributes.html
│ ├── Built_in_Components._internal_.OptgroupHTMLAttributes.html
│ ├── Built_in_Components._internal_.OptionHTMLAttributes.html
│ ├── Built_in_Components._internal_.OutputHTMLAttributes.html
│ ├── Built_in_Components._internal_.ParamHTMLAttributes.html
│ ├── Built_in_Components._internal_.PointerEvent-1.html
│ ├── Built_in_Components._internal_.PointerEvent.html
│ ├── Built_in_Components._internal_.ProgressHTMLAttributes.html
│ ├── Built_in_Components._internal_.Properties.html
│ ├── Built_in_Components._internal_.QuoteHTMLAttributes.html
│ ├── Built_in_Components._internal_.RefAttributes.html
│ ├── Built_in_Components._internal_.RefObject.html
│ ├── Built_in_Components._internal_.RemoteAudioMuteInterface.html
│ ├── Built_in_Components._internal_.RemoteControlsInterface.html
│ ├── Built_in_Components._internal_.RemoteSwapInterface.html
│ ├── Built_in_Components._internal_.RemoteVideoMuteInterface.html
│ ├── Built_in_Components._internal_.RtcPropsInterface.html
│ ├── Built_in_Components._internal_.SVGAttributes.html
│ ├── Built_in_Components._internal_.SVGCircleElement.html
│ ├── Built_in_Components._internal_.SVGClipPathElement.html
│ ├── Built_in_Components._internal_.SVGDefsElement.html
│ ├── Built_in_Components._internal_.SVGDescElement.html
│ ├── Built_in_Components._internal_.SVGElement.html
│ ├── Built_in_Components._internal_.SVGEllipseElement.html
│ ├── Built_in_Components._internal_.SVGFEBlendElement.html
│ ├── Built_in_Components._internal_.SVGFEColorMatrixElement.html
│ ├── Built_in_Components._internal_.SVGFEComponentTransferElement.html
│ ├── Built_in_Components._internal_.SVGFECompositeElement.html
│ ├── Built_in_Components._internal_.SVGFEConvolveMatrixElement.html
│ ├── Built_in_Components._internal_.SVGFEDiffuseLightingElement.html
│ ├── Built_in_Components._internal_.SVGFEDisplacementMapElement.html
│ ├── Built_in_Components._internal_.SVGFEDistantLightElement.html
│ ├── Built_in_Components._internal_.SVGFEDropShadowElement.html
│ ├── Built_in_Components._internal_.SVGFEFloodElement.html
│ ├── Built_in_Components._internal_.SVGFEFuncAElement.html
│ ├── Built_in_Components._internal_.SVGFEFuncBElement.html
│ ├── Built_in_Components._internal_.SVGFEFuncGElement.html
│ ├── Built_in_Components._internal_.SVGFEFuncRElement.html
│ ├── Built_in_Components._internal_.SVGFEGaussianBlurElement.html
│ ├── Built_in_Components._internal_.SVGFEImageElement.html
│ ├── Built_in_Components._internal_.SVGFEMergeElement.html
│ ├── Built_in_Components._internal_.SVGFEMergeNodeElement.html
│ ├── Built_in_Components._internal_.SVGFEMorphologyElement.html
│ ├── Built_in_Components._internal_.SVGFEOffsetElement.html
│ ├── Built_in_Components._internal_.SVGFEPointLightElement.html
│ ├── Built_in_Components._internal_.SVGFESpecularLightingElement.html
│ ├── Built_in_Components._internal_.SVGFESpotLightElement.html
│ ├── Built_in_Components._internal_.SVGFETileElement.html
│ ├── Built_in_Components._internal_.SVGFETurbulenceElement.html
│ ├── Built_in_Components._internal_.SVGFilterElement.html
│ ├── Built_in_Components._internal_.SVGForeignObjectElement.html
│ ├── Built_in_Components._internal_.SVGGElement.html
│ ├── Built_in_Components._internal_.SVGImageElement.html
│ ├── Built_in_Components._internal_.SVGLineElement.html
│ ├── Built_in_Components._internal_.SVGLinearGradientElement.html
│ ├── Built_in_Components._internal_.SVGMarkerElement.html
│ ├── Built_in_Components._internal_.SVGMaskElement.html
│ ├── Built_in_Components._internal_.SVGMetadataElement.html
│ ├── Built_in_Components._internal_.SVGPathElement.html
│ ├── Built_in_Components._internal_.SVGPatternElement.html
│ ├── Built_in_Components._internal_.SVGPolygonElement.html
│ ├── Built_in_Components._internal_.SVGPolylineElement.html
│ ├── Built_in_Components._internal_.SVGProps.html
│ ├── Built_in_Components._internal_.SVGRadialGradientElement.html
│ ├── Built_in_Components._internal_.SVGRectElement.html
│ ├── Built_in_Components._internal_.SVGSVGElement.html
│ ├── Built_in_Components._internal_.SVGStopElement.html
│ ├── Built_in_Components._internal_.SVGSwitchElement.html
│ ├── Built_in_Components._internal_.SVGSymbolElement.html
│ ├── Built_in_Components._internal_.SVGTSpanElement.html
│ ├── Built_in_Components._internal_.SVGTextElement.html
│ ├── Built_in_Components._internal_.SVGTextPathElement.html
│ ├── Built_in_Components._internal_.SVGUseElement.html
│ ├── Built_in_Components._internal_.SVGViewElement.html
│ ├── Built_in_Components._internal_.ScriptHTMLAttributes.html
│ ├── Built_in_Components._internal_.SelectHTMLAttributes.html
│ ├── Built_in_Components._internal_.SlotHTMLAttributes.html
│ ├── Built_in_Components._internal_.SourceHTMLAttributes.html
│ ├── Built_in_Components._internal_.StandardLonghandProperties.html
│ ├── Built_in_Components._internal_.StandardProperties.html
│ ├── Built_in_Components._internal_.StandardShorthandProperties.html
│ ├── Built_in_Components._internal_.StaticLifecycle.html
│ ├── Built_in_Components._internal_.StyleHTMLAttributes.html
│ ├── Built_in_Components._internal_.StyleMedia.html
│ ├── Built_in_Components._internal_.SvgProperties.html
│ ├── Built_in_Components._internal_.SyntheticEvent.html
│ ├── Built_in_Components._internal_.TableHTMLAttributes.html
│ ├── Built_in_Components._internal_.TdHTMLAttributes.html
│ ├── Built_in_Components._internal_.TextareaHTMLAttributes.html
│ ├── Built_in_Components._internal_.ThHTMLAttributes.html
│ ├── Built_in_Components._internal_.TimeHTMLAttributes.html
│ ├── Built_in_Components._internal_.Touch.html
│ ├── Built_in_Components._internal_.TouchEvent-1.html
│ ├── Built_in_Components._internal_.TouchEvent.html
│ ├── Built_in_Components._internal_.TouchList.html
│ ├── Built_in_Components._internal_.TrackHTMLAttributes.html
│ ├── Built_in_Components._internal_.TransitionEvent-1.html
│ ├── Built_in_Components._internal_.TransitionEvent.html
│ ├── Built_in_Components._internal_.UIEvent-1.html
│ ├── Built_in_Components._internal_.UIEvent.html
│ ├── Built_in_Components._internal_.UidStateInterface.html
│ ├── Built_in_Components._internal_.VendorLonghandProperties.html
│ ├── Built_in_Components._internal_.VendorProperties.html
│ ├── Built_in_Components._internal_.VendorShorthandProperties.html
│ ├── Built_in_Components._internal_.VideoHTMLAttributes.html
│ ├── Built_in_Components._internal_.WebViewHTMLAttributes.html
│ ├── Built_in_Components._internal_.WheelEvent-1.html
│ ├── Built_in_Components._internal_.WheelEvent.html
│ ├── React_Contexts._internal_.AgoraPeerMessage.html
│ ├── React_Contexts._internal_.ChannelAttributeOptions.html
│ ├── React_Contexts._internal_.ConnectionState.html
│ ├── React_Contexts._internal_.EmitterSubscription.html
│ ├── React_Contexts._internal_.EventSubscription.html
│ ├── React_Contexts._internal_.ListPeerStatus.html
│ ├── React_Contexts._internal_.LocalInvitation.html
│ ├── React_Contexts._internal_.LocalInvitationProps.html
│ ├── React_Contexts._internal_.MemberInfo.html
│ ├── React_Contexts._internal_.MemberStatus.html
│ ├── React_Contexts._internal_.Members.html
│ ├── React_Contexts._internal_.Object.html
│ ├── React_Contexts._internal_.PeersOnlineStatus.html
│ ├── React_Contexts._internal_.PropsInterface.html
│ ├── React_Contexts._internal_.RTMChannelMessage.html
│ ├── React_Contexts._internal_.RTMLocalInvitationErrorMessage.html
│ ├── React_Contexts._internal_.RTMLocalInvitationMessage.html
│ ├── React_Contexts._internal_.RTMMemberInfo.html
│ ├── React_Contexts._internal_.RTMPeerMessage.html
│ ├── React_Contexts._internal_.RTMRemoteInvitationErrorMessage.html
│ ├── React_Contexts._internal_.RTMRemoteInvitationMessage.html
│ ├── React_Contexts._internal_.RemoteInvitation.html
│ ├── React_Contexts._internal_.RemoteInvitationProps.html
│ ├── React_Contexts._internal_.RtcContextInterface.html
│ ├── React_Contexts._internal_.RtmChannelMember.html
│ ├── React_Contexts._internal_.RtmClientEvents.html
│ ├── React_Contexts._internal_.RtmEngineEvents.html
│ ├── React_Contexts._internal_.RtmPropsInterface.html
│ ├── React_Contexts._internal_.SendMessageOptions.html
│ ├── React_Contexts._internal_.UserAttribute.html
│ ├── React_Contexts._internal_.UserInfo.html
│ ├── React_Contexts._internal_.UserProfile.html
│ └── React_Contexts._internal_.rtmContext.html
├── modules.html
└── modules
│ ├── Agora_UIKit._internal_.html
│ ├── Agora_UIKit.html
│ ├── Built_in_Components._internal_.html
│ ├── Built_in_Components.html
│ ├── React_Contexts._internal_.html
│ └── React_Contexts.html
├── index.ts
├── package-lock.json
├── package.json
├── src
├── AgoraUIKit.tsx
├── Contexts
│ ├── LocalUserContext.tsx
│ ├── MaxUidContext.tsx
│ ├── MinUidContext.tsx
│ ├── PropsContext.tsx
│ ├── RtcContext.tsx
│ └── RtmContext.tsx
├── Controls
│ ├── BtnTemplate.tsx
│ ├── Icons.ts
│ ├── ImageIcon.tsx
│ ├── Local
│ │ ├── EndCall.tsx
│ │ ├── LocalAudioMute.tsx
│ │ ├── LocalVideoMute.tsx
│ │ └── SwitchCamera.tsx
│ ├── LocalControls.tsx
│ ├── Remote
│ │ ├── RemoteAudioMute.tsx
│ │ ├── RemoteMutePopUp.tsx
│ │ ├── RemoteSwap.tsx
│ │ └── RemoteVideoMute.tsx
│ ├── RemoteControls.tsx
│ └── types.ts
├── RTMEngine.tsx
├── Reducer
│ ├── BecomeAudience.ts
│ ├── LocalMuteAudio.ts
│ ├── LocalMuteVideo.ts
│ ├── RemoteAudioStateChanged.ts
│ ├── RemoteVideoStateChanged.ts
│ ├── UpdateDualStreamMode.ts
│ ├── UserJoined.ts
│ ├── UserMuteRemoteAudio.ts
│ ├── UserMuteRemoteVideo.ts
│ ├── UserOffline.ts
│ └── index.ts
├── Rtc
│ ├── Create.tsx
│ └── Join.tsx
├── RtcConfigure.tsx
├── RtmConfigure.tsx
├── Style.ts
├── Utils
│ ├── actionTypeGuard.tsx
│ ├── isSafariBrowser.ts
│ ├── permission.ts
│ └── quality.tsx
├── Views
│ ├── GridVideo.tsx
│ ├── MaxVideoView.native.tsx
│ ├── MaxVideoView.tsx
│ ├── MinVideoView.tsx
│ ├── PinnedVideo.tsx
│ └── Usernames.tsx
└── hooks
│ └── useImageDelay.tsx
└── tsconfig.json
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: '@react-native-community',
4 | parser: '@typescript-eslint/parser',
5 | plugins: ['@typescript-eslint'],
6 | overrides: [
7 | {
8 | files: ['*.ts', '*.tsx'],
9 | rules: {
10 | '@typescript-eslint/no-shadow': ['error'],
11 | 'react-native/no-inline-styles': 'off',
12 | 'no-shadow': 'off',
13 | 'no-undef': 'off',
14 | },
15 | },
16 | ],
17 | };
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Issue report
3 | about: Report any issues while trying to use the UIKit
4 | title: ''
5 | labels: ''
6 | ---
7 |
8 | **Target device:** (Note: simulators are not supported for running the UIKit)
9 | - Device: [e.g. iPhone6]
10 | - OS:
11 | - [X] IOS
12 | - [ ] Android
13 | - OS Version: [e.g. 22, 14.1]
14 |
15 | **App Info**
16 | - Type:
17 | - [X] React Native CLI
18 | - [ ] Expo CLI (using custom development clients)
19 | - CLI Version: [e.g. 22]
20 |
21 | **Describe the issue**
22 | A clear and concise description of what the issue is.
23 |
24 | **To Reproduce**
25 | Steps to reproduce the behavior:
26 | 1. Go to '...'
27 | 2. Click on '....'
28 | 3. See error
29 |
30 | **Screenshots**
31 | If applicable, add screenshots to help explain your problem.
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 |
24 | # Android/IntelliJ
25 | #
26 | build/
27 | .idea
28 | .gradle
29 | local.properties
30 | *.iml
31 |
32 | # Visual Studio Code
33 | #
34 | .vscode/
35 |
36 | # node.js
37 | #
38 | node_modules/
39 | npm-debug.log
40 | yarn-error.log
41 |
42 | # BUCK
43 | buck-out/
44 | \.buckd/
45 | *.keystore
46 | !debug.keystore
47 |
48 | # fastlane
49 | #
50 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
51 | # screenshots whenever they are needed.
52 | # For more information about the recommended setup visit:
53 | # https://docs.fastlane.tools/best-practices/source-control/
54 |
55 | */fastlane/report.xml
56 | */fastlane/Preview.html
57 | */fastlane/screenshots
58 |
59 | # Bundle artifact
60 | *.jsbundle
61 |
62 | # CocoaPods
63 | /ios/Pods/
64 |
65 | example
66 | notes.md
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | bracketSpacing: false,
3 | jsxBracketSameLine: true,
4 | singleQuote: true,
5 | trailingComma: 'all',
6 | };
7 |
--------------------------------------------------------------------------------
/Components.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @module Built-in Components
3 | */
4 | import Endcall from './src/Controls/Local/EndCall';
5 | import LocalAudioMute from './src/Controls/Local/LocalAudioMute';
6 | import LocalVideoMute from './src/Controls/Local/LocalVideoMute';
7 | import SwitchCamera from './src/Controls/Local/SwitchCamera';
8 | import LocalControls from './src/Controls/LocalControls';
9 | import RemoteAudioMute from './src/Controls/Remote/RemoteAudioMute';
10 | import RemoteSwap from './src/Controls/Remote/RemoteSwap';
11 | import RemoteVideoMute from './src/Controls/Remote/RemoteVideoMute';
12 | import RemoteControls from './src/Controls/RemoteControls';
13 | import MaxVideoView from './src/Views/MaxVideoView';
14 | import MinVideoView from './src/Views/MinVideoView';
15 | import RtcConfigure from './src/RtcConfigure';
16 | import RtmConfigure from './src/RtmConfigure';
17 | import LocalUserContext from './src/Contexts/LocalUserContext';
18 | import BtnTemplate from './src/Controls/BtnTemplate';
19 | import PinnedVideo from './src/Views/PinnedVideo';
20 | import GridVideo from './src/Views/GridVideo';
21 | import ImageIcon from './src/Controls/ImageIcon';
22 | import Create from './src/Rtc/Create';
23 | import Join from './src/Rtc/Join';
24 | import Username from './src/Views/Usernames';
25 | export {
26 | Endcall,
27 | LocalAudioMute,
28 | LocalVideoMute,
29 | SwitchCamera,
30 | LocalControls,
31 | RemoteAudioMute,
32 | RemoteSwap,
33 | RemoteVideoMute,
34 | RemoteControls,
35 | Create,
36 | Join,
37 | MaxVideoView,
38 | MinVideoView,
39 | RtcConfigure,
40 | RtmConfigure,
41 | LocalUserContext,
42 | BtnTemplate,
43 | PinnedVideo,
44 | GridVideo,
45 | Username,
46 | ImageIcon,
47 | };
48 |
--------------------------------------------------------------------------------
/Contexts.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @module React Contexts
3 | */
4 | import MaxUidContext from './src/Contexts/MaxUidContext';
5 | import MinUidContext from './src/Contexts/MinUidContext';
6 | import PropsContext from './src/Contexts/PropsContext';
7 | import RtcContext from './src/Contexts/RtcContext';
8 | import RtmContext from './src/Contexts/RtmContext';
9 | import {LocalContext} from './src/Contexts/LocalUserContext';
10 |
11 | export {
12 | MaxUidContext,
13 | MinUidContext,
14 | PropsContext,
15 | RtcContext,
16 | RtmContext,
17 | LocalContext,
18 | };
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Agora.io Community
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/UI Kit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-ReactNative/4f7b24ae23725058dd189973689b6db7a860ee36/UI Kit.png
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.
--------------------------------------------------------------------------------
/docs/UI Kit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-ReactNative/4f7b24ae23725058dd189973689b6db7a860ee36/docs/UI Kit.png
--------------------------------------------------------------------------------
/docs/assets/highlight.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --light-hl-0: #001080;
3 | --dark-hl-0: #9CDCFE;
4 | --light-hl-1: #000000;
5 | --dark-hl-1: #D4D4D4;
6 | --light-hl-2: #AF00DB;
7 | --dark-hl-2: #C586C0;
8 | --light-hl-3: #A31515;
9 | --dark-hl-3: #CE9178;
10 | --light-hl-4: #0000FF;
11 | --dark-hl-4: #569CD6;
12 | --light-hl-5: #795E26;
13 | --dark-hl-5: #DCDCAA;
14 | --light-hl-6: #0070C1;
15 | --dark-hl-6: #4FC1FF;
16 | --light-hl-7: #800000;
17 | --dark-hl-7: #808080;
18 | --light-hl-8: #267F99;
19 | --dark-hl-8: #4EC9B0;
20 | --light-hl-9: #FF0000;
21 | --dark-hl-9: #9CDCFE;
22 | --light-hl-10: #000000FF;
23 | --dark-hl-10: #D4D4D4;
24 | --light-hl-11: #008000;
25 | --dark-hl-11: #6A9955;
26 | --light-hl-12: #000000;
27 | --dark-hl-12: #C8C8C8;
28 | --light-hl-13: #098658;
29 | --dark-hl-13: #B5CEA8;
30 | --light-hl-14: #CD3131;
31 | --dark-hl-14: #F44747;
32 | --light-code-background: #F5F5F5;
33 | --dark-code-background: #1E1E1E;
34 | }
35 |
36 | @media (prefers-color-scheme: light) { :root {
37 | --hl-0: var(--light-hl-0);
38 | --hl-1: var(--light-hl-1);
39 | --hl-2: var(--light-hl-2);
40 | --hl-3: var(--light-hl-3);
41 | --hl-4: var(--light-hl-4);
42 | --hl-5: var(--light-hl-5);
43 | --hl-6: var(--light-hl-6);
44 | --hl-7: var(--light-hl-7);
45 | --hl-8: var(--light-hl-8);
46 | --hl-9: var(--light-hl-9);
47 | --hl-10: var(--light-hl-10);
48 | --hl-11: var(--light-hl-11);
49 | --hl-12: var(--light-hl-12);
50 | --hl-13: var(--light-hl-13);
51 | --hl-14: var(--light-hl-14);
52 | --code-background: var(--light-code-background);
53 | } }
54 |
55 | @media (prefers-color-scheme: dark) { :root {
56 | --hl-0: var(--dark-hl-0);
57 | --hl-1: var(--dark-hl-1);
58 | --hl-2: var(--dark-hl-2);
59 | --hl-3: var(--dark-hl-3);
60 | --hl-4: var(--dark-hl-4);
61 | --hl-5: var(--dark-hl-5);
62 | --hl-6: var(--dark-hl-6);
63 | --hl-7: var(--dark-hl-7);
64 | --hl-8: var(--dark-hl-8);
65 | --hl-9: var(--dark-hl-9);
66 | --hl-10: var(--dark-hl-10);
67 | --hl-11: var(--dark-hl-11);
68 | --hl-12: var(--dark-hl-12);
69 | --hl-13: var(--dark-hl-13);
70 | --hl-14: var(--dark-hl-14);
71 | --code-background: var(--dark-code-background);
72 | } }
73 |
74 | body.light {
75 | --hl-0: var(--light-hl-0);
76 | --hl-1: var(--light-hl-1);
77 | --hl-2: var(--light-hl-2);
78 | --hl-3: var(--light-hl-3);
79 | --hl-4: var(--light-hl-4);
80 | --hl-5: var(--light-hl-5);
81 | --hl-6: var(--light-hl-6);
82 | --hl-7: var(--light-hl-7);
83 | --hl-8: var(--light-hl-8);
84 | --hl-9: var(--light-hl-9);
85 | --hl-10: var(--light-hl-10);
86 | --hl-11: var(--light-hl-11);
87 | --hl-12: var(--light-hl-12);
88 | --hl-13: var(--light-hl-13);
89 | --hl-14: var(--light-hl-14);
90 | --code-background: var(--light-code-background);
91 | }
92 |
93 | body.dark {
94 | --hl-0: var(--dark-hl-0);
95 | --hl-1: var(--dark-hl-1);
96 | --hl-2: var(--dark-hl-2);
97 | --hl-3: var(--dark-hl-3);
98 | --hl-4: var(--dark-hl-4);
99 | --hl-5: var(--dark-hl-5);
100 | --hl-6: var(--dark-hl-6);
101 | --hl-7: var(--dark-hl-7);
102 | --hl-8: var(--dark-hl-8);
103 | --hl-9: var(--dark-hl-9);
104 | --hl-10: var(--dark-hl-10);
105 | --hl-11: var(--dark-hl-11);
106 | --hl-12: var(--dark-hl-12);
107 | --hl-13: var(--dark-hl-13);
108 | --hl-14: var(--dark-hl-14);
109 | --code-background: var(--dark-code-background);
110 | }
111 |
112 | .hl-0 { color: var(--hl-0); }
113 | .hl-1 { color: var(--hl-1); }
114 | .hl-2 { color: var(--hl-2); }
115 | .hl-3 { color: var(--hl-3); }
116 | .hl-4 { color: var(--hl-4); }
117 | .hl-5 { color: var(--hl-5); }
118 | .hl-6 { color: var(--hl-6); }
119 | .hl-7 { color: var(--hl-7); }
120 | .hl-8 { color: var(--hl-8); }
121 | .hl-9 { color: var(--hl-9); }
122 | .hl-10 { color: var(--hl-10); }
123 | .hl-11 { color: var(--hl-11); }
124 | .hl-12 { color: var(--hl-12); }
125 | .hl-13 { color: var(--hl-13); }
126 | .hl-14 { color: var(--hl-14); }
127 | pre, code { background: var(--code-background); }
128 |
--------------------------------------------------------------------------------
/docs/assets/icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-ReactNative/4f7b24ae23725058dd189973689b6db7a860ee36/docs/assets/icons.png
--------------------------------------------------------------------------------
/docs/assets/icons@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-ReactNative/4f7b24ae23725058dd189973689b6db7a860ee36/docs/assets/icons@2x.png
--------------------------------------------------------------------------------
/docs/assets/widgets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-ReactNative/4f7b24ae23725058dd189973689b6db7a860ee36/docs/assets/widgets.png
--------------------------------------------------------------------------------
/docs/assets/widgets@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO-Community/VideoUIKit-ReactNative/4f7b24ae23725058dd189973689b6db7a860ee36/docs/assets/widgets@2x.png
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.AnimationEvent-1.html:
--------------------------------------------------------------------------------
1 |
AnimationEvent | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.ClipboardEvent-1.html:
--------------------------------------------------------------------------------
1 | ClipboardEvent | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.DataTransfer.html:
--------------------------------------------------------------------------------
1 | DataTransfer | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.Document.html:
--------------------------------------------------------------------------------
1 | Document | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.DragEvent-1.html:
--------------------------------------------------------------------------------
1 | DragEvent | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.EventTarget.html:
--------------------------------------------------------------------------------
1 | EventTarget | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.FocusEvent-1.html:
--------------------------------------------------------------------------------
1 | FocusEvent | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.HTMLBRElement.html:
--------------------------------------------------------------------------------
1 | HTMLBRElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.HTMLDivElement.html:
--------------------------------------------------------------------------------
1 | HTMLDivElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.HTMLHRElement.html:
--------------------------------------------------------------------------------
1 | HTMLHRElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.HTMLLIElement.html:
--------------------------------------------------------------------------------
1 | HTMLLIElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.HTMLMapElement.html:
--------------------------------------------------------------------------------
1 | HTMLMapElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.HTMLModElement.html:
--------------------------------------------------------------------------------
1 | HTMLModElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.HTMLPreElement.html:
--------------------------------------------------------------------------------
1 | HTMLPreElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.KeyboardEvent-1.html:
--------------------------------------------------------------------------------
1 | KeyboardEvent | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.MouseEvent-1.html:
--------------------------------------------------------------------------------
1 | MouseEvent | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.PointerEvent-1.html:
--------------------------------------------------------------------------------
1 | PointerEvent | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.SVGDefsElement.html:
--------------------------------------------------------------------------------
1 | SVGDefsElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.SVGDescElement.html:
--------------------------------------------------------------------------------
1 | SVGDescElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.SVGGElement.html:
--------------------------------------------------------------------------------
1 | SVGGElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.SVGLineElement.html:
--------------------------------------------------------------------------------
1 | SVGLineElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.SVGMaskElement.html:
--------------------------------------------------------------------------------
1 | SVGMaskElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.SVGPathElement.html:
--------------------------------------------------------------------------------
1 | SVGPathElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.SVGRectElement.html:
--------------------------------------------------------------------------------
1 | SVGRectElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.SVGSVGElement.html:
--------------------------------------------------------------------------------
1 | SVGSVGElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.SVGStopElement.html:
--------------------------------------------------------------------------------
1 | SVGStopElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.SVGUseElement.html:
--------------------------------------------------------------------------------
1 | SVGUseElement | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.StyleMedia.html:
--------------------------------------------------------------------------------
1 | StyleMedia | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.TouchEvent-1.html:
--------------------------------------------------------------------------------
1 | TouchEvent | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.UIEvent-1.html:
--------------------------------------------------------------------------------
1 | UIEvent | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/interfaces/Built_in_Components._internal_.WheelEvent-1.html:
--------------------------------------------------------------------------------
1 | WheelEvent | agora-rn-uikit
--------------------------------------------------------------------------------
/docs/modules.html:
--------------------------------------------------------------------------------
1 | agora-rn-uikit
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @module Agora UIKit
3 | */
4 | import AgoraUIKit from './src/AgoraUIKit';
5 | export {
6 | StreamFallbackOptions,
7 | RenderModeType,
8 | ChannelProfileType,
9 | ClientRoleType,
10 | } from 'react-native-agora';
11 | export {Layout, DualStreamMode, ToggleState} from './src/Contexts/PropsContext';
12 | export {default as icons} from './src/Controls/Icons';
13 | export type {
14 | IconsInterface,
15 | UidInterface,
16 | AgoraUIKitProps,
17 | ConnectionData,
18 | Settings,
19 | StylePropInterface,
20 | rtcCallbacks,
21 | } from './src/Contexts/PropsContext';
22 | export type {rtmCallbacks} from './src/Contexts/RtmContext';
23 |
24 | export default AgoraUIKit;
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "agora-rn-uikit",
3 | "version": "5.0.2",
4 | "author": {
5 | "name": "Ekaansh Arora",
6 | "email": "ekaansh@agora.io"
7 | },
8 | "main": "./index.ts",
9 | "contributors": [
10 | {
11 | "name": "Vineeth S",
12 | "email": "vineeth@agora.io"
13 | }
14 | ],
15 | "license": "MIT",
16 | "files": [
17 | "Components.ts",
18 | "Contexts.ts",
19 | "index.ts",
20 | "/src/**/*"
21 | ],
22 | "description": "Agora react native UI KIT. Rapidly integrate video calling into your React Native application with built in UI Elements.",
23 | "repository": {
24 | "type": "git",
25 | "url": "https://github.com/AgoraIO-Community/ReactNative-UIKit"
26 | },
27 | "bugs": {
28 | "url": "https://github.com/AgoraIO-Community/ReactNative-UIKit/issues"
29 | },
30 | "peerDependencies": {
31 | "agora-react-native-rtm": "^1.5.1",
32 | "react": "*",
33 | "react-native": ">=0.62.2",
34 | "react-native-agora": "^4.1.0"
35 | },
36 | "scripts": {
37 | "docs": "typedoc --entryPoints index.ts --entryPoints Components.ts --entryPoints Contexts.ts && cp 'UI Kit.png' './docs/UI Kit.png'"
38 | },
39 | "devDependencies": {
40 | "@react-native-community/eslint-config": "^3.1.0",
41 | "@tsconfig/react-native": "^2.0.2",
42 | "@types/react": "^18.0.21",
43 | "@types/react-native": "^0.70.6",
44 | "@typescript-eslint/eslint-plugin": "^5.40.1",
45 | "@typescript-eslint/parser": "^5.40.1",
46 | "eslint": "^8.25.0",
47 | "typedoc": "^0.22.15",
48 | "typedoc-plugin-missing-exports": "^0.22.6",
49 | "typedoc-plugin-rename-defaults": "^0.6.4"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/AgoraUIKit.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @module AgoraUIKit
3 | */
4 | import React from 'react';
5 | import {View} from 'react-native';
6 | import RtcConfigure from './RtcConfigure';
7 | import {
8 | PropsProvider,
9 | PropsInterface,
10 | Layout,
11 | AgoraUIKitProps,
12 | } from './Contexts/PropsContext';
13 | import LocalControls from './Controls/LocalControls';
14 | import GridVideo from './Views/GridVideo';
15 | import PinnedVideo from './Views/PinnedVideo';
16 | import RtmConfigure from './RtmConfigure';
17 | import LocalUserContext from './Contexts/LocalUserContext';
18 | import PopUp from './Controls/Remote/RemoteMutePopUp';
19 |
20 | /**
21 | * Agora UIKit component following the v3 props
22 | * @returns Renders the UIKit
23 | */
24 | const AgoraUIKitv3: React.FC = (props) => {
25 | const {layout} = props.rtcProps;
26 | return (
27 |
28 |
29 |
30 |
31 | {props.rtcProps.disableRtm ? (
32 | <>
33 | {layout === Layout.Grid ? : }
34 |
35 | >
36 | ) : (
37 |
38 | {layout === Layout.Grid ? : }
39 |
40 |
41 |
42 | )}
43 |
44 |
45 |
46 |
47 | );
48 | };
49 |
50 | /**
51 | * Agora UIKit component
52 | * @returns Renders the UIKit
53 | */
54 | const AgoraUIKit: React.FC = (props) => {
55 | const {rtcUid, rtcToken, rtmToken, rtmUid, ...restConnectonData} =
56 | props.connectionData;
57 | const adaptedProps: PropsInterface = {
58 | rtcProps: {
59 | uid: rtcUid,
60 | token: rtcToken,
61 | ...restConnectonData,
62 | ...props.settings,
63 | callActive: true,
64 | },
65 | rtmProps: {
66 | token: rtmToken,
67 | uid: rtmUid,
68 | ...restConnectonData,
69 | ...props.settings,
70 | },
71 | };
72 |
73 | return (
74 |
81 | );
82 | };
83 |
84 | const containerStyle = {backgroundColor: '#000', flex: 1};
85 |
86 | export default AgoraUIKit;
87 |
--------------------------------------------------------------------------------
/src/Contexts/LocalUserContext.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext, createContext} from 'react';
2 | import MaxUidContext from './MaxUidContext';
3 | import MinUidContext from './MinUidContext';
4 | import {UidInterface} from './PropsContext';
5 |
6 | export const LocalContext = createContext({} as UidInterface);
7 | export const LocalProvider = LocalContext.Provider;
8 | export const LocalConsumer = LocalContext.Consumer;
9 |
10 | interface LocalUserContextInterface {
11 | children: React.ReactNode;
12 | }
13 |
14 | const LocalUserContext: React.FC = (props) => {
15 | const max = useContext(MaxUidContext);
16 | const min = useContext(MinUidContext);
17 | // if(min && min[0] && max )
18 | let localUser: UidInterface = max[0].uid === 'local' ? max[0] : min[0];
19 | return (
20 |
21 | {props.children}
22 |
23 | );
24 | };
25 |
26 | export default LocalUserContext;
27 |
--------------------------------------------------------------------------------
/src/Contexts/MaxUidContext.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {UidStateInterface} from './RtcContext';
3 |
4 | const MaxUidContext = React.createContext([]);
5 | export const MaxUidProvider = MaxUidContext.Provider;
6 | export const MaxUidConsumer = MaxUidContext.Consumer;
7 | export default MaxUidContext;
8 |
--------------------------------------------------------------------------------
/src/Contexts/MinUidContext.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {UidStateInterface} from './RtcContext';
3 |
4 | const MinUidContext = React.createContext([]);
5 |
6 | export const MinUidProvider = MinUidContext.Provider;
7 | export const MinUidConsumer = MinUidContext.Consumer;
8 | export default MinUidContext;
9 |
--------------------------------------------------------------------------------
/src/Contexts/RtcContext.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {CallbacksInterface} from './PropsContext';
3 | import {IRtcEngine} from 'react-native-agora';
4 | import type {DualStreamMode, UidInterface} from './PropsContext';
5 |
6 | export interface UidStateInterface {
7 | min: Array;
8 | max: Array;
9 | }
10 |
11 | export interface ActionInterface {
12 | type: T;
13 | value: Parameters;
14 | }
15 |
16 | export type DispatchType = <
17 | T extends keyof CallbacksInterface,
18 | V extends Parameters,
19 | >(action: {
20 | type: T;
21 | value: V;
22 | }) => void;
23 |
24 | export type ActionType = ActionInterface;
25 |
26 | export interface RtcContextInterface {
27 | RtcEngine: IRtcEngine;
28 | dispatch: DispatchType;
29 | rtcUidRef: React.MutableRefObject;
30 | rtcChannelJoined: boolean;
31 | setDualStreamMode: React.Dispatch>;
32 | }
33 |
34 | const RtcContext = React.createContext(
35 | {} as RtcContextInterface,
36 | );
37 |
38 | export const RtcProvider = RtcContext.Provider;
39 | export const RtcConsumer = RtcContext.Consumer;
40 | export default RtcContext;
41 |
--------------------------------------------------------------------------------
/src/Contexts/RtmContext.tsx:
--------------------------------------------------------------------------------
1 | import RtmClient from 'agora-react-native-rtm';
2 | import {RtmClientEvents} from 'agora-react-native-rtm/src/RtmEngine';
3 | import React, {createContext} from 'react';
4 |
5 | /**
6 | * Callbacks to pass to RTM events
7 | */
8 | export type rtmCallbacks = Partial;
9 |
10 | export enum rtmStatusEnum {
11 | // Initialisation failed
12 | initFailed,
13 | // Login has not been attempted
14 | offline,
15 | // RTM is initialising, process is not yet complete
16 | initialising,
17 | // Currently attempting to log in
18 | loggingIn,
19 | // RTM has logged in
20 | loggedIn,
21 | // RTM is logged in, and connected to the current channel
22 | connected,
23 | // RTM Login Failed
24 | loginFailed,
25 | }
26 |
27 | export type messageType = 'UserData' | 'MuteRequest';
28 |
29 | export type messageObjectType = userDataType | muteRequest | genericAction;
30 |
31 | export enum clientRoleRaw {
32 | broadcaster,
33 | audience,
34 | }
35 |
36 | export enum mutingDevice {
37 | camera,
38 | microphone,
39 | }
40 |
41 | export type genericAction = {
42 | messageType: 'RtmDataRequest';
43 | type: 'ping' | 'pong' | 'userData';
44 | };
45 |
46 | export type muteRequest = {
47 | messageType: 'MuteRequest';
48 | rtcId: number;
49 | mute: boolean;
50 | device: mutingDevice;
51 | isForceful: boolean;
52 | };
53 |
54 | export type userDataType = {
55 | messageType: 'UserData';
56 | rtmId: string;
57 | rtcId: number;
58 | username?: string;
59 | role: clientRoleRaw;
60 | uikit: {
61 | platform: string;
62 | framework: string;
63 | version: string;
64 | };
65 | agora: {
66 | rtm: string;
67 | rtc: string;
68 | };
69 | };
70 |
71 | export enum popUpStateEnum {
72 | closed,
73 | muteMic,
74 | muteCamera,
75 | unmuteMic,
76 | unmuteCamera,
77 | }
78 |
79 | type NewType = React.Dispatch>;
80 |
81 | /**
82 | * Interface for RTM Context
83 | */
84 | interface rtmContext {
85 | /**
86 | * rtm connection status
87 | */
88 | rtmStatus: rtmStatusEnum;
89 | /**
90 | * send message to everyone in the channel
91 | */
92 | sendChannelMessage: (msg: messageObjectType) => void;
93 | /**
94 | * send message to a specific user
95 | */
96 | sendPeerMessage: (msg: messageObjectType, uid: string) => void;
97 | /**
98 | * RTM Client instance
99 | */
100 | rtmClient: RtmClient;
101 | /**
102 | * map with userdata for each rtc uid in the channel
103 | */
104 | userDataMap: Object;
105 | /**
106 | * map with rtm uid for each rtc uid in the channel
107 | */
108 | uidMap: Record;
109 | /**
110 | * Send a mute request
111 | */
112 | sendMuteRequest: (device: mutingDevice, rtcId: number, mute: boolean) => void;
113 | /**
114 | * RTM usernames
115 | */
116 | usernames: Record;
117 | /**
118 | * state to display pop up on remote mute request
119 | */
120 | popUpState: popUpStateEnum;
121 | /**
122 | * set state to hide pop up
123 | */
124 | setPopUpState: NewType;
125 | }
126 | /**
127 | * Context to access RTM data. It's setup by {@link RtmConfigure}.
128 | */
129 | const RtmContext = createContext(null as unknown as rtmContext);
130 |
131 | export const RtmProvider = RtmContext.Provider;
132 | export const RtmConsumer = RtmContext.Consumer;
133 | export default RtmContext;
134 |
--------------------------------------------------------------------------------
/src/Controls/BtnTemplate.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import {
3 | TouchableOpacity,
4 | Image,
5 | StyleProp,
6 | TouchableOpacityProps,
7 | ViewStyle,
8 | Text,
9 | View,
10 | Platform,
11 | } from 'react-native';
12 | import PropsContext, {IconsInterface} from '../Contexts/PropsContext';
13 | import styles from '../Style';
14 | import icons from './Icons';
15 | import useImageDelay from '../hooks/useImageDelay';
16 | import {Either} from './types';
17 |
18 | interface BtnTemplateBasicInterface {
19 | color?: string;
20 | onPress?: TouchableOpacityProps['onPress'];
21 | style?: StyleProp;
22 | btnText?: string;
23 | disabled?: boolean;
24 | }
25 | interface BtnTemplateInterfaceWithName extends BtnTemplateBasicInterface {
26 | name?: keyof IconsInterface;
27 | }
28 | interface BtnTemplateInterfaceWithIcon extends BtnTemplateBasicInterface {
29 | icon?: any;
30 | }
31 | type BtnTemplateInterface = Either<
32 | BtnTemplateInterfaceWithIcon,
33 | BtnTemplateInterfaceWithName
34 | >;
35 |
36 | const BtnTemplate: React.FC = (props) => {
37 | const {disabled = false} = props;
38 | const {styleProps} = useContext(PropsContext);
39 | const {BtnTemplateStyles, theme, iconSize, customIcon} = styleProps || {};
40 |
41 | const imageRef = React.useRef(null);
42 |
43 | // This fixes the tint issue in safari browser
44 | useImageDelay(imageRef, 10, '', props?.color || '');
45 |
46 | return (
47 |
51 |
56 |
73 |
74 |
81 | {props.btnText}
82 |
83 |
84 | );
85 | };
86 |
87 | export default BtnTemplate;
88 |
--------------------------------------------------------------------------------
/src/Controls/ImageIcon.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | ********************************************
3 | Copyright © 2021 Agora Lab, Inc., all rights reserved.
4 | AppBuilder and all associated components, source code, APIs, services, and documentation
5 | (the “Materials”) are owned by Agora Lab, Inc. and its licensors. The Materials may not be
6 | accessed, used, modified, or distributed for any purpose without a license from Agora Lab, Inc.
7 | Use without a license or in violation of any license terms and conditions (including use for
8 | any purpose competitive to Agora Lab, Inc.’s business) is strictly prohibited. For more
9 | information visit https://appbuilder.agora.io.
10 | *********************************************
11 | */
12 | import React, {useContext} from 'react';
13 | import {Image, Platform, StyleProp, ViewStyle} from 'react-native';
14 | import icons from './Icons';
15 | import PropsContext, {IconsInterface} from './../Contexts/PropsContext';
16 | import useImageDelay from '../hooks/useImageDelay';
17 | import {Either} from './types';
18 |
19 | interface BaseInterface {
20 | color?: string;
21 | style?: StyleProp;
22 | }
23 |
24 | interface BaseInterfaceWithName extends BaseInterface {
25 | name?: keyof IconsInterface;
26 | }
27 | interface BaseInterfaceWithIcon extends BaseInterface {
28 | icon?: any;
29 | }
30 |
31 | type ImageIconInterface = Either;
32 |
33 | const ImageIcon: React.FC = (props) => {
34 | const {styleProps} = useContext(PropsContext);
35 | const {theme} = styleProps || {};
36 | const imageRef = React.useRef(null);
37 |
38 | useImageDelay(imageRef, 10, props?.name || '', props?.color);
39 |
40 | return (
41 |
54 | );
55 | };
56 |
57 | export default ImageIcon;
58 |
--------------------------------------------------------------------------------
/src/Controls/Local/EndCall.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import PropsContext from '../../Contexts/PropsContext';
3 | import RtcContext from '../../Contexts/RtcContext';
4 | import BtnTemplate from '../BtnTemplate';
5 | import styles from '../../Style';
6 |
7 | /**
8 | * React Component that renders the endcall button
9 | * @returns Renders the endcall button
10 | */
11 | const EndCall: React.FC = () => {
12 | const {styleProps} = useContext(PropsContext);
13 | const {localBtnStyles} = styleProps || {};
14 | const {endCall} = localBtnStyles || {};
15 | const {dispatch} = useContext(RtcContext);
16 |
17 | return (
18 |
24 | dispatch({
25 | type: 'EndCall',
26 | value: [],
27 | })
28 | }
29 | />
30 | );
31 | };
32 |
33 | export default EndCall;
34 |
--------------------------------------------------------------------------------
/src/Controls/Local/LocalAudioMute.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import PropsContext, {
3 | ToggleState,
4 | UidInterface,
5 | } from '../../Contexts/PropsContext';
6 | import RtcContext from '../../Contexts/RtcContext';
7 | import BtnTemplate from '../BtnTemplate';
8 | import styles from '../../Style';
9 | import {LocalContext} from '../../Contexts/LocalUserContext';
10 | import {DispatchType} from '../../Contexts/RtcContext';
11 | import {IRtcEngine} from 'react-native-agora';
12 | interface LocalAudioMuteProps {
13 | btnText?: string;
14 | variant?: 'outlined' | 'text';
15 | }
16 |
17 | const LocalAudioMute: React.FC = (props) => {
18 | const {btnText = 'Audio', variant = 'Outlined'} = props;
19 | const {styleProps} = useContext(PropsContext);
20 | const {localBtnStyles, remoteBtnStyles} = styleProps || {};
21 | const {muteLocalAudio} = localBtnStyles || {};
22 | const {muteRemoteAudio} = remoteBtnStyles || {};
23 | const {RtcEngine, dispatch} = useContext(RtcContext);
24 | const localUser = useContext(LocalContext);
25 |
26 | return (
27 | muteAudio(localUser, dispatch, RtcEngine)}
37 | />
38 | );
39 | };
40 |
41 | export const muteAudio = async (
42 | local: UidInterface,
43 | dispatch: DispatchType,
44 | RtcEngine: IRtcEngine,
45 | ) => {
46 | const localState = local.audio;
47 | // Don't do anything if it is in a transitional state
48 | if (
49 | localState === ToggleState.enabled ||
50 | localState === ToggleState.disabled
51 | ) {
52 | // Disable UI
53 | dispatch({
54 | type: 'LocalMuteAudio',
55 | value: [
56 | localState === ToggleState.enabled
57 | ? ToggleState.disabling
58 | : ToggleState.enabling,
59 | ],
60 | });
61 |
62 | try {
63 | await RtcEngine.muteLocalAudioStream(localState === ToggleState.enabled);
64 | // Enable UI
65 | dispatch({
66 | type: 'LocalMuteAudio',
67 | value: [
68 | localState === ToggleState.enabled
69 | ? ToggleState.disabled
70 | : ToggleState.enabled,
71 | ],
72 | });
73 | } catch (e) {
74 | console.error(e);
75 | dispatch({
76 | type: 'LocalMuteAudio',
77 | value: [localState],
78 | });
79 | }
80 | } else {
81 | console.log('LocalMuteAudio in transition', local, ToggleState);
82 | }
83 | };
84 |
85 | export default LocalAudioMute;
86 |
--------------------------------------------------------------------------------
/src/Controls/Local/LocalVideoMute.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import PropsContext, {
3 | ToggleState,
4 | UidInterface,
5 | } from '../../Contexts/PropsContext';
6 | import RtcContext from '../../Contexts/RtcContext';
7 | import BtnTemplate from '../BtnTemplate';
8 | import styles from '../../Style';
9 | import {LocalContext} from '../../Contexts/LocalUserContext';
10 | import {DispatchType} from '../../Contexts/RtcContext';
11 | import {IRtcEngine} from 'react-native-agora';
12 |
13 | interface LocalVideoMuteProps {
14 | btnText?: string;
15 | variant?: 'outlined' | 'text';
16 | }
17 |
18 | const LocalVideoMute: React.FC = (props) => {
19 | const {btnText = 'Video', variant = 'Outlined'} = props;
20 | const {styleProps} = useContext(PropsContext);
21 | const {localBtnStyles, remoteBtnStyles} = styleProps || {};
22 | const {muteLocalVideo} = localBtnStyles || {};
23 | const {muteRemoteVideo} = remoteBtnStyles || {};
24 | const {RtcEngine, dispatch} = useContext(RtcContext);
25 | const local = useContext(LocalContext);
26 |
27 | return (
28 | muteVideo(local, dispatch, RtcEngine)}
38 | />
39 | );
40 | };
41 |
42 | export const muteVideo = async (
43 | local: UidInterface,
44 | dispatch: DispatchType,
45 | RtcEngine: IRtcEngine,
46 | ) => {
47 | const localState = local.video;
48 | // Don't do anything if it is in a transitional state
49 | if (
50 | localState === ToggleState.enabled ||
51 | localState === ToggleState.disabled
52 | ) {
53 | // Disable UI
54 | dispatch({
55 | type: 'LocalMuteVideo',
56 | value: [
57 | localState === ToggleState.enabled
58 | ? ToggleState.disabling
59 | : ToggleState.enabling,
60 | ],
61 | });
62 |
63 | try {
64 | await RtcEngine.muteLocalVideoStream(localState === ToggleState.enabled);
65 | // Enable UI
66 | dispatch({
67 | type: 'LocalMuteVideo',
68 | value: [
69 | localState === ToggleState.enabled
70 | ? ToggleState.disabled
71 | : ToggleState.enabled,
72 | ],
73 | });
74 | } catch (e) {
75 | console.error(e);
76 | dispatch({
77 | type: 'LocalMuteVideo',
78 | value: [localState],
79 | });
80 | }
81 | } else {
82 | console.log('LocalMuteVideo in transition', local, ToggleState);
83 | }
84 | };
85 |
86 | export default LocalVideoMute;
87 |
--------------------------------------------------------------------------------
/src/Controls/Local/SwitchCamera.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import PropsContext, {ToggleState} from '../../Contexts/PropsContext';
3 | import {LocalContext} from '../../Contexts/LocalUserContext';
4 | import RtcContext from '../../Contexts/RtcContext';
5 | import BtnTemplate from '../BtnTemplate';
6 | import styles from '../../Style';
7 |
8 | const SwitchCamera: React.FC = () => {
9 | const {styleProps, callbacks} = useContext(PropsContext);
10 | const {localBtnStyles} = styleProps || {};
11 | const {switchCamera} = localBtnStyles || {};
12 | const {RtcEngine} = useContext(RtcContext);
13 | const local = useContext(LocalContext);
14 | return (
15 | {
21 | RtcEngine.switchCamera();
22 | callbacks?.SwitchCamera && callbacks.SwitchCamera();
23 | }}
24 | />
25 | );
26 | };
27 |
28 | export default SwitchCamera;
29 |
--------------------------------------------------------------------------------
/src/Controls/LocalControls.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import {View} from 'react-native';
3 | import styles from '../Style';
4 | import EndCall from './Local/EndCall';
5 | import LocalAudioMute from './Local/LocalAudioMute';
6 | import LocalVideoMute from './Local/LocalVideoMute';
7 | import SwitchCamera from './Local/SwitchCamera';
8 | import RemoteControls from './RemoteControls';
9 | import {MaxUidConsumer} from '../Contexts/MaxUidContext';
10 | import PropsContext, {Layout} from '../Contexts/PropsContext';
11 | import {ClientRoleType} from 'react-native-agora';
12 |
13 | interface ControlsPropsInterface {
14 | showButton?: boolean;
15 | }
16 |
17 | const Controls: React.FC = (props) => {
18 | const {styleProps, rtcProps} = useContext(PropsContext);
19 | const {localBtnContainer} = styleProps || {};
20 | const showButton = props.showButton !== undefined ? props.showButton : true;
21 | return (
22 | <>
23 |
24 | {rtcProps.role !== ClientRoleType.ClientRoleAudience && (
25 | <>
26 |
27 |
28 |
29 | >
30 | )}
31 |
32 |
33 | {showButton ? (
34 |
35 | {(users) => (
36 |
41 | {rtcProps.layout !== Layout.Grid && (
42 |
43 | )}
44 |
45 | )}
46 |
47 | ) : (
48 | <>>
49 | )}
50 | >
51 | );
52 | };
53 |
54 | export default Controls;
55 |
--------------------------------------------------------------------------------
/src/Controls/Remote/RemoteAudioMute.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import PropsContext, {
3 | ToggleState,
4 | UidInterface,
5 | } from '../../Contexts/PropsContext';
6 | import RtmContext, {mutingDevice} from '../../Contexts/RtmContext';
7 | import BtnTemplate from '../BtnTemplate';
8 | import styles from '../../Style';
9 |
10 | interface RemoteAudioMuteInterface {
11 | user: UidInterface;
12 | }
13 |
14 | const RemoteAudioMute: React.FC = (props) => {
15 | const {user} = props;
16 | const {sendMuteRequest, uidMap} = useContext(RtmContext || {});
17 | const {styleProps} = useContext(PropsContext);
18 | const {remoteBtnStyles} = styleProps || {};
19 | const {muteRemoteAudio} = remoteBtnStyles || {};
20 | const isMuted = user.audio === ToggleState.disabled;
21 |
22 | return user.uid !== 0 && uidMap[user.uid as number] ? (
23 | {
27 | sendMuteRequest(mutingDevice.microphone, user.uid as number, !isMuted);
28 | }}
29 | />
30 | ) : (
31 | <>>
32 | );
33 | };
34 |
35 | export default RemoteAudioMute;
36 |
--------------------------------------------------------------------------------
/src/Controls/Remote/RemoteMutePopUp.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import PropsContext, {ToggleState} from '../../Contexts/PropsContext';
3 | import RtmContext, {popUpStateEnum} from '../../Contexts/RtmContext';
4 | import RtcContext from '../../Contexts/RtcContext';
5 | import {muteVideo} from '../Local/LocalVideoMute';
6 | import {LocalContext} from '../../Contexts/LocalUserContext';
7 | import {muteAudio} from '../Local/LocalAudioMute';
8 | import {
9 | Dimensions,
10 | StyleSheet,
11 | Text,
12 | TouchableOpacity,
13 | View,
14 | } from 'react-native';
15 |
16 | function PopUp() {
17 | const {styleProps} = useContext(PropsContext);
18 | const {popUpState, setPopUpState} = useContext(RtmContext || {});
19 | const {popUpContainer} = styleProps || {};
20 | const {dispatch, RtcEngine} = useContext(RtcContext);
21 | const local = useContext(LocalContext);
22 |
23 | return popUpState !== popUpStateEnum.closed ? (
24 |
25 |
26 | {popUpState === popUpStateEnum.muteCamera ||
27 | popUpState === popUpStateEnum.muteMic
28 | ? 'Mute '
29 | : 'Unmute '}
30 | {popUpState === popUpStateEnum.muteCamera ||
31 | popUpState === popUpStateEnum.unmuteCamera
32 | ? 'Camera'
33 | : 'Mic'}
34 | ?
35 |
36 |
37 | {
39 | switch (popUpState) {
40 | case popUpStateEnum.muteCamera:
41 | local.video === ToggleState.enabled &&
42 | muteVideo(local, dispatch, RtcEngine);
43 | break;
44 | case popUpStateEnum.muteMic:
45 | local.audio === ToggleState.enabled &&
46 | muteAudio(local, dispatch, RtcEngine);
47 | break;
48 | case popUpStateEnum.unmuteCamera:
49 | local.video === ToggleState.disabled &&
50 | muteVideo(local, dispatch, RtcEngine);
51 | break;
52 | case popUpStateEnum.unmuteMic:
53 | local.audio === ToggleState.disabled &&
54 | muteAudio(local, dispatch, RtcEngine);
55 | break;
56 | }
57 | setPopUpState(popUpStateEnum.closed);
58 | }}
59 | style={styles.button}>
60 | Confirm
61 |
62 | setPopUpState(popUpStateEnum.closed)}>
65 | Close
66 |
67 |
68 |
69 | ) : null;
70 | }
71 |
72 | const styles = StyleSheet.create({
73 | button: {
74 | color: '#fff',
75 | borderWidth: 2,
76 | borderStyle: 'solid',
77 | borderColor: '#fff',
78 | padding: 2,
79 | borderRadius: 4,
80 | },
81 | buttonClose: {
82 | color: '#fff',
83 | borderWidth: 2,
84 | borderStyle: 'solid',
85 | borderColor: '#fff',
86 | padding: 2,
87 | borderRadius: 4,
88 | },
89 | container: {
90 | backgroundColor: '#007bffaa',
91 | position: 'absolute',
92 | width: 240,
93 | height: 80,
94 | top: Dimensions.get('screen').height / 2 - 80,
95 | left: Dimensions.get('screen').width / 2 - 120,
96 | zIndex: 100,
97 | display: 'flex',
98 | flexDirection: 'column',
99 | justifyContent: 'space-evenly',
100 | alignItems: 'center',
101 | },
102 | fontStyle: {color: '#fff', fontWeight: '700', fontSize: 18},
103 | buttonText: {color: '#fff', paddingHorizontal: 8},
104 | buttonHolder: {
105 | flexDirection: 'row',
106 | display: 'flex',
107 | width: '100%',
108 | justifyContent: 'space-around',
109 | },
110 | });
111 |
112 | export default PopUp;
113 |
--------------------------------------------------------------------------------
/src/Controls/Remote/RemoteSwap.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import RtcContext from '../../Contexts/RtcContext';
3 | import BtnTemplate from '../BtnTemplate';
4 | import styles from '../../Style';
5 | import PropsContext, {UidInterface} from '../../Contexts/PropsContext';
6 |
7 | interface RemoteSwapInterface {
8 | user: UidInterface;
9 | }
10 |
11 | const RemoteSwap: React.FC = (props) => {
12 | const {dispatch} = useContext(RtcContext);
13 | const {styleProps} = useContext(PropsContext);
14 | const {remoteBtnStyles, theme} = styleProps || {};
15 | const {remoteSwap} = remoteBtnStyles || {};
16 |
17 | return (
18 | {
32 | dispatch({
33 | type: 'SwapVideo',
34 | value: [props.user],
35 | });
36 | }}
37 | />
38 | );
39 | };
40 |
41 | export default RemoteSwap;
42 |
--------------------------------------------------------------------------------
/src/Controls/Remote/RemoteVideoMute.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import PropsContext, {
3 | ToggleState,
4 | UidInterface,
5 | } from '../../Contexts/PropsContext';
6 | import RtmContext, {mutingDevice} from '../../Contexts/RtmContext';
7 | import BtnTemplate from '../BtnTemplate';
8 | import styles from '../../Style';
9 |
10 | interface RemoteVideoMuteInterface {
11 | user: UidInterface;
12 | rightButton: boolean;
13 | }
14 |
15 | const RemoteVideoMute: React.FC = (props) => {
16 | const {user} = props;
17 | const {sendMuteRequest, uidMap} = useContext(RtmContext || {});
18 | const {styleProps} = useContext(PropsContext);
19 | const {remoteBtnStyles, theme} = styleProps || {};
20 | const {muteRemoteVideo} = remoteBtnStyles || {};
21 | const isMuted = user.video === ToggleState.disabled;
22 |
23 | return user.uid !== 0 && uidMap[user.uid as number] ? (
24 | {
38 | sendMuteRequest(mutingDevice.camera, user.uid as number, !isMuted);
39 | }}
40 | />
41 | ) : (
42 | <>>
43 | );
44 | };
45 |
46 | export default RemoteVideoMute;
47 |
--------------------------------------------------------------------------------
/src/Controls/RemoteControls.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import {View} from 'react-native';
3 | import PropsContext, {UidInterface} from '../Contexts/PropsContext';
4 | import styles from '../Style';
5 | import RemoteAudioMute from './Remote/RemoteAudioMute';
6 | import RemoteSwap from './Remote/RemoteSwap';
7 | import RemoteVideoMute from './Remote/RemoteVideoMute';
8 |
9 | interface RemoteControlsInterface {
10 | showMuteRemoteVideo?: boolean;
11 | showMuteRemoteAudio?: boolean;
12 | showRemoteSwap?: boolean;
13 | user: UidInterface;
14 | }
15 |
16 | const RemoteControls: React.FC = (props) => {
17 | const {styleProps, rtcProps} = useContext(PropsContext);
18 | const {remoteBtnContainer} = styleProps || {};
19 |
20 | return (
21 |
23 | {rtcProps.disableRtm ? (
24 | <>>
25 | ) : (
26 | <>
27 | {props.showMuteRemoteAudio !== false ? (
28 |
29 | ) : (
30 | <>>
31 | )}
32 | {props.showMuteRemoteVideo !== false ? (
33 |
37 | ) : (
38 | <>>
39 | )}
40 | >
41 | )}
42 | {props.showRemoteSwap !== false ? (
43 |
44 | ) : (
45 | <>>
46 | )}
47 |
48 | );
49 | };
50 |
51 | export default RemoteControls;
52 |
--------------------------------------------------------------------------------
/src/Controls/types.ts:
--------------------------------------------------------------------------------
1 | export type Only = {[P in keyof T]: T[P]} & Omit<
2 | {[P in keyof U]?: never},
3 | keyof T
4 | >;
5 |
6 | export type Either = Only | Only;
7 |
--------------------------------------------------------------------------------
/src/RTMEngine.tsx:
--------------------------------------------------------------------------------
1 | import RtmEngine from 'agora-react-native-rtm';
2 |
3 | class RTMEngine {
4 | engine!: RtmEngine;
5 | private localUID: string = '';
6 | private channelId: string = '';
7 |
8 | private static _instance: RTMEngine | null = null;
9 |
10 | public static getInstance(appId: string) {
11 | if (!RTMEngine._instance) {
12 | return new RTMEngine(appId);
13 | }
14 | return RTMEngine._instance;
15 | }
16 |
17 | private async createClientInstance(appId: string) {
18 | await this.engine.createInstance(appId);
19 | }
20 |
21 | private async destroyClientInstance() {
22 | try {
23 | await this.engine?.logout();
24 | } catch (error) {
25 | console.log('Error logout: ', error);
26 | }
27 | try {
28 | await this.engine?.release();
29 | } catch (error) {
30 | console.log('Error release: ', error);
31 | }
32 | }
33 |
34 | private constructor(appId: string) {
35 | if (RTMEngine._instance) {
36 | return RTMEngine._instance;
37 | }
38 | RTMEngine._instance = this;
39 | this.engine = new RtmEngine();
40 | this.localUID = '';
41 | this.channelId = '';
42 | this.createClientInstance(appId);
43 |
44 | return RTMEngine._instance;
45 | }
46 |
47 | setLoginInfo(localUID: string, channelID: string) {
48 | this.localUID = localUID;
49 | this.channelId = channelID;
50 | }
51 | get localUid() {
52 | return this.localUID;
53 | }
54 | get channelUid() {
55 | return this.channelId;
56 | }
57 | destroy() {
58 | try {
59 | if (RTMEngine._instance) {
60 | this.destroyClientInstance();
61 | RTMEngine._instance = null;
62 | } else {
63 | console.log('destroy called but no instance');
64 | }
65 | } catch (error) {
66 | console.log('Error destroying instance error: ', error);
67 | }
68 | }
69 | }
70 |
71 | export default RTMEngine;
72 |
--------------------------------------------------------------------------------
/src/Reducer/BecomeAudience.ts:
--------------------------------------------------------------------------------
1 | import {UidStateInterface} from '../Contexts/RtcContext';
2 |
3 | export default function BecomeAudience(state: UidStateInterface) {
4 | console.log('!BecomeAudience');
5 | if (state.max[0].uid === 'local' && state.min.length !== 0) {
6 | let stateUpdate = {};
7 | let minUpdate = [...state.min];
8 | stateUpdate = {
9 | max: [minUpdate.pop()],
10 | min: [...minUpdate, state.max[0]],
11 | };
12 | return stateUpdate;
13 | } else {
14 | return state;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Reducer/LocalMuteAudio.ts:
--------------------------------------------------------------------------------
1 | import {UidInterface} from '../Contexts/PropsContext';
2 | import {ActionType, UidStateInterface} from '../Contexts/RtcContext';
3 |
4 | export default function LocalMuteAudio(
5 | state: UidStateInterface,
6 | action: ActionType<'LocalMuteAudio'>,
7 | ) {
8 | let stateUpdate = {};
9 | const LocalAudioMute = (user: UidInterface) => {
10 | if (user.uid === 'local') {
11 | user.audio = action.value[0];
12 | }
13 | return user;
14 | };
15 | stateUpdate = {
16 | min: state.min.map(LocalAudioMute),
17 | max: state.max.map(LocalAudioMute),
18 | };
19 | return stateUpdate;
20 | }
21 |
--------------------------------------------------------------------------------
/src/Reducer/LocalMuteVideo.ts:
--------------------------------------------------------------------------------
1 | import {UidInterface} from '../Contexts/PropsContext';
2 | import {ActionType, UidStateInterface} from '../Contexts/RtcContext';
3 |
4 | export default function LocalMuteVideo(
5 | state: UidStateInterface,
6 | action: ActionType<'LocalMuteVideo'>,
7 | ) {
8 | let stateUpdate = {};
9 | const LocalVideoMute = (user: UidInterface) => {
10 | if (user.uid === 'local') {
11 | user.video = action.value[0];
12 | }
13 | return user;
14 | };
15 | stateUpdate = {
16 | min: state.min.map(LocalVideoMute),
17 | max: state.max.map(LocalVideoMute),
18 | };
19 | return stateUpdate;
20 | }
21 |
--------------------------------------------------------------------------------
/src/Reducer/RemoteAudioStateChanged.ts:
--------------------------------------------------------------------------------
1 | import {RemoteAudioState} from 'react-native-agora';
2 | import {ToggleState, UidInterface} from '../Contexts/PropsContext';
3 | import {ActionType, UidStateInterface} from '../Contexts/RtcContext';
4 |
5 | export default function RemoteAudioStateChanged(
6 | state: UidStateInterface,
7 | action: ActionType<'RemoteAudioStateChanged'>,
8 | ) {
9 | let stateUpdate = {};
10 | let audioState: ToggleState;
11 | if (
12 | action.value[2] === RemoteAudioState.RemoteAudioStateStarting ||
13 | action.value[2] === RemoteAudioState.RemoteAudioStateDecoding
14 | ) {
15 | audioState = ToggleState.enabled;
16 | } else {
17 | audioState = ToggleState.disabled;
18 | }
19 | const audioChange = (user: UidInterface) => {
20 | if (user.uid === action.value[1]) {
21 | user.audio = audioState;
22 | }
23 | return user;
24 | };
25 | stateUpdate = {
26 | min: state.min.map(audioChange),
27 | max: state.max.map(audioChange),
28 | };
29 | return stateUpdate;
30 | }
31 |
--------------------------------------------------------------------------------
/src/Reducer/RemoteVideoStateChanged.ts:
--------------------------------------------------------------------------------
1 | import {RemoteVideoState} from 'react-native-agora';
2 | import {ToggleState, UidInterface} from '../Contexts/PropsContext';
3 | import {ActionType, UidStateInterface} from '../Contexts/RtcContext';
4 |
5 | export default function RemoteVideoStateChanged(
6 | state: UidStateInterface,
7 | action: ActionType<'RemoteVideoStateChanged'>,
8 | ) {
9 | let stateUpdate = {};
10 | let videoState: ToggleState;
11 | if (action.value[2] === RemoteVideoState.RemoteVideoStateStopped) {
12 | videoState = ToggleState.disabled;
13 | } else if (
14 | // action.value[1] === RemoteVideoState.RemoteVideoStateStarting ||
15 | action.value[2] === RemoteVideoState.RemoteVideoStateDecoding
16 | ) {
17 | videoState = ToggleState.enabled;
18 | }
19 | const videoChange = (user: UidInterface) => {
20 | if (user.uid === action.value[1] && videoState !== undefined) {
21 | user.video = videoState;
22 | }
23 | return user;
24 | };
25 | stateUpdate = {
26 | min: state.min.map(videoChange),
27 | max: state.max.map(videoChange),
28 | };
29 | return stateUpdate;
30 | }
31 |
--------------------------------------------------------------------------------
/src/Reducer/UpdateDualStreamMode.ts:
--------------------------------------------------------------------------------
1 | import {DualStreamMode, UidInterface} from '../Contexts/PropsContext';
2 | import {ActionType, UidStateInterface} from '../Contexts/RtcContext';
3 |
4 | export default function UpdateDualStreamMode(
5 | state: UidStateInterface,
6 | action: ActionType<'UpdateDualStreamMode'>,
7 | ) {
8 | const newMode = action.value[0];
9 | let stateUpdate: UidStateInterface;
10 | if (newMode === DualStreamMode.HIGH) {
11 | // Update everybody to high
12 | const maxStateUpdate: UidInterface[] = state.max.map((user) => ({
13 | ...user,
14 | streamType: 'high',
15 | }));
16 | const minStateUpdate: UidInterface[] = state.min.map((user) => ({
17 | ...user,
18 | streamType: 'high',
19 | }));
20 | stateUpdate = {min: minStateUpdate, max: maxStateUpdate};
21 | } else if (newMode === DualStreamMode.LOW) {
22 | // Update everybody to low
23 | const maxStateUpdate: UidInterface[] = state.max.map((user) => ({
24 | ...user,
25 | streamType: 'low',
26 | }));
27 | const minStateUpdate: UidInterface[] = state.min.map((user) => ({
28 | ...user,
29 | streamType: 'low',
30 | }));
31 | stateUpdate = {min: minStateUpdate, max: maxStateUpdate};
32 | } else {
33 | // if (newMode === DualStreamMode.DYNAMIC)
34 | // Max users are high other are low
35 | const maxStateUpdate: UidInterface[] = state.max.map((user) => ({
36 | ...user,
37 | streamType: 'high',
38 | }));
39 | const minStateUpdate: UidInterface[] = state.min.map((user) => ({
40 | ...user,
41 | streamType: 'low',
42 | }));
43 | stateUpdate = {min: minStateUpdate, max: maxStateUpdate};
44 | }
45 | return stateUpdate;
46 | }
47 |
--------------------------------------------------------------------------------
/src/Reducer/UserJoined.ts:
--------------------------------------------------------------------------------
1 | import {DualStreamMode, ToggleState} from '../Contexts/PropsContext';
2 | import {ActionType, UidStateInterface} from '../Contexts/RtcContext';
3 |
4 | export default function UserJoined(
5 | state: UidStateInterface,
6 | action: ActionType<'UserJoined'>,
7 | dualStreamMode: DualStreamMode,
8 | uids: (string | number)[],
9 | ) {
10 | let stateUpdate = {};
11 | if (uids.indexOf(action.value[1]) === -1) {
12 | // If new user has joined
13 | // By default add to minimized
14 | let minUpdate = [
15 | ...state.min,
16 | {
17 | uid: action.value[1],
18 | audio: ToggleState.disabled,
19 | video: ToggleState.disabled,
20 | streamType: dualStreamMode === DualStreamMode.HIGH ? 'high' : 'low', // Low if DualStreamMode is LOW or DYNAMIC by default
21 | },
22 | ];
23 |
24 | if (minUpdate.length === 1 && state.max[0].uid === 'local') {
25 | //Only one remote and local is maximized
26 | //Change stream type to high if dualStreaMode is DYNAMIC
27 | if (dualStreamMode === DualStreamMode.DYNAMIC) {
28 | minUpdate[0].streamType = 'high';
29 | }
30 | //Swap max and min
31 | stateUpdate = {
32 | max: minUpdate,
33 | min: state.max,
34 | };
35 | } else {
36 | //More than one remote
37 | stateUpdate = {
38 | min: minUpdate,
39 | };
40 | }
41 |
42 | // console.log('new user joined!\n', state, stateUpdate, {
43 | // dualStreamMode,
44 | // });
45 | }
46 | return stateUpdate;
47 | }
48 |
--------------------------------------------------------------------------------
/src/Reducer/UserMuteRemoteAudio.ts:
--------------------------------------------------------------------------------
1 | import {UidInterface} from '../Contexts/PropsContext';
2 | import {ActionType, UidStateInterface} from '../Contexts/RtcContext';
3 |
4 | export default function UserMuteRemoteAudio(
5 | state: UidStateInterface,
6 | action: ActionType<'UserMuteRemoteAudio'>,
7 | ) {
8 | let stateUpdate = {};
9 | const audioMute = (user: UidInterface) => {
10 | if (user.uid === action.value[0].uid) {
11 | user.audio = action.value[1];
12 | }
13 | return user;
14 | };
15 | stateUpdate = {
16 | min: state.min.map(audioMute),
17 | max: state.max.map(audioMute),
18 | };
19 | return stateUpdate;
20 | }
21 |
--------------------------------------------------------------------------------
/src/Reducer/UserMuteRemoteVideo.ts:
--------------------------------------------------------------------------------
1 | import {UidInterface} from '../Contexts/PropsContext';
2 | import {ActionType, UidStateInterface} from '../Contexts/RtcContext';
3 |
4 | export default function UserMuteRemoteVideo(
5 | state: UidStateInterface,
6 | action: ActionType<'UserMuteRemoteVideo'>,
7 | ) {
8 | let stateUpdate = {};
9 | const videoMute = (user: UidInterface) => {
10 | if (user.uid === action.value[0].uid) {
11 | user.video = action.value[1];
12 | }
13 | return user;
14 | };
15 | stateUpdate = {
16 | min: state.min.map(videoMute),
17 | max: state.max.map(videoMute),
18 | };
19 | return stateUpdate;
20 | }
21 |
--------------------------------------------------------------------------------
/src/Reducer/UserOffline.ts:
--------------------------------------------------------------------------------
1 | import {ActionType, UidStateInterface} from '../Contexts/RtcContext';
2 |
3 | export default function UserOffline(
4 | state: UidStateInterface,
5 | action: ActionType<'UserOffline'>,
6 | ) {
7 | let stateUpdate = {};
8 | if (state.max[0].uid === action.value[1]) {
9 | //If max has the remote video
10 | let minUpdate = [...state.min];
11 | stateUpdate = {
12 | max: [minUpdate.pop()],
13 | min: minUpdate,
14 | };
15 | } else {
16 | stateUpdate = {
17 | min: state.min.filter((user) => user.uid !== action.value[1]),
18 | };
19 | }
20 | return stateUpdate;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Reducer/index.ts:
--------------------------------------------------------------------------------
1 | export {default as UpdateDualStreamMode} from './UpdateDualStreamMode';
2 | export {default as UserJoined} from './UserJoined';
3 | export {default as UserOffline} from './UserOffline';
4 | export {default as UserMuteRemoteAudio} from './UserMuteRemoteAudio';
5 | export {default as UserMuteRemoteVideo} from './UserMuteRemoteVideo';
6 | export {default as LocalMuteAudio} from './LocalMuteAudio';
7 | export {default as LocalMuteVideo} from './LocalMuteVideo';
8 | export {default as RemoteAudioStateChanged} from './RemoteAudioStateChanged';
9 | export {default as RemoteVideoStateChanged} from './RemoteVideoStateChanged';
10 | export {default as BecomeAudience} from './BecomeAudience';
11 |
--------------------------------------------------------------------------------
/src/Style.ts:
--------------------------------------------------------------------------------
1 | import {StyleSheet} from 'react-native';
2 |
3 | export default StyleSheet.create({
4 | max: {
5 | flex: 1,
6 | },
7 | buttonHolder: {
8 | height: 100,
9 | alignItems: 'center',
10 | flex: 1,
11 | flexDirection: 'row',
12 | justifyContent: 'space-evenly',
13 | },
14 | button: {
15 | paddingHorizontal: 20,
16 | paddingVertical: 10,
17 | backgroundColor: '#0093E9',
18 | borderRadius: 25,
19 | },
20 | buttonText: {
21 | color: '#fff',
22 | },
23 | fullView: {
24 | width: '100%',
25 | height: '100%',
26 | },
27 | minView: {
28 | width: 240,
29 | height: 135,
30 | },
31 | minViewFallback: {
32 | width: 240,
33 | justifyContent: 'center',
34 | height: 135,
35 | backgroundColor: '#111',
36 | },
37 | minContainer: {
38 | position: 'absolute',
39 | top: 15,
40 | left: 0,
41 | padding: 0,
42 | margin: 0,
43 | // height: 135,
44 | },
45 | Controls: {
46 | position: 'absolute',
47 | bottom: 25,
48 | left: 0,
49 | width: '100%',
50 | height: 70,
51 | zIndex: 10,
52 | display: 'flex',
53 | flexDirection: 'row',
54 | justifyContent: 'space-evenly',
55 | alignItems: 'center',
56 | },
57 | minOverlay: {
58 | ...(StyleSheet.absoluteFill as object),
59 | backgroundColor: 'black',
60 | opacity: 0.7,
61 | zIndex: 2,
62 | height: '100%',
63 | },
64 | minCloseBtn: {
65 | alignItems: 'center',
66 | justifyContent: 'center',
67 | backgroundColor: 'rgba(0,0,0,0.1)',
68 | width: 46,
69 | height: 46,
70 | borderRadius: 23,
71 | position: 'absolute',
72 | right: 5,
73 | top: 5,
74 | },
75 | controlBtn: {
76 | width: 40,
77 | height: 40,
78 | backgroundColor: 'rgba(0,0,0,0.1)',
79 | alignItems: 'center',
80 | justifyContent: 'center',
81 | },
82 | leftRemoteBtn: {
83 | borderTopLeftRadius: 23,
84 | borderBottomLeftRadius: 23,
85 | borderRightWidth: 4 * StyleSheet.hairlineWidth,
86 | borderColor: '#fff',
87 | },
88 | rightRemoteBtn: {
89 | borderTopRightRadius: 23,
90 | borderBottomRightRadius: 23,
91 | borderLeftWidth: 4 * StyleSheet.hairlineWidth,
92 | borderColor: '#fff',
93 | },
94 | remoteBtnContainer: {
95 | width: '100%',
96 | display: 'flex',
97 | marginTop: 50,
98 | flexDirection: 'row',
99 | justifyContent: 'center',
100 | alignSelf: 'center',
101 | },
102 | localBtn: {
103 | borderRadius: 23,
104 | borderWidth: 4 * StyleSheet.hairlineWidth,
105 | borderColor: '#007aff',
106 | backgroundColor: '#007aff',
107 | },
108 | endCall: {
109 | borderRadius: 30,
110 | borderWidth: 4 * StyleSheet.hairlineWidth,
111 | borderColor: '#ff0000',
112 | width: 48,
113 | height: 48,
114 | backgroundColor: '#ff0000',
115 | },
116 | placeholderIcon: {
117 | width: 50,
118 | height: 50,
119 | alignSelf: 'center',
120 | opacity: 0.5,
121 | },
122 | });
123 |
--------------------------------------------------------------------------------
/src/Utils/actionTypeGuard.tsx:
--------------------------------------------------------------------------------
1 | import {CallbacksInterface} from '../Contexts/PropsContext';
2 | import {ActionType} from '../Contexts/RtcContext';
3 |
4 | export function actionTypeGuard(
5 | _act: ActionType,
6 | _type: T,
7 | ): _act is ActionType {
8 | return true;
9 | }
10 |
--------------------------------------------------------------------------------
/src/Utils/isSafariBrowser.ts:
--------------------------------------------------------------------------------
1 | //@ts-nocheck
2 | import {Platform} from 'react-native';
3 |
4 | const isSafariBrowser = () => {
5 | if (Platform.OS !== 'web') {
6 | return false;
7 | }
8 | if (!('userAgent' in navigator)) {
9 | console.warn('unable to detect browser');
10 | return false;
11 | }
12 |
13 | const userAgentString = navigator.userAgent;
14 | // Detect Chrome
15 | const chromeAgent = userAgentString.indexOf('Chrome') > -1;
16 | // Detect Safari
17 | const safariAgent = userAgentString.indexOf('Safari') > -1;
18 |
19 | // One additional check is required in the case of the Safari browser
20 | // as the user-agent of the Chrome browser also includes the Safari browser’s user-agent.
21 | // If both the user-agents of Chrome and Safari are in the user-agent,
22 | // it means that the browser is Chrome, and hence the Safari browser value is discarded.
23 |
24 | if (chromeAgent && safariAgent) {
25 | return false;
26 | } // Discard Safari since it also matches Chrome
27 | return true;
28 | };
29 |
30 | export default isSafariBrowser;
31 |
--------------------------------------------------------------------------------
/src/Utils/permission.ts:
--------------------------------------------------------------------------------
1 | import {PermissionsAndroid} from 'react-native';
2 | /**
3 | * @name requestCameraAndAudioPermission
4 | * @description Function to request permission for Audio and Camera
5 | */
6 | export default async function requestCameraAndAudioPermission() {
7 | try {
8 | const granted = await PermissionsAndroid.requestMultiple([
9 | PermissionsAndroid.PERMISSIONS.CAMERA,
10 | PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
11 | ]);
12 | if (
13 | granted['android.permission.RECORD_AUDIO'] ===
14 | PermissionsAndroid.RESULTS.GRANTED &&
15 | granted['android.permission.CAMERA'] ===
16 | PermissionsAndroid.RESULTS.GRANTED
17 | ) {
18 | console.log('You can use the cameras & mic');
19 | } else {
20 | console.log('Permission denied');
21 | }
22 | } catch (err) {
23 | console.warn(err);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Views/GridVideo.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | Dispatch,
3 | SetStateAction,
4 | useContext,
5 | useMemo,
6 | useState,
7 | } from 'react';
8 | import {Dimensions, StyleSheet, View} from 'react-native';
9 | import MaxVideoView from './MaxVideoView';
10 | import MinUidContext from '../Contexts/MinUidContext';
11 | import MaxUidContext from '../Contexts/MaxUidContext';
12 | import PropsContext from '../Contexts/PropsContext';
13 | import {ClientRoleType} from 'react-native-agora';
14 |
15 | const layout = (len: number, isDesktop: boolean = true) => {
16 | console.log('layout');
17 | const rows = Math.round(Math.sqrt(len));
18 | const cols = Math.ceil(len / rows);
19 | let [r, c] = isDesktop ? [rows, cols] : [cols, rows];
20 | return {
21 | matrix:
22 | len > 0
23 | ? [
24 | ...Array(r - 1)
25 | .fill(null)
26 | .map(() => Array(c).fill('X')),
27 | Array(len - (r - 1) * c).fill('X'),
28 | ]
29 | : [],
30 | dims: {r, c},
31 | };
32 | };
33 |
34 | const GridVideo: React.FC = () => {
35 | const max = useContext(MaxUidContext);
36 | const min = useContext(MinUidContext);
37 | const {rtcProps, styleProps} = useContext(PropsContext);
38 | const users =
39 | rtcProps.role === ClientRoleType.ClientRoleAudience
40 | ? [...max, ...min].filter((user) => user.uid !== 'local')
41 | : [...max, ...min];
42 | let onLayout = (e: any) => {
43 | setDim([
44 | e.nativeEvent.layout.width,
45 | e.nativeEvent.layout.height,
46 | e.nativeEvent.layout.width > e.nativeEvent.layout.height,
47 | ]);
48 | };
49 | const [dim, setDim]: [
50 | [number, number, boolean],
51 | Dispatch>,
52 | ] = useState([
53 | Dimensions.get('window').width,
54 | Dimensions.get('window').height,
55 | Dimensions.get('window').width > Dimensions.get('window').height,
56 | ]);
57 | const isDesktop = dim[0] > dim[1] + 100;
58 | let {matrix, dims} = useMemo(
59 | () => layout(users.length, isDesktop),
60 | [users.length, isDesktop],
61 | );
62 | return (
63 |
64 | {matrix.map((r, ridx) => (
65 |
66 | {r.map((c, cidx) => (
67 |
68 |
73 | {rtcProps.role === ClientRoleType.ClientRoleAudience &&
74 | users[ridx * dims.c + cidx].uid === 'local' ? null : (
75 |
79 | )}
80 |
81 |
82 | ))}
83 |
84 | ))}
85 |
86 | );
87 | };
88 |
89 | const style = StyleSheet.create({
90 | full: {
91 | width: '100%',
92 | // flex: 1,
93 | height: '100%',
94 | },
95 | gridRow: {
96 | flex: 1,
97 | flexDirection: 'row',
98 | width: '100%',
99 | },
100 | gridVideoContainerInner: {
101 | // borderColor: '#fff',
102 | // borderWidth:2,
103 | flex: 1,
104 | margin: 1,
105 | },
106 | col: {
107 | flex: 1,
108 | marginHorizontal: 'auto',
109 | },
110 | });
111 |
112 | export default GridVideo;
113 |
--------------------------------------------------------------------------------
/src/Views/MaxVideoView.native.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import {RenderModeType, RtcSurfaceView} from 'react-native-agora';
3 | import styles from '../Style';
4 | import PropsContext, {UidInterface} from '../Contexts/PropsContext';
5 | import {StyleSheet, View} from 'react-native';
6 | import ImageIcon from '../Controls/ImageIcon';
7 | import Username from './Usernames';
8 |
9 | // const LocalView = RtcLocalView.SurfaceView;
10 | // const RemoteView = RtcRemoteView.SurfaceView;
11 |
12 | interface MaxViewInterface {
13 | user: UidInterface;
14 | fallback?: React.ComponentType;
15 | }
16 | /**
17 | * MaxVideoView takes in a user and renders the video
18 | */
19 | const MaxVideoView: React.FC = (props) => {
20 | const {styleProps, rtcProps} = useContext(PropsContext);
21 | const {maxViewStyles} = styleProps || {};
22 | const Fallback = props.fallback;
23 |
24 | return (
25 |
26 | {!rtcProps.disableRtm && }
27 | {props.user.uid === 'local' ? (
28 | props.user.video ? (
29 |
33 | ) : Fallback ? (
34 |
35 | ) : (
36 |
37 | )
38 | ) : props.user.video ? (
39 | <>
40 |
47 | >
48 | ) : Fallback ? (
49 |
50 | ) : (
51 |
52 | )}
53 |
54 | );
55 | };
56 |
57 | const DefaultFallback = () => {
58 | const {styleProps} = useContext(PropsContext);
59 | const {videoPlaceholderContainer} = styleProps || {};
60 | return (
61 |
62 |
66 |
67 | );
68 | };
69 |
70 | const style = StyleSheet.create({
71 | placeholderContainer: {
72 | flex: 1,
73 | backgroundColor: '#000',
74 | justifyContent: 'center',
75 | },
76 | });
77 |
78 | export default MaxVideoView;
79 |
--------------------------------------------------------------------------------
/src/Views/MaxVideoView.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import {RenderModeType, RtcSurfaceView} from 'react-native-agora';
3 | import styles from '../Style';
4 | import PropsContext, {UidInterface} from '../Contexts/PropsContext';
5 | import {View} from 'react-native';
6 | // import {RtcContext} from 'Contexts';
7 |
8 | interface MaxViewInterface {
9 | user: UidInterface;
10 | fallback?: React.ComponentType;
11 | }
12 |
13 | const MaxVideoView: React.FC = (props) => {
14 | const {styleProps} = useContext(PropsContext);
15 | // const {rtcUidRef} = useContext(RtcContext);
16 | const {maxViewStyles} = styleProps || {};
17 | const Fallback = props.fallback;
18 |
19 | return props.user.uid === 'local' ? (
20 | props.user.video ? (
21 |
25 | ) : Fallback ? (
26 |
27 | ) : (
28 |
29 | )
30 | ) : (
31 | <>
32 |
33 |
40 |
41 | {props.user.video ? (
42 | <>>
43 | ) : (
44 | <>
45 | {Fallback ? (
46 |
47 | ) : (
48 |
49 | )}
50 | >
51 | )}
52 | >
53 | );
54 | };
55 |
56 | export default MaxVideoView;
57 |
--------------------------------------------------------------------------------
/src/Views/MinVideoView.tsx:
--------------------------------------------------------------------------------
1 | import React, {useState, useContext} from 'react';
2 | import {View, TouchableOpacity, Image} from 'react-native';
3 | import {RenderModeType, RtcSurfaceView} from 'react-native-agora';
4 | import styles from '../Style';
5 | import icons from '../Controls/Icons';
6 | import RemoteControls from '../Controls/RemoteControls';
7 | import PropsContext, {UidInterface} from '../Contexts/PropsContext';
8 | import ImageIcon from '../Controls/ImageIcon';
9 | import Username from './Usernames';
10 | // import RtcContext from '../Contexts/RtcContext';
11 |
12 | interface MinViewInterface {
13 | user: UidInterface;
14 | color?: string;
15 | showOverlay?: boolean;
16 | Fallback?: React.ComponentType;
17 | }
18 |
19 | const MinVideoView: React.FC = (props) => {
20 | const [overlay, setOverlay] = useState(false);
21 | const {styleProps, rtcProps} = useContext(PropsContext);
22 | const {theme, remoteBtnStyles, customIcon} = styleProps || {};
23 | const {minCloseBtnStyles} = remoteBtnStyles || {};
24 | const {showOverlay} = props || {};
25 |
26 | return (
27 |
28 | {showOverlay ? (
29 | setOverlay(true)}>
30 |
31 |
32 | ) : (
33 |
34 | )}
35 |
36 | {overlay && showOverlay ? (
37 |
38 | setOverlay(!overlay)}>
41 |
51 |
52 |
53 |
54 | ) : (
55 | <>>
56 | )}
57 | {!rtcProps.disableRtm && }
58 |
59 | );
60 | };
61 |
62 | const UserVideoWithFallback = (props: {
63 | user: UidInterface;
64 | Fallback?: React.ComponentType;
65 | }) => {
66 | const {Fallback, user} = props;
67 | const {styleProps} = useContext(PropsContext);
68 | const {videoPlaceholderContainer, videoPlaceholderIcon} = styleProps || {};
69 |
70 | return user.video ? (
71 |
72 | ) : Fallback ? (
73 |
74 | ) : (
75 |
76 |
80 |
81 | );
82 | };
83 |
84 | const UserVideo = (props: {user: UidInterface}) => {
85 | const {styleProps} = useContext(PropsContext);
86 | // const {rtcUidRef} = useContext(RtcContext);
87 | const {minViewStyles} = styleProps || {};
88 | return props.user.uid === 'local' ? (
89 |
94 | ) : (
95 |
103 | );
104 | };
105 |
106 | export default MinVideoView;
107 |
--------------------------------------------------------------------------------
/src/Views/PinnedVideo.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext, useEffect, useState} from 'react';
2 | import {Dimensions, ScrollView} from 'react-native';
3 | import MaxVideoView from './MaxVideoView';
4 | import MinVideoView from './MinVideoView';
5 | import {MinUidConsumer} from '../Contexts/MinUidContext';
6 | import {MaxUidConsumer} from '../Contexts/MaxUidContext';
7 | import styles from '../Style';
8 | import PropsContext from '../Contexts/PropsContext';
9 | import {ClientRoleType} from 'react-native-agora';
10 |
11 | const PinnedVideo: React.FC = () => {
12 | const {rtcProps, styleProps} = useContext(PropsContext);
13 | const [width, setWidth] = useState(Dimensions.get('screen').width);
14 |
15 | useEffect(() => {
16 | Dimensions.addEventListener('change', () => {
17 | setWidth(Dimensions.get('screen').width);
18 | });
19 | });
20 |
21 | return (
22 | <>
23 |
24 | {(maxUsers) =>
25 | maxUsers[0] ? ( // check if audience & live don't render if uid === local
26 |
27 | ) : null
28 | }
29 |
30 |
38 |
39 | {(minUsers) =>
40 | minUsers.map((user) =>
41 | rtcProps.role === ClientRoleType.ClientRoleAudience &&
42 | user.uid === 'local' ? null : (
43 |
44 | ),
45 | )
46 | }
47 |
48 |
49 | >
50 | );
51 | };
52 |
53 | export default PinnedVideo;
54 |
--------------------------------------------------------------------------------
/src/Views/Usernames.tsx:
--------------------------------------------------------------------------------
1 | import React, {useContext} from 'react';
2 | import RtmContext from '../Contexts/RtmContext';
3 | import PropsContext, {UidInterface} from '../Contexts/PropsContext';
4 | import {StyleSheet, Text} from 'react-native';
5 |
6 | const Username: React.FC<{user: UidInterface; style?: React.CSSProperties}> = (
7 | props,
8 | ) => {
9 | const {usernames} = useContext(RtmContext);
10 | const {rtmProps, styleProps} = useContext(PropsContext);
11 | const {user} = props;
12 |
13 | return rtmProps?.displayUsername ? (
14 |
15 | {usernames[user.uid]}
16 |
17 | ) : (
18 |
19 | );
20 | };
21 |
22 | const styles = StyleSheet.create({
23 | username: {
24 | position: 'absolute',
25 | paddingHorizontal: 6,
26 | paddingVertical: 2,
27 | backgroundColor: '#007bffaa',
28 | color: '#fff',
29 | margin: 0,
30 | bottom: 0,
31 | right: 0,
32 | zIndex: 1,
33 | },
34 | });
35 |
36 | export default Username;
37 |
--------------------------------------------------------------------------------
/src/hooks/useImageDelay.tsx:
--------------------------------------------------------------------------------
1 | //@ts-nocheck
2 | import React from 'react';
3 | import isSafariBrowser from '../Utils/isSafariBrowser';
4 |
5 | function useImageDelay(
6 | elementRef: any,
7 | delay: number | null,
8 | imageName: string,
9 | tintColor?: string,
10 | ) {
11 | React.useEffect(() => {
12 | if (!isSafariBrowser()) {
13 | return;
14 | }
15 | // The following block allows to repaint the icon
16 | let imageElement: any;
17 | if (elementRef && elementRef.current) {
18 | const imageContainer = elementRef.current as any;
19 | if (imageContainer && imageContainer.children.length > 0) {
20 | imageElement = imageContainer.children[0];
21 | imageElement.style.display = 'none';
22 | }
23 | }
24 | const timer = setTimeout(() => {
25 | if (imageElement) {
26 | imageElement.style.display = 'initial';
27 | }
28 | }, delay);
29 |
30 | return () => clearTimeout(timer);
31 | // eslint-disable-next-line react-hooks/exhaustive-deps
32 | }, [imageName, tintColor]);
33 | }
34 |
35 | export default useImageDelay;
36 |
--------------------------------------------------------------------------------