Pārlūkot izejas kodu

feat(lobby,notifications) refactor lobby notifications

master
Tudor D. Pop 3 gadus atpakaļ
vecāks
revīzija
28f5ddc81d
Revīzijas autora e-pasta adrese nav piesaistīta nevienam kontam

+ 0
- 58
css/premeeting/_lobby.scss Parādīt failu

@@ -41,64 +41,6 @@
41 41
     }
42 42
 }
43 43
 
44
-#notification-participant-list {
45
-    background-color: $newToolbarBackgroundColor;
46
-    border: 1px solid rgba(255, 255, 255, .4);
47
-    border-radius: 8px;
48
-    left: 0;
49
-    margin: 20px;
50
-    max-height: 600px;
51
-    overflow: hidden;
52
-    overflow-y: auto;
53
-    position: fixed;
54
-    top: 30px;
55
-    z-index: $toolbarZ + 1;
56
-
57
-    &:empty {
58
-        border: none;
59
-    }
60
-
61
-    &.toolbox-visible {
62
-        // Same as toolbox subject position
63
-        top: 120px;
64
-    }
65
-
66
-    .title {
67
-        background-color: rgba(0, 0, 0, .2);
68
-        font-size: 1.2em;
69
-        padding: 15px
70
-    }
71
-
72
-    button {
73
-        align-self: stretch;
74
-        margin-bottom: 8px 0;
75
-        padding: 12px;
76
-        transition: .2s transform ease;
77
-
78
-        &:disabled {
79
-            opacity: .5;
80
-        }
81
-
82
-        &:hover {
83
-            transform: scale(1.05);
84
-
85
-            &:disabled {
86
-                transform: none;
87
-            }
88
-        }
89
-
90
-        &.borderLess {
91
-            background-color: transparent;
92
-            border-width: 0;
93
-        }
94
-
95
-        &.primary {
96
-            background-color: rgb(3, 118, 218);
97
-            border-width: 0;
98
-        }
99
-    }
100
-}
101
-
102 44
 .knocking-participants-container {
103 45
     list-style-type: none;
104 46
     padding: 0 15px 15px 15px;

+ 5
- 1
lang/main.json Parādīt failu

@@ -642,6 +642,8 @@
642 642
         "oldElectronClientDescription1": "You appear to be using an old version of the Jitsi Meet client which has known security vulnerabilities. Please make sure you update to our ",
643 643
         "oldElectronClientDescription2": "latest build",
644 644
         "oldElectronClientDescription3": " now!",
645
+        "participantWantsToJoin": "Wants to join the meeting",
646
+        "participantsWantToJoin": "Want to join the meeting",
645 647
         "passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removed by another participant",
646 648
         "passwordSetRemotely": "$t(lockRoomPasswordUppercase) set by another participant",
647 649
         "raiseHandAction": "Raise hand",
@@ -661,7 +663,9 @@
661 663
         "videoMutedRemotelyDescription": "You can always turn it on again.",
662 664
         "videoMutedRemotelyTitle": "Your video has been turned off by {{participantDisplayName}}",
663 665
         "videoUnmuteBlockedDescription": "Camera unmute and desktop sharing operation have been temporarily blocked because of system limits.",
664
-        "videoUnmuteBlockedTitle": "Camera unmute and desktop sharing blocked!"
666
+        "videoUnmuteBlockedTitle": "Camera unmute and desktop sharing blocked!",
667
+        "viewLobby": "View lobby",
668
+        "waitingParticipants": "{{waitingParticipants}} people"
665 669
     },
