aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/auth
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/auth')
-rw-r--r--src/components/auth/AuthLayout.js12
-rw-r--r--src/components/auth/ChangeServer.js79
-rw-r--r--src/components/auth/Import.js43
-rw-r--r--src/components/auth/Invite.js52
-rw-r--r--src/components/auth/Locked.js97
-rw-r--r--src/components/auth/Login.js132
-rw-r--r--src/components/auth/Password.js77
-rw-r--r--src/components/auth/SetupAssistant.js18
-rw-r--r--src/components/auth/Signup.js127
-rw-r--r--src/components/auth/Welcome.js57
10 files changed, 346 insertions, 348 deletions
diff --git a/src/components/auth/AuthLayout.js b/src/components/auth/AuthLayout.js
index 8235932c2..3e2b75731 100644
--- a/src/components/auth/AuthLayout.js
+++ b/src/components/auth/AuthLayout.js
@@ -1,9 +1,9 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import { intlShape } from 'react-intl';
5import { TitleBar } from 'electron-react-titlebar'; 4import { TitleBar } from 'electron-react-titlebar';
6 5
6import { injectIntl } from 'react-intl';
7import Link from '../ui/Link'; 7import Link from '../ui/Link';
8import InfoBar from '../ui/InfoBar'; 8import InfoBar from '../ui/InfoBar';
9 9
@@ -17,7 +17,6 @@ import { isWindows } from '../../environment';
17import AppUpdateInfoBar from '../AppUpdateInfoBar'; 17import AppUpdateInfoBar from '../AppUpdateInfoBar';
18import { GITHUB_FERDI_URL } from '../../config'; 18import { GITHUB_FERDI_URL } from '../../config';
19 19
20export default
21@observer 20@observer
22class AuthLayout extends Component { 21class AuthLayout extends Component {
23 static propTypes = { 22 static propTypes = {
@@ -36,10 +35,6 @@ class AuthLayout extends Component {
36 shouldShowAppUpdateInfoBar: true, 35 shouldShowAppUpdateInfoBar: true,
37 }; 36 };
38 37
39 static contextTypes = {
40 intl: intlShape,
41 };
42
43 render() { 38 render() {
44 const { 39 const {
45 children, 40 children,
@@ -52,7 +47,8 @@ class AuthLayout extends Component {
52 installAppUpdate, 47 installAppUpdate,
53 appUpdateIsDownloaded, 48 appUpdateIsDownloaded,
54 } = this.props; 49 } = this.props;
55 const { intl } = this.context; 50
51 const { intl } = this.props;
56 52
57 return ( 53 return (
58 <> 54 <>
@@ -108,3 +104,5 @@ class AuthLayout extends Component {
108 ); 104 );
109 } 105 }
110} 106}
107
108export default injectIntl(AuthLayout);
diff --git a/src/components/auth/ChangeServer.js b/src/components/auth/ChangeServer.js
index 8e8a7af32..b98fb50f7 100644
--- a/src/components/auth/ChangeServer.js
+++ b/src/components/auth/ChangeServer.js
@@ -1,7 +1,7 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, injectIntl } from 'react-intl';
5import Form from '../../lib/Form'; 5import Form from '../../lib/Form';
6import Input from '../ui/Input'; 6import Input from '../ui/Input';
7import Select from '../ui/Select'; 7import Select from '../ui/Select';
@@ -14,56 +14,65 @@ import globalMessages from '../../i18n/globalMessages';
14const messages = defineMessages({ 14const messages = defineMessages({
15 headline: { 15 headline: {
16 id: 'changeserver.headline', 16 id: 'changeserver.headline',
17 defaultMessage: '!!!Change server', 17 defaultMessage: 'Change server',
18 }, 18 },
19 label: { 19 label: {
20 id: 'changeserver.label', 20 id: 'changeserver.label',
21 defaultMessage: '!!!Server', 21 defaultMessage: 'Server',
22 }, 22 },
23 warning: { 23 warning: {
24 id: 'changeserver.warning', 24 id: 'changeserver.warning',
25 defaultMessage: '!!!Extra settings offered by Ferdi will not be saved', 25 defaultMessage: 'Extra settings offered by Ferdi will not be saved',
26 }, 26 },
27 customServerLabel: { 27 customServerLabel: {
28 id: 'changeserver.customServerLabel', 28 id: 'changeserver.customServerLabel',
29 defaultMessage: '!!!Custom server', 29 defaultMessage: 'Custom server',
30 }, 30 },
31 urlError: { 31 urlError: {
32 id: 'changeserver.urlError', 32 id: 'changeserver.urlError',
33 defaultMessage: '!!!Enter a valid URL', 33 defaultMessage: 'Enter a valid URL',
34 }, 34 },
35}); 35});
36 36
37export default @observer class ChangeServer extends Component { 37@observer
38class ChangeServer extends Component {
38 static propTypes = { 39 static propTypes = {
39 onSubmit: PropTypes.func.isRequired, 40 onSubmit: PropTypes.func.isRequired,
40 server: PropTypes.string.isRequired, 41 server: PropTypes.string.isRequired,
41 }; 42 };
42 43
43 static contextTypes = { 44 ferdiServer = LIVE_FERDI_API;
44 intl: intlShape,
45 };
46
47 ferdiServer=LIVE_FERDI_API;
48 45
49 franzServer=LIVE_FRANZ_API; 46 franzServer = LIVE_FRANZ_API;
50 47
51 defaultServers=[this.franzServer, this.ferdiServer]; 48 defaultServers = [this.franzServer, this.ferdiServer];
52 49
53 form = new Form({ 50 form = new Form(
54 fields: { 51 {
55 server: { 52 fields: {
56 label: this.context.intl.formatMessage(messages.label), 53 server: {
57 value: this.props.server, 54 label: this.props.intl.formatMessage(messages.label),
58 options: [{ value: this.ferdiServer, label: 'Ferdi' }, { value: this.franzServer, label: 'Franz' }, { value: this.defaultServers.includes(this.props.server) ? '' : this.props.server, label: 'Custom' }], 55 value: this.props.server,
59 }, 56 options: [
60 customServer: { 57 { value: this.ferdiServer, label: 'Ferdi' },
61 label: this.context.intl.formatMessage(messages.customServerLabel), 58 { value: this.franzServer, label: 'Franz' },
62 value: '', 59 {
63 validators: [url, required], 60 value: this.defaultServers.includes(this.props.server)
61 ? ''
62 : this.props.server,
63 label: 'Custom',
64 },
65 ],
66 },
67 customServer: {
68 label: this.props.intl.formatMessage(messages.customServerLabel),
69 value: '',
70 validators: [url, required],
71 },
64 }, 72 },
65 }, 73 },
66 }, this.context.intl); 74 this.props.intl,
75 );
67 76
68 componentDidMount() { 77 componentDidMount() {
69 if (this.defaultServers.includes(this.props.server)) { 78 if (this.defaultServers.includes(this.props.server)) {
@@ -77,13 +86,13 @@ export default @observer class ChangeServer extends Component {
77 submit(e) { 86 submit(e) {
78 e.preventDefault(); 87 e.preventDefault();
79 this.form.submit({ 88 this.form.submit({
80 onSuccess: (form) => { 89 onSuccess: form => {
81 if (!this.defaultServers.includes(form.values().server)) { 90 if (!this.defaultServers.includes(form.values().server)) {
82 form.$('server').onChange(form.values().customServer); 91 form.$('server').onChange(form.values().customServer);
83 } 92 }
84 this.props.onSubmit(form.values()); 93 this.props.onSubmit(form.values());
85 }, 94 },
86 onError: (form) => { 95 onError: form => {
87 if (this.defaultServers.includes(form.values().server)) { 96 if (this.defaultServers.includes(form.values().server)) {
88 this.props.onSubmit(form.values()); 97 this.props.onSubmit(form.values());
89 } 98 }
@@ -93,23 +102,21 @@ export default @observer class ChangeServer extends Component {
93 102
94 render() { 103 render() {
95 const { form } = this; 104 const { form } = this;
96 const { intl } = this.context; 105 const { intl } = this.props;
97 return ( 106 return (
98 <div className="auth__container"> 107 <div className="auth__container">
99 <form className="franz-form auth__form" onSubmit={(e) => this.submit(e)}> 108 <form className="franz-form auth__form" onSubmit={e => this.submit(e)}>
100 <h1>{intl.formatMessage(messages.headline)}</h1> 109 <h1>{intl.formatMessage(messages.headline)}</h1>
101 {form.$('server').value === this.franzServer 110 {form.$('server').value === this.franzServer && (
102 && (
103 <Infobox type="warning"> 111 <Infobox type="warning">
104 {intl.formatMessage(messages.warning)} 112 {intl.formatMessage(messages.warning)}
105 </Infobox> 113 </Infobox>
106 )} 114 )}
107 <Select field={form.$('server')} /> 115 <Select field={form.$('server')} />
108 {!this.defaultServers.includes(form.$('server').value) 116 {!this.defaultServers.includes(form.$('server').value) && (
109 && (
110 <Input 117 <Input
111 placeholder="Custom Server" 118 placeholder="Custom Server"
112 onChange={(e) => this.submit(e)} 119 onChange={e => this.submit(e)}
113 field={form.$('customServer')} 120 field={form.$('customServer')}
114 /> 121 />
115 )} 122 )}
@@ -123,3 +130,5 @@ export default @observer class ChangeServer extends Component {
123 ); 130 );
124 } 131 }
125} 132}
133
134export default injectIntl(ChangeServer);
diff --git a/src/components/auth/Import.js b/src/components/auth/Import.js
index 3073cad73..44cb7e791 100644
--- a/src/components/auth/Import.js
+++ b/src/components/auth/Import.js
@@ -1,7 +1,7 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; 3import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, injectIntl } from 'react-intl';
5import { Link } from 'react-router'; 5import { Link } from 'react-router';
6import classnames from 'classnames'; 6import classnames from 'classnames';
7 7
@@ -12,23 +12,22 @@ import Button from '../ui/Button';
12const messages = defineMessages({ 12const messages = defineMessages({
13 headline: { 13 headline: {
14 id: 'import.headline', 14 id: 'import.headline',
15 defaultMessage: '!!!Import your Ferdi 4 services', 15 defaultMessage: 'Import your Ferdi 4 services',
16 }, 16 },
17 notSupportedHeadline: { 17 notSupportedHeadline: {
18 id: 'import.notSupportedHeadline', 18 id: 'import.notSupportedHeadline',
19 defaultMessage: '!!!Services not yet supported in Ferdi 5', 19 defaultMessage: 'Services not yet supported in Ferdi 5',
20 }, 20 },
21 submitButtonLabel: { 21 submitButtonLabel: {
22 id: 'import.submit.label', 22 id: 'import.submit.label',
23 defaultMessage: '!!!Import {count} services', 23 defaultMessage: 'Import {count} services',
24 }, 24 },
25 skipButtonLabel: { 25 skipButtonLabel: {
26 id: 'import.skip.label', 26 id: 'import.skip.label',
27 defaultMessage: '!!!I want to add services manually', 27 defaultMessage: 'I want to add services manually',
28 }, 28 },
29}); 29});
30 30
31export default
32@observer 31@observer
33class Import extends Component { 32class Import extends Component {
34 static propTypes = { 33 static propTypes = {
@@ -38,17 +37,13 @@ class Import extends Component {
38 inviteRoute: PropTypes.string.isRequired, 37 inviteRoute: PropTypes.string.isRequired,
39 }; 38 };
40 39
41 static contextTypes = {
42 intl: intlShape,
43 };
44
45 componentDidMount() { 40 componentDidMount() {
46 const config = { 41 const config = {
47 fields: { 42 fields: {
48 import: [ 43 import: [
49 ...this.props.services 44 ...this.props.services
50 .filter((s) => s.recipe) 45 .filter(s => s.recipe)
51 .map((s) => ({ 46 .map(s => ({
52 fields: { 47 fields: {
53 add: { 48 add: {
54 default: true, 49 default: true,
@@ -60,20 +55,20 @@ class Import extends Component {
60 }, 55 },
61 }; 56 };
62 57
63 this.form = new Form(config, this.context.intl); 58 this.form = new Form(config, this.props.intl);
64 } 59 }
65 60
66 submit(e) { 61 submit(e) {
67 const { services } = this.props; 62 const { services } = this.props;
68 e.preventDefault(); 63 e.preventDefault();
69 this.form.submit({ 64 this.form.submit({
70 onSuccess: (form) => { 65 onSuccess: form => {
71 const servicesImport = form 66 const servicesImport = form
72 .values() 67 .values()
73 .import.map( 68 .import.map(
74 (value, i) => !value.add || services.filter((s) => s.recipe)[i], 69 (value, i) => !value.add || services.filter(s => s.recipe)[i],
75 ) 70 )
76 .filter((s) => typeof s !== 'boolean'); 71 .filter(s => typeof s !== 'boolean');
77 72
78 this.props.onSubmit({ services: servicesImport }); 73 this.props.onSubmit({ services: servicesImport });
79 }, 74 },
@@ -82,18 +77,18 @@ class Import extends Component {
82 } 77 }
83 78
84 render() { 79 render() {
85 const { intl } = this.context; 80 const { intl } = this.props;
86 const { services, isSubmitting, inviteRoute } = this.props; 81 const { services, isSubmitting, inviteRoute } = this.props;
87 82
88 const availableServices = services.filter((s) => s.recipe); 83 const availableServices = services.filter(s => s.recipe);
89 const unavailableServices = services.filter((s) => !s.recipe); 84 const unavailableServices = services.filter(s => !s.recipe);
90 85
91 return ( 86 return (
92 <div className="auth__scroll-container"> 87 <div className="auth__scroll-container">
93 <div className="auth__container auth__container--signup"> 88 <div className="auth__container auth__container--signup">
94 <form 89 <form
95 className="franz-form auth__form" 90 className="franz-form auth__form"
96 onSubmit={(e) => this.submit(e)} 91 onSubmit={e => this.submit(e)}
97 > 92 >
98 <img src="./assets/images/logo.svg" className="auth__logo" alt="" /> 93 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
99 <h1>{intl.formatMessage(messages.headline)}</h1> 94 <h1>{intl.formatMessage(messages.headline)}</h1>
@@ -107,8 +102,8 @@ class Import extends Component {
107 <td className="service-table__column-icon"> 102 <td className="service-table__column-icon">
108 <img 103 <img
109 src={ 104 src={
110 availableServices[i].custom_icon 105 availableServices[i].custom_icon ||
111 || availableServices[i].recipe.icons.svg 106 availableServices[i].recipe.icons.svg
112 } 107 }
113 className={classnames({ 108 className={classnames({
114 'service-table__icon': true, 109 'service-table__icon': true,
@@ -133,7 +128,7 @@ class Import extends Component {
133 </strong> 128 </strong>
134 <p> 129 <p>
135 {services 130 {services
136 .filter((s) => !s.recipe) 131 .filter(s => !s.recipe)
137 .map((service, i) => ( 132 .map((service, i) => (
138 <span key={service.id}> 133 <span key={service.id}>
139 {service.name !== '' ? service.name : service.service} 134 {service.name !== '' ? service.name : service.service}
@@ -170,3 +165,5 @@ class Import extends Component {
170 ); 165 );
171 } 166 }
172} 167}
168
169export default injectIntl(Import);
diff --git a/src/components/auth/Invite.js b/src/components/auth/Invite.js
index 4b4d63a6b..519691ede 100644
--- a/src/components/auth/Invite.js
+++ b/src/components/auth/Invite.js
@@ -1,7 +1,7 @@
1import React, { Component, Fragment } from 'react'; 1import React, { Component, Fragment } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, injectIntl } from 'react-intl';
5import { Link } from 'react-router'; 5import { Link } from 'react-router';
6import classnames from 'classnames'; 6import classnames from 'classnames';
7 7
@@ -15,35 +15,34 @@ import Button from '../ui/Button';
15const messages = defineMessages({ 15const messages = defineMessages({
16 settingsHeadline: { 16 settingsHeadline: {
17 id: 'settings.invite.headline', 17 id: 'settings.invite.headline',
18 defaultMessage: '!!!Invite Friends', 18 defaultMessage: 'Invite Friends',
19 }, 19 },
20 headline: { 20 headline: {
21 id: 'invite.headline.friends', 21 id: 'invite.headline.friends',
22 defaultMessage: '!!!Invite 3 of your friends or colleagues', 22 defaultMessage: 'Invite 3 of your friends or colleagues',
23 }, 23 },
24 nameLabel: { 24 nameLabel: {
25 id: 'invite.name.label', 25 id: 'invite.name.label',
26 defaultMessage: '!!!Name', 26 defaultMessage: 'Name',
27 }, 27 },
28 emailLabel: { 28 emailLabel: {
29 id: 'invite.email.label', 29 id: 'invite.email.label',
30 defaultMessage: '!!!Email address', 30 defaultMessage: 'Email address',
31 }, 31 },
32 submitButtonLabel: { 32 submitButtonLabel: {
33 id: 'invite.submit.label', 33 id: 'invite.submit.label',
34 defaultMessage: '!!!Send invites', 34 defaultMessage: 'Send invites',
35 }, 35 },
36 skipButtonLabel: { 36 skipButtonLabel: {
37 id: 'invite.skip.label', 37 id: 'invite.skip.label',
38 defaultMessage: '!!!I want to do this later', 38 defaultMessage: 'I want to do this later',
39 }, 39 },
40 inviteSuccessInfo: { 40 inviteSuccessInfo: {
41 id: 'invite.successInfo', 41 id: 'invite.successInfo',
42 defaultMessage: '!!!Invitations sent successfully', 42 defaultMessage: 'Invitations sent successfully',
43 }, 43 },
44}); 44});
45 45
46export default
47@observer 46@observer
48class Invite extends Component { 47class Invite extends Component {
49 static propTypes = { 48 static propTypes = {
@@ -59,10 +58,6 @@ class Invite extends Component {
59 isLoadingInvite: false, 58 isLoadingInvite: false,
60 }; 59 };
61 60
62 static contextTypes = {
63 intl: intlShape,
64 };
65
66 state = { showSuccessInfo: false }; 61 state = { showSuccessInfo: false };
67 62
68 componentDidMount() { 63 componentDidMount() {
@@ -73,8 +68,8 @@ class Invite extends Component {
73 ...Array(3).fill({ 68 ...Array(3).fill({
74 fields: { 69 fields: {
75 name: { 70 name: {
76 label: this.context.intl.formatMessage(messages.nameLabel), 71 label: this.props.intl.formatMessage(messages.nameLabel),
77 placeholder: this.context.intl.formatMessage( 72 placeholder: this.props.intl.formatMessage(
78 messages.nameLabel, 73 messages.nameLabel,
79 ), 74 ),
80 onChange: () => { 75 onChange: () => {
@@ -83,8 +78,8 @@ class Invite extends Component {
83 // related: ['invite.0.email'], // path accepted but does not work 78 // related: ['invite.0.email'], // path accepted but does not work
84 }, 79 },
85 email: { 80 email: {
86 label: this.context.intl.formatMessage(messages.emailLabel), 81 label: this.props.intl.formatMessage(messages.emailLabel),
87 placeholder: this.context.intl.formatMessage( 82 placeholder: this.props.intl.formatMessage(
88 messages.emailLabel, 83 messages.emailLabel,
89 ), 84 ),
90 onChange: () => { 85 onChange: () => {
@@ -97,7 +92,7 @@ class Invite extends Component {
97 ], 92 ],
98 }, 93 },
99 }, 94 },
100 this.context.intl, 95 this.props.intl,
101 ); 96 );
102 97
103 document.querySelector('input:first-child').focus(); 98 document.querySelector('input:first-child').focus();
@@ -107,7 +102,7 @@ class Invite extends Component {
107 e.preventDefault(); 102 e.preventDefault();
108 103
109 this.form.submit({ 104 this.form.submit({
110 onSuccess: (form) => { 105 onSuccess: form => {
111 this.props.onSubmit({ invites: form.values().invite }); 106 this.props.onSubmit({ invites: form.values().invite });
112 107
113 this.form.clear(); 108 this.form.clear();
@@ -121,13 +116,13 @@ class Invite extends Component {
121 116
122 render() { 117 render() {
123 const { form } = this; 118 const { form } = this;
124 const { intl } = this.context; 119 const { intl } = this.props;
125 const { embed, isInviteSuccessful, isLoadingInvite } = this.props; 120 const { embed, isInviteSuccessful, isLoadingInvite } = this.props;
126 121
127 const atLeastOneEmailAddress = form 122 const atLeastOneEmailAddress = form
128 .$('invite') 123 .$('invite')
129 .map((invite) => invite.$('email').value) 124 .map(invite => invite.$('email').value)
130 .some((emailValue) => emailValue.trim() !== ''); 125 .some(emailValue => emailValue.trim() !== '');
131 126
132 const sendButtonClassName = classnames({ 127 const sendButtonClassName = classnames({
133 auth__button: true, 128 auth__button: true,
@@ -148,17 +143,14 @@ class Invite extends Component {
148 </Appear> 143 </Appear>
149 )} 144 )}
150 145
151 <form 146 <form className="franz-form auth__form" onSubmit={e => this.submit(e)}>
152 className="franz-form auth__form"
153 onSubmit={(e) => this.submit(e)}
154 >
155 {!embed && ( 147 {!embed && (
156 <img src="./assets/images/logo.svg" className="auth__logo" alt="" /> 148 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
157 )} 149 )}
158 <h1 className={embed && 'invite__embed'}> 150 <h1 className={embed && 'invite__embed'}>
159 {intl.formatMessage(messages.headline)} 151 {intl.formatMessage(messages.headline)}
160 </h1> 152 </h1>
161 {form.$('invite').map((invite) => ( 153 {form.$('invite').map(invite => (
162 <div className="grid" key={invite.key}> 154 <div className="grid" key={invite.key}>
163 <div className="grid__row"> 155 <div className="grid__row">
164 <Input field={invite.$('name')} showLabel={false} /> 156 <Input field={invite.$('name')} showLabel={false} />
@@ -193,9 +185,7 @@ class Invite extends Component {
193 > 185 >
194 {embed && ( 186 {embed && (
195 <div className="settings__header"> 187 <div className="settings__header">
196 <h1> 188 <h1>{this.props.intl.formatMessage(messages.settingsHeadline)}</h1>
197 {this.context.intl.formatMessage(messages.settingsHeadline)}
198 </h1>
199 </div> 189 </div>
200 )} 190 )}
201 {!embed ? ( 191 {!embed ? (
@@ -207,3 +197,5 @@ class Invite extends Component {
207 ); 197 );
208 } 198 }
209} 199}
200
201export default injectIntl(Invite);
diff --git a/src/components/auth/Locked.js b/src/components/auth/Locked.js
index 2ad8a2409..a507ba140 100644
--- a/src/components/auth/Locked.js
+++ b/src/components/auth/Locked.js
@@ -2,7 +2,7 @@ import { systemPreferences } from '@electron/remote';
2import React, { Component } from 'react'; 2import React, { Component } from 'react';
3import PropTypes from 'prop-types'; 3import PropTypes from 'prop-types';
4import { observer } from 'mobx-react'; 4import { observer } from 'mobx-react';
5import { defineMessages, intlShape } from 'react-intl'; 5import { defineMessages, injectIntl } from 'react-intl';
6 6
7import Form from '../../lib/Form'; 7import Form from '../../lib/Form';
8import Input from '../ui/Input'; 8import Input from '../ui/Input';
@@ -15,39 +15,41 @@ import { globalError as globalErrorPropType } from '../../prop-types';
15const messages = defineMessages({ 15const messages = defineMessages({
16 headline: { 16 headline: {
17 id: 'locked.headline', 17 id: 'locked.headline',
18 defaultMessage: '!!!Locked', 18 defaultMessage: 'Locked',
19 }, 19 },
20 info: { 20 info: {
21 id: 'locked.info', 21 id: 'locked.info',
22 defaultMessage: '!!!Ferdi is currently locked. Please unlock Ferdi with your password to see your messages.', 22 defaultMessage:
23 'Ferdi is currently locked. Please unlock Ferdi with your password to see your messages.',
23 }, 24 },
24 touchId: { 25 touchId: {
25 id: 'locked.touchId', 26 id: 'locked.touchId',
26 defaultMessage: '!!!Unlock with Touch ID', 27 defaultMessage: 'Unlock with Touch ID',
27 }, 28 },
28 touchIdPrompt: { 29 touchIdPrompt: {
29 id: 'locked.touchIdPrompt', 30 id: 'locked.touchIdPrompt',
30 defaultMessage: '!!!unlock via Touch ID', 31 defaultMessage: 'unlock via Touch ID',
31 }, 32 },
32 passwordLabel: { 33 passwordLabel: {
33 id: 'locked.password.label', 34 id: 'locked.password.label',
34 defaultMessage: '!!!Password', 35 defaultMessage: 'Password',
35 }, 36 },
36 submitButtonLabel: { 37 submitButtonLabel: {
37 id: 'locked.submit.label', 38 id: 'locked.submit.label',
38 defaultMessage: '!!!Unlock', 39 defaultMessage: 'Unlock',
39 }, 40 },
40 unlockWithPassword: { 41 unlockWithPassword: {
41 id: 'locked.unlockWithPassword', 42 id: 'locked.unlockWithPassword',
42 defaultMessage: '!!!Unlock with Password', 43 defaultMessage: 'Unlock with Password',
43 }, 44 },
44 invalidCredentials: { 45 invalidCredentials: {
45 id: 'locked.invalidCredentials', 46 id: 'locked.invalidCredentials',
46 defaultMessage: '!!!Password invalid', 47 defaultMessage: 'Password invalid',
47 }, 48 },
48}); 49});
49 50
50export default @observer class Locked extends Component { 51@observer
52class Locked extends Component {
51 static propTypes = { 53 static propTypes = {
52 onSubmit: PropTypes.func.isRequired, 54 onSubmit: PropTypes.func.isRequired,
53 unlock: PropTypes.func.isRequired, 55 unlock: PropTypes.func.isRequired,
@@ -56,62 +58,57 @@ export default @observer class Locked extends Component {
56 error: globalErrorPropType.isRequired, 58 error: globalErrorPropType.isRequired,
57 }; 59 };
58 60
59 static contextTypes = { 61 form = new Form(
60 intl: intlShape, 62 {
61 }; 63 fields: {
62 64 password: {
63 form = new Form({ 65 label: this.props.intl.formatMessage(messages.passwordLabel),
64 fields: { 66 value: '',
65 password: { 67 type: 'password',
66 label: this.context.intl.formatMessage(messages.passwordLabel), 68 },
67 value: '',
68 type: 'password',
69 }, 69 },
70 }, 70 },
71 }, this.context.intl); 71 this.props.intl,
72 );
72 73
73 submit(e) { 74 submit(e) {
74 e.preventDefault(); 75 e.preventDefault();
75 this.form.submit({ 76 this.form.submit({
76 onSuccess: (form) => { 77 onSuccess: form => {
77 this.props.onSubmit(form.values()); 78 this.props.onSubmit(form.values());
78 }, 79 },
79 onError: () => { }, 80 onError: () => {},
80 }); 81 });
81 } 82 }
82 83
83 touchIdUnlock() { 84 touchIdUnlock() {
84 const { intl } = this.context; 85 const { intl } = this.props;
85 86
86 systemPreferences.promptTouchID(intl.formatMessage(messages.touchIdPrompt)).then(() => { 87 systemPreferences
87 this.props.unlock(); 88 .promptTouchID(intl.formatMessage(messages.touchIdPrompt))
88 }); 89 .then(() => {
90 this.props.unlock();
91 });
89 } 92 }
90 93
91 render() { 94 render() {
92 const { form } = this; 95 const { form } = this;
93 const { intl } = this.context; 96 const { intl } = this.props;
94 const { 97 const { isSubmitting, error, useTouchIdToUnlock } = this.props;
95 isSubmitting,
96 error,
97 useTouchIdToUnlock,
98 } = this.props;
99 98
100 const touchIdEnabled = isMac ? (useTouchIdToUnlock && systemPreferences.canPromptTouchID()) : false; 99 const touchIdEnabled = isMac
101 const submitButtonLabel = touchIdEnabled ? intl.formatMessage(messages.unlockWithPassword) : intl.formatMessage(messages.submitButtonLabel); 100 ? useTouchIdToUnlock && systemPreferences.canPromptTouchID()
101 : false;
102 const submitButtonLabel = touchIdEnabled
103 ? intl.formatMessage(messages.unlockWithPassword)
104 : intl.formatMessage(messages.submitButtonLabel);
102 105
103 return ( 106 return (
104 <div className="auth__container"> 107 <div className="auth__container">
105 <form className="franz-form auth__form" onSubmit={(e) => this.submit(e)}> 108 <form className="franz-form auth__form" onSubmit={e => this.submit(e)}>
106 <img 109 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
107 src="./assets/images/logo.svg"
108 className="auth__logo"
109 alt=""
110 />
111 <h1>{intl.formatMessage(messages.headline)}</h1> 110 <h1>{intl.formatMessage(messages.headline)}</h1>
112 <Infobox type="warning"> 111 <Infobox type="warning">{intl.formatMessage(messages.info)}</Infobox>
113 {intl.formatMessage(messages.info)}
114 </Infobox>
115 112
116 {touchIdEnabled && ( 113 {touchIdEnabled && (
117 <> 114 <>
@@ -125,13 +122,11 @@ export default @observer class Locked extends Component {
125 </> 122 </>
126 )} 123 )}
127 124
128 <Input 125 <Input field={form.$('password')} showPasswordToggle focus />
129 field={form.$('password')}
130 showPasswordToggle
131 focus
132 />
133 {error.code === 'invalid-credentials' && ( 126 {error.code === 'invalid-credentials' && (
134 <p className="error-message center">{intl.formatMessage(messages.invalidCredentials)}</p> 127 <p className="error-message center">
128 {intl.formatMessage(messages.invalidCredentials)}
129 </p>
135 )} 130 )}
136 {isSubmitting ? ( 131 {isSubmitting ? (
137 <Button 132 <Button
@@ -153,3 +148,5 @@ export default @observer class Locked extends Component {
153 ); 148 );
154 } 149 }
155} 150}
151
152export default injectIntl(Locked);
diff --git a/src/components/auth/Login.js b/src/components/auth/Login.js
index 9e6a8d046..a47834e19 100644
--- a/src/components/auth/Login.js
+++ b/src/components/auth/Login.js
@@ -2,7 +2,7 @@
2import React, { Component } from 'react'; 2import React, { Component } from 'react';
3import PropTypes from 'prop-types'; 3import PropTypes from 'prop-types';
4import { observer, inject } from 'mobx-react'; 4import { observer, inject } from 'mobx-react';
5import { defineMessages, intlShape } from 'react-intl'; 5import { defineMessages, injectIntl } from 'react-intl';
6 6
7import { LIVE_FRANZ_API } from '../../config'; 7import { LIVE_FRANZ_API } from '../../config';
8import { API_VERSION, isDevMode, useLiveAPI } from '../../environment'; 8import { API_VERSION, isDevMode, useLiveAPI } from '../../environment';
@@ -19,59 +19,61 @@ import { globalError as globalErrorPropType } from '../../prop-types';
19const messages = defineMessages({ 19const messages = defineMessages({
20 headline: { 20 headline: {
21 id: 'login.headline', 21 id: 'login.headline',
22 defaultMessage: '!!!Sign in', 22 defaultMessage: 'Sign in',
23 }, 23 },
24 emailLabel: { 24 emailLabel: {
25 id: 'login.email.label', 25 id: 'login.email.label',
26 defaultMessage: '!!!Email address', 26 defaultMessage: 'Email address',
27 }, 27 },
28 passwordLabel: { 28 passwordLabel: {
29 id: 'login.password.label', 29 id: 'login.password.label',
30 defaultMessage: '!!!Password', 30 defaultMessage: 'Password',
31 }, 31 },
32 submitButtonLabel: { 32 submitButtonLabel: {
33 id: 'login.submit.label', 33 id: 'login.submit.label',
34 defaultMessage: '!!!Sign in', 34 defaultMessage: 'Sign in',
35 }, 35 },
36 invalidCredentials: { 36 invalidCredentials: {
37 id: 'login.invalidCredentials', 37 id: 'login.invalidCredentials',
38 defaultMessage: '!!!Email or password not valid', 38 defaultMessage: 'Email or password not valid',
39 }, 39 },
40 customServerQuestion: { 40 customServerQuestion: {
41 id: 'login.customServerQuestion', 41 id: 'login.customServerQuestion',
42 defaultMessage: '!!!Using a Franz account to log in?', 42 defaultMessage: 'Using a Franz account to log in?',
43 }, 43 },
44 customServerSuggestion: { 44 customServerSuggestion: {
45 id: 'login.customServerSuggestion', 45 id: 'login.customServerSuggestion',
46 defaultMessage: '!!!Try importing your Franz account into Ferdi', 46 defaultMessage: 'Try importing your Franz account into Ferdi',
47 }, 47 },
48 tokenExpired: { 48 tokenExpired: {
49 id: 'login.tokenExpired', 49 id: 'login.tokenExpired',
50 defaultMessage: '!!!Your session expired, please login again.', 50 defaultMessage: 'Your session expired, please login again.',
51 }, 51 },
52 serverLogout: { 52 serverLogout: {
53 id: 'login.serverLogout', 53 id: 'login.serverLogout',
54 defaultMessage: '!!!Your session expired, please login again.', 54 defaultMessage: 'Your session expired, please login again.',
55 }, 55 },
56 signupLink: { 56 signupLink: {
57 id: 'login.link.signup', 57 id: 'login.link.signup',
58 defaultMessage: '!!!Create a free account', 58 defaultMessage: 'Create a free account',
59 }, 59 },
60 changeServer: { 60 changeServer: {
61 id: 'login.changeServer', 61 id: 'login.changeServer',
62 defaultMessage: '!!!Change server', 62 defaultMessage: 'Change server',
63 }, 63 },
64 serverless: { 64 serverless: {
65 id: 'services.serverless', 65 id: 'services.serverless',
66 defaultMessage: '!!!Use Ferdi without an Account', 66 defaultMessage: 'Use Ferdi without an Account',
67 }, 67 },
68 passwordLink: { 68 passwordLink: {
69 id: 'login.link.password', 69 id: 'login.link.password',
70 defaultMessage: '!!!Forgot password', 70 defaultMessage: 'Forgot password',
71 }, 71 },
72}); 72});
73 73
74export default @inject('actions') @observer class Login extends Component { 74@inject('actions')
75@observer
76class Login extends Component {
75 static propTypes = { 77 static propTypes = {
76 onSubmit: PropTypes.func.isRequired, 78 onSubmit: PropTypes.func.isRequired,
77 isSubmitting: PropTypes.bool.isRequired, 79 isSubmitting: PropTypes.bool.isRequired,
@@ -84,35 +86,34 @@ export default @inject('actions') @observer class Login extends Component {
84 actions: PropTypes.object.isRequired, 86 actions: PropTypes.object.isRequired,
85 }; 87 };
86 88
87 static contextTypes = { 89 form = new Form(
88 intl: intlShape, 90 {
89 }; 91 fields: {
90 92 email: {
91 form = new Form({ 93 label: this.props.intl.formatMessage(messages.emailLabel),
92 fields: { 94 value: '',
93 email: { 95 validators: [required, email],
94 label: this.context.intl.formatMessage(messages.emailLabel), 96 },
95 value: '', 97 password: {
96 validators: [required, email], 98 label: this.props.intl.formatMessage(messages.passwordLabel),
97 }, 99 value: '',
98 password: { 100 validators: [required],
99 label: this.context.intl.formatMessage(messages.passwordLabel), 101 type: 'password',
100 value: '', 102 },
101 validators: [required],
102 type: 'password',
103 }, 103 },
104 }, 104 },
105 }, this.context.intl); 105 this.props.intl,
106 );
106 107
107 emailField = null; 108 emailField = null;
108 109
109 submit(e) { 110 submit(e) {
110 e.preventDefault(); 111 e.preventDefault();
111 this.form.submit({ 112 this.form.submit({
112 onSuccess: (form) => { 113 onSuccess: form => {
113 this.props.onSubmit(form.values()); 114 this.props.onSubmit(form.values());
114 }, 115 },
115 onError: () => { }, 116 onError: () => {},
116 }); 117 });
117 } 118 }
118 119
@@ -122,7 +123,7 @@ export default @inject('actions') @observer class Login extends Component {
122 123
123 render() { 124 render() {
124 const { form } = this; 125 const { form } = this;
125 const { intl } = this.context; 126 const { intl } = this.props;
126 const { 127 const {
127 isSubmitting, 128 isSubmitting,
128 isTokenExpired, 129 isTokenExpired,
@@ -135,42 +136,47 @@ export default @inject('actions') @observer class Login extends Component {
135 136
136 return ( 137 return (
137 <div className="auth__container"> 138 <div className="auth__container">
138 <form className="franz-form auth__form" onSubmit={(e) => this.submit(e)}> 139 <form className="franz-form auth__form" onSubmit={e => this.submit(e)}>
139 <img 140 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
140 src="./assets/images/logo.svg"
141 className="auth__logo"
142 alt=""
143 />
144 <h1>{intl.formatMessage(messages.headline)}</h1> 141 <h1>{intl.formatMessage(messages.headline)}</h1>
145 {isDevMode && !useLiveAPI && ( 142 {isDevMode && !useLiveAPI && (
146 <Infobox type="warning"> 143 <Infobox type="warning">
147 In Dev Mode your data is not persistent. Please use the live app for accessing the production API. 144 In Dev Mode your data is not persistent. Please use the live app
145 for accessing the production API.
148 </Infobox> 146 </Infobox>
149 )} 147 )}
150 {isTokenExpired && ( 148 {isTokenExpired && (
151 <p className="error-message center">{intl.formatMessage(messages.tokenExpired)}</p> 149 <p className="error-message center">
150 {intl.formatMessage(messages.tokenExpired)}
151 </p>
152 )} 152 )}
153 {isServerLogout && ( 153 {isServerLogout && (
154 <p className="error-message center">{intl.formatMessage(messages.serverLogout)}</p> 154 <p className="error-message center">
155 {intl.formatMessage(messages.serverLogout)}
156 </p>
155 )} 157 )}
156 <Input 158 <Input
157 field={form.$('email')} 159 field={form.$('email')}
158 ref={(element) => { this.emailField = element; }} 160 ref={element => {
161 this.emailField = element;
162 }}
159 focus 163 focus
160 /> 164 />
161 <Input 165 <Input field={form.$('password')} showPasswordToggle />
162 field={form.$('password')}
163 showPasswordToggle
164 />
165 {error.code === 'invalid-credentials' && ( 166 {error.code === 'invalid-credentials' && (
166 <> 167 <>
167 <p className="error-message center">{intl.formatMessage(messages.invalidCredentials)}</p> 168 <p className="error-message center">
168 { window.ferdi.stores.settings.all.app.server !== LIVE_FRANZ_API && ( 169 {intl.formatMessage(messages.invalidCredentials)}
170 </p>
171 {window.ferdi.stores.settings.all.app.server !==
172 LIVE_FRANZ_API && (
169 <p className="error-message center"> 173 <p className="error-message center">
170 {intl.formatMessage(messages.customServerQuestion)} 174 {intl.formatMessage(messages.customServerQuestion)}{' '}
171 {' '}
172 <Link 175 <Link
173 to={`${window.ferdi.stores.settings.all.app.server.replace(API_VERSION, '')}/import`} 176 to={`${window.ferdi.stores.settings.all.app.server.replace(
177 API_VERSION,
178 '',
179 )}/import`}
174 target="_blank" 180 target="_blank"
175 style={{ cursor: 'pointer', textDecoration: 'underline' }} 181 style={{ cursor: 'pointer', textDecoration: 'underline' }}
176 > 182 >
@@ -197,12 +203,22 @@ export default @inject('actions') @observer class Login extends Component {
197 )} 203 )}
198 </form> 204 </form>
199 <div className="auth__links"> 205 <div className="auth__links">
200 <Link to={changeServerRoute}>{intl.formatMessage(messages.changeServer)}</Link> 206 <Link to={changeServerRoute}>
201 <a onClick={this.useLocalServer.bind(this)}>{intl.formatMessage(messages.serverless)}</a> 207 {intl.formatMessage(messages.changeServer)}
202 <Link to={signupRoute}>{intl.formatMessage(messages.signupLink)}</Link> 208 </Link>
203 <Link to={passwordRoute}>{intl.formatMessage(messages.passwordLink)}</Link> 209 <a onClick={this.useLocalServer.bind(this)}>
210 {intl.formatMessage(messages.serverless)}
211 </a>
212 <Link to={signupRoute}>
213 {intl.formatMessage(messages.signupLink)}
214 </Link>
215 <Link to={passwordRoute}>
216 {intl.formatMessage(messages.passwordLink)}
217 </Link>
204 </div> 218 </div>
205 </div> 219 </div>
206 ); 220 );
207 } 221 }
208} 222}
223
224export default injectIntl(Login);
diff --git a/src/components/auth/Password.js b/src/components/auth/Password.js
index 1be2097bd..74346b382 100644
--- a/src/components/auth/Password.js
+++ b/src/components/auth/Password.js
@@ -1,7 +1,7 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; 3import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, injectIntl } from 'react-intl';
5 5
6import Form from '../../lib/Form'; 6import Form from '../../lib/Form';
7import { required, email } from '../../helpers/validation-helpers'; 7import { required, email } from '../../helpers/validation-helpers';
@@ -14,31 +14,32 @@ import globalMessages from '../../i18n/globalMessages';
14const messages = defineMessages({ 14const messages = defineMessages({
15 headline: { 15 headline: {
16 id: 'password.headline', 16 id: 'password.headline',
17 defaultMessage: '!!!Forgot password', 17 defaultMessage: 'Forgot password',
18 }, 18 },
19 emailLabel: { 19 emailLabel: {
20 id: 'password.email.label', 20 id: 'password.email.label',
21 defaultMessage: '!!!Email address', 21 defaultMessage: 'Email address',
22 }, 22 },
23 successInfo: { 23 successInfo: {
24 id: 'password.successInfo', 24 id: 'password.successInfo',
25 defaultMessage: '!!!Your new password was sent to your email address', 25 defaultMessage: 'Your new password was sent to your email address',
26 }, 26 },
27 noUser: { 27 noUser: {
28 id: 'password.noUser', 28 id: 'password.noUser',
29 defaultMessage: '!!!No user affiliated with that email address', 29 defaultMessage: 'No user affiliated with that email address',
30 }, 30 },
31 signupLink: { 31 signupLink: {
32 id: 'password.link.signup', 32 id: 'password.link.signup',
33 defaultMessage: '!!!Create a free account', 33 defaultMessage: 'Create a free account',
34 }, 34 },
35 loginLink: { 35 loginLink: {
36 id: 'password.link.login', 36 id: 'password.link.login',
37 defaultMessage: '!!!Sign in to your account', 37 defaultMessage: 'Sign in to your account',
38 }, 38 },
39}); 39});
40 40
41export default @observer class Password extends Component { 41@observer
42class Password extends Component {
42 static propTypes = { 43 static propTypes = {
43 onSubmit: PropTypes.func.isRequired, 44 onSubmit: PropTypes.func.isRequired,
44 isSubmitting: PropTypes.bool.isRequired, 45 isSubmitting: PropTypes.bool.isRequired,
@@ -47,24 +48,23 @@ export default @observer class Password extends Component {
47 status: MobxPropTypes.arrayOrObservableArray.isRequired, 48 status: MobxPropTypes.arrayOrObservableArray.isRequired,
48 }; 49 };
49 50
50 static contextTypes = { 51 form = new Form(
51 intl: intlShape, 52 {
52 }; 53 fields: {
53 54 email: {
54 form = new Form({ 55 label: this.props.intl.formatMessage(messages.emailLabel),
55 fields: { 56 value: '',
56 email: { 57 validators: [required, email],
57 label: this.context.intl.formatMessage(messages.emailLabel), 58 },
58 value: '',
59 validators: [required, email],
60 }, 59 },
61 }, 60 },
62 }, this.context.intl); 61 this.props.intl,
62 );
63 63
64 submit(e) { 64 submit(e) {
65 e.preventDefault(); 65 e.preventDefault();
66 this.form.submit({ 66 this.form.submit({
67 onSuccess: (form) => { 67 onSuccess: form => {
68 this.props.onSubmit(form.values()); 68 this.props.onSubmit(form.values());
69 }, 69 },
70 onError: () => {}, 70 onError: () => {},
@@ -73,37 +73,24 @@ export default @observer class Password extends Component {
73 73
74 render() { 74 render() {
75 const { form } = this; 75 const { form } = this;
76 const { intl } = this.context; 76 const { intl } = this.props;
77 const { 77 const { isSubmitting, signupRoute, loginRoute, status } = this.props;
78 isSubmitting,
79 signupRoute,
80 loginRoute,
81 status,
82 } = this.props;
83 78
84 return ( 79 return (
85 <div className="auth__container"> 80 <div className="auth__container">
86 <form className="franz-form auth__form" onSubmit={(e) => this.submit(e)}> 81 <form className="franz-form auth__form" onSubmit={e => this.submit(e)}>
87 <img 82 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
88 src="./assets/images/logo.svg"
89 className="auth__logo"
90 alt=""
91 />
92 <h1>{intl.formatMessage(messages.headline)}</h1> 83 <h1>{intl.formatMessage(messages.headline)}</h1>
93 {status.length > 0 && status.includes('sent') && ( 84 {status.length > 0 && status.includes('sent') && (
94 <Infobox 85 <Infobox type="success" icon="checkbox-marked-circle-outline">
95 type="success"
96 icon="checkbox-marked-circle-outline"
97 >
98 {intl.formatMessage(messages.successInfo)} 86 {intl.formatMessage(messages.successInfo)}
99 </Infobox> 87 </Infobox>
100 )} 88 )}
101 <Input 89 <Input field={form.$('email')} focus />
102 field={form.$('email')}
103 focus
104 />
105 {status.length > 0 && status.includes('no-user') && ( 90 {status.length > 0 && status.includes('no-user') && (
106 <p className="error-message center">{intl.formatMessage(messages.noUser)}</p> 91 <p className="error-message center">
92 {intl.formatMessage(messages.noUser)}
93 </p>
107 )} 94 )}
108 {isSubmitting ? ( 95 {isSubmitting ? (
109 <Button 96 <Button
@@ -123,9 +110,13 @@ export default @observer class Password extends Component {
123 </form> 110 </form>
124 <div className="auth__links"> 111 <div className="auth__links">
125 <Link to={loginRoute}>{intl.formatMessage(messages.loginLink)}</Link> 112 <Link to={loginRoute}>{intl.formatMessage(messages.loginLink)}</Link>
126 <Link to={signupRoute}>{intl.formatMessage(messages.signupLink)}</Link> 113 <Link to={signupRoute}>
114 {intl.formatMessage(messages.signupLink)}
115 </Link>
127 </div> 116 </div>
128 </div> 117 </div>
129 ); 118 );
130 } 119 }
131} 120}
121
122export default injectIntl(Password);
diff --git a/src/components/auth/SetupAssistant.js b/src/components/auth/SetupAssistant.js
index ded36bbe7..299c40c63 100644
--- a/src/components/auth/SetupAssistant.js
+++ b/src/components/auth/SetupAssistant.js
@@ -1,7 +1,7 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, injectIntl } from 'react-intl';
5import injectSheet from 'react-jss'; 5import injectSheet from 'react-jss';
6import classnames from 'classnames'; 6import classnames from 'classnames';
7 7
@@ -19,20 +19,20 @@ const SLACK_ID = 'slack';
19const messages = defineMessages({ 19const messages = defineMessages({
20 headline: { 20 headline: {
21 id: 'setupAssistant.headline', 21 id: 'setupAssistant.headline',
22 defaultMessage: "!!!Let's get started", 22 defaultMessage: "Let's get started",
23 }, 23 },
24 subHeadline: { 24 subHeadline: {
25 id: 'setupAssistant.subheadline', 25 id: 'setupAssistant.subheadline',
26 defaultMessage: 26 defaultMessage:
27 '!!!Choose from our most used services and get back on top of your messaging now.', 27 'Choose from our most used services and get back on top of your messaging now.',
28 }, 28 },
29 submitButtonLabel: { 29 submitButtonLabel: {
30 id: 'setupAssistant.submit.label', 30 id: 'setupAssistant.submit.label',
31 defaultMessage: "!!!Let's go", 31 defaultMessage: "Let's go",
32 }, 32 },
33 inviteSuccessInfo: { 33 inviteSuccessInfo: {
34 id: 'invite.successInfo', 34 id: 'invite.successInfo',
35 defaultMessage: '!!!Invitations sent successfully', 35 defaultMessage: 'Invitations sent successfully',
36 }, 36 },
37}); 37});
38 38
@@ -145,10 +145,6 @@ class SetupAssistant extends Component {
145 isInviteSuccessful: false, 145 isInviteSuccessful: false,
146 }; 146 };
147 147
148 static contextTypes = {
149 intl: intlShape,
150 };
151
152 state = { 148 state = {
153 services: [ 149 services: [
154 { 150 {
@@ -189,7 +185,7 @@ class SetupAssistant extends Component {
189 } 185 }
190 186
191 render() { 187 render() {
192 const { intl } = this.context; 188 const { intl } = this.props;
193 const { 189 const {
194 classes, 190 classes,
195 isInviteSuccessful, 191 isInviteSuccessful,
@@ -330,4 +326,4 @@ class SetupAssistant extends Component {
330 } 326 }
331} 327}
332 328
333export default SetupAssistant; 329export default injectIntl(SetupAssistant);
diff --git a/src/components/auth/Signup.js b/src/components/auth/Signup.js
index 6fb41a164..4d39835a2 100644
--- a/src/components/auth/Signup.js
+++ b/src/components/auth/Signup.js
@@ -2,7 +2,7 @@
2import React, { Component } from 'react'; 2import React, { Component } from 'react';
3import PropTypes from 'prop-types'; 3import PropTypes from 'prop-types';
4import { observer, inject } from 'mobx-react'; 4import { observer, inject } from 'mobx-react';
5import { defineMessages, intlShape } from 'react-intl'; 5import { defineMessages, injectIntl } from 'react-intl';
6 6
7import { isDevMode, useLiveAPI } from '../../environment'; 7import { isDevMode, useLiveAPI } from '../../environment';
8import Form from '../../lib/Form'; 8import Form from '../../lib/Form';
@@ -19,63 +19,65 @@ import { termsBase } from '../../api/apiBase';
19const messages = defineMessages({ 19const messages = defineMessages({
20 headline: { 20 headline: {
21 id: 'signup.headline', 21 id: 'signup.headline',
22 defaultMessage: '!!!Sign up', 22 defaultMessage: 'Sign up',
23 }, 23 },
24 firstnameLabel: { 24 firstnameLabel: {
25 id: 'signup.firstname.label', 25 id: 'signup.firstname.label',
26 defaultMessage: '!!!Firstname', 26 defaultMessage: 'Firstname',
27 }, 27 },
28 lastnameLabel: { 28 lastnameLabel: {
29 id: 'signup.lastname.label', 29 id: 'signup.lastname.label',
30 defaultMessage: '!!!Lastname', 30 defaultMessage: 'Lastname',
31 }, 31 },
32 emailLabel: { 32 emailLabel: {
33 id: 'signup.email.label', 33 id: 'signup.email.label',
34 defaultMessage: '!!!Email address', 34 defaultMessage: 'Email address',
35 }, 35 },
36 // companyLabel: { 36 // companyLabel: {
37 // id: 'signup.company.label', 37 // id: 'signup.company.label',
38 // defaultMessage: '!!!Company', 38 // defaultMessage: 'Company',
39 // }, 39 // },
40 passwordLabel: { 40 passwordLabel: {
41 id: 'signup.password.label', 41 id: 'signup.password.label',
42 defaultMessage: '!!!Password', 42 defaultMessage: 'Password',
43 }, 43 },
44 legalInfo: { 44 legalInfo: {
45 id: 'signup.legal.info', 45 id: 'signup.legal.info',
46 defaultMessage: '!!!By creating a Ferdi account you accept the', 46 defaultMessage: 'By creating a Ferdi account you accept the',
47 }, 47 },
48 terms: { 48 terms: {
49 id: 'signup.legal.terms', 49 id: 'signup.legal.terms',
50 defaultMessage: '!!!Terms of service', 50 defaultMessage: 'Terms of service',
51 }, 51 },
52 privacy: { 52 privacy: {
53 id: 'signup.legal.privacy', 53 id: 'signup.legal.privacy',
54 defaultMessage: '!!!Privacy Statement', 54 defaultMessage: 'Privacy Statement',
55 }, 55 },
56 submitButtonLabel: { 56 submitButtonLabel: {
57 id: 'signup.submit.label', 57 id: 'signup.submit.label',
58 defaultMessage: '!!!Create account', 58 defaultMessage: 'Create account',
59 }, 59 },
60 loginLink: { 60 loginLink: {
61 id: 'signup.link.login', 61 id: 'signup.link.login',
62 defaultMessage: '!!!Already have an account, sign in?', 62 defaultMessage: 'Already have an account, sign in?',
63 }, 63 },
64 changeServer: { 64 changeServer: {
65 id: 'login.changeServer', 65 id: 'login.changeServer',
66 defaultMessage: '!!!Change server', 66 defaultMessage: 'Change server',
67 }, 67 },
68 serverless: { 68 serverless: {
69 id: 'services.serverless', 69 id: 'services.serverless',
70 defaultMessage: '!!!Use Ferdi without an Account', 70 defaultMessage: 'Use Ferdi without an Account',
71 }, 71 },
72 emailDuplicate: { 72 emailDuplicate: {
73 id: 'signup.emailDuplicate', 73 id: 'signup.emailDuplicate',
74 defaultMessage: '!!!A user with that email address already exists', 74 defaultMessage: 'A user with that email address already exists',
75 }, 75 },
76}); 76});
77 77
78export default @inject('actions') @observer class Signup extends Component { 78@inject('actions')
79@observer
80class Signup extends Component {
79 static propTypes = { 81 static propTypes = {
80 onSubmit: PropTypes.func.isRequired, 82 onSubmit: PropTypes.func.isRequired,
81 isSubmitting: PropTypes.bool.isRequired, 83 isSubmitting: PropTypes.bool.isRequired,
@@ -85,40 +87,39 @@ export default @inject('actions') @observer class Signup extends Component {
85 actions: PropTypes.object.isRequired, 87 actions: PropTypes.object.isRequired,
86 }; 88 };
87 89
88 static contextTypes = { 90 form = new Form(
89 intl: intlShape, 91 {
90 }; 92 fields: {
91 93 firstname: {
92 form = new Form({ 94 label: this.props.intl.formatMessage(messages.firstnameLabel),
93 fields: { 95 value: '',
94 firstname: { 96 validators: [required],
95 label: this.context.intl.formatMessage(messages.firstnameLabel), 97 },
96 value: '', 98 lastname: {
97 validators: [required], 99 label: this.props.intl.formatMessage(messages.lastnameLabel),
98 }, 100 value: '',
99 lastname: { 101 validators: [required],
100 label: this.context.intl.formatMessage(messages.lastnameLabel), 102 },
101 value: '', 103 email: {
102 validators: [required], 104 label: this.props.intl.formatMessage(messages.emailLabel),
103 }, 105 value: '',
104 email: { 106 validators: [required, email],
105 label: this.context.intl.formatMessage(messages.emailLabel), 107 },
106 value: '', 108 password: {
107 validators: [required, email], 109 label: this.props.intl.formatMessage(messages.passwordLabel),
108 }, 110 value: '',
109 password: { 111 validators: [required, minLength(6)],
110 label: this.context.intl.formatMessage(messages.passwordLabel), 112 type: 'password',
111 value: '', 113 },
112 validators: [required, minLength(6)],
113 type: 'password',
114 }, 114 },
115 }, 115 },
116 }, this.context.intl); 116 this.props.intl,
117 );
117 118
118 submit(e) { 119 submit(e) {
119 e.preventDefault(); 120 e.preventDefault();
120 this.form.submit({ 121 this.form.submit({
121 onSuccess: (form) => { 122 onSuccess: form => {
122 this.props.onSubmit(form.values()); 123 this.props.onSubmit(form.values());
123 }, 124 },
124 onError: () => {}, 125 onError: () => {},
@@ -131,24 +132,22 @@ export default @inject('actions') @observer class Signup extends Component {
131 132
132 render() { 133 render() {
133 const { form } = this; 134 const { form } = this;
134 const { intl } = this.context; 135 const { intl } = this.props;
135 const { 136 const { isSubmitting, loginRoute, error, changeServerRoute } = this.props;
136 isSubmitting, loginRoute, error, changeServerRoute,
137 } = this.props;
138 137
139 return ( 138 return (
140 <div className="auth__scroll-container"> 139 <div className="auth__scroll-container">
141 <div className="auth__container auth__container--signup"> 140 <div className="auth__container auth__container--signup">
142 <form className="franz-form auth__form" onSubmit={(e) => this.submit(e)}> 141 <form
143 <img 142 className="franz-form auth__form"
144 src="./assets/images/logo.svg" 143 onSubmit={e => this.submit(e)}
145 className="auth__logo" 144 >
146 alt="" 145 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
147 />
148 <h1>{intl.formatMessage(messages.headline)}</h1> 146 <h1>{intl.formatMessage(messages.headline)}</h1>
149 {isDevMode && !useLiveAPI && ( 147 {isDevMode && !useLiveAPI && (
150 <Infobox type="warning"> 148 <Infobox type="warning">
151 In Dev Mode your data is not persistent. Please use the live app for accesing the production API. 149 In Dev Mode your data is not persistent. Please use the live app
150 for accesing the production API.
152 </Infobox> 151 </Infobox>
153 )} 152 )}
154 <div className="grid__row"> 153 <div className="grid__row">
@@ -162,7 +161,9 @@ export default @inject('actions') @observer class Signup extends Component {
162 scorePassword 161 scorePassword
163 /> 162 />
164 {error.code === 'email-duplicate' && ( 163 {error.code === 'email-duplicate' && (
165 <p className="error-message center">{intl.formatMessage(messages.emailDuplicate)}</p> 164 <p className="error-message center">
165 {intl.formatMessage(messages.emailDuplicate)}
166 </p>
166 )} 167 )}
167 {isSubmitting ? ( 168 {isSubmitting ? (
168 <Button 169 <Button
@@ -200,12 +201,20 @@ export default @inject('actions') @observer class Signup extends Component {
200 </p> 201 </p>
201 </form> 202 </form>
202 <div className="auth__links"> 203 <div className="auth__links">
203 <Link to={changeServerRoute}>{intl.formatMessage(messages.changeServer)}</Link> 204 <Link to={changeServerRoute}>
204 <a onClick={this.useLocalServer.bind(this)}>{intl.formatMessage(messages.serverless)}</a> 205 {intl.formatMessage(messages.changeServer)}
205 <Link to={loginRoute}>{intl.formatMessage(messages.loginLink)}</Link> 206 </Link>
207 <a onClick={this.useLocalServer.bind(this)}>
208 {intl.formatMessage(messages.serverless)}
209 </a>
210 <Link to={loginRoute}>
211 {intl.formatMessage(messages.loginLink)}
212 </Link>
206 </div> 213 </div>
207 </div> 214 </div>
208 </div> 215 </div>
209 ); 216 );
210 } 217 }
211} 218}
219
220export default injectIntl(Signup);
diff --git a/src/components/auth/Welcome.js b/src/components/auth/Welcome.js
index cb522e26e..2d2e2ab28 100644
--- a/src/components/auth/Welcome.js
+++ b/src/components/auth/Welcome.js
@@ -2,7 +2,7 @@
2import React, { Component } from 'react'; 2import React, { Component } from 'react';
3import PropTypes from 'prop-types'; 3import PropTypes from 'prop-types';
4import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react'; 4import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react';
5import { defineMessages, intlShape } from 'react-intl'; 5import { defineMessages, injectIntl } from 'react-intl';
6import serverlessLogin from '../../helpers/serverless-helpers'; 6import serverlessLogin from '../../helpers/serverless-helpers';
7 7
8import Link from '../ui/Link'; 8import Link from '../ui/Link';
@@ -10,19 +10,21 @@ import Link from '../ui/Link';
10const messages = defineMessages({ 10const messages = defineMessages({
11 signupButton: { 11 signupButton: {
12 id: 'welcome.signupButton', 12 id: 'welcome.signupButton',
13 defaultMessage: '!!!Create a free account', 13 defaultMessage: 'Create a free account',
14 }, 14 },
15 loginButton: { 15 loginButton: {
16 id: 'welcome.loginButton', 16 id: 'welcome.loginButton',
17 defaultMessage: '!!!Login to your account', 17 defaultMessage: 'Login to your account',
18 }, 18 },
19 serverless: { 19 serverless: {
20 id: 'services.serverless', 20 id: 'services.serverless',
21 defaultMessage: '!!!Use Ferdi without an Account', 21 defaultMessage: 'Use Ferdi without an Account',
22 }, 22 },
23}); 23});
24 24
25export default @inject('actions') @observer class Login extends Component { 25@inject('actions')
26@observer
27class Login extends Component {
26 static propTypes = { 28 static propTypes = {
27 loginRoute: PropTypes.string.isRequired, 29 loginRoute: PropTypes.string.isRequired,
28 signupRoute: PropTypes.string.isRequired, 30 signupRoute: PropTypes.string.isRequired,
@@ -31,27 +33,22 @@ export default @inject('actions') @observer class Login extends Component {
31 actions: PropTypes.object.isRequired, 33 actions: PropTypes.object.isRequired,
32 }; 34 };
33 35
34 static contextTypes = {
35 intl: intlShape,
36 };
37
38 useLocalServer() { 36 useLocalServer() {
39 serverlessLogin(this.props.actions); 37 serverlessLogin(this.props.actions);
40 } 38 }
41 39
42 render() { 40 render() {
43 const { intl } = this.context; 41 const { intl } = this.props;
44 const { 42 const { loginRoute, signupRoute, changeServerRoute, recipes } = this.props;
45 loginRoute,
46 signupRoute,
47 changeServerRoute,
48 recipes,
49 } = this.props;
50 43
51 return ( 44 return (
52 <div className="welcome"> 45 <div className="welcome">
53 <div className="welcome__content"> 46 <div className="welcome__content">
54 <img src="./assets/images/logo.svg" className="welcome__logo" alt="" /> 47 <img
48 src="./assets/images/logo.svg"
49 className="welcome__logo"
50 alt=""
51 />
55 {/* <img src="./assets/images/welcome.png" className="welcome__services" alt="" /> */} 52 {/* <img src="./assets/images/welcome.png" className="welcome__services" alt="" /> */}
56 <div className="welcome__text"> 53 <div className="welcome__text">
57 <h1>Ferdi</h1> 54 <h1>Ferdi</h1>
@@ -73,27 +70,21 @@ export default @inject('actions') @observer class Login extends Component {
73 <br /> 70 <br />
74 71
75 <Link to={changeServerRoute}> 72 <Link to={changeServerRoute}>
76 <span style={{ 73 <span
77 textAlign: 'center', 74 style={{
78 width: '100%', 75 textAlign: 'center',
79 cursor: 'pointer', 76 width: '100%',
80 }} 77 cursor: 'pointer',
78 }}
81 > 79 >
82 Change server 80 Change server
83 </span> 81 </span>
84 </Link> 82 </Link>
85 </div> 83 </div>
86 <div className="welcome__featured-services"> 84 <div className="welcome__featured-services">
87 {recipes.map((recipe) => ( 85 {recipes.map(recipe => (
88 <div 86 <div key={recipe.id} className="welcome__featured-service">
89 key={recipe.id} 87 <img key={recipe.id} src={recipe.icons.svg} alt="" />
90 className="welcome__featured-service"
91 >
92 <img
93 key={recipe.id}
94 src={recipe.icons.svg}
95 alt=""
96 />
97 </div> 88 </div>
98 ))} 89 ))}
99 </div> 90 </div>
@@ -101,3 +92,5 @@ export default @inject('actions') @observer class Login extends Component {
101 ); 92 );
102 } 93 }
103} 94}
95
96export default injectIntl(Login);