react-native-gifted-chat
react-native-gifted-chat copied to clipboard
Chat freezes on Android 13 phone
Issue Description
After updating my phone to android 13, I noticed that the chat in the release version of our app freezes after a short time. I can easily reproduce the problem in the sample app by enabling the typing indicator.
Here you can see a video of the problem: https://user-images.githubusercontent.com/86466408/188175596-c715b724-8b34-40a4-9a4b-26eebe99fdc2.mp4
Meanwhile I found out that the typing indicator within the inverted FlatList is the cause of the problem, and therefore it is actually an React-Native issue. I have already created an issue on the React-Native repository: https://github.com/facebook/react-native/issues/34583
Nevertheless, I created an additional issue here, in case other users of gifted chat are facing the same problem and are looking for a solution here. Also, it might help to draw attention to the problem.
Steps to Reproduce / Code Snippets
- Prepare to start the example app (within the
examplefolder of this repo). - Start the app on an android phone. I am currently not sure which phones are affected, as I was only able to reproduce this on my Pixel 4 / Android 13.
- Activate the typing indicator by tapping the message icon on the right bottom.
- After a few seconds (up to 20), the app freezes.
Expected Results
The app should not freeze.
Additional Information
- Nodejs version: 14.19.1
- React version: 17.0.1
- React Native version: 0.64.3
- react-native-gifted-chat version: 1.0.4
- Platform(s) (iOS, Android, or both?): only Android (possibly only 13)
- TypeScript version: 4.3.5
Did you ever find a solution for this? I resorted to editing the package directly in node_modules and came up with the following solution:
-
GiftedChat.js, line 172- change 'inverted' to false
-
add 'transform: [{rotate: '180deg',}]' to the following locations:
- MessageContainer.js, line 31 (listStyle)
- SystemMessage.js, line 13 (container)
- Day.js, line 15 (container)
- Message.js, line 19 (left) and line 29 (right)
Obviously this is not a permanent fix and the problem is a RN issue, but for a temporary workaround it's fine.
If it helps anyone, we currently use the { scaleY: -1 } fix for all devices with android >= 13.
This is the patch we are using for Gifted Chat 1.1.0
diff --git a/node_modules/react-native-gifted-chat/.DS_Store b/node_modules/react-native-gifted-chat/.DS_Store
new file mode 100644
index 0000000..e69de29
diff --git a/node_modules/react-native-gifted-chat/lib/MessageContainer.js b/node_modules/react-native-gifted-chat/lib/MessageContainer.js
index 6bdf6da..2aed446 100644
--- a/node_modules/react-native-gifted-chat/lib/MessageContainer.js
+++ b/node_modules/react-native-gifted-chat/lib/MessageContainer.js
@@ -168,6 +168,9 @@ export default class MessageContainer extends React.PureComponent {
}
};
this.keyExtractor = (item) => `${item._id}`;
+ const version = parseFloat(Platform.Version);
+ this.renderMessageContainer = Platform.OS === 'android' && version >= 33 ? this.renderMessageContainerWithAndroidFix.bind(this) : this.renderRegularMessageContainer.bind(this);
+ this.invert = (orignialRenderFunction) => (props) => <View style={{scaleY: -1}}>{orignialRenderFunction(props)}</View>;
}
scrollTo(options) {
if (this.props.forwardRef && this.props.forwardRef.current && options) {
@@ -189,10 +192,17 @@ export default class MessageContainer extends React.PureComponent {
</TouchableOpacity>
</View>);
}
- render() {
+ renderMessageContainerWithAndroidFix() {
+ const { inverted } = this.props;
+ return (<FlatList ref={this.props.forwardRef} extraData={[this.props.extraData, this.props.isTyping]} keyExtractor={this.keyExtractor} enableEmptySections automaticallyAdjustContentInsets={false} data={this.props.messages} style={[styles.listStyle, inverted ? {scaleY: -1} : null]} contentContainerStyle={styles.contentContainerStyle} renderItem={inverted ? this.invert(this.renderRow) : this.renderRow} {...this.props.invertibleScrollViewProps} ListEmptyComponent={this.renderChatEmpty} ListFooterComponent={inverted ? this.invert(this.renderHeaderWrapper) : this.renderFooter} ListHeaderComponent={inverted ? this.invert(this.renderFooter) : this.renderHeaderWrapper} onScroll={this.handleOnScroll} scrollEventThrottle={100} onLayout={this.onLayoutList} onEndReached={this.onEndReached} onEndReachedThreshold={0.1} {...this.props.listViewProps} inverted={false} />);
+ }
+ renderRegularMessageContainer() {
const { inverted } = this.props;
+ return (<FlatList ref={this.props.forwardRef} extraData={[this.props.extraData, this.props.isTyping]} keyExtractor={this.keyExtractor} enableEmptySections automaticallyAdjustContentInsets={false} inverted={inverted} data={this.props.messages} style={styles.listStyle} contentContainerStyle={styles.contentContainerStyle} renderItem={this.renderRow} {...this.props.invertibleScrollViewProps} ListEmptyComponent={this.renderChatEmpty} ListFooterComponent={inverted ? this.renderHeaderWrapper : this.renderFooter} ListHeaderComponent={inverted ? this.renderFooter : this.renderHeaderWrapper} onScroll={this.handleOnScroll} scrollEventThrottle={100} onLayout={this.onLayoutList} onEndReached={this.onEndReached} onEndReachedThreshold={0.1} {...this.props.listViewProps}/>);
+ }
+ render() {
return (<View style={this.props.alignTop ? styles.containerAlignTop : styles.container}>
- <FlatList ref={this.props.forwardRef} extraData={[this.props.extraData, this.props.isTyping]} keyExtractor={this.keyExtractor} enableEmptySections automaticallyAdjustContentInsets={false} inverted={inverted} data={this.props.messages} style={styles.listStyle} contentContainerStyle={styles.contentContainerStyle} renderItem={this.renderRow} {...this.props.invertibleScrollViewProps} ListEmptyComponent={this.renderChatEmpty} ListFooterComponent={inverted ? this.renderHeaderWrapper : this.renderFooter} ListHeaderComponent={inverted ? this.renderFooter : this.renderHeaderWrapper} onScroll={this.handleOnScroll} scrollEventThrottle={100} onLayout={this.onLayoutList} onEndReached={this.onEndReached} onEndReachedThreshold={0.1} {...this.props.listViewProps}/>
+ {this.renderMessageContainer()}
{this.state.showScrollBottom && this.props.scrollToBottom
? this.renderScrollToBottomWrapper()
: null}
For all those who use a RN >= 70 it is important to mention that scaleY is no longer supported. To work around this we are currently using a second hack in the index.js of our app, which makes scaleY available again:
import ViewReactNativeStyleAttributes from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes'
ViewReactNativeStyleAttributes.scaleY = true
If it helps anyone, we currently use the
{ scaleY: -1 }fix for all devices with android >= 13. This is the patch we are using for Gifted Chat 1.1.0diff --git a/node_modules/react-native-gifted-chat/.DS_Store b/node_modules/react-native-gifted-chat/.DS_Store new file mode 100644 index 0000000..e69de29 diff --git a/node_modules/react-native-gifted-chat/lib/MessageContainer.js b/node_modules/react-native-gifted-chat/lib/MessageContainer.js index 6bdf6da..2aed446 100644 --- a/node_modules/react-native-gifted-chat/lib/MessageContainer.js +++ b/node_modules/react-native-gifted-chat/lib/MessageContainer.js @@ -168,6 +168,9 @@ export default class MessageContainer extends React.PureComponent { } }; this.keyExtractor = (item) => `${item._id}`; + const version = parseFloat(Platform.Version); + this.renderMessageContainer = Platform.OS === 'android' && version >= 33 ? this.renderMessageContainerWithAndroidFix.bind(this) : this.renderRegularMessageContainer.bind(this); + this.invert = (orignialRenderFunction) => (props) => <View style={{scaleY: -1}}>{orignialRenderFunction(props)}</View>; } scrollTo(options) { if (this.props.forwardRef && this.props.forwardRef.current && options) { @@ -189,10 +192,17 @@ export default class MessageContainer extends React.PureComponent { </TouchableOpacity> </View>); } - render() { + renderMessageContainerWithAndroidFix() { + const { inverted } = this.props; + return (<FlatList ref={this.props.forwardRef} extraData={[this.props.extraData, this.props.isTyping]} keyExtractor={this.keyExtractor} enableEmptySections automaticallyAdjustContentInsets={false} data={this.props.messages} style={[styles.listStyle, inverted ? {scaleY: -1} : null]} contentContainerStyle={styles.contentContainerStyle} renderItem={inverted ? this.invert(this.renderRow) : this.renderRow} {...this.props.invertibleScrollViewProps} ListEmptyComponent={this.renderChatEmpty} ListFooterComponent={inverted ? this.invert(this.renderHeaderWrapper) : this.renderFooter} ListHeaderComponent={inverted ? this.invert(this.renderFooter) : this.renderHeaderWrapper} onScroll={this.handleOnScroll} scrollEventThrottle={100} onLayout={this.onLayoutList} onEndReached={this.onEndReached} onEndReachedThreshold={0.1} {...this.props.listViewProps} inverted={false} />); + } + renderRegularMessageContainer() { const { inverted } = this.props; + return (<FlatList ref={this.props.forwardRef} extraData={[this.props.extraData, this.props.isTyping]} keyExtractor={this.keyExtractor} enableEmptySections automaticallyAdjustContentInsets={false} inverted={inverted} data={this.props.messages} style={styles.listStyle} contentContainerStyle={styles.contentContainerStyle} renderItem={this.renderRow} {...this.props.invertibleScrollViewProps} ListEmptyComponent={this.renderChatEmpty} ListFooterComponent={inverted ? this.renderHeaderWrapper : this.renderFooter} ListHeaderComponent={inverted ? this.renderFooter : this.renderHeaderWrapper} onScroll={this.handleOnScroll} scrollEventThrottle={100} onLayout={this.onLayoutList} onEndReached={this.onEndReached} onEndReachedThreshold={0.1} {...this.props.listViewProps}/>); + } + render() { return (<View style={this.props.alignTop ? styles.containerAlignTop : styles.container}> - <FlatList ref={this.props.forwardRef} extraData={[this.props.extraData, this.props.isTyping]} keyExtractor={this.keyExtractor} enableEmptySections automaticallyAdjustContentInsets={false} inverted={inverted} data={this.props.messages} style={styles.listStyle} contentContainerStyle={styles.contentContainerStyle} renderItem={this.renderRow} {...this.props.invertibleScrollViewProps} ListEmptyComponent={this.renderChatEmpty} ListFooterComponent={inverted ? this.renderHeaderWrapper : this.renderFooter} ListHeaderComponent={inverted ? this.renderFooter : this.renderHeaderWrapper} onScroll={this.handleOnScroll} scrollEventThrottle={100} onLayout={this.onLayoutList} onEndReached={this.onEndReached} onEndReachedThreshold={0.1} {...this.props.listViewProps}/> + {this.renderMessageContainer()} {this.state.showScrollBottom && this.props.scrollToBottom ? this.renderScrollToBottomWrapper() : null}For all those who use a RN >= 70 it is important to mention that scaleY is no longer supported. To work around this we are currently using a second hack in the
index.jsof our app, which makes scaleY available again:import ViewReactNativeStyleAttributes from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes' ViewReactNativeStyleAttributes.scaleY = true
Hey @c-goettert , I had one question about your solution. First of all thanks. Are you having an issue where the messages container stays resized when closing the text input? It started after the solution was implemented. Thanks!
@devlom113 no, we don't have that problem. However, we don't use the standard message input from gifted-chat, but our own component, so I can't really assess that issue.
This is the patch I am using for Gifted Chat 0.16.3
thanks @wjb8!
diff --git a/node_modules/react-native-gifted-chat/lib/Day.js b/node_modules/react-native-gifted-chat/lib/Day.js
index 7829a72..129c975 100644
--- a/node_modules/react-native-gifted-chat/lib/Day.js
+++ b/node_modules/react-native-gifted-chat/lib/Day.js
@@ -11,6 +11,7 @@ const styles = StyleSheet.create({
justifyContent: 'center',
marginTop: 5,
marginBottom: 10,
+ transform: [{rotate: '180deg',}]
},
text: {
backgroundColor: Color.backgroundTransparent,
diff --git a/node_modules/react-native-gifted-chat/lib/GiftedChat.js b/node_modules/react-native-gifted-chat/lib/GiftedChat.js
index 73ba4ee..eb06680 100644
--- a/node_modules/react-native-gifted-chat/lib/GiftedChat.js
+++ b/node_modules/react-native-gifted-chat/lib/GiftedChat.js
@@ -167,7 +167,7 @@ class GiftedChat extends React.Component {
}
};
this.invertibleScrollViewProps = {
- inverted: this.props.inverted,
+ inverted: false,
keyboardShouldPersistTaps: this.props.keyboardShouldPersistTaps,
onKeyboardWillShow: this.onKeyboardWillShow,
onKeyboardWillHide: this.onKeyboardWillHide,
diff --git a/node_modules/react-native-gifted-chat/lib/Message.js b/node_modules/react-native-gifted-chat/lib/Message.js
index 2490081..aebbde3 100644
--- a/node_modules/react-native-gifted-chat/lib/Message.js
+++ b/node_modules/react-native-gifted-chat/lib/Message.js
@@ -14,6 +14,7 @@ const styles = {
justifyContent: 'flex-start',
marginLeft: 8,
marginRight: 0,
+ transform: [{rotate: '180deg',}]
},
}),
right: StyleSheet.create({
@@ -23,6 +24,7 @@ const styles = {
justifyContent: 'flex-end',
marginLeft: 0,
marginRight: 8,
+ transform: [{rotate: '180deg',}]
},
}),
};
diff --git a/node_modules/react-native-gifted-chat/lib/MessageContainer.js b/node_modules/react-native-gifted-chat/lib/MessageContainer.js
index 193772a..50908d1 100644
--- a/node_modules/react-native-gifted-chat/lib/MessageContainer.js
+++ b/node_modules/react-native-gifted-chat/lib/MessageContainer.js
@@ -27,6 +27,13 @@ const styles = StyleSheet.create({
},
listStyle: {
flex: 1,
+ transform: [{ rotate: '180deg', }]
+ },
+ listFooterComponentStyle: {
+ transform: [{ rotate: '180deg', }]
+ },
+ listHeaderComponentStyle: {
+ transform: [{ rotate: '180deg', }]
},
scrollToBottomStyle: {
opacity: 0.8,
@@ -55,18 +62,17 @@ export default class MessageContainer extends React.PureComponent {
this.attachKeyboardListeners = () => {
const { invertibleScrollViewProps: invertibleProps } = this.props;
if (invertibleProps) {
- Keyboard.addListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
- Keyboard.addListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
- Keyboard.addListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
- Keyboard.addListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
+ this.willShowSub = Keyboard.addListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
+ this.didShowSub = Keyboard.addListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
+ this.willHideSub = Keyboard.addListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
+ this.didHideSub = Keyboard.addListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
}
};
this.detachKeyboardListeners = () => {
- const { invertibleScrollViewProps: invertibleProps } = this.props;
- Keyboard.removeListener('keyboardWillShow', invertibleProps.onKeyboardWillShow);
- Keyboard.removeListener('keyboardDidShow', invertibleProps.onKeyboardDidShow);
- Keyboard.removeListener('keyboardWillHide', invertibleProps.onKeyboardWillHide);
- Keyboard.removeListener('keyboardDidHide', invertibleProps.onKeyboardDidHide);
+ this.willShowSub?.remove();
+ this.didShowSub?.remove();
+ this.willHideSub?.remove();
+ this.didHideSub?.remove();
};
this.renderTypingIndicator = () => {
if (Platform.OS === 'web') {
@@ -156,8 +162,8 @@ export default class MessageContainer extends React.PureComponent {
this.renderChatEmpty = () => {
if (this.props.renderChatEmpty) {
return this.props.inverted ? (this.props.renderChatEmpty()) : (<View style={styles.emptyChatContainer}>
- {this.props.renderChatEmpty()}
- </View>);
+ {this.props.renderChatEmpty()}
+ </View>);
}
return <View style={styles.container}/>;
};
@@ -220,19 +226,19 @@ export default class MessageContainer extends React.PureComponent {
renderScrollToBottomWrapper() {
const propsStyle = this.props.scrollToBottomStyle || {};
return (<View style={[styles.scrollToBottomStyle, propsStyle]}>
- <TouchableOpacity onPress={() => this.scrollToBottom()} hitSlop={{ top: 5, left: 5, right: 5, bottom: 5 }}>
- {this.renderScrollBottomComponent()}
- </TouchableOpacity>
- </View>);
+ <TouchableOpacity onPress={() => this.scrollToBottom()} hitSlop={{ top: 5, left: 5, right: 5, bottom: 5 }}>
+ {this.renderScrollBottomComponent()}
+ </TouchableOpacity>
+ </View>);
}
render() {
const { inverted } = this.props;
return (<View style={this.props.alignTop ? styles.containerAlignTop : styles.container}>
- {this.state.showScrollBottom && this.props.scrollToBottom
- ? this.renderScrollToBottomWrapper()
- : null}
- <FlatList ref={this.props.forwardRef} extraData={[this.props.extraData, this.props.isTyping]} keyExtractor={this.keyExtractor} enableEmptySections automaticallyAdjustContentInsets={false} inverted={inverted} data={this.props.messages} style={styles.listStyle} contentContainerStyle={styles.contentContainerStyle} renderItem={this.renderRow} {...this.props.invertibleScrollViewProps} ListEmptyComponent={this.renderChatEmpty} ListFooterComponent={inverted ? this.renderHeaderWrapper : this.renderFooter} ListHeaderComponent={inverted ? this.renderFooter : this.renderHeaderWrapper} onScroll={this.handleOnScroll} scrollEventThrottle={100} onLayout={this.onLayoutList} onEndReached={this.onEndReached} onEndReachedThreshold={0.1} {...this.props.listViewProps}/>
- </View>);
+ {this.state.showScrollBottom && this.props.scrollToBottom
+ ? this.renderScrollToBottomWrapper()
+ : null}
+ <FlatList ref={this.props.forwardRef} extraData={[this.props.extraData, this.props.isTyping]} keyExtractor={this.keyExtractor} enableEmptySections automaticallyAdjustContentInsets={false} inverted={inverted} data={this.props.messages} style={styles.listStyle} contentContainerStyle={styles.contentContainerStyle} renderItem={this.renderRow} {...this.props.invertibleScrollViewProps} ListEmptyComponent={this.renderChatEmpty} ListFooterComponent={inverted ? this.renderHeaderWrapper : this.renderFooter} ListFooterComponentStyle={styles.listFooterComponentStyle} ListHeaderComponentStyle={styles.listHeaderComponentStyle} ListHeaderComponent={inverted ? this.renderFooter : this.renderHeaderWrapper} onScroll={this.handleOnScroll} scrollEventThrottle={100} onLayout={this.onLayoutList} onEndReached={this.onEndReached} onEndReachedThreshold={0.1} {...this.props.listViewProps}/>
+ </View>);
}
}
MessageContainer.defaultProps = {
diff --git a/node_modules/react-native-gifted-chat/lib/SystemMessage.js b/node_modules/react-native-gifted-chat/lib/SystemMessage.js
index c2e1516..a6180e5 100644
--- a/node_modules/react-native-gifted-chat/lib/SystemMessage.js
+++ b/node_modules/react-native-gifted-chat/lib/SystemMessage.js
@@ -10,6 +10,7 @@ const styles = StyleSheet.create({
flex: 1,
marginTop: 5,
marginBottom: 10,
+ transform: [{rotate: '180deg',}]
},
text: {
backgroundColor: Color.backgroundTransparent,
Any update?