[MM-29381] Show an indicator if channel is still loading after 10 seconds (#5031)

* Show still-loading indicator

* Update snapshot test

* Call retryLoadChannels on a 10 second interval

* Select default team on retryLoad if no currentTeamId

* Fix use of jest.useFakeTimers
This commit is contained in:
Miguel Alatzar 2020-12-11 11:32:56 -07:00 committed by GitHub
parent 0ee7b60e84
commit 68d76af4dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 174 additions and 1 deletions

View file

@ -16,6 +16,68 @@ exports[`ChannelLoader should match snapshot 1`] = `
]
}
>
<ForwardRef(AnimatedComponentWrapper)
style={
Array [
Object {
"backgroundColor": "#3D3C40",
"height": 38,
"position": "absolute",
"width": "100%",
"zIndex": 9,
},
Object {
"top": -38,
},
]
}
>
<ForwardRef(AnimatedComponentWrapper)
edges={
Array [
"left",
"right",
]
}
style={
Object {
"alignItems": "center",
"flexDirection": "row",
"height": 38,
"paddingLeft": 12,
"paddingRight": 5,
}
}
>
<View
style={
Object {
"alignItems": "flex-start",
"height": 24,
"justifyContent": "center",
"paddingRight": 10,
}
}
>
<ActivityIndicator
animating={true}
color="#FFFFFF"
hidesWhenStopped={true}
size="small"
/>
</View>
<FormattedText
defaultMessage="Still trying to load your content..."
id="mobile.channel_loader.still_loading"
style={
Object {
"color": "#fff",
"fontWeight": "bold",
}
}
/>
</ForwardRef(AnimatedComponentWrapper)>
</ForwardRef(AnimatedComponentWrapper)>
<View
style={
Array [

View file

@ -4,15 +4,22 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
ActivityIndicator,
Animated,
View,
Dimensions,
Platform,
View,
} from 'react-native';
import * as RNPlaceholder from 'rn-placeholder';
import {SafeAreaView} from 'react-native-safe-area-context';
import FormattedText from '@components/formatted_text';
import CustomPropTypes from '@constants/custom_prop_types';
import {INDICATOR_BAR_HEIGHT} from '@constants/view';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
const AnimatedSafeAreaView = Animated.createAnimatedComponent(SafeAreaView);
function calculateMaxRows(height) {
return Math.round(height / 100);
}
@ -35,6 +42,7 @@ export default class ChannelLoader extends PureComponent {
style: CustomPropTypes.Style,
theme: PropTypes.object.isRequired,
height: PropTypes.number,
retryLoad: PropTypes.func.isRequired,
};
constructor(props) {
@ -48,6 +56,8 @@ export default class ChannelLoader extends PureComponent {
switch: false,
maxRows,
};
this.top = new Animated.Value(-INDICATOR_BAR_HEIGHT);
}
static getDerivedStateFromProps(nextProps, prevState) {
@ -64,6 +74,25 @@ export default class ChannelLoader extends PureComponent {
return Object.keys(state) ? state : null;
}
componentDidMount() {
this.stillLoadingTimeout = setTimeout(this.showIndicator, 10000);
this.retryLoadInterval = setInterval(this.props.retryLoad, 10000);
}
componentWillUnmount() {
clearTimeout(this.stillLoadingTimeout);
clearInterval(this.retryLoadInterval);
}
showIndicator = () => {
Animated.timing(this.top, {
toValue: 0,
duration: 300,
delay: 500,
useNativeDriver: false,
}).start();
}
buildSections({key, style, bg, color}) {
return (
<View
@ -119,6 +148,26 @@ export default class ChannelLoader extends PureComponent {
style={[style.container, styleProp, {backgroundColor: bg}]}
onLayout={this.handleLayout}
>
<Animated.View
style={[style.indicator, {top: this.top}]}
>
<AnimatedSafeAreaView
edges={['left', 'right']}
style={style.indicatorWrapper}
>
<View style={style.activityIndicator}>
<ActivityIndicator
color='#FFFFFF'
size='small'
/>
</View>
<FormattedText
id='mobile.channel_loader.still_loading'
defaultMessage='Still trying to load your content...'
style={style.indicatorText}
/>
</AnimatedSafeAreaView>
</Animated.View>
{Array(this.state.maxRows).fill().map((item, index) => this.buildSections({
key: index,
style,
@ -143,5 +192,36 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
paddingRight: 20,
marginVertical: 10,
},
indicator: {
position: 'absolute',
height: INDICATOR_BAR_HEIGHT,
width: '100%',
...Platform.select({
android: {
elevation: 9,
},
ios: {
zIndex: 9,
},
}),
backgroundColor: '#3D3C40',
},
indicatorWrapper: {
alignItems: 'center',
height: INDICATOR_BAR_HEIGHT,
flexDirection: 'row',
paddingLeft: 12,
paddingRight: 5,
},
indicatorText: {
color: '#fff',
fontWeight: 'bold',
},
activityIndicator: {
alignItems: 'flex-start',
height: 24,
justifyContent: 'center',
paddingRight: 10,
},
};
});

View file

@ -8,14 +8,34 @@ import Preferences from '@mm-redux/constants/preferences';
import ChannelLoader from './channel_loader';
jest.useFakeTimers();
describe('ChannelLoader', () => {
const baseProps = {
channelIsLoading: true,
theme: Preferences.THEMES.default,
retryLoad: jest.fn(),
};
test('should match snapshot', () => {
const wrapper = shallow(<ChannelLoader {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
test('should call setTimeout and setInterval for showIndicator and retryLoad on mount', () => {
const wrapper = shallow(<ChannelLoader {...baseProps}/>);
const instance = wrapper.instance();
expect(setTimeout).toHaveBeenCalledWith(instance.showIndicator, 10000);
expect(setInterval).toHaveBeenCalledWith(baseProps.retryLoad, 10000);
});
test('should clear timer and interval on unmount', () => {
const wrapper = shallow(<ChannelLoader {...baseProps}/>);
const instance = wrapper.instance();
instance.componentWillUnmount();
expect(clearTimeout).toHaveBeenCalledWith(instance.stillLoadingTimeout);
expect(clearInterval).toHaveBeenCalledWith(instance.retryLoadInterval);
});
});

View file

@ -233,6 +233,15 @@ export default class ChannelBase extends PureComponent {
}
};
retryLoad = () => {
const {currentTeamId, actions} = this.props;
if (currentTeamId) {
this.loadChannels(currentTeamId);
} else {
actions.selectDefaultTeam();
}
}
retryLoadChannels = () => {
this.loadChannels(this.props.currentTeamId);
};
@ -270,6 +279,7 @@ export default class ChannelBase extends PureComponent {
<Loading
channelIsLoading={true}
color={theme.centerChannelColor}
retryLoad={this.retryLoad}
/>
);
}

View file

@ -198,6 +198,7 @@
"mobile.channel_list.members": "MEMBERS",
"mobile.channel_list.not_member": "NOT A MEMBER",
"mobile.channel_list.unreads": "UNREADS",
"mobile.channel_loader.still_loading": "Still trying to load your content...",
"mobile.channel_members.add_members_alert": "You must select at least one member to add to the channel.",
"mobile.client_upgrade": "Update App",
"mobile.client_upgrade.can_upgrade_subtitle": "A new version is available for download.",