diff --git a/app/presentation/RoomItem/Actions.js b/app/presentation/RoomItem/Actions.js
new file mode 100644
index 000000000..8793dc791
--- /dev/null
+++ b/app/presentation/RoomItem/Actions.js
@@ -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 (
+
+
+
+
+
+
+ {I18n.t(isRead ? 'Unread' : 'Read')}
+
+
+
+
+
+ );
+});
+
+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 (
+
+
+
+
+
+ {I18n.t(favorite ? 'Unfavorite' : 'Favorite')}
+
+
+
+
+
+
+
+ {I18n.t('Hide')}
+
+
+
+
+ );
+});
+
+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
+};
diff --git a/app/presentation/RoomItem/index.js b/app/presentation/RoomItem/index.js
index b9e2639b9..1b5105f62 100644
--- a/app/presentation/RoomItem/index.js
+++ b/app/presentation/RoomItem/index.js
@@ -2,27 +2,22 @@ import React from 'react';
import moment from 'moment';
import PropTypes from 'prop-types';
import { View, Text, Animated } from 'react-native';
-import { connect } from 'react-redux';
import { RectButton, PanGestureHandler, State } from 'react-native-gesture-handler';
import Avatar from '../../containers/Avatar';
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 TypeIcon from './TypeIcon';
import LastMessage from './LastMessage';
-import { CustomIcon } from '../../lib/Icons';
+import { LeftActions, RightActions } from './Actions';
export { ROW_HEIGHT };
-const OPTION_WIDTH = 80;
-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
-}))
+const attrs = ['name', 'unread', 'userMentions', 'showLastMessage', 'alert', 'type', 'width', 'isRead', 'favorite'];
+
export default class RoomItem extends React.Component {
static propTypes = {
type: PropTypes.string.isRequired,
@@ -43,7 +38,6 @@ export default class RoomItem extends React.Component {
avatarSize: PropTypes.number,
testID: PropTypes.string,
width: PropTypes.number,
- height: PropTypes.number,
favorite: PropTypes.bool,
isRead: PropTypes.bool,
rid: PropTypes.string,
@@ -60,46 +54,36 @@ export default class RoomItem extends React.Component {
// eslint-disable-next-line no-useless-constructor
constructor(props) {
super(props);
- const dragX = new Animated.Value(0);
- const rowOffSet = new Animated.Value(0);
- this.rowTranslation = Animated.add(
- rowOffSet,
- dragX
+ this.dragX = new Animated.Value(0);
+ this.rowOffSet = new Animated.Value(0);
+ this.transX = Animated.add(
+ this.rowOffSet,
+ this.dragX
);
this.state = {
- dragX,
- rowOffSet,
rowState: 0 // 0: closed, 1: right opened, -1: left opened
};
this._onGestureEvent = Animated.event(
- [{ nativeEvent: { translationX: dragX } }]
+ [{ nativeEvent: { translationX: this.dragX } }]
);
this._value = 0;
- this.rowTranslation.addListener(({ value }) => { this._value = value; });
}
shouldComponentUpdate(nextProps) {
- const { lastMessage, _updatedAt, isRead } = this.props;
+ const { lastMessage, _updatedAt } = this.props;
const oldlastMessage = lastMessage;
const newLastmessage = nextProps.lastMessage;
if (oldlastMessage && newLastmessage && oldlastMessage.ts !== newLastmessage.ts) {
return true;
}
- if (_updatedAt && nextProps._updatedAt && nextProps._updatedAt !== _updatedAt) {
- return true;
- }
- if (isRead !== nextProps.isRead) {
+ if (_updatedAt && nextProps._updatedAt && nextProps._updatedAt.toISOString() !== _updatedAt.toISOString()) {
return true;
}
// eslint-disable-next-line react/destructuring-assignment
return attrs.some(key => nextProps[key] !== this.props[key]);
}
- componentWillUnmount() {
- this.rowTranslation.removeAllListeners();
- }
-
_onHandlerStateChange = ({ nativeEvent }) => {
if (nativeEvent.oldState === State.ACTIVE) {
this._handleRelease(nativeEvent);
@@ -109,21 +93,22 @@ export default class RoomItem extends React.Component {
_handleRelease = (nativeEvent) => {
const { translationX } = nativeEvent;
const { rowState } = this.state;
- const { width } = this.props;
- const halfScreen = width / 2;
+ this._value = this._value + translationX;
+
let toValue = 0;
if (rowState === 0) { // if no option is opened
- if (translationX > 0 && translationX < halfScreen) {
- toValue = OPTION_WIDTH; // open left option if he swipe right but not enough to trigger action
+ if (translationX > 0 && translationX < LONG_SWIPE) {
+ toValue = ACTION_WIDTH; // open left option if he swipe right but not enough to trigger action
this.setState({ rowState: -1 });
- } else if (translationX >= halfScreen) {
+ } else if (translationX >= LONG_SWIPE) {
toValue = 0;
this.toggleRead();
- } else if (translationX < 0 && translationX > -halfScreen) {
- toValue = -2 * OPTION_WIDTH; // open right option if he swipe left
+ } else if (translationX < 0 && translationX > -LONG_SWIPE) {
+ toValue = -2 * ACTION_WIDTH; // open right option if he swipe left
this.setState({ rowState: 1 });
- } else if (translationX <= -halfScreen) {
- toValue = -width;
+ } else if (translationX <= -LONG_SWIPE) {
+ toValue = 0;
+ this.setState({ rowState: 0 });
this.hideChannel();
} else {
toValue = 0;
@@ -134,12 +119,12 @@ export default class RoomItem extends React.Component {
if (this._value < SMALL_SWIPE) {
toValue = 0;
this.setState({ rowState: 0 });
- } else if (this._value > halfScreen) {
+ } else if (this._value > LONG_SWIPE) {
toValue = 0;
this.setState({ rowState: 0 });
this.toggleRead();
} else {
- toValue = OPTION_WIDTH;
+ toValue = ACTION_WIDTH;
}
}
@@ -147,32 +132,28 @@ export default class RoomItem extends React.Component {
if (this._value > -2 * SMALL_SWIPE) {
toValue = 0;
this.setState({ rowState: 0 });
- } else if (this._value < -halfScreen) {
+ } else if (this._value < -LONG_SWIPE) {
toValue = 0;
this.setState({ rowState: 0 });
this.hideChannel();
} else {
- toValue = -2 * OPTION_WIDTH;
+ toValue = -2 * ACTION_WIDTH;
}
}
this._animateRow(toValue);
}
_animateRow = (toValue) => {
- const { dragX, rowOffSet } = this.state;
- rowOffSet.setValue(this._value);
- dragX.setValue(0);
- Animated.spring(rowOffSet, {
+ this.rowOffSet.setValue(this._value);
+ this._value = toValue;
+ this.dragX.setValue(0);
+ Animated.spring(this.rowOffSet, {
toValue,
- bounciness: 0
+ bounciness: 0,
+ useNativeDriver: true
}).start();
}
- handleLeftButtonPress = () => {
- this.toggleRead();
- this.close();
- }
-
close = () => {
this.setState({ rowState: 0 });
this._animateRow(0);
@@ -193,11 +174,6 @@ export default class RoomItem extends React.Component {
}
}
- handleHideButtonPress = () => {
- this.hideChannel();
- this.close();
- }
-
hideChannel = () => {
const { hideChannel, rid, type } = this.props;
if (hideChannel) {
@@ -205,6 +181,16 @@ export default class RoomItem extends React.Component {
}
}
+ onToggleReadPress = () => {
+ this.toggleRead();
+ this.close();
+ }
+
+ onHidePress = () => {
+ this.hideChannel();
+ this.close();
+ }
+
onPress = () => {
const { rowState } = this.state;
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 (
-
-
-
- {isRead ? (
-
-
- {I18n.t('Unread')}
-
- ) : (
-
-
- {I18n.t('Read')}
-
- )}
-
-
-
- );
- };
-
- 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 (
-
-
-
- {favorite ? (
-
-
- {I18n.t('Unfavorite')}
-
- ) : (
-
-
- {I18n.t('Favorite')}
-
- )}
-
-
-
-
-
-
- {I18n.t('Hide')}
-
-
-
-
- );
- }
-
formatDate = date => moment(date).calendar(null, {
lastDay: `[${ I18n.t('Yesterday') }]`,
sameDay: 'h:mm A',
@@ -329,7 +212,7 @@ export default class RoomItem extends React.Component {
render() {
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;
const date = this.formatDate(_updatedAt);
@@ -351,17 +234,28 @@ export default class RoomItem extends React.Component {
return (
-
- {this.renderLeftActions()}
- {this.renderRightActions()}
+
+
+
@@ -373,7 +267,7 @@ export default class RoomItem extends React.Component {
style={styles.button}
>
@@ -391,7 +285,7 @@ export default class RoomItem extends React.Component {
-
+
);
}
diff --git a/app/presentation/RoomItem/styles.js b/app/presentation/RoomItem/styles.js
index a39a1037f..6ad29fd6d 100644
--- a/app/presentation/RoomItem/styles.js
+++ b/app/presentation/RoomItem/styles.js
@@ -6,6 +6,9 @@ import {
} from '../../constants/colors';
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({
container: {
@@ -97,6 +100,15 @@ export default StyleSheet.create({
avatar: {
marginRight: 10
},
+ upperContainer: {
+ overflow: 'hidden'
+ },
+ actionsContainer: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ height: ROW_HEIGHT
+ },
actionText: {
color: COLOR_WHITE,
fontSize: 15,
@@ -105,37 +117,24 @@ export default StyleSheet.create({
marginTop: 4,
...sharedStyles.textSemibold
},
- actionButtonLeft: {
- flex: 1,
- backgroundColor: '#497AFC',
+ actionLeftButtonContainer: {
+ position: 'absolute',
+ height: ROW_HEIGHT,
+ backgroundColor: COLOR_PRIMARY,
justifyContent: 'center',
- alignItems: 'flex-end'
+ top: 0
},
- actionButtonRightFav: {
- flex: 1,
+ actionRightButtonContainer: {
+ position: 'absolute',
+ height: ROW_HEIGHT,
justifyContent: 'center',
- alignItems: 'flex-start',
- backgroundColor: '#F4BD3E'
+ top: 0,
+ backgroundColor: '#54585e'
},
- actionButtonRightHide: {
- flex: 1,
- justifyContent: 'center',
- alignItems: 'flex-start',
- backgroundColor: '#55585D'
- },
- actionView: {
- width: 80,
- alignItems: 'center'
- },
- leftAction: {
- ...StyleSheet.absoluteFill,
- flexDirection: 'row-reverse'
- },
- rightAction: {
- ...StyleSheet.absoluteFill,
- flexDirection: 'row'
- },
- upperContainer: {
- overflow: 'hidden'
+ actionButton: {
+ width: ACTION_WIDTH,
+ height: '100%',
+ alignItems: 'center',
+ justifyContent: 'center'
}
});
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index fbb9c169b..c2dc0a8a8 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -39,6 +39,8 @@ const keyExtractor = item => item.rid;
@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,
isAuthenticated: state.login.isAuthenticated,
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 = {
navigation: PropTypes.object,
userId: PropTypes.string,
+ username: PropTypes.string,
+ token: PropTypes.string,
baseUrl: PropTypes.string,
server: PropTypes.string,
searchText: PropTypes.string,
@@ -395,7 +399,15 @@ export default class RoomsListView extends React.Component {
toggleFav = async(rid, favorite) => {
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) {
log('error_toggle_favorite', e);
}
@@ -461,7 +473,7 @@ export default class RoomsListView extends React.Component {
renderItem = ({ item }) => {
const { width } = this.state;
const {
- userId, baseUrl, StoreLastMessage
+ userId, username, token, baseUrl, StoreLastMessage
} = this.props;
const id = item.rid.replace(userId, '').trim();
@@ -478,6 +490,9 @@ export default class RoomsListView extends React.Component {
_updatedAt={item.roomUpdatedAt}
key={item._id}
id={id}
+ userId={userId}
+ username={username}
+ token={token}
rid={item.rid}
type={item.t}
baseUrl={baseUrl}
@@ -486,7 +501,6 @@ export default class RoomsListView extends React.Component {
onPress={() => this._onPressItem(item)}
testID={`rooms-list-view-item-${ item.name }`}
width={width}
- height={ROW_HEIGHT}
toggleFav={this.toggleFav}
toggleRead={this.toggleRead}
hideChannel={this.hideChannel}
@@ -591,7 +605,7 @@ export default class RoomsListView extends React.Component {
renderItem={this.renderItem}
ListHeaderComponent={this.renderListHeader}
getItemLayout={getItemLayout}
- removeClippedSubviews
+ // removeClippedSubviews
keyboardShouldPersistTaps='always'
initialNumToRender={9}
windowSize={9}