feat(conference) added React Navigation

Introduce navigation for all in-conference screens.
This commit is contained in:
Calinteodor 2021-10-20 22:29:21 +03:00 committed by GitHub
parent a5129ef291
commit 9df59b4a6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 1712 additions and 605 deletions

View File

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

View File

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

View File

@ -70,9 +70,14 @@ dependencies {
implementation project(':react-native-calendar-events')
implementation project(':react-native-community_netinfo')
implementation project(':react-native-default-preference')
implementation project(':react-native-gesture-handler')
implementation project(':react-native-immersive')
implementation project(':react-native-keep-awake')
implementation project(':react-native-masked-view_masked-view')
implementation project(':react-native-performance')
implementation project(':react-native-reanimated')
implementation project(':react-native-safe-area-context')
implementation project(':react-native-screens')
implementation project(':react-native-slider')
implementation project(':react-native-sound')
implementation project(':react-native-splash-screen')

View File

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

View File

@ -175,21 +175,26 @@ class ReactInstanceManagerHolder {
List<ReactPackage> packages
= new ArrayList<>(Arrays.asList(
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
new com.ocetnik.timer.BackgroundTimerPackage(),
new com.calendarevents.CalendarEventsPackage(),
new com.corbt.keepawake.KCKeepAwakePackage(),
new com.facebook.react.shell.MainReactPackage(),
new com.horcrux.svg.SvgPackage(),
new com.reactnativecommunity.netinfo.NetInfoPackage(),
new com.oblador.performance.PerformancePackage(),
new com.reactnativecommunity.slider.ReactSliderPackage(),
new com.brentvatne.react.ReactVideoPackage(),
new com.swmansion.reanimated.ReanimatedPackage(),
new org.reactnative.maskedview.RNCMaskedViewPackage(),
new com.reactnativecommunity.webview.RNCWebViewPackage(),
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
new com.learnium.RNDeviceInfo.RNDeviceInfo(),
new com.oblador.performance.PerformancePackage(),
new com.ocetnik.timer.BackgroundTimerPackage(),
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
new com.reactnativecommunity.netinfo.NetInfoPackage(),
new com.reactnativecommunity.slider.ReactSliderPackage(),
new com.reactnativecommunity.webview.RNCWebViewPackage(),
new com.swmansion.gesturehandler.react.RNGestureHandlerPackage(),
new com.rnimmersive.RNImmersivePackage(),
new com.swmansion.rnscreens.RNScreensPackage(),
new com.zmxv.RNSound.RNSoundPackage(),
new com.brentvatne.react.ReactVideoPackage(),
new com.th3rdwave.safeareacontext.SafeAreaContextPackage(),
new com.horcrux.svg.SvgPackage(),
new ReactPackageAdapter() {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

View File

@ -15,14 +15,24 @@ include ':react-native-default-preference'
project(':react-native-default-preference').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-default-preference/android')
include ':react-native-device-info'
project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android')
include ':react-native-gesture-handler'
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
include ':react-native-google-signin'
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/google-signin/android')
include ':react-native-immersive'
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
include ':react-native-keep-awake'
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
include ':react-native-masked-view_masked-view'
project(':react-native-masked-view_masked-view').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-masked-view/masked-view/android')
include ':react-native-performance'
project(':react-native-performance').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-performance/android')
include ':react-native-reanimated'
project(':react-native-reanimated').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-reanimated/android')
include ':react-native-safe-area-context'
project(':react-native-safe-area-context').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-safe-area-context/android')
include ':react-native-screens'
project(':react-native-screens').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-screens/android')
include ':react-native-slider'
project(':react-native-slider').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/slider/android')
include ':react-native-sound'

View File

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

View File

@ -109,7 +109,7 @@ PODS:
- GTMAppAuth (1.2.2):
- AppAuth/Core (~> 1.4)
- GTMSessionFetcher/Core (~> 1.5)
- GTMSessionFetcher/Core (1.6.1)
- GTMSessionFetcher/Core (1.7.0)
- nanopb (1.30906.0):
- nanopb/decode (= 1.30906.0)
- nanopb/encode (= 1.30906.0)
@ -290,6 +290,8 @@ PODS:
- React
- react-native-performance (2.0.0):
- React-Core
- react-native-safe-area-context (3.3.2):
- React-Core
- react-native-slider (3.0.3):
- React
- react-native-splash-screen (3.2.0):
@ -359,13 +361,21 @@ PODS:
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.2)
- RNCAsyncStorage (1.15.5):
- React-Core
- RNCMaskedView (0.2.6):
- React-Core
- RNDefaultPreference (1.4.2):
- React
- RNDeviceInfo (8.0.0):
- React-Core
- RNGestureHandler (1.10.3):
- React-Core
- RNGoogleSignin (3.0.1):
- GoogleSignIn (~> 5.0.0)
- React
- RNReanimated (1.13.3):
- React-Core
- RNScreens (2.18.1):
- React-Core
- RNSound (0.11.0):
- React
- RNSound/Core (= 0.11.0)
@ -405,6 +415,7 @@ DEPENDENCIES:
- react-native-keep-awake (from `../node_modules/react-native-keep-awake`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-performance (from `../node_modules/react-native-performance/ios`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
- react-native-video (from `../node_modules/react-native-video/react-native-video.podspec`)
@ -421,9 +432,13 @@ DEPENDENCIES:
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- ReactCommon/turbomodule (from `../node_modules/react-native/ReactCommon`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- "RNCMaskedView (from `../node_modules/@react-native-masked-view/masked-view`)"
- RNDefaultPreference (from `../node_modules/react-native-default-preference`)
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- "RNGoogleSignin (from `../node_modules/@react-native-community/google-signin`)"
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNSound (from `../node_modules/react-native-sound`)
- RNSVG (from `../node_modules/react-native-svg`)
- RNWatch (from `../node_modules/react-native-watch-connectivity`)
@ -493,6 +508,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-community/netinfo"
react-native-performance:
:path: "../node_modules/react-native-performance/ios"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-slider:
:path: "../node_modules/@react-native-community/slider"
react-native-splash-screen:
@ -525,12 +542,20 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
RNCAsyncStorage:
:path: "../node_modules/@react-native-async-storage/async-storage"
RNCMaskedView:
:path: "../node_modules/@react-native-masked-view/masked-view"
RNDefaultPreference:
:path: "../node_modules/react-native-default-preference"
RNDeviceInfo:
:path: "../node_modules/react-native-device-info"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNGoogleSignin:
:path: "../node_modules/@react-native-community/google-signin"
RNReanimated:
:path: "../node_modules/react-native-reanimated"
RNScreens:
:path: "../node_modules/react-native-screens"
RNSound:
:path: "../node_modules/react-native-sound"
RNSVG:
@ -563,7 +588,7 @@ SPEC CHECKSUMS:
GoogleSignIn: 7137d297ddc022a7e0aa4619c86d72c909fa7213
GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89
GTMSessionFetcher: 36689134877faeb055b27dfa4ccc9ceaa42e029e
GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
ObjectiveDropboxOfficial: b4765572e334d6fc6214b43a7595510324bbbbaa
PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
@ -581,6 +606,7 @@ SPEC CHECKSUMS:
react-native-keep-awake: afad8a51dfef9fe9655a6344771be32c8596d774
react-native-netinfo: 0e563248a4b9a99c33ec29bd03c2d50767db22a6
react-native-performance: 6bd6cfac80594775fb782405fceaaf206becf53b
react-native-safe-area-context: 584dc04881deb49474363f3be89e4ca0e854c057
react-native-slider: e99fc201cefe81270fc9d81714a7a0f5e566b168
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
react-native-video: 0bb76b6d6b77da3009611586c7dbf817b947f30e
@ -597,14 +623,18 @@ SPEC CHECKSUMS:
React-RCTVibration: c1041024893fdfdb8371e7c720c437751b711676
ReactCommon: 18014e1d98dbeb9141e935cfe35fc93bd511ffb6
RNCAsyncStorage: 56a3355a10b5d660c48c6e37325ac85ebfd09885
RNCMaskedView: c298b644a10c0c142055b3ae24d83879ecb13ccd
RNDefaultPreference: 1f8133ec0bc0f9453cdada578564ba1ef551fb44
RNDeviceInfo: 87d2d175c760f6bcf58acd036f887e8b2392802c
RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211
RNGoogleSignin: 39336070b35fc4cea6a98cf111e00480317be0ae
RNReanimated: 514a11da3a2bcc6c3dfd9de32b38e2b9bf101926
RNScreens: f7ad633b2e0190b77b6a7aab7f914fad6f198d8d
RNSound: da030221e6ac7e8290c6b43f2b5f2133a8e225b0
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
RNWatch: a5320c959c75e72845c07985f3e935e58998f1d3
Yoga: 96b469c5e81ff51b917b92e8c3390642d4ded30c
PODFILE CHECKSUM: 3cc305fd6ee83fff506c10c4805471fa72b61c9a
PODFILE CHECKSUM: 42be6796ba6ac039dae5c02125677728ecd0df0d
COCOAPODS: 1.10.1

610
package-lock.json generated
View File

@ -37,6 +37,10 @@
"@react-native-community/google-signin": "3.0.1",
"@react-native-community/netinfo": "4.1.5",
"@react-native-community/slider": "3.0.3",
"@react-native-masked-view/masked-view": "0.2.6",
"@react-navigation/material-top-tabs": "5.3.19",
"@react-navigation/native": "5.9.8",
"@react-navigation/stack": "5.14.9",
"@svgr/webpack": "4.3.2",
"amplitude-js": "8.2.1",
"base64-js": "1.3.1",
@ -76,14 +80,19 @@
"react-native-collapsible": "1.5.1",
"react-native-default-preference": "1.4.2",
"react-native-device-info": "8.0.0",
"react-native-gesture-handler": "1.10.3",
"react-native-immersive": "2.0.0",
"react-native-keep-awake": "4.0.0",
"react-native-paper": "4.8.1",
"react-native-performance": "2.0.0",
"react-native-reanimated": "1.13.3",
"react-native-safe-area-context": "3.3.2",
"react-native-screens": "2.18.1",
"react-native-sound": "github:jitsi/react-native-sound#3fe5480fce935e888d5089d94a191c7c7e3aa190",
"react-native-splash-screen": "3.2.0",
"react-native-svg": "12.1.0",
"react-native-svg-transformer": "0.14.3",
"react-native-tab-view": "2.16.0",
"react-native-url-polyfill": "1.2.0",
"react-native-video": "5.1.1",
"react-native-watch-connectivity": "0.4.3",
@ -2929,6 +2938,17 @@
"node": ">=10.0.0"
}
},
"node_modules/@egjs/hammerjs": {
"version": "2.0.17",
"resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
"integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
"dependencies": {
"@types/hammerjs": "^2.0.36"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@emotion/cache": {
"version": "10.0.29",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz",
@ -4036,6 +4056,133 @@
"react-native": "*"
}
},
"node_modules/@react-native-masked-view/masked-view": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/@react-native-masked-view/masked-view/-/masked-view-0.2.6.tgz",
"integrity": "sha512-303CxmetUmgiX9NSUxatZkNh9qTYYdiM8xkGf9I3Uj20U3eGY3M78ljeNQ4UVCJA+FNGS5nC1dtS9GjIqvB4dg==",
"peerDependencies": {
"react": "16 || 17",
"react-native": ">=0.57"
}
},
"node_modules/@react-navigation/core": {
"version": "5.16.1",
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.16.1.tgz",
"integrity": "sha512-3AToC7vPNeSNcHFLd1h71L6u34hfXoRAS1CxF9Fc4uC8uOrVqcNvphpeFbE0O9Bw6Zpl0BnMFl7E5gaL3KGzNA==",
"dependencies": {
"@react-navigation/routers": "^5.7.4",
"escape-string-regexp": "^4.0.0",
"nanoid": "^3.1.15",
"query-string": "^6.13.6",
"react-is": "^16.13.0"
},
"peerDependencies": {
"react": "*"
}
},
"node_modules/@react-navigation/core/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@react-navigation/core/node_modules/query-string": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
"integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
"dependencies": {
"decode-uri-component": "^0.2.0",
"filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@react-navigation/core/node_modules/strict-uri-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=",
"engines": {
"node": ">=4"
}
},
"node_modules/@react-navigation/material-top-tabs": {
"version": "5.3.19",
"resolved": "https://registry.npmjs.org/@react-navigation/material-top-tabs/-/material-top-tabs-5.3.19.tgz",
"integrity": "sha512-I7bEF99THxxcY7kCUZ5pPmwXr6kgo6L2sg3P1YJo+CcBWSGvGiHyNbZXNs15HuKRuFvEuueChNV9n8QuKBWbDA==",
"dependencies": {
"color": "^3.1.3"
},
"peerDependencies": {
"@react-navigation/native": "^5.0.5",
"react": "*",
"react-native": "*",
"react-native-gesture-handler": ">= 1.0.0",
"react-native-reanimated": ">= 1.0.0",
"react-native-tab-view": ">= 2.0.0"
}
},
"node_modules/@react-navigation/native": {
"version": "5.9.8",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.9.8.tgz",
"integrity": "sha512-DNbcDHXQPSFDLn51kkVVJjT3V7jJy2GztNYZe/2bEg29mi5QEcHHcpifjMCtyFKntAOWzKlG88UicIQ17UEghg==",
"dependencies": {
"@react-navigation/core": "^5.16.1",
"escape-string-regexp": "^4.0.0",
"nanoid": "^3.1.15"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/@react-navigation/native/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@react-navigation/routers": {
"version": "5.7.4",
"resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-5.7.4.tgz",
"integrity": "sha512-0N202XAqsU/FlE53Nmh6GHyMtGm7g6TeC93mrFAFJOqGRKznT0/ail+cYlU6tNcPA9AHzZu1Modw1eoDINSliQ==",
"dependencies": {
"nanoid": "^3.1.15"
}
},
"node_modules/@react-navigation/stack": {
"version": "5.14.9",
"resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-5.14.9.tgz",
"integrity": "sha512-DuvrT9P+Tz8ezZLQYxORZqOGqO+vEufaxlW1hSLw1knLD4jNxkz8TJDXtfKwaz//9gb43UhTNccNM02vm7iPqQ==",
"dependencies": {
"color": "^3.1.3",
"react-native-iphone-x-helper": "^1.3.0"
},
"peerDependencies": {
"@react-native-community/masked-view": ">= 0.1.0",
"@react-navigation/native": "^5.0.5",
"react": "*",
"react-native": "*",
"react-native-gesture-handler": ">= 1.0.0",
"react-native-safe-area-context": ">= 0.6.0",
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
}
},
"node_modules/@svgr/babel-plugin-add-jsx-attribute": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
@ -5045,6 +5192,11 @@
"integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==",
"dev": true
},
"node_modules/@types/hammerjs": {
"version": "2.0.40",
"resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.40.tgz",
"integrity": "sha512-VbjwR1fhsn2h2KXAY4oy1fm7dCxaKy0D+deTb8Ilc3Eo3rc5+5eA4rfYmZaHgNJKxVyI0f6WIXzO2zLkVmQPHA=="
},
"node_modules/@types/http-proxy": {
"version": "1.17.7",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz",
@ -7651,6 +7803,22 @@
"object-assign": "^4.1.1"
}
},
"node_modules/cross-fetch": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz",
"integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==",
"dependencies": {
"node-fetch": "2.6.1"
}
},
"node_modules/cross-fetch/node_modules/node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/cross-os": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/cross-os/-/cross-os-1.4.0.tgz",
@ -10014,6 +10182,14 @@
"node": ">=0.10.0"
}
},
"node_modules/filter-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
"integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@ -10582,12 +10758,12 @@
}
},
"node_modules/fsevents/node_modules/mkdirp": {
"version": "0.5.5",
"version": "0.5.1",
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"minimist": "^1.2.5"
"minimist": "0.0.8"
},
"bin": {
"mkdirp": "bin/cmd.js"
@ -10764,7 +10940,7 @@
}
},
"node_modules/fsevents/node_modules/rc/node_modules/minimist": {
"version": "1.2.5",
"version": "1.2.0",
"inBundle": true,
"license": "MIT",
"optional": true
@ -11812,9 +11988,9 @@
}
},
"node_modules/invariant": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.3.tgz",
"integrity": "sha512-7Z5PPegwDTyjbaeCnV0efcyS6vdKAU51kpEmS7QFib3P4822l8ICYyMn7qvJnc+WzLoDsuI9gPMKbJ8pCu8XtA==",
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
@ -12382,14 +12558,6 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
},
"node_modules/jest-haste-map/node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/jest-message-util": {
"version": "24.9.0",
"resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz",
@ -13641,14 +13809,6 @@
"vlq": "^1.0.0"
}
},
"node_modules/metro-source-map/node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/metro-symbolicate": {
"version": "0.56.4",
"resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.56.4.tgz",
@ -13667,14 +13827,6 @@
"node": ">=8.3"
}
},
"node_modules/metro-symbolicate/node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/metro/node_modules/@babel/helper-plugin-utils": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
@ -13759,14 +13911,6 @@
"klaw": "^1.0.0"
}
},
"node_modules/metro/node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/metro/node_modules/jsonfile": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
@ -14124,6 +14268,17 @@
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
"optional": true
},
"node_modules/nanoid": {
"version": "3.1.30",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz",
"integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/nanomatch": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@ -16558,6 +16713,32 @@
"react-native": "*"
}
},
"node_modules/react-native-gesture-handler": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
"integrity": "sha512-cBGMi1IEsIVMgoox4RvMx7V2r6bNKw0uR1Mu1o7NbuHS6BRSVLq0dP34l2ecnPlC+jpWd3le6Yg1nrdCjby2Mw==",
"dependencies": {
"@egjs/hammerjs": "^2.0.17",
"fbjs": "^3.0.0",
"hoist-non-react-statics": "^3.3.0",
"invariant": "^2.2.4",
"prop-types": "^15.7.2"
}
},
"node_modules/react-native-gesture-handler/node_modules/fbjs": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz",
"integrity": "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==",
"dependencies": {
"cross-fetch": "^3.0.4",
"fbjs-css-vars": "^1.0.0",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
},
"node_modules/react-native-immersive": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-native-immersive/-/react-native-immersive-2.0.0.tgz",
@ -16602,6 +16783,51 @@
"react-native": "*"
}
},
"node_modules/react-native-reanimated": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-1.13.3.tgz",
"integrity": "sha512-i714H24dv6ncpFO7/SZ0PfAMbvjgVbF8Ow2NPtowoZAz8osS54DmTMrkgJ9Za+uEku/s0AEaxqiXG2Xgntvv2g==",
"dependencies": {
"fbjs": "^1.0.0"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-reanimated/node_modules/fbjs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-1.0.0.tgz",
"integrity": "sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA==",
"dependencies": {
"core-js": "^2.4.1",
"fbjs-css-vars": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
},
"node_modules/react-native-safe-area-context": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz",
"integrity": "sha512-yOwiiPJ1rk+/nfK13eafbpW6sKW0jOnsRem2C1LPJjM3tfTof6hlvV5eWHATye3XOpu2cJ7N+HdkUvUDGwFD2Q==",
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-screens": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-2.18.1.tgz",
"integrity": "sha512-r5WZLpmx2hHjC1RgMdPq5YpSU9tEhBpUaZ5M1SUtNIONyiLqQVxabhRCINdebIk4depJiIl7yw2Q85zJyeX6fw==",
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-sound": {
"version": "0.11.0",
"resolved": "git+ssh://git@github.com/jitsi/react-native-sound.git#3fe5480fce935e888d5089d94a191c7c7e3aa190",
@ -16734,6 +16960,17 @@
"semver": "bin/semver"
}
},
"node_modules/react-native-tab-view": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-2.16.0.tgz",
"integrity": "sha512-ac2DmT7+l13wzIFqtbfXn4wwfgtPoKzWjjZyrK1t+T8sdemuUvD4zIt+UImg03fu3s3VD8Wh/fBrIdcqQyZJWg==",
"peerDependencies": {
"react": "*",
"react-native": "*",
"react-native-gesture-handler": "*",
"react-native-reanimated": "*"
}
},
"node_modules/react-native-url-polyfill": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-1.2.0.tgz",
@ -16809,14 +17046,6 @@
"node": ">=8"
}
},
"node_modules/react-native-webview/node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/react-native-youtube-iframe": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-native-youtube-iframe/-/react-native-youtube-iframe-2.1.1.tgz",
@ -16854,14 +17083,6 @@
"ua-parser-js": "^0.7.18"
}
},
"node_modules/react-native/node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/react-native/node_modules/whatwg-fetch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
@ -16902,14 +17123,6 @@
"redux": "^2.0.0 || ^3.0.0 || ^4.0.0-0"
}
},
"node_modules/react-redux/node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/react-redux/node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -18275,6 +18488,14 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/split-on-first": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
"engines": {
"node": ">=6"
}
},
"node_modules/split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@ -24126,6 +24347,14 @@
"integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==",
"dev": true
},
"@egjs/hammerjs": {
"version": "2.0.17",
"resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
"integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
"requires": {
"@types/hammerjs": "^2.0.36"
}
},
"@emotion/cache": {
"version": "10.0.29",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz",
@ -24988,6 +25217,88 @@
"resolved": "https://registry.npmjs.org/@react-native-community/slider/-/slider-3.0.3.tgz",
"integrity": "sha512-8IeHfDwJ9/CTUwFs6x90VlobV3BfuPgNLjTgC6dRZovfCWigaZwVNIFFJnHBakK3pW2xErAPwhdvNR4JeNoYbw=="
},
"@react-native-masked-view/masked-view": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/@react-native-masked-view/masked-view/-/masked-view-0.2.6.tgz",
"integrity": "sha512-303CxmetUmgiX9NSUxatZkNh9qTYYdiM8xkGf9I3Uj20U3eGY3M78ljeNQ4UVCJA+FNGS5nC1dtS9GjIqvB4dg=="
},
"@react-navigation/core": {
"version": "5.16.1",
"resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.16.1.tgz",
"integrity": "sha512-3AToC7vPNeSNcHFLd1h71L6u34hfXoRAS1CxF9Fc4uC8uOrVqcNvphpeFbE0O9Bw6Zpl0BnMFl7E5gaL3KGzNA==",
"requires": {
"@react-navigation/routers": "^5.7.4",
"escape-string-regexp": "^4.0.0",
"nanoid": "^3.1.15",
"query-string": "^6.13.6",
"react-is": "^16.13.0"
},
"dependencies": {
"escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
},
"query-string": {
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
"integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
"requires": {
"decode-uri-component": "^0.2.0",
"filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
}
},
"strict-uri-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
}
}
},
"@react-navigation/material-top-tabs": {
"version": "5.3.19",
"resolved": "https://registry.npmjs.org/@react-navigation/material-top-tabs/-/material-top-tabs-5.3.19.tgz",
"integrity": "sha512-I7bEF99THxxcY7kCUZ5pPmwXr6kgo6L2sg3P1YJo+CcBWSGvGiHyNbZXNs15HuKRuFvEuueChNV9n8QuKBWbDA==",
"requires": {
"color": "^3.1.3"
}
},
"@react-navigation/native": {
"version": "5.9.8",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.9.8.tgz",
"integrity": "sha512-DNbcDHXQPSFDLn51kkVVJjT3V7jJy2GztNYZe/2bEg29mi5QEcHHcpifjMCtyFKntAOWzKlG88UicIQ17UEghg==",
"requires": {
"@react-navigation/core": "^5.16.1",
"escape-string-regexp": "^4.0.0",
"nanoid": "^3.1.15"
},
"dependencies": {
"escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
}
}
},
"@react-navigation/routers": {
"version": "5.7.4",
"resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-5.7.4.tgz",
"integrity": "sha512-0N202XAqsU/FlE53Nmh6GHyMtGm7g6TeC93mrFAFJOqGRKznT0/ail+cYlU6tNcPA9AHzZu1Modw1eoDINSliQ==",
"requires": {
"nanoid": "^3.1.15"
}
},
"@react-navigation/stack": {
"version": "5.14.9",
"resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-5.14.9.tgz",
"integrity": "sha512-DuvrT9P+Tz8ezZLQYxORZqOGqO+vEufaxlW1hSLw1knLD4jNxkz8TJDXtfKwaz//9gb43UhTNccNM02vm7iPqQ==",
"requires": {
"color": "^3.1.3",
"react-native-iphone-x-helper": "^1.3.0"
}
},
"@svgr/babel-plugin-add-jsx-attribute": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
@ -25801,6 +26112,11 @@
"integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==",
"dev": true
},
"@types/hammerjs": {
"version": "2.0.40",
"resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.40.tgz",
"integrity": "sha512-VbjwR1fhsn2h2KXAY4oy1fm7dCxaKy0D+deTb8Ilc3Eo3rc5+5eA4rfYmZaHgNJKxVyI0f6WIXzO2zLkVmQPHA=="
},
"@types/http-proxy": {
"version": "1.17.7",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.7.tgz",
@ -27871,6 +28187,21 @@
"object-assign": "^4.1.1"
}
},
"cross-fetch": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.4.tgz",
"integrity": "sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==",
"requires": {
"node-fetch": "2.6.1"
},
"dependencies": {
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
}
}
},
"cross-os": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/cross-os/-/cross-os-1.4.0.tgz",
@ -29761,6 +30092,11 @@
}
}
},
"filter-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
"integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs="
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@ -30184,11 +30520,11 @@
}
},
"mkdirp": {
"version": "0.5.5",
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
"minimist": "0.0.8"
}
},
"ms": {
@ -30316,7 +30652,7 @@
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"version": "1.2.0",
"bundled": true,
"optional": true
}
@ -31156,9 +31492,9 @@
"dev": true
},
"invariant": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.3.tgz",
"integrity": "sha512-7Z5PPegwDTyjbaeCnV0efcyS6vdKAU51kpEmS7QFib3P4822l8ICYyMn7qvJnc+WzLoDsuI9gPMKbJ8pCu8XtA==",
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
@ -31559,14 +31895,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
"integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ=="
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
@ -32390,14 +32718,6 @@
"klaw": "^1.0.0"
}
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"jsonfile": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
@ -32840,16 +33160,6 @@
"ob1": "^0.56.4",
"source-map": "^0.5.6",
"vlq": "^1.0.0"
},
"dependencies": {
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
"metro-symbolicate": {
@ -32862,16 +33172,6 @@
"source-map": "^0.5.6",
"through2": "^2.0.1",
"vlq": "^1.0.0"
},
"dependencies": {
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
"micromatch": {
@ -33046,6 +33346,11 @@
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
"optional": true
},
"nanoid": {
"version": "3.1.30",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.30.tgz",
"integrity": "sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ=="
},
"nanomatch": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@ -34896,14 +35201,6 @@
"ua-parser-js": "^0.7.18"
}
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"whatwg-fetch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
@ -34948,6 +35245,34 @@
"resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-8.0.0.tgz",
"integrity": "sha512-7/DOEhg8GtyW1hpVtWf8F6RvGLaFaOGmex+IkmiBWQC2uW4NFDcfXm+lMMZnduFavTyUTX7AF6lAM3y286cEfA=="
},
"react-native-gesture-handler": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
"integrity": "sha512-cBGMi1IEsIVMgoox4RvMx7V2r6bNKw0uR1Mu1o7NbuHS6BRSVLq0dP34l2ecnPlC+jpWd3le6Yg1nrdCjby2Mw==",
"requires": {
"@egjs/hammerjs": "^2.0.17",
"fbjs": "^3.0.0",
"hoist-non-react-statics": "^3.3.0",
"invariant": "^2.2.4",
"prop-types": "^15.7.2"
},
"dependencies": {
"fbjs": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.0.tgz",
"integrity": "sha512-dJd4PiDOFuhe7vk4F80Mba83Vr2QuK86FoxtgPmzBqEJahncp+13YCmfoa53KHCo6OnlXLG7eeMWPfB5CrpVKg==",
"requires": {
"cross-fetch": "^3.0.4",
"fbjs-css-vars": "^1.0.0",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
}
}
},
"react-native-immersive": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-native-immersive/-/react-native-immersive-2.0.0.tgz",
@ -34978,6 +35303,41 @@
"resolved": "https://registry.npmjs.org/react-native-performance/-/react-native-performance-2.0.0.tgz",
"integrity": "sha512-jKM9Qg0SkL9D9ad377nxb1VV+OXJSyYyIrBHKmM6CABNxfrLVA5xkQMEibjmZQde7b0ndJOZoQAiObgJjjc4VQ=="
},
"react-native-reanimated": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-1.13.3.tgz",
"integrity": "sha512-i714H24dv6ncpFO7/SZ0PfAMbvjgVbF8Ow2NPtowoZAz8osS54DmTMrkgJ9Za+uEku/s0AEaxqiXG2Xgntvv2g==",
"requires": {
"fbjs": "^1.0.0"
},
"dependencies": {
"fbjs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-1.0.0.tgz",
"integrity": "sha512-MUgcMEJaFhCaF1QtWGnmq9ZDRAzECTCRAF7O6UZIlAlkTs1SasiX9aP0Iw7wfD2mJ7wDTNfg2w7u5fSCwJk1OA==",
"requires": {
"core-js": "^2.4.1",
"fbjs-css-vars": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.18"
}
}
}
},
"react-native-safe-area-context": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz",
"integrity": "sha512-yOwiiPJ1rk+/nfK13eafbpW6sKW0jOnsRem2C1LPJjM3tfTof6hlvV5eWHATye3XOpu2cJ7N+HdkUvUDGwFD2Q=="
},
"react-native-screens": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-2.18.1.tgz",
"integrity": "sha512-r5WZLpmx2hHjC1RgMdPq5YpSU9tEhBpUaZ5M1SUtNIONyiLqQVxabhRCINdebIk4depJiIl7yw2Q85zJyeX6fw=="
},
"react-native-sound": {
"version": "git+ssh://git@github.com/jitsi/react-native-sound.git#3fe5480fce935e888d5089d94a191c7c7e3aa190",
"integrity": "sha512-364A1CvMgh5MnzI4iJgg+AqpePO63Jmf1ESvkTlW+VK3S513fM3092+5mupmGO8KIP77PuYpuNjTYpjZukbgkw==",
@ -35076,6 +35436,11 @@
}
}
},
"react-native-tab-view": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-2.16.0.tgz",
"integrity": "sha512-ac2DmT7+l13wzIFqtbfXn4wwfgtPoKzWjjZyrK1t+T8sdemuUvD4zIt+UImg03fu3s3VD8Wh/fBrIdcqQyZJWg=="
},
"react-native-url-polyfill": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-1.2.0.tgz",
@ -35131,14 +35496,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
"integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
}
}
},
@ -35177,14 +35534,6 @@
"react-is": "^16.8.6"
},
"dependencies": {
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -36283,6 +36632,11 @@
}
}
},
"split-on-first": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
},
"split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",

