summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/auth/Invite.js54
-rw-r--r--src/components/settings/account/AccountDashboard.js29
-rw-r--r--src/components/ui/Link.js5
-rw-r--r--src/containers/auth/InviteScreen.js7
-rw-r--r--src/containers/settings/AccountScreen.js1
-rw-r--r--src/helpers/validation-helpers.js2
-rw-r--r--src/i18n/locales/de.json1
-rw-r--r--src/i18n/locales/en-US.json2
-rw-r--r--src/i18n/locales/es.json1
-rw-r--r--src/i18n/locales/fr.json1
-rw-r--r--src/i18n/locales/it.json1
-rw-r--r--src/lib/Form.js5
-rw-r--r--src/models/User.js2
-rw-r--r--src/stores/UserStore.js8
-rw-r--r--src/styles/invite.scss4
-rw-r--r--src/styles/main.scss1
-rw-r--r--src/styles/settings.scss9
17 files changed, 108 insertions, 25 deletions
diff --git a/src/components/auth/Invite.js b/src/components/auth/Invite.js
index a420f98a2..1fe594d73 100644
--- a/src/components/auth/Invite.js
+++ b/src/components/auth/Invite.js
@@ -30,6 +30,10 @@ const messages = defineMessages({
30 id: 'invite.skip.label', 30 id: 'invite.skip.label',
31 defaultMessage: '!!!I want to do this later', 31 defaultMessage: '!!!I want to do this later',
32 }, 32 },
33 noEmailAddresses: {
34 id: 'invite.error.noEmails',
35 defaultMessage: '!!!At least one email address is required',
36 }
33}); 37});
34 38
35@observer 39@observer
@@ -45,17 +49,17 @@ export default class Invite extends Component {
45 form = new Form({ 49 form = new Form({
46 fields: { 50 fields: {
47 invite: [...Array(3).fill({ 51 invite: [...Array(3).fill({
48 name: { 52 fields: {
49 label: this.context.intl.formatMessage(messages.nameLabel), 53 name: {
50 // value: '', 54 label: this.context.intl.formatMessage(messages.nameLabel),
51 placeholder: this.context.intl.formatMessage(messages.nameLabel), 55 placeholder: this.context.intl.formatMessage(messages.nameLabel),
52 }, 56 },
53 email: { 57 email: {
54 label: this.context.intl.formatMessage(messages.emailLabel), 58 label: this.context.intl.formatMessage(messages.emailLabel),
55 // value: '', 59 placeholder: this.context.intl.formatMessage(messages.emailLabel),
56 validators: [email], 60 validators: [email],
57 placeholder: this.context.intl.formatMessage(messages.emailLabel), 61 }
58 }, 62 }
59 })], 63 })],
60 }, 64 },
61 }, this.context.intl); 65 }, this.context.intl);
@@ -64,6 +68,21 @@ export default class Invite extends Component {
64 e.preventDefault(); 68 e.preventDefault();
65 this.form.submit({ 69 this.form.submit({
66 onSuccess: (form) => { 70 onSuccess: (form) => {
71
72 this.props.onSubmit({
73 invites: form.values().invite,
74 from: this.props.from
75 });
76
77 const atLeastOneEmailAddress = form.$('invite')
78 .map(invite => {return invite.$('email').value})
79 .some(email => email.trim() !== '')
80
81 if (!atLeastOneEmailAddress) {
82 form.invalidate('no-email-addresses')
83 return
84 }
85
67 this.props.onSubmit({ invites: form.values().invite }); 86 this.props.onSubmit({ invites: form.values().invite });
68 }, 87 },
69 onError: () => {}, 88 onError: () => {},
@@ -73,6 +92,11 @@ export default class Invite extends Component {
73 render() { 92 render() {
74 const { form } = this; 93 const { form } = this;
75 const { intl } = this.context; 94 const { intl } = this.context;
95 const { from } = this.props;
96
97 const atLeastOneEmailAddress = form.$('invite')
98 .map(invite => {return invite.$('email').value})
99 .some(email => email.trim() !== '')
76 100
77 return ( 101 return (
78 <div className="auth__container auth__container--signup"> 102 <div className="auth__container auth__container--signup">
@@ -93,13 +117,19 @@ export default class Invite extends Component {
93 </div> 117 </div>
94 </div> 118 </div>
95 ))} 119 ))}
120 {form.error === 'no-email-addresses' && (
121 <p className="franz-form__error invite-form__error">
122 {intl.formatMessage(messages.noEmailAddresses)}
123 </p>
124 )}
96 <Button 125 <Button
97 type="submit" 126 type="submit"
98 className="auth__button" 127 className="auth__button"
128 disabled={!atLeastOneEmailAddress}
99 label={intl.formatMessage(messages.submitButtonLabel)} 129 label={intl.formatMessage(messages.submitButtonLabel)}
100 /> 130 />
101 <Link 131 <Link
102 to="/" 132 to={ !!from ? from : '/'}
103 className="franz-form__button franz-form__button--secondary auth__button auth__button--skip" 133 className="franz-form__button franz-form__button--secondary auth__button auth__button--skip"
104 > 134 >
105 {intl.formatMessage(messages.skipButtonLabel)} 135 {intl.formatMessage(messages.skipButtonLabel)}
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js
index 43272fe96..d5f2e238c 100644
--- a/src/components/settings/account/AccountDashboard.js
+++ b/src/components/settings/account/AccountDashboard.js
@@ -48,6 +48,11 @@ const messages = defineMessages({
48 id: 'settings.account.account.editButton', 48 id: 'settings.account.account.editButton',
49 defaultMessage: '!!!Edit Account', 49 defaultMessage: '!!!Edit Account',
50 }, 50 },
51 accountInviteButton: {
52 id: "settings.account.account.inviteButton",
53 defaultMessage: '!!!Invite Friends',
54 },
55
51 invoiceDownload: { 56 invoiceDownload: {
52 id: 'settings.account.invoiceDownload', 57 id: 'settings.account.invoiceDownload',
53 defaultMessage: '!!!Download', 58 defaultMessage: '!!!Download',
@@ -113,6 +118,7 @@ export default class AccountDashboard extends Component {
113 deleteAccount, 118 deleteAccount,
114 isLoadingDeleteAccount, 119 isLoadingDeleteAccount,
115 isDeleteAccountSuccessful, 120 isDeleteAccountSuccessful,
121 pathname,
116 } = this.props; 122 } = this.props;
117 const { intl } = this.context; 123 const { intl } = this.context;
118 124
@@ -174,10 +180,25 @@ export default class AccountDashboard extends Component {
174 <span className="badge badge--premium">{intl.formatMessage(messages.accountTypePremium)}</span> 180 <span className="badge badge--premium">{intl.formatMessage(messages.accountTypePremium)}</span>
175 )} 181 )}
176 </div> 182 </div>
177 <Link to="/settings/user/edit" className="button"> 183 <div className="grid">
178 {intl.formatMessage(messages.accountEditButton)} 184 <div className="grid__row">
179 </Link> 185 <Link to="/settings/user/edit" className="button account__edit-button">
180 186 {intl.formatMessage(messages.accountEditButton)}
187 </Link>
188 </div>
189 <div className="grid__row">
190 <Link
191 to={{
192 pathname: '/auth/signup/invite',
193 query: { from: pathname },
194 // state: { "from": 'hi' } // is not being passed to route
195 }}
196 className="button account__invite-button">
197 {intl.formatMessage(messages.accountInviteButton)}
198 </Link>
199 </div>
200 </div>
201
181 {user.emailValidated} 202 {user.emailValidated}
182 </div> 203 </div>
183 </div> 204 </div>
diff --git a/src/components/ui/Link.js b/src/components/ui/Link.js
index f5da921fa..bc3c2d8aa 100644
--- a/src/components/ui/Link.js
+++ b/src/components/ui/Link.js
@@ -62,7 +62,10 @@ Link.wrappedComponent.propTypes = {
62 oneOrManyChildElements, 62 oneOrManyChildElements,
63 PropTypes.string, 63 PropTypes.string,
64 ]).isRequired, 64 ]).isRequired,
65 to: PropTypes.string.isRequired, 65 to: PropTypes.oneOfType([
66 PropTypes.string,
67 PropTypes.object
68 ]).isRequired,
66 className: PropTypes.string, 69 className: PropTypes.string,
67 activeClassName: PropTypes.string, 70 activeClassName: PropTypes.string,
68 strictFilter: PropTypes.bool, 71 strictFilter: PropTypes.bool,
diff --git a/src/containers/auth/InviteScreen.js b/src/containers/auth/InviteScreen.js
index 51971f436..a624e2245 100644
--- a/src/containers/auth/InviteScreen.js
+++ b/src/containers/auth/InviteScreen.js
@@ -11,10 +11,15 @@ export default class InviteScreen extends Component {
11 } 11 }
12 12
13 render() { 13 render() {
14 const { actions } = this.props; 14 const {
15 actions,
16 location
17 } = this.props;
18
15 return ( 19 return (
16 <Invite 20 <Invite
17 onSubmit={actions.user.invite} 21 onSubmit={actions.user.invite}
22 from={location.query.from}
18 /> 23 />
19 ); 24 );
20 } 25 }
diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js
index c5c2982b0..b57530884 100644
--- a/src/containers/settings/AccountScreen.js
+++ b/src/containers/settings/AccountScreen.js
@@ -83,6 +83,7 @@ export default class AccountScreen extends Component {
83 deleteAccount={userActions.delete} 83 deleteAccount={userActions.delete}
84 isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting} 84 isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting}
85 isDeleteAccountSuccessful={user.deleteAccountRequest.wasExecuted && !user.deleteAccountRequest.isError} 85 isDeleteAccountSuccessful={user.deleteAccountRequest.wasExecuted && !user.deleteAccountRequest.isError}
86 pathname={this.props.location.pathname}
86 /> 87 />
87 ); 88 );
88 } 89 }
diff --git a/src/helpers/validation-helpers.js b/src/helpers/validation-helpers.js
index eeb12cab7..a8a242d54 100644
--- a/src/helpers/validation-helpers.js
+++ b/src/helpers/validation-helpers.js
@@ -13,7 +13,7 @@ export function email({ field }) {
13 isValid = true; 13 isValid = true;
14 } 14 }
15 15
16 return [isValid, `${field.label} is not a valid email address`]; 16 return [isValid, `${field.label} not valid`];
17} 17}
18 18
19export function url({ field }) { 19export function url({ field }) {
diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json
index 1ea0554ba..180db3018 100644
--- a/src/i18n/locales/de.json
+++ b/src/i18n/locales/de.json
@@ -45,6 +45,7 @@
45 "services.getStarted" : "Loslegen", 45 "services.getStarted" : "Loslegen",
46 "services.welcome" : "Willkommen bei Franz.", 46 "services.welcome" : "Willkommen bei Franz.",
47 "settings.account.account.editButton" : "Konto bearbeiten", 47 "settings.account.account.editButton" : "Konto bearbeiten",
48 "settings.account.account.inviteButton" : "Freunde einladen",
48 "settings.account.accountType.basic" : "Basis Konto", 49 "settings.account.accountType.basic" : "Basis Konto",
49 "settings.account.accountType.premium" : "Premium-Supporter Konto", 50 "settings.account.accountType.premium" : "Premium-Supporter Konto",
50 "settings.account.buttonSave" : "Profil aktualisieren", 51 "settings.account.buttonSave" : "Profil aktualisieren",
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 63d7ce60b..c9e9b860b 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -45,6 +45,7 @@
45 "invite.name.label": "Name", 45 "invite.name.label": "Name",
46 "invite.email.label": "Email address", 46 "invite.email.label": "Email address",
47 "invite.skip.label": "I want to do this later", 47 "invite.skip.label": "I want to do this later",
48 "invite.error.noEmails": "At least one email address is required",
48 "subscription.submit.label": "I want to support the development of Franz", 49 "subscription.submit.label": "I want to support the development of Franz",
49 "subscription.paymentSessionError": "Could not initialize payment form", 50 "subscription.paymentSessionError": "Could not initialize payment form",
50 "subscription.includedFeatures": "Paid Franz Premium Supporter Account includes", 51 "subscription.includedFeatures": "Paid Franz Premium Supporter Account includes",
@@ -76,6 +77,7 @@
76 "settings.account.accountType.basic": "Basic Account", 77 "settings.account.accountType.basic": "Basic Account",
77 "settings.account.accountType.premium": "Premium Supporter Account", 78 "settings.account.accountType.premium": "Premium Supporter Account",
78 "settings.account.account.editButton": "Edit account", 79 "settings.account.account.editButton": "Edit account",
80 "settings.account.account.inviteButton" : "Invite friends",
79 "settings.account.invoiceDownload": "Download", 81 "settings.account.invoiceDownload": "Download",
80 "settings.account.userInfoRequestFailed": "Could not load user information", 82 "settings.account.userInfoRequestFailed": "Could not load user information",
81 "settings.account.tryReloadUserInfoRequest": "Try again", 83 "settings.account.tryReloadUserInfoRequest": "Try again",
diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json
index c4f598bd1..dddd1bf1f 100644
--- a/src/i18n/locales/es.json
+++ b/src/i18n/locales/es.json
@@ -45,6 +45,7 @@
45 "services.getStarted" : "Primeros pasos", 45 "services.getStarted" : "Primeros pasos",
46 "services.welcome" : "Bienvenido a Franz", 46 "services.welcome" : "Bienvenido a Franz",
47 "settings.account.account.editButton" : "Editar cuenta", 47 "settings.account.account.editButton" : "Editar cuenta",
48 "settings.account.account.inviteButton" : "Invitar amigos",
48 "settings.account.accountType.basic" : "Cuenta Básica", 49 "settings.account.accountType.basic" : "Cuenta Básica",
49 "settings.account.accountType.premium" : "Cuenta Colaborador Premium", 50 "settings.account.accountType.premium" : "Cuenta Colaborador Premium",
50 "settings.account.buttonSave" : "Actualizar perfil", 51 "settings.account.buttonSave" : "Actualizar perfil",
diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json
index 4a06975bb..a717577be 100644
--- a/src/i18n/locales/fr.json
+++ b/src/i18n/locales/fr.json
@@ -45,6 +45,7 @@
45 "services.getStarted" : "Commencer", 45 "services.getStarted" : "Commencer",
46 "services.welcome" : "Bienvenue dans Franz", 46 "services.welcome" : "Bienvenue dans Franz",
47 "settings.account.account.editButton" : "Modifier le compte", 47 "settings.account.account.editButton" : "Modifier le compte",
48 "settings.account.account.inviteButton" : "Inviter des amis",
48 "settings.account.accountType.basic" : "Compte de base", 49 "settings.account.accountType.basic" : "Compte de base",
49 "settings.account.accountType.premium" : "Compte supporteur premium", 50 "settings.account.accountType.premium" : "Compte supporteur premium",
50 "settings.account.buttonSave" : "Mettre à jour le profil", 51 "settings.account.buttonSave" : "Mettre à jour le profil",
diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json
index 1d2485b19..5b89b3a12 100644
--- a/src/i18n/locales/it.json
+++ b/src/i18n/locales/it.json
@@ -45,6 +45,7 @@
45 "services.getStarted" : "Iniziamo", 45 "services.getStarted" : "Iniziamo",
46 "services.welcome" : "Benvenuto in Franz", 46 "services.welcome" : "Benvenuto in Franz",
47 "settings.account.account.editButton" : "Modifica account", 47 "settings.account.account.editButton" : "Modifica account",
48 "settings.account.account.inviteButton" : "Invitare amicos",
48 "settings.account.accountType.basic" : "Account Basic", 49 "settings.account.accountType.basic" : "Account Basic",
49 "settings.account.accountType.premium" : "Premium Supporter Account", 50 "settings.account.accountType.premium" : "Premium Supporter Account",
50 "settings.account.buttonSave" : "Aggiorna profilo", 51 "settings.account.buttonSave" : "Aggiorna profilo",
diff --git a/src/lib/Form.js b/src/lib/Form.js
index a22699b45..9b8321948 100644
--- a/src/lib/Form.js
+++ b/src/lib/Form.js
@@ -21,8 +21,9 @@ export default class DefaultForm extends Form {
21 21
22 options() { 22 options() {
23 return { 23 return {
24 validateOnInit: false, 24 validateOnInit: false, // default: true
25 // validateOnBlur: true, 25 // validateOnBlur: true, // default: true
26 // validateOnChange: true // default: false
26 // // validationDebounceWait: { 27 // // validationDebounceWait: {
27 // // trailing: true, 28 // // trailing: true,
28 // // }, 29 // // },
diff --git a/src/models/User.js b/src/models/User.js
index 2e5df4795..d2a455e20 100644
--- a/src/models/User.js
+++ b/src/models/User.js
@@ -10,7 +10,7 @@ export default class User {
10 @observable emailIsConfirmed = true; // better assume it's confirmed to avoid noise 10 @observable emailIsConfirmed = true; // better assume it's confirmed to avoid noise
11 @observable subscription = {}; 11 @observable subscription = {};
12 @observable isSubscriptionOwner = false; 12 @observable isSubscriptionOwner = false;
13 @observable isPremium = false; 13 @observable isPremium = true;
14 @observable beta = false; 14 @observable beta = false;
15 @observable donor = {}; 15 @observable donor = {};
16 @observable isDonor = false; 16 @observable isDonor = false;
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js
index 09000dcdb..476c1f040 100644
--- a/src/stores/UserStore.js
+++ b/src/stores/UserStore.js
@@ -160,13 +160,13 @@ export default class UserStore extends Store {
160 gaEvent('User', 'retrievePassword'); 160 gaEvent('User', 'retrievePassword');
161 } 161 }
162 162
163 @action _invite({ invites }) { 163 @action _invite({ invites, from }) {
164 const data = invites.filter(invite => invite.email !== ''); 164 const data = invites.filter(invite => invite.email !== '');
165 165
166 this.inviteRequest.execute(data); 166 this.inviteRequest.execute(data);
167 167
168 // we do not wait for a server response before redirecting the user 168 // we do not wait for a server response before redirecting the user
169 this.stores.router.push('/'); 169 this.stores.router.push(!!from ? from : '/');
170 170
171 gaEvent('User', 'inviteUsers'); 171 gaEvent('User', 'inviteUsers');
172 } 172 }
@@ -237,7 +237,9 @@ export default class UserStore extends Store {
237 && currentRoute.includes(this.BASE_ROUTE) 237 && currentRoute.includes(this.BASE_ROUTE)
238 && (this.hasCompletedSignup 238 && (this.hasCompletedSignup
239 || this.hasCompletedSignup === null)) { 239 || this.hasCompletedSignup === null)) {
240 this.stores.router.push('/'); 240 if (!isDevMode) {
241 this.stores.router.push('/');
242 }
241 } 243 }
242 }; 244 };
243 245
diff --git a/src/styles/invite.scss b/src/styles/invite.scss
new file mode 100644
index 000000000..345095da8
--- /dev/null
+++ b/src/styles/invite.scss
@@ -0,0 +1,4 @@
1.invite-form__error {
2 text-align: center;
3 margin-bottom: 20px !important; /* otherwise overridden by rule: p:last-of-type */
4}
diff --git a/src/styles/main.scss b/src/styles/main.scss
index 261396f6f..20f1803e4 100644
--- a/src/styles/main.scss
+++ b/src/styles/main.scss
@@ -18,6 +18,7 @@ $mdi-font-path: '../node_modules/mdi/fonts';
18@import './type.scss'; 18@import './type.scss';
19@import './welcome.scss'; 19@import './welcome.scss';
20@import './auth.scss'; 20@import './auth.scss';
21@import './invite.scss';
21@import './tooltip.scss'; 22@import './tooltip.scss';
22@import './info-bar.scss'; 23@import './info-bar.scss';
23@import './status-bar-target-url.scss'; 24@import './status-bar-target-url.scss';
diff --git a/src/styles/settings.scss b/src/styles/settings.scss
index 2182c9b5f..f436b1eda 100644
--- a/src/styles/settings.scss
+++ b/src/styles/settings.scss
@@ -282,6 +282,15 @@
282 } 282 }
283 } 283 }
284 284
285 .account__edit-button {
286 width: 170px;
287 margin-bottom: 10px;
288 }
289
290 .account__invite-button {
291 width: 170px;
292 }
293
285 .account__subscription { 294 .account__subscription {
286 display: flex; 295 display: flex;
287 align-items: center; 296 align-items: center;