diff options
-rw-r--r-- | src/app.js | 2 | ||||
-rw-r--r-- | src/components/auth/Invite.js | 64 | ||||
-rw-r--r-- | src/components/settings/account/AccountDashboard.js | 7 | ||||
-rw-r--r-- | src/components/settings/navigation/SettingsNavigation.js | 11 | ||||
-rw-r--r-- | src/components/settings/services/ServiceError.js | 2 | ||||
-rw-r--r-- | src/components/settings/user/EditUserForm.js | 2 | ||||
-rw-r--r-- | src/components/ui/Link.js | 5 | ||||
-rw-r--r-- | src/containers/auth/InviteScreen.js | 21 | ||||
-rw-r--r-- | src/containers/settings/AccountScreen.js | 6 | ||||
-rw-r--r-- | src/containers/settings/InviteScreen.js | 63 | ||||
-rw-r--r-- | src/i18n/locales/de.json | 1 | ||||
-rw-r--r-- | src/i18n/locales/en-US.json | 4 | ||||
-rw-r--r-- | src/i18n/locales/es.json | 1 | ||||
-rw-r--r-- | src/i18n/locales/fr.json | 1 | ||||
-rw-r--r-- | src/i18n/locales/it.json | 1 | ||||
-rw-r--r-- | src/models/User.js | 2 | ||||
-rw-r--r-- | src/stores/UserStore.js | 17 | ||||
-rw-r--r-- | src/styles/invite.scss | 15 | ||||
-rw-r--r-- | src/styles/main.scss | 1 |
19 files changed, 203 insertions, 23 deletions
diff --git a/src/app.js b/src/app.js index 8e62776d2..edcf273dc 100644 --- a/src/app.js +++ b/src/app.js | |||
@@ -27,6 +27,7 @@ import EditServiceScreen from './containers/settings/EditServiceScreen'; | |||
27 | import AccountScreen from './containers/settings/AccountScreen'; | 27 | import AccountScreen from './containers/settings/AccountScreen'; |
28 | import EditUserScreen from './containers/settings/EditUserScreen'; | 28 | import EditUserScreen from './containers/settings/EditUserScreen'; |
29 | import EditSettingsScreen from './containers/settings/EditSettingsScreen'; | 29 | import EditSettingsScreen from './containers/settings/EditSettingsScreen'; |
30 | import InviteSettingsScreen from './containers/settings/InviteScreen'; | ||
30 | import WelcomeScreen from './containers/auth/WelcomeScreen'; | 31 | import WelcomeScreen from './containers/auth/WelcomeScreen'; |
31 | import LoginScreen from './containers/auth/LoginScreen'; | 32 | import LoginScreen from './containers/auth/LoginScreen'; |
32 | import PasswordScreen from './containers/auth/PasswordScreen'; | 33 | import PasswordScreen from './containers/auth/PasswordScreen'; |
@@ -74,6 +75,7 @@ window.addEventListener('load', () => { | |||
74 | <Route path="/settings/user" component={AccountScreen} /> | 75 | <Route path="/settings/user" component={AccountScreen} /> |
75 | <Route path="/settings/user/edit" component={EditUserScreen} /> | 76 | <Route path="/settings/user/edit" component={EditUserScreen} /> |
76 | <Route path="/settings/app" component={EditSettingsScreen} /> | 77 | <Route path="/settings/app" component={EditSettingsScreen} /> |
78 | <Route path="/settings/invite" component={InviteSettingsScreen} /> | ||
77 | </Route> | 79 | </Route> |
78 | </Route> | 80 | </Route> |
79 | <Route path="/auth" component={AuthLayoutContainer}> | 81 | <Route path="/auth" component={AuthLayoutContainer}> |
diff --git a/src/components/auth/Invite.js b/src/components/auth/Invite.js index c6dca3a65..2dcce7f83 100644 --- a/src/components/auth/Invite.js +++ b/src/components/auth/Invite.js | |||
@@ -3,7 +3,10 @@ import PropTypes from 'prop-types'; | |||
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, intlShape } from 'react-intl'; | 4 | import { defineMessages, intlShape } from 'react-intl'; |
5 | import { Link } from 'react-router'; | 5 | import { Link } from 'react-router'; |
6 | import classnames from 'classnames'; | ||
6 | 7 | ||
8 | import Infobox from '../ui/Infobox'; | ||
9 | import Appear from '../ui/effects/Appear'; | ||
7 | import Form from '../../lib/Form'; | 10 | import Form from '../../lib/Form'; |
8 | import { email } from '../../helpers/validation-helpers'; | 11 | import { email } from '../../helpers/validation-helpers'; |
9 | import Input from '../ui/Input'; | 12 | import Input from '../ui/Input'; |
@@ -30,18 +33,39 @@ const messages = defineMessages({ | |||
30 | id: 'invite.skip.label', | 33 | id: 'invite.skip.label', |
31 | defaultMessage: '!!!I want to do this later', | 34 | defaultMessage: '!!!I want to do this later', |
32 | }, | 35 | }, |
36 | inviteSuccessInfo: { | ||
37 | id: 'invite.successInfo', | ||
38 | defaultMessage: '!!!Invitations sent successfully', | ||
39 | }, | ||
33 | }); | 40 | }); |
34 | 41 | ||
35 | @observer | 42 | @observer |
36 | export default class Invite extends Component { | 43 | export default class Invite extends Component { |
37 | static propTypes = { | 44 | static propTypes = { |
38 | onSubmit: PropTypes.func.isRequired, | 45 | onSubmit: PropTypes.func.isRequired, |
46 | embed: PropTypes.bool, | ||
47 | isInviteSuccessful: PropTypes.bool, | ||
48 | isLoadingInvite: PropTypes.bool, | ||
49 | }; | ||
50 | |||
51 | static defaultProps = { | ||
52 | embed: false, | ||
53 | isInviteSuccessful: false, | ||
54 | isLoadingInvite: false, | ||
39 | }; | 55 | }; |
40 | 56 | ||
41 | static contextTypes = { | 57 | static contextTypes = { |
42 | intl: intlShape, | 58 | intl: intlShape, |
43 | }; | 59 | }; |
44 | 60 | ||
61 | state = { showSuccessInfo: false }; | ||
62 | |||
63 | handlers = { | ||
64 | onChange: () => { | ||
65 | this.setState({ showSuccessInfo: false }); | ||
66 | }, | ||
67 | }; | ||
68 | |||
45 | form = new Form({ | 69 | form = new Form({ |
46 | fields: { | 70 | fields: { |
47 | invite: [...Array(3).fill({ | 71 | invite: [...Array(3).fill({ |
@@ -49,10 +73,13 @@ export default class Invite extends Component { | |||
49 | name: { | 73 | name: { |
50 | label: this.context.intl.formatMessage(messages.nameLabel), | 74 | label: this.context.intl.formatMessage(messages.nameLabel), |
51 | placeholder: this.context.intl.formatMessage(messages.nameLabel), | 75 | placeholder: this.context.intl.formatMessage(messages.nameLabel), |
76 | handlers: this.handlers, | ||
77 | // related: ['invite.0.email'], // path accepted but does not work | ||
52 | }, | 78 | }, |
53 | email: { | 79 | email: { |
54 | label: this.context.intl.formatMessage(messages.emailLabel), | 80 | label: this.context.intl.formatMessage(messages.emailLabel), |
55 | placeholder: this.context.intl.formatMessage(messages.emailLabel), | 81 | placeholder: this.context.intl.formatMessage(messages.emailLabel), |
82 | handlers: this.handlers, | ||
56 | validators: [email], | 83 | validators: [email], |
57 | }, | 84 | }, |
58 | }, | 85 | }, |
@@ -62,9 +89,15 @@ export default class Invite extends Component { | |||
62 | 89 | ||
63 | submit(e) { | 90 | submit(e) { |
64 | e.preventDefault(); | 91 | e.preventDefault(); |
92 | |||
65 | this.form.submit({ | 93 | this.form.submit({ |
66 | onSuccess: (form) => { | 94 | onSuccess: (form) => { |
67 | this.props.onSubmit({ invites: form.values().invite }); | 95 | this.props.onSubmit({ invites: form.values().invite }); |
96 | |||
97 | this.form.clear(); | ||
98 | // this.form.$('invite.0.name').focus(); // path accepted but does not focus ;( | ||
99 | document.querySelector('input:first-child').focus(); | ||
100 | this.setState({ showSuccessInfo: true }); | ||
68 | }, | 101 | }, |
69 | onError: () => {}, | 102 | onError: () => {}, |
70 | }); | 103 | }); |
@@ -73,20 +106,36 @@ export default class Invite extends Component { | |||
73 | render() { | 106 | render() { |
74 | const { form } = this; | 107 | const { form } = this; |
75 | const { intl } = this.context; | 108 | const { intl } = this.context; |
109 | const { embed, isInviteSuccessful, isLoadingInvite } = this.props; | ||
76 | 110 | ||
77 | const atLeastOneEmailAddress = form.$('invite') | 111 | const atLeastOneEmailAddress = form.$('invite') |
78 | .map(invite => invite.$('email').value) | 112 | .map(invite => invite.$('email').value) |
79 | .some(emailValue => emailValue.trim() !== ''); | 113 | .some(emailValue => emailValue.trim() !== ''); |
80 | 114 | ||
115 | const sendButtonClassName = classnames({ | ||
116 | auth__button: true, | ||
117 | 'invite__embed--button': embed, | ||
118 | }); | ||
119 | |||
81 | return ( | 120 | return ( |
82 | <div className="auth__container auth__container--signup"> | 121 | <div> |
122 | {this.state.showSuccessInfo && isInviteSuccessful && (<Appear> | ||
123 | <Infobox | ||
124 | type="success" | ||
125 | icon="checkbox-marked-circle-outline" | ||
126 | dismissable | ||
127 | > | ||
128 | {intl.formatMessage(messages.inviteSuccessInfo)} | ||
129 | </Infobox> | ||
130 | </Appear>)} | ||
131 | |||
83 | <form className="franz-form auth__form" onSubmit={e => this.submit(e)}> | 132 | <form className="franz-form auth__form" onSubmit={e => this.submit(e)}> |
84 | <img | 133 | {!embed && (<img |
85 | src="./assets/images/logo.svg" | 134 | src="./assets/images/logo.svg" |
86 | className="auth__logo" | 135 | className="auth__logo" |
87 | alt="" | 136 | alt="" |
88 | /> | 137 | />)} |
89 | <h1> | 138 | <h1 className={embed && 'invite__embed'}> |
90 | {intl.formatMessage(messages.headline)} | 139 | {intl.formatMessage(messages.headline)} |
91 | </h1> | 140 | </h1> |
92 | {form.$('invite').map(invite => ( | 141 | {form.$('invite').map(invite => ( |
@@ -99,16 +148,17 @@ export default class Invite extends Component { | |||
99 | ))} | 148 | ))} |
100 | <Button | 149 | <Button |
101 | type="submit" | 150 | type="submit" |
102 | className="auth__button" | 151 | className={sendButtonClassName} |
103 | disabled={!atLeastOneEmailAddress} | 152 | disabled={!atLeastOneEmailAddress} |
104 | label={intl.formatMessage(messages.submitButtonLabel)} | 153 | label={intl.formatMessage(messages.submitButtonLabel)} |
154 | loaded={!isLoadingInvite} | ||
105 | /> | 155 | /> |
106 | <Link | 156 | {!embed && (<Link |
107 | to="/" | 157 | to="/" |
108 | className="franz-form__button franz-form__button--secondary auth__button auth__button--skip" | 158 | className="franz-form__button franz-form__button--secondary auth__button auth__button--skip" |
109 | > | 159 | > |
110 | {intl.formatMessage(messages.skipButtonLabel)} | 160 | {intl.formatMessage(messages.skipButtonLabel)} |
111 | </Link> | 161 | </Link>)} |
112 | </form> | 162 | </form> |
113 | </div> | 163 | </div> |
114 | ); | 164 | ); |
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js index 43272fe96..e079fd2f4 100644 --- a/src/components/settings/account/AccountDashboard.js +++ b/src/components/settings/account/AccountDashboard.js | |||
@@ -48,6 +48,10 @@ 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 | }, | ||
51 | invoiceDownload: { | 55 | invoiceDownload: { |
52 | id: 'settings.account.invoiceDownload', | 56 | id: 'settings.account.invoiceDownload', |
53 | defaultMessage: '!!!Download', | 57 | defaultMessage: '!!!Download', |
@@ -174,10 +178,9 @@ export default class AccountDashboard extends Component { | |||
174 | <span className="badge badge--premium">{intl.formatMessage(messages.accountTypePremium)}</span> | 178 | <span className="badge badge--premium">{intl.formatMessage(messages.accountTypePremium)}</span> |
175 | )} | 179 | )} |
176 | </div> | 180 | </div> |
177 | <Link to="/settings/user/edit" className="button"> | 181 | <Link to="/settings/user/edit" className="button account__edit-button"> |
178 | {intl.formatMessage(messages.accountEditButton)} | 182 | {intl.formatMessage(messages.accountEditButton)} |
179 | </Link> | 183 | </Link> |
180 | |||
181 | {user.emailValidated} | 184 | {user.emailValidated} |
182 | </div> | 185 | </div> |
183 | </div> | 186 | </div> |
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index fea8d682d..66539f324 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js | |||
@@ -21,6 +21,10 @@ const messages = defineMessages({ | |||
21 | id: 'settings.navigation.settings', | 21 | id: 'settings.navigation.settings', |
22 | defaultMessage: '!!!Settings', | 22 | defaultMessage: '!!!Settings', |
23 | }, | 23 | }, |
24 | inviteFriends: { | ||
25 | id: 'settings.navigation.inviteFriends', | ||
26 | defaultMessage: '!!!Invite Friends', | ||
27 | }, | ||
24 | logout: { | 28 | logout: { |
25 | id: 'settings.navigation.logout', | 29 | id: 'settings.navigation.logout', |
26 | defaultMessage: '!!!Logout', | 30 | defaultMessage: '!!!Logout', |
@@ -70,6 +74,13 @@ export default class SettingsNavigation extends Component { | |||
70 | > | 74 | > |
71 | {intl.formatMessage(messages.settings)} | 75 | {intl.formatMessage(messages.settings)} |
72 | </Link> | 76 | </Link> |
77 | <Link | ||
78 | to="/settings/invite" | ||
79 | className="settings-navigation__link" | ||
80 | activeClassName="is-active" | ||
81 | > | ||
82 | {intl.formatMessage(messages.inviteFriends)} | ||
83 | </Link> | ||
73 | <span className="settings-navigation__expander" /> | 84 | <span className="settings-navigation__expander" /> |
74 | <Link | 85 | <Link |
75 | to="/auth/logout" | 86 | to="/auth/logout" |
diff --git a/src/components/settings/services/ServiceError.js b/src/components/settings/services/ServiceError.js index 923053296..1f1512927 100644 --- a/src/components/settings/services/ServiceError.js +++ b/src/components/settings/services/ServiceError.js | |||
@@ -26,7 +26,7 @@ const messages = defineMessages({ | |||
26 | }); | 26 | }); |
27 | 27 | ||
28 | @observer | 28 | @observer |
29 | export default class EditServiceForm extends Component { | 29 | export default class ServiceError extends Component { |
30 | static contextTypes = { | 30 | static contextTypes = { |
31 | intl: intlShape, | 31 | intl: intlShape, |
32 | }; | 32 | }; |
diff --git a/src/components/settings/user/EditUserForm.js b/src/components/settings/user/EditUserForm.js index f36887fc2..0a7b7c396 100644 --- a/src/components/settings/user/EditUserForm.js +++ b/src/components/settings/user/EditUserForm.js | |||
@@ -135,7 +135,7 @@ export default class EditServiceForm extends Component { | |||
135 | <Button | 135 | <Button |
136 | type="submit" | 136 | type="submit" |
137 | label={intl.formatMessage(messages.buttonSave)} | 137 | label={intl.formatMessage(messages.buttonSave)} |
138 | htmlForm="form" | 138 | htmlForm="form" // why is form attribute escaped in JSX?? couldn't find any info on that, did you mean "htmlFor"? |
139 | /> | 139 | /> |
140 | )} | 140 | )} |
141 | </div> | 141 | </div> |
diff --git a/src/components/ui/Link.js b/src/components/ui/Link.js index f5da921fa..693be84ea 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..7102df0b9 100644 --- a/src/containers/auth/InviteScreen.js +++ b/src/containers/auth/InviteScreen.js | |||
@@ -11,11 +11,19 @@ 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 | <div className="auth__container auth__container--signup"> |
17 | onSubmit={actions.user.invite} | 21 | <Invite |
18 | /> | 22 | onSubmit={actions.user.invite} |
23 | from={location.query.from} | ||
24 | embed={false} | ||
25 | /> | ||
26 | </div> | ||
19 | ); | 27 | ); |
20 | } | 28 | } |
21 | } | 29 | } |
@@ -26,4 +34,9 @@ InviteScreen.wrappedComponent.propTypes = { | |||
26 | invite: PropTypes.func.isRequired, | 34 | invite: PropTypes.func.isRequired, |
27 | }).isRequired, | 35 | }).isRequired, |
28 | }).isRequired, | 36 | }).isRequired, |
37 | location: PropTypes.shape({ | ||
38 | query: PropTypes.shape({ | ||
39 | from: PropTypes.string, | ||
40 | }), | ||
41 | }).isRequired, | ||
29 | }; | 42 | }; |
diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js index c5c2982b0..9ee93a9e9 100644 --- a/src/containers/settings/AccountScreen.js +++ b/src/containers/settings/AccountScreen.js | |||
@@ -61,7 +61,7 @@ export default class AccountScreen extends Component { | |||
61 | render() { | 61 | render() { |
62 | const { user, payment } = this.props.stores; | 62 | const { user, payment } = this.props.stores; |
63 | const { openExternalUrl } = this.props.actions.app; | 63 | const { openExternalUrl } = this.props.actions.app; |
64 | const { user: userActions } = this.props.actions; | 64 | const { user: userActions } = this.props.actions; // @adlk: :+1 what's the opposite of git blame? |
65 | 65 | ||
66 | const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; | 66 | const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; |
67 | const isLoadingOrdersInfo = payment.ordersDataRequest.isExecuting; | 67 | const isLoadingOrdersInfo = payment.ordersDataRequest.isExecuting; |
@@ -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 | } |
@@ -106,4 +107,7 @@ AccountScreen.wrappedComponent.propTypes = { | |||
106 | delete: PropTypes.func.isRequired, | 107 | delete: PropTypes.func.isRequired, |
107 | }).isRequired, | 108 | }).isRequired, |
108 | }).isRequired, | 109 | }).isRequired, |
110 | location: PropTypes.shape({ | ||
111 | pathname: PropTypes.string, | ||
112 | }).isRequired, | ||
109 | }; | 113 | }; |
diff --git a/src/containers/settings/InviteScreen.js b/src/containers/settings/InviteScreen.js new file mode 100644 index 000000000..1947e79f0 --- /dev/null +++ b/src/containers/settings/InviteScreen.js | |||
@@ -0,0 +1,63 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { inject, observer } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | |||
6 | import Invite from '../../components/auth/Invite'; | ||
7 | import { gaPage } from '../../lib/analytics'; | ||
8 | |||
9 | const messages = defineMessages({ | ||
10 | headline: { | ||
11 | id: 'settings.invite.headline', | ||
12 | defaultMessage: '!!!Invite Friends', | ||
13 | }, | ||
14 | }); | ||
15 | |||
16 | @inject('stores', 'actions') @observer | ||
17 | export default class InviteScreen extends Component { | ||
18 | static contextTypes = { | ||
19 | intl: intlShape, | ||
20 | }; | ||
21 | |||
22 | componentDidMount() { | ||
23 | gaPage('Settings/Invite'); | ||
24 | } | ||
25 | |||
26 | componentWillUnmount() { | ||
27 | this.props.stores.user.inviteRequest.reset(); | ||
28 | } | ||
29 | |||
30 | render() { | ||
31 | const { actions } = this.props; | ||
32 | const { user } = this.props.stores; | ||
33 | |||
34 | return ( | ||
35 | <div className="settings__main"> | ||
36 | <div className="settings__header"> | ||
37 | <h1>{this.context.intl.formatMessage(messages.headline)}</h1> | ||
38 | </div> | ||
39 | <div className="settings__body invite__form"> | ||
40 | <Invite | ||
41 | onSubmit={actions.user.invite} | ||
42 | isLoadingInvite={user.inviteRequest.isExecuting} | ||
43 | isInviteSuccessful={user.inviteRequest.wasExecuted && !user.inviteRequest.isError} | ||
44 | embed | ||
45 | /> | ||
46 | </div> | ||
47 | </div> | ||
48 | ); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | InviteScreen.wrappedComponent.propTypes = { | ||
53 | actions: PropTypes.shape({ | ||
54 | user: PropTypes.shape({ | ||
55 | invite: PropTypes.func.isRequired, | ||
56 | }).isRequired, | ||
57 | }).isRequired, | ||
58 | stores: PropTypes.shape({ | ||
59 | user: PropTypes.shape({ | ||
60 | inviteRequest: PropTypes.object, | ||
61 | }).isRequired, | ||
62 | }).isRequired, | ||
63 | }; | ||
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..29b979838 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.successInfo": "Invitations sent successfully", | ||
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", |
@@ -87,10 +89,12 @@ | |||
87 | "settings.account.deleteAccount": "Delete account", | 89 | "settings.account.deleteAccount": "Delete account", |
88 | "settings.account.deleteInfo": "If you don't need your Franz account any longer, you can delete your account and all related data here.", | 90 | "settings.account.deleteInfo": "If you don't need your Franz account any longer, you can delete your account and all related data here.", |
89 | "settings.account.deleteEmailSent": "You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!", | 91 | "settings.account.deleteEmailSent": "You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!", |
92 | "settings.invite.headline": "Invite Friends", | ||
90 | "settings.navigation.availableServices": "Available services", | 93 | "settings.navigation.availableServices": "Available services", |
91 | "settings.navigation.yourServices": "Your services", | 94 | "settings.navigation.yourServices": "Your services", |
92 | "settings.navigation.account": "Account", | 95 | "settings.navigation.account": "Account", |
93 | "settings.navigation.settings": "Settings", | 96 | "settings.navigation.settings": "Settings", |
97 | "settings.navigation.inviteFriends": "Invite Friends", | ||
94 | "settings.navigation.logout": "Logout", | 98 | "settings.navigation.logout": "Logout", |
95 | "settings.recipes.headline": "Available services", | 99 | "settings.recipes.headline": "Available services", |
96 | "settings.recipes.mostPopular": "Most popular", | 100 | "settings.recipes.mostPopular": "Most popular", |
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/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..abec4df5d 100644 --- a/src/stores/UserStore.js +++ b/src/stores/UserStore.js | |||
@@ -2,6 +2,7 @@ import { observable, computed, action } from 'mobx'; | |||
2 | import moment from 'moment'; | 2 | import moment from 'moment'; |
3 | import jwt from 'jsonwebtoken'; | 3 | import jwt from 'jsonwebtoken'; |
4 | 4 | ||
5 | import { isDevMode } from '../environment'; | ||
5 | import Store from './lib/Store'; | 6 | import Store from './lib/Store'; |
6 | import Request from './lib/Request'; | 7 | import Request from './lib/Request'; |
7 | import CachedRequest from './lib/CachedRequest'; | 8 | import CachedRequest from './lib/CachedRequest'; |
@@ -160,13 +161,17 @@ export default class UserStore extends Store { | |||
160 | gaEvent('User', 'retrievePassword'); | 161 | gaEvent('User', 'retrievePassword'); |
161 | } | 162 | } |
162 | 163 | ||
163 | @action _invite({ invites }) { | 164 | @action async _invite({ invites }) { |
164 | const data = invites.filter(invite => invite.email !== ''); | 165 | const data = invites.filter(invite => invite.email !== ''); |
165 | 166 | ||
166 | this.inviteRequest.execute(data); | 167 | const response = await this.inviteRequest.execute(data)._promise; |
167 | 168 | ||
168 | // we do not wait for a server response before redirecting the user | 169 | this.actionStatus = response.status || []; |
169 | this.stores.router.push('/'); | 170 | |
171 | // we do not wait for a server response before redirecting the user ONLY DURING SIGNUP | ||
172 | if (this.stores.router.location.pathname.includes(this.INVITE_ROUTE)) { | ||
173 | this.stores.router.push('/'); | ||
174 | } | ||
170 | 175 | ||
171 | gaEvent('User', 'inviteUsers'); | 176 | gaEvent('User', 'inviteUsers'); |
172 | } | 177 | } |
@@ -237,7 +242,9 @@ export default class UserStore extends Store { | |||
237 | && currentRoute.includes(this.BASE_ROUTE) | 242 | && currentRoute.includes(this.BASE_ROUTE) |
238 | && (this.hasCompletedSignup | 243 | && (this.hasCompletedSignup |
239 | || this.hasCompletedSignup === null)) { | 244 | || this.hasCompletedSignup === null)) { |
240 | this.stores.router.push('/'); | 245 | if (!isDevMode) { |
246 | this.stores.router.push('/'); | ||
247 | } | ||
241 | } | 248 | } |
242 | }; | 249 | }; |
243 | 250 | ||
diff --git a/src/styles/invite.scss b/src/styles/invite.scss new file mode 100644 index 000000000..bfb1a4b6b --- /dev/null +++ b/src/styles/invite.scss | |||
@@ -0,0 +1,15 @@ | |||
1 | .invite__form { | ||
2 | /* play with values to see different layouts */ | ||
3 | // display: flex; | ||
4 | align-items: center; | ||
5 | align-self: center; | ||
6 | justify-content: center; | ||
7 | } | ||
8 | |||
9 | .invite__embed { | ||
10 | text-align: center; | ||
11 | } | ||
12 | |||
13 | .invite__embed--button { | ||
14 | width: 100%; | ||
15 | } \ No newline at end of file | ||
diff --git a/src/styles/main.scss b/src/styles/main.scss index 261396f6f..446bdca14 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss | |||
@@ -27,6 +27,7 @@ $mdi-font-path: '../node_modules/mdi/fonts'; | |||
27 | @import './subscription.scss'; | 27 | @import './subscription.scss'; |
28 | @import './subscription-popup.scss'; | 28 | @import './subscription-popup.scss'; |
29 | @import './content-tabs.scss'; | 29 | @import './content-tabs.scss'; |
30 | @import './invite.scss'; | ||
30 | 31 | ||
31 | // form | 32 | // form |
32 | @import './input.scss'; | 33 | @import './input.scss'; |