@livekit/react-native 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +141 -0
  3. package/android/build.gradle +133 -0
  4. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  5. package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  6. package/android/gradle.properties +3 -0
  7. package/android/gradlew +185 -0
  8. package/android/gradlew.bat +89 -0
  9. package/android/local.properties +8 -0
  10. package/android/src/main/AndroidManifest.xml +13 -0
  11. package/android/src/main/java/com/livekit/reactnative/LivekitReactNativeModule.kt +50 -0
  12. package/android/src/main/java/com/livekit/reactnative/LivekitReactNativePackage.kt +17 -0
  13. package/android/src/main/java/com/livekit/reactnative/audio/AudioDeviceKind.java +40 -0
  14. package/android/src/main/java/com/livekit/reactnative/audio/AudioSwitchManager.java +140 -0
  15. package/ios/LivekitReactNative-Bridging-Header.h +2 -0
  16. package/ios/LivekitReactNative.h +11 -0
  17. package/ios/LivekitReactNative.m +111 -0
  18. package/ios/LivekitReactNative.xcodeproj/project.pbxproj +274 -0
  19. package/ios/LivekitReactNative.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
  20. package/ios/LivekitReactNative.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  21. package/ios/LivekitReactNative.xcodeproj/project.xcworkspace/xcuserdata/davidliu.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  22. package/ios/LivekitReactNative.xcodeproj/xcuserdata/davidliu.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  23. package/lib/commonjs/audio/AudioSession.js +80 -0
  24. package/lib/commonjs/audio/AudioSession.js.map +1 -0
  25. package/lib/commonjs/components/VideoView.js +165 -0
  26. package/lib/commonjs/components/VideoView.js.map +1 -0
  27. package/lib/commonjs/components/ViewPortDetector.js +109 -0
  28. package/lib/commonjs/components/ViewPortDetector.js.map +1 -0
  29. package/lib/commonjs/index.js +103 -0
  30. package/lib/commonjs/index.js.map +1 -0
  31. package/lib/commonjs/useParticipant.js +100 -0
  32. package/lib/commonjs/useParticipant.js.map +1 -0
  33. package/lib/commonjs/useRoom.js +137 -0
  34. package/lib/commonjs/useRoom.js.map +1 -0
  35. package/lib/module/audio/AudioSession.js +70 -0
  36. package/lib/module/audio/AudioSession.js.map +1 -0
  37. package/lib/module/components/VideoView.js +144 -0
  38. package/lib/module/components/VideoView.js.map +1 -0
  39. package/lib/module/components/ViewPortDetector.js +97 -0
  40. package/lib/module/components/ViewPortDetector.js.map +1 -0
  41. package/lib/module/index.js +45 -0
  42. package/lib/module/index.js.map +1 -0
  43. package/lib/module/useParticipant.js +91 -0
  44. package/lib/module/useParticipant.js.map +1 -0
  45. package/lib/module/useRoom.js +126 -0
  46. package/lib/module/useRoom.js.map +1 -0
  47. package/lib/typescript/audio/AudioSession.d.ts +88 -0
  48. package/lib/typescript/components/VideoView.d.ts +10 -0
  49. package/lib/typescript/components/ViewPortDetector.d.ts +26 -0
  50. package/lib/typescript/index.d.ts +12 -0
  51. package/lib/typescript/useParticipant.d.ts +13 -0
  52. package/lib/typescript/useRoom.d.ts +20 -0
  53. package/livekit-react-native.podspec +22 -0
  54. package/package.json +157 -0
  55. package/src/audio/AudioSession.ts +132 -0
  56. package/src/components/VideoView.tsx +143 -0
  57. package/src/components/ViewPortDetector.tsx +93 -0
  58. package/src/index.tsx +37 -0
  59. package/src/useParticipant.ts +144 -0
  60. package/src/useRoom.ts +163 -0
