Browse Source

feat(conference) added React Navigation

Introduce navigation for all in-conference screens.
master
Calinteodor 3 years ago
parent
commit
9df59b4a6f
No account linked to committer's email address
54 changed files with 1708 additions and 601 deletions
  1. 1
    1
      android/app/src/main/java/org/jitsi/meet/MainActivity.java
  2. 2
    2
      android/build.gradle
  3. 5
    0
      android/sdk/build.gradle
  4. 2
    2
      android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java
  5. 12
    7
      android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java
  6. 10
    0
      android/settings.gradle
  7. 5
    0
      ios/Podfile
  8. 33
    3
      ios/Podfile.lock
  9. 482
    128
      package-lock.json
  10. 9
    0
      package.json
  11. 52
    0
      react/features/app/getRouteToRender.native.js
  12. 10
    52
      react/features/app/getRouteToRender.web.js
  13. 83
    0
      react/features/base/modal/components/JitsiKeyboardAvoidingView.js
  14. 69
    0
      react/features/base/modal/components/JitsiScreen.js
  15. 60
    0
      react/features/base/modal/components/functions.native.js
  16. 5
    0
      react/features/base/modal/components/styles.js
  17. 6
    1
      react/features/base/ui/Tokens.js
  18. 19
    4
      react/features/chat/components/PrivateMessageButton.js
  19. 62
    82
      react/features/chat/components/native/Chat.js
  20. 43
    0
      react/features/chat/components/native/ChatAndPolls.js
  21. 14
    5
      react/features/chat/components/native/ChatButton.js
  22. 0
    14
      react/features/chat/components/native/ChatInputBar.js
  23. 51
    11
      react/features/chat/components/native/MessageRecipient.js
  24. 1
    0
      react/features/chat/components/native/index.js
  25. 7
    2
      react/features/chat/components/native/styles.js
  26. 0
    2
      react/features/chat/constants.js
  27. 0
    17
      react/features/chat/middleware.js
  28. 21
    29
      react/features/conference/components/native/Conference.js
  29. 100
    0
      react/features/conference/components/native/ConferenceNavigationContainer.js
  30. 40
    0
      react/features/conference/components/native/ConferenceNavigationContainerRef.js
  31. 111
    0
      react/features/conference/components/native/ConferenceNavigatorScreenOptions.js
  32. 39
    0
      react/features/conference/components/native/HeaderNavigationButton.js
  33. 1
    0
      react/features/conference/components/native/index.js
  34. 17
    0
      react/features/conference/components/native/routes.js
  35. 4
    0
      react/features/conference/components/native/styles.js
  36. 20
    0
      react/features/conference/functions.native.js
  37. 6
    11
      react/features/etherpad/components/SharedDocumentButton.js
  38. 33
    58
      react/features/etherpad/components/native/SharedDocument.js
  39. 8
    0
      react/features/etherpad/components/native/styles.js
  40. 3
    3
      react/features/invite/actions.native.js
  41. 67
    31
      react/features/invite/components/add-people-dialog/native/AddPeopleDialog.js
  42. 22
    8
      react/features/invite/components/add-people-dialog/native/styles.js
  43. 0
    5
      react/features/invite/constants.js
  44. 15
    6
      react/features/lobby/components/native/LobbyScreen.js
  45. 24
    5
      react/features/lobby/components/native/styles.js
  46. 10
    11
      react/features/participants-pane/components/native/LobbyParticipantList.js
  47. 4
    1
      react/features/participants-pane/components/native/MeetingParticipantList.js
  48. 9
    12
      react/features/participants-pane/components/native/ParticipantsPane.js
  49. 4
    4
      react/features/participants-pane/components/native/ParticipantsPaneButton.js
  50. 16
    18
      react/features/participants-pane/components/native/styles.js
  51. 32
    33
      react/features/polls/components/native/PollCreate.js
  52. 42
    19
      react/features/polls/components/native/PollsPane.js
  53. 12
    6
      react/features/polls/components/native/styles.js
  54. 5
    8
      react/features/toolbox/components/native/Toolbox.js

+ 1
- 1
android/app/src/main/java/org/jitsi/meet/MainActivity.java View File

@@ -82,7 +82,7 @@ public class MainActivity extends JitsiMeetActivity {
82 82
     @Override
83 83
     protected void onCreate(Bundle savedInstanceState) {
84 84
         JitsiMeet.showSplashScreen(this);
85
-        super.onCreate(savedInstanceState);
85
+        super.onCreate(null);
86 86
     }
87 87
 
88 88
     @Override

+ 2
- 2
android/build.gradle View File

@@ -110,7 +110,7 @@ allprojects {
110 110
 
111 111
             project.version = "${json.version}-jitsi-${versionQualifierNumber}"
112 112
 
113
-            task androidSourcesJar(type: Jar) {
113
+            task jitsiAndroidSourcesJar(type: Jar) {
114 114
                 classifier = 'sources'
115 115
                 from android.sourceSets.main.java.source
116 116
             }
@@ -124,7 +124,7 @@ allprojects {
124 124
                     artifact("${project.buildDir}/outputs/aar/${project.name}-release.aar") {
125 125
                         extension "aar"
126 126
                     }
127
-                    artifact(androidSourcesJar)
127
+                    artifact(jitsiAndroidSourcesJar)
128 128
                     pom.withXml {
129 129
                         def pomXml = asNode()
130 130
                         pomXml.appendNode('name', project.name)

+ 5
- 0
android/sdk/build.gradle View File

@@ -70,9 +70,14 @@ dependencies {
70 70
     implementation project(':react-native-calendar-events')
71 71
     implementation project(':react-native-community_netinfo')
72 72
     implementation project(':react-native-default-preference')
73
+    implementation project(':react-native-gesture-handler')
73 74
     implementation project(':react-native-immersive')
74 75
     implementation project(':react-native-keep-awake')
76
+    implementation project(':react-native-masked-view_masked-view')
75 77
     implementation project(':react-native-performance')
78
+    implementation project(':react-native-reanimated')
79
+    implementation project(':react-native-safe-area-context')
80
+    implementation project(':react-native-screens')
76 81
     implementation project(':react-native-slider')
77 82
     implementation project(':react-native-sound')
78 83
     implementation project(':react-native-splash-screen')

+ 2
- 2
android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetActivity.java View File

@@ -24,7 +24,7 @@ import android.net.Uri;
24 24
 import android.os.Bundle;
25 25
 
26 26
 import androidx.annotation.Nullable;
27
-import androidx.fragment.app.FragmentActivity;
27
+import androidx.appcompat.app.AppCompatActivity;
28 28
 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
29 29
 
30 30
 import com.facebook.react.modules.core.PermissionListener;
@@ -38,7 +38,7 @@ import android.app.Activity;
38 38
  * A base activity for SDK users to embed. It uses {@link JitsiMeetFragment} to do the heavy
39 39
  * lifting and wires the remaining Activity lifecycle methods so it works out of the box.
40 40
  */
41
-public class JitsiMeetActivity extends FragmentActivity
41
+public class JitsiMeetActivity extends AppCompatActivity
42 42
     implements JitsiMeetActivityInterface {
43 43
 
44 44
     protected static final String TAG = JitsiMeetActivity.class.getSimpleName();

+ 12
- 7
android/sdk/src/main/java/org/jitsi/meet/sdk/ReactInstanceManagerHolder.java View File

@@ -175,21 +175,26 @@ class ReactInstanceManagerHolder {
175 175
 
176 176
         List<ReactPackage> packages
177 177
             = new ArrayList<>(Arrays.asList(
178
+                new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
179
+                new com.ocetnik.timer.BackgroundTimerPackage(),
178 180
                 new com.calendarevents.CalendarEventsPackage(),
179 181
                 new com.corbt.keepawake.KCKeepAwakePackage(),
180 182
                 new com.facebook.react.shell.MainReactPackage(),
181
-                new com.horcrux.svg.SvgPackage(),
182
-                new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
183
-                new com.learnium.RNDeviceInfo.RNDeviceInfo(),
184
-                new com.oblador.performance.PerformancePackage(),
185
-                new com.ocetnik.timer.BackgroundTimerPackage(),
186
-                new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
187 183
                 new com.reactnativecommunity.netinfo.NetInfoPackage(),
184
+                new com.oblador.performance.PerformancePackage(),
188 185
                 new com.reactnativecommunity.slider.ReactSliderPackage(),
186
+                new com.brentvatne.react.ReactVideoPackage(),
187
+                new com.swmansion.reanimated.ReanimatedPackage(),
188
+                new org.reactnative.maskedview.RNCMaskedViewPackage(),
189 189
                 new com.reactnativecommunity.webview.RNCWebViewPackage(),
190
+                new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
191
+                new com.learnium.RNDeviceInfo.RNDeviceInfo(),
192
+                new com.swmansion.gesturehandler.react.RNGestureHandlerPackage(),
190 193
                 new com.rnimmersive.RNImmersivePackage(),
194
+                new com.swmansion.rnscreens.RNScreensPackage(),
191 195
                 new com.zmxv.RNSound.RNSoundPackage(),
192
-                new com.brentvatne.react.ReactVideoPackage(),
196
+                new com.th3rdwave.safeareacontext.SafeAreaContextPackage(),
197
+                new com.horcrux.svg.SvgPackage(),
193 198
                 new ReactPackageAdapter() {
194 199
                     @Override
195 200
                     public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

+ 10
- 0
android/settings.gradle View File

@@ -15,14 +15,24 @@ include ':react-native-default-preference'
15 15
 project(':react-native-default-preference').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-default-preference/android')
16 16
 include ':react-native-device-info'
17 17
 project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android')
18
+include ':react-native-gesture-handler'
19
+project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
18 20
 include ':react-native-google-signin'
19 21
 project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/google-signin/android')
20 22
 include ':react-native-immersive'
21 23
 project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
22 24
 include ':react-native-keep-awake'
23 25
 project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
26
+include ':react-native-masked-view_masked-view'
27
+project(':react-native-masked-view_masked-view').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-masked-view/masked-view/android')
24 28
 include ':react-native-performance'
25 29
 project(':react-native-performance').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-performance/android')
30
+include ':react-native-reanimated'
31
+project(':react-native-reanimated').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-reanimated/android')
32
+include ':react-native-safe-area-context'
33
+project(':react-native-safe-area-context').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-safe-area-context/android')
34
+include ':react-native-screens'
35
+project(':react-native-screens').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-screens/android')
26 36
 include ':react-native-slider'
27 37
 project(':react-native-slider').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/slider/android')
28 38
 include ':react-native-sound'

+ 5
- 0
ios/Podfile View File

@@ -72,6 +72,11 @@ target 'JitsiMeetSDK' do
72 72
   pod 'RNSVG', :path => '../node_modules/react-native-svg'
73 73
   pod 'RNWatch', :path => '../node_modules/react-native-watch-connectivity'
74 74
   pod 'RNDefaultPreference', :path => '../node_modules/react-native-default-preference'
75
+  pod 'RNGestureHandler', :path => '../node_modules/react-native-gesture-handler'
76
+  pod 'RNReanimated', :path => '../node_modules/react-native-reanimated'
77
+  pod 'RNScreens', :path => '../node_modules/react-native-screens'
78
+  pod 'react-native-safe-area-context', :path => '../node_modules/react-native-safe-area-context'
79
+  pod 'RNCMaskedView', :path => '../node_modules/@react-native-masked-view/masked-view'
75 80
 
76 81
   # Native pod dependencies
77 82
   #

+ 33
- 3
ios/Podfile.lock View File

@@ -109,7 +109,7 @@ PODS:
109 109
   - GTMAppAuth (1.2.2):
110 110
     - AppAuth/Core (~> 1.4)
111 111
     - GTMSessionFetcher/Core (~> 1.5)
112
-  - GTMSessionFetcher/Core (1.6.1)
112
+  - GTMSessionFetcher/Core (1.7.0)
113 113
   - nanopb (1.30906.0):
114 114
     - nanopb/decode (= 1.30906.0)
115 115
     - nanopb/encode (= 1.30906.0)
@@ -290,6 +290,8 @@ PODS:
290 290
     - React
291 291
   - react-native-performance (2.0.0):
292 292
     - React-Core
293
+  - react-native-safe-area-context (3.3.2):
294
+    - React-Core
293 295
   - react-native-slider (3.0.3):
294 296
     - React
295 297
   - react-native-splash-screen (3.2.0):
@@ -359,13 +361,21 @@ PODS:
359 361
     - ReactCommon/turbomodule/core (= 0.61.5-jitsi.2)
360 362
   - RNCAsyncStorage (1.15.5):
361 363
     - React-Core
364
+  - RNCMaskedView (0.2.6):
365
+    - React-Core
362 366
   - RNDefaultPreference (1.4.2):
363 367
     - React
364 368
   - RNDeviceInfo (8.0.0):
365 369
     - React-Core
370
+  - RNGestureHandler (1.10.3):
371
+    - React-Core
366 372
   - RNGoogleSignin (3.0.1):
367 373
     - GoogleSignIn (~> 5.0.0)
368 374
     - React
375
+  - RNReanimated (1.13.3):
376
+    - React-Core
377
+  - RNScreens (2.18.1):
378
+    - React-Core
369 379
   - RNSound (0.11.0):
370 380
     - React
371 381
     - RNSound/Core (= 0.11.0)
@@ -405,6 +415,7 @@ DEPENDENCIES:
405 415
   - react-native-keep-awake (from `../node_modules/react-native-keep-awake`)
406 416
   - "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
407 417
   - react-native-performance (from `../node_modules/react-native-performance/ios`)
418
+  - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
408 419
   - "react-native-slider (from `../node_modules/@react-native-community/slider`)"
409 420
   - react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
410 421
   - react-native-video (from `../node_modules/react-native-video/react-native-video.podspec`)
@@ -421,9 +432,13 @@ DEPENDENCIES:
421 432
   - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
422 433
   - ReactCommon/turbomodule (from `../node_modules/react-native/ReactCommon`)
423 434
   - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
435
+  - "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)"
424 436
   - RNDefaultPreference (from `../node_modules/react-native-default-preference`)
425 437
   - RNDeviceInfo (from `../node_modules/react-native-device-info`)
438
+  - RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
426 439
   - "RNGoogleSignin (from `../node_modules/@react-native-community/google-signin`)"
440
+  - RNReanimated (from `../node_modules/react-native-reanimated`)
441
+  - RNScreens (from `../node_modules/react-native-screens`)
427 442
   - RNSound (from `../node_modules/react-native-sound`)
428 443
   - RNSVG (from `../node_modules/react-native-svg`)
429 444
   - RNWatch (from `../node_modules/react-native-watch-connectivity`)
@@ -493,6 +508,8 @@ EXTERNAL SOURCES:
493 508
     :path: "../node_modules/@react-native-community/netinfo"
494 509
   react-native-performance:
495 510
     :path: "../node_modules/react-native-performance/ios"
511
+  react-native-safe-area-context:
512
+    :path: "../node_modules/react-native-safe-area-context"
496 513
   react-native-slider:
497 514
     :path: "../node_modules/@react-native-community/slider"
498 515
   react-native-splash-screen:
@@ -525,12 +542,20 @@ EXTERNAL SOURCES:
525 542
     :path: "../node_modules/react-native/ReactCommon"
526 543
   RNCAsyncStorage:
527 544
     :path: "../node_modules/@react-native-async-storage/async-storage"
545
+  RNCMaskedView:
546
+    :path: "../node_modules/@react-native-masked-view/masked-view"
528 547
   RNDefaultPreference:
529 548
     :path: "../node_modules/react-native-default-preference"
530 549
   RNDeviceInfo:
531 550
     :path: "../node_modules/react-native-device-info"
551
+  RNGestureHandler:
552
+    :path: "../node_modules/react-native-gesture-handler"
532 553
   RNGoogleSignin:
533 554
     :path: "../node_modules/@react-native-community/google-signin"
555
+  RNReanimated:
556
+    :path: "../node_modules/react-native-reanimated"
557
+  RNScreens:
558
+    :path: "../node_modules/react-native-screens"
534 559
   RNSound:
535 560
     :path: "../node_modules/react-native-sound"
536 561
   RNSVG:
@@ -563,7 +588,7 @@ SPEC CHECKSUMS:
563 588
   GoogleSignIn: 7137d297ddc022a7e0aa4619c86d72c909fa7213
564 589
   GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
565 590
   GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89
566
-  GTMSessionFetcher: 36689134877faeb055b27dfa4ccc9ceaa42e029e
591
+  GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91
567 592
   nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
568 593
   ObjectiveDropboxOfficial: b4765572e334d6fc6214b43a7595510324bbbbaa
569 594
   PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
@@ -581,6 +606,7 @@ SPEC CHECKSUMS:
581 606
   react-native-keep-awake: afad8a51dfef9fe9655a6344771be32c8596d774
582 607
   react-native-netinfo: 0e563248a4b9a99c33ec29bd03c2d50767db22a6
583 608
   react-native-performance: 6bd6cfac80594775fb782405fceaaf206becf53b
609
+  react-native-safe-area-context: 584dc04881deb49474363f3be89e4ca0e854c057
584 610
   react-native-slider: e99fc201cefe81270fc9d81714a7a0f5e566b168
585 611
   react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
586 612
   react-native-video: 0bb76b6d6b77da3009611586c7dbf817b947f30e
@@ -597,14 +623,18 @@ SPEC CHECKSUMS:
597 623
   React-RCTVibration: c1041024893fdfdb8371e7c720c437751b711676
598 624
   ReactCommon: 18014e1d98dbeb9141e935cfe35fc93bd511ffb6
599 625
   RNCAsyncStorage: 56a3355a10b5d660c48c6e37325ac85ebfd09885
626
+  RNCMaskedView: c298b644a10c0c142055b3ae24d83879ecb13ccd
600 627
   RNDefaultPreference: 1f8133ec0bc0f9453cdada578564ba1ef551fb44
601 628
   RNDeviceInfo: 87d2d175c760f6bcf58acd036f887e8b2392802c
629
+  RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211
602 630
   RNGoogleSignin: 39336070b35fc4cea6a98cf111e00480317be0ae
631
+  RNReanimated: 514a11da3a2bcc6c3dfd9de32b38e2b9bf101926
632
+  RNScreens: f7ad633b2e0190b77b6a7aab7f914fad6f198d8d
603 633
   RNSound: da030221e6ac7e8290c6b43f2b5f2133a8e225b0
604 634
   RNSVG: ce9d996113475209013317e48b05c21ee988d42e
605 635
   RNWatch: a5320c959c75e72845c07985f3e935e58998f1d3
606 636
   Yoga: 96b469c5e81ff51b917b92e8c3390642d4ded30c
607 637
 
608
-PODFILE CHECKSUM: 3cc305fd6ee83fff506c10c4805471fa72b61c9a
638
+PODFILE CHECKSUM: 42be6796ba6ac039dae5c02125677728ecd0df0d
609 639
 
610 640
 COCOAPODS: 1.10.1

+ 482
- 128
package-lock.json View File

@@ -37,6 +37,10 @@
37 37
         "@react-native-community/google-signin": "3.0.1",
38 38
         "@react-native-community/netinfo": "4.1.5",
39 39
         "@react-native-community/slider": "3.0.3",
40
+        "@react-native-masked-view/masked-view": "0.2.6",
41
+        "@react-navigation/material-top-tabs": "5.3.19",
42
+        "@react-navigation/native": "5.9.8",
43
+        "@react-navigation/stack": "5.14.9",
40 44
         "@svgr/webpack": "4.3.2",
41 45
         "amplitude-js": "8.2.1",
42 46
         "base64-js": "1.3.1",
@@ -76,14 +80,19 @@
76 80
         "react-native-collapsible": "1.5.1",
77 81
         "react-native-default-preference": "1.4.2",
78 82
         "react-native-device-info": "8.0.0",
83
+        "react-native-gesture-handler": "1.10.3",
79 84
         "react-native-immersive": "2.0.0",
80 85
         "react-native-keep-awake": "4.0.0",
81 86
         "react-native-paper": "4.8.1",
82 87
         "react-native-performance": "2.0.0",
88
+        "react-native-reanimated": "1.13.3",
89
+        "react-native-safe-area-context": "3.3.2",
90
+        "react-native-screens": "2.18.1",
83 91
         "react-native-sound": "github:jitsi/react-native-sound#3fe5480fce935e888d5089d94a191c7c7e3aa190",
84 92
         "react-native-splash-screen": "3.2.0",
85 93
         "react-native-svg": "12.1.0",
86 94
         "react-native-svg-transformer": "0.14.3",
95
+        "react-native-tab-view": "2.16.0",
87 96
         "react-native-url-polyfill": "1.2.0",
88 97
         "react-native-video": "5.1.1",
89 98
         "react-native-watch-connectivity": "0.4.3",
@@ -2929,6 +2938,17 @@
2929 2938
         "node": ">=10.0.0"
2930 2939
       }
2931 2940
     },
2941
+    "node_modules/@egjs/hammerjs": {
2942
+      "version": "2.0.17",
2943
+      "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
2944
+      "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
2945
+      "dependencies": {
2946
+        "@types/hammerjs": "^2.0.36"
2947
+      },
2948
+      "engines": {
2949
+        "node": ">=0.8.0"
2950
+      }
2951
+    },
2932 2952
     "node_modules/@emotion/cache": {
2933 2953
       "version": "10.0.29",
2934 2954
       "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz",
@@ -4036,6 +4056,133 @@
4036 4056
         "react-native": "*"
4037 4057
       }
4038 4058
     },
4059
+    "node_modules/@react-native-masked-view/masked-view": {
4060
+      "version": "0.2.6",
4061
+      "resolved": "https://registry.npmjs.org/@react-native-masked-view/masked-view/-/masked-view-0.2.6.tgz",
4062
+      "integrity": "sha512-303CxmetUmgiX9NSUxatZkNh9qTYYdiM8xkGf9I3Uj20U3eGY3M78ljeNQ4UVCJA+FNGS5nC1dtS9GjIqvB4dg==",
4063
+      "peerDependencies": {
4064
+        "react": "16 || 17",
4065
+        "react-native": ">=0.57"
4066
+      }
4067
+    },
4068
+    "node_modules/@react-navigation/core": {
4069
+      "version": "5.16.1",
4070
+      "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.16.1.tgz",
4071
+      "integrity": "sha512-3AToC7vPNeSNcHFLd1h71L6u34hfXoRAS1CxF9Fc4uC8uOrVqcNvphpeFbE0O9Bw6Zpl0BnMFl7E5gaL3KGzNA==",
4072
+      "dependencies": {
4073
+        "@react-navigation/routers": "^5.7.4",
4074
+        "escape-string-regexp": "^4.0.0",
4075
+        "nanoid": "^3.1.15",
4076
+        "query-string": "^6.13.6",
4077
+        "react-is": "^16.13.0"
4078
+      },
4079
+      "peerDependencies": {
4080
+        "react": "*"
4081
+      }
4082
+    },
4083
+    "node_modules/@react-navigation/core/node_modules/escape-string-regexp": {
4084
+      "version": "4.0.0",
4085
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
4086
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
4087
+      "engines": {
4088
+        "node": ">=10"
4089
+      },
4090
+      "funding": {
4091
+        "url": "https://github.com/sponsors/sindresorhus"
4092
+      }
4093
+    },
4094
+    "node_modules/@react-navigation/core/node_modules/query-string": {
4095
+      "version": "6.14.1",
4096
+      "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
4097
+      "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
4098
+      "dependencies": {
4099
+        "decode-uri-component": "^0.2.0",
4100
+        "filter-obj": "^1.1.0",
4101
+        "split-on-first": "^1.0.0",
4102
+        "strict-uri-encode": "^2.0.0"
4103
+      },
4104
+      "engines": {
4105
+        "node": ">=6"
4106
+      },
4107
+      "funding": {
4108
+        "url": "https://github.com/sponsors/sindresorhus"
4109
+      }
4110
+    },
4111
+    "node_modules/@react-navigation/core/node_modules/strict-uri-encode": {
4112
+      "version": "2.0.0",
4113
+      "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
4114
+      "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=",
4115
+      "engines": {
4116
+        "node": ">=4"
4117
+      }
4118
+    },
4119
+    "node_modules/@react-navigation/material-top-tabs": {
4120
+      "version": "5.3.19",
4121
+      "resolved": "https://registry.npmjs.org/@react-navigation/material-top-tabs/-/material-top-tabs-5.3.19.tgz",
4122
+      "integrity": "sha512-I7bEF99THxxcY7kCUZ5pPmwXr6kgo6L2sg3P1YJo+CcBWSGvGiHyNbZXNs15HuKRuFvEuueChNV9n8QuKBWbDA==",
4123
+      "dependencies": {
4124
+        "color": "^3.1.3"
4125
+      },
4126
+      "peerDependencies": {
4127
+        "@react-navigation/native": "^5.0.5",
4128
+        "react": "*",
4129
+        "react-native": "*",
4130
+        "react-native-gesture-handler": ">= 1.0.0",
4131
+        "react-native-reanimated": ">= 1.0.0",
4132
+        "react-native-tab-view": ">= 2.0.0"
4133
+      }
4134
+    },
4135
+    "node_modules/@react-navigation/native": {
4136
+      "version": "5.9.8",
4137
+      "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.9.8.tgz",
4138
+      "integrity": "sha512-DNbcDHXQPSFDLn51kkVVJjT3V7jJy2GztNYZe/2bEg29mi5QEcHHcpifjMCtyFKntAOWzKlG88UicIQ17UEghg==",
4139
+      "dependencies": {
4140
+        "@react-navigation/core": "^5.16.1",
4141
+        "escape-string-regexp": "^4.0.0",
4142
+        "nanoid": "^3.1.15"
4143
+      },
4144
+      "peerDependencies": {
4145
+        "react": "*",
4146
+        "react-native": "*"
4147
+      }
4148
+    },
4149
+    "node_modules/@react-navigation/native/node_modules/escape-string-regexp": {
4150
+      "version": "4.0.0",
4151
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
4152
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
4153
+      "engines": {
4154
+        "node": ">=10"
4155
+      },
4156
+      "funding": {
4157
+        "url": "https://github.com/sponsors/sindresorhus"
4158
+      }
4159
+    },
4160
+    "node_modules/@react-navigation/routers": {
4161
+      "version": "5.7.4",
4162
+      "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-5.7.4.tgz",
4163
+      "integrity": "sha512-0N202XAqsU/FlE53Nmh6GHyMtGm7g6TeC93mrFAFJOqGRKznT0/ail+cYlU6tNcPA9AHzZu1Modw1eoDINSliQ==",
4164
+      "dependencies": {
4165
+        "nanoid": "^3.1.15"
4166
+      }
4167
+    },
4168
+    "node_modules/@react-navigation/stack": {
4169
+      "version": "5.14.9",
4170
+      "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-5.14.9.tgz",
4171
+      "integrity": "sha512-DuvrT9P+Tz8ezZLQYxORZqOGqO+vEufaxlW1hSLw1knLD4jNxkz8TJDXtfKwaz//9gb43UhTNccNM02vm7iPqQ==",
4172
+      "dependencies": {
4173
+        "color": "^3.1.3",
4174
+        "react-native-iphone-x-helper": "^1.3.0"
4175
+      },
4176
+      "peerDependencies": {
4177
+        "@react-native-community/masked-view": ">= 0.1.0",
4178
+        "@react-navigation/native": "^5.0.5",
4179
+        "react": "*",
4180
+        "react-native": "*",
4181
+        "react-native-gesture-handler": ">= 1.0.0",
4182
+        "react-native-safe-area-context": ">= 0.6.0",
4183
+        "react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
4184
+      }
4185
+    },
4039 4186
     "node_modules/@svgr/babel-plugin-add-jsx-attribute": {
4040 4187
       "version": "4.2.0",
4041 4188
       "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
@@ -5045,6 +5192,11 @@
5045 5192
       "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==",
5046 5193
       "dev": true
5047 5194
     },
5195
+    "node_modules/@types/hammerjs": {
5196
+      "version": "2.0.40",
5197
+      "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.40.tgz",
5198
+      "integrity": "sha512-VbjwR1fhsn2h2KXAY4oy1fm7dCxaKy0D+deTb8Ilc3Eo3rc5+5eA4rfYmZaHgNJKxVyI0f6WIXzO2zLkVmQPHA=="
5199
+    },
5048 5200
     "node_modules/@types/http-proxy": {
5049 5201
       "version": "1.17.7",
5050 5202
       "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz",
@@ -7651,6 +7803,22 @@
7651 7803
         "object-assign": "^4.1.1"
7652 7804
       }
7653 7805
     },
7806
+    "node_modules/cross-fetch": {
7807
+      "version": "3.1.4",
7808
+      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz",
7809
+      "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==",
7810
+      "dependencies": {
7811
+        "node-fetch": "2.6.1"
7812
+      }
7813
+    },
7814
+    "node_modules/cross-fetch/node_modules/node-fetch": {
7815
+      "version": "2.6.1",
7816
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
7817
+      "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
7818
+      "engines": {
7819
+        "node": "4.x || >=6.0.0"
7820
+      }
7821
+    },
7654 7822
     "node_modules/cross-os": {
7655 7823
       "version": "1.4.0",
7656 7824
       "resolved": "https://registry.npmjs.org/cross-os/-/cross-os-1.4.0.tgz",
@@ -10014,6 +10182,14 @@
10014 10182
         "node": ">=0.10.0"
10015 10183
       }
10016 10184
     },
10185
+    "node_modules/filter-obj": {
10186
+      "version": "1.1.0",
10187
+      "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
10188
+      "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=",
10189
+      "engines": {
10190
+        "node": ">=0.10.0"
10191
+      }
10192
+    },
10017 10193
     "node_modules/finalhandler": {
10018 10194
       "version": "1.1.2",
10019 10195
       "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@@ -10582,12 +10758,12 @@
10582 10758
       }
10583 10759
     },
10584 10760
     "node_modules/fsevents/node_modules/mkdirp": {
10585
-      "version": "0.5.5",
10761
+      "version": "0.5.1",
10586 10762
       "inBundle": true,
10587 10763
       "license": "MIT",
10588 10764
       "optional": true,
10589 10765
       "dependencies": {
10590
-        "minimist": "^1.2.5"
10766
+        "minimist": "0.0.8"
10591 10767
       },
10592 10768
       "bin": {
10593 10769
         "mkdirp": "bin/cmd.js"
@@ -10764,7 +10940,7 @@
10764 10940
       }
10765 10941
     },
10766 10942
     "node_modules/fsevents/node_modules/rc/node_modules/minimist": {
10767
-      "version": "1.2.5",
10943
+      "version": "1.2.0",
10768 10944
       "inBundle": true,
10769 10945
       "license": "MIT",
10770 10946
       "optional": true
@@ -11812,9 +11988,9 @@
11812 11988
       }
11813 11989
     },
11814 11990
     "node_modules/invariant": {
11815
-      "version": "2.2.3",
11816
-      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.3.tgz",
11817
-      "integrity": "sha512-7Z5PPegwDTyjbaeCnV0efcyS6vdKAU51kpEmS7QFib3P4822l8ICYyMn7qvJnc+WzLoDsuI9gPMKbJ8pCu8XtA==",
11991
+      "version": "2.2.4",
11992
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
11993
+      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
11818 11994
       "dependencies": {
11819 11995
         "loose-envify": "^1.0.0"
11820 11996
       }
@@ -12382,14 +12558,6 @@
12382 12558
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
12383 12559
       "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
12384 12560
     },
12385
-    "node_modules/jest-haste-map/node_modules/invariant": {
12386
-      "version": "2.2.4",
12387
-      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
12388
-      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
12389
-      "dependencies": {
12390
-        "loose-envify": "^1.0.0"
12391
-      }
12392
-    },
12393 12561
     "node_modules/jest-message-util": {
12394 12562
       "version": "24.9.0",
12395 12563
       "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz",
@@ -13641,14 +13809,6 @@
13641 13809
         "vlq": "^1.0.0"
13642 13810
       }
13643 13811
     },
13644
-    "node_modules/metro-source-map/node_modules/invariant": {
13645
-      "version": "2.2.4",
13646
-      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
13647
-      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
13648
-      "dependencies": {
13649
-        "loose-envify": "^1.0.0"
13650
-      }
13651
-    },
13652 13812
     "node_modules/metro-symbolicate": {
13653 13813
       "version": "0.56.4",
13654 13814
       "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.56.4.tgz",
@@ -13667,14 +13827,6 @@
13667 13827
         "node": ">=8.3"
13668 13828
       }
13669 13829
     },
13670
-    "node_modules/metro-symbolicate/node_modules/invariant": {
13671
-      "version": "2.2.4",
13672
-      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
13673
-      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
13674
-      "dependencies": {
13675
-        "loose-envify": "^1.0.0"
13676
-      }
13677
-    },
13678 13830
     "node_modules/metro/node_modules/@babel/helper-plugin-utils": {
13679 13831
       "version": "7.8.3",
13680 13832
       "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
@@ -13759,14 +13911,6 @@
13759 13911
         "klaw": "^1.0.0"
13760 13912
       }
13761 13913
     },
13762
-    "node_modules/metro/node_modules/invariant": {
13763
-      "version": "2.2.4",
13764
-      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
13765
-      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
13766
-      "dependencies": {
13767
-        "loose-envify": "^1.0.0"
13768
-      }
13769
-    },
13770 13914
     "node_modules/metro/node_modules/jsonfile": {
13771 13915
       "version": "2.4.0",
13772 13916
       "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
@@ -14124,6 +14268,17 @@
14124 14268
       "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
14125 14269
       "optional": true
14126 14270
     },
14271
+    "node_modules/nanoid": {
14272
+      "version": "3.1.30",
14273
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz",
14274
+      "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==",
14275
+      "bin": {
14276
+        "nanoid": "bin/nanoid.cjs"
14277
+      },
14278
+      "engines": {
14279
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
14280
+      }
14281
+    },
14127 14282
     "node_modules/nanomatch": {
14128 14283
       "version": "1.2.13",
14129 14284
       "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -16558,6 +16713,32 @@
16558 16713
         "react-native": "*"
16559 16714
       }
16560 16715
     },
