agora-appbuilder-core 4.1.29 → 4.1.30-beta.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 (44) hide show
  1. package/Readme.md +2 -0
  2. package/package.json +1 -1
  3. package/template/Gulpfile.js +34 -0
  4. package/template/_package-lock.json +2491 -3084
  5. package/template/android/app/build.gradle +33 -23
  6. package/template/android/app/src/main/AndroidManifest.xml +5 -3
  7. package/template/android/app/src/main/java/com/helloworld/MainApplication.java +14 -2
  8. package/template/android/build.gradle +42 -7
  9. package/template/android/gradle/wrapper/gradle-wrapper.properties +3 -3
  10. package/template/android/gradle.properties +0 -3
  11. package/template/android/settings.gradle +4 -2
  12. package/template/babel.config.js +1 -1
  13. package/template/defaultConfig.js +2 -2
  14. package/template/ios/HelloWorld.xcodeproj/project.pbxproj +8 -6
  15. package/template/ios/Podfile +3 -45
  16. package/template/ios/Podfile.lock +1806 -524
  17. package/template/metro.config.js +6 -1
  18. package/template/package.json +28 -20
  19. package/template/patches/@supersami+rn-foreground-service+1.1.1.patch +12 -0
  20. package/template/scripts/verify-android-native-libs.py +106 -0
  21. package/template/src/AppWrapper.tsx +2 -7
  22. package/template/src/atoms/Carousel.native.tsx +22 -5
  23. package/template/src/atoms/CustomSwitch.tsx +0 -1
  24. package/template/src/atoms/Dropdown.tsx +0 -1
  25. package/template/src/atoms/MeetingLink.tsx +5 -12
  26. package/template/src/atoms/Popup.tsx +26 -2
  27. package/template/src/components/AppSafeAreaView.android.tsx +15 -0
  28. package/template/src/components/AppSafeAreaView.tsx +1 -0
  29. package/template/src/components/Precall.native.tsx +40 -10
  30. package/template/src/components/Share.tsx +27 -6
  31. package/template/src/components/meeting-info-invite/MeetingInfo.tsx +21 -3
  32. package/template/src/components/meeting-info-invite/MeetingInfoGridTile.tsx +11 -2
  33. package/template/src/components/useVideoCall.tsx +35 -1
  34. package/template/src/language/default-labels/joinScreenLabels.ts +2 -2
  35. package/template/src/pages/Create.tsx +26 -3
  36. package/template/src/pages/video-call/ActionSheet.native.tsx +11 -11
  37. package/template/src/pages/video-call/VideoCallScreen.native.tsx +34 -5
  38. package/template/src/pages/video-call/__tests__/VideoCallScreen.native.test.tsx +95 -0
  39. package/template/src/subComponents/Clipboard.native.tsx +1 -1
  40. package/template/src/subComponents/caption/CaptionIcon.tsx +11 -14
  41. package/template/src/subComponents/caption/TranscriptIcon.tsx +10 -14
  42. package/template/src/utils/useIsTablet.ts +10 -0
  43. package/template/android/app/src/debug/java/com/helloworld/ReactNativeFlipper.java +0 -75
  44. package/template/android/app/src/release/java/com/helloworld/ReactNativeFlipper.java +0 -20
@@ -6,6 +6,11 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
6
6
  *
7
7
  * @type {import('metro-config').MetroConfig}
8
8
  */
9
- const config = {};
9
+ const config = {
10
+ resolver: {
11
+ // CI builds should not depend on a running Watchman service.
12
+ useWatchman: process.env.CI !== 'true',
13
+ },
14
+ };
10
15
 
11
16
  module.exports = mergeConfig(getDefaultConfig(__dirname), config);
@@ -8,6 +8,9 @@
8
8
  "android:build": "npm run android:build:unix",
9
9
  "android:build:unix": "cross-env TARGET=android gulp androidUnix",
10
10
  "android:build:windows": "cross-env TARGET=android gulp androidWin",
11
+ "android:bundle": "npm run android:bundle:unix",
12
+ "android:bundle:unix": "cross-env TARGET=android gulp androidBundleUnix",
13
+ "android:bundle:windows": "cross-env TARGET=android gulp androidBundleWin",
11
14
  "ios": "cd ios && ls | grep .xcworkspace | xargs open",
12
15
  "ios:build": "cd ios && ls | grep .xcworkspace | xargs open",
13
16
  "start": "react-native start",
