Remove connection badge (#862)

* Connecting indicator on RoomsListView header

* Connecting indicator on RoomView header

* Remove ConnectionBadge

* Show updating on RoomView load messages
This commit is contained in:
Diego Mello 2019-04-30 16:31:51 -03:00 committed by GitHub
parent 44f3b7f1a9
commit 94e32368dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 90 additions and 163 deletions

View File

@ -1,140 +0,0 @@
import React, { Component } from 'react';
import {
Text, StyleSheet, ActivityIndicator, Animated, Easing
} from 'react-native';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import I18n from '../i18n';
import debounce from '../utils/debounce';
import sharedStyles from '../views/Styles';
import {
COLOR_BACKGROUND_CONTAINER, COLOR_DANGER, COLOR_SUCCESS, COLOR_WHITE, COLOR_TEXT_DESCRIPTION
} from '../constants/colors';
const styles = StyleSheet.create({
container: {
width: '100%',
position: 'absolute',
top: 0,
height: 41,
backgroundColor: COLOR_BACKGROUND_CONTAINER,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
elevation: 4
},
text: {
color: COLOR_WHITE,
fontSize: 15,
...sharedStyles.textRegular
},
textConnecting: {
...sharedStyles.textColorDescription
},
containerConnected: {
backgroundColor: COLOR_SUCCESS
},
containerOffline: {
backgroundColor: COLOR_DANGER
},
activityIndicator: {
marginRight: 15
}
});
const ANIMATION_DURATION = 300;
@connect(state => ({
connecting: state.meteor.connecting,
connected: state.meteor.connected,
disconnected: !state.meteor.connecting && !state.meteor.connected
}))
class ConnectionBadge extends Component {
static propTypes = {
connecting: PropTypes.bool,
connected: PropTypes.bool,
disconnected: PropTypes.bool
}
constructor(props) {
super(props);
this.animatedValue = new Animated.Value(0);
if (props.connecting) {
this.show();
}
}
componentDidUpdate() {
const { connected, disconnected } = this.props;
this.show(connected || disconnected);
}
componentWillUnmount() {
if (this.timeout) {
clearTimeout(this.timeout);
}
}
// eslint-disable-next-line react/sort-comp
animate = debounce((toValue, autoHide) => {
Animated.timing(
this.animatedValue,
{
toValue,
duration: ANIMATION_DURATION,
easing: Easing.ease,
useNativeDriver: true
},
).start(() => {
if (toValue === 1 && autoHide) {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this.hide();
}, 1000);
}
});
}, 300);
show = (autoHide) => {
this.animate(1, autoHide);
}
hide = () => {
this.animate(0);
}
render() {
const { connecting, connected } = this.props;
const translateY = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [-42, 0]
});
if (connecting) {
return (
<Animated.View style={[styles.container, { transform: [{ translateY }] }]}>
<ActivityIndicator color={COLOR_TEXT_DESCRIPTION} style={styles.activityIndicator} />
<Text style={[styles.text, styles.textConnecting]}>{I18n.t('Connecting')}</Text>
</Animated.View>
);
} else if (connected) {
return (
<Animated.View style={[styles.container, styles.containerConnected, { transform: [{ translateY }] }]}>
<Text style={styles.text}>{I18n.t('Connected')}</Text>
</Animated.View>
);
}
return (
<Animated.View style={[styles.container, styles.containerOffline, { transform: [{ translateY }] }]}>
<Text style={styles.text}>{I18n.t('Offline')}</Text>
</Animated.View>
);
}
}
export default ConnectionBadge;

View File

