agora-appbuilder-core 4.0.0-test.10 → 4.0.0-test.12

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agora-appbuilder-core",
3
- "version": "4.0.0-test.10",
3
+ "version": "4.0.0-test.12",
4
4
  "description": "React Native template for RTE app builder",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -9,7 +9,7 @@
9
9
  ],
10
10
  "scripts": {
11
11
  "vercel-build": "npm run dev-setup && cd template && npm run web:build && cd .. && npm run copy-vercel",
12
- "uikit": "rm -rf template/agora-rn-uikit && git clone https://github.com/AgoraIO-Community/ReactNative-UIKit.git template/agora-rn-uikit && cd template/agora-rn-uikit && git checkout feature/recording-2-0",
12
+ "uikit": "rm -rf template/agora-rn-uikit && git clone https://github.com/AgoraIO-Community/ReactNative-UIKit.git template/agora-rn-uikit && cd template/agora-rn-uikit && git checkout v3-release-fixes-encryption",
13
13
  "deps": "cd template && npm i",
14
14
  "dev-setup": "npm run uikit && npm run deps && node devSetup.js",
15
15
  "web-build": "cd template && npm run web:build && cd .. && npm run copy-vercel",
@@ -20,7 +20,7 @@
20
20
  "agora-extension-ai-denoiser": "1.1.0",
21
21
  "agora-extension-virtual-background": "^1.1.3",
22
22
  "agora-react-native-rtm": "1.5.1",
23
- "agora-rtc-sdk-ng": "4.20.1",
23
+ "agora-rtc-sdk-ng": "4.19.0",
24
24
  "agora-rtm-sdk": "1.5.1",
25
25
  "buffer": "^6.0.3",
26
26
  "electron-log": "4.3.5",
@@ -7668,14 +7668,14 @@
7668
7668
  }
7669
7669
  },
