[MM-25349] centre failed network action component and improve responsiveness to device orientation (#4834)
This commit is contained in:
parent
0db46f2003
commit
1f6cd323f4
10 changed files with 167 additions and 108 deletions
|
|
@ -16,7 +16,10 @@ export default class CloudSvg extends PureComponent {
|
|||
render() {
|
||||
const {color, height, width} = this.props;
|
||||
return (
|
||||
<View style={{height, width, alignItems: 'flex-start'}}>
|
||||
<View
|
||||
style={{height, width, alignItems: 'flex-start'}}
|
||||
testID='failed_network_action.cloud_icon'
|
||||
>
|
||||
<Svg
|
||||
width={width}
|
||||
height={height}
|
||||
|
|
|
|||
115
app/components/failed_network_action/failed_network_action.js
Normal file
115
app/components/failed_network_action/failed_network_action.js
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Text, View} from 'react-native';
|
||||
import {intlShape} from 'react-intl';
|
||||
import Button from 'react-native-button';
|
||||
|
||||
import {INDICATOR_BAR_HEIGHT} from '@constants/view';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import Cloud from './cloud';
|
||||
|
||||
export default class FailedNetworkAction extends PureComponent {
|
||||
static propTypes = {
|
||||
actionText: PropTypes.string,
|
||||
errorMessage: PropTypes.string,
|
||||
errorTitle: PropTypes.string,
|
||||
isLandscape: PropTypes.bool.isRequired,
|
||||
onRetry: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
render() {
|
||||
const {formatMessage} = this.context.intl;
|
||||
const {onRetry, theme} = this.props;
|
||||
const style = getStyleFromTheme(theme);
|
||||
|
||||
const actionText = this.props.actionText || formatMessage({
|
||||
id: 'mobile.failed_network_action.retry',
|
||||
defaultMessage: 'Try again',
|
||||
});
|
||||
const errorTitle = this.props.errorTitle || formatMessage({
|
||||
id: 'mobile.failed_network_action.title',
|
||||
defaultMessage: 'No internet connection',
|
||||
});
|
||||
const errorMessage = this.props.errorMessage || formatMessage({
|
||||
id: 'mobile.failed_network_action.shortDescription',
|
||||
defaultMessage: 'Messages will load when you have an internet connection.',
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
{ !this.props.isLandscape &&
|
||||
<Cloud
|
||||
color={changeOpacity(theme.centerChannelColor, 0.15)}
|
||||
height={76}
|
||||
width={76}
|
||||
/>
|
||||
}
|
||||
<Text
|
||||
style={style.title}
|
||||
testID='error_title'
|
||||
>
|
||||
{errorTitle}
|
||||
</Text>
|
||||
<Text
|
||||
style={style.description}
|
||||
testID='error_text'
|
||||
>
|
||||
{errorMessage}
|
||||
</Text>
|
||||
<Button
|
||||
onPress={onRetry}
|
||||
containerStyle={style.buttonContainer}
|
||||
>
|
||||
<Text style={style.link}>{actionText}</Text>
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: INDICATOR_BAR_HEIGHT,
|
||||
paddingBottom: 15,
|
||||
},
|
||||
title: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.8),
|
||||
fontSize: 20,
|
||||
fontWeight: '600',
|
||||
marginBottom: 15,
|
||||
marginTop: 10,
|
||||
},
|
||||
description: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.4),
|
||||
fontSize: 17,
|
||||
lineHeight: 25,
|
||||
textAlign: 'center',
|
||||
},
|
||||
link: {
|
||||
color: theme.buttonColor,
|
||||
fontSize: 15,
|
||||
},
|
||||
buttonContainer: {
|
||||
backgroundColor: theme.buttonBg,
|
||||
borderRadius: 5,
|
||||
height: 42,
|
||||
justifyContent: 'center',
|
||||
marginTop: 20,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React from 'react';
|
||||
|
||||
import FailedNetworkAction from './failed_network_action';
|
||||
import {renderWithReduxIntl} from 'test/testing_library';
|
||||
import Preferences from '@mm-redux/constants/preferences';
|
||||
import {waitFor} from '@testing-library/react-native';
|
||||
|
||||
describe('FailedNetworkAction', () => {
|
||||
const baseProps = {
|
||||
onRetry: jest.fn(),
|
||||
theme: Preferences.THEMES.default,
|
||||
isLandscape: false,
|
||||
errorMessage: 'Error Message',
|
||||
errorTitle: 'Error Title',
|
||||
};
|
||||
|
||||
test('Cloud in portrait', async () => {
|
||||
const {getByTestId} = renderWithReduxIntl(
|
||||
<FailedNetworkAction {...baseProps}/>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(getByTestId('failed_network_action.cloud_icon')).toBeTruthy());
|
||||
});
|
||||
|
||||
test('Cloud NOT in landscape', async () => {
|
||||
const props = {...baseProps, isLandscape: true};
|
||||
const {queryByTestId} = renderWithReduxIntl(
|
||||
<FailedNetworkAction {...props}/>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(queryByTestId('failed_network_action.cloud_icon')).toBeNull());
|
||||
});
|
||||
});
|
||||
|
|
@ -1,104 +1,16 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Text, View} from 'react-native';
|
||||
import {intlShape} from 'react-intl';
|
||||
import Button from 'react-native-button';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {isLandscape} from 'app/selectors/device';
|
||||
|
||||
import Cloud from './cloud';
|
||||
import FailedNetworkAction from './failed_network_action';
|
||||
|
||||
export default class FailedNetworkAction extends PureComponent {
|
||||
static propTypes = {
|
||||
onRetry: PropTypes.func.isRequired,
|
||||
actionText: PropTypes.string,
|
||||
errorMessage: PropTypes.string,
|
||||
errorTitle: PropTypes.string,
|
||||
theme: PropTypes.object.isRequired,
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
isLandscape: isLandscape(state),
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
showAction: true,
|
||||
};
|
||||
|
||||
render() {
|
||||
const {formatMessage} = this.context.intl;
|
||||
const {onRetry, theme} = this.props;
|
||||
const style = getStyleFromTheme(theme);
|
||||
|
||||
const actionText = this.props.actionText || formatMessage({
|
||||
id: 'mobile.failed_network_action.retry',
|
||||
defaultMessage: 'Try again',
|
||||
});
|
||||
const errorTitle = this.props.errorTitle || formatMessage({
|
||||
id: 'mobile.failed_network_action.title',
|
||||
defaultMessage: 'No internet connection',
|
||||
});
|
||||
const errorMessage = this.props.errorMessage || formatMessage({
|
||||
id: 'mobile.failed_network_action.shortDescription',
|
||||
defaultMessage: 'Messages will load when you have an internet connection.',
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<Cloud
|
||||
color={changeOpacity(theme.centerChannelColor, 0.15)}
|
||||
height={76}
|
||||
width={76}
|
||||
/>
|
||||
<Text style={style.title}>{errorTitle}</Text>
|
||||
<Text style={style.description}>{errorMessage}</Text>
|
||||
<Button
|
||||
onPress={onRetry}
|
||||
containerStyle={style.buttonContainer}
|
||||
>
|
||||
<Text style={style.link}>{actionText}</Text>
|
||||
</Button>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 15,
|
||||
paddingBottom: 100,
|
||||
},
|
||||
title: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.8),
|
||||
fontSize: 20,
|
||||
fontWeight: '600',
|
||||
marginBottom: 15,
|
||||
marginTop: 10,
|
||||
},
|
||||
description: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.4),
|
||||
fontSize: 17,
|
||||
lineHeight: 25,
|
||||
textAlign: 'center',
|
||||
},
|
||||
link: {
|
||||
color: theme.buttonColor,
|
||||
fontSize: 15,
|
||||
},
|
||||
buttonContainer: {
|
||||
backgroundColor: theme.buttonBg,
|
||||
borderRadius: 5,
|
||||
height: 42,
|
||||
justifyContent: 'center',
|
||||
marginTop: 20,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
};
|
||||
});
|
||||
export default connect(mapStateToProps)(FailedNetworkAction);
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ exports[`ErrorTeamsList should match snapshot 1`] = `
|
|||
}
|
||||
>
|
||||
<Connect(StatusBar) />
|
||||
<FailedNetworkAction
|
||||
<Connect(FailedNetworkAction)
|
||||
errorMessage="Teams could not be loaded."
|
||||
errorTitle="Something went wrong"
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
|
|
|||
|
|
@ -39,9 +39,8 @@ exports[`FlaggedPosts should match snapshot when getFlaggedPosts failed 1`] = `
|
|||
}
|
||||
>
|
||||
<Connect(StatusBar) />
|
||||
<FailedNetworkAction
|
||||
<Connect(FailedNetworkAction)
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
|
|
|||
|
|
@ -39,9 +39,8 @@ exports[`PinnedPosts should match snapshot when getPinnedPosts failed 1`] = `
|
|||
}
|
||||
>
|
||||
<Connect(StatusBar) />
|
||||
<FailedNetworkAction
|
||||
<Connect(FailedNetworkAction)
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
|
|
|||
|
|
@ -39,9 +39,8 @@ exports[`RecentMentions should match snapshot when getRecentMentions failed 1`]
|
|||
}
|
||||
>
|
||||
<Connect(StatusBar) />
|
||||
<FailedNetworkAction
|
||||
<Connect(FailedNetworkAction)
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SelectTeam should match snapshot for fail of teams 1`] = `
|
||||
<FailedNetworkAction
|
||||
<Connect(FailedNetworkAction)
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
|
|
|||
|
|
@ -285,9 +285,8 @@ exports[`TermsOfService should match snapshot for fail of get terms 1`] = `
|
|||
}
|
||||
>
|
||||
<Connect(StatusBar) />
|
||||
<FailedNetworkAction
|
||||
<Connect(FailedNetworkAction)
|
||||
onRetry={[Function]}
|
||||
showAction={true}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
|
|
|
|||
Loading…
Reference in a new issue