diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2018-07-05 18:55:40 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2018-07-05 18:55:40 +0200 |
| commit | 6901311224f274620e723b26e20a418b34843d95 (patch) | |
| tree | 47f68c9749766fac5ef14caeadd3a15d6e4028e1 /app | |
| parent | f0ac8a80c03a77fa325e2ef48b3a662692e4bbc2 (diff) | |
| parent | 9f9e9091981f3bf5ddc3cc80a4f5e4681ea4ef55 (diff) | |
| download | mullvadvpn-6901311224f274620e723b26e20a418b34843d95.tar.xz mullvadvpn-6901311224f274620e723b26e20a418b34843d95.zip | |
Merge branch 'keep-problem-report-until-submitted'
Diffstat (limited to 'app')
| -rw-r--r-- | app/components/Support.js | 136 | ||||
| -rw-r--r-- | app/containers/SupportPage.js | 21 | ||||
| -rw-r--r-- | app/redux/store.js | 11 | ||||
| -rw-r--r-- | app/redux/support/actions.js | 32 | ||||
| -rw-r--r-- | app/redux/support/reducers.js | 37 |
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; + } +} |