@@ -34,7 +37,8 @@
34
37
  "package:mac": "npm run mac:build && electron-builder --publish=always",
35
38
  "release": "electron-builder --mac --windows --linux --publish always --config ./electron-builder.js ",
36
39
  "make-rsdk-ts-defs": "cross-env TARGET=rsdk NODE_ENV=production gulp makeRsdkTsDefs",
37
- "make-wsdk-ts-defs": "cross-env TARGET=wsdk NODE_ENV=production gulp makeWsdkTsDefs"
40
+ "make-wsdk-ts-defs": "cross-env TARGET=wsdk NODE_ENV=production gulp makeWsdkTsDefs",
41
+ "postinstall": "patch-package --error-on-fail"
38
42
  },
39
43
  "publish": [
40
44
  {
@@ -54,10 +58,10 @@
54
58
  "@netless/react-native-whiteboard": "^0.0.14",
55
59
  "@openspacelabs/react-native-zoomable-view": "^2.1.1",
56
60
  "@react-native-async-storage/async-storage": "1.19.2",
61
+ "@react-native-clipboard/clipboard": "1.16.3",
57
62
  "@react-native-community/checkbox": "0.5.16",
58
- "@react-native-community/clipboard": "1.5.1",
59
63
  "@splinetool/runtime": "^1.9.69",
60
- "@supersami/rn-foreground-service": "^1.1.1",
64
+ "@supersami/rn-foreground-service": "1.1.1",
61
65
  "add": "^2.0.6",
62
66
  "agora-agent-client-toolkit": "2.9.0",
63
67
  "agora-chat": "1.2.2-3",
@@ -84,29 +88,30 @@
84
88
  "npm": "^9.7.2",
85
89
  "p-queue": "^7.3.4",
86
90
  "protobufjs": "^7.2.5",
87
- "react": "18.2.0",
88
- "react-dom": "18.2.0",
91
+ "react": "18.3.1",
92
+ "react-dom": "18.3.1",
89
93
  "react-is": "18.0.0",
90
- "react-native": "0.72.4",
94
+ "react-native": "0.77.3",
91
95
  "react-native-agora": "4.6.4",
92
- "react-native-agora-chat": "1.2.1",
96
+ "react-native-agora-chat": "1.4.1",
93
97
  "react-native-document-picker": "^9.1.0",
94
98
  "react-native-exit-app": "1.1.0",
95
99
  "react-native-fs": "^2.20.0",
96
- "react-native-gesture-handler": "2.8.0",
100
+ "react-native-gesture-handler": "2.25.0",
97
101
  "react-native-get-random-values": "^1.11.0",
98
102
  "react-native-hyperlink": "0.0.19",
99
103
  "react-native-inappbrowser-reborn": "3.5.1",
100
104
  "react-native-keep-awake": "4.0.0",
101
105
  "react-native-keyboard-manager": "6.5.4-1",
102
- "react-native-reanimated": "3.4.2",
106
+ "react-native-reanimated": "3.17.5",
103
107
  "react-native-responsive-fontsize": "0.5.1",
108
+ "react-native-safe-area-context": "5.4.0",
104
109
  "react-native-share": "10.0.2",
105
- "react-native-svg": "13.6.0",
110
+ "react-native-svg": "15.11.2",
106
111
  "react-native-vector-icons": "9.2.0",
107
112
  "react-native-walkthrough-tooltip": "1.4.0",
108
113
  "react-native-web": "0.19.7",
109
- "react-native-webview": "13.8.1",
114
+ "react-native-webview": "13.13.5",
110
115
  "react-router-dom": "5.2.0",
111
116
  "react-router-native": "5.2.0",
112
117
  "react-spring-bottom-sheet": "3.4.1",
@@ -117,24 +122,27 @@
117
122
  "white-web-sdk": "2.16.42"
118
123
  },
119
124
  "devDependencies": {
120
- "@babel/core": "^7.20.0",
125
+ "@babel/core": "^7.25.2",
121
126
  "@babel/plugin-proposal-class-properties": "7.14.5",
122
127
  "@babel/plugin-proposal-optional-chaining": "7.16.0",
123
128
  "@babel/preset-env": "^7.20.0",
124
129
  "@babel/preset-react": "7.13.13",
125
130
  "@babel/preset-typescript": "7.13.0",
126
- "@babel/runtime": "^7.20.0",
131
+ "@babel/runtime": "^7.25.0",
127
132
  "@bam.tech/react-native-make": "3.0.3",
128
133
  "@pmmmwh/react-refresh-webpack-plugin": "0.5.5",
134
+ "@react-native-community/cli": "15.0.1",
135
+ "@react-native-community/cli-platform-android": "15.0.1",
136
+ "@react-native-community/cli-platform-ios": "15.0.1",
129
137
  "@react-native-community/eslint-config": "1.1.0",
130
- "@react-native/eslint-config": "^0.72.2",
131
- "@react-native/metro-config": "^0.72.11",
138
+ "@react-native/babel-preset": "0.77.3",
139
+ "@react-native/eslint-config": "0.77.3",
140
+ "@react-native/metro-config": "0.77.3",
132
141
  "@tsconfig/react-native": "^3.0.0",
133
142
  "@types/hark": "^1.2.2",
134
143
  "@types/jest": "25.2.3",
135
- "@types/react": "17.0.2",
144
+ "@types/react": "^18.3.1",
136
145
  "@types/react-is": "17.0.3",
137
- "@types/react-native": "0.67.6",
138
146
  "@types/react-native-keep-awake": "2.0.2",
139
147
  "@types/react-router-dom": "5.1.7",
140
148
  "@types/react-router-native": "5.1.0",
@@ -162,12 +170,12 @@
162
170
  "html-webpack-plugin": "4.5.2",
163
171
  "ignore-loader": "^0.1.2",
164
172
  "jest": "^29.2.1",
165
- "metro-react-native-babel-preset": "0.76.8",
173
+ "patch-package": "8.0.0",
166
174
  "prettier": "^2.4.1",
167
175
  "react-devtools-electron": "4.7.0",
168
176
  "react-native-url-polyfill": "^2.0.0",
169
177
  "react-refresh": "0.12.0",
170
- "react-test-renderer": "18.2.0",
178
+ "react-test-renderer": "18.3.1",
171
179
  "style-loader": "3.3.1",
172
180
  "ts-loader": "^8.3.0",
173
181
  "typescript": "4.8.4",
@@ -180,7 +188,7 @@
180
188
  },
181
189
  "overrides": {
182
190
  "@netless/react-native-whiteboard": {
183
- "react-native-webview": "13.8.1"
191
+ "react-native-webview": "13.13.5"
184
192
  },
185
193
  "agora-extension-ai-denoiser": {
186
194
  "agora-rtc-sdk-ng": "4.20.1-1"
@@ -0,0 +1,12 @@
1
+ diff --git a/node_modules/@supersami/rn-foreground-service/android/src/main/java/com/supersami/foregroundservice/NotificationHelper.java b/node_modules/@supersami/rn-foreground-service/android/src/main/java/com/supersami/foregroundservice/NotificationHelper.java
2
+ --- a/node_modules/@supersami/rn-foreground-service/android/src/main/java/com/supersami/foregroundservice/NotificationHelper.java
3
+ +++ b/node_modules/@supersami/rn-foreground-service/android/src/main/java/com/supersami/foregroundservice/NotificationHelper.java
4
+ @@ -59 +59 @@
5
+ - PendingIntent pendingIntent = PendingIntent.getActivity(context, uniqueInt1, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
6
+ + PendingIntent pendingIntent = PendingIntent.getActivity(context, uniqueInt1, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
7
+ @@ -66 +66 @@
8
+ - pendingBtnIntent = PendingIntent.getActivity(context, uniqueInt, notificationBtnIntent, PendingIntent.FLAG_UPDATE_CURRENT);
9
+ + pendingBtnIntent = PendingIntent.getActivity(context, uniqueInt, notificationBtnIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
10
+ @@ -125 +125 @@
11
+ - notificationBuilder.addAction(R.drawable.redbox_top_border_background, bundle.getString("buttonText", "Button"), pendingBtnIntent);
12
+ + notificationBuilder.addAction(android.R.drawable.ic_media_play, bundle.getString("buttonText", "Button"), pendingBtnIntent);
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env python3
2
+ """Check 64-bit ELF/APK alignment and shared Agora AOSL symbol compatibility."""
3
+ import argparse
4
+ import struct
5
+ import sys
6
+ import zipfile
7
+ from pathlib import Path
8
+
9
+ PAGE_SIZE = 16384
10
+
11
+
12
+ def aosl_symbols(data):
13
+ """Read required and exported AOSL symbols from ELF32/ELF64 dynamic tables."""
14
+ if data[:4] != b'\x7fELF':
15
+ raise ValueError('expected an ELF library')
16
+ is_64 = data[4] == 2
17
+ endian = '<' if data[5] == 1 else '>'
18
+ table_offset = struct.unpack_from(endian + ('Q' if is_64 else 'I'), data, 40 if is_64 else 32)[0]
19
+ entry_size, entry_count = struct.unpack_from(endian + 'HH', data, 58 if is_64 else 46)
20
+ section_format = endian + ('IIQQQQIIQQ' if is_64 else 'IIIIIIIIII')
21
+ sections = [struct.unpack_from(section_format, data, table_offset + i * entry_size)
22
+ for i in range(entry_count)]
23
+ required, exported = set(), set()
24
+ for section in sections:
25
+ if section[1] != 11: # SHT_DYNSYM
26
+ continue
27
+ strings = sections[section[6]]
28
+ string_data = data[strings[4]:strings[4] + strings[5]]
29
+ for offset in range(section[4], section[4] + section[5], section[9]):
30
+ symbol = struct.unpack_from(endian + ('IBBHQQ' if is_64 else 'IIIBBH'), data, offset)
31
+ name_offset = symbol[0]
32
+ name_bytes = string_data[name_offset:string_data.find(b'\0', name_offset)]
33
+ if not name_bytes.startswith(b'aosl_'):
34
+ continue
35
+ name = name_bytes.decode('ascii')
36
+ info, section_index = (symbol[1], symbol[3]) if is_64 else (symbol[3], symbol[5])
37
+ if section_index == 0:
38
+ if info >> 4 == 1: # Undefined GLOBAL symbols; WEAK imports are optional.
39
+ required.add(name)
40
+ elif info >> 4 in (1, 2):
41
+ exported.add(name)
42
+ return required, exported
43
+
44
+
45
+ def check_artifact(path):
46
+ failures = []
47
+ checked = 0
48
+ aosl_providers = {}
49
+ aosl_consumers = []
50
+ with zipfile.ZipFile(path) as archive, path.open('rb') as artifact:
51
+ for entry in archive.infolist():
52
+ name = entry.filename
53
+ if name.endswith('.so'):
54
+ data = archive.read(entry)
55
+ required, exported = aosl_symbols(data)
56
+ abi = name.split('/')[-2]
57
+ if name.endswith('/libaosl.so'):
58
+ aosl_providers[abi] = exported
59
+ if required:
60
+ aosl_consumers.append((name, abi, required))
61
+ if not name.endswith('.so') or not any(
62
+ f'/{abi}/' in name for abi in ('arm64-v8a', 'x86_64')
63
+ ):
64
+ continue
65
+ checked += 1
66
+ data = archive.read(entry)
67
+ if data[:5] != b'\x7fELF\x02':
68
+ failures.append(f'{name}: expected a 64-bit ELF library')
69
+ continue
70
+ endian = '<' if data[5] == 1 else '>'
71
+ table_offset = struct.unpack_from(endian + 'Q', data, 32)[0]
72
+ entry_size, entry_count = struct.unpack_from(endian + 'HH', data, 54)
73
+ for index in range(entry_count):
74
+ offset = table_offset + index * entry_size
75
+ segment_type = struct.unpack_from(endian + 'I', data, offset)[0]
76
+ if segment_type == 1: # PT_LOAD
77
+ alignment = struct.unpack_from(endian + 'Q', data, offset + 48)[0]
78
+ if alignment < PAGE_SIZE:
79
+ failures.append(f'{name}: ELF load segment is aligned to {alignment} bytes')
80
+ break
81
+ if path.suffix == '.apk' and entry.compress_type == zipfile.ZIP_STORED:
82
+ artifact.seek(entry.header_offset + 26)
83
+ name_size, extra_size = struct.unpack('<HH', artifact.read(4))
84
+ data_offset = entry.header_offset + 30 + name_size + extra_size
85
+ if data_offset % PAGE_SIZE:
86
+ failures.append(f'{name}: uncompressed APK entry lacks 16 KB ZIP alignment')
87
+ if not checked:
88
+ failures.append('No 64-bit native libraries found')
89
+ for name, abi, required in aosl_consumers:
90
+ missing = required - aosl_providers.get(abi, set())
91
+ if missing:
92
+ failures.append(f'{name}: packaged libaosl.so lacks symbols: {", ".join(sorted(missing))}')
93
+ for failure in failures:
94
+ print(f'FAIL: {failure}', file=sys.stderr)
95
+ print(f'{path.name}: checked alignment of {checked} native libraries and '
96
+ f'AOSL imports in {len(aosl_consumers)} libraries; {len(failures)} failures')
97
+ if path.suffix == '.aab':
98
+ print('AAB check covers ELF alignment. Also verify ZIP alignment in generated APKs.')
99
+ return bool(failures)
100
+
101
+
102
+ if __name__ == '__main__':
103
+ parser = argparse.ArgumentParser(description=__doc__)
104
+ parser.add_argument('artifact', type=Path)
105
+ args = parser.parse_args()
106
+ sys.exit(check_artifact(args.artifact))
@@ -14,13 +14,7 @@ import {Router} from './components/Router';
14
14
  import Navigation from './components/Navigation';
15
15
  import {StorageProvider} from './components/StorageContext';
16
16
  import {SessionProvider} from './components/SessionContext';
17
- import {
18
- ImageBackground,
19
- SafeAreaView,
20
- StatusBar,
21
- Platform,
22
- View,
23
- } from 'react-native';
17
+ import {ImageBackground, StatusBar, Platform, View} from 'react-native';
24
18
  import ColorConfigure from './components/ColorConfigure';
25
19
  import {isValidReactComponent} from './utils/common';
26
20
  import DimensionProvider from './components/dimension/DimensionProvider';
@@ -35,6 +29,7 @@ import {ToastContext, ToastProvider} from './components/useToast';
35
29
  import {SdkApiContext} from './components/SdkApiContext';
36
30
  import isSDK from './utils/isSDK';
37
31
  import BlockUI from './subComponents/BlockUI';
32
+ import SafeAreaView from './components/AppSafeAreaView';
38
33
 
39
34
  interface AppWrapperProps {
40
35
  children: React.ReactNode;
@@ -1,9 +1,7 @@
1
- import React, {useState} from 'react';
2
- import {View, StyleSheet, Dimensions, Pressable} from 'react-native';
1
+ import React, {useEffect, useState} from 'react';
2
+ import {View, StyleSheet, useWindowDimensions, Pressable} from 'react-native';
3
3
  import {FlatList} from 'react-native-gesture-handler';
4
4
 
5
- const {width} = Dimensions.get('window');
6
-
7
5
  interface CarouselItem {
8
6
  id: string;
9
7
  component: React.ReactNode;
@@ -14,9 +12,22 @@ interface CarouselProps {
14
12
  }
15
13
 
16
14
  const Carousel: React.FC<CarouselProps> = ({data}) => {
15
+ const {width: windowWidth} = useWindowDimensions();
16
+ const [width, setWidth] = useState(windowWidth);
17
17
  const [activeIndex, setActiveIndex] = useState(0);
18
18
 
19
19
  const flatListRef = React.useRef<FlatList | null>(null);
20
+ const previousWidth = React.useRef(width);
21
+
22
+ useEffect(() => {
23
+ if (previousWidth.current !== width) {
24
+ previousWidth.current = width;
25
+ flatListRef.current?.scrollToOffset({
26
+ offset: activeIndex * width,
27
+ animated: false,
28
+ });
29
+ }
30
+ }, [width, activeIndex]);
20
31
 
21
32
  const handleScroll = event => {
22
33
  const {contentOffset} = event.nativeEvent;
@@ -37,7 +48,13 @@ const Carousel: React.FC<CarouselProps> = ({data}) => {
37
48
  };
38
49
 
39
50
  return (
40
- <View style={styles.container}>
51
+ <View
52
+ style={styles.container}
53
+ onLayout={({nativeEvent: {layout}}) => {
54
+ if (layout.width > 0) {
55
+ setWidth(layout.width);
56
+ }
57
+ }}>
41
58
  <FlatList
42
59
  ref={flatListRef}
43
60
  data={data}
@@ -7,7 +7,6 @@ import {
7
7
  Animated,
8
8
  PanResponder,
9
9
  TouchableOpacity,
10
- ViewPropTypes,
11
10
  } from 'react-native';
12
11
  import PropTypes from 'prop-types';
13
12
 
@@ -222,7 +222,6 @@ const PlatformWrapper = ({children, onPress}) => {
222
222
 
223
223
  const styles = StyleSheet.create({
224
224
  dropdownOptionContainer: {
225
- flex: 1,
226
225
  flexDirection: 'row',
227
226
  minHeight: 40,
228
227
  backgroundColor: $config.INPUT_FIELD_BACKGROUND_COLOR,
@@ -1,12 +1,9 @@
1
1
  import React from 'react';
2
- import {StyleSheet, Text, View} from 'react-native';
3
- import {
4
- SHARE_LINK_CONTENT_TYPE,
5
- useShareLink,
6
- } from '../components/useShareLink';
2
+ import {Platform, StyleSheet, Text, View} from 'react-native';
3
+ import {SHARE_LINK_CONTENT_TYPE} from '../components/useShareLink';
7
4
  import platform from '../subComponents/Platform';
8
- import Spacer from './Spacer';
9
5
  import isSDKCheck from '../utils/isSDK';
6
+ import Spacer from './Spacer';
10
7
  import ClipboardIconButton from './ClipboardIconButton';
11
8
  import ThemeConfig, {FontSizes} from '../theme';
12
9
  import hexadecimalTransparency from '../utils/hexadecimalTransparency';
@@ -41,12 +38,8 @@ const MeetingLink = (props: MeetingLinkProps) => {
41
38
  } = props;
42
39
 
43
40
  const style = useStyles(styleProps);
44
-
45
- const {getShareLink} = useShareLink();
46
-
47
- const isSDK = isSDKCheck();
48
41
  const isWebCheck =
49
- $config.FRONTEND_ENDPOINT || (platform === 'web' && !isSDK);
42
+ $config.FRONTEND_ENDPOINT || (platform === 'web' && !isSDKCheck());
50
43
 
51
44
  return (
52
45
  <>
@@ -60,7 +53,7 @@ const MeetingLink = (props: MeetingLinkProps) => {
60
53
  style={[
61
54
  style.linkText,
62
55
  //@ts-ignore
63
- isWebCheck ? urlWeb : {opacity: 1},
56
+ Platform.OS === 'web' && isWebCheck ? urlWeb : {opacity: 1},
64
57
  ]}>
65
58
  {link}
66
59
  </Text>
@@ -3,6 +3,7 @@ import {
3
3
  Text,
4
4
  View,
5
5
  Modal,
6
+ ScrollView,
6
7
  TouchableWithoutFeedback,
7
8
  ModalProps,
8
9
  ViewStyle,
@@ -13,6 +14,7 @@ import ThemeConfig from '../theme';
13
14
  import hexadecimalTransparency from '../utils/hexadecimalTransparency';
14
15
  import {isMobileUA, useIsDesktop} from '../../src/utils/common';
15
16
  import Spacer from './Spacer';
17
+ import useIsTablet from '../utils/useIsTablet';
16
18
 
17
19
  interface PopupProps extends ModalProps {
18
20
  title?: string;
@@ -44,6 +46,7 @@ const Popup = (props: PopupProps) => {
44
46
  } = props;
45
47
 
46
48
  const isDesktop = useIsDesktop()('popup');
49
+ const isTabletView = useIsTablet();
47
50
 
48
51
  return (
49
52
  <Modal
@@ -67,7 +70,12 @@ const Popup = (props: PopupProps) => {
67
70
  <View style={styles.backDrop} />
68
71
  </TouchableWithoutFeedback>
69
72
 
70
- <View style={[styles.modalView, props?.contentContainerStyle]}>
73
+ <View
74
+ style={[
75
+ styles.modalView,
76
+ props?.contentContainerStyle,
77
+ isTabletView && styles.tabletModalView,
78
+ ]}>
71
79
  {title || showCloseIcon || headerComponent ? (
72
80
  <>
73
81
  <View style={styles.header}>
@@ -111,7 +119,15 @@ const Popup = (props: PopupProps) => {
111
119
  ) : (
112
120
  <></>
113
121
  )}
114
- <View style={bodyContainerStyle}>{children}</View>
122
+ {isTabletView ? (
123
+ <ScrollView
124
+ style={styles.tabletBody}
125
+ contentContainerStyle={bodyContainerStyle}>
126
+ {children}
127
+ </ScrollView>
128
+ ) : (
129
+ <View style={bodyContainerStyle}>{children}</View>
130
+ )}
115
131
  </View>
116
132
  </View>
117
133
  </Modal>
@@ -153,6 +169,14 @@ const styles = StyleSheet.create({
153
169
  backgroundColor:
154
170
  $config.HARD_CODED_BLACK_COLOR + hexadecimalTransparency['60%'],
155
171
  },
172
+ tabletModalView: {
173
+ maxHeight: '90%',
174
+ flexShrink: 1,
175
+ },
176
+ tabletBody: {
177
+ flexGrow: 0,
178
+ flexShrink: 1,
179
+ },
156
180
  header: {
157
181
  flexDirection: 'row',
158
182
  justifyContent: 'space-between',
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import {
3
+ SafeAreaProvider,
4
+ SafeAreaView,
5
+ SafeAreaViewProps,
6
+ initialWindowMetrics,
7
+ } from 'react-native-safe-area-context';
8
+
9
+ export default function AppSafeAreaView(props: SafeAreaViewProps) {
10
+ return (
11
+ <SafeAreaProvider initialMetrics={initialWindowMetrics}>
12
+ <SafeAreaView {...props} />
13
+ </SafeAreaProvider>
14
+ );
15
+ }
@@ -0,0 +1 @@
1
+ export {SafeAreaView as default} from 'react-native';
@@ -13,6 +13,7 @@ import React, {useState, useContext, useEffect} from 'react';
13
13
  import {View, Text, StyleSheet, ScrollView} from 'react-native';
14
14
  import {PropsContext, ClientRoleType, ToggleState} from '../../agora-rn-uikit';
15
15
  import {isValidReactComponent, isWebInternal, trimText} from '../utils/common';
16
+ import useIsTablet from '../utils/useIsTablet';
16
17
  import ColorContext from './ColorContext';
17
18
  import {useRoomInfo} from './room-info/useRoomInfo';
18
19
  import PreCallLogo from './common/Logo';
@@ -214,6 +215,7 @@ const JoinRoomButton = () => {
214
215
  };
215
216
 
216
217
  const Precall = (props: any) => {
218
+ const isTabletView = useIsTablet();
217
219
  const {primaryColor} = useContext(ColorContext);
218
220
  const {rtcProps} = useContext(PropsContext);
219
221
  const [isSettingsOpen, setIsSettingsOpen] = React.useState(true);
@@ -424,16 +426,28 @@ const Precall = (props: any) => {
424
426
  <></>
425
427
  )}
426
428
  </View>
427
- <View style={style.content}>
428
- <View style={style.preview}>
429
+ <View
430
+ style={[
431
+ style.content,
432
+ isTabletView && style.tabletContent,
433
+ ]}>
434
+ <View
435
+ style={[
436
+ style.preview,
437
+ isTabletView && style.tabletPreview,
438
+ isTabletView && style.tabletFrame,
439
+ ]}>
429
440
  <View
430
- style={{
431
- flex: 1,
432
- position: 'relative',
433
- overflow: 'hidden',
434
- borderTopLeftRadius: 12,
435
- borderTopRightRadius: 12,
436
- }}>
441
+ style={[
442
+ {
443
+ flex: 1,
444
+ position: 'relative',
445
+ overflow: 'hidden',
446
+ borderTopLeftRadius: 12,
447
+ borderTopRightRadius: 12,
448
+ },
449
+ isTabletView && style.tabletVideo,
450
+ ]}>
437
451
  {isLocalVideoON ? (
438
452
  <View style={style.switchCamera}>
439
453
  <LocalSwitchCamera
@@ -458,7 +472,8 @@ const Precall = (props: any) => {
458
472
  />
459
473
  </View>
460
474
  </View>
461
- <View style={style.footer}>
475
+ <View
476
+ style={[style.footer, isTabletView && style.tabletFrame]}>
462
477
  <JoinRoomName isDesktop={false} isOnPrecall={true} />
463
478
  <Spacer size={8} horizontal={false} />
464
479
  <JoinRoomButton />
@@ -495,6 +510,21 @@ const style = StyleSheet.create({
495
510
  content: {
496
511
  flex: 1,
497
512
  },
513
+ tabletContent: {
514
+ justifyContent: 'center',
515
+ },
516
+ tabletPreview: {
517
+ flex: 0,
518
+ },
519
+ tabletFrame: {
520
+ width: '100%',
521
+ maxWidth: 600,
522
+ alignSelf: 'center',
523
+ },
524
+ tabletVideo: {
525
+ flex: 0,
526
+ aspectRatio: 4 / 3,
527
+ },
498
528
  preview: {
499
529
  // paddingHorizontal: 20,
500
530
  // paddingBottom: 8,
@@ -18,6 +18,7 @@ import {
18
18
  TouchableOpacity,
19
19
  Image,
20
20
  Pressable,
21
+ Platform,
21
22
  } from 'react-native';
22
23
  import platform from '../subComponents/Platform';
23
24
  import PrimaryButton from '../atoms/PrimaryButton';
@@ -25,6 +26,7 @@ import SecondaryButton from '../atoms/SecondaryButton';
25
26
  import {SHARE_LINK_CONTENT_TYPE, useShareLink} from './useShareLink';
26
27
  import {useString} from '../utils/useString';
27
28
  import isSDKCheck from '../utils/isSDK';
29
+ import useIsTablet from '../utils/useIsTablet';
28
30
  import Logo from '../components/common/Logo';
29
31
  import {useRoomInfo} from './room-info/useRoomInfo';
30
32
  import {useHistory} from '../components/Router';
@@ -144,7 +146,7 @@ export const CopyMeetingInfo = (props?: CopyMeetingInfoProps) => {
144
146
  style.url,
145
147
  style.urlPadding,
146
148
  //@ts-ignore
147
- isWebCheck ? urlWeb : {opacity: 1},
149
+ Platform.OS === 'web' && isWebCheck ? urlWeb : {opacity: 1},
148
150
  ]}>
149
151
  {getShareLink(SHARE_LINK_CONTENT_TYPE.HOST)}
150
152
  </Text>
@@ -178,7 +180,7 @@ export const CopyMeetingInfo = (props?: CopyMeetingInfoProps) => {
178
180
  style.url,
179
181
  style.urlPadding,
180
182
  //@ts-ignore
181
- isWebCheck ? urlWeb : {opacity: 1},
183
+ Platform.OS === 'web' && isWebCheck ? urlWeb : {opacity: 1},
182
184
  ]}>
183
185
  {getShareLink(SHARE_LINK_CONTENT_TYPE.ATTENDEE)}
184
186
  </Text>
@@ -210,7 +212,7 @@ export const CopyMeetingInfo = (props?: CopyMeetingInfoProps) => {
210
212
  style={[
211
213
  style.url,
212
214
  //@ts-ignore
213
- isWebCheck ? urlWeb : {opacity: 1},
215
+ Platform.OS === 'web' && isWebCheck ? urlWeb : {opacity: 1},
214
216
  ]}>
215
217
  {shareRoomPSTNNumber} - {pstn?.number} {' | '}{' '}
216
218
  {shareRoomPSTNPin} - {pstn?.pin}
@@ -302,7 +304,7 @@ export const ShowInputURL = (props: ShowInputURLProps) => {
302
304
  style.url,
303
305
  style.urlPadding,
304
306
  //@ts-ignore
305
- urlWeb,
307
+ Platform.OS === 'web' ? urlWeb : {opacity: 1},
306
308
  ]}>
307
309
  {url}
308
310
  </Text>
@@ -314,6 +316,7 @@ export const ShowInputURL = (props: ShowInputURLProps) => {
314
316
  };
315
317
 
316
318
  const Share = () => {
319
+ const isTabletView = useIsTablet();
317
320
  const {FpeShareComponent} = useCustomization(data => {
318
321
  let components: {
319
322
  FpeShareComponent?: React.ElementType;
@@ -368,8 +371,12 @@ const Share = () => {
368
371
  ) : (
369
372
  <></>
370
373
  )}
371
- <ScrollView contentContainerStyle={style.scrollMain}>
372
- <Card>
374
+ <ScrollView
375
+ contentContainerStyle={[
376
+ style.scrollMain,
377
+ isTabletView && style.tabletScrollMain,
378
+ ]}>
379
+ <Card cardContainerStyle={isTabletView ? style.tabletCard : undefined}>
373
380
  <View>
374
381
  <View
375
382
  style={{
@@ -425,6 +432,20 @@ const style = StyleSheet.create({
425
432
  flexDirection: 'row',
426
433
  justifyContent: 'center',
427
434
  },
435
+ tabletScrollMain: {
436
+ alignItems: 'center',
437
+ padding: 24,
438
+ },
439
+ tabletCard: {
440
+ flex: 0,
441
+ alignSelf: 'center',
442
+ justifyContent: 'flex-start',
443
+ paddingHorizontal: 40,
444
+ paddingVertical: 40,
445
+ borderRadius: ThemeConfig.BorderRadius.extraLarge,
446
+ borderWidth: 1,
447
+ borderColor: $config.CARD_LAYER_3_COLOR,
448
+ },
428
449
  heading: {
429
450
  fontSize: ThemeConfig.FontSize.extraLarge,
430
451
  fontWeight: '700',