Skip to content
This repository has been archived by the owner on Jun 7, 2023. It is now read-only.

Mobile: Add retry button, error log and change node to Realm migration #1041

Merged
merged 6 commits into from
Feb 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 88 additions & 14 deletions src/mobile/src/ui/components/Migration.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import size from 'lodash/size';
import sample from 'lodash/sampleSize';
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import PropTypes from 'prop-types';
Expand All @@ -8,13 +9,16 @@ import { startTrackingProgress } from 'shared-modules/actions/progress';
import { connect } from 'react-redux';
import { Styling } from 'ui/theme/general';
import { migrate } from 'shared-modules/actions/migrations';
import { setFullNode } from 'shared-modules/actions/settings';
import { reduxPersistStorageAdapter } from 'libs/store';
import ProgressSteps from 'libs/progressSteps';
import { getThemeFromState } from 'shared-modules/selectors/global';
import Header from 'ui/components/Header';
import InfoBox from 'ui/components/InfoBox';
import NotificationButtonComponent from 'ui/components/NotificationButton';
import ProgressBar from 'ui/components/OldProgressBar';
import { height } from 'libs/dimensions';
import DualFooterButtons from './DualFooterButtons';

const styles = StyleSheet.create({
container: {
Expand All @@ -29,15 +33,25 @@ const styles = StyleSheet.create({
paddingTop: height / 16,
},
midContainer: {
flex: 2.6,
flex: 1.9,
alignItems: 'center',
},
bottomContainer: {
flex: 0.7,
alignItems: 'center',
justifyContent: 'flex-end',
},
infoText: {
fontFamily: 'SourceSansPro-Light',
fontSize: Styling.fontSize3,
textAlign: 'left',
backgroundColor: 'transparent',
},
notificationButton: {
position: 'absolute',
top: height / 30 + Styling.statusBarHeight,
left: height / 30,
},
});

/**
Expand All @@ -59,18 +73,37 @@ class Migration extends Component {
startTrackingProgress: PropTypes.func.isRequired,
/** @ignore */
completedMigration: PropTypes.bool.isRequired,
/** @ignore */
notificationLog: PropTypes.array.isRequired,
/** @ignore */
nodes: PropTypes.array.isRequired,
/** @ignore */
setFullNode: PropTypes.func.isRequired,
/** @ignore */
isChangingNode: PropTypes.bool.isRequired,
};

constructor() {
super();
this.state = {
hasFailedMigration: false,
};
this.changeNode = this.changeNode.bind(this);
this.retryMigration = this.retryMigration.bind(this);
}

componentDidMount() {
this.props.startTrackingProgress(ProgressSteps.migration);

this.props.migrate(reduxPersistStorageAdapter);
}

componentWillReceiveProps(newProps) {
if (!this.props.completedMigration && newProps.completedMigration) {
this.navigateToLoadingScreen();
}
if (size(this.props.notificationLog) !== size(newProps.notificationLog)) {
this.setState({ hasFailedMigration: true });
}
}

/**
Expand Down Expand Up @@ -107,6 +140,25 @@ class Migration extends Component {
});
}

/**
* Changes to a random node
*
* @method changeNode
*/
changeNode() {
this.props.setFullNode(...sample(this.props.nodes));
}

/**
* Retries migration in case of failure
*
* @method retryMigration
*/
retryMigration() {
this.setState({ hasFailedMigration: false });
this.props.migrate(reduxPersistStorageAdapter);
}

/**
* Renders progress bar textual information
*
Expand All @@ -119,7 +171,7 @@ class Migration extends Component {
}

render() {
const { t, theme: { body, primary }, activeSteps, activeStepIndex } = this.props;
const { t, theme: { body, primary }, activeSteps, activeStepIndex, isChangingNode } = this.props;
const textColor = { color: body.color };
const sizeOfActiveSteps = size(activeSteps) - 1;

Expand All @@ -135,18 +187,36 @@ class Migration extends Component {
</View>
</InfoBox>
<View style={{ flex: 0.4 }} />
<ProgressBar
style={{
textWrapper: { flex: 0.3 },
}}
indeterminate={activeStepIndex === -1}
progress={activeStepIndex / sizeOfActiveSteps}
color={primary.color}
textColor={body.color}
>
{t(this.renderProgressBarChildren())}
</ProgressBar>
{activeStepIndex > -1 && (
<ProgressBar
style={{
textWrapper: { flex: 0.3 },
}}
indeterminate={activeStepIndex === -1}
progress={activeStepIndex / sizeOfActiveSteps}
color={primary.color}
textColor={body.color}
>
{t(this.renderProgressBarChildren())}
</ProgressBar>
)}
</View>
<View style={styles.bottomContainer}>
{this.state.hasFailedMigration && (
<DualFooterButtons
onLeftButtonPress={this.changeNode}
onRightButtonPress={this.retryMigration}
leftButtonText={t('login:changeNode')}
rightButtonText={t('retry')}
isLeftButtonLoading={isChangingNode}
/>
)}
</View>
{this.state.hasFailedMigration && (
<View style={styles.notificationButton}>
<NotificationButtonComponent displayTopBar={false} />
</View>
)}
</View>
);
}
Expand All @@ -157,11 +227,15 @@ const mapStateToProps = (state) => ({
activeStepIndex: state.progress.activeStepIndex,
activeSteps: state.progress.activeSteps,
completedMigration: state.settings.completedMigration,
notificationLog: state.alerts.notificationLog,
nodes: state.settings.nodes,
isChangingNode: state.ui.isChangingNode,
});

const mapDispatchToProps = {
migrate,
startTrackingProgress,
setFullNode,
};

export default withNamespaces(['migration'])(connect(mapStateToProps, mapDispatchToProps)(Migration));
104 changes: 104 additions & 0 deletions src/mobile/src/ui/components/NotificationButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import size from 'lodash/size';
import React, { Component } from 'react';
import { StyleSheet, View, TouchableOpacity } from 'react-native';
import { getThemeFromState } from 'shared-modules/selectors/global';
import { clearLog } from 'shared-modules/actions/alerts';
import { toggleModalActivity } from 'shared-modules/actions/ui';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withNamespaces } from 'react-i18next';
import { width } from 'libs/dimensions';
import { Icon } from 'ui/theme/icons';

const styles = StyleSheet.create({
container: {
width: width / 9,
height: width / 9,
},
innerContainer: {
justifyContent: 'center',
alignItems: 'center',
width: width / 9,
flex: 1,
},
disabled: {
color: '#a9a9a9',
},
});

export class NotificationButton extends Component {
static propTypes = {
/** @ignore */
theme: PropTypes.object.isRequired,
/** @ignore */
isModalActive: PropTypes.bool.isRequired,
/** @ignore */
clearLog: PropTypes.func.isRequired,
/** @ignore */
toggleModalActivity: PropTypes.func.isRequired,
/** @ignore */
isTransitioning: PropTypes.bool.isRequired,
/** @ignore */
notificationLog: PropTypes.array.isRequired,
/** Determines whether to display topBar when modal is open */
displayTopBar: PropTypes.bool,
};

