Bläddra i källkod

feat(chat): Improve responsiveness.

* Fix toolbox buttons not displaying properly when chat is open.
* Open chat in fullscreen dialog past custom thresholds when mobile/desktop toolbox would become unusable due to chat
* Remove mobile chat check when displaying toolbox
j8
Mihai-Andrei Uscat 4 år sedan
förälder
incheckning
43e655b619
Inget konto är kopplat till bidragsgivarens mejladress

+ 1
- 71
css/_base.scss Visa fil

@@ -17,6 +17,7 @@ textarea {
17 17
 html {
18 18
     height: 100%;
19 19
     width: 100%;
20
+    overflow: hidden;
20 21
 }
21 22
 
22 23
 body {
@@ -201,74 +202,3 @@ form {
201 202
     background: rgba(0, 0, 0, .5);
202 203
     border-radius: 4px;
203 204
 }
204
-
205
-.desktop-browser {
206
-    @media only screen and (max-width: $smallScreen) {
207
-        .watermark {
208
-            width: 20%;
209
-            height: 20%;
210
-        }
211
-
212
-        .new-toolbox {
213
-            .toolbox-content {
214
-                .button-group-center, .button-group-left, .button-group-right {
215
-                    .toolbox-button {
216
-                        .toolbox-icon {
217
-                            width: 28px;
218
-                            height: 28px;
219
-                            svg {
220
-                                width: 18px;
221
-                                height: 18px;
222
-                            }
223
-                        }
224
-
225
-                        &:nth-child(2) {
226
-                            .toolbox-icon {
227
-                                width: 30px;
228
-                                height: 30px;
229
-                            }
230
-                        }
231
-                    }
232
-                }
233
-            }
234
-        }
235
-    }
236
-
237
-    @media only screen and (max-width: $verySmallScreen) {
238
-        #videoResolutionLabel {
239
-            display: none;
240
-        }
241
-        .vertical-filmstrip .filmstrip {
242
-            display: none;
243
-        }
244
-        .new-toolbox {
245
-            .toolbox-content {
246
-                .button-group-center, .button-group-left, .button-group-right {
247
-                    .settings-button-small-icon {
248
-                        display: none;
249
-                    }
250
-                    .toolbox-button {
251
-                        .toolbox-icon {
252
-                            width: 18px;
253
-                            height: 18px;
254
-                            svg {
255
-                                width: 12px;
256
-                                height: 12px;
257
-                            }
258
-                        }
259
-
260
-                        &:nth-child(2) {
261
-                            .toolbox-icon {
262
-                                width: 20px;
263
-                                height: 20px;
264
-                            }
265
-                        }
266
-                    }
267
-                }
268
-            }
269
-        }
270
-        .chrome-extension-banner {
271
-            display: none;
272
-        }
273
-    }
274
-}

+ 28
- 0
css/_chat.scss Visa fil

@@ -379,3 +379,31 @@
379 379
         }
380 380
     }
381 381
 }
382
+
383
+.chat-dialog {
384
+    display: flex;
385
+    flex-direction: column;
386
+    height: 100%;
387
+    margin-top: -5px; // Margin set by atlaskit.
388
+
389
+    &-header {
390
+        display: flex;
391
+        justify-content: space-between;
392
+        margin: 16px 16px 24px;
393
+        width: calc(100% - 32px);
394
+        box-sizing: border-box;
395
+        color: #fff;
396
+        font-weight: 600;
397
+        font-size: 24px;
398
+        line-height: 32px;
399
+
400
+        .jitsi-icon > svg {
401
+            cursor: pointer;
402
+            fill: #A4B8D1;
403
+        }
404
+    }
405
+
406
+    #chatconversation {
407
+        width: 100%;
408
+    }
409
+}

+ 132
- 0
css/_responsive.scss Visa fil

@@ -1,3 +1,73 @@
1
+@mixin small-button-size() {
2
+    .new-toolbox {
3
+        .toolbox-content {
4
+            .button-group-center, .button-group-left, .button-group-right {
5
+                .toolbox-button {
6
+                    .toolbox-icon {
7
+                        width: 28px;
8
+                        height: 28px;
9
+                        svg {
10
+                            width: 18px;
11
+                            height: 18px;
12
+                        }
13
+                    }
14
+
15
+                    &:nth-child(2) {
16
+                        .toolbox-icon {
17
+                            width: 30px;
18
+                            height: 30px;
19
+                        }
20
+                    }
21
+                }
22
+            }
23
+        }
24
+    }
25
+}
26
+
27
+@mixin very-small-button-size() {
28
+    .new-toolbox {
29
+        .toolbox-content {
30
+            .button-group-center, .button-group-left, .button-group-right {
31
+                .settings-button-small-icon {
32
+                    display: none;
33
+                }
34
+                .toolbox-button {
35
+                    .toolbox-icon {
36
+                        width: 18px;
37
+                        height: 18px;
38
+                        svg {
39
+                            width: 12px;
40
+                            height: 12px;
41
+                        }
42
+                    }
43
+
44
+                    &:nth-child(2) {
45
+                        .toolbox-icon {
46
+                            width: 20px;
47
+                            height: 20px;
48
+                        }
49
+                    }
50
+                }
51
+            }
52
+        }
53
+    }
54
+}
55
+
56
+@mixin full-size-modal-positioner() {
57
+    height: 100%;
58
+    left: 0;
59
+    position: fixed;
60
+    top: 0;
61
+    max-width: 100%;
62
+    width: 100%;
63
+}
64
+
65
+@mixin full-size-modal-dialog() {
66
+    height: 100%;
67
+    max-height: 100%;
68
+    border-radius: 0;
69
+}
70
+
1 71
 @media only screen and (max-width: $verySmallScreen) {
2 72
     .welcome {
3 73
         display: block;
@@ -65,3 +135,65 @@
65 135
         }
66 136
     }
