Compare commits

...

53 Commits

Author SHA1 Message Date
paweldomas
855eeafc79 feat(accessibility): set html 'lang' attribute
...to match the currently selected language. Starts with
'en' by default.
2020-04-03 18:23:21 -05:00
Ruben Kerkhof
06ae1861ee log: fix showing verbatim %s
So turn 'hover in %s 1a5cd940' into 'hover in 1a5cd940'
2020-04-03 15:41:30 +02:00
Alexander Neumann
e0afd8fadb lang: fix german translation
This commit fixes a few translation errors, especially in the
kickParticipantTitle.
2020-04-03 15:39:38 +02:00
Michael Telatynski
953f838a2a Improve accessibility of Buttons in Webapp (#5432)
* Expose toggle buttons better via ARIA

* Wire up the divs/li as role=button as per ARIA patterns

* Add flow annotations to appease the linter

* For role=button use aria-pressed not aria-checked
2020-04-03 08:19:32 -05:00
ljo
bf5f1f0168 lang: improve sv translation 2020-04-03 15:13:26 +02:00
Saúl Ibarra Corretgé
cdf977ff3f doc: add documentation index 2020-04-03 14:57:40 +02:00
Joris Bodin
ee1dc9dd8e lang: update translation for fr, de, es, it for mobile app 2020-04-03 12:14:21 +02:00
Ali Karpuzoglu
7bec68e492 doc: fix typo 2020-04-03 12:13:20 +02:00
Bettenbuk Zoltan
97fff02c15 web: use password i18n placeholder 2020-04-03 11:22:11 +02:00
Дамян Минков
b372b2ccf2 Fix turn file link (#5601)
* debian: Does not add a link of turnserver will not be configured. Fixes #5596.

* debian: Partially reverts 990c77bd.

* debian: Skip filename duplication, use variable.
2020-04-02 18:20:41 -05:00
Bettenbuk Zoltan
d00ead932b feat: better room lock prompt 2020-04-02 17:27:22 +02:00
Saúl Ibarra Corretgé
bb2b1b58ec conference: add room name to browser title
Re-do of
86ebfe8dad
2020-04-02 16:33:39 +02:00
GK2
ceeea7314c debian: fix typo 2020-04-02 15:27:19 +02:00
Andrei Gavrilescu
9d6a93119b feat(screenSharing): Add support for audio screen sharing on electron 2020-04-02 16:18:10 +03:00
GK2
08be68cda4 debian: fix install-letsencrypt-cert.sh to create misssing directory
Update install-letsencrypt-cert.sh to fix missing cron.weekly
REFS : https://github.com/jitsi/jitsi-meet/issues/5576
2020-04-02 15:10:27 +02:00
Bettenbuk Zoltan
3a2081ffed ref: change Chat to JitsiModal 2020-04-02 12:40:12 +02:00
Bettenbuk Zoltan
57d14d9517 feat: help centre 2020-04-02 12:40:12 +02:00
Saúl Ibarra Corretgé
c5e693f14a rn,invite: use custom clear button also on iOS
Otherwise it won't be visible if Dark Mode is enabled.
2020-04-02 11:46:29 +02:00
Saúl Ibarra Corretgé
a9da047d3a rn,invite: fix bottom padding on AddPeopleDialog on Android 2020-04-02 11:46:29 +02:00
bgrozev
171b065db1 Revert "Add room name to browser tab title" (#5560)
Because it displays "undefined" in the title.

This reverts commit 86ebfe8dad.
2020-04-01 22:03:14 -05:00
Jaya Allamsetty
9da0b7fee3 deps(ljm): Bump adapter.js to 7.5.0 and validate ice candidate info 2020-04-01 18:28:30 -04:00
damencho
716c9eb46f debian: Change coturn unit file to start on 443. 2020-04-01 17:11:55 -05:00
Stefan
a85c72d859 doc: use sudo 2020-04-01 22:57:08 +02:00
Kenneth Peiruza
0ba567856e lang: fix catalan translation
There was a huge mistake in Catalan translation. Stop streaming and recording said "start" (inicia) instead of "stop" (atura).

Then, 1 small fix on a more polite way of refering to "using a software" -> *emprar* rather than *usar*.
2020-04-01 22:51:38 +02:00
Horatiu Muresan
7f1eb83dbd feat(notifications): Manage audio notifications 2020-04-01 22:40:07 +02:00
damencho
6e4c1f64d8 debian: Adds syslog logging. 2020-04-01 15:18:49 -05:00
damencho
82aa51770a debian: Skip turn config if other sites are available listening on 443. 2020-04-01 15:18:49 -05:00
damencho
990c77bd3d debian: Skip turnserver config if it is already configured. 2020-04-01 15:18:49 -05:00
bgrozev
5bb23b2d17 chore(package.json): Update js-utils (#5541) 2020-04-01 15:09:11 -05:00
Saúl Ibarra Corretgé
9cc05ef838 config: remove unneeded option 2020-04-01 20:40:18 +02:00
Saúl Ibarra Corretgé
a21e3a1946 Merge pull request #5510 from rubenk/add-roomname-to-title
Add room name to browser tab title
2020-04-01 20:08:05 +02:00
Vlad Piersec
c05ca1d9fc fix(prejoin_page) Add labels for video & more UI fixes 2020-04-01 19:57:04 +02:00
Vlad Piersec
1b05d7269c feat(prejoin_page) Add settings buttons
This reverts commit faf24ca7ec.
2020-04-01 19:57:04 +02:00
paweldomas
c170970992 feat(Filmstrip): accessibility label for toggle filmstrip button 2020-04-01 08:49:12 -05:00
Saúl Ibarra Corretgé
d19a659871 ios: update Crasshlytics and fix uploadding symbols 2020-04-01 15:31:28 +02:00
Saúl Ibarra Corretgé
955b24be9d rn,cc: add feature-flag to disable close captions 2020-04-01 15:29:21 +02:00
Saúl Ibarra Corretgé
de6c7e0117 rn,invite: add share button to add people dialog 2020-04-01 15:26:29 +02:00
Bettenbuk Zoltan
feb8fe9e34 api: add private message flag to outgoingMessage 2020-04-01 11:58:48 +02:00
Steve Frécinaux
aff6d4b36d Fix mod_muc_max_occupants to properly ignore whitelisted users
In a typical Jitsi Meet setup, this plugin can be used to limit the number of
occupants in a meeting room, while ignoring "utility" users. Such a
configuration could be:

    muc_max_occupants = 2
    muc_access_whitelist = {
        "focus@auth.meet.jitsi";
    }

It would be expected that this configuration allows two users to attend the
meeting room, but in practice only one is allowed, because the whitelist is not
honoured.

This commit fixes it by actually updating the `user` and `domain` variables
being checked. After this change, the scenario above works just fine.
2020-03-31 16:38:23 -05:00
damencho
45c60717d6 debian: add post remove script for jitsi-meet-turnserver package
Reloads the webserver as the config had changed.
2020-03-31 16:27:25 -05:00
Saúl Ibarra Corretgé
6596e27f69 Prevent meetings from being crawled by Google 2020-03-31 21:45:28 +02:00
Saúl Ibarra Corretgé
8cba7e91d3 ci: build the frontend in GH actions
It should help catch bugs such as bundle bloat easier.
2020-03-31 21:44:48 +02:00
Hristo Terezov
3a871cbed8 feat(remote-menu):option for disable mute and kick 2020-03-31 10:06:30 -05:00
Bettenbuk Zoltan
a46fd60788 fix: accented room name display 2020-03-31 14:16:21 +02:00
Ruben Kerkhof
86ebfe8dad Add room name to browser tab title
Fixes #5480

Signed-off-by: Ruben Kerkhof <ruben@rubenkerkhof.com>
2020-03-31 13:58:12 +02:00
Juri
97735ff548 lang: fix Estonian translations 2020-03-31 13:10:38 +02:00
James Baird
9fdc18d1ec welcome: hide tabs if nothing to show 2020-03-31 13:06:04 +02:00
Bartosz Dziewoński
4a21882345 lang: translation improvements and corrections for Polish 2020-03-31 09:43:13 +02:00
damencho
cb0cea4ebd Adds a note to re-run Let’s Encrypt script on jetty upgrade. 2020-03-31 09:36:30 +02:00
damencho
b894daa9cf Cleans created users on purging. 2020-03-31 09:36:30 +02:00
damencho
06641a7dd6 This is to prevent item-not-found when multiple users try to join at the same time.
This is to prevent item-not-found when multiple users try to join while jicofo is still configuring the room. Credits @paweldomas
2020-03-31 09:36:30 +02:00
Saúl Ibarra Corretgé
94f7b570d7 debian: fix creating internal MUC 2020-03-30 17:01:27 -05:00
Saúl Ibarra Corretgé
ca5605620a rn: update versions, apps 20.2, sdk 2.7 2020-03-30 15:45:16 +02:00
131 changed files with 3744 additions and 432 deletions

View File

@@ -1,10 +1,10 @@
name: Linter
name: Simple CI
on: [pull_request]
jobs:
run-linter:
name: Run Linter
run-ci:
name: Build Frontend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -13,3 +13,4 @@ jobs:
node-version: '12.x'
- run: npm install
- run: npm run lint
- run: make

View File

@@ -20,5 +20,5 @@
android.useAndroidX=true
android.enableJetifier=true
appVersion=20.1.0
sdkVersion=2.6.0
appVersion=20.2.0
sdkVersion=2.8.0

View File

@@ -390,6 +390,9 @@ var config = {
// userRegion: "asia"
},
// Decides whether the start/stop recording audio notifications should play on record.
// disableRecordAudioNotification: false,
// Information for the chrome extension banner
// chromeExtensionBanner: {
// // The chrome extension to be installed address
@@ -459,6 +462,15 @@ var config = {
// downloadAppsUrl: 'https://docs.example.com/our-apps.html'
// },
// Options related to the remote participant menu.
// remoteVideoMenu: {
// // If set to true the 'Kick out' button will be disabled.
// disableKick: true
// },
// If set to true all muting operations of remote participants will be disabled.
// disableRemoteMute: true,
// List of undocumented settings used in jitsi-meet
/**
_immediateReloadThreshold

130
css/_audio-preview.css Normal file
View File

@@ -0,0 +1,130 @@
.audio-preview {
&-content {
font-size: 15px;
line-height: 24px;
max-height: 456px;
overflow: auto;
width: 328px;
}
&-header {
color: #fff;
display: flex;
padding: 16px;
&-icon {
display: inline-block;
}
&-text {
font-weight: bold;
margin-left: 8px;
}
}
&-entry {
align-items: center;
color: #fff;
cursor: pointer;
display: flex;
padding: 12px 0;
margin-left: 48px;
&--selected {
background: rgba(28,32,37,0.5);
cursor: initial;
margin-left: 0;
padding-left: 21px;
}
&-text {
color: #fff;
font-size: 15px;
display: inline-block;
line-height: 24px;
text-overflow: ellipsis;
max-width: 213px;
overflow: hidden;
white-space: nowrap;
}
}
&-speaker {
position: relative;
&:hover {
.audio-preview-entry {
background: rgba(255,255,255, 0.2);
margin-left: 0;
padding-left: 48px;
&--selected {
padding-left: 21px;
}
}
.audio-preview-test-button {
display: inline-block;
}
}
.audio-preview-entry-text {
max-width: 256px;
}
}
&-microphone {
position: relative;
}
&-icon {
border-radius: 50%;
display: inline-block;
width: 14px;
& svg {
fill: #1C2025;
}
&--check {
background: #31B76A;
margin-right: 13px;
}
&--exclamation {
margin-left: 6px;
& svg {
fill: #E54B4B;
}
}
}
&-test-button {
display: none;
background: #FFF;
border: 1px solid #D1DBE8;
border-radius: 3px;
color: #1C2025;
cursor: pointer;
font-weight: 600;
font-size: 15px;
line-height: 24px;
padding: 4px 16px;
position: absolute;
right: 16px;
top: 8px;
}
&-meter-mic {
position: absolute;
right: 16px;
top: 18px;
}
// Override @atlaskit/InlineDialog container which is made with styled components
& > div > div:nth-child(2) > div > div {
outline: none;
padding: 0;
}
}

30
css/_meter.css Normal file
View File

@@ -0,0 +1,30 @@
.jitsi-icon {
&.metr {
display: inline-block;
& > svg {
fill: #4E5E6C;
width: 38px;
}
}
&.metr--disabled {
& > svg {
fill: #4E5E6C;
}
}
}
.metr-l-0 {
rect:first-child {
fill: #31B76A;
}
}
@for $i from 1 through 7 {
.metr-l-#{$i} {
rect:nth-child(-n+#{$i+1}) {
fill: #31B76A;
}
}
}

79
css/_settings-button.scss Normal file
View File

@@ -0,0 +1,79 @@
.settings-button {
&-container {
position: relative;
.toolbox-icon {
align-items: center;
cursor: pointer;
display: flex;
background-color: #fff;
border-radius: 50%;
border: 1px solid #d1dbe8;
justify-content: center;
width: 38px;
height: 38px;
&:hover {
background-color: #daebfa;
border: 1px solid #daebfa;
}
&.toggled {
background: #2a3a4b;
border: 1px solid #5e6d7a;
svg {
fill: #fff;
}
&:hover {
background-color: #5e6d7a;
}
}
&.disabled, .disabled & {
cursor: initial;
color: #fff;
background-color: #a4b8d1;
}
svg {
fill: #5e6d7a;
}
}
}
&-small-icon {
background: #FFF;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 50%;
bottom: 0;
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.25);
cursor: pointer;
height: 16px;
position: absolute;
text-align: center;
right: 4px;
width: 16px;
&> svg {
margin-top: 5px;
}
&--disabled {
background-color: #a4b8d1;
cursor: default;
}
&--hovered {
bottom: -1px;
height: 20px;
right: 2px;
width: 20px;
&> svg {
margin-top: 6px;
}
}
}
}

65
css/_video-preview.css Normal file
View File

@@ -0,0 +1,65 @@
.video-preview {
max-height: 290px;
overflow: auto;
&-entry {
cursor: pointer;
height: 135px;
margin-bottom: 16px;
position: relative;
width: 240px;
&:last-child {
margin-bottom: 0;
}
&--selected {
border: 3px solid #31B76A;
cursor: default;
height: 129px;
width: 234px;
}
}
&-video {
height: 100%;
object-fit: cover;
width: 100%;
}
&-overlay {
background: rgba(42, 58, 75, 0.6);
height: 100%;
position: absolute;
width: 100%;
z-index: 1;
}
&-error {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
position: absolute;
width: 100%;
}
&-label {
color: #fff;
font-size: 13px;
line-height: 20px;
overflow: hidden;
padding: 8px;
position: absolute;
text-align: center;
text-overflow: ellipsis;
width: 220px;
z-index: 2;
}
// Override @atlaskit/InlineDialog container which is made with styled components
& > div > div:nth-child(2) > div > div {
outline: none;
padding: 16px;
}
}

View File

@@ -86,5 +86,9 @@ $flagsImagePath: "../images/";
@import 'avatar';
@import 'promotional-footer';
@import 'chrome-extension-banner';
@import 'settings-button';
@import 'meter';
@import 'audio-preview';
@import 'video-preview';
/* Modules END */

View File

@@ -136,7 +136,7 @@ case "$1" in
echo -e "\nComponent \"internal.auth.$JVB_HOSTNAME\" \"muc\"" >> $PROSODY_HOST_CONFIG
echo -e " storage = \"null\"" >> $PROSODY_HOST_CONFIG
echo -e " modules_enabled = { \"ping\"; }" >> $PROSODY_HOST_CONFIG
echo -e " admins = { \"focusUser@auth.jitmeet.example.com\", \"jvb@auth.jitmeet.example.com\" }" >> $PROSODY_HOST_CONFIG
echo -e " admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\", \"jvb@auth.$JVB_HOSTNAME\" }" >> $PROSODY_HOST_CONFIG
fi
if [ ! -f /var/lib/prosody/$JVB_HOSTNAME.crt ]; then

View File

@@ -36,13 +36,17 @@ case "$1" in
rm -f /etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua
rm -f /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua
JICOFO_AUTH_DOMAIN="auth.$JVB_HOSTNAME"
# clean up generated certificates
rm -f /etc/prosody/certs/$JVB_HOSTNAME.crt
rm -f /etc/prosody/certs/$JVB_HOSTNAME.key
rm -f /etc/prosody/certs/auth.$JVB_HOSTNAME.crt
rm -f /etc/prosody/certs/auth.$JVB_HOSTNAME.key
rm -rf /var/lib/prosody/auth.$JVB_HOSTNAME.*
rm -f /etc/prosody/certs/$JICOFO_AUTH_DOMAIN.crt
rm -f /etc/prosody/certs/$JICOFO_AUTH_DOMAIN.key
rm -rf /var/lib/prosody/$JICOFO_AUTH_DOMAIN.*
rm -rf /var/lib/prosody/$JVB_HOSTNAME.*
# clean created users
rm -rf /var/lib/prosody/`echo $JICOFO_AUTH_DOMAIN | sed -e "s/\./%2e/g"`
fi
# Clear the debconf variable

View File

@@ -1 +0,0 @@
/usr/share/jitsi-meet-turnserver/jitsi-meet.conf /etc/nginx/modules-enabled/60-jitsi-meet.conf

View File

@@ -36,20 +36,60 @@ case "$1" in
NGINX_CONFIG="/etc/nginx/sites-available/$JVB_HOSTNAME.conf"
JITSI_MEET_CONFIG="/etc/jitsi/meet/$JVB_HOSTNAME-config.js"
NGINX_SITES_ENABLED="/etc/nginx/sites-enabled/"
NGINX_CONFIG_ENABLED="${NGINX_SITES_ENABLED}${JVB_HOSTNAME}.conf"
for site in ${NGINX_SITES_ENABLED}*; do
# if it is not a file continue
[ -f "${site}" ] || continue
# if it is our config skip
[ "${site}" != "${NGINX_CONFIG_ENABLED}" ] || continue
# check whether other enabled hosts has listen 443
if cat ${site} | grep -v "^[[:space:]]*#" | grep listen | grep -q "^.*[[:space:]:]443[;[:space:]].*" ; then
# nothing to do
echo "------------------------------------------------"
echo ""
echo "turnserver not configured as other nginx sites use port 443"
echo ""
echo "------------------------------------------------"
db_stop
exit 0
fi
done
# if there was a turn config backup it so we can configure
# we cannot recognize at the moment is this a user config or default config when installing coturn
if [[ -f $TURN_CONFIG ]] && ! grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then
mv $TURN_CONFIG $TURN_CONFIG.bak
fi
# detect dpkg-reconfigure, just delete old links
db_get jitsi-meet-turnserver/jvb-hostname
JVB_HOSTNAME_OLD=$RET
if [ -n "$RET" ] && [ ! "$JVB_HOSTNAME_OLD" = "$JVB_HOSTNAME" ] ; then
rm -f $TURN_CONFIG
if [[ -f $TURN_CONFIG ]] && grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then
rm -f $TURN_CONFIG
fi
fi
# this detect only old installations with no nginx
db_get jitsi-meet/jvb-serve || true
if [ ! -f $NGINX_CONFIG -o "$RET" = "true" ] ; then
# nothing to do
echo "------------------------------------------------"
echo ""
echo "turnserver not configured as no nginx found to multiplex traffic"
echo ""
echo "------------------------------------------------"
db_stop
exit 0
fi
if [[ -f $TURN_CONFIG ]] ; then
echo "------------------------------------------------"
echo ""
echo "turnserver is already configured on this machine, skipping."
echo ""
echo "------------------------------------------------"
db_stop
exit 0
fi
@@ -65,46 +105,53 @@ case "$1" in
fi
TURN_SECRET="$RET"
if [[ -f $TURN_CONFIG ]] && ! grep -q "jitsi-meet coturn config" "$TURN_CONFIG" ; then
PUBLIC_IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
cp /usr/share/jitsi-meet-turnserver/turnserver.conf $TURN_CONFIG
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $TURN_CONFIG
sed -i "s/__turnSecret__/$TURN_SECRET/g" $TURN_CONFIG
sed -i "s/__external_ip_address__/$JVB_HOSTNAME/g" $TURN_CONFIG
# no turn config exists, lt's copy template and fill it in
PUBLIC_IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
cp /usr/share/jitsi-meet-turnserver/turnserver.conf $TURN_CONFIG
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $TURN_CONFIG
sed -i "s/__turnSecret__/$TURN_SECRET/g" $TURN_CONFIG
sed -i "s/__external_ip_address__/$JVB_HOSTNAME/g" $TURN_CONFIG
# SSL for nginx
db_get jitsi-meet/cert-choice
CERT_CHOICE="$RET"
# Hack Debian Buster coturn to be able to bind privileged port 443
COTURN_UNIT_FILE="/lib/systemd/system/coturn.service"
if [[ -f $COTURN_UNIT_FILE ]] && ! grep -q "CAP_NET_BIND_SERVICE" "$COTURN_UNIT_FILE" ; then
sed -i "s/\[Service\]/\[Service\]\nAmbientCapabilities=CAP_NET_BIND_SERVICE/g" $COTURN_UNIT_FILE
systemctl daemon-reload
fi
if [ "$CERT_CHOICE" = "I want to use my own certificate" ] ; then
db_get jitsi-meet/cert-path-key
CERT_KEY="$RET"
db_get jitsi-meet/cert-path-crt
CERT_CRT="$RET"
# SSL for nginx
db_get jitsi-meet/cert-choice
CERT_CHOICE="$RET"
# replace self-signed certificate paths with user provided ones
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
sed -i "s/pkey=\/etc\/jitsi\/meet\/.*key/pkey=$CERT_KEY_ESC/g" $TURN_CONFIG
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
sed -i "s/cert=\/etc\/jitsi\/meet\/.*crt/cert=$CERT_CRT_ESC/g" $TURN_CONFIG
fi
if [ "$CERT_CHOICE" = "I want to use my own certificate" ] ; then
db_get jitsi-meet/cert-path-key
CERT_KEY="$RET"
db_get jitsi-meet/cert-path-crt
CERT_CRT="$RET"
sed -i "s/#TURNSERVER_ENABLED/TURNSERVER_ENABLED/g" /etc/default/coturn
invoke-rc.d coturn restart || true
# replace self-signed certificate paths with user provided ones
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
sed -i "s/pkey=\/etc\/jitsi\/meet\/.*key/pkey=$CERT_KEY_ESC/g" $TURN_CONFIG
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
sed -i "s/cert=\/etc\/jitsi\/meet\/.*crt/cert=$CERT_CRT_ESC/g" $TURN_CONFIG
fi
NGINX_STREAM_CONFIG="/etc/nginx/modules-enabled/60-jitsi-meet.conf"
if [ -f $NGINX_STREAM_CONFIG ] && [ -f $NGINX_CONFIG ] ; then
sed -i "s/listen 443 ssl/listen 4444 ssl http2/g" $NGINX_CONFIG
sed -i "s/listen \[\:\:\]\:443 ssl/listen \[\:\:\]\:4444 ssl http2/g" $NGINX_CONFIG
invoke-rc.d nginx reload || true
fi
sed -i "s/#TURNSERVER_ENABLED/TURNSERVER_ENABLED/g" /etc/default/coturn
invoke-rc.d coturn restart || true
# Enable turn server in config.js
if [ -f $JITSI_MEET_CONFIG ] ; then
sed -i "s/\/\/ useStunTurn: true/useStunTurn: true/g" $JITSI_MEET_CONFIG
fi
NGINX_STREAM_CONFIG="/etc/nginx/modules-enabled/60-jitsi-meet.conf"
if [ ! -f $NGINX_STREAM_CONFIG ] && [ -f $NGINX_CONFIG ] ; then
ln -s /usr/share/jitsi-meet-turnserver/jitsi-meet.conf $NGINX_STREAM_CONFIG
sed -i "s/listen 443 ssl/listen 4444 ssl http2/g" $NGINX_CONFIG
sed -i "s/listen \[\:\:\]\:443 ssl/listen \[\:\:\]\:4444 ssl http2/g" $NGINX_CONFIG
invoke-rc.d nginx reload || true
fi
# Enable turn server in config.js
if [ -f $JITSI_MEET_CONFIG ] ; then
sed -i "s/\/\/ useStunTurn: true/useStunTurn: true/g" $JITSI_MEET_CONFIG
fi
# and we're done with debconf

63
debian/jitsi-meet-turnserver.postrm vendored Normal file
View File

@@ -0,0 +1,63 @@
#!/bin/sh
# postrm script for jitsi-meet-turnserver
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postrm> `remove'
# * <postrm> `purge'
# * <old-postrm> `upgrade' <new-version>
# * <new-postrm> `failed-upgrade' <old-version>
# * <new-postrm> `abort-install'
# * <new-postrm> `abort-install' <old-version>
# * <new-postrm> `abort-upgrade' <old-version>
# * <disappearer's-postrm> `disappear' <overwriter>
# <overwriter-version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
# Load debconf
. /usr/share/debconf/confmodule
case "$1" in
remove)
rm -rf /etc/nginx/modules-enabled/60-jitsi-meet.conf
if [ -x "/etc/init.d/nginx" ]; then
invoke-rc.d nginx reload || true
fi
if [ -x "/etc/init.d/apache2" ]; then
invoke-rc.d apache2 reload || true
fi
;;
purge)
rm -rf /etc/nginx/modules-enabled/60-jitsi-meet.conf
rm -rf /etc/turnserver.conf
if [ -x "/etc/init.d/nginx" ]; then
invoke-rc.d nginx reload || true
fi
if [ -x "/etc/init.d/apache2" ]; then
invoke-rc.d apache2 reload || true
fi
# Clear the debconf variable
db_purge
;;
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
;;
*)
echo "postrm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
db_stop
exit 0

View File

@@ -120,6 +120,8 @@ case "$1" in
echo ""
echo "You are using jetty to serve jitsi-meet, we are now upgrading you to use nginx!"
echo ""
echo "If you are using Lets Encrypt certificates please re-run the script."
echo ""
echo "------------------------------------------------"
echo ""

View File

@@ -11,4 +11,5 @@ fonts /usr/share/jitsi-meet/
images /usr/share/jitsi-meet/
lang /usr/share/jitsi-meet/
connection_optimization /usr/share/jitsi-meet/
resources/robots.txt /usr/share/jitsi-meet/
resources/*.sh /usr/share/jitsi-meet/scripts/

43
doc/README.md Normal file
View File

@@ -0,0 +1,43 @@
# Documentation
This document is the entrypoint to different guides, divided in three groups:
* User guide: these documents are designed to help users of the service, to better
understand all the available features and how to use them.
* Developer guide: these documents are designed to help developers who want to either
integrate the Jitsi Meet API / SDK in their products or want to improve Jitsi Meet
itself by developing new features or fixing bugs.
* DevOps guide: these documents are designed for DevOps folks, system administrators
or anyone who wishes to deploy and operate their own Jitsi Meet instance.
## User guide
Work in progress.
## Developer guide
### Web
* [iframe API](https://github.com/jitsi/jitsi-meet/blob/master/doc/api.md)
* [Jitsi Meet development](https://github.com/jitsi/jitsi-meet/blob/master/doc/development.md)
### Mobile
* [Building the mobile apps](https://github.com/jitsi/jitsi-meet/blob/master/doc/mobile.md)
* [SDK usage examples](https://github.com/jitsi/jitsi-meet-sdk-samples)
* [Enabling Dropbox support](https://github.com/jitsi/jitsi-meet/blob/master/doc/mobile-dropbox.md)
* [Enabling Google authentication](https://github.com/jitsi/jitsi-meet/blob/master/doc/mobile-google-auth.md)
## DevOps guide
* [Quick install](https://github.com/jitsi/jitsi-meet/blob/master/doc/quick-install.md)
* [Docker install](https://github.com/jitsi/docker-jitsi-meet/blob/master/README.md)
* [Google Calendar, MS Calendar, Dropbox integrations](https://github.com/jitsi/jitsi-meet/blob/master/doc/integrations.md)
* [Video tutorials on deployment and scalability](https://jitsi.org/tutorials/)
* [Configuring a video SIP gateway](https://github.com/jitsi/jitsi-meet/blob/master/doc/sipgw-config.md)
* [Enabling speaker stats](https://github.com/jitsi/jitsi-meet/blob/master/doc/speakerstats-prosody.md)
* [Enabling TURN](https://github.com/jitsi/jitsi-meet/blob/master/doc/turn.md)
* [Networking FAQ](https://github.com/jitsi/jitsi-meet/blob/master/doc/faq.md)
* [Cloud APIs](https://github.com/jitsi/jitsi-meet/blob/master/doc/cloud-api.md)

View File

@@ -50,6 +50,8 @@ Component "conference.jitmeet.example.com" "muc"
-- "token_verification";
}
admins = { "focusUser@auth.jitmeet.example.com" }
muc_room_locking = false
muc_room_default_public_jids = true
-- internal muc component
Component "internal.auth.jitmeet.example.com" "muc"

View File

@@ -11,3 +11,5 @@ no-tcp
listening-port=443
tls-listening-port=4445
external-ip=__external_ip_address__
syslog

View File

@@ -23,7 +23,7 @@ Finally on the same machine test that you can ping the FQDN with: `ping "$(hostn
### Add the Jitsi package repository
```sh
echo 'deb https://download.jitsi.org stable/' >> /etc/apt/sources.list.d/jitsi-stable.list
wget -qO - https://download.jitsi.org/jitsi-key.gpg.key | apt-key add -
wget -qO - https://download.jitsi.org/jitsi-key.gpg.key | sudo apt-key add -
```
### Install Jitsi Meet

View File

@@ -1,4 +1,4 @@
<html itemscope itemtype="http://schema.org/Product" prefix="og: http://ogp.me/ns#" xmlns="http://www.w3.org/1999/html">
<html itemscope itemtype="http://schema.org/Product" lang="en" prefix="og: http://ogp.me/ns#" xmlns="http://www.w3.org/1999/html">
<head>
<!--#include virtual="head.html" -->
<meta charset="utf-8">

View File

@@ -5,10 +5,10 @@ require_relative '../node_modules/@react-native-community/cli-platform-ios/nativ
target 'jitsi-meet' do
project 'app/app.xcodeproj'
pod 'Crashlytics', '~> 3.12.0'
pod 'Fabric', '~> 1.9.0'
pod 'Firebase/Core', '~> 5.18.0'
pod 'Firebase/DynamicLinks', '~> 5.18.0'
pod 'Crashlytics', '~> 3.14.0'
pod 'Fabric', '~> 1.10.2'
pod 'Firebase/Core', '~> 6.16.0'
pod 'Firebase/DynamicLinks', '~> 6.16.0'
end
target 'JitsiMeet' do

View File

@@ -11,10 +11,10 @@ PODS:
- CocoaLumberjack (3.5.3):
- CocoaLumberjack/Core (= 3.5.3)
- CocoaLumberjack/Core (3.5.3)
- Crashlytics (3.12.0):
- Fabric (~> 1.9.0)
- Crashlytics (3.14.0):
- Fabric (~> 1.10.2)
- DoubleConversion (1.1.6)
- Fabric (1.9.0)
- Fabric (1.10.2)
- FBLazyVector (0.61.5-jitsi.1)
- FBReactNativeSpec (0.61.5-jitsi.1):
- Folly (= 2018.10.22.00)
@@ -23,34 +23,48 @@ PODS:
- React-Core (= 0.61.5-jitsi.1)
- React-jsi (= 0.61.5-jitsi.1)
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.1)
- Firebase/Core (5.18.0):
- Firebase/Core (6.16.0):
- Firebase/CoreOnly
- FirebaseAnalytics (= 5.7.0)
- Firebase/CoreOnly (5.18.0):
- FirebaseCore (= 5.3.1)
- Firebase/DynamicLinks (5.18.0):
- FirebaseAnalytics (= 6.2.2)
- Firebase/CoreOnly (6.16.0):
- FirebaseCore (= 6.6.1)
- Firebase/DynamicLinks (6.16.0):
- Firebase/CoreOnly
- FirebaseDynamicLinks (= 3.4.1)
- FirebaseAnalytics (5.7.0):
- FirebaseCore (~> 5.3)
- FirebaseInstanceID (~> 3.6)
- GoogleAppMeasurement (= 5.7.0)
- GoogleUtilities/AppDelegateSwizzler (~> 5.2)
- GoogleUtilities/MethodSwizzler (~> 5.2)
- GoogleUtilities/Network (~> 5.2)
- "GoogleUtilities/NSData+zlib (~> 5.2)"
- nanopb (~> 0.3)
- FirebaseAnalyticsInterop (1.2.0)
- FirebaseCore (5.3.1):
- GoogleUtilities/Logger (~> 5.2)
- FirebaseDynamicLinks (3.4.1):
- FirebaseAnalytics (~> 5.1)
- FirebaseAnalyticsInterop (~> 1.0)
- FirebaseCore (~> 5.2)
- FirebaseInstanceID (3.7.0):
- FirebaseCore (~> 5.2)
- GoogleUtilities/Environment (~> 5.2)
- GoogleUtilities/UserDefaults (~> 5.2)
- FirebaseDynamicLinks (~> 4.0.6)
- FirebaseAnalytics (6.2.2):
- FirebaseCore (~> 6.6)
- FirebaseInstanceID (~> 4.3)
- GoogleAppMeasurement (= 6.2.2)
- GoogleUtilities/AppDelegateSwizzler (~> 6.0)
- GoogleUtilities/MethodSwizzler (~> 6.0)
- GoogleUtilities/Network (~> 6.0)
- "GoogleUtilities/NSData+zlib (~> 6.0)"
- nanopb (= 0.3.9011)
- FirebaseAnalyticsInterop (1.5.0)
- FirebaseCore (6.6.1):
- FirebaseCoreDiagnostics (~> 1.2)
- FirebaseCoreDiagnosticsInterop (~> 1.2)
- GoogleUtilities/Environment (~> 6.5)
- GoogleUtilities/Logger (~> 6.5)
- FirebaseCoreDiagnostics (1.2.2):
- FirebaseCoreDiagnosticsInterop (~> 1.2)
- GoogleDataTransportCCTSupport (~> 2.0)
- GoogleUtilities/Environment (~> 6.5)
- GoogleUtilities/Logger (~> 6.5)
- nanopb (~> 0.3.901)
- FirebaseCoreDiagnosticsInterop (1.2.0)
- FirebaseDynamicLinks (4.0.8):
- FirebaseAnalyticsInterop (~> 1.3)
- FirebaseCore (~> 6.2)
- FirebaseInstallations (1.1.1):
- FirebaseCore (~> 6.6)
- GoogleUtilities/UserDefaults (~> 6.5)
- PromisesObjC (~> 1.2)
- FirebaseInstanceID (4.3.2):
- FirebaseCore (~> 6.6)
- FirebaseInstallations (~> 1.0)
- GoogleUtilities/Environment (~> 6.5)
- GoogleUtilities/UserDefaults (~> 6.5)
- Folly (2018.10.22.00):
- boost-for-react-native
- DoubleConversion
@@ -61,33 +75,37 @@ PODS:
- DoubleConversion
- glog
- glog (0.3.5)
- GoogleAppMeasurement (5.7.0):
- GoogleUtilities/AppDelegateSwizzler (~> 5.2)
- GoogleUtilities/MethodSwizzler (~> 5.2)
- GoogleUtilities/Network (~> 5.2)
- "GoogleUtilities/NSData+zlib (~> 5.2)"
- nanopb (~> 0.3)
- GoogleAppMeasurement (6.2.2):
- GoogleUtilities/AppDelegateSwizzler (~> 6.0)
- GoogleUtilities/MethodSwizzler (~> 6.0)
- GoogleUtilities/Network (~> 6.0)
- "GoogleUtilities/NSData+zlib (~> 6.0)"
- nanopb (= 0.3.9011)
- GoogleDataTransport (5.1.0)
- GoogleDataTransportCCTSupport (2.0.1):
- GoogleDataTransport (~> 5.1)
- nanopb (~> 0.3.901)
- GoogleSignIn (5.0.1):
- AppAuth (~> 1.2)
- GTMAppAuth (~> 1.0)
- GTMSessionFetcher/Core (~> 1.1)
- GoogleUtilities/AppDelegateSwizzler (5.4.1):
- GoogleUtilities/AppDelegateSwizzler (6.5.2):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Environment (5.4.1)
- GoogleUtilities/Logger (5.4.1):
- GoogleUtilities/Environment (6.5.2)
- GoogleUtilities/Logger (6.5.2):
- GoogleUtilities/Environment
- GoogleUtilities/MethodSwizzler (5.4.1):
- GoogleUtilities/MethodSwizzler (6.5.2):
- GoogleUtilities/Logger
- GoogleUtilities/Network (5.4.1):
- GoogleUtilities/Network (6.5.2):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (5.4.1)"
- GoogleUtilities/Reachability (5.4.1):
- "GoogleUtilities/NSData+zlib (6.5.2)"
- GoogleUtilities/Reachability (6.5.2):
- GoogleUtilities/Logger
- GoogleUtilities/UserDefaults (5.4.1):
- GoogleUtilities/UserDefaults (6.5.2):
- GoogleUtilities/Logger
- GTMAppAuth (1.0.0):
- AppAuth/Core (~> 1.0)
@@ -97,12 +115,13 @@ PODS:
- GTMSessionFetcher/Core (1.2.2)
- GTMSessionFetcher/Full (1.2.2):
- GTMSessionFetcher/Core (= 1.2.2)
- nanopb (0.3.901):
- nanopb/decode (= 0.3.901)
- nanopb/encode (= 0.3.901)
- nanopb/decode (0.3.901)
- nanopb/encode (0.3.901)
- nanopb (0.3.9011):
- nanopb/decode (= 0.3.9011)
- nanopb/encode (= 0.3.9011)
- nanopb/decode (0.3.9011)
- nanopb/encode (0.3.9011)
- ObjectiveDropboxOfficial (3.9.4)
- PromisesObjC (1.2.8)
- RCTRequired (0.61.5-jitsi.1)
- RCTTypeSafety (0.61.5-jitsi.1):
- FBLazyVector (= 0.61.5-jitsi.1)
@@ -352,13 +371,13 @@ DEPENDENCIES:
- Amplitude-iOS (~> 4.0.4)
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
- CocoaLumberjack (~> 3.5.3)
- Crashlytics (~> 3.12.0)
- Crashlytics (~> 3.14.0)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- Fabric (~> 1.9.0)
- Fabric (~> 1.10.2)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector/`)
- FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec/`)
- Firebase/Core (~> 5.18.0)
- Firebase/DynamicLinks (~> 5.18.0)
- Firebase/Core (~> 6.16.0)
- Firebase/DynamicLinks (~> 6.16.0)
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- ObjectiveDropboxOfficial (~> 3.9.4)
@@ -401,23 +420,29 @@ SPEC REPOS:
- Amplitude-iOS
- boost-for-react-native
- CocoaLumberjack
- ObjectiveDropboxOfficial
trunk:
- AppAuth
- Crashlytics
- Fabric
- Firebase
- FirebaseAnalytics
- FirebaseAnalyticsInterop
- FirebaseCore
- FirebaseCoreDiagnostics
- FirebaseCoreDiagnosticsInterop
- FirebaseDynamicLinks
- FirebaseInstallations
- FirebaseInstanceID
- GoogleAppMeasurement
- GoogleUtilities
- nanopb
- ObjectiveDropboxOfficial
trunk:
- AppAuth
- GoogleDataTransport
- GoogleDataTransportCCTSupport
- GoogleSignIn
- GoogleUtilities
- GTMAppAuth
- GTMSessionFetcher
- nanopb
- PromisesObjC
EXTERNAL SOURCES:
BVLinearGradient:
@@ -501,26 +526,32 @@ SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872
CocoaLumberjack: 2f44e60eb91c176d471fdba43b9e3eae6a721947
Crashlytics: 07fb167b1694128c1c9a5a5cc319b0e9c3ca0933
Crashlytics: 540b7e5f5da5a042647227a5e3ac51d85eed06df
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
Fabric: f988e33c97f08930a413e08123064d2e5f68d655
Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74
FBLazyVector: 4a5251159a3ed05dc11cc8b74cf937869935814b
FBReactNativeSpec: 6fa602a20993212cc9877a81838578ffb0008bc9
Firebase: 02f3281965c075426141a0ce1277e9de6649cab9
FirebaseAnalytics: 23851fe602c872130a2c5c55040b302120346cc2
FirebaseAnalyticsInterop: efbe45c8385ec626e29f9525e5ebd38520dfb6c1
FirebaseCore: 52f851b30e11360f1e67cf04b1edfebf0a47a2d3
FirebaseDynamicLinks: f209c3caccd82102caa0e91d393e3ccc593501fd
FirebaseInstanceID: bd6fc5a258884e206fd5c474ebe4f5b00e21770e
Firebase: 497158b816d0a86fc31babbd05546fcd7e6083ff
FirebaseAnalytics: cf95d3aab897612783020fbd98401d5366f135ee
FirebaseAnalyticsInterop: 3f86269c38ae41f47afeb43ebf32a001f58fcdae
FirebaseCore: 85064903ed6c28e47fec9c7bd149d94ba1b6b6e7
FirebaseCoreDiagnostics: e9b4cd8ba60dee0f2d13347332e4b7898cca5b61
FirebaseCoreDiagnosticsInterop: 296e2c5f5314500a850ad0b83e9e7c10b011a850
FirebaseDynamicLinks: 417dc6dbb6013233c77558290d73296f429656a6
FirebaseInstallations: acb3216eb9784d3b1d2d2d635ff74fa892cc0c44
FirebaseInstanceID: 7ee0d6777013bb952f377b41965bf132b6a075be
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
GoogleAppMeasurement: 6cf307834da065863f9faf4c0de0a936d81dd832
GoogleAppMeasurement: d0560d915abf15e692e8538ba1d58442217b6aff
GoogleDataTransport: b29a21d813e906014ca16c00897827e40e4a24ab
GoogleDataTransportCCTSupport: 6f15a89b0ca35d6fa523e1f752ef818588885988
GoogleSignIn: 3a51b9bb8e48b635fd7f4272cee06ca260345b86
GoogleUtilities: 1e25823cbf46540b4284f6ef8e17b3a68ee12bbc
GoogleUtilities: ad0f3b691c67909d03a3327cc205222ab8f42e0e
GTMAppAuth: 4deac854479704f348309e7b66189e604cf5e01e
GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23
nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48
nanopb: 18003b5e52dab79db540fe93fe9579f399bd1ccd
ObjectiveDropboxOfficial: a5afefc83f6467c42c45f2253f583f2ad1ffc701
PromisesObjC: c119f3cd559f50b7ae681fa59dc1acd19173b7e6
RCTRequired: f63dd90a89a60602acdd44c42e5d2645ca60ab79
RCTTypeSafety: 24a3c6d55684046ed550b1d0ef083a9bf71c8bd4
React: 71c5a51135f291c3b32c0b558e167b858ae50e84
@@ -553,6 +584,6 @@ SPEC CHECKSUMS:
RNWatch: 09738b339eceb66e4d80a2371633ca5fb380fa42
Yoga: 7b4209fda2441f99d54dd6cf4c82b094409bb68f
PODFILE CHECKSUM: 0fdfa45ae809c9460c80be3e0d4bbb822fccc418
PODFILE CHECKSUM: f615794fb9184757b00cd16e534824ba6ee2fc98
COCOAPODS: 1.8.4

View File

@@ -19,7 +19,7 @@ There are 2 ways to integrate the SDK into your project:
Follow the instructions [here](https://github.com/jitsi/jitsi-meet-ios-sdk-releases/blob/master/README.md).
### Builduing it yourself
### Building it yourself
1. Install all required [dependencies](https://github.com/jitsi/jitsi-meet/blob/master/doc/mobile.md).

View File

@@ -293,7 +293,7 @@
B35383AD1DDA0083008F406A /* Adjust embedded framework architectures */,
0BB7DA181EC9E695007AAE98 /* Adjust ATS */,
DEF4813D224925A2002AD03A /* Copy Google Plist file */,
DEC2069321CBBD6900072F03 /* Setup Fabric */,
DEC2069321CBBD6900072F03 /* Setup Crashlytics */,
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */,
DE4F6D6E22005C0400DE699E /* Setup Dropbox */,
0BEA5C491F7B8F73000D0AB4 /* Embed Watch Content */,
@@ -492,7 +492,7 @@
shellPath = /bin/sh;
shellScript = "INFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\nDROPBOX_KEY_FILE=\"$PROJECT_DIR/dropbox.key\"\n\nif [[ -f $DROPBOX_KEY_FILE ]]; then\n /usr/libexec/PlistBuddy -c \"Delete :LSApplicationQueriesSchemes\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes array\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes:0 string 'dbapi-2'\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes:1 string 'dbapi-8-emm'\" $INFO_PLIST\n\n DROPBOX_KEY=$(head -n 1 $DROPBOX_KEY_FILE)\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLName string dropbox\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLSchemes array\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLSchemes:0 string $DROPBOX_KEY\" $INFO_PLIST\nfi\n";
};
DEC2069321CBBD6900072F03 /* Setup Fabric */ = {
DEC2069321CBBD6900072F03 /* Setup Crashlytics */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -501,14 +501,14 @@
);
inputPaths = (
);
name = "Setup Fabric";
name = "Setup Crashlytics";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "${PODS_ROOT}/Fabric/run\n";
shellScript = "GOOGLE_PLIST=\"$PROJECT_DIR/GoogleService-Info.plist\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n if [ \"${CONFIGURATION}\" != \"Debug\" ]; then\n find \"${DWARF_DSYM_FOLDER_PATH}\" -name \"*.dSYM\" | xargs -I \\{\\} ${PODS_ROOT}/Fabric/upload-symbols -gsp $GOOGLE_PLIST -p ios \\{\\}\n fi\nfi\n";
};
DEF4813D224925A2002AD03A /* Copy Google Plist file */ = {
isa = PBXShellScriptBuildPhase;

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>20.1.0</string>
<string>20.2.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>20.1.0</string>
<string>20.2.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UISupportedInterfaceOrientations</key>

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>20.1.0</string>
<string>20.2.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CLKComplicationPrincipalClass</key>

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.6.0</string>
<string>2.8.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>

View File

@@ -28,5 +28,5 @@
"tr": "Turco",
"vi": "Vietnamita",
"zhCN": "Chino (China)",
"zhTW": "Chino (Taiwan)\"msgstr \""
"zhTW": "Chino (Taiwan)"
}