@@ -0,0 +1,144 @@
1
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
2
+
3
+ import * as React from 'react';
4
+ import { PixelRatio, StyleSheet, View } from 'react-native';
5
+ import { LocalVideoTrack, TrackEvent } from 'livekit-client';
6
+ import { RTCView } from 'react-native-webrtc';
7
+ import { useEffect, useState } from 'react';
8
+ import { RemoteVideoTrack } from 'livekit-client';
9
+ import ViewPortDetector from './ViewPortDetector';
10
+ export const VideoView = _ref => {
11
+ var _mediaStream$toURL;
12
+
13
+ let {
14
+ style = {},
15
+ videoTrack,
16
+ objectFit = 'cover',
17
+ zOrder,
18
+ mirror
19
+ } = _ref;
20
+ const [elementInfo] = useState(() => {
21
+ let info = new VideoViewElementInfo();
22
+ info.id = videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.sid;
23
+ info.something = videoTrack;
24
+ return info;
25
+ });
26
+ const [mediaStream, setMediaStream] = useState(videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.mediaStream);
27
+ useEffect(() => {
28
+ setMediaStream(videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.mediaStream);
29
+
30
+ if (videoTrack instanceof LocalVideoTrack) {
31
+ const onRestarted = track => {
32
+ setMediaStream(track === null || track === void 0 ? void 0 : track.mediaStream);
33
+ };
34
+
35
+ videoTrack.on(TrackEvent.Restarted, onRestarted);
36
+ return () => {
37
+ videoTrack.off(TrackEvent.Restarted, onRestarted);
38
+ };
39
+ } else {
40
+ return () => {};
41
+ }
42
+ }, [videoTrack]);
43
+ useEffect(() => {
44
+ if (videoTrack instanceof RemoteVideoTrack && videoTrack.isAdaptiveStream) {
45
+ videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.observeElementInfo(elementInfo);
46
+ return () => {
47
+ videoTrack === null || videoTrack === void 0 ? void 0 : videoTrack.stopObservingElementInfo(elementInfo);
48
+ };
49
+ } else {
50
+ return () => {};
51
+ }
52
+ }, [videoTrack, elementInfo]);
53
+ return /*#__PURE__*/React.createElement(View, {
54
+ style: { ...style,
55
+ ...styles.container
56
+ },
57
+ onLayout: event => {
58
+ elementInfo.onLayout(event);
59
+ }
60
+ }, /*#__PURE__*/React.createElement(ViewPortDetector, {
61
+ onChange: isVisible => elementInfo.onVisibility(isVisible),
62
+ style: styles.videoView
63
+ }, /*#__PURE__*/React.createElement(RTCView, {
64
+ style: styles.videoView,
65
+ streamURL: (_mediaStream$toURL = mediaStream === null || mediaStream === void 0 ? void 0 : mediaStream.toURL()) !== null && _mediaStream$toURL !== void 0 ? _mediaStream$toURL : '',
66
+ objectFit: objectFit,
67
+ zOrder: zOrder,
68
+ mirror: mirror
69
+ })));
70
+ };
71
+ const styles = StyleSheet.create({
72
+ container: {},
73
+ videoView: {
74
+ flex: 1,
75
+ width: '100%'
76
+ }
77
+ });
78
+
79
+ class VideoViewElementInfo {
80
+ constructor() {
81
+ _defineProperty(this, "element", {});
82
+
83
+ _defineProperty(this, "something", void 0);
84
+
85
+ _defineProperty(this, "id", void 0);
86
+
87
+ _defineProperty(this, "_width", 0);
88
+
89
+ _defineProperty(this, "_height", 0);
90
+
91
+ _defineProperty(this, "_observing", false);
92
+
93
+ _defineProperty(this, "visible", true);
94
+
95
+ _defineProperty(this, "visibilityChangedAt", void 0);
96
+
97
+ _defineProperty(this, "handleResize", void 0);
98
+
99
+ _defineProperty(this, "handleVisibilityChanged", void 0);
100
+
101
+ _defineProperty(this, "width", () => this._width);
102
+
103
+ _defineProperty(this, "height", () => this._height);
104
+ }
105
+
106
+ observe() {
107
+ this._observing = true;
108
+ }
109
+
110
+ stopObserving() {
111
+ this._observing = false;
112
+ }
113
+
114
+ onLayout(event) {
115
+ let {
116
+ width,
117
+ height
118
+ } = event.nativeEvent.layout;
119
+ const pixelRatio = PixelRatio.get();
120
+ this._width = width * pixelRatio;
121
+ this._height = height * pixelRatio;
122
+
123
+ if (this._observing) {
124
+ var _this$handleResize;
125
+
126
+ (_this$handleResize = this.handleResize) === null || _this$handleResize === void 0 ? void 0 : _this$handleResize.call(this);
127
+ }
128
+ }
129
+
130
+ onVisibility(isVisible) {
131
+ if (this.visible !== isVisible) {
132
+ this.visible = isVisible;
133
+ this.visibilityChangedAt = Date.now();
134
+
135
+ if (this._observing) {
136
+ var _this$handleVisibilit;
137
+
138
+ (_this$handleVisibilit = this.handleVisibilityChanged) === null || _this$handleVisibilit === void 0 ? void 0 : _this$handleVisibilit.call(this);
139
+ }
140
+ }
141
+ }
142
+
143
+ }
144
+ //# sourceMappingURL=VideoView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["VideoView.tsx"],"names":["React","PixelRatio","StyleSheet","View","LocalVideoTrack","TrackEvent","RTCView","useEffect","useState","RemoteVideoTrack","ViewPortDetector","VideoView","style","videoTrack","objectFit","zOrder","mirror","elementInfo","info","VideoViewElementInfo","id","sid","something","mediaStream","setMediaStream","onRestarted","track","on","Restarted","off","isAdaptiveStream","observeElementInfo","stopObservingElementInfo","styles","container","event","onLayout","isVisible","onVisibility","videoView","toURL","create","flex","width","_width","_height","observe","_observing","stopObserving","height","nativeEvent","layout","pixelRatio","get","handleResize","visible","visibilityChangedAt","Date","now","handleVisibilityChanged"],"mappings":"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","sourcesContent":["import * as React from 'react';\n\nimport {\n LayoutChangeEvent,\n PixelRatio,\n StyleSheet,\n View,\n ViewStyle,\n} from 'react-native';\nimport {\n ElementInfo,\n LocalVideoTrack,\n Track,\n TrackEvent,\n VideoTrack,\n} from 'livekit-client';\nimport { RTCView } from 'react-native-webrtc';\nimport { useEffect, useState } from 'react';\nimport { RemoteVideoTrack } from 'livekit-client';\nimport ViewPortDetector from './ViewPortDetector';\n\nexport type Props = {\n videoTrack?: VideoTrack | undefined;\n style?: ViewStyle;\n objectFit?: 'cover' | 'contain' | undefined;\n mirror?: boolean;\n zOrder?: number;\n};\n\nexport const VideoView = ({\n style = {},\n videoTrack,\n objectFit = 'cover',\n zOrder,\n mirror,\n}: Props) => {\n const [elementInfo] = useState(() => {\n let info = new VideoViewElementInfo();\n info.id = videoTrack?.sid;\n info.something = videoTrack;\n return info;\n });\n\n const [mediaStream, setMediaStream] = useState(videoTrack?.mediaStream);\n useEffect(() => {\n setMediaStream(videoTrack?.mediaStream);\n if (videoTrack instanceof LocalVideoTrack) {\n const onRestarted = (track: Track | null) => {\n setMediaStream(track?.mediaStream);\n };\n videoTrack.on(TrackEvent.Restarted, onRestarted);\n\n return () => {\n videoTrack.off(TrackEvent.Restarted, onRestarted);\n };\n } else {\n return () => {};\n }\n }, [videoTrack]);\n\n useEffect(() => {\n if (videoTrack instanceof RemoteVideoTrack && videoTrack.isAdaptiveStream) {\n videoTrack?.observeElementInfo(elementInfo);\n return () => {\n videoTrack?.stopObservingElementInfo(elementInfo);\n };\n } else {\n return () => {};\n }\n }, [videoTrack, elementInfo]);\n\n return (\n <View\n style={{ ...style, ...styles.container }}\n onLayout={(event) => {\n elementInfo.onLayout(event);\n }}\n >\n <ViewPortDetector\n onChange={(isVisible: boolean) => elementInfo.onVisibility(isVisible)}\n style={styles.videoView}\n >\n <RTCView\n style={styles.videoView}\n streamURL={mediaStream?.toURL() ?? ''}\n objectFit={objectFit}\n zOrder={zOrder}\n mirror={mirror}\n />\n </ViewPortDetector>\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {},\n videoView: {\n flex: 1,\n width: '100%',\n },\n});\n\nclass VideoViewElementInfo implements ElementInfo {\n element: object = {};\n something?: any;\n id?: string;\n _width = 0;\n _height = 0;\n _observing = false;\n visible: boolean = true;\n visibilityChangedAt: number | undefined;\n handleResize?: (() => void) | undefined;\n handleVisibilityChanged?: (() => void) | undefined;\n width = () => this._width;\n height = () => this._height;\n\n observe(): void {\n this._observing = true;\n }\n stopObserving(): void {\n this._observing = false;\n }\n\n onLayout(event: LayoutChangeEvent) {\n let { width, height } = event.nativeEvent.layout;\n const pixelRatio = PixelRatio.get();\n this._width = width * pixelRatio;\n this._height = height * pixelRatio;\n\n if (this._observing) {\n this.handleResize?.();\n }\n }\n onVisibility(isVisible: boolean) {\n if (this.visible !== isVisible) {\n this.visible = isVisible;\n this.visibilityChangedAt = Date.now();\n if (this._observing) {\n this.handleVisibilityChanged?.();\n }\n }\n }\n}\n"]}
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
4
+
5
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
+
7
+ import React, { Component } from 'react';
8
+ import { View } from 'react-native';
9
+
10
+ /**
11
+ * Detects when this is in the viewport and visible.
12
+ *
13
+ * Will not fire visibility changes for zero width/height components.
14
+ */
15
+ export default class ViewPortDetector extends Component {
16
+ constructor(props) {
17
+ super(props);
18
+
19
+ _defineProperty(this, "lastValue", null);
20
+
21
+ _defineProperty(this, "interval", null);
22
+
23
+ _defineProperty(this, "view", null);
24
+
25
+ this.state = {
26
+ rectTop: 0,
27
+ rectBottom: 0
28
+ };
29
+ }
30
+
31
+ componentDidMount() {
32
+ if (!this.props.disabled) {
33
+ this.startWatching();
34
+ }
35
+ }
36
+
37
+ componentWillUnmount() {
38
+ this.stopWatching();
39
+ }
40
+
41
+ UNSAFE_componentWillReceiveProps(nextProps) {
42
+ if (nextProps.disabled) {
43
+ this.stopWatching();
44
+ } else {
45
+ this.lastValue = null;
46
+ this.startWatching();
47
+ }
48
+ }
49
+
50
+ startWatching() {
51
+ if (this.interval) {
52
+ return;
53
+ }
54
+
55
+ this.interval = setInterval(() => {
56
+ if (!this.view) {
57
+ return;
58
+ }
59
+
60
+ this.view.measure((_x, _y, width, height, _pageX, _pageY) => {
61
+ this.checkInViewPort(width, height);
62
+ });
63
+ }, this.props.delay || 100);
64
+ }
65
+
66
+ stopWatching() {
67
+ this.interval = clearInterval(this.interval);
68
+ }
69
+
70
+ checkInViewPort(width, height) {
71
+ let isVisible; // Not visible if any of these are missing.
72
+
73
+ if (!width || !height) {
74
+ isVisible = false;
75
+ } else {
76
+ isVisible = true;
77
+ }
78
+
79
+ if (this.lastValue !== isVisible) {
80
+ var _this$props$onChange, _this$props;
81
+
82
+ this.lastValue = isVisible;
83
+ (_this$props$onChange = (_this$props = this.props).onChange) === null || _this$props$onChange === void 0 ? void 0 : _this$props$onChange.call(_this$props, isVisible);
84
+ }
85
+ }
86
+
87
+ render() {
88
+ return /*#__PURE__*/React.createElement(View, _extends({
89
+ collapsable: false,
90
+ ref: component => {
91
+ this.view = component;
92
+ }
93
+ }, this.props), this.props.children);
94
+ }
95
+
96
+ }
97
+ //# sourceMappingURL=ViewPortDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["ViewPortDetector.tsx"],"names":["React","Component","View","ViewPortDetector","constructor","props","state","rectTop","rectBottom","componentDidMount","disabled","startWatching","componentWillUnmount","stopWatching","UNSAFE_componentWillReceiveProps","nextProps","lastValue","interval","setInterval","view","measure","_x","_y","width","height","_pageX","_pageY","checkInViewPort","delay","clearInterval","isVisible","onChange","render","component","children"],"mappings":"AAAA;;;;;;AAEA,OAAOA,KAAP,IAAgBC,SAAhB,QAAiC,OAAjC;AACA,SAASC,IAAT,QAAgC,cAAhC;;AASA;AACA;AACA;AACA;AACA;AACA,eAAe,MAAMC,gBAAN,SAA+BF,SAA/B,CAAgD;AAK7DG,EAAAA,WAAW,CAACC,KAAD,EAAe;AACxB,UAAMA,KAAN;;AADwB,uCAJU,IAIV;;AAAA,sCAHK,IAGL;;AAAA,kCAFE,IAEF;;AAExB,SAAKC,KAAL,GAAa;AAAEC,MAAAA,OAAO,EAAE,CAAX;AAAcC,MAAAA,UAAU,EAAE;AAA1B,KAAb;AACD;;AAEDC,EAAAA,iBAAiB,GAAG;AAClB,QAAI,CAAC,KAAKJ,KAAL,CAAWK,QAAhB,EAA0B;AACxB,WAAKC,aAAL;AACD;AACF;;AAEDC,EAAAA,oBAAoB,GAAG;AACrB,SAAKC,YAAL;AACD;;AAEDC,EAAAA,gCAAgC,CAACC,SAAD,EAAmB;AACjD,QAAIA,SAAS,CAACL,QAAd,EAAwB;AACtB,WAAKG,YAAL;AACD,KAFD,MAEO;AACL,WAAKG,SAAL,GAAiB,IAAjB;AACA,WAAKL,aAAL;AACD;AACF;;AAEOA,EAAAA,aAAa,GAAG;AACtB,QAAI,KAAKM,QAAT,EAAmB;AACjB;AACD;;AACD,SAAKA,QAAL,GAAgBC,WAAW,CAAC,MAAM;AAChC,UAAI,CAAC,KAAKC,IAAV,EAAgB;AACd;AACD;;AACD,WAAKA,IAAL,CAAUC,OAAV,CAAkB,CAACC,EAAD,EAAKC,EAAL,EAASC,KAAT,EAAgBC,MAAhB,EAAwBC,MAAxB,EAAgCC,MAAhC,KAA2C;AAC3D,aAAKC,eAAL,CAAqBJ,KAArB,EAA4BC,MAA5B;AACD,OAFD;AAGD,KAP0B,EAOxB,KAAKnB,KAAL,CAAWuB,KAAX,IAAoB,GAPI,CAA3B;AAQD;;AAEOf,EAAAA,YAAY,GAAG;AACrB,SAAKI,QAAL,GAAgBY,aAAa,CAAC,KAAKZ,QAAN,CAA7B;AACD;;AAEOU,EAAAA,eAAe,CAACJ,KAAD,EAAiBC,MAAjB,EAAkC;AACvD,QAAIM,SAAJ,CADuD,CAEvD;;AACA,QAAI,CAACP,KAAD,IAAU,CAACC,MAAf,EAAuB;AACrBM,MAAAA,SAAS,GAAG,KAAZ;AACD,KAFD,MAEO;AACLA,MAAAA,SAAS,GAAG,IAAZ;AACD;;AAED,QAAI,KAAKd,SAAL,KAAmBc,SAAvB,EAAkC;AAAA;;AAChC,WAAKd,SAAL,GAAiBc,SAAjB;AACA,kDAAKzB,KAAL,EAAW0B,QAAX,gGAAsBD,SAAtB;AACD;AACF;;AAEDE,EAAAA,MAAM,GAAG;AACP,wBACE,oBAAC,IAAD;AACE,MAAA,WAAW,EAAE,KADf;AAEE,MAAA,GAAG,EAAGC,SAAD,IAAe;AAClB,aAAKd,IAAL,GAAYc,SAAZ;AACD;AAJH,OAKM,KAAK5B,KALX,GAOG,KAAKA,KAAL,CAAW6B,QAPd,CADF;AAWD;;AA1E4D","sourcesContent":["'use strict';\n\nimport React, { Component } from 'react';\nimport { View, ViewStyle } from 'react-native';\n\nexport type Props = {\n disabled?: boolean;\n style?: ViewStyle;\n onChange?: (isVisible: boolean) => void;\n delay?: number;\n};\n\n/**\n * Detects when this is in the viewport and visible.\n *\n * Will not fire visibility changes for zero width/height components.\n */\nexport default class ViewPortDetector extends Component<Props> {\n private lastValue: boolean | null = null;\n private interval: any | null = null;\n private view: View | null = null;\n\n constructor(props: Props) {\n super(props);\n this.state = { rectTop: 0, rectBottom: 0 };\n }\n\n componentDidMount() {\n if (!this.props.disabled) {\n this.startWatching();\n }\n }\n\n componentWillUnmount() {\n this.stopWatching();\n }\n\n UNSAFE_componentWillReceiveProps(nextProps: Props) {\n if (nextProps.disabled) {\n this.stopWatching();\n } else {\n this.lastValue = null;\n this.startWatching();\n }\n }\n\n private startWatching() {\n if (this.interval) {\n return;\n }\n this.interval = setInterval(() => {\n if (!this.view) {\n return;\n }\n this.view.measure((_x, _y, width, height, _pageX, _pageY) => {\n this.checkInViewPort(width, height);\n });\n }, this.props.delay || 100);\n }\n\n private stopWatching() {\n this.interval = clearInterval(this.interval);\n }\n\n private checkInViewPort(width?: number, height?: number) {\n let isVisible: boolean;\n // Not visible if any of these are missing.\n if (!width || !height) {\n isVisible = false;\n } else {\n isVisible = true;\n }\n\n if (this.lastValue !== isVisible) {\n this.lastValue = isVisible;\n this.props.onChange?.(isVisible);\n }\n }\n\n render() {\n return (\n <View\n collapsable={false}\n ref={(component) => {\n this.view = component;\n }}\n {...this.props}\n >\n {this.props.children}\n </View>\n );\n }\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import { registerGlobals as webrtcRegisterGlobals } from 'react-native-webrtc';
2
+ import { setupURLPolyfill } from 'react-native-url-polyfill';
3
+ import AudioSession from './audio/AudioSession';
4
+
5
+ /**
6
+ * Registers the required globals needed for LiveKit to work.
7
+ *
8
+ * Must be called before using LiveKit.
9
+ */
10
+ export function registerGlobals() {
11
+ webrtcRegisterGlobals();
12
+ setupURLPolyfill();
13
+ fixWebrtcAdapter();
14
+ shimPromiseAllSettled();
15
+ }
16
+
17
+ function fixWebrtcAdapter() {
18
+ var _window;
19
+
20
+ // @ts-ignore
21
+ if (((_window = window) === null || _window === void 0 ? void 0 : _window.navigator) !== undefined) {
22
+ // @ts-ignore
23
+ const {
24
+ navigator
25
+ } = window;
26
+
27
+ if (navigator.userAgent === undefined) {
28
+ var _navigator$product;
29
+
30
+ navigator.userAgent = (_navigator$product = navigator.product) !== null && _navigator$product !== void 0 ? _navigator$product : 'Unknown';
31
+ }
32
+ }
33
+ }
34
+
35
+ function shimPromiseAllSettled() {
36
+ var allSettled = require('promise.allsettled');
37
+
38
+ allSettled.shim();
39
+ }
40
+
41
+ export * from './components/VideoView';
42
+ export * from './useParticipant';
43
+ export * from './useRoom';
44
+ export { AudioSession };
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["index.tsx"],"names":["registerGlobals","webrtcRegisterGlobals","setupURLPolyfill","AudioSession","fixWebrtcAdapter","shimPromiseAllSettled","window","navigator","undefined","userAgent","product","allSettled","require","shim"],"mappings":"AAAA,SAASA,eAAe,IAAIC,qBAA5B,QAAyD,qBAAzD;AACA,SAASC,gBAAT,QAAiC,2BAAjC;AACA,OAAOC,YAAP,MAAyB,sBAAzB;;AAGA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASH,eAAT,GAA2B;AAChCC,EAAAA,qBAAqB;AACrBC,EAAAA,gBAAgB;AAChBE,EAAAA,gBAAgB;AAChBC,EAAAA,qBAAqB;AACtB;;AAED,SAASD,gBAAT,GAA4B;AAAA;;AAC1B;AACA,MAAI,YAAAE,MAAM,UAAN,0CAAQC,SAAR,MAAsBC,SAA1B,EAAqC;AACnC;AACA,UAAM;AAAED,MAAAA;AAAF,QAAgBD,MAAtB;;AACA,QAAIC,SAAS,CAACE,SAAV,KAAwBD,SAA5B,EAAuC;AAAA;;AACrCD,MAAAA,SAAS,CAACE,SAAV,yBAAsBF,SAAS,CAACG,OAAhC,mEAA2C,SAA3C;AACD;AACF;AACF;;AAED,SAASL,qBAAT,GAAiC;AAC/B,MAAIM,UAAU,GAAGC,OAAO,CAAC,oBAAD,CAAxB;;AACAD,EAAAA,UAAU,CAACE,IAAX;AACD;;AAED,cAAc,wBAAd;AACA,cAAc,kBAAd;AACA,cAAc,WAAd;AACA,SAASV,YAAT","sourcesContent":["import { registerGlobals as webrtcRegisterGlobals } from 'react-native-webrtc';\nimport { setupURLPolyfill } from 'react-native-url-polyfill';\nimport AudioSession from './audio/AudioSession';\nimport type { AudioConfiguration } from './audio/AudioSession';\n\n/**\n * Registers the required globals needed for LiveKit to work.\n *\n * Must be called before using LiveKit.\n */\nexport function registerGlobals() {\n webrtcRegisterGlobals();\n setupURLPolyfill();\n fixWebrtcAdapter();\n shimPromiseAllSettled();\n}\n\nfunction fixWebrtcAdapter() {\n // @ts-ignore\n if (window?.navigator !== undefined) {\n // @ts-ignore\n const { navigator } = window;\n if (navigator.userAgent === undefined) {\n navigator.userAgent = navigator.product ?? 'Unknown';\n }\n }\n}\n\nfunction shimPromiseAllSettled() {\n var allSettled = require('promise.allsettled');\n allSettled.shim();\n}\n\nexport * from './components/VideoView';\nexport * from './useParticipant';\nexport * from './useRoom';\nexport { AudioSession, AudioConfiguration };\n"]}
@@ -0,0 +1,91 @@
1
+ import { LocalParticipant, ParticipantEvent, Track } from 'livekit-client';
2
+ import { useEffect, useState } from 'react';
3
+ export function useParticipant(participant) {
4
+ const [isAudioMuted, setAudioMuted] = useState(false);
5
+ const [, setVideoMuted] = useState(false);
6
+ const [connectionQuality, setConnectionQuality] = useState(participant.connectionQuality);
7
+ const [isSpeaking, setSpeaking] = useState(false);
8
+ const [metadata, setMetadata] = useState();
9
+ const [publications, setPublications] = useState([]);
10
+ const [subscribedTracks, setSubscribedTracks] = useState([]);
11
+ const [cameraPublication, setCameraPublication] = useState(participant.getTrack(Track.Source.Camera));
12
+ const [microphonePublication, setMicrophonePublication] = useState(participant.getTrack(Track.Source.Microphone));
13
+ const [screenSharePublication, setScreenSharePublication] = useState(participant.getTrack(Track.Source.ScreenShare));
14
+ useEffect(() => {
15
+ const onPublicationsChanged = () => {
16
+ setPublications(Array.from(participant.tracks.values()));
17
+ setCameraPublication(participant.getTrack(Track.Source.Camera));
18
+ setMicrophonePublication(participant.getTrack(Track.Source.Microphone));
19
+ setScreenSharePublication(participant.getTrack(Track.Source.ScreenShare));
20
+ setSubscribedTracks(Array.from(participant.tracks.values()).filter(pub => {
21
+ return pub.isSubscribed && pub.track !== undefined;
22
+ }));
23
+ };
24
+
25
+ const onMuted = pub => {
26
+ if (pub.kind === Track.Kind.Audio) {
27
+ setAudioMuted(true);
28
+ } else if (pub.kind === Track.Kind.Video) {
29
+ setVideoMuted(true);
30
+ }
31
+ };
32
+
33
+ const onUnmuted = pub => {
34
+ if (pub.kind === Track.Kind.Audio) {
35
+ setAudioMuted(false);
36
+ } else if (pub.kind === Track.Kind.Video) {
37
+ setVideoMuted(false);
38
+ }
39
+ };
40
+
41
+ const onMetadataChanged = () => {
42
+ if (participant.metadata) {
43
+ setMetadata(participant.metadata);
44
+ }
45
+ };
46
+
47
+ const onIsSpeakingChanged = () => {
48
+ setSpeaking(participant.isSpeaking);
49
+ };
50
+
51
+ const onConnectionQualityUpdate = () => {
52
+ setConnectionQuality(participant.connectionQuality);
53
+ }; // register listeners
54
+
55
+
56
+ participant.on(ParticipantEvent.TrackMuted, onMuted).on(ParticipantEvent.TrackUnmuted, onUnmuted).on(ParticipantEvent.ParticipantMetadataChanged, onMetadataChanged).on(ParticipantEvent.IsSpeakingChanged, onIsSpeakingChanged).on(ParticipantEvent.TrackPublished, onPublicationsChanged).on(ParticipantEvent.TrackUnpublished, onPublicationsChanged).on(ParticipantEvent.TrackSubscribed, onPublicationsChanged).on(ParticipantEvent.TrackUnsubscribed, onPublicationsChanged).on(ParticipantEvent.LocalTrackPublished, onPublicationsChanged).on(ParticipantEvent.LocalTrackUnpublished, onPublicationsChanged).on(ParticipantEvent.ConnectionQualityChanged, onConnectionQualityUpdate); // set initial state
57
+
58
+ onMetadataChanged();
59
+ onIsSpeakingChanged();
60
+ onPublicationsChanged();
61
+ return () => {
62
+ // cleanup
63
+ participant.off(ParticipantEvent.TrackMuted, onMuted).off(ParticipantEvent.TrackUnmuted, onUnmuted).off(ParticipantEvent.ParticipantMetadataChanged, onMetadataChanged).off(ParticipantEvent.IsSpeakingChanged, onIsSpeakingChanged).off(ParticipantEvent.TrackPublished, onPublicationsChanged).off(ParticipantEvent.TrackUnpublished, onPublicationsChanged).off(ParticipantEvent.TrackSubscribed, onPublicationsChanged).off(ParticipantEvent.TrackUnsubscribed, onPublicationsChanged).off(ParticipantEvent.LocalTrackPublished, onPublicationsChanged).off(ParticipantEvent.LocalTrackUnpublished, onPublicationsChanged).off(ParticipantEvent.ConnectionQualityChanged, onConnectionQualityUpdate);
64
+ };
65
+ }, [participant]);
66
+ let muted;
67
+ participant.audioTracks.forEach(pub => {
68
+ muted = pub.isMuted;
69
+ });
70
+
71
+ if (muted === undefined) {
72
+ muted = true;
73
+ }
74
+
75
+ if (isAudioMuted !== muted) {
76
+ setAudioMuted(muted);
77
+ }
78
+
79
+ return {
80
+ isLocal: participant instanceof LocalParticipant,
81
+ isSpeaking,
82
+ connectionQuality,
83
+ publications,
84
+ subscribedTracks,
85
+ cameraPublication,
86
+ microphonePublication,
87
+ screenSharePublication,
88
+ metadata
89
+ };
90
+ }
91
+ //# sourceMappingURL=useParticipant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["useParticipant.ts"],"names":["LocalParticipant","ParticipantEvent","Track","useEffect","useState","useParticipant","participant","isAudioMuted","setAudioMuted","setVideoMuted","connectionQuality","setConnectionQuality","isSpeaking","setSpeaking","metadata","setMetadata","publications","setPublications","subscribedTracks","setSubscribedTracks","cameraPublication","setCameraPublication","getTrack","Source","Camera","microphonePublication","setMicrophonePublication","Microphone","screenSharePublication","setScreenSharePublication","ScreenShare","onPublicationsChanged","Array","from","tracks","values","filter","pub","isSubscribed","track","undefined","onMuted","kind","Kind","Audio","Video","onUnmuted","onMetadataChanged","onIsSpeakingChanged","onConnectionQualityUpdate","on","TrackMuted","TrackUnmuted","ParticipantMetadataChanged","IsSpeakingChanged","TrackPublished","TrackUnpublished","TrackSubscribed","TrackUnsubscribed","LocalTrackPublished","LocalTrackUnpublished","ConnectionQualityChanged","off","muted","audioTracks","forEach","isMuted","isLocal"],"mappings":"AAAA,SAEEA,gBAFF,EAIEC,gBAJF,EAKEC,KALF,QAOO,gBAPP;AAQA,SAASC,SAAT,EAAoBC,QAApB,QAAoC,OAApC;AAcA,OAAO,SAASC,cAAT,CAAwBC,WAAxB,EAAoE;AACzE,QAAM,CAACC,YAAD,EAAeC,aAAf,IAAgCJ,QAAQ,CAAC,KAAD,CAA9C;AACA,QAAM,GAAGK,aAAH,IAAoBL,QAAQ,CAAC,KAAD,CAAlC;AACA,QAAM,CAACM,iBAAD,EAAoBC,oBAApB,IAA4CP,QAAQ,CACxDE,WAAW,CAACI,iBAD4C,CAA1D;AAGA,QAAM,CAACE,UAAD,EAAaC,WAAb,IAA4BT,QAAQ,CAAC,KAAD,CAA1C;AACA,QAAM,CAACU,QAAD,EAAWC,WAAX,IAA0BX,QAAQ,EAAxC;AACA,QAAM,CAACY,YAAD,EAAeC,eAAf,IAAkCb,QAAQ,CAAqB,EAArB,CAAhD;AACA,QAAM,CAACc,gBAAD,EAAmBC,mBAAnB,IAA0Cf,QAAQ,CACtD,EADsD,CAAxD;AAIA,QAAM,CAACgB,iBAAD,EAAoBC,oBAApB,IAA4CjB,QAAQ,CACxDE,WAAW,CAACgB,QAAZ,CAAqBpB,KAAK,CAACqB,MAAN,CAAaC,MAAlC,CADwD,CAA1D;AAGA,QAAM,CAACC,qBAAD,EAAwBC,wBAAxB,IAAoDtB,QAAQ,CAChEE,WAAW,CAACgB,QAAZ,CAAqBpB,KAAK,CAACqB,MAAN,CAAaI,UAAlC,CADgE,CAAlE;AAGA,QAAM,CAACC,sBAAD,EAAyBC,yBAAzB,IAAsDzB,QAAQ,CAClEE,WAAW,CAACgB,QAAZ,CAAqBpB,KAAK,CAACqB,MAAN,CAAaO,WAAlC,CADkE,CAApE;AAGA3B,EAAAA,SAAS,CAAC,MAAM;AACd,UAAM4B,qBAAqB,GAAG,MAAM;AAClCd,MAAAA,eAAe,CAACe,KAAK,CAACC,IAAN,CAAW3B,WAAW,CAAC4B,MAAZ,CAAmBC,MAAnB,EAAX,CAAD,CAAf;AACAd,MAAAA,oBAAoB,CAACf,WAAW,CAACgB,QAAZ,CAAqBpB,KAAK,CAACqB,MAAN,CAAaC,MAAlC,CAAD,CAApB;AACAE,MAAAA,wBAAwB,CAACpB,WAAW,CAACgB,QAAZ,CAAqBpB,KAAK,CAACqB,MAAN,CAAaI,UAAlC,CAAD,CAAxB;AACAE,MAAAA,yBAAyB,CAACvB,WAAW,CAACgB,QAAZ,CAAqBpB,KAAK,CAACqB,MAAN,CAAaO,WAAlC,CAAD,CAAzB;AACAX,MAAAA,mBAAmB,CACjBa,KAAK,CAACC,IAAN,CAAW3B,WAAW,CAAC4B,MAAZ,CAAmBC,MAAnB,EAAX,EAAwCC,MAAxC,CAAgDC,GAAD,IAAS;AACtD,eAAOA,GAAG,CAACC,YAAJ,IAAoBD,GAAG,CAACE,KAAJ,KAAcC,SAAzC;AACD,OAFD,CADiB,CAAnB;AAKD,KAVD;;AAWA,UAAMC,OAAO,GAAIJ,GAAD,IAA2B;AACzC,UAAIA,GAAG,CAACK,IAAJ,KAAaxC,KAAK,CAACyC,IAAN,CAAWC,KAA5B,EAAmC;AACjCpC,QAAAA,aAAa,CAAC,IAAD,CAAb;AACD,OAFD,MAEO,IAAI6B,GAAG,CAACK,IAAJ,KAAaxC,KAAK,CAACyC,IAAN,CAAWE,KAA5B,EAAmC;AACxCpC,QAAAA,aAAa,CAAC,IAAD,CAAb;AACD;AACF,KAND;;AAOA,UAAMqC,SAAS,GAAIT,GAAD,IAA2B;AAC3C,UAAIA,GAAG,CAACK,IAAJ,KAAaxC,KAAK,CAACyC,IAAN,CAAWC,KAA5B,EAAmC;AACjCpC,QAAAA,aAAa,CAAC,KAAD,CAAb;AACD,OAFD,MAEO,IAAI6B,GAAG,CAACK,IAAJ,KAAaxC,KAAK,CAACyC,IAAN,CAAWE,KAA5B,EAAmC;AACxCpC,QAAAA,aAAa,CAAC,KAAD,CAAb;AACD;AACF,KAND;;AAOA,UAAMsC,iBAAiB,GAAG,MAAM;AAC9B,UAAIzC,WAAW,CAACQ,QAAhB,EAA0B;AACxBC,QAAAA,WAAW,CAACT,WAAW,CAACQ,QAAb,CAAX;AACD;AACF,KAJD;;AAKA,UAAMkC,mBAAmB,GAAG,MAAM;AAChCnC,MAAAA,WAAW,CAACP,WAAW,CAACM,UAAb,CAAX;AACD,KAFD;;AAGA,UAAMqC,yBAAyB,GAAG,MAAM;AACtCtC,MAAAA,oBAAoB,CAACL,WAAW,CAACI,iBAAb,CAApB;AACD,KAFD,CAlCc,CAsCd;;;AACAJ,IAAAA,WAAW,CACR4C,EADH,CACMjD,gBAAgB,CAACkD,UADvB,EACmCV,OADnC,EAEGS,EAFH,CAEMjD,gBAAgB,CAACmD,YAFvB,EAEqCN,SAFrC,EAGGI,EAHH,CAGMjD,gBAAgB,CAACoD,0BAHvB,EAGmDN,iBAHnD,EAIGG,EAJH,CAIMjD,gBAAgB,CAACqD,iBAJvB,EAI0CN,mBAJ1C,EAKGE,EALH,CAKMjD,gBAAgB,CAACsD,cALvB,EAKuCxB,qBALvC,EAMGmB,EANH,CAMMjD,gBAAgB,CAACuD,gBANvB,EAMyCzB,qBANzC,EAOGmB,EAPH,CAOMjD,gBAAgB,CAACwD,eAPvB,EAOwC1B,qBAPxC,EAQGmB,EARH,CAQMjD,gBAAgB,CAACyD,iBARvB,EAQ0C3B,qBAR1C,EASGmB,EATH,CASMjD,gBAAgB,CAAC0D,mBATvB,EAS4C5B,qBAT5C,EAUGmB,EAVH,CAUMjD,gBAAgB,CAAC2D,qBAVvB,EAU8C7B,qBAV9C,EAWGmB,EAXH,CAWMjD,gBAAgB,CAAC4D,wBAXvB,EAWiDZ,yBAXjD,EAvCc,CAoDd;;AACAF,IAAAA,iBAAiB;AACjBC,IAAAA,mBAAmB;AACnBjB,IAAAA,qBAAqB;AAErB,WAAO,MAAM;AACX;AACAzB,MAAAA,WAAW,CACRwD,GADH,CACO7D,gBAAgB,CAACkD,UADxB,EACoCV,OADpC,EAEGqB,GAFH,CAEO7D,gBAAgB,CAACmD,YAFxB,EAEsCN,SAFtC,EAGGgB,GAHH,CAGO7D,gBAAgB,CAACoD,0BAHxB,EAGoDN,iBAHpD,EAIGe,GAJH,CAIO7D,gBAAgB,CAACqD,iBAJxB,EAI2CN,mBAJ3C,EAKGc,GALH,CAKO7D,gBAAgB,CAACsD,cALxB,EAKwCxB,qBALxC,EAMG+B,GANH,CAMO7D,gBAAgB,CAACuD,gBANxB,EAM0CzB,qBAN1C,EAOG+B,GAPH,CAOO7D,gBAAgB,CAACwD,eAPxB,EAOyC1B,qBAPzC,EAQG+B,GARH,CAQO7D,gBAAgB,CAACyD,iBARxB,EAQ2C3B,qBAR3C,EASG+B,GATH,CASO7D,gBAAgB,CAAC0D,mBATxB,EAS6C5B,qBAT7C,EAUG+B,GAVH,CAUO7D,gBAAgB,CAAC2D,qBAVxB,EAU+C7B,qBAV/C,EAWG+B,GAXH,CAYI7D,gBAAgB,CAAC4D,wBAZrB,EAaIZ,yBAbJ;AAeD,KAjBD;AAkBD,GA3EQ,EA2EN,CAAC3C,WAAD,CA3EM,CAAT;AA6EA,MAAIyD,KAAJ;AACAzD,EAAAA,WAAW,CAAC0D,WAAZ,CAAwBC,OAAxB,CAAiC5B,GAAD,IAAS;AACvC0B,IAAAA,KAAK,GAAG1B,GAAG,CAAC6B,OAAZ;AACD,GAFD;;AAGA,MAAIH,KAAK,KAAKvB,SAAd,EAAyB;AACvBuB,IAAAA,KAAK,GAAG,IAAR;AACD;;AACD,MAAIxD,YAAY,KAAKwD,KAArB,EAA4B;AAC1BvD,IAAAA,aAAa,CAACuD,KAAD,CAAb;AACD;;AAED,SAAO;AACLI,IAAAA,OAAO,EAAE7D,WAAW,YAAYN,gBAD3B;AAELY,IAAAA,UAFK;AAGLF,IAAAA,iBAHK;AAILM,IAAAA,YAJK;AAKLE,IAAAA,gBALK;AAMLE,IAAAA,iBANK;AAOLK,IAAAA,qBAPK;AAQLG,IAAAA,sBARK;AASLd,IAAAA;AATK,GAAP;AAWD","sourcesContent":["import {\n ConnectionQuality,\n LocalParticipant,\n Participant,\n ParticipantEvent,\n Track,\n TrackPublication,\n} from 'livekit-client';\nimport { useEffect, useState } from 'react';\n\nexport interface ParticipantState {\n isSpeaking: boolean;\n connectionQuality: ConnectionQuality;\n isLocal: boolean;\n metadata?: string;\n publications: TrackPublication[];\n subscribedTracks: TrackPublication[];\n cameraPublication?: TrackPublication;\n microphonePublication?: TrackPublication;\n screenSharePublication?: TrackPublication;\n}\n\nexport function useParticipant(participant: Participant): ParticipantState {\n const [isAudioMuted, setAudioMuted] = useState(false);\n const [, setVideoMuted] = useState(false);\n const [connectionQuality, setConnectionQuality] = useState<ConnectionQuality>(\n participant.connectionQuality\n );\n const [isSpeaking, setSpeaking] = useState(false);\n const [metadata, setMetadata] = useState<string>();\n const [publications, setPublications] = useState<TrackPublication[]>([]);\n const [subscribedTracks, setSubscribedTracks] = useState<TrackPublication[]>(\n []\n );\n\n const [cameraPublication, setCameraPublication] = useState(\n participant.getTrack(Track.Source.Camera)\n );\n const [microphonePublication, setMicrophonePublication] = useState(\n participant.getTrack(Track.Source.Microphone)\n );\n const [screenSharePublication, setScreenSharePublication] = useState(\n participant.getTrack(Track.Source.ScreenShare)\n );\n useEffect(() => {\n const onPublicationsChanged = () => {\n setPublications(Array.from(participant.tracks.values()));\n setCameraPublication(participant.getTrack(Track.Source.Camera));\n setMicrophonePublication(participant.getTrack(Track.Source.Microphone));\n setScreenSharePublication(participant.getTrack(Track.Source.ScreenShare));\n setSubscribedTracks(\n Array.from(participant.tracks.values()).filter((pub) => {\n return pub.isSubscribed && pub.track !== undefined;\n })\n );\n };\n const onMuted = (pub: TrackPublication) => {\n if (pub.kind === Track.Kind.Audio) {\n setAudioMuted(true);\n } else if (pub.kind === Track.Kind.Video) {\n setVideoMuted(true);\n }\n };\n const onUnmuted = (pub: TrackPublication) => {\n if (pub.kind === Track.Kind.Audio) {\n setAudioMuted(false);\n } else if (pub.kind === Track.Kind.Video) {\n setVideoMuted(false);\n }\n };\n const onMetadataChanged = () => {\n if (participant.metadata) {\n setMetadata(participant.metadata);\n }\n };\n const onIsSpeakingChanged = () => {\n setSpeaking(participant.isSpeaking);\n };\n const onConnectionQualityUpdate = () => {\n setConnectionQuality(participant.connectionQuality);\n };\n\n // register listeners\n participant\n .on(ParticipantEvent.TrackMuted, onMuted)\n .on(ParticipantEvent.TrackUnmuted, onUnmuted)\n .on(ParticipantEvent.ParticipantMetadataChanged, onMetadataChanged)\n .on(ParticipantEvent.IsSpeakingChanged, onIsSpeakingChanged)\n .on(ParticipantEvent.TrackPublished, onPublicationsChanged)\n .on(ParticipantEvent.TrackUnpublished, onPublicationsChanged)\n .on(ParticipantEvent.TrackSubscribed, onPublicationsChanged)\n .on(ParticipantEvent.TrackUnsubscribed, onPublicationsChanged)\n .on(ParticipantEvent.LocalTrackPublished, onPublicationsChanged)\n .on(ParticipantEvent.LocalTrackUnpublished, onPublicationsChanged)\n .on(ParticipantEvent.ConnectionQualityChanged, onConnectionQualityUpdate);\n\n // set initial state\n onMetadataChanged();\n onIsSpeakingChanged();\n onPublicationsChanged();\n\n return () => {\n // cleanup\n participant\n .off(ParticipantEvent.TrackMuted, onMuted)\n .off(ParticipantEvent.TrackUnmuted, onUnmuted)\n .off(ParticipantEvent.ParticipantMetadataChanged, onMetadataChanged)\n .off(ParticipantEvent.IsSpeakingChanged, onIsSpeakingChanged)\n .off(ParticipantEvent.TrackPublished, onPublicationsChanged)\n .off(ParticipantEvent.TrackUnpublished, onPublicationsChanged)\n .off(ParticipantEvent.TrackSubscribed, onPublicationsChanged)\n .off(ParticipantEvent.TrackUnsubscribed, onPublicationsChanged)\n .off(ParticipantEvent.LocalTrackPublished, onPublicationsChanged)\n .off(ParticipantEvent.LocalTrackUnpublished, onPublicationsChanged)\n .off(\n ParticipantEvent.ConnectionQualityChanged,\n onConnectionQualityUpdate\n );\n };\n }, [participant]);\n\n let muted: boolean | undefined;\n participant.audioTracks.forEach((pub) => {\n muted = pub.isMuted;\n });\n if (muted === undefined) {\n muted = true;\n }\n if (isAudioMuted !== muted) {\n setAudioMuted(muted);\n }\n\n return {\n isLocal: participant instanceof LocalParticipant,\n isSpeaking,\n connectionQuality,\n publications,\n subscribedTracks,\n cameraPublication,\n microphonePublication,\n screenSharePublication,\n metadata,\n };\n}\n"]}
@@ -0,0 +1,126 @@
1
+ import { ConnectionState, RoomEvent, Track } from 'livekit-client';
2
+ import { useEffect, useState } from 'react';
3
+ export function useRoom(room, options) {
4
+ var _options$sortParticip;
5
+
6
+ const [error] = useState();
7
+ const [participants, setParticipants] = useState([]);
8
+ const [audioTracks, setAudioTracks] = useState([]);
9
+ const sortFunc = (_options$sortParticip = options === null || options === void 0 ? void 0 : options.sortParticipants) !== null && _options$sortParticip !== void 0 ? _options$sortParticip : sortParticipants;
10
+ useEffect(() => {
11
+ const onParticipantsChanged = () => {
12
+ const remotes = Array.from(room.participants.values());
13
+ const newParticipants = [room.localParticipant];
14
+ newParticipants.push(...remotes);
15
+ sortFunc(newParticipants, room.localParticipant);
16
+ setParticipants(newParticipants);
17
+ };
18
+
19
+ const onSubscribedTrackChanged = track => {
20
+ // ordering may have changed, re-sort
21
+ onParticipantsChanged();
22
+
23
+ if (track && track.kind !== Track.Kind.Audio) {
24
+ return;
25
+ }
26
+
27
+ const tracks = [];
28
+ room.participants.forEach(p => {
29
+ p.audioTracks.forEach(pub => {
30
+ if (pub.audioTrack) {
31
+ tracks.push(pub.audioTrack);
32
+ }
33
+ });
34
+ });
35
+ setAudioTracks(tracks);
36
+ };
37
+
38
+ const onConnectionStateChanged = state => {
39
+ if (state === ConnectionState.Connected) {
40
+ onParticipantsChanged();
41
+ }
42
+ };
43
+
44
+ room.once(RoomEvent.Disconnected, () => {
45
+ room.off(RoomEvent.ParticipantConnected, onParticipantsChanged).off(RoomEvent.ParticipantDisconnected, onParticipantsChanged).off(RoomEvent.ActiveSpeakersChanged, onParticipantsChanged).off(RoomEvent.TrackSubscribed, onSubscribedTrackChanged).off(RoomEvent.TrackUnsubscribed, onSubscribedTrackChanged).off(RoomEvent.LocalTrackPublished, onParticipantsChanged).off(RoomEvent.LocalTrackUnpublished, onParticipantsChanged).off(RoomEvent.AudioPlaybackStatusChanged, onParticipantsChanged).off(RoomEvent.ConnectionStateChanged, onConnectionStateChanged);
46
+ });
47
+ room.on(RoomEvent.ConnectionStateChanged, onConnectionStateChanged).on(RoomEvent.Reconnected, onParticipantsChanged).on(RoomEvent.ParticipantConnected, onParticipantsChanged).on(RoomEvent.ParticipantDisconnected, onParticipantsChanged).on(RoomEvent.ActiveSpeakersChanged, onParticipantsChanged).on(RoomEvent.TrackSubscribed, onSubscribedTrackChanged).on(RoomEvent.TrackUnsubscribed, onSubscribedTrackChanged).on(RoomEvent.LocalTrackPublished, onParticipantsChanged).on(RoomEvent.LocalTrackUnpublished, onParticipantsChanged) // trigger a state change by re-sorting participants
48
+ .on(RoomEvent.AudioPlaybackStatusChanged, onParticipantsChanged);
49
+ onSubscribedTrackChanged();
50
+ return () => {
51
+ room.disconnect();
52
+ };
53
+ }, [room, sortFunc]);
54
+ return {
55
+ error,
56
+ participants,
57
+ audioTracks
58
+ };
59
+ }
60
+ /**
61
+ * Default sort for participants, it'll order participants by:
62
+ * 1. dominant speaker (speaker with the loudest audio level)
63
+ * 2. local participant
64
+ * 3. other speakers that are recently active
65
+ * 4. participants with video on
66
+ * 5. by joinedAt
67
+ */
68
+
69
+ export function sortParticipants(participants, localParticipant) {
70
+ participants.sort((a, b) => {
71
+ var _a$joinedAt$getTime, _a$joinedAt, _b$joinedAt$getTime, _b$joinedAt;
72
+
73
+ // loudest speaker first
74
+ if (a.isSpeaking && b.isSpeaking) {
75
+ return b.audioLevel - a.audioLevel;
76
+ } // speaker goes first
77
+
78
+
79
+ if (a.isSpeaking !== b.isSpeaking) {
80
+ if (a.isSpeaking) {
81
+ return -1;
82
+ } else {
83
+ return 1;
84
+ }
85
+ } // last active speaker first
86
+
87
+
88
+ if (a.lastSpokeAt !== b.lastSpokeAt) {
89
+ var _a$lastSpokeAt$getTim, _a$lastSpokeAt, _b$lastSpokeAt$getTim, _b$lastSpokeAt;
90
+
91
+ const aLast = (_a$lastSpokeAt$getTim = (_a$lastSpokeAt = a.lastSpokeAt) === null || _a$lastSpokeAt === void 0 ? void 0 : _a$lastSpokeAt.getTime()) !== null && _a$lastSpokeAt$getTim !== void 0 ? _a$lastSpokeAt$getTim : 0;
92
+ const bLast = (_b$lastSpokeAt$getTim = (_b$lastSpokeAt = b.lastSpokeAt) === null || _b$lastSpokeAt === void 0 ? void 0 : _b$lastSpokeAt.getTime()) !== null && _b$lastSpokeAt$getTim !== void 0 ? _b$lastSpokeAt$getTim : 0;
93
+ return bLast - aLast;
94
+ } // video on
95
+
96
+
97
+ const aVideo = a.videoTracks.size > 0;
98
+ const bVideo = b.videoTracks.size > 0;
99
+
100
+ if (aVideo !== bVideo) {
101
+ if (aVideo) {
102
+ return -1;
103
+ } else {
104
+ return 1;
105
+ }
106
+ } // joinedAt
107
+
108
+
109
+ return ((_a$joinedAt$getTime = (_a$joinedAt = a.joinedAt) === null || _a$joinedAt === void 0 ? void 0 : _a$joinedAt.getTime()) !== null && _a$joinedAt$getTime !== void 0 ? _a$joinedAt$getTime : 0) - ((_b$joinedAt$getTime = (_b$joinedAt = b.joinedAt) === null || _b$joinedAt === void 0 ? void 0 : _b$joinedAt.getTime()) !== null && _b$joinedAt$getTime !== void 0 ? _b$joinedAt$getTime : 0);
110
+ });
111
+
112
+ if (localParticipant) {
113
+ const localIdx = participants.indexOf(localParticipant);
114
+
115
+ if (localIdx >= 0) {
116
+ participants.splice(localIdx, 1);
117
+
118
+ if (participants.length > 0) {
119
+ participants.splice(1, 0, localParticipant);
120
+ } else {
121
+ participants.push(localParticipant);
122
+ }
123
+ }
124
+ }
125
+ }
126
+ //# sourceMappingURL=useRoom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["useRoom.ts"],"names":["ConnectionState","RoomEvent","Track","useEffect","useState","useRoom","room","options","error","participants","setParticipants","audioTracks","setAudioTracks","sortFunc","sortParticipants","onParticipantsChanged","remotes","Array","from","values","newParticipants","localParticipant","push","onSubscribedTrackChanged","track","kind","Kind","Audio","tracks","forEach","p","pub","audioTrack","onConnectionStateChanged","state","Connected","once","Disconnected","off","ParticipantConnected","ParticipantDisconnected","ActiveSpeakersChanged","TrackSubscribed","TrackUnsubscribed","LocalTrackPublished","LocalTrackUnpublished","AudioPlaybackStatusChanged","ConnectionStateChanged","on","Reconnected","disconnect","sort","a","b","isSpeaking","audioLevel","lastSpokeAt","aLast","getTime","bLast","aVideo","videoTracks","size","bVideo","joinedAt","localIdx","indexOf","splice","length"],"mappings":"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","sourcesContent":["import {\n AudioTrack,\n ConnectionState,\n LocalParticipant,\n Participant,\n RemoteTrack,\n Room,\n RoomEvent,\n Track,\n} from 'livekit-client';\nimport { useEffect, useState } from 'react';\n\nexport interface RoomState {\n room?: Room;\n /* all participants in the room, including the local participant. */\n participants: Participant[];\n /* all subscribed audio tracks in the room, not including local participant. */\n audioTracks: AudioTrack[];\n error?: Error;\n}\n\nexport interface RoomOptions {\n sortParticipants?: (participants: Participant[]) => void;\n}\n\nexport function useRoom(room: Room, options?: RoomOptions): RoomState {\n const [error] = useState<Error>();\n const [participants, setParticipants] = useState<Participant[]>([]);\n const [audioTracks, setAudioTracks] = useState<AudioTrack[]>([]);\n\n const sortFunc = options?.sortParticipants ?? sortParticipants;\n\n useEffect(() => {\n const onParticipantsChanged = () => {\n const remotes = Array.from(room.participants.values());\n const newParticipants: Participant[] = [room.localParticipant];\n newParticipants.push(...remotes);\n sortFunc(newParticipants, room.localParticipant);\n setParticipants(newParticipants);\n };\n const onSubscribedTrackChanged = (track?: RemoteTrack) => {\n // ordering may have changed, re-sort\n onParticipantsChanged();\n if (track && track.kind !== Track.Kind.Audio) {\n return;\n }\n const tracks: AudioTrack[] = [];\n room.participants.forEach((p) => {\n p.audioTracks.forEach((pub) => {\n if (pub.audioTrack) {\n tracks.push(pub.audioTrack);\n }\n });\n });\n setAudioTracks(tracks);\n };\n\n const onConnectionStateChanged = (state: ConnectionState) => {\n if (state === ConnectionState.Connected) {\n onParticipantsChanged();\n }\n };\n\n room.once(RoomEvent.Disconnected, () => {\n room\n .off(RoomEvent.ParticipantConnected, onParticipantsChanged)\n .off(RoomEvent.ParticipantDisconnected, onParticipantsChanged)\n .off(RoomEvent.ActiveSpeakersChanged, onParticipantsChanged)\n .off(RoomEvent.TrackSubscribed, onSubscribedTrackChanged)\n .off(RoomEvent.TrackUnsubscribed, onSubscribedTrackChanged)\n .off(RoomEvent.LocalTrackPublished, onParticipantsChanged)\n .off(RoomEvent.LocalTrackUnpublished, onParticipantsChanged)\n .off(RoomEvent.AudioPlaybackStatusChanged, onParticipantsChanged)\n .off(RoomEvent.ConnectionStateChanged, onConnectionStateChanged);\n });\n room\n .on(RoomEvent.ConnectionStateChanged, onConnectionStateChanged)\n .on(RoomEvent.Reconnected, onParticipantsChanged)\n .on(RoomEvent.ParticipantConnected, onParticipantsChanged)\n .on(RoomEvent.ParticipantDisconnected, onParticipantsChanged)\n .on(RoomEvent.ActiveSpeakersChanged, onParticipantsChanged)\n .on(RoomEvent.TrackSubscribed, onSubscribedTrackChanged)\n .on(RoomEvent.TrackUnsubscribed, onSubscribedTrackChanged)\n .on(RoomEvent.LocalTrackPublished, onParticipantsChanged)\n .on(RoomEvent.LocalTrackUnpublished, onParticipantsChanged)\n // trigger a state change by re-sorting participants\n .on(RoomEvent.AudioPlaybackStatusChanged, onParticipantsChanged);\n\n onSubscribedTrackChanged();\n\n return () => {\n room.disconnect();\n };\n }, [room, sortFunc]);\n\n return {\n error,\n participants,\n audioTracks,\n };\n}\n\n/**\n * Default sort for participants, it'll order participants by:\n * 1. dominant speaker (speaker with the loudest audio level)\n * 2. local participant\n * 3. other speakers that are recently active\n * 4. participants with video on\n * 5. by joinedAt\n */\nexport function sortParticipants(\n participants: Participant[],\n localParticipant?: LocalParticipant\n) {\n participants.sort((a, b) => {\n // loudest speaker first\n if (a.isSpeaking && b.isSpeaking) {\n return b.audioLevel - a.audioLevel;\n }\n\n // speaker goes first\n if (a.isSpeaking !== b.isSpeaking) {\n if (a.isSpeaking) {\n return -1;\n } else {\n return 1;\n }\n }\n\n // last active speaker first\n if (a.lastSpokeAt !== b.lastSpokeAt) {\n const aLast = a.lastSpokeAt?.getTime() ?? 0;\n const bLast = b.lastSpokeAt?.getTime() ?? 0;\n return bLast - aLast;\n }\n\n // video on\n const aVideo = a.videoTracks.size > 0;\n const bVideo = b.videoTracks.size > 0;\n if (aVideo !== bVideo) {\n if (aVideo) {\n return -1;\n } else {\n return 1;\n }\n }\n\n // joinedAt\n return (a.joinedAt?.getTime() ?? 0) - (b.joinedAt?.getTime() ?? 0);\n });\n\n if (localParticipant) {\n const localIdx = participants.indexOf(localParticipant);\n if (localIdx >= 0) {\n participants.splice(localIdx, 1);\n if (participants.length > 0) {\n participants.splice(1, 0, localParticipant);\n } else {\n participants.push(localParticipant);\n }\n }\n }\n}\n"]}