Переглянути джерело

feat(chat): on web, group messages by sender

factor2
Leonard Kim 6 роки тому
джерело
коміт
fb5a45f714

+ 40
- 15
css/_chat.scss Переглянути файл

@@ -130,7 +130,7 @@
130 130
     border-radius:0;
131 131
     box-shadow: none;
132 132
     color: white;
133
-    font-size: 10pt;
133
+    font-size: 15px;
134 134
     line-height: 30px;
135 135
     padding: 5px;
136 136
     max-height:150px;
@@ -162,25 +162,12 @@
162 162
     }
163 163
 
164 164
     .display-name {
165
-        float: left;
166
-        padding-left: 5px;
165
+        font-size: 13px;
167 166
         font-weight: bold;
168 167
         white-space: nowrap;
169 168
         text-overflow: ellipsis;
170
-        width: 95%;
171 169
         overflow: hidden;
172 170
     }
173
-
174
-    .timestamp {
175
-        float: right;
176
-        padding-right: 5px;
177
-        font-size: 11px;
178
-    }
179
-}
180
-
181
-.usermessage {
182
-    padding-top: 20px;
183
-    padding-left: 5px;
184 171
 }
185 172
 
186 173
 .chatmessage {
@@ -290,3 +277,41 @@
290 277
 #usermsg::-webkit-scrollbar-track-piece {
291 278
     background: #3a3a3a;
292 279
 }
280
+
281
+.chat-message-group {
282
+    display: flex;
283
+    flex-direction: column;
284
+
285
+    &.local {
286
+        align-items: flex-end;
287
+
288
+        .display-name {
289
+            display: none;
290
+        }
291
+
292
+        .chatmessage {
293
+            background-color: $chatLocalMessageBackgroundColor;
294
+            border-radius: 6px 0px 6px 6px;
295
+        }
296
+    }
297
+
298
+    &.error {
299
+        .chatmessage {
300
+            border-radius: 0px;
301
+            color: red;
302
+        }
303
+
304
+        .display-name {
305
+            display: none;
306
+        }
307
+    }
308
+
309
+    .chatmessage {
310
+        background-color: $chatRemoteMessageBackgroundColor;
311
+        border-radius: 0px 6px 6px 6px;
312
+        display: inline-block;
313
+        margin-top: 3px;
314
+        color: white;
315
+        padding: 8px;
316
+    }
317
+}

+ 11
- 1
react/features/chat/components/AbstractChatMessage.js Переглянути файл

