Bladeren bron

rn: replace 3rd party chat library with custom implementation

master
Bettenbuk Zoltan 6 jaren geleden
bovenliggende
commit
0b6c51f666

+ 3
- 76
package-lock.json Bestand weergeven

@@ -2423,15 +2423,6 @@
2423 2423
       "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.8.2.tgz",
2424 2424
       "integrity": "sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw=="
2425 2425
     },
2426
-    "@expo/react-native-action-sheet": {
2427
-      "version": "1.1.2",
2428
-      "resolved": "https://registry.npmjs.org/@expo/react-native-action-sheet/-/react-native-action-sheet-1.1.2.tgz",
2429
-      "integrity": "sha512-//2EvHVBFVGSAzuJvG0I1UoQVzGJBo2f1CkO+RMnEWdR0FeWYmV7+pCThIroL1czRm/oOtoMxiGS6FgXt6QgVA==",
2430
-      "requires": {
2431
-        "hoist-non-react-statics": "^2.2.2",
2432
-        "prop-types": "^15.5.10"
2433
-      }
2434
-    },
2435 2426
     "@jitsi/sdp-interop": {
2436 2427
       "version": "0.1.14",
2437 2428
       "resolved": "https://registry.npmjs.org/@jitsi/sdp-interop/-/sdp-interop-0.1.14.tgz",
@@ -2843,12 +2834,13 @@
2843 2834
         "@segment/top-domain": "^3.0.0",
2844 2835
         "blueimp-md5": "^2.10.0",
2845 2836
         "json3": "^3.3.2",
2846
-        "lodash": "^4.17.4"
2837
+        "lodash": "^4.17.4",
2838
+        "ua-parser-js": "github:amplitude/ua-parser-js#ed538f16f5c6ecd8357da989b617d4f156dcf35d"
2847 2839
       },
2848 2840
       "dependencies": {
2849 2841
         "ua-parser-js": {
2850 2842
           "version": "github:amplitude/ua-parser-js#ed538f16f5c6ecd8357da989b617d4f156dcf35d",
2851
-          "from": "github:amplitude/ua-parser-js#ed538f16f5c6ecd8357da989b617d4f156dcf35d"
2843
+          "from": "github:amplitude/ua-parser-js#ed538f1"
2852 2844
         }
2853 2845
       }
2854 2846
     },
@@ -3506,14 +3498,6 @@
3506 3498
         "util.promisify": "^1.0.0"
3507 3499
       }
3508 3500
     },
3509
-    "babel-plugin-check-es2015-constants": {
3510
-      "version": "6.22.0",
3511
-      "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
3512
-      "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
3513
-      "requires": {
3514
-        "babel-runtime": "^6.22.0"
3515
-      }
3516
-    },
3517 3501
     "babel-plugin-emotion": {
3518 3502
       "version": "9.2.11",
3519 3503
       "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-9.2.11.tgz",
@@ -8868,11 +8852,6 @@
8868 8852
       "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.1.9.tgz",
8869 8853
       "integrity": "sha1-lkojxU5IiUBbSGGlyfBIDUUUHfo="
8870 8854
     },
8871
-    "keymirror": {
8872
-      "version": "0.1.1",
8873
-      "resolved": "https://registry.npmjs.org/keymirror/-/keymirror-0.1.1.tgz",
8874
-      "integrity": "sha1-kYiJ6hP40KQufFVyUO7nE63JXDU="
8875
-    },
8876 8855
     "killable": {
8877 8856
       "version": "1.0.1",
8878 8857
       "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -12181,37 +12160,11 @@
12181 12160
         "jssha": "^2.2.0"
12182 12161
       }
12183 12162
     },
12184
-    "react-native-communications": {
12185
-      "version": "2.2.1",
12186
-      "resolved": "https://registry.npmjs.org/react-native-communications/-/react-native-communications-2.2.1.tgz",
12187
-      "integrity": "sha1-eIO1ayCgAu63kMET+GFuqGksp5U="
12188
-    },
12189 12163
     "react-native-fast-image": {
12190 12164
       "version": "5.1.1",
12191 12165
       "resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-5.1.1.tgz",
12192 12166
       "integrity": "sha512-kEzgZxbbXYhy27u5GnhrKitn+XDBFAHSDUJdYC6llMi5cDPjgcqhOAQABj0K+ga5pn+/xPZLmD882rrUGiwVVA=="
12193 12167
     },
