diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2019-01-16 10:34:57 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2019-01-16 10:34:57 +0100 |
| commit | 7a8b8a8c42555df3456084fb33a8d7f4ba98b890 (patch) | |
| tree | 02179347442c8c332c9b6b47f0d571b756699c31 | |
| parent | e094b526b94bf75ccf25fc5e575cc641a62071ab (diff) | |
| parent | d55a6546b8dcea90ae19aba75c73d50796e4d87b (diff) | |
| download | mullvadvpn-7a8b8a8c42555df3456084fb33a8d7f4ba98b890.tar.xz mullvadvpn-7a8b8a8c42555df3456084fb33a8d7f4ba98b890.zip | |
Merge branch 'confirm-no-email-dialog'
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | gui/package.json | 2 | ||||
| -rw-r--r-- | gui/packages/components/src/Modal.tsx | 52 | ||||
| -rw-r--r-- | gui/packages/components/src/index.ts | 1 | ||||
| -rw-r--r-- | gui/packages/desktop/src/renderer/components/Support.js | 174 | ||||
| -rw-r--r-- | gui/packages/desktop/src/renderer/components/SupportStyles.js | 22 | ||||
| -rw-r--r-- | gui/yarn.lock | 8 |
7 files changed, 187 insertions, 73 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index c5183fae9d..15da6f0f23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ Line wrap the file at 100 chars. Th - Add a drop-down warning to notify the user when the account credits are running low. - Allow the 169.254.0.0/16 private network in addition to the other networks allowed when local network sharing is enabled. +- Improve the confirmation dialog when submitting a bug report without an email specified. #### macOS - Add a monochromatic tray icon option for the GUI. diff --git a/gui/package.json b/gui/package.json index 504ddbe0d9..2a62958615 100644 --- a/gui/package.json +++ b/gui/package.json @@ -23,6 +23,6 @@ "eslint-plugin-react": "^7.10.0", "flow-bin": "0.82", "flow-typed": "^2.5.1", - "prettier": "1.15.1" + "prettier": "1.15.3" } } diff --git a/gui/packages/components/src/Modal.tsx b/gui/packages/components/src/Modal.tsx new file mode 100644 index 0000000000..6eb070262b --- /dev/null +++ b/gui/packages/components/src/Modal.tsx @@ -0,0 +1,52 @@ +import * as React from 'react'; + +export class ModalContent extends React.Component { + public render() { + return ( + <div + style={{ + position: 'absolute', + display: 'flex', + flexDirection: 'column', + flex: 1, + top: 0, + left: 0, + right: 0, + bottom: 0, + }}> + {this.props.children} + </div> + ); + } +} + +export class ModalAlert extends React.Component { + public render() { + return ( + <div + style={{ + backgroundColor: 'rgba(0,0,0,0.5)', + position: 'absolute', + display: 'flex', + flexDirection: 'column', + flex: 1, + top: 0, + left: 0, + right: 0, + bottom: 0, + }}> + {this.props.children} + </div> + ); + } +} + +interface IModalContainerProps { + children?: Array<React.ReactElement<ModalContent | ModalAlert>>; +} + +export class ModalContainer extends React.Component<IModalContainerProps> { + public render() { + return <div style={{ position: 'relative', flex: 1 }}>{this.props.children}</div>; + } +} diff --git a/gui/packages/components/src/index.ts b/gui/packages/components/src/index.ts index 46c4ec3195..31c5ee6fec 100644 --- a/gui/packages/components/src/index.ts +++ b/gui/packages/components/src/index.ts @@ -5,3 +5,4 @@ export { default as SecuredLabel, SecuredDisplayStyle } from './SecuredLabel'; export { default as HeaderBar, HeaderBarStyle, Brand, SettingsBarButton } from './HeaderBar'; export { default as SettingsHeader, HeaderTitle, HeaderSubTitle } from './SettingsHeader'; export { default as ImageView } from './ImageView'; +export { ModalContainer, ModalAlert, ModalContent } from './Modal'; diff --git a/gui/packages/desktop/src/renderer/components/Support.js b/gui/packages/desktop/src/renderer/components/Support.js index 7db2f7458b..0398976589 100644 --- a/gui/packages/desktop/src/renderer/components/Support.js +++ b/gui/packages/desktop/src/renderer/components/Support.js @@ -2,7 +2,15 @@ import * as React from 'react'; import { Component, Text, View, TextInput } from 'reactxp'; -import { ImageView, SettingsHeader, HeaderTitle, HeaderSubTitle } from '@mullvad/components'; +import { + ImageView, + SettingsHeader, + HeaderTitle, + HeaderSubTitle, + ModalContainer, + ModalContent, + ModalAlert, +} from '@mullvad/components'; import * as AppButton from './AppButton'; import { Layout, Container } from './Layout'; import { NavigationBar, BackBarItem } from './NavigationBar'; @@ -14,7 +22,7 @@ type SupportState = { email: string, message: string, savedReport: ?string, - sendState: 'INITIAL' | 'CONFIRM_NO_EMAIL' | 'LOADING' | 'SUCCESS' | 'FAILED', + sendState: 'INITIAL' | 'CONFIRM' | 'LOADING' | 'SUCCESS' | 'FAILED', }; export type SupportProps = { @@ -103,17 +111,36 @@ export default class Support extends Component<SupportProps, SupportState> { } onSend = async (): Promise<void> => { - if (this.state.sendState === 'INITIAL' && this.state.email.length === 0) { - return new Promise((resolve) => { - this.setState({ sendState: 'CONFIRM_NO_EMAIL' }, () => resolve()); - }); - } else { - try { - await this._sendReport(); - } catch (error) { - // No-op - } + switch (this.state.sendState) { + case 'INITIAL': + if (this.state.email.length === 0) { + this.setState({ sendState: 'CONFIRM' }); + } else { + try { + await this._sendReport(); + } catch (error) { + // No-op + } + } + return Promise.resolve(); + + case 'CONFIRM': + try { + await this._sendReport(); + } catch (error) { + // No-op + } + return Promise.resolve(); + + default: + break; } + + return Promise.resolve(); + }; + + onCancelConfirmation = () => { + this.setState({ sendState: 'INITIAL' }); }; _sendReport(): Promise<void> { @@ -141,7 +168,7 @@ export default class Support extends Component<SupportProps, SupportState> { const header = ( <SettingsHeader> <HeaderTitle>Report a problem</HeaderTitle> - {(sendState === 'INITIAL' || sendState === 'CONFIRM_NO_EMAIL') && ( + {(sendState === 'INITIAL' || sendState === 'CONFIRM') && ( <HeaderSubTitle> { "To help you more effectively, your app's log file will be attached to this message. Your data will remain secure and private, as it is anonymised before being sent over an encrypted channel." @@ -156,17 +183,20 @@ export default class Support extends Component<SupportProps, SupportState> { return ( <Layout> <Container> - <View style={styles.support}> - <NavigationBar> - <BackBarItem action={this.props.onClose} title={'Settings'} /> - </NavigationBar> - - <View style={styles.support__container}> - {header} - - {content} - </View> - </View> + <ModalContainer> + <ModalContent> + <View style={styles.support}> + <NavigationBar> + <BackBarItem action={this.props.onClose} title={'Settings'} /> + </NavigationBar> + <View style={styles.support__container}> + {header} + {content} + </View> + </View> + </ModalContent> + {sendState === 'CONFIRM' && <ModalAlert>{this._renderConfirm()}</ModalAlert>} + </ModalContainer> </Container> </Layout> ); @@ -175,7 +205,7 @@ export default class Support extends Component<SupportProps, SupportState> { _renderContent() { switch (this.state.sendState) { case 'INITIAL': - case 'CONFIRM_NO_EMAIL': + case 'CONFIRM': return this._renderForm(); case 'LOADING': return this._renderLoading(); @@ -188,6 +218,10 @@ export default class Support extends Component<SupportProps, SupportState> { } } + _renderConfirm() { + return <ConfirmNoEmailDialog onConfirm={this.onSend} onDismiss={this.onCancelConfirmation} />; + } + _renderForm() { return ( <View style={styles.support__content}> @@ -214,52 +248,25 @@ export default class Support extends Component<SupportProps, SupportState> { </View> </View> <View style={styles.support__footer}> - {this.state.sendState === 'CONFIRM_NO_EMAIL' - ? this._renderNoEmailWarning() - : this._renderActionButtons()} + <AppButton.BlueButton + style={styles.view_logs_button} + onPress={this.onViewLog} + testName="support__view_logs"> + <AppButton.Label>View app logs</AppButton.Label> + <ImageView source="icon-extLink" height={16} width={16} /> + </AppButton.BlueButton> + <AppButton.GreenButton + disabled={!this.validate()} + onPress={this.onSend} + testName="support__send_logs"> + Send + </AppButton.GreenButton> </View> </View> </View> ); } - _renderNoEmailWarning() { - return ( - <View> - <Text style={styles.support__no_email_warning}> - You are about to send the problem report without a way for us to get back to you. If you - want an answer to your report you will have to enter an email address. - </Text> - <AppButton.GreenButton - disabled={!this.validate()} - onPress={this.onSend} - testName="support__send_logs"> - {'Send anyway'} - </AppButton.GreenButton> - </View> - ); - } - - _renderActionButtons() { - return ( - <View> - <AppButton.BlueButton - style={styles.view_logs_button} - onPress={this.onViewLog} - testName="support__view_logs"> - <AppButton.Label>View app logs</AppButton.Label> - <ImageView source="icon-extLink" height={16} width={16} /> - </AppButton.BlueButton> - <AppButton.GreenButton - disabled={!this.validate() || this.props.isOffline} - onPress={this.onSend} - testName="support__send_logs"> - Send - </AppButton.GreenButton> - </View> - ); - } - _renderLoading() { return ( <View style={styles.support__content}> @@ -331,3 +338,40 @@ export default class Support extends Component<SupportProps, SupportState> { ); } } + +type ConfirmNoEmailDialogProps = { + onConfirm: () => void, + onDismiss: () => void, +}; + +class ConfirmNoEmailDialog extends Component<ConfirmNoEmailDialogProps> { + render() { + return ( + <View style={styles.confirm_no_email_background}> + <View style={styles.confirm_no_email_dialog}> + <Text style={styles.confirm_no_email_warning}> + You are about to send the problem report without a way for us to get back to you. If you + want an answer to your report you will have to enter an email address. + </Text> + <AppButton.GreenButton onPress={this.props.onConfirm} testName="support__send_logs"> + {'Send anyway'} + </AppButton.GreenButton> + <AppButton.RedButton + onPress={this._dismiss} + style={styles.confirm_no_email_back_button} + testName="support__back"> + {'Back'} + </AppButton.RedButton> + </View> + </View> + ); + } + + _confirm = () => { + this.props.onConfirm(); + }; + + _dismiss = () => { + this.props.onDismiss(); + }; +} diff --git a/gui/packages/desktop/src/renderer/components/SupportStyles.js b/gui/packages/desktop/src/renderer/components/SupportStyles.js index e3b8491862..c2b4e2dbb7 100644 --- a/gui/packages/desktop/src/renderer/components/SupportStyles.js +++ b/gui/packages/desktop/src/renderer/components/SupportStyles.js @@ -119,11 +119,27 @@ export default { color: colors.white, marginBottom: 4, }), - support__no_email_warning: Styles.createTextStyle({ + confirm_no_email_background: Styles.createViewStyle({ + display: 'flex', + flex: 1, + justifyContent: 'center', + paddingLeft: 14, + paddingRight: 14, + }), + confirm_no_email_dialog: Styles.createViewStyle({ + backgroundColor: colors.darkBlue, + borderRadius: 11, + padding: 16, + }), + confirm_no_email_warning: Styles.createTextStyle({ fontFamily: 'Open Sans', - fontSize: 13, - lineHeight: 16, + fontSize: 16, + fontWeight: '500', + lineHeight: 20, color: colors.white80, marginBottom: 12, }), + confirm_no_email_back_button: Styles.createViewStyle({ + marginTop: 16, + }), }; diff --git a/gui/yarn.lock b/gui/yarn.lock index eb27f004cb..20dd7a3fea 100644 --- a/gui/yarn.lock +++ b/gui/yarn.lock @@ -7505,10 +7505,10 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= -prettier@1.15.1: - version "1.15.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.1.tgz#06c67106afb1b40e74b002353b2079cc7e0e67bf" - integrity sha512-4rgV2hyc/5Pk0XHH4VjJWHRgVjgRbpMfLQjREAhHBtyW1UvTFkjJEsueGYNYYZd9mn97K+1qv0EBwm11zoaSgA== +prettier@1.15.3: + version "1.15.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.3.tgz#1feaac5bdd181237b54dbe65d874e02a1472786a" + integrity sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg== prettier@^1.13.7: version "1.14.2" |
