mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-03-02 17:50:21 +00:00
Compare commits
56 Commits
android-sd
...
5259
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4acafebe5f | ||
|
|
88a11b9f3e | ||
|
|
bcc326c150 | ||
|
|
e348270099 | ||
|
|
fc59cdbdbe | ||
|
|
59ef5c4789 | ||
|
|
b6b943e7de | ||
|
|
48efa4ac61 | ||
|
|
2f1105e6d2 | ||
|
|
9d5024cc5d | ||
|
|
8b23265a50 | ||
|
|
5bc424459f | ||
|
|
605ce9db62 | ||
|
|
e2f760c7f1 | ||
|
|
e63b3016c9 | ||
|
|
4d07d4ae76 | ||
|
|
2616e126fb | ||
|
|
38f9c97f40 | ||
|
|
c7a91e1974 | ||
|
|
fe41eef398 | ||
|
|
08177af182 | ||
|
|
229520f74f | ||
|
|
0d9af05a4b | ||
|
|
1ad9046a38 | ||
|
|
49a73ac446 | ||
|
|
8e4a22bdbf | ||
|
|
ddbf334930 | ||
|
|
cd5f2b483f | ||
|
|
cf34b0a783 | ||
|
|
10cc3b2b31 | ||
|
|
27e4e862fd | ||
|
|
4b3d92dcbd | ||
|
|
5c1e2b4bd2 | ||
|
|
a4d516ca86 | ||
|
|
d41c597446 | ||
|
|
4788720edd | ||
|
|
40099e97ec | ||
|
|
a7a44902ec | ||
|
|
7af23f35ba | ||
|
|
cf49af1a9f | ||
|
|
186125607f | ||
|
|
508decc3f3 | ||
|
|
6d3a4b920b | ||
|
|
595df524b0 | ||
|
|
9914a5d14a | ||
|
|
fbf9d489f0 | ||
|
|
8106fb06e4 | ||
|
|
036ef0f387 | ||
|
|
307699a34c | ||
|
|
c23375793e | ||
|
|
4dc642fb4d | ||
|
|
d47e67c28c | ||
|
|
db0b861353 | ||
|
|
e40d4a48b8 | ||
|
|
8c82c0f56e | ||
|
|
c123ff9e15 |
2
Makefile
2
Makefile
@@ -3,7 +3,7 @@ CLEANCSS = ./node_modules/.bin/cleancss
|
||||
DEPLOY_DIR = libs
|
||||
LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet/
|
||||
LIBFLAC_DIR = node_modules/libflacjs/dist/min/
|
||||
OLM_DIR = node_modules/olm
|
||||
OLM_DIR = node_modules/@matrix-org/olm
|
||||
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist/
|
||||
TFLITE_WASM = react/features/stream-effects/virtual-background/vendor/tflite
|
||||
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models/
|
||||
|
||||
@@ -25,5 +25,5 @@ android.enableDexingArtifactTransform.desugaring=false
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
appVersion=21.3.0
|
||||
sdkVersion=3.8.0
|
||||
appVersion=21.4.0
|
||||
sdkVersion=3.9.0
|
||||
|
||||
@@ -56,6 +56,7 @@ dependencies {
|
||||
exclude group: 'com.android.installreferrer'
|
||||
}
|
||||
} else {
|
||||
implementation project(':amplitudereactnative')
|
||||
implementation project(':react-native-device-info')
|
||||
implementation(project(":react-native-google-signin")) {
|
||||
exclude group: 'com.google.android.gms'
|
||||
|
||||
@@ -203,6 +203,16 @@ class ReactInstanceManagerHolder {
|
||||
}
|
||||
}));
|
||||
|
||||
// AmplitudeReactNativePackage
|
||||
try {
|
||||
Class<?> amplitudePackageClass = Class.forName("com.amplitude.reactnative.AmplitudeReactNativePackage");
|
||||
Constructor constructor = amplitudePackageClass.getConstructor();
|
||||
packages.add((ReactPackage)constructor.newInstance());
|
||||
} catch (Exception e) {
|
||||
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
|
||||
}
|
||||
|
||||
// RNGoogleSigninPackage
|
||||
try {
|
||||
Class<?> googlePackageClass = Class.forName("co.apptailor.googlesignin.RNGoogleSigninPackage");
|
||||
Constructor constructor = googlePackageClass.getConstructor();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
rootProject.name = 'jitsi-meet'
|
||||
|
||||
include ':app', ':sdk'
|
||||
include ':amplitudereactnative'
|
||||
project(':amplitudereactnative').projectDir = new File(rootProject.projectDir, '../node_modules/@amplitude/react-native//android')
|
||||
include ':react-native-async-storage'
|
||||
project(':react-native-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-async-storage/async-storage/android')
|
||||
include ':react-native-background-timer'
|
||||
|
||||
2
app.js
2
app.js
@@ -2,7 +2,7 @@
|
||||
|
||||
import 'jquery';
|
||||
|
||||
import 'olm';
|
||||
import '@matrix-org/olm';
|
||||
|
||||
import 'focus-visible';
|
||||
|
||||
|
||||
@@ -2106,7 +2106,7 @@ export default {
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
|
||||
id => APP.store.dispatch(dominantSpeakerChanged(id, room)));
|
||||
(dominant, previous) => APP.store.dispatch(dominantSpeakerChanged(dominant, previous, room)));
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP,
|
||||
|
||||
36
config.js
36
config.js
@@ -27,9 +27,6 @@ var config = {
|
||||
// Websocket URL
|
||||
// websocket: 'wss://jitsi-meet.example.com/xmpp-websocket',
|
||||
|
||||
// The name of client node advertised in XEP-0115 'c' stanza
|
||||
clientNode: 'http://jitsi.org/jitsimeet',
|
||||
|
||||
// The real JID of focus participant - can be overridden here
|
||||
// Do not change username - FIXME: Make focus username configurable
|
||||
// https://github.com/jitsi/jitsi-meet/issues/7376
|
||||
@@ -73,6 +70,9 @@ var config = {
|
||||
// Enables reactions feature.
|
||||
// enableReactions: false,
|
||||
|
||||
// Disables polls feature.
|
||||
// disablePolls: false,
|
||||
|
||||
// Disables ICE/UDP by filtering out local and remote UDP candidates in
|
||||
// signalling.
|
||||
// webrtcIceUdpDisable: false,
|
||||
@@ -144,6 +144,9 @@ var config = {
|
||||
// Sets the preferred resolution (height) for local video. Defaults to 720.
|
||||
// resolution: 720,
|
||||
|
||||
// Specifies whether there will be a search field in speaker stats or not
|
||||
// disableSpeakerStatsSearch: false,
|
||||
|
||||
// How many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD.
|
||||
// Use -1 to disable.
|
||||
// maxFullResolutionParticipants: 2,
|
||||
@@ -382,6 +385,11 @@ var config = {
|
||||
// bridge itself is reachable via UDP)
|
||||
// useTurnUdp: false
|
||||
|
||||
// Enable support for encoded transform in supported browsers. This allows
|
||||
// E2EE to work in Safari if the corresponding flag is enabled in the browser.
|
||||
// Experimental.
|
||||
// enableEncodedTransformSupport: false,
|
||||
|
||||
// UI
|
||||
//
|
||||
|
||||
@@ -524,6 +532,28 @@ var config = {
|
||||
// callStatsID: '',
|
||||
// callStatsSecret: '',
|
||||
|
||||
// The callstats initialize config params as described in the API:
|
||||
// https://docs.callstats.io/docs/javascript#callstatsinitialize-with-app-secret
|
||||
// callStatsConfigParams: {
|
||||
// disableBeforeUnloadHandler: true, // disables callstats.js's window.onbeforeunload parameter.
|
||||
// applicationVersion: "app_version", // Application version specified by the developer.
|
||||
// disablePrecalltest: true, // disables the pre-call test, it is enabled by default.
|
||||
// siteID: "siteID", // The name/ID of the site/campus from where the call/pre-call test is made.
|
||||
// additionalIDs: { // additionalIDs object, contains application related IDs.
|
||||
// customerID: "Customer Identifier. Example, walmart.",
|
||||
// tenantID: "Tenant Identifier. Example, monster.",
|
||||
// productName: "Product Name. Example, Jitsi.",
|
||||
// meetingsName: "Meeting Name. Example, Jitsi loves callstats.",
|
||||
// serverName: "Server/MiddleBox Name. Example, jvb-prod-us-east-mlkncws12.",
|
||||
// pbxID: "PBX Identifier. Example, walmart.",
|
||||
// pbxExtensionID: "PBX Extension Identifier. Example, 5625.",
|
||||
// fqExtensionID: "Fully qualified Extension Identifier. Example, +71 (US) +5625.",
|
||||
// sessionID: "Session Identifier. Example, session-12-34"
|
||||
// },
|
||||
// collectLegacyStats: true, //enables the collection of legacy stats in chrome browser
|
||||
// collectIP: true //enables the collection localIP address
|
||||
// },
|
||||
|
||||
// Enables sending participants' display names to callstats
|
||||
// enableDisplayNameInStats: false,
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
JitsiConnectionErrors,
|
||||
JitsiConnectionEvents
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
import { getCustomerDetails } from './react/features/jaas/actions.any';
|
||||
import { isVpaasMeeting, getJaasJWT } from './react/features/jaas/functions';
|
||||
import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
|
||||
const logger = Logger.getLogger(__filename);
|
||||
@@ -90,9 +91,13 @@ export async function connect(id, password, roomName) {
|
||||
let { jwt } = state['features/base/jwt'];
|
||||
const { iAmRecorder, iAmSipGateway } = state['features/base/config'];
|
||||
|
||||
if (!iAmRecorder && !iAmSipGateway && !jwt && isVpaasMeeting(state)) {
|
||||
jwt = await getJaasJWT(state);
|
||||
APP.store.dispatch(setJWT(jwt));
|
||||
if (!iAmRecorder && !iAmSipGateway && isVpaasMeeting(state)) {
|
||||
await APP.store.dispatch(getCustomerDetails());
|
||||
|
||||
if (!jwt) {
|
||||
jwt = await getJaasJWT(state);
|
||||
APP.store.dispatch(setJWT(jwt));
|
||||
}
|
||||
}
|
||||
|
||||
// Use Websocket URL for the web app if configured. Note that there is no 'isWeb' check, because there's assumption
|
||||
|
||||
@@ -51,8 +51,10 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.jitsi-icon svg {
|
||||
fill: white;
|
||||
.jitsi-icon {
|
||||
&-default svg {
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
|
||||
.disabled .jitsi-icon svg {
|
||||
|
||||
@@ -574,3 +574,41 @@
|
||||
background: #36383C;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.chat-tabs-container {
|
||||
width: 100%;
|
||||
border-bottom: thin solid #292929;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.chat-tab {
|
||||
font-size: 1.2em;
|
||||
padding-bottom: 0.5em;
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
color: #8B8B8B;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.chat-tab-focus {
|
||||
border-bottom-style: solid;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.chat-tab-title {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.chat-tab-badge {
|
||||
background-color: #165ecc;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
padding: 0 4px;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
max-height: calc(80vh - 64px);
|
||||
background: #242528;
|
||||
border-radius: 16px 16px 0 0;
|
||||
overflow-y: hidden;
|
||||
overflow-y: scroll;
|
||||
margin-bottom: env(safe-area-inset-bottom, 0);
|
||||
width: 100%;
|
||||
|
||||
|
||||
@@ -5,14 +5,6 @@
|
||||
.description {
|
||||
font-size: 13px;
|
||||
margin: 15px 0;
|
||||
|
||||
.read-more {
|
||||
cursor: pointer;
|
||||
opacity: .9;
|
||||
color: #fff;
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.control-row {
|
||||
|
||||
@@ -206,13 +206,3 @@
|
||||
bottom: 0;
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes elements width to fill the whole screen width with some margin
|
||||
*/
|
||||
@mixin adjust-for-max-width($width, $margin) {
|
||||
@media (max-width: $width) {
|
||||
margin: 0 $margin;
|
||||
width: $width - 2 * $margin;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,4 +48,11 @@
|
||||
.participants_pane-content {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.jitsi-icon {
|
||||
&-dominant-speaker {
|
||||
background-color: #1EC26A;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
448
css/_polls.scss
Normal file
448
css/_polls.scss
Normal file
@@ -0,0 +1,448 @@
|
||||
.poll-dialog {
|
||||
font-size: 1rem;
|
||||
|
||||
h1, span, li, strong {
|
||||
color: #bce;
|
||||
}
|
||||
ol {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-question-field {
|
||||
padding: 8px 16px;
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid #525252;
|
||||
}
|
||||
|
||||
.poll-header {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.poll-answer-container{
|
||||
padding: 8px;
|
||||
background: #3D3D3D;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.poll-answer-field-list, .poll-answer-list, .poll-result-list {
|
||||
list-style-type: none;
|
||||
padding: 0 16px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ol.poll-result-list {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.poll-result-list > li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.poll-answer-field {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
margin-bottom: 16;
|
||||
|
||||
}
|
||||
|
||||
.poll-answer-field:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.poll-create-option-row {
|
||||
display: 'flex';
|
||||
margin-bottom: 4;
|
||||
}
|
||||
|
||||
// Needeed to override atlaskit default blue color
|
||||
.poll-create-container .jsYMHu {
|
||||
background: #292929;
|
||||
border-color: #808090;
|
||||
color: white // #808090
|
||||
}
|
||||
|
||||
.poll-add-button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.poll-remove-option-button {
|
||||
background: 0 0;
|
||||
border: none;
|
||||
color: #8B8B8B;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.poll-create-add-option {
|
||||
border: none;
|
||||
background-color: #292929;
|
||||
padding: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.poll-icon-button, .poll-drag-handle {
|
||||
.jitsi-icon svg {
|
||||
fill: #bce;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-drag-handle {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: grab;
|
||||
padding-left: 8;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.poll-dragged {
|
||||
opacity: 0.5;
|
||||
* {
|
||||
cursor: grabbing !important;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-question {
|
||||
font-size: 1.2em;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.poll-answer-voters {
|
||||
font-size: 1em;
|
||||
font-weight: lighter;
|
||||
list-style-type: none;
|
||||
border: #616161 solid 1px;
|
||||
border-radius: 3px;
|
||||
padding: 2px 6px;
|
||||
margin: 4px 0px 12px;
|
||||
background-color: #616161;
|
||||
}
|
||||
|
||||
.poll-answer-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.poll-answer-vote-name {
|
||||
flex-shrink: 1;
|
||||
overflow-wrap: anywhere
|
||||
}
|
||||
|
||||
.poll-answer-vote-count-container{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.poll-answer-vote-count {
|
||||
margin-left: 10px;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.poll-answer-short-results{
|
||||
display: flex;
|
||||
min-width: 10em;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.poll-bar-container, .poll-bar {
|
||||
border-radius: 3px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.poll-bar-container {
|
||||
background-color: #616161;
|
||||
max-width: 160px;
|
||||
margin-top: 3px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.poll-bar {
|
||||
background-color: #246FE5;
|
||||
}
|
||||
|
||||
.poll-message-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.poll-notice {
|
||||
font-weight: 100;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.poll-show-details {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-result-links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
a.poll-detail-link, a.poll-change-vote-link {
|
||||
color: #246FE5;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.polls-pane-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-weight: 600;
|
||||
height: 85%;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.pane-content{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.empty-pane-icon {
|
||||
width: 50%;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.empty-pane-icon svg {
|
||||
fill: #3D3D3D;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.empty-pane-message {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.poll-results {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.poll-answer {
|
||||
h1, strong ,span {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-results, .poll-answer {
|
||||
margin-bottom: 16px;
|
||||
background: #292929;
|
||||
border-radius: 8px;
|
||||
padding: 12px 8px;
|
||||
border-width: thin;
|
||||
border-style: solid;
|
||||
border-color: #616161;
|
||||
}
|
||||
|
||||
.poll-create-label {
|
||||
color: white;
|
||||
margin-bottom: 4;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.expandable-input{
|
||||
resize: none;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
border: 1px solid #666666;
|
||||
background-color: #141414;
|
||||
color: #FFF;
|
||||
border-radius: 6px;
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.poll-container {
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
padding: 16px;
|
||||
|
||||
& > * + *:not(.ignore-child) {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-create-header {
|
||||
font-size: 20px;
|
||||
margin: 20px 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.poll-create-container {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.poll-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 8px 16px;
|
||||
height: 40px;
|
||||
align-items: stretch;
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-primary-button {
|
||||
align-items: center;
|
||||
background-color: #0056E0;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
font-weight: unset;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
flex: 1;
|
||||
|
||||
&:hover {
|
||||
background-color: #246FE5;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #0045B3;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: #0045B3;
|
||||
border: 3px solid #99BBF3;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #00225A;
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-secondary-button {
|
||||
align-items: center;
|
||||
background-color: #3D3D3D;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
font-weight: unset;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
background-color: #525252;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #292929;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: #292929;
|
||||
border: 3px solid #858585;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #141414;
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-small-primary-button {
|
||||
align-items: center;
|
||||
background-color: #0056E0;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
font-weight: unset;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
height: 40px;
|
||||
width: 50%;
|
||||
|
||||
&:hover {
|
||||
background-color: #246FE5;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #0045B3;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: #0045B3;
|
||||
border: 3px solid #99BBF3;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #00225A;
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.poll-small-secondary-button {
|
||||
align-items: center;
|
||||
background-color: #3D3D3D;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
font-weight: unset;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
height: 40px;
|
||||
width: 50%;
|
||||
|
||||
&:hover {
|
||||
background-color: #525252;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #292929;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: #292929;
|
||||
border: 3px solid #858585;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background-color: #141414;
|
||||
color: #858585;
|
||||
}
|
||||
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
.prejoin {
|
||||
|
||||
&-input-area {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
|
||||
&-label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: #ffffff;
|
||||
font-weight: 300;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&-title {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
&-text-btns {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&-input-label {
|
||||
color: #A4B8D1;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin-top: 32px 0 8px 0;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-checkbox {
|
||||
border: 0;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
padding: 0;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
&-checkbox-container {
|
||||
margin-bottom: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-error {
|
||||
color: white;
|
||||
background-color: rgba(225, 45, 45, 0.6);
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
padding: 2px;
|
||||
box-sizing: border-box;
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin name-placeholder {
|
||||
color: #fff;
|
||||
font-weight: 300;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.prejoin-preview {
|
||||
&-status {
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
bottom: 0;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
min-height: 24px;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
|
||||
&--warning {
|
||||
background: rgba(241, 173, 51, 1);
|
||||
}
|
||||
&--ok {
|
||||
background: rgba(49, 183, 106, 1);
|
||||
}
|
||||
}
|
||||
|
||||
&-icon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
&-error-desc {
|
||||
margin-right: 4px;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.settings-button-container {
|
||||
width: 49px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
&-dropdown-btns {
|
||||
width: 320px;
|
||||
padding: 8px 0;
|
||||
|
||||
@include adjust-for-max-width(320px, 8px);
|
||||
}
|
||||
|
||||
&-dropdown-btn {
|
||||
align-items: center;
|
||||
color: #1C2025;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
padding: 0 16px;
|
||||
|
||||
&:hover {
|
||||
background-color: #DAEBFA;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-icon {
|
||||
display: inline-block;
|
||||
margin-right: 16px;
|
||||
|
||||
& > svg {
|
||||
fill: #1C2025;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-container {
|
||||
margin-top: 16px;
|
||||
|
||||
& > div:nth-child(2) {
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -48,6 +48,13 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: font-size ease .1s;
|
||||
|
||||
@for $i from 1 through 12 {
|
||||
&.increase-#{$i}{
|
||||
font-size: calc(20px + #{$i}px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -334,7 +334,7 @@
|
||||
border-radius: 0;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
padding: 6px 0;
|
||||
padding: 8px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -264,3 +264,9 @@ $chromeExtensionBannerRightInMeeeting: 10px;
|
||||
*/
|
||||
$smallScreen: 700px;
|
||||
$verySmallScreen: 500px;
|
||||
|
||||
/**
|
||||
* Prejoin / premeeting screen
|
||||
*/
|
||||
|
||||
$prejoinDefaultContentWidth: 336px;
|
||||
@@ -79,7 +79,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'filmstrip/vertical_filmstrip';
|
||||
@import 'filmstrip/vertical_filmstrip_overrides';
|
||||
@import 'labels';
|
||||
@import 'lobby';
|
||||
@import 'unsupported-browser/main';
|
||||
@import 'modals/invite/add-people';
|
||||
@import 'deep-linking/main';
|
||||
@@ -95,18 +94,16 @@ $flagsImagePath: "../images/";
|
||||
@import 'meter';
|
||||
@import 'audio-preview';
|
||||
@import 'video-preview';
|
||||
@import 'prejoin';
|
||||
@import 'prejoin-dialog';
|
||||
@import 'premeeting/main';
|
||||
@import 'country-picker';
|
||||
@import 'modals/invite/invite_more';
|
||||
@import 'modals/security/security';
|
||||
@import 'premeeting-screens';
|
||||
@import 'e2ee';
|
||||
@import 'responsive';
|
||||
@import 'connection-status';
|
||||
@import 'drawer';
|
||||
@import 'participants-pane';
|
||||
@import 'reactions-menu';
|
||||
@import 'plan-limit';
|
||||
@import 'polls';
|
||||
|
||||
/* Modules END */
|
||||
|
||||
@@ -1,32 +1,24 @@
|
||||
.con-status {
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.16px;
|
||||
line-height: 16px;
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
width: 100%;
|
||||
z-index: $toolbarZ + 3;
|
||||
|
||||
&-container {
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
line-height: 13px;
|
||||
margin: 0 auto;
|
||||
width: 320px;
|
||||
|
||||
@include adjust-for-max-width(320px, 8px);
|
||||
}
|
||||
|
||||
&-header {
|
||||
background: rgba(28, 32, 37, .5);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
&-circle {
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
margin: 8px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&--good {
|
||||
@@ -42,14 +34,7 @@
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
border-radius: 3px;
|
||||
margin-left: 8px;
|
||||
margin-right: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
transition: background-color 0.16s ease-out;
|
||||
|
||||
&--up {
|
||||
@@ -70,7 +55,7 @@
|
||||
}
|
||||
|
||||
&-details {
|
||||
background: rgba(28, 32, 37, .5);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-top: 1px solid #5E6D7A;
|
||||
padding: 16px;
|
||||
transition: opacity 0.16s ease-out;
|
||||
35
css/premeeting/_device-status.scss
Normal file
35
css/premeeting/_device-status.scss
Normal file
@@ -0,0 +1,35 @@
|
||||
.device {
|
||||
&-status {
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
justify-content: center;
|
||||
line-height: 20px;
|
||||
margin-top: 8px;
|
||||
padding: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
margin-right: 10px;
|
||||
width: 16px;
|
||||
|
||||
&--warning {
|
||||
svg path {
|
||||
fill: rgba(241, 173, 51, 1);
|
||||
}
|
||||
}
|
||||
&--ok {
|
||||
svg path {
|
||||
fill: #189b55;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,21 @@
|
||||
#lobby-screen {
|
||||
.content {
|
||||
.lobby-screen {
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 26px;
|
||||
|
||||
.container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
&-content {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.spinner {
|
||||
margin: 30px;
|
||||
}
|
||||
.spinner {
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.joining-message {
|
||||
margin: 10px;
|
||||
}
|
||||
.joining-message {
|
||||
color: white;
|
||||
margin: 24px auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +54,10 @@
|
||||
top: 30px;
|
||||
z-index: $toolbarZ + 1;
|
||||
|
||||
&:empty {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.toolbox-visible {
|
||||
// Same as toolbox subject position
|
||||
top: 120px;
|
||||
@@ -64,7 +71,7 @@
|
||||
|
||||
button {
|
||||
align-self: stretch;
|
||||
margin: 8px 0;
|
||||
margin-bottom: 8px 0;
|
||||
padding: 12px;
|
||||
transition: .2s transform ease;
|
||||
|
||||
7
css/premeeting/_main.scss
Normal file
7
css/premeeting/_main.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
@import 'connection-status';
|
||||
@import 'device-status';
|
||||
@import 'lobby';
|
||||
@import 'premeeting-screens';
|
||||
@import 'prejoin';
|
||||
@import 'prejoin-dialog';
|
||||
@import 'prejoin-third-party';
|
||||
39
css/premeeting/_prejoin-third-party.scss
Normal file
39
css/premeeting/_prejoin-third-party.scss
Normal file
@@ -0,0 +1,39 @@
|
||||
$sidePanelWidth: 300px;
|
||||
|
||||
.prejoin-third-party {
|
||||
flex-direction: column-reverse;
|
||||
|
||||
.content {
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
|
||||
.new-toolbox {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
#preview {
|
||||
background-color: transparent;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
||||
.avatar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.splash {
|
||||
.content {
|
||||
margin-left: calc((100% - #{$prejoinDefaultContentWidth} + #{$sidePanelWidth}) / 2)
|
||||
}
|
||||
}
|
||||
|
||||
&.guest {
|
||||
.content {
|
||||
margin-bottom: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
css/premeeting/_prejoin.scss
Normal file
73
css/premeeting/_prejoin.scss
Normal file
@@ -0,0 +1,73 @@
|
||||
.prejoin {
|
||||
&-input-area {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-checkbox-container {
|
||||
margin-bottom: 16px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-error {
|
||||
color: white;
|
||||
background-color: #E04757;
|
||||
border-radius: 6px;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 16px;
|
||||
margin-top: -8px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.prejoin-preview {
|
||||
&-dropdown-btns {
|
||||
padding: 8px 0;
|
||||
width: calc(100% - 48px);
|
||||
}
|
||||
|
||||
&-dropdown-btn {
|
||||
align-items: center;
|
||||
color: #1C2025;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: 40px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
padding: 0 16px;
|
||||
|
||||
&:hover {
|
||||
background-color: #DAEBFA;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-icon {
|
||||
display: inline-block;
|
||||
margin-right: 16px;
|
||||
|
||||
& > svg {
|
||||
fill: #1C2025;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
/**
|
||||
* Override default InlineDialog behaviour, since it does not play nicely with relative widths
|
||||
*/
|
||||
& > div:nth-child(2) {
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
position: absolute !important;
|
||||
top: 48px !important;
|
||||
transform: none !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,27 @@
|
||||
/**
|
||||
* Shared style for full screen local track based dialogs/modals.
|
||||
*/
|
||||
.premeeting-screen {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.premeeting-screen {
|
||||
align-items: stretch;
|
||||
background: radial-gradient(50% 50% at 50% 50%, #2A3A4B 20.83%, #1E2A36 100%);
|
||||
background: #292929;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 1.3em;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: $toolbarZ + 1;
|
||||
|
||||
&-avatar {
|
||||
background-color: #A4B8D1;
|
||||
margin-bottom: 24px;
|
||||
|
||||
text {
|
||||
fill: black;
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
border-radius: 3px;
|
||||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
margin-bottom: 16px;
|
||||
padding: 7px 16px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 320px;
|
||||
|
||||
@include adjust-for-max-width(320px, 8px);
|
||||
width: 100%;
|
||||
|
||||
&.primary {
|
||||
background: #0376DA;
|
||||
@@ -49,8 +29,8 @@
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background: transparent;
|
||||
border: 1px solid #5E6D7A;
|
||||
background: #3D3D3D;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
&.text {
|
||||
@@ -96,130 +76,150 @@
|
||||
|
||||
.content {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: 24px;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
margin: 0 110px;
|
||||
padding: 24px 0 16px;
|
||||
position: relative;
|
||||
width: $prejoinDefaultContentWidth;
|
||||
z-index: $toolbarZ + 2;
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.copy-meeting {
|
||||
&-controls {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
justify-content: center;
|
||||
line-height: 24px;
|
||||
margin-bottom: 16px;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
|
||||
.url {
|
||||
background: rgba(28, 32, 37, 0.5);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
padding: 8px 10px;
|
||||
transition: background 0.16s ease-out;
|
||||
|
||||
&:hover {
|
||||
background: #1C2025;
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.015;
|
||||
line-height: 36px;
|
||||
margin-bottom: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input.field {
|
||||
background-color: white;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
margin-bottom: 16px;
|
||||
color: #1C2025;
|
||||
padding: 10px 16px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
&.error {
|
||||
border: 1px solid #E04757;
|
||||
}
|
||||
|
||||
&.done {
|
||||
background: #31B76A;
|
||||
}
|
||||
|
||||
.jitsi-icon {
|
||||
margin-left: 10px;
|
||||
|
||||
&.focused {
|
||||
box-shadow: 0px 0px 1px 1.5px black, 0px 0px 1.3px 4px white;
|
||||
}
|
||||
}
|
||||
.copy-button{
|
||||
width: 298px;
|
||||
}
|
||||
|
||||
.copy-meeting-text {
|
||||
width: 266px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#new-toolbox {
|
||||
bottom: 0;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
transition: none;
|
||||
|
||||
&:hover {
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border-width: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
padding: 0;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
input.field {
|
||||
background-color: white;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 3px;
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
color: #1C2025;
|
||||
padding: 8px 0;
|
||||
text-align: center;
|
||||
width: 320px;
|
||||
|
||||
@include adjust-for-max-width(320px, 8px);
|
||||
|
||||
&.error {
|
||||
box-shadow: 0px 0px 4px 3px rgba(225, 45, 45, 0.4);
|
||||
}
|
||||
|
||||
&.focused {
|
||||
box-shadow: 0px 0px 1px 1.5px black, 0px 0px 1.3px 4px white;
|
||||
.toolbox-content,
|
||||
.toolbox-content-wrapper,
|
||||
.toolbox-content-items {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media-btn-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 24px 0 16px 0;
|
||||
width: 100%;
|
||||
|
||||
&> div {
|
||||
margin: 0 12px;
|
||||
@media (max-width: 1000px) {
|
||||
flex-direction: column-reverse;
|
||||
|
||||
.content {
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.con-status {
|
||||
margin: 24px auto;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: $prejoinDefaultContentWidth;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.content {
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
letter-spacing: -0.012;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.con-status {
|
||||
margin: 16px;
|
||||
width: calc(100% - 32px);
|
||||
}
|
||||
|
||||
input.field {
|
||||
font-size: 16px;
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
font-size: 16px;
|
||||
padding: 11px 16px;
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
border-radius: 0;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
color: #040404;
|
||||
}
|
||||
}
|
||||
|
||||
#preview {
|
||||
background: #040404;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
&.no-video {
|
||||
background: radial-gradient(50% 50% at 50% 50%, #5B6F80 0%, #365067 100%), #FFFFFF;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
background: #A4B8D1;
|
||||
margin: 0 auto;
|
||||
background: #0045B3;
|
||||
|
||||
text {
|
||||
fill: white;
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
video {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -241,16 +241,14 @@
|
||||
}
|
||||
|
||||
.toggle-button {
|
||||
border-radius: 3px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
height: 40px;
|
||||
margin: 0 auto;
|
||||
transition: background 0.16s ease-out;
|
||||
width: 320px;
|
||||
|
||||
@include adjust-for-max-width(320px, 8px);
|
||||
@include flex-centered();
|
||||
|
||||
svg {
|
||||
@@ -65,6 +65,7 @@ Component "conference.jitmeet.example.com" "muc"
|
||||
modules_enabled = {
|
||||
"muc_meeting_id";
|
||||
"muc_domain_mapper";
|
||||
"polls";
|
||||
--"token_verification";
|
||||
}
|
||||
admins = { "focusUser@auth.jitmeet.example.com" }
|
||||
|
||||
10
index.html
10
index.html
@@ -25,8 +25,16 @@
|
||||
Component: JitsiMeetJS.app.entryPoints.APP
|
||||
})
|
||||
|
||||
const inIframe = () => {
|
||||
try {
|
||||
return window.self !== window.top;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const isElectron = navigator.userAgent.includes('Electron');
|
||||
const shouldRegisterWorker = !isElectron && 'serviceWorker' in navigator;
|
||||
const shouldRegisterWorker = !isElectron && !inIframe() && 'serviceWorker' in navigator;
|
||||
|
||||
if (shouldRegisterWorker) {
|
||||
navigator.serviceWorker
|
||||
|
||||
@@ -29,7 +29,7 @@ target 'JitsiMeetSDK' do
|
||||
'CoreModulesHeaders',
|
||||
'DevSupport',
|
||||
'RCTWebSocket'
|
||||
]
|
||||
], :modular_headers => true
|
||||
pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
|
||||
pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
|
||||
pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
|
||||
@@ -54,6 +54,7 @@ target 'JitsiMeetSDK' do
|
||||
# React Native plugins
|
||||
#
|
||||
|
||||
pod 'amplitude-react-native', :path => '../node_modules/@amplitude/react-native'
|
||||
pod 'react-native-background-timer', :path => '../node_modules/react-native-background-timer'
|
||||
pod 'react-native-calendar-events', :path => '../node_modules/react-native-calendar-events'
|
||||
pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
PODS:
|
||||
- Amplitude (8.2.0)
|
||||
- amplitude-react-native (2.3.3):
|
||||
- Amplitude (= 8.2.0)
|
||||
- React-Core
|
||||
- AppAuth (1.4.0):
|
||||
- AppAuth/Core (= 1.4.0)
|
||||
- AppAuth/ExternalUserAgent (= 1.4.0)
|
||||
@@ -372,6 +376,7 @@ PODS:
|
||||
- Yoga (1.14.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- "amplitude-react-native (from `../node_modules/@amplitude/react-native`)"
|
||||
- CocoaLumberjack (~> 3.5.3)
|
||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector/`)
|
||||
@@ -423,6 +428,7 @@ DEPENDENCIES:
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- Amplitude
|
||||
- AppAuth
|
||||
- boost-for-react-native
|
||||
- CocoaLumberjack
|
||||
@@ -444,6 +450,8 @@ SPEC REPOS:
|
||||
- PromisesObjC
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
amplitude-react-native:
|
||||
:path: "../node_modules/@amplitude/react-native"
|
||||
DoubleConversion:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||
FBLazyVector:
|
||||
@@ -528,6 +536,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Amplitude: 263118b9e691e73a1c864b05bb08a3aff3636d16
|
||||
amplitude-react-native: 833a4bd7f656f826bda1de01a7b8593b58842209
|
||||
AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7
|
||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||
CocoaLumberjack: 2f44e60eb91c176d471fdba43b9e3eae6a721947
|
||||
@@ -589,6 +599,6 @@ SPEC CHECKSUMS:
|
||||
RNWatch: a5320c959c75e72845c07985f3e935e58998f1d3
|
||||
Yoga: 96b469c5e81ff51b917b92e8c3390642d4ded30c
|
||||
|
||||
PODFILE CHECKSUM: f4db44d934caeae7212dbaa33abe62ed164363e8
|
||||
PODFILE CHECKSUM: 1ae1a9823f3eab0b6e735b9637ba7588e0890d08
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.3.0</string>
|
||||
<string>21.4.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.3.0</string>
|
||||
<string>21.4.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.3.0</string>
|
||||
<string>21.4.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.3.0</string>
|
||||
<string>21.4.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
@@ -437,10 +437,12 @@
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-JitsiMeetSDK/Pods-JitsiMeetSDK-resources.sh",
|
||||
"${PODS_ROOT}/Amplitude/Sources/Amplitude/Resources/ComodoRsaDomainValidationCA.der",
|
||||
"${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ComodoRsaDomainValidationCA.der",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@@ -125,12 +125,19 @@ RCT_EXPORT_MODULE();
|
||||
return _workerQueue;
|
||||
}
|
||||
|
||||
- (BOOL)setConfigWithoutLock:(RTCAudioSessionConfiguration *)config
|
||||
error:(NSError * _Nullable *)outError {
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
|
||||
return [session setConfiguration:config error:outError];
|
||||
}
|
||||
|
||||
- (BOOL)setConfig:(RTCAudioSessionConfiguration *)config
|
||||
error:(NSError * _Nullable *)outError {
|
||||
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
[session lockForConfiguration];
|
||||
BOOL success = [session setConfiguration:config error:outError];
|
||||
BOOL success = [self setConfigWithoutLock:config error:outError];
|
||||
[session unlockForConfiguration];
|
||||
|
||||
return success;
|
||||
@@ -196,7 +203,7 @@ RCT_EXPORT_METHOD(setAudioDevice:(NSString *)device
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (port != nil) {
|
||||
// First remove the override if we are going to select a different device.
|
||||
if (isSpeakerOn) {
|
||||
@@ -206,11 +213,11 @@ RCT_EXPORT_METHOD(setAudioDevice:(NSString *)device
|
||||
// Special case for the earpiece.
|
||||
if ([port.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
|
||||
forceEarpiece = YES;
|
||||
[self setConfig:earpieceConfig error:nil];
|
||||
[self setConfigWithoutLock:earpieceConfig error:nil];
|
||||
} else if (isEarpieceOn) {
|
||||
// Reset the config.
|
||||
RTCAudioSessionConfiguration *config = [self configForMode:activeMode];
|
||||
[self setConfig:config error:nil];
|
||||
[self setConfigWithoutLock:config error:nil];
|
||||
}
|
||||
|
||||
// Select our preferred input.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.8.0</string>
|
||||
<string>3.9.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -586,6 +586,7 @@
|
||||
},
|
||||
"speaker": "Говорещ",
|
||||
"speakerStats": {
|
||||
"search": "Търсене",
|
||||
"hours": "{{count}}ч",
|
||||
"minutes": "{{count}}мин",
|
||||
"name": "Име",
|
||||
|
||||
@@ -209,7 +209,6 @@
|
||||
"e2eeLabel": "Ende-zu-Ende-Verschlüsselung aktivieren",
|
||||
"e2eeWarning": "WARNUNG: Nicht alle Personen dieser Konferenz scheinen Ende-zu-Ende-Verschlüsselung zu unterstützen. Wenn Sie diese aktivieren, können die entsprechenden Personen nichts mehr sehen oder hören.",
|
||||
"enterDisplayName": "Bitte geben Sie hier Ihren Namen ein",
|
||||
"enterDisplayNameToJoin" : "Benutzername für Konferenz eingeben" ,
|
||||
"embedMeeting": "Besprechung einbetten",
|
||||
"error": "Fehler",
|
||||
"gracefulShutdown": "Der Dienst steht momentan wegen Wartungsarbeiten nicht zur Verfügung. Bitte versuchen Sie es später noch einmal.",
|
||||
@@ -802,6 +801,7 @@
|
||||
},
|
||||
"speaker": "Sprecher/-in",
|
||||
"speakerStats": {
|
||||
"search": "Suche",
|
||||
"hours": "{{count}} Std. ",
|
||||
"minutes": "{{count}} Min. ",
|
||||
"name": "Name",
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
"e2eeLabel": "Ŝlosilo",
|
||||
"e2eeTitle": "Tutvoja ĉifrado",
|
||||
"e2eeWarning": "<br /><p><strong>ATENTIGO:</strong> Ne ĉiuj partoprenantoj en ĉi tiu kunveno ŝajnas havi subtenon de tutvoja ĉifrado. Se vi ŝaltos ĝin, ili ne povos vidi aŭ aŭdi vin.</p>",
|
||||
"enterDisplayName": "Please enter your name here",
|
||||
"enterDisplayName": "Enter your name here",
|
||||
"error": "Eraro",
|
||||
"externalInstallationMsg": "Vi devas instali nian ekranvidadan kromprogramon.",
|
||||
"externalInstallationTitle": "Kromprogramo bezonata",
|
||||
|
||||
@@ -203,7 +203,6 @@
|
||||
"e2eeLabel": "Aktibatu puntutik punturako zifratzea",
|
||||
"e2eeWarning": "OHARRA: bileraren partaide guztiek ezin dute puntutik punturako zifratzea erabili. Aukera hau aktibatzen baduzu, batzuk ezingo zaituzte ikusi eta entzun.",
|
||||
"enterDisplayName": "Sartu zure izena hemen",
|
||||
"enterDisplayNameToJoin": "Mesedez idatzi zure izena bileran sartzeko",
|
||||
"embedMeeting": "Kapsulatu bilera",
|
||||
"error": "Errorea",
|
||||
"gracefulShutdown": "Zerbitzua ez dago erabilgarri mantentze-lanak direla eta. Saiatu berriro beranduago.",
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"today": "Aujourd'hui"
|
||||
},
|
||||
"chat": {
|
||||
"enter": "Entrez dans le salon de chat",
|
||||
"enter": "Entrez dans le salon",
|
||||
"error": "Erreur : votre message n'a pas été envoyé. Raison : {{error}}",
|
||||
"fieldPlaceHolder": "Tapez votre message ici",
|
||||
"messagebox": "Saisissez un message",
|
||||
@@ -66,15 +66,19 @@
|
||||
"noMessagesMessage": "Il n'y a pas encore de messages dans cette réunion. Démarrez une conversation ici !",
|
||||
"nickname": {
|
||||
"popover": "Choisissez un pseudonyme",
|
||||
"title": "Entrez un pseudonyme pour utiliser le chat"
|
||||
"title": "Entrez un pseudonyme pour utiliser le chat et les sondages"
|
||||
},
|
||||
"privateNotice": "Message privé à {{recipient}}",
|
||||
"title": "Chat",
|
||||
"you": "vous",
|
||||
"message": "Message",
|
||||
"messageAccessibleTitle": "{{user}} dit: ",
|
||||
"messageAccessibleTitleMe": "Je dis: ",
|
||||
"smileysPanel": "Panneaux des Émojis"
|
||||
"smileysPanel": "Panneaux des Émojis",
|
||||
"tabs": {
|
||||
"chat": "Chat",
|
||||
"polls": "Sondages"
|
||||
},
|
||||
"title": "Chat et Sondages",
|
||||
"you": "vous"
|
||||
},
|
||||
"chromeExtensionBanner": {
|
||||
"installExtensionText": "Installer l'extension pour l'intégration de Google Calendar et Office 365",
|
||||
@@ -209,7 +213,6 @@
|
||||
"e2eeLabel": "Activer le chiffrement de Bout-en-Bout",
|
||||
"e2eeWarning": "ATTENTION : Tous les participants de cette réunion ne semblent pas prendre en charge le chiffrement de Bout-en-Bout. Si vous activez le chiffrement, ils ne pourront ni vous voir, ni vous entendre.",
|
||||
"enterDisplayName": "Merci de saisir votre nom ici",
|
||||
"enterDisplayNameToJoin": "Merci de saisir votre nom pour rejoindre",
|
||||
"embedMeeting": "Intégrer la réunion",
|
||||
"error": "Erreur",
|
||||
"gracefulShutdown": "Notre service est actuellement en maintenance. Veuillez réessayer plus tard.",
|
||||
@@ -568,6 +571,34 @@
|
||||
},
|
||||
"passwordSetRemotely": "défini par un autre participant",
|
||||
"passwordDigitsOnly": "Jusqu'à {{number}} chiffres",
|
||||
"polls": {
|
||||
"create": {
|
||||
"addOption": "Ajouter une option",
|
||||
"answerPlaceholder": "Option {{index}}",
|
||||
"create": "Créer un sondage",
|
||||
"cancel": "Annuler",
|
||||
"pollOption" : "Option {{index}}",
|
||||
"pollQuestion" : "Question du sondage",
|
||||
"questionPlaceholder": "Poser une question",
|
||||
"removeOption": "Supprimer l'option",
|
||||
"send": "Envoyer"
|
||||
},
|
||||
"answer": {
|
||||
"skip": "Passer",
|
||||
"submit": "Envoyer"
|
||||
},
|
||||
"results": {
|
||||
"vote": "Voter",
|
||||
"changeVote": "Changer le vote",
|
||||
"empty": "Il n'y a pas encore de sondages dans cette réunion. Démarrez un sondage ici !",
|
||||
"hideDetailedResults": "Cacher les détails",
|
||||
"showDetailedResults": "Montrer les détails"
|
||||
},
|
||||
"notification": {
|
||||
"title": "Un nouveau sondage a été ajouté à la réunion",
|
||||
"description": "Ouvrez l'onget des sondages pour voter"
|
||||
}
|
||||
},
|
||||
"poweredby": "produit par",
|
||||
"prejoin": {
|
||||
"audioAndVideoError": "Erreur audio et video:",
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
"dismiss": "Dismiss",
|
||||
"displayNameRequired": "Hi! What’s your name?",
|
||||
"done": "Done",
|
||||
"enterDisplayName": "Please enter your name here",
|
||||
"enterDisplayName": "Enter your name here",
|
||||
"error": "Error",
|
||||
"externalInstallationMsg": "You need to install our desktop sharing extension.",
|
||||
"externalInstallationTitle": "Extension required",
|
||||
|
||||
@@ -58,17 +58,26 @@
|
||||
"today": "Hoje"
|
||||
},
|
||||
"chat": {
|
||||
"enter": "Entrar na sala",
|
||||
"error": "Erro: a sua mensagem não foi enviada. Motivo: {{error}}",
|
||||
"fieldPlaceHolder": "",
|
||||
"messagebox": "Digite uma mensagem",
|
||||
"fieldPlaceHolder": "Escreva aqui a sua mensagem",
|
||||
"messagebox": "Escreva uma mensagem",
|
||||
"messageTo": "Mensagem privada para {{recipient}}",
|
||||
"noMessagesMessage": "",
|
||||
"noMessagesMessage": "Ainda não há mensagens na reunião. Comece aqui uma conversa!",
|
||||
"nickname": {
|
||||
"popover": "Escolha um apelido",
|
||||
"title": "Digite um apelido para usar na conversação"
|
||||
"title": "Introduza um apelido para usar no chat e nas sondagens"
|
||||
},
|
||||
"privateNotice": "Mensagem privada para {{recipient}}",
|
||||
"title": "Conversação",
|
||||
"message": "Mensagem",
|
||||
"messageAccessibleTitle": "{{user}} disse:",
|
||||
"messageAccessibleTitleMe": "Você disse:",
|
||||
"smileysPanel": "Painel de Emojis",
|
||||
"tabs": {
|
||||
"chat": "Chat",
|
||||
"polls": "Sondagens"
|
||||
},
|
||||
"title": "Chat e Sondagens",
|
||||
"you": "você"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
@@ -184,11 +193,10 @@
|
||||
"dismiss": "Dispensar",
|
||||
"displayNameRequired": "Olá! Qual é o seu nome?",
|
||||
"done": "Feito",
|
||||
"e2eeDescription": "A encriptação de ponta a ponta é actualmente EXPERIMENTAL. Tenha em mente que ligar a encriptação de ponta a ponta irá efectivamente desactivar os serviços fornecidos do lado do servidor, tais como: gravação, transmissão em directo e participação telefónica. Tenha também em mente que o encontro só funcionará para pessoas que se juntem a partir de browsers com suporte para \"insertable streams\".",
|
||||
"e2eeDescription": "A encriptação de ponta a ponta é actualmente EXPERIMENTAL. Tenha em mente que ligar a encriptação de ponta a ponta irá efectivamente desactivar os serviços fornecidos do lado do servidor, tais como: gravação, transmissão em direto e participação telefónica. Tenha também em mente que o encontro só funcionará para pessoas que se juntem a partir de browsers com suporte para \"insertable streams\".",
|
||||
"e2eeLabel": "Habilitar encriptação de ponta a ponta",
|
||||
"e2eeWarning": "AVISO: Nem todos os participantes neste encontro parecem ter apoio para a encriptação de ponta a ponta. Se o permitir, eles não o poderão ver nem ouvir.",
|
||||
"enterDisplayName": "Digite o seu nome aqui",
|
||||
"enterDisplayNameToJoin": "Por favor, digite o seu nome para participar",
|
||||
"embedMeeting": "Embutir reunião",
|
||||
"error": "Erro",
|
||||
"gracefulShutdown": "O nosso serviço está atualmente em manutenção. Por favor, tente novamente mais tarde.",
|
||||
@@ -279,11 +287,11 @@
|
||||
"sessTerminated": "Chamada terminada",
|
||||
"sessionRestarted": "Chamada reiniciada pela ponte",
|
||||
"Share": "Partilhar",
|
||||
"shareVideoLinkError": "Por favor, forneça uma ligação correcta ao youtube.",
|
||||
"shareVideoLinkError": "Por favor, forneça um link correcto do vídeo.",
|
||||
"shareVideoTitle": "Partilhar vídeo",
|
||||
"shareYourScreen": "Partilhe o seu ecrã",
|
||||
"shareYourScreenDisabled": "Partilha de ecrã desactivada.",
|
||||
"startLiveStreaming": "Iniciar a transmissão em directo",
|
||||
"startLiveStreaming": "Iniciar a transmissão em direto",
|
||||
"startRecording": "Iniciar gravação",
|
||||
"startRemoteControlErrorMessage": "Ocorreu um erro ao tentar iniciar a sessão de controlo remoto!",
|
||||
"stopLiveStreaming": "Parar a transmissão em direto",
|
||||
@@ -301,7 +309,7 @@
|
||||
"user": "Utilizador",
|
||||
"userIdentifier": "Identificador do utilizador",
|
||||
"userPassword": "Palavra-passe do utilizador",
|
||||
"videoLink": "Ligação do vídeo",
|
||||
"videoLink": "Link do vídeo",
|
||||
"WaitForHostMsg": "A conferência <b>{{room}}</b> ainda não começou. Se for o anfitrião, por favor autentique. Caso contrário, por favor aguarde que o anfitrião chegue.",
|
||||
"WaitForHostMsgWOk": "A conferência <b>{{room}}</b> ainda não começou. Se for o anfitrião, por favor prima Ok para autenticar. Caso contrário, por favor aguarde que o anfitrião chegue.",
|
||||
"WaitingForHostTitle": "À espera do anfitrião ...",
|
||||
@@ -345,13 +353,13 @@
|
||||
"dialInSummaryError": "Ocorreu um erro ao buscar a informação de discagem. Tente novamente mais tarde.",
|
||||
"dialInTollFree": "Chamada gratuita",
|
||||
"genericError": "Oops, alguma coisa deu errado.",
|
||||
"inviteLiveStream": "Para ver a transmissão ao vivo da reunião, clique no link: {{url}}",
|
||||
"inviteLiveStream": "Para ver a transmissão em direto da reunião, clique no link: {{url}}",
|
||||
"invitePhone": "Para participar por telefone, toque aqui: {{number}} ,, {{conferenceID}} # \\ n",
|
||||
"invitePhoneAlternatives": "Procurando um número de discagem diferente?\nVeja os números de discagem da reunião: {{url}} \n\n\nSe você também estiver discando através de um telefone da sala, participe sem conectar-se ao áudio: {{silentUrl}}",
|
||||
"inviteURLFirstPartGeneral": "Você foi convidado para uma reunião.",
|
||||
"inviteURLFirstPartPersonal": "{{name}} está convidando você para uma reunião.\n",
|
||||
"inviteURLSecondPart": "\nEntre na reunião:\n{{url}}\n",
|
||||
"liveStreamURL": "Transmissão ao vivo:",
|
||||
"liveStreamURL": "Transmissão em direto:",
|
||||
"moreNumbers": "Mais números",
|
||||
"noNumbers": "Sem números de discagem.",
|
||||
"noPassword": "Nenhum",
|
||||
@@ -379,49 +387,49 @@
|
||||
"keyboardShortcuts": {
|
||||
"focusLocal": "Focar no seu vídeo",
|
||||
"focusRemote": "Focar no vídeo de outro participante",
|
||||
"fullScreen": "Entrar ou sair da tela cheia",
|
||||
"fullScreen": "Entrar ou sair do ecrã completo",
|
||||
"keyboardShortcuts": "Atalhos de teclado",
|
||||
"localRecording": "Mostrar ou ocultar controlos de gravação local",
|
||||
"mute": "Deixar mudo ou não o microfone",
|
||||
"mute": "Ligar ou desligar o seu microfone",
|
||||
"pushToTalk": "Pressione para falar",
|
||||
"raiseHand": "Erga ou baixe sua mão",
|
||||
"showSpeakerStats": "Exibir estatísticas do alto falante",
|
||||
"toggleChat": "Abrir ou fechar o painel de bate-papo",
|
||||
"raiseHand": "Levantar ou baixar a sua mão",
|
||||
"showSpeakerStats": "Mostrar as estatísticas dos participantes",
|
||||
"toggleChat": "Abrir ou fechar o painel de chat",
|
||||
"toggleFilmstrip": "Mostrar ou ocultar miniaturas de vídeo",
|
||||
"toggleScreensharing": "Trocar entre câmera e compartilhamento de tela",
|
||||
"toggleParticipantsPane": "Mostrar ou ocultar o painel de participantes",
|
||||
"toggleScreensharing": "Alternar entre a partilha de câmara e de ecrã",
|
||||
"toggleShortcuts": "Mostrar ou ocultar atalhos de teclado",
|
||||
"videoMute": "Iniciar ou parar sua câmera",
|
||||
"videoQuality": "Gerenciar qualidade da chamada"
|
||||
"videoMute": "Iniciar ou parar a sua câmara"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"busy": "Estamos trabalhando para liberar os recursos de transmissão. Tente novamente em alguns minutos.",
|
||||
"busyTitle": "Todas as transmissões estão atualmente ocupadas",
|
||||
"changeSignIn": "Alternar contas.",
|
||||
"choose": "Escolha uma transmissão ao vivo",
|
||||
"choose": "Escolha uma transmissão em direto",
|
||||
"chooseCTA": "Escolha uma opção de transmissão. Você está conectado atualmente como {{email}}.",
|
||||
"enterStreamKey": "Insira sua chave de transmissão ao vivo do YouTube aqui.",
|
||||
"error": "Falha na transmissão ao vivo. Tente de novo.",
|
||||
"enterStreamKey": "Insira sua chave de transmissão em direto do YouTube aqui.",
|
||||
"error": "Falha na transmissão em direto. Tente de novo.",
|
||||
"errorAPI": "Ocorreu um erro ao acessar suas transmissões do YouTube. Por favor tente logar novamente.",
|
||||
"errorLiveStreamNotEnabled": "Transmissão ao vivo não está ativada em {{email}}. Ative a transmissão ao vivo ou registre numa conta com transmissão ao vivo ativada.",
|
||||
"expandedOff": "A transmissão ao vivo foi encerrada",
|
||||
"errorLiveStreamNotEnabled": "Transmissão em direto não está ativada em {{email}}. Ative a transmissão em direto ou registre numa conta com transmissão direto ativada.",
|
||||
"expandedOff": "A transmissão em direto foi encerrada",
|
||||
"expandedOn": "A reunião está sendo transmitida pelo YouTube.",
|
||||
"expandedPending": "Iniciando a transmissão ao vivo...",
|
||||
"failedToStart": "Falha ao iniciar a transmissão ao vivo",
|
||||
"getStreamKeyManually": "Não conseguimos buscar nenhuma transmissão ao vivo. Tente obter sua chave de transmissão ao vivo no YouTube.",
|
||||
"invalidStreamKey": "A senha para transmissão ao vivo pode estar incorreta.",
|
||||
"off": "Transmissão ao vivo encerrada",
|
||||
"offBy": "{{name}} parou a transmissão ao vivo",
|
||||
"on": "Transmissão ao Vivo",
|
||||
"onBy": "{{name}} iniciou a transmissão ao vivo",
|
||||
"pending": "Iniciando Transmissão ao Vivo...",
|
||||
"serviceName": "Serviço de Transmissão ao Vivo",
|
||||
"expandedPending": "Iniciando a transmissão em direto...",
|
||||
"failedToStart": "Falha ao iniciar a transmissão em direto",
|
||||
"getStreamKeyManually": "Não conseguimos buscar nenhuma transmissão em direto. Tente obter sua chave de transmissão em direto no YouTube.",
|
||||
"invalidStreamKey": "A senha para transmissão em direto pode estar incorreta.",
|
||||
"off": "Transmissão em direto encerrada",
|
||||
"offBy": "{{name}} parou a transmissão em direto",
|
||||
"on": "Transmissão em Direto",
|
||||
"onBy": "{{name}} iniciou a transmissão em direto",
|
||||
"pending": "Iniciando Transmissão em Direto...",
|
||||
"serviceName": "Serviço de Transmissão em Direto",
|
||||
"signedInAs": "Você está conectado como:",
|
||||
"signIn": "Faça login no Google",
|
||||
"signInCTA": "Faça login ou insira sua chave de transmissão ao vivo do YouTube.",
|
||||
"signInCTA": "Faça login ou insira sua chave de transmissão em Direto do YouTube.",
|
||||
"signOut": "Sair",
|
||||
"start": "Iniciar uma transmissão ao vivo",
|
||||
"start": "Iniciar uma transmissão em direto",
|
||||
"streamIdHelp": "O que é isso?",
|
||||
"unavailableTitle": "Transmissão ao vivo indisponível"
|
||||
"unavailableTitle": "Transmissão em direto indisponível"
|
||||
},
|
||||
"localRecording": {
|
||||
"clientState": {
|
||||
@@ -514,6 +522,34 @@
|
||||
},
|
||||
"passwordSetRemotely": "Definido por outro participante",
|
||||
"passwordDigitsOnly": "Até {{number}} dígitos",
|
||||
"polls": {
|
||||
"create": {
|
||||
"addOption": "Adicionar opção",
|
||||
"answerPlaceholder": "Opção {{index}}",
|
||||
"create": "Criar uma sondagem",
|
||||
"cancel": "Cancelar",
|
||||
"pollOption" : "Opção de sondagem {{index}}",
|
||||
"pollQuestion" : "Pergunta de Sondagem",
|
||||
"questionPlaceholder": "Faça uma pergunta",
|
||||
"removeOption": "Remover opção",
|
||||
"send": "Enviar"
|
||||
},
|
||||
"answer": {
|
||||
"skip": "Ignorar",
|
||||
"submit": "Submeter"
|
||||
},
|
||||
"results": {
|
||||
"vote": "Voto",
|
||||
"changeVote": "Mudar o voto",
|
||||
"empty": "Ainda não há sondagens na reunião. Comece aqui uma sondagem!",
|
||||
"hideDetailedResults": "Ocultar detalhes",
|
||||
"showDetailedResults": "Mostrar detalhes"
|
||||
},
|
||||
"notification": {
|
||||
"title": "Uma nova sondagem foi adicionada a esta reunião",
|
||||
"description": "Abrir o separador das sondagens para votar"
|
||||
}
|
||||
},
|
||||
"poweredby": "distribuído por",
|
||||
"prejoin": {
|
||||
"audioAndVideoError": "Erro no áudio e vídeo:",
|
||||
@@ -544,7 +580,7 @@
|
||||
"videoLowQuality": "Prevemos que o seu vídeo tenha baixa qualidade em termos de velocidade de fotogramas e resolução.",
|
||||
"videoTearing": "Prevemos que o seu vídeo seja pixelizado ou que tenha artefactos visuais."
|
||||
},
|
||||
"copyAndShare": "Copiar e partilhar a ligação da reunião.",
|
||||
"copyAndShare": "Copiar e partilhar o link da reunião.",
|
||||
"dialInMeeting": "Entrar com chamada telefónica",
|
||||
"dialInPin": "Entrar com chamada telefónica e introduzir o código PIN:",
|
||||
"dialing": "A marcar",
|
||||
@@ -561,8 +597,8 @@
|
||||
"joinMeeting": "Entrar na reunião",
|
||||
"joinWithoutAudio": "Entrar sem áudio",
|
||||
"initiated": "Chamada iniciada",
|
||||
"linkCopied": "Ligação copiada para a área de transferência",
|
||||
"lookGood": "Parece que o seu microfone está a funcionar corretamente",
|
||||
"linkCopied": "Link copiado para a área de transferência",
|
||||
"lookGood": "O microfone está a funcionar corretamente",
|
||||
"or": "ou",
|
||||
"premeeting": "Pré-reunião",
|
||||
"showScreen": "Ativar o ecrã de pré-reunião",
|
||||
@@ -606,7 +642,7 @@
|
||||
"expandedPending": "Iniciando gravação...",
|
||||
"failedToStart": "Falha ao iniciar a gravação",
|
||||
"fileSharingdescription": "Compartilhar gravação com participantes da reunião",
|
||||
"live": "AOVIVO",
|
||||
"live": "EM DIRETO",
|
||||
"loggedIn": "Conectado como {{userName}}",
|
||||
"off": "Gravação parada",
|
||||
"offBy": "{{name}} parou a gravação",
|
||||
@@ -689,18 +725,19 @@
|
||||
"dialInfoText": "\n\n=====\n\nDeseja apenas discar no seu telefone?\n\n{{defaultDialInNumber}}Clique neste link para ver os números de telefone para esta reunião\n{{dialInfoPageUrl}}",
|
||||
"mainText": "Clique no seguinte link para entrar na reunião:{{roomUrl}}\n"
|
||||
},
|
||||
"speaker": "Alto-falantes",
|
||||
"speaker": "Participante",
|
||||
"speakerStats": {
|
||||
"hours": "{{count}}h",
|
||||
"minutes": "{{count}}m",
|
||||
"name": "Nome",
|
||||
"seconds": "{{count}}s",
|
||||
"speakerStats": "Estatísticas do Apresentador",
|
||||
"speakerTime": "Tempo do Apresentador"
|
||||
"speakerStats": "Estatísticas dos Participantes",
|
||||
"speakerTime": "Tempo do Participante"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
"title": "O {{app}} precisa usar seu microfone e câmera."
|
||||
"genericTitle": "A reunião precisa de usar o seu microfone e câmara.",
|
||||
"title": "{{app}} precisa de usar o seu microfone e câmara."
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
"rejoinKeyTitle": "Reconectar",
|
||||
@@ -719,7 +756,7 @@
|
||||
"embedMeeting": "Reunião incorporada",
|
||||
"feedback": "Deixar comentários",
|
||||
"fullScreen": "Mudar para ecrã completo",
|
||||
"grantModerator": "Atribuir Moderador",
|
||||
"grantModerator": "Converter em moderador",
|
||||
"hangup": "Sair da reunião",
|
||||
"help": "Ajuda",
|
||||
"invite": "Convidar pessoas",
|
||||
@@ -730,7 +767,7 @@
|
||||
"moreActions": "Mais ações",
|
||||
"moreActionsMenu": "Menu de mais ações",
|
||||
"moreOptions": "Mostrar mais opções",
|
||||
"mute": "Mudo / Não mudo",
|
||||
"mute": "Ativar / Desativar microfone",
|
||||
"muteEveryone": "Silenciar a todos",
|
||||
"muteEveryoneElse": "Silenciar todos os outros",
|
||||
"muteEveryonesVideo": "Desativar a câmara de todos",
|
||||
@@ -741,14 +778,14 @@
|
||||
"profile": "Editar o seu perfil",
|
||||
"raiseHand": "Levantar / Baixar a mão",
|
||||
"recording": "Mudar gravação",
|
||||
"remoteMute": "Participante mudo",
|
||||
"remoteMute": "Participante sem som",
|
||||
"remoteVideoMute": "Desativar a câmara do participante",
|
||||
"security": "Opções de segurança",
|
||||
"Settings": "Mudar configurações",
|
||||
"shareaudio": "Partilhar áudio",
|
||||
"sharedvideo": "Mudar a partilha de vídeos do YouTube",
|
||||
"shareRoom": "Convidar alguém",
|
||||
"shareYourScreen": "Iniciar / Parar de partilhar o seu ecrã",
|
||||
"shareYourScreen": "Iniciar / Parar partilha de ecrã",
|
||||
"shortcuts": "Mostrar / Esconder atalhos",
|
||||
"show": "Mostrar no palco",
|
||||
"speakerStats": "Mostrar / Esconder estatísticas dos participantes",
|
||||
@@ -791,7 +828,7 @@
|
||||
"lowerYourHand": "Baixar a mão",
|
||||
"moreActions": "Mais ações",
|
||||
"moreOptions": "Mais opções",
|
||||
"mute": "Mudo / Não mudo",
|
||||
"mute": "Ativar / Desativar microfone",
|
||||
"muteEveryone": "Silenciar todos",
|
||||
"muteEveryonesVideo": "Desativar a câmara de todos",
|
||||
"noAudioSignalTitle": "Não há nenhuma entrada vinda do seu microfone!",
|
||||
@@ -820,7 +857,7 @@
|
||||
"stopScreenSharing": "Parar partilha de ecrã",
|
||||
"stopSubtitles": "Parar legendas",
|
||||
"stopSharedVideo": "Parar vídeo do YouTube",
|
||||
"talkWhileMutedPopup": "Está a tentar falar? Está mudo.",
|
||||
"talkWhileMutedPopup": "Está a tentar falar? Está com o som desligado.",
|
||||
"tileViewToggle": "Mudar para vista em quadrícula",
|
||||
"toggleCamera": "Mudar a câmara",
|
||||
"videomute": "Iniciar / Parar câmara",
|
||||
@@ -879,15 +916,20 @@
|
||||
"standardDefinition": "Definição padrão"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"domute": "Mudo",
|
||||
"connectionInfo": "Informações sobre a ligação",
|
||||
"domute": "Sem som",
|
||||
"domuteVideo": "Desativar a câmara",
|
||||
"domuteOthers": "Silenciar todos os outros",
|
||||
"domuteVideoOfOthers": "Desactivar a câmara de todos os outros",
|
||||
"flip": "Inverter",
|
||||
"grantModerator": "Converter em moderador",
|
||||
"kick": "Expulsar",
|
||||
"moderator": "Moderador",
|
||||
"mute": "Participante está mudo",
|
||||
"muted": "Mudo",
|
||||
"remoteControl": "Controle remoto",
|
||||
"show": "Mostrar no palco",
|
||||
"videomute": "O participante parou a câmera"
|
||||
"mute": "Participante está sem som",
|
||||
"muted": "Sem som",
|
||||
"videomute": "O participante parou a câmara",
|
||||
"remoteControl": "Iniciar / Parar controlo remoto",
|
||||
"show": "Mostrar no palco"
|
||||
},
|
||||
"welcomepage": {
|
||||
"accessibilityLabel": {
|
||||
@@ -924,7 +966,7 @@
|
||||
"admitAll": "Aceitar todos",
|
||||
"knockingParticipantList": "Lista de participantes a expulsar",
|
||||
"allow": "Permitir",
|
||||
"backToKnockModeButton": "Sem senha, peça para aderir em vez disso",
|
||||
"backToKnockModeButton": "Peça para aderir",
|
||||
"dialogTitle": "Modo sala de espera",
|
||||
"disableDialogContent": "O modo sala de espera está actualmente activada. Esta característica assegura que os participantes indesejados não possam juntar-se à sua reunião. Quer desativá-la?",
|
||||
"disableDialogSubmit": "Desativar",
|
||||
@@ -934,6 +976,7 @@
|
||||
"enableDialogText": "O modo sala de espera permite-lhe proteger a sua reunião apenas permitindo a entrada de pessoas após uma aprovação formal por um moderador.",
|
||||
"enterPasswordButton": "Introduza a senha da reunião",
|
||||
"enterPasswordTitle": "Introduzir a senha para participar na reunião",
|
||||
"errorMissingPassword": "Por favor introduza a senha da reunião",
|
||||
"invalidPassword": "Senha inválida",
|
||||
"joiningMessage": "Participará na reunião assim que alguém aceitar o seu pedido",
|
||||
"joinWithPasswordMessage": "Tentando aderir com senha, por favor aguarde...",
|
||||
|
||||
@@ -209,7 +209,6 @@
|
||||
"e2eeLabel": "Enable End-to-End Encryption",
|
||||
"e2eeWarning": "WARNING: Not all participants in this meeting seem to have support for End-to-End encryption. If you enable it they won't be able to see nor hear you.",
|
||||
"enterDisplayName": "Digite seu nome aqui",
|
||||
"enterDisplayNameToJoin": "Digite seu nome para participar",
|
||||
"embedMeeting": "Reunião em formato compacto",
|
||||
"error": "Erro",
|
||||
"gracefulShutdown": "Nosso serviço está em manutenção. Tente novamente mais tarde.",
|
||||
|
||||
@@ -209,7 +209,6 @@
|
||||
"e2eeLabel": "Aktivizo Fshehtëzim Skaj-më-Skaj",
|
||||
"e2eeWarning": "KUJDES: Jo të gjithë pjesëmarrësit në këtë takim duket të kenë mbulim për fshehtëzim Skaj-më-Skaj. Në e aktivizofshi, ata s’do të jenë në gjendje t’ju shohin apo dëgjojnë.",
|
||||
"enterDisplayName": "Ju lutemi, jepni këtu emrin tuaj",
|
||||
"enterDisplayNameToJoin": "Që të merrni pjesë, ju lutemi, jepni emrin tuaj",
|
||||
"embedMeeting": "Trupëzoni takim",
|
||||
"error": "Gabim",
|
||||
"gracefulShutdown": "Shërbimi ynë është aktualisht i ndërprerë, për punë mirëmbajtjeje. Ju lutemi, riprovoni më vonë.",
|
||||
|
||||
@@ -209,7 +209,6 @@
|
||||
"e2eeLabel": "啟用端對端加密",
|
||||
"e2eeWarning": "警告:看來不是每位此會議的參與者都有啟用端對端加密,如果您啟用了,他們可能無法看/聽到您。",
|
||||
"enterDisplayName": "請在此輸入您自己的名字",
|
||||
"enterDisplayNameToJoin": "請輸入您的名字以加入",
|
||||
"embedMeeting": "嵌入會議",
|
||||
"error": "錯誤",
|
||||
"gracefulShutdown": "我們的服務目前關閉維護中,請稍後再試。",
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"today": "Today"
|
||||
},
|
||||
"chat": {
|
||||
"enter": "Enter chat room",
|
||||
"enter": "Enter room",
|
||||
"error": "Error: your message was not sent. Reason: {{error}}",
|
||||
"fieldPlaceHolder": "Type your message here",
|
||||
"messagebox": "Type a message",
|
||||
@@ -66,15 +66,19 @@
|
||||
"noMessagesMessage": "There are no messages in the meeting yet. Start a conversation here!",
|
||||
"nickname": {
|
||||
"popover": "Choose a nickname",
|
||||
"title": "Enter a nickname to use chat"
|
||||
"title": "Enter a nickname to use chat and polls"
|
||||
},
|
||||
"privateNotice": "Private message to {{recipient}}",
|
||||
"title": "Chat",
|
||||
"you": "you",
|
||||
"message": "Message",
|
||||
"messageAccessibleTitle": "{{user}} says:",
|
||||
"messageAccessibleTitleMe": "me says:",
|
||||
"smileysPanel": "Emoji panel"
|
||||
"smileysPanel": "Emoji panel",
|
||||
"tabs": {
|
||||
"chat": "Chat",
|
||||
"polls": "Polls"
|
||||
},
|
||||
"title": "Chat and Polls",
|
||||
"you": "you"
|
||||
},
|
||||
"chromeExtensionBanner": {
|
||||
"installExtensionText": "Install the extension for Google Calendar and Office 365 integration",
|
||||
@@ -205,11 +209,10 @@
|
||||
"dismiss": "Dismiss",
|
||||
"displayNameRequired": "Hi! What’s your name?",
|
||||
"done": "Done",
|
||||
"e2eeDescription": "End-to-End Encryption is currently EXPERIMENTAL. Please keep in mind that turning on end-to-end encryption will effectively disable server-side provided services such as: recording, live streaming and phone participation. Also keep in mind that the meeting will only work for people joining from browsers with support for insertable streams.",
|
||||
"e2eeDescription": "End-to-End Encryption is currently EXPERIMENTAL. Please keep in mind that turning on end-to-end encryption will effectively disable server-side provided services such as: phone participation. Also keep in mind that the meeting will only work for people joining from browsers with support for insertable streams.",
|
||||
"e2eeLabel": "Enable End-to-End Encryption",
|
||||
"e2eeWarning": "WARNING: Not all participants in this meeting seem to have support for End-to-End encryption. If you enable it they won't be able to see nor hear you.",
|
||||
"enterDisplayName": "Please enter your name here",
|
||||
"enterDisplayNameToJoin": "Please enter your name to join",
|
||||
"enterDisplayName": "Enter your name here",
|
||||
"embedMeeting": "Embed meeting",
|
||||
"error": "Error",
|
||||
"gracefulShutdown": "Our service is currently down for maintenance. Please try again later.",
|
||||
@@ -588,6 +591,7 @@
|
||||
"moderationStoppedTitle": "Moderation stopped",
|
||||
"moderationToggleDescription": "by {{participantDisplayName}}",
|
||||
"raiseHandAction": "Raise hand",
|
||||
"reactionSounds": "Disable sounds",
|
||||
"groupTitle": "Notifications"
|
||||
},
|
||||
"participantsPane": {
|
||||
@@ -614,6 +618,34 @@
|
||||
},
|
||||
"passwordSetRemotely": "Set by another participant",
|
||||
"passwordDigitsOnly": "Up to {{number}} digits",
|
||||
"polls": {
|
||||
"create": {
|
||||
"addOption": "Add option",
|
||||
"answerPlaceholder": "Option {{index}}",
|
||||
"create": "Create a poll",
|
||||
"cancel": "Cancel",
|
||||
"pollOption" : "Poll option {{index}}",
|
||||
"pollQuestion" : "Poll Question",
|
||||
"questionPlaceholder": "Ask a question",
|
||||
"removeOption": "Remove option",
|
||||
"send": "Send"
|
||||
},
|
||||
"answer": {
|
||||
"skip": "Skip",
|
||||
"submit": "Submit"
|
||||
},
|
||||
"results": {
|
||||
"vote": "Vote",
|
||||
"changeVote": "Change vote",
|
||||
"empty": "There are no polls in the meeting yet. Start a poll here!",
|
||||
"hideDetailedResults": "Hide details",
|
||||
"showDetailedResults": "Show details"
|
||||
},
|
||||
"notification": {
|
||||
"title": "A new poll was added to this meeting",
|
||||
"description": "Open polls tab to vote"
|
||||
}
|
||||
},
|
||||
"poweredby": "powered by",
|
||||
"prejoin": {
|
||||
"audioAndVideoError": "Audio and video error:",
|
||||
@@ -662,7 +694,7 @@
|
||||
"joinWithoutAudio": "Join without audio",
|
||||
"initiated": "Call initiated",
|
||||
"linkCopied": "Link copied to clipboard",
|
||||
"lookGood": "It sounds like your microphone is working properly",
|
||||
"lookGood": "Your microphone is working properly",
|
||||
"or": "or",
|
||||
"premeeting": "Pre meeting",
|
||||
"showScreen": "Enable pre meeting screen",
|
||||
@@ -763,6 +795,7 @@
|
||||
"participantJoined": "Participant Joined",
|
||||
"participantLeft": "Participant Left",
|
||||
"playSounds": "Play sound on",
|
||||
"reactions": "Meeting reactions",
|
||||
"sameAsSystem": "Same as system ({{label}})",
|
||||
"selectAudioOutput": "Audio output",
|
||||
"selectCamera": "Camera",
|
||||
@@ -802,6 +835,7 @@
|
||||
},
|
||||
"speaker": "Speaker",
|
||||
"speakerStats": {
|
||||
"search": "Search",
|
||||
"hours": "{{count}}h",
|
||||
"minutes": "{{count}}m",
|
||||
"name": "Name",
|
||||
@@ -852,7 +886,6 @@
|
||||
"muteEveryonesVideo": "Disable everyone's camera",
|
||||
"muteEveryoneElsesVideo": "Disable everyone else's camera",
|
||||
"participants": "Participants",
|
||||
"party": "Party Popper",
|
||||
"pip": "Toggle Picture-in-Picture mode",
|
||||
"privateMessage": "Send private message",
|
||||
"profile": "Edit your profile",
|
||||
@@ -869,6 +902,7 @@
|
||||
"shareYourScreen": "Start / Stop sharing your screen",
|
||||
"shortcuts": "Toggle shortcuts",
|
||||
"show": "Show on stage",
|
||||
"silence": "Silence",
|
||||
"speakerStats": "Toggle speaker statistics",
|
||||
"surprised": "Surprised",
|
||||
"tileView": "Toggle tile view",
|
||||
@@ -893,6 +927,7 @@
|
||||
"clap": "Clap",
|
||||
"closeChat": "Close chat",
|
||||
"closeReactionsMenu": "Close reactions menu",
|
||||
"disableReactionSounds": "You can disable reaction sounds for this meeting",
|
||||
"documentClose": "Close shared document",
|
||||
"documentOpen": "Open shared document",
|
||||
"download": "Download our apps",
|
||||
@@ -928,7 +963,6 @@
|
||||
"openChat": "Open chat",
|
||||
"openReactionsMenu": "Open reactions menu",
|
||||
"participants": "Participants",
|
||||
"party": "Celebration",
|
||||
"pip": "Enter Picture-in-Picture mode",
|
||||
"privateMessage": "Send private message",
|
||||
"profile": "Edit your profile",
|
||||
@@ -938,7 +972,7 @@
|
||||
"reactionClap": "Send clap reaction",
|
||||
"reactionLaugh": "Send laugh reaction",
|
||||
"reactionLike": "Send thumbs up reaction",
|
||||
"reactionParty": "Send party popper reaction",
|
||||
"reactionSilence": "Send silence reaction",
|
||||
"reactionSurprised": "Send surprised reaction",
|
||||
"security": "Security options",
|
||||
"Settings": "Settings",
|
||||
@@ -946,6 +980,7 @@
|
||||
"sharedvideo": "Share video",
|
||||
"shareRoom": "Invite someone",
|
||||
"shortcuts": "View shortcuts",
|
||||
"silence": "Silence",
|
||||
"speakerStats": "Speaker stats",
|
||||
"startScreenSharing": "Start screen sharing",
|
||||
"startSubtitles": "Start subtitles",
|
||||
@@ -1088,7 +1123,7 @@
|
||||
"admitAll": "Admit all",
|
||||
"knockingParticipantList": "Knocking participant list",
|
||||
"allow": "Allow",
|
||||
"backToKnockModeButton": "No password, ask to join instead",
|
||||
"backToKnockModeButton": "Ask to join",
|
||||
"dialogTitle": "Lobby mode",
|
||||
"disableDialogContent": "Lobby mode is currently enabled. This feature ensures that unwanted participants can't join your meeting. Do you want to disable it?",
|
||||
"disableDialogSubmit": "Disable",
|
||||
@@ -1098,6 +1133,7 @@
|
||||
"enableDialogText": "Lobby mode lets you protect your meeting by only allowing people to enter after a formal approval by a moderator.",
|
||||
"enterPasswordButton": "Enter meeting password",
|
||||
"enterPasswordTitle": "Enter password to join meeting",
|
||||
"errorMissingPassword": "Please enter the meeting password",
|
||||
"invalidPassword": "Invalid password",
|
||||
"joiningMessage": "You'll join the meeting as soon as someone accepts your request",
|
||||
"joinWithPasswordMessage": "Trying to join with password, please wait...",
|
||||
|
||||
31
package-lock.json
generated
31
package-lock.json
generated
@@ -4,6 +4,11 @@
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@amplitude/react-native": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@amplitude/react-native/-/react-native-2.3.3.tgz",
|
||||
"integrity": "sha512-QTpwy4lKy9kpBjB2334HCEIU7QwGFAkGRfp21aeDA87D6pkiUMAvyDYbz58CnB5HCXuqcvws3GN8d60RO9KF9A=="
|
||||
},
|
||||
"@amplitude/types": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@amplitude/types/-/types-1.5.5.tgz",
|
||||
@@ -2898,6 +2903,10 @@
|
||||
"react-is": "^16.8.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"@matrix-org/olm": {
|
||||
"version": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"integrity": "sha512-OhC9wwZ/ox9vputA1MR2A7QlYlvfXCV+tdbADOR7Jn7o9qoXh3HWf+AbSpXTK3daF0GIHA69Ws8XOnWqu5n53A=="
|
||||
},
|
||||
"@microsoft/microsoft-graph-client": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-1.1.0.tgz",
|
||||
@@ -7353,6 +7362,11 @@
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
|
||||
},
|
||||
"diacritics": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz",
|
||||
"integrity": "sha1-PvqHMj67hj5mls67AILUj/PW96E="
|
||||
},
|
||||
"didyoumean": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz",
|
||||
@@ -9868,9 +9882,12 @@
|
||||
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
||||
},
|
||||
"i18n-iso-countries": {
|
||||
"version": "3.7.8",
|
||||
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-3.7.8.tgz",
|
||||
"integrity": "sha512-NkT3lRiw7D4kKtSAVjVdHCvGlc2UOe0ALKa9IfEx0LkEDf0q3YgjP/veVk0d/OZ7yqUNzV8aJP4lJc6RPj++Gw=="
|
||||
"version": "6.8.0",
|
||||
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-6.8.0.tgz",
|
||||
"integrity": "sha512-jJs/+CA6+VUICFxqGcB0vFMERGfhfvyNk+8Vb9EagSZkl7kSpm/kT0VyhvzM/zixDWEV/+oN9L7v/GT9BwzoGg==",
|
||||
"requires": {
|
||||
"diacritics": "1.3.0"
|
||||
}
|
||||
},
|
||||
"i18next": {
|
||||
"version": "17.0.6",
|
||||
@@ -11087,8 +11104,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "github:jitsi/lib-jitsi-meet#6eaffc4b11b42d851a17ed941a19a9513265d3bb",
|
||||
"from": "github:jitsi/lib-jitsi-meet#6eaffc4b11b42d851a17ed941a19a9513265d3bb",
|
||||
"version": "github:jitsi/lib-jitsi-meet#61c977f70ab353013a40e7daaeb5fc3713526984",
|
||||
"from": "github:jitsi/lib-jitsi-meet#61c977f70ab353013a40e7daaeb5fc3713526984",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "1.0.2",
|
||||
"@jitsi/sdp-interop": "github:jitsi/sdp-interop#5fc4af6dcf8a6e6af9fedbcd654412fd47b1b4ae",
|
||||
@@ -12955,10 +12972,6 @@
|
||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
|
||||
"dev": true
|
||||
},
|
||||
"olm": {
|
||||
"version": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
||||
"integrity": "sha512-B87bTpGIGieuV2FNauChjjQtVltwTGagQFoHm+3Dcse4amKAAGJB/I54dnP/JtbHZ+RYVoApM2OQ46Z4VH6eNg=="
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"author": "",
|
||||
"readmeFilename": "README.md",
|
||||
"dependencies": {
|
||||
"@amplitude/react-native": "2.3.3",
|
||||
"@atlaskit/button": "15.1.4",
|
||||
"@atlaskit/checkbox": "12.0.0",
|
||||
"@atlaskit/dropdown-menu": "10.1.2",
|
||||
@@ -35,6 +36,7 @@
|
||||
"@hapi/bourne": "2.0.0",
|
||||
"@jitsi/js-utils": "1.0.6",
|
||||
"@material-ui/core": "4.11.3",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"@microsoft/microsoft-graph-client": "1.1.0",
|
||||
"@react-native-async-storage/async-storage": "1.15.5",
|
||||
"@react-native-community/google-signin": "3.0.1",
|
||||
@@ -47,7 +49,7 @@
|
||||
"clipboard-copy": "4.0.1",
|
||||
"dropbox": "4.0.9",
|
||||
"focus-visible": "5.1.0",
|
||||
"i18n-iso-countries": "3.7.8",
|
||||
"i18n-iso-countries": "6.8.0",
|
||||
"i18next": "17.0.6",
|
||||
"i18next-browser-languagedetector": "3.0.1",
|
||||
"i18next-xhr-backend": "3.0.0",
|
||||
@@ -56,12 +58,11 @@
|
||||
"jquery-i18next": "1.2.1",
|
||||
"js-md5": "0.6.1",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#6eaffc4b11b42d851a17ed941a19a9513265d3bb",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#61c977f70ab353013a40e7daaeb5fc3713526984",
|
||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.1",
|
||||
"moment-duration-format": "2.2.2",
|
||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
||||
"optional-require": "1.0.3",
|
||||
"pixelmatch": "5.1.0",
|
||||
"promise.allsettled": "1.0.4",
|
||||
|
||||
@@ -11,6 +11,39 @@ index bd48f44..d243ed0 100644
|
||||
withDispatchGroup:(dispatch_group_t)dispatchGroup
|
||||
lazilyDiscovered:(BOOL)lazilyDiscovered
|
||||
{
|
||||
diff --git a/node_modules/react-native/React/Modules/RCTTiming.m b/node_modules/react-native/React/Modules/RCTTiming.m
|
||||
index 8a09022..265d7b6 100644
|
||||
--- a/node_modules/react-native/React/Modules/RCTTiming.m
|
||||
+++ b/node_modules/react-native/React/Modules/RCTTiming.m
|
||||
@@ -130,6 +130,11 @@ - (void)setBridge:(RCTBridge *)bridge
|
||||
object:nil];
|
||||
}
|
||||
|
||||
+ [[NSNotificationCenter defaultCenter] addObserver:self
|
||||
+ selector:@selector(proximityChanged)
|
||||
+ name:UIDeviceProximityStateDidChangeNotification
|
||||
+ object:nil];
|
||||
+
|
||||
_bridge = bridge;
|
||||
}
|
||||
|
||||
@@ -276,6 +281,16 @@ - (void)didUpdateFrame:(RCTFrameUpdate *)update
|
||||
}
|
||||
}
|
||||
|
||||
+-(void)proximityChanged
|
||||
+{
|
||||
+ BOOL near = [UIDevice currentDevice].proximityState;
|
||||
+ if (near) {
|
||||
+ [self appDidMoveToBackground];
|
||||
+ } else {
|
||||
+ [self appDidMoveToForeground];
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
- (void)scheduleSleepTimer:(NSDate *)sleepTarget
|
||||
{
|
||||
@synchronized (self) {
|
||||
diff --git a/node_modules/react-native/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm b/node_modules/react-native/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm
|
||||
index 3cb73b5..e4a14b4 100644
|
||||
--- a/node_modules/react-native/ReactCommon/turbomodule/core/platform/ios/RCTTurboModuleManager.mm
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import amplitude from 'amplitude-js';
|
||||
|
||||
import logger from '../logger';
|
||||
|
||||
import AbstractHandler from './AbstractHandler';
|
||||
import { fixDeviceID } from './amplitude';
|
||||
import { fixDeviceID } from './amplitude/fixDeviceID';
|
||||
import amplitude from './amplitude/lib';
|
||||
|
||||
/**
|
||||
* Analytics handler for Amplitude.
|
||||
@@ -19,41 +18,39 @@ export default class AmplitudeHandler extends AbstractHandler {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
const { amplitudeAPPKey, host, user } = options;
|
||||
const { amplitudeAPPKey, user } = options;
|
||||
|
||||
this._enabled = true;
|
||||
this._host = host; // Only used on React Native.
|
||||
|
||||
const onError = e => {
|
||||
logger.error('Error initializing Amplitude', e);
|
||||
this._enabled = false;
|
||||
};
|
||||
|
||||
const amplitudeOptions = {
|
||||
domain: navigator.product === 'ReactNative' ? host : undefined,
|
||||
includeReferrer: true,
|
||||
onError
|
||||
};
|
||||
if (navigator.product === 'ReactNative') {
|
||||
amplitude.getInstance().init(amplitudeAPPKey);
|
||||
fixDeviceID(amplitude.getInstance()).then(() => {
|
||||
amplitude.getInstance().getDeviceId()
|
||||
.then(deviceId => {
|
||||
this._deviceId = deviceId;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const amplitudeOptions = {
|
||||
includeReferrer: true,
|
||||
onError
|
||||
};
|
||||
|
||||
this._getInstance().init(amplitudeAPPKey, undefined, amplitudeOptions);
|
||||
fixDeviceID(this._getInstance());
|
||||
amplitude.getInstance().init(amplitudeAPPKey, undefined, amplitudeOptions);
|
||||
fixDeviceID(amplitude.getInstance());
|
||||
}
|
||||
|
||||
if (user) {
|
||||
this._getInstance().setUserId(user);
|
||||
this._userId = user;
|
||||
amplitude.getInstance().setUserId(user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AmplitudeClient instance.
|
||||
*
|
||||
* @returns {AmplitudeClient}
|
||||
*/
|
||||
_getInstance() {
|
||||
const name = navigator.product === 'ReactNative' ? this._host : undefined;
|
||||
|
||||
return amplitude.getInstance(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Amplitude user properties.
|
||||
*
|
||||
@@ -62,7 +59,7 @@ export default class AmplitudeHandler extends AbstractHandler {
|
||||
*/
|
||||
setUserProperties(userProps) {
|
||||
if (this._enabled) {
|
||||
this._getInstance().setUserProperties(userProps);
|
||||
amplitude.getInstance().setUserProperties(userProps);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +76,7 @@ export default class AmplitudeHandler extends AbstractHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
this._getInstance().logEvent(this._extractName(event), event);
|
||||
amplitude.getInstance().logEvent(this._extractName(event), event);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,10 +85,17 @@ export default class AmplitudeHandler extends AbstractHandler {
|
||||
* @returns {Object}
|
||||
*/
|
||||
getIdentityProps() {
|
||||
if (navigator.product === 'ReactNative') {
|
||||
return {
|
||||
deviceId: this._deviceId,
|
||||
userId: this._userId
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
sessionId: this._getInstance().getSessionId(),
|
||||
deviceId: this._getInstance().options.deviceId,
|
||||
userId: this._getInstance().options.userId
|
||||
sessionId: amplitude.getInstance().getSessionId(),
|
||||
deviceId: amplitude.getInstance().options.deviceId,
|
||||
userId: amplitude.getInstance().options.userId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './fixDeviceID';
|
||||
@@ -0,0 +1,3 @@
|
||||
import { Amplitude } from '@amplitude/react-native';
|
||||
|
||||
export default Amplitude;
|
||||
3
react/features/analytics/handlers/amplitude/lib.web.js
Normal file
3
react/features/analytics/handlers/amplitude/lib.web.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import amplitude from 'amplitude-js';
|
||||
|
||||
export default amplitude;
|
||||
@@ -43,9 +43,11 @@ export class App extends AbstractApp {
|
||||
*/
|
||||
_renderDialogContainer() {
|
||||
return (
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
<DialogContainer />
|
||||
</AtlasKitThemeProvider>
|
||||
<JitsiThemeProvider>
|
||||
<AtlasKitThemeProvider mode = 'dark'>
|
||||
<DialogContainer />
|
||||
</AtlasKitThemeProvider>
|
||||
</JitsiThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ import '../large-video/middleware';
|
||||
import '../lobby/middleware';
|
||||
import '../notifications/middleware';
|
||||
import '../overlay/middleware';
|
||||
import '../polls/middleware';
|
||||
import '../polls/subscriber';
|
||||
import '../reactions/middleware';
|
||||
import '../recent-list/middleware';
|
||||
import '../recording/middleware';
|
||||
|
||||
@@ -41,6 +41,7 @@ import '../lobby/reducer';
|
||||
import '../notifications/reducer';
|
||||
import '../overlay/reducer';
|
||||
import '../participants-pane/reducer';
|
||||
import '../polls/reducer';
|
||||
import '../reactions/reducer';
|
||||
import '../recent-list/reducer';
|
||||
import '../recording/reducer';
|
||||
|
||||
@@ -124,15 +124,8 @@ class LoginDialog extends Component<Props, State> {
|
||||
*/
|
||||
_onCancelLogin() {
|
||||
const { dispatch } = this.props;
|
||||
const cancelButton = document.getElementById('modal-dialog-cancel-button');
|
||||
|
||||
if (cancelButton) {
|
||||
cancelButton.onclick = () => {
|
||||
dispatch(cancelLogin());
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
dispatch(cancelLogin());
|
||||
}
|
||||
|
||||
_onLogin: () => void;
|
||||
@@ -252,6 +245,7 @@ class LoginDialog extends Component<Props, State> {
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
disableBlanketClickDismiss = { true }
|
||||
hideCloseIconButton = { true }
|
||||
okDisabled = {
|
||||
connecting
|
||||
|
||||
@@ -64,15 +64,8 @@ class WaitForOwnerDialog extends PureComponent<Props> {
|
||||
*/
|
||||
_onCancelWaitForOwner() {
|
||||
const { dispatch } = this.props;
|
||||
const cancelButton = document.getElementById('modal-dialog-cancel-button');
|
||||
|
||||
if (cancelButton) {
|
||||
cancelButton.onclick = () => {
|
||||
dispatch(cancelWaitForOwner());
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
dispatch(cancelWaitForOwner());
|
||||
}
|
||||
|
||||
_onIAmHost: () => void;
|
||||
@@ -102,6 +95,7 @@ class WaitForOwnerDialog extends PureComponent<Props> {
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
disableBlanketClickDismiss = { true }
|
||||
hideCloseIconButton = { true }
|
||||
okKey = { t('dialog.IamHost') }
|
||||
onCancel = { this._onCancelWaitForOwner }
|
||||
|
||||
@@ -5,8 +5,11 @@ import { getConferenceState } from '../base/conference';
|
||||
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
|
||||
import { MEDIA_TYPE } from '../base/media';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantDisplayName,
|
||||
getRemoteParticipants,
|
||||
isLocalParticipantModerator,
|
||||
isParticipantModerator,
|
||||
PARTICIPANT_UPDATED,
|
||||
raiseHand
|
||||
} from '../base/participants';
|
||||
@@ -124,19 +127,29 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
case PARTICIPANT_UPDATED: {
|
||||
const state = getState();
|
||||
const audioModerationEnabled = isEnabledFromState(MEDIA_TYPE.AUDIO, state);
|
||||
const participant = action.participant;
|
||||
|
||||
// this is handled only by moderators
|
||||
if (audioModerationEnabled && isLocalParticipantModerator(state)) {
|
||||
const participant = action.participant;
|
||||
if (participant && audioModerationEnabled) {
|
||||
if (isLocalParticipantModerator(state)) {
|
||||
|
||||
if (participant.raisedHand) {
|
||||
// if participant raises hand show notification
|
||||
!isParticipantApproved(participant.id, MEDIA_TYPE.AUDIO)(state)
|
||||
// this is handled only by moderators
|
||||
if (participant.raisedHand) {
|
||||
// if participant raises hand show notification
|
||||
!isParticipantApproved(participant.id, MEDIA_TYPE.AUDIO)(state)
|
||||
&& dispatch(participantPendingAudio(participant));
|
||||
} else {
|
||||
// if participant lowers hand hide notification
|
||||
isParticipantPending(participant, MEDIA_TYPE.AUDIO)(state)
|
||||
} else {
|
||||
// if participant lowers hand hide notification
|
||||
isParticipantPending(participant, MEDIA_TYPE.AUDIO)(state)
|
||||
&& dispatch(dismissPendingAudioParticipant(participant));
|
||||
}
|
||||
} else if (participant.id === getLocalParticipant(state).id
|
||||
&& /* the new role */ isParticipantModerator(participant)) {
|
||||
|
||||
// this is the granted moderator case
|
||||
getRemoteParticipants(state).forEach(p => {
|
||||
p.raisedHand && !isParticipantApproved(p.id, MEDIA_TYPE.AUDIO)(state)
|
||||
&& dispatch(participantPendingAudio(p));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ function _addConferenceListeners(conference, dispatch, state) {
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,
|
||||
id => dispatch(dominantSpeakerChanged(id, conference)));
|
||||
(dominant, previous) => dispatch(dominantSpeakerChanged(dominant, previous, conference)));
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
|
||||
@@ -234,11 +234,11 @@ export function getConferenceOptions(stateful: Function | Object) {
|
||||
options.applicationName = getName();
|
||||
options.transcriptionLanguage = determineTranscriptionLanguage(options);
|
||||
|
||||
// Disable analytics, if requessted.
|
||||
// Disable analytics, if requested.
|
||||
if (options.disableThirdPartyRequests) {
|
||||
delete config.analytics.scriptURLs;
|
||||
delete config.analytics.amplitudeAPPKey;
|
||||
delete config.analytics.googleAnalyticsTrackingId;
|
||||
delete config.analytics?.scriptURLs;
|
||||
delete config.analytics?.amplitudeAPPKey;
|
||||
delete config.analytics?.googleAnalyticsTrackingId;
|
||||
delete options.callStatsID;
|
||||
delete options.callStatsSecret;
|
||||
} else {
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
// @flow
|
||||
|
||||
import { setPrejoinPageVisibility, setSkipPrejoinOnReload } from '../../prejoin';
|
||||
import { PREJOIN_SCREEN_STATES } from '../../prejoin/constants';
|
||||
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
|
||||
import { MiddlewareRegistry } from '../redux';
|
||||
|
||||
import { CONFERENCE_FAILED, CONFERENCE_JOINED } from './actionTypes';
|
||||
import './middleware.any';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
const { enableForcedReload } = getState()['features/base/config'];
|
||||
|
||||
switch (action.type) {
|
||||
case CONFERENCE_JOINED: {
|
||||
if (enableForcedReload) {
|
||||
dispatch(setPrejoinPageVisibility(false));
|
||||
dispatch(setPrejoinPageVisibility(PREJOIN_SCREEN_STATES.HIDDEN));
|
||||
dispatch(setSkipPrejoinOnReload(false));
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ export default [
|
||||
*/
|
||||
'callHandle',
|
||||
'callStatsConfIDNamespace',
|
||||
'callStatsConfigParams',
|
||||
'callStatsID',
|
||||
'callStatsSecret',
|
||||
|
||||
@@ -91,6 +92,7 @@ export default [
|
||||
'disableJoinLeaveSounds',
|
||||
'disableLocalVideoFlip',
|
||||
'disableNS',
|
||||
'disablePolls',
|
||||
'disableProfile',
|
||||
'disableRemoteControl',
|
||||
'disableRemoteMute',
|
||||
@@ -98,6 +100,7 @@ export default [
|
||||
'disableRtx',
|
||||
'disableShortcuts',
|
||||
'disableShowMoreStats',
|
||||
'disableSpeakerStatsSearch',
|
||||
'disableSimulcast',
|
||||
'disableThirdPartyRequests',
|
||||
'disableTileView',
|
||||
@@ -108,6 +111,7 @@ export default [
|
||||
'e2eping',
|
||||
'enableDisplayNameInStats',
|
||||
'enableEmailInStats',
|
||||
'enableEncodedTransformSupport',
|
||||
'enableIceRestart',
|
||||
'enableInsecureRoomNameWarning',
|
||||
'enableLayerSuspension',
|
||||
|
||||
@@ -33,7 +33,6 @@ export function createFakeConfig(baseURL: string) {
|
||||
muc: `conference.${url.hostname}`
|
||||
},
|
||||
bosh: `${baseURL}http-bind`,
|
||||
clientNode: 'https://jitsi.org/jitsi-meet',
|
||||
p2p: {
|
||||
enabled: true
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ export * from './functions.any';
|
||||
export function _cleanupConfig(config: Object) {
|
||||
config.analytics.scriptURLs = [];
|
||||
if (NativeModules.AppInfo.LIBRE_BUILD) {
|
||||
delete config.analytics.amplitudeAPPKey;
|
||||
delete config.analytics.googleAnalyticsTrackingId;
|
||||
delete config.analytics?.amplitudeAPPKey;
|
||||
delete config.analytics?.googleAnalyticsTrackingId;
|
||||
delete config.callStatsID;
|
||||
delete config.callStatsSecret;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = {
|
||||
}
|
||||
};
|
||||
|
||||
const WARNING_DISPLAY_TIMER = 4000;
|
||||
|
||||
/**
|
||||
* A listener for device permissions changed reported from lib-jitsi-meet.
|
||||
@@ -133,7 +134,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
description: additionalCameraErrorMsg,
|
||||
descriptionKey: cameraErrorMsg,
|
||||
titleKey
|
||||
}));
|
||||
}, WARNING_DISPLAY_TIMER));
|
||||
|
||||
if (isPrejoinPageVisible(store.getState())) {
|
||||
store.dispatch(setDeviceStatusWarning(titleKey));
|
||||
@@ -162,7 +163,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
description: additionalMicErrorMsg,
|
||||
descriptionKey: micErrorMsg,
|
||||
titleKey
|
||||
}));
|
||||
}, WARNING_DISPLAY_TIMER));
|
||||
|
||||
if (isPrejoinPageVisible(store.getState())) {
|
||||
store.dispatch(setDeviceStatusWarning(titleKey));
|
||||
|
||||
@@ -50,7 +50,7 @@ class BaseDialog<P: Props, S: State> extends AbstractDialog<P, S> {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _dialogStyles, style } = this.props;
|
||||
const { _dialogStyles, style, t, titleKey } = this.props;
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback>
|
||||
@@ -65,13 +65,18 @@ class BaseDialog<P: Props, S: State> extends AbstractDialog<P, S> {
|
||||
_dialogStyles.dialog,
|
||||
style
|
||||
] }>
|
||||
<TouchableOpacity
|
||||
onPress = { this._onCancel }
|
||||
style = { styles.closeWrapper }>
|
||||
<Icon
|
||||
src = { IconClose }
|
||||
style = { _dialogStyles.closeStyle } />
|
||||
</TouchableOpacity>
|
||||
<View style = { styles.headerWrapper }>
|
||||
<Text style = { styles.dialogTitle }>
|
||||
{ titleKey ? t(titleKey) : ' ' }
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
onPress = { this._onCancel }
|
||||
style = { styles.closeWrapper }>
|
||||
<Icon
|
||||
src = { IconClose }
|
||||
style = { _dialogStyles.closeStyle } />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{ this._renderContent() }
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
|
||||
@@ -28,6 +28,12 @@ type Props = BaseProps & {
|
||||
*/
|
||||
contentKey: string | { key: string, params: Object},
|
||||
|
||||
/**
|
||||
* The handler for the event when clicking the 'confirmNo' button.
|
||||
* Defaults to onCancel if absent.
|
||||
*/
|
||||
onDecline?: Function,
|
||||
|
||||
t: Function
|
||||
}
|
||||
|
||||
@@ -55,11 +61,11 @@ class ConfirmDialog extends BaseSubmitDialog<Props, *> {
|
||||
* @inheritdoc
|
||||
*/
|
||||
_renderAdditionalButtons() {
|
||||
const { _dialogStyles, cancelKey, t } = this.props;
|
||||
const { _dialogStyles, cancelKey, onDecline, t } = this.props;
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress = { this._onCancel }
|
||||
onPress = { onDecline || this._onCancel }
|
||||
style = { [
|
||||
_dialogStyles.button,
|
||||
brandedDialog.buttonFarLeft,
|
||||
|
||||
@@ -81,10 +81,20 @@ export const brandedDialog = {
|
||||
},
|
||||
|
||||
closeWrapper: {
|
||||
alignSelf: 'flex-end',
|
||||
padding: BoxModel.padding
|
||||
},
|
||||
|
||||
dialogTitle: {
|
||||
fontWeight: 'bold',
|
||||
paddingLeft: BoxModel.padding * 2
|
||||
},
|
||||
|
||||
headerWrapper: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
|
||||
mainWrapper: {
|
||||
alignSelf: 'stretch',
|
||||
padding: BoxModel.padding * 2,
|
||||
|
||||
@@ -71,6 +71,12 @@ type Props = {
|
||||
*/
|
||||
isModal: boolean,
|
||||
|
||||
/**
|
||||
* The handler for the event when clicking the 'confirmNo' button.
|
||||
* Defaults to onCancel if absent.
|
||||
*/
|
||||
onDecline?: Function,
|
||||
|
||||
/**
|
||||
* Disables rendering of the submit button.
|
||||
*/
|
||||
@@ -268,7 +274,8 @@ class StatelessDialog extends Component<Props> {
|
||||
}
|
||||
|
||||
const {
|
||||
t /* The following fixes a flow error: */ = _.identity
|
||||
t /* The following fixes a flow error: */ = _.identity,
|
||||
onDecline
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@@ -276,7 +283,7 @@ class StatelessDialog extends Component<Props> {
|
||||
appearance = 'subtle'
|
||||
id = { CANCEL_BUTTON_ID }
|
||||
key = 'cancel'
|
||||
onClick = { this._onCancel }
|
||||
onClick = { onDecline || this._onCancel }
|
||||
type = 'button'>
|
||||
{ t(this.props.cancelKey || 'dialog.Cancel') }
|
||||
</Button>
|
||||
|
||||
@@ -92,6 +92,12 @@ export const IOS_RECORDING_ENABLED = 'ios.recording.enabled';
|
||||
*/
|
||||
export const IOS_SCREENSHARING_ENABLED = 'ios.screensharing.enabled';
|
||||
|
||||
/**
|
||||
* Flag indicating if screen sharing should be enabled in android.
|
||||
* Default: enabled (true).
|
||||
*/
|
||||
export const ANDROID_SCREENSHARING_ENABLED = 'android.screensharing.enabled';
|
||||
|
||||
/**
|
||||
* Flag indicating if kickout is enabled.
|
||||
* Default: enabled (true).
|
||||
|
||||
@@ -153,6 +153,8 @@ export default function Icon(props: Props) {
|
||||
}
|
||||
}, [ onClick, onKeyPress ]);
|
||||
|
||||
const jitsiIconClassName = calculatedColor ? 'jitsi-icon' : 'jitsi-icon jitsi-icon-default';
|
||||
|
||||
return (
|
||||
<Container
|
||||
{ ...rest }
|
||||
@@ -163,7 +165,7 @@ export default function Icon(props: Props) {
|
||||
aria-haspopup = { ariaHasPopup }
|
||||
aria-label = { ariaLabel }
|
||||
aria-pressed = { ariaPressed }
|
||||
className = { `jitsi-icon ${className || ''}` }
|
||||
className = { `${jitsiIconClassName} ${className || ''}` }
|
||||
id = { containerId }
|
||||
onClick = { onClick }
|
||||
onKeyDown = { onKeyDown }
|
||||
|
||||
3
react/features/base/icons/svg/check-solid.svg
Normal file
3
react/features/base/icons/svg/check-solid.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.666687 9.00002C0.666687 13.6024 4.39765 17.3334 9.00002 17.3334C13.6024 17.3334 17.3334 13.6024 17.3334 9.00002C17.3334 4.39765 13.6024 0.666687 9.00002 0.666687C4.39765 0.666687 0.666687 4.39765 0.666687 9.00002ZM13.7119 5.86983C13.3639 5.56869 12.8376 5.60672 12.5365 5.95477L7.55616 11.711L5.42261 9.57743C5.09717 9.25199 4.56954 9.25199 4.2441 9.57743C3.91866 9.90287 3.91866 10.4305 4.2441 10.7559L7.01102 13.5229C7.35319 13.865 7.91386 13.8448 8.23047 13.4789L13.7969 7.04527C14.098 6.69722 14.06 6.17096 13.7119 5.86983Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 699 B |
@@ -26,6 +26,7 @@ export { default as IconChat } from './chat.svg';
|
||||
export { default as IconChatSend } from './send.svg';
|
||||
export { default as IconChatUnread } from './chat-unread.svg';
|
||||
export { default as IconCheck } from './check.svg';
|
||||
export { default as IconCheckSolid } from './check-solid.svg';
|
||||
export { default as IconClose } from './close.svg';
|
||||
export { default as IconCloseCircle } from './close-circle.svg';
|
||||
export { default as IconCloseX } from './close-x.svg';
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
* {
|
||||
* type: DOMINANT_SPEAKER_CHANGED,
|
||||
* participant: {
|
||||
* id: string
|
||||
* conference: JitsiConference,
|
||||
* id: string,
|
||||
* previousSpeakers: Array<string>
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,8 @@ import logger from './logger';
|
||||
/**
|
||||
* Create an action for when dominant speaker changes.
|
||||
*
|
||||
* @param {string} id - Participant's ID.
|
||||
* @param {string} dominantSpeaker - Participant ID of the dominant speaker.
|
||||
* @param {Array<string>} previousSpeakers - Participant IDs of the previous speakers.
|
||||
* @param {JitsiConference} conference - The {@code JitsiConference} associated
|
||||
* with the participant identified by the specified {@code id}. Only the local
|
||||
* participant is allowed to not specify an associated {@code JitsiConference}
|
||||
@@ -40,16 +41,18 @@ import logger from './logger';
|
||||
* type: DOMINANT_SPEAKER_CHANGED,
|
||||
* participant: {
|
||||
* conference: JitsiConference,
|
||||
* id: string
|
||||
* id: string,
|
||||
* previousSpeakers: Array<string>
|
||||
* }
|
||||
* }}
|
||||
*/
|
||||
export function dominantSpeakerChanged(id, conference) {
|
||||
export function dominantSpeakerChanged(dominantSpeaker, previousSpeakers, conference) {
|
||||
return {
|
||||
type: DOMINANT_SPEAKER_CHANGED,
|
||||
participant: {
|
||||
conference,
|
||||
id
|
||||
id: dominantSpeaker,
|
||||
previousSpeakers
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -443,3 +443,52 @@ async function _getFirstLoadableAvatarUrl(participant, store) {
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Selector for retrieving sorted participants by display name.
|
||||
*
|
||||
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
|
||||
* {@code getState} function to be used to retrieve the state
|
||||
* features/base/participants.
|
||||
* @returns {Array<Object>}
|
||||
*/
|
||||
export function getSortedParticipants(stateful: Object | Function) {
|
||||
const localParticipant = getLocalParticipant(stateful);
|
||||
const remoteParticipants = getRemoteParticipants(stateful);
|
||||
|
||||
const items = [];
|
||||
const dominantSpeaker = getDominantSpeakerParticipant(stateful);
|
||||
|
||||
remoteParticipants.forEach(p => {
|
||||
if (p !== dominantSpeaker) {
|
||||
items.push(p);
|
||||
}
|
||||
});
|
||||
|
||||
items.sort((a, b) =>
|
||||
getParticipantDisplayName(stateful, a.id).localeCompare(getParticipantDisplayName(stateful, b.id))
|
||||
);
|
||||
|
||||
items.unshift(localParticipant);
|
||||
|
||||
if (dominantSpeaker && dominantSpeaker !== localParticipant) {
|
||||
items.unshift(dominantSpeaker);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector for retrieving ids of alphabetically sorted participants by name.
|
||||
*
|
||||
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's
|
||||
* {@code getState} function to be used to retrieve the state
|
||||
* features/base/participants.
|
||||
* @returns {Array<string>}
|
||||
*/
|
||||
export function getSortedParticipantIds(stateful: Object | Function): Array<string> {
|
||||
const participantIds = getSortedParticipants(stateful).map((p): Object => p.id);
|
||||
|
||||
return participantIds;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
import { LOCAL_PARTICIPANT_DEFAULT_ID, PARTICIPANT_ROLE } from './constants';
|
||||
import { isParticipantModerator } from './functions';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* Participant object.
|
||||
* @typedef {Object} Participant
|
||||
@@ -30,8 +32,6 @@ import { isParticipantModerator } from './functions';
|
||||
* @property {string} email - Participant email.
|
||||
*/
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* The participant properties which cannot be updated through
|
||||
* {@link PARTICIPANT_UPDATED}. They either identify the participant or can only
|
||||
@@ -53,13 +53,15 @@ const PARTICIPANT_PROPS_TO_OMIT_WHEN_UPDATE = [
|
||||
];
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
haveParticipantWithScreenSharingFeature: false,
|
||||
dominantSpeaker: undefined,
|
||||
everyoneIsModerator: false,
|
||||
pinnedParticipant: undefined,
|
||||
fakeParticipants: new Map(),
|
||||
haveParticipantWithScreenSharingFeature: false,
|
||||
local: undefined,
|
||||
pinnedParticipant: undefined,
|
||||
remote: new Map(),
|
||||
fakeParticipants: new Map()
|
||||
sortedRemoteParticipants: new Map(),
|
||||
speakersList: []
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -93,8 +95,13 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
|
||||
}
|
||||
case DOMINANT_SPEAKER_CHANGED: {
|
||||
const { participant } = action;
|
||||
const { id } = participant;
|
||||
const { dominantSpeaker } = state;
|
||||
const { id, previousSpeakers = [] } = participant;
|
||||
const { dominantSpeaker, local } = state;
|
||||
const speakersList = [];
|
||||
|
||||
// Update the speakers list.
|
||||
id !== local?.id && speakersList.push(id);
|
||||
speakersList.push(...previousSpeakers.filter(p => p !== local?.id));
|
||||
|
||||
// Only one dominant speaker is allowed.
|
||||
if (dominantSpeaker) {
|
||||
@@ -104,7 +111,8 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
|
||||
if (_updateParticipantProperty(state, id, 'dominantSpeaker', true)) {
|
||||
return {
|
||||
...state,
|
||||
dominantSpeaker: id
|
||||
dominantSpeaker: id,
|
||||
speakersList
|
||||
};
|
||||
}
|
||||
|
||||
@@ -182,21 +190,22 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
|
||||
}
|
||||
case PARTICIPANT_JOINED: {
|
||||
const participant = _participantJoined(action);
|
||||
const { id, isFakeParticipant, name, pinned } = participant;
|
||||
const { pinnedParticipant, dominantSpeaker } = state;
|
||||
|
||||
if (participant.pinned) {
|
||||
if (pinned) {
|
||||
if (pinnedParticipant) {
|
||||
_updateParticipantProperty(state, pinnedParticipant, 'pinned', false);
|
||||
}
|
||||
|
||||
state.pinnedParticipant = participant.id;
|
||||
state.pinnedParticipant = id;
|
||||
}
|
||||
|
||||
if (participant.dominantSpeaker) {
|
||||
if (dominantSpeaker) {
|
||||
_updateParticipantProperty(state, dominantSpeaker, 'dominantSpeaker', false);
|
||||
}
|
||||
state.dominantSpeaker = participant.id;
|
||||
state.dominantSpeaker = id;
|
||||
}
|
||||
|
||||
const isModerator = isParticipantModerator(participant);
|
||||
@@ -215,10 +224,21 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
|
||||
};
|
||||
}
|
||||
|
||||
state.remote.set(participant.id, participant);
|
||||
state.remote.set(id, participant);
|
||||
|
||||
if (participant.isFakeParticipant) {
|
||||
state.fakeParticipants.set(participant.id, participant);
|
||||
// Insert the new participant.
|
||||
const displayName = name
|
||||
?? (typeof interfaceConfig === 'object' ? interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME : 'Fellow Jitser');
|
||||
const sortedRemoteParticipants = Array.from(state.sortedRemoteParticipants);
|
||||
|
||||
sortedRemoteParticipants.push([ id, displayName ]);
|
||||
sortedRemoteParticipants.sort((a, b) => a[1].localeCompare(b[1]));
|
||||
|
||||
// The sort order of participants is preserved since Map remembers the original insertion order of the keys.
|
||||
state.sortedRemoteParticipants = new Map(sortedRemoteParticipants);
|
||||
|
||||
if (isFakeParticipant) {
|
||||
state.fakeParticipants.set(id, participant);
|
||||
}
|
||||
|
||||
return { ...state };
|
||||
@@ -244,6 +264,8 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
|
||||
return state;
|
||||
}
|
||||
|
||||
state.sortedRemoteParticipants.delete(id);
|
||||
|
||||
if (!state.everyoneIsModerator && !isParticipantModerator(oldParticipant)) {
|
||||
state.everyoneIsModerator = _isEveryoneModerator(state);
|
||||
}
|
||||
@@ -274,6 +296,9 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
|
||||
state.dominantSpeaker = undefined;
|
||||
}
|
||||
|
||||
// Remove the participant from the list of speakers.
|
||||
state.speakersList = state.speakersList.filter(speaker => speaker !== id);
|
||||
|
||||
if (pinnedParticipant === id) {
|
||||
state.pinnedParticipant = undefined;
|
||||
}
|
||||
|
||||
@@ -79,37 +79,35 @@ function ConnectionStatus({ connectionDetails, t, connectionType }: Props) {
|
||||
|
||||
return (
|
||||
<div className = 'con-status'>
|
||||
<div className = 'con-status-container'>
|
||||
<div
|
||||
aria-level = { 1 }
|
||||
className = 'con-status-header'
|
||||
role = 'heading'>
|
||||
<div className = { `con-status-circle ${connectionClass}` }>
|
||||
<Icon
|
||||
size = { 16 }
|
||||
src = { icon } />
|
||||
</div>
|
||||
<span
|
||||
aria-hidden = { !showDetails }
|
||||
className = 'con-status-text'
|
||||
id = 'connection-status-description'>{t(connectionText)}</span>
|
||||
<div
|
||||
aria-level = { 1 }
|
||||
className = 'con-status-header'
|
||||
role = 'heading'>
|
||||
<div className = { `con-status-circle ${connectionClass}` }>
|
||||
<Icon
|
||||
ariaDescribedBy = 'connection-status-description'
|
||||
ariaPressed = { showDetails }
|
||||
className = { arrowClassName }
|
||||
onClick = { onToggleDetails }
|
||||
onKeyPress = { onKeyPressToggleDetails }
|
||||
role = 'button'
|
||||
size = { 24 }
|
||||
src = { IconArrowDownSmall }
|
||||
tabIndex = { 0 } />
|
||||
size = { 16 }
|
||||
src = { icon } />
|
||||
</div>
|
||||
<div
|
||||
aria-level = '2'
|
||||
className = { `con-status-details ${detailsClassName}` }
|
||||
role = 'heading'>
|
||||
{detailsText}</div>
|
||||
<span
|
||||
aria-hidden = { !showDetails }
|
||||
className = 'con-status-text'
|
||||
id = 'connection-status-description'>{t(connectionText)}</span>
|
||||
<Icon
|
||||
ariaDescribedBy = 'connection-status-description'
|
||||
ariaPressed = { showDetails }
|
||||
className = { arrowClassName }
|
||||
onClick = { onToggleDetails }
|
||||
onKeyPress = { onKeyPressToggleDetails }
|
||||
role = 'button'
|
||||
size = { 24 }
|
||||
src = { IconArrowDownSmall }
|
||||
tabIndex = { 0 } />
|
||||
</div>
|
||||
<div
|
||||
aria-level = '2'
|
||||
className = { `con-status-details ${detailsClassName}` }
|
||||
role = 'heading'>
|
||||
{detailsText}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import CopyMeetingLinkSection
|
||||
from '../../../../invite/components/add-people-dialog/web/CopyMeetingLinkSection';
|
||||
import { getCurrentConferenceUrl } from '../../../connection';
|
||||
import { translate } from '../../../i18n';
|
||||
import { connect } from '../../../redux';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The meeting url.
|
||||
*/
|
||||
url: string,
|
||||
|
||||
/**
|
||||
* Used for translation.
|
||||
*/
|
||||
t: Function,
|
||||
|
||||
/**
|
||||
* Used to determine if invitation link should be automatically copied
|
||||
* after creating a meeting.
|
||||
*/
|
||||
_enableAutomaticUrlCopy: boolean,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Component used to copy meeting url on prejoin page.
|
||||
*/
|
||||
class CopyMeetingUrl extends Component<Props> {
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<div className = 'copy-meeting'>
|
||||
<CopyMeetingLinkSection url = { this.props.url } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React {@code Component} props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function mapStateToProps(state) {
|
||||
const { enableAutomaticUrlCopy } = state['features/base/config'];
|
||||
const { customizationReady } = state['features/dynamic-branding'];
|
||||
|
||||
return {
|
||||
url: customizationReady ? getCurrentConferenceUrl(state) : '',
|
||||
_enableAutomaticUrlCopy: enableAutomaticUrlCopy || false
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(translate(CopyMeetingUrl));
|
||||
@@ -2,14 +2,10 @@
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { AudioSettingsButton, VideoSettingsButton } from '../../../../toolbox/components/web';
|
||||
import { VideoBackgroundButton } from '../../../../virtual-background';
|
||||
import { checkBlurSupport } from '../../../../virtual-background/functions';
|
||||
import { Avatar } from '../../../avatar';
|
||||
import { allowUrlSharing } from '../../functions';
|
||||
import DeviceStatus from '../../../../prejoin/components/preview/DeviceStatus';
|
||||
import { Toolbox } from '../../../../toolbox/components/web';
|
||||
|
||||
import ConnectionStatus from './ConnectionStatus';
|
||||
import CopyMeetingUrl from './CopyMeetingUrl';
|
||||
import Preview from './Preview';
|
||||
|
||||
type Props = {
|
||||
@@ -17,12 +13,12 @@ type Props = {
|
||||
/**
|
||||
* Children component(s) to be rendered on the screen.
|
||||
*/
|
||||
children: React$Node,
|
||||
children?: React$Node,
|
||||
|
||||
/**
|
||||
* Footer to be rendered for the page (if any).
|
||||
* Additional CSS class names to set on the icon container.
|
||||
*/
|
||||
footer?: React$Node,
|
||||
className?: string,
|
||||
|
||||
/**
|
||||
* The name of the participant.
|
||||
@@ -35,25 +31,25 @@ type Props = {
|
||||
showCopyUrlButton: boolean,
|
||||
|
||||
/**
|
||||
* Indicates whether the avatar should be shown when video is off
|
||||
* Indicates whether the device status should be shown
|
||||
*/
|
||||
showAvatar: boolean,
|
||||
|
||||
/**
|
||||
* Indicates whether the label and copy url action should be shown
|
||||
*/
|
||||
showConferenceInfo: boolean,
|
||||
|
||||
/**
|
||||
* Title of the screen.
|
||||
*/
|
||||
title: string,
|
||||
showDeviceStatus: boolean,
|
||||
|
||||
/**
|
||||
* The 'Skip prejoin' button to be rendered (if any).
|
||||
*/
|
||||
skipPrejoinButton?: React$Node,
|
||||
|
||||
/**
|
||||
* Title of the screen.
|
||||
*/
|
||||
title?: string,
|
||||
|
||||
/**
|
||||
* Override for default toolbar buttons
|
||||
*/
|
||||
toolbarButtons?: Array<string>,
|
||||
|
||||
/**
|
||||
* True if the preview overlay should be muted, false otherwise.
|
||||
*/
|
||||
@@ -62,14 +58,11 @@ type Props = {
|
||||
/**
|
||||
* The video track to render as preview (if omitted, the default local track will be rendered).
|
||||
*/
|
||||
videoTrack?: Object,
|
||||
|
||||
/**
|
||||
* Array with the buttons which this Toolbox should display.
|
||||
*/
|
||||
visibleButtons?: Array<string>
|
||||
videoTrack?: Object
|
||||
}
|
||||
|
||||
const buttons = [ 'microphone', 'camera', 'select-background', 'invite', 'settings' ];
|
||||
|
||||
/**
|
||||
* Implements a pre-meeting screen that can be used at various pre-meeting phases, for example
|
||||
* on the prejoin screen (pre-connection) or lobby (post-connection).
|
||||
@@ -81,9 +74,8 @@ export default class PreMeetingScreen extends PureComponent<Props> {
|
||||
* @static
|
||||
*/
|
||||
static defaultProps = {
|
||||
showAvatar: true,
|
||||
showCopyUrlButton: true,
|
||||
showConferenceInfo: true
|
||||
showToolbox: true
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -93,57 +85,37 @@ export default class PreMeetingScreen extends PureComponent<Props> {
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
name,
|
||||
showAvatar,
|
||||
showConferenceInfo,
|
||||
showCopyUrlButton,
|
||||
children,
|
||||
className,
|
||||
showDeviceStatus,
|
||||
skipPrejoinButton,
|
||||
title,
|
||||
toolbarButtons,
|
||||
videoMuted,
|
||||
videoTrack,
|
||||
visibleButtons
|
||||
videoTrack
|
||||
} = this.props;
|
||||
const showSharingButton = allowUrlSharing() && showCopyUrlButton;
|
||||
|
||||
const containerClassName = `premeeting-screen ${className ? className : ''}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className = 'premeeting-screen'
|
||||
id = 'lobby-screen'>
|
||||
<ConnectionStatus />
|
||||
<div className = { containerClassName }>
|
||||
<div className = 'content'>
|
||||
<ConnectionStatus />
|
||||
|
||||
<div className = 'content-controls'>
|
||||
<h1 className = 'title'>
|
||||
{ title }
|
||||
</h1>
|
||||
{ children }
|
||||
<Toolbox toolbarButtons = { toolbarButtons || buttons } />
|
||||
{ skipPrejoinButton }
|
||||
{ showDeviceStatus && <DeviceStatus /> }
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Preview
|
||||
videoMuted = { videoMuted }
|
||||
videoTrack = { videoTrack } />
|
||||
<div className = 'content'>
|
||||
{showAvatar && videoMuted && (
|
||||
<Avatar
|
||||
className = 'premeeting-screen-avatar'
|
||||
displayName = { name }
|
||||
dynamicColor = { false }
|
||||
participantId = 'local'
|
||||
size = { 80 } />
|
||||
)}
|
||||
{showConferenceInfo && (
|
||||
<>
|
||||
<h1 className = 'title'>
|
||||
{ title }
|
||||
</h1>
|
||||
{showSharingButton ? <CopyMeetingUrl /> : null}
|
||||
</>
|
||||
)}
|
||||
{ this.props.children }
|
||||
<div className = 'media-btn-container'>
|
||||
<div className = 'toolbox-content'>
|
||||
<div className = 'toolbox-content-items'>
|
||||
<AudioSettingsButton visible = { true } />
|
||||
<VideoSettingsButton visible = { true } />
|
||||
{ ((visibleButtons && visibleButtons.includes('select-background'))
|
||||
|| (visibleButtons && visibleButtons.includes('videobackgroundblur')))
|
||||
&& <VideoBackgroundButton visible = { checkBlurSupport() } /> }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{ this.props.skipPrejoinButton }
|
||||
{ this.props.footer }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,17 +2,30 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { getDisplayName } from '../../../../base/settings';
|
||||
import { Avatar } from '../../../avatar';
|
||||
import { Video } from '../../../media';
|
||||
import { getLocalParticipant } from '../../../participants';
|
||||
import { connect } from '../../../redux';
|
||||
import { getLocalVideoTrack } from '../../../tracks';
|
||||
|
||||
export type Props = {
|
||||
|
||||
/**
|
||||
* Local participant id
|
||||
*/
|
||||
_participantId: string,
|
||||
|
||||
/**
|
||||
* Flag controlling whether the video should be flipped or not.
|
||||
*/
|
||||
flipVideo: boolean,
|
||||
|
||||
/**
|
||||
* The name of the user that is about to join.
|
||||
*/
|
||||
name: string,
|
||||
|
||||
/**
|
||||
* Flag signaling the visibility of camera preview.
|
||||
*/
|
||||
@@ -31,20 +44,27 @@ export type Props = {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
function Preview(props: Props) {
|
||||
const { videoMuted, videoTrack, flipVideo } = props;
|
||||
const { _participantId, flipVideo, name, videoMuted, videoTrack } = props;
|
||||
const className = flipVideo ? 'flipVideoX' : '';
|
||||
|
||||
if (!videoMuted && videoTrack) {
|
||||
return (
|
||||
<div id = 'preview'>
|
||||
<Video
|
||||
className = { className }
|
||||
videoTrack = {{ jitsiTrack: videoTrack }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
return (
|
||||
<div id = 'preview'>
|
||||
{!videoMuted && videoTrack
|
||||
? (
|
||||
<Video
|
||||
className = { className }
|
||||
videoTrack = {{ jitsiTrack: videoTrack }} />
|
||||
)
|
||||
: (
|
||||
<Avatar
|
||||
className = 'premeeting-screen-avatar'
|
||||
displayName = { name }
|
||||
dynamicColor = { false }
|
||||
participantId = { _participantId }
|
||||
size = { 180 } />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,8 +75,13 @@ function Preview(props: Props) {
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps) {
|
||||
const name = getDisplayName(state);
|
||||
const { id: _participantId } = getLocalParticipant(state);
|
||||
|
||||
return {
|
||||
_participantId,
|
||||
flipVideo: state['features/base/settings'].localFlipX,
|
||||
name,
|
||||
videoMuted: ownProps.videoTrack ? ownProps.videoMuted : state['features/base/media'].video.muted,
|
||||
videoTrack: ownProps.videoTrack || (getLocalVideoTrack(state['features/base/tracks']) || {}).jitsiTrack
|
||||
};
|
||||
|
||||
@@ -213,14 +213,3 @@ export function getConnectionData(state: Object) {
|
||||
connectionDetails: []
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if url sharing is enabled in interface configuration.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function allowUrlSharing() {
|
||||
return typeof interfaceConfig === 'undefined'
|
||||
|| typeof interfaceConfig.SHARING_FEATURES === 'undefined'
|
||||
|| (interfaceConfig.SHARING_FEATURES.length && interfaceConfig.SHARING_FEATURES.indexOf('url') > -1);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ const DEFAULT_STATE = {
|
||||
soundsParticipantJoined: true,
|
||||
soundsParticipantLeft: true,
|
||||
soundsTalkWhileMuted: true,
|
||||
soundsReactions: true,
|
||||
startAudioOnly: false,
|
||||
startWithAudioMuted: false,
|
||||
startWithVideoMuted: false,
|
||||
@@ -61,7 +62,7 @@ filterSubtree.audioOutputDeviceId = false;
|
||||
filterSubtree.cameraDeviceId = false;
|
||||
filterSubtree.micDeviceId = false;
|
||||
|
||||
PersistenceRegistry.register(STORE_NAME, filterSubtree);
|
||||
PersistenceRegistry.register(STORE_NAME, filterSubtree, DEFAULT_STATE);
|
||||
|
||||
ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
|
||||
@@ -64,3 +64,13 @@ export const SEND_MESSAGE = 'SEND_MESSAGE';
|
||||
* }
|
||||
*/
|
||||
export const SET_PRIVATE_MESSAGE_RECIPIENT = 'SET_PRIVATE_MESSAGE_RECIPIENT';
|
||||
|
||||
/**
|
||||
* The type of action which signals the update a _isPollsTabFocused.
|
||||
*
|
||||
* {
|
||||
* isPollsTabFocused: boolean,
|
||||
* type: SET_PRIVATE_MESSAGE_RECIPIENT
|
||||
* }
|
||||
*/
|
||||
export const SET_IS_POLL_TAB_FOCUSED = 'SET_IS_POLL_TAB_FOCUSED';
|
||||
|
||||
@@ -5,7 +5,8 @@ import {
|
||||
CLEAR_MESSAGES,
|
||||
CLOSE_CHAT,
|
||||
SEND_MESSAGE,
|
||||
SET_PRIVATE_MESSAGE_RECIPIENT
|
||||
SET_PRIVATE_MESSAGE_RECIPIENT,
|
||||
SET_IS_POLL_TAB_FOCUSED
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
@@ -97,3 +98,16 @@ export function setPrivateMessageRecipient(participant: Object) {
|
||||
type: SET_PRIVATE_MESSAGE_RECIPIENT
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of _isPollsTabFocused.
|
||||
*
|
||||
* @param {boolean} isPollsTabFocused - The new value for _isPollsTabFocused.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function setIsPollsTabFocused(isPollsTabFocused: boolean) {
|
||||
return {
|
||||
isPollsTabFocused,
|
||||
type: SET_IS_POLL_TAB_FOCUSED
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Component } from 'react';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { getLocalParticipant } from '../../base/participants';
|
||||
import { sendMessage } from '../actions';
|
||||
import { sendMessage, setIsPollsTabFocused } from '../actions';
|
||||
import { SMALL_WIDTH_THRESHOLD } from '../constants';
|
||||
|
||||
/**
|
||||
@@ -22,11 +22,31 @@ export type Props = {
|
||||
*/
|
||||
_isOpen: boolean,
|
||||
|
||||
/**
|
||||
* True if the polls feature is enabled.
|
||||
*/
|
||||
_isPollsEnabled: boolean,
|
||||
|
||||
/**
|
||||
* Whether the poll tab is focused or not.
|
||||
*/
|
||||
_isPollsTabFocused: boolean,
|
||||
|
||||
/**
|
||||
* All the chat messages in the conference.
|
||||
*/
|
||||
_messages: Array<Object>,
|
||||
|
||||
/**
|
||||
* Number of unread chat messages.
|
||||
*/
|
||||
_nbUnreadMessages: number,
|
||||
|
||||
/**
|
||||
* Number of unread poll messages.
|
||||
*/
|
||||
_nbUnreadPolls: number,
|
||||
|
||||
/**
|
||||
* Function to send a text message.
|
||||
*
|
||||
@@ -34,6 +54,20 @@ export type Props = {
|
||||
*/
|
||||
_onSendMessage: Function,
|
||||
|
||||
/**
|
||||
* Function to display the chat tab.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
_onToggleChatTab: Function,
|
||||
|
||||
/**
|
||||
* Function to display the polls tab.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
_onTogglePollsTab: Function,
|
||||
|
||||
/**
|
||||
* Function to toggle the chat window.
|
||||
*/
|
||||
@@ -52,7 +86,7 @@ export type Props = {
|
||||
/**
|
||||
* Function to be used to translate i18n labels.
|
||||
*/
|
||||
t: Function
|
||||
t: Function,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -71,6 +105,8 @@ export default class AbstractChat<P: Props> extends Component<P> {
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onSendMessage = this._onSendMessage.bind(this);
|
||||
this._onToggleChatTab = this._onToggleChatTab.bind(this);
|
||||
this._onTogglePollsTab = this._onTogglePollsTab.bind(this);
|
||||
}
|
||||
|
||||
_onSendMessage: (string) => void;
|
||||
@@ -86,6 +122,30 @@ export default class AbstractChat<P: Props> extends Component<P> {
|
||||
_onSendMessage(text: string) {
|
||||
this.props.dispatch(sendMessage(text));
|
||||
}
|
||||
|
||||
_onToggleChatTab: () => void;
|
||||
|
||||
/**
|
||||
* Display the Chat tab.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onToggleChatTab() {
|
||||
this.props.dispatch(setIsPollsTabFocused(false));
|
||||
}
|
||||
|
||||
_onTogglePollsTab: () => void;
|
||||
|
||||
/**
|
||||
* Display the Polls tab.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onTogglePollsTab() {
|
||||
this.props.dispatch(setIsPollsTabFocused(true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,13 +161,19 @@ export default class AbstractChat<P: Props> extends Component<P> {
|
||||
* }}
|
||||
*/
|
||||
export function _mapStateToProps(state: Object) {
|
||||
const { isOpen, messages } = state['features/chat'];
|
||||
const { isOpen, isPollsTabFocused, messages, nbUnreadMessages } = state['features/chat'];
|
||||
const { nbUnreadPolls } = state['features/polls'];
|
||||
const _localParticipant = getLocalParticipant(state);
|
||||
const { disablePolls } = state['features/base/config'];
|
||||
|
||||
return {
|
||||
_isModal: window.innerWidth <= SMALL_WIDTH_THRESHOLD,
|
||||
_isOpen: isOpen,
|
||||
_isPollsEnabled: !disablePolls,
|
||||
_isPollsTabFocused: isPollsTabFocused,
|
||||
_messages: messages,
|
||||
_nbUnreadMessages: nbUnreadMessages,
|
||||
_nbUnreadPolls: nbUnreadPolls,
|
||||
_showNamePrompt: !_localParticipant?.name
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { Button } from 'react-native-paper';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { JitsiModal } from '../../../base/modal';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { PollsPane } from '../../../polls/components';
|
||||
import { closeChat } from '../../actions.any';
|
||||
import { CHAT_VIEW_MODAL_ID } from '../../constants';
|
||||
import { BUTTON_MODES, CHAT_VIEW_MODAL_ID } from '../../constants';
|
||||
import AbstractChat, {
|
||||
_mapStateToProps,
|
||||
type Props
|
||||
@@ -15,6 +18,7 @@ import AbstractChat, {
|
||||
import ChatInputBar from './ChatInputBar';
|
||||
import MessageContainer from './MessageContainer';
|
||||
import MessageRecipient from './MessageRecipient';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
* Implements a React native component that renders the chat window (modal) of
|
||||
@@ -45,10 +49,49 @@ class Chat extends AbstractChat<Props> {
|
||||
}}
|
||||
modalId = { CHAT_VIEW_MODAL_ID }
|
||||
onClose = { this._onClose }>
|
||||
|
||||
<MessageContainer messages = { this.props._messages } />
|
||||
<MessageRecipient />
|
||||
<ChatInputBar onSend = { this._onSendMessage } />
|
||||
{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>
|
||||
);
|
||||
}
|
||||
@@ -57,6 +100,9 @@ class Chat extends AbstractChat<Props> {
|
||||
|
||||
_onClose: () => boolean
|
||||
|
||||
_onTogglePollsTab: () => void;
|
||||
_onToggleChatTab: () => void;
|
||||
|
||||
/**
|
||||
* Closes the modal.
|
||||
*
|
||||
|
||||
@@ -124,6 +124,25 @@ export default {
|
||||
timeText: {
|
||||
color: 'rgb(164, 184, 209)',
|
||||
fontSize: 13
|
||||
},
|
||||
|
||||
tabContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
|
||||
tabLeftButton: {
|
||||
flex: 1,
|
||||
borderTopLeftRadius: 0,
|
||||
borderTopRightRadius: 0,
|
||||
borderBottomLeftRadius: 0
|
||||
},
|
||||
|
||||
tabRightButton: {
|
||||
flex: 1,
|
||||
borderTopLeftRadius: 0,
|
||||
borderTopRightRadius: 0,
|
||||
borderBottomRightRadius: 0
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import React from 'react';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { PollsPane } from '../../../polls/components';
|
||||
import { toggleChat } from '../../actions.web';
|
||||
import AbstractChat, {
|
||||
_mapStateToProps,
|
||||
@@ -128,8 +129,20 @@ class Chat extends AbstractChat<Props> {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderChat() {
|
||||
|
||||
if (this.props._isPollsTabFocused) {
|
||||
return (
|
||||
<>
|
||||
{ this.props._isPollsEnabled && this._renderTabs()}
|
||||
<PollsPane />
|
||||
<KeyboardAvoider />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.props._isPollsEnabled && this._renderTabs()}
|
||||
<TouchmoveHack isModal = { this.props._isModal }>
|
||||
<MessageContainer
|
||||
messages = { this.props._messages }
|
||||
@@ -144,6 +157,50 @@ class Chat extends AbstractChat<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a React Element showing the Chat and Polls tab.
|
||||
*
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderTabs() {
|
||||
|
||||
return (
|
||||
<div className = { 'chat-tabs-container' }>
|
||||
<div
|
||||
className = { `chat-tab ${
|
||||
this.props._isPollsTabFocused ? '' : 'chat-tab-focus'
|
||||
}` }
|
||||
onClick = { this._onToggleChatTab }>
|
||||
<span className = { 'chat-tab-title' }>
|
||||
{this.props.t('chat.tabs.chat')}
|
||||
</span>
|
||||
{this.props._isPollsTabFocused
|
||||
&& this.props._nbUnreadMessages > 0 && (
|
||||
<span className = { 'chat-tab-badge' }>
|
||||
{this.props._nbUnreadMessages}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className = { `chat-tab ${
|
||||
this.props._isPollsTabFocused ? 'chat-tab-focus' : ''
|
||||
}` }
|
||||
onClick = { this._onTogglePollsTab }>
|
||||
<span className = { 'chat-tab-title' }>
|
||||
{this.props.t('chat.tabs.polls')}
|
||||
</span>
|
||||
{!this.props._isPollsTabFocused
|
||||
&& this.props._nbUnreadPolls > 0 && (
|
||||
<span className = { 'chat-tab-badge' }>
|
||||
{this.props._nbUnreadPolls}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a React Element to display at the top of {@code Chat} to
|
||||
* close {@code Chat}.
|
||||
@@ -233,6 +290,8 @@ class Chat extends AbstractChat<Props> {
|
||||
_onToggleChat() {
|
||||
this.props.dispatch(toggleChat());
|
||||
}
|
||||
_onTogglePollsTab: () => void;
|
||||
_onToggleChatTab: () => void;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { connect } from '../../../base/redux';
|
||||
import { getUnreadPollCount } from '../../../polls/functions';
|
||||
import { getUnreadCount } from '../../functions';
|
||||
|
||||
/**
|
||||
@@ -64,8 +65,10 @@ function _mapStateToProps(state) {
|
||||
const { isOpen } = state['features/chat'];
|
||||
|
||||
return {
|
||||
_count: getUnreadCount(state),
|
||||
|
||||
_count: getUnreadCount(state) + getUnreadPollCount(state),
|
||||
_isOpen: isOpen
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,3 +31,11 @@ export const MESSAGE_TYPE_LOCAL = 'local';
|
||||
export const MESSAGE_TYPE_REMOTE = 'remote';
|
||||
|
||||
export const SMALL_WIDTH_THRESHOLD = 580;
|
||||
|
||||
/**
|
||||
* The modes of the buttons of the chat and polls tabs.
|
||||
*/
|
||||
export const BUTTON_MODES = {
|
||||
CONTAINED: 'contained',
|
||||
TEXT: 'text'
|
||||
};
|
||||
|
||||
@@ -78,3 +78,15 @@ export function getUnreadCount(state: Object) {
|
||||
|
||||
return messagesCount - (lastReadIndex + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector for calculating the number of unread chat messages.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {number} The number of unread messages.
|
||||
*/
|
||||
export function getUnreadMessagesCount(state: Object) {
|
||||
const { nbUnreadMessages } = state['features/chat'];
|
||||
|
||||
return nbUnreadMessages;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
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';
|
||||
import { getReactionMessageFromBuffer } from '../reactions/functions.any';
|
||||
@@ -33,7 +34,7 @@ import {
|
||||
setToolboxVisible
|
||||
} from '../toolbox/actions.web';
|
||||
|
||||
import { ADD_MESSAGE, SEND_MESSAGE, OPEN_CHAT, CLOSE_CHAT } from './actionTypes';
|
||||
import { ADD_MESSAGE, SEND_MESSAGE, OPEN_CHAT, CLOSE_CHAT, SET_IS_POLL_TAB_FOCUSED } from './actionTypes';
|
||||
import { addMessage, clearMessages } from './actions';
|
||||
import { closeChat } from './actions.any';
|
||||
import { ChatPrivacyDialog } from './components';
|
||||
@@ -112,15 +113,27 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
}
|
||||
break;
|
||||
|
||||
case CLOSE_CHAT:
|
||||
case CLOSE_CHAT: {
|
||||
const isPollTabOpen = getState()['features/chat'].isPollsTabFocused;
|
||||
|
||||
unreadCount = 0;
|
||||
|
||||
if (typeof APP !== 'undefined') {
|
||||
APP.API.notifyChatUpdated(unreadCount, false);
|
||||
}
|
||||
|
||||
if (isPollTabOpen) {
|
||||
dispatch(resetNbUnreadPollsMessages());
|
||||
}
|
||||
|
||||
dispatch(setActiveModalId());
|
||||
break;
|
||||
}
|
||||
|
||||
case SET_IS_POLL_TAB_FOCUSED: {
|
||||
dispatch(resetNbUnreadPollsMessages());
|
||||
break;
|
||||
}
|
||||
|
||||
case SEND_MESSAGE: {
|
||||
const state = store.getState();
|
||||
|
||||
@@ -7,13 +7,17 @@ import {
|
||||
CLEAR_MESSAGES,
|
||||
CLOSE_CHAT,
|
||||
OPEN_CHAT,
|
||||
SET_PRIVATE_MESSAGE_RECIPIENT
|
||||
SET_PRIVATE_MESSAGE_RECIPIENT,
|
||||
SET_IS_POLL_TAB_FOCUSED
|
||||
} from './actionTypes';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
isOpen: false,
|
||||
isPollsTabFocused: false,
|
||||
lastReadMessage: undefined,
|
||||
lastReadPoll: undefined,
|
||||
messages: [],
|
||||
nbUnreadMessages: 0,
|
||||
privateMessageRecipient: undefined
|
||||
};
|
||||
|
||||
@@ -46,6 +50,7 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
|
||||
...state,
|
||||
lastReadMessage:
|
||||
action.hasRead ? newMessage : state.lastReadMessage,
|
||||
nbUnreadMessages: state.isPollsTabFocused ? state.nbUnreadMessages + 1 : state.nbUnreadMessages,
|
||||
messages
|
||||
};
|
||||
}
|
||||
@@ -78,6 +83,13 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
|
||||
navigator.product === 'ReactNative' ? 0 : state.messages.length - 1],
|
||||
privateMessageRecipient: action.participant
|
||||
};
|
||||
|
||||
case SET_IS_POLL_TAB_FOCUSED: {
|
||||
return {
|
||||
...state,
|
||||
isPollsTabFocused: action.isPollsTabFocused,
|
||||
nbUnreadMessages: 0
|
||||
}; }
|
||||
}
|
||||
|
||||
return state;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user