12194
-    "react-native-gifted-chat": {
12195
-      "version": "0.6.0",
12196
-      "resolved": "https://registry.npmjs.org/react-native-gifted-chat/-/react-native-gifted-chat-0.6.0.tgz",
12197
-      "integrity": "sha512-KYI/okKUZmjcJM3I6BP10KG1WNkCKBZhY8N47wk407dr+KqLS4+LR13UKo7j3f++5SrX2Ex+7vYvIQ2pBdzCiA==",
12198
-      "requires": {
12199
-        "@expo/react-native-action-sheet": "^1.0.1",
12200
-        "moment": "^2.19.0",
12201
-        "react-native-communications": "2.2.1",
12202
-        "react-native-lightbox": "^0.7.0",
12203
-        "react-native-parsed-text": "^0.0.20",
12204
-        "react-native-video": "^3.2.1",
12205
-        "uuid": "3.3.0"
12206
-      },
12207
-      "dependencies": {
12208
-        "uuid": {
12209
-          "version": "3.3.0",
12210
-          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.0.tgz",
12211
-          "integrity": "sha512-ijO9N2xY/YaOqQ5yz5c4sy2ZjWmA6AR6zASb/gdpeKZ8+948CxwfMW9RrKVk5may6ev8c0/Xguu32e2Llelpqw=="
12212
-        }
12213
-      }
12214
-    },
12215 12168
     "react-native-google-signin": {
12216 12169
       "version": "1.0.2",
12217 12170
       "resolved": "https://registry.npmjs.org/react-native-google-signin/-/react-native-google-signin-1.0.2.tgz",
@@ -12227,28 +12180,11 @@
12227 12180
       "resolved": "https://registry.npmjs.org/react-native-keep-awake/-/react-native-keep-awake-4.0.0.tgz",
12228 12181
       "integrity": "sha512-0Fotox+eLXQooeibVs3P60yASYUWjtRw9MZNmbuHt5UZQrgUrAKsE4jm7gTr4tPU1m1RkwGzcgUFpcOkh/ec7g=="
12229 12182
     },
12230
-    "react-native-lightbox": {
12231
-      "version": "0.7.0",
12232
-      "resolved": "https://registry.npmjs.org/react-native-lightbox/-/react-native-lightbox-0.7.0.tgz",
12233
-      "integrity": "sha512-HS3T4WlCd0Gb3us2d6Jse5m6KjNhngnKm35Wapq30WtQa9s+/VMmtuktbGPGaWtswcDyOj6qByeJBw9W80iPCA==",
12234
-      "requires": {
12235
-        "prop-types": "^15.5.10"
12236
-      }
12237
-    },
12238 12183
     "react-native-linear-gradient": {
12239 12184
       "version": "2.5.3",
12240 12185
       "resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.5.3.tgz",
12241 12186
       "integrity": "sha512-XdusrOXXlkI+yQpUW7YLeiq9cZiBwkvQX4XEkHPVrJ9H47gsKmdgBwObkZBzBQUP0dKK/Sg6aVpETEis4w43bQ=="
12242 12187
     },
12243
-    "react-native-parsed-text": {
12244
-      "version": "0.0.20",
12245
-      "resolved": "https://registry.npmjs.org/react-native-parsed-text/-/react-native-parsed-text-0.0.20.tgz",
12246
-      "integrity": "sha512-n77hYu64Tr3oclzIXBXXaiLh1WbMKdA2Y0x6bX/yqwxAM4afcObENY5VrNB+EsTBJBEDqrypA9D1p2cLEIHkuQ==",
12247
-      "requires": {
12248
-        "babel-plugin-check-es2015-constants": "6.22.0",
12249
-        "prop-types": "^15.5.10"
12250
-      }
12251
-    },
12252 12188
     "react-native-sound": {
12253 12189
       "version": "0.10.12",
12254 12190
       "resolved": "https://registry.npmjs.org/react-native-sound/-/react-native-sound-0.10.12.tgz",
@@ -12305,15 +12241,6 @@
12305 12241
         }
12306 12242
       }
12307 12243
     },
