From 625d2683735ed92aa5665de89ec4947fd4db938e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=8F=D0=BD=20=D0=9C=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2?= Date: Tue, 2 Jul 2019 14:14:58 +0100 Subject: [PATCH] Room lock update (#4394) * Adds a notification when remote lock happens. * Updates translations. Removes unused strings and extracts room password to separate translation, to be able to change it when deployment uses only digits. * Formats the conference pin when showing it. * Removes member from translation in favour of participant. * Updates formatting of the pin. * Adds a notification when password is remotely removed. --- lang/main.json | 79 ++++++------------- react/features/invite/_utils.js | 28 +++++++ .../dial-in-summary/web/ConferenceID.js | 4 +- .../info-dialog/web/DialInNumber.js | 4 +- react/features/room-lock/middleware.js | 28 ++++++- 5 files changed, 84 insertions(+), 59 deletions(-) create mode 100644 react/features/invite/_utils.js diff --git a/lang/main.json b/lang/main.json index db11f654f..167c98f6b 100644 --- a/lang/main.json +++ b/lang/main.json @@ -4,7 +4,7 @@ "countryNotSupported": "We do not support this destination yet.", "countryReminder": "Calling outside the US? Please make sure you start with the country code!", "disabled": "You can't invite people.", - "failedToAdd": "Failed to add members", + "failedToAdd": "Failed to add participants", "footerText": "Dialing out is disabled.", "invite": "Invite", "loading": "Searching for people and phone numbers", @@ -112,7 +112,6 @@ "transport_plural": "Transports:", "turn": " (turn)" }, - "contactlist_plural": "__count__ Members", "dateUtils": { "earlier": "Earlier", "today": "Today", @@ -148,7 +147,7 @@ "liveStreaming": "Live Stream" }, "allow": "Allow", - "alreadySharedVideoMsg": "Another member is already sharing a video. This conference allows only one shared video at a time.", + "alreadySharedVideoMsg": "Another participant is already sharing a video. This conference allows only one shared video at a time.", "alreadySharedVideoTitle": "Only one shared video is allowed at a time", "applicationWindow": "Application window", "Back": "Back", @@ -173,7 +172,6 @@ "connecting": "Connecting", "contactSupport": "Contact support", "copy": "Copy", - "currentPassword": "The current password is", "defaultError": "There was some kind of error", "detectext": "Error when trying to detect desktopsharing extension.", "dismiss": "Dismiss", @@ -200,18 +198,18 @@ "kickMessage": "You can contact __participantDisplayName__ for more details.", "kickParticipantButton": "Kick", "kickParticipantDialog": "Are you sure you want to kick this participant?", - "kickParticipantTitle": "Kick this member?", + "kickParticipantTitle": "Kick this participant?", "kickTitle": "Ouch! __participantDisplayName__ kicked you out of the meeting", "liveStreaming": "Live Streaming", "liveStreamingDisabledForGuestTooltip": "Guests can't start live streaming.", "liveStreamingDisabledTooltip": "Start live stream disabled.", "lockMessage": "Failed to lock the conference.", - "lockRoom": "Add meeting password", + "lockRoom": "Add meeting $t(lockRoomPasswordUppercase)", "lockTitle": "Lock failed", "logoutQuestion": "Are you sure you want to logout and stop the conference?", "logoutTitle": "Logout", - "maxUsersLimitReached": "The limit for maximum number of members has been reached. The conference is full. Please contact the meeting owner or try again later!", - "maxUsersLimitReachedTitle": "Maximum members limit reached", + "maxUsersLimitReached": "The limit for maximum number of participants has been reached. The conference is full. Please contact the meeting owner or try again later!", + "maxUsersLimitReachedTitle": "Maximum participants limit reached", "micConstraintFailedError": "Your microphone does not satisfy some of the required constraints.", "micNotFoundError": "Microphone was not found.", "micNotSendingData": "Go to your computer's settings to unmute your mic and adjust its level", @@ -221,17 +219,13 @@ "muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.", "muteParticipantButton": "Mute", "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 member?", + "muteParticipantTitle": "Mute this participant?", "Ok": "Ok", "oops": "Oops!", - "password": "Enter password", - "passwordError": "This conversation is currently protected by a password. Only the owner of the conference can set a password.", - "passwordError2": "This conversation isn't currently protected by a password. Only the owner of the conference can set a password.", - "passwordErrorTitle": "Password Error", - "passwordLabel": "Password", - "passwordNotSupported": "Setting a meeting password is not supported.", - "passwordNotSupportedTitle": "Password not supported", - "passwordRequired": "Password required", + "passwordLabel": "$t(lockRoomPasswordUppercase)", + "passwordNotSupported": "Setting a meeting $t(lockRoomPassword) is not supported.", + "passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) not supported", + "passwordRequired": "$t(lockRoomPasswordUppercase) required", "permissionDenied": "Permission Denied", "popupError": "Your browser is blocking pop-up windows from this site. Please enable pop-ups in your browser's security settings and try again.", "popupErrorTitle": "Pop-up blocked", @@ -248,7 +242,7 @@ "remoteControlStopMessage": "The remote control session ended!", "remoteControlTitle": "Remote desktop control", "Remove": "Remove", - "removePassword": "Remove password", + "removePassword": "Remove $t(lockRoomPassword)", "removeSharedVideoMsg": "Are you sure you would like to remove your shared video?", "removeSharedVideoTitle": "Remove shared video", "reservationError": "Reservation system error", @@ -286,7 +280,7 @@ "tokenAuthFailedTitle": "Authentication failed", "transcribing": "Transcribing", "unableToSwitch": "Unable to switch video stream.", - "unlockRoom": "Remove meeting password", + "unlockRoom": "Remove meeting $t(lockRoomPassword)", "userPassword": "user password", "WaitForHostMsg": "The conference __room__ has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.", "WaitForHostMsgWOk": "The conference __room__ has not yet started. If you are the host then please press Ok to authenticate. Otherwise, please wait for the host to arrive.", @@ -298,34 +292,6 @@ "dialOut": { "statusMessage": "is now __status__" }, - "email": { - "and": "and", - "body": [ - " Note that __appName__ is currently only supported by __supportedBrowsers__, so you need to be using one of these browsers.", - "", - "", - "", - "", - "", - "", - "", - "", - "Hey there, I%27d like to invite you to a __appName__ conference I%27ve just set up.", - "Please click on the following link in order to join the conference.", - "Talk to you in a sec!", - "__roomUrl__", - "__sharedKeyText__" - ], - "sharedKey": [ - "", - "", - "", - "", - "This conference is password-protected. Please use the following pin when joining:", - "__sharedKey__" - ], - "subject": "Invitation to a __appName__ (__conferenceName__)" - }, "feedback": { "average": "Average", "bad": "Bad", @@ -344,8 +310,8 @@ }, "info": { "accessibilityLabel": "Show info", - "addPassword": "Add password", - "cancelPassword": "Cancel password", + "addPassword": "Add $t(lockRoomPassword)", + "cancelPassword": "Cancel $t(lockRoomPassword)", "conferenceURL": "Link:", "country": "Country", "dialANumber": "To join your meeting, dial one of these numbers and then enter the pin.", @@ -367,7 +333,7 @@ "noPassword": "None", "noRoom": "No room was specified to dial-in into.", "numbers": "Dial-in Numbers", - "password": "Password:", + "password": "$t(lockRoomPasswordUppercase):", "title": "Share", "tooltip": "Share link and dial-in info for this meeting", "label": "Meeting info" @@ -463,6 +429,8 @@ "stop": "Stop Recording", "yes": "Yes" }, + "lockRoomPassword": "password", + "lockRoomPasswordUppercase": "Password", "me": "me", "notify": { "connectedOneMember": "__name__ joined the meeting", @@ -482,6 +450,8 @@ "mutedTitle": "You're muted!", "mutedRemotelyTitle": "You have been muted by __participantDisplayName__!", "mutedRemotelyDescription": "You can always unmute when you're ready to speak. Mute back when you're done to keep noise away from the meeting.", + "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removed by another participant", + "passwordSetRemotely": "$t(lockRoomPasswordUppercase) set by another participant", "raisedHand": "__name__ would like to speak.", "somebody": "Somebody", "startSilentTitle": "You joined with no audio output!", @@ -493,7 +463,7 @@ "newDeviceAudioTitle": "New audio device detected", "newDeviceAction": "Use" }, - "passwordSetRemotely": "set by another member", + "passwordSetRemotely": "set by another participant", "passwordDigitsOnly": "Up to __number__ digits", "poweredby": "powered by", "presenceStatus": { @@ -568,7 +538,6 @@ "more": "More", "name": "Name", "noDevice": "None", - "password": "SET PASSWORD", "selectAudioOutput": "Audio output", "selectCamera": "Camera", "selectMic": "Microphone", @@ -685,7 +654,7 @@ "raiseYourHand": "Raise your hand", "Settings": "Settings", "sharedvideo": "Share a YouTube video", - "sharedVideoMutedPopup": "Your shared video has been muted so that you can talk to the other members.", + "sharedVideoMutedPopup": "Your shared video has been muted so that you can talk to the other participants.", "shareRoom": "Invite someone", "shortcuts": "View shortcuts", "sip": "Call SIP number", @@ -765,11 +734,11 @@ "flip": "Flip", "kick": "Kick out", "moderator": "Moderator", - "mute": "Member is muted", + "mute": "Participant is muted", "muted": "Muted", "remoteControl": "Remote control", "show": "Show on stage", - "videomute": "Member has stopped the camera" + "videomute": "Participant has stopped the camera" }, "welcomepage": { "accessibilityLabel": { diff --git a/react/features/invite/_utils.js b/react/features/invite/_utils.js new file mode 100644 index 000000000..adc0dbeff --- /dev/null +++ b/react/features/invite/_utils.js @@ -0,0 +1,28 @@ +// @flow + +/** + * Utility class with no dependencies. Used in components that are stripped in separate bundles + * and requires as less dependencies as possible. + */ + +/** + * Formats the conference pin in readable way for UI to display it. + * Formats the pin in 3 groups of digits: + * XXXX XXXX XX or XXXXX XXXXX XXX. + * The length of first and second group is Math.ceil(pin.length / 3) + * + * @param {Object} conferenceID - The conference id to format, string or number. + * @returns {string} - The formatted conference pin. + * @private + */ +export function _formatConferenceIDPin(conferenceID: Object) { + const conferenceIDStr = conferenceID.toString(); + + // let's split the conferenceID in 3 parts, to be easier to read + const partLen = Math.ceil(conferenceIDStr.length / 3); + + return `${ + conferenceIDStr.substring(0, partLen)} ${ + conferenceIDStr.substring(partLen, 2 * partLen)} ${ + conferenceIDStr.substring(2 * partLen, conferenceIDStr.length)}`; +} diff --git a/react/features/invite/components/dial-in-summary/web/ConferenceID.js b/react/features/invite/components/dial-in-summary/web/ConferenceID.js index 4227cc0ba..916e95d68 100644 --- a/react/features/invite/components/dial-in-summary/web/ConferenceID.js +++ b/react/features/invite/components/dial-in-summary/web/ConferenceID.js @@ -4,6 +4,8 @@ import React, { Component } from 'react'; import { translate } from '../../../../base/i18n'; +import { _formatConferenceIDPin } from '../../../_utils'; + /** * The type of the React {@code Component} props of {@link ConferenceID}. */ @@ -49,7 +51,7 @@ class ConferenceID extends Component { { t('info.dialANumber') }
- { `${t('info.dialInConferenceID')} ${conferenceID}` } + { `${t('info.dialInConferenceID')} ${_formatConferenceIDPin(conferenceID)}` }
); diff --git a/react/features/invite/components/info-dialog/web/DialInNumber.js b/react/features/invite/components/info-dialog/web/DialInNumber.js index f8dc205c2..f76f9fb88 100644 --- a/react/features/invite/components/info-dialog/web/DialInNumber.js +++ b/react/features/invite/components/info-dialog/web/DialInNumber.js @@ -4,6 +4,8 @@ import React, { Component } from 'react'; import { translate } from '../../../../base/i18n'; +import { _formatConferenceIDPin } from '../../../_utils'; + /** * The type of the React {@code Component} props of {@link DialInNumber}. */ @@ -61,7 +63,7 @@ class DialInNumber extends Component {   - { `${conferenceID}#` } + { `${_formatConferenceIDPin(conferenceID)}#` } diff --git a/react/features/room-lock/middleware.js b/react/features/room-lock/middleware.js index 345c073b4..34cb36827 100644 --- a/react/features/room-lock/middleware.js +++ b/react/features/room-lock/middleware.js @@ -8,10 +8,15 @@ import { import { hideDialog } from '../base/dialog'; import { JitsiConferenceErrors } from '../base/lib-jitsi-meet'; import { MiddlewareRegistry } from '../base/redux'; +import { + NOTIFICATION_TIMEOUT, + showNotification +} from '../notifications'; import UIEvents from '../../../service/UI/UIEvents'; import { _openPasswordRequiredPrompt } from './actions'; import { PasswordRequiredPrompt, RoomLockPrompt } from './components'; +import { LOCKED_REMOTELY } from './constants'; declare var APP: Object; @@ -29,14 +34,33 @@ MiddlewareRegistry.register(store => next => action => { case CONFERENCE_FAILED: return _conferenceFailed(store, next, action); - case LOCK_STATE_CHANGED: + case LOCK_STATE_CHANGED: { // TODO Remove this logic when all components interested in the lock // state change event are moved into react/redux. if (typeof APP !== 'undefined') { APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK, action.locked); } - break; + const previousLockedState = store.getState()['features/base/conference'].locked; + + const result = next(action); + + const currentLockedState = store.getState()['features/base/conference'].locked; + + if (currentLockedState === LOCKED_REMOTELY) { + store.dispatch( + showNotification({ + titleKey: 'notify.passwordSetRemotely' + }, NOTIFICATION_TIMEOUT)); + } else if (previousLockedState === LOCKED_REMOTELY && !currentLockedState) { + store.dispatch( + showNotification({ + titleKey: 'notify.passwordRemovedRemotely' + }, NOTIFICATION_TIMEOUT)); + } + + return result; + } case SET_PASSWORD_FAILED: return _setPasswordFailed(store, next, action); }