@ -65,8 +65,34 @@ Typing.propTypes = {
usersTyping: PropTypes.array usersTyping: PropTypes.array
}; };
const HeaderTitle = React.memo(({
title, scale, connecting, isFetching
}) => {
if (connecting) {
title = I18n.t('Connecting');
}
if (isFetching) {
title = I18n.t('Updating');
}
return (
<Text
style={[styles.title, { fontSize: TITLE_SIZE * scale }]}
numberOfLines={1}
testID={`room-view-title-${ title }`}
>{title}
</Text>
);
});
HeaderTitle.propTypes = {
title: PropTypes.string,
scale: PropTypes.number,
connecting: PropTypes.bool,
isFetching: PropTypes.bool
};
const Header = React.memo(({ const Header = React.memo(({
title, type, status, usersTyping, width, height, prid, tmid, widthOffset title, type, status, usersTyping, width, height, prid, tmid, widthOffset, connecting, isFetching
}) => { }) => {
const portrait = height > width; const portrait = height > width;
let scale = 1; let scale = 1;
@ -93,7 +119,15 @@ const Header = React.memo(({
contentContainerStyle={styles.scroll} contentContainerStyle={styles.scroll}
> >
<Icon type={prid ? 'discussion' : type} status={status} /> <Icon type={prid ? 'discussion' : type} status={status} />
<Text style={[styles.title, { fontSize: TITLE_SIZE * scale }]} numberOfLines={1} testID={`room-view-title-${ title }`}>{title}</Text> <HeaderTitle
prid={prid}
type={type}
status={status}
title={title}
scale={scale}
connecting={connecting}
isFetching={isFetching}
/>
</ScrollView> </ScrollView>
</View> </View>
{type === 'thread' ? null : <Typing usersTyping={usersTyping} />} {type === 'thread' ? null : <Typing usersTyping={usersTyping} />}
@ -110,7 +144,9 @@ Header.propTypes = {
tmid: PropTypes.string, tmid: PropTypes.string,
status: PropTypes.string, status: PropTypes.string,
usersTyping: PropTypes.array, usersTyping: PropTypes.array,
widthOffset: PropTypes.number widthOffset: PropTypes.number,
connecting: PropTypes.bool,
isFetching: PropTypes.bool
}; };
Header.defaultProps = { Header.defaultProps = {

View File

@ -26,6 +26,7 @@ import RightButtons from './RightButtons';
} }
return { return {
connecting: state.meteor.connecting,
userId, userId,
isLoggedUser, isLoggedUser,
status status
@ -40,6 +41,8 @@ export default class RoomHeaderView extends Component {
rid: PropTypes.string, rid: PropTypes.string,
window: PropTypes.object, window: PropTypes.object,
status: PropTypes.string, status: PropTypes.string,
connecting: PropTypes.bool,
isFetching: PropTypes.bool,
widthOffset: PropTypes.number, widthOffset: PropTypes.number,
isLoggedUser: PropTypes.bool, isLoggedUser: PropTypes.bool,
userId: PropTypes.string userId: PropTypes.string
@ -63,7 +66,7 @@ export default class RoomHeaderView extends Component {
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {
const { usersTyping, user } = this.state; const { usersTyping, user } = this.state;
const { const {
type, title, status, window type, title, status, window, connecting, isFetching
} = this.props; } = this.props;
if (nextProps.type !== type) { if (nextProps.type !== type) {
return true; return true;
@ -74,6 +77,12 @@ export default class RoomHeaderView extends Component {
if (nextProps.status !== status) { if (nextProps.status !== status) {
return true; return true;
} }
if (nextProps.connecting !== connecting) {
return true;
}
if (nextProps.isFetching !== isFetching) {
return true;
}
if (nextProps.window.width !== window.width) { if (nextProps.window.width !== window.width) {
return true; return true;
} }
@ -102,7 +111,7 @@ export default class RoomHeaderView extends Component {
render() { render() {
const { usersTyping, user } = this.state; const { usersTyping, user } = this.state;
const { const {
window, title, type, prid, tmid, widthOffset, isLoggedUser, status: userStatus window, title, type, prid, tmid, widthOffset, isLoggedUser, status: userStatus, connecting, isFetching
} = this.props; } = this.props;
let status = 'offline'; let status = 'offline';
@ -125,6 +134,8 @@ export default class RoomHeaderView extends Component {
height={window.height} height={window.height}
usersTyping={usersTyping} usersTyping={usersTyping}
widthOffset={widthOffset} widthOffset={widthOffset}
connecting={connecting}
isFetching={isFetching}
/> />
); );
} }

View File

@ -31,7 +31,6 @@ import log from '../../utils/log';
import { isIOS } from '../../utils/deviceInfo'; import { isIOS } from '../../utils/deviceInfo';
import EventEmitter from '../../utils/events'; import EventEmitter from '../../utils/events';
import I18n from '../../i18n'; import I18n from '../../i18n';
import ConnectionBadge from '../../containers/ConnectionBadge';
import RoomHeaderView, { RightButtons } from './Header'; import RoomHeaderView, { RightButtons } from './Header';
import StatusBar from '../../containers/StatusBar'; import StatusBar from '../../containers/StatusBar';
import Separator from './Separator'; import Separator from './Separator';
@ -67,10 +66,19 @@ export default class RoomView extends LoggedView {
const title = navigation.getParam('name'); const title = navigation.getParam('name');
const t = navigation.getParam('t'); const t = navigation.getParam('t');
const tmid = navigation.getParam('tmid'); const tmid = navigation.getParam('tmid');
const isFetching = navigation.getParam('isFetching', false);
return { return {
headerTitleContainerStyle: styles.headerTitleContainerStyle, headerTitleContainerStyle: styles.headerTitleContainerStyle,
headerTitle: ( headerTitle: (
<RoomHeaderView rid={rid} prid={prid} tmid={tmid} title={title} type={t} widthOffset={tmid ? 95 : 130} /> <RoomHeaderView
rid={rid}
prid={prid}
tmid={tmid}
title={title}
type={t}
widthOffset={tmid ? 95 : 130}
isFetching={isFetching}
/>
), ),
headerRight: <RightButtons rid={rid} tmid={tmid} t={t} navigation={navigation} /> headerRight: <RightButtons rid={rid} tmid={tmid} t={t} navigation={navigation} />
}; };
@ -312,14 +320,18 @@ export default class RoomView extends LoggedView {
return ((room.prid || useRealName) && room.fname) || room.name; return ((room.prid || useRealName) && room.fname) || room.name;
} }
getMessages = () => { getMessages = async() => {
const { room } = this.state; const { room } = this.state;
const { navigation } = this.props;
try { try {
navigation.setParams({ isFetching: true });
if (room.lastOpen) { if (room.lastOpen) {
return RocketChat.loadMissedMessages(room); await RocketChat.loadMissedMessages(room);
} else { } else {
return RocketChat.loadMessagesForRoom(room); await RocketChat.loadMessagesForRoom(room);
} }
navigation.setParams({ isFetching: false });
return Promise.resolve();
} catch (e) { } catch (e) {
console.log('TCL: getMessages -> e', e); console.log('TCL: getMessages -> e', e);
log('getMessages', e); log('getMessages', e);
@ -519,7 +531,6 @@ export default class RoomView extends LoggedView {
{this.renderActions()} {this.renderActions()}
<ReactionPicker onEmojiSelected={this.onReactionPress} /> <ReactionPicker onEmojiSelected={this.onReactionPress} />
<UploadProgress rid={this.rid} /> <UploadProgress rid={this.rid} />
<ConnectionBadge />
</SafeAreaView> </SafeAreaView>
); );
} }

View File

@ -42,8 +42,8 @@ const styles = StyleSheet.create({
} }
}); });
const Header = ({ const Header = React.memo(({
isFetching, serverName, showServerDropdown, setSearchInputRef, showSearchHeader, onSearchChangeText, onPress connecting, isFetching, serverName, showServerDropdown, setSearchInputRef, showSearchHeader, onSearchChangeText, onPress
}) => { }) => {
if (showSearchHeader) { if (showSearchHeader) {
return ( return (
@ -61,6 +61,7 @@ const Header = ({
return ( return (
<View style={styles.container}> <View style={styles.container}>
<TouchableOpacity onPress={onPress} testID='rooms-list-header-server-dropdown-button'> <TouchableOpacity onPress={onPress} testID='rooms-list-header-server-dropdown-button'>
{connecting ? <Text style={styles.updating}>{I18n.t('Connecting')}</Text> : null}
{isFetching ? <Text style={styles.updating}>{I18n.t('Updating')}</Text> : null} {isFetching ? <Text style={styles.updating}>{I18n.t('Updating')}</Text> : null}
<View style={styles.button}> <View style={styles.button}>
<Text style={[styles.server, isFetching && styles.serverSmall]}>{serverName}</Text> <Text style={[styles.server, isFetching && styles.serverSmall]}>{serverName}</Text>
@ -69,7 +70,7 @@ const Header = ({
</TouchableOpacity> </TouchableOpacity>
</View> </View>
); );
}; });
Header.propTypes = { Header.propTypes = {
showServerDropdown: PropTypes.bool.isRequired, showServerDropdown: PropTypes.bool.isRequired,
@ -77,6 +78,7 @@ Header.propTypes = {
onPress: PropTypes.func.isRequired, onPress: PropTypes.func.isRequired,
onSearchChangeText: PropTypes.func.isRequired, onSearchChangeText: PropTypes.func.isRequired,
setSearchInputRef: PropTypes.func.isRequired, setSearchInputRef: PropTypes.func.isRequired,
connecting: PropTypes.bool,
isFetching: PropTypes.bool, isFetching: PropTypes.bool,
serverName: PropTypes.string serverName: PropTypes.string
}; };

View File

@ -39,15 +39,18 @@ const styles = StyleSheet.create({
} }
}); });
const HeaderTitle = ({ isFetching }) => { const HeaderTitle = React.memo(({ connecting, isFetching }) => {
if (connecting) {
return <Text style={styles.title}>{I18n.t('Connecting')}</Text>;
}
if (isFetching) { if (isFetching) {
return <Text style={styles.title}>{I18n.t('Updating')}</Text>; return <Text style={styles.title}>{I18n.t('Updating')}</Text>;
} }
return <Text style={styles.title}>{I18n.t('Messages')}</Text>; return <Text style={styles.title}>{I18n.t('Messages')}</Text>;
}; });
const Header = ({ const Header = React.memo(({
isFetching, serverName, showServerDropdown, onPress connecting, isFetching, serverName, showServerDropdown, onPress
}) => ( }) => (
<View style={styles.container}> <View style={styles.container}>
<TouchableOpacity <TouchableOpacity
@ -55,16 +58,17 @@ const Header = ({
testID='rooms-list-header-server-dropdown-button' testID='rooms-list-header-server-dropdown-button'
style={styles.container} style={styles.container}
> >
<HeaderTitle isFetching={isFetching} /> <HeaderTitle connecting={connecting} isFetching={isFetching} />
<View style={styles.button}> <View style={styles.button}>
<Text style={styles.server}>{serverName}</Text> <Text style={styles.server}>{serverName}</Text>
<Image style={[styles.disclosure, showServerDropdown && styles.upsideDown]} source={{ uri: 'disclosure_indicator_server' }} /> <Image style={[styles.disclosure, showServerDropdown && styles.upsideDown]} source={{ uri: 'disclosure_indicator_server' }} />
</View> </View>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
); ));
Header.propTypes = { Header.propTypes = {
connecting: PropTypes.bool,
isFetching: PropTypes.bool, isFetching: PropTypes.bool,
serverName: PropTypes.string, serverName: PropTypes.string,
showServerDropdown: PropTypes.bool.isRequired, showServerDropdown: PropTypes.bool.isRequired,
@ -76,6 +80,7 @@ Header.defaultProps = {
}; };
HeaderTitle.propTypes = { HeaderTitle.propTypes = {
connecting: PropTypes.bool,
isFetching: PropTypes.bool isFetching: PropTypes.bool
}; };

View File

@ -11,6 +11,7 @@ import Header from './Header';
showServerDropdown: state.rooms.showServerDropdown, showServerDropdown: state.rooms.showServerDropdown,
showSortDropdown: state.rooms.showSortDropdown, showSortDropdown: state.rooms.showSortDropdown,
showSearchHeader: state.rooms.showSearchHeader, showSearchHeader: state.rooms.showSearchHeader,
connecting: state.meteor.connecting,
isFetching: state.rooms.isFetching, isFetching: state.rooms.isFetching,
serverName: state.settings.Site_Name serverName: state.settings.Site_Name
}), dispatch => ({ }), dispatch => ({
@ -25,6 +26,7 @@ export default class RoomsListHeaderView extends PureComponent {
showSortDropdown: PropTypes.bool, showSortDropdown: PropTypes.bool,
showSearchHeader: PropTypes.bool, showSearchHeader: PropTypes.bool,
serverName: PropTypes.string, serverName: PropTypes.string,
connecting: PropTypes.bool,
isFetching: PropTypes.bool, isFetching: PropTypes.bool,
open: PropTypes.func, open: PropTypes.func,
close: PropTypes.func, close: PropTypes.func,
@ -68,13 +70,15 @@ export default class RoomsListHeaderView extends PureComponent {
render() { render() {
const { const {
serverName, showServerDropdown, showSearchHeader, isFetching serverName, showServerDropdown, showSearchHeader, connecting, isFetching
} = this.props; } = this.props;
return ( return (
<Header <Header
serverName={serverName} serverName={serverName}
showServerDropdown={showServerDropdown} showServerDropdown={showServerDropdown}
showSearchHeader={showSearchHeader} showSearchHeader={showSearchHeader}
connecting={connecting}
isFetching={isFetching} isFetching={isFetching}
setSearchInputRef={this.setSearchInputRef} setSearchInputRef={this.setSearchInputRef}
onPress={this.onPress} onPress={this.onPress}

View File

@ -8,7 +8,6 @@ import { isEqual } from 'lodash';
import { SafeAreaView, NavigationEvents } from 'react-navigation'; import { SafeAreaView, NavigationEvents } from 'react-navigation';
import Orientation from 'react-native-orientation-locker'; import Orientation from 'react-native-orientation-locker';
import ConnectionBadge from '../../containers/ConnectionBadge';
import database, { safeAddListener } from '../../lib/realm'; import database, { safeAddListener } from '../../lib/realm';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import RoomItem, { ROW_HEIGHT } from '../../presentation/RoomItem'; import RoomItem, { ROW_HEIGHT } from '../../presentation/RoomItem';
@ -559,7 +558,6 @@ export default class RoomsListView extends LoggedView {
: null : null
} }
{showServerDropdown ? <ServerDropdown /> : null} {showServerDropdown ? <ServerDropdown /> : null}
<ConnectionBadge />
<NavigationEvents <NavigationEvents
onDidFocus={() => BackHandler.addEventListener('hardwareBackPress', this.handleBackPress)} onDidFocus={() => BackHandler.addEventListener('hardwareBackPress', this.handleBackPress)}
onWillBlur={() => BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress)} onWillBlur={() => BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress)}