12308
-    "react-native-video": {
12309
-      "version": "3.2.1",
12310
-      "resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-3.2.1.tgz",
12311
-      "integrity": "sha512-Xansfoo/to80FwhM1HKlf7pCxDZ5RtV+kG3piCVvsNAhPY4GGwiOGUH9y3Y+mFQIDEWcY8I9j16lsFYAbnue3g==",
12312
-      "requires": {
12313
-        "keymirror": "0.1.1",
12314
-        "prop-types": "^15.5.10"
12315
-      }
12316
-    },
12317 12244
     "react-native-webrtc": {
12318 12245
       "version": "github:jitsi/react-native-webrtc#659d2fe417b52356b1b706636de97e23bae3e9f5",
12319 12246
       "from": "github:jitsi/react-native-webrtc#659d2fe417b52356b1b706636de97e23bae3e9f5",

+ 0
- 1
package.json Bestand weergeven

@@ -67,7 +67,6 @@
67 67
     "react-native-calendar-events": "1.6.4",
68 68
     "react-native-callstats": "3.58.2",
69 69
     "react-native-fast-image": "5.1.1",
70
-    "react-native-gifted-chat": "0.6.0",
71 70
     "react-native-google-signin": "1.0.2",
72 71
     "react-native-immersive": "2.0.0",
73 72
     "react-native-keep-awake": "4.0.0",

+ 1
- 1
react/features/chat/components/AbstractChatMessage.js Bestand weergeven

@@ -43,6 +43,6 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
43 43
     const { message } = ownProps;
44 44
 
45 45
     return {
46
-        _avatarURL: getAvatarURLByParticipantId(state, message.user._id)
46
+        _avatarURL: getAvatarURLByParticipantId(state, message.id)
47 47
     };
48 48
 }

+ 9
- 89
react/features/chat/components/native/Chat.js Bestand weergeven

@@ -1,8 +1,7 @@
1 1
 // @flow
2 2
 
3 3
 import React from 'react';
4
-import { SafeAreaView, View } from 'react-native';
5
-import { GiftedChat } from 'react-native-gifted-chat';
4
+import { KeyboardAvoidingView, SafeAreaView } from 'react-native';
6 5
 
7 6
 import { translate } from '../../../base/i18n';
8 7
 
@@ -20,7 +19,8 @@ import AbstractChat, {
20 19
     type Props
21 20
 } from '../AbstractChat';
22 21
 
23
-import ChatMessage from './ChatMessage';
22
+import ChatInputBar from './ChatInputBar';
23
+import MessageContainer from './MessageContainer';
24 24
 import styles from './styles';
25 25
 
26 26
 /**
@@ -28,111 +28,31 @@ import styles from './styles';
28 28
  * the mobile client.
29 29
  */