@@ -19,6 +19,12 @@ export type Props = {
19 19
      */
20 20
     message: Object,
21 21
 
22
+    /**
23
+     * Whether or not the name of the participant which sent the message should
24
+     * be displayed.
25
+     */
26
+    showDisplayName: boolean,
27
+
22 28
     /**
23 29
      * Invoked to receive translated strings.
24 30
      */
@@ -28,7 +34,11 @@ export type Props = {
28 34
 /**
29 35
  * Abstract component to display a chat message.
30 36
  */
31
-export default class AbstractChatMessage<P: Props> extends PureComponent<P> {}
37
+export default class AbstractChatMessage<P: Props> extends PureComponent<P> {
38
+    static defaultProps = {
39
+        showDisplayName: true
40
+    };
41
+}
32 42
 
33 43
 /**
34 44
  * Maps part of the Redux state to the props of this component.

+ 51
- 20
react/features/chat/components/web/Chat.js Переглянути файл

@@ -12,7 +12,7 @@ import AbstractChat, {
12 12
     type Props
13 13
 } from '../AbstractChat';
14 14
 import ChatInput from './ChatInput';
15
-import ChatMessage from './ChatMessage';
15
+import ChatMessageGroup from './ChatMessageGroup';
16 16
 import DisplayNameForm from './DisplayNameForm';
17 17
 
18 18
 /**
@@ -46,7 +46,6 @@ class Chat extends AbstractChat<Props> {
46 46
         this._messagesListEnd = null;
47 47
 
48 48
         // Bind event handlers so they are only bound once for every instance.
49
-        this._renderMessage = this._renderMessage.bind(this);
50 49
         this._renderPanelContent = this._renderPanelContent.bind(this);
51 50
         this._setMessageListEndRef = this._setMessageListEndRef.bind(this);
52 51
     }
@@ -88,6 +87,37 @@ class Chat extends AbstractChat<Props> {
88 87
         );
89 88
     }
90 89
 
90
+    /**
91
+     * Iterates over all the messages and creates nested arrays which hold
92
+     * consecutive messages sent be the same participant.
93
+     *
94
+     * @private
95
+     * @returns {Array<Array<Object>>}
96
+     */
97
+    _getMessagesGroupedBySender() {
98
+        const messagesCount = this.props._messages.length;
99
+        const groups = [];
100
+        let currentGrouping = [];
101
+        let currentGroupParticipantId;
102
+
103
+        for (let i = 0; i < messagesCount; i++) {
104
+            const message = this.props._messages[i];
105
+
106
+            if (message.id === currentGroupParticipantId) {
107
+                currentGrouping.push(message);
108
+            } else {
109
+                groups.push(currentGrouping);
110
+
111
+                currentGrouping = [ message ];
112
+                currentGroupParticipantId = message.id;
113
+            }
114
+        }
115
+
116
+        groups.push(currentGrouping);
117
+
118
+        return groups;
119
+    }
120
+
91 121
     /**
92 122
      * Returns a React Element for showing chat messages and a form to send new
93 123
      * chat messages.
@@ -96,7 +126,25 @@ class Chat extends AbstractChat<Props> {
96 126
      * @returns {ReactElement}
97 127
      */
98 128
     _renderChat() {
99
-        const messages = this.props._messages.map(this._renderMessage);
129
+        const groupedMessages = this._getMessagesGroupedBySender();
130
+
131
+        const messages = groupedMessages.map((group, index) => {
132
+            const messageType = group[0] && group[0].messageType;
133
+            let className = 'remote';
134
+
135
+            if (messageType === 'local') {
136
+                className = 'local';
137
+            } else if (messageType === 'error') {
138
+                className = 'error';
139
+            }
140
+
141
+            return (
142
+                <ChatMessageGroup
143
+                    className = { className }
144
+                    key = { index }
145
+                    messages = { group } />
146
+            );
147
+        });
100 148
 
101 149
         messages.push(<div
102 150
             key = 'end-marker'
@@ -129,23 +177,6 @@ class Chat extends AbstractChat<Props> {
129 177
         );
130 178
     }
131 179
 
132
-    _renderMessage: (Object) => void;
133
-
134
-    /**
135
-     * Called by {@code _onSubmitMessage} to create the chat div.
136
-     *
137
-     * @param {string} message - The chat message to display.
138
-     * @param {string} id - The chat message ID to use as a unique key.
139
-     * @returns {Array<ReactElement>}
140
-     */
141
-    _renderMessage(message: Object, id: string) {
142
-        return (
143
-            <ChatMessage
144
-                key = { id }
145
-                message = { message } />
146
-        );
147
-    }
148
-
149 180
     _renderPanelContent: (string) => React$Node | null;
150 181
 
151 182
     /**

+ 8
- 51
react/features/chat/components/web/ChatMessage.js Переглянути файл

@@ -23,24 +23,12 @@ class ChatMessage extends AbstractChatMessage<Props> {
23 23
      */
24 24
     render() {
25 25
         const { message } = this.props;
26
-        let messageTypeClassname = '';
27
-        let messageToDisplay = message.message;
28
-
29
-        switch (message.messageType) {
30
-        case 'local':
31
-            messageTypeClassname = 'localuser';
32
-
33
-            break;
34
-        case 'error':
35
-            messageTypeClassname = 'error';
36
-            messageToDisplay = this.props.t('chat.error', {
26
+        const messageToDisplay = message.messageType === 'error'
27
+            ? this.props.t('chat.error', {
37 28
                 error: message.error,
38
-                originalText: messageToDisplay
39
-            });
40
-            break;
41
-        default:
42
-            messageTypeClassname = 'remoteuser';
43
-        }
29
+                originalText: message.message
30
+            })
31
+            : message.message;
44 32
 
45 33
         // replace links and smileys
46 34
         // Strophe already escapes special symbols on sending,
@@ -68,47 +56,16 @@ class ChatMessage extends AbstractChatMessage<Props> {
68 56
         });
69 57
 
70 58
         return (
71
-            <div className = { `chatmessage ${messageTypeClassname}` }>
72
-                <div className = 'display-name'>
59
+            <div className = 'chatmessage'>
60
+                { this.props.showDisplayName && <div className = 'display-name'>
73 61
                     { message.displayName }
74
-                </div>
75
-                <div className = { 'timestamp' }>
76
-                    { ChatMessage.formatTimestamp(message.timestamp) }
77
-                </div>
62
+                </div> }
78 63
                 <div className = 'usermessage'>
79 64
                     { processedMessage }
80 65
                 </div>
81 66
             </div>
82 67
         );
83 68
     }
84
-
85
-    /**
86
-     * Returns a timestamp formatted for display.
87
-     *
88
-     * @param {number} timestamp - The timestamp for the chat message.
89
-     * @private
90
-     * @returns {string}
91
-     */
92
-    static formatTimestamp(timestamp) {
93
-        const now = new Date(timestamp);
94
-        let hour = now.getHours();
95
-        let minute = now.getMinutes();
96
-        let second = now.getSeconds();
97
-
98
-        if (hour.toString().length === 1) {
99
-            hour = `0${hour}`;
100
-        }
101
-
102
-        if (minute.toString().length === 1) {
103
-            minute = `0${minute}`;
104
-        }
105
-
106
-        if (second.toString().length === 1) {
107
-            second = `0${second}`;
108
-        }
109
-
110
-        return `${hour}:${minute}:${second}`;
111
-    }
112 69
 }
113 70
 
114 71
 export default translate(ChatMessage, { wait: false });

+ 68
- 0
react/features/chat/components/web/ChatMessageGroup.js Переглянути файл

@@ -0,0 +1,68 @@
1
+// @flow
2
+
3
+import React, { Component } from 'react';
4
+import ChatMessage from './ChatMessage';
5
+
6
+import { getLocalizedDateFormatter } from '../../../base/i18n';
7
+
8
+type Props = {
9
+
10
+    /**
11
+     * Additional CSS classes to apply to the root element.
12
+     */
13
+    className: string,
14
+
15
+    /**
16
+     * The messages to display as a group.
17
+     */
18
+    messages: Array<Object>,
19
+};
20
+
21
+/**
22
+ * Displays a list of chat messages. Will show only the display name for the
23
+ * first chat message and the timestamp for the last chat message.
24
+ *
25
+ * @extends React.Component
26
+ */
27
+class ChatMessageGroup extends Component<Props> {
28
+    static defaultProps = {
29
+        className: ''
30
+    };
31
+
32
+    /**
33
+     * Implements React's {@link Component#render()}.
34
+     *
35
+     * @inheritdoc
36
+     */
37
+    render() {
38
+        const { className, messages } = this.props;
39
+
40
+        const messagesLength = messages.length;
41
+
42
+        if (!messagesLength) {
43
+            return null;
44
+        }
45
+
46
+        const { timestamp } = messages[messagesLength - 1];
47
+
48
+        return (
49
+            <div className = { `chat-message-group ${className}` }>
50
+                {
51
+                    messages.map((message, i) => (
52
+                        <div key = { i }>
53
+                            <ChatMessage
54
+                                key = { i }
55
+                                message = { message }
56
+                                showDisplayName = { i === 0 } />
57
+                        </div>))
58
+                }
59
+                <div className = 'chat-message-group-footer'>
60
+                    { getLocalizedDateFormatter(
61
+                        new Date(timestamp)).format('H:mm') }
62
+                </div>
63
+            </div>
64
+        );
65
+    }
66
+}
67
+
68
+export default ChatMessageGroup;

Завантаження…
Відмінити
Зберегти