summaryrefslogtreecommitdiffhomepage
path: root/app
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2018-07-05 18:55:40 +0200
committerAndrej Mihajlov <and@mullvad.net>2018-07-05 18:55:40 +0200
commit6901311224f274620e723b26e20a418b34843d95 (patch)
tree47f68c9749766fac5ef14caeadd3a15d6e4028e1 /app
parentf0ac8a80c03a77fa325e2ef48b3a662692e4bbc2 (diff)
parent9f9e9091981f3bf5ddc3cc80a4f5e4681ea4ef55 (diff)
downloadmullvadvpn-6901311224f274620e723b26e20a418b34843d95.tar.xz
mullvadvpn-6901311224f274620e723b26e20a418b34843d95.zip
Merge branch 'keep-problem-report-until-submitted'
Diffstat (limited to 'app')
-rw-r--r--app/components/Support.js136
-rw-r--r--app/containers/SupportPage.js21
-rw-r--r--app/redux/store.js11
-rw-r--r--app/redux/support/actions.js32
-rw-r--r--app/redux/support/reducers.js37
5 files changed, 172 insertions, 65 deletions
diff --git a/app/components/Support.js b/app/components/Support.js
index 7897f594ec..1f03d67375 100644
--- a/app/components/Support.js
+++ b/app/components/Support.js
@@ -6,14 +6,8 @@ import { Layout, Container } from './Layout';
import styles from './SupportStyles';
import Img from './Img';
-import type { AccountReduxState } from '../redux/account/reducers';
-
-export type SupportReport = {
- email: string,
- message: string,
- savedReport: ?string,
-};
-
+import type { AccountToken } from '../lib/daemon-rpc';
+import type { SupportReportForm } from '../redux/support/actions';
type SupportState = {
email: string,
message: string,
@@ -22,11 +16,15 @@ type SupportState = {
};
export type SupportProps = {
- account: AccountReduxState,
+ defaultEmail: string,
+ defaultMessage: string,
+ accountHistory: Array<AccountToken>,
onClose: () => void,
- onViewLog: (string) => void,
- onCollectLog: (Array<string>) => Promise<string>,
- onSend: (email: string, message: string, savedReport: string) => void,
+ viewLog: (path: string) => void,
+ saveReportForm: (form: SupportReportForm) => void,
+ clearReportForm: () => void,
+ collectProblemReport: (accountsToRedact: Array<string>) => Promise<string>,
+ sendProblemReport: (email: string, message: string, savedReport: string) => Promise<void>,
};
export default class Support extends Component<SupportProps, SupportState> {
@@ -37,68 +35,102 @@ export default class Support extends Component<SupportProps, SupportState> {
sendState: 'INITIAL',
};
+ _collectLogPromise: ?Promise<string>;
+
+ constructor(props: SupportProps) {
+ super(props);
+
+ // seed initial data from props
+ this.state.email = props.defaultEmail;
+ this.state.message = props.defaultMessage;
+ }
+
validate() {
return this.state.message.trim().length > 0;
}
onChangeEmail = (email: string) => {
- this.setState({ email: email });
+ this.setState({ email: email }, () => {
+ this._saveFormData();
+ });
};
onChangeDescription = (description: string) => {
- this.setState({ message: description });
+ this.setState({ message: description }, () => {
+ this._saveFormData();
+ });
};
- onViewLog = () => {
- this._getLog().then((path) => {
- this.props.onViewLog(path);
- });
+ onViewLog = async (): Promise<void> => {
+ try {
+ const reportPath = await this._collectLog();
+ this.props.viewLog(reportPath);
+ } catch (error) {
+ // TODO: handle error
+ }
};
- _getLog(): Promise<string> {
- const accountsToRedact = this.props.account.accountHistory;
- const { savedReport } = this.state;
- return savedReport
- ? Promise.resolve(savedReport)
- : this.props.onCollectLog(accountsToRedact).then((path) => {
- return new Promise((resolve) =>
- this.setState({ savedReport: path }, () => resolve(path)),
- );
+ _saveFormData() {
+ this.props.saveReportForm({
+ email: this.state.email,
+ message: this.state.message,
+ });
+ }
+
+ async _collectLog(): Promise<string> {
+ if (this._collectLogPromise) {
+ return this._collectLogPromise;
+ } else {
+ const collectPromise = this.props.collectProblemReport(this.props.accountHistory);
+
+ // save promise to prevent subsequent requests
+ this._collectLogPromise = collectPromise;
+
+ try {
+ const reportPath = await collectPromise;
+ return new Promise((resolve) => {
+ this.setState({ savedReport: reportPath }, () => resolve(reportPath));
});
+ } catch (error) {
+ this._collectLogPromise = null;
+
+ throw error;
+ }
+ }
}
- onSend = () => {
+ onSend = async (): Promise<void> => {
if (this.state.sendState === 'INITIAL' && this.state.email.length === 0) {
- this.setState({
- sendState: 'CONFIRM_NO_EMAIL',
+ return new Promise((resolve) => {
+ this.setState({ sendState: 'CONFIRM_NO_EMAIL' }, () => resolve());
});
} else {
- this._sendProblemReport();
+ try {
+ await this._sendReport();
+ } catch (error) {
+ // No-op
+ }
}
};
- _sendProblemReport() {
- this.setState(
- {
- sendState: 'LOADING',
- },
- () => {
- this._getLog()
- .then((path) => {
- return this.props.onSend(this.state.email, this.state.message, path);
- })
- .then(() => {
- this.setState({
- sendState: 'SUCCESS',
- });
- })
- .catch(() => {
- this.setState({
- sendState: 'FAILED',
- });
+ _sendReport(): Promise<void> {
+ return new Promise((resolve, reject) => {
+ this.setState({ sendState: 'LOADING' }, async () => {
+ try {
+ const { email, message } = this.state;
+ const reportPath = await this._collectLog();
+ await this.props.sendProblemReport(email, message, reportPath);
+ this.props.clearReportForm();
+ this.setState({ sendState: 'SUCCESS' }, () => {
+ resolve();
});
- },
- );
+ } catch (error) {
+ this.setState({ sendState: 'FAILED' }, () => {
+ reject(error);
+ });
+ }
+ });
+ });
}
render() {
diff --git a/app/containers/SupportPage.js b/app/containers/SupportPage.js
index d9ce8ad6ec..c80e9508de 100644
--- a/app/containers/SupportPage.js
+++ b/app/containers/SupportPage.js
@@ -8,26 +8,25 @@ import { collectProblemReport, sendProblemReport } from '../lib/problem-report';
import type { ReduxState, ReduxDispatch } from '../redux/store';
import type { SharedRouteProps } from '../routes';
+import supportActions from '../redux/support/actions';
const mapStateToProps = (state: ReduxState) => ({
- account: state.account,
+ defaultEmail: state.support.email,
+ defaultMessage: state.support.message,
+ accountHistory: state.account.accountHistory,
});
const mapDispatchToProps = (dispatch: ReduxDispatch, _props: SharedRouteProps) => {
+ const { saveReportForm, clearReportForm } = bindActionCreators(supportActions, dispatch);
const { push: pushHistory } = bindActionCreators({ push }, dispatch);
return {
onClose: () => pushHistory('/settings'),
-
- onCollectLog: (toRedact) => {
- return collectProblemReport(toRedact);
- },
-
- onViewLog: (path) => openItem(path),
-
- onSend: (email, message, savedReport) => {
- return sendProblemReport(email, message, savedReport);
- },
+ viewLog: (path) => openItem(path),
+ saveReportForm,
+ clearReportForm,
+ collectProblemReport,
+ sendProblemReport,
};
};
diff --git a/app/redux/store.js b/app/redux/store.js
index 9e15b3a325..0070248099 100644
--- a/app/redux/store.js
+++ b/app/redux/store.js
@@ -9,24 +9,29 @@ import connection from './connection/reducers';
import connectionActions from './connection/actions';
import settings from './settings/reducers';
import settingsActions from './settings/actions';
+import support from './support/reducers';
+import supportActions from './support/actions';
import type { Store } from 'redux';
import type { History } from 'history';
import type { AccountReduxState } from './account/reducers';
import type { ConnectionReduxState } from './connection/reducers';
import type { SettingsReduxState } from './settings/reducers';
+import type { SupportReduxState } from './support/reducers';
-import type { ConnectionAction } from './connection/actions';
import type { AccountAction } from './account/actions';
+import type { ConnectionAction } from './connection/actions';
import type { SettingsAction } from './settings/actions';
+import type { SupportAction } from './support/actions';
export type ReduxState = {
account: AccountReduxState,
connection: ConnectionReduxState,
settings: SettingsReduxState,
+ support: SupportReduxState,
};
-export type ReduxAction = AccountAction | SettingsAction | ConnectionAction;
+export type ReduxAction = AccountAction | ConnectionAction | SettingsAction | SupportAction;
export type ReduxStore = Store<ReduxState, ReduxAction, ReduxDispatch>;
export type ReduxGetState = () => ReduxState;
export type ReduxDispatch = (action: ReduxAction | ReduxThunk) => any;
@@ -42,6 +47,7 @@ export default function configureStore(
...accountActions,
...connectionActions,
...settingsActions,
+ ...supportActions,
pushRoute: (route) => push(route),
replaceRoute: (route) => replace(route),
};
@@ -50,6 +56,7 @@ export default function configureStore(
account,
connection,
settings,
+ support,
router: routerReducer,
};
diff --git a/app/redux/support/actions.js b/app/redux/support/actions.js
new file mode 100644
index 0000000000..d81fd19c0d
--- /dev/null
+++ b/app/redux/support/actions.js
@@ -0,0 +1,32 @@
+// @flow
+
+export type SupportReportForm = {
+ email: string,
+ message: string,
+};
+
+export type KeepReportFormAction = {
+ type: 'SAVE_REPORT_FORM',
+ form: SupportReportForm,
+};
+
+export type ClearReportFormAction = {
+ type: 'CLEAR_REPORT_FORM',
+};
+
+export type SupportAction = KeepReportFormAction | ClearReportFormAction;
+
+function saveReportForm(form: SupportReportForm): KeepReportFormAction {
+ return {
+ type: 'SAVE_REPORT_FORM',
+ form,
+ };
+}
+
+function clearReportForm(): ClearReportFormAction {
+ return {
+ type: 'CLEAR_REPORT_FORM',
+ };
+}
+
+export default { saveReportForm, clearReportForm };
diff --git a/app/redux/support/reducers.js b/app/redux/support/reducers.js
new file mode 100644
index 0000000000..f885f22dd5
--- /dev/null
+++ b/app/redux/support/reducers.js
@@ -0,0 +1,37 @@
+// @flow
+
+import type { ReduxAction } from '../store';
+
+export type SupportReduxState = {
+ email: string,
+ message: string,
+};
+
+const initialState: SupportReduxState = {
+ email: '',
+ message: '',
+};
+
+export default function(
+ state: SupportReduxState = initialState,
+ action: ReduxAction,
+): SupportReduxState {
+ switch (action.type) {
+ case 'SAVE_REPORT_FORM':
+ return {
+ ...state,
+ email: action.form.email,
+ message: action.form.message,
+ };
+
+ case 'CLEAR_REPORT_FORM':
+ return {
+ ...state,
+ email: '',
+ message: '',
+ };
+
+ default:
+ return state;
+ }
+}