30 30
 class Chat extends AbstractChat<Props> {
31
-
32
-    /**
33
-       * Initializes a new instance.
34
-       *
35
-       * @inheritdoc
36
-       */
37
-    constructor(props: Props) {
38
-        super(props);
39
-
40
-        this._onSend = this._onSend.bind(this);
41
-        this._renderMessage = this._renderMessage.bind(this);
42
-        this._transformMessage = this._transformMessage.bind(this);
43
-    }
44
-
45 31
     /**
46 32
      * Implements React's {@link Component#render()}.
47 33
      *
48 34
      * @inheritdoc
49 35
      */
50 36
     render() {
51
-        // Gifted chat requires a special object format and a reversed list
52
-        // of messages.
53
-        const messages
54
-            = this.props._messages.map(this._transformMessage).reverse();
55
-
56 37
         return (
57 38
             <SlidingView
58 39
                 position = 'bottom'
59 40
                 show = { this.props._isOpen } >
60
-                <View style = { styles.chatContainer }>
41
+                <KeyboardAvoidingView
42
+                    behavior = 'padding'
43
+                    style = { styles.chatContainer }>
61 44
                     <Header>
62 45
                         <BackButton onPress = { this.props._onToggleChat } />
63 46
                         <HeaderLabel labelKey = 'chat.title' />
64 47
                     </Header>
65 48
                     <SafeAreaView style = { styles.backdrop }>
66
-                        <GiftedChat
67
-                            messages = { messages }
68
-                            onSend = { this._onSend }
69
-                            renderMessage = { this._renderMessage } />
49
+                        <MessageContainer messages = { this.props._messages } />
50
+                        <ChatInputBar onSend = { this.props._onSendMessage } />
70 51
                     </SafeAreaView>
71
-                </View>
52
+                </KeyboardAvoidingView>
72 53
             </SlidingView>
73 54
         );
74 55
     }
75
-
76
-    _onSend: (Array<Object>) => void;
77
-
78
-    /**
79
-     * Callback to trigger a message send action.
80
-     *
81
-     * @param {string} message - The chat message to display.
82
-     * @returns {void}
83
-     */
84
-    _onSend([ message ]) {
85
-        this.props._onSendMessage(message.text);
86
-    }
87
-
88
-    _renderMessage: Object => React$Element<*>
89
-
90
-    /**
91
-     * Renders a single message.
92
-     *
93
-     * @param {Object} messageProps - The message props object to be rendered.
94
-     * @returns {React$Element<*>}
95
-     */
96
-    _renderMessage(messageProps) {
97
-        const { currentMessage } = messageProps;
98
-
99
-        return (
100
-            <ChatMessage message = { currentMessage } />
101
-        );
102
-    }
103
-
104
-    _transformMessage: (Object, number) => Object;
105
-
106
-    /**
107
-     * Transforms a Jitsi message object to a format that gifted-chat can
108
-     * handle.
109
-     *
110
-     * @param {Object} message - The chat message in our internal format.
111
-     * @param {number} index - The index of the message in the array.
112
-     * @returns {Object}
113
-     */
114
-    _transformMessage(message, index) {
115
-        const system = message.messageType === 'error';
116
-
117
-        return (
118
-            {
119
-                _id: index,
120
-                createdAt: new Date(message.timestamp),
121
-                messageType: message.messageType,
122
-                system,
123
-                text: system
124
-                    ? this.props.t('chat.error', {
125
-                        error: message.error,
126
-                        originalText: message.message
127
-                    })
128
-                    : message.message,
129
-                user: {
130
-                    _id: message.id,
131
-                    name: message.displayName
132
-                }
133
-            }
134
-        );
135
-    }
136 56
 }
137 57
 
138 58
 export default translate(connect(_mapStateToProps, _mapDispatchToProps)(Chat));

+ 122
- 0
react/features/chat/components/native/ChatInputBar.js Bestand weergeven

@@ -0,0 +1,122 @@
1
+// @flow
2
+
3
+import React, { Component } from 'react';
4
+import { TextInput, View } from 'react-native';
5
+
6
+import { Platform } from '../../../base/react';
7
+
8
+import styles from './styles';
9
+
10
+type Props = {
11
+
12
+    /**
13
+     * Callback to invoke on message send.
14
+     */
15
+    onSend: Function
16
+};
17
+
18
+type State = {
19
+
20
+    /**
21
+     * Boolean to show if an extra padding needs to be added to the bar.
22
+     */
23
+    addPadding: boolean,
24
+
25
+    /**
26
+     * The value of the input field.
27
+     */
28
+    message: string
29
+};
30
+
31
+/**
32
+ * Implements the chat input bar with text field and action(s).
33
+ */
34
+export default class ChatInputBar extends Component<Props, State> {
35
+    /**
36
+     * Instantiates a new instance of the component.
37
+     *
38
+     * @inheritdoc
39
+     */
40
+    constructor(props: Props) {
41
+        super(props);
42
+
43
+        this.state = {
44
+            addPadding: false,
45
+            message: ''
46
+        };
47
+
48
+        this._onChangeText = this._onChangeText.bind(this);
49
+        this._onFocused = this._onFocused.bind(this);
50
+        this._onSubmit = this._onSubmit.bind(this);
51
+    }
52
+
53
+    /**
54
+     * Implements {@code Component#render}.
55
+     *
56
+     * @inheritdoc
57
+     */
58
+    render() {
59
+        return (
60
+            <View
61
+                style = { [
62
+                    styles.inputBar,
63
+                    this.state.addPadding ? styles.extraBarPadding : null
64
+                ] }>
65
+                <TextInput
66
+                    blurOnSubmit = { false }
67
+                    multiline = { false }
68
+                    onBlur = { this._onFocused(false) }
69
+                    onChangeText = { this._onChangeText }
70
+                    onFocus = { this._onFocused(true) }
71
+                    onSubmitEditing = { this._onSubmit }
72
+                    returnKeyType = 'send'
73
+                    style = { styles.inputField }
74
+                    value = { this.state.message } />
75
+            </View>
76
+        );
77
+    }
78
+
79
+    _onChangeText: string => void;
80
+
81
+    /**
82
+     * Callback to handle the change of the value of the text field.
83
+     *
84
+     * @param {string} text - The current value of the field.
85
+     * @returns {void}
86
+     */
87
+    _onChangeText(text) {
88
+        this.setState({
89
+            message: text
90
+        });
91
+    }
92
+
93
+    _onFocused: boolean => Function;
94
+
95
+    /**
96
+     * Constructs a callback to be used to update the padding of the field if necessary.
97
+     *
98
+     * @param {boolean} focused - True of the field is focused.
99
+     * @returns {Function}
100
+     */
101
+    _onFocused(focused) {
102
+        return () => {
103
+            Platform.OS === 'android' && this.setState({
104
+                addPadding: focused
105
+            });
106
+        };
107
+    }
108
+
109
+    _onSubmit: () => void;
110
+
111
+    /**
112
+     * Callback to handle the submit event of the text field.
113
+     *
114
+     * @returns {void}
115
+     */
116
+    _onSubmit() {
117
+        const message = this.state.message.trim();
118
+
119
+        message && this.props.onSend(message);
120
+        this.setState({ message: '' });
121
+    }
122
+}