16716
+    "node_modules/react-native-gesture-handler": {
16717
+      "version": "1.10.3",
16718
+      "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
16719
+      "integrity": "sha512-cBGMi1IEsIVMgoox4RvMx7V2r6bNKw0uR1Mu1o7NbuHS6BRSVLq0dP34l2ecnPlC+jpWd3le6Yg1nrdCjby2Mw==",
16720
+      "dependencies": {
16721
+        "@egjs/hammerjs": "^2.0.17",
16722
+        "fbjs": "^3.0.0",
16723
+        "hoist-non-react-statics": "^3.3.0",
16724
+        "invariant": "^2.2.4",
16725
+        "prop-types": "^15.7.2"
16726
+      }
16727
+    },
16728
+    "node_modules/react-native-gesture-handler/node_modules/fbjs": {
16729
+      "version": "3.0.0",
16730
+      "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz",
16731
+      "integrity": "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==",
16732
+      "dependencies": {
16733
+        "cross-fetch": "^3.0.4",
16734
+        "fbjs-css-vars": "^1.0.0",
16735
+        "loose-envify": "^1.0.0",
16736
+        "object-assign": "^4.1.0",
16737
+        "promise": "^7.1.1",
16738
+        "setimmediate": "^1.0.5",
16739
+        "ua-parser-js": "^0.7.18"
16740
+      }
16741
+    },
16561 16742
     "node_modules/react-native-immersive": {
16562 16743
       "version": "2.0.0",
16563 16744
       "resolved": "https://registry.npmjs.org/react-native-immersive/-/react-native-immersive-2.0.0.tgz",
@@ -16602,6 +16783,51 @@
16602 16783
         "react-native": "*"
16603 16784
       }
16604 16785
     },
16786
+    "node_modules/react-native-reanimated": {
16787
+      "version": "1.13.3",
16788
+      "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-1.13.3.tgz",
16789
+      "integrity": "sha512-i714H24dv6ncpFO7/SZ0PfAMbvjgVbF8Ow2NPtowoZAz8osS54DmTMrkgJ9Za+uEku/s0AEaxqiXG2Xgntvv2g==",
16790
+      "dependencies": {
16791
+        "fbjs": "^1.0.0"
16792
+      },
16793
+      "peerDependencies": {
16794
+        "react": "*",
16795
+        "react-native": "*"
16796
+      }
16797
+    },
16798
+    "node_modules/react-native-reanimated/node_modules/fbjs": {
16799
+      "version": "1.0.0",
16800
+      "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-1.0.0.tgz",
16801
+      "integrity": "sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA==",
16802
+      "dependencies": {
16803
+        "core-js": "^2.4.1",
16804
+        "fbjs-css-vars": "^1.0.0",
16805
+        "isomorphic-fetch": "^2.1.1",
16806
+        "loose-envify": "^1.0.0",
16807
+        "object-assign": "^4.1.0",
16808
+        "promise": "^7.1.1",
16809
+        "setimmediate": "^1.0.5",
16810
+        "ua-parser-js": "^0.7.18"
16811
+      }
16812
+    },
16813
+    "node_modules/react-native-safe-area-context": {
16814
+      "version": "3.3.2",
16815
+      "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz",
16816
+      "integrity": "sha512-yOwiiPJ1rk+/nfK13eafbpW6sKW0jOnsRem2C1LPJjM3tfTof6hlvV5eWHATye3XOpu2cJ7N+HdkUvUDGwFD2Q==",
16817
+      "peerDependencies": {
16818
+        "react": "*",
16819
+        "react-native": "*"
16820
+      }
16821
+    },
16822
+    "node_modules/react-native-screens": {
16823
+      "version": "2.18.1",
16824
+      "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-2.18.1.tgz",
16825
+      "integrity": "sha512-r5WZLpmx2hHjC1RgMdPq5YpSU9tEhBpUaZ5M1SUtNIONyiLqQVxabhRCINdebIk4depJiIl7yw2Q85zJyeX6fw==",
16826
+      "peerDependencies": {
16827
+        "react": "*",
16828
+        "react-native": "*"
16829
+      }
16830
+    },
16605 16831
     "node_modules/react-native-sound": {
16606 16832
       "version": "0.11.0",
16607 16833
       "resolved": "git+ssh://git@github.com/jitsi/react-native-sound.git#3fe5480fce935e888d5089d94a191c7c7e3aa190",
@@ -16734,6 +16960,17 @@
16734 16960
         "semver": "bin/semver"
16735 16961
       }
16736 16962
     },
16963
+    "node_modules/react-native-tab-view": {
16964
+      "version": "2.16.0",
16965
+      "resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-2.16.0.tgz",
16966
+      "integrity": "sha512-ac2DmT7+l13wzIFqtbfXn4wwfgtPoKzWjjZyrK1t+T8sdemuUvD4zIt+UImg03fu3s3VD8Wh/fBrIdcqQyZJWg==",
16967
+      "peerDependencies": {
16968
+        "react": "*",
16969
+        "react-native": "*",
16970
+        "react-native-gesture-handler": "*",
16971
+        "react-native-reanimated": "*"
16972
+      }
16973
+    },
16737 16974
     "node_modules/react-native-url-polyfill": {
16738 16975
       "version": "1.2.0",
16739 16976
       "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-1.2.0.tgz",
@@ -16809,14 +17046,6 @@
16809 17046
         "node": ">=8"
16810 17047
       }
16811 17048
     },