View File

@@ -28,5 +28,5 @@
"tr": "Turco",
"vi": "Vietnamita",
"zhCN": "Chino (China)",
"zhTW": "Chino (Taiwan)\"msgstr \""
"zhTW": "Chino (Taiwan)"
}

View File

@@ -28,5 +28,5 @@
"tr": "Török",
"vi": "Vietnámi",
"zhCN": "Kínai (Kína)",
"zhTW": "Kínai (Tajvan)\"msgstr \""
"zhTW": "Kínai (Tajvan)"
}

View File

@@ -1,32 +1,32 @@
{
"en": "Angielski",
"af": "Afrykanerski",
"bg": "Bułgarski",
"ca": "Kataloński",
"cs": "Czeski",
"de": "Niemiecki",
"el": "Grecki",
"enGB": "Angielski (Zjednoczone Królestwo)",
"eo": "Esperanto",
"es": "Hiszpański",
"esUS": "Hiszpański (Ameryka Łacińska)",
"fi": "Fiński",
"fr": "Francuski",
"frCA": "Francuski (kanadyjski)",
"hr": "Chorwacki",
"hu": "Węgierski",
"hy": "Ormiański",
"it": "Włoski",
"ja": "Japoński",
"ko": "Koreański",
"nl": "Holenderski",
"oc": "Oksytański",
"pl": "Polski",
"ptBR": "Portugalski (brazylijski)",
"ru": "Rosyjski",
"sv": "Szwedzki",
"tr": "Turecki",
"vi": "Wietnamski",
"zhCN": "Chiński (Chiny)",
"zhTW": "Chiński (Tajwan)\"msgstr \""
"en": "angielski",
"af": "afrykanerski",
"bg": "bułgarski",
"ca": "kataloński",
"cs": "czeski",
"de": "niemiecki",
"el": "grecki",
"enGB": "angielski (Zjednoczone Królestwo)",
"eo": "esperanto",
"es": "hiszpański",
"esUS": "hiszpański (Ameryka Łacińska)",
"fi": "fiński",
"fr": "francuski",
"frCA": "francuski (kanadyjski)",
"hr": "chorwacki",
"hu": "węgierski",
"hy": "ormiański",
"it": "włoski",
"ja": "japoński",
"ko": "koreański",
"nl": "holenderski",
"oc": "oksytański",
"pl": "polski",
"ptBR": "portugalski (brazylijski)",
"ru": "rosyjski",
"sv": "szwedzki",
"tr": "turecki",
"vi": "wietnamski",
"zhCN": "chiński (Chiny)",
"zhTW": "chiński (Tajwan)"
}