+ 9
- 4
react/features/chat/components/native/ChatMessage.js Bestand weergeven

@@ -35,7 +35,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
35 35
     render() {
36 36
         const { message } = this.props;
37 37
         const timeStamp = getLocalizedDateFormatter(
38
-            message.createdAt).format(TIMESTAMP_FORMAT);
38
+            new Date(message.timestamp)).format(TIMESTAMP_FORMAT);
39 39
         const localMessage = message.messageType === 'local';
40 40
 
41 41
         // Style arrays that need to be updated in various scenarios, such as
@@ -53,7 +53,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
53 53
 
54 54
             // The bubble needs to be differently styled.
55 55
             textWrapperStyle.push(styles.ownTextWrapper);
56
-        } else if (message.system) {
56
+        } else if (message.messageType === 'error') {
57 57
             // The bubble needs to be differently styled.
58 58
             textWrapperStyle.push(styles.systemTextWrapper);
59 59
         }
@@ -74,7 +74,12 @@ class ChatMessage extends AbstractChatMessage<Props> {
74 74
                             !localMessage && this._renderDisplayName()
75 75
                         }
76 76
                         <Text style = { styles.messageText }>
77
-                            { message.text }
77
+                            { message.messageType === 'error'
78
+                                ? this.props.t('chat.error', {
79
+                                    error: message.error,
80
+                                    originalText: message.message
81
+                                })
82
+                                : message.message }
78 83
                         </Text>
79 84
                     </View>
80 85
                     <Text style = { styles.timeText }>
@@ -112,7 +117,7 @@ class ChatMessage extends AbstractChatMessage<Props> {
112 117
 
113 118
         return (
114 119
             <Text style = { styles.displayName }>
115
-                { message.user.name }
120
+                { message.displayName }
116 121
             </Text>
117 122
         );
118 123
     }

+ 76
- 0
react/features/chat/components/native/MessageContainer.js Bestand weergeven

@@ -0,0 +1,76 @@
1
+// @flow
2
+
3
+import React, { Component } from 'react';
4
+import { FlatList } from 'react-native';
5
+
6
+import ChatMessage from './ChatMessage';
7
+import styles from './styles';
8
+
9
+type Props = {
10
+
11
+    /**
12
+     * The messages array to render.
13
+     */
14
+    messages: Array<Object>
15
+}
16
+
17
+/**
18
+ * Implements a container to render all the chat messages in a conference.
19
+ */
20
+export default class MessageContainer extends Component<Props> {
21
+    /**
22
+     * Instantiates a new instance of the component.
23
+     *
24
+     * @inheritdoc
25
+     */
26
+    constructor(props: Props) {
27
+        super(props);
28
+
29
+        this._keyExtractor = this._keyExtractor.bind(this);
30
+        this._renderMessage = this._renderMessage.bind(this);
31
+    }
32
+
33
+    /**
34
+     * Implements {@code Component#render}.
35
+     *
36
+     * @inheritdoc
37
+     */
38
+    render() {
39
+        return (
40
+            <FlatList
41
+                data = { this.props.messages }
42
+                inverted = { true }
43
+                keyExtractor = { this._keyExtractor }
44
+                renderItem = { this._renderMessage }
45
+                style = { styles.messageContainer } />
46
+        );
47
+    }
48
+
49
+    _keyExtractor: Object => string
50
+
51
+    /**
52
+     * Key extractor for the flatlist.
53
+     *
54
+     * @param {Object} item - The flatlist item that we need the key to be
55
+     * generated for.
56
+     * @param {number} index - The index of the element.
57
+     * @returns {string}
58
+     */
59
+    _keyExtractor(item, index) {
60
+        return `key_${index}`;
61
+    }
62
+
63
+    _renderMessage: Object => React$Element<*>;
64
+
65
+    /**
66
+     * Renders a single chat message.
67
+     *
68
+     * @param {Object} message - The chat message to render.
69
+     * @returns {React$Element<*>}
70
+     */
71
+    _renderMessage({ item: message }) {
72
+        return (
73
+            <ChatMessage message = { message } />
74
+        );
75
+    }
76
+}