16812
-    "node_modules/react-native-webview/node_modules/invariant": {
16813
-      "version": "2.2.4",
16814
-      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
16815
-      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
16816
-      "dependencies": {
16817
-        "loose-envify": "^1.0.0"
16818
-      }
16819
-    },
16820 17049
     "node_modules/react-native-youtube-iframe": {
16821 17050
       "version": "2.1.1",
16822 17051
       "resolved": "https://registry.npmjs.org/react-native-youtube-iframe/-/react-native-youtube-iframe-2.1.1.tgz",
@@ -16854,14 +17083,6 @@
16854 17083
         "ua-parser-js": "^0.7.18"
16855 17084
       }
16856 17085
     },
16857
-    "node_modules/react-native/node_modules/invariant": {
16858
-      "version": "2.2.4",
16859
-      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
16860
-      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
16861
-      "dependencies": {
16862
-        "loose-envify": "^1.0.0"
16863
-      }
16864
-    },
16865 17086
     "node_modules/react-native/node_modules/whatwg-fetch": {
16866 17087
       "version": "3.0.0",
16867 17088
       "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
@@ -16902,14 +17123,6 @@
16902 17123
         "redux": "^2.0.0 || ^3.0.0 || ^4.0.0-0"
16903 17124
       }
16904 17125
     },
16905
-    "node_modules/react-redux/node_modules/invariant": {
16906
-      "version": "2.2.4",
16907
-      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
16908
-      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
16909
-      "dependencies": {
16910
-        "loose-envify": "^1.0.0"
16911
-      }
16912
-    },
16913 17126
     "node_modules/react-redux/node_modules/loose-envify": {
16914 17127
       "version": "1.4.0",
16915 17128
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -18275,6 +18488,14 @@
18275 18488
       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
18276 18489
       "dev": true
18277 18490
     },
18491
+    "node_modules/split-on-first": {
18492
+      "version": "1.1.0",
18493
+      "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
18494
+      "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
18495
+      "engines": {
18496
+        "node": ">=6"
18497
+      }
18498
+    },
18278 18499
     "node_modules/split-string": {
18279 18500
       "version": "3.1.0",
18280 18501
       "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -24126,6 +24347,14 @@
24126 24347
       "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==",
24127 24348
       "dev": true
24128 24349
     },
24350
+    "@egjs/hammerjs": {
24351
+      "version": "2.0.17",
24352
+      "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
24353
+      "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
24354
+      "requires": {
24355
+        "@types/hammerjs": "^2.0.36"
24356
+      }
24357
+    },
24129 24358
     "@emotion/cache": {
24130 24359
       "version": "10.0.29",
24131 24360
       "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz",
@@ -24988,6 +25217,88 @@
24988 25217
       "resolved": "https://registry.npmjs.org/@react-native-community/slider/-/slider-3.0.3.tgz",
24989 25218
       "integrity": "sha512-8IeHfDwJ9/CTUwFs6x90VlobV3BfuPgNLjTgC6dRZovfCWigaZwVNIFFJnHBakK3pW2xErAPwhdvNR4JeNoYbw=="
24990 25219
     },
25220
+    "@react-native-masked-view/masked-view": {
25221
+      "version": "0.2.6",
25222
+      "resolved": "https://registry.npmjs.org/@react-native-masked-view/masked-view/-/masked-view-0.2.6.tgz",
25223
+      "integrity": "sha512-303CxmetUmgiX9NSUxatZkNh9qTYYdiM8xkGf9I3Uj20U3eGY3M78ljeNQ4UVCJA+FNGS5nC1dtS9GjIqvB4dg=="
25224
+    },
25225
+    "@react-navigation/core": {
25226
+      "version": "5.16.1",
25227
+      "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.16.1.tgz",
25228
+      "integrity": "sha512-3AToC7vPNeSNcHFLd1h71L6u34hfXoRAS1CxF9Fc4uC8uOrVqcNvphpeFbE0O9Bw6Zpl0BnMFl7E5gaL3KGzNA==",
25229
+      "requires": {
25230
+        "@react-navigation/routers": "^5.7.4",
25231
+        "escape-string-regexp": "^4.0.0",
25232
+        "nanoid": "^3.1.15",
25233
+        "query-string": "^6.13.6",
25234
+        "react-is": "^16.13.0"
25235
+      },
25236
+      "dependencies": {
25237
+        "escape-string-regexp": {
25238
+          "version": "4.0.0",
25239
+          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
25240
+          "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
25241
+        },
25242
+        "query-string": {
25243
+          "version": "6.14.1",
25244
+          "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
25245
+          "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
25246
+          "requires": {
25247
+            "decode-uri-component": "^0.2.0",
25248
+            "filter-obj": "^1.1.0",
25249
+            "split-on-first": "^1.0.0",
25250
+            "strict-uri-encode": "^2.0.0"
25251
+          }
25252
+        },
25253
+        "strict-uri-encode": {
25254
+          "version": "2.0.0",
25255
+          "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
25256
+          "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
25257
+        }
25258
+      }
25259
+    },
25260
+    "@react-navigation/material-top-tabs": {
25261
+      "version": "5.3.19",
25262
+      "resolved": "https://registry.npmjs.org/@react-navigation/material-top-tabs/-/material-top-tabs-5.3.19.tgz",
25263
+      "integrity": "sha512-I7bEF99THxxcY7kCUZ5pPmwXr6kgo6L2sg3P1YJo+CcBWSGvGiHyNbZXNs15HuKRuFvEuueChNV9n8QuKBWbDA==",
25264
+      "requires": {
25265
+        "color": "^3.1.3"
25266
+      }
25267
+    },
25268
+    "@react-navigation/native": {
25269
+      "version": "5.9.8",
25270
+      "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.9.8.tgz",
25271
+      "integrity": "sha512-DNbcDHXQPSFDLn51kkVVJjT3V7jJy2GztNYZe/2bEg29mi5QEcHHcpifjMCtyFKntAOWzKlG88UicIQ17UEghg==",
25272
+      "requires": {
25273
+        "@react-navigation/core": "^5.16.1",
25274
+        "escape-string-regexp": "^4.0.0",
25275
+        "nanoid": "^3.1.15"
25276
+      },
25277
+      "dependencies": {
25278
+        "escape-string-regexp": {
25279
+          "version": "4.0.0",
25280
+          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
25281
+          "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
25282
+        }
25283
+      }
25284
+    },
25285
+    "@react-navigation/routers": {
25286
+      "version": "5.7.4",
25287
+      "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-5.7.4.tgz",
25288
+      "integrity": "sha512-0N202XAqsU/FlE53Nmh6GHyMtGm7g6TeC93mrFAFJOqGRKznT0/ail+cYlU6tNcPA9AHzZu1Modw1eoDINSliQ==",
25289
+      "requires": {
25290
+        "nanoid": "^3.1.15"
25291
+      }
25292
+    },
25293
+    "@react-navigation/stack": {
25294
+      "version": "5.14.9",
25295
+      "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-5.14.9.tgz",
25296
+      "integrity": "sha512-DuvrT9P+Tz8ezZLQYxORZqOGqO+vEufaxlW1hSLw1knLD4jNxkz8TJDXtfKwaz//9gb43UhTNccNM02vm7iPqQ==",
25297
+      "requires": {
25298
+        "color": "^3.1.3",
25299
+        "react-native-iphone-x-helper": "^1.3.0"
25300
+      }
25301
+    },
24991 25302
     "@svgr/babel-plugin-add-jsx-attribute": {
24992 25303
       "version": "4.2.0",
24993 25304
       "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
@@ -25801,6 +26112,11 @@
25801 26112
       "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==",
25802 26113
       "dev": true
25803 26114
     },
26115
+    "@types/hammerjs": {
26116
+      "version": "2.0.40",
26117
+      "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.40.tgz",
26118
+      "integrity": "sha512-VbjwR1fhsn2h2KXAY4oy1fm7dCxaKy0D+deTb8Ilc3Eo3rc5+5eA4rfYmZaHgNJKxVyI0f6WIXzO2zLkVmQPHA=="
26119
+    },
25804 26120
     "@types/http-proxy": {
25805 26121
       "version": "1.17.7",
25806 26122
       "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz",
@@ -27871,6 +28187,21 @@
27871 28187
         "object-assign": "^4.1.1"
27872 28188
       }
27873 28189
     },
28190
+    "cross-fetch": {
28191
+      "version": "3.1.4",
28192
+      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz",
28193
+      "integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==",
28194
+      "requires": {
28195
+        "node-fetch": "2.6.1"
28196
+      },
28197
+      "dependencies": {
28198
+        "node-fetch": {
28199
+          "version": "2.6.1",
28200
+          "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
28201
+          "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
28202
+        }
28203
+      }
28204
+    },
27874 28205
     "cross-os": {
27875 28206
       "version": "1.4.0",
27876 28207
       "resolved": "https://registry.npmjs.org/cross-os/-/cross-os-1.4.0.tgz",
@@ -29761,6 +30092,11 @@
29761 30092
         }
29762 30093
       }
29763 30094
     },
30095
+    "filter-obj": {
30096
+      "version": "1.1.0",
30097
+      "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
30098
+      "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs="
30099
+    },
29764 30100
     "finalhandler": {
29765 30101
       "version": "1.1.2",
29766 30102
       "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@@ -30184,11 +30520,11 @@
30184 30520
           }
30185 30521
         },
30186 30522
         "mkdirp": {
30187
-          "version": "0.5.5",
30523
+          "version": "0.5.1",
30188 30524
           "bundled": true,
30189 30525
           "optional": true,
30190 30526
           "requires": {
30191
-            "minimist": "^1.2.5"
30527
+            "minimist": "0.0.8"
30192 30528
           }
30193 30529
         },
30194 30530
         "ms": {
@@ -30316,7 +30652,7 @@
30316 30652
           },
30317 30653
           "dependencies": {
30318 30654
             "minimist": {
30319
-              "version": "1.2.5",
30655
+              "version": "1.2.0",
30320 30656
               "bundled": true,
30321 30657
               "optional": true
30322 30658
             }
@@ -31156,9 +31492,9 @@
31156 31492
       "dev": true
31157 31493
     },
31158 31494
     "invariant": {
31159
-      "version": "2.2.3",
31160
-      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.3.tgz",
31161
-      "integrity": "sha512-7Z5PPegwDTyjbaeCnV0efcyS6vdKAU51kpEmS7QFib3P4822l8ICYyMn7qvJnc+WzLoDsuI9gPMKbJ8pCu8XtA==",
31495
+      "version": "2.2.4",
31496
+      "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
31497
+      "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
31162 31498
       "requires": {
31163 31499
         "loose-envify": "^1.0.0"
31164 31500
       }
@@ -31559,14 +31895,6 @@
31559 31895
           "version": "4.2.3",
31560 31896
           "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
31561 31897
           "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
31562
-        },
31563
-        "invariant": {
31564
-          "version": "2.2.4",
31565
-          "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
31566
-          "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
31567
-          "requires": {
31568
-            "loose-envify": "^1.0.0"
31569
-          }
31570 31898
         }
31571 31899
       }
31572 31900
     },
@@ -32390,14 +32718,6 @@
32390 32718
             "klaw": "^1.0.0"
32391 32719
           }
32392 32720
         },
32393
-        "invariant": {
32394
-          "version": "2.2.4",
32395
-          "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
32396
-          "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
32397
-          "requires": {
32398
-            "loose-envify": "^1.0.0"
32399
-          }
32400
-        },
32401 32721
         "jsonfile": {
32402 32722
           "version": "2.4.0",
32403 32723
           "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
@@ -32840,16 +33160,6 @@
32840 33160
         "ob1": "^0.56.4",
32841 33161
         "source-map": "^0.5.6",
32842 33162
         "vlq": "^1.0.0"
32843
-      },
32844
-      "dependencies": {
32845
-        "invariant": {
32846
-          "version": "2.2.4",
32847
-          "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
32848
-          "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
32849
-          "requires": {
32850
-            "loose-envify": "^1.0.0"
32851
-          }
32852
-        }
32853 33163
       }
32854 33164
     },
32855 33165
     "metro-symbolicate": {
@@ -32862,16 +33172,6 @@
32862 33172
         "source-map": "^0.5.6",
32863 33173
         "through2": "^2.0.1",
32864 33174
         "vlq": "^1.0.0"
32865
-      },
32866
-      "dependencies": {
32867
-        "invariant": {
32868
-          "version": "2.2.4",
32869
-          "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
32870
-          "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
32871
-          "requires": {
32872
-            "loose-envify": "^1.0.0"
32873
-          }
32874
-        }
32875 33175
       }
32876 33176
     },
32877 33177
     "micromatch": {
@@ -33046,6 +33346,11 @@
33046 33346
       "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
33047 33347
       "optional": true
33048 33348
     },
33349
+    "nanoid": {
33350
+      "version": "3.1.30",
33351
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz",
33352
+      "integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ=="
33353
+    },
33049 33354
     "nanomatch": {
33050 33355
       "version": "1.2.13",
33051 33356
       "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -34896,14 +35201,6 @@
34896 35201
             "ua-parser-js": "^0.7.18"
34897 35202
           }
34898 35203
         },
34899
-        "invariant": {
34900
-          "version": "2.2.4",
34901
-          "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
34902
-          "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
34903
-          "requires": {
34904
-            "loose-envify": "^1.0.0"
34905
-          }
34906
-        },
34907 35204
         "whatwg-fetch": {
34908 35205
           "version": "3.0.0",
34909 35206
           "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
@@ -34948,6 +35245,34 @@
34948 35245
       "resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-8.0.0.tgz",
34949 35246
       "integrity": "sha512-7/DOEhg8GtyW1hpVtWf8F6RvGLaFaOGmex+IkmiBWQC2uW4NFDcfXm+lMMZnduFavTyUTX7AF6lAM3y286cEfA=="
34950 35247
     },
35248
+    "react-native-gesture-handler": {
35249
+      "version": "1.10.3",
35250
+      "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
35251
+      "integrity": "sha512-cBGMi1IEsIVMgoox4RvMx7V2r6bNKw0uR1Mu1o7NbuHS6BRSVLq0dP34l2ecnPlC+jpWd3le6Yg1nrdCjby2Mw==",
35252
+      "requires": {
35253
+        "@egjs/hammerjs": "^2.0.17",
35254
+        "fbjs": "^3.0.0",
35255
+        "hoist-non-react-statics": "^3.3.0",
35256
+        "invariant": "^2.2.4",
35257
+        "prop-types": "^15.7.2"
35258
+      },
35259
+      "dependencies": {
35260
+        "fbjs": {
35261
+          "version": "3.0.0",
35262
+          "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz",
35263
+          "integrity": "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==",
35264
+          "requires": {
35265
+            "cross-fetch": "^3.0.4",
35266
+            "fbjs-css-vars": "^1.0.0",
35267
+            "loose-envify": "^1.0.0",
35268
+            "object-assign": "^4.1.0",
35269
+            "promise": "^7.1.1",
35270
+            "setimmediate": "^1.0.5",
35271
+            "ua-parser-js": "^0.7.18"
35272
+          }
35273
+        }
35274
+      }
35275
+    },
34951 35276
     "react-native-immersive": {
34952 35277
       "version": "2.0.0",
34953 35278
       "resolved": "https://registry.npmjs.org/react-native-immersive/-/react-native-immersive-2.0.0.tgz",
@@ -34978,6 +35303,41 @@
34978 35303
       "resolved": "https://registry.npmjs.org/react-native-performance/-/react-native-performance-2.0.0.tgz",
34979 35304
       "integrity": "sha512-jKM9Qg0SkL9D9ad377nxb1VV+OXJSyYyIrBHKmM6CABNxfrLVA5xkQMEibjmZQde7b0ndJOZoQAiObgJjjc4VQ=="
34980 35305
     },
35306
+    "react-native-reanimated": {
35307
+      "version": "1.13.3",
35308
+      "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-1.13.3.tgz",
35309
+      "integrity": "sha512-i714H24dv6ncpFO7/SZ0PfAMbvjgVbF8Ow2NPtowoZAz8osS54DmTMrkgJ9Za+uEku/s0AEaxqiXG2Xgntvv2g==",
35310
+      "requires": {
35311
+        "fbjs": "^1.0.0"
35312
+      },
35313
+      "dependencies": {
35314
+        "fbjs": {
35315
+          "version": "1.0.0",
35316
+          "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-1.0.0.tgz",
35317
+          "integrity": "sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA==",
35318
+          "requires": {
35319
+            "core-js": "^2.4.1",
35320
+            "fbjs-css-vars": "^1.0.0",
35321
+            "isomorphic-fetch": "^2.1.1",
35322
+            "loose-envify": "^1.0.0",
35323
+            "object-assign": "^4.1.0",
35324
+            "promise": "^7.1.1",
35325
+            "setimmediate": "^1.0.5",
35326
+            "ua-parser-js": "^0.7.18"
35327
+          }
35328
+        }
35329
+      }
35330
+    },
35331
+    "react-native-safe-area-context": {
35332
+      "version": "3.3.2",
35333
+      "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz",
35334
+      "integrity": "sha512-yOwiiPJ1rk+/nfK13eafbpW6sKW0jOnsRem2C1LPJjM3tfTof6hlvV5eWHATye3XOpu2cJ7N+HdkUvUDGwFD2Q=="
35335
+    },
35336
+    "react-native-screens": {
35337
+      "version": "2.18.1",
35338
+      "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-2.18.1.tgz",
35339
+      "integrity": "sha512-r5WZLpmx2hHjC1RgMdPq5YpSU9tEhBpUaZ5M1SUtNIONyiLqQVxabhRCINdebIk4depJiIl7yw2Q85zJyeX6fw=="
35340
+    },
34981 35341
     "react-native-sound": {
34982 35342
       "version": "git+ssh://git@github.com/jitsi/react-native-sound.git#3fe5480fce935e888d5089d94a191c7c7e3aa190",
34983 35343
       "integrity": "sha512-364A1CvMgh5MnzI4iJgg+AqpePO63Jmf1ESvkTlW+VK3S513fM3092+5mupmGO8KIP77PuYpuNjTYpjZukbgkw==",
@@ -35076,6 +35436,11 @@
35076 35436
         }
35077 35437
       }
35078 35438
     },
35439
+    "react-native-tab-view": {
35440
+      "version": "2.16.0",
35441
+      "resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-2.16.0.tgz",
35442
+      "integrity": "sha512-ac2DmT7+l13wzIFqtbfXn4wwfgtPoKzWjjZyrK1t+T8sdemuUvD4zIt+UImg03fu3s3VD8Wh/fBrIdcqQyZJWg=="
35443
+    },
35079 35444
     "react-native-url-polyfill": {
35080 35445
       "version": "1.2.0",
35081 35446
       "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-1.2.0.tgz",
@@ -35131,14 +35496,6 @@
35131 35496
           "version": "2.0.0",
35132 35497
           "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
35133 35498
           "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="
35134
-        },
35135
-        "invariant": {
35136
-          "version": "2.2.4",
35137
-          "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
35138
-          "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
35139
-          "requires": {
35140
-            "loose-envify": "^1.0.0"
35141
-          }
35142 35499
         }
35143 35500
       }
35144 35501
     },
@@ -35177,14 +35534,6 @@
35177 35534
         "react-is": "^16.8.6"
35178 35535
       },
35179 35536
       "dependencies": {
35180
-        "invariant": {
35181
-          "version": "2.2.4",
35182
-          "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
35183
-          "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
35184
-          "requires": {
35185
-            "loose-envify": "^1.0.0"
35186
-          }
35187
-        },
35188 35537
         "loose-envify": {
35189 35538
           "version": "1.4.0",
35190 35539
           "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -36283,6 +36632,11 @@
36283 36632
         }
36284 36633
       }
36285 36634
     },
