From 29dc63fbcbb3e81e0573d7852181c864b1ba55f6 Mon Sep 17 00:00:00 2001 From: Bettenbuk Zoltan Date: Wed, 20 May 2020 10:25:31 +0200 Subject: [PATCH] ref: merge prejoin with lobby --- css/_lobby.scss | 125 +++-------- css/_prejoin.scss | 160 +-------------- css/_premeeting-screens.scss | 194 ++++++++++++++++++ css/main.scss | 1 + css/modals/security/_security.scss | 52 +++-- interface_config.js | 2 +- lang/main.json | 15 +- react/features/base/conference/actions.js | 34 ++- react/features/base/conference/middleware.js | 9 +- react/features/base/conference/reducer.js | 52 +++-- react/features/base/connection/actionTypes.js | 3 +- .../base/connection/actions.native.js | 14 +- react/features/base/dialog/actions.js | 5 +- .../components/AbstractDialogContainer.js | 14 +- .../dialog/components/web/DialogContainer.js | 4 + react/features/base/dialog/reducer.js | 6 +- react/features/base/icons/svg/copy.svg | 4 +- react/features/base/icons/svg/index.js | 2 +- .../premeeting/components/index.native.js | 0 .../base/premeeting/components/index.web.js | 3 + .../premeeting/components/web/ActionButton.js | 77 +++++++ .../components/web}/CopyMeetingUrl.js | 47 ++--- .../premeeting/components/web/InputField.js | 175 ++++++++++++++++ .../components/web/PreMeetingScreen.js | 73 +++++++ .../base/premeeting/components/web/Preview.js | 73 +++++++ .../base/premeeting/components/web/index.js | 5 + react/features/base/premeeting/index.js | 3 + react/features/base/premeeting/logger.js | 5 + .../conference/components/web/Conference.js | 4 +- react/features/lobby/actionTypes.js | 5 + react/features/lobby/actions.native.js | 25 +++ .../lobby/{actions.js => actions.web.js} | 126 +++++++----- .../AbstractDisableLobbyModeDialog.js | 47 ----- .../AbstractEnableLobbyModeDialog.js | 75 ------- .../AbstractKnockingParticipantList.js | 19 +- .../lobby/components/AbstractLobbyScreen.js | 83 ++++++-- .../features/lobby/components/index.native.js | 2 - react/features/lobby/components/index.web.js | 2 - .../native/DisableLobbyModeDialog.js | 36 +++- .../native/EnableLobbyModeDialog.js | 59 +++--- .../native/KnockingParticipantList.js | 33 ++- .../{ => native}/LobbyModeButton.js | 15 +- .../lobby/components/native/LobbyScreen.js | 19 +- .../features/lobby/components/native/index.js | 1 + .../lobby/components/native/styles.js | 11 +- .../components/web/DisableLobbyModeDialog.js | 36 ---- .../components/web/EnableLobbyModeDialog.js | 51 ----- .../components/web/KnockingParticipantList.js | 31 ++- .../lobby/components/web/LobbyScreen.js | 145 +++++-------- .../lobby/components/web/LobbySection.js | 136 ++++++++++++ react/features/lobby/components/web/index.js | 3 +- react/features/lobby/functions.js | 20 +- react/features/lobby/middleware.js | 46 +++-- react/features/lobby/reducer.js | 25 ++- react/features/overlay/middleware.js | 4 +- react/features/prejoin/components/Prejoin.js | 77 +++---- .../components/buttons/ActionButton.js | 88 -------- .../components/dialogs/DialInDialog.js | 2 +- .../components/dialogs/DialOutDialog.js | 2 +- .../components/preview/DeviceStatus.js | 4 +- .../components/preview/ParticipantName.js | 110 ---------- .../prejoin/components/preview/Preview.js | 76 ------- react/features/prejoin/functions.js | 3 +- .../security-dialog/PasswordSection.js | 29 +-- .../security-dialog/SecurityDialog.js | 8 +- .../security-dialog/SecurityDialogButton.js | 3 +- .../toolbox/components/native/OverflowMenu.js | 2 +- .../toolbox/components/web/Toolbox.js | 6 - 68 files changed, 1415 insertions(+), 1211 deletions(-) create mode 100644 css/_premeeting-screens.scss create mode 100644 react/features/base/premeeting/components/index.native.js create mode 100644 react/features/base/premeeting/components/index.web.js create mode 100644 react/features/base/premeeting/components/web/ActionButton.js rename react/features/{prejoin/components/preview => base/premeeting/components/web}/CopyMeetingUrl.js (74%) create mode 100644 react/features/base/premeeting/components/web/InputField.js create mode 100644 react/features/base/premeeting/components/web/PreMeetingScreen.js create mode 100644 react/features/base/premeeting/components/web/Preview.js create mode 100644 react/features/base/premeeting/components/web/index.js create mode 100644 react/features/base/premeeting/index.js create mode 100644 react/features/base/premeeting/logger.js create mode 100644 react/features/lobby/actions.native.js rename react/features/lobby/{actions.js => actions.web.js} (72%) delete mode 100644 react/features/lobby/components/AbstractDisableLobbyModeDialog.js delete mode 100644 react/features/lobby/components/AbstractEnableLobbyModeDialog.js rename react/features/lobby/components/{ => native}/LobbyModeButton.js (80%) delete mode 100644 react/features/lobby/components/web/DisableLobbyModeDialog.js delete mode 100644 react/features/lobby/components/web/EnableLobbyModeDialog.js create mode 100644 react/features/lobby/components/web/LobbySection.js delete mode 100644 react/features/prejoin/components/buttons/ActionButton.js delete mode 100644 react/features/prejoin/components/preview/ParticipantName.js delete mode 100644 react/features/prejoin/components/preview/Preview.js diff --git a/css/_lobby.scss b/css/_lobby.scss index 9f7249c3ec28..5744e5622bb8 100644 --- a/css/_lobby.scss +++ b/css/_lobby.scss @@ -1,115 +1,48 @@ #lobby-screen { - align-items: center; - color: $overflowMenuItemColor; - display: flex; - flex-direction: column; - font-size: 1.2em; - margin: 48px 36px; - - span { - padding: 8px 0; - } - - .title { - color: $defaultColor; - font-size: 2em; - } - - .roomName { - font-size: 1em; - } + .content { - .participantInfo { - align-items: center; - align-self: stretch; - border: 1px solid #B8C7E0; - border-radius: 4px; - display: flex; - flex-direction: column; - margin: 24px 0; - padding: 34px 0; - - &:hover { - padding-top: 0px; + .container { + align-items: center; + display: flex; + flex-direction: column; - .editButton { - display: flex; + .spinner { + margin: 30px; } - } - - .editButton { - align-self: stretch; - display: none; - justify-content: flex-end; - padding: 5px; - position: relative; - - button { - background-color: transparent; - border-width: 0; - margin: 0; - padding: 0; + + .joining-message { + margin: 10px; } } - .displayName { - color: $defaultColor; - font-size: 1.3em; - } - } - - .form { - align-self: stretch; - display: flex; - flex-direction: column; - margin: 32px 0; - - input { - margin: 5px 0 15px 0; - } - - span { - color: white; - font-size: 1.3em; - text-align: center; + .form { + align-items: stretch; + display: flex; + flex-direction: column; + min-width: 400px; } - } - - .joiningContainer { - align-items: center; - display: flex; - flex-direction: column; - margin: 36px 0; - span { - margin-top: 36px; - text-align: center; + .participant-info { + align-items: center; + display: flex; + flex-direction: column; } } } -#lobby-dialog { - align-self: stretch; +#lobby-section { display: flex; flex-direction: column; - margin: 32px 0; - - .description { - margin-bottom: 18px; - } - .field { + .control-row { display: flex; flex-direction: row; + justify-content: space-between; + margin-top: 15px; - :first-child { - align-items: center; - display: flex; - padding-right: 15px; - } - - :last-child { - flex: 1; + label { + font-size: 14px; + font-weight: bold; } } } @@ -162,11 +95,7 @@ } } } -} -// Common styles - -#lobby-dialog, #lobby-screen, #knocking-participant-list { input { align-self: stretch; background-color: transparent; @@ -208,4 +137,4 @@ border-width: 0; } } -} \ No newline at end of file +} diff --git a/css/_prejoin.scss b/css/_prejoin.scss index e98a30623bcf..52a34be64706 100644 --- a/css/_prejoin.scss +++ b/css/_prejoin.scss @@ -1,18 +1,4 @@ .prejoin { - &-full-page { - background: #1C2025; - position: absolute; - width: 100%; - height: 100%; - z-index: $toolbarZ + 1; - } - - &-input-area-container { - position: absolute; - bottom: 48px; - width: 100%; - z-index: 2; - } &-input-area { margin: 0 auto; @@ -27,65 +13,6 @@ margin-bottom: 16px; } - &-btn { - border-radius: 3px; - color: #fff; - cursor: pointer; - display: inline-block; - font-size: 15px; - line-height: 24px; - padding: 7px 16px; - position: relative; - text-align: center; - width: 286px; - - &--primary { - background: #0376DA; - border: 1px solid #0376DA; - } - - &--secondary { - background: #2A3A4B; - border: 1px solid #5E6D7A; - } - - &--text { - width: auto; - font-size: 13px; - margin: 0; - padding: 0; - } - - &--disabled { - background: #5E6D7A; - border: 1px solid #5E6D7A; - color: #AFB6BC; - cursor: initial; - - .prejoin-btn-icon { - & > svg { - fill: #AFB6BC; - } - } - - .prejoin-btn-options { - border-left: 1px solid #AFB6BC; - } - } - } - - &-btn-options { - align-items: center; - border-left: 1px solid #fff; - display: flex; - height: 100%; - justify-content: center; - position: absolute; - right: 0; - top: 0; - width: 40px; - } - &-text-btns { display: flex; justify-content: space-between; @@ -179,25 +106,6 @@ margin: 200px auto 0 auto; } - &-btn-container { - display: flex; - justify-content: center; - margin-top: 32px; - width: 100%; - - &> div { - margin: 0 12px; - } - - .settings-button-small-icon { - right: -8px; - - &--hovered { - right: -10px; - } - } - } - &-overlay { height: 100%; position: absolute; @@ -217,22 +125,20 @@ &-status { align-items: center; - bottom: 0; + align-self: stretch; color: #fff; display: flex; font-size: 13px; min-height: 24px; justify-content: center; - position: absolute; text-align: center; - width: 100%; z-index: 1; &--warning { - background: rgba(241, 173, 51, 0.5) + background: rgba(241, 173, 51, 0.7) } &--ok { - background: rgba(49, 183, 106, 0.5); + background: rgba(49, 183, 106, 0.7); } } @@ -291,63 +197,3 @@ } } - -.prejoin-copy { - &-meeting { - cursor: pointer; - color: #fff; - font-size: 15px; - font-weight: 300; - line-height: 24px; - position: relative; - } - - &-url { - max-width: 278px; - padding: 8px 10px; - overflow: hidden; - text-overflow: ellipsis; - } - - &-badge { - border-radius: 4px; - height: 100%; - line-height: 38px; - position: absolute; - padding-left: 10px; - text-align: left; - top: 0; - width: 100%; - - &--hover { - background: #1C2025; - } - - &--done { - background: #31B76A; - } - } - - &-icon { - position: absolute; - right: 8px; - top: 8px; - - &--white { - &> svg > path { - fill: #fff - } - } - - &--light { - &> svg > path { - fill: #D1DBE8; - } - } - } - - &-textarea { - position: absolute; - left: -9999px; - } -} diff --git a/css/_premeeting-screens.scss b/css/_premeeting-screens.scss new file mode 100644 index 000000000000..32d207c0c61a --- /dev/null +++ b/css/_premeeting-screens.scss @@ -0,0 +1,194 @@ +/** + * Shared style for full screen local track based dialogs/modals. + */ + .premeeting-screen { + align-items: stretch; + background: #1C2025; + bottom: 0; + display: flex; + flex-direction: column; + font-size: 1.3em; + left: 0; + position: absolute; + right: 0; + top: 0; + z-index: $toolbarZ + 1; + + .content { + align-items: center; + background-image: linear-gradient(transparent, black); + display: flex; + flex: 1; + flex-direction: column; + justify-content: flex-end; + z-index: $toolbarZ + 2; + + .title { + color: #fff; + font-size: 24px; + line-height: 32px; + margin-bottom: 16px; + } + + .copy-meeting { + align-items: center; + cursor: pointer; + color: #fff; + display: flex; + flex-direction: row; + font-size: 15px; + font-weight: 300; + justify-content: center; + line-height: 24px; + + .url { + display: flex; + padding: 8px 10px; + + &:hover { + background: #1C2025; + border-radius: 4px; + } + + &.done { + background: #31B76A; + } + + .jitsi-icon { + margin-left: 10px; + } + } + + &:hover { + align-self: stretch; + } + + textarea { + border-width: 0; + height: 0; + opacity: 0; + padding: 0; + width: 0; + } + } + + input.field { + background-color: transparent; + border: 1px solid transparent; + color: white; + outline-width: 0; + padding: 20px; + text-align: center; + + &.focused { + border-bottom: 1px solid white; + } + + &.error::placeholder { + color: $defaultWarningColor; + } + } + + .action-btn { + border-radius: 3px; + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 15px; + line-height: 24px; + margin: 10px; + padding: 7px 16px; + position: relative; + text-align: center; + width: 286px; + + &.primary { + background: #0376DA; + border: 1px solid #0376DA; + } + + &.secondary { + background: transparent; + border: 1px solid #5E6D7A; + } + + &.text { + width: auto; + font-size: 13px; + margin: 0; + padding: 0; + } + + &.disabled { + background: #5E6D7A; + border: 1px solid #5E6D7A; + color: #AFB6BC; + cursor: initial; + + .icon { + & > svg { + fill: #AFB6BC; + } + } + + .options { + border-left: 1px solid #AFB6BC; + } + } + + .options { + align-items: center; + border-left: 1px solid #fff; + display: flex; + height: 100%; + justify-content: center; + position: absolute; + right: 0; + top: 0; + width: 40px; + } + } + } + + .media-btn-container { + display: flex; + justify-content: center; + margin: 32px 0; + width: 100%; + + &> div { + margin: 0 12px; + } + + .settings-button-small-icon { + right: -8px; + + &--hovered { + right: -10px; + } + } + } +} + +#preview { + height: 100%; + position: absolute; + width: 100%; + + &.no-video { + background: radial-gradient(50% 50% at 50% 50%, #5B6F80 0%, #365067 100%), #FFFFFF; + text-align: center; + } + + .avatar { + background: #A4B8D1; + margin: 200px auto 0 auto; + } + + video { + height: 100%; + object-fit: cover; + position: absolute; + width: 100%; + } +} \ No newline at end of file diff --git a/css/main.scss b/css/main.scss index ad61723a45e9..8dded2d6f7ae 100644 --- a/css/main.scss +++ b/css/main.scss @@ -97,5 +97,6 @@ $flagsImagePath: "../images/"; @import 'country-picker'; @import 'modals/invite/invite_more'; @import 'modals/security/security'; +@import 'premeeting-screens'; /* Modules END */ diff --git a/css/modals/security/_security.scss b/css/modals/security/_security.scss index 7c1eb41085ec..50f22997e1a0 100644 --- a/css/modals/security/_security.scss +++ b/css/modals/security/_security.scss @@ -3,25 +3,47 @@ color: #fff; font-size: 15px; line-height: 24px; - - &.password { + + &.password-section { display: flex; - justify-content: space-between; - align-items: center; - - &-actions { - a { - cursor: pointer; - text-decoration: none; - font-size: 14px; - color: #6FB1EA; - } + flex-direction: column; + + .password { + align-items: center; + display: flex; + justify-content: space-between; + margin-top: 15px; - & > :first-child:not(:last-child) { - margin-right: 24px; + &-actions { + a { + cursor: pointer; + text-decoration: none; + font-size: 14px; + color: #6FB1EA; + } + + &>a+a { + margin-left: 24px; + } } } } + + &> :first-child:not(:last-child) { + margin-right: 24px; + } + + .separator-line { + margin: 24px 0 24px -20px; + padding: 0 20px; + width: 100%; + height: 1px; + background: #5E6D7A; + + &:last-child { + display: none; + } + } } } @@ -34,4 +56,4 @@ background: rgba(241, 173, 51, 0.7); border: 1px solid rgba(255, 255, 255, 0.4); } -} +} \ No newline at end of file diff --git a/interface_config.js b/interface_config.js index bd82972db04b..6081cc29da6d 100644 --- a/interface_config.js +++ b/interface_config.js @@ -48,7 +48,7 @@ var interfaceConfig = { */ TOOLBAR_BUTTONS: [ 'microphone', 'camera', 'closedcaptions', 'desktop', 'fullscreen', - 'fodeviceselection', 'hangup', 'lobby', 'profile', 'chat', 'recording', + 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording', 'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand', 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts', 'tileview', 'videobackgroundblur', 'download', 'help', 'mute-everyone', diff --git a/lang/main.json b/lang/main.json index f7e197b30e71..1f3e40db56cf 100644 --- a/lang/main.json +++ b/lang/main.json @@ -867,26 +867,29 @@ }, "lobby": { "allow": "Allow", - "backToKnockModeButton": "No password, knock instead", + "backToKnockModeButton": "No password, ask to join instead", "dialogTitle": "Lobby mode", "disableDialogContent": "Lobby mode is currently enabled. This feature ensures that unwanted participants can't join your meeting. Do you want to disable it?", "disableDialogSubmit": "Disable", "emailField": "Enter your email address", "enableDialogPasswordField": "Set password (optional)", "enableDialogSubmit": "Enable", - "enableDialogText": "Lobby mode lets you protect your meeting by only allowing people to enter after a formal approve of a moderator or by entering an optional predefined password.", + "enableDialogText": "Lobby mode lets you protect your meeting by only allowing people to enter after a formal approval by a moderator.", "enterPasswordButton": "Enter meeting password", + "enterPasswordTitle": "Enter password to join meeting", + "invalidPassword": "Invalid password", "joiningMessage": "You'll join the meeting as soon as someone accepts your request", "joinWithPasswordMessage": "Trying to join with password, please wait...", "joinRejectedMessage": "Your join request was rejected by a moderator.", "joinTitle": "Join Meeting", - "joiningTitle": "Asking to join", - "joiningWithPasswordTitle": "Joining", + "joiningTitle": "Asking to join meeting...", + "joiningWithPasswordTitle": "Joining with password...", "knockButton": "Ask to Join", "knockTitle": "Someone wants to join the meeting", "nameField": "Enter your name", - "passwordField": "Enter password", + "passwordField": "Enter meeting password", "passwordJoinButton": "Join", - "reject": "Reject" + "reject": "Reject", + "toggleLabel": "Enable lobby" } } diff --git a/react/features/base/conference/actions.js b/react/features/base/conference/actions.js index 4070b943ebee..edfae3ee96cc 100644 --- a/react/features/base/conference/actions.js +++ b/react/features/base/conference/actions.js @@ -249,6 +249,7 @@ export function authStatusChanged(authEnabled: boolean, authLogin: string) { * @param {JitsiConference} conference - The JitsiConference that has failed. * @param {string} error - The error describing/detailing the cause of the * failure. + * @param {any} params - Rest of the params that we receive together with the event. * @returns {{ * type: CONFERENCE_FAILED, * conference: JitsiConference, @@ -651,28 +652,23 @@ export function setPassword( case conference.join: { let state = getState()['features/base/conference']; - // Make sure that the action will set a password for a conference - // that the application wants joined. - if (state.passwordRequired === conference) { - dispatch({ - type: SET_PASSWORD, - conference, - method, - password - }); + dispatch({ + type: SET_PASSWORD, + conference, + method, + password + }); - // Join the conference with the newly-set password. + // Join the conference with the newly-set password. - // Make sure that the action did set the password. - state = getState()['features/base/conference']; - if (state.password === password - && !state.passwordRequired + // Make sure that the action did set the password. + state = getState()['features/base/conference']; + if (state.password === password - // Make sure that the application still wants the - // conference joined. - && !state.conference) { - method.call(conference, password); - } + // Make sure that the application still wants the + // conference joined. + && !state.conference) { + method.call(conference, password); } break; } diff --git a/react/features/base/conference/middleware.js b/react/features/base/conference/middleware.js index 11e46869e2fa..20e5114e90c8 100644 --- a/react/features/base/conference/middleware.js +++ b/react/features/base/conference/middleware.js @@ -145,10 +145,6 @@ function _conferenceFailed({ dispatch, getState }, next, action) { const result = next(action); const { conference, error } = action; - if (error.name === JitsiConferenceErrors.OFFER_ANSWER_FAILED) { - sendAnalytics(createOfferAnswerFailedEvent()); - } - // Handle specific failure reasons. switch (error.name) { case JitsiConferenceErrors.CONFERENCE_DESTROYED: { @@ -167,7 +163,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) { case JitsiConferenceErrors.CONNECTION_ERROR: { const [ msg ] = error.params; - dispatch(connectionDisconnected(getState()['features/base/connection'].connection, 'Disconnected')); + dispatch(connectionDisconnected(getState()['features/base/connection'].connection)); dispatch(showErrorNotification({ descriptionArguments: { msg }, descriptionKey: msg ? 'dialog.connectErrorWithMsg' : 'dialog.connectError', @@ -176,6 +172,9 @@ function _conferenceFailed({ dispatch, getState }, next, action) { break; } + case JitsiConferenceErrors.OFFER_ANSWER_FAILED: + sendAnalytics(createOfferAnswerFailedEvent()); + break; } // FIXME: Workaround for the web version. Currently, the creation of the diff --git a/react/features/base/conference/reducer.js b/react/features/base/conference/reducer.js index 3903ce0fd8fd..b1f850b6c6bf 100644 --- a/react/features/base/conference/reducer.js +++ b/react/features/base/conference/reducer.js @@ -387,34 +387,30 @@ function _setDesktopSharingEnabled(state, action) { function _setPassword(state, { conference, method, password }) { switch (method) { case conference.join: - if (state.passwordRequired === conference) { - return assign(state, { - // XXX 1. The JitsiConference which transitions away from - // passwordRequired MUST remain in the redux state - // features/base/conference until it transitions into - // conference; otherwise, there is a span of time during which - // the redux state does not even know that there is a - // JitsiConference whatsoever. - // - // 2. The redux action setPassword will attempt to join the - // JitsiConference so joining is an appropriate transitional - // redux state. - // - // 3. The redux action setPassword will perform the same check - // before it proceeds with the re-join. - joining: state.conference ? state.joining : conference, - locked: LOCKED_REMOTELY, - - /** - * The password with which the conference is to be joined. - * - * @type {string} - */ - password, - passwordRequired: undefined - }); - } - break; + return assign(state, { + // 1. The JitsiConference which transitions away from + // passwordRequired MUST remain in the redux state + // features/base/conference until it transitions into + // conference; otherwise, there is a span of time during which + // the redux state does not even know that there is a + // JitsiConference whatsoever. + // + // 2. The redux action setPassword will attempt to join the + // JitsiConference so joining is an appropriate transitional + // redux state. + // + // 3. The redux action setPassword will perform the same check + // before it proceeds with the re-join. + joining: state.conference ? state.joining : conference, + locked: LOCKED_REMOTELY, + + /** + * The password with which the conference is to be joined. + * + * @type {string} + */ + password + }); case conference.lock: return assign(state, { diff --git a/react/features/base/connection/actionTypes.js b/react/features/base/connection/actionTypes.js index 3b5d3fa2e166..ab61df68c8c4 100644 --- a/react/features/base/connection/actionTypes.js +++ b/react/features/base/connection/actionTypes.js @@ -5,8 +5,7 @@ * * { * type: CONNECTION_DISCONNECTED, - * connection: JitsiConnection, - * message: string + * connection: JitsiConnection * } */ export const CONNECTION_DISCONNECTED = 'CONNECTION_DISCONNECTED'; diff --git a/react/features/base/connection/actions.native.js b/react/features/base/connection/actions.native.js index f9117d7af997..1aceb3f31c64 100644 --- a/react/features/base/connection/actions.native.js +++ b/react/features/base/connection/actions.native.js @@ -113,13 +113,12 @@ export function connect(id: ?string, password: ?string) { * Dispatches {@code CONNECTION_DISCONNECTED} action when connection is * disconnected. * - * @param {string} message - Disconnect reason. * @private * @returns {void} */ - function _onConnectionDisconnected(message: string) { + function _onConnectionDisconnected() { unsubscribe(); - dispatch(connectionDisconnected(connection, message)); + dispatch(connectionDisconnected(connection)); } /** @@ -187,19 +186,16 @@ export function connect(id: ?string, password: ?string) { * * @param {JitsiConnection} connection - The {@code JitsiConnection} which * disconnected. - * @param {string} message - Error message. * @private * @returns {{ * type: CONNECTION_DISCONNECTED, - * connection: JitsiConnection, - * message: string + * connection: JitsiConnection * }} */ -export function connectionDisconnected(connection: Object, message: string) { +export function connectionDisconnected(connection: Object) { return { type: CONNECTION_DISCONNECTED, - connection, - message + connection }; } diff --git a/react/features/base/dialog/actions.js b/react/features/base/dialog/actions.js index 31075f028c27..823f00ec027c 100644 --- a/react/features/base/dialog/actions.js +++ b/react/features/base/dialog/actions.js @@ -30,14 +30,17 @@ export function hideDialog(component: ?Object) { * @param {Object} component - The component to display as dialog. * @param {Object} [componentProps] - The React {@code Component} props of the * specified {@code component}. + * @param {boolean} rawDialog - True if the dialog is a raw dialog. + * (Doesn't inherit behavior from other common frameworks). * @returns {{ * type: OPEN_DIALOG, * component: React.Component, * componentProps: (Object | undefined) * }} */ -export function openDialog(component: Object, componentProps: ?Object) { +export function openDialog(component: Object, componentProps: ?Object, rawDialog?: boolean) { return { + rawDialog, type: OPEN_DIALOG, component, componentProps diff --git a/react/features/base/dialog/components/AbstractDialogContainer.js b/react/features/base/dialog/components/AbstractDialogContainer.js index 6f04f2ff9b0c..edbf102cf818 100644 --- a/react/features/base/dialog/components/AbstractDialogContainer.js +++ b/react/features/base/dialog/components/AbstractDialogContainer.js @@ -17,6 +17,11 @@ type Props = { */ _componentProps: Object, + /** + * True if the dialog is a raw dialog (doesn't inherit behavior from other common frameworks, such as atlaskit). + */ + _rawDialog: boolean, + /** * True if the UI is in a compact state where we don't show dialogs. */ @@ -52,19 +57,16 @@ export default class AbstractDialogContainer extends Component { * * @param {Object} state - The redux state. * @private - * @returns {{ - * _component: React.Component, - * _componentProps: Object, - * _reducedUI: boolean - * }} + * @returns {Props} */ -export function abstractMapStateToProps(state: Object) { +export function abstractMapStateToProps(state: Object): $Shape { const stateFeaturesBaseDialog = state['features/base/dialog']; const { reducedUI } = state['features/base/responsive-ui']; return { _component: stateFeaturesBaseDialog.component, _componentProps: stateFeaturesBaseDialog.componentProps, + _rawDialog: stateFeaturesBaseDialog.rawDialog, _reducedUI: reducedUI }; } diff --git a/react/features/base/dialog/components/web/DialogContainer.js b/react/features/base/dialog/components/web/DialogContainer.js index 05342dd7d5e0..4aa5c3bc21ff 100644 --- a/react/features/base/dialog/components/web/DialogContainer.js +++ b/react/features/base/dialog/components/web/DialogContainer.js @@ -20,6 +20,10 @@ class DialogContainer extends AbstractDialogContainer { * @returns {ReactElement} */ render() { + if (this.props._rawDialog) { + return this._renderDialogContent(); + } + return ( { this._renderDialogContent() } diff --git a/react/features/base/dialog/reducer.js b/react/features/base/dialog/reducer.js index cafde62be8fc..20d04f5981b4 100644 --- a/react/features/base/dialog/reducer.js +++ b/react/features/base/dialog/reducer.js @@ -21,7 +21,8 @@ ReducerRegistry.register('features/base/dialog', (state = {}, action) => { if (typeof component === 'undefined' || state.component === component) { return assign(state, { component: undefined, - componentProps: undefined + componentProps: undefined, + rawDialog: false }); } break; @@ -30,7 +31,8 @@ ReducerRegistry.register('features/base/dialog', (state = {}, action) => { case OPEN_DIALOG: return assign(state, { component: action.component, - componentProps: action.componentProps + componentProps: action.componentProps, + rawDialog: action.rawDialog }); } diff --git a/react/features/base/icons/svg/copy.svg b/react/features/base/icons/svg/copy.svg index 3d9672ea7841..b87e5e80b283 100644 --- a/react/features/base/icons/svg/copy.svg +++ b/react/features/base/icons/svg/copy.svg @@ -1,3 +1,3 @@ - - + + diff --git a/react/features/base/icons/svg/index.js b/react/features/base/icons/svg/index.js index 112ea3aee3d0..53abfc92a3f9 100644 --- a/react/features/base/icons/svg/index.js +++ b/react/features/base/icons/svg/index.js @@ -31,8 +31,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 IconE2EE } from './e2ee.svg'; -export { default as IconEmail } from './envelope.svg'; export { default as IconEdit } from './edit.svg'; +export { default as IconEmail } from './envelope.svg'; export { default as IconEventNote } from './event_note.svg'; export { default as IconExclamation } from './exclamation.svg'; export { default as IconExclamationSolid } from './exclamation-solid.svg'; diff --git a/react/features/base/premeeting/components/index.native.js b/react/features/base/premeeting/components/index.native.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/react/features/base/premeeting/components/index.web.js b/react/features/base/premeeting/components/index.web.js new file mode 100644 index 000000000000..40d5f46528c2 --- /dev/null +++ b/react/features/base/premeeting/components/index.web.js @@ -0,0 +1,3 @@ +// @flow + +export * from './web'; diff --git a/react/features/base/premeeting/components/web/ActionButton.js b/react/features/base/premeeting/components/web/ActionButton.js new file mode 100644 index 000000000000..9ba2a8aee9d9 --- /dev/null +++ b/react/features/base/premeeting/components/web/ActionButton.js @@ -0,0 +1,77 @@ +// @flow + +import React from 'react'; + +import { Icon, IconArrowDown } from '../../../icons'; + +type Props = { + + /** + * Text of the button. + */ + children: React$Node, + + /** + * Text css class of the button. + */ + className?: string, + + /** + * If the button is disabled or not. + */ + disabled?: boolean, + + /** + * If the button has options. + */ + hasOptions?: boolean, + + /** + * The type of th button: primary, secondary, text. + */ + type: string, + + /** + * OnClick button handler. + */ + onClick: Function, + + /** + * Click handler for options. + */ + onOptionsClick?: Function +}; + +/** + * Button used for pre meeting actions. + * + * @returns {ReactElement} + */ +function ActionButton({ + children, + className = '', + disabled, + hasOptions, + type = 'primary', + onClick, + onOptionsClick +}: Props) { + return ( +
+ {children} + {hasOptions &&
+ +
+ } +
+ ); +} + +export default ActionButton; diff --git a/react/features/prejoin/components/preview/CopyMeetingUrl.js b/react/features/base/premeeting/components/web/CopyMeetingUrl.js similarity index 74% rename from react/features/prejoin/components/preview/CopyMeetingUrl.js rename to react/features/base/premeeting/components/web/CopyMeetingUrl.js index 548904ffda9c..8d8bd91b1653 100644 --- a/react/features/prejoin/components/preview/CopyMeetingUrl.js +++ b/react/features/base/premeeting/components/web/CopyMeetingUrl.js @@ -2,10 +2,10 @@ import React, { Component } from 'react'; -import { getCurrentConferenceUrl } from '../../../base/connection'; -import { translate } from '../../../base/i18n'; -import { Icon, IconCopy, IconCheck } from '../../../base/icons'; -import { connect } from '../../../base/redux'; +import { getCurrentConferenceUrl } from '../../../connection'; +import { translate } from '../../../i18n'; +import { Icon, IconCopy, IconCheck } from '../../../icons'; +import { connect } from '../../../redux'; import logger from '../../logger'; type Props = { @@ -108,7 +108,8 @@ class CopyMeetingUrl extends Component { */ _hideCopyLink() { this.setState({ - showCopyLink: false + showCopyLink: false, + showLinkCopied: false }); } @@ -122,7 +123,8 @@ class CopyMeetingUrl extends Component { */ _showCopyLink() { this.setState({ - showCopyLink: true + showCopyLink: true, + showLinkCopied: false }); } @@ -152,35 +154,30 @@ class CopyMeetingUrl extends Component { const { url, t } = this.props; const { _copyUrl, _showCopyLink, _hideCopyLink } = this; const src = showLinkCopied ? IconCheck : IconCopy; - const iconCls = showCopyLink || showCopyLink ? 'prejoin-copy-icon--white' : 'prejoin-copy-icon--light'; return (
-
{url}
- {showCopyLink &&
- {t('prejoin.copyAndShare')} -
} - {showLinkCopied &&
- {t('prejoin.linkCopied')} -
} - +
+ { !showCopyLink && !showLinkCopied && url } + { showCopyLink && t('prejoin.copyAndShare') } + { showLinkCopied && t('prejoin.linkCopied') } + +