666 670
     "participantsPane": {
667 671
         "actions": {

+ 1
- 1
react/features/conference/components/native/Conference.js Parādīt failu

@@ -20,7 +20,7 @@ import {
20 20
 } from '../../../filmstrip';
21 21
 import { CalleeInfoContainer } from '../../../invite';
22 22
 import { LargeVideo } from '../../../large-video';
23
-import { KnockingParticipantList } from '../../../lobby';
23
+import { KnockingParticipantList } from '../../../lobby/components/native';
24 24
 import { getIsLobbyVisible } from '../../../lobby/functions';
25 25
 import { BackButtonRegistry } from '../../../mobile/back-button';
26 26
 import { Captions } from '../../../subtitles';

+ 1
- 13
react/features/conference/components/web/Conference.js Parādīt failu

@@ -14,10 +14,9 @@ import { Chat } from '../../../chat';
14 14
 import { Filmstrip } from '../../../filmstrip';
15 15
 import { CalleeInfoContainer } from '../../../invite';
16 16
 import { LargeVideo } from '../../../large-video';
17
-import { KnockingParticipantList, LobbyScreen } from '../../../lobby';
17
+import { LobbyScreen } from '../../../lobby';
18 18
 import { getIsLobbyVisible } from '../../../lobby/functions';
19 19
 import { ParticipantsPane } from '../../../participants-pane/components/web';
20
-import { getParticipantsPaneOpen } from '../../../participants-pane/functions';
21 20
 import { Prejoin, isPrejoinPageVisible } from '../../../prejoin';
22 21
 import { toggleToolboxVisible } from '../../../toolbox/actions.any';
23 22
 import { fullScreenChanged, showToolbox } from '../../../toolbox/actions.web';
@@ -72,11 +71,6 @@ type Props = AbstractProps & {
72 71
      */
73 72
     _backgroundAlpha: number,
74 73
 
75
-    /**
76
-     * If participants pane is visible or not.
77
-     */
78
-    _isParticipantsPaneVisible: boolean,
79
-
80 74
     /**
81 75
      * The CSS class to apply to the root of {@link Conference} to modify the
82 76
      * application layout.
@@ -216,7 +210,6 @@ class Conference extends AbstractConference<Props, *> {
216 210
      */
217 211
     render() {
218 212
         const {
219
-            _isParticipantsPaneVisible,
220 213
             _layoutClassName,
221 214
             _notificationsVisible,
222 215
             _overflowDrawer,
@@ -242,10 +235,6 @@ class Conference extends AbstractConference<Props, *> {
242 235
                         id = 'videospace'
243 236
                         onTouchStart = { this._onVidespaceTouchStart }>
244 237
                         <LargeVideo />
245
-                        {!_isParticipantsPaneVisible
246
-                         && <div id = 'notification-participant-list'>
247
-                             <KnockingParticipantList />
248
-                         </div>}
249 238
                         <Filmstrip />
250 239
                     </div>
251 240
 
@@ -401,7 +390,6 @@ function _mapStateToProps(state) {
401 390
     return {
402 391
         ...abstractMapStateToProps(state),
403 392
         _backgroundAlpha: backgroundAlpha,
404
-        _isParticipantsPaneVisible: getParticipantsPaneOpen(state),
405 393
         _layoutClassName: LAYOUT_CLASSNAMES[getCurrentLayout(state)],
406 394
         _mouseMoveCallbackInterval: mouseMoveCallbackInterval,
407 395
         _overflowDrawer: overflowDrawer,

+ 0
- 78
react/features/lobby/components/AbstractKnockingParticipantList.js Parādīt failu

@@ -1,78 +0,0 @@
1
-// @flow
2
-
3
-import { PureComponent } from 'react';
4
-
5
-import { isLocalParticipantModerator } from '../../base/participants';
6
-import { setKnockingParticipantApproval } from '../actions';
7
-import { getKnockingParticipants, getLobbyEnabled } from '../functions';
8
-
9
-export type Props = {
10
-
11
-    /**
12
-     * The list of participants.
13
-     */
14
-    _participants: Array<Object>,
15
-
16
-    /**
17
-     * True if the list should be rendered.
18
-     */
19
-    _visible: boolean,
20
-
21
-    /**
22
-     * The Redux Dispatch function.
23
-     */
24
-    dispatch: Function,
25
-
26
-    /**
27
-     * Function to be used to translate i18n labels.
28
-     */
29
-    t: Function
30
-};
31
-
32
-/**
33
- * Abstract class to encapsulate the platform common code of the {@code KnockingParticipantList}.
34
- */
35
-export default class AbstractKnockingParticipantList<P: Props = Props> extends PureComponent<P> {
36
-    /**
37
-     * Instantiates a new component.
38
-     *
39
-     * @inheritdoc
40
-     */
41
-    constructor(props: P) {
42
-        super(props);
43
-
44
-        this._onRespondToParticipant = this._onRespondToParticipant.bind(this);
45
-    }
46
-
47
-    _onRespondToParticipant: (string, boolean) => Function;
48
-
49
-    /**
50
-     * Function that constructs a callback for the response handler button.
51
-     *
52
-     * @param {string} id - The id of the knocking participant.
53
-     * @param {boolean} approve - The response for the knocking.
54
-     * @returns {Function}
55
-     */
56
-    _onRespondToParticipant(id, approve) {
57
-        return () => {
58
-            this.props.dispatch(setKnockingParticipantApproval(id, approve));
59
-        };
60
-    }
61
-}
62
-
63
-/**
64
- * Maps part of the Redux state to the props of this component.
65
- *
66
- * @param {Object} state - The Redux state.
67
- * @returns {Props}
68
- */
69
-export function mapStateToProps(state: Object): $Shape<Props> {
70
-    const lobbyEnabled = getLobbyEnabled(state);
71
-    const knockingParticipants = getKnockingParticipants(state);
72
-
73
-    return {
74
-        _participants: knockingParticipants,
75
-        _visible: lobbyEnabled && isLocalParticipantModerator(state)
76
-          && Boolean(knockingParticipants && knockingParticipants.length)
77
-    };
78
-}

+ 61
- 10
react/features/lobby/components/native/KnockingParticipantList.js Parādīt failu

@@ -1,23 +1,60 @@
1 1
 // @flow
2 2
 
3
-import React from 'react';
3
+import React, { PureComponent } from 'react';
4 4
 import { ScrollView, Text, View, TouchableOpacity } from 'react-native';
5 5
 
6 6
 import { Avatar } from '../../../base/avatar';
7 7
 import { translate } from '../../../base/i18n';
8
+import { isLocalParticipantModerator } from '../../../base/participants';
8 9
 import { connect } from '../../../base/redux';
10
+import { setKnockingParticipantApproval } from '../../actions';
9 11
 import { HIDDEN_EMAILS } from '../../constants';
10
-import AbstractKnockingParticipantList, {
11
-    mapStateToProps as abstractMapStateToProps,
12
-    type Props
13
-} from '../AbstractKnockingParticipantList';
12
+import { getKnockingParticipants, getLobbyEnabled } from '../../functions';
14 13
 
15 14
 import styles from './styles';
16 15
 
16
+/**
17
+ * Props type of the component.
18
+ */
19
+export type Props = {
20
+
21
+    /**
22
+     * The list of participants.
23
+     */
24
+    _participants: Array<Object>,
25
+
26
+    /**
27
+     * True if the list should be rendered.
28
+     */
29
+    _visible: boolean,
30
+
31
+    /**
32
+     * The Redux Dispatch function.
33
+     */
34
+    dispatch: Function,
35
+
36
+    /**
37
+     * Function to be used to translate i18n labels.
38
+     */
39
+    t: Function
40
+};
41
+
17 42
 /**
18 43
  * Component to render a list for the actively knocking participants.
19 44
  */
20
-class KnockingParticipantList extends AbstractKnockingParticipantList {
45
+class KnockingParticipantList extends PureComponent<Props> {
46
+    /**
47
+     * Instantiates a new component.
48
+     *
49
+     * @param {Object} props - The read-only properties with which the new
50
+     * instance is to be initialized.
51
+     */
52
+    constructor(props: Props) {
53
+        super(props);
54
+
55
+        this._onRespondToParticipant = this._onRespondToParticipant.bind(this);
56
+    }
57
+
21 58
     /**
22 59
      * Implements {@code PureComponent#render}.
23 60
      *
@@ -78,6 +115,19 @@ class KnockingParticipantList extends AbstractKnockingParticipantList {
78 115
     }
79 116
 
80 117
     _onRespondToParticipant: (string, boolean) => Function;
118
+
119
+    /**
120
+     * Function that constructs a callback for the response handler button.
121
+     *
122
+     * @param {string} id - The id of the knocking participant.
123
+     * @param {boolean} approve - The response for the knocking.
124
+     * @returns {Function}
125
+     */
126
+    _onRespondToParticipant(id, approve) {
127
+        return () => {
128
+            this.props.dispatch(setKnockingParticipantApproval(id, approve));
129
+        };
130
+    }
81 131
 }
82 132
 
83 133
 /**
@@ -86,14 +136,15 @@ class KnockingParticipantList extends AbstractKnockingParticipantList {
86 136
  * @param {Object} state - The Redux state.
87 137
  * @returns {Props}
88 138
  */
89
-function _mapStateToProps(state: Object): $Shape<Props> {
90
-    const abstractProps = abstractMapStateToProps(state);
139
+function _mapStateToProps(state): Object {
140
+    const lobbyEnabled = getLobbyEnabled(state);
141
+    const knockingParticipants = getKnockingParticipants(state);
91 142
 
92 143
     return {
93
-        ...abstractProps,
144
+        _visible: lobbyEnabled && isLocalParticipantModerator(state),
94 145
 
95 146
         // On mobile we only show a portion of the list for screen real estate reasons
96
-        _participants: abstractProps._participants.slice(0, 2)
147
+        _participants: knockingParticipants.slice(0, 2)
97 148
     };
98 149
 }
99 150
 

+ 0
- 57
react/features/lobby/components/web/KnockingParticipantList.js Parādīt failu

@@ -1,57 +0,0 @@
1
-// @flow
2
-
3
-import React from 'react';
4
-
5
-import { translate } from '../../../base/i18n';
6
-import { connect } from '../../../base/redux';
7
-import NotificationWithParticipants from '../../../notifications/components/web/NotificationWithParticipants';
8
-import { approveKnockingParticipant, rejectKnockingParticipant } from '../../actions';
9
-import AbstractKnockingParticipantList, {
10
-    mapStateToProps as abstractMapStateToProps,
11
-    type Props as AbstractProps
12
-} from '../AbstractKnockingParticipantList';
13
-
14
-type Props = AbstractProps & {
15
-
16
-    /**
17
-     * True if the toolbox is visible, so we need to adjust the position.
18
-     */
19
-    _toolboxVisible: boolean
20
-};
21
-
22
-/**
23
- * Component to render a list for the actively knocking participants.
24
- */
25
-class KnockingParticipantList extends AbstractKnockingParticipantList<Props> {
26
-    /**
27
-     * Implements {@code PureComponent#render}.
28
-     *
29
-     * @inheritdoc
30
-     */
31
-    render() {
32
-        const { _participants, _visible, t } = this.props;
33
-
34
-        if (!_visible) {
35
-            return null;
36
-        }
37
-
38
-        return (
39
-            <div id = 'knocking-participant-list'>
40
-                <div className = 'title'>
41
-                    { t('lobby.knockingParticipantList') }
42
-                </div>
43
-                <NotificationWithParticipants
44
-                    approveButtonText = { t('lobby.allow') }
45
-                    onApprove = { approveKnockingParticipant }
46
-                    onReject = { rejectKnockingParticipant }
47
-                    participants = { _participants }
48
-                    rejectButtonText = { t('lobby.reject') }
49
-                    testIdPrefix = 'lobby' />
50
-            </div>
51
-        );
52
-    }
53
-
54
-    _onRespondToParticipant: (string, boolean) => Function;
55
-}
56
-
57
-export default translate(connect(abstractMapStateToProps)(KnockingParticipantList));

+ 0
- 1
react/features/lobby/components/web/index.js Parādīt failu

@@ -1,5 +1,4 @@
1 1
 // @flow
2 2
 
3
-export { default as KnockingParticipantList } from './KnockingParticipantList';
4 3
 export { default as LobbySection } from './LobbySection';
5 4
 export { default as LobbyScreen } from './LobbyScreen';

+ 57
- 0
react/features/lobby/middleware.js Parādīt failu

@@ -1,5 +1,6 @@
1 1
 // @flow
2 2
 
3
+import i18n from 'i18next';
3 4
 import { batch } from 'react-redux';
4 5
 
5 6
 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
@@ -13,11 +14,17 @@ import { getFirstLoadableAvatarUrl, getParticipantDisplayName } from '../base/pa
13 14
 import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
14 15
 import { playSound, registerSound, unregisterSound } from '../base/sounds';
15 16
 import { isTestModeEnabled } from '../base/testing';
17
+import { approveKnockingParticipant, rejectKnockingParticipant } from '../lobby/actions';
16 18
 import {
19
+    LOBBY_NOTIFICATION_ID,
20
+    NOTIFICATION_ICON,
17 21
     NOTIFICATION_TIMEOUT_TYPE,
18 22
     NOTIFICATION_TYPE,
23
+    hideNotification,
19 24
     showNotification
20 25
 } from '../notifications';
26
+import { open as openParticipantsPane } from '../participants-pane/actions';
27
+import { getParticipantsPaneOpen } from '../participants-pane/functions';
21 28
 import { shouldAutoKnock } from '../prejoin/functions';
22 29
 
23 30
 import { KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED } from './actionTypes';
@@ -31,6 +38,7 @@ import {
31 38
     setPasswordJoinFailed
32 39
 } from './actions';
33 40
 import { KNOCKING_PARTICIPANT_SOUND_ID } from './constants';
41
+import { getKnockingParticipants } from './functions';
34 42
 import { KNOCKING_PARTICIPANT_FILE } from './sounds';
35 43
 
36 44
 declare var APP: Object;
@@ -81,6 +89,55 @@ StateListenerRegistry.register(
81 89
                         })
82 90
                     );
83 91
                     dispatch(playSound(KNOCKING_PARTICIPANT_SOUND_ID));
92
+
93
+                    const isParticipantsPaneVisible = getParticipantsPaneOpen(getState());
94
+
95
+                    if (navigator.product === 'ReactNative' || isParticipantsPaneVisible) {
96
+                        return;
97
+                    }
98
+                    let notificationTitle;
99
+                    let customActionNameKey;
100
+                    let customActionHandler;
101
+                    let descriptionKey;
102
+                    let icon;
103
+
104
+                    const knockingParticipants = getKnockingParticipants(getState());
105
+                    const firstParticipant = knockingParticipants[0];
106
+
107
+                    if (knockingParticipants.length > 1) {
108
+                        descriptionKey = 'notify.participantsWantToJoin';
109
+                        notificationTitle = i18n.t('notify.waitingParticipants', {
110
+                            waitingParticipants: knockingParticipants.length
111
+                        });
112
+                        icon = NOTIFICATION_ICON.PARTICIPANTS;
113
+                        customActionNameKey = [ 'notify.viewLobby' ];
114
+                        customActionHandler = [ () => batch(() => {
115
+                            dispatch(hideNotification(LOBBY_NOTIFICATION_ID));
116
+                            dispatch(openParticipantsPane());
117
+                        }) ];
118
+                    } else {
119
+                        descriptionKey = 'notify.participantWantsToJoin';
120
+                        notificationTitle = firstParticipant.name;
121
+                        icon = NOTIFICATION_ICON.PARTICIPANT;
122
+                        customActionNameKey = [ 'lobby.admit', 'lobby.reject' ];
123
+                        customActionHandler = [ () => batch(() => {
124
+                            dispatch(hideNotification(LOBBY_NOTIFICATION_ID));
125
+                            dispatch(approveKnockingParticipant(firstParticipant.id));
126
+                        }),
127
+                        () => batch(() => {
128
+                            dispatch(hideNotification(LOBBY_NOTIFICATION_ID));
129
+                            dispatch(rejectKnockingParticipant(firstParticipant.id));
130
+                        }) ];
131
+                    }
132
+                    dispatch(showNotification({
133
+                        title: notificationTitle,
134
+                        descriptionKey,
135
+                        uid: LOBBY_NOTIFICATION_ID,
136
+                        customActionNameKey,
137
+                        customActionHandler,
138
+                        icon
139
+                    }, NOTIFICATION_TIMEOUT_TYPE.STICKY));
140
+
84 141
                     if (typeof APP !== 'undefined') {
85 142
                         APP.API.notifyKnockingParticipant({
86 143
                             id,

+ 8
- 0
react/features/notifications/components/web/Notification.js Parādīt failu

@@ -5,6 +5,8 @@ import EditorErrorIcon from '@atlaskit/icon/glyph/editor/error';
5 5
 import EditorInfoIcon from '@atlaskit/icon/glyph/editor/info';
6 6
 import EditorSuccessIcon from '@atlaskit/icon/glyph/editor/success';
7 7
 import EditorWarningIcon from '@atlaskit/icon/glyph/editor/warning';
8
+import PeopleIcon from '@atlaskit/icon/glyph/people';
9
+import PersonIcon from '@atlaskit/icon/glyph/person';
8 10
 import QuestionsIcon from '@atlaskit/icon/glyph/questions';
9 11
 import React from 'react';
10 12
 
@@ -171,6 +173,12 @@ class Notification extends AbstractNotification<Props> {
171 173
         case NOTIFICATION_ICON.MESSAGE:
172 174
             Icon = QuestionsIcon;
173 175
             break;
176
+        case NOTIFICATION_ICON.PARTICIPANT:
177
+            Icon = PersonIcon;
178
+            break;
179
+        case NOTIFICATION_ICON.PARTICIPANTS:
180
+            Icon = PeopleIcon;
181
+            break;
174 182
         default:
175 183
             Icon = EditorInfoIcon;
176 184
             break;

+ 10
- 1
react/features/notifications/constants.js Parādīt failu

@@ -53,9 +53,18 @@ export const NOTIFICATION_TYPE_PRIORITIES = {
53 53
  */
54 54
 export const NOTIFICATION_ICON = {
55 55
     ...NOTIFICATION_TYPE,
56
-    MESSAGE: 'message'
56
+    MESSAGE: 'message',
57
+    PARTICIPANT: 'participant',
58
+    PARTICIPANTS: 'participants'
57 59
 };
58 60
 
61
+/**
62
+ * The identifier of the lobby notification.
63
+ *
64
+ * @type {string}
65
+ */
66
+export const LOBBY_NOTIFICATION_ID = 'LOBBY_NOTIFICATION';
67
+
59 68
 /**
60 69
  * The identifier of the raise hand notification.
61 70
  *

Notiek ielāde…
Atcelt
Saglabāt