View File

@@ -28,5 +28,5 @@
"tr": "Turco",
"vi": "Vietnamita",
"zhCN": "Chinês (China)",
"zhTW": "Chinês (Taiwan)\"msgstr \""
"zhTW": "Chinês (Taiwan)"
}

View File

@@ -28,5 +28,5 @@
"tr": "Turkish",
"vi": "Vietnamese",
"zhCN": "中文 简体 (中国)",
"zhTW": "中文 正體 (台灣)\"msgstr \""
"zhTW": "中文 正體 (台灣)"
}

View File

@@ -264,13 +264,13 @@
"startLiveStreaming": "Inicia la transmissió en directe",
"startRecording": "Inicia l'enregistrament",
"startRemoteControlErrorMessage": "S'ha produït un error en intentar iniciar la sessió de control remot!",
"stopLiveStreaming": "Inicia la transmissió en directe",
"stopRecording": "Inicia l'enregistrament",
"stopRecordingWarning": "Esteu segur de voler iniciar l'enregistrament?",
"stopLiveStreaming": "Atura la transmissió en directe",
"stopRecording": "Atura l'enregistrament",
"stopRecordingWarning": "Esteu segur de voler aturar l'enregistrament?",
"stopStreamingWarning": "Esteu segur de voler aturar la transmissió en directe?",
"streamKey": "Clau de transmissió en directe",
"Submit": "Tramet",
"thankYou": "Gràcies per usar {{appName}}!",
"thankYou": "Gràcies per emprar {{appName}}!",
"token": "identificador",
"tokenAuthFailed": "No esteu autoritzat a unir-vos a aquesta trucada.",
"tokenAuthFailedTitle": "L'autenticació ha fallat",

View File

@@ -33,7 +33,7 @@
"error": {
"appConfiguration": "Kalenderintegration ist nicht richtig konfiguriert.",
"generic": "Ein Fehler ist aufgetreten. Prüfen Sie Ihre Kalendereinstellungen oder versuchen Sie, den Kalender zu aktualisieren.",
"notSignedIn": "Ein Fehler ist während der Authentifizierung zur Anzeige von Kalenderertermine aufgetreten. Prüfen Sie Ihre Kalendereinstellungen oder versuchen Sie, sich erneut anzumelden."
"notSignedIn": "Ein Fehler ist während der Authentifizierung zur Anzeige von Kalenderterminen aufgetreten. Prüfen Sie Ihre Kalendereinstellungen oder versuchen Sie, sich erneut anzumelden."
},
"join": "Teilnehmen",
"joinTooltip": "Am Meeting teilnehmen",
@@ -191,7 +191,7 @@
"kickMessage": "Sie können sich für weitere Einzelheiten an {{participantDisplayName}}} wenden.",
"kickParticipantButton": "Entfernen",
"kickParticipantDialog": "Wollen Sie diesen Teilnehmer wirklich entfernen?",
"kickParticipantTitle": "Teilnehmer stummschalten?",
"kickParticipantTitle": "Teilnehmer entfernen?",
"kickTitle": "Autsch! {{participantDisplayName}} hat Sie aus dem Meeting geworfen.",
"liveStreaming": "Livestreaming",
"liveStreamingDisabledForGuestTooltip": "Gäste können kein Livestreaming starten.",
@@ -241,7 +241,7 @@
"removePassword": "$t(lockRoomPassword) entfernen",
"removeSharedVideoMsg": "Sind Sie sicher, dass Sie das geteilte Video entfernen möchten?",
"removeSharedVideoTitle": "Freigegebenes Video entfernen",
"reservationError": "Fehler im Reservationssystem",
"reservationError": "Fehler im Reservierungssystem",
"reservationErrorMsg": "Fehler, Nummer: {{code}}, Nachricht: {{msg}}",
"retry": "Wiederholen",
"screenSharingFailedToInstall": "Oh! Die Erweiterung für die Bildschirmfreigabe konnte nicht installiert werden.",
@@ -315,7 +315,7 @@
"dialInConferenceID": "PIN:",
"dialInNotSupported": "Entschuldigung, leider wird das Einwählen derzeit nicht unterstützt.",
"dialInNumber": "Einwählen:",
"dialInSummaryError": "Fehler beim Abrufen der Einwählinformationen. Versuchen Sie es später erneut.",
"dialInSummaryError": "Fehler beim Abrufen der Einwahlinformationen. Versuchen Sie es später erneut.",
"dialInTollFree": "Gebührenfrei",
"genericError": "Es ist leider etwas schiefgegangen.",
"inviteLiveStream": "Klicken Sie auf {{url}} um den Livestream dieser Konferenz zu öffnen",
@@ -328,11 +328,11 @@
"moreNumbers": "Weitere Telefonnummern",
"noNumbers": "Keine Telefonnummern verfügbar.",
"noPassword": "Kein",
"noRoom": "Keine Konferenz für die Einwähl-Informationen angegeben.",
"noRoom": "Keine Konferenz für die Einwahlinformationen angegeben.",
"numbers": "Einwählnummern",
"password": "$t(lockRoomPasswordUppercase):",
"title": "Teilen",
"tooltip": "Freigabe-Link und Einwählinformationen für dieses Meeting",
"tooltip": "Freigabe-Link und Einwahlinformationen für dieses Meeting",
"label": "Meeting-Informationen"
},
"inviteDialog": {
@@ -367,7 +367,7 @@
"videoQuality": "Anrufqualität verwalten"
},
"liveStreaming": {
"busy": "Es werden Resourcen zum Streamen bereitgestellt. Bitte in ein paar Minuten erneut versuchen.",
"busy": "Es werden Ressourcen zum Streamen bereitgestellt. Bitte in ein paar Minuten erneut versuchen.",
"busyTitle": "Alle Streaming-Instanzen sind in Gebrauch",
"changeSignIn": "Konten wechseln.",
"choose": "Livestream auswählen",
@@ -405,7 +405,7 @@
"dialogTitle": "Lokale Aufzeichnungssteuerelemente",
"duration": "Dauer",
"durationNA": "N. v.",
"encoding": "Codierung",
"encoding": "Kodierung",
"label": "LOR",
"labelToolTip": "Lokale Aufzeichnung ist aktiviert",
"localRecording": "Lokale Aufzeichnung",
@@ -769,5 +769,9 @@
"sendFeedback": "Feedback senden",
"terms": "AGB",
"title": "Sichere, mit umfassenden Funktionen ausgestattete und vollkommen kostenlose Videokonferenzen"
},
"lonelyMeetingExperience": {
"button": "Weitere einladen",
"youAreAlone": "Sie sind der einzige in diesem Meeting"
}
}

