diff options
| author | Erik Larkö <erik@mullvad.net> | 2017-11-02 10:51:12 +0100 |
|---|---|---|
| committer | Erik Larkö <erik@mullvad.net> | 2017-11-09 12:42:56 +0100 |
| commit | ae8e86bb81cc722d9667c439d3508660398da707 (patch) | |
| tree | 05a467e918755c290194df119d5977862d1e6f4b /app/components | |
| parent | 01d617bab821f25412f4874354468afea30bd7b9 (diff) | |
| download | mullvadvpn-ae8e86bb81cc722d9667c439d3508660398da707.tar.xz mullvadvpn-ae8e86bb81cc722d9667c439d3508660398da707.zip | |
Problem report
Diffstat (limited to 'app/components')
| -rw-r--r-- | app/components/Settings.js | 2 | ||||
| -rw-r--r-- | app/components/Support.css | 45 | ||||
| -rw-r--r-- | app/components/Support.js | 232 |
3 files changed, 226 insertions, 53 deletions
diff --git a/app/components/Settings.js b/app/components/Settings.js index fd9fbebebd..95506cddc2 100644 --- a/app/components/Settings.js +++ b/app/components/Settings.js @@ -96,7 +96,7 @@ export default class Settings extends Component { <img className="settings__cell-icon" src="./assets/images/icon-extLink.svg" /> </div> <div className="settings__view-support settings__cell settings__cell--active" onClick={ this.props.onViewSupport }> - <div className="settings__cell-label">Contact support</div> + <div className="settings__cell-label">Report a problem</div> <img className="settings__cell-disclosure" src="assets/images/icon-chevron.svg" /> </div> </div> diff --git a/app/components/Support.css b/app/components/Support.css index 58f4df3a20..8e8198742c 100644 --- a/app/components/Support.css +++ b/app/components/Support.css @@ -77,7 +77,7 @@ margin-top: 8px; } -.support__form-row--description { +.support__form-row-message { display: flex; flex: 1 1 auto; } @@ -100,14 +100,14 @@ color: rgba(41,77,115,0.4); } -.support__form-description-scroll-wrap { +.support__form-message-scroll-wrap { width: 100%; display: flex; border-radius: 4px; overflow: hidden; } -.support__form-description { +.support__form-message { width: 100%; border: 0; overflow-y: scroll; @@ -121,7 +121,7 @@ background-color: #fff; } -.support__form-description::-webkit-input-placeholder { +.support__form-message::-webkit-input-placeholder { color: rgba(41,77,115,0.4); } @@ -131,4 +131,39 @@ .support__footer .button + .button { margin-top: 16px; -}
\ No newline at end of file +} + +.support__sent-email { + display: inline; + font-weight: 900; + color: white; +} + +.support__status-security--secure { + font-family: "Open Sans"; + font-size: 16px; + font-weight: 800; + line-height: 22px; + margin-bottom: 4px; + color: #44AD4D; + text-transform: uppercase; +} + +.support__send-status { + font-family: DINPro; + font-size: 38px; + font-weight: 900; + line-height: 1.16em; + max-height: calc(1.16em * 2); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + letter-spacing: -0.9px; + color: #FFFFFF; + margin-bottom: 4px; +} + +.support__status-icon { + text-align: center; + margin-bottom: 32px; +} diff --git a/app/components/Support.js b/app/components/Support.js index 783133568f..2dcb6ad1ee 100644 --- a/app/components/Support.js +++ b/app/components/Support.js @@ -5,25 +5,34 @@ import ExternalLinkSVG from '../assets/images/icon-extLink.svg'; export type SupportReport = { email: string, - description: string + message: string, + savedReport: ?string, }; -export type SupportState = SupportReport; +export type SupportState = { + email: string, + message: string, + savedReport: ?string, + sendState: 'INITIAL' | 'LOADING' | 'SUCCESS' | 'FAILED', +}; export type SupportProps = { onClose: () => void; - onViewLogs: () => void; - onSend: (report: SupportReport) => void; + onViewLog: (string) => void; + onCollectLog: () => Promise<string>; + onSend: (email: string, message: string, savedReport: string) => void; }; export default class Support extends Component { props: SupportProps; state: SupportState = { email: '', - description: '' + message: '', + savedReport: null, + sendState: 'INITIAL', } validate() { - return this.state.description.trim().length > 0; + return this.state.message.trim().length > 0; } onChangeEmail = (e: Event) => { @@ -39,14 +48,61 @@ export default class Support extends Component { if(!(input instanceof HTMLTextAreaElement)) { throw new Error('input must be an instance of HTMLTextAreaElement'); } - this.setState({ description: input.value }); + this.setState({ message: input.value }); + } + + onViewLog = () => { + + this._getLog() + .then((path) => { + this.props.onViewLog(path); + }); + } + + _getLog() { + const { savedReport } = this.state; + return savedReport ? + Promise.resolve(savedReport) : + this.props.onCollectLog() + .then( path => { + return new Promise(resolve => this.setState({ savedReport: path }, () => resolve(path))); + }); } onSend = () => { - this.props.onSend(this.state); + 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', + }); + }); + }); } render() { + + const header = <div className="support__header"> + <h2 className="support__title">Report a problem</h2> + { this.state.sendState === 'INITIAL' && <div className="support__subtitle"> + { `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 encrypted & anonymised before sending.` } + </div> + } + </div>; + + const content = this._renderContent(); + return ( <Layout> <Header hidden={ true } style={ 'defaultDark' } /> @@ -58,46 +114,9 @@ export default class Support extends Component { </div> <div className="support__container"> - <div className="support__header"> - <h2 className="support__title">Contact support</h2> - <div className="support__subtitle"> - { `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 encrypted & anonymised before sending.` } - </div> - </div> + { header } - <div className="support__content"> - <div className="support__form"> - <div className="support__form-row"> - <input className="support__form-email" - type="email" - placeholder="Your email" - value={ this.state.email } - onChange={ this.onChangeEmail } - autoFocus={ true } /> - </div> - <div className="support__form-row support__form-row--description"> - <div className="support__form-description-scroll-wrap"> - <textarea className="support__form-description" - placeholder="Describe your problem" - value={ this.state.description } - onChange={ this.onChangeDescription } /> - </div> - </div> - <div className="support__footer"> - <button type="button" - className="button button--primary" - onClick={ this.props.onViewLogs }> - <span className="support__form-view-logs button-label">View app logs</span> - <ExternalLinkSVG className="button-icon button-icon--16" /> - </button> - <button type="button" - className="support__form-send button button--positive" - disabled={ !this.validate() } - onClick={ this.onSend }>Send</button> - </div> - </div> - </div> + { content } </div> </div> @@ -105,4 +124,123 @@ export default class Support extends Component { </Layout> ); } + + _renderContent() { + switch(this.state.sendState) { + case 'INITIAL': + return this._renderForm(); + case 'LOADING': + return this._renderLoading(); + case 'SUCCESS': + return this._renderSent(); + case 'FAILED': + return this._renderFailed(); + default: + return null; + } + } + + _renderForm() { + return <div className="support__content"> + <div className="support__form"> + <div className="support__form-row"> + <input className="support__form-email" + type="email" + placeholder="Your email" + value={ this.state.email } + onChange={ this.onChangeEmail } + autoFocus={ true } /> + </div> + <div className="support__form-row support__form-row-message"> + <div className="support__form-message-scroll-wrap"> + <textarea className="support__form-message" + placeholder="Describe your problem" + value={ this.state.message } + onChange={ this.onChangeDescription } /> + </div> + </div> + <div className="support__footer"> + <button type="button" + className="support__form-view-logs button button--primary" + onClick={ this.onViewLog }> + <span className="button-label">View app logs</span> + <ExternalLinkSVG className="button-icon button-icon--16" /> + </button> + <button type="button" + className="support__form-send button button--positive" + disabled={ !this.validate() } + onClick={ this.onSend }>Send</button> + </div> + </div> + </div>; + } + + _renderLoading() { + return <div className="support__content"> + + <div className="support__form"> + <div className="support__form-row"> + <div className="support__status-icon"> + <img src="./assets/images/icon-spinner.svg" alt="" /> + </div> + <div className="support__status-security--secure"> + Secure Connection + </div> + <div className="support__send-status"> + <span>Sending...</span> + </div> + </div> + </div> + </div>; + } + + _renderSent() { + return <div className="support__content"> + <div className="support__form"> + <div className="support__form-row"> + <div className="support__status-icon"> + <img src="./assets/images/icon-success.svg" alt="" /> + </div> + <div className="support__status-security--secure"> + Secure Connection + </div> + <div className="support__send-status"> + <span>Sent</span> + </div> + <div className="support__subtitle"> + Thanks! We will look into this. If needed we will contact you on {'\u00A0'} + <div className="support__sent-email">{ this.state.email }</div> + </div> + </div> + </div> + </div>; + } + + _renderFailed() { + return <div className="support__content"> + <div className="support__form"> + <div className="support__form-row"> + <div className="support__status-icon"> + <img src="./assets/images/icon-fail.svg" alt="" /> + </div> + <div className="support__status-security--secure"> + Secure Connection + </div> + <div className="support__send-status"> + <span>Failed to send</span> + </div> + </div> + </div> + <div className="support__footer"> + <button type="button" + className="support__form-view-logs button button--primary" + onClick={ () => this.setState({ sendState: 'INITIAL' }) }> + <span className="button-label">Edit message</span> + </button> + <button type="button" + className="support__form-send button button--positive" + onClick={ this.onSend }>Try again</button> + </div> + </div>; + } } |
