Browse Source

fix(prejoin) implement ux improvements for mobile (#9939)

master
Avram Tudor 3 years ago
parent
commit
adbb5f8ead
No account linked to committer's email address

+ 1
- 1
css/premeeting/_connection-status.scss View File

@@ -11,7 +11,7 @@
11 11
         background-color: rgba(0, 0, 0, 0.7);
12 12
         align-items: center;
13 13
         display: flex;
14
-        padding: 8px 12px;
14
+        padding: 14px 16px;
15 15
     }
16 16
 
17 17
     &-circle {

+ 13
- 10
css/premeeting/_device-status.scss View File

@@ -1,16 +1,25 @@
1 1
 .device {
2 2
     &-status {
3 3
         align-items: center;
4
-        align-self: stretch;
5 4
         color: #fff;
6 5
         display: flex;
7 6
         font-size: 14px;
8
-        font-weight: 400;
9
-        justify-content: center;
10 7
         line-height: 20px;
11
-        margin-top: 8px;
12 8
         padding: 6px;
13 9
         text-align: center;
10
+
11
+        &-error {
12
+            align-items: flex-start;
13
+            background-color: #F8AE1A;
14
+            border-radius: 6px;
15
+            color: #040404;
16
+            padding: 12px 16px;
17
+            text-align: left;
18
+        }
19
+
20
+        span {
21
+            margin-left: 16px;
22
+        }
14 23
     }
15 24
 
16 25
     &-icon {
@@ -18,14 +27,8 @@
18 27
         background-repeat: no-repeat;
19 28
         display: inline-block;
20 29
         height: 16px;
21
-        margin-right: 10px;
22 30
         width: 16px;
23 31
 
24
-        &--warning {
25
-            svg path {
26
-                fill: rgba(241, 173, 51, 1);
27
-            }
28
-        }
29 32
         &--ok {
30 33
             svg path {
31 34
                 fill: #189b55;

+ 4
- 9
css/premeeting/_prejoin.scss View File

@@ -3,21 +3,16 @@
3 3
         width: 100%;
4 4
     }
5 5
 
6
-    &-checkbox-container {
7
-        margin-bottom: 16px;
8
-        width: 100%;
9
-        text-align: center;
10
-    }
11
-
12 6
     &-error {
13
-        color: white;
14 7
         background-color: #E04757;
15 8
         border-radius: 6px;
16
-        padding: 4px;
17 9
         box-sizing: border-box;
10
+        color: white;
11
+        font-size: 12px;
12
+        line-height: 16px;
18 13
         margin-bottom: 16px;
19 14
         margin-top: -8px;
20
-        font-size: 12px;
15
+        padding: 4px;
21 16
         text-align: center;
22 17
         width: 100%;
23 18
     }

+ 30
- 79
css/premeeting/_premeeting-screens.scss View File

@@ -16,6 +16,7 @@
16 16
         cursor: pointer;
17 17
         display: inline-block;
18 18
         font-size: 14px;
19
+        font-weight: 600;
19 20
         line-height: 24px;
20 21
         margin-bottom: 16px;
21 22
         padding: 7px 16px;
@@ -128,10 +129,22 @@
128 129
 
129 130
             #new-toolbox {
130 131
                 bottom: 0;
131
-                margin-bottom: 16px;
132 132
                 position: relative;
133 133
                 transition: none;
134 134
 
135
+                .toolbox-content {
136
+                    margin-bottom: 4px;
137
+                }
138
+
139
+                .toolbox-content-items {
140
+                    background: transparent;
141
+                    border-radius: 0;
142
+                    box-shadow: none;
143
+                    display: flex;
144
+                    justify-content: space-evenly;
145
+                    padding: 8px 0;
146
+                }
147
+
135 148
                 .toolbox-content,
136 149
                 .toolbox-content-wrapper,
137 150
                 .toolbox-content-items {
@@ -163,17 +176,26 @@
163 176
             padding: 16px;
164 177
             width: 100%;
165 178
 
179
+            &-controls {
180
+                input.field {
181
+                    font-size: 16px;
182
+                    padding: 14px 16px;
183
+                }
184
+            }
185
+
166 186
             .title {
167
-                font-size: 20px;
168
-                line-height: 28px;
169
-                letter-spacing: -0.012;
170
-                margin-bottom: 24px;
187
+                display: none;
171 188
             }
172 189
         }
173 190
 
174 191
         .con-status {
175
-            margin: 16px;
176
-            width: calc(100% - 32px);
192
+            margin: 0;
193
+            width: 100%;
194
+        }
195
+
196
+        .device-status-error {
197
+            border-radius: 0;
198
+            margin: 0 -16px;
177 199
         }
178 200
 
179 201
         input.field {
@@ -183,15 +205,9 @@
183 205
 
184 206
         .action-btn {
185 207
             font-size: 16px;
208
+            margin-bottom: 8px;
186 209
             padding: 11px 16px;
187 210
         }
188
-
189
-        .toolbox-content-items {
190
-            border-radius: 0;
191
-            display: flex;
192
-            justify-content: space-evenly;
193
-            padding: 8px 0;
194
-        }
195 211
     }
196 212
 
197 213
     input::placeholder {
@@ -227,68 +243,3 @@
227 243
     display: flex;
228 244
     justify-content: center;
229 245
 }
230
-
231
-@mixin icon-container($bg, $fill) {
232
-    .toggle-button-icon-container {
233
-        background: $bg;
234
-
235
-        svg {
236
-            fill: $fill
237
-        }
238
-    }
239
-}
240
-
241
-.toggle-button {
242
-    border-radius: 6px;
243
-    cursor: pointer;
244
-    color: #fff;
245
-    font-size: 13px;
246
-    height: 40px;
247
-    margin: 0 auto;
248
-    transition: background 0.16s ease-out;
249
-
250
-    @include flex-centered();
251
-
252
-    svg {
253
-        fill: transparent;
254
-    }
255
-
256
-    label {
257
-        cursor: pointer;
258
-    }
259
-
260
-    &:hover {
261
-        background: rgba(255, 255, 255, 0.1);
262
-
263
-        .toggle-button-icon-container {
264
-            display: none;
265
-        }
266
-    }
267
-
268
-    &-container {
269
-        position: relative;
270
-
271
-        @include flex-centered();
272
-    }
273
-
274
-    &-icon-container {
275
-        border-radius: 50%;
276
-        left: -22px;
277
-        padding: 2px;
278
-        position: absolute;
279
-    }
280
-
281
-    &--toggled {
282
-        @include icon-container(white, #1C2025);
283
-
284
-        &:hover {
285
-            .toggle-button-icon-container {
286
-                display: block;
287
-            }
288
-        }
289
-
290
-        .toggle-button-icon-container {
291
-            display: block;
292
-        }
293
-    }
294
-}

+ 1
- 0
lang/main.json View File

@@ -696,6 +696,7 @@
696 696
         "errorDialOutFailed": "Could not dial out. Call failed",
697 697
         "errorDialOutStatus": "Error getting dial out status",
698 698
         "errorMissingName": "Please enter your name to join the meeting",
699
+        "errorNoPermissions": "You need to enable microphone and camera access",
699 700
         "errorStatusCode": "Error dialing out, status code: {{status}}",
700 701
         "errorValidation": "Number validation failed",
701 702
         "iWantToDialIn": "I want to dial in",

+ 3
- 0
react/features/base/icons/svg/exclamation-triangle.svg View File

@@ -0,0 +1,3 @@
1
+<svg width="18" height="16" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.2332 14.3254L9.70838 0.910718C9.63451 0.77902 9.52397 0.670633 9.38965 0.598199C8.99846 0.387243 8.50693 0.52716 8.29177 0.910713L0.766813 14.3254C0.701168 14.4424 0.666748 14.5738 0.666748 14.7073C0.666748 15.1451 1.02867 15.4999 1.47512 15.4999H16.5249C16.6611 15.4999 16.7951 15.4662 16.9145 15.4018C17.3057 15.1909 17.4484 14.7089 17.2332 14.3254ZM2.84224 13.9147L9.00002 2.93733L15.1577 13.9147H2.84224ZM8.19177 11.5371C8.19177 11.0993 8.54663 10.7445 8.98437 10.7445H9.01591C9.45365 10.7445 9.80851 11.0993 9.80851 11.5371C9.80851 11.9748 9.45365 12.3297 9.01591 12.3297H8.98437C8.54663 12.3297 8.19177 11.9748 8.19177 11.5371ZM9.00014 6.7815C8.55369 6.7815 8.19177 7.14341 8.19177 7.58986V9.14351C8.19177 9.58996 8.55369 9.95188 9.00014 9.95188C9.44659 9.95188 9.80851 9.58996 9.80851 9.14351V7.58986C9.80851 7.14341 9.44659 6.7815 9.00014 6.7815Z" fill="#040404"/>
3
+</svg>

+ 1
- 0
react/features/base/icons/svg/index.js View File

@@ -51,6 +51,7 @@ export { default as IconEmail } from './envelope.svg';
51 51
 export { default as IconEventNote } from './event_note.svg';
52 52
 export { default as IconExclamation } from './exclamation.svg';
53 53
 export { default as IconExclamationSolid } from './exclamation-solid.svg';
54
+export { default as IconExclamationTriangle } from './exclamation-triangle.svg';
54 55
 export { default as IconExitFullScreen } from './exit-full-screen.svg';
55 56
 export { default as IconFeedback } from './feedback.svg';
56 57
 export { default as IconFullScreen } from './full-screen.svg';

+ 0
- 64
react/features/base/premeeting/components/web/ToggleButton.js View File

@@ -1,64 +0,0 @@
1
-// @flow
2
-
3
-import React, { useCallback } from 'react';
4
-
5
-import { Icon, IconCheck } from '../../../icons';
6
-
7
-const mainClass = 'toggle-button';
8
-
9
-type Props = {
10
-
11
-    /**
12
-     * Text of the button.
13
-     */
14
-    children: React$Node,
15
-
16
-    /**
17
-     * If the button is toggled or not.
18
-     */
19
-    isToggled?: boolean,
20
-
21
-    /**
22
-     * OnClick button handler.
23
-     */
24
-    onClick: Function
25
-}
26
-
27
-/**
28
- * Button used as a toggle.
29
- *
30
- * @returns {ReactElement}
31
- */
32
-function ToggleButton({ children, isToggled, onClick }: Props) {
33
-    const className = isToggled ? `${mainClass} ${mainClass}--toggled` : mainClass;
34
-
35
-    const onKeyPressHandler = useCallback(e => {
36
-        if (onClick && (e.key === ' ')) {
37
-            e.preventDefault();
38
-            onClick();
39
-        }
40
-    }, [ onClick ]);
41
-
42
-    return (
43
-        <div
44
-            aria-checked = { isToggled }
45
-            className = { className }
46
-            id = 'toggle-button'
47
-            onClick = { onClick }
48
-            onKeyPress = { onKeyPressHandler }
49
-            role = 'switch'
50
-            tabIndex = { 0 }>
51
-            <div className = 'toggle-button-container'>
52
-                <div className = 'toggle-button-icon-container'>
53
-                    <Icon
54
-                        className = 'toggle-button-icon'
55
-                        size = { 10 }
56
-                        src = { IconCheck } />
57
-                </div>
58
-                <label htmlFor = 'toggle-button'>{children}</label>
59
-            </div>
60
-        </div>
61
-    );
62
-}
63
-
64
-export default ToggleButton;

+ 0
- 1
react/features/base/premeeting/components/web/index.js View File

@@ -3,4 +3,3 @@
3 3
 export { default as ActionButton } from './ActionButton';
4 4
 export { default as InputField } from './InputField';
5 5
 export { default as PreMeetingScreen } from './PreMeetingScreen';
6
-export { default as ToggleButton } from './ToggleButton';

+ 2
- 49
react/features/prejoin/components/Prejoin.js View File

@@ -7,33 +7,26 @@ import { getRoomName } from '../../base/conference';
7 7
 import { translate } from '../../base/i18n';
8 8
 import { Icon, IconArrowDown, IconArrowUp, IconPhone, IconVolumeOff } from '../../base/icons';
9 9
 import { isVideoMutedByUser } from '../../base/media';
10
-import { ActionButton, InputField, PreMeetingScreen, ToggleButton } from '../../base/premeeting';
10
+import { ActionButton, InputField, PreMeetingScreen } from '../../base/premeeting';
11 11
 import { connect } from '../../base/redux';
12 12
 import { getDisplayName, updateSettings } from '../../base/settings';
13 13
 import { getLocalJitsiVideoTrack } from '../../base/tracks';
14 14
 import {
15 15
     joinConference as joinConferenceAction,
16 16
     joinConferenceWithoutAudio as joinConferenceWithoutAudioAction,
17
-    setSkipPrejoin as setSkipPrejoinAction,
18 17
     setJoinByPhoneDialogVisiblity as setJoinByPhoneDialogVisiblityAction
19 18
 } from '../actions';
20 19
 import {
21 20
     isDeviceStatusVisible,
22 21
     isDisplayNameRequired,
23 22
     isJoinByPhoneButtonVisible,
24
-    isJoinByPhoneDialogVisible,
25
-    isPrejoinSkipped
23
+    isJoinByPhoneDialogVisible
26 24
 } from '../functions';
27 25
 
28 26
 import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog';
29 27
 
30 28
 type Props = {
31 29
 
32
-    /**
33
-     * Flag signaling if the 'skip prejoin' button is toggled or not.
34
-     */
35
-    buttonIsToggled: boolean,
36
-
37 30
     /**
38 31
      * Flag signaling if the device status is visible or not.
39 32
      */
@@ -69,11 +62,6 @@ type Props = {
69 62
      */
70 63
     roomName: string,
71 64
 
72
-    /**
73
-     * Sets visibility of the prejoin page for the next sessions.
74
-     */
75
-    setSkipPrejoin: Function,
76
-
77 65
     /**
78 66
      * Sets visibility of the 'JoinByPhoneDialog'.
79 67
      */
@@ -138,7 +126,6 @@ class Prejoin extends Component<Props, State> {
138 126
         this._closeDialog = this._closeDialog.bind(this);
139 127
         this._showDialog = this._showDialog.bind(this);
140 128
         this._onJoinButtonClick = this._onJoinButtonClick.bind(this);
141
-        this._onToggleButtonClick = this._onToggleButtonClick.bind(this);
142 129
         this._onDropdownClose = this._onDropdownClose.bind(this);
143 130
         this._onOptionsClick = this._onOptionsClick.bind(this);
144 131
         this._setName = this._setName.bind(this);
@@ -183,18 +170,6 @@ class Prejoin extends Component<Props, State> {
183 170
         }
184 171
     }
185 172
 
186
-    _onToggleButtonClick: () => void;
187
-
188
-    /**
189
-     * Handler for the toggle button.
190
-     *
191
-     * @param {Object} e - The synthetic event.
192
-     * @returns {void}
193
-     */
194
-    _onToggleButtonClick() {
195
-        this.props.setSkipPrejoin(!this.props.buttonIsToggled);
196
-    }
197
-
198 173
     _onDropdownClose: () => void;
199 174
 
200 175
     /**
@@ -321,7 +296,6 @@ class Prejoin extends Component<Props, State> {
321 296
         return (
322 297
             <PreMeetingScreen
323 298
                 showDeviceStatus = { deviceStatusVisible }
324
-                skipPrejoinButton = { this._renderSkipPrejoinButton() }
325 299
                 title = { t('prejoin.joinMeeting') }
326 300
                 videoMuted = { !showCameraPreview }
327 301
                 videoTrack = { videoTrack }>
@@ -400,25 +374,6 @@ class Prejoin extends Component<Props, State> {
400 374
             </PreMeetingScreen>
401 375
         );
402 376
     }
403
-
404
-    /**
405
-     * Renders the 'skip prejoin' button.
406
-     *
407
-     * @returns {React$Element}
408
-     */
409
-    _renderSkipPrejoinButton() {
410
-        const { buttonIsToggled, t } = this.props;
411
-
412
-        return (
413
-            <div className = 'prejoin-checkbox-container'>
414
-                <ToggleButton
415
-                    isToggled = { buttonIsToggled }
416
-                    onClick = { this._onToggleButtonClick }>
417
-                    {t('prejoin.doNotShow')}
418
-                </ToggleButton>
419
-            </div>
420
-        );
421
-    }
422 377
 }
423 378
 
424 379
 /**
@@ -432,7 +387,6 @@ function mapStateToProps(state): Object {
432 387
     const showErrorOnJoin = isDisplayNameRequired(state) && !name;
433 388
 
434 389
     return {
435
-        buttonIsToggled: isPrejoinSkipped(state),
436 390
         name,
437 391
         deviceStatusVisible: isDeviceStatusVisible(state),
438 392
         roomName: getRoomName(state),
@@ -448,7 +402,6 @@ const mapDispatchToProps = {
448 402
     joinConferenceWithoutAudio: joinConferenceWithoutAudioAction,
449 403
     joinConference: joinConferenceAction,
450 404
     setJoinByPhoneDialogVisiblity: setJoinByPhoneDialogVisiblityAction,
451
-    setSkipPrejoin: setSkipPrejoinAction,
452 405
     updateSettings
453 406
 };
454 407
 

+ 10
- 19
react/features/prejoin/components/preview/DeviceStatus.js View File

@@ -3,12 +3,11 @@
3 3
 import React from 'react';
4 4
 
5 5
 import { translate } from '../../../base/i18n';
6
-import { Icon, IconCheckSolid, IconExclamation } from '../../../base/icons';
6
+import { Icon, IconCheckSolid, IconExclamationTriangle } from '../../../base/icons';
7 7
 import { connect } from '../../../base/redux';
8 8
 import {
9 9
     getDeviceStatusType,
10
-    getDeviceStatusText,
11
-    getRawError
10
+    getDeviceStatusText
12 11
 } from '../../functions';
13 12
 
14 13
 export type Props = {
@@ -24,11 +23,6 @@ export type Props = {
24 23
      */
25 24
     deviceStatusType: string,
26 25
 
27
-    /**
28
-     * The error coming from device configuration.
29
-     */
30
-    rawError: string,
31
-
32 26
     /**
33 27
      * Used for translation.
34 28
      */
@@ -37,7 +31,7 @@ export type Props = {
37 31
 
38 32
 const iconMap = {
39 33
     warning: {
40
-        src: IconExclamation,
34
+        src: IconExclamationTriangle,
41 35
         className: 'device-icon--warning'
42 36
     },
43 37
     ok: {
@@ -52,25 +46,23 @@ const iconMap = {
52 46
  *
53 47
  * @returns {ReactElement}
54 48
  */
55
-function DeviceStatus({ deviceStatusType, deviceStatusText, rawError, t }: Props) {
49
+function DeviceStatus({ deviceStatusType, deviceStatusText, t }: Props) {
56 50
     const { src, className } = iconMap[deviceStatusType];
51
+    const hasError = deviceStatusType === 'warning';
52
+    const containerClassName = `device-status ${hasError ? 'device-status-error' : ''}`;
57 53
 
58 54
     return (
59 55
         <div
60
-            className = 'device-status'
56
+            className = { containerClassName }
61 57
             role = 'alert'
62 58
             tabIndex = { -1 }>
63 59
             <Icon
64 60
                 className = { `device-icon ${className}` }
65 61
                 size = { 16 }
66 62
                 src = { src } />
67
-            <span
68
-                role = 'heading'>
69
-                {t(deviceStatusText)}
63
+            <span role = 'heading'>
64
+                {hasError ? t('prejoin.errorNoPermissions') : t(deviceStatusText)}
70 65
             </span>
71
-            { rawError && <span>
72
-                { rawError }
73
-            </span> }
74 66
         </div>
75 67
     );
76 68
 }
@@ -84,8 +76,7 @@ function DeviceStatus({ deviceStatusType, deviceStatusText, rawError, t }: Props
84 76
 function mapStateToProps(state) {
85 77
     return {
86 78
         deviceStatusText: getDeviceStatusText(state),
87
-        deviceStatusType: getDeviceStatusType(state),
88
-        rawError: getRawError(state)
79
+        deviceStatusType: getDeviceStatusType(state)
89 80
     };
90 81
 }
91 82
 

Loading…
Cancel
Save