[FIX] Swipe animations (#1044)

* Comment removeClippedSubviews

* Comment width animation

* Remove redux from RoomItem

* Fix wrong re-render comparison

* Remove listener

* Raise minDeltaX

* memo actions

* Spring with native driver

* Refactor functions

* Fix props issues

* Remove RoomItem.height

* Long swipe

* Refactor animations

* this.rowTranslation -> this.transX

* Moved state to this

* Fix favorite button
This commit is contained in:
Diego Mello 2019-07-15 13:54:28 -03:00 committed by GitHub
parent ba217ca5ff
commit c2497145fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 237 additions and 201 deletions

View File

@ -0,0 +1,129 @@
import React from 'react';
import { Animated, View, Text } from 'react-native';
import { RectButton } from 'react-native-gesture-handler';
import PropTypes from 'prop-types';
import I18n from '../../i18n';
import styles, { ACTION_WIDTH, LONG_SWIPE } from './styles';
import { CustomIcon } from '../../lib/Icons';
export const LeftActions = React.memo(({
transX, isRead, width, onToggleReadPress
}) => {
const translateX = transX.interpolate({
inputRange: [0, ACTION_WIDTH],
outputRange: [-ACTION_WIDTH, 0]
});
const translateXIcon = transX.interpolate({
inputRange: [0, ACTION_WIDTH, LONG_SWIPE - 2, LONG_SWIPE],
outputRange: [0, 0, -LONG_SWIPE + ACTION_WIDTH + 2, 0],
extrapolate: 'clamp'
});
return (
<View
style={styles.actionsContainer}
pointerEvents='box-none'
>
<Animated.View
style={[
styles.actionLeftButtonContainer,
{
right: width - ACTION_WIDTH,
width,
transform: [{ translateX }]
}
]}
>
<Animated.View
style={[
styles.actionLeftButtonContainer,
{
right: 0,
transform: [{ translateX: translateXIcon }]
}
]}
>
<RectButton style={styles.actionButton} onPress={onToggleReadPress}>
<React.Fragment>
<CustomIcon size={20} name={isRead ? 'flag' : 'check'} color='white' />
<Text style={styles.actionText}>{I18n.t(isRead ? 'Unread' : 'Read')}</Text>
</React.Fragment>
</RectButton>
</Animated.View>
</Animated.View>
</View>
);
});
export const RightActions = React.memo(({
transX, favorite, width, toggleFav, onHidePress
}) => {
const translateXFav = transX.interpolate({
inputRange: [-width / 2, -ACTION_WIDTH * 2, 0],
outputRange: [width / 2, width - ACTION_WIDTH * 2, width]
});
const translateXHide = transX.interpolate({
inputRange: [-width, -LONG_SWIPE, -ACTION_WIDTH * 2, 0],
outputRange: [0, width - LONG_SWIPE, width - ACTION_WIDTH, width]
});
return (
<View
style={{
position: 'absolute',
left: 0,
right: 0,
height: 75,
flexDirection: 'row'
}}
pointerEvents='box-none'
>
<Animated.View
style={[
styles.actionRightButtonContainer,
{
width,
transform: [{ translateX: translateXFav }]
}
]}
>
<RectButton style={[styles.actionButton, { backgroundColor: '#ffbb00' }]} onPress={toggleFav}>
<React.Fragment>
<CustomIcon size={20} name={favorite ? 'Star-filled' : 'star'} color='white' />
<Text style={styles.actionText}>{I18n.t(favorite ? 'Unfavorite' : 'Favorite')}</Text>
</React.Fragment>
</RectButton>
</Animated.View>
<Animated.View
style={[
styles.actionRightButtonContainer,
{
width,
transform: [{ translateX: translateXHide }]
}
]}
>
<RectButton style={[styles.actionButton, { backgroundColor: '#54585e' }]} onPress={onHidePress}>
<React.Fragment>
<CustomIcon size={20} name='eye-off' color='white' />
<Text style={styles.actionText}>{I18n.t('Hide')}</Text>
</React.Fragment>
</RectButton>
</Animated.View>
</View>
);
});
LeftActions.propTypes = {
transX: PropTypes.object,
isRead: PropTypes.bool,
width: PropTypes.number,
onToggleReadPress: PropTypes.func
};
RightActions.propTypes = {
transX: PropTypes.object,
favorite: PropTypes.bool,
width: PropTypes.number,
toggleFav: PropTypes.func,
onHidePress: PropTypes.func
};

View File

@ -2,27 +2,22 @@ import React from 'react';
import moment from 'moment'; import moment from 'moment';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { View, Text, Animated } from 'react-native'; import { View, Text, Animated } from 'react-native';
import { connect } from 'react-redux';
import { RectButton, PanGestureHandler, State } from 'react-native-gesture-handler'; import { RectButton, PanGestureHandler, State } from 'react-native-gesture-handler';
import Avatar from '../../containers/Avatar'; import Avatar from '../../containers/Avatar';
import I18n from '../../i18n'; import I18n from '../../i18n';
import styles, { ROW_HEIGHT } from './styles'; import styles, {
ROW_HEIGHT, ACTION_WIDTH, SMALL_SWIPE, LONG_SWIPE
} from './styles';
import UnreadBadge from './UnreadBadge'; import UnreadBadge from './UnreadBadge';
import TypeIcon from './TypeIcon'; import TypeIcon from './TypeIcon';
import LastMessage from './LastMessage'; import LastMessage from './LastMessage';
import { CustomIcon } from '../../lib/Icons'; import { LeftActions, RightActions } from './Actions';
export { ROW_HEIGHT }; export { ROW_HEIGHT };
const OPTION_WIDTH = 80; const attrs = ['name', 'unread', 'userMentions', 'showLastMessage', 'alert', 'type', 'width', 'isRead', 'favorite'];
const SMALL_SWIPE = 40;
const attrs = ['name', 'unread', 'userMentions', 'showLastMessage', 'alert', 'type', 'width'];
@connect(state => ({
userId: state.login.user && state.login.user.id,
username: state.login.user && state.login.user.username,
token: state.login.user && state.login.user.token
}))
export default class RoomItem extends React.Component { export default class RoomItem extends React.Component {
static propTypes = { static propTypes = {
type: PropTypes.string.isRequired, type: PropTypes.string.isRequired,
@ -43,7 +38,6 @@ export default class RoomItem extends React.Component {
avatarSize: PropTypes.number, avatarSize: PropTypes.number,
testID: PropTypes.string, testID: PropTypes.string,
width: PropTypes.number, width: PropTypes.number,
height: PropTypes.number,
favorite: PropTypes.bool, favorite: PropTypes.bool,
isRead: PropTypes.bool, isRead: PropTypes.bool,
rid: PropTypes.string, rid: PropTypes.string,
@ -60,46 +54,36 @@ export default class RoomItem extends React.Component {
// eslint-disable-next-line no-useless-constructor // eslint-disable-next-line no-useless-constructor
constructor(props) { constructor(props) {
super(props); super(props);
const dragX = new Animated.Value(0); this.dragX = new Animated.Value(0);
const rowOffSet = new Animated.Value(0); this.rowOffSet = new Animated.Value(0);
this.rowTranslation = Animated.add( this.transX = Animated.add(
rowOffSet, this.rowOffSet,
dragX this.dragX
); );
this.state = { this.state = {
dragX,
rowOffSet,
rowState: 0 // 0: closed, 1: right opened, -1: left opened rowState: 0 // 0: closed, 1: right opened, -1: left opened
}; };
this._onGestureEvent = Animated.event( this._onGestureEvent = Animated.event(
[{ nativeEvent: { translationX: dragX } }] [{ nativeEvent: { translationX: this.dragX } }]
); );
this._value = 0; this._value = 0;
this.rowTranslation.addListener(({ value }) => { this._value = value; });
} }
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
const { lastMessage, _updatedAt, isRead } = this.props; const { lastMessage, _updatedAt } = this.props;
const oldlastMessage = lastMessage; const oldlastMessage = lastMessage;
const newLastmessage = nextProps.lastMessage; const newLastmessage = nextProps.lastMessage;
if (oldlastMessage && newLastmessage && oldlastMessage.ts !== newLastmessage.ts) { if (oldlastMessage && newLastmessage && oldlastMessage.ts !== newLastmessage.ts) {
return true; return true;
} }
if (_updatedAt && nextProps._updatedAt && nextProps._updatedAt !== _updatedAt) { if (_updatedAt && nextProps._updatedAt && nextProps._updatedAt.toISOString() !== _updatedAt.toISOString()) {
return true;
}
if (isRead !== nextProps.isRead) {
return true; return true;
} }
// eslint-disable-next-line react/destructuring-assignment // eslint-disable-next-line react/destructuring-assignment
return attrs.some(key => nextProps[key] !== this.props[key]); return attrs.some(key => nextProps[key] !== this.props[key]);
} }
componentWillUnmount() {
this.rowTranslation.removeAllListeners();
}
_onHandlerStateChange = ({ nativeEvent }) => { _onHandlerStateChange = ({ nativeEvent }) => {
if (nativeEvent.oldState === State.ACTIVE) { if (nativeEvent.oldState === State.ACTIVE) {
this._handleRelease(nativeEvent); this._handleRelease(nativeEvent);
@ -109,21 +93,22 @@ export default class RoomItem extends React.Component {
_handleRelease = (nativeEvent) => { _handleRelease = (nativeEvent) => {
const { translationX } = nativeEvent; const { translationX } = nativeEvent;
const { rowState } = this.state; const { rowState } = this.state;
const { width } = this.props; this._value = this._value + translationX;
const halfScreen = width / 2;
let toValue = 0; let toValue = 0;
if (rowState === 0) { // if no option is opened if (rowState === 0) { // if no option is opened
if (translationX > 0 && translationX < halfScreen) { if (translationX > 0 && translationX < LONG_SWIPE) {
toValue = OPTION_WIDTH; // open left option if he swipe right but not enough to trigger action toValue = ACTION_WIDTH; // open left option if he swipe right but not enough to trigger action
this.setState({ rowState: -1 }); this.setState({ rowState: -1 });
} else if (translationX >= halfScreen) { } else if (translationX >= LONG_SWIPE) {
toValue = 0; toValue = 0;
this.toggleRead(); this.toggleRead();
} else if (translationX < 0 && translationX > -halfScreen) { } else if (translationX < 0 && translationX > -LONG_SWIPE) {
toValue = -2 * OPTION_WIDTH; // open right option if he swipe left toValue = -2 * ACTION_WIDTH; // open right option if he swipe left
this.setState({ rowState: 1 }); this.setState({ rowState: 1 });
} else if (translationX <= -halfScreen) { } else if (translationX <= -LONG_SWIPE) {
toValue = -width; toValue = 0;
this.setState({ rowState: 0 });
this.hideChannel(); this.hideChannel();
} else { } else {
toValue = 0; toValue = 0;
@ -134,12 +119,12 @@ export default class RoomItem extends React.Component {
if (this._value < SMALL_SWIPE) { if (this._value < SMALL_SWIPE) {
toValue = 0; toValue = 0;
this.setState({ rowState: 0 }); this.setState({ rowState: 0 });
} else if (this._value > halfScreen) { } else if (this._value > LONG_SWIPE) {
toValue = 0; toValue = 0;
this.setState({ rowState: 0 }); this.setState({ rowState: 0 });
this.toggleRead(); this.toggleRead();
} else { } else {
toValue = OPTION_WIDTH; toValue = ACTION_WIDTH;
} }
} }
@ -147,32 +132,28 @@ export default class RoomItem extends React.Component {
if (this._value > -2 * SMALL_SWIPE) { if (this._value > -2 * SMALL_SWIPE) {
toValue = 0; toValue = 0;
this.setState({ rowState: 0 }); this.setState({ rowState: 0 });
} else if (this._value < -halfScreen) { } else if (this._value < -LONG_SWIPE) {
toValue = 0; toValue = 0;
this.setState({ rowState: 0 }); this.setState({ rowState: 0 });
this.hideChannel(); this.hideChannel();
} else { } else {
toValue = -2 * OPTION_WIDTH; toValue = -2 * ACTION_WIDTH;
} }
} }
this._animateRow(toValue); this._animateRow(toValue);
} }
_animateRow = (toValue) => { _animateRow = (toValue) => {
const { dragX, rowOffSet } = this.state; this.rowOffSet.setValue(this._value);
rowOffSet.setValue(this._value); this._value = toValue;
dragX.setValue(0); this.dragX.setValue(0);
Animated.spring(rowOffSet, { Animated.spring(this.rowOffSet, {
toValue, toValue,
bounciness: 0 bounciness: 0,
useNativeDriver: true
}).start(); }).start();
} }
handleLeftButtonPress = () => {
this.toggleRead();
this.close();
}
close = () => { close = () => {
this.setState({ rowState: 0 }); this.setState({ rowState: 0 });
this._animateRow(0); this._animateRow(0);
@ -193,11 +174,6 @@ export default class RoomItem extends React.Component {
} }
} }
handleHideButtonPress = () => {
this.hideChannel();
this.close();
}
hideChannel = () => { hideChannel = () => {
const { hideChannel, rid, type } = this.props; const { hideChannel, rid, type } = this.props;
if (hideChannel) { if (hideChannel) {
@ -205,6 +181,16 @@ export default class RoomItem extends React.Component {
} }
} }
onToggleReadPress = () => {
this.toggleRead();
this.close();
}
onHidePress = () => {
this.hideChannel();
this.close();
}
onPress = () => { onPress = () => {
const { rowState } = this.state; const { rowState } = this.state;
if (rowState !== 0) { if (rowState !== 0) {
@ -217,109 +203,6 @@ export default class RoomItem extends React.Component {
} }
} }
renderLeftActions = () => {
const { isRead, width } = this.props;
const halfWidth = width / 2;
const trans = this.rowTranslation.interpolate({
inputRange: [0, OPTION_WIDTH],
outputRange: [-width, -width + OPTION_WIDTH]
});
const iconTrans = this.rowTranslation.interpolate({
inputRange: [0, OPTION_WIDTH, halfWidth - 1, halfWidth, width],
outputRange: [0, 0, -(OPTION_WIDTH + 10), 0, 0]
});
return (
<Animated.View
style={[
styles.leftAction,
{ transform: [{ translateX: trans }] }
]}
>
<RectButton style={styles.actionButtonLeft} onPress={this.handleLeftButtonPress}>
<Animated.View
style={{ transform: [{ translateX: iconTrans }] }}
>
{isRead ? (
<View style={styles.actionView}>
<CustomIcon size={20} name='flag' color='white' />
<Text style={styles.actionText}>{I18n.t('Unread')}</Text>
</View>
) : (
<View style={styles.actionView}>
<CustomIcon size={20} name='check' color='white' />
<Text style={styles.actionText}>{I18n.t('Read')}</Text>
</View>
)}
</Animated.View>
</RectButton>
</Animated.View>
);
};
renderRightActions = () => {
const { favorite, width } = this.props;
const halfWidth = width / 2;
const trans = this.rowTranslation.interpolate({
inputRange: [-OPTION_WIDTH, 0],
outputRange: [width - OPTION_WIDTH, width]
});
const iconHideTrans = this.rowTranslation.interpolate({
inputRange: [-(halfWidth - 20), -2 * OPTION_WIDTH, 0],
outputRange: [0, 0, -OPTION_WIDTH]
});
const iconFavWidth = this.rowTranslation.interpolate({
inputRange: [-halfWidth, -2 * OPTION_WIDTH, 0],
outputRange: [0, OPTION_WIDTH, OPTION_WIDTH],
extrapolate: 'clamp'
});
const iconHideWidth = this.rowTranslation.interpolate({
inputRange: [-width, -halfWidth, -2 * OPTION_WIDTH, 0],
outputRange: [width, halfWidth, OPTION_WIDTH, OPTION_WIDTH]
});
return (
<Animated.View
style={[
styles.rightAction,
{ transform: [{ translateX: trans }] }
]}
>
<Animated.View
style={{ width: iconFavWidth }}
>
<RectButton style={[styles.actionButtonRightFav]} onPress={this.toggleFav}>
{favorite ? (
<View style={styles.actionView}>
<CustomIcon size={20} name='Star-filled' color='white' />
<Text style={styles.actionText}>{I18n.t('Unfavorite')}</Text>
</View>
) : (
<View style={styles.actionView}>
<CustomIcon size={20} name='star' color='white' />
<Text style={styles.actionText}>{I18n.t('Favorite')}</Text>
</View>
)}
</RectButton>
</Animated.View>
<Animated.View style={[
{ width: iconHideWidth },
{ transform: [{ translateX: iconHideTrans }] }
]}
>
<RectButton
style={[styles.actionButtonRightHide]}
onPress={this.handleHideButtonPress}
>
<View style={styles.actionView}>
<CustomIcon size={20} name='eye-off' color='white' />
<Text style={styles.actionText}>{I18n.t('Hide')}</Text>
</View>
</RectButton>
</Animated.View>
</Animated.View>
);
}
formatDate = date => moment(date).calendar(null, { formatDate = date => moment(date).calendar(null, {
lastDay: `[${ I18n.t('Yesterday') }]`, lastDay: `[${ I18n.t('Yesterday') }]`,
sameDay: 'h:mm A', sameDay: 'h:mm A',
@ -329,7 +212,7 @@ export default class RoomItem extends React.Component {
render() { render() {
const { const {
unread, userMentions, name, _updatedAt, alert, testID, height, type, avatarSize, baseUrl, userId, username, token, id, prid, showLastMessage, lastMessage unread, userMentions, name, _updatedAt, alert, testID, type, avatarSize, baseUrl, userId, username, token, id, prid, showLastMessage, lastMessage, isRead, width, favorite
} = this.props; } = this.props;
const date = this.formatDate(_updatedAt); const date = this.formatDate(_updatedAt);
@ -351,17 +234,28 @@ export default class RoomItem extends React.Component {
return ( return (
<PanGestureHandler <PanGestureHandler
minDeltaX={10} minDeltaX={20}
onGestureEvent={this._onGestureEvent} onGestureEvent={this._onGestureEvent}
onHandlerStateChange={this._onHandlerStateChange} onHandlerStateChange={this._onHandlerStateChange}
> >
<View style={styles.upperContainer}> <Animated.View>
{this.renderLeftActions()} <LeftActions
{this.renderRightActions()} transX={this.transX}
isRead={isRead}
width={width}
onToggleReadPress={this.onToggleReadPress}
/>
<RightActions
transX={this.transX}
favorite={favorite}
width={width}
toggleFav={this.toggleFav}
onHidePress={this.onHidePress}
/>
<Animated.View <Animated.View
style={ style={
{ {
transform: [{ translateX: this.rowTranslation }] transform: [{ translateX: this.transX }]
} }
} }
> >
@ -373,7 +267,7 @@ export default class RoomItem extends React.Component {
style={styles.button} style={styles.button}
> >
<View <View
style={[styles.container, height && { height }]} style={styles.container}
accessibilityLabel={accessibilityLabel} accessibilityLabel={accessibilityLabel}
> >
<Avatar text={name} size={avatarSize} type={type} baseUrl={baseUrl} style={styles.avatar} userId={userId} token={token} /> <Avatar text={name} size={avatarSize} type={type} baseUrl={baseUrl} style={styles.avatar} userId={userId} token={token} />
@ -391,7 +285,7 @@ export default class RoomItem extends React.Component {
</View> </View>
</RectButton> </RectButton>
</Animated.View> </Animated.View>
</View> </Animated.View>
</PanGestureHandler> </PanGestureHandler>
); );
} }

View File

@ -6,6 +6,9 @@ import {
} from '../../constants/colors'; } from '../../constants/colors';
export const ROW_HEIGHT = 75 * PixelRatio.getFontScale(); export const ROW_HEIGHT = 75 * PixelRatio.getFontScale();
export const ACTION_WIDTH = 80;
export const SMALL_SWIPE = ACTION_WIDTH / 2;
export const LONG_SWIPE = ACTION_WIDTH * 3;
export default StyleSheet.create({ export default StyleSheet.create({
container: { container: {
@ -97,6 +100,15 @@ export default StyleSheet.create({
avatar: { avatar: {
marginRight: 10 marginRight: 10
}, },
upperContainer: {
overflow: 'hidden'
},
actionsContainer: {
position: 'absolute',
left: 0,
right: 0,
height: ROW_HEIGHT
},
actionText: { actionText: {
color: COLOR_WHITE, color: COLOR_WHITE,
fontSize: 15, fontSize: 15,
@ -105,37 +117,24 @@ export default StyleSheet.create({
marginTop: 4, marginTop: 4,
...sharedStyles.textSemibold ...sharedStyles.textSemibold
}, },
actionButtonLeft: { actionLeftButtonContainer: {
flex: 1, position: 'absolute',
backgroundColor: '#497AFC', height: ROW_HEIGHT,
backgroundColor: COLOR_PRIMARY,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'flex-end' top: 0
}, },
actionButtonRightFav: { actionRightButtonContainer: {
flex: 1, position: 'absolute',
height: ROW_HEIGHT,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'flex-start', top: 0,
backgroundColor: '#F4BD3E' backgroundColor: '#54585e'
}, },
actionButtonRightHide: { actionButton: {
flex: 1, width: ACTION_WIDTH,
justifyContent: 'center', height: '100%',
alignItems: 'flex-start', alignItems: 'center',
backgroundColor: '#55585D' justifyContent: 'center'
},
actionView: {
width: 80,
alignItems: 'center'
},
leftAction: {
...StyleSheet.absoluteFill,
flexDirection: 'row-reverse'
},
rightAction: {
...StyleSheet.absoluteFill,
flexDirection: 'row'
},
upperContainer: {
overflow: 'hidden'
} }
}); });

View File

@ -39,6 +39,8 @@ const keyExtractor = item => item.rid;
@connect(state => ({ @connect(state => ({
userId: state.login.user && state.login.user.id, userId: state.login.user && state.login.user.id,
username: state.login.user && state.login.user.username,
token: state.login.user && state.login.user.token,
isAuthenticated: state.login.isAuthenticated, isAuthenticated: state.login.isAuthenticated,
server: state.server.server, server: state.server.server,
baseUrl: state.settings.baseUrl || state.server ? state.server.server : '', baseUrl: state.settings.baseUrl || state.server ? state.server.server : '',
@ -95,6 +97,8 @@ export default class RoomsListView extends React.Component {
static propTypes = { static propTypes = {
navigation: PropTypes.object, navigation: PropTypes.object,
userId: PropTypes.string, userId: PropTypes.string,
username: PropTypes.string,
token: PropTypes.string,
baseUrl: PropTypes.string, baseUrl: PropTypes.string,
server: PropTypes.string, server: PropTypes.string,
searchText: PropTypes.string, searchText: PropTypes.string,
@ -395,7 +399,15 @@ export default class RoomsListView extends React.Component {
toggleFav = async(rid, favorite) => { toggleFav = async(rid, favorite) => {
try { try {
await RocketChat.toggleFavorite(rid, !favorite); const result = await RocketChat.toggleFavorite(rid, !favorite);
if (result.success) {
database.write(() => {
const sub = database.objects('subscriptions').filtered('rid == $0', rid)[0];
if (sub) {
sub.f = !favorite;
}
});
}
} catch (e) { } catch (e) {
log('error_toggle_favorite', e); log('error_toggle_favorite', e);
} }
@ -461,7 +473,7 @@ export default class RoomsListView extends React.Component {
renderItem = ({ item }) => { renderItem = ({ item }) => {
const { width } = this.state; const { width } = this.state;
const { const {
userId, baseUrl, StoreLastMessage userId, username, token, baseUrl, StoreLastMessage
} = this.props; } = this.props;
const id = item.rid.replace(userId, '').trim(); const id = item.rid.replace(userId, '').trim();
@ -478,6 +490,9 @@ export default class RoomsListView extends React.Component {
_updatedAt={item.roomUpdatedAt} _updatedAt={item.roomUpdatedAt}
key={item._id} key={item._id}
id={id} id={id}
userId={userId}
username={username}
token={token}
rid={item.rid} rid={item.rid}
type={item.t} type={item.t}
baseUrl={baseUrl} baseUrl={baseUrl}
@ -486,7 +501,6 @@ export default class RoomsListView extends React.Component {
onPress={() => this._onPressItem(item)} onPress={() => this._onPressItem(item)}
testID={`rooms-list-view-item-${ item.name }`} testID={`rooms-list-view-item-${ item.name }`}
width={width} width={width}
height={ROW_HEIGHT}
toggleFav={this.toggleFav} toggleFav={this.toggleFav}
toggleRead={this.toggleRead} toggleRead={this.toggleRead}
hideChannel={this.hideChannel} hideChannel={this.hideChannel}
@ -591,7 +605,7 @@ export default class RoomsListView extends React.Component {
renderItem={this.renderItem} renderItem={this.renderItem}
ListHeaderComponent={this.renderListHeader} ListHeaderComponent={this.renderListHeader}
getItemLayout={getItemLayout} getItemLayout={getItemLayout}
removeClippedSubviews // removeClippedSubviews
keyboardShouldPersistTaps='always' keyboardShouldPersistTaps='always'
initialNumToRender={9} initialNumToRender={9}
windowSize={9} windowSize={9}