View File

@@ -628,6 +628,7 @@
"logout": "Cerrar sesión",
"lowerYourHand": "Bajar la mano",
"moreActions": "Más acciones",
"moreOptions": "Más opciones",
"mute": "Activar / Desactivar Silencio",
"noAudioSignalTitle": "¡No hay entrada proveniente de su micrófono!",
"noAudioSignalDesc": "Si no lo silenció a propósito desde la configuración del sistema o el dispositivo, considere cambiar el dispositivo.",
@@ -752,5 +753,9 @@
"sendFeedback": "Enviar comentarios",
"terms": "Términos",
"title": "Seguro, lleno de funcionalidades y videoconferencias completamente gratuitas"
},
"lonelyMeetingExperience": {
"button": "Invitar a otros",
"youAreAlone": "Eres el único en la reunión"
}
}

View File

@@ -21,7 +21,8 @@
"bluetooth": "Bluetooth",
"headphones": "Kõrvaklapid",
"phone": "Telefon",
"speaker": "Kõlar"
"speaker": "Kõlar",
"none": "Heli vahendid pole kättesaadavad"
},
"audioOnly": {
"audioOnly": "Ainult heli"
@@ -46,12 +47,22 @@
},
"chat": {
"error": "Viga: sõnumi \"{{originalText}}\" saatmine ebaõnnestus. Põhjus: {{error}}",
"fieldPlaceHolder": "Sisesta oma sõnum siia",
"messagebox": "Sisesta sõnum",
"messageTo": "Privaatsõnum kasutajale {{recipient}}",
"noMessagesMessage": "Kirjavahetust pole veel alustatud. Alusta kirjavahetust siin!",
"nickname": {
"popover": "Sisesta nimi",
"title": "Sisesta nimi, et kõnega alustada"
},
"title": "Kõne"
"privateNotice": "Privaatsõnum kasutajale {{recipient}}",
"title": "Kõne",
"you": "you"
},
"chromeExtensionBanner": {
"installExtensionText": "Paigalda Google Kalendri laiendus ja Office 365 integratsioon",
"buttonText": "Paigalda Chrome'i laiendus",
"dontShowAgain": "Ära rohkem näita"
},
"connectingOverlay": {
"joiningRoom": "Kõnega ühendamine..."
@@ -66,7 +77,10 @@
"DISCONNECTED": "Ühendus katkestatud",
"DISCONNECTING": "Ühenduse katkestamine...",
"ERROR": "Viga",
"RECONNECTING": "Võrguühenduse viga. Ühenduse taastamine..."
"FETCH_SESSION_ID": "Sessiooni-ID püüdmine...",
"GET_SESSION_ID_ERROR": "Sessiooni-ID püüdmisel tekkis viga: {{code}}",
"GOT_SESSION_ID": "Sessiooni-ID püüdmine... Tehtud",
"LOW_BANDWIDTH": "Kasutaja {{displayName}} video on ülekandekiiruse parandamiseks välja lülitatud"
},
"connectionindicator": {
"address": "Aadress:",
@@ -74,6 +88,7 @@
"bitrate": "Andmeedastuskiirus:",
"bridgeCount": "Serverite arv: ",
"connectedTo": "Ühendatud:",
"e2e_rtt": "E2E RTT:",
"framerate": "Ekraani eraldusvõime:",
"less": "Näita vähem",
"localaddress": "Lokaalne aadress:",
@@ -96,8 +111,7 @@
"resolution": "Resolutsioon:",
"status": "Ühendus:",
"transport": "Transport:",
"transport_plural": "Transpordid:",
"turn": " (turn)"
"transport_plural": "Transpordid:"
},
"dateUtils": {
"earlier": "Varem",
@@ -107,7 +121,7 @@
"deepLinking": {
"appNotInstalled": "Kõnega liitumiseks läbi mobiiltelefoni on vaja paigaldada {{app}} rakendus.",
"description": "Midagi ei juhtunud? Proovisime ühendust luua töölaua rakendusega. Proovi uuesti või käivita {{app}} rakendus.",
"descriptionWithoutWeb": "",
"descriptionWithoutWeb": "Midagi ei juhtunud? Televastuvõttu prooviti avada töölaua rakendusest {{app}}.",
"downloadApp": "Laadi rakendus alla",
"launchWebButton": "Käivita veebis",
"openApp": "Jätka töölaua rakendusega",
@@ -115,6 +129,7 @@
"tryAgainButton": "Proovi töölaua rakendusega uuesti "
},
"defaultLink": "nt {{url}}",
"defaultNickname": "nt. Mari Maasikas",
"deviceError": {
"cameraError": "Ei saanud kaameraga ühendust",
"cameraPermission": "Puudub õigus kasutada kaamerat",
@@ -167,7 +182,7 @@
"goToStore": "Mine veebipoodi",
"gracefulShutdown": "Rakendus on hoolduseks ajutiselt maas. Proovi hiljem uuesti!",
"IamHost": "Mina olen võõrustaja",
"incorrectRoomLockPassword": "",
"incorrectRoomLockPassword": "Vale parool",
"incorrectPassword": "Vale kasutajanimi või parool",
"inlineInstallationMsg": "Töölauale tuleb paigaldada jagamise laiendus.",
"inlineInstallExtension": "Paiglada kohe",
@@ -194,6 +209,12 @@
"micNotSendingDataTitle": "Mikrofon pole kättesaadav.",
"micPermissionDeniedError": "Mikrofoni kasutamine on keelatud. Kõnega on võimalik ühineda, aga teised Sind ei kuule. Mikrofoni kasutamiseks vajuta aadressiribal mikrofoni nupule.",
"micUnknownError": "Mikrofoni ei saa kasutada. Põhjus teadmata.",
"muteEveryoneElseDialog": "Peale mikrofoni vaigistamist saab ainult kasutaja ise oma mikrofoni tagasi sisse lülitada.",
"muteEveryoneElseTitle": "Vaigista kõikide teiste mikrofonid, välja arvatud {{whom}}?",
"muteEveryoneDialog": "Oled kindel, et soovid kõikide teiste mikrofonid vaigistada? Neid saab tagasi sisse lülitada ainult kasutaja ise.",
"muteEveryoneTitle": "Vaigista kõik?",
"muteEveryoneSelf": "Sina ise",
"muteEveryoneStartMuted": "Edaspidi alustavad kõik välja lülitatud mikrofonidega",
"muteParticipantBody": "Teiste kasutajate mikrofoni ei saa sisse lülitada. Kasutaja saab ise otsutada, kas mikrofon on sees või väljas.",
"muteParticipantButton": "Lülita mikrofon välja",
"muteParticipantDialog": "Oled kindel, et soovid kasutaja mikrofoni välja lülitada? Seda saab ainult kasutaja ise sisse tagasi lülitada.",
@@ -228,6 +249,10 @@
"screenSharingFirefoxPermissionDeniedError": "Ekraani jagamisega läks midagi valesti! Veendu, et oled ekraani jagamiseks loa andnud.",
"screenSharingFirefoxPermissionDeniedTitle": "Oih, ekraani jagamist ei saanud alustada!",
"screenSharingPermissionDeniedError": "Oih, midagi läks valesti ekraanijagamise laienduse õigustega! Värskenda ja proovi uuesti.",
"sendPrivateMessage": "Sulle saabus privaatsõnum. Kas soovid vastata privaatselt või avalikult?",
"sendPrivateMessageCancel": "Saada sõnum avalikult",
"sendPrivateMessageOk": "Saada sõnum privaatselt",
"sendPrivateMessageTitle": "Saada privaatselt?",
"serviceUnavailable": "Teenus pole kättesaadav",
"sessTerminated": "Kõne lõpetatud",
"Share": "Jaga",
@@ -261,6 +286,9 @@
"dialOut": {
"statusMessage": "on staatusega {{status}}"
},
"documentSharing": {
"title": "Jagatud dokument"
},
"feedback": {
"average": "Keskmine",
"bad": "Halb",
@@ -274,7 +302,7 @@
"answer": "Vasta",
"audioCallTitle": "Sissetulev kõne",
"decline": "Lõpeta",
"productLabel": "Jitsi Meet televastuvõtt",
"productLabel": "Jitsi",
"videoCallTitle": "Sissetulev videokõne"
},
"info": {
@@ -292,7 +320,7 @@
"genericError": "Oih, midagi läks valesti!",
"inviteLiveStream": "Otseülekande nägemiseks vajuta lingile: {{url}}",
"invitePhone": "Üks klikk heliga sissehelistamiseks: {{number}},,{{conferenceID}}#",
"invitePhoneAlternatives": "",
"invitePhoneAlternatives": "Otsid teist sissehelistamise numbrit?\nVaata sissehelistamise numbreid: {{url}}\n\n\nKui helistad läbi ruumi, ühine kõnega väljalülitatud mikrofoni režiimis: {{silentUrl}}",
"inviteURLFirstPartGeneral": "Oled kõnesse kutsutud.",
"inviteURLFirstPartPersonal": "{{name}} kutsub Sind kõnesse.\n",
"inviteURLSecondPart": "\nÜhine kõnega:\n{{url}}\n",
@@ -335,7 +363,8 @@
"toggleFilmstrip": "Näita/peida video eelvaade",
"toggleScreensharing": "Vaheta kaamera ja ekraanijagamise vahel",
"toggleShortcuts": "Näita/peida klaviatuuri kiirvalikud",
"videoMute": "Lülita kaamera sisse/välja"
"videoMute": "Lülita kaamera sisse/välja",
"videoQuality": "Halda kõne kvaliteeti"
},
"liveStreaming": {
"busy": "Toimub ülekande ressursi vabastamine. Proovi mõne minuti pärast uuesti.",
@@ -354,7 +383,9 @@
"getStreamKeyManually": "Ülekandjaid ei leitud. Leia YouTubest otseülekande võti.",
"invalidStreamKey": "Otseülekande võti võib olla vale.",
"off": "Otseülekanne on peatatud",
"offBy": "{{name}} lõpetas otseülekande",
"on": "Otseülekanne",
"onBy": "{{name}} alustas otseülekandega",
"pending": "Otseülekande alustamine...",
"serviceName": "Otseülekande teenus",
"signedInAs": "Oled sisse logitud kasutajana:",
@@ -414,16 +445,16 @@
"muted": "Alustasid kõnet väljalülitatud mikrofoniga.",
"mutedTitle": "Mikrofon on välja lülitatud!",
"mutedRemotelyTitle": "Sinu mikrofon lülitati välja kasutaja {{participantDisplayName}} poolt!",
"mutedRemotelyDescription": "",
"passwordRemovedRemotely": "",
"passwordSetRemotely": "",
"mutedRemotelyDescription": "Saad alati oma mikrofoni sisse lülitada, kui soovid rääkida. Lülita mikrofon peale rääkimist uuesti välja liigse müra ja kaja vältimiseks.",
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) eemaldatud teise kasutaja poolt",
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) lisatud teise kasutaja poolt",
"raisedHand": "{{name}} soovib rääkida.",
"somebody": "Keegi",
"startSilentTitle": "",
"startSilentDescription": "",
"startSilentTitle": "Ühinesid ilma mikrofonita!",
"startSilentDescription": "Mikrofoni kasutamiseks ühine kõnega uuesti",
"suboptimalExperienceDescription": "Rakenduse {{appName}} parima kvaliteedi tagamiseks palun kasuta <a href='static/recommendedBrowsers.html' target='_blank'>ühte nendest veebilehitsejatest</a>.",
"suboptimalExperienceTitle": "Veebilehitseja hoiatus",
"unmute": "",
"unmute": "Lülita mikrofon sisse",
"newDeviceCameraTitle": "Leitud uus kaamera",
"newDeviceAudioTitle": "Leitud uus heliseadeldis",
"newDeviceAction": "Kasuta"
@@ -451,6 +482,7 @@
"setEmailLabel": "Sisesta gravatar email",
"title": "Profiil"
},
"raisedHand": "Soovin rääkida",
"recording": {
"authDropboxText": "Lisa Dropboxi",
"availableSpace": "Vaba ruum: {{spaceLeft}} MB (ca {{duration}} minutit salvestamist)",
@@ -466,7 +498,9 @@
"live": "Otse",
"loggedIn": "Sisseloginud kasutajana {{userName}}",
"off": "Salvestamine on lõpetatud",
"offBy": "{{name}} lõpetas salvestamise",
"on": "Salvestatakse",
"onBy": "{{name}} alustas salvestamist",
"pending": "Kõne salvestamise ettevalmistus...",
"rec": "REC",
"serviceDescription": "Salvestus toimub teenuse kaudu",
@@ -503,16 +537,20 @@
"title": "Seaded"
},
"settingsView": {
"advanced": "Täpsem",
"alertOk": "OK",
"alertTitle": "Hoiatus",
"alertURLText": "Sisestatud link ei ole õige",
"buildInfoSection": "Versioon",
"conferenceSection": "Kõne",
"disableCallIntegration": "Lülita kohaliku kõne integratsioon välja",
"disableP2P": "Lülita otseühendus välja",
"displayName": "Kasutatav nimi",
"email": "E-mail",
"header": "Seaded",
"profileSection": "Profiil",
"serverURL": "Serveri link",
"showAdvanced": "Näita täpsemaid seadistusi",
"startWithAudioMuted": "Alusta väljalülitatud heliga",
"startWithVideoMuted": "Alusta väljalülitatud videoga",
"version": "Versioon"
@@ -547,17 +585,22 @@
"cc": "Kasuta subtiitreid",
"chat": "Kasuta vestluse akent",
"document": "Kasuta jagatud dokumente",
"download": "Laadi rakendus alla",
"feedback": "Jäta tagasiside",
"fullScreen": "Kasuta täisekraani",
"hangup": "Lahku kõnest",
"help": "Abi",
"invite": "Kutsu inimesi",
"kick": "Eemalda kõneleja",
"localRecording": "Näita salvestamise paneeli",
"lockRoom": "Kasuta kõne parooli",
"moreActions": "Kasuta toimingute menüüd",
"moreActionsMenu": "Toimingute menüü",
"moreOptions": "Näita rohkem valikuid",
"mute": "Lülita heli välja",
"muteEveryone": "Vaigista kõikide mikrofonid",
"pip": "Kasuta pilt-pildis vaadet",
"privateMessage": "Saada privaatsõnum",
"profile": "Muuda profiili",
"raiseHand": "Käe tõstmine",
"recording": "Salvestamine",
@@ -567,12 +610,12 @@
"shareRoom": "Kutsu",
"shareYourScreen": "Jaga ekraani",
"shortcuts": "Kasuta kiirvalikuid",
"show": "",
"show": "Näita laval",
"speakerStats": "Kõnelejate statistika",
"tileView": "Paneelvaade",
"toggleCamera": "Kasuta kaamerat",
"videomute": "Video väljalülitamine",
"videoblur": ""
"videoblur": "Video hägustamine"
},
"addPeople": "Lisa kõnesse inimesi",
"audioOnlyOff": "Lülita “ainult heli” valik välja",
@@ -584,20 +627,32 @@
"closeChat": "Sulge vestlus",
"documentClose": "Sulge jagatud dokument",
"documentOpen": "Ava jagatud dokument",
"download": "Laadi rakendus alla",
"enterFullScreen": "Vaata täisekraanil",
"enterTileView": "Vaata paneelvaates",
"exitFullScreen": "Välju täisekraani vaatest",
"exitTileView": "Välju paneelvaatest",
"feedback": "Jäta tagasiside",
"hangup": "Lahku",
"help": "Abi",
"invite": "Kutsu",
"login": "Logi sisse",
"logout": "Logi välja",
"lowerYourHand": "Langeta kätt",
"moreActions": "Rohkem tegevusi",
"moreOptions": "Rohkem valikuid",
"mute": "Mikrofon sisse/välja",
"muteEveryone": "Vaigista kõikide mikrofonid",
"noAudioSignalTitle": "Mikrofon ei püüa sisendit kinni!",
"noAudioSignalDesc": "Kui Sa ei lülitanud mikrofoni seadistustest tahtlikult välja, kaalu seadme vahetamist.",
"noAudioSignalDescSuggestion": "Kui Sa ei lülitanud mikrofoni seadistustest tahtlikult välja, kaalu seadme vahetamist.",
"noAudioSignalDialInDesc": "Võid sisse helistada valides:",
"noAudioSignalDialInLinkDesc" : "Sissehelistamise numbrid",
"noisyAudioInputTitle": "Mikrofonis on müra! Tundub, et läbi mikrofoni kostub palju helisid. Kaalu mikrofoni välja lülitamist või seadme vahetamist.",
"noisyAudioInputDesc": "Tundub, et läbi mikrofoni kostub palju helisid. Kaalu mikrofoni välja lülitamist või seadme vahetamist.",
"openChat": "Ava vestlus",
"pip": "Ava pilt-pildis vaade",
"privateMessage": "Saada privaatsõnum",
"profile": "Muuda profiili",
"raiseHand": "Tõsta/langeta kätt",
"raiseYourHand": "Tõsta kätt",
@@ -615,8 +670,8 @@
"tileViewToggle": "Näita paneelvaadet",
"toggleCamera": "Kasuta kaamerat",
"videomute": "Kaamera sisse/välja",
"startvideoblur": "",
"stopvideoblur": ""
"startvideoblur": "Tausta hägustamine",
"stopvideoblur": "Lülita tausta hägustamine välja"
},
"transcribing": {
"ccButtonTooltip": "Subtiitrid sisse/välja",
@@ -657,28 +712,32 @@
"audioOnlyExpanded": "Kasutad kõnes ainult heli. See hõivab ülekandekiirust vähem, aga video jagamine on välja lülitatud.",
"callQuality": "Kõne kvaliteet",
"hd": "HD",
"hdTooltip": "Video vaatamine kõrgkvaliteediga",
"highDefinition": "Kõrgresolutsioon",
"labelTooiltipNoVideo": "Video puudub",
"labelTooltipAudioOnly": "Valitud on “ainult heli” seadistus",
"ld": "LD",
"lowDefinition": "Madal resolutsioon",
"ldTooltip": "Video vaatamine madala kvaliteediga",
"lowDefinition": "Madal",
"onlyAudioAvailable": "Saab kasutada ainult heli",
"onlyAudioSupported": "Selles veebilehitsejas on toetatud ainult heli.",
"p2pEnabled": "Otseühendus on sisse lülitatud",
"p2pVideoQualityDescription": "Otseühenduse režiimis saab vastuvõetava kõne heli olla “ainult heli“, või kõrge. Teisi seadistusi ei saa valida.",
"recHighDefinitionOnly": "Eelistan kõrgresolutsiooni.",
"sd": "SD",
"standardDefinition": "Vaikimisi kasutatav resolutsioon"
"sdTooltip": "Video vaatamine vaikekvaliteediga",
"standardDefinition": "Vaike resolutsioon"
},
"videothumbnail": {
"domute": "Lülita mikrofon välja",
"domuteOthers": "Vaigista teiste mikrofonid",
"flip": "Pööra",
"kick": "Eemalda kõnest",
"moderator": "Moderaator",
"mute": "Kasutaja mikrofon välja lülitatud",
"muted": "Mikrofon välja lülitatud",
"remoteControl": "Kaugjuhtimine",
"show": "",
"show": "Näita laval",
"videomute": "Kasutaja on kaamera peatanud"
},
"welcomepage": {
@@ -693,20 +752,26 @@
},
"calendar": "Kalender",
"connectCalendarButton": "Ühenda kalender",
"connectCalendarText": "",
"connectCalendarText": "Ühenda oma kalender, et kõiki kohtumisi näha rakenduses {{app}}. Lisa {{provider}} kohtumised kalendrisse ja alusta kõnesid ühe klikiga.",
"enterRoomTitle": "Alusta uut kõnet",
"roomNameAllowedChars": "Televastuvõtu nimi ei tohi sisaldada sümboleid: ?, &, :, ', \", %, #.",
"go": "Mine",
"goSmall": "Mine",
"join": "Ühine",
"info": "Info",
"privacy": "Privaatsus",
"recentList": "Hiljutised",
"recentListDelete": "Kustuta",
"recentListEmpty": "Hiljutiste valikute nimekiri on tühi. Vestle kasutajatega ja leia oma hiljutised kõned siit.",
"reducedUIText": "",
"reducedUIText": "Tere tulemast rakendusse {{app}}!",
"roomname": "Sisesta ruumi nimi",
"roomnameHint": "Sisesta ruumi nimi või link, millega soovid ühinega. Võid nime välja mõelda, aga anna osalejatele sellest teada, et nad saaksid ruumiga ühineda.",
"sendFeedback": "Saada tagasiside",
"terms": "Tingimused",
"title": "Turvaline, võimalusi täis ja tasuta keskkond videokõnede jaoks."
},
"lonelyMeetingExperience": {
"button": "Kutsu teisi",
"youAreAlone": "Asud hetkel ruumis üksi"
}
}

