Преглед изворни кода

feat: improve chat UX

master
Bettenbuk Zoltan пре 5 година
родитељ
комит
6121e9fc65

+ 2
- 0
lang/main.json Прегледај датотеку

47
     },
47
     },
48
     "chat": {
48
     "chat": {
49
         "error": "Error: your message was not sent. Reason: {{error}}",
49
         "error": "Error: your message was not sent. Reason: {{error}}",
50
+        "fieldPlaceHolder": "Type your message here",
50
         "messagebox": "Type a message",
51
         "messagebox": "Type a message",
51
         "messageTo": "Private message to {{recipient}}",
52
         "messageTo": "Private message to {{recipient}}",
53
+        "noMessagesMessage": "There are no messages in the meeting yet. Start a conversation here!",
52
         "nickname": {
54
         "nickname": {
53
             "popover": "Choose a nickname",
55
             "popover": "Choose a nickname",
54
             "title": "Enter a nickname to use chat"
56
             "title": "Enter a nickname to use chat"

+ 2
- 2
react/features/chat/components/AbstractMessageContainer.js Прегледај датотеку

15
  *
15
  *
16
  * @extends PureComponent
16
  * @extends PureComponent
17
  */
17
  */
18
-export default class AbstractMessageContainer extends PureComponent<Props> {
18
+export default class AbstractMessageContainer<P: Props> extends PureComponent<P> {
19
     static defaultProps = {
19
     static defaultProps = {
20
         messages: []
20
         messages: []
21
     };
21
     };
46
             }
46
             }
47
         }
47
         }
48
 
48
 
49
-        groups.push(currentGrouping);
49
+        currentGrouping.length && groups.push(currentGrouping);
50
 
50
 
51
         return groups;
51
         return groups;
52
     }
52
     }

+ 25
- 2
react/features/chat/components/native/ChatInputBar.js Прегледај датотеку

3
 import React, { Component } from 'react';
3
 import React, { Component } from 'react';
4
 import { TextInput, TouchableOpacity, View } from 'react-native';
4
 import { TextInput, TouchableOpacity, View } from 'react-native';
5
 
5
 
6
+import { translate } from '../../../base/i18n';
6
 import { Icon, IconChatSend } from '../../../base/icons';
7
 import { Icon, IconChatSend } from '../../../base/icons';
7
 import { Platform } from '../../../base/react';
8
 import { Platform } from '../../../base/react';
8
 
9
 
13
     /**
14
     /**
14
      * Callback to invoke on message send.
15
      * Callback to invoke on message send.
15
      */
16
      */
16
-    onSend: Function
17
+    onSend: Function,
18
+
19
+    /**
20
+     * Function to be used to translate i18n labels.
21
+     */
22
+    t: Function
17
 };
23
 };
18
 
24
 