View File

@ -42,6 +42,10 @@
"@react-native-community/google-signin": "3.0.1",
"@react-native-community/netinfo": "4.1.5",
"@react-native-community/slider": "3.0.3",
"@react-native-masked-view/masked-view": "0.2.6",
"@react-navigation/material-top-tabs": "5.3.19",
"@react-navigation/native": "5.9.8",
"@react-navigation/stack": "5.14.9",
"@svgr/webpack": "4.3.2",
"amplitude-js": "8.2.1",
"base64-js": "1.3.1",
@ -81,14 +85,19 @@
"react-native-collapsible": "1.5.1",
"react-native-default-preference": "1.4.2",
"react-native-device-info": "8.0.0",
"react-native-gesture-handler": "1.10.3",
"react-native-immersive": "2.0.0",
"react-native-keep-awake": "4.0.0",
"react-native-paper": "4.8.1",
"react-native-performance": "2.0.0",
"react-native-reanimated": "1.13.3",
"react-native-safe-area-context": "3.3.2",
"react-native-screens": "2.18.1",
"react-native-sound": "github:jitsi/react-native-sound#3fe5480fce935e888d5089d94a191c7c7e3aa190",
"react-native-splash-screen": "3.2.0",
"react-native-svg": "12.1.0",
"react-native-svg-transformer": "0.14.3",
"react-native-tab-view": "2.16.0",
"react-native-url-polyfill": "1.2.0",
"react-native-video": "5.1.1",
"react-native-watch-connectivity": "0.4.3",

