diff --git a/app/presentation/RoomItem/index.js b/app/presentation/RoomItem/index.js index b93c9f1d..b9e2639b 100644 --- a/app/presentation/RoomItem/index.js +++ b/app/presentation/RoomItem/index.js @@ -1,9 +1,7 @@ import React from 'react'; import moment from 'moment'; import PropTypes from 'prop-types'; -import { - View, Text, Dimensions, 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'; @@ -18,9 +16,8 @@ import { CustomIcon } from '../../lib/Icons'; export { ROW_HEIGHT }; const OPTION_WIDTH = 80; -const SMALL_SWIPE = 80; -const ACTION_OFFSET = 220; -const attrs = ['name', 'unread', 'userMentions', 'showLastMessage', 'alert', 'type']; +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, @@ -45,6 +42,7 @@ export default class RoomItem extends React.Component { token: PropTypes.string, avatarSize: PropTypes.number, testID: PropTypes.string, + width: PropTypes.number, height: PropTypes.number, favorite: PropTypes.bool, isRead: PropTypes.bool, @@ -71,9 +69,7 @@ export default class RoomItem extends React.Component { this.state = { dragX, rowOffSet, - rowState: 0, // 0: closed, 1: right opened, -1: left opened - leftWidth: undefined, - rightOffset: undefined + rowState: 0 // 0: closed, 1: right opened, -1: left opened }; this._onGestureEvent = Animated.event( [{ nativeEvent: { translationX: dragX } }] @@ -104,43 +100,30 @@ export default class RoomItem extends React.Component { this.rowTranslation.removeAllListeners(); } - close = () => { - this.swipeableRow.close(); - }; - _onHandlerStateChange = ({ nativeEvent }) => { if (nativeEvent.oldState === State.ACTIVE) { this._handleRelease(nativeEvent); } }; - _currentOffset = () => { - const { leftWidth = 0, rowState } = this.state; - const { rightOffset } = this.state; - if (rowState === 1) { - return leftWidth; - } else if (rowState === -1) { - return rightOffset; - } - return 0; - }; - _handleRelease = (nativeEvent) => { const { translationX } = nativeEvent; const { rowState } = this.state; + const { width } = this.props; + const halfScreen = width / 2; let toValue = 0; if (rowState === 0) { // if no option is opened - if (translationX > 0 && translationX < ACTION_OFFSET) { + if (translationX > 0 && translationX < halfScreen) { toValue = OPTION_WIDTH; // open left option if he swipe right but not enough to trigger action this.setState({ rowState: -1 }); - } else if (translationX > ACTION_OFFSET) { + } else if (translationX >= halfScreen) { toValue = 0; this.toggleRead(); - } else if (translationX < 0 && translationX > -ACTION_OFFSET) { + } else if (translationX < 0 && translationX > -halfScreen) { toValue = -2 * OPTION_WIDTH; // open right option if he swipe left this.setState({ rowState: 1 }); - } else if (translationX < -ACTION_OFFSET) { - toValue = 0; + } else if (translationX <= -halfScreen) { + toValue = -width; this.hideChannel(); } else { toValue = 0; @@ -151,7 +134,7 @@ export default class RoomItem extends React.Component { if (this._value < SMALL_SWIPE) { toValue = 0; this.setState({ rowState: 0 }); - } else if (this._value > ACTION_OFFSET) { + } else if (this._value > halfScreen) { toValue = 0; this.setState({ rowState: 0 }); this.toggleRead(); @@ -164,7 +147,7 @@ export default class RoomItem extends React.Component { if (this._value > -2 * SMALL_SWIPE) { toValue = 0; this.setState({ rowState: 0 }); - } else if (this._value < -ACTION_OFFSET) { + } else if (this._value < -halfScreen) { toValue = 0; this.setState({ rowState: 0 }); this.hideChannel(); @@ -181,7 +164,7 @@ export default class RoomItem extends React.Component { dragX.setValue(0); Animated.spring(rowOffSet, { toValue, - bounciness: 5 + bounciness: 0 }).start(); } @@ -191,6 +174,7 @@ export default class RoomItem extends React.Component { } close = () => { + this.setState({ rowState: 0 }); this._animateRow(0); } @@ -221,16 +205,28 @@ export default class RoomItem extends React.Component { } } + onPress = () => { + const { rowState } = this.state; + if (rowState !== 0) { + this.close(); + return; + } + const { onPress } = this.props; + if (onPress) { + onPress(); + } + } + renderLeftActions = () => { - const { isRead } = this.props; - const { width } = Dimensions.get('window'); + 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, ACTION_OFFSET - 20, ACTION_OFFSET, width], + inputRange: [0, OPTION_WIDTH, halfWidth - 1, halfWidth, width], outputRange: [0, 0, -(OPTION_WIDTH + 10), 0, 0] }); return ( @@ -246,12 +242,12 @@ export default class RoomItem extends React.Component { > {isRead ? ( - + {I18n.t('Unread')} ) : ( - + {I18n.t('Read')} )} @@ -262,23 +258,24 @@ export default class RoomItem extends React.Component { }; renderRightActions = () => { - const { favorite } = this.props; - const { width } = Dimensions.get('window'); + 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: [-(ACTION_OFFSET - 20), -2 * OPTION_WIDTH, 0], + inputRange: [-(halfWidth - 20), -2 * OPTION_WIDTH, 0], outputRange: [0, 0, -OPTION_WIDTH] }); const iconFavWidth = this.rowTranslation.interpolate({ - inputRange: [-(ACTION_OFFSET + 1), -ACTION_OFFSET, -(ACTION_OFFSET - 20), -2 * OPTION_WIDTH, 0], - outputRange: [0, 0, OPTION_WIDTH + 20, OPTION_WIDTH, OPTION_WIDTH] + inputRange: [-halfWidth, -2 * OPTION_WIDTH, 0], + outputRange: [0, OPTION_WIDTH, OPTION_WIDTH], + extrapolate: 'clamp' }); const iconHideWidth = this.rowTranslation.interpolate({ - inputRange: [-(ACTION_OFFSET + 1), -ACTION_OFFSET, -(ACTION_OFFSET - 20), -2 * OPTION_WIDTH, 0], - outputRange: [(ACTION_OFFSET + 1), ACTION_OFFSET, OPTION_WIDTH + 20, OPTION_WIDTH, OPTION_WIDTH] + inputRange: [-width, -halfWidth, -2 * OPTION_WIDTH, 0], + outputRange: [width, halfWidth, OPTION_WIDTH, OPTION_WIDTH] }); return ( {favorite ? ( - + {I18n.t('Unfavorite')} ) : ( - + {I18n.t('Favorite')} )} @@ -314,7 +311,7 @@ export default class RoomItem extends React.Component { onPress={this.handleHideButtonPress} > - + {I18n.t('Hide')} @@ -332,7 +329,7 @@ export default class RoomItem extends React.Component { render() { const { - unread, userMentions, name, _updatedAt, alert, testID, height, type, avatarSize, baseUrl, userId, username, token, onPress, id, prid, showLastMessage, lastMessage + unread, userMentions, name, _updatedAt, alert, testID, height, type, avatarSize, baseUrl, userId, username, token, id, prid, showLastMessage, lastMessage } = this.props; const date = this.formatDate(_updatedAt); @@ -358,7 +355,7 @@ export default class RoomItem extends React.Component { onGestureEvent={this._onGestureEvent} onHandlerStateChange={this._onHandlerStateChange} > - + {this.renderLeftActions()} {this.renderRightActions()} - + ); } diff --git a/app/presentation/RoomItem/styles.js b/app/presentation/RoomItem/styles.js index fba962e7..a39a1037 100644 --- a/app/presentation/RoomItem/styles.js +++ b/app/presentation/RoomItem/styles.js @@ -9,12 +9,14 @@ export const ROW_HEIGHT = 75 * PixelRatio.getFontScale(); export default StyleSheet.create({ container: { - backgroundColor: COLOR_WHITE, flexDirection: 'row', alignItems: 'center', paddingLeft: 14, height: ROW_HEIGHT }, + button: { + backgroundColor: COLOR_WHITE + }, centerContainer: { flex: 1, paddingVertical: 10, @@ -96,10 +98,12 @@ export default StyleSheet.create({ marginRight: 10 }, actionText: { - color: 'white', - fontSize: 14, + color: COLOR_WHITE, + fontSize: 15, backgroundColor: 'transparent', - justifyContent: 'center' + justifyContent: 'center', + marginTop: 4, + ...sharedStyles.textSemibold }, actionButtonLeft: { flex: 1, diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index f8901afc..fbb9c169 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { - View, FlatList, BackHandler, ActivityIndicator, Text, ScrollView, Keyboard, LayoutAnimation, InteractionManager + View, FlatList, BackHandler, ActivityIndicator, Text, ScrollView, Keyboard, LayoutAnimation, InteractionManager, Dimensions } from 'react-native'; import { connect } from 'react-redux'; import { isEqual } from 'lodash'; @@ -121,6 +121,7 @@ export default class RoomsListView extends React.Component { console.time(`${ this.constructor.name } init`); console.time(`${ this.constructor.name } mount`); + const { width } = Dimensions.get('window'); this.data = []; this.state = { searching: false, @@ -133,7 +134,8 @@ export default class RoomsListView extends React.Component { channels: [], privateGroup: [], direct: [], - livechat: [] + livechat: [], + width }; Orientation.unlockAllOrientations(); this.didFocusListener = props.navigation.addListener('didFocus', () => BackHandler.addEventListener('hardwareBackPress', this.handleBackPress)); @@ -148,6 +150,7 @@ export default class RoomsListView extends React.Component { initSearchingAndroid: this.initSearchingAndroid, cancelSearchingAndroid: this.cancelSearchingAndroid }); + Dimensions.addEventListener('change', this.onDimensionsChange); console.timeEnd(`${ this.constructor.name } mount`); } @@ -172,7 +175,7 @@ export default class RoomsListView extends React.Component { return true; } - const { loading, searching } = this.state; + const { loading, searching, width } = this.state; if (nextState.loading !== loading) { return true; } @@ -180,6 +183,10 @@ export default class RoomsListView extends React.Component { return true; } + if (nextState.width !== width) { + return true; + } + const { search } = this.state; if (!isEqual(nextState.search, search)) { return true; @@ -220,9 +227,12 @@ export default class RoomsListView extends React.Component { if (this.willBlurListener && this.willBlurListener.remove) { this.willBlurListener.remove(); } + Dimensions.removeEventListener('change', this.onDimensionsChange); console.countReset(`${ this.constructor.name }.render calls`); } + onDimensionsChange = ({ window: { width } }) => this.setState({ width }) + // eslint-disable-next-line react/sort-comp internalSetState = (...args) => { const { navigation } = this.props; @@ -393,7 +403,15 @@ export default class RoomsListView extends React.Component { toggleRead = async(rid, isRead) => { try { - await RocketChat.toggleRead(isRead, rid); + const result = await RocketChat.toggleRead(isRead, rid); + if (result.success) { + database.write(() => { + const sub = database.objects('subscriptions').filtered('rid == $0', rid)[0]; + if (sub) { + sub.alert = isRead; + } + }); + } } catch (e) { log('error_toggle_read', e); } @@ -401,7 +419,13 @@ export default class RoomsListView extends React.Component { hideChannel = async(rid, type) => { try { - await RocketChat.hideRoom(rid, type); + const result = await RocketChat.hideRoom(rid, type); + if (result.success) { + database.write(() => { + const sub = database.objects('subscriptions').filtered('rid == $0', rid)[0]; + database.delete(sub); + }); + } } catch (e) { log('error_hide_channel', e); } @@ -435,6 +459,7 @@ export default class RoomsListView extends React.Component { } renderItem = ({ item }) => { + const { width } = this.state; const { userId, baseUrl, StoreLastMessage } = this.props; @@ -460,6 +485,7 @@ export default class RoomsListView extends React.Component { showLastMessage={StoreLastMessage} onPress={() => this._onPressItem(item)} testID={`rooms-list-view-item-${ item.name }`} + width={width} height={ROW_HEIGHT} toggleFav={this.toggleFav} toggleRead={this.toggleRead} diff --git a/storybook/stories/RoomItem.js b/storybook/stories/RoomItem.js index 48fddcb1..dd22e393 100644 --- a/storybook/stories/RoomItem.js +++ b/storybook/stories/RoomItem.js @@ -1,5 +1,5 @@ import React from 'react'; -import { ScrollView } from 'react-native'; +import { ScrollView, Dimensions } from 'react-native'; // import moment from 'moment'; import RoomItemComponent from '../../app/presentation/RoomItem'; @@ -7,6 +7,7 @@ import StoriesSeparator from './StoriesSeparator'; const date = '2017-10-10T10:00:00Z'; const baseUrl = 'https://open.rocket.chat'; +const { width } = Dimensions.get('window'); const RoomItem = props => ( ( name='rocket.cat' _updatedAt={date} baseUrl={baseUrl} + width={width} {...props} /> );