+ 25
- 1
react/features/chat/components/native/styles.js Bestand weergeven

@@ -1,6 +1,6 @@
1 1
 // @flow
2 2
 
3
-import { ColorPalette } from '../../../base/styles';
3
+import { BoxModel, ColorPalette } from '../../../base/styles';
4 4
 
5 5
 /**
6 6
  * The styles of the feature chat.
@@ -28,6 +28,7 @@ export default {
28 28
     },
29 29
 
30 30
     chatContainer: {
31
+        alignItems: 'stretch',
31 32
         flex: 1,
32 33
         flexDirection: 'column'
33 34
     },
@@ -49,6 +50,29 @@ export default {
49 50
         fontSize: 13
50 51
     },
51 52
 
53
+    /**
54
+     * A special padding to avoid issues on some devices (such as Android devices with custom suggestions bar).
55
+     */
56
+    extraBarPadding: {
57
+        paddingBottom: 30
58
+    },
59
+
60
+    inputBar: {
61
+        borderTopColor: 'rgb(209, 219, 231)',
62
+        borderTopWidth: 1,
63
+        flexDirection: 'row',
64
+        paddingHorizontal: BoxModel.padding
65
+    },
66
+
67
+    inputField: {
68
+        flex: 1,
69
+        height: 48
70
+    },
71
+
72
+    messageContainer: {
73
+        flex: 1
74
+    },
75
+
52 76
     /**
53 77
      * The message text itself.
54 78
      */

+ 5
- 0
react/features/chat/functions.js Bestand weergeven

@@ -14,6 +14,11 @@ export function getUnreadCount(state: Object) {
14 14
         return 0;
15 15
     }
16 16
 
17
+    if (navigator.product === 'ReactNative') {
18
+        // React native stores the messages in a reversed order.
19
+        return messages.indexOf(lastReadMessage);
20
+    }
21
+
17 22
     const lastReadIndex = messages.lastIndexOf(lastReadMessage);
18 23
 
19 24
     return messagesCount - (lastReadIndex + 1);

+ 14
- 5
react/features/chat/reducer.js Bestand weergeven

@@ -22,14 +22,22 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
22 22
             timestamp: action.timestamp
23 23
         };
24 24
 
25
+        // React native, unlike web, needs a reverse sorted message list.
26
+        const messages = navigator.product === 'ReactNative'
27
+            ? [
28
+                newMessage,
29
+                ...state.messages
30
+            ]
31
+            : [
32
+                ...state.messages,
33
+                newMessage
34
+            ];
35
+
25 36
         return {
26 37
             ...state,
27 38
             lastReadMessage:
28 39
                 action.hasRead ? newMessage : state.lastReadMessage,
29
-            messages: [
30
-                ...state.messages,
31
-                newMessage
32
-            ]
40
+            messages
33 41
         };
34 42
     }
35 43
 
@@ -44,7 +52,8 @@ ReducerRegistry.register('features/chat', (state = DEFAULT_STATE, action) => {
44 52
         return {
45 53
             ...state,
46 54
             isOpen: !state.isOpen,
47
-            lastReadMessage: state.messages[state.messages.length - 1]
55
+            lastReadMessage: state.messages[
56
+                navigator.product === 'ReactNative' ? 0 : state.messages.length - 1]
48 57
         };
49 58
     }
50 59
 

Laden…
Annuleren
Opslaan