7670
7670
  "node_modules/agora-rtc-sdk-ng": {
7671
- "version": "4.20.1",
7672
- "resolved": "https://registry.npmjs.org/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.20.1.tgz",
7673
- "integrity": "sha512-k/heeT3DE72K6D1sZCN1k5K2wIBzSwW4q1hsxk9AOQLdrdPY5GFWQVn66k/WGyP67lEPFe0ZTKFh9+8Gc284FA==",
7674
- "dependencies": {
7675
- "@agora-js/media": "4.20.1",
7676
- "@agora-js/report": "4.20.1",
7677
- "@agora-js/shared": "4.20.1",
7678
- "agora-rte-extension": "^1.2.4",
7671
+ "version": "4.19.0",
7672
+ "resolved": "https://registry.npmjs.org/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.19.0.tgz",
7673
+ "integrity": "sha512-uVljhNooPWnJQGOvGWLPtjHuTET15pLz1lEPA1NFNoe94/yl6Ib5Ayl253+Y0gSzQwrQswRuRIITNNec/eXpSw==",
7674
+ "dependencies": {
7675
+ "@agora-js/media": "^4.19.0",
7676
+ "@agora-js/report": "^4.19.0",
7677
+ "@agora-js/shared": "^4.19.0",
7678
+ "agora-rte-extension": "^1.2.3",
7679
7679
  "axios": "^0.27.2",
7680
7680
  "formdata-polyfill": "^4.0.7",
7681
7681
  "ua-parser-js": "^0.7.34",
@@ -37782,14 +37782,14 @@
37782
37782
  "requires": {}
37783
37783
  },
37784
37784
  "agora-rtc-sdk-ng": {
37785
- "version": "4.20.1",
37786
- "resolved": "https://registry.npmjs.org/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.20.1.tgz",
37787
- "integrity": "sha512-k/heeT3DE72K6D1sZCN1k5K2wIBzSwW4q1hsxk9AOQLdrdPY5GFWQVn66k/WGyP67lEPFe0ZTKFh9+8Gc284FA==",
37788
- "requires": {
37789
- "@agora-js/media": "4.20.1",
37790
- "@agora-js/report": "4.20.1",
37791
- "@agora-js/shared": "4.20.1",
37792
- "agora-rte-extension": "^1.2.4",
37785
+ "version": "4.19.0",
37786
+ "resolved": "https://registry.npmjs.org/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.19.0.tgz",
37787
+ "integrity": "sha512-uVljhNooPWnJQGOvGWLPtjHuTET15pLz1lEPA1NFNoe94/yl6Ib5Ayl253+Y0gSzQwrQswRuRIITNNec/eXpSw==",
37788
+ "requires": {
37789
+ "@agora-js/media": "^4.19.0",
37790
+ "@agora-js/report": "^4.19.0",
37791
+ "@agora-js/shared": "^4.19.0",
37792
+ "agora-rte-extension": "^1.2.3",
37793
37793
  "axios": "^0.27.2",
37794
37794
  "formdata-polyfill": "^4.0.7",
37795
37795
  "ua-parser-js": "^0.7.34",
@@ -136,7 +136,6 @@ export interface RtcPropsInterface {
136
136
  //core only
137
137
  screenShareUid?: number;
138
138
  screenShareToken?: string;
139
- recordingBot?: false;
140
139
  //core only
141
140
  }
142
141
 
@@ -15,7 +15,6 @@ import PropsContext, {
15
15
  PermissionState,
16
16
  } from '../Contexts/PropsContext';
17
17
  import quality from '../Utils/quality';
18
- import {isBotUser} from '../Utils/isBotUser';
19
18
 
20
19
  const Create = ({
21
20
  dispatch,
@@ -297,21 +296,14 @@ const Create = ({
297
296
  if (
298
297
  !(
299
298
  mode === ChannelProfile.LiveBroadcasting &&
300
- rtcProps?.role == ClientRole.Audience &&
301
- Platform.OS === 'web' &&
302
- rtcProps?.recordingBot
299
+ rtcProps.role == ClientRole.Audience &&
300
+ Platform.OS === 'web'
303
301
  )
304
302
  ) {
305
- if (rtcProps?.recordingBot) {
306
- console.log(
307
- 'supriya recordingBot, hence not asking audio/video permission',
308
- );
309
- } else {
310
- enableVideoAndAudioWithInitialStates().then(() => {
311
- setTracksReady(true);
312
- isVideoEnabledRef.current = true;
313
- });
314
- }
303
+ enableVideoAndAudioWithInitialStates().then(() => {
304
+ setTracksReady(true);
305
+ isVideoEnabledRef.current = true;
306
+ });
315
307
  }
316
308
 
317
309
  engine.current.addListener(
@@ -330,8 +322,9 @@ const Create = ({
330
322
  );
331
323
 
332
324
  engine.current.addListener('UserJoined', async (...args) => {
333
- // preventing bots(ex: STT, recording) in renderlist
334
- if (isBotUser(args)) {
325
+ // preventing STT pusher bot in renderlist
326
+ //@ts-ignore
327
+ if (args[0] === 111111) {
335
328
  return;
336
329
  }
337
330
  //Get current peer IDs
@@ -45,7 +45,7 @@ const Join: React.FC<{
45
45
  }
46
46
  const {defaultContent, activeUids} = uidState;
47
47
  const [maxUid] = activeUids;
48
- const videoState = defaultContent[maxUid]?.video;
48
+ const videoState = defaultContent[maxUid].video;
49
49
  async function join() {
50
50
  if (
51
51
  rtcProps?.encryption &&
@@ -47,42 +47,23 @@ const RtcConfigure = (props: {children: React.ReactNode}) => {
47
47
  );
48
48
  const localUid = useLocalUid();
49
49
 
50
- const initialLocalState: Partial<ContentStateInterface> =
51
- rtcProps?.recordingBot
52
- ? {
53
- customContent: {},
54
- defaultContent: {
55
- [localUid]: {
56
- uid: localUid,
57
- audio: ToggleState.disabled,
58
- video: ToggleState.disabled,
59
- streamType: 'high',
60
- type: 'rtc',
61
- permissionStatus: PermissionState.NOT_REQUESTED,
62
- },
63
- },
64
- activeUids: [],
65
- pinnedUid: undefined,
66
- secondaryPinnedUid: undefined,
67
- lastJoinedUid: 0,
68
- }
69
- : {
70
- customContent: {},
71
- defaultContent: {
72
- [localUid]: {
73
- uid: localUid,
74
- audio: ToggleState.disabled,
75
- video: ToggleState.disabled,
76
- streamType: 'high',
77
- type: 'rtc',
78
- permissionStatus: PermissionState.NOT_REQUESTED,
79
- },
80
- },
81
- activeUids: [localUid],
82
- pinnedUid: undefined,
83
- secondaryPinnedUid: undefined,
84
- lastJoinedUid: 0,
85
- };
50
+ const initialLocalState: Partial<ContentStateInterface> = {
51
+ customContent: {},
52
+ defaultContent: {
53
+ [localUid]: {
54
+ uid: localUid,
55
+ audio: ToggleState.disabled,
56
+ video: ToggleState.disabled,
57
+ streamType: 'high',
58
+ type: 'rtc',
59
+ permissionStatus: PermissionState.NOT_REQUESTED,
60
+ },
61
+ },
62
+ activeUids: [localUid],
63
+ pinnedUid: undefined,
64
+ secondaryPinnedUid: undefined,
65
+ lastJoinedUid: 0,
66
+ };
86
67
 
87
68
  const [initialState, setInitialState] = React.useState(
88
69
  JSON.parse(JSON.stringify(initialLocalState)),
@@ -60,7 +60,7 @@
60
60
  "agora-extension-ai-denoiser": "1.1.0",
61
61
  "agora-extension-virtual-background": "^1.1.3",
62
62
  "agora-react-native-rtm": "1.5.1",
63
- "agora-rtc-sdk-ng": "4.20.1",
63
+ "agora-rtc-sdk-ng": "4.19.0",
64
64
  "agora-rtm-sdk": "1.5.1",
65
65
  "buffer": "^6.0.3",
66
66
  "electron-log": "4.3.5",
@@ -17,21 +17,6 @@ import {Route, Switch, Redirect} from './components/Router';
17
17
  import AuthRoute from './auth/AuthRoute';
18
18
  import {IDPAuth} from './auth/IDPAuth';
19
19
  import {Text} from 'react-native';
20
- import RecordingBotRoute from './components/recording-bot/RecordingBotRoute';
21
- import {useIsRecordingBot} from './subComponents/recording/useIsRecordingBot';
22
-
23
- function VideoCallWrapper(props) {
24
- const {isRecordingBotRoute} = useIsRecordingBot();
25
- return isRecordingBotRoute ? (
26
- <RecordingBotRoute history={props.history}>
27
- <VideoCall />
28
- </RecordingBotRoute>
29
- ) : (
30
- <AuthRoute>
31
- <VideoCall />
32
- </AuthRoute>
33
- );
34
- }
35
20
 
36
21
  function AppRoutes() {
37
22
  return (
@@ -48,7 +33,9 @@ function AppRoutes() {
48
33
  <AuthRoute exact path={'/create'}>
49
34
  <Create />
50
35
  </AuthRoute>
51
- <Route exact path={'/:phrase'} component={VideoCallWrapper} />
36
+ <AuthRoute path={'/:phrase'}>
37
+ <VideoCall />
38
+ </AuthRoute>
52
39
  <Route path="*">
53
40
  <Text>Page not found</Text>
54
41
  </Route>
@@ -87,24 +87,22 @@ const AppWrapper = (props: AppWrapperProps) => {
87
87
  <StatusBar hidden={true} />
88
88
  <StorageProvider>
89
89
  <LanguageProvider>
90
- <GraphQLProvider>
91
- <Router
92
- /*@ts-ignore Router will be memory Router in sdk*/
93
- initialEntries={[
94
- //@ts-ignore
95
- isSDK && SdkJoinState.phrase
96
- ? //@ts-ignore
97
- `/${SdkJoinState.phrase}`
98
- : '',
99
- ]}>
100
- <ToastProvider>
101
- <ToastContext.Consumer>
102
- {({isActionSheetVisible}) => {
103
- return !isActionSheetVisible ? (
104
- <ToastComponent />
105
- ) : null;
106
- }}
107
- </ToastContext.Consumer>
90
+ <ToastProvider>
91
+ <ToastContext.Consumer>
92
+ {({isActionSheetVisible}) => {
93
+ return !isActionSheetVisible ? <ToastComponent /> : null;
94
+ }}
95
+ </ToastContext.Consumer>
96
+ <GraphQLProvider>
97
+ <Router
98
+ /*@ts-ignore Router will be memory Router in sdk*/
99
+ initialEntries={[
100
+ //@ts-ignore
101
+ isSDK && SdkJoinState.phrase
102
+ ? //@ts-ignore
103
+ `/${SdkJoinState.phrase}`
104
+ : '',
105
+ ]}>
108
106
  <AuthProvider>
109
107
  <SessionProvider>
110
108
  <ColorConfigure>
@@ -118,9 +116,9 @@ const AppWrapper = (props: AppWrapperProps) => {
118
116
  </ColorConfigure>
119
117
  </SessionProvider>
120
118
  </AuthProvider>
121
- </ToastProvider>
122
- </Router>
123
- </GraphQLProvider>
119
+ </Router>
120
+ </GraphQLProvider>
121
+ </ToastProvider>
124
122
  </LanguageProvider>
125
123
  </StorageProvider>
126
124
  </SafeAreaView>
@@ -39,7 +39,6 @@ import {useLayout} from '../utils/useLayout';
39
39
  import {getGridLayoutName} from '../pages/video-call/DefaultLayouts';
40
40
  import {ChatHeader} from '../pages/video-call/SidePanelHeader';
41
41
  import useCaptionWidth from '../../src/subComponents/caption/useCaptionWidth';
42
- import {useIsRecordingBot} from '../subComponents/recording/useIsRecordingBot';
43
42
 
44
43
  export interface ChatProps {
45
44
  chatBubble?: React.ComponentType<ChatBubbleProps>;
@@ -52,7 +51,7 @@ const Chat = (props?: ChatProps) => {
52
51
  const isSmall = useIsSmall();
53
52
  const {setSidePanel} = useSidePanel();
54
53
  const {showHeader = true} = props;
55
- const {isRecordingBot} = useIsRecordingBot();
54
+
56
55
  const {chatType, setChatType, setPrivateChatUser} = useChatUIControls();
57
56
 
58
57
  const {
@@ -171,13 +170,9 @@ const Chat = (props?: ChatProps) => {
171
170
  {chatType === ChatType.Group ? (
172
171
  <>
173
172
  <ChatContainer {...props} />
174
- {isRecordingBot ? (
175
- <></>
176
- ) : (
177
- <View style={style.chatInputContainer}>
178
- <ChatInputComponent />
179
- </View>
180
- )}
173
+ <View style={style.chatInputContainer}>
174
+ <ChatInputComponent />
175
+ </View>
181
176
  </>
182
177
  ) : (
183
178
  <></>
@@ -190,15 +185,11 @@ const Chat = (props?: ChatProps) => {
190
185
  {chatType === ChatType.Private ? (
191
186
  <>
192
187
  <ChatContainer {...props} />
193
- {isRecordingBot ? (
194
- <></>
195
- ) : (
196
- <View>
197
- <View style={style.chatInputContainer}>
198
- <ChatInputComponent />
199
- </View>
188
+ <View>
189
+ <View style={style.chatInputContainer}>
190
+ <ChatInputComponent />
200
191
  </View>
201
- )}
192
+ </View>
202
193
  </>
203
194
  ) : (
204
195
  <></>
@@ -15,11 +15,13 @@ import {
15
15
  InMemoryCache,
16
16
  ApolloProvider,
17
17
  NormalizedCacheObject,
18
- ApolloLink,
19
18
  // from,
20
19
  } from '@apollo/client';
20
+ import {setContext} from '@apollo/client/link/context';
21
+ // import useMount from './useMount';
21
22
  import React, {createContext, useContext, useEffect, useState} from 'react';
22
23
  import StorageContext from './StorageContext';
24
+ import AsyncStorage from '@react-native-async-storage/async-storage';
23
25
 
24
26
  export const GraphQLContext = createContext<{
25
27
  client: ApolloClient<NormalizedCacheObject>;
@@ -33,57 +35,38 @@ const httpLink = createHttpLink({
33
35
  credentials: 'include',
34
36
  });
35
37
 
36
- const cache = new InMemoryCache();
37
-
38
- const DEFAULT_CLIENT = new ApolloClient({
39
- link: httpLink,
40
- cache: cache,
41
- });
42
-
43
- const authLink = (token: string) => {
44
- /**
45
- * Below we create a new link, whenever a token changes, set
46
- * context with headers and forward the link so as to
47
- * execute the next link in the chain
48
- */
49
- return new ApolloLink((operation, forward) => {
50
- if (token) {
51
- operation.setContext(({headers = {}}) => ({
52
- headers: {
53
- ...headers,
54
- 'X-Project-ID': $config.PROJECT_ID,
55
- 'X-Platform-ID': 'turnkey_web',
56
- ...(token && {
57
- authorization: token ? `Bearer ${token}` : '',
58
- }),
59
- },
60
- }));
61
- }
62
- return forward(operation);
38
+ const authLink = (token: string) =>
39
+ setContext(async (_, {headers}) => {
40
+ // get the authentication token from local storage if it exists
41
+ // return the headers to the context so httpLink can read them
42
+ return {
43
+ headers: {
44
+ ...headers,
45
+ 'X-Project-ID': $config.PROJECT_ID,
46
+ 'X-Platform-ID': 'turnkey_web',
47
+ ...(token && {
48
+ authorization: token ? `Bearer ${token}` : '',
49
+ }),
50
+ },
51
+ };
63
52
  });
64
- };
65
53
 
66
54
  const GraphQLProvider = (props: {children: React.ReactNode}) => {
67
55
  const {store} = useContext(StorageContext);
68
- const [client, setClient] = useState(DEFAULT_CLIENT);
69
-
70
- // useEffect(() => {
71
- // setClient(
72
- // new ApolloClient({
73
- // link: authLink(store?.token).concat(httpLink),
74
- // cache: new InMemoryCache(),
75
- // }),
76
- // );
77
- // }, [store?.token]);
56
+ const [client, setClient] = useState(
57
+ new ApolloClient({
58
+ link: authLink(store?.token).concat(httpLink),
59
+ cache: new InMemoryCache(),
60
+ }),
61
+ );
78
62
 
79
63
  useEffect(() => {
80
- if (!store?.token) {
81
- return;
82
- }
83
- (async () => {
84
- const link = authLink(store?.token).concat(httpLink);
85
- setClient(new ApolloClient({link, cache}));
86
- })();
64
+ setClient(
65
+ new ApolloClient({
66
+ link: authLink(store?.token).concat(httpLink),
67
+ cache: new InMemoryCache(),
68
+ }),
69
+ );
87
70
  }, [store?.token]);
88
71
 
89
72
  // const errorLink = onError(
@@ -91,7 +74,7 @@ const GraphQLProvider = (props: {children: React.ReactNode}) => {
91
74
  // // To retry on network errors, we recommend the RetryLink
92
75
  // // instead of the onError link. This just logs the error.
93
76
  // if (networkError) {
94
-
77
+
95
78
  // // switch (err.extensions.code) {
96
79
  // // // Apollo Server sets code to UNAUTHENTICATED
97
80
  // // // when an AuthenticationError is thrown in a resolver
@@ -78,12 +78,12 @@ export const StorageProvider = (props: {children: React.ReactNode}) => {
78
78
  setReady(true);
79
79
  } else {
80
80
  const storeFromStorage = JSON.parse(storeString);
81
- Object.keys(initStoreValue).forEach(key => {
81
+ Object.keys(initStoreValue).forEach((key) => {
82
82
  if (!storeFromStorage[key]) {
83
83
  storeFromStorage[key] = initStoreValue[key];
84
84
  }
85
85
  });
86
- // unauth flow delete token from the localstoage if any
86
+ //unauth flow delete token from the localstoage if any
87
87
  if (!ENABLE_AUTH) {
88
88
  storeFromStorage['token'] = null;
89
89
  }
@@ -1,17 +1,11 @@
1
1
  import React from 'react';
2
2
  import Toast from '../../react-native-toast-message';
3
3
  import ToastConfig from '../subComponents/ToastConfig';
4
- import {useIsRecordingBot} from '../subComponents/recording/useIsRecordingBot';
5
4
 
6
5
  const ToastComponent = () => {
7
- const {isRecordingBot} = useIsRecordingBot();
8
-
9
6
  // if ($config.TOAST_NOTIFICATIONS) {
10
7
  // return <Toast ref={(ref) => Toast.setRef(ref)} config={ToastConfig} />;
11
8
  // } else return <></>;
12
- if (isRecordingBot) {
13
- return <></>;
14
- }
15
- return <Toast ref={ref => Toast.setRef(ref)} config={ToastConfig} />;
9
+ return <Toast ref={(ref) => Toast.setRef(ref)} config={ToastConfig} />;
16
10
  };
17
11
  export default ToastComponent;
@@ -438,12 +438,6 @@ export const videoRoomScreenShareErrorToastSubHeading = `video${room}ScreenShare
438
438
 
439
439
  export const videoRoomRecordingToastHeading = `video${room}RecordingToastHeading`;
440
440
  export const videoRoomRecordingToastSubHeading = `video${room}RecordingToastSubHeading`;
441
- export const videoRoomRecordingStartErrorToastHeading =
442
- 'videoRoomRecordingStartErrorToastHeading';
443
- export const videoRoomRecordingStopErrorToastHeading =
444
- 'videoRoomRecordingStopErrorToastHeading';
445
- export const videoRoomRecordingErrorToastSubHeading =
446
- 'videoRoomRecordingErrorToastSubHeading';
447
441
 
448
442
  export const peoplePanelUserNotFoundLabel = 'peoplePanelUserNotFoundLabel';
449
443
  export const peoplePanelStreamingRequestSectionHeader =
@@ -731,10 +725,6 @@ export interface I18nVideoCallScreenLabelsInterface {
731
725
  [videoRoomScreenShareErrorToastSubHeading]?: I18nBaseType;
732
726
  [videoRoomRecordingToastHeading]?: I18nConditionalType;
733
727
  [videoRoomRecordingToastSubHeading]?: I18nDynamicType;
734
- [videoRoomRecordingStartErrorToastHeading]?: I18nBaseType;
735
- [videoRoomRecordingStopErrorToastHeading]?: I18nBaseType;
736
- [videoRoomRecordingErrorToastSubHeading]?: I18nBaseType;
737
-
738
728
  [peoplePanelUserNotFoundLabel]?: I18nBaseType;
739
729
  [peoplePanelStreamingRequestSectionHeader]?: I18nBaseType;
740
730
  [peoplePanelLivestreamingApprovalBtnText]?: I18nBaseType;
@@ -1165,10 +1155,6 @@ export const VideoCallScreenLabels: I18nVideoCallScreenLabelsInterface = {
1165
1155
  active ? 'Recording Started' : 'Recording Stopped',
1166
1156
  [videoRoomRecordingToastSubHeading]: name =>
1167
1157
  `This room is being recorded by ${name}`,
1168
- [videoRoomRecordingStartErrorToastHeading]: 'Recording failed to start',
1169
- [videoRoomRecordingStopErrorToastHeading]: 'Recording failed to stop',
1170
- [videoRoomRecordingErrorToastSubHeading]: 'There was an internal error.',
1171
-
1172
1158
  [peoplePanelUserNotFoundLabel]: 'User not found',
1173
1159
  [peoplePanelStreamingRequestSectionHeader]: 'STREAMING REQUEST',
1174
1160
  [peoplePanelLivestreamingApprovalBtnText]: 'Accept',
@@ -11,7 +11,6 @@
11
11
  */
12
12
  // @ts-nocheck
13
13
  import React, {useState, useContext, useEffect, useRef} from 'react';
14
- import {useApolloClient} from '@apollo/client';
15
14
  import {View, StyleSheet, Text} from 'react-native';
16
15
  import {
17
16
  RtcConfigure,
@@ -50,6 +49,7 @@ import {
50
49
  WaitingRoomStatus,
51
50
  } from '../components/room-info/useRoomInfo';
52
51
  import {SidePanelProvider} from '../utils/useSidePanel';
52
+ import VideoCallScreen from './video-call/VideoCallScreen';
53
53
  import {NetworkQualityProvider} from '../components/NetworkQualityContext';
54
54
  import {ChatNotificationProvider} from '../components/chat-notification/useChatNotification';
55
55
  import {ChatUIControlsProvider} from '../components/chat-ui/useChatUIControls';
@@ -70,13 +70,13 @@ import {CaptionProvider} from '../subComponents/caption/useCaption';
70
70
  import SdkMuteToggleListener from '../components/SdkMuteToggleListener';
71
71
  import StorageContext from '../components/StorageContext';
72
72
  import {useSetRoomInfo} from '../components/room-info/useSetRoomInfo';
73
+ import WhiteboardConfigure from '../components/whiteboard/WhiteboardConfigure';
73
74
  import {NoiseSupressionProvider} from '../app-state/useNoiseSupression';
74
75
  import {VideoQualityContextProvider} from '../app-state/useVideoQuality';
75
76
  import {VBProvider} from '../components/virtual-background/useVB';
76
77
  import {DisableChatProvider} from '../components/disable-chat/useDisableChat';
77
78
  import {WaitingRoomProvider} from '../components/contexts/WaitingRoomContext';
78
- import VideoCallScreenWrapper from './video-call/VideoCallScreenWrapper';
79
- import {useIsRecordingBot} from '../subComponents/recording/useIsRecordingBot';
79
+ import {isWeb} from '../utils/common';
80
80
  import {videoRoomStartingCallText} from '../language/default-labels/videoCallScreenLabels';
81
81
  import {useString} from '../utils/useString';
82
82
 
@@ -129,26 +129,9 @@ const VideoCall: React.FC = () => {
129
129
  const hasBrandLogo = useHasBrandLogo();
130
130
  const joiningLoaderLabel = useString(videoRoomStartingCallText)();
131
131
 
132
- const client = useApolloClient();
133
-
134
132
  const {setGlobalErrorMessage} = useContext(ErrorContext);
135
133
  const {awake, release} = useWakeLock();
136
- const {isRecordingBot} = useIsRecordingBot();
137
- /**
138
- * Should we set the callscreen to active ??
139
- * a) If Recording bot( i.e prop: recordingBot) is TRUE then it means,
140
- * the recording bot is accessing the screen - so YES we should set
141
- * the callActive as true and we need not check for whether
142
- * $config.PRECALL is enabled or not.
143
- * b) If Recording bot( i.e prop: recordingBot) is FALSE then we should set
144
- * the callActive depending upon the value of magic variable - $config.PRECALL
145
- */
146
- const shouldCallBeSetToActive = isRecordingBot
147
- ? true
148
- : $config.PRECALL
149
- ? false
150
- : true;
151
- const [callActive, setCallActive] = useState(shouldCallBeSetToActive);
134
+ const [callActive, setCallActive] = useState($config.PRECALL ? false : true);
152
135
 
153
136
  //layouts
154
137
  const layouts = useLayoutsData();
@@ -197,7 +180,6 @@ const VideoCall: React.FC = () => {
197
180
  sdkCameraDevice.deviceId || store?.activeDeviceId?.videoinput || null,
198
181
  preferredMicrophoneId:
199
182
  sdkMicrophoneDevice.deviceId || store?.activeDeviceId?.audioinput || null,
200
- recordingBot: isRecordingBot ? true : false,
201
183
  });
202
184
 
203
185
  const history = useHistory();
@@ -342,7 +324,6 @@ const VideoCall: React.FC = () => {
342
324
  // TODO: These callbacks are being called twice
343
325
  SDKEvents.emit('leave');
344
326
  history.push('/');
345
- client.resetStore();
346
327
  }, 0);
347
328
  },
348
329
  UserJoined: (uid: UidType) => {
@@ -447,7 +428,14 @@ const VideoCall: React.FC = () => {
447
428
  <VideoMeetingDataProvider>
448
429
  <VideoCallProvider>
449
430
  <DisableChatProvider>
450
- <VideoCallScreenWrapper />
431
+ {$config.ENABLE_WHITEBOARD &&
432
+ isWebInternal() ? (
433
+ <WhiteboardConfigure>
434
+ <VideoCallScreen />
435
+ </WhiteboardConfigure>
436
+ ) : (
437
+ <VideoCallScreen />
438
+ )}
451
439
  </DisableChatProvider>
452
440
  </VideoCallProvider>
453
441
  </VideoMeetingDataProvider>
@@ -35,12 +35,12 @@ import events, {PersistanceLevel} from '../../rtm-events-api';
35
35
  import VideoCallMobileView from './VideoCallMobileView';
36
36
  import CaptionContainer from '../../subComponents/caption/CaptionContainer';
37
37
  import Transcript from '../../subComponents/caption/Transcript';
38
+
38
39
  import Spacer from '../../atoms/Spacer';
39
40
  import Leftbar, {LeftbarProps} from '../../components/Leftbar';
40
41
  import Rightbar, {RightbarProps} from '../../components/Rightbar';
41
42
  import useFindActiveSpeaker from '../../utils/useFindActiveSpeaker';
42
43
  import VBPanel from '../../components/virtual-background/VBPanel';
43
- import {useIsRecordingBot} from '../../subComponents/recording/useIsRecordingBot';
44
44
 
45
45
  const VideoCallScreen = () => {
46
46
  useFindActiveSpeaker();
@@ -231,8 +231,6 @@ const VideoCallScreen = () => {
231
231
  const isDesktop = useIsDesktop();
232
232
  const isSmall = useIsSmall();
233
233
 
234
- const {isRecordingBot, recordingBotUIConfig} = useIsRecordingBot();
235
-
236
234
  return VideocallComponent ? (
237
235
  <VideocallComponent />
238
236
  ) : // ) : !isDesktop ? (
@@ -257,23 +255,16 @@ const VideoCallScreen = () => {
257
255
  )}
258
256
  </ToolbarProvider>
259
257
  <View style={style.full}>
260
- <View
261
- style={
262
- isRecordingBot &&
263
- !recordingBotUIConfig.topBar &&
264
- style.zeroHeight
265
- }>
266
- <ToolbarProvider value={{position: ToolbarPosition.top}}>
267
- {TopbarProps?.length ? (
268
- <TopbarComponent
269
- customItems={TopbarProps}
270
- includeDefaultItems={false}
271
- />
272
- ) : (
273
- <TopbarComponent />
274
- )}
275
- </ToolbarProvider>
276
- </View>
258
+ <ToolbarProvider value={{position: ToolbarPosition.top}}>
259
+ {TopbarProps?.length ? (
260
+ <TopbarComponent
261
+ customItems={TopbarProps}
262
+ includeDefaultItems={false}
263
+ />
264
+ ) : (
265
+ <TopbarComponent />
266
+ )}
267
+ </ToolbarProvider>
277
268
  <View
278
269
  style={[
279
270
  style.videoView,
@@ -325,14 +316,7 @@ const VideoCallScreen = () => {
325
316
  <>
326
317
  <CaptionContainer />
327
318
  <Spacer size={10} />
328
- <View
329
- style={
330
- isRecordingBot &&
331
- !recordingBotUIConfig.bottomBar &&
332
- style.zeroHeight
333
- }>
334
- <BottombarComponent />
335
- </View>
319
+ <BottombarComponent />
336
320
  </>
337
321
  )}
338
322
  </ToolbarProvider>
@@ -378,8 +362,4 @@ const style = StyleSheet.create({
378
362
  flex: 12,
379
363
  flexDirection: 'row',
380
364
  },
381
- zeroHeight: {
382
- height: 0,
383
- visibility: 'hidden',
384
- },
385
365
  });
@@ -17,31 +17,24 @@ import React, {
17
17
  useRef,
18
18
  useState,
19
19
  } from 'react';
20
+ import {gql, useMutation} from '@apollo/client';
21
+ import {useParams} from '../../components/Router';
22
+ import {PropsContext} from '../../../agora-rn-uikit';
20
23
  import Toast from '../../../react-native-toast-message';
21
24
  import {createHook} from 'customization-implementation';
22
25
  import {useString} from '../../utils/useString';
23
26
  import ChatContext from '../../components/ChatContext';
24
27
  import events, {PersistanceLevel} from '../../rtm-events-api';
25
28
  import {EventActions, EventNames} from '../../rtm-events';
29
+ import useRecordingLayoutQuery from './useRecordingLayoutQuery';
30
+ import {useScreenContext} from '../../components/contexts/ScreenShareContext';
26
31
  import {useContent} from 'customization-api';
27
32
  import {trimText} from '../../utils/common';
28
33
  import {useRoomInfo} from 'customization-api';
29
- import StorageContext from '../../components/StorageContext';
30
- import {useSidePanel} from '../../utils/useSidePanel';
31
- import {useCaption} from '../caption/useCaption';
32
- import {SidePanelType} from '../SidePanelEnum';
33
- import {
34
- ChatType,
35
- useChatUIControls,
36
- } from '../../components/chat-ui/useChatUIControls';
37
- import {useIsRecordingBot} from './useIsRecordingBot';
38
34
  import {
39
35
  videoRoomRecordingToastHeading,
40
36
  videoRoomRecordingToastSubHeading,
41
37
  videoRoomUserFallbackText,
42
- videoRoomRecordingStartErrorToastHeading,
43
- videoRoomRecordingStopErrorToastHeading,
44
- videoRoomRecordingErrorToastSubHeading,
45
38
  } from '../../language/default-labels/videoCallScreenLabels';
46
39
 
47
40
  export interface RecordingContextInterface {
@@ -58,6 +51,26 @@ const RecordingContext = createContext<RecordingContextInterface>({
58
51
  inProgress: false,
59
52
  });
60
53
 
54
+ const START_RECORDING = gql`
55
+ mutation startRecordingSession(
56
+ $passphrase: String!
57
+ $secret: String
58
+ $config: recordingConfig!
59
+ ) {
60
+ startRecordingSession(
61
+ passphrase: $passphrase
62
+ secret: $secret
63
+ config: $config
64
+ )
65
+ }
66
+ `;
67
+
68
+ const STOP_RECORDING = gql`
69
+ mutation stopRecordingSession($passphrase: String!) {
70
+ stopRecordingSession(passphrase: $passphrase)
71
+ }
72
+ `;
73
+
61
74
  /**
62
75
  * Component to start / stop Agora cloud recording.
63
76
  * Sends a control message to all users in the channel over RTM to indicate that
@@ -87,13 +100,17 @@ interface RecordingProviderProps {
87
100
  */
88
101
 
89
102
  const RecordingProvider = (props: RecordingProviderProps) => {
103
+ const {rtcProps} = useContext(PropsContext);
90
104
  const {setRecordingActive, isRecordingActive, callActive} = props?.value;
91
105
  const {
92
- data: {isHost, roomId},
106
+ data: {isHost},
93
107
  } = useRoomInfo();
94
108
  const [inProgress, setInProgress] = useState(false);
95
109
  const [uidWhoStarted, setUidWhoStarted] = useState(0);
96
110
  const {defaultContent, activeUids} = useContent();
111
+ const {phrase} = useParams<{phrase: string}>();
112
+ const [startRecordingQuery] = useMutation(START_RECORDING);
113
+ const [stopRecordingQuery] = useMutation(STOP_RECORDING);
97
114
  const prevRecordingState = usePrevious<{isRecordingActive: boolean}>({
98
115
  isRecordingActive,
99
116
  });
@@ -101,41 +118,11 @@ const RecordingProvider = (props: RecordingProviderProps) => {
101
118
  videoRoomRecordingToastHeading,
102
119
  );
103
120
  const subheading = useString(videoRoomRecordingToastSubHeading);
104
-
105
- const headingStartError = useString(
106
- videoRoomRecordingStartErrorToastHeading,
107
- )();
108
- const headingStopError = useString(videoRoomRecordingStopErrorToastHeading)();
109
- const subheadingError = useString(videoRoomRecordingErrorToastSubHeading)();
110
-
111
121
  const userlabel = useString(videoRoomUserFallbackText)();
112
122
 
123
+ const {executePresenterQuery, executeNormalQuery} = useRecordingLayoutQuery();
113
124
  const {localUid} = useContext(ChatContext);
114
- const {store} = React.useContext(StorageContext);
115
-
116
- const {setChatType} = useChatUIControls();
117
- const {setSidePanel} = useSidePanel();
118
- const {setIsCaptionON} = useCaption();
119
- const {isRecordingBot, recordingBotUIConfig} = useIsRecordingBot();
120
-
121
- const setRecordingBotUI = () => {
122
- if (isRecordingBot) {
123
- if (recordingBotUIConfig?.chat && $config.CHAT) {
124
- setSidePanel(SidePanelType.Chat);
125
- setChatType(ChatType.Group);
126
- }
127
- if (recordingBotUIConfig.stt && $config.ENABLE_STT) {
128
- setIsCaptionON(true);
129
- }
130
- }
131
- };
132
-
133
- useEffect(() => {
134
- if (callActive) {
135
- setRecordingBotUI();
136
- }
137
- // eslint-disable-next-line react-hooks/exhaustive-deps
138
- }, [callActive]);
125
+ const {screenShareData} = useScreenContext();
139
126
 
140
127
  React.useEffect(() => {
141
128
  events.on(EventNames.RECORDING_ATTRIBUTE, data => {
@@ -193,36 +180,25 @@ const RecordingProvider = (props: RecordingProviderProps) => {
193
180
  }
194
181
  }, [isRecordingActive, callActive, isHost]);
195
182
 
196
- const showErrorToast = (text1: string, text2?: string) => {
197
- Toast.show({
198
- leadingIconName: 'alert',
199
- type: 'error',
200
- text1: text1,
201
- text2: text2 ? text2 : '',
202
- visibilityTime: 3000,
203
- primaryBtn: null,
204
- secondaryBtn: null,
205
- leadingIcon: null,
206
- });
207
- };
208
-
209
183
  const startRecording = () => {
210
- const passphrase = roomId.host || '';
211
- console.log('web-recording - start recording API called');
212
- fetch(`${$config.BACKEND_ENDPOINT}/v1/recording/start`, {
213
- method: 'POST',
214
- headers: {
215
- 'Content-Type': 'application/json',
216
- authorization: store.token ? `Bearer ${store.token}` : '',
184
+ setInProgress(true);
185
+ // If recording is not going on, start the recording by executing the graphql query
186
+ startRecordingQuery({
187
+ variables: {
188
+ passphrase: phrase,
189
+ secret:
190
+ rtcProps.encryption && rtcProps.encryption.key
191
+ ? rtcProps.encryption.key
192
+ : '',
193
+ config: {
194
+ resolution: 'SD360p',
195
+ trigger: 'AUTO',
196
+ },
217
197
  },
218
- body: JSON.stringify({
219
- passphrase: roomId.host,
220
- url: `${$config.FRONTEND_ENDPOINT}/${passphrase}`,
221
- }),
222
198
  })
223
- .then((res: any) => {
199
+ .then(res => {
224
200
  setInProgress(false);
225
- if (res.status === 200) {
201
+ if (res.data.startRecordingSession === 'success') {
226
202
  /**
227
203
  * 1. Once the backend sucessfuly starts recording, send message
228
204
  * in the channel indicating that cloud recording is now active.
@@ -238,10 +214,22 @@ const RecordingProvider = (props: RecordingProviderProps) => {
238
214
  // 2. set the local recording state to true to update the UI
239
215
  setUidWhoStarted(localUid);
240
216
  setRecordingActive(true);
241
- } else if (res.status === 500) {
242
- showErrorToast(headingStartError, subheadingError);
243
- } else {
244
- showErrorToast(headingStartError);
217
+ // 3. set the presenter mode if screen share is active
218
+ // 3.a Get the most recent screenshare uid
219
+ const sorted = Object.entries(screenShareData)
220
+ .filter(el => el[1]?.ts && el[1].ts > 0 && el[1]?.isActive)
221
+ .sort((a, b) => b[1].ts - a[1].ts);
222
+
223
+ const activeScreenshareUid = sorted.length > 0 ? sorted[0][0] : 0;
224
+ if (activeScreenshareUid) {
225
+ console.log(
226
+ 'screenshare: Executing presenter query for screenuid',
227
+ activeScreenshareUid,
228
+ );
229
+ executePresenterQuery(parseInt(activeScreenshareUid));
230
+ } else {
231
+ executeNormalQuery();
232
+ }
245
233
  }
246
234
  })
247
235
  .catch(err => {
@@ -268,21 +256,11 @@ const RecordingProvider = (props: RecordingProviderProps) => {
268
256
  activeUids.indexOf(uidWhoStarted) === -1
269
257
  ) {
270
258
  setInProgress(true);
271
- // If recording is already going on, stop the recording by executing the below query.
272
- console.log('web-recording - stop recording API called');
273
- fetch(`${$config.BACKEND_ENDPOINT}/v1/recording/stop`, {
274
- method: 'POST',
275
- headers: {
276
- 'Content-Type': 'application/json',
277
- authorization: store.token ? `Bearer ${store.token}` : '',
278
- },
279
- body: JSON.stringify({
280
- passphrase: roomId.host,
281
- }),
282
- })
259
+ // If recording is already going on, stop the recording by executing the graphql query.
260
+ stopRecordingQuery({variables: {passphrase: phrase}})
283
261
  .then(res => {
284
262
  setInProgress(false);
285
- if (res.status === 200) {
263
+ if (res.data.stopRecordingSession === 'success') {
286
264
  /**
287
265
  * 1. Once the backend sucessfuly starts recording, send message
288
266
  * in the channel indicating that cloud recording is now inactive.
@@ -297,10 +275,6 @@ const RecordingProvider = (props: RecordingProviderProps) => {
297
275
  );
298
276
  // 2. set the local recording state to false to update the UI
299
277
  setRecordingActive(false);
300
- } else if (res.status === 500) {
301
- showErrorToast(headingStopError, subheadingError);
302
- } else {
303
- showErrorToast(headingStopError);
304
278
  }
305
279
  })
306
280
  .catch(err => {
@@ -5,7 +5,6 @@ import {useAsyncEffect} from './useAsyncEffect';
5
5
  import LocalEventEmitter, {
6
6
  LocalEventsEnum,
7
7
  } from '../rtm-events-api/LocalEvents';
8
- import {useIsRecordingBot} from '../subComponents/recording/useIsRecordingBot';
9
8
 
10
9
  const useIsLocalUserSpeaking = () => {
11
10
  const log: (arg1: string, ...args: any[]) => void = (arg1, ...args) => {
@@ -16,7 +15,6 @@ const useIsLocalUserSpeaking = () => {
16
15
  const speechRef = useRef(null);
17
16
  const audioRef = useRef(audio);
18
17
  const audioTrackRef = useRef(null);
19
- const {isRecordingBot} = useIsRecordingBot();
20
18
 
21
19
  useEffect(() => {
22
20
  audioRef.current = audio;
@@ -42,9 +40,6 @@ const useIsLocalUserSpeaking = () => {
42
40
 
43
41
  useEffect(() => {
44
42
  LocalEventEmitter.on(LocalEventsEnum.MIC_CHANGED, () => {
45
- if (isRecordingBot) {
46
- return;
47
- }
48
43
  listenForSpeaker();
49
44
  });
50
45
  }, []);
@@ -80,7 +75,7 @@ const useIsLocalUserSpeaking = () => {
80
75
  };
81
76
 
82
77
  useAsyncEffect(async () => {
83
- if ($config.ACTIVE_SPEAKER && !isRecordingBot) {
78
+ if ($config.ACTIVE_SPEAKER) {
84
79
  await listenForSpeaker();
85
80
  return () => {
86
81
  try {
@@ -1,16 +0,0 @@
1
- export function isBotUser(args: [uid: number, elapsed: number]): boolean {
2
- console.log('supriya inside UserJoined verifying if bot user', args[0]);
3
- // STT bot
4
- if (args[0] === 111111) {
5
- return true;
6
- }
7
- // Web Recording bot (userUid)
8
- if (args[0] === 100000) {
9
- return true;
10
- }
11
- // Web Recording bot (screenUid)
12
- if (args[0] === 100001) {
13
- return true;
14
- }
15
- return false;
16
- }
@@ -1,42 +0,0 @@
1
- /*
2
- ********************************************
3
- Copyright © 2024 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, useEffect, useState} from 'react';
13
- import type {RouteProps} from 'react-router';
14
- import StorageContext from '../StorageContext';
15
- import Loading from '../../subComponents/Loading';
16
- import {useIsRecordingBot} from '../../subComponents/recording/useIsRecordingBot';
17
-
18
- interface RecordingBotRouteProps extends RouteProps {
19
- children: React.ReactNode;
20
- history: any;
21
- }
22
-
23
- const RecordingBotRoute: React.FC<RecordingBotRouteProps> = props => {
24
- const {setStore, store} = useContext(StorageContext);
25
- const [ready, setReady] = useState(false);
26
- const {recordingBotToken} = useIsRecordingBot();
27
- useEffect(() => {
28
- setStore &&
29
- setStore(prevState => ({
30
- ...prevState,
31
- token: recordingBotToken,
32
- }));
33
- }, []);
34
-
35
- useEffect(() => {
36
- store?.token === recordingBotToken && setReady(true);
37
- }, [store?.token, recordingBotToken]);
38
-
39
- return ready ? <>{props.children}</> : <Loading text={'Loading...'} />;
40
- };
41
-
42
- export default RecordingBotRoute;
@@ -1,41 +0,0 @@
1
- import React, {useContext, useEffect} from 'react';
2
- import {PropsContext} from '../../../agora-rn-uikit';
3
- import VideoCallScreen from '../video-call/VideoCallScreen';
4
- import {isWebInternal} from '../../utils/common';
5
- import {useLocation} from '../../components/Router';
6
- import {getParamFromURL} from '../../utils/common';
7
- import {useUserPreference} from '../../components/useUserPreference';
8
- import WhiteboardConfigure from '../../components/whiteboard/WhiteboardConfigure';
9
-
10
- const VideoCallScreenWithRecordingBot: React.FC = () => {
11
- const location = useLocation();
12
- const {setDisplayName} = useUserPreference();
13
-
14
- const recordingBotName = getParamFromURL(location?.search, 'user_name');
15
-
16
- useEffect(() => {
17
- setDisplayName(recordingBotName);
18
- }, []);
19
- return <VideoCallScreen />;
20
- };
21
-
22
- const VideoCallScreenWrapper: React.FC = () => {
23
- const {rtcProps} = useContext(PropsContext);
24
-
25
- if ($config.ENABLE_WHITEBOARD && isWebInternal()) {
26
- return (
27
- <WhiteboardConfigure>
28
- {rtcProps?.recordingBot ? (
29
- <VideoCallScreenWithRecordingBot />
30
- ) : (
31
- <VideoCallScreen />
32
- )}
33
- </WhiteboardConfigure>
34
- );
35
- } else if (rtcProps?.recordingBot) {
36
- return <VideoCallScreenWithRecordingBot />;
37
- }
38
- return <VideoCallScreen />;
39
- };
40
-
41
- export default VideoCallScreenWrapper;
@@ -1,38 +0,0 @@
1
- import {useSearchParams} from '../../utils/useSearchParams';
2
-
3
- interface RecordingBotUIConfig {
4
- chat: boolean;
5
- topBar: boolean;
6
- bottomBar: boolean;
7
- stt: boolean;
8
- }
9
-
10
- const regexPattern = new RegExp('true');
11
-
12
- export function useIsRecordingBot() {
13
- // Reading and setting URL params
14
- const isRecordingBot = useSearchParams().get('bot');
15
- const recordingBotToken = useSearchParams().get('token');
16
- const recordingBotName = useSearchParams().get('user_name');
17
- const isRecordingBotRoute = isRecordingBot && recordingBotToken;
18
-
19
- const chatParam = useSearchParams().get('chat');
20
- const topBarParam = useSearchParams().get('topBar');
21
- const bottomBarParam = useSearchParams().get('bottomBar');
22
- const sttParam = useSearchParams().get('stt');
23
-
24
- const recordingBotUIConfig: RecordingBotUIConfig = {
25
- chat: chatParam ? regexPattern.test(chatParam) : true,
26
- topBar: topBarParam ? regexPattern.test(topBarParam) : false,
27
- bottomBar: bottomBarParam ? regexPattern.test(bottomBarParam) : false,
28
- stt: sttParam ? regexPattern.test(sttParam) : false,
29
- };
30
-
31
- return {
32
- isRecordingBotRoute,
33
- isRecordingBot,
34
- recordingBotToken,
35
- recordingBotName,
36
- recordingBotUIConfig,
37
- };
38
- }
@@ -1,18 +0,0 @@
1
- import {useLocation} from '../components/Router';
2
- import {useMemo} from 'react';
3
-
4
- interface ReadOnlyURLSearchParams extends URLSearchParams {
5
- append: never;
6
- set: never;
7
- delete: never;
8
- sort: never;
9
- }
10
-
11
- export function useSearchParams() {
12
- const {search} = useLocation();
13
-
14
- return useMemo(
15
- () => new URLSearchParams(search) as ReadOnlyURLSearchParams,
16
- [search],
17
- );
18
- }