View File

@@ -628,6 +628,7 @@
"logout": "Déconnexion",
"lowerYourHand": "Baisser la main",
"moreActions": "Plus d'actions",
"moreOptions": "Plus d'options",
"mute": "Muet / Actif",
"noAudioSignalTitle": "Il n'y a pas d'entrée provenant de votre micro !",
"noAudioSignalDesc": "Si vous n'avez pas délibérément coupé le son des paramètres du système ou du matériel, envisagez de changer le périphérique.",
@@ -752,5 +753,12 @@
"sendFeedback": "Envoyer votre avis",
"terms": "Termes",
"title": "Vidéoconférence Sécurisée, entièrement en vedette et gratuite"
},
"lonelyMeetingExperience": {
"button": "Inviter d'autres personnes",
"youAreAlone": "Vous êtes le seul participant de la réunion"
},
"helpView": {
"header": "Centre d'aide"
}
}

View File

@@ -594,6 +594,7 @@
"logout": "Logout",
"lowerYourHand": "Abbassa la mano",
"moreActions": "Più azioni",
"moreOptions": "Più opzioni",
"mute": "Microfono Attiva / Disattiva",
"openChat": "Apri una chat",
"pip": "Abilita visualizzazione immagine nellimmagine",
@@ -707,5 +708,9 @@
"sendFeedback": "Invia feedback",
"terms": "Termini di utilizzo",
"title": "Il sistema di conferenza sicuro, funzionale e completamente gratuito."
},
"lonelyMeetingExperience": {
"button": "Invita gli altri",
"youAreAlone": "Sei l'unico in riunione"
}
}

View File

@@ -53,7 +53,7 @@
"noMessagesMessage": "",
"nickname": {
"popover": "Wybierz swój nick",
"title": "Wpisz swoje imię i nazwisko, aby użyć rozmowy"
"title": "Wpisz swoją nazwę, aby użyć rozmowy"
},
"privateNotice": "Prywatna wiadomość do {{recipient}}",
"title": "Rozmowa",
@@ -76,38 +76,38 @@
},
"connectionindicator": {
"address": "Adres:",
"bandwidth": "Zakładana przepustowość:",
"bandwidth": "Szacowana przepustowość:",
"bitrate": "Szybkość transmisji:",
"bridgeCount": "Liczba serwerów: ",
"connectedTo": "Podłączone do:",
"framerate": "Częstotliwość wyświetlania klatek:",
"framerate": "Klatek na sekundę:",
"less": "Pokaż mniej",
"localaddress_0": "Adres lokalny:",
"localaddress_1": "Adresy lokalne:",
"localaddress_2": "Adresów lokalnych:",
"localaddress_2": "Adresy lokalne:",
"localport_0": "Port lokalny:",
"localport_1": "Porty lokalne:",
"localport_2": "Portów lokalnych:",
"localport_2": "Porty lokalne:",
"more": "Pokaż więcej",
"packetloss": "Strata pakietów:",
"packetloss": "Utrata pakietów:",
"quality": {
"good": "Prawdziwy",
"inactive": "Nieaktywny",
"lost": "Zaginiony",
"good": "Dobre",
"inactive": "Nieaktywne",
"lost": "Utracone",
"nonoptimal": "Nieoptymalne",
"poor": "Słaby"
"poor": "Słabe"
},
"remoteaddress_0": "Adres zdalny:",
"remoteaddress_1": "Adresy zdalne:",
"remoteaddress_2": "Adresów zdalnych:",
"remoteaddress_2": "Adresy zdalne:",
"remoteport_0": "Port zdalny:",
"remoteport_1": "Porty zdalne:",
"remoteport_2": "Portów zdalnych:",
"remoteport_2": "Porty zdalne:",
"resolution": "Rozdzielczość:",
"status": "Połączenie:",
"transport_0": "Przekazywanie:",
"transport_1": "Przekazywania:",
"transport_2": "Przekazywań:"
"transport_0": "Transport:",
"transport_1": "Transporty:",
"transport_2": "Transporty:"
},
"dateUtils": {
"earlier": "Wcześniej",
@@ -128,7 +128,7 @@
"defaultNickname": "np. Ziutek Kowalski",
"deviceError": {
"cameraError": "Błąd dostępu do Twojej kamery",
"cameraPermission": "Błąd podczas przetwarzania uprawnień do kamery",
"cameraPermission": "Błąd podczas otrzymywania uprawnień do kamery",
"microphoneError": "Błąd dostępu do Twojego mikrofonu",
"microphonePermission": "Błąd podczas otrzymywania uprawnień do mikrofonu"
},
@@ -209,7 +209,7 @@
"muteParticipantButton": "Wyciszenie",
"muteParticipantDialog": "Czy na pewno wyciszyć tego uczestnika? Nie będziesz mógł wyłączyć wyciszenia uczestników, ale oni mogą samodzielnie wyłączyć wyciszenie w dowolnym momencie.",
"muteParticipantTitle": "Wyciszyć tego uczestnika?",
"Ok": "Ok",
"Ok": "OK",
"passwordLabel": "$t(lockRoomPasswordUppercase)",
"passwordNotSupported": "Ustanowienie spotkania $t(lockRoomPassword) nie jest obsługiwane.",
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) nie jest obsługiwane",
@@ -280,16 +280,16 @@
"title": "Współdzielony dokument"
},
"feedback": {
"average": "Średni",
"bad": "Źle",
"average": "Średnio",
"bad": "Źle",
"detailsLabel": "Powiedz nam o tym więcej.",
"good": "Prawdziwy",
"rateExperience": "Oceń proszę swoje doświadczenia z konferencji",
"good": "Dobrze",
"rateExperience": "Jak oceniasz tę konferencję?",
"veryBad": "Bardzo źle",
"veryGood": "1: Bardzo dobrze"
"veryGood": "Bardzo dobrze"
},
"incomingCall": {
"answer": "Odpowiedz",
"answer": "Odbierz",
"audioCallTitle": "Przychodzące połączenie",
"decline": "Odrzuć",
"productLabel": "z Jitsi Meet",
@@ -512,18 +512,18 @@
"title": "Kalendarz"
},
"devices": "Urządzenia",
"followMe": "Wszyscy za mną",
"followMe": "Wszyscy widzą mnie",
"language": "Język",
"loggedIn": "Zalogowano jako {{name}}",
"moderator": "Moderujący",
"moderator": "Moderacja",
"more": "Więcej",
"name": "Nazwa",
"noDevice": "Brak",
"selectAudioOutput": "Wyjście audio",
"selectCamera": "Kamera",
"selectMic": "Mikrofon",
"startAudioMuted": "Wszyscy się wyciszyli",
"startVideoMuted": "Wszyscy się ukryli",
"startAudioMuted": "Wycisz wszystkich dołączających",
"startVideoMuted": "Ukryj wszystkich dołączających",
"title": "Ustawienia"
},
"settingsView": {
@@ -600,7 +600,7 @@
"shortcuts": "Przełączanie skrótów klawiszowych",
"show": "",
"speakerStats": "Przełączanie statystyk dotyczących mówców",
"tileView": "Przełączanie kafelkowego widoku",
"tileView": "Przełącz widok kafelkowy",
"toggleCamera": "Przełączanie kamery",
"videomute": "Przełączanie wyciszonego filmu wideo",
"videoblur": "Przełącz rozmazanie obrazu"
@@ -626,9 +626,9 @@
"invite": "Zapraszaj ludzi",
"login": "Zaloguj",
"logout": "Wyloguj",
"lowerYourHand": "Opuść swą rękę",
"lowerYourHand": "Opuść rękę",
"moreActions": "Więcej działań",
"mute": "Wycisz / Pogłośnij",
"mute": "Włącz / Wyłącz mikrofon",
"noAudioSignalTitle": "",
"noAudioSignalDesc": "",
"noAudioSignalDescSuggestion": "",
@@ -636,13 +636,13 @@
"pip": "Wprowadź tryb obrazu w obrazie",
"privateMessage": "Wyślij wiadomość prywatną",
"profile": "Edytuj swój profil",
"raiseHand": "Podnieś / Opuść swą rękę",
"raiseYourHand": "Podnieś swą rękę",
"raiseHand": "Podnieś / Opuść rękę",
"raiseYourHand": "Podnieś rękę",
"Settings": "Ustawienia",
"sharedvideo": "Udostępniaj wideo w Youtube",
"sharedvideo": "Udostępnij wideo w Youtube",
"shareRoom": "Zaproś kogoś",
"shortcuts": "Wyświetlanie skrótów",
"speakerStats": "Statystyki głośników",
"shortcuts": "Wyświetl skróty",
"speakerStats": "Statystyki mówców",
"startScreenSharing": "Zacznij współdzielenie ekranu",
"startSubtitles": "Uruchom napisy",
"stopScreenSharing": "Zatrzymaj współdzielenie ekranu",

View File

@@ -569,9 +569,9 @@
"shareYourScreen": "Slå av eller på skärmdelning",
"shortcuts": "Stäng eller öppna genvägar",
"show": "",
"speakerStats": "Stäng eller öppna högstalarstatistik",
"speakerStats": "Stäng eller öppna talarstatistik",
"tileView": "Öppna eller stäng panelvyn",
"toggleCamera": "Byta kamera",
"toggleCamera": "Växla kamera",
"videomute": "Sätt på eller stäng av mikrofonen",
"videoblur": ""
},
@@ -606,7 +606,7 @@
"sharedvideo": "Dela en Youtube-video",
"shareRoom": "Bjud in någon",
"shortcuts": "Visa genvägar",
"speakerStats": "Högtalarspecifikationer",
"speakerStats": "Talarstatistik",
"startScreenSharing": "Starta skärmdelning",
"startSubtitles": "Starta undertextning",
"stopScreenSharing": "Avsluta skämdelning",

View File

@@ -220,7 +220,7 @@
"muteParticipantDialog": "Are you sure you want to mute this participant? You won't be able to unmute them, but they can unmute themselves at any time.",
"muteParticipantTitle": "Mute this participant?",
"Ok": "Ok",
"passwordLabel": "$t(lockRoomPasswordUppercase)",
"passwordLabel": "The meeting has been locked by a participant. Please enter the $t(lockRoomPassword) to join.",
"passwordNotSupported": "Setting a meeting $t(lockRoomPassword) is not supported.",
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) not supported",
"passwordRequired": "$t(lockRoomPasswordUppercase) required",
@@ -244,6 +244,7 @@
"reservationError": "Reservation system error",
"reservationErrorMsg": "Error code: {{code}}, message: {{msg}}",
"retry": "Retry",
"screenSharingAudio": "Share audio",
"screenSharingFailedToInstall": "Oops! Your screen sharing extension failed to install.",
"screenSharingFailedToInstallTitle": "Screen sharing extension failed to install",
"screenSharingFirefoxPermissionDeniedError": "Something went wrong while we were trying to share your screen. Please make sure that you have given us permission to do so. ",
@@ -525,6 +526,7 @@
"followMe": "Everyone follows me",
"language": "Language",
"loggedIn": "Logged in as {{name}}",
"microphones": "Microphones",
"moderator": "Moderator",
"more": "More",
"name": "Name",
@@ -532,6 +534,7 @@
"selectAudioOutput": "Audio output",
"selectCamera": "Camera",
"selectMic": "Microphone",
"speakers": "Speakers",
"startAudioMuted": "Everyone starts muted",
"startVideoMuted": "Everyone starts hidden",
"title": "Settings"
@@ -614,6 +617,7 @@
"speakerStats": "Toggle speaker statistics",
"tileView": "Toggle tile view",
"toggleCamera": "Toggle camera",
"toggleFilmstrip": "Toggle filmstrip",
"videomute": "Toggle mute video",
"videoblur": "Toggle video blur"
},
@@ -647,7 +651,7 @@
"noAudioSignalDesc": "If you did not purposely mute it from system settings or hardware, consider switching the device.",
"noAudioSignalDescSuggestion": "If you did not purposely mute it from system settings or hardware, consider switching to the suggested device.",
"noAudioSignalDialInDesc": "You can also dial-in using:",
"noAudioSignalDialInLinkDesc" : "Dial-in numbers",
"noAudioSignalDialInLinkDesc": "Dial-in numbers",
"noisyAudioInputTitle": "Your microphone appears to be noisy!",
"noisyAudioInputDesc": "It sounds like your microphone is making noise, please consider muting or changing the device.",
"openChat": "Open chat",
@@ -754,6 +758,7 @@
"connectCalendarButton": "Connect your calendar",
"connectCalendarText": "Connect your calendar to view all your meetings in {{app}}. Plus, add {{provider}} meetings to your calendar and start them with one click.",
"enterRoomTitle": "Start a new meeting",
"getHelp": "Get help",
"roomNameAllowedChars": "Meeting name should not contain any of these characters: ?, &, :, ', \", %, #.",
"go": "GO",
"goSmall": "GO",
@@ -773,5 +778,8 @@
"lonelyMeetingExperience": {
"button": "Invite others",
"youAreAlone": "You are the only one in the meeting"
},
"helpView": {
"header": "Help centre"
}
}

View File