67 137
 }
138
+
139
+.desktop-browser {
140
+    @media only screen and (max-width: $smallScreen) {
141
+        @include small-button-size();
142
+    }
143
+
144
+    @media only screen and (max-width: $verySmallScreen) {
145
+        @include very-small-button-size();
146
+
147
+        #videoResolutionLabel {
148
+            display: none;
149
+        }
150
+        .vertical-filmstrip .filmstrip {
151
+            display: none;
152
+        }
153
+        .chrome-extension-banner {
154
+            display: none;
155
+        }
156
+    }
157
+
158
+    &.shift-right {
159
+        @media only screen and (max-width: $smallScreen + $sidebarWidth) {
160
+            @include small-button-size()
161
+        }
162
+
163
+        @media only screen and (max-width: $verySmallScreen + $sidebarWidth) {
164
+            @include very-small-button-size();
165
+
166
+            #videoResolutionLabel {
167
+                display: none;
168
+            }
169
+            .vertical-filmstrip .filmstrip {
170
+                display: none;
171
+            }
172
+            .chrome-extension-banner {
173
+                display: none;
174
+            }
175
+        }
176
+    }
177
+}
178
+
179
+@media (min-width: 480px) and (max-width: 580px) {
180
+    .shift-right [class^="Modal__PositionerAbsolute"] {
181
+        @include full-size-modal-positioner();
182
+    }
183
+
184
+    .shift-right [class^="Modal__Dialog-"] {
185
+        @include full-size-modal-dialog();
186
+    }
187
+}
188
+
189
+@media (min-width: 580px) and (max-width: 680px) {
190
+    .mobile-browser {
191
+        &.shift-right [class^="Modal__PositionerAbsolute"] {
192
+            @include full-size-modal-positioner();
193
+        }
194
+
195
+        &.shift-right [class^="Modal__Dialog-"] {
196
+            @include full-size-modal-dialog();
197
+        }
198
+    }
199
+}

+ 10
- 0
react/features/chat/components/AbstractChat.js Visa fil

@@ -3,14 +3,21 @@
3 3
 import { Component } from 'react';
4 4
 import type { Dispatch } from 'redux';
5 5
 
6
+import { isMobileBrowser } from '../../base/environment/utils';
6 7
 import { getLocalParticipant } from '../../base/participants';
7 8
 import { sendMessage, toggleChat } from '../actions';
9
+import { DESKTOP_SMALL_WIDTH_THRESHOLD, MOBILE_SMALL_WIDTH_THRESHOLD } from '../constants';
8 10
 
9 11
 /**
10 12
  * The type of the React {@code Component} props of {@code AbstractChat}.
11 13
  */
