ソースを参照

fix(chat): maintain bottom scroll on input resize

master
Leonard Kim 6年前
コミット
d86b60ea72

+ 17
- 1
react/features/chat/components/web/Chat.js ファイルの表示

@@ -47,6 +47,9 @@ class Chat extends AbstractChat<Props> {
47 47
 
48 48
         // Bind event handlers so they are only bound once for every instance.
49 49
         this._renderPanelContent = this._renderPanelContent.bind(this);
50
+
51
+        // Bind event handlers so they are only bound once for every instance.
52
+        this._onChatInputResize = this._onChatInputResize.bind(this);
50 53
     }
51 54
 
52 55
     /**
@@ -87,6 +90,19 @@ class Chat extends AbstractChat<Props> {
87 90
         );
88 91
     }
89 92
 
93
+    _onChatInputResize: () => void;
94
+
95
+    /**
96
+     * Callback invoked when {@code ChatInput} changes height. Preserves
97
+     * displaying the latest message if it is scrolled to.
98
+     *
99
+     * @private
100
+     * @returns {void}
101
+     */
102
+    _onChatInputResize() {
103
+        this._messageContainerRef.current.maybeUpdateBottomScroll();
104
+    }
105
+
90 106
     /**
91 107
      * Returns a React Element for showing chat messages and a form to send new
92 108
      * chat messages.
@@ -100,7 +116,7 @@ class Chat extends AbstractChat<Props> {
100 116
                 <MessageContainer
101 117
                     messages = { this.props._messages }
102 118
                     ref = { this._messageContainerRef } />
103
-                <ChatInput />
119
+                <ChatInput onResize = { this._onChatInputResize } />
104 120
             </>
105 121
         );
106 122
     }

+ 7
- 0
react/features/chat/components/web/ChatInput.js ファイルの表示

@@ -22,6 +22,12 @@ type Props = {
22 22
      */
23 23
     dispatch: Dispatch<any>,
24 24
 
25
+    /**
26
+     * Optional callback to invoke when the chat textarea has auto-resized to
27
+     * fit overflowing text.
28
+     */
29
+    onResize: ?Function,
30
+
25 31
     /**
26 32
      * Invoked to obtain translated strings.
27 33
      */
@@ -120,6 +126,7 @@ class ChatInput extends Component<Props, State> {
120 126
                         inputRef = { this._setTextAreaRef }
121 127
                         maxRows = { 5 }
122 128
                         onChange = { this._onMessageChange }
129
+                        onHeightChange = { this.props.onResize }
123 130
                         onKeyDown = { this._onDetectSubmit }
124 131
                         placeholder = { this.props.t('chat.messagebox') }
125 132
                         value = { this.state.message } />

+ 50
- 1
react/features/chat/components/web/MessageContainer.js ファイルの表示

@@ -13,12 +13,25 @@ import ChatMessageGroup from './ChatMessageGroup';
13 13
  * @extends AbstractMessageContainer
14 14
  */
15 15
 export default class MessageContainer extends AbstractMessageContainer {
16
+    /**
17
+     * Whether or not chat has been scrolled to the bottom of the screen. Used
18
+     * to determine if chat should be scrolled automatically to the bottom when
19
+     * the {@code ChatInput} resizes.
20
+     */
21
+    _isScrolledToBottom: boolean;
22
+
16 23
     /**
17 24
      * Reference to the HTML element at the end of the list of displayed chat
18 25
      * messages. Used for scrolling to the end of the chat messages.
19 26
      */
20 27
     _messagesListEndRef: Object;
21 28
 
29
+    /**
30
+     * A React ref to the HTML element containing all {@code ChatMessageGroup}
31
+     * instances.
32
+     */
33
+    _messageListRef: Object;
34
+
22 35
     /**
23 36
      * Initializes a new {@code MessageContainer} instance.
24 37
      *
@@ -28,7 +41,12 @@ export default class MessageContainer extends AbstractMessageContainer {
28 41
     constructor(props: Props) {
29 42
         super(props);
30 43
 
44
+        this._isScrolledToBottom = true;
45
+
46
+        this._messageListRef = React.createRef();
31 47
         this._messagesListEndRef = React.createRef();
48
+
49
+        this._onChatScroll = this._onChatScroll.bind(this);
32 50
     }
33 51
 
34 52
     /**
@@ -50,13 +68,29 @@ export default class MessageContainer extends AbstractMessageContainer {
50 68
         });
51 69
 
52 70
         return (
53
-            <div id = 'chatconversation'>
71
+            <div
72
+                id = 'chatconversation'
73
+                onScroll = { this._onChatScroll }
74
+                ref = { this._messageListRef }>
54 75
                 { messages }
55 76
                 <div ref = { this._messagesListEndRef } />
56 77
             </div>
57 78
         );
58 79
     }
59 80
 
81
+    /**
82
+     * Scrolls to the bottom again if the instance had previously been scrolled
83
+     * to the bottom. This method is used when a resize has occurred below the
84
+     * instance and bottom scroll needs to be maintained.
85
+     *
86
+     * @returns {void}
87
+     */
88
+    maybeUpdateBottomScroll() {
89
+        if (this._isScrolledToBottom) {
90
+            this.scrollToBottom(false);
91
+        }
92
+    }
93
+
60 94
     /**
61 95
      * Automatically scrolls the displayed chat messages down to the latest.
62 96
      *
@@ -71,4 +105,19 @@ export default class MessageContainer extends AbstractMessageContainer {
71 105
     }
72 106
 
73 107
     _getMessagesGroupedBySender: () => Array<Array<Object>>;
108
+
109
+    _onChatScroll: () => void;
110
+
111
+    /**
112
+     * Callback invoked to listen to the current scroll location.
113
+     *
114
+     * @private
115
+     * @returns {void}
116
+     */
117
+    _onChatScroll() {
118
+        const element = this._messageListRef.current;
119
+
120
+        this._isScrolledToBottom
121
+            = element.scrollHeight - element.scrollTop === element.clientHeight;
122
+    }
74 123
 }

読み込み中…
キャンセル
保存