@@ -369,12 +369,14 @@ class API {
* Notify external application (if API is enabled) that message was sent.
*
* @param {string} message - Message body.
* @param {boolean} privateMessage - True if the message was a private message.
* @returns {void}
*/
notifySendingChatMessage(message: string) {
notifySendingChatMessage(message: string, privateMessage: boolean) {
this._sendEvent({
name: 'outgoing-message',
message
message,
privateMessage
});
}

View File

@@ -184,7 +184,7 @@ export default class LargeVideoManager {
this.newStreamData = null;
logger.info('hover in %s', id);
logger.info(`hover in ${id}`);
this.state = videoType;
// eslint-disable-next-line no-shadow
const container = this.getCurrentContainer();

19
package-lock.json generated
View File

@@ -10653,8 +10653,8 @@
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
},
"js-utils": {
"version": "github:jitsi/js-utils#91c5e53ca5fa42907c88d56bc78254e6e56e058d",
"from": "github:jitsi/js-utils#91c5e53ca5fa42907c88d56bc78254e6e56e058d",
"version": "github:jitsi/js-utils#0b2cef90613a74777fefd98d4ee3eda3879809ab",
"from": "github:jitsi/js-utils#0b2cef90613a74777fefd98d4ee3eda3879809ab",
"requires": {
"bowser": "2.7.0",
"js-md5": "0.7.3",
@@ -10883,8 +10883,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#a7950f8ebb489225c2e8bf41fe65f330b3de0874",
"from": "github:jitsi/lib-jitsi-meet#a7950f8ebb489225c2e8bf41fe65f330b3de0874",
"version": "github:jitsi/lib-jitsi-meet#960eea3c5087ce07e9135fad70268c7d338e0de5",
"from": "github:jitsi/lib-jitsi-meet#960eea3c5087ce07e9135fad70268c7d338e0de5",
"requires": {
"@jitsi/sdp-interop": "0.1.14",
"@jitsi/sdp-simulcast": "0.2.2",
@@ -10897,7 +10897,7 @@
"strophe.js": "1.3.4",
"strophejs-plugin-disco": "0.0.2",
"strophejs-plugin-stream-management": "github:jitsi/strophejs-plugin-stream-management#cec7608601c1bc098543823fc658e3ddf758c009",
"webrtc-adapter": "github:webrtc/adapter#1eec19782b4058d186341263e7d049cea3e3290a"
"webrtc-adapter": "7.5.0"
},
"dependencies": {
"js-md5": {
@@ -18999,11 +18999,12 @@
}
},
"webrtc-adapter": {
"version": "github:webrtc/adapter#1eec19782b4058d186341263e7d049cea3e3290a",
"from": "github:webrtc/adapter#1eec19782b4058d186341263e7d049cea3e3290a",
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.5.0.tgz",
"integrity": "sha512-cUqlw310uLLSYvO8FTNCVmGWSMlMt6vuSDkcYL1nW+RUvAILJ3jEIvAUgFQU5EFGnU+mf9/No14BFv3U+hoxBQ==",
"requires": {
"rtcpeerconnection-shim": "^1.1.13",
"sdp": "^2.3.0"
"rtcpeerconnection-shim": "^1.2.15",
"sdp": "^2.12.0"
}
},
"websocket-driver": {

View File

@@ -53,10 +53,10 @@
"jquery-contextmenu": "2.4.5",
"jquery-i18next": "1.2.1",
"js-md5": "0.6.1",
"js-utils": "github:jitsi/js-utils#91c5e53ca5fa42907c88d56bc78254e6e56e058d",
"js-utils": "github:jitsi/js-utils#0b2cef90613a74777fefd98d4ee3eda3879809ab",
"jsrsasign": "8.0.12",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#a7950f8ebb489225c2e8bf41fe65f330b3de0874",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#960eea3c5087ce07e9135fad70268c7d338e0de5",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.13",
"moment": "2.19.4",

View File

@@ -89,10 +89,22 @@ export class AbstractApp extends BaseApp<Props, *> {
return (
<Fragment>
<OverlayContainer />
{ this._createExtraPlatformSpecificElement() }
</Fragment>
);
}
/**
* Renders platform specific extra elements to be added alongside with the main element, if need be.
*
* NOTE: Overridden by child components.
*
* @returns {React$Element}
*/
_createExtraPlatformSpecificElement() {
return null;
}
_createMainElement: (React$Element<*>, Object) => ?React$Element<*>;
/**

View File

@@ -12,6 +12,7 @@ import { Platform } from '../../base/react';
import '../../base/responsive-ui';
import { updateSettings } from '../../base/settings';
import '../../google-api';
import { HelpView } from '../../help';
import '../../mobile/audio-mode';
import '../../mobile/back-button';
import '../../mobile/background';
@@ -107,6 +108,17 @@ export class App extends AbstractApp {
});
}
/**
* Renders platform specific extra elements to be added alongside with the main element, if need be.
*
* @inheritdoc
*/
_createExtraPlatformSpecificElement() {
return (
<HelpView />
);
}
/**
* Attempts to disable the use of React Native
* {@link ExceptionsManager#handleException} on platforms and in

View File

@@ -39,6 +39,7 @@ export default {
statusBarContent: ColorPalette.white,
text: ColorPalette.white
},
'Modal': {},
'LargeVideo': {
background: 'rgb(42, 58, 75)'
},

View File

@@ -164,7 +164,7 @@ export function getConferenceName(stateful: Function | Object): string {
|| subject
|| callDisplayName
|| (callee && callee.name)
|| _.startCase(safeDecodeURIComponent(room));
|| safeStartCase(safeDecodeURIComponent(room));
}
/**
@@ -351,3 +351,19 @@ export function sendLocalParticipant(
conference.setDisplayName(name);
}
/**
* A safe implementation of lodash#startCase that doesn't deburr the string.
*
* NOTE: According to lodash roadmap, lodash v5 will have this function.
*
* Code based on https://github.com/lodash/lodash/blob/master/startCase.js.
*
* @param {string} s - The string to do start case on.
* @returns {string}
*/
function safeStartCase(s = '') {
return _.words(`${s}`.replace(/['\u2019]/g, '')).reduce(
(result, word, index) => result + (index ? ' ' : '') + _.upperFirst(word)
, '');
}

View File

@@ -1,3 +1,5 @@
import extraConfigWhitelist from './extraConfigWhitelist';
/**
* The config keys to whitelist, the keys that can be overridden.
* Currently we can only whitelist the first part of the properties, like
@@ -88,6 +90,7 @@ export default [
'disableLocalVideoFlip',
'disableNS',
'disableRemoteControl',
'disableRemoteMute',
'disableRtx',
'disableSuspendVideo',
'disableThirdPartyRequests',
@@ -126,6 +129,7 @@ export default [
'pcStatsInterval',
'preferH264',
'requireDisplayName',
'remoteVideoMenu',
'resolution',
'startAudioMuted',
'startAudioOnly',
@@ -143,4 +147,4 @@ export default [
'useStunTurn',
'webrtcIceTcpDisable',
'webrtcIceUdpDisable'
];
].concat(extraConfigWhitelist);

View File

@@ -0,0 +1,4 @@
/**
* Deploy-specific configuration whitelists
*/
export default [];

View File

@@ -43,7 +43,9 @@ const INITIAL_RN_STATE = {
p2p: {
disableH264: false,
preferH264: true
}
},
remoteVideoMenu: {}
};
ReducerRegistry.register('features/base/config', (state = _getInitialState(), action) => {

View File

@@ -216,6 +216,18 @@ export function setAudioInputDevice(deviceId) {
};
}
/**
* Updates the output device id.
*
* @param {string} deviceId - The id of the new output device.
* @returns {Function}
*/
export function setAudioOutputDevice(deviceId) {
return function(dispatch) {
return setAudioOutputDeviceId(deviceId, dispatch);
};
}
/**
* Signals to update the currently used video input device.
*

View File

@@ -174,6 +174,60 @@ export function formatDeviceLabel(label: string) {
return formattedLabel;
}
/**
* Returns a list of objects containing all the microphone device ids and labels.
*
* @param {Object} state - The state of the application.
* @returns {Object[]}
*/
export function getAudioInputDeviceData(state: Object) {
return state['features/base/devices'].availableDevices.audioInput.map(
({ deviceId, label }) => {
return {
deviceId,
label
};
});
}
/**
* Returns a list of objectes containing all the output device ids and labels.
*
* @param {Object} state - The state of the application.
* @returns {Object[]}
*/
export function getAudioOutputDeviceData(state: Object) {
return state['features/base/devices'].availableDevices.audioOutput.map(
({ deviceId, label }) => {
return {
deviceId,
label
};
});
}
/**
* Returns a list of all the camera device ids.
*
* @param {Object} state - The state of the application.
* @returns {string[]}
*/
export function getVideoDeviceIds(state: Object) {
return state['features/base/devices'].availableDevices.videoInput.map(({ deviceId }) => deviceId);
}
/**
* Returns true if there are devices of a specific type.
*
* @param {Object} state - The state of the application.
* @param {string} type - The type of device: VideoOutput | audioOutput | audioInput.
*
* @returns {boolean}
*/
export function hasAvailableDevices(state: Object, type: string) {
return state['features/base/devices'].availableDevices[type].length > 0;
}
/**
* Set device id of the audio output device which is currently in use.
* Empty string stands for default device.

View File

@@ -13,6 +13,12 @@ export const CALENDAR_ENABLED = 'calendar.enabled';
*/
export const CALL_INTEGRATION_ENABLED = 'call-integration.enabled';
/**
* Flag indicating if chat should be enabled.
* Default: enabled (true).
*/
export const CLOSE_CAPTIONS_ENABLED = 'close-captions.enabled';
/**
* Flag indicating if chat should be enabled.
* Default: enabled (true).

View File

@@ -0,0 +1,6 @@
/**
* See {@link _setLanguage}.
*
* @type {string}
*/
export const SET_LANGUAGE = 'SET_LANGUAGE';

View File

@@ -0,0 +1,33 @@
// @flow
import { i18next } from './index';
import { SET_LANGUAGE } from './actionTypes';
/**
* Makes an attempt to change the language.
*
* @param {string} newLanguage - The new language code which identifies
* the language to be set.
* @returns {Function}
*/
export function changeLanguage(newLanguage: string): Function {
return function(dispatch: (Object) => Object) {
return i18next.changeLanguage(newLanguage)
.then(() => {
dispatch(_setLanguage(i18next.language));
});
};
}
/**
* Sets the current language on the Redux store.
*
* @param {string} language - The new language code that will be stored.
* @returns {Object}
* @private
*/
export function _setLanguage(language: string): Object {
return {
type: SET_LANGUAGE,
language
};
}

View File

@@ -1,6 +1,10 @@
export * from './actions';
export * from './dateUtil';
export * from './functions';
// TODO Eventually (e.g. when the non-React Web app is rewritten into React), it
// should not be necessary to export i18next.
export { default as i18next, DEFAULT_LANGUAGE, LANGUAGES } from './i18next';
import './middleware';
import './reducer';

View File

@@ -0,0 +1,69 @@
/* @flow */
/* global document */
import i18next from 'i18next';
import { APP_WILL_MOUNT } from '../app';
import { MiddlewareRegistry } from '../redux';
import { SET_LANGUAGE } from './actionTypes';
import { _setLanguage } from './actions';
/**
* The Redux middleware of the feature base/i18n.
*
* @param {Store} store - The Redux store.
* @returns {Function}
* @private
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case APP_WILL_MOUNT:
return _appWillMount(store, next, action);
case SET_LANGUAGE:
return handleSetLanguage(store, next, action);
}
return next(action);
});
/**
* Side effects for {@link APP_WILL_MOUNT}.
*
* @param {Store} store - The Redux store in which the specified {@code action}
* is being dispatched.
* @param {Dispatch} next - The Redux {@code dispatch} function to dispatch the
* specified {@code action} to the specified {@code store}.
* @param {Action} action - The Redux action {@code APP_WILL_MOUNT} which is
* being dispatched in the specified {@code store}.
* @private
* @returns {Object} The new state that is the result of the reduction of the
* specified {@code action}.
*/
function _appWillMount({ dispatch }, next, action) {
if (i18next.language) {
dispatch(_setLanguage(i18next.language));
} else {
i18next.on('initialized', () => {
dispatch(_setLanguage(i18next.language));
});
}
return next(action);
}
/**
* Side effects for the {@link SET_LANGUAGE} action.
*
* @param {Store} store - The Redux store in which the specified {@code action}
* is being dispatched.
* @param {Dispatch} next - The Redux {@code dispatch} function to dispatch the
* specified {@code action} to the specified {@code store}.
* @param {Action} action - The Redux action {@code SET_LANGUAGE} which is
* being dispatched in the specified {@code store}.
* @returns {*}
*/
function handleSetLanguage(store, next, action) {
document && document.documentElement && document.documentElement.setAttribute('lang', action.language);
return next(action);
}

View File

@@ -0,0 +1,30 @@
// @flow
import { ReducerRegistry, set } from '../redux';
import {
SET_LANGUAGE
} from './actionTypes';
import { DEFAULT_LANGUAGE } from './i18next';
/**
* The default/initial redux state of the feature base/i18n.
*
* @type {{
* language: string
* }}
*/
const DEFAULT_STATE = {
language: DEFAULT_LANGUAGE
};
ReducerRegistry.register(
'features/base/i18n',
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case SET_LANGUAGE:
return set(state, 'language', action.language);
default:
return state;
}
});

View File

@@ -0,0 +1,3 @@
<svg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.07001 0.248238C8.3471 -0.0596449 8.82132 -0.0846038 9.1292 0.192491C9.43709 0.469585 9.46205 0.943802 9.18495 1.25168L5.65622 5.19348C5.35829 5.52451 4.83922 5.52451 4.54128 5.19348L1.06752 1.25168C0.79043 0.943802 0.81539 0.469585 1.12327 0.192491C1.43115 -0.0846038 1.90537 -0.0596449 2.18247 0.248238L5.09875 3.57062L8.07001 0.248238Z" fill="#5E6D7A"/>
</svg>

After

Width:  |  Height:  |  Size: 509 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 14.6667C4.3181 14.6667 1.33333 11.682 1.33333 8.00007C1.33333 4.31817 4.3181 1.3334 8 1.3334C11.6819 1.3334 14.6667 4.31817 14.6667 8.00007C14.6667 11.682 11.6819 14.6667 8 14.6667ZM7.33333 4.66676C7.33333 4.29857 7.6318 4.00009 8 4.00009C8.36819 4.00009 8.66666 4.29857 8.66666 4.66676V8.00009C8.66666 8.36828 8.36819 8.66676 8 8.66676C7.6318 8.66676 7.33333 8.36828 7.33333 8.00009V4.66676ZM8 10.0001C7.63181 10.0001 7.33333 10.2985 7.33333 10.6667C7.33333 11.0349 7.63181 11.3334 8 11.3334C8.36818 11.3334 8.66666 11.0349 8.66666 10.6667C8.66666 10.2985 8.36818 10.0001 8 10.0001Z" />
</svg>

After

Width:  |  Height:  |  Size: 731 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.33331 8.00004C1.33331 11.6819 4.31808 14.6667 7.99998 14.6667C11.6819 14.6667 14.6666 11.6819 14.6666 8.00004C14.6666 4.31814 11.6819 1.33337 7.99998 1.33337C4.31808 1.33337 1.33331 4.31814 1.33331 8.00004ZM13.3333 8.00005C13.3333 10.9456 10.9455 13.3334 7.99998 13.3334C5.05446 13.3334 2.66665 10.9456 2.66665 8.00005C2.66665 5.05453 5.05446 2.66672 7.99998 2.66672C10.9455 2.66672 13.3333 5.05453 13.3333 8.00005ZM7.33331 4.66673C7.33331 4.29854 7.63179 4.00006 7.99998 4.00006C8.36817 4.00006 8.66665 4.29854 8.66665 4.66673V8.00006C8.66665 8.36825 8.36817 8.66673 7.99998 8.66673C7.63179 8.66673 7.33331 8.36825 7.33331 8.00006V4.66673ZM7.99998 10C7.63179 10 7.33331 10.2985 7.33331 10.6667C7.33331 11.0349 7.63179 11.3334 7.99998 11.3334C8.36817 11.3334 8.66665 11.0349 8.66665 10.6667C8.66665 10.2985 8.36817 10 7.99998 10Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1001 B

View File

@@ -3,6 +3,7 @@
export { default as IconAdd } from './add.svg';
export { default as IconAddPeople } from './link.svg';
export { default as IconArrowBack } from './arrow_back.svg';
export { default as IconArrowDown } from './arrow_down.svg';
export { default as IconAudioOnly } from './visibility.svg';
export { default as IconAudioOnlyOff } from './visibility-off.svg';
export { default as IconAudioRoute } from './volume.svg';
@@ -27,6 +28,8 @@ export { default as IconDominantSpeaker } from './dominant-speaker.svg';
export { default as IconDownload } from './download.svg';
export { default as IconDragHandle } from './drag-handle.svg';
export { default as IconEventNote } from './event_note.svg';
export { default as IconExclamation } from './exclamation.svg';
export { default as IconExclamationSolid } from './exclamation-solid.svg';
export { default as IconExitFullScreen } from './exit-full-screen.svg';
export { default as IconFeedback } from './feedback.svg';
export { default as IconFullScreen } from './full-screen.svg';
@@ -41,8 +44,10 @@ export { default as IconMenuDown } from './menu-down.svg';
export { default as IconMenuThumb } from './thumb-menu.svg';
export { default as IconMenuUp } from './menu-up.svg';
export { default as IconMessage } from './message.svg';
export { default as IconMeter } from './meter.svg';
export { default as IconMicDisabled } from './mic-disabled.svg';
export { default as IconMicrophone } from './microphone.svg';
export { default as IconMicrophoneEmpty } from './microphone-empty.svg';
export { default as IconModerator } from './star.svg';
export { default as IconMuteEveryone } from './mute-everyone.svg';
export { default as IconMuteEveryoneElse } from './mute-everyone-else.svg';
@@ -64,6 +69,7 @@ export { default as IconSettings } from './settings.svg';
export { default as IconSignalLevel0 } from './signal_cellular_0.svg';
export { default as IconSignalLevel1 } from './signal_cellular_1.svg';
export { default as IconSignalLevel2 } from './signal_cellular_2.svg';
export { default as IconShare } from './share.svg';
export { default as IconShareDesktop } from './share-desktop.svg';
export { default as IconShareDoc } from './share-doc.svg';
export { default as IconShareVideo } from './shared-video.svg';
@@ -75,3 +81,4 @@ export { default as IconVideoQualityHD } from './HD.svg';
export { default as IconVideoQualityLD } from './LD.svg';
export { default as IconVideoQualitySD } from './SD.svg';
export { default as IconVolume } from './volume.svg';
export { default as IconVolumeEmpty } from './volume-empty.svg';

View File

@@ -0,0 +1,10 @@
<svg width="38" height="12" viewBox="0 0 38 12" fill="#5E6D7A" xmlns="http://www.w3.org/2000/svg">
<rect width="3" height="12" rx="1"/>
<rect x="5" width="3" height="12" rx="1" />
<rect x="10" width="3" height="12" rx="1" />
<rect x="15" width="3" height="12" rx="1" />
<rect x="20" width="3" height="12" rx="1" />
<rect x="25" width="3" height="12" rx="1" />
<rect x="30" width="3" height="12" rx="1" />
<rect x="35" width="3" height="12" rx="1" />
</svg>

After

Width:  |  Height:  |  Size: 457 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 6C16 3.79086 14.2091 2 12 2C9.79086 2 8 3.79086 8 6V12C8 13.8666 9.27853 15.4346 11.0076 15.8759C11.0026 15.9166 11 15.958 11 16V17.917C8.16229 17.441 6 14.973 6 12C6 11.4477 5.55228 11 5 11C4.44772 11 4 11.4477 4 12C4 16.0796 7.05369 19.446 11 19.9381V21C11 21.5523 11.4477 22 12 22C12.5523 22 13 21.5523 13 21V19.9381C16.9463 19.446 20 16.0796 20 12C20 11.4477 19.5523 11 19 11C18.4477 11 18 11.4477 18 12C18 14.973 15.8377 17.441 13 17.917V16C13 15.958 12.9974 15.9166 12.9924 15.8759C14.7215 15.4346 16 13.8666 16 12V6ZM12 4C10.8954 4 10 4.89543 10 6V12C10 13.1046 10.8954 14 12 14C13.1046 14 14 13.1046 14 12V6C14 4.89543 13.1046 4 12 4Z" fill="#A4B8D1"/>
</svg>

After

Width:  |  Height:  |  Size: 817 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" enable-background="new 0 0 50 50"><path d="M30.3 13.7L25 8.4l-5.3 5.3-1.4-1.4L25 5.6l6.7 6.7z"/><path d="M24 7h2v21h-2z"/><path d="M35 40H15c-1.7 0-3-1.3-3-3V19c0-1.7 1.3-3 3-3h7v2h-7c-.6 0-1 .4-1 1v18c0 .6.4 1 1 1h20c.6 0 1-.4 1-1V19c0-.6-.4-1-1-1h-7v-2h7c1.7 0 3 1.3 3 3v18c0 1.7-1.3 3-3 3z"/></svg>

After

Width:  |  Height:  |  Size: 361 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.1799 3.68341L6 8H3C2.44772 8 2 8.44771 2 9V15C2 15.5523 2.44772 16 3 16H6L11.1799 20.3166C11.2698 20.3915 11.383 20.4325 11.5 20.4325C11.7761 20.4325 12 20.2086 12 19.9325V4.06752C12 3.95055 11.959 3.83728 11.8841 3.74743C11.7073 3.53529 11.392 3.50662 11.1799 3.68341ZM4 10H6.7241L10 7.27008V16.7299L6.7241 14H4V10ZM14 8C16.2091 8 18 9.79086 18 12C18 14.2091 16.2091 16 14 16V14C15.1046 14 16 13.1046 16 12C16 10.8954 15.1046 10 14 10V8ZM14 4C18.4183 4 22 7.58172 22 12C22 16.4183 18.4183 20 14 20V18C17.3137 18 20 15.3137 20 12C20 8.68629 17.3137 6 14 6V4Z" fill="#A4B8D1"/>
</svg>

After

Width:  |  Height:  |  Size: 733 B

View File

@@ -0,0 +1,6 @@
// @flow
/**
* Action type to set the ID of the active modal (or undefined if needs to be hidden).
*/
export const SET_ACTIVE_MODAL_ID = 'SET_ACTIVE_MODAL_ID';

View File

@@ -0,0 +1,19 @@
// @flow
import { SET_ACTIVE_MODAL_ID } from './actionTypes';
/**
* Action to set the ID of the active modal (or undefined if needs to be hidden).
*
* @param {string} activeModalId - The new modal ID or undefined.
* @returns {{
* activeModalId: string,
* type: SET_ACTIVE_MODAL_ID
* }}
*/
export function setActiveModalId(activeModalId: ?string) {
return {
activeModalId,
type: SET_ACTIVE_MODAL_ID
};
}

View File

@@ -0,0 +1,151 @@
// @flow
import React, { PureComponent } from 'react';
import { SafeAreaView, View } from 'react-native';
import { ColorSchemeRegistry } from '../../color-scheme';
import { HeaderWithNavigation, SlidingView } from '../../react';
import { connect } from '../../redux';
import { StyleType } from '../../styles';
import { setActiveModalId } from '../actions';
import styles from './styles';
type Props = {
/**
* The color schemed style of the common header component.
*/
_headerStyles: StyleType,
/**
* True if the modal should be shown, false otherwise.
*/
_show: boolean,
/**
* The color schemed style of the modal.
*/
_styles: StyleType,
/**
* The children component(s) of the Modal, to be rendered.
*/
children: React$Node,
/**
* The Redux Dispatch function.
*/
dispatch: Function,
/**
* The i18n label key of the header title.
*/
headerLabelKey: string,
/**
* The ID of the modal that is being rendered. This is used to show/hide the modal.
*/
modalId: string,
/**
* Callback to be invoked when the modal closes.
*/
onClose?: Function,
/**
* The position from where the modal should be opened. This is derived from the
* props of the {@code SlidingView} with the same name.
*/
position?: string
};
/**
* Implements a custom Jitsi Modal that doesn't use the built in native
* Modal component of React Native.
*/
class JitsiModal extends PureComponent<Props> {
static defaultProps = {
position: 'bottom'
};
/**
* Instantiates a new component.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this._onRequestClose = this._onRequestClose.bind(this);
}
/**
* Implements {@code PureComponent#render}.
*
* @inheritdoc
*/
render() {
const { _headerStyles, _show, _styles, children, headerLabelKey, position } = this.props;
return (
<SlidingView
onHide = { this._onRequestClose }
position = { position }
show = { _show }>
<View
style = { [
_headerStyles.page,
_styles.page
] }>
<HeaderWithNavigation
headerLabelKey = { headerLabelKey }
onPressBack = { this._onRequestClose } />
<SafeAreaView style = { styles.safeArea }>
{ children }
</SafeAreaView>
</View>
</SlidingView>
);
}
_onRequestClose: () => boolean;
/**
* Callback to be invoked when the SlidingView requests closing.
*
* @returns {boolean}
*/
_onRequestClose() {
const { _show, dispatch, onClose } = this.props;
if (_show) {
if (typeof onClose === 'function') {
onClose();
}
dispatch(setActiveModalId());
return true;
}
return false;
}
}
/**
* Maps part of the Redix state to the props of this component.
*
* @param {Object} state - The Redux state.
* @param {Props} ownProps - The own props of the component.
* @returns {Props}
*/
function _mapStateToProps(state, ownProps): $Shape<Props> {
return {
_headerStyles: ColorSchemeRegistry.get(state, 'Header'),
_show: state['features/base/modal'].activeModalId === ownProps.modalId,
_styles: ColorSchemeRegistry.get(state, 'Modal')
};
}
export default connect(_mapStateToProps)(JitsiModal);

View File

@@ -0,0 +1,3 @@
// @flow
export { default as JitsiModal } from './JitsiModal';

View File

@@ -0,0 +1,6 @@
// @flow
import { Component } from 'react';
export const JitsiModal = Component;

View File

@@ -0,0 +1,15 @@
// @flow
import { ColorSchemeRegistry, schemeColor } from '../../color-scheme';
export default {
safeArea: {
flex: 1
}
};
ColorSchemeRegistry.register('Modal', {
page: {
backgroundColor: schemeColor('background')
}
});

View File

@@ -0,0 +1,7 @@
// @flow
import './reducer';
export * from './actions';
export * from './actionTypes';
export * from './components';

View File

@@ -0,0 +1,17 @@
// @flow
import { ReducerRegistry } from '../redux';
import { SET_ACTIVE_MODAL_ID } from './actionTypes';
ReducerRegistry.register('features/base/modal', (state = {}, action) => {
switch (action.type) {
case SET_ACTIVE_MODAL_ID:
return {
...state,
activeModalId: action.activeModalId
};
}
return state;
});

View File

@@ -2,6 +2,36 @@
export * from './functions.any';
/**
* Returns the deviceId for the currently used camera.
*
* @param {Object} state - The state of the application.
* @returns {void}
*/
export function getCurrentCameraDeviceId(state: Object) {
return state['features/base/settings'].cameraDeviceId;
}
/**
* Returns the deviceId for the currently used microphone.
*
* @param {Object} state - The state of the application.
* @returns {void}
*/
export function getCurrentMicDeviceId(state: Object) {
return state['features/base/settings'].micDeviceId;
}
/**
* Returns the deviceId for the currently used speaker.
*
* @param {Object} state - The state of the application.
* @returns {void}
*/
export function getCurrentOutputDeviceId(state: Object) {
return state['features/base/settings'].audioOutputDeviceId;
}
/**
* Handles changes to the `disableCallIntegration` setting.
* Noop on web.

View File

@@ -230,13 +230,14 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
/**
* Helper function to be implemented by subclasses, which must return a
* {@code boolean} value indicating if this button is toggled or not.
* {@code boolean} value indicating if this button is toggled or not or
* undefined if the button is not toggleable.
*
* @protected
* @returns {boolean}
* @returns {?boolean}
*/
_isToggled() {
return false;
return undefined;
}
_onClick: (*) => void;

View File

@@ -0,0 +1,137 @@
// @flow
import React, { Component } from 'react';
import { Icon } from '../../icons';
type Props = {
/**
* The decorated component (ToolboxButton).
*/
children: React$Node,
/**
* Icon of the button.
*/
icon: Function,
/**
* Flag used for disabling the small icon.
*/
iconDisabled: boolean,
/**
* Click handler for the small icon.
*/
onIconClick: Function,
/**
* Additional styles.
*/
styles?: Object,
};
type State = {
/**
* Whether the button is hovered or not.
*/
isHovered: boolean,
};
/**
* Displayes the `ToolboxButtonWithIcon` component.
*
* @returns {ReactElement}
*/
export default class ToolboxButtonWithIcon extends Component<Props, State> {
/**
* Initializes a new {@code ToolboxButtonWithIcon} instance.
*
* @param {Props} props - The props of the component.
*/
constructor(props: Props) {
super(props);
this.state = {
isHovered: false
};
this._onMouseEnter = this._onMouseEnter.bind(this);
this._onMouseLeave = this._onMouseLeave.bind(this);
}
_onMouseEnter: () => void;
/**
* Handler for when the small button has the mouse over.
*
* @returns {void}.
*/
_onMouseEnter() {
this.setState({
isHovered: true
});
}
_onMouseLeave: () => void;
/**
* Handler for when the mouse leaves the small button.
*
* @returns {void}
*/
_onMouseLeave() {
this.setState({
isHovered: false
});
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {React$Node}
*/
render() {
const {
children,
icon,
iconDisabled,
onIconClick,
styles
} = this.props;
const iconProps = {};
let size = 9;
if (iconDisabled) {
iconProps.className
= 'settings-button-small-icon settings-button-small-icon--disabled';
} else {
iconProps.className = 'settings-button-small-icon';
iconProps.onClick = onIconClick;
if (this.state.isHovered) {
iconProps.className = `${iconProps.className} settings-button-small-icon--hovered`;
size = 11;
}
}
return (
<div
className = 'settings-button-container'
styles = { styles }>
{children}
<div
onMouseEnter = { this._onMouseEnter }
onMouseLeave = { this._onMouseLeave }>
<Icon
{ ...iconProps }
size = { size }
src = { icon } />
</div>
</div>
);
}
}

View File

@@ -12,6 +12,69 @@ import type { Props } from './AbstractToolboxItem';
* Web implementation of {@code AbstractToolboxItem}.
*/
export default class ToolboxItem extends AbstractToolboxItem<Props> {
/**
* Initializes a new {@code ToolboxItem} instance.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this._onKeyDown = this._onKeyDown.bind(this);
this._onKeyUp = this._onKeyUp.bind(this);
}
_onKeyDown: (Object) => void;
/**
* Handles 'Enter' key on the button to trigger onClick for accessibility.
*
* @param {Object} event - The key event.
* @private
* @returns {void}
*/
_onKeyDown(event) {
// If the event coming to the dialog has been subject to preventDefault
// we don't handle it here.
if (event.defaultPrevented) {
return;
}
if (event.key === 'Enter') {
event.preventDefault();
event.stopPropagation();
this.props.onClick();
} else if (event.key === ' ') {
// Space triggers button onKeyUp but we need to prevent PTT here
event.preventDefault();
event.stopPropagation();
}
}
_onKeyUp: (Object) => void;
/**
* Handles ' ' (Space) key on the button to trigger onClick for
* accessibility.
*
* @param {Object} event - The key event.
* @private
* @returns {void}
*/
_onKeyUp(event) {
// If the event coming to the dialog has been subject to preventDefault
// we don't handle it here.
if (event.defaultPrevented) {
return;
}
if (event.key === ' ') {
event.preventDefault();
event.stopPropagation();
this.props.onClick();
}
}
/**
* Handles rendering of the actual item. If the label is being shown, which
* is controlled with the `showLabel` prop, the item is rendered for its
@@ -27,14 +90,22 @@ export default class ToolboxItem extends AbstractToolboxItem<Props> {
elementAfter,
onClick,
showLabel,
tooltipPosition
tooltipPosition,
toggled
} = this.props;
const className = showLabel ? 'overflow-menu-item' : 'toolbox-button';
const props = {
'aria-pressed': toggled,
'aria-disabled': disabled,
'aria-label': this.accessibilityLabel,
className: className + (disabled ? ' disabled' : ''),
onClick: disabled ? undefined : onClick
onClick: disabled ? undefined : onClick,
onKeyDown: this._onKeyDown,
onKeyUp: this._onKeyUp,
tabIndex: 0,
role: 'button'
};
const elementType = showLabel ? 'li' : 'div';
const useTooltip = this.tooltip && this.tooltip.length > 0;
let children = (

View File

@@ -7,3 +7,4 @@ export { default as AbstractHangupButton } from './AbstractHangupButton';
export { default as AbstractVideoMuteButton } from './AbstractVideoMuteButton';
export { default as BetaTag } from './BetaTag';
export { default as OverflowMenuItem } from './OverflowMenuItem';
export { default as ToolboxButtonWithIcon } from './ToolboxButtonWithIcon';

View File

@@ -1,18 +1,18 @@
// @flow
import React from 'react';
import { KeyboardAvoidingView, SafeAreaView } from 'react-native';
import { KeyboardAvoidingView } from 'react-native';
import { ColorSchemeRegistry } from '../../../base/color-scheme';
import { translate } from '../../../base/i18n';
import { HeaderWithNavigation, SlidingView } from '../../../base/react';
import { JitsiModal } from '../../../base/modal';
import { connect } from '../../../base/redux';
import { StyleType } from '../../../base/styles';
import { CHAT_VIEW_MODAL_ID } from '../../constants';
import AbstractChat, {
_mapDispatchToProps,
_mapStateToProps as _abstractMapStateToProps,
type Props as AbstractProps
_mapStateToProps,
type Props
} from '../AbstractChat';
import ChatInputBar from './ChatInputBar';
@@ -20,88 +20,31 @@ import MessageContainer from './MessageContainer';
import MessageRecipient from './MessageRecipient';
import styles from './styles';
type Props = AbstractProps & {
/**
* The color-schemed stylesheet of the feature.
*/
_styles: StyleType
};
/**
* Implements a React native component that renders the chat window (modal) of
* the mobile client.
*/
class Chat extends AbstractChat<Props> {
/**
* Instantiates a new instance.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this._onClose = this._onClose.bind(this);
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
*/
render() {
const { _styles } = this.props;
return (
<SlidingView
onHide = { this._onClose }
position = 'bottom'
show = { this.props._isOpen } >
<JitsiModal
headerLabelKey = 'chat.title'
modalId = { CHAT_VIEW_MODAL_ID }>
<KeyboardAvoidingView
behavior = 'padding'
style = { styles.chatContainer }>
<HeaderWithNavigation
headerLabelKey = 'chat.title'
onPressBack = { this._onClose } />
<SafeAreaView style = { _styles.backdrop }>
<MessageContainer messages = { this.props._messages } />
<MessageRecipient />
<ChatInputBar onSend = { this.props._onSendMessage } />
</SafeAreaView>
<MessageContainer messages = { this.props._messages } />
<MessageRecipient />
<ChatInputBar onSend = { this.props._onSendMessage } />
</KeyboardAvoidingView>
</SlidingView>
</JitsiModal>
);
}
_onClose: () => boolean
/**
* Closes the chat window.
*
* @returns {boolean}
*/
_onClose() {
if (this.props._isOpen) {
this.props._onToggleChat();
return true;
}
return false;
}
}
/**
* Maps part of the redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @returns {Props}
*/
function _mapStateToProps(state) {
return {
..._abstractMapStateToProps(state),
_styles: ColorSchemeRegistry.get(state, 'Chat')
};
}
export default translate(connect(_mapStateToProps, _mapDispatchToProps)(Chat));

View File

@@ -1,6 +1,7 @@
// @flow
import { IconChat, IconChatUnread } from '../../../base/icons';
import { setActiveModalId } from '../../../base/modal';
import { getLocalParticipant } from '../../../base/participants';
import { connect } from '../../../base/redux';
import {
@@ -9,7 +10,7 @@ import {
} from '../../../base/toolbox';
import { openDisplayNamePrompt } from '../../../display-name';
import { toggleChat } from '../../actions';
import { CHAT_VIEW_MODAL_ID } from '../../constants';
import { getUnreadCount } from '../../functions';
type Props = AbstractButtonProps & {
@@ -93,7 +94,7 @@ function _mapDispatchToProps(dispatch: Function) {
* @returns {void}
*/
_displayChat() {
dispatch(toggleChat());
dispatch(setActiveModalId(CHAT_VIEW_MODAL_ID));
},
/**

View File

@@ -5,7 +5,6 @@ import { TextInput, TouchableOpacity, View } from 'react-native';
import { translate } from '../../../base/i18n';
import { Icon, IconChatSend } from '../../../base/icons';
import { Platform } from '../../../base/react';
import styles from './styles';
@@ -136,7 +135,7 @@ class ChatInputBar extends Component<Props, State> {
*/
_onFocused(focused) {
return () => {
Platform.OS === 'android' && this.setState({
this.setState({
addPadding: focused
});
};

View File

@@ -1,5 +1,7 @@
// @flow
export const CHAT_VIEW_MODAL_ID = 'chatView';
/**
* The audio ID of the audio element for which the {@link playAudio} action is
* triggered when new chat message is received.

View File

@@ -76,12 +76,12 @@ MiddlewareRegistry.register(store => next => action => {
} else {
// Sending the message if privacy notice doesn't need to be shown.
if (typeof APP !== 'undefined') {
APP.API.notifySendingChatMessage(action.message);
}
const { privateMessageRecipient } = state['features/chat'];
if (typeof APP !== 'undefined') {
APP.API.notifySendingChatMessage(action.message, Boolean(privateMessageRecipient));
}
if (privateMessageRecipient) {
conference.sendPrivateTextMessage(privateMessageRecipient.id, action.message);
_persistSentPrivateMessage(store, privateMessageRecipient.id, action.message);

View File

@@ -1,5 +1,6 @@
// @flow
import { SET_ACTIVE_MODAL_ID } from '../base/modal';
import { ReducerRegistry } from '../base/redux';
import {
@@ -8,6 +9,7 @@ import {
SET_PRIVATE_MESSAGE_RECIPIENT,
TOGGLE_CHAT
} from './actionTypes';
import { CHAT_VIEW_MODAL_ID } from './constants';
const DEFAULT_STATE = {
isOpen: false,
@@ -56,6 +58,12 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
messages: []
};
case SET_ACTIVE_MODAL_ID:
if (action.activeModalId === CHAT_VIEW_MODAL_ID) {
return updateChatState(state);
}
break;
case SET_PRIVATE_MESSAGE_RECIPIENT:
return {
...state,
@@ -64,14 +72,24 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
};
case TOGGLE_CHAT:
return {
...state,
isOpen: !state.isOpen,
lastReadMessage: state.messages[
navigator.product === 'ReactNative' ? 0 : state.messages.length - 1],
privateMessageRecipient: state.isOpen ? undefined : state.privateMessageRecipient
};
return updateChatState(state);
}
return state;
});
/**
* Updates the chat status on opening the chat view.
*
* @param {Object} state - The Redux state of the feature.
* @returns {Object}
*/
function updateChatState(state) {
return {
...state,
isOpen: !state.isOpen,
lastReadMessage: state.messages[
navigator.product === 'ReactNative' ? 0 : state.messages.length - 1],
privateMessageRecipient: state.isOpen ? undefined : state.privateMessageRecipient
};
}

View File

@@ -8,6 +8,7 @@ import VideoLayout from '../../../../../modules/UI/videolayout/VideoLayout';
import { connect, disconnect } from '../../../base/connection';
import { translate } from '../../../base/i18n';
import { connect as reactReduxConnect } from '../../../base/redux';
import { getBackendSafeRoomName } from '../../../base/util';
import { Chat } from '../../../chat';
import { Filmstrip } from '../../../filmstrip';
import { CalleeInfoContainer } from '../../../invite';
@@ -78,6 +79,11 @@ type Props = AbstractProps & {
*/
_layoutClassName: string,
/**
* Name for this conference room.
*/
_roomName: string,
dispatch: Function,
t: Function
}
@@ -120,7 +126,7 @@ class Conference extends AbstractConference<Props, *> {
* @inheritdoc
*/
componentDidMount() {
document.title = interfaceConfig.APP_NAME;
document.title = `${this.props._roomName} | ${interfaceConfig.APP_NAME}`;
this._start();
}
@@ -259,11 +265,13 @@ class Conference extends AbstractConference<Props, *> {
*/
function _mapStateToProps(state) {
const currentLayout = getCurrentLayout(state);
const roomName = getBackendSafeRoomName(state['features/base/conference'].room);
return {
...abstractMapStateToProps(state),
_iAmRecorder: state['features/base/config'].iAmRecorder,
_layoutClassName: LAYOUT_CLASSNAMES[currentLayout]
_layoutClassName: LAYOUT_CLASSNAMES[currentLayout],
_roomName: roomName
};
}

View File

@@ -74,6 +74,11 @@ type Props = {
*/
type State = {
/**
* The state of the audio screen share checkbox.
*/
screenShareAudio: boolean,
/**
* The currently higlighted DesktopCapturerSource.
*/
@@ -128,6 +133,7 @@ class DesktopPicker extends PureComponent<Props, State> {
_poller = null;
state = {
screenShareAudio: false,
selectedSource: {},
selectedTab: 0,
sources: {},
@@ -153,6 +159,7 @@ class DesktopPicker extends PureComponent<Props, State> {
// Bind event handlers so they are only bound once per instance.
this._onCloseModal = this._onCloseModal.bind(this);
this._onPreviewClick = this._onPreviewClick.bind(this);
this._onShareAudioChecked = this._onShareAudioChecked.bind(this);
this._onSubmit = this._onSubmit.bind(this);
this._onTabSelected = this._onTabSelected.bind(this);
this._updateSources = this._updateSources.bind(this);
@@ -241,7 +248,7 @@ class DesktopPicker extends PureComponent<Props, State> {
return selectedSource;
}
_onCloseModal: (?string, string) => void;
_onCloseModal: (?string, string, ?boolean) => void;
/**
* Dispatches an action to hide the DesktopPicker and invokes the passed in
@@ -251,10 +258,12 @@ class DesktopPicker extends PureComponent<Props, State> {
* the onSourceChoose callback.
* @param {string} type - The type of the DesktopCapturerSource to pass into
* the onSourceChoose callback.
* @param {boolean} screenShareAudio - Whether or not to add system audio to
* screen sharing session.
* @returns {void}
*/
_onCloseModal(id = '', type) {
this.props.onSourceChoose(id, type);
_onCloseModal(id = '', type, screenShareAudio = false) {
this.props.onSourceChoose(id, type, screenShareAudio);
this.props.dispatch(hideDialog());
}
@@ -285,9 +294,9 @@ class DesktopPicker extends PureComponent<Props, State> {
* @returns {void}
*/
_onSubmit() {
const { id, type } = this.state.selectedSource;
const { selectedSource: { id, type }, screenShareAudio } = this.state;
this._onCloseModal(id, type);
this._onCloseModal(id, type, screenShareAudio);
}
_onTabSelected: () => void;
@@ -306,12 +315,29 @@ class DesktopPicker extends PureComponent<Props, State> {
const { types, sources } = this.state;
this._selectedTabType = types[tabIndex];
// When we change tabs also reset the screenShareAudio state so we don't
// use the option from one tab when sharing from another.
this.setState({
screenShareAudio: false,
selectedSource: this._getSelectedSource(sources),
selectedTab: tabIndex
});
}
_onShareAudioChecked: (boolean) => void;
/**
* Set the screenSharingAudio state indicating whether or not to also share
* system audio.
*
* @param {boolean} checked - Share audio or not.
* @returns {void}
*/
_onShareAudioChecked(checked) {
this.setState({ screenShareAudio: checked });
}
/**
* Configures and renders the tabs for display.
*
@@ -328,7 +354,8 @@ class DesktopPicker extends PureComponent<Props, State> {
content: <DesktopPickerPane
key = { type }
onClick = { this._onPreviewClick }
onDoubleClick = { this._onCloseModal }
onDoubleClick = { this._onSubmit }
onShareAudioChecked = { this._onShareAudioChecked }
selectedSourceId = { selectedSource.id }
sources = { sources[type] }
type = { type } />,

View File

@@ -1,8 +1,12 @@
/* @flow */
import { Checkbox } from '@atlaskit/checkbox';
import Spinner from '@atlaskit/spinner';
import React, { Component } from 'react';
import { Platform } from '../../base/react';
import { translate } from '../../base/i18n';
import DesktopSourcePreview from './DesktopSourcePreview';
/**
@@ -20,6 +24,11 @@ type Props = {
*/
onDoubleClick: Function,
/**
* The handler to be invoked if the users checks the audio screen sharing checkbox.
*/
onShareAudioChecked: Function,
/**
* The id of the DesktopCapturerSource that is currently selected.
*/
@@ -33,7 +42,12 @@ type Props = {
/**
* The source type of the DesktopCapturerSources to display.
*/
type: string
type: string,
/**
* Used to obtain translations.
*/
t: Function
};
/**
@@ -42,6 +56,31 @@ type Props = {
* @extends Component
*/
class DesktopPickerPane extends Component<Props> {
/**
* Initializes a new DesktopPickerPane instance.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props: Props) {
super(props);
this._onShareAudioCheck = this._onShareAudioCheck.bind(this);
}
_onShareAudioCheck: (Object) => void;
/**
* Function to be called when the Checkbox is used.
*
* @param {boolean} checked - Checkbox status (checked or not).
* @returns {void}
*/
_onShareAudioCheck({ target: { checked } }) {
this.props.onShareAudioChecked(checked);
}
/**
* Implements React's {@link Component#render()}.
*
@@ -54,7 +93,8 @@ class DesktopPickerPane extends Component<Props> {
onDoubleClick,
selectedSourceId,
sources,
type
type,
t
} = this.props;
const classNames
@@ -77,12 +117,25 @@ class DesktopPickerPane extends Component<Props> {
</div>
);
let checkBox;
// Only display the share audio checkbox if we're on windows and on
// desktop sharing tab.
// App window and Mac OS screen sharing doesn't work with system audio.
if (type === 'screen' && Platform.OS === 'windows') {
checkBox = (<Checkbox
label = { t('dialog.screenSharingAudio') }
name = 'share-system-audio'
onChange = { this._onShareAudioCheck } />);
}
return (
<div className = { classNames }>
{ previews }
{ checkBox }
</div>
);
}
}
export default DesktopPickerPane;
export default translate(DesktopPickerPane);

View File

@@ -10,6 +10,7 @@ import {
sendAnalytics
} from '../../../analytics';
import { Icon, IconMenuDown, IconMenuUp } from '../../../base/icons';
import { translate } from '../../../base/i18n';
import { connect } from '../../../base/redux';
import { dockToolbox } from '../../../toolbox';
@@ -87,7 +88,12 @@ type Props = {
/**
* The redux {@code dispatch} function.
*/
dispatch: Dispatch<any>
dispatch: Dispatch<any>,
/**
* Invoked to obtain translated strings.
*/
t: Function
};
/**
@@ -338,10 +344,12 @@ class Filmstrip extends Component <Props> {
*/
_renderToggleButton() {
const icon = this.props._visible ? IconMenuDown : IconMenuUp;
const { t } = this.props;
return (
<div className = 'filmstrip__toolbar'>
<button
aria-label = { t('toolbar.accessibilityLabel.toggleFilmstrip') }
id = 'toggleFilmstripButton'
onClick = { this._onToolbarToggleFilmstrip }>
<Icon src = { icon } />
@@ -387,4 +395,4 @@ function _mapStateToProps(state) {
};
}
export default connect(_mapStateToProps)(Filmstrip);
export default translate(connect(_mapStateToProps)(Filmstrip));

View File

@@ -0,0 +1,54 @@
// @flow
import React, { PureComponent } from 'react';
import WebView from 'react-native-webview';
import { JitsiModal } from '../../base/modal';
import { connect } from '../../base/redux';
import { HELP_VIEW_MODAL_ID } from '../constants';
const DEFAULT_HELP_CENTRE_URL = 'https://web-cdn.jitsi.net/faq/meet-faq.html';
type Props = {
/**
* The URL to display in the Help Centre.
*/
_url: string
}
/**
* Implements a page that renders the help content for the app.
*/
class HelpView extends PureComponent<Props> {
/**
* Implements {@code PureComponent#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
return (
<JitsiModal
headerLabelKey = 'helpView.header'
modalId = { HELP_VIEW_MODAL_ID }>
<WebView source = {{ uri: this.props._url }} />
</JitsiModal>
);
}
}
/**
* Maps part of the Redux state to the props of this component.
*
* @param {Object} state - The Redux state.
* @returns {Props}
*/
function _mapStateToProps(state) {
return {
_url: state['features/base/config'].helpCentreURL || DEFAULT_HELP_CENTRE_URL
};
}
export default connect(_mapStateToProps)(HelpView);

View File

@@ -0,0 +1,3 @@
// @flow
export { default as HelpView } from './HelpView';

View File

@@ -0,0 +1,3 @@
// @flow
export const HELP_VIEW_MODAL_ID = 'helpView';

View File

@@ -0,0 +1,4 @@
// @flow
export * from './components';
export * from './constants';

View File

@@ -13,9 +13,18 @@ import {
View
} from 'react-native';
import { ColorSchemeRegistry } from '../../../../base/color-scheme';
import { AlertDialog, openDialog } from '../../../../base/dialog';
import { translate } from '../../../../base/i18n';
import { Icon, IconCancelSelection, IconCheck, IconClose, IconPhone, IconSearch } from '../../../../base/icons';
import {
Icon,
IconCancelSelection,
IconCheck,
IconClose,
IconPhone,
IconSearch,
IconShare
} from '../../../../base/icons';
import {
AvatarListItem,
HeaderWithNavigation,
@@ -23,6 +32,7 @@ import {
type Item
} from '../../../../base/react';
import { connect } from '../../../../base/redux';
import { beginShareRoom } from '../../../../share-room';
import { setAddPeopleDialogVisible } from '../../../actions.native';
@@ -39,6 +49,11 @@ import styles, {
type Props = AbstractProps & {
/**
* The color schemed style of the Header.
*/
_headerStyles: Object,
/**
* True if the invite dialog should be open, false otherwise.
*/
@@ -52,6 +67,11 @@ type Props = AbstractProps & {
type State = AbstractState & {
/**
* Boolean to show if an extra padding needs to be added to the bottom bar.
*/
bottomPadding: boolean,
/**
* State variable to keep track of the search field value.
*/
@@ -79,6 +99,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
defaultState = {
addToCallError: false,
addToCallInProgress: false,
bottomPadding: false,
fieldValue: '',
inviteItems: [],
searchInprogress: false,
@@ -113,6 +134,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
this._onCloseAddPeopleDialog = this._onCloseAddPeopleDialog.bind(this);
this._onInvite = this._onInvite.bind(this);
this._onPressItem = this._onPressItem.bind(this);
this._onShareMeeting = this._onShareMeeting.bind(this);
this._onTypeQuery = this._onTypeQuery.bind(this);
this._setFieldRef = this._setFieldRef.bind(this);
}
@@ -137,7 +159,8 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
render() {
const {
_addPeopleEnabled,
_dialOutEnabled
_dialOutEnabled,
_headerStyles
} = this.props;
const { inviteItems, selectableItems } = this.state;
@@ -177,16 +200,17 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
</View>
<TextInput
autoCorrect = { false }
autoFocus = { true }
clearButtonMode = 'always' // iOS only
autoFocus = { false }
onBlur = { this._onFocused(false) }
onChangeText = { this._onTypeQuery }
onFocus = { this._onFocused(true) }
placeholder = {
this.props.t(`inviteDialog.${placeholderKey}`)
}
ref = { this._setFieldRef }
style = { styles.searchField }
value = { this.state.fieldValue } />
{ this._renderAndroidClearButton() }
{ this._renderClearButton() }
</View>
{ Boolean(inviteItems.length) && <View style = { styles.invitedList }>
<FlatList
@@ -206,6 +230,13 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
renderItem = { this._renderItem } />
</View>
</SafeAreaView>
<SafeAreaView
style = { [
styles.bottomBar,
_headerStyles.headerOverlay,
this.state.bottomPadding ? styles.extraBarPadding : null ] }>
{ this._renderShareMeetingButton() }
</SafeAreaView>
</KeyboardAvoidingView>
</SlidingView>
);
@@ -297,6 +328,22 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
return false;
}
_onFocused: boolean => Function;
/**
* Constructs a callback to be used to update the padding of the field if necessary.
*
* @param {boolean} focused - True of the field is focused.
* @returns {Function}
*/
_onFocused(focused) {
return () => {
Platform.OS === 'android' && this.setState({
bottomPadding: focused
});
};
}
_onInvite: () => void
/**
@@ -349,6 +396,22 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
};
}
_onShareMeeting: () => void
/**
* Shows the system share sheet to share the meeting information.
*
* @returns {void}
*/
_onShareMeeting() {
if (this.state.inviteItems.length > 0) {
// The use probably intended to invite people.
this._onInvite();
} else {
this.props.dispatch(beginShareRoom());
}
}
_onTypeQuery: string => void
/**
@@ -397,14 +460,12 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
_query: (string) => Promise<Array<Object>>;
/**
* Renders a button to clear the text field on Android.
*
* NOTE: For the best platform experience we use the native solution on iOS.
* Renders a button to clear the text field.
*
* @returns {React#Element<*>}
*/
_renderAndroidClearButton() {
if (Platform.OS !== 'android' || !this.state.fieldValue.length) {
_renderClearButton() {
if (!this.state.fieldValue.length) {
return null;
}
@@ -526,6 +587,24 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
);
}
/**
* Renders a button to share the meeting info.
*
* @returns {React#Element<*>}
*/
_renderShareMeetingButton() {
const { _headerStyles } = this.props;
return (
<TouchableOpacity
onPress = { this._onShareMeeting }>
<Icon
src = { IconShare }
style = { [ _headerStyles.headerButtonText, styles.shareIcon ] } />
</TouchableOpacity>
);
}
_setFieldRef: ?TextInput => void
/**
@@ -567,6 +646,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
function _mapStateToProps(state: Object) {
return {
..._abstractMapStateToProps(state),
_headerStyles: ColorSchemeRegistry.get(state, 'Header'),
_isVisible: state['features/invite'].inviteDialogVisible
};
}

View File

@@ -23,6 +23,12 @@ export default {
flex: 1
},
bottomBar: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-around'
},
clearButton: {
alignItems: 'center',
justifyContent: 'center',
@@ -52,6 +58,13 @@ export default {
justifyContent: 'flex-start'
},
/**
* A special padding to avoid issues on some devices (such as Android devices with custom suggestions bar).
*/
extraBarPadding: {
paddingBottom: 30
},
invitedList: {
padding: 3
},
@@ -124,6 +137,10 @@ export default {
width: ICON_SIZE + 16
},
shareIcon: {
fontSize: 42
},
unselectIcon: {
color: LIGHT_GREY,
fontSize: 16,

View File

@@ -130,7 +130,8 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
// but we want to indicate those in case of sip gateway
const {
iAmRecorder,
iAmSipGateway
iAmSipGateway,
disableRecordAudioNotification
} = getState()['features/base/config'];
if (iAmRecorder && !iAmSipGateway) {
@@ -153,6 +154,11 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const initiatorName = initiator && getParticipantDisplayName(getState, initiator.getId());
initiatorName && dispatch(showStartedRecordingNotification(mode, initiatorName));
sendAnalytics(createRecordingEvent('start', mode));
if (disableRecordAudioNotification) {
break;
}
let soundID;
@@ -163,7 +169,6 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
}
if (soundID) {
sendAnalytics(createRecordingEvent('start', mode));
dispatch(playSound(soundID));
}
} else if (updatedSessionData.status === OFF
@@ -176,6 +181,11 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
duration
= (Date.now() / 1000) - oldSessionData.timestamp;
}
sendAnalytics(createRecordingEvent('stop', mode, duration));
if (disableRecordAudioNotification) {
break;
}
if (mode === JitsiRecordingConstants.mode.FILE) {
soundOff = RECORDING_OFF_SOUND_ID;
@@ -186,7 +196,6 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
}
if (soundOff && soundOn) {
sendAnalytics(createRecordingEvent('stop', mode, duration));
dispatch(stopSound(soundOn));
dispatch(playSound(soundOff));
}

Some files were not shown because too many files have changed in this diff Show More