12 14
 export type Props = {
13 15
 
16
+    /**
17
+     * Whether the chat is opened in a modal or not (computed based on window width).
18
+     */
19
+    _isModal: boolean,
20
+
14 21
     /**
15 22
      * True if the chat window should be rendered.
16 23
      */
@@ -106,6 +113,9 @@ export function _mapStateToProps(state: Object) {
106 113
     const _localParticipant = getLocalParticipant(state);
107 114
 
108 115
     return {
116
+        _isModal: isMobileBrowser()
117
+            ? window.innerWidth <= MOBILE_SMALL_WIDTH_THRESHOLD
118
+            : window.innerWidth <= DESKTOP_SMALL_WIDTH_THRESHOLD,
109 119
         _isOpen: isOpen,
110 120
         _messages: messages,
111 121
         _showNamePrompt: !_localParticipant.name

+ 20
- 10
react/features/chat/components/web/Chat.js Visa fil

@@ -11,6 +11,7 @@ import AbstractChat, {
11 11
     type Props
12 12
 } from '../AbstractChat';
13 13
 
14
+import ChatDialog from './ChatDialog';
14 15
 import ChatInput from './ChatInput';
15 16
 import DisplayNameForm from './DisplayNameForm';
16 17
 import MessageContainer from './MessageContainer';
@@ -151,16 +152,25 @@ class Chat extends AbstractChat<Props> {
151 152
      * @returns {ReactElement | null}
152 153
      */
153 154
     _renderPanelContent() {
154
-        const { _isOpen, _showNamePrompt } = this.props;
155
-        const ComponentToRender = _isOpen
156
-            ? (
157
-                <>
158
-                    { this._renderChatHeader() }
159
-                    { _showNamePrompt
160
-                        ? <DisplayNameForm /> : this._renderChat() }
161
-                </>
162
-            )
163
-            : null;
155
+        const { _isModal, _isOpen, _showNamePrompt } = this.props;
156
+        let ComponentToRender = null;
157
+
158
+        if (_isOpen) {
159
+            if (_isModal) {
160
+                ComponentToRender = (
161
+                    <ChatDialog>
162
+                        { _showNamePrompt ? <DisplayNameForm /> : this._renderChat() }
163
+                    </ChatDialog>
164
+                );
165
+            } else {
166
+                ComponentToRender = (
167
+                    <>
168
+                        { this._renderChatHeader() }
169
+                        { _showNamePrompt ? <DisplayNameForm /> : this._renderChat() }
170
+                    </>
171
+                );
172
+            }
173
+        }
164 174
         let className = '';
165 175
 
166 176
         if (_isOpen) {

+ 37
- 0
react/features/chat/components/web/ChatDialog.js Visa fil

@@ -0,0 +1,37 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+
5
+import { Dialog } from '../../../base/dialog';
6
+
7
+import Header from './ChatDialogHeader';
8
+
9
+type Props = {
10
+
11
+    /**
12
+     * Children of the component.
13
+     */
14
+    children: React$Node
15
+}
16
+
17
+/**
18
+ * Component that renders the content of the chat in a modal.
19
+ *
20
+ * @returns {React$Element<any>}
21
+ */
22
+function ChatDialog({ children }: Props) {
23
+    return (
24
+        <Dialog
25
+            customHeader = { Header }
26
+            disableEnter = { true }
27
+            hideCancelButton = { true }
28
+            submitDisabled = { true }
29
+            titleKey = 'chat.title'>
30
+            <div className = 'chat-dialog'>
31
+                {children}
32
+            </div>
33
+        </Dialog>
34
+    );
35
+}
36
+
37
+export default ChatDialog;

+ 42
- 0
react/features/chat/components/web/ChatDialogHeader.js Visa fil

@@ -0,0 +1,42 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+
5
+import { translate } from '../../../base/i18n';
6
+import { Icon, IconClose } from '../../../base/icons';
7
+import { connect } from '../../../base/redux';
8
+import { toggleChat } from '../../../chat';
9
+
10
+type Props = {
11
+
12
+    /**
13
+     * Function to be called when pressing the close button.
14
+     */
15
+    onCancel: Function,
16
+
17
+    /**
18
+     * Invoked to obtain translated strings.
19
+     */
20
+    t: Function
21
+};
22
+
23
+/**
24
+ * Custom header of the {@code ChatDialog}.
25
+ *
26
+ * @returns {React$Element<any>}
27
+ */
28
+function Header({ onCancel, t }: Props) {
29
+    return (
30
+        <div
31
+            className = 'chat-dialog-header'>
32
+            { t('chat.title') }
33
+            <Icon
34
+                onClick = { onCancel }
35
+                src = { IconClose } />
36
+        </div>
37
+    );
38
+}
39
+
40
+const mapDispatchToProps = { onCancel: toggleChat };
41
+
42
+export default translate(connect(null, mapDispatchToProps)(Header));

+ 4
- 0
react/features/chat/constants.js Visa fil

@@ -29,3 +29,7 @@ export const MESSAGE_TYPE_LOCAL = 'local';
29 29
  * The {@code messageType} of remote messages.
30 30
  */
31 31
 export const MESSAGE_TYPE_REMOTE = 'remote';
32
+
33
+export const DESKTOP_SMALL_WIDTH_THRESHOLD = 580;
34
+
35
+export const MOBILE_SMALL_WIDTH_THRESHOLD = 680;

+ 1
- 4
react/features/toolbox/functions.web.js Visa fil

@@ -1,7 +1,6 @@
1 1
 // @flow
2 2
 
3 3
 import { hasAvailableDevices } from '../base/devices';
4
-import { isMobileBrowser } from '../base/environment/utils';
5 4
 
6 5
 declare var interfaceConfig: Object;
7 6
 
@@ -49,10 +48,8 @@ export function isToolboxVisible(state: Object) {
49 48
         visible
50 49
     } = state['features/toolbox'];
51 50
     const { audioSettingsVisible, videoSettingsVisible } = state['features/settings'];
52
-    const { isOpen } = state['features/chat'];
53
-    const isMobileChatOpen = isMobileBrowser() && isOpen;
54 51
 
55
-    return Boolean(!isMobileChatOpen && !iAmSipGateway && (timeoutID || visible || alwaysVisible
52
+    return Boolean(!iAmSipGateway && (timeoutID || visible || alwaysVisible
56 53
                                       || audioSettingsVisible || videoSettingsVisible));
57 54
 }
58 55
 

Laddar…
Avbryt
Spara