36635
+    "split-on-first": {
36636
+      "version": "1.1.0",
36637
+      "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
36638
+      "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
36639
+    },
36286 36640
     "split-string": {
36287 36641
       "version": "3.1.0",
36288 36642
       "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",

+ 9
- 0
package.json View File

@@ -42,6 +42,10 @@
42 42
     "@react-native-community/google-signin": "3.0.1",
43 43
     "@react-native-community/netinfo": "4.1.5",
44 44
     "@react-native-community/slider": "3.0.3",
45
+    "@react-native-masked-view/masked-view": "0.2.6",
46
+    "@react-navigation/material-top-tabs": "5.3.19",
47
+    "@react-navigation/native": "5.9.8",
48
+    "@react-navigation/stack": "5.14.9",
45 49
     "@svgr/webpack": "4.3.2",
46 50
     "amplitude-js": "8.2.1",
47 51
     "base64-js": "1.3.1",
@@ -81,14 +85,19 @@
81 85
     "react-native-collapsible": "1.5.1",
82 86
     "react-native-default-preference": "1.4.2",
83 87
     "react-native-device-info": "8.0.0",
88
+    "react-native-gesture-handler": "1.10.3",
84 89
     "react-native-immersive": "2.0.0",
85 90
     "react-native-keep-awake": "4.0.0",
86 91
     "react-native-paper": "4.8.1",
87 92
     "react-native-performance": "2.0.0",
93
+    "react-native-reanimated": "1.13.3",
94
+    "react-native-safe-area-context": "3.3.2",
95
+    "react-native-screens": "2.18.1",
88 96
     "react-native-sound": "github:jitsi/react-native-sound#3fe5480fce935e888d5089d94a191c7c7e3aa190",
89 97
     "react-native-splash-screen": "3.2.0",
90 98
     "react-native-svg": "12.1.0",
91 99
     "react-native-svg-transformer": "0.14.3",
100
+    "react-native-tab-view": "2.16.0",
92 101
     "react-native-url-polyfill": "1.2.0",
93 102
     "react-native-video": "5.1.1",
94 103
     "react-native-watch-connectivity": "0.4.3",

+ 52
- 0
react/features/app/getRouteToRender.native.js View File

@@ -0,0 +1,52 @@
1
+
2
+import { isRoomValid } from '../base/conference';
3
+import { toState } from '../base/redux';
4
+import { ConferenceNavigationContainer } from '../conference';
5
+import { isWelcomePageAppEnabled } from '../welcome';
6
+import { BlankPage, WelcomePage } from '../welcome/components';
7
+
8
+/**
9
+ * Determines which route is to be rendered in order to depict a specific Redux
10
+ * store.
11
+ *
12
+ * @param {(Function|Object)} stateful - THe redux store, state, or
13
+ * {@code getState} function.
14
+ * @returns {Promise<Object>}
15
+ */
16
+export function _getRouteToRender(stateful) {
17
+    const state = toState(stateful);
18
+
19
+    return _getMobileRoute(state);
20
+}
21
+
22
+/**
23
+ * Returns the {@code Route} to display on the React Native app.
24
+ *
25
+ * @param {Object} state - The redux state.
26
+ * @returns {Promise}
27
+ */
28
+function _getMobileRoute(state) {
29
+    const route = _getEmptyRoute();
30
+
31
+    if (isRoomValid(state['features/base/conference'].room)) {
32
+        route.component = ConferenceNavigationContainer;
33
+    } else if (isWelcomePageAppEnabled(state)) {
34
+        route.component = WelcomePage;
35
+    } else {
36
+        route.component = BlankPage;
37
+    }
38
+
39
+    return Promise.resolve(route);
40
+}
41
+
42
+/**
43
+ * Returns the default {@code Route}.
44
+ *
45
+ * @returns {Object}
46
+ */
47
+function _getEmptyRoute() {
48
+    return {
49
+        component: BlankPage,
50
+        href: undefined
51
+    };
52
+}

react/features/app/getRouteToRender.js → react/features/app/getRouteToRender.web.js View File

@@ -1,7 +1,5 @@
1
-// @flow
2 1
 
3 2
 import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random';
4
-import type { Component } from 'react';
5 3
 
6 4
 import { isRoomValid } from '../base/conference';
7 5
 import { isSupportedBrowser } from '../base/environment';
@@ -9,25 +7,9 @@ import { toState } from '../base/redux';
9 7
 import { Conference } from '../conference';
10 8
 import { getDeepLinkingPage } from '../deep-linking';
11 9
 import { UnsupportedDesktopBrowser } from '../unsupported-browser';
12
-import {
13
-    BlankPage,
14
-    WelcomePage,
15
-    isWelcomePageAppEnabled,
16
-    isWelcomePageUserEnabled
17
-} from '../welcome';
10
+import { isWelcomePageUserEnabled } from '../welcome';
11
+import { BlankPage, WelcomePage } from '../welcome/components';
18 12
 
19
-/**
20
- * Object describing application route.
21
- *
22
- * @typedef {Object} Route
23
- * @property {Component} component - React Component constructor.
24
- * @property {string|undefined} href - New location, in case navigation involves
25
- * a location change.
26
- */
27
-export type Route = {
28
-    component: Class<Component<*>>,
29
-    href: ?string
30
-};
31 13
 
32 14
 /**
33 15
  * Determines which route is to be rendered in order to depict a specific Redux
@@ -35,46 +17,22 @@ export type Route = {
35 17
  *
36 18
  * @param {(Function|Object)} stateful - THe redux store, state, or
37 19
  * {@code getState} function.
38
- * @returns {Promise<Route>}
20
+ * @returns {Promise<Object>}
39 21
  */
40
-export function _getRouteToRender(stateful: Function | Object): Promise<Route> {
22
+export function _getRouteToRender(stateful) {
41 23
     const state = toState(stateful);
42 24
 
43
-    if (navigator.product === 'ReactNative') {
44
-        return _getMobileRoute(state);
45
-    }
46
-
47 25
     return _getWebConferenceRoute(state) || _getWebWelcomePageRoute(state);
48 26
 }
49 27
 
50
-/**
51
- * Returns the {@code Route} to display on the React Native app.
52
- *
53
- * @param {Object} state - The redux state.
54
- * @returns {Promise<Route>}
55
- */
56
-function _getMobileRoute(state): Promise<Route> {
57
-    const route = _getEmptyRoute();
58
-
59
-    if (isRoomValid(state['features/base/conference'].room)) {
60
-        route.component = Conference;
61
-    } else if (isWelcomePageAppEnabled(state)) {
62
-        route.component = WelcomePage;
63
-    } else {
64
-        route.component = BlankPage;
65
-    }
66
-
67
-    return Promise.resolve(route);
68
-}
69
-
70 28
 /**
71 29
  * Returns the {@code Route} to display when trying to access a conference if
72 30
  * a valid conference is being joined.
73 31
  *
74 32
  * @param {Object} state - The redux state.
75
- * @returns {Promise<Route>|undefined}
33
+ * @returns {Promise|undefined}
76 34
  */
77
-function _getWebConferenceRoute(state): ?Promise<Route> {
35
+function _getWebConferenceRoute(state) {
78 36
     if (!isRoomValid(state['features/base/conference'].room)) {
79 37
         return;
80 38
     }
@@ -111,9 +69,9 @@ function _getWebConferenceRoute(state): ?Promise<Route> {
111 69
  * Returns the {@code Route} to display when trying to access the welcome page.
112 70
  *
113 71
  * @param {Object} state - The redux state.
114
- * @returns {Promise<Route>}
72
+ * @returns {Promise<Object>}
115 73
  */
116
-function _getWebWelcomePageRoute(state): Promise<Route> {
74
+function _getWebWelcomePageRoute(state) {
117 75
     const route = _getEmptyRoute();
118 76
 
119 77
     if (isWelcomePageUserEnabled(state)) {
@@ -137,9 +95,9 @@ function _getWebWelcomePageRoute(state): Promise<Route> {
137 95
 /**
138 96
  * Returns the default {@code Route}.
139 97
  *
140
- * @returns {Route}
98
+ * @returns {Object}
141 99
  */
142
-function _getEmptyRoute(): Route {
100
+function _getEmptyRoute() {
143 101
     return {
144 102
         component: BlankPage,
145 103
         href: undefined

+ 83
- 0
react/features/base/modal/components/JitsiKeyboardAvoidingView.js View File

@@ -0,0 +1,83 @@
1
+// @flow
2
+
3
+import { useHeaderHeight } from '@react-navigation/stack';
4
+import React, { useEffect, useState } from 'react';
5
+import {
6
+    Keyboard,
7
+    KeyboardAvoidingView,
8
+    Platform,
9
+    TouchableWithoutFeedback
10
+} from 'react-native';
11
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
12
+
13
+import { StyleType } from '../../styles';
14
+
15
+type Props = {
16
+
17
+    /**
18
+     * The children component(s) of the Modal, to be rendered.
19
+     */
20
+    children: React$Node,
21
+
22
+    /**
23
+     * Additional style to be appended to the KeyboardAvoidingView content container.
24
+     */
25
+    contentContainerStyle?: StyleType,
26
+
27
+    /**
28
+     * Is the screen rendering a tab navigator?
29
+     */
30
+    hasTabNavigator: boolean,
31
+
32
+    /**
33
+     * Additional style to be appended to the KeyboardAvoidingView.
34
+     */
35
+    style?: StyleType
36
+}
37
+
38
+const JitsiKeyboardAvoidingView = (
39
+        {
40
+            children,
41
+            contentContainerStyle,
42
+            hasTabNavigator,
43
+            style
44
+        }: Props) => {
45
+    const headerHeight = useHeaderHeight();
46
+    const insets = useSafeAreaInsets();
47
+    const [ bottomPadding, setBottomPadding ] = useState(insets.bottom);
48
+
49
+    useEffect(() => {
50
+        // This useEffect is needed because insets are undefined at first for some reason
51
+        // https://github.com/th3rdwave/react-native-safe-area-context/issues/54
52
+        setBottomPadding(insets.bottom);
53
+
54
+    }, [ insets.bottom ]);
55
+
56
+    const tabNavigatorPadding
57
+        = hasTabNavigator ? headerHeight : 0;
58
+    const noNotchDevicePadding = bottomPadding || 10;
59
+    const iosVerticalOffset = headerHeight + noNotchDevicePadding + tabNavigatorPadding;
60
+    const androidVerticalOffset = headerHeight;
61
+
62
+    return (
63
+        <TouchableWithoutFeedback
64
+            /* eslint-disable-next-line react/jsx-handler-names */
65
+            onPress = { Keyboard.dismiss }>
66
+            <KeyboardAvoidingView
67
+                behavior = { Platform.OS === 'ios' ? 'padding' : 'height' }
68
+                contentContainerStyle = { contentContainerStyle }
69
+                enabled = { true }
70
+                keyboardVerticalOffset = {
71
+                    Platform.OS === 'ios'
72
+                        ? iosVerticalOffset
73
+                        : androidVerticalOffset
74
+                }
75
+                style = { style }>
76
+                { children }
77
+            </KeyboardAvoidingView>
78
+        </TouchableWithoutFeedback>
79
+    );
80
+};
81
+
82
+
83
+export default JitsiKeyboardAvoidingView;

+ 69
- 0
react/features/base/modal/components/JitsiScreen.js View File

@@ -0,0 +1,69 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+import { View } from 'react-native';
5
+import { SafeAreaView } from 'react-native-safe-area-context';
6
+
7
+import { StyleType } from '../../styles';
8
+
9
+import JitsiKeyboardAvoidingView from './JitsiKeyboardAvoidingView';
10
+import styles from './styles';
11
+
12
+
13
+type Props = {
14
+
15
+    /**
16
+     * Additional style to be appended to the KeyboardAvoidingView content container.
17
+     */
18
+    contentContainerStyle?: StyleType,
19
+
20
+    /**
21
+     * The children component(s) of the Modal, to be rendered.
22
+     */
23
+    children: React$Node,
24
+
25
+    /**
26
+     * Optional function that renders a footer component, if needed.
27
+     */
28
+    footerComponent?: Function,
29
+
30
+    /**
31
+     * Is the screen rendering a tab navigator?
32
+     */
33
+    hasTabNavigator: boolean,
34
+
35
+    /**
36
+     * Additional style to be appended to the KeyboardAvoidingView containing the content of the modal.
37
+     */
38
+    style?: StyleType
39
+}
40
+
41
+const JitsiScreen = ({
42
+    contentContainerStyle,
43
+    children,
44
+    footerComponent,
45
+    hasTabNavigator,
46
+    style
47
+}: Props) => (
48
+    <View
49
+        style = { styles.jitsiScreenContainer }>
50
+        <JitsiKeyboardAvoidingView
51
+            contentContainerStyle = { contentContainerStyle }
52
+            hasTabNavigator = { hasTabNavigator }
53
+            style = { style }>
54
+            <SafeAreaView
55
+                edges = { [
56
+                    'bottom',
57
+                    'left',
58
+                    'right'
59
+                ] }
60
+                style = { styles.safeArea }>
61
+                { children }
62
+            </SafeAreaView>
63
+            { footerComponent && footerComponent() }
64
+        </JitsiKeyboardAvoidingView>
65
+    </View>
66
+);
67
+
68
+
69
+export default JitsiScreen;

+ 60
- 0
react/features/base/modal/components/functions.native.js View File

@@ -0,0 +1,60 @@
1
+// @flow
2
+
3
+import { useEffect, useState } from 'react';
4
+import { Keyboard } from 'react-native';
5
+
6
+import { toState } from '../../redux';
7
+
8
+export const useKeyboardHeight = () => {
9
+    const [ keyboardHeight, setKeyboardHeight ] = useState(0);
10
+
11
+    const onKeyboardDidShow = e => {
12
+        setKeyboardHeight(e.endCoordinates.height);
13
+    };
14
+
15
+    const onKeyboardDidHide = () => {
16
+        setKeyboardHeight(0);
17
+    };
18
+
19
+    useEffect(() => {
20
+        const keyboardShow = Keyboard.addListener('keyboardDidShow', onKeyboardDidShow);
21
+        const keyboardHide = Keyboard.addListener('keyboardDidHide', onKeyboardDidHide);
22
+
23
+        return () => {
24
+            keyboardShow.remove();
25
+            keyboardHide.remove();
26
+        };
27
+    }, []);
28
+
29
+    return keyboardHeight;
30
+};
31
+
32
+/**
33
+ *
34
+ * Returns the client width.
35
+ *
36
+ * @param {(Function|Object)} stateful - The (whole) redux state, or redux's
37
+ * {@code getState} function to be used to retrieve the state
38
+ * features/base/config.
39
+ * @returns {number}.
40
+ */
41
+export function getClientWidth(stateful: Object) {
42
+    const state = toState(stateful['features/base/responsive-ui']);
43
+
44
+    return state.clientWidth;
45
+}
46
+
47
+/**
48
+ *
49
+ * Returns the client height.
50
+ *
51
+ * @param {(Function|Object)} stateful - The (whole) redux state, or redux's
52
+ * {@code getState} function to be used to retrieve the state
53
+ * features/base/config.
54
+ * @returns {number}.
55
+ */
56
+export function getClientHeight(stateful: Object) {
57
+    const state = toState(stateful['features/base/responsive-ui']);
58
+
59
+    return state.clientHeight;
60
+}

+ 5
- 0
react/features/base/modal/components/styles.js View File

@@ -3,6 +3,11 @@
3 3
 import { ColorSchemeRegistry, schemeColor } from '../../color-scheme';
4 4
 
5 5
 export default {
6
+
7
+    jitsiScreenContainer: {
8
+        flex: 1
9
+    },
10
+
6 11
     safeArea: {
7 12
         flex: 1
8 13
     }

+ 6
- 1
react/features/base/ui/Tokens.js View File

@@ -17,6 +17,7 @@ export const colors = {
17 17
     primary07: '#669AEC',
18 18
     primary08: '#99BBF3',
19 19
     primary09: '#CCDDF9',
20
+    primary10: '#17A0DB',
20 21
 
21 22
     surface00: '#111111',
22 23
     surface01: '#040404',
@@ -54,6 +55,9 @@ export const colorMap = {
54 55
     // Primary buttons
55 56
     action01: 'primary05',
56 57
 
58
+    // Screen header
59
+    screen01Header: 'primary10',
60
+
57 61
     // Hover state for primary buttons
58 62
     action01Hover: 'primary06',
59 63
 
@@ -226,7 +230,8 @@ export const shape = {
226 230
     boxShadow: 'inset 0px -1px 0px rgba(255, 255, 255, 0.15)'
227 231
 };
228 232
 
229
-export const spacing = [ 0, 4, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80 ];
233
+export const spacing
234
+    = [ 0, 4, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128 ];
230 235
 
231 236
 export const typography = {
232 237
     labelRegular: {

+ 19
- 4
react/features/chat/components/PrivateMessageButton.js View File

@@ -6,7 +6,8 @@ import { IconMessage, IconReply } from '../../base/icons';
6 6
 import { getParticipantById } from '../../base/participants';
7 7
 import { connect } from '../../base/redux';
8 8
 import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
9
-import { openChat } from '../actions';
9
+import { navigate } from '../../conference/components/native/ConferenceNavigationContainerRef';
10
+import { screen } from '../../conference/components/native/routes';
10 11
 
11 12
 export type Props = AbstractButtonProps & {
12 13
 
@@ -30,6 +31,11 @@ export type Props = AbstractButtonProps & {
30 31
      */
31 32
     dispatch: Function,
32 33
 
34
+    /**
35
+     * True if the polls feature is disabled.
36
+     */
37
+    _isPollsDisabled: boolean,
38
+
33 39
     /**
34 40
      * The participant object retrieved from Redux.
35 41
      */
@@ -52,9 +58,16 @@ class PrivateMessageButton extends AbstractButton<Props, any> {
52 58
      * @returns {void}
53 59
      */
54 60
     _handleClick() {
55
-        const { dispatch, _participant } = this.props;
56
-
57
-        dispatch(openChat(_participant));
61
+        this.props._isPollsDisabled
62
+            ? navigate(screen.conference.chat, {
63
+                privateMessageRecipient: this.props._participant
64
+            })
65
+            : navigate(screen.conference.chatandpolls.main, {
66
+                screen: screen.conference.chatandpolls.tab.chat,
67
+                params: {
68
+                    privateMessageRecipient: this.props._participant
69
+                }
70
+            });
58 71
     }
59 72
 
60 73
     /**
@@ -79,9 +92,11 @@ class PrivateMessageButton extends AbstractButton<Props, any> {
79 92
  */
80 93
 export function _mapStateToProps(state: Object, ownProps: Props): $Shape<Props> {
81 94
     const enabled = getFeatureFlag(state, CHAT_ENABLED, true);
95
+    const { disablePolls } = state['features/base/config'];
82 96
     const { visible = enabled } = ownProps;
83 97
 
84 98
     return {
99
+        _isPollsDisabled: disablePolls,
85 100
         _participant: getParticipantById(state, ownProps.participantID),
86 101
         visible
87 102
     };

+ 62
- 82
react/features/chat/components/native/Chat.js View File

@@ -1,18 +1,16 @@
1 1
 // @flow
2 2
 
3
-import React from 'react';
4
-import { View } from 'react-native';
5
-import { Button } from 'react-native-paper';
3
+import { useIsFocused } from '@react-navigation/native';
4
+import React, { useEffect } from 'react';
6 5
 
7 6
 import { translate } from '../../../base/i18n';
8
-import { JitsiModal } from '../../../base/modal';
7
+import JitsiScreen from '../../../base/modal/components/JitsiScreen';
9 8
 import { connect } from '../../../base/redux';
10
-import { PollsPane } from '../../../polls/components';
11
-import { closeChat } from '../../actions.any';
12
-import { BUTTON_MODES, CHAT_VIEW_MODAL_ID } from '../../constants';
9
+import { screen } from '../../../conference/components/native/routes';
10
+import { closeChat, openChat } from '../../actions.native';
13 11
 import AbstractChat, {
14 12
     _mapStateToProps,
15
-    type Props
13
+    type Props as AbstractProps
16 14
 } from '../AbstractChat';
17 15
 
18 16
 import ChatInputBar from './ChatInputBar';
@@ -20,21 +18,30 @@ import MessageContainer from './MessageContainer';
20 18
 import MessageRecipient from './MessageRecipient';
21 19
 import styles from './styles';
22 20
 
21
+
22
+type Props = AbstractProps & {
23
+
24
+    /**
25
+     * Is this screen focused or not(React Navigation)
26
+     */
27
+    isChatScreenFocused: boolean,
28
+
29
+    /**
30
+     * Default prop for navigating between screen components(React Navigation)
31
+     */
32
+    navigation: Object,
33
+
34
+    /**
35
+     * Default prop for navigating between screen components(React Navigation)
36
+     */
37
+    route: Object
38
+};
39
+
23 40
 /**
24 41
  * Implements a React native component that renders the chat window (modal) of
25 42
  * the mobile client.
26 43
  */
27 44
 class Chat extends AbstractChat<Props> {
28
-    /**
29
-     * Creates a new instance.
30
-     *
31
-     * @inheritdoc
32
-     */
33
-    constructor(props: Props) {
34
-        super(props);
35
-
36
-        this._onClose = this._onClose.bind(this);
37
-    }
38 45
 
39 46
     /**
40 47
      * Implements React's {@link Component#render()}.
@@ -42,77 +49,50 @@ class Chat extends AbstractChat<Props> {
42 49
      * @inheritdoc
43 50
      */
44 51
     render() {
52
+        const { _messages, route } = this.props;
53
+        const privateMessageRecipient = route.params?.privateMessageRecipient;
54
+
45 55
         return (
46
-            <JitsiModal
47
-                headerProps = {{
48
-                    headerLabelKey: this.props._isPollsEnabled ? 'chat.titleWithPolls' : 'chat.title'
49
-                }}
50
-                modalId = { CHAT_VIEW_MODAL_ID }
51
-                onClose = { this._onClose }>
52
-                {this.props._isPollsEnabled && <View style = { styles.tabContainer }>
53
-                    <Button
54
-                        color = '#17a0db'
55
-                        mode = {
56
-                            this.props._isPollsTabFocused
57
-                                ? BUTTON_MODES.CONTAINED
58
-                                : BUTTON_MODES.TEXT
59
-                        }
60
-                        onPress = { this._onToggleChatTab }
61
-                        style = { styles.tabLeftButton }
62
-                        uppercase = { false }>
63
-                        {`${this.props.t('chat.tabs.chat')}${this.props._isPollsTabFocused
64
-                                && this.props._nbUnreadMessages > 0
65
-                            ? `(${this.props._nbUnreadMessages})`
66
-                            : ''
67
-                        }`}
68
-                    </Button>
69
-                    <Button
70
-                        color = '#17a0db'
71
-                        mode = {
72
-                            this.props._isPollsTabFocused
73
-                                ? BUTTON_MODES.TEXT
74
-                                : BUTTON_MODES.CONTAINED
75
-                        }
76
-                        onPress = { this._onTogglePollsTab }
77
-                        style = { styles.tabRightButton }
78
-                        uppercase = { false }>
79
-                        {`${this.props.t('chat.tabs.polls')}${!this.props._isPollsTabFocused
80
-                                && this.props._nbUnreadPolls > 0
81
-                            ? `(${this.props._nbUnreadPolls})`
82
-                            : ''
83
-                        }`}
84
-                    </Button>
85
-                </View>}
86
-                {this.props._isPollsTabFocused
87
-                    ? <PollsPane />
88
-                    : (
89
-                    <>
90
-                        <MessageContainer messages = { this.props._messages } />
91
-                        <MessageRecipient />
92
-                        <ChatInputBar onSend = { this._onSendMessage } />
93
-                    </>
94
-                    )}
95
-            </JitsiModal>
56
+            <JitsiScreen
57
+                hasTabNavigator = { true }
58
+                style = { styles.chatContainer }>
59
+                <MessageContainer messages = { _messages } />
60
+                <MessageRecipient privateMessageRecipient = { privateMessageRecipient } />
61
+                <ChatInputBar onSend = { this._onSendMessage } />
62
+            </JitsiScreen>
96 63
         );
97 64
     }
98 65
 
99 66
     _onSendMessage: (string) => void;
67
+}
100 68
 
101
-    _onClose: () => boolean
69
+export default translate(connect(_mapStateToProps)(props => {
70
+    const {
71
+        _nbUnreadMessages,
72
+        dispatch,
73
+        navigation,
74
+        route
75
+    } = props;
76
+    const isChatScreenFocused = useIsFocused();
77
+    const privateMessageRecipient = route.params?.privateMessageRecipient;
102 78
 
103
-    _onTogglePollsTab: () => void;
104
-    _onToggleChatTab: () => void;
79
+    const nrUnreadMessages
80
+        = !isChatScreenFocused && _nbUnreadMessages > 0
81
+            ? `(${_nbUnreadMessages})` : '';
105 82
 
106
-    /**
107
-     * Closes the modal.
108
-     *
109
-     * @returns {boolean}
110
-     */
111
-    _onClose() {
112
-        this.props.dispatch(closeChat());
83
+    useEffect(() => {
84
+        dispatch(openChat(privateMessageRecipient));
113 85
 
114
-        return true;
115
-    }
116
-}
86
+        navigation.setOptions({
87
+            tabBarLabel: `${screen.conference.chatandpolls.tab.chat} ${nrUnreadMessages}`
88
+        });
89
+
90
+        return () => dispatch(closeChat());
91
+    }, [ nrUnreadMessages ]);
117 92
 
118
-export default translate(connect(_mapStateToProps)(Chat));
93
+    return (
94
+        <Chat
95
+            { ...props }
96
+            isChatScreenFocused = { isChatScreenFocused } />
97
+    );
98
+}));

+ 43
- 0
react/features/chat/components/native/ChatAndPolls.js View File

@@ -0,0 +1,43 @@
1
+// @flow
2
+
3
+import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
4
+import React from 'react';
5
+import { useSelector } from 'react-redux';
6
+
7
+import {
8
+    getClientHeight,
9
+    getClientWidth
10
+} from '../../../base/modal/components/functions.native';
11
+import { Chat } from '../../../chat';
12
+import { chatTabBarOptions } from '../../../conference/components/native/ConferenceNavigatorScreenOptions';
13
+import { screen } from '../../../conference/components/native/routes';
14
+import { PollsPane } from '../../../polls/components';
15
+
16
+const ChatTab = createMaterialTopTabNavigator();
17
+
18
+
19
+const ChatAndPolls = () => {
20
+    const clientHeight = useSelector(getClientHeight);
21
+    const clientWidth = useSelector(getClientWidth);
22
+
23
+    return (
24
+        <ChatTab.Navigator
25
+            backBehavior = 'none'
26
+            initialLayout = {{
27
+                height: clientHeight,
28
+                width: clientWidth
29
+            }}
30
+            tabBarOptions = {{
31
+                ...chatTabBarOptions
32
+            }}>
33
+            <ChatTab.Screen
34
+                component = { Chat }
35
+                name = { screen.conference.chatandpolls.tab.chat } />
36
+            <ChatTab.Screen
37
+                component = { PollsPane }
38
+                name = { screen.conference.chatandpolls.tab.polls } />
39
+        </ChatTab.Navigator>
40
+    );
41
+};
42
+
43
+export default ChatAndPolls;

+ 14
- 5
react/features/chat/components/native/ChatButton.js View File

@@ -7,17 +7,22 @@ import {
7 7
     AbstractButton,
8 8
     type AbstractButtonProps
9 9
 } from '../../../base/toolbox/components';
10
-import { openChat } from '../../actions.native';
10
+import { navigate } from '../../../conference/components/native/ConferenceNavigationContainerRef';
11
+import { screen } from '../../../conference/components/native/routes';
11 12
 import { getUnreadCount } from '../../functions';
12 13
 
14
+
13 15
 type Props = AbstractButtonProps & {
14 16
 
15 17
     /**
16
-     * The unread message count.
18
+     * True if the polls feature is disabled.
17 19
      */
18
-    _unreadMessageCount: number,
20
+    _isPollsDisabled: boolean,
19 21
 
20
-    dispatch: Function
22
+    /**
23
+     * The unread message count.
24
+     */
25
+    _unreadMessageCount: number
21 26
 };
22 27
 
23 28
 /**
@@ -36,7 +41,9 @@ class ChatButton extends AbstractButton<Props, *> {
36 41
      * @returns {void}
37 42
      */
38 43
     _handleClick() {
39
-        this.props.dispatch(openChat());
44
+        this.props._isPollsDisabled
45
+            ? navigate(screen.conference.chat)
46
+            : navigate(screen.conference.chatandpolls.main);
40 47
     }
41 48
 
42 49
     /**
@@ -59,9 +66,11 @@ class ChatButton extends AbstractButton<Props, *> {
59 66
  */
60 67
 function _mapStateToProps(state, ownProps) {
61 68
     const enabled = getFeatureFlag(state, CHAT_ENABLED, true);
69
+    const { disablePolls } = state['features/base/config'];
62 70
     const { visible = enabled } = ownProps;
63 71
 
64 72
     return {
73
+        _isPollsDisabled: disablePolls,
65 74
         _unreadMessageCount: getUnreadCount(state),
66 75
         visible
67 76
     };

+ 0
- 14
react/features/chat/components/native/ChatInputBar.js View File

@@ -58,7 +58,6 @@ class ChatInputBar extends Component<Props, State> {
58 58
         };
59 59
 
60 60
         this._onChangeText = this._onChangeText.bind(this);
61
-        this._onFieldReferenceAvailable = this._onFieldReferenceAvailable.bind(this);
62 61
         this._onFocused = this._onFocused.bind(this);
63 62
         this._onSubmit = this._onSubmit.bind(this);
64 63
     }
@@ -83,7 +82,6 @@ class ChatInputBar extends Component<Props, State> {
83 82
                     onFocus = { this._onFocused(true) }
84 83
                     onSubmitEditing = { this._onSubmit }
85 84
                     placeholder = { this.props.t('chat.fieldPlaceHolder') }
86
-                    ref = { this._onFieldReferenceAvailable }
87 85
                     returnKeyType = 'send'
88 86
                     style = { styles.inputField }
89 87
                     value = { this.state.message } />
@@ -113,18 +111,6 @@ class ChatInputBar extends Component<Props, State> {
113 111
         });
114 112
     }
115 113
 
116
-    _onFieldReferenceAvailable: Object => void;
117
-
118
-    /**
119
-     * Callback to be invoked when the field reference is available.
120
-     *
121
-     * @param {Object} field - The reference to the field.
122
-     * @returns {void}
123
-     */
124
-    _onFieldReferenceAvailable(field) {
125
-        field && field.focus();
126
-    }
127
-
128 114
     _onFocused: boolean => Function;
129 115
 
130 116
     /**

+ 51
- 11
react/features/chat/components/native/MessageRecipient.js View File

@@ -8,9 +8,11 @@ import { translate } from '../../../base/i18n';
8 8
 import { Icon, IconCancelSelection } from '../../../base/icons';
9 9
 import { connect } from '../../../base/redux';
10 10
 import { type StyleType } from '../../../base/styles';
11
+import {
12
+    setParams
13
+} from '../../../conference/components/native/ConferenceNavigationContainerRef';
14
+import { setPrivateMessageRecipient } from '../../actions.any';
11 15
 import AbstractMessageRecipient, {
12
-    _mapDispatchToProps,
13
-    _mapStateToProps as _abstractMapStateToProps,
14 16
     type Props as AbstractProps
15 17
 } from '../AbstractMessageRecipient';
16 18
 
@@ -19,35 +21,74 @@ type Props = AbstractProps & {
19 21
     /**
20 22
      * The color-schemed stylesheet of the feature.
21 23
      */
22
-    _styles: StyleType
24
+    _styles: StyleType,
25
+
26
+    /**
27
+     * The Redux dispatch function.
28
+     */
29
+    dispatch: Function,
30
+
31
+    /**
32
+     * The participant object set for private messaging.
33
+     */
34
+    privateMessageRecipient: Object,
23 35
 };
24 36
 
25 37
 /**
26 38
  * Class to implement the displaying of the recipient of the next message.
27 39
  */
28 40
 class MessageRecipient extends AbstractMessageRecipient<Props> {
41
+
42
+    /**
43
+     * Constructor of the component.
44
+     *
45
+     * @param {Props} props - The props of the component.
46
+     */
47
+    constructor(props: Props) {
48
+        super(props);
49
+
50
+        this._onResetPrivateMessageRecipient = this._onResetPrivateMessageRecipient.bind(this);
51
+    }
52
+
53
+    _onResetPrivateMessageRecipient: () => void;
54
+
55
+    /**
56
+     * Resets private message recipient from state.
57
+     *
58
+     * @returns {void}
59
+     */
60
+    _onResetPrivateMessageRecipient() {
61
+        const { dispatch } = this.props;
62
+
63
+        dispatch(setPrivateMessageRecipient());
64
+
65
+        setParams({
66
+            privateMessageRecipient: undefined
67
+        });
68
+    }
69
+
29 70
     /**
30 71
      * Implements {@code PureComponent#render}.
31 72
      *
32 73
      * @inheritdoc
74
+     * @returns {ReactElement}
33 75
      */
34 76
     render() {
35
-        const { _privateMessageRecipient, _styles } = this.props;
77
+        const { _styles, privateMessageRecipient, t } = this.props;
36 78
 
37
-        if (!_privateMessageRecipient) {
79
+        if (!privateMessageRecipient) {
38 80
             return null;
39 81
         }
40 82
 
41
-        const { t } = this.props;
42
-
43 83
         return (
44 84
             <View style = { _styles.messageRecipientContainer }>
45 85
                 <Text style = { _styles.messageRecipientText }>
46 86
                     { t('chat.messageTo', {
47
-                        recipient: _privateMessageRecipient
87
+                        recipient: privateMessageRecipient.name
48 88
                     }) }
49 89
                 </Text>
50
-                <TouchableHighlight onPress = { this.props._onRemovePrivateMessageRecipient }>
90
+                <TouchableHighlight
91
+                    onPress = { this._onResetPrivateMessageRecipient }>
51 92
                     <Icon
52 93
                         src = { IconCancelSelection }
53 94
                         style = { _styles.messageRecipientCancelIcon } />
@@ -65,9 +106,8 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
65 106
  */
66 107
 function _mapStateToProps(state) {
67 108
     return {
68
-        ..._abstractMapStateToProps(state),
69 109
         _styles: ColorSchemeRegistry.get(state, 'Chat')
70 110
     };
71 111
 }
72 112
 
73
-export default translate(connect(_mapStateToProps, _mapDispatchToProps)(MessageRecipient));
113
+export default translate(connect(_mapStateToProps)(MessageRecipient));

+ 1
- 0
react/features/chat/components/native/index.js View File

@@ -1,5 +1,6 @@
1 1
 // @flow
2 2
 
3 3
 export { default as Chat } from './Chat';
4
+export { default as ChatAndPolls } from './ChatAndPolls';
4 5
 export { default as ChatButton } from './ChatButton';
5 6
 export { default as ChatPrivacyDialog } from './ChatPrivacyDialog';

+ 7
- 2
react/features/chat/components/native/styles.js View File

@@ -2,6 +2,7 @@
2 2
 
3 3
 import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
4 4
 import { BoxModel, ColorPalette } from '../../../base/styles';
5
+import BaseTheme from '../../../base/ui/components/BaseTheme.native';
5 6
 
6 7
 const BUBBLE_RADIUS = 8;
7 8
 
@@ -40,7 +41,7 @@ export default {
40 41
         alignSelf: 'center',
41 42
         flex: 1,
42 43
         padding: BoxModel.padding,
43
-        paddingTop: '10%'
44
+        paddingTop: '8%'
44 45
     },
45 46
 
46 47
     /**
@@ -126,6 +127,10 @@ export default {
126 127
         fontSize: 13
127 128
     },
128 129
 
130
+    chatContainer: {
131
+        flex: 1
132
+    },
133
+
129 134
     tabContainer: {
130 135
         flexDirection: 'row',
131 136
         justifyContent: 'center'
@@ -164,7 +169,7 @@ ColorSchemeRegistry.register('Chat', {
164 169
     },
165 170
 
166 171
     emptyComponentText: {
167
-        color: schemeColor('displayName'),
172
+        color: BaseTheme.palette.ui05,
168 173
         textAlign: 'center'
169 174
     },
170 175
 

+ 0
- 2
react/features/chat/constants.js View File

@@ -1,7 +1,5 @@
1 1
 // @flow
2 2
 
3
-export const CHAT_VIEW_MODAL_ID = 'chatView';
4
-
5 3
 /**
6 4
  * The size of the chat.
7 5
  */

+ 0
- 17
react/features/chat/middleware.js View File

@@ -10,7 +10,6 @@ import {
10 10
     JitsiConferenceErrors,
11 11
     JitsiConferenceEvents
12 12
 } from '../base/lib-jitsi-meet';
13
-import { setActiveModalId } from '../base/modal';
14 13
 import {
15 14
     getLocalParticipant,
16 15
     getParticipantById,
@@ -18,7 +17,6 @@ import {
18 17
 } from '../base/participants';
19 18
 import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
20 19
 import { playSound, registerSound, unregisterSound } from '../base/sounds';
21
-import { openDisplayNamePrompt } from '../display-name';
22 20
 import { resetNbUnreadPollsMessages } from '../polls/actions';
23 21
 import { ADD_REACTION_MESSAGE } from '../reactions/actionTypes';
24 22
 import { pushReactions } from '../reactions/actions.any';
@@ -35,7 +33,6 @@ import { addMessage, clearMessages } from './actions';
35 33
 import { closeChat } from './actions.any';
36 34
 import { ChatPrivacyDialog } from './components';
37 35
 import {
38
-    CHAT_VIEW_MODAL_ID,
39 36
     INCOMING_MSG_SOUND_ID,
40 37
     MESSAGE_TYPE_ERROR,
41 38
     MESSAGE_TYPE_LOCAL,
@@ -95,18 +92,6 @@ MiddlewareRegistry.register(store => next => action => {
95 92
         break;
96 93
 
97 94
     case OPEN_CHAT:
98
-        if (navigator.product === 'ReactNative') {
99
-            if (localParticipant.name) {
100
-                dispatch(setActiveModalId(CHAT_VIEW_MODAL_ID));
101
-            } else {
102
-                dispatch(openDisplayNamePrompt(() => {
103
-                    dispatch(setActiveModalId(CHAT_VIEW_MODAL_ID));
104
-                }));
105
-            }
106
-        } else {
107
-            dispatch(setActiveModalId(CHAT_VIEW_MODAL_ID));
108
-        }
109
-
110 95
         unreadCount = 0;
111 96
 
112 97
         if (typeof APP !== 'undefined') {
@@ -126,8 +111,6 @@ MiddlewareRegistry.register(store => next => action => {
126 111
         if (isPollTabOpen) {
127 112
             dispatch(resetNbUnreadPollsMessages());
128 113
         }
129
-
130
-        dispatch(setActiveModalId());
131 114
         break;
132 115
     }
133 116
 

+ 21
- 29
react/features/conference/components/native/Conference.js View File

@@ -10,22 +10,18 @@ import { connect } from '../../../base/redux';
10 10
 import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
11 11
 import { TestConnectionInfo } from '../../../base/testing';
12 12
 import { ConferenceNotification, isCalendarEnabled } from '../../../calendar-sync';
13
-import { Chat } from '../../../chat';
14 13
 import { DisplayNameLabel } from '../../../display-name';
15
-import { SharedDocument } from '../../../etherpad';
16 14
 import {
17 15
     FILMSTRIP_SIZE,
18 16
     Filmstrip,
19 17
     isFilmstripVisible,
20 18
     TileView
21 19
 } from '../../../filmstrip';
22
-import { AddPeopleDialog, CalleeInfoContainer } from '../../../invite';
20
+import { CalleeInfoContainer } from '../../../invite';
23 21
 import { LargeVideo } from '../../../large-video';
24 22
 import { KnockingParticipantList } from '../../../lobby';
25
-import { LobbyScreen } from '../../../lobby/components/native';
26 23
 import { getIsLobbyVisible } from '../../../lobby/functions';
27 24
 import { BackButtonRegistry } from '../../../mobile/back-button';
28
-import { ParticipantsPane } from '../../../participants-pane/components/native';
29 25
 import { Captions } from '../../../subtitles';
30 26
 import { setToolboxVisible } from '../../../toolbox/actions';
31 27
 import { Toolbox } from '../../../toolbox/components/native';
@@ -36,8 +32,10 @@ import {
36 32
 } from '../AbstractConference';
37 33
 import type { AbstractProps } from '../AbstractConference';
38 34
 
35
+import { navigate } from './ConferenceNavigationContainerRef';
39 36
 import LonelyMeetingExperience from './LonelyMeetingExperience';
40 37
 import NavigationBar from './NavigationBar';
38
+import { screen } from './routes';
41 39
 import styles from './styles';
42 40
 
43 41
 
@@ -141,6 +139,23 @@ class Conference extends AbstractConference<Props, *> {
141 139
         BackButtonRegistry.addListener(this._onHardwareBackPress);
142 140
     }
143 141
 
142
+    /**
143
+     * Implements {@code Component#componentDidUpdate}.
144
+     *
145
+     * @inheritdoc
146
+     */
147
+    componentDidUpdate(prevProps) {
148
+        const { _showLobby } = this.props;
149
+
150
+        if (!prevProps._showLobby && _showLobby) {
151
+            navigate(screen.lobby);
152
+        }
153
+
154
+        if (prevProps._showLobby && !_showLobby) {
155
+            navigate(screen.conference.main);
156
+        }
157
+    }
158
+
144 159
     /**
145 160
      * Implements {@link Component#componentWillUnmount()}. Invoked immediately
146 161
      * before this component is unmounted and destroyed. Disconnects the
@@ -161,11 +176,7 @@ class Conference extends AbstractConference<Props, *> {
161 176
      * @returns {ReactElement}
162 177
      */
163 178
     render() {
164
-        const { _fullscreenEnabled, _showLobby } = this.props;
165
-
166
-        if (_showLobby) {
167
-            return <LobbyScreen />;
168
-        }
179
+        const { _fullscreenEnabled } = this.props;
169 180
 
170 181
         return (
171 182
             <Container style = { styles.conference }>
@@ -217,19 +228,6 @@ class Conference extends AbstractConference<Props, *> {
217 228
         return true;
218 229
     }
219 230
 
220
-    /**
221
-     * Renders JitsiModals that are supposed to be on the conference screen.
222
-     *
223
-     * @returns {Array<ReactElement>}
224
-     */
225
-    _renderConferenceModals() {
226
-        return [
227
-            <AddPeopleDialog key = 'addPeopleDialog' />,
228
-            <Chat key = 'chat' />,
229
-            <SharedDocument key = 'sharedDocument' />
230
-        ];
231
-    }
232
-
233 231
     /**
234 232
      * Renders the conference notification badge if the feature is enabled.
235 233
      *
@@ -254,7 +252,6 @@ class Conference extends AbstractConference<Props, *> {
254 252
     _renderContent() {
255 253
         const {
256 254
             _connecting,
257
-            _isParticipantsPaneOpen,
258 255
             _largeVideoParticipantId,
259 256
             _reducedUI,
260 257
             _shouldDisplayTileView
@@ -316,12 +313,7 @@ class Conference extends AbstractConference<Props, *> {
316 313
                 <TestConnectionInfo />
317 314
                 { this._renderConferenceNotification() }
318 315
 
319
-                { this._renderConferenceModals() }
320
-
321 316
                 {_shouldDisplayTileView && <Toolbox />}
322
-
323
-                { _isParticipantsPaneOpen && <ParticipantsPane /> }
324
-
325 317
             </>
326 318
         );
327 319
     }

+ 100
- 0
react/features/conference/components/native/ConferenceNavigationContainer.js View File

@@ -0,0 +1,100 @@
1
+// @flow
2
+
3
+import { NavigationContainer } from '@react-navigation/native';
4
+import { createStackNavigator } from '@react-navigation/stack';
5
+import React from 'react';
6
+import { SafeAreaProvider } from 'react-native-safe-area-context';
7
+import { useSelector } from 'react-redux';
8
+
9
+import { Chat, ChatAndPolls } from '../../../chat';
10
+import { SharedDocument } from '../../../etherpad';
11
+import AddPeopleDialog
12
+    from '../../../invite/components/add-people-dialog/native/AddPeopleDialog';
13
+import LobbyScreen from '../../../lobby/components/native/LobbyScreen';
14
+import { ParticipantsPane } from '../../../participants-pane/components/native';
15
+import { getDisablePolls } from '../../functions';
16
+
17
+import Conference from './Conference';
18
+import {
19
+    conferenceNavigationRef
20
+} from './ConferenceNavigationContainerRef';
21
+import {
22
+    chatScreenOptions,
23
+    conferenceScreenOptions,
24
+    inviteScreenOptions,
25
+    lobbyScreenOptions,
26
+    participantsScreenOptions,
27
+    sharedDocumentScreenOptions
28
+} from './ConferenceNavigatorScreenOptions';
29
+import { screen } from './routes';
30
+
31
+const ConferenceStack = createStackNavigator();
32
+
33
+const ConferenceNavigationContainer = () => {
34
+    const isPollsDisabled = useSelector(getDisablePolls);
35
+    const ChatScreen
36
+        = isPollsDisabled
37
+            ? Chat
38
+            : ChatAndPolls;
39
+    const chatScreenName
40
+        = isPollsDisabled
41
+            ? screen.conference.chat
42
+            : screen.conference.chatandpolls.main;
43
+
44
+    return (
45
+        <SafeAreaProvider>
46
+            <NavigationContainer
47
+                independent = { true }
48
+                ref = { conferenceNavigationRef }
49
+                theme = {{
50
+                    colors: {
51
+                        background: '#fff'
52
+                    }
53
+                }}>
54
+                <ConferenceStack.Navigator
55
+                    initialRouteName = { screen.conference.main }
56
+                    mode = 'modal'>
57
+                    <ConferenceStack.Screen
58
+                        component = { Conference }
59
+                        name = { screen.conference.main }
60
+                        options = {{
61
+                            ...conferenceScreenOptions
62
+                        }} />
63
+                    <ConferenceStack.Screen
64
+                        /* eslint-disable-next-line react/jsx-no-bind */
65
+                        component = { ChatScreen }
66
+                        name = { chatScreenName }
67
+                        options = {{
68
+                            ...chatScreenOptions
69
+                        }} />
70
+                    <ConferenceStack.Screen
71
+                        component = { ParticipantsPane }
72
+                        name = { screen.conference.participants }
73
+                        options = {{
74
+                            ...participantsScreenOptions
75
+                        }} />
76
+                    <ConferenceStack.Screen
77
+                        component = { LobbyScreen }
78
+                        name = { screen.lobby }
79
+                        options = {{
80
+                            ...lobbyScreenOptions
81
+                        }} />
82
+                    <ConferenceStack.Screen
83
+                        component = { AddPeopleDialog }
84
+                        name = { screen.conference.invite }
85
+                        options = {{
86
+                            ...inviteScreenOptions
87
+                        }} />
88
+                    <ConferenceStack.Screen
89
+                        component = { SharedDocument }
90
+                        name = { screen.conference.sharedDocument }
91
+                        options = {{
92
+                            ...sharedDocumentScreenOptions
93
+                        }} />
94
+                </ConferenceStack.Navigator>
95
+            </NavigationContainer>
96
+        </SafeAreaProvider>
97
+    );
98
+};
99
+
100
+export default ConferenceNavigationContainer;

+ 40
- 0
react/features/conference/components/native/ConferenceNavigationContainerRef.js View File

@@ -0,0 +1,40 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+
5
+// $FlowExpectedError
6
+export const conferenceNavigationRef = React.createRef();
7
+
8
+/**
9
+ * User defined navigation action included inside the reference to the container.
10
+ *
11
+ * @param {string} name - Destination name of the route that has been defined somewhere.
12
+ * @param {Object} params - Params to pass to the destination route.
13
+ * @returns {Function}
14
+ */
15
+export function navigate(name: string, params: Object) {
16
+    // $FlowExpectedError
17
+    return conferenceNavigationRef.current?.navigate(name, params);
18
+}
19
+
20
+/**
21
+ * User defined navigation action included inside the reference to the container.
22
+ *
23
+ * @returns {Function}
24
+ */
25
+export function goBack() {
26
+    // $FlowExpectedError
27
+    return conferenceNavigationRef.current?.goBack();
28
+}
29
+
30
+/**
31
+ * User defined navigation action included inside the reference to the container.
32
+ *
33
+ * @param {Object} params - Params to pass to the destination route.
34
+ * @returns {Function}
35
+ */
36
+export function setParams(params: Object) {
37
+    // $FlowExpectedError
38
+    return conferenceNavigationRef.current?.setParams(params);
39
+}
40
+

+ 111
- 0
react/features/conference/components/native/ConferenceNavigatorScreenOptions.js View File

@@ -0,0 +1,111 @@
1
+import { TransitionPresets } from '@react-navigation/stack';
2
+import React from 'react';
3
+import { Platform } from 'react-native';
4
+
5
+import { IconClose } from '../../../base/icons';
6
+import BaseTheme from '../../../base/ui/components/BaseTheme';
7
+
8
+import { goBack } from './ConferenceNavigationContainerRef';
9
+import HeaderNavigationButton from './HeaderNavigationButton';
10
+
11
+
12
+/**
13
+ * Default modal transition for the current platform.
14
+ */
15
+export const conferenceModalPresentation = Platform.select({
16
+    ios: TransitionPresets.ModalPresentationIOS,
17
+    default: TransitionPresets.DefaultTransition
18
+});
19
+
20
+/**
21
+ * Screen options and transition types.
22
+ */
23
+export const screenOptions = {
24
+    ...TransitionPresets.ModalTransition,
25
+    gestureEnabled: false,
26
+    headerShown: false
27
+};
28
+
29
+/**
30
+ * Screen options for conference.
31
+ */
32
+export const conferenceScreenOptions = {
33
+    ...screenOptions
34
+};
35
+
36
+/**
37
+ * Screen options for lobby modal.
38
+ */
39
+export const lobbyScreenOptions = {
40
+    ...screenOptions
41
+};
42
+
43
+/**
44
+ * Tab bar options for chat screen.
45
+ */
46
+export const chatTabBarOptions = {
47
+    activeTintColor: BaseTheme.palette.screen01Header,
48
+    labelStyle: {
49
+        fontSize: BaseTheme.typography.labelRegular.fontSize
50
+    },
51
+    inactiveTintColor: BaseTheme.palette.field02Disabled,
52
+    indicatorStyle: {
53
+        backgroundColor: BaseTheme.palette.screen01Header
54
+    }
55
+};
56
+
57
+/**
58
+ * Screen options for presentation type modals.
59
+ */
60
+export const presentationScreenOptions = {
61
+    ...conferenceModalPresentation,
62
+    headerBackTitleVisible: false,
63
+    headerLeft: () => (
64
+        <HeaderNavigationButton
65
+            onPress = { goBack }
66
+            src = { IconClose } />
67
+    ),
68
+    headerStatusBarHeight: 0,
69
+    headerStyle: {
70
+        backgroundColor: BaseTheme.palette.screen01Header
71
+    },
72
+    headerTitleStyle: {
73
+        color: BaseTheme.palette.text01
74
+    }
75
+};
76
+
77
+/**
78
+ * Screen options for chat.
79
+ */
80
+export const chatScreenOptions = {
81
+    ...presentationScreenOptions
82
+};
83
+
84
+/**
85
+ * Screen options for invite modal.
86
+ */
87
+export const inviteScreenOptions = {
88
+    ...presentationScreenOptions
89
+};
90
+
91
+/**
92
+ * Screen options for participants modal.
93
+ */
94
+export const participantsScreenOptions = {
95
+    ...presentationScreenOptions
96
+};
97
+
98
+/**
99
+ * Screen options for shared document.
100
+ */
101
+export const sharedDocumentScreenOptions = {
102
+    ...TransitionPresets.DefaultTransition,
103
+    headerBackTitleVisible: false,
104
+    headerShown: true,
105
+    headerStyle: {
106
+        backgroundColor: BaseTheme.palette.screen01Header
107
+    },
108
+    headerTitleStyle: {
109
+        color: BaseTheme.palette.text01
110
+    }
111
+};

+ 39
- 0
react/features/conference/components/native/HeaderNavigationButton.js View File

@@ -0,0 +1,39 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+import { TouchableWithoutFeedback } from 'react-native';
5
+
6
+import { Icon } from '../../../base/icons';
7
+
8
+import styles from './styles';
9
+
10
+type Props = {
11
+
12
+    /**
13
+     * Callback to invoke when the {@code HeaderNavigationButton} is clicked/pressed.
14
+     */
15
+    onPress: Function,
16
+
17
+    /**
18
+     * The ImageSource to be rendered as image.
19
+     */
20
+    src: Object,
21
+
22
+    /**
23
+     * The component's external style
24
+     */
25
+    style: Object
26
+}
27
+
28
+const HeaderNavigationButton = ({ onPress, src, style }: Props) => (
29
+    <TouchableWithoutFeedback
30
+        onPress = { onPress } >
31
+        <Icon
32
+            size = { 20 }
33
+            src = { src }
34
+            style = { [ styles.headerNavigationButton, style ] } />
35
+    </TouchableWithoutFeedback>
36
+);
37
+
38
+
39
+export default HeaderNavigationButton;

+ 1
- 0
react/features/conference/components/native/index.js View File

@@ -1,5 +1,6 @@
1 1
 // @flow
2 2
 
3 3
 export { default as Conference } from './Conference';
4
+export { default as ConferenceNavigationContainer } from './ConferenceNavigationContainer';
4 5
 export { default as renderConferenceTimer } from './ConferenceTimerDisplay';
5 6
 export { default as InsecureRoomNameLabel } from './InsecureRoomNameLabel';

+ 17
- 0
react/features/conference/components/native/routes.js View File

@@ -0,0 +1,17 @@
1
+export const screen = {
2
+    conference: {
3
+        main: 'Conference',
4
+        chat: 'Chat',
5
+        chatandpolls: {
6
+            main: 'Chat and Polls',
7
+            tab: {
8
+                chat: 'Chat',
9
+                polls: 'Polls'
10
+            }
11
+        },
12
+        participants: 'Participants',
13
+        invite: 'Invite',
14
+        sharedDocument: 'Shared document'
15
+    },
16
+    lobby: 'Lobby'
17
+};

+ 4
- 0
react/features/conference/components/native/styles.js View File

@@ -23,6 +23,10 @@ export default {
23 23
         margin: 10
24 24
     },
25 25
 
26
+    headerNavigationButton: {
27
+        marginLeft: 12
28
+    },
29
+
26 30
     /**
27 31
      * View that contains the indicators.
28 32
      */

+ 20
- 0
react/features/conference/functions.native.js View File

@@ -1 +1,21 @@
1
+// @flow
2
+
3
+import { toState } from '../base/redux';
4
+
1 5
 export * from './functions.any';
6
+
7
+
8
+/**
9
+ *
10
+ * Returns true if polls feature is disabled.
11
+ *
12
+ * @param {(Function|Object)} stateful - The (whole) redux state, or redux's
13
+ * {@code getState} function to be used to retrieve the state
14
+ * features/base/config.
15
+ * @returns {boolean}.
16
+ */
17
+export function getDisablePolls(stateful: Object) {
18
+    const state = toState(stateful['features/base/config']);
19
+
20
+    return state.disablePolls;
21
+}

+ 6
- 11
react/features/etherpad/components/SharedDocumentButton.js View File

@@ -1,13 +1,12 @@
1 1
 // @flow
2 2
 
3
-import type { Dispatch } from 'redux';
4
-
5 3
 import { createToolbarEvent, sendAnalytics } from '../../analytics';
6 4
 import { translate } from '../../base/i18n';
7 5
 import { IconShareDoc } from '../../base/icons';
8 6
 import { connect } from '../../base/redux';
9 7
 import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
10
-import { toggleDocument } from '../actions';
8
+import { navigate } from '../../conference/components/native/ConferenceNavigationContainerRef';
9
+import { screen } from '../../conference/components/native/routes';
11 10
 
12 11
 
13 12
 type Props = AbstractButtonProps & {
@@ -15,12 +14,7 @@ type Props = AbstractButtonProps & {
15 14
     /**
16 15
      * Whether the shared document is being edited or not.
17 16
      */
18
-    _editing: boolean,
19
-
20
-    /**
21
-     * Redux dispatch function.
22
-     */
23
-    dispatch: Dispatch<any>,
17
+    _editing: boolean
24 18
 };
25 19
 
26 20
 /**
@@ -59,7 +53,7 @@ class SharedDocumentButton extends AbstractButton<Props, *> {
59 53
      * @returns {void}
60 54
      */
61 55
     _handleClick() {
62
-        const { _editing, dispatch, handleClick } = this.props;
56
+        const { _editing, handleClick } = this.props;
63 57
 
64 58
         if (handleClick) {
65 59
             handleClick();
@@ -72,7 +66,8 @@ class SharedDocumentButton extends AbstractButton<Props, *> {
72 66
             {
73 67
                 enable: !_editing
74 68
             }));
75
-        dispatch(toggleDocument());
69
+
70
+        navigate(screen.conference.sharedDocument);
76 71
     }
77 72
 
78 73
     /**

+ 33
- 58
react/features/etherpad/components/native/SharedDocument.js View File

@@ -3,15 +3,16 @@
3 3
 import React, { PureComponent } from 'react';
4 4
 import { View } from 'react-native';
5 5
 import { WebView } from 'react-native-webview';
6
-import type { Dispatch } from 'redux';
7 6
 
8 7
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
9 8
 import { translate } from '../../../base/i18n';
10
-import { JitsiModal } from '../../../base/modal';
9
+import { IconArrowBack } from '../../../base/icons';
10
+import JitsiScreen from '../../../base/modal/components/JitsiScreen';
11 11
 import { LoadingIndicator } from '../../../base/react';
12 12
 import { connect } from '../../../base/redux';
13
-import { toggleDocument } from '../../actions';
14
-import { SHARE_DOCUMENT_VIEW_ID } from '../../constants';
13
+import { goBack } from '../../../conference/components/native/ConferenceNavigationContainerRef';
14
+import HeaderNavigationButton
15
+    from '../../../conference/components/native/HeaderNavigationButton';
15 16
 import { getSharedDocumentUrl } from '../../functions';
16 17
 
17 18
 import styles, { INDICATOR_COLOR } from './styles';
@@ -32,14 +33,9 @@ type Props = {
32 33
     _headerStyles: Object,
33 34
 
34 35
     /**
35
-     * True if the chat window should be rendered.
36
+     * Default prop for navigation between screen components(React Navigation)
36 37
      */
37
-    _isOpen: boolean,
38
-
39
-    /**
40
-     * The Redux dispatch function.
41
-     */
42
-    dispatch: Dispatch<any>,
38
+    navigation: Object,
43 39
 
44 40
     /**
45 41
      * Function to be used to translate i18n labels.
@@ -59,11 +55,29 @@ class SharedDocument extends PureComponent<Props> {
59 55
     constructor(props: Props) {
60 56
         super(props);
61 57
 
62
-        this._onClose = this._onClose.bind(this);
63
-        this._onError = this._onError.bind(this);
64 58
         this._renderLoading = this._renderLoading.bind(this);
65 59
     }
66 60
 
61
+    /**
62
+     * Implements React's {@link Component#componentDidMount()}. Invoked
63
+     * immediately after this component is mounted.
64
+     *
65
+     * @inheritdoc
66
+     * @returns {void}
67
+     */
68
+    componentDidMount() {
69
+        const { navigation } = this.props;
70
+
71
+        navigation.setOptions({
72
+            headerLeft: () => (
73
+                <HeaderNavigationButton
74
+                    onPress = { goBack }
75
+                    src = { IconArrowBack }
76
+                    style = { styles.headerArrowBack } />
77
+            )
78
+        });
79
+    }
80
+
67 81
     /**
68 82
      * Implements React's {@link Component#render()}.
69 83
      *
@@ -73,55 +87,18 @@ class SharedDocument extends PureComponent<Props> {
73 87
         const { _documentUrl } = this.props;
74 88
 
75 89
         return (
76
-            <JitsiModal
77
-                headerProps = {{
78
-                    headerLabelKey: 'documentSharing.title'
79
-                }}
80
-                modalId = { SHARE_DOCUMENT_VIEW_ID }
81
-                style = { styles.webView }>
90
+            <JitsiScreen
91
+                addHeaderHeightValue = { true }
92
+                hasTabNavigator = { false }
93
+                style = { styles.sharedDocContainer }>
82 94
                 <WebView
83
-                    onError = { this._onError }
84 95
                     renderLoading = { this._renderLoading }
85 96
                     source = {{ uri: _documentUrl }}
86 97
                     startInLoadingState = { true } />
87
-            </JitsiModal>
98
+            </JitsiScreen>
88 99
         );
89 100
     }
90 101
 
91
-    _onClose: () => boolean
92
-
93
-    /**
94
-     * Closes the window.
95
-     *
96
-     * @returns {boolean}
97
-     */
98
-    _onClose() {
99
-        const { _isOpen, dispatch } = this.props;
100
-
101
-        if (_isOpen) {
102
-            dispatch(toggleDocument());
103
-
104
-            return true;
105
-        }
106
-
107
-        return false;
108
-    }
109
-
110
-    _onError: () => void;
111
-
112
-    /**
113
-     * Callback to handle the error if the page fails to load.
114
-     *
115
-     * @returns {void}
116
-     */
117
-    _onError() {
118
-        const { _isOpen, dispatch } = this.props;
119
-
120
-        if (_isOpen) {
121
-            dispatch(toggleDocument());
122
-        }
123
-    }
124
-
125 102
     _renderLoading: () => React$Component<any>;
126 103
 
127 104
     /**
@@ -148,13 +125,11 @@ class SharedDocument extends PureComponent<Props> {
148 125
  * @returns {Object}
149 126
  */
150 127
 export function _mapStateToProps(state: Object) {
151
-    const { editing } = state['features/etherpad'];
152 128
     const documentUrl = getSharedDocumentUrl(state);
153 129
 
154 130
     return {
155 131
         _documentUrl: documentUrl,
156
-        _headerStyles: ColorSchemeRegistry.get(state, 'Header'),
157
-        _isOpen: editing
132
+        _headerStyles: ColorSchemeRegistry.get(state, 'Header')
158 133
     };
159 134
 }
160 135
 

+ 8
- 0
react/features/etherpad/components/native/styles.js View File

@@ -6,6 +6,10 @@ export const INDICATOR_COLOR = ColorPalette.lightGrey;
6 6
 
7 7
 export default {
8 8
 
9
+    headerArrowBack: {
10
+        marginLeft: 12
11
+    },
12
+
9 13
     indicatorWrapper: {
10 14
         alignItems: 'center',
11 15
         backgroundColor: ColorPalette.white,
@@ -13,6 +17,10 @@ export default {
13 17
         justifyContent: 'center'
14 18
     },
15 19
 
20
+    sharedDocContainer: {
21
+        flex: 1
22
+    },
23
+
16 24
     webView: {
17 25
         backgroundColor: 'rgb(242, 242, 242)'
18 26
     }

+ 3
- 3
react/features/invite/actions.native.js View File

@@ -3,10 +3,10 @@
3 3
 import type { Dispatch } from 'redux';
4 4
 
5 5
 import { getFeatureFlag, ADD_PEOPLE_ENABLED } from '../base/flags';
6
-import { setActiveModalId } from '../base/modal';
6
+import { navigate } from '../conference/components/native/ConferenceNavigationContainerRef';
7
+import { screen } from '../conference/components/native/routes';
7 8
 import { beginShareRoom } from '../share-room';
8 9
 
9
-import { ADD_PEOPLE_DIALOG_VIEW_ID } from './constants';
10 10
 import { isAddPeopleEnabled, isDialOutEnabled } from './functions';
11 11
 
12 12
 export * from './actions.any';
@@ -24,7 +24,7 @@ export function doInvitePeople() {
24 24
             && (isAddPeopleEnabled(state) || isDialOutEnabled(state));
25 25
 
26 26
         if (addPeopleEnabled) {
27
-            return dispatch(setActiveModalId(ADD_PEOPLE_DIALOG_VIEW_ID));
27
+            return navigate(screen.conference.invite);
28 28
         }
29 29
 
30 30
         return dispatch(beginShareRoom());

+ 67
- 31
react/features/invite/components/add-people-dialog/native/AddPeopleDialog.js View File

@@ -6,13 +6,12 @@ import {
6 6
     ActivityIndicator,
7 7
     FlatList,
8 8
     Platform,
9
-    SafeAreaView,
10 9
     TextInput,
11 10
     TouchableOpacity,
12 11
     View
13 12
 } from 'react-native';
13
+import { Text, TouchableRipple, withTheme } from 'react-native-paper';
14 14
 
15
-import { ColorSchemeRegistry } from '../../../../base/color-scheme';
16 15
 import { AlertDialog, openDialog } from '../../../../base/dialog';
17 16
 import { translate } from '../../../../base/i18n';
18 17
 import {
@@ -24,15 +23,14 @@ import {
24 23
     IconSearch,
25 24
     IconShare
26 25
 } from '../../../../base/icons';
27
-import { JitsiModal, setActiveModalId } from '../../../../base/modal';
26
+import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
28 27
 import {
29 28
     AvatarListItem,
30 29
     type Item
31 30
 } from '../../../../base/react';
32 31
 import { connect } from '../../../../base/redux';
33
-import { ColorPalette } from '../../../../base/styles';
34 32
 import { beginShareRoom } from '../../../../share-room';
35
-import { ADD_PEOPLE_DIALOG_VIEW_ID, INVITE_TYPES } from '../../../constants';
33
+import { INVITE_TYPES } from '../../../constants';
36 34
 import AbstractAddPeopleDialog, {
37 35
     type Props as AbstractProps,
38 36
     type State as AbstractState,
@@ -47,19 +45,24 @@ import styles, {
47 45
 type Props = AbstractProps & {
48 46
 
49 47
     /**
50
-     * The color schemed style of the Header.
48
+     * True if the invite dialog should be open, false otherwise.
51 49
      */
52
-    _headerStyles: Object,
50
+    _isVisible: boolean,
53 51
 
54 52
     /**
55
-     * True if the invite dialog should be open, false otherwise.
53
+     * Default prop for navigation between screen components(React Navigation)
56 54
      */
57
-    _isVisible: boolean,
55
+    navigation: Object,
58 56
 
59 57
     /**
60 58
      * Function used to translate i18n labels.
61 59
      */
62
-    t: Function
60
+    t: Function,
61
+
62
+    /**
63
+     * Theme used for styles.
64
+     */
65
+    theme: Object
63 66
 };
64 67
 
65 68
 type State = AbstractState & {
@@ -136,12 +139,54 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
136 139
         this._setFieldRef = this._setFieldRef.bind(this);
137 140
     }
138 141
 
142
+    /**
143
+     * Implements React's {@link Component#componentDidMount()}. Invoked
144
+     * immediately after this component is mounted.
145
+     *
146
+     * @inheritdoc
147
+     * @returns {void}
148
+     */
149
+    componentDidMount() {
150
+        const { navigation, t, theme } = this.props;
151
+        const { palette } = theme;
152
+
153
+        navigation.setOptions({
154
+            headerRight: () => (
155
+                <TouchableRipple
156
+                    disabled = { this._isAddDisabled() }
157
+                    rippleColor = { palette.screen01Header } >
158
+                    <Text
159
+                        style = { styles.headerSendInvite }>{ t('inviteDialog.send') }
160
+                    </Text>
161
+                </TouchableRipple>
162
+            )
163
+        });
164
+    }
165
+
139 166
     /**
140 167
      * Implements {@code Component#componentDidUpdate}.
141 168
      *
142 169
      * @inheritdoc
143 170
      */
144 171
     componentDidUpdate(prevProps) {
172
+        const { navigation, t, theme } = this.props;
173
+        const { palette } = theme;
174
+
175
+        navigation.setOptions({
176
+            // eslint-disable-next-line react/no-multi-comp
177
+            headerRight: () => (
178
+                <TouchableRipple
179
+                    disabled = { this._isAddDisabled() }
180
+                    onPress = { this._onInvite }
181
+                    rippleColor = { palette.screen01Header } >
182
+                    <Text
183
+                        /* eslint-disable-next-line react-native/no-inline-styles */
184
+                        style = { styles.headerSendInvite }>{ t('inviteDialog.send') }
185
+                    </Text>
186
+                </TouchableRipple>
187
+            )
188
+        });
189
+
145 190
         if (prevProps._isVisible !== this.props._isVisible) {
146 191
             // Clear state
147 192
             this._clearState();
@@ -159,6 +204,8 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
159 204
             _dialOutEnabled
160 205
         } = this.props;
161 206
         const { inviteItems, selectableItems } = this.state;
207
+        const { theme } = this.props;
208
+        const { palette } = theme;
162 209
 
163 210
         let placeholderKey = 'searchPlaceholder';
164 211
 
@@ -169,15 +216,10 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
169 216
         }
170 217
 
171 218
         return (
172
-            <JitsiModal
219
+            <JitsiScreen
173 220
                 footerComponent = { this._renderShareMeetingButton }
174
-                headerProps = {{
175
-                    forwardDisabled: this._isAddDisabled(),
176
-                    forwardLabelKey: 'inviteDialog.send',
177
-                    headerLabelKey: 'inviteDialog.header',
178
-                    onPressForward: this._onInvite
179
-                }}
180
-                modalId = { ADD_PEOPLE_DIALOG_VIEW_ID }>
221
+                hasTabNavigator = { false }
222
+                style = { styles.addPeopleContainer }>
181 223
                 <View
182 224
                     style = { styles.searchFieldWrapper }>
183 225
                     <View style = { styles.searchIconWrapper }>
@@ -198,7 +240,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
198 240
                         placeholder = {
199 241
                             this.props.t(`inviteDialog.${placeholderKey}`)
200 242
                         }
201
-                        placeholderTextColor = { ColorPalette.lightGrey }
243
+                        placeholderTextColor = { palette.text04 }
202 244
                         ref = { this._setFieldRef }
203 245
                         spellCheck = { false }
204 246
                         style = { styles.searchField }
@@ -222,7 +264,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
222 264
                         keyboardShouldPersistTaps = 'always'
223 265
                         renderItem = { this._renderItem } />
224 266
                 </View>
225
-            </JitsiModal>
267
+            </JitsiScreen>
226 268
         );
227 269
     }
228 270
 
@@ -326,8 +368,6 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
326 368
                         inviteItems: invitesLeftToSend
327 369
                     });
328 370
                     this._showFailedInviteAlert();
329
-                } else {
330
-                    this.props.dispatch(setActiveModalId());
331 371
                 }
332 372
             });
333 373
     }
@@ -562,22 +602,20 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
562 602
      * @returns {React#Element<*>}
563 603
      */
564 604
     _renderShareMeetingButton() {
565
-        const { _headerStyles } = this.props;
566 605
 
567 606
         return (
568
-            <SafeAreaView
607
+            <View
569 608
                 style = { [
570 609
                     styles.bottomBar,
571
-                    _headerStyles.headerOverlay,
572 610
                     this.state.bottomPadding ? styles.extraBarPadding : null
573 611
                 ] }>
574 612
                 <TouchableOpacity
575 613
                     onPress = { this._onShareMeeting }>
576 614
                     <Icon
577 615
                         src = { IconShare }
578
-                        style = { [ _headerStyles.headerButtonText, styles.shareIcon ] } />
616
+                        style = { styles.shareIcon } />
579 617
                 </TouchableOpacity>
580
-            </SafeAreaView>
618
+            </View>
581 619
         );
582 620
     }
583 621
 
@@ -621,10 +659,8 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
621 659
  */
622 660
 function _mapStateToProps(state: Object) {
623 661
     return {
624
-        ..._abstractMapStateToProps(state),
625
-        _headerStyles: ColorSchemeRegistry.get(state, 'Header'),
626
-        _isVisible: state['features/base/modal'].activeModalId === ADD_PEOPLE_DIALOG_VIEW_ID
662
+        ..._abstractMapStateToProps(state)
627 663
     };
628 664
 }
629 665
 
630
-export default translate(connect(_mapStateToProps)(AddPeopleDialog));
666
+export default translate(connect(_mapStateToProps)(withTheme(AddPeopleDialog)));

+ 22
- 8
react/features/invite/components/add-people-dialog/native/styles.js View File

@@ -1,15 +1,19 @@
1 1
 // @flow
2 2
 
3 3
 import { BoxModel } from '../../../../base/styles';
4
+import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
4 5
 
5 6
 export const AVATAR_SIZE = 40;
6 7
 export const DARK_GREY = 'rgb(28, 32, 37)';
7 8
 export const LIGHT_GREY = 'rgb(209, 219, 232)';
8 9
 export const ICON_SIZE = 15;
9 10
 
10
-const FIELD_COLOR = 'rgb(240, 243, 247)';
11
-
12 11
 export default {
12
+
13
+    addPeopleContainer: {
14
+        flex: 1
15
+    },
16
+
13 17
     avatar: {
14 18
         backgroundColor: LIGHT_GREY
15 19
     },
@@ -21,8 +25,9 @@ export default {
21 25
 
22 26
     bottomBar: {
23 27
         alignItems: 'center',
24
-        flexDirection: 'row',
25
-        justifyContent: 'space-around'
28
+        justifyContent: 'center',
29
+        backgroundColor: BaseTheme.palette.screen01Header,
30
+        height: BaseTheme.spacing[10]
26 31
     },
27 32
 
28 33
     clearButton: {
@@ -39,7 +44,7 @@ export default {
39 44
 
40 45
     clearIconContainer: {
41 46
         alignItems: 'center',
42
-        backgroundColor: FIELD_COLOR,
47
+        backgroundColor: BaseTheme.palette.section01,
43 48
         borderRadius: 12,
44 49
         justifyContent: 'center',
45 50
         height: 24,
@@ -53,6 +58,15 @@ export default {
53 58
         paddingBottom: 30
54 59
     },
55 60
 
61
+    headerCloseIcon: {
62
+        marginLeft: 12
63
+    },
64
+
65
+    headerSendInvite: {
66
+        color: BaseTheme.palette.text01,
67
+        marginRight: 12
68
+    },
69
+
56 70
     invitedList: {
57 71
         padding: 3
58 72
     },
@@ -80,7 +94,7 @@ export default {
80 94
     },
81 95
 
82 96
     searchField: {
83
-        backgroundColor: FIELD_COLOR,
97
+        backgroundColor: BaseTheme.palette.section01,
84 98
         borderBottomRightRadius: 10,
85 99
         borderTopRightRadius: 10,
86 100
         color: DARK_GREY,
@@ -106,7 +120,7 @@ export default {
106 120
         alignItems: 'stretch',
107 121
         flexDirection: 'row',
108 122
         height: 52,
109
-        paddingHorizontal: 15,
123
+        paddingHorizontal: 12,
110 124
         paddingVertical: 8
111 125
     },
112 126
 
@@ -117,7 +131,7 @@ export default {
117 131
 
118 132
     searchIconWrapper: {
119 133
         alignItems: 'center',
120
-        backgroundColor: FIELD_COLOR,
134
+        backgroundColor: BaseTheme.palette.section01,
121 135
         borderBottomLeftRadius: 10,
122 136
         borderTopLeftRadius: 10,
123 137
         flexDirection: 'row',

+ 0
- 5
react/features/invite/constants.js View File

@@ -1,10 +1,5 @@
1 1
 // @flow
2 2
 
3
-/**
4
- * Modal ID for the AddPeopleDialog modal.
5
- */
6
-export const ADD_PEOPLE_DIALOG_VIEW_ID = 'ADD_PEOPLE_DIALOG_VIEW_ID';
7
-
8 3
 /**
9 4
  * Modal ID for the DialInSummary modal.
10 5
  */

+ 15
- 6
react/features/lobby/components/native/LobbyScreen.js View File

@@ -2,11 +2,12 @@
2 2
 
3 3
 import React from 'react';
4 4
 import { Text, View, TouchableOpacity, TextInput } from 'react-native';
5
+import { SafeAreaView } from 'react-native-safe-area-context';
5 6
 
6 7
 import { Avatar } from '../../../base/avatar';
7
-import { CustomDialog } from '../../../base/dialog';
8 8
 import { translate } from '../../../base/i18n';
9 9
 import { Icon, IconEdit } from '../../../base/icons';
10
+import JitsiScreen from '../../../base/modal/components/JitsiScreen';
10 11
 import { LoadingIndicator } from '../../../base/react';
11 12
 import { connect } from '../../../base/redux';
12 13
 import AbstractLobbyScreen, { _mapStateToProps } from '../AbstractLobbyScreen';
@@ -26,9 +27,10 @@ class LobbyScreen extends AbstractLobbyScreen {
26 27
         const { _meetingName, t } = this.props;
27 28
 
28 29
         return (
29
-            <CustomDialog
30
-                onCancel = { this._onCancel }>
31
-                <View style = { styles.contentWrapper }>
30
+            <JitsiScreen
31
+                hasTabNavigator = { false }
32
+                style = { styles.contentWrapper }>
33
+                <SafeAreaView>
32 34
                     <Text style = { styles.dialogTitle }>
33 35
                         { t(this._getScreenTitleKey()) }
34 36
                     </Text>
@@ -36,8 +38,8 @@ class LobbyScreen extends AbstractLobbyScreen {
36 38
                         { _meetingName }
37 39
                     </Text>
38 40
                     { this._renderContent() }
39
-                </View>
40
-            </CustomDialog>
41
+                </SafeAreaView>
42
+            </JitsiScreen>
41 43
         );
42 44
     }
43 45
 
@@ -234,6 +236,13 @@ class LobbyScreen extends AbstractLobbyScreen {
234 236
                         { t('lobby.enterPasswordButton') }
235 237
                     </Text>
236 238
                 </TouchableOpacity> }
239
+                <TouchableOpacity
240
+                    onPress = { this._onCancel }
241
+                    style = { styles.cancelButton }>
242
+                    <Text>
243
+                        { t('dialog.Cancel') }
244
+                    </Text>
245
+                </TouchableOpacity>
237 246
             </>
238 247
         );
239 248
     }

+ 24
- 5
react/features/lobby/components/native/styles.js View File

@@ -12,20 +12,29 @@ export default {
12 12
     button: {
13 13
         alignItems: 'center',
14 14
         borderRadius: 4,
15
-        marginVertical: 8,
16
-        paddingVertical: 10
15
+        marginVertical: 4,
16
+        paddingVertical: 8
17 17
     },
18 18
 
19 19
     contentWrapper: {
20 20
         alignItems: 'center',
21
+        display: 'flex',
21 22
         flexDirection: 'column',
22
-        padding: 32
23
+        justifyItems: 'center',
24
+        height: '100%'
25
+    },
26
+
27
+    closeIcon: {
28
+        color: 'red',
29
+        fontSize: 20
23 30
     },
24 31
 
25 32
     dialogTitle: {
26 33
         fontSize: 18,
27 34
         fontWeight: 'bold',
28
-        marginBottom: 10
35
+        margin: 'auto',
36
+        marginVertical: 24,
37
+        textAlign: 'center'
29 38
     },
30 39
 
31 40
     displayNameText: {
@@ -71,6 +80,8 @@ export default {
71 80
     },
72 81
 
73 82
     joiningMessage: {
83
+        color: 'rgba(0, 0, 0, .7)',
84
+        paddingBottom: 36,
74 85
         textAlign: 'center'
75 86
     },
76 87
 
@@ -103,7 +114,15 @@ export default {
103 114
     },
104 115
 
105 116
     secondaryText: {
106
-        color: 'rgba(0, 0, 0, .7)'
117
+        color: 'rgba(0, 0, 0, .7)',
118
+        margin: 'auto',
119
+        textAlign: 'center'
120
+    },
121
+
122
+    cancelButton: {
123
+        alignItems: 'center',
124
+        backgroundColor: 'transparent',
125
+        marginVertical: 4
107 126
     },
108 127
 
109 128
     // KnockingParticipantList

+ 10
- 11
react/features/participants-pane/components/native/LobbyParticipantList.js View File

@@ -2,7 +2,7 @@
2 2
 
3 3
 import React, { useCallback } from 'react';
4 4
 import { useTranslation } from 'react-i18next';
5
-import { ScrollView, Text, View } from 'react-native';
5
+import { Text, View } from 'react-native';
6 6
 import { Button, withTheme } from 'react-native-paper';
7 7
 import { useDispatch, useSelector } from 'react-redux';
8 8
 
@@ -36,7 +36,8 @@ const LobbyParticipantList = ({ theme }: Props) => {
36 36
     }
37 37
 
38 38
     return (
39
-        <View style = { styles.lobbyList }>
39
+        <View
40
+            style = { styles.lobbyListContainer } >
40 41
             <View style = { styles.lobbyListDetails } >
41 42
                 <Text style = { styles.lobbyListDescription }>
42 43
                     {t('participantsPane.headings.waitingLobby',
@@ -54,15 +55,13 @@ const LobbyParticipantList = ({ theme }: Props) => {
54 55
                     )
55 56
                 }
56 57
             </View>
57
-            <ScrollView>
58
-                {
59
-                    participants.map(p => (
60
-                        <LobbyParticipantItem
61
-                            key = { p.id }
62
-                            participant = { p } />)
63
-                    )
64
-                }
65
-            </ScrollView>
58
+            {
59
+                participants.map(p => (
60
+                    <LobbyParticipantItem
61
+                        key = { p.id }
62
+                        participant = { p } />)
63
+                )
64
+            }
66 65
         </View>
67 66
     );
68 67
 };

+ 4
- 1
react/features/participants-pane/components/native/MeetingParticipantList.js View File

@@ -134,7 +134,8 @@ class MeetingParticipantList extends PureComponent<Props> {
134 134
         } = this.props;
135 135
 
136 136
         return (
137
-            <View style = { styles.meetingList }>
137
+            <View
138
+                style = { styles.meetingListContainer }>
138 139
                 <Text style = { styles.meetingListDescription }>
139 140
                     {t('participantsPane.headings.participantsList',
140 141
                         { count: _participantsCount })}
@@ -155,7 +156,9 @@ class MeetingParticipantList extends PureComponent<Props> {
155 156
                     horizontal = { false }
156 157
                     keyExtractor = { this._keyExtractor }
157 158
                     renderItem = { this._renderParticipant }
159
+                    scrollEnabled = { false }
158 160
                     showsHorizontalScrollIndicator = { false }
161
+                    style = { styles.meetingList }
159 162
                     windowSize = { 2 } />
160 163
             </View>
161 164
         );

+ 9
- 12
react/features/participants-pane/components/native/ParticipantsPane.js View File

@@ -2,18 +2,17 @@
2 2
 
3 3
 import React, { useCallback } from 'react';
4 4
 import { useTranslation } from 'react-i18next';
5
-import { View } from 'react-native';
5
+import { ScrollView, View } from 'react-native';
6 6
 import { Button } from 'react-native-paper';
7 7
 import { useDispatch, useSelector } from 'react-redux';
8 8
 
9 9
 import { openDialog } from '../../../base/dialog';
10
-import { JitsiModal } from '../../../base/modal';
10
+import JitsiScreen from '../../../base/modal/components/JitsiScreen';
11 11
 import {
12 12
     isLocalParticipantModerator
13 13
 } from '../../../base/participants';
14 14
 import MuteEveryoneDialog
15 15
     from '../../../video-menu/components/native/MuteEveryoneDialog';
16
-import { close } from '../../actions.native';
17 16
 
18 17
 import { ContextMenuMore } from './ContextMenuMore';
19 18
 import HorizontalDotsIcon from './HorizontalDotsIcon';
@@ -29,21 +28,19 @@ import styles from './styles';
29 28
 const ParticipantsPane = () => {
30 29
     const dispatch = useDispatch();
31 30
     const openMoreMenu = useCallback(() => dispatch(openDialog(ContextMenuMore)), [ dispatch ]);
32
-    const closePane = useCallback(() => dispatch(close()), [ dispatch ]);
33 31
     const isLocalModerator = useSelector(isLocalParticipantModerator);
34 32
     const muteAll = useCallback(() => dispatch(openDialog(MuteEveryoneDialog)),
35 33
         [ dispatch ]);
36 34
     const { t } = useTranslation();
37 35
 
38 36
     return (
39
-        <JitsiModal
40
-            headerProps = {{
41
-                headerLabelKey: 'participantsPane.header'
42
-            }}
43
-            onClose = { closePane }
37
+        <JitsiScreen
38
+            hasTabNavigator = { false }
44 39
             style = { styles.participantsPane }>
45
-            <LobbyParticipantList />
46
-            <MeetingParticipantList />
40
+            <ScrollView bounces = { false }>
41
+                <LobbyParticipantList />
42
+                <MeetingParticipantList />
43
+            </ScrollView>
47 44
             {
48 45
                 isLocalModerator
49 46
                 && <View style = { styles.footer }>
@@ -61,7 +58,7 @@ const ParticipantsPane = () => {
61 58
                         style = { styles.moreButton } />
62 59
                 </View>
63 60
             }
64
-        </JitsiModal>
61
+        </JitsiScreen>
65 62
     );
66 63
 };
67 64
 

+ 4
- 4
react/features/participants-pane/components/native/ParticipantsPaneButton.js View File

@@ -6,7 +6,9 @@ import { translate } from '../../../base/i18n';
6 6
 import { IconParticipants } from '../../../base/icons';
7 7
 import { connect } from '../../../base/redux';
8 8
 import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
9
-import { open } from '../../actions.native';
9
+import { navigate }
10
+    from '../../../conference/components/native/ConferenceNavigationContainerRef';
11
+import { screen } from '../../../conference/components/native/routes';
10 12
 
11 13
 type Props = AbstractButtonProps & {
12 14
 
@@ -32,9 +34,7 @@ class ParticipantsPaneButton extends AbstractButton<Props, *> {
32 34
      * @returns {void}
33 35
      */
34 36
     _handleClick() {
35
-        const { dispatch } = this.props;
36
-
37
-        dispatch(open());
37
+        return navigate(screen.conference.participants);
38 38
     }
39 39
 }
40 40
 

+ 16
- 18
react/features/participants-pane/components/native/styles.js View File

@@ -165,11 +165,13 @@ export default {
165 165
     isLocal: {
166 166
         alignSelf: 'center',
167 167
         color: BaseTheme.palette.text01,
168
-        marginLeft: 4
168
+        marginLeft: BaseTheme.spacing[1]
169 169
     },
170 170
 
171 171
     participantsPane: {
172
-        backgroundColor: BaseTheme.palette.ui01
172
+        backgroundColor: BaseTheme.palette.ui01,
173
+        flex: 1,
174
+        justifyContent: 'center'
173 175
     },
174 176
 
175 177
     participantStatesContainer: {
@@ -196,13 +198,12 @@ export default {
196 198
         top: BaseTheme.spacing[1]
197 199
     },
198 200
 
199
-    lobbyList: {
201
+    lobbyListContainer: {
200 202
         position: 'relative'
201 203
     },
202 204
 
203
-    meetingList: {
204
-        position: 'relative',
205
-        marginTop: BaseTheme.spacing[3]
205
+    lobbyListDescription: {
206
+        ...participantListDescription
206 207
     },
207 208
 
208 209
     lobbyListDetails: {
@@ -216,8 +217,8 @@ export default {
216 217
         width: '100%'
217 218
     },
218 219
 
219
-    lobbyListDescription: {
220
-        ...participantListDescription
220
+    meetingListContainer: {
221
+        flex: 1
221 222
     },
222 223
 
223 224
     meetingListDescription: {
@@ -227,21 +228,18 @@ export default {
227 228
 
228 229
     footer: {
229 230
         alignItems: 'center',
230
-        backgroundColor: BaseTheme.palette.ui01,
231
-        bottom: BaseTheme.spacing[0],
232
-        display: 'flex',
233 231
         flexDirection: 'row',
234
-        height: BaseTheme.spacing[10],
235
-        justifyContent: 'space-between',
236
-        paddingRight: BaseTheme.spacing[3],
237
-        position: 'relative',
238
-        right: BaseTheme.spacing[0],
239
-        left: BaseTheme.spacing[0]
232
+        paddingHorizontal: BaseTheme.spacing[3],
233
+        paddingVertical: BaseTheme.spacing[2]
234
+    },
235
+
236
+    headerCloseIcon: {
237
+        marginLeft: 12
240 238
     },
241 239
 
242 240
     inviteButton: {
243 241
         backgroundColor: BaseTheme.palette.action01,
244
-        marginTop: BaseTheme.spacing[2],
242
+        marginBottom: BaseTheme.spacing[4],
245 243
         marginLeft: BaseTheme.spacing[3],
246 244
         marginRight: BaseTheme.spacing[3]
247 245
     },

+ 32
- 33
react/features/polls/components/native/PollCreate.js View File

@@ -141,39 +141,38 @@ const PollCreate = (props: AbstractProps) => {
141 141
                     keyExtractor = { (item, index) => index.toString() }
142 142
                     ref = { answerListRef }
143 143
                     renderItem = { renderListItem } />
144
-
145
-                <Button
146
-                    color = '#3D3D3D'
147
-                    mode = { BUTTON_MODES.CONTAINED }
148
-                    onPress = { () => {
149
-                        // adding and answer
150
-                        addAnswer();
151
-                        requestFocus(answers.length);
152
-                    } }
153
-                    style = { chatStyles.pollCreateAddButton }>
154
-                    {t('polls.create.addOption')}
155
-                </Button>
156
-            </View>
157
-
158
-            <View
159
-                style = { chatStyles.buttonRow }>
160
-
161
-                <Button
162
-                    color = '#3D3D3D'
163
-                    mode = { BUTTON_MODES.CONTAINED }
164
-                    onPress = { () => setCreateMode(false) }
165
-                    style = { chatStyles.pollCreateButton } >
166
-                    {t('polls.create.cancel')}
167
-                </Button>
168
-
169
-                <Button
170
-                    color = '#17a0db'
171
-                    disabled = { isSubmitDisabled }
172
-                    mode = { BUTTON_MODES.CONTAINED }
173
-                    onPress = { onSubmit }
174
-                    style = { chatStyles.pollCreateButton } >
175
-                    {t('polls.create.send')}
176
-                </Button>
144
+                <View style = { chatStyles.pollCreateButtons }>
145
+                    <Button
146
+                        color = '#3D3D3D'
147
+                        mode = { BUTTON_MODES.CONTAINED }
148
+                        onPress = { () => {
149
+                            // adding and answer
150
+                            addAnswer();
151
+                            requestFocus(answers.length);
152
+                        } }
153
+                        style = { chatStyles.pollCreateAddButton }>
154
+                        {t('polls.create.addOption')}
155
+                    </Button>
156
+                    <View
157
+                        style = { chatStyles.buttonRow }>
158
+                        <Button
159
+                            color = '#3D3D3D'
160
+                            mode = { BUTTON_MODES.CONTAINED }
161
+                            onPress = { () => setCreateMode(false) }
162
+                            style = { chatStyles.pollCreateButton } >
163
+                            {t('polls.create.cancel')}
164
+                        </Button>
165
+
166
+                        <Button
167
+                            color = '#17a0db'
168
+                            disabled = { isSubmitDisabled }
169
+                            mode = { BUTTON_MODES.CONTAINED }
170
+                            onPress = { onSubmit }
171
+                            style = { chatStyles.pollCreateButton } >
172
+                            {t('polls.create.send')}
173
+                        </Button>
174
+                    </View>
175
+                </View>
177 176
             </View>
178 177
         </View>
179 178
     );

+ 42
- 19
react/features/polls/components/native/PollsPane.js View File

@@ -1,11 +1,15 @@
1 1
 /* eslint-disable react-native/no-color-literals */
2 2
 // @flow
3 3
 
4
-import React from 'react';
5
-import { View } from 'react-native';
6
-import { Button } from 'react-native-paper';
4
+import { useNavigation, useIsFocused } from '@react-navigation/native';
5
+import React, { useEffect } from 'react';
6
+import { Button, useTheme } from 'react-native-paper';
7
+import { useSelector } from 'react-redux';
7 8
 
9
+import JitsiScreen from '../../../base/modal/components/JitsiScreen';
8 10
 import { BUTTON_MODES } from '../../../chat/constants';
11
+import { screen } from '../../../conference/components/native/routes';
12
+import { getUnreadPollCount } from '../../functions';
9 13
 import AbstractPollsPane from '../AbstractPollsPane';
10 14
 import type { AbstractProps } from '../AbstractPollsPane';
11 15
 
@@ -13,26 +17,45 @@ import PollCreate from './PollCreate';
13 17
 import PollsList from './PollsList';
14 18
 import { chatStyles } from './styles';
15 19
 
16
-const PollsPane = (props: AbstractProps) => {
17 20
 
21
+const PollsPane = (props: AbstractProps) => {
18 22
     const { createMode, onCreate, setCreateMode, t } = props;
23
+    const isPollsScreenFocused = useIsFocused();
24
+    const navigation = useNavigation();
25
+    const nbUnreadPolls = useSelector(getUnreadPollCount);
26
+    const { palette } = useTheme();
27
+
28
+    const nrUnreadPolls = !isPollsScreenFocused && nbUnreadPolls > 0
29
+        ? `(${nbUnreadPolls})`
30
+        : '';
31
+
32
+    useEffect(() => {
33
+        navigation.setOptions({
34
+            tabBarLabel: `${screen.conference.chatandpolls.tab.polls} ${nrUnreadPolls}`
35
+        });
36
+    }, [ nrUnreadPolls ]);
19 37
 
20 38
     return (
21
-        <View style = { chatStyles.PollPane }>
22
-            { createMode
23
-                ? <PollCreate setCreateMode = { setCreateMode } />
24
-                : <View style = { chatStyles.PollPaneContent }>
25
-                    {/* <View /> */}
26
-                    <PollsList />
27
-                    <Button
28
-                        color = '#17a0db'
29
-                        mode = { BUTTON_MODES.CONTAINED }
30
-                        onPress = { onCreate }
31
-                        style = { chatStyles.createPollButton } >
32
-                        {t('polls.create.create')}
33
-                    </Button>
34
-                </View>}
35
-        </View>
39
+        <JitsiScreen
40
+            contentContainerStyle = { chatStyles.PollPane }
41
+            hasTabNavigator = { true }
42
+            style = { chatStyles.PollPaneContainer }>
43
+            {
44
+                createMode
45
+                    ? <PollCreate setCreateMode = { setCreateMode } />
46
+                    : <PollsList />
47
+
48
+            }
49
+            {
50
+                !createMode && <Button
51
+                    color = { palette.screen01Header }
52
+                    mode = { BUTTON_MODES.CONTAINED }
53
+                    onPress = { onCreate }
54
+                    style = { chatStyles.createPollButton } >
55
+                    {t('polls.create.create')}
56
+                </Button>
57
+            }
58
+        </JitsiScreen>
36 59
     );
37 60
 };
38 61
 

+ 12
- 6
react/features/polls/components/native/styles.js View File

@@ -1,7 +1,7 @@
1 1
 // @flow
2 2
 
3
-import { schemeColor } from '../../../base/color-scheme';
4 3
 import { ColorPalette, createStyleSheet } from '../../../base/styles';
4
+import BaseTheme from '../../../base/ui/components/BaseTheme';
5 5
 
6 6
 export const answerStyles = createStyleSheet({
7 7
     question: {
@@ -110,6 +110,7 @@ export const resultsStyles = createStyleSheet({
110 110
 
111 111
 export const chatStyles = createStyleSheet({
112 112
     messageFooter: {
113
+        flex: 1,
113 114
         flexDirection: 'row',
114 115
         justifyContent: 'space-between',
115 116
         alignItems: 'center',
@@ -123,9 +124,9 @@ export const chatStyles = createStyleSheet({
123 124
 
124 125
     noPollText: {
125 126
         flex: 1,
126
-        color: schemeColor('displayName'),
127
+        color: BaseTheme.palette.ui05,
127 128
         textAlign: 'center',
128
-        paddingTop: '10%'
129
+        paddingTop: '8%'
129 130
     },
130 131
 
131 132
     pollItemContainer: {
@@ -165,17 +166,17 @@ export const chatStyles = createStyleSheet({
165 166
     },
166 167
 
167 168
     pollCreateAddButton: {
168
-        margin: 8
169
+        margin: BaseTheme.spacing[2]
169 170
     },
170 171
 
171 172
     toggleText: {
172 173
         color: ColorPalette.blue,
173
-        paddingTop: 16
174
+        paddingTop: BaseTheme.spacing[3]
174 175
     },
175 176
 
176 177
     createPollButton: {
177 178
         padding: 8,
178
-        margin: 4
179
+        margin: BaseTheme.spacing[2]
179 180
     },
180 181
 
181 182
     PollPane: {
@@ -183,8 +184,13 @@ export const chatStyles = createStyleSheet({
183 184
         padding: 8
184 185
     },
185 186
 
187
+    PollPaneContainer: {
188
+        flex: 1
189
+    },
190
+
186 191
     PollPaneContent: {
187 192
         justifyContent: 'space-between',
193
+        padding: BaseTheme.spacing[3],
188 194
         flex: 1
189 195
     },
190 196
 

+ 5
- 8
react/features/toolbox/components/native/Toolbox.js View File

@@ -44,12 +44,7 @@ type Props = {
44 44
     /**
45 45
      * Whether or not the reactions feature is enabled.
46 46
      */
47
-    _reactionsEnabled: boolean,
48
-
49
-    /**
50
-     * The redux {@code dispatch} function.
51
-     */
52
-    dispatch: Function
47
+    _reactionsEnabled: boolean
53 48
 };
54 49
 
55 50
 /**
@@ -88,10 +83,12 @@ function Toolbox(props: Props) {
88 83
                 <VideoMuteButton
89 84
                     styles = { buttonStylesBorderless }
90 85
                     toggledStyles = { toggledButtonStyles } />
91
-                { additionalButtons.has('chat')
86
+                {
87
+                    additionalButtons.has('chat')
92 88
                       && <ChatButton
93 89
                           styles = { buttonStylesBorderless }
94
-                          toggledStyles = { backgroundToggledStyle } />}
90
+                          toggledStyles = { backgroundToggledStyle } />
91
+                }
95 92
 
96 93
                 { additionalButtons.has('raisehand') && (_reactionsEnabled
97 94
                     ? <ReactionsMenuButton

Loading…
Cancel
Save