static defaultProps = {
displayTopBar: true,
};

/**
* Displays error log
*
* @method showModal
*/
showModal() {
const { isTransitioning, theme, notificationLog, displayTopBar } = this.props;
if (!isTransitioning) {
this.props.toggleModalActivity('notificationLog', {
hideModal: this.props.toggleModalActivity,
theme,
notificationLog,
clearLog: this.props.clearLog,
displayTopBar,
});
}
}

render() {
const { theme: { bar }, isModalActive, notificationLog } = this.props;
const hasNotifications = size(notificationLog) && notificationLog.length > 0;

return (
<View style={styles.container}>
{(hasNotifications && (
<TouchableOpacity onPress={() => this.showModal()} style={styles.innerContainer}>
<Icon
name="notification"
size={width / 18}
color={bar.color}
style={isModalActive && styles.disabled && { opacity: 0.5 }}
/>
</TouchableOpacity>
)) || <View style={styles.innerContainer} />}
</View>
);
}
}

const mapStateToProps = (state) => ({
theme: getThemeFromState(state),
isModalActive: state.ui.isModalActive,
isTransitioning: state.ui.isTransitioning,
notificationLog: state.alerts.notificationLog,
});

const mapDispatchToProps = {
clearLog,
toggleModalActivity,
};

export default withNamespaces(['enterSeed', 'global'])(
connect(mapStateToProps, mapDispatchToProps)(NotificationButton),
);
8 changes: 7 additions & 1 deletion src/mobile/src/ui/components/NotificationLogModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ export class NotificationLogModal extends PureComponent {
clearLog: PropTypes.func.isRequired,
/** @ignore */
t: PropTypes.func.isRequired,
/** Deteremines whether to display the topbar */
displayTopBar: PropTypes.bool,
};

static defaultProps = {
displayTopBar: true,
};

constructor() {
Expand Down Expand Up @@ -84,7 +90,7 @@ export class NotificationLogModal extends PureComponent {

return (
<ModalView
displayTopBar
displayTopBar={this.props.displayTopBar}
dualButtons
onLeftButtonPress={() => this.clearNotificationLog()}
onRightButtonPress={() => this.props.hideModal()}
Expand Down
Loading