From 09efaecc41e38502fc8d1162af3b26e539f1d5c4 Mon Sep 17 00:00:00 2001 From: wfleischer <70745309+wfleischer@users.noreply.github.com> Date: Fri, 26 Aug 2022 20:25:04 +0200 Subject: [PATCH] feat(conference) add end conference Add the ability (for moderators) to end the meeting for everyone. --- css/_toolbars.scss | 14 ++ css/_variables.scss | 2 + .../prosody.cfg.lua-jvb.example | 5 + lang/main-ar.json | 3 +- lang/main-ca.json | 3 +- lang/main-de.json | 3 +- lang/main-hsb.json | 3 +- lang/main-it.json | 3 +- lang/main-pt.json | 3 +- lang/main.json | 5 +- react/features/base/conference/actions.js | 37 ++++- .../base/conference/middleware.any.js | 7 +- .../native/carmode/EndMeetingButton.tsx | 4 +- react/features/toolbox/actionTypes.ts | 10 ++ react/features/toolbox/actions.web.js | 17 +++ .../toolbox/components/HangupButton.js | 11 +- .../toolbox/components/native/HangupMenu.tsx | 83 +++++++++++ .../components/native/HangupMenuButton.tsx | 36 +++++ .../toolbox/components/native/Toolbox.js | 23 +++- .../toolbox/components/native/styles.js | 11 ++ .../components/web/EndConferenceButton.tsx | 38 +++++ .../components/web/HangupMenuButton.tsx | 130 ++++++++++++++++++ .../components/web/HangupToggleButton.tsx | 75 ++++++++++ .../components/web/LeaveConferenceButton.tsx | 35 +++++ .../toolbox/components/web/Toolbox.tsx | 98 +++++++++++-- react/features/toolbox/reducer.js | 14 ++ .../prosody-plugins/mod_end_conference.lua | 84 +++++++++++ .../mod_muc_breakout_rooms.lua | 8 +- 28 files changed, 723 insertions(+), 42 deletions(-) create mode 100644 react/features/toolbox/components/native/HangupMenu.tsx create mode 100644 react/features/toolbox/components/native/HangupMenuButton.tsx create mode 100644 react/features/toolbox/components/web/EndConferenceButton.tsx create mode 100644 react/features/toolbox/components/web/HangupMenuButton.tsx create mode 100644 react/features/toolbox/components/web/HangupToggleButton.tsx create mode 100644 react/features/toolbox/components/web/LeaveConferenceButton.tsx create mode 100644 resources/prosody-plugins/mod_end_conference.lua diff --git a/css/_toolbars.scss b/css/_toolbars.scss index d84159e12..a470fd0de 100644 --- a/css/_toolbars.scss +++ b/css/_toolbars.scss @@ -134,6 +134,20 @@ } } +.hangup-menu-button { + background-color: $hangupMenuButtonColor; + + @media (hover: hover) and (pointer: fine) { + &:hover { + background-color: $hangupMenuButtonHoverColor; + } + } + + svg { + fill: #fff; + } +} + .profile-button-avatar { align-items: center; } diff --git a/css/_variables.scss b/css/_variables.scss index dadc6289c..0133d80f2 100644 --- a/css/_variables.scss +++ b/css/_variables.scss @@ -6,6 +6,8 @@ $baseFontFamily: -apple-system, BlinkMacSystemFont, 'open_sanslight', 'Helvetica Neue', Helvetica, Arial, sans-serif; $hangupColor:#DD3849; $hangupHoverColor: #F25363; +$hangupMenuButtonColor:#0056E0;; +$hangupMenuButtonHoverColor: #246FE5; $hangupFontSize: 2em; /** diff --git a/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example b/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example index 77412d523..c4296810a 100644 --- a/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example +++ b/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example @@ -52,6 +52,7 @@ VirtualHost "jitmeet.example.com" av_moderation_component = "avmoderation.jitmeet.example.com" speakerstats_component = "speakerstats.jitmeet.example.com" conference_duration_component = "conferenceduration.jitmeet.example.com" + end_conference_component = "endconference.jitmeet.example.com" -- we need bosh modules_enabled = { "bosh"; @@ -60,6 +61,7 @@ VirtualHost "jitmeet.example.com" "speakerstats"; "external_services"; "conference_duration"; + "end_conference"; "muc_lobby_rooms"; "muc_breakout_rooms"; "av_moderation"; @@ -123,6 +125,9 @@ Component "speakerstats.jitmeet.example.com" "speakerstats_component" Component "conferenceduration.jitmeet.example.com" "conference_duration_component" muc_component = "conference.jitmeet.example.com" +Component "endconference.jitmeet.example.com" "end_conference" + muc_component = "conference.jitmeet.example.com" + Component "avmoderation.jitmeet.example.com" "av_moderation_component" muc_component = "conference.jitmeet.example.com" diff --git a/lang/main-ar.json b/lang/main-ar.json index 3daddaaae..b58b099f4 100644 --- a/lang/main-ar.json +++ b/lang/main-ar.json @@ -79,7 +79,6 @@ }, "carmode": { "actions": { - "leaveMeeting": "اترك الاجتماع", "selectSoundDevice": "حدد جهاز الصوت" }, "labels": { @@ -1072,6 +1071,7 @@ "invite": "ادعُ آخرين", "kick": "اطرد مشاركًا", "laugh": "يضحك", + "leaveConference": "اترك الاجتماع", "like": "رفع الإبهام متمنيا النجاح", "linkToSalesforce": "ارتباط إلى Salesforce", "lobbyButton": "فعِّل/عطِّل وضع غرفة الانتظار", @@ -1146,6 +1146,7 @@ "joinBreakoutRoom": "انضم إلى غرفة الجانبية", "laugh": "يضحك", "leaveBreakoutRoom": "اترك إلى غرفة الجانبية", + "leaveConference": "اترك الاجتماع", "like": "رفع الإبهام متمنيا النجاح", "linkToSalesforce": "ارتباط إلى Salesforce", "lobbyButtonDisable": "عطِّل وضع غرفة الانتظار", diff --git a/lang/main-ca.json b/lang/main-ca.json index 1ceb6c404..0bee986e5 100644 --- a/lang/main-ca.json +++ b/lang/main-ca.json @@ -79,7 +79,6 @@ }, "carmode": { "actions": { - "leaveMeeting": "Abandona la reunió", "selectSoundDevice": "Seleccioneu l'aparell d'àudio" }, "labels": { @@ -1039,6 +1038,7 @@ "invite": "Convida-hi persones", "kick": "Expulsa el participant", "laugh": "Riure", + "leaveConference": "Abandona la reunió", "like": "Polzes amunt", "linkToSalesforce": "Enllaç a Salesforce", "lobbyButton": "Activa o desactiva la sala d'espera", @@ -1111,6 +1111,7 @@ "joinBreakoutRoom": "Entra a la sala de descans", "laugh": "Riure", "leaveBreakoutRoom": "Surt de la sala de descans", + "leaveConference": "Abandona la reunió", "like": "Polzes amunt", "linkToSalesforce": "Enllaç a Salesforce", "lobbyButtonDisable": "Desactiva el mode de sala d'espera", diff --git a/lang/main-de.json b/lang/main-de.json index 83ebc26a8..a6466b123 100644 --- a/lang/main-de.json +++ b/lang/main-de.json @@ -79,7 +79,6 @@ }, "carmode": { "actions": { - "leaveMeeting": "Konferenz verlassen", "selectSoundDevice": "Audiogerät auswählen" }, "labels": { @@ -1066,6 +1065,7 @@ "invite": "Person einladen", "kick": "Person entfernen", "laugh": "Lachen", + "leaveConference": "Konferenz verlassen", "like": "Daumen nach oben", "linkToSalesforce": "Mit Salesforce verlinken", "lobbyButton": "Lobbymodus ein-/ausschalten", @@ -1140,6 +1140,7 @@ "joinBreakoutRoom": "In Breakout-Raum wechseln", "laugh": "Lachen", "leaveBreakoutRoom": "Breakout-Raum verlassen", + "leaveConference": "Konferenz verlassen", "like": "Daumen hoch", "linkToSalesforce": "Mit Salesforce verknüpfen", "lobbyButtonDisable": "Lobbymodus deaktivieren", diff --git a/lang/main-hsb.json b/lang/main-hsb.json index 84cc4292a..85d447495 100644 --- a/lang/main-hsb.json +++ b/lang/main-hsb.json @@ -79,7 +79,6 @@ }, "carmode": { "actions": { - "leaveMeeting": "konferencu wopušćić", "selectSoundDevice": "nastroj za zwuk wuzwolić" }, "labels": { @@ -1045,6 +1044,7 @@ "invite": "wobdźělnika přeprosyć", "kick": " wobdźělnika wuzamknyć", "laugh": "so smjeć", + "leaveConference": "konferencu wopušćić", "like": "palc horje", "linkToSalesforce": "ze Salesforce zwjazać", "lobbyButton": "lobby-modus zapnyć/hasnyć", @@ -1117,6 +1117,7 @@ "joinBreakoutRoom": "do breakout rumnosće měnić", "laugh": "so smjeć", "leaveBreakoutRoom": "breakout rumnosć wopusćić", + "leaveConference": "konferencu wopušćić", "like": "palc horje", "linkToSalesforce": "ze Salesforce zwjazać", "lobbyButtonDisable": "lobby-modus deaktiwěrować", diff --git a/lang/main-it.json b/lang/main-it.json index b17626573..6400850f7 100644 --- a/lang/main-it.json +++ b/lang/main-it.json @@ -79,7 +79,6 @@ }, "carmode": { "actions": { - "leaveMeeting": " Lascia riunione", "selectSoundDevice": "Scegli audio" }, "labels": { @@ -1045,6 +1044,7 @@ "invite": "Invita partecipanti", "kick": "Espelli partecipante", "laugh": "Ridi", + "leaveConference": " Lascia riunione", "like": "Mi piace", "linkToSalesforce": "Collega a Salesforce", "lobbyButton": "Attiva/Disattiva sala d'attesa", @@ -1117,6 +1117,7 @@ "joinBreakoutRoom": "Entra in sottogruppo", "laugh": "Ridi", "leaveBreakoutRoom": "Lascia breakout room", + "leaveConference": " Lascia riunione", "like": "Mi piace", "linkToSalesforce": "Collega a Salesforce", "lobbyButtonDisable": "Disabilita sala d'attesa", diff --git a/lang/main-pt.json b/lang/main-pt.json index d469686c8..f6059d00c 100644 --- a/lang/main-pt.json +++ b/lang/main-pt.json @@ -79,7 +79,6 @@ }, "carmode": { "actions": { - "leaveMeeting": " Deixar a reunião", "selectSoundDevice": "Seleccionar dispositivo de som" }, "labels": { @@ -1076,6 +1075,7 @@ "invite": "Convidar pessoas", "kick": "Remover participante", "laugh": "Risos", + "leaveConference": "Deixar a reunião", "like": "Aprovado", "linkToSalesforce": "Link para a Salesforce", "lobbyButton": "Ativar/desativar sala de espera", @@ -1150,6 +1150,7 @@ "joinBreakoutRoom": "Entrar na sala", "laugh": "Risos", "leaveBreakoutRoom": "Sair da sala", + "leaveConference": "Deixar a reunião", "like": "Aprovado", "linkToSalesforce": "Link para a Salesforce", "lobbyButtonDisable": "Desativar sala de espera", diff --git a/lang/main.json b/lang/main.json index 9dac7530e..445f91bbb 100644 --- a/lang/main.json +++ b/lang/main.json @@ -78,7 +78,6 @@ }, "carmode": { "actions": { - "leaveMeeting": " Leave meeting", "selectSoundDevice": "Select sound device" }, "labels": { @@ -1064,6 +1063,7 @@ "document": "Toggle shared document", "download": "Download our apps", "embedMeeting": "Embed meeting", + "endConference": "End meeting for all", "expand": "Expand", "feedback": "Leave feedback", "fullScreen": "Toggle full screen", @@ -1074,6 +1074,7 @@ "invite": "Invite people", "kick": "Kick participant", "laugh": "Laugh", + "leaveConference": "Leave meeting", "like": "Thumbs Up", "linkToSalesforce": "Link to Salesforce", "lobbyButton": "Enable/disable lobby mode", @@ -1136,6 +1137,7 @@ "download": "Download our apps", "e2ee": "End-to-End Encryption", "embedMeeting": "Embed meeting", + "endConference": "End meeting for all", "enterFullScreen": "View full screen", "enterTileView": "Enter tile view", "exitFullScreen": "Exit full screen", @@ -1148,6 +1150,7 @@ "joinBreakoutRoom": "Join breakout room", "laugh": "Laugh", "leaveBreakoutRoom": "Leave breakout room", + "leaveConference": "Leave meeting", "like": "Thumbs Up", "linkToSalesforce": "Link to Salesforce", "lobbyButtonDisable": "Disable lobby mode", diff --git a/react/features/base/conference/actions.js b/react/features/base/conference/actions.js index 2b46af2bb..fd1e6bb01 100644 --- a/react/features/base/conference/actions.js +++ b/react/features/base/conference/actions.js @@ -6,9 +6,10 @@ import { createStartMutedConfigurationEvent, sendAnalytics } from '../../analytics'; +import { appNavigate } from '../../app/actions'; import { endpointMessageReceived } from '../../subtitles'; import { getReplaceParticipant } from '../config/functions'; -import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection'; +import { JITSI_CONNECTION_CONFERENCE_KEY, disconnect } from '../connection'; import { JitsiConferenceEvents, JitsiE2ePingEvents } from '../lib-jitsi-meet'; import { MEDIA_TYPE, @@ -27,6 +28,7 @@ import { participantRoleChanged, participantUpdated } from '../participants'; +import { toState } from '../redux'; import { destroyLocalTracks, getLocalTracks, @@ -75,6 +77,7 @@ import { commonUserLeftHandling, getConferenceOptions, getCurrentConference, + getConferenceState, sendLocalParticipant } from './functions'; import logger from './logger'; @@ -584,6 +587,19 @@ export function dataChannelOpened() { }; } +/** + * Action to end a conference for all participants. + * + * @returns {Function} + */ +export function endConference() { + return async (dispatch: Dispatch, getState: Function) => { + const { conference } = getConferenceState(toState(getState)); + + conference?.end(); + }; +} + /** * Signals that we've been kicked out of the conference. * @@ -605,6 +621,25 @@ export function kickedOut(conference: Object, participant: Object) { }; } + +/** + * Action to leave a conference. + * + * @returns {Function} + */ +export function leaveConference() { + return async (dispatch: Dispatch) => { + + // FIXME: these should be unified. + if (navigator.product === 'ReactNative') { + dispatch(appNavigate(undefined)); + } else { + dispatch(disconnect(true)); + } + }; +} + + /** * Signals that the lock state of a specific JitsiConference changed. * diff --git a/react/features/base/conference/middleware.any.js b/react/features/base/conference/middleware.any.js index a5e57c47f..c081c2945 100644 --- a/react/features/base/conference/middleware.any.js +++ b/react/features/base/conference/middleware.any.js @@ -11,10 +11,7 @@ import { import { reloadNow } from '../../app/actions'; import { removeLobbyChatParticipant } from '../../chat/actions.any'; import { openDisplayNamePrompt } from '../../display-name'; -import { - NOTIFICATION_TIMEOUT_TYPE, - showErrorNotification -} from '../../notifications'; +import { NOTIFICATION_TIMEOUT_TYPE, showErrorNotification, showWarningNotification } from '../../notifications'; import { CONNECTION_ESTABLISHED, CONNECTION_FAILED, connectionDisconnected } from '../connection'; import { validateJwt } from '../jwt'; import { JitsiConferenceErrors } from '../lib-jitsi-meet'; @@ -132,7 +129,7 @@ function _conferenceFailed({ dispatch, getState }, next, action) { case JitsiConferenceErrors.CONFERENCE_DESTROYED: { const [ reason ] = error.params; - dispatch(showErrorNotification({ + dispatch(showWarningNotification({ description: reason, titleKey: 'dialog.sessTerminated' }, NOTIFICATION_TIMEOUT_TYPE.LONG)); diff --git a/react/features/conference/components/native/carmode/EndMeetingButton.tsx b/react/features/conference/components/native/carmode/EndMeetingButton.tsx index b565b125d..addf605f1 100644 --- a/react/features/conference/components/native/carmode/EndMeetingButton.tsx +++ b/react/features/conference/components/native/carmode/EndMeetingButton.tsx @@ -29,9 +29,9 @@ const EndMeetingButton = () : JSX.Element => { return (