View File

@ -0,0 +1,52 @@
import { isRoomValid } from '../base/conference';
import { toState } from '../base/redux';
import { ConferenceNavigationContainer } from '../conference';
import { isWelcomePageAppEnabled } from '../welcome';
import { BlankPage, WelcomePage } from '../welcome/components';
/**
* Determines which route is to be rendered in order to depict a specific Redux
* store.
*
* @param {(Function|Object)} stateful - THe redux store, state, or
* {@code getState} function.
* @returns {Promise<Object>}
*/
export function _getRouteToRender(stateful) {
const state = toState(stateful);
return _getMobileRoute(state);
}
/**
* Returns the {@code Route} to display on the React Native app.
*
* @param {Object} state - The redux state.
* @returns {Promise}
*/
function _getMobileRoute(state) {
const route = _getEmptyRoute();
if (isRoomValid(state['features/base/conference'].room)) {
route.component = ConferenceNavigationContainer;
} else if (isWelcomePageAppEnabled(state)) {
route.component = WelcomePage;
} else {
route.component = BlankPage;
}
return Promise.resolve(route);
}
/**
* Returns the default {@code Route}.
*
* @returns {Object}
*/
function _getEmptyRoute() {
return {
component: BlankPage,
href: undefined
};
}

View File

@ -1,7 +1,5 @@
// @flow
import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random';
import type { Component } from 'react';
import { isRoomValid } from '../base/conference';
import { isSupportedBrowser } from '../base/environment';
@ -9,25 +7,9 @@ import { toState } from '../base/redux';
import { Conference } from '../conference';
import { getDeepLinkingPage } from '../deep-linking';
import { UnsupportedDesktopBrowser } from '../unsupported-browser';
import {
BlankPage,
WelcomePage,
isWelcomePageAppEnabled,
isWelcomePageUserEnabled
} from '../welcome';
import { isWelcomePageUserEnabled } from '../welcome';
import { BlankPage, WelcomePage } from '../welcome/components';
/**
* Object describing application route.
*
* @typedef {Object} Route
* @property {Component} component - React Component constructor.
* @property {string|undefined} href - New location, in case navigation involves
* a location change.
*/
export type Route = {
component: Class<Component<*>>,
href: ?string
};
/**
* Determines which route is to be rendered in order to depict a specific Redux
@ -35,46 +17,22 @@ export type Route = {
*
* @param {(Function|Object)} stateful - THe redux store, state, or
* {@code getState} function.
* @returns {Promise<Route>}
* @returns {Promise<Object>}
*/
export function _getRouteToRender(stateful: Function | Object): Promise<Route> {
export function _getRouteToRender(stateful) {
const state = toState(stateful);
if (navigator.product === 'ReactNative') {
return _getMobileRoute(state);
}
return _getWebConferenceRoute(state) || _getWebWelcomePageRoute(state);
}
/**
* Returns the {@code Route} to display on the React Native app.
*
* @param {Object} state - The redux state.
* @returns {Promise<Route>}
*/
function _getMobileRoute(state): Promise<Route> {
const route = _getEmptyRoute();
if (isRoomValid(state['features/base/conference'].room)) {
route.component = Conference;
} else if (isWelcomePageAppEnabled(state)) {
route.component = WelcomePage;
} else {
route.component = BlankPage;
}
return Promise.resolve(route);
}
/**
* Returns the {@code Route} to display when trying to access a conference if
* a valid conference is being joined.
*
* @param {Object} state - The redux state.
* @returns {Promise<Route>|undefined}
* @returns {Promise|undefined}
*/
function _getWebConferenceRoute(state): ?Promise<Route> {
function _getWebConferenceRoute(state) {
if (!isRoomValid(state['features/base/conference'].room)) {
return;
}
@ -111,9 +69,9 @@ function _getWebConferenceRoute(state): ?Promise<Route> {
* Returns the {@code Route} to display when trying to access the welcome page.
*
* @param {Object} state - The redux state.
* @returns {Promise<Route>}
* @returns {Promise<Object>}
*/
function _getWebWelcomePageRoute(state): Promise<Route> {
function _getWebWelcomePageRoute(state) {
const route = _getEmptyRoute();
if (isWelcomePageUserEnabled(state)) {
@ -137,9 +95,9 @@ function _getWebWelcomePageRoute(state): Promise<Route> {
/**
* Returns the default {@code Route}.
*
* @returns {Route}
* @returns {Object}
*/
function _getEmptyRoute(): Route {
function _getEmptyRoute() {
return {
component: BlankPage,
href: undefined

View File

@ -0,0 +1,83 @@
// @flow
import { useHeaderHeight } from '@react-navigation/stack';
import React, { useEffect, useState } from 'react';
import {
Keyboard,
KeyboardAvoidingView,
Platform,
TouchableWithoutFeedback
} from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { StyleType } from '../../styles';
type Props = {
/**
* The children component(s) of the Modal, to be rendered.
*/
children: React$Node,
/**
* Additional style to be appended to the KeyboardAvoidingView content container.
*/
contentContainerStyle?: StyleType,
/**
* Is the screen rendering a tab navigator?
*/
hasTabNavigator: boolean,
/**
* Additional style to be appended to the KeyboardAvoidingView.
*/
style?: StyleType
}
const JitsiKeyboardAvoidingView = (
{
children,
contentContainerStyle,
hasTabNavigator,
style
}: Props) => {
const headerHeight = useHeaderHeight();
const insets = useSafeAreaInsets();
const [ bottomPadding, setBottomPadding ] = useState(insets.bottom);
useEffect(() => {
// This useEffect is needed because insets are undefined at first for some reason
// https://github.com/th3rdwave/react-native-safe-area-context/issues/54
setBottomPadding(insets.bottom);
}, [ insets.bottom ]);
const tabNavigatorPadding
= hasTabNavigator ? headerHeight : 0;
const noNotchDevicePadding = bottomPadding || 10;
const iosVerticalOffset = headerHeight + noNotchDevicePadding + tabNavigatorPadding;
const androidVerticalOffset = headerHeight;
return (
<TouchableWithoutFeedback
/* eslint-disable-next-line react/jsx-handler-names */
onPress = { Keyboard.dismiss }>
<KeyboardAvoidingView
behavior = { Platform.OS === 'ios' ? 'padding' : 'height' }
contentContainerStyle = { contentContainerStyle }
enabled = { true }
keyboardVerticalOffset = {
Platform.OS === 'ios'
? iosVerticalOffset
: androidVerticalOffset
}
style = { style }>
{ children }
</KeyboardAvoidingView>
</TouchableWithoutFeedback>
);
};
export default JitsiKeyboardAvoidingView;

View File

@ -0,0 +1,69 @@
// @flow
import React from 'react';
import { View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { StyleType } from '../../styles';
import JitsiKeyboardAvoidingView from './JitsiKeyboardAvoidingView';
import styles from './styles';
type Props = {
/**
* Additional style to be appended to the KeyboardAvoidingView content container.
*/
contentContainerStyle?: StyleType,
/**
* The children component(s) of the Modal, to be rendered.
*/
children: React$Node,
/**
* Optional function that renders a footer component, if needed.
*/
footerComponent?: Function,
/**
* Is the screen rendering a tab navigator?
*/
hasTabNavigator: boolean,
/**
* Additional style to be appended to the KeyboardAvoidingView containing the content of the modal.
*/
style?: StyleType
}
const JitsiScreen = ({
contentContainerStyle,
children,
footerComponent,
hasTabNavigator,
style
}: Props) => (
<View
style = { styles.jitsiScreenContainer }>
<JitsiKeyboardAvoidingView
contentContainerStyle = { contentContainerStyle }
hasTabNavigator = { hasTabNavigator }
style = { style }>
<SafeAreaView
edges = { [
'bottom',
'left',
'right'
] }
style = { styles.safeArea }>
{ children }
</SafeAreaView>
{ footerComponent && footerComponent() }
</JitsiKeyboardAvoidingView>
</View>
);
export default JitsiScreen;

View File

@ -0,0 +1,60 @@
// @flow
import { useEffect, useState } from 'react';
import { Keyboard } from 'react-native';
import { toState } from '../../redux';
export const useKeyboardHeight = () => {
const [ keyboardHeight, setKeyboardHeight ] = useState(0);
const onKeyboardDidShow = e => {
setKeyboardHeight(e.endCoordinates.height);
};
const onKeyboardDidHide = () => {
setKeyboardHeight(0);
};
useEffect(() => {
const keyboardShow = Keyboard.addListener('keyboardDidShow', onKeyboardDidShow);
const keyboardHide = Keyboard.addListener('keyboardDidHide', onKeyboardDidHide);
return () => {
keyboardShow.remove();
keyboardHide.remove();
};
}, []);
return keyboardHeight;
};
/**
*
* Returns the client width.
*
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
* {@code getState} function to be used to retrieve the state
* features/base/config.
* @returns {number}.
*/
export function getClientWidth(stateful: Object) {
const state = toState(stateful['features/base/responsive-ui']);
return state.clientWidth;
}
/**
*
* Returns the client height.
*
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
* {@code getState} function to be used to retrieve the state
* features/base/config.
* @returns {number}.
*/
export function getClientHeight(stateful: Object) {
const state = toState(stateful['features/base/responsive-ui']);
return state.clientHeight;
}

View File

@ -3,6 +3,11 @@
import { ColorSchemeRegistry, schemeColor } from '../../color-scheme';
export default {
jitsiScreenContainer: {
flex: 1
},
safeArea: {
flex: 1
}

View File

@ -17,6 +17,7 @@ export const colors = {
primary07: '#669AEC',
primary08: '#99BBF3',
primary09: '#CCDDF9',
primary10: '#17A0DB',
surface00: '#111111',
surface01: '#040404',
@ -54,6 +55,9 @@ export const colorMap = {
// Primary buttons
action01: 'primary05',
// Screen header
screen01Header: 'primary10',
// Hover state for primary buttons
action01Hover: 'primary06',
@ -226,7 +230,8 @@ export const shape = {
boxShadow: 'inset 0px -1px 0px rgba(255, 255, 255, 0.15)'
};
export const spacing = [ 0, 4, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80 ];
export const spacing
= [ 0, 4, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128 ];
export const typography = {
labelRegular: {

View File

@ -6,7 +6,8 @@ import { IconMessage, IconReply } from '../../base/icons';
import { getParticipantById } from '../../base/participants';
import { connect } from '../../base/redux';
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
import { openChat } from '../actions';
import { navigate } from '../../conference/components/native/ConferenceNavigationContainerRef';
import { screen } from '../../conference/components/native/routes';
export type Props = AbstractButtonProps & {
@ -30,6 +31,11 @@ export type Props = AbstractButtonProps & {
*/
dispatch: Function,
/**
* True if the polls feature is disabled.
*/
_isPollsDisabled: boolean,
/**
* The participant object retrieved from Redux.
*/
@ -52,9 +58,16 @@ class PrivateMessageButton extends AbstractButton<Props, any> {
* @returns {void}
*/
_handleClick() {
const { dispatch, _participant } = this.props;
dispatch(openChat(_participant));
this.props._isPollsDisabled
? navigate(screen.conference.chat, {
privateMessageRecipient: this.props._participant
})
: navigate(screen.conference.chatandpolls.main, {
screen: screen.conference.chatandpolls.tab.chat,
params: {
privateMessageRecipient: this.props._participant
}
});
}
/**
@ -79,9 +92,11 @@ class PrivateMessageButton extends AbstractButton<Props, any> {
*/
export function _mapStateToProps(state: Object, ownProps: Props): $Shape<Props> {
const enabled = getFeatureFlag(state, CHAT_ENABLED, true);
const { disablePolls } = state['features/base/config'];
const { visible = enabled } = ownProps;
return {
_isPollsDisabled: disablePolls,
_participant: getParticipantById(state, ownProps.participantID),
visible
};

View File

@ -1,18 +1,16 @@
// @flow
import React from 'react';
import { View } from 'react-native';
import { Button } from 'react-native-paper';
import { useIsFocused } from '@react-navigation/native';
import React, { useEffect } from 'react';
import { translate } from '../../../base/i18n';
import { JitsiModal } from '../../../base/modal';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { connect } from '../../../base/redux';
import { PollsPane } from '../../../polls/components';
import { closeChat } from '../../actions.any';
import { BUTTON_MODES, CHAT_VIEW_MODAL_ID } from '../../constants';
import { screen } from '../../../conference/components/native/routes';
import { closeChat, openChat } from '../../actions.native';
import AbstractChat, {
_mapStateToProps,
type Props
type Props as AbstractProps
} from '../AbstractChat';
import ChatInputBar from './ChatInputBar';
@ -20,21 +18,30 @@ import MessageContainer from './MessageContainer';
import MessageRecipient from './MessageRecipient';
import styles from './styles';
type Props = AbstractProps & {
/**
* Is this screen focused or not(React Navigation)
*/
isChatScreenFocused: boolean,
/**
* Default prop for navigating between screen components(React Navigation)
*/
navigation: Object,
/**
* Default prop for navigating between screen components(React Navigation)
*/
route: Object
};
/**
* Implements a React native component that renders the chat window (modal) of
* the mobile client.
*/
class Chat extends AbstractChat<Props> {
/**
* Creates a new instance.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this._onClose = this._onClose.bind(this);
}
/**
* Implements React's {@link Component#render()}.
@ -42,77 +49,50 @@ class Chat extends AbstractChat<Props> {
* @inheritdoc
*/
render() {
const { _messages, route } = this.props;
const privateMessageRecipient = route.params?.privateMessageRecipient;
return (
<JitsiModal
headerProps = {{
headerLabelKey: this.props._isPollsEnabled ? 'chat.titleWithPolls' : 'chat.title'
}}
modalId = { CHAT_VIEW_MODAL_ID }
onClose = { this._onClose }>
{this.props._isPollsEnabled && <View style = { styles.tabContainer }>
<Button
color = '#17a0db'
mode = {
this.props._isPollsTabFocused
? BUTTON_MODES.CONTAINED
: BUTTON_MODES.TEXT
}
onPress = { this._onToggleChatTab }
style = { styles.tabLeftButton }
uppercase = { false }>
{`${this.props.t('chat.tabs.chat')}${this.props._isPollsTabFocused
&& this.props._nbUnreadMessages > 0
? `(${this.props._nbUnreadMessages})`
: ''
}`}
</Button>
<Button
color = '#17a0db'
mode = {
this.props._isPollsTabFocused
? BUTTON_MODES.TEXT
: BUTTON_MODES.CONTAINED
}
onPress = { this._onTogglePollsTab }
style = { styles.tabRightButton }
uppercase = { false }>
{`${this.props.t('chat.tabs.polls')}${!this.props._isPollsTabFocused
&& this.props._nbUnreadPolls > 0
? `(${this.props._nbUnreadPolls})`
: ''
}`}
</Button>
</View>}
{this.props._isPollsTabFocused
? <PollsPane />
: (
<>
<MessageContainer messages = { this.props._messages } />
<MessageRecipient />
<ChatInputBar onSend = { this._onSendMessage } />
</>
)}
</JitsiModal>
<JitsiScreen
hasTabNavigator = { true }
style = { styles.chatContainer }>
<MessageContainer messages = { _messages } />
<MessageRecipient privateMessageRecipient = { privateMessageRecipient } />
<ChatInputBar onSend = { this._onSendMessage } />
</JitsiScreen>
);
}
_onSendMessage: (string) => void;
_onClose: () => boolean
_onTogglePollsTab: () => void;
_onToggleChatTab: () => void;
/**
* Closes the modal.
*
* @returns {boolean}
*/
_onClose() {
this.props.dispatch(closeChat());
return true;
}
}
export default translate(connect(_mapStateToProps)(Chat));
export default translate(connect(_mapStateToProps)(props => {
const {
_nbUnreadMessages,
dispatch,
navigation,
route
} = props;
const isChatScreenFocused = useIsFocused();
const privateMessageRecipient = route.params?.privateMessageRecipient;
const nrUnreadMessages
= !isChatScreenFocused && _nbUnreadMessages > 0
? `(${_nbUnreadMessages})` : '';
useEffect(() => {
dispatch(openChat(privateMessageRecipient));
navigation.setOptions({
tabBarLabel: `${screen.conference.chatandpolls.tab.chat} ${nrUnreadMessages}`
});
return () => dispatch(closeChat());
}, [ nrUnreadMessages ]);
return (
<Chat
{ ...props }
isChatScreenFocused = { isChatScreenFocused } />
);
}));

View File

@ -0,0 +1,43 @@
// @flow
import { createMaterialTopTabNavigator } from '@react-navigation/material-top-tabs';
import React from 'react';
import { useSelector } from 'react-redux';
import {
getClientHeight,
getClientWidth
} from '../../../base/modal/components/functions.native';
import { Chat } from '../../../chat';
import { chatTabBarOptions } from '../../../conference/components/native/ConferenceNavigatorScreenOptions';
import { screen } from '../../../conference/components/native/routes';
import { PollsPane } from '../../../polls/components';
const ChatTab = createMaterialTopTabNavigator();
const ChatAndPolls = () => {
const clientHeight = useSelector(getClientHeight);
const clientWidth = useSelector(getClientWidth);
return (
<ChatTab.Navigator
backBehavior = 'none'
initialLayout = {{
height: clientHeight,
width: clientWidth
}}
tabBarOptions = {{
...chatTabBarOptions
}}>
<ChatTab.Screen
component = { Chat }
name = { screen.conference.chatandpolls.tab.chat } />
<ChatTab.Screen
component = { PollsPane }
name = { screen.conference.chatandpolls.tab.polls } />
</ChatTab.Navigator>
);
};
export default ChatAndPolls;

View File

@ -7,17 +7,22 @@ import {
AbstractButton,
type AbstractButtonProps
} from '../../../base/toolbox/components';
import { openChat } from '../../actions.native';
import { navigate } from '../../../conference/components/native/ConferenceNavigationContainerRef';
import { screen } from '../../../conference/components/native/routes';
import { getUnreadCount } from '../../functions';
type Props = AbstractButtonProps & {
/**
* True if the polls feature is disabled.
*/
_isPollsDisabled: boolean,
/**
* The unread message count.
*/
_unreadMessageCount: number,
dispatch: Function
_unreadMessageCount: number
};
/**
@ -36,7 +41,9 @@ class ChatButton extends AbstractButton<Props, *> {
* @returns {void}
*/
_handleClick() {
this.props.dispatch(openChat());
this.props._isPollsDisabled
? navigate(screen.conference.chat)
: navigate(screen.conference.chatandpolls.main);
}
/**
@ -59,9 +66,11 @@ class ChatButton extends AbstractButton<Props, *> {
*/
function _mapStateToProps(state, ownProps) {
const enabled = getFeatureFlag(state, CHAT_ENABLED, true);
const { disablePolls } = state['features/base/config'];
const { visible = enabled } = ownProps;
return {
_isPollsDisabled: disablePolls,
_unreadMessageCount: getUnreadCount(state),
visible
};

View File

@ -58,7 +58,6 @@ class ChatInputBar extends Component<Props, State> {
};
this._onChangeText = this._onChangeText.bind(this);
this._onFieldReferenceAvailable = this._onFieldReferenceAvailable.bind(this);
this._onFocused = this._onFocused.bind(this);
this._onSubmit = this._onSubmit.bind(this);
}
@ -83,7 +82,6 @@ class ChatInputBar extends Component<Props, State> {
onFocus = { this._onFocused(true) }
onSubmitEditing = { this._onSubmit }
placeholder = { this.props.t('chat.fieldPlaceHolder') }
ref = { this._onFieldReferenceAvailable }
returnKeyType = 'send'
style = { styles.inputField }
value = { this.state.message } />
@ -113,18 +111,6 @@ class ChatInputBar extends Component<Props, State> {
});
}
_onFieldReferenceAvailable: Object => void;
/**
* Callback to be invoked when the field reference is available.
*
* @param {Object} field - The reference to the field.
* @returns {void}
*/
_onFieldReferenceAvailable(field) {
field && field.focus();
}
_onFocused: boolean => Function;
/**

View File

@ -8,9 +8,11 @@ import { translate } from '../../../base/i18n';
import { Icon, IconCancelSelection } from '../../../base/icons';
import { connect } from '../../../base/redux';
import { type StyleType } from '../../../base/styles';
import {
setParams
} from '../../../conference/components/native/ConferenceNavigationContainerRef';
import { setPrivateMessageRecipient } from '../../actions.any';
import AbstractMessageRecipient, {
_mapDispatchToProps,
_mapStateToProps as _abstractMapStateToProps,
type Props as AbstractProps
} from '../AbstractMessageRecipient';
@ -19,35 +21,74 @@ type Props = AbstractProps & {
/**
* The color-schemed stylesheet of the feature.
*/
_styles: StyleType
_styles: StyleType,
/**
* The Redux dispatch function.
*/
dispatch: Function,
/**
* The participant object set for private messaging.
*/
privateMessageRecipient: Object,
};
/**
* Class to implement the displaying of the recipient of the next message.
*/
class MessageRecipient extends AbstractMessageRecipient<Props> {
/**
* Constructor of the component.
*
* @param {Props} props - The props of the component.
*/
constructor(props: Props) {
super(props);
this._onResetPrivateMessageRecipient = this._onResetPrivateMessageRecipient.bind(this);
}
_onResetPrivateMessageRecipient: () => void;
/**
* Resets private message recipient from state.
*
* @returns {void}
*/
_onResetPrivateMessageRecipient() {
const { dispatch } = this.props;
dispatch(setPrivateMessageRecipient());
setParams({
privateMessageRecipient: undefined
});
}
/**
* Implements {@code PureComponent#render}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const { _privateMessageRecipient, _styles } = this.props;
const { _styles, privateMessageRecipient, t } = this.props;
if (!_privateMessageRecipient) {
if (!privateMessageRecipient) {
return null;
}
const { t } = this.props;
return (
<View style = { _styles.messageRecipientContainer }>
<Text style = { _styles.messageRecipientText }>
{ t('chat.messageTo', {
recipient: _privateMessageRecipient
recipient: privateMessageRecipient.name
}) }
</Text>
<TouchableHighlight onPress = { this.props._onRemovePrivateMessageRecipient }>
<TouchableHighlight
onPress = { this._onResetPrivateMessageRecipient }>
<Icon
src = { IconCancelSelection }
style = { _styles.messageRecipientCancelIcon } />
@ -65,9 +106,8 @@ class MessageRecipient extends AbstractMessageRecipient<Props> {
*/
function _mapStateToProps(state) {
return {
..._abstractMapStateToProps(state),
_styles: ColorSchemeRegistry.get(state, 'Chat')
};
}
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(MessageRecipient));
export default translate(connect(_mapStateToProps)(MessageRecipient));

View File

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

View File

@ -2,6 +2,7 @@
import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
import { BoxModel, ColorPalette } from '../../../base/styles';
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
const BUBBLE_RADIUS = 8;
@ -40,7 +41,7 @@ export default {
alignSelf: 'center',
flex: 1,
padding: BoxModel.padding,
paddingTop: '10%'
paddingTop: '8%'
},
/**
@ -126,6 +127,10 @@ export default {
fontSize: 13
},
chatContainer: {
flex: 1
},
tabContainer: {
flexDirection: 'row',
justifyContent: 'center'
@ -164,7 +169,7 @@ ColorSchemeRegistry.register('Chat', {
},
emptyComponentText: {
color: schemeColor('displayName'),
color: BaseTheme.palette.ui05,
textAlign: 'center'
},

View File

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

View File

@ -10,7 +10,6 @@ import {
JitsiConferenceErrors,
JitsiConferenceEvents
} from '../base/lib-jitsi-meet';
import { setActiveModalId } from '../base/modal';
import {
getLocalParticipant,
getParticipantById,
@ -18,7 +17,6 @@ import {
} from '../base/participants';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { playSound, registerSound, unregisterSound } from '../base/sounds';
import { openDisplayNamePrompt } from '../display-name';
import { resetNbUnreadPollsMessages } from '../polls/actions';
import { ADD_REACTION_MESSAGE } from '../reactions/actionTypes';
import { pushReactions } from '../reactions/actions.any';
@ -35,7 +33,6 @@ import { addMessage, clearMessages } from './actions';
import { closeChat } from './actions.any';
import { ChatPrivacyDialog } from './components';
import {
CHAT_VIEW_MODAL_ID,
INCOMING_MSG_SOUND_ID,
MESSAGE_TYPE_ERROR,
MESSAGE_TYPE_LOCAL,
@ -95,18 +92,6 @@ MiddlewareRegistry.register(store => next => action => {
break;
case OPEN_CHAT:
if (navigator.product === 'ReactNative') {
if (localParticipant.name) {
dispatch(setActiveModalId(CHAT_VIEW_MODAL_ID));
} else {
dispatch(openDisplayNamePrompt(() => {
dispatch(setActiveModalId(CHAT_VIEW_MODAL_ID));
}));
}
} else {
dispatch(setActiveModalId(CHAT_VIEW_MODAL_ID));
}
unreadCount = 0;
if (typeof APP !== 'undefined') {
@ -126,8 +111,6 @@ MiddlewareRegistry.register(store => next => action => {
if (isPollTabOpen) {
dispatch(resetNbUnreadPollsMessages());
}
dispatch(setActiveModalId());
break;
}

View File

@ -10,22 +10,18 @@ import { connect } from '../../../base/redux';
import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
import { TestConnectionInfo } from '../../../base/testing';
import { ConferenceNotification, isCalendarEnabled } from '../../../calendar-sync';
import { Chat } from '../../../chat';
import { DisplayNameLabel } from '../../../display-name';
import { SharedDocument } from '../../../etherpad';
import {
FILMSTRIP_SIZE,
Filmstrip,
isFilmstripVisible,
TileView
} from '../../../filmstrip';
import { AddPeopleDialog, CalleeInfoContainer } from '../../../invite';
import { CalleeInfoContainer } from '../../../invite';
import { LargeVideo } from '../../../large-video';
import { KnockingParticipantList } from '../../../lobby';
import { LobbyScreen } from '../../../lobby/components/native';
import { getIsLobbyVisible } from '../../../lobby/functions';
import { BackButtonRegistry } from '../../../mobile/back-button';
import { ParticipantsPane } from '../../../participants-pane/components/native';
import { Captions } from '../../../subtitles';
import { setToolboxVisible } from '../../../toolbox/actions';
import { Toolbox } from '../../../toolbox/components/native';
@ -36,8 +32,10 @@ import {
} from '../AbstractConference';
import type { AbstractProps } from '../AbstractConference';
import { navigate } from './ConferenceNavigationContainerRef';
import LonelyMeetingExperience from './LonelyMeetingExperience';
import NavigationBar from './NavigationBar';
import { screen } from './routes';
import styles from './styles';
@ -141,6 +139,23 @@ class Conference extends AbstractConference<Props, *> {
BackButtonRegistry.addListener(this._onHardwareBackPress);
}
/**
* Implements {@code Component#componentDidUpdate}.
*
* @inheritdoc
*/
componentDidUpdate(prevProps) {
const { _showLobby } = this.props;
if (!prevProps._showLobby && _showLobby) {
navigate(screen.lobby);
}
if (prevProps._showLobby && !_showLobby) {
navigate(screen.conference.main);
}
}
/**
* Implements {@link Component#componentWillUnmount()}. Invoked immediately
* before this component is unmounted and destroyed. Disconnects the
@ -161,11 +176,7 @@ class Conference extends AbstractConference<Props, *> {
* @returns {ReactElement}
*/
render() {
const { _fullscreenEnabled, _showLobby } = this.props;
if (_showLobby) {
return <LobbyScreen />;
}
const { _fullscreenEnabled } = this.props;
return (
<Container style = { styles.conference }>
@ -217,19 +228,6 @@ class Conference extends AbstractConference<Props, *> {
return true;
}
/**
* Renders JitsiModals that are supposed to be on the conference screen.
*
* @returns {Array<ReactElement>}
*/
_renderConferenceModals() {
return [
<AddPeopleDialog key = 'addPeopleDialog' />,
<Chat key = 'chat' />,
<SharedDocument key = 'sharedDocument' />
];
}
/**
* Renders the conference notification badge if the feature is enabled.
*
@ -254,7 +252,6 @@ class Conference extends AbstractConference<Props, *> {
_renderContent() {
const {
_connecting,
_isParticipantsPaneOpen,
_largeVideoParticipantId,
_reducedUI,
_shouldDisplayTileView
@ -316,12 +313,7 @@ class Conference extends AbstractConference<Props, *> {
<TestConnectionInfo />
{ this._renderConferenceNotification() }
{ this._renderConferenceModals() }
{_shouldDisplayTileView && <Toolbox />}
{ _isParticipantsPaneOpen && <ParticipantsPane /> }
</>
);
}

View File

@ -0,0 +1,100 @@
// @flow
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import React from 'react';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { useSelector } from 'react-redux';
import { Chat, ChatAndPolls } from '../../../chat';
import { SharedDocument } from '../../../etherpad';
import AddPeopleDialog
from '../../../invite/components/add-people-dialog/native/AddPeopleDialog';
import LobbyScreen from '../../../lobby/components/native/LobbyScreen';
import { ParticipantsPane } from '../../../participants-pane/components/native';
import { getDisablePolls } from '../../functions';
import Conference from './Conference';
import {
conferenceNavigationRef
} from './ConferenceNavigationContainerRef';
import {
chatScreenOptions,
conferenceScreenOptions,
inviteScreenOptions,
lobbyScreenOptions,
participantsScreenOptions,
sharedDocumentScreenOptions
} from './ConferenceNavigatorScreenOptions';
import { screen } from './routes';
const ConferenceStack = createStackNavigator();
const ConferenceNavigationContainer = () => {
const isPollsDisabled = useSelector(getDisablePolls);
const ChatScreen
= isPollsDisabled
? Chat
: ChatAndPolls;
const chatScreenName
= isPollsDisabled
? screen.conference.chat
: screen.conference.chatandpolls.main;
return (
<SafeAreaProvider>
<NavigationContainer
independent = { true }
ref = { conferenceNavigationRef }
theme = {{
colors: {
background: '#fff'
}
}}>
<ConferenceStack.Navigator
initialRouteName = { screen.conference.main }
mode = 'modal'>
<ConferenceStack.Screen
component = { Conference }
name = { screen.conference.main }
options = {{
...conferenceScreenOptions
}} />
<ConferenceStack.Screen
/* eslint-disable-next-line react/jsx-no-bind */
component = { ChatScreen }
name = { chatScreenName }
options = {{
...chatScreenOptions
}} />
<ConferenceStack.Screen
component = { ParticipantsPane }
name = { screen.conference.participants }
options = {{
...participantsScreenOptions
}} />
<ConferenceStack.Screen
component = { LobbyScreen }
name = { screen.lobby }
options = {{
...lobbyScreenOptions
}} />
<ConferenceStack.Screen
component = { AddPeopleDialog }
name = { screen.conference.invite }
options = {{
...inviteScreenOptions
}} />
<ConferenceStack.Screen
component = { SharedDocument }
name = { screen.conference.sharedDocument }
options = {{
...sharedDocumentScreenOptions
}} />
</ConferenceStack.Navigator>
</NavigationContainer>
</SafeAreaProvider>
);
};
export default ConferenceNavigationContainer;

View File

@ -0,0 +1,40 @@
// @flow
import React from 'react';
// $FlowExpectedError
export const conferenceNavigationRef = React.createRef();
/**
* User defined navigation action included inside the reference to the container.
*
* @param {string} name - Destination name of the route that has been defined somewhere.
* @param {Object} params - Params to pass to the destination route.
* @returns {Function}
*/
export function navigate(name: string, params: Object) {
// $FlowExpectedError
return conferenceNavigationRef.current?.navigate(name, params);
}
/**
* User defined navigation action included inside the reference to the container.
*
* @returns {Function}
*/
export function goBack() {
// $FlowExpectedError
return conferenceNavigationRef.current?.goBack();
}
/**
* User defined navigation action included inside the reference to the container.
*
* @param {Object} params - Params to pass to the destination route.
* @returns {Function}
*/
export function setParams(params: Object) {
// $FlowExpectedError
return conferenceNavigationRef.current?.setParams(params);
}

View File

@ -0,0 +1,111 @@
import { TransitionPresets } from '@react-navigation/stack';
import React from 'react';
import { Platform } from 'react-native';
import { IconClose } from '../../../base/icons';
import BaseTheme from '../../../base/ui/components/BaseTheme';
import { goBack } from './ConferenceNavigationContainerRef';
import HeaderNavigationButton from './HeaderNavigationButton';
/**
* Default modal transition for the current platform.
*/
export const conferenceModalPresentation = Platform.select({
ios: TransitionPresets.ModalPresentationIOS,
default: TransitionPresets.DefaultTransition
});
/**
* Screen options and transition types.
*/
export const screenOptions = {
...TransitionPresets.ModalTransition,
gestureEnabled: false,
headerShown: false
};
/**
* Screen options for conference.
*/
export const conferenceScreenOptions = {
...screenOptions
};
/**
* Screen options for lobby modal.
*/
export const lobbyScreenOptions = {
...screenOptions
};
/**
* Tab bar options for chat screen.
*/
export const chatTabBarOptions = {
activeTintColor: BaseTheme.palette.screen01Header,
labelStyle: {
fontSize: BaseTheme.typography.labelRegular.fontSize
},
inactiveTintColor: BaseTheme.palette.field02Disabled,
indicatorStyle: {
backgroundColor: BaseTheme.palette.screen01Header
}
};
/**
* Screen options for presentation type modals.
*/
export const presentationScreenOptions = {
...conferenceModalPresentation,
headerBackTitleVisible: false,
headerLeft: () => (
<HeaderNavigationButton
onPress = { goBack }
src = { IconClose } />
),
headerStatusBarHeight: 0,
headerStyle: {
backgroundColor: BaseTheme.palette.screen01Header
},
headerTitleStyle: {
color: BaseTheme.palette.text01
}
};
/**
* Screen options for chat.
*/
export const chatScreenOptions = {
...presentationScreenOptions
};
/**
* Screen options for invite modal.
*/
export const inviteScreenOptions = {
...presentationScreenOptions
};
/**
* Screen options for participants modal.
*/
export const participantsScreenOptions = {
...presentationScreenOptions
};
/**
* Screen options for shared document.
*/
export const sharedDocumentScreenOptions = {
...TransitionPresets.DefaultTransition,
headerBackTitleVisible: false,
headerShown: true,
headerStyle: {
backgroundColor: BaseTheme.palette.screen01Header
},
headerTitleStyle: {
color: BaseTheme.palette.text01
}
};

View File

@ -0,0 +1,39 @@
// @flow
import React from 'react';
import { TouchableWithoutFeedback } from 'react-native';
import { Icon } from '../../../base/icons';
import styles from './styles';
type Props = {
/**
* Callback to invoke when the {@code HeaderNavigationButton} is clicked/pressed.
*/
onPress: Function,
/**
* The ImageSource to be rendered as image.
*/
src: Object,
/**
* The component's external style
*/
style: Object
}
const HeaderNavigationButton = ({ onPress, src, style }: Props) => (
<TouchableWithoutFeedback
onPress = { onPress } >
<Icon
size = { 20 }
src = { src }
style = { [ styles.headerNavigationButton, style ] } />
</TouchableWithoutFeedback>
);
export default HeaderNavigationButton;

View File

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

View File

@ -0,0 +1,17 @@
export const screen = {
conference: {
main: 'Conference',
chat: 'Chat',
chatandpolls: {
main: 'Chat and Polls',
tab: {
chat: 'Chat',
polls: 'Polls'
}
},
participants: 'Participants',
invite: 'Invite',
sharedDocument: 'Shared document'
},
lobby: 'Lobby'
};

View File

@ -23,6 +23,10 @@ export default {
margin: 10
},
headerNavigationButton: {
marginLeft: 12
},
/**
* View that contains the indicators.
*/

View File

@ -1 +1,21 @@
// @flow
import { toState } from '../base/redux';
export * from './functions.any';
/**
*
* Returns true if polls feature is disabled.
*
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
* {@code getState} function to be used to retrieve the state
* features/base/config.
* @returns {boolean}.
*/
export function getDisablePolls(stateful: Object) {
const state = toState(stateful['features/base/config']);
return state.disablePolls;
}

View File

@ -1,13 +1,12 @@
// @flow
import type { Dispatch } from 'redux';
import { createToolbarEvent, sendAnalytics } from '../../analytics';
import { translate } from '../../base/i18n';
import { IconShareDoc } from '../../base/icons';
import { connect } from '../../base/redux';
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
import { toggleDocument } from '../actions';
import { navigate } from '../../conference/components/native/ConferenceNavigationContainerRef';
import { screen } from '../../conference/components/native/routes';
type Props = AbstractButtonProps & {
@ -15,12 +14,7 @@ type Props = AbstractButtonProps & {
/**
* Whether the shared document is being edited or not.
*/
_editing: boolean,
/**
* Redux dispatch function.
*/
dispatch: Dispatch<any>,
_editing: boolean
};
/**
@ -59,7 +53,7 @@ class SharedDocumentButton extends AbstractButton<Props, *> {
* @returns {void}
*/
_handleClick() {
const { _editing, dispatch, handleClick } = this.props;
const { _editing, handleClick } = this.props;
if (handleClick) {
handleClick();
@ -72,7 +66,8 @@ class SharedDocumentButton extends AbstractButton<Props, *> {
{
enable: !_editing
}));
dispatch(toggleDocument());
navigate(screen.conference.sharedDocument);
}
/**

View File

@ -3,15 +3,16 @@
import React, { PureComponent } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';
import type { Dispatch } from 'redux';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { translate } from '../../../base/i18n';
import { JitsiModal } from '../../../base/modal';
import { IconArrowBack } from '../../../base/icons';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { LoadingIndicator } from '../../../base/react';
import { connect } from '../../../base/redux';
import { toggleDocument } from '../../actions';
import { SHARE_DOCUMENT_VIEW_ID } from '../../constants';
import { goBack } from '../../../conference/components/native/ConferenceNavigationContainerRef';
import HeaderNavigationButton
from '../../../conference/components/native/HeaderNavigationButton';
import { getSharedDocumentUrl } from '../../functions';
import styles, { INDICATOR_COLOR } from './styles';
@ -32,14 +33,9 @@ type Props = {
_headerStyles: Object,
/**
* True if the chat window should be rendered.
* Default prop for navigation between screen components(React Navigation)
*/
_isOpen: boolean,
/**
* The Redux dispatch function.
*/
dispatch: Dispatch<any>,
navigation: Object,
/**
* Function to be used to translate i18n labels.
@ -59,11 +55,29 @@ class SharedDocument extends PureComponent<Props> {
constructor(props: Props) {
super(props);
this._onClose = this._onClose.bind(this);
this._onError = this._onError.bind(this);
this._renderLoading = this._renderLoading.bind(this);
}
/**
* Implements React's {@link Component#componentDidMount()}. Invoked
* immediately after this component is mounted.
*
* @inheritdoc
* @returns {void}
*/
componentDidMount() {
const { navigation } = this.props;
navigation.setOptions({
headerLeft: () => (
<HeaderNavigationButton
onPress = { goBack }
src = { IconArrowBack }
style = { styles.headerArrowBack } />
)
});
}
/**
* Implements React's {@link Component#render()}.
*
@ -73,55 +87,18 @@ class SharedDocument extends PureComponent<Props> {
const { _documentUrl } = this.props;
return (
<JitsiModal
headerProps = {{
headerLabelKey: 'documentSharing.title'
}}
modalId = { SHARE_DOCUMENT_VIEW_ID }
style = { styles.webView }>
<JitsiScreen
addHeaderHeightValue = { true }
hasTabNavigator = { false }
style = { styles.sharedDocContainer }>
<WebView
onError = { this._onError }
renderLoading = { this._renderLoading }
source = {{ uri: _documentUrl }}
startInLoadingState = { true } />
</JitsiModal>
</JitsiScreen>
);
}
_onClose: () => boolean
/**
* Closes the window.
*
* @returns {boolean}
*/
_onClose() {
const { _isOpen, dispatch } = this.props;
if (_isOpen) {
dispatch(toggleDocument());
return true;
}
return false;
}
_onError: () => void;
/**
* Callback to handle the error if the page fails to load.
*
* @returns {void}
*/
_onError() {
const { _isOpen, dispatch } = this.props;
if (_isOpen) {
dispatch(toggleDocument());
}
}
_renderLoading: () => React$Component<any>;
/**
@ -148,13 +125,11 @@ class SharedDocument extends PureComponent<Props> {
* @returns {Object}
*/
export function _mapStateToProps(state: Object) {
const { editing } = state['features/etherpad'];
const documentUrl = getSharedDocumentUrl(state);
return {
_documentUrl: documentUrl,
_headerStyles: ColorSchemeRegistry.get(state, 'Header'),
_isOpen: editing
_headerStyles: ColorSchemeRegistry.get(state, 'Header')
};
}

View File

@ -6,6 +6,10 @@ export const INDICATOR_COLOR = ColorPalette.lightGrey;
export default {
headerArrowBack: {
marginLeft: 12
},
indicatorWrapper: {
alignItems: 'center',
backgroundColor: ColorPalette.white,
@ -13,6 +17,10 @@ export default {
justifyContent: 'center'
},
sharedDocContainer: {
flex: 1
},
webView: {
backgroundColor: 'rgb(242, 242, 242)'
}

View File

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

View File

@ -6,13 +6,12 @@ import {
ActivityIndicator,
FlatList,
Platform,
SafeAreaView,
TextInput,
TouchableOpacity,
View
} from 'react-native';
import { Text, TouchableRipple, withTheme } from 'react-native-paper';
import { ColorSchemeRegistry } from '../../../../base/color-scheme';
import { AlertDialog, openDialog } from '../../../../base/dialog';
import { translate } from '../../../../base/i18n';
import {
@ -24,15 +23,14 @@ import {
IconSearch,
IconShare
} from '../../../../base/icons';
import { JitsiModal, setActiveModalId } from '../../../../base/modal';
import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
import {
AvatarListItem,
type Item
} from '../../../../base/react';
import { connect } from '../../../../base/redux';
import { ColorPalette } from '../../../../base/styles';
import { beginShareRoom } from '../../../../share-room';
import { ADD_PEOPLE_DIALOG_VIEW_ID, INVITE_TYPES } from '../../../constants';
import { INVITE_TYPES } from '../../../constants';
import AbstractAddPeopleDialog, {
type Props as AbstractProps,
type State as AbstractState,
@ -46,20 +44,25 @@ import styles, {
type Props = AbstractProps & {
/**
* The color schemed style of the Header.
*/
_headerStyles: Object,
/**
* True if the invite dialog should be open, false otherwise.
*/
_isVisible: boolean,
/**
* Default prop for navigation between screen components(React Navigation)
*/
navigation: Object,
/**
* Function used to translate i18n labels.
*/
t: Function
t: Function,
/**
* Theme used for styles.
*/
theme: Object
};
type State = AbstractState & {
@ -136,12 +139,54 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
this._setFieldRef = this._setFieldRef.bind(this);
}
/**
* Implements React's {@link Component#componentDidMount()}. Invoked
* immediately after this component is mounted.
*
* @inheritdoc
* @returns {void}
*/
componentDidMount() {
const { navigation, t, theme } = this.props;
const { palette } = theme;
navigation.setOptions({
headerRight: () => (
<TouchableRipple
disabled = { this._isAddDisabled() }
rippleColor = { palette.screen01Header } >
<Text
style = { styles.headerSendInvite }>{ t('inviteDialog.send') }
</Text>
</TouchableRipple>
)
});
}
/**
* Implements {@code Component#componentDidUpdate}.
*
* @inheritdoc
*/
componentDidUpdate(prevProps) {
const { navigation, t, theme } = this.props;
const { palette } = theme;
navigation.setOptions({
// eslint-disable-next-line react/no-multi-comp
headerRight: () => (
<TouchableRipple
disabled = { this._isAddDisabled() }
onPress = { this._onInvite }
rippleColor = { palette.screen01Header } >
<Text
/* eslint-disable-next-line react-native/no-inline-styles */
style = { styles.headerSendInvite }>{ t('inviteDialog.send') }
</Text>
</TouchableRipple>
)
});
if (prevProps._isVisible !== this.props._isVisible) {
// Clear state
this._clearState();
@ -159,6 +204,8 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
_dialOutEnabled
} = this.props;
const { inviteItems, selectableItems } = this.state;
const { theme } = this.props;
const { palette } = theme;
let placeholderKey = 'searchPlaceholder';
@ -169,15 +216,10 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
}
return (
<JitsiModal
<JitsiScreen
footerComponent = { this._renderShareMeetingButton }
headerProps = {{
forwardDisabled: this._isAddDisabled(),
forwardLabelKey: 'inviteDialog.send',
headerLabelKey: 'inviteDialog.header',
onPressForward: this._onInvite
}}
modalId = { ADD_PEOPLE_DIALOG_VIEW_ID }>
hasTabNavigator = { false }
style = { styles.addPeopleContainer }>
<View
style = { styles.searchFieldWrapper }>
<View style = { styles.searchIconWrapper }>
@ -198,7 +240,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
placeholder = {
this.props.t(`inviteDialog.${placeholderKey}`)
}
placeholderTextColor = { ColorPalette.lightGrey }
placeholderTextColor = { palette.text04 }
ref = { this._setFieldRef }
spellCheck = { false }
style = { styles.searchField }
@ -222,7 +264,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
keyboardShouldPersistTaps = 'always'
renderItem = { this._renderItem } />
</View>
</JitsiModal>
</JitsiScreen>
);
}
@ -326,8 +368,6 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
inviteItems: invitesLeftToSend
});
this._showFailedInviteAlert();
} else {
this.props.dispatch(setActiveModalId());
}
});
}
@ -562,22 +602,20 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
* @returns {React#Element<*>}
*/
_renderShareMeetingButton() {
const { _headerStyles } = this.props;
return (
<SafeAreaView
<View
style = { [
styles.bottomBar,
_headerStyles.headerOverlay,
this.state.bottomPadding ? styles.extraBarPadding : null
] }>
<TouchableOpacity
onPress = { this._onShareMeeting }>
<Icon
src = { IconShare }
style = { [ _headerStyles.headerButtonText, styles.shareIcon ] } />
style = { styles.shareIcon } />
</TouchableOpacity>
</SafeAreaView>
</View>
);
}
@ -621,10 +659,8 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
*/
function _mapStateToProps(state: Object) {
return {
..._abstractMapStateToProps(state),
_headerStyles: ColorSchemeRegistry.get(state, 'Header'),
_isVisible: state['features/base/modal'].activeModalId === ADD_PEOPLE_DIALOG_VIEW_ID
..._abstractMapStateToProps(state)
};
}
export default translate(connect(_mapStateToProps)(AddPeopleDialog));
export default translate(connect(_mapStateToProps)(withTheme(AddPeopleDialog)));

View File

@ -1,15 +1,19 @@
// @flow
import { BoxModel } from '../../../../base/styles';
import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
export const AVATAR_SIZE = 40;
export const DARK_GREY = 'rgb(28, 32, 37)';
export const LIGHT_GREY = 'rgb(209, 219, 232)';
export const ICON_SIZE = 15;
const FIELD_COLOR = 'rgb(240, 243, 247)';
export default {
addPeopleContainer: {
flex: 1
},
avatar: {
backgroundColor: LIGHT_GREY
},
@ -21,8 +25,9 @@ export default {
bottomBar: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-around'
justifyContent: 'center',
backgroundColor: BaseTheme.palette.screen01Header,
height: BaseTheme.spacing[10]
},
clearButton: {
@ -39,7 +44,7 @@ export default {
clearIconContainer: {
alignItems: 'center',
backgroundColor: FIELD_COLOR,
backgroundColor: BaseTheme.palette.section01,
borderRadius: 12,
justifyContent: 'center',
height: 24,
@ -53,6 +58,15 @@ export default {
paddingBottom: 30
},
headerCloseIcon: {
marginLeft: 12
},
headerSendInvite: {
color: BaseTheme.palette.text01,
marginRight: 12
},
invitedList: {
padding: 3
},
@ -80,7 +94,7 @@ export default {
},
searchField: {
backgroundColor: FIELD_COLOR,
backgroundColor: BaseTheme.palette.section01,
borderBottomRightRadius: 10,
borderTopRightRadius: 10,
color: DARK_GREY,
@ -106,7 +120,7 @@ export default {
alignItems: 'stretch',
flexDirection: 'row',
height: 52,
paddingHorizontal: 15,
paddingHorizontal: 12,
paddingVertical: 8
},
@ -117,7 +131,7 @@ export default {
searchIconWrapper: {
alignItems: 'center',
backgroundColor: FIELD_COLOR,
backgroundColor: BaseTheme.palette.section01,
borderBottomLeftRadius: 10,
borderTopLeftRadius: 10,
flexDirection: 'row',

View File

@ -1,10 +1,5 @@
// @flow
/**
* Modal ID for the AddPeopleDialog modal.
*/
export const ADD_PEOPLE_DIALOG_VIEW_ID = 'ADD_PEOPLE_DIALOG_VIEW_ID';
/**
* Modal ID for the DialInSummary modal.
*/

View File

@ -2,11 +2,12 @@
import React from 'react';
import { Text, View, TouchableOpacity, TextInput } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Avatar } from '../../../base/avatar';
import { CustomDialog } from '../../../base/dialog';
import { translate } from '../../../base/i18n';
import { Icon, IconEdit } from '../../../base/icons';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { LoadingIndicator } from '../../../base/react';
import { connect } from '../../../base/redux';
import AbstractLobbyScreen, { _mapStateToProps } from '../AbstractLobbyScreen';
@ -26,9 +27,10 @@ class LobbyScreen extends AbstractLobbyScreen {
const { _meetingName, t } = this.props;
return (
<CustomDialog
onCancel = { this._onCancel }>
<View style = { styles.contentWrapper }>
<JitsiScreen
hasTabNavigator = { false }
style = { styles.contentWrapper }>
<SafeAreaView>
<Text style = { styles.dialogTitle }>
{ t(this._getScreenTitleKey()) }
</Text>
@ -36,8 +38,8 @@ class LobbyScreen extends AbstractLobbyScreen {
{ _meetingName }
</Text>
{ this._renderContent() }
</View>
</CustomDialog>
</SafeAreaView>
</JitsiScreen>
);
}
@ -234,6 +236,13 @@ class LobbyScreen extends AbstractLobbyScreen {
{ t('lobby.enterPasswordButton') }
</Text>
</TouchableOpacity> }
<TouchableOpacity
onPress = { this._onCancel }
style = { styles.cancelButton }>
<Text>
{ t('dialog.Cancel') }
</Text>
</TouchableOpacity>
</>
);
}

View File

@ -12,20 +12,29 @@ export default {
button: {
alignItems: 'center',
borderRadius: 4,
marginVertical: 8,
paddingVertical: 10
marginVertical: 4,
paddingVertical: 8
},
contentWrapper: {
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
padding: 32
justifyItems: 'center',
height: '100%'
},
closeIcon: {
color: 'red',
fontSize: 20
},
dialogTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10
margin: 'auto',
marginVertical: 24,
textAlign: 'center'
},
displayNameText: {
@ -71,6 +80,8 @@ export default {
},
joiningMessage: {
color: 'rgba(0, 0, 0, .7)',
paddingBottom: 36,
textAlign: 'center'
},
@ -103,7 +114,15 @@ export default {
},
secondaryText: {
color: 'rgba(0, 0, 0, .7)'
color: 'rgba(0, 0, 0, .7)',
margin: 'auto',
textAlign: 'center'
},
cancelButton: {
alignItems: 'center',
backgroundColor: 'transparent',
marginVertical: 4
},
// KnockingParticipantList

View File

@ -2,7 +2,7 @@
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { ScrollView, Text, View } from 'react-native';
import { Text, View } from 'react-native';
import { Button, withTheme } from 'react-native-paper';
import { useDispatch, useSelector } from 'react-redux';
@ -36,7 +36,8 @@ const LobbyParticipantList = ({ theme }: Props) => {
}
return (
<View style = { styles.lobbyList }>
<View
style = { styles.lobbyListContainer } >
<View style = { styles.lobbyListDetails } >
<Text style = { styles.lobbyListDescription }>
{t('participantsPane.headings.waitingLobby',
@ -54,15 +55,13 @@ const LobbyParticipantList = ({ theme }: Props) => {
)
}
</View>
<ScrollView>
{
participants.map(p => (
<LobbyParticipantItem
key = { p.id }
participant = { p } />)
)
}
</ScrollView>
{
participants.map(p => (
<LobbyParticipantItem
key = { p.id }
participant = { p } />)
)
}
</View>
);
};

View File

@ -134,7 +134,8 @@ class MeetingParticipantList extends PureComponent<Props> {
} = this.props;
return (
<View style = { styles.meetingList }>
<View
style = { styles.meetingListContainer }>
<Text style = { styles.meetingListDescription }>
{t('participantsPane.headings.participantsList',
{ count: _participantsCount })}
@ -155,7 +156,9 @@ class MeetingParticipantList extends PureComponent<Props> {
horizontal = { false }
keyExtractor = { this._keyExtractor }
renderItem = { this._renderParticipant }
scrollEnabled = { false }
showsHorizontalScrollIndicator = { false }
style = { styles.meetingList }
windowSize = { 2 } />
</View>
);

View File

@ -2,18 +2,17 @@
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { View } from 'react-native';
import { ScrollView, View } from 'react-native';
import { Button } from 'react-native-paper';
import { useDispatch, useSelector } from 'react-redux';
import { openDialog } from '../../../base/dialog';
import { JitsiModal } from '../../../base/modal';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import {
isLocalParticipantModerator
} from '../../../base/participants';
import MuteEveryoneDialog
from '../../../video-menu/components/native/MuteEveryoneDialog';
import { close } from '../../actions.native';
import { ContextMenuMore } from './ContextMenuMore';
import HorizontalDotsIcon from './HorizontalDotsIcon';
@ -29,21 +28,19 @@ import styles from './styles';
const ParticipantsPane = () => {
const dispatch = useDispatch();
const openMoreMenu = useCallback(() => dispatch(openDialog(ContextMenuMore)), [ dispatch ]);
const closePane = useCallback(() => dispatch(close()), [ dispatch ]);
const isLocalModerator = useSelector(isLocalParticipantModerator);
const muteAll = useCallback(() => dispatch(openDialog(MuteEveryoneDialog)),
[ dispatch ]);
const { t } = useTranslation();
return (
<JitsiModal
headerProps = {{
headerLabelKey: 'participantsPane.header'
}}
onClose = { closePane }
<JitsiScreen
hasTabNavigator = { false }
style = { styles.participantsPane }>
<LobbyParticipantList />
<MeetingParticipantList />
<ScrollView bounces = { false }>
<LobbyParticipantList />
<MeetingParticipantList />
</ScrollView>
{
isLocalModerator
&& <View style = { styles.footer }>
@ -61,7 +58,7 @@ const ParticipantsPane = () => {
style = { styles.moreButton } />
</View>
}
</JitsiModal>
</JitsiScreen>
);
};

View File

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

View File

@ -165,11 +165,13 @@ export default {
isLocal: {
alignSelf: 'center',
color: BaseTheme.palette.text01,
marginLeft: 4
marginLeft: BaseTheme.spacing[1]
},
participantsPane: {
backgroundColor: BaseTheme.palette.ui01
backgroundColor: BaseTheme.palette.ui01,
flex: 1,
justifyContent: 'center'
},
participantStatesContainer: {
@ -196,13 +198,12 @@ export default {
top: BaseTheme.spacing[1]
},
lobbyList: {
lobbyListContainer: {
position: 'relative'
},
meetingList: {
position: 'relative',
marginTop: BaseTheme.spacing[3]
lobbyListDescription: {
...participantListDescription
},
lobbyListDetails: {
@ -216,8 +217,8 @@ export default {
width: '100%'
},
lobbyListDescription: {
...participantListDescription
meetingListContainer: {
flex: 1
},
meetingListDescription: {
@ -227,21 +228,18 @@ export default {
footer: {
alignItems: 'center',
backgroundColor: BaseTheme.palette.ui01,
bottom: BaseTheme.spacing[0],
display: 'flex',
flexDirection: 'row',
height: BaseTheme.spacing[10],
justifyContent: 'space-between',
paddingRight: BaseTheme.spacing[3],
position: 'relative',
right: BaseTheme.spacing[0],
left: BaseTheme.spacing[0]
paddingHorizontal: BaseTheme.spacing[3],
paddingVertical: BaseTheme.spacing[2]
},
headerCloseIcon: {
marginLeft: 12
},
inviteButton: {
backgroundColor: BaseTheme.palette.action01,
marginTop: BaseTheme.spacing[2],
marginBottom: BaseTheme.spacing[4],
marginLeft: BaseTheme.spacing[3],
marginRight: BaseTheme.spacing[3]
},

View File

@ -141,39 +141,38 @@ const PollCreate = (props: AbstractProps) => {
keyExtractor = { (item, index) => index.toString() }
ref = { answerListRef }
renderItem = { renderListItem } />
<View style = { chatStyles.pollCreateButtons }>
<Button
color = '#3D3D3D'
mode = { BUTTON_MODES.CONTAINED }
onPress = { () => {
// adding and answer
addAnswer();
requestFocus(answers.length);
} }
style = { chatStyles.pollCreateAddButton }>
{t('polls.create.addOption')}
</Button>
<View
style = { chatStyles.buttonRow }>
<Button
color = '#3D3D3D'
mode = { BUTTON_MODES.CONTAINED }
onPress = { () => setCreateMode(false) }
style = { chatStyles.pollCreateButton } >
{t('polls.create.cancel')}
</Button>
<Button
color = '#3D3D3D'
mode = { BUTTON_MODES.CONTAINED }
onPress = { () => {
// adding and answer
addAnswer();
requestFocus(answers.length);
} }
style = { chatStyles.pollCreateAddButton }>
{t('polls.create.addOption')}
</Button>
</View>
<View
style = { chatStyles.buttonRow }>
<Button
color = '#3D3D3D'
mode = { BUTTON_MODES.CONTAINED }
onPress = { () => setCreateMode(false) }
style = { chatStyles.pollCreateButton } >
{t('polls.create.cancel')}
</Button>
<Button
color = '#17a0db'
disabled = { isSubmitDisabled }
mode = { BUTTON_MODES.CONTAINED }
onPress = { onSubmit }
style = { chatStyles.pollCreateButton } >
{t('polls.create.send')}
</Button>
<Button
color = '#17a0db'
disabled = { isSubmitDisabled }
mode = { BUTTON_MODES.CONTAINED }
onPress = { onSubmit }
style = { chatStyles.pollCreateButton } >
{t('polls.create.send')}
</Button>
</View>
</View>
</View>
</View>
);

View File

@ -1,11 +1,15 @@
/* eslint-disable react-native/no-color-literals */
// @flow
import React from 'react';
import { View } from 'react-native';
import { Button } from 'react-native-paper';
import { useNavigation, useIsFocused } from '@react-navigation/native';
import React, { useEffect } from 'react';
import { Button, useTheme } from 'react-native-paper';
import { useSelector } from 'react-redux';
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
import { BUTTON_MODES } from '../../../chat/constants';
import { screen } from '../../../conference/components/native/routes';
import { getUnreadPollCount } from '../../functions';
import AbstractPollsPane from '../AbstractPollsPane';
import type { AbstractProps } from '../AbstractPollsPane';
@ -13,26 +17,45 @@ import PollCreate from './PollCreate';
import PollsList from './PollsList';
import { chatStyles } from './styles';
const PollsPane = (props: AbstractProps) => {
const PollsPane = (props: AbstractProps) => {
const { createMode, onCreate, setCreateMode, t } = props;
const isPollsScreenFocused = useIsFocused();
const navigation = useNavigation();
const nbUnreadPolls = useSelector(getUnreadPollCount);
const { palette } = useTheme();
const nrUnreadPolls = !isPollsScreenFocused && nbUnreadPolls > 0
? `(${nbUnreadPolls})`
: '';
useEffect(() => {
navigation.setOptions({
tabBarLabel: `${screen.conference.chatandpolls.tab.polls} ${nrUnreadPolls}`
});
}, [ nrUnreadPolls ]);
return (
<View style = { chatStyles.PollPane }>
{ createMode
? <PollCreate setCreateMode = { setCreateMode } />
: <View style = { chatStyles.PollPaneContent }>
{/* <View /> */}
<PollsList />
<Button
color = '#17a0db'
mode = { BUTTON_MODES.CONTAINED }
onPress = { onCreate }
style = { chatStyles.createPollButton } >
{t('polls.create.create')}
</Button>
</View>}
</View>
<JitsiScreen
contentContainerStyle = { chatStyles.PollPane }
hasTabNavigator = { true }
style = { chatStyles.PollPaneContainer }>
{
createMode
? <PollCreate setCreateMode = { setCreateMode } />
: <PollsList />
}
{
!createMode && <Button
color = { palette.screen01Header }
mode = { BUTTON_MODES.CONTAINED }
onPress = { onCreate }
style = { chatStyles.createPollButton } >
{t('polls.create.create')}
</Button>
}
</JitsiScreen>
);
};

View File

@ -1,7 +1,7 @@
// @flow
import { schemeColor } from '../../../base/color-scheme';
import { ColorPalette, createStyleSheet } from '../../../base/styles';
import BaseTheme from '../../../base/ui/components/BaseTheme';
export const answerStyles = createStyleSheet({
question: {
@ -110,6 +110,7 @@ export const resultsStyles = createStyleSheet({
export const chatStyles = createStyleSheet({
messageFooter: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
@ -123,9 +124,9 @@ export const chatStyles = createStyleSheet({
noPollText: {
flex: 1,
color: schemeColor('displayName'),
color: BaseTheme.palette.ui05,
textAlign: 'center',
paddingTop: '10%'
paddingTop: '8%'
},
pollItemContainer: {
@ -165,17 +166,17 @@ export const chatStyles = createStyleSheet({
},
pollCreateAddButton: {
margin: 8
margin: BaseTheme.spacing[2]
},
toggleText: {
color: ColorPalette.blue,
paddingTop: 16
paddingTop: BaseTheme.spacing[3]
},
createPollButton: {
padding: 8,
margin: 4
margin: BaseTheme.spacing[2]
},
PollPane: {
@ -183,8 +184,13 @@ export const chatStyles = createStyleSheet({
padding: 8
},
PollPaneContainer: {
flex: 1
},
PollPaneContent: {
justifyContent: 'space-between',
padding: BaseTheme.spacing[3],
flex: 1
},

View File

@ -44,12 +44,7 @@ type Props = {
/**
* Whether or not the reactions feature is enabled.
*/
_reactionsEnabled: boolean,
/**
* The redux {@code dispatch} function.
*/
dispatch: Function
_reactionsEnabled: boolean
};
/**
@ -88,10 +83,12 @@ function Toolbox(props: Props) {
<VideoMuteButton
styles = { buttonStylesBorderless }
toggledStyles = { toggledButtonStyles } />
{ additionalButtons.has('chat')
{
additionalButtons.has('chat')
&& <ChatButton
styles = { buttonStylesBorderless }
toggledStyles = { backgroundToggledStyle } />}
toggledStyles = { backgroundToggledStyle } />
}
{ additionalButtons.has('raisehand') && (_reactionsEnabled
? <ReactionsMenuButton