19
 type State = {
25
 type State = {
37
 /**
43
 /**
38
  * Implements the chat input bar with text field and action(s).
44
  * Implements the chat input bar with text field and action(s).
39
  */
45
  */
40
-export default class ChatInputBar extends Component<Props, State> {
46
+class ChatInputBar extends Component<Props, State> {
41
     /**
47
     /**
42
      * Instantiates a new instance of the component.
48
      * Instantiates a new instance of the component.
43
      *
49
      *
53
         };
59
         };
54
 
60
 
55
         this._onChangeText = this._onChangeText.bind(this);
61
         this._onChangeText = this._onChangeText.bind(this);
62
+        this._onFieldReferenceAvailable = this._onFieldReferenceAvailable.bind(this);
56
         this._onFocused = this._onFocused.bind(this);
63
         this._onFocused = this._onFocused.bind(this);
57
         this._onSubmit = this._onSubmit.bind(this);
64
         this._onSubmit = this._onSubmit.bind(this);
58
     }
65
     }
76
                     onChangeText = { this._onChangeText }
83
                     onChangeText = { this._onChangeText }
77
                     onFocus = { this._onFocused(true) }
84
                     onFocus = { this._onFocused(true) }
78
                     onSubmitEditing = { this._onSubmit }
85
                     onSubmitEditing = { this._onSubmit }
86
+                    placeholder = { this.props.t('chat.fieldPlaceHolder') }
87
+                    ref = { this._onFieldReferenceAvailable }
79
                     returnKeyType = 'send'
88
                     returnKeyType = 'send'
80
                     style = { styles.inputField }
89
                     style = { styles.inputField }
81
                     value = { this.state.message } />
90
                     value = { this.state.message } />
105
         });
114
         });
106
     }
115
     }
107
 
116
 
117
+    _onFieldReferenceAvailable: Object => void;
118
+
119
+    /**
120
+     * Callback to be invoked when the field reference is available.
121
+     *
122
+     * @param {Object} field - The reference to the field.
123
+     * @returns {void}
124
+     */
125
+    _onFieldReferenceAvailable(field) {
126
+        field && field.focus();
127
+    }
128
+
108
     _onFocused: boolean => Function;
129
     _onFocused: boolean => Function;
109
 
130
 
110
     /**
131
     /**
138
         });
159
         });
139
     }
160
     }
140
 }
161
 }
162
+
163
+export default translate(ChatInputBar);

+ 64
- 6
react/features/chat/components/native/MessageContainer.js Прегледај датотеку

1
 // @flow
1
 // @flow
2
 
2
 
3
 import React from 'react';
3
 import React from 'react';
4
-import { FlatList } from 'react-native';
4
+import { FlatList, Text, View } from 'react-native';
5
 
5
 
6
-import AbstractMessageContainer, { type Props }
6
+import { ColorSchemeRegistry } from '../../../base/color-scheme';
7
+import { translate } from '../../../base/i18n';
8
+import { connect } from '../../../base/redux';
9
+import { StyleType } from '../../../base/styles';
10
+
11
+import AbstractMessageContainer, { type Props as AbstractProps }
7
     from '../AbstractMessageContainer';
12
     from '../AbstractMessageContainer';
8
 
13
 
9
 import ChatMessageGroup from './ChatMessageGroup';
14
 import ChatMessageGroup from './ChatMessageGroup';
10
 import styles from './styles';
15
 import styles from './styles';
11
 
16
 
17
+type Props = AbstractProps & {
18
+
19
+    /**
20
+     * The color-schemed stylesheet of the feature.
21
+     */
22
+    _styles: StyleType,
23
+
24
+    /**
25
+     * Function to be used to translate i18n labels.
26
+     */
27
+    t: Function
28
+};
29
+
12
 /**
30
 /**
13
  * Implements a container to render all the chat messages in a conference.
31
  * Implements a container to render all the chat messages in a conference.
14
  */
32
  */
15
-export default class MessageContainer extends AbstractMessageContainer {
33
+class MessageContainer extends AbstractMessageContainer<Props> {
16
     /**
34
     /**
17
      * Instantiates a new instance of the component.
35
      * Instantiates a new instance of the component.
18
      *
36
      *
22
         super(props);
40
         super(props);
23
 
41
 
24
         this._keyExtractor = this._keyExtractor.bind(this);
42
         this._keyExtractor = this._keyExtractor.bind(this);
43
+        this._renderListEmptyComponent = this._renderListEmptyComponent.bind(this);
25
         this._renderMessageGroup = this._renderMessageGroup.bind(this);
44
         this._renderMessageGroup = this._renderMessageGroup.bind(this);
26
     }
45
     }
27
 
46
 
31
      * @inheritdoc
50
      * @inheritdoc
32
      */
51
      */
33
     render() {
52
     render() {
53
+        const data = this._getMessagesGroupedBySender();
54
+
34
         return (
55
         return (
35
             <FlatList
56
             <FlatList
36
-                data = { this._getMessagesGroupedBySender() }
37
-                inverted = { true }
57
+                ListEmptyComponent = { this._renderListEmptyComponent }
58
+                data = { data }
59
+
60
+                // Workaround for RN bug:
61
+                // https://github.com/facebook/react-native/issues/21196
62
+                inverted = { Boolean(data.length) }
38
                 keyExtractor = { this._keyExtractor }
63
                 keyExtractor = { this._keyExtractor }
39
                 keyboardShouldPersistTaps = 'always'
64
                 keyboardShouldPersistTaps = 'always'
40
                 renderItem = { this._renderMessageGroup }
65
                 renderItem = { this._renderMessageGroup }
58
         return `key_${index}`;
83
         return `key_${index}`;
59
     }
84
     }
60
 
85
 
61
-    _renderMessageGroup: Object => React$Element<*>;
86
+    _renderListEmptyComponent: () => React$Element<any>;
87
+
88
+    /**
89
+     * Renders a message when there are no messages in the chat yet.
90
+     *
91
+     * @returns {React$Element<any>}
92
+     */
93
+    _renderListEmptyComponent() {
94
+        const { _styles, t } = this.props;
95
+
96
+        return (
97
+            <View style = { styles.emptyComponentWrapper }>
98
+                <Text style = { _styles.emptyComponentText }>
99
+                    { t('chat.noMessagesMessage') }
100
+                </Text>
101
+            </View>
102
+        );
103
+    }
104
+
105
+    _renderMessageGroup: Object => React$Element<any>;
62
 
106
 
63
     /**
107
     /**
64
      * Renders a single chat message.
108
      * Renders a single chat message.
70
         return <ChatMessageGroup messages = { messages } />;
114
         return <ChatMessageGroup messages = { messages } />;
71
     }
115
     }
72
 }
116
 }
117
+
118
+/**
119
+ * Maps part of the redux state to the props of this component.
120
+ *
121
+ * @param {Object} state - The Redux state.
122
+ * @returns {Props}
123
+ */
124
+function _mapStateToProps(state) {
125
+    return {
126
+        _styles: ColorSchemeRegistry.get(state, 'Chat')
127
+    };
128
+}
129
+
130
+export default translate(connect(_mapStateToProps)(MessageContainer));

+ 12
- 0
react/features/chat/components/native/styles.js Прегледај датотеку

42
         flexDirection: 'column'
42
         flexDirection: 'column'
43
     },
43
     },
44
 
44
 
45
+    emptyComponentWrapper: {
46
+        alignSelf: 'center',
47
+        flex: 1,
48
+        padding: BoxModel.padding,
49
+        paddingTop: '10%'
50
+    },
51
+
45
     /**
52
     /**
46
      * A special padding to avoid issues on some devices (such as Android devices with custom suggestions bar).
53
      * A special padding to avoid issues on some devices (such as Android devices with custom suggestions bar).
47
      */
54
      */
143
         fontSize: 13
150
         fontSize: 13
144
     },
151
     },
145
 
152
 
153
+    emptyComponentText: {
154
+        color: schemeColor('displayName'),
155
+        textAlign: 'center'
156
+    },
157
+
146
     localMessageBubble: {
158
     localMessageBubble: {
147
         backgroundColor: schemeColor('localMsgBackground'),
159
         backgroundColor: schemeColor('localMsgBackground'),
148
         borderTopRightRadius: 0
160
         borderTopRightRadius: 0

+ 1
- 1
react/features/chat/components/web/MessageContainer.js Прегледај датотеку

14
  *
14
  *
15
  * @extends AbstractMessageContainer
15
  * @extends AbstractMessageContainer
16
  */
16
  */
17
-export default class MessageContainer extends AbstractMessageContainer {
17
+export default class MessageContainer extends AbstractMessageContainer<Props> {
18
     /**
18
     /**
19
      * Whether or not chat has been scrolled to the bottom of the screen. Used
19
      * Whether or not chat has been scrolled to the bottom of the screen. Used
20
      * to determine if chat should be scrolled automatically to the bottom when
20
      * to determine if chat should be scrolled automatically to the bottom when

Loading…
Откажи
Сачувај