aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Stefan Malzner <stefan@adlk.io>2019-09-05 11:50:52 +0200
committerLibravatar Stefan Malzner <stefan@adlk.io>2019-09-05 11:50:52 +0200
commitcbde6455dbe25cbc5da3f4fd33add6b533d6d89a (patch)
tree707674d41f287d173e28c6466c140b50adaac394 /src
parentMerge branch 'develop' into i18n (diff)
parentRevert "create test build" (diff)
downloadferdium-app-cbde6455dbe25cbc5da3f4fd33add6b533d6d89a.tar.gz
ferdium-app-cbde6455dbe25cbc5da3f4fd33add6b533d6d89a.tar.zst
ferdium-app-cbde6455dbe25cbc5da3f4fd33add6b533d6d89a.zip
Merge branch 'develop' into i18n
Diffstat (limited to 'src')
-rw-r--r--src/components/settings/account/AccountDashboard.js23
-rw-r--r--src/components/settings/recipes/RecipesDashboard.js4
-rw-r--r--src/components/settings/team/TeamDashboard.js62
-rw-r--r--src/components/subscription/TrialForm.js3
-rw-r--r--src/components/ui/ActivateTrialButton/index.js115
-rw-r--r--src/components/ui/FeatureItem.js2
-rw-r--r--src/components/ui/PremiumFeatureContainer/index.js22
-rw-r--r--src/components/ui/UpgradeButton/index.js89
-rw-r--r--src/config.js6
-rw-r--r--src/containers/settings/AccountScreen.js2
-rw-r--r--src/containers/settings/TeamScreen.js1
-rw-r--r--src/containers/subscription/SubscriptionFormScreen.js2
-rw-r--r--src/environment.js2
-rw-r--r--src/features/shareFranz/Component.js12
-rw-r--r--src/features/todos/components/TodosWebview.js19
-rw-r--r--src/features/todos/containers/TodosScreen.js6
-rw-r--r--src/features/todos/store.js19
-rw-r--r--src/features/workspaces/components/WorkspacesDashboard.js173
-rw-r--r--src/features/workspaces/containers/WorkspacesScreen.js2
-rw-r--r--src/features/workspaces/store.js5
-rw-r--r--src/helpers/plan-helpers.js10
-rw-r--r--src/i18n/globalMessages.js4
-rw-r--r--src/i18n/locales/defaultMessages.json176
-rw-r--r--src/i18n/locales/en-US.json4
-rw-r--r--src/i18n/messages/src/components/settings/team/TeamDashboard.json24
-rw-r--r--src/i18n/messages/src/components/ui/ActivateTrialButton/index.json54
-rw-r--r--src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json4
-rw-r--r--src/i18n/messages/src/components/ui/UpgradeButton/index.json15
-rw-r--r--src/i18n/messages/src/features/shareFranz/Component.json28
-rw-r--r--src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json32
-rw-r--r--src/i18n/messages/src/i18n/globalMessages.json13
-rw-r--r--src/stores/UIStore.js1
-rw-r--r--src/stores/UserStore.js26
-rw-r--r--src/styles/settings.scss71
34 files changed, 781 insertions, 250 deletions
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js
index 900a83a78..ac2594604 100644
--- a/src/components/settings/account/AccountDashboard.js
+++ b/src/components/settings/account/AccountDashboard.js
@@ -93,9 +93,12 @@ const messages = defineMessages({
93 }, 93 },
94}); 94});
95 95
96export default @observer class AccountDashboard extends Component { 96@observer
97class AccountDashboard extends Component {
97 static propTypes = { 98 static propTypes = {
98 user: MobxPropTypes.observableObject.isRequired, 99 user: MobxPropTypes.observableObject.isRequired,
100 isPremiumOverrideUser: PropTypes.bool.isRequired,
101 isProUser: PropTypes.bool.isRequired,
99 isLoading: PropTypes.bool.isRequired, 102 isLoading: PropTypes.bool.isRequired,
100 userInfoRequestFailed: PropTypes.bool.isRequired, 103 userInfoRequestFailed: PropTypes.bool.isRequired,
101 retryUserInfoRequest: PropTypes.func.isRequired, 104 retryUserInfoRequest: PropTypes.func.isRequired,
@@ -115,6 +118,8 @@ export default @observer class AccountDashboard extends Component {
115 render() { 118 render() {
116 const { 119 const {
117 user, 120 user,
121 isPremiumOverrideUser,
122 isProUser,
118 isLoading, 123 isLoading,
119 userInfoRequestFailed, 124 userInfoRequestFailed,
120 retryUserInfoRequest, 125 retryUserInfoRequest,
@@ -210,7 +215,7 @@ export default @observer class AccountDashboard extends Component {
210 {intl.formatMessage(messages.yourLicense)} 215 {intl.formatMessage(messages.yourLicense)}
211 </H2> 216 </H2>
212 <p> 217 <p>
213 {planName} 218 {isPremiumOverrideUser ? 'Franz Premium' : planName}
214 {user.team.isTrial && ( 219 {user.team.isTrial && (
215 <> 220 <>
216 {' – '} 221 {' – '}
@@ -234,11 +239,13 @@ export default @observer class AccountDashboard extends Component {
234 </> 239 </>
235 )} 240 )}
236 <div className="manage-user-links"> 241 <div className="manage-user-links">
237 <Button 242 {!isProUser && (
238 label={intl.formatMessage(messages.upgradeAccountToPro)} 243 <Button
239 className="franz-form__button--primary" 244 label={intl.formatMessage(messages.upgradeAccountToPro)}
240 onClick={upgradeToPro} 245 className="franz-form__button--primary"
241 /> 246 onClick={upgradeToPro}
247 />
248 )}
242 <Button 249 <Button
243 label={intl.formatMessage(messages.manageSubscriptionButtonLabel)} 250 label={intl.formatMessage(messages.manageSubscriptionButtonLabel)}
244 className="franz-form__button--inverted" 251 className="franz-form__button--inverted"
@@ -290,3 +297,5 @@ export default @observer class AccountDashboard extends Component {
290 ); 297 );
291 } 298 }
292} 299}
300
301export default AccountDashboard;
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js
index 75e60b7ec..08988024a 100644
--- a/src/components/settings/recipes/RecipesDashboard.js
+++ b/src/components/settings/recipes/RecipesDashboard.js
@@ -218,7 +218,7 @@ export default @injectSheet(styles) @observer class RecipesDashboard extends Com
218 <> 218 <>
219 <H2> 219 <H2>
220 {intl.formatMessage(messages.headlineCustomRecipes)} 220 {intl.formatMessage(messages.headlineCustomRecipes)}
221 {isCommunityRecipesIncludedInCurrentPlan && ( 221 {!isCommunityRecipesIncludedInCurrentPlan && (
222 <ProBadge className={classes.proBadge} /> 222 <ProBadge className={classes.proBadge} />
223 )} 223 )}
224 </H2> 224 </H2>
@@ -247,7 +247,7 @@ export default @injectSheet(styles) @observer class RecipesDashboard extends Com
247 </> 247 </>
248 )} 248 )}
249 <PremiumFeatureContainer 249 <PremiumFeatureContainer
250 condition={(recipeFilter === 'dev' && communityRecipes.length > 0) && isCommunityRecipesIncludedInCurrentPlan} 250 condition={(recipeFilter === 'dev' && communityRecipes.length > 0) && !isCommunityRecipesIncludedInCurrentPlan}
251 > 251 >
252 {recipeFilter === 'dev' && communityRecipes.length > 0 && ( 252 {recipeFilter === 'dev' && communityRecipes.length > 0 && (
253 <H3>{intl.formatMessage(messages.headlineCommunityRecipes)}</H3> 253 <H3>{intl.formatMessage(messages.headlineCommunityRecipes)}</H3>
diff --git a/src/components/settings/team/TeamDashboard.js b/src/components/settings/team/TeamDashboard.js
index 990ee52e7..a9df980d7 100644
--- a/src/components/settings/team/TeamDashboard.js
+++ b/src/components/settings/team/TeamDashboard.js
@@ -5,10 +5,12 @@ import { defineMessages, intlShape } from 'react-intl';
5import ReactTooltip from 'react-tooltip'; 5import ReactTooltip from 'react-tooltip';
6import injectSheet from 'react-jss'; 6import injectSheet from 'react-jss';
7 7
8import { Badge } from '@meetfranz/ui';
8import Loader from '../../ui/Loader'; 9import Loader from '../../ui/Loader';
9import Button from '../../ui/Button'; 10import Button from '../../ui/Button';
10import Infobox from '../../ui/Infobox'; 11import Infobox from '../../ui/Infobox';
11import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; 12import globalMessages from '../../../i18n/globalMessages';
13import UpgradeButton from '../../ui/UpgradeButton';
12 14
13const messages = defineMessages({ 15const messages = defineMessages({
14 headline: { 16 headline: {
@@ -40,6 +42,7 @@ const messages = defineMessages({
40const styles = { 42const styles = {
41 cta: { 43 cta: {
42 margin: [40, 'auto'], 44 margin: [40, 'auto'],
45 height: 'auto',
43 }, 46 },
44 container: { 47 container: {
45 display: 'flex', 48 display: 'flex',
@@ -69,6 +72,17 @@ const styles = {
69 order: 1, 72 order: 1,
70 }, 73 },
71 }, 74 },
75 headline: {
76 marginBottom: 0,
77 },
78 proRequired: {
79 margin: [10, 0, 40],
80 height: 'auto',
81 },
82 buttonContainer: {
83 display: 'flex',
84 height: 'auto',
85 },
72}; 86};
73 87
74 88
@@ -79,6 +93,7 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon
79 retryUserInfoRequest: PropTypes.func.isRequired, 93 retryUserInfoRequest: PropTypes.func.isRequired,
80 openTeamManagement: PropTypes.func.isRequired, 94 openTeamManagement: PropTypes.func.isRequired,
81 classes: PropTypes.object.isRequired, 95 classes: PropTypes.object.isRequired,
96 isProUser: PropTypes.bool.isRequired,
82 }; 97 };
83 98
84 static contextTypes = { 99 static contextTypes = {
@@ -91,6 +106,7 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon
91 userInfoRequestFailed, 106 userInfoRequestFailed,
92 retryUserInfoRequest, 107 retryUserInfoRequest,
93 openTeamManagement, 108 openTeamManagement,
109 isProUser,
94 classes, 110 classes,
95 } = this.props; 111 } = this.props;
96 const { intl } = this.context; 112 const { intl } = this.context;
@@ -123,23 +139,35 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon
123 <> 139 <>
124 {!isLoading && ( 140 {!isLoading && (
125 <> 141 <>
126 <PremiumFeatureContainer> 142 <>
127 <> 143 <h1 className={classes.headline}>{intl.formatMessage(messages.contentHeadline)}</h1>
128 <h1>{intl.formatMessage(messages.contentHeadline)}</h1> 144 {!isProUser && (
129 <div className={classes.container}> 145 <Badge className={classes.proRequired}>{intl.formatMessage(globalMessages.proRequired)}</Badge>
130 <div className={classes.content}> 146 )}
131 <p>{intl.formatMessage(messages.intro)}</p> 147 <div className={classes.container}>
132 <p>{intl.formatMessage(messages.copy)}</p> 148 <div className={classes.content}>
133 </div> 149 <p>{intl.formatMessage(messages.intro)}</p>
134 <img className={classes.image} src="https://cdn.franzinfra.com/announcements/assets/teams.png" alt="Franz for Teams" /> 150 <p>{intl.formatMessage(messages.copy)}</p>
135 </div> 151 </div>
136 </> 152 <img className={classes.image} src="https://cdn.franzinfra.com/announcements/assets/teams.png" alt="Franz for Teams" />
137 </PremiumFeatureContainer> 153 </div>
138 <Button 154 <div className={classes.buttonContainer}>
139 label={intl.formatMessage(messages.manageButton)} 155 {!isProUser ? (
140 onClick={openTeamManagement} 156 <UpgradeButton
141 className={classes.cta} 157 className={classes.cta}
142 /> 158 gaEventInfo={{ category: 'Todos', event: 'upgrade' }}
159 requiresPro
160 short
161 />
162 ) : (
163 <Button
164 label={intl.formatMessage(messages.manageButton)}
165 onClick={openTeamManagement}
166 className={classes.cta}
167 />
168 )}
169 </div>
170 </>
143 </> 171 </>
144 )} 172 )}
145 </> 173 </>
diff --git a/src/components/subscription/TrialForm.js b/src/components/subscription/TrialForm.js
index 9fe1c93b7..9ed548f16 100644
--- a/src/components/subscription/TrialForm.js
+++ b/src/components/subscription/TrialForm.js
@@ -41,7 +41,7 @@ const messages = defineMessages({
41 }, 41 },
42}); 42});
43 43
44const styles = () => ({ 44const styles = theme => ({
45 activateTrialButton: { 45 activateTrialButton: {
46 margin: [40, 0, 10], 46 margin: [40, 0, 10],
47 }, 47 },
@@ -49,6 +49,7 @@ const styles = () => ({
49 margin: [0, 0, 40], 49 margin: [0, 0, 40],
50 background: 'none', 50 background: 'none',
51 border: 'none', 51 border: 'none',
52 color: theme.colorText,
52 }, 53 },
53 keyTerms: { 54 keyTerms: {
54 marginTop: 20, 55 marginTop: 20,
diff --git a/src/components/ui/ActivateTrialButton/index.js b/src/components/ui/ActivateTrialButton/index.js
new file mode 100644
index 000000000..c3e5f4a6f
--- /dev/null
+++ b/src/components/ui/ActivateTrialButton/index.js
@@ -0,0 +1,115 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { inject, observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl';
5import classnames from 'classnames';
6
7import { Button } from '@meetfranz/forms';
8import { gaEvent } from '../../../lib/analytics';
9
10import UserStore from '../../../stores/UserStore';
11
12const messages = defineMessages({
13 action: {
14 id: 'feature.delayApp.upgrade.action',
15 defaultMessage: '!!!Get a Franz Supporter License',
16 },
17 actionTrial: {
18 id: 'feature.delayApp.trial.action',
19 defaultMessage: '!!!Yes, I want the free 14 day trial of Franz Professional',
20 },
21 shortAction: {
22 id: 'feature.delayApp.upgrade.actionShort',
23 defaultMessage: '!!!Upgrade account',
24 },
25 shortActionTrial: {
26 id: 'feature.delayApp.trial.actionShort',
27 defaultMessage: '!!!Activate the free Franz Professional trial',
28 },
29});
30
31@inject('stores', 'actions') @observer
32class ActivateTrialButton extends Component {
33 static propTypes = {
34 // eslint-disable-next-line
35 classes: PropTypes.object.isRequired,
36 className: PropTypes.string,
37 short: PropTypes.bool,
38 gaEventInfo: PropTypes.shape({
39 category: PropTypes.string.isRequired,
40 event: PropTypes.string.isRequired,
41 label: PropTypes.string,
42 }),
43 };
44
45 static defaultProps = {
46 className: '',
47 short: false,
48 gaEventInfo: null,
49 }
50
51 static contextTypes = {
52 intl: intlShape,
53 };
54
55 handleCTAClick() {
56 const { actions, stores, gaEventInfo } = this.props;
57 const { hadSubscription } = stores.user.data;
58 const { defaultTrialPlan } = stores.features.features;
59
60 let label = '';
61 if (!hadSubscription) {
62 actions.user.activateTrial({ planId: defaultTrialPlan });
63
64 label = 'Start Trial';
65 } else {
66 actions.ui.openSettings({ path: 'user' });
67
68 label = 'Upgrade Account';
69 }
70
71 if (gaEventInfo) {
72 const { category, event } = gaEventInfo;
73 gaEvent(category, event, label);
74 }
75 }
76
77 render() {
78 const { stores, className, short } = this.props;
79 const { intl } = this.context;
80
81 const { hadSubscription } = stores.user.data;
82
83 let label;
84 if (hadSubscription) {
85 label = short ? messages.shortAction : messages.action;
86 } else {
87 label = short ? messages.shortActionTrial : messages.actionTrial;
88 }
89
90 return (
91 <Button
92 label={intl.formatMessage(label)}
93 className={classnames({
94 [className]: className,
95 })}
96 buttonType="inverted"
97 onClick={this.handleCTAClick.bind(this)}
98 busy={stores.user.activateTrialRequest.isExecuting}
99 />
100 );
101 }
102}
103
104export default ActivateTrialButton;
105
106ActivateTrialButton.wrappedComponent.propTypes = {
107 stores: PropTypes.shape({
108 user: PropTypes.instanceOf(UserStore).isRequired,
109 }).isRequired,
110 actions: PropTypes.shape({
111 ui: PropTypes.shape({
112 openSettings: PropTypes.func.isRequired,
113 }).isRequired,
114 }).isRequired,
115};
diff --git a/src/components/ui/FeatureItem.js b/src/components/ui/FeatureItem.js
index 53616f2eb..7c482c4d4 100644
--- a/src/components/ui/FeatureItem.js
+++ b/src/components/ui/FeatureItem.js
@@ -6,7 +6,7 @@ import { mdiCheckCircle } from '@mdi/js';
6 6
7const styles = theme => ({ 7const styles = theme => ({
8 featureItem: { 8 featureItem: {
9 borderBottom: [1, 'solid', theme.legacyStyles.themeGrayDark], 9 borderBottom: [1, 'solid', theme.defaultContentBorder],
10 padding: [8, 0], 10 padding: [8, 0],
11 display: 'flex', 11 display: 'flex',
12 alignItems: 'center', 12 alignItems: 'center',
diff --git a/src/components/ui/PremiumFeatureContainer/index.js b/src/components/ui/PremiumFeatureContainer/index.js
index 3c1e0fac3..8d2746e22 100644
--- a/src/components/ui/PremiumFeatureContainer/index.js
+++ b/src/components/ui/PremiumFeatureContainer/index.js
@@ -10,6 +10,7 @@ import UserStore from '../../../stores/UserStore';
10 10
11import styles from './styles'; 11import styles from './styles';
12import { gaEvent } from '../../../lib/analytics'; 12import { gaEvent } from '../../../lib/analytics';
13import { FeatureStore } from '../../../features/utils/FeatureStore';
13 14
14const messages = defineMessages({ 15const messages = defineMessages({
15 action: { 16 action: {
@@ -22,7 +23,10 @@ const messages = defineMessages({
22class PremiumFeatureContainer extends Component { 23class PremiumFeatureContainer extends Component {
23 static propTypes = { 24 static propTypes = {
24 classes: PropTypes.object.isRequired, 25 classes: PropTypes.object.isRequired,
25 condition: PropTypes.bool, 26 condition: PropTypes.oneOfType([
27 PropTypes.bool,
28 PropTypes.func,
29 ]),
26 gaEventInfo: PropTypes.shape({ 30 gaEventInfo: PropTypes.shape({
27 category: PropTypes.string.isRequired, 31 category: PropTypes.string.isRequired,
28 event: PropTypes.string.isRequired, 32 event: PropTypes.string.isRequired,
@@ -31,7 +35,7 @@ class PremiumFeatureContainer extends Component {
31 }; 35 };
32 36
33 static defaultProps = { 37 static defaultProps = {
34 condition: true, 38 condition: null,
35 gaEventInfo: null, 39 gaEventInfo: null,
36 }; 40 };
37 41
@@ -51,7 +55,18 @@ class PremiumFeatureContainer extends Component {
51 55
52 const { intl } = this.context; 56 const { intl } = this.context;
53 57
54 return !stores.user.data.isPremium && !!condition ? ( 58 let showWrapper = !!condition;
59
60 if (condition === null) {
61 showWrapper = !stores.user.data.isPremium;
62 } else if (typeof condition === 'function') {
63 showWrapper = condition({
64 isPremium: stores.user.data.isPremium,
65 features: stores.features.features,
66 });
67 }
68
69 return showWrapper ? (
55 <div className={classes.container}> 70 <div className={classes.container}>
56 <div className={classes.titleContainer}> 71 <div className={classes.titleContainer}>
57 <p className={classes.title}>Premium Feature</p> 72 <p className={classes.title}>Premium Feature</p>
@@ -81,6 +96,7 @@ PremiumFeatureContainer.wrappedComponent.propTypes = {
81 children: oneOrManyChildElements.isRequired, 96 children: oneOrManyChildElements.isRequired,
82 stores: PropTypes.shape({ 97 stores: PropTypes.shape({
83 user: PropTypes.instanceOf(UserStore).isRequired, 98 user: PropTypes.instanceOf(UserStore).isRequired,
99 features: PropTypes.instanceOf(FeatureStore).isRequired,
84 }).isRequired, 100 }).isRequired,
85 actions: PropTypes.shape({ 101 actions: PropTypes.shape({
86 ui: PropTypes.shape({ 102 ui: PropTypes.shape({
diff --git a/src/components/ui/UpgradeButton/index.js b/src/components/ui/UpgradeButton/index.js
new file mode 100644
index 000000000..73762f0bf
--- /dev/null
+++ b/src/components/ui/UpgradeButton/index.js
@@ -0,0 +1,89 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { inject, observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl';
5
6import { Button } from '@meetfranz/forms';
7import { gaEvent } from '../../../lib/analytics';
8
9import UserStore from '../../../stores/UserStore';
10import ActivateTrialButton from '../ActivateTrialButton';
11
12const messages = defineMessages({
13 upgradeToPro: {
14 id: 'global.upgradeButton.upgradeToPro',
15 defaultMessage: '!!!Upgrade to Franz Professional',
16 },
17});
18
19@inject('stores', 'actions') @observer
20class UpgradeButton extends Component {
21 static propTypes = {
22 // eslint-disable-next-line
23 classes: PropTypes.object.isRequired,
24 className: PropTypes.string,
25 gaEventInfo: PropTypes.shape({
26 category: PropTypes.string.isRequired,
27 event: PropTypes.string.isRequired,
28 label: PropTypes.string,
29 }),
30 requiresPro: PropTypes.bool,
31 };
32
33 static defaultProps = {
34 className: '',
35 gaEventInfo: null,
36 requiresPro: false,
37 }
38
39 static contextTypes = {
40 intl: intlShape,
41 };
42
43 handleCTAClick() {
44 const { actions, gaEventInfo } = this.props;
45
46 actions.ui.openSettings({ path: 'user' });
47 if (gaEventInfo) {
48 const { category, event } = gaEventInfo;
49 gaEvent(category, event, 'Upgrade Account');
50 }
51 }
52
53 render() {
54 const { stores, requiresPro } = this.props;
55 const { intl } = this.context;
56
57 const { isPremium, isPersonal } = stores.user;
58
59 if (isPremium && isPersonal && requiresPro) {
60 return (
61 <Button
62 label={intl.formatMessage(messages.upgradeToPro)}
63 onClick={this.handleCTAClick.bind(this)}
64 className={this.props.className}
65 buttonType="inverted"
66 />
67 );
68 }
69
70 if (!isPremium) {
71 return <ActivateTrialButton {...this.props} />;
72 }
73
74 return null;
75 }
76}
77
78export default UpgradeButton;
79
80UpgradeButton.wrappedComponent.propTypes = {
81 stores: PropTypes.shape({
82 user: PropTypes.instanceOf(UserStore).isRequired,
83 }).isRequired,
84 actions: PropTypes.shape({
85 ui: PropTypes.shape({
86 openSettings: PropTypes.func.isRequired,
87 }).isRequired,
88 }).isRequired,
89};
diff --git a/src/config.js b/src/config.js
index 405cc5253..78a92d948 100644
--- a/src/config.js
+++ b/src/config.js
@@ -20,7 +20,7 @@ export const LIVE_WS_API = 'wss://api.franzinfra.com';
20 20
21export const LOCAL_API_WEBSITE = 'http://localhost:3333'; 21export const LOCAL_API_WEBSITE = 'http://localhost:3333';
22// export const DEV_API_WEBSITE = 'https://meetfranz.com';t 22// export const DEV_API_WEBSITE = 'https://meetfranz.com';t
23export const DEV_API_WEBSITE = 'http://hash-3ac3ccd2472269cf585c58a4f6973d86f3c9e7bd.franzstaging.com/'; // TODO: revert me 23export const DEV_API_WEBSITE = 'http://hash-58883791519ef6288c952316bdce7fb462283893.franzstaging.com/'; // TODO: revert me
24export const LIVE_API_WEBSITE = 'https://meetfranz.com'; 24export const LIVE_API_WEBSITE = 'https://meetfranz.com';
25 25
26export const STATS_API = 'https://stats.franzinfra.com'; 26export const STATS_API = 'https://stats.franzinfra.com';
@@ -91,8 +91,8 @@ export const ALLOWED_PROTOCOLS = [
91]; 91];
92 92
93export const PLANS = { 93export const PLANS = {
94 PERSONAL: 'PERSONAL_MONTHLY', 94 PERSONAL: 'PERSONAL',
95 PRO: 'PRO_MONTHLY', 95 PRO: 'PRO',
96 LEGACY: 'LEGACY', 96 LEGACY: 'LEGACY',
97 FREE: 'FREE', 97 FREE: 'FREE',
98}; 98};
diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js
index f9eae4957..9c74cf2ab 100644
--- a/src/containers/settings/AccountScreen.js
+++ b/src/containers/settings/AccountScreen.js
@@ -42,6 +42,8 @@ export default @inject('stores', 'actions') @observer class AccountScreen extend
42 <ErrorBoundary> 42 <ErrorBoundary>
43 <AccountDashboard 43 <AccountDashboard
44 user={user.data} 44 user={user.data}
45 isPremiumOverrideUser={user.isPremiumOverride}
46 isProUser={user.isPro}
45 isLoading={isLoadingUserInfo} 47 isLoading={isLoadingUserInfo}
46 isLoadingPlans={isLoadingPlans} 48 isLoadingPlans={isLoadingPlans}
47 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError} 49 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError}
diff --git a/src/containers/settings/TeamScreen.js b/src/containers/settings/TeamScreen.js
index b7b1b78cb..f600c9947 100644
--- a/src/containers/settings/TeamScreen.js
+++ b/src/containers/settings/TeamScreen.js
@@ -30,6 +30,7 @@ export default @inject('stores', 'actions') @observer class TeamScreen extends C
30 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError} 30 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError}
31 retryUserInfoRequest={() => this.reloadData()} 31 retryUserInfoRequest={() => this.reloadData()}
32 openTeamManagement={() => this.handleWebsiteLink('/user/team')} 32 openTeamManagement={() => this.handleWebsiteLink('/user/team')}
33 isProUser={user.isPro}
33 /> 34 />
34 </ErrorBoundary> 35 </ErrorBoundary>
35 ); 36 );
diff --git a/src/containers/subscription/SubscriptionFormScreen.js b/src/containers/subscription/SubscriptionFormScreen.js
index e9e457084..726b10628 100644
--- a/src/containers/subscription/SubscriptionFormScreen.js
+++ b/src/containers/subscription/SubscriptionFormScreen.js
@@ -19,7 +19,7 @@ export default @inject('stores', 'actions') @observer class SubscriptionFormScre
19 features, 19 features,
20 } = stores; 20 } = stores;
21 21
22 let hostedPageURL = !user.data.hadSubscription ? features.features.planSelectionURL : features.features.subscribeURL; 22 let hostedPageURL = features.features.planSelectionURL;
23 hostedPageURL = user.getAuthURL(hostedPageURL); 23 hostedPageURL = user.getAuthURL(hostedPageURL);
24 24
25 actions.app.openExternalUrl({ url: hostedPageURL }); 25 actions.app.openExternalUrl({ url: hostedPageURL });
diff --git a/src/environment.js b/src/environment.js
index 707449e09..4be2c7d70 100644
--- a/src/environment.js
+++ b/src/environment.js
@@ -37,8 +37,10 @@ let web;
37let todos; 37let todos;
38if (!isDevMode || (isDevMode && useLiveAPI)) { 38if (!isDevMode || (isDevMode && useLiveAPI)) {
39 api = LIVE_API; 39 api = LIVE_API;
40 // api = DEV_API;
40 wsApi = LIVE_WS_API; 41 wsApi = LIVE_WS_API;
41 web = LIVE_API_WEBSITE; 42 web = LIVE_API_WEBSITE;
43 // web = DEV_API_WEBSITE;
42 todos = PRODUCTION_TODOS_FRONTEND_URL; 44 todos = PRODUCTION_TODOS_FRONTEND_URL;
43} else if (isDevMode && useLocalAPI) { 45} else if (isDevMode && useLocalAPI) {
44 api = LOCAL_API; 46 api = LOCAL_API;
diff --git a/src/features/shareFranz/Component.js b/src/features/shareFranz/Component.js
index 859c0ebe9..a33315e17 100644
--- a/src/features/shareFranz/Component.js
+++ b/src/features/shareFranz/Component.js
@@ -6,7 +6,9 @@ import { defineMessages, intlShape } from 'react-intl';
6import { Button } from '@meetfranz/forms'; 6import { Button } from '@meetfranz/forms';
7import { H1, Icon } from '@meetfranz/ui'; 7import { H1, Icon } from '@meetfranz/ui';
8 8
9import { mdiHeart } from '@mdi/js'; 9import {
10 mdiHeart, mdiEmail, mdiFacebookBox, mdiTwitter,
11} from '@mdi/js';
10import Modal from '../../components/ui/Modal'; 12import Modal from '../../components/ui/Modal';
11import { state } from '.'; 13import { state } from '.';
12import { gaEvent } from '../../lib/analytics'; 14import { gaEvent } from '../../lib/analytics';
@@ -76,7 +78,7 @@ const styles = theme => ({
76 }, 78 },
77 cta: { 79 cta: {
78 background: theme.styleTypes.primary.contrast, 80 background: theme.styleTypes.primary.contrast,
79 color: theme.styleTypes.primary.accent, 81 color: `${theme.styleTypes.primary.accent} !important`,
80 82
81 '& svg': { 83 '& svg': {
82 fill: theme.styleTypes.primary.accent, 84 fill: theme.styleTypes.primary.accent,
@@ -127,7 +129,7 @@ export default @injectSheet(styles) @inject('stores') @observer class ShareFranz
127 <Button 129 <Button
128 label={intl.formatMessage(messages.actionsEmail)} 130 label={intl.formatMessage(messages.actionsEmail)}
129 className={classes.cta} 131 className={classes.cta}
130 icon="mdiEmail" 132 icon={mdiEmail}
131 href={`mailto:?subject=Meet the cool app Franz&body=${intl.formatMessage(messages.shareTextEmail, { count: serviceCount })}}`} 133 href={`mailto:?subject=Meet the cool app Franz&body=${intl.formatMessage(messages.shareTextEmail, { count: serviceCount })}}`}
132 target="_blank" 134 target="_blank"
133 onClick={() => { 135 onClick={() => {
@@ -137,7 +139,7 @@ export default @injectSheet(styles) @inject('stores') @observer class ShareFranz
137 <Button 139 <Button
138 label={intl.formatMessage(messages.actionsFacebook)} 140 label={intl.formatMessage(messages.actionsFacebook)}
139 className={classes.cta} 141 className={classes.cta}
140 icon="mdiFacebookBox" 142 icon={mdiFacebookBox}
141 href="https://www.facebook.com/sharer/sharer.php?u=https://www.meetfranz.com?utm_source=facebook&utm_medium=referral&utm_campaign=share-button" 143 href="https://www.facebook.com/sharer/sharer.php?u=https://www.meetfranz.com?utm_source=facebook&utm_medium=referral&utm_campaign=share-button"
142 target="_blank" 144 target="_blank"
143 onClick={() => { 145 onClick={() => {
@@ -147,7 +149,7 @@ export default @injectSheet(styles) @inject('stores') @observer class ShareFranz
147 <Button 149 <Button
148 label={intl.formatMessage(messages.actionsTwitter)} 150 label={intl.formatMessage(messages.actionsTwitter)}
149 className={classes.cta} 151 className={classes.cta}
150 icon="mdiTwitter" 152 icon={mdiTwitter}
151 href={`http://twitter.com/intent/tweet?status=${intl.formatMessage(messages.shareTextTwitter, { count: serviceCount })}`} 153 href={`http://twitter.com/intent/tweet?status=${intl.formatMessage(messages.shareTextTwitter, { count: serviceCount })}`}
152 target="_blank" 154 target="_blank"
153 onClick={() => { 155 onClick={() => {
diff --git a/src/features/todos/components/TodosWebview.js b/src/features/todos/components/TodosWebview.js
index d8d96ba85..fa530daed 100644
--- a/src/features/todos/components/TodosWebview.js
+++ b/src/features/todos/components/TodosWebview.js
@@ -7,9 +7,9 @@ import { Icon } from '@meetfranz/ui';
7import { defineMessages, intlShape } from 'react-intl'; 7import { defineMessages, intlShape } from 'react-intl';
8 8
9import { mdiChevronRight, mdiCheckAll } from '@mdi/js'; 9import { mdiChevronRight, mdiCheckAll } from '@mdi/js';
10import { Button } from '@meetfranz/forms';
11import * as environment from '../../../environment'; 10import * as environment from '../../../environment';
12import Appear from '../../../components/ui/effects/Appear'; 11import Appear from '../../../components/ui/effects/Appear';
12import UpgradeButton from '../../../components/ui/UpgradeButton';
13 13
14const OPEN_TODOS_BUTTON_SIZE = 45; 14const OPEN_TODOS_BUTTON_SIZE = 45;
15const CLOSE_TODOS_BUTTON_SIZE = 35; 15const CLOSE_TODOS_BUTTON_SIZE = 35;
@@ -70,7 +70,7 @@ const styles = theme => ({
70 height: OPEN_TODOS_BUTTON_SIZE, 70 height: OPEN_TODOS_BUTTON_SIZE,
71 background: theme.todos.toggleButton.background, 71 background: theme.todos.toggleButton.background,
72 position: 'absolute', 72 position: 'absolute',
73 bottom: 80, 73 bottom: 120,
74 right: props => (props.width + (props.isVisible ? -OPEN_TODOS_BUTTON_SIZE / 2 : 0)), 74 right: props => (props.width + (props.isVisible ? -OPEN_TODOS_BUTTON_SIZE / 2 : 0)),
75 borderRadius: OPEN_TODOS_BUTTON_SIZE / 2, 75 borderRadius: OPEN_TODOS_BUTTON_SIZE / 2,
76 opacity: props => (props.isVisible ? 0 : 1), 76 opacity: props => (props.isVisible ? 0 : 1),
@@ -94,7 +94,7 @@ const styles = theme => ({
94 height: CLOSE_TODOS_BUTTON_SIZE, 94 height: CLOSE_TODOS_BUTTON_SIZE,
95 background: theme.todos.toggleButton.background, 95 background: theme.todos.toggleButton.background,
96 position: 'absolute', 96 position: 'absolute',
97 bottom: 80, 97 bottom: 120,
98 right: ({ width }) => (width + -CLOSE_TODOS_BUTTON_SIZE / 2), 98 right: ({ width }) => (width + -CLOSE_TODOS_BUTTON_SIZE / 2),
99 borderRadius: CLOSE_TODOS_BUTTON_SIZE / 2, 99 borderRadius: CLOSE_TODOS_BUTTON_SIZE / 2,
100 opacity: ({ isTodosIncludedInCurrentPlan }) => (!isTodosIncludedInCurrentPlan ? 1 : 0), 100 opacity: ({ isTodosIncludedInCurrentPlan }) => (!isTodosIncludedInCurrentPlan ? 1 : 0),
@@ -116,7 +116,7 @@ const styles = theme => ({
116 alignItems: 'center', 116 alignItems: 'center',
117 width: '80%', 117 width: '80%',
118 maxWidth: 300, 118 maxWidth: 300,
119 margin: [-50, 'auto', 0], 119 margin: [0, 'auto'],
120 textAlign: 'center', 120 textAlign: 'center',
121 }, 121 },
122 premiumIcon: { 122 premiumIcon: {
@@ -143,7 +143,6 @@ class TodosWebview extends Component {
143 width: PropTypes.number.isRequired, 143 width: PropTypes.number.isRequired,
144 minWidth: PropTypes.number.isRequired, 144 minWidth: PropTypes.number.isRequired,
145 isTodosIncludedInCurrentPlan: PropTypes.bool.isRequired, 145 isTodosIncludedInCurrentPlan: PropTypes.bool.isRequired,
146 upgradeAccount: PropTypes.func.isRequired,
147 }; 146 };
148 147
149 state = { 148 state = {
@@ -235,7 +234,6 @@ class TodosWebview extends Component {
235 isVisible, 234 isVisible,
236 togglePanel, 235 togglePanel,
237 isTodosIncludedInCurrentPlan, 236 isTodosIncludedInCurrentPlan,
238 upgradeAccount,
239 } = this.props; 237 } = this.props;
240 238
241 const { 239 const {
@@ -288,14 +286,13 @@ class TodosWebview extends Component {
288 ) : ( 286 ) : (
289 <Appear> 287 <Appear>
290 <div className={classes.premiumContainer}> 288 <div className={classes.premiumContainer}>
291 <Icon icon={mdiCheckAll} className={classes.premiumIcon} size={5} /> 289 <Icon icon={mdiCheckAll} className={classes.premiumIcon} size={4} />
292 <p>{intl.formatMessage(messages.premiumInfo)}</p> 290 <p>{intl.formatMessage(messages.premiumInfo)}</p>
293 <p>{intl.formatMessage(messages.rolloutInfo)}</p> 291 <p>{intl.formatMessage(messages.rolloutInfo)}</p>
294 <Button 292 <UpgradeButton
295 label={intl.formatMessage(messages.upgradeCTA)}
296 className={classes.premiumCTA} 293 className={classes.premiumCTA}
297 onClick={upgradeAccount} 294 gaEventInfo={{ category: 'Todos', event: 'upgrade' }}
298 buttonType="inverted" 295 short
299 /> 296 />
300 </div> 297 </div>
301 </Appear> 298 </Appear>
diff --git a/src/features/todos/containers/TodosScreen.js b/src/features/todos/containers/TodosScreen.js
index 7f3688828..65afc985b 100644
--- a/src/features/todos/containers/TodosScreen.js
+++ b/src/features/todos/containers/TodosScreen.js
@@ -26,7 +26,6 @@ class TodosScreen extends Component {
26 minWidth={TODOS_MIN_WIDTH} 26 minWidth={TODOS_MIN_WIDTH}
27 resize={width => todoActions.resize({ width })} 27 resize={width => todoActions.resize({ width })}
28 isTodosIncludedInCurrentPlan={this.props.stores.features.features.isTodosIncludedInCurrentPlan || false} 28 isTodosIncludedInCurrentPlan={this.props.stores.features.features.isTodosIncludedInCurrentPlan || false}
29 upgradeAccount={() => this.props.actions.ui.openSettings({ path: 'user' })}
30 /> 29 />
31 </ErrorBoundary> 30 </ErrorBoundary>
32 ); 31 );
@@ -39,9 +38,4 @@ TodosScreen.wrappedComponent.propTypes = {
39 stores: PropTypes.shape({ 38 stores: PropTypes.shape({
40 features: PropTypes.instanceOf(FeaturesStore).isRequired, 39 features: PropTypes.instanceOf(FeaturesStore).isRequired,
41 }).isRequired, 40 }).isRequired,
42 actions: PropTypes.shape({
43 ui: PropTypes.shape({
44 openSettings: PropTypes.func.isRequired,
45 }).isRequired,
46 }).isRequired,
47}; 41};
diff --git a/src/features/todos/store.js b/src/features/todos/store.js
index 7da3b7f49..5c6abff4c 100644
--- a/src/features/todos/store.js
+++ b/src/features/todos/store.js
@@ -12,6 +12,7 @@ import { createReactions } from '../../stores/lib/Reaction';
12import { createActionBindings } from '../utils/ActionBinding'; 12import { createActionBindings } from '../utils/ActionBinding';
13import { DEFAULT_TODOS_WIDTH, TODOS_MIN_WIDTH, DEFAULT_TODOS_VISIBLE } from '.'; 13import { DEFAULT_TODOS_WIDTH, TODOS_MIN_WIDTH, DEFAULT_TODOS_VISIBLE } from '.';
14import { IPC } from './constants'; 14import { IPC } from './constants';
15import { state as delayAppState } from '../delayApp';
15 16
16const debug = require('debug')('Franz:feature:todos:store'); 17const debug = require('debug')('Franz:feature:todos:store');
17 18
@@ -29,7 +30,7 @@ export default class TodoStore extends FeatureStore {
29 } 30 }
30 31
31 @computed get isTodosPanelVisible() { 32 @computed get isTodosPanelVisible() {
32 if (this.stores.services.all.length === 0) return false; 33 if (delayAppState.isDelayAppScreenVisible) return false;
33 if (this.settings.isTodosPanelVisible === undefined) return DEFAULT_TODOS_VISIBLE; 34 if (this.settings.isTodosPanelVisible === undefined) return DEFAULT_TODOS_VISIBLE;
34 35
35 return this.settings.isTodosPanelVisible; 36 return this.settings.isTodosPanelVisible;
@@ -60,6 +61,7 @@ export default class TodoStore extends FeatureStore {
60 61
61 this._allReactions = createReactions([ 62 this._allReactions = createReactions([
62 this._setFeatureEnabledReaction, 63 this._setFeatureEnabledReaction,
64 this._firstLaunchReaction,
63 ]); 65 ]);
64 66
65 this._registerReactions(this._allReactions); 67 this._registerReactions(this._allReactions);
@@ -145,4 +147,19 @@ export default class TodoStore extends FeatureStore {
145 147
146 this.isFeatureEnabled = isTodosEnabled; 148 this.isFeatureEnabled = isTodosEnabled;
147 }; 149 };
150
151 _firstLaunchReaction = () => {
152 const { stats } = this.stores.settings.all;
153
154 // Hide todos layer on first app start but show on second
155 if (stats.appStarts <= 1) {
156 this._updateSettings({
157 isTodosPanelVisible: false,
158 });
159 } else if (stats.appStarts <= 2) {
160 this._updateSettings({
161 isTodosPanelVisible: true,
162 });
163 }
164 };
148} 165}
diff --git a/src/features/workspaces/components/WorkspacesDashboard.js b/src/features/workspaces/components/WorkspacesDashboard.js
index 09c98ab8c..70e213912 100644
--- a/src/features/workspaces/components/WorkspacesDashboard.js
+++ b/src/features/workspaces/components/WorkspacesDashboard.js
@@ -1,9 +1,9 @@
1import React, { Component, Fragment } from 'react'; 1import React, { Component, Fragment } 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, inject } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, intlShape } from 'react-intl';
5import injectSheet from 'react-jss'; 5import injectSheet from 'react-jss';
6import { Infobox } from '@meetfranz/ui'; 6import { Infobox, Badge } from '@meetfranz/ui';
7 7
8import Loader from '../../../components/ui/Loader'; 8import Loader from '../../../components/ui/Loader';
9import WorkspaceItem from './WorkspaceItem'; 9import WorkspaceItem from './WorkspaceItem';
@@ -11,7 +11,9 @@ import CreateWorkspaceForm from './CreateWorkspaceForm';
11import Request from '../../../stores/lib/Request'; 11import Request from '../../../stores/lib/Request';
12import Appear from '../../../components/ui/effects/Appear'; 12import Appear from '../../../components/ui/effects/Appear';
13import { workspaceStore } from '../index'; 13import { workspaceStore } from '../index';
14import PremiumFeatureContainer from '../../../components/ui/PremiumFeatureContainer'; 14import UIStore from '../../../stores/UIStore';
15import globalMessages from '../../../i18n/globalMessages';
16import UpgradeButton from '../../../components/ui/UpgradeButton';
15 17
16const messages = defineMessages({ 18const messages = defineMessages({
17 headline: { 19 headline: {
@@ -48,7 +50,7 @@ const messages = defineMessages({
48 }, 50 },
49}); 51});
50 52
51const styles = theme => ({ 53const styles = () => ({
52 table: { 54 table: {
53 width: '100%', 55 width: '100%',
54 '& td': { 56 '& td': {
@@ -62,17 +64,28 @@ const styles = theme => ({
62 height: 'auto', 64 height: 'auto',
63 }, 65 },
64 premiumAnnouncement: { 66 premiumAnnouncement: {
65 padding: '20px',
66 backgroundColor: '#3498db',
67 marginLeft: '-20px',
68 marginBottom: '20px',
69 height: 'auto', 67 height: 'auto',
70 color: 'white', 68 },
71 borderRadius: theme.borderRadius, 69 premiumAnnouncementContainer: {
70 display: 'flex',
71 },
72 announcementHeadline: {
73 marginBottom: 0,
74 },
75 teaserImage: {
76 width: 250,
77 margin: [-8, 0, 0, 20],
78 alignSelf: 'center',
79 },
80 upgradeCTA: {
81 margin: [40, 'auto'],
82 },
83 proRequired: {
84 margin: [10, 0, 40],
72 }, 85 },
73}); 86});
74 87
75@injectSheet(styles) @observer 88@inject('stores') @injectSheet(styles) @observer
76class WorkspacesDashboard extends Component { 89class WorkspacesDashboard extends Component {
77 static propTypes = { 90 static propTypes = {
78 classes: PropTypes.object.isRequired, 91 classes: PropTypes.object.isRequired,
@@ -100,7 +113,9 @@ class WorkspacesDashboard extends Component {
100 onWorkspaceClick, 113 onWorkspaceClick,
101 workspaces, 114 workspaces,
102 } = this.props; 115 } = this.props;
116
103 const { intl } = this.context; 117 const { intl } = this.context;
118
104 return ( 119 return (
105 <div className="settings__main"> 120 <div className="settings__main">
106 <div className="settings__header"> 121 <div className="settings__header">
@@ -138,68 +153,80 @@ class WorkspacesDashboard extends Component {
138 153
139 {workspaceStore.isPremiumUpgradeRequired && ( 154 {workspaceStore.isPremiumUpgradeRequired && (
140 <div className={classes.premiumAnnouncement}> 155 <div className={classes.premiumAnnouncement}>
141 <h2>{intl.formatMessage(messages.workspaceFeatureHeadline)}</h2> 156
142 <p>{intl.formatMessage(messages.workspaceFeatureInfo)}</p> 157 <h1 className={classes.announcementHeadline}>{intl.formatMessage(messages.workspaceFeatureHeadline)}</h1>
158 <Badge className={classes.proRequired}>{intl.formatMessage(globalMessages.proRequired)}</Badge>
159 <div className={classes.premiumAnnouncementContainer}>
160 <div className={classes.premiumAnnouncementContent}>
161 <p>{intl.formatMessage(messages.workspaceFeatureInfo)}</p>
162 <UpgradeButton
163 className={classes.upgradeCTA}
164 gaEventInfo={{ category: 'Workspaces', event: 'upgrade' }}
165 short
166 requiresPro
167 />
168 </div>
169 <img src={`https://cdn.franzinfra.com/announcements/assets/workspaces_${this.props.stores.ui.isDarkThemeActive ? 'dark' : 'light'}.png`} className={classes.teaserImage} alt="" />
170 </div>
143 </div> 171 </div>
144 )} 172 )}
145 173
146 <PremiumFeatureContainer 174 {!workspaceStore.isPremiumUpgradeRequired && (
147 condition={workspaceStore.isPremiumFeature} 175 <>
148 gaEventInfo={{ category: 'User', event: 'upgrade', label: 'workspaces' }} 176 {/* ===== Create workspace form ===== */}
149 > 177 <div className={classes.createForm}>
150 {/* ===== Create workspace form ===== */} 178 <CreateWorkspaceForm
151 <div className={classes.createForm}> 179 isSubmitting={createWorkspaceRequest.isExecuting}
152 <CreateWorkspaceForm 180 onSubmit={onCreateWorkspaceSubmit}
153 isSubmitting={createWorkspaceRequest.isExecuting} 181 />
154 onSubmit={onCreateWorkspaceSubmit} 182 </div>
155 /> 183 {getUserWorkspacesRequest.isExecuting ? (
156 </div> 184 <Loader />
157 {getUserWorkspacesRequest.isExecuting ? ( 185 ) : (
158 <Loader /> 186 <Fragment>
159 ) : ( 187 {/* ===== Workspace could not be loaded error ===== */}
160 <Fragment> 188 {getUserWorkspacesRequest.error ? (
161 {/* ===== Workspace could not be loaded error ===== */} 189 <Infobox
162 {getUserWorkspacesRequest.error ? ( 190 icon="alert"
163 <Infobox 191 type="danger"
164 icon="alert" 192 ctaLabel={intl.formatMessage(messages.tryReloadWorkspaces)}
165 type="danger" 193 ctaLoading={getUserWorkspacesRequest.isExecuting}
166 ctaLabel={intl.formatMessage(messages.tryReloadWorkspaces)} 194 ctaOnClick={getUserWorkspacesRequest.retry}
167 ctaLoading={getUserWorkspacesRequest.isExecuting} 195 >
168 ctaOnClick={getUserWorkspacesRequest.retry} 196 {intl.formatMessage(messages.workspacesRequestFailed)}
169 > 197 </Infobox>
170 {intl.formatMessage(messages.workspacesRequestFailed)} 198 ) : (
171 </Infobox> 199 <Fragment>
172 ) : ( 200 {workspaces.length === 0 ? (
173 <Fragment> 201 <div className="align-middle settings__empty-state">
174 {workspaces.length === 0 ? ( 202 {/* ===== Workspaces empty state ===== */}
175 <div className="align-middle settings__empty-state"> 203 <p className="settings__empty-text">
176 {/* ===== Workspaces empty state ===== */} 204 <span className="emoji">
177 <p className="settings__empty-text"> 205 <img src="./assets/images/emoji/sad.png" alt="" />
178 <span className="emoji"> 206 </span>
179 <img src="./assets/images/emoji/sad.png" alt="" /> 207 {intl.formatMessage(messages.noServicesAdded)}
180 </span> 208 </p>
181 {intl.formatMessage(messages.noServicesAdded)} 209 </div>
182 </p> 210 ) : (
183 </div> 211 <table className={classes.table}>
184 ) : ( 212 {/* ===== Workspaces list ===== */}
185 <table className={classes.table}> 213 <tbody>
186 {/* ===== Workspaces list ===== */} 214 {workspaces.map(workspace => (
187 <tbody> 215 <WorkspaceItem
188 {workspaces.map(workspace => ( 216 key={workspace.id}
189 <WorkspaceItem 217 workspace={workspace}
190 key={workspace.id} 218 onItemClick={w => onWorkspaceClick(w)}
191 workspace={workspace} 219 />
192 onItemClick={w => onWorkspaceClick(w)} 220 ))}
193 /> 221 </tbody>
194 ))} 222 </table>
195 </tbody> 223 )}
196 </table> 224 </Fragment>
197 )} 225 )}
198 </Fragment> 226 </Fragment>
199 )} 227 )}
200 </Fragment> 228 </>
201 )} 229 )}
202 </PremiumFeatureContainer>
203 </div> 230 </div>
204 </div> 231 </div>
205 ); 232 );
@@ -207,3 +234,9 @@ class WorkspacesDashboard extends Component {
207} 234}
208 235
209export default WorkspacesDashboard; 236export default WorkspacesDashboard;
237
238WorkspacesDashboard.wrappedComponent.propTypes = {
239 stores: PropTypes.shape({
240 ui: PropTypes.instanceOf(UIStore).isRequired,
241 }).isRequired,
242};
diff --git a/src/features/workspaces/containers/WorkspacesScreen.js b/src/features/workspaces/containers/WorkspacesScreen.js
index 2ab565fa1..affbd230d 100644
--- a/src/features/workspaces/containers/WorkspacesScreen.js
+++ b/src/features/workspaces/containers/WorkspacesScreen.js
@@ -11,7 +11,7 @@ import {
11 updateWorkspaceRequest, 11 updateWorkspaceRequest,
12} from '../api'; 12} from '../api';
13 13
14@inject('actions') @observer 14@inject('stores', 'actions') @observer
15class WorkspacesScreen extends Component { 15class WorkspacesScreen extends Component {
16 static propTypes = { 16 static propTypes = {
17 actions: PropTypes.shape({ 17 actions: PropTypes.shape({
diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js
index e44569be9..4a1f80b4e 100644
--- a/src/features/workspaces/store.js
+++ b/src/features/workspaces/store.js
@@ -253,11 +253,10 @@ export default class WorkspacesStore extends FeatureStore {
253 }; 253 };
254 254
255 _setIsPremiumFeatureReaction = () => { 255 _setIsPremiumFeatureReaction = () => {
256 const { features, user } = this.stores; 256 const { features } = this.stores;
257 const { isPremium } = user.data;
258 const { isWorkspaceIncludedInCurrentPlan } = features.features; 257 const { isWorkspaceIncludedInCurrentPlan } = features.features;
259 this.isPremiumFeature = !isWorkspaceIncludedInCurrentPlan; 258 this.isPremiumFeature = !isWorkspaceIncludedInCurrentPlan;
260 this.isPremiumUpgradeRequired = !isWorkspaceIncludedInCurrentPlan && !isPremium; 259 this.isPremiumUpgradeRequired = !isWorkspaceIncludedInCurrentPlan;
261 }; 260 };
262 261
263 _setWorkspaceBeingEditedReaction = () => { 262 _setWorkspaceBeingEditedReaction = () => {
diff --git a/src/helpers/plan-helpers.js b/src/helpers/plan-helpers.js
index 19392585e..e0f1fd89a 100644
--- a/src/helpers/plan-helpers.js
+++ b/src/helpers/plan-helpers.js
@@ -33,3 +33,13 @@ export function i18nPlanName(planId, intl) {
33 33
34 return intl.formatMessage(messages[plan]); 34 return intl.formatMessage(messages[plan]);
35} 35}
36
37export function getPlan(planId) {
38 if (!planId) {
39 throw new Error('planId is required');
40 }
41
42 const plan = PLANS_MAPPING[planId];
43
44 return plan;
45}
diff --git a/src/i18n/globalMessages.js b/src/i18n/globalMessages.js
index 1b5ece223..af8479c59 100644
--- a/src/i18n/globalMessages.js
+++ b/src/i18n/globalMessages.js
@@ -25,4 +25,8 @@ export default defineMessages({
25 id: 'global.spellchecking.autodetect.short', 25 id: 'global.spellchecking.autodetect.short',
26 defaultMessage: '!!!Automatic', 26 defaultMessage: '!!!Automatic',
27 }, 27 },
28 proRequired: {
29 id: 'global.franzProRequired',
30 defaultMessage: '!!!Franz Professional Required',
31 },
28}); 32});
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json
index dabe2f11f..02920d5ca 100644
--- a/src/i18n/locales/defaultMessages.json
+++ b/src/i18n/locales/defaultMessages.json
@@ -2530,78 +2530,78 @@
2530 "defaultMessage": "!!!Team", 2530 "defaultMessage": "!!!Team",
2531 "end": { 2531 "end": {
2532 "column": 3, 2532 "column": 3,
2533 "line": 17 2533 "line": 19
2534 }, 2534 },
2535 "file": "src/components/settings/team/TeamDashboard.js", 2535 "file": "src/components/settings/team/TeamDashboard.js",
2536 "id": "settings.team.headline", 2536 "id": "settings.team.headline",
2537 "start": { 2537 "start": {
2538 "column": 12, 2538 "column": 12,
2539 "line": 14 2539 "line": 16
2540 } 2540 }
2541 }, 2541 },
2542 { 2542 {
2543 "defaultMessage": "!!!Franz for Teams", 2543 "defaultMessage": "!!!Franz for Teams",
2544 "end": { 2544 "end": {
2545 "column": 3, 2545 "column": 3,
2546 "line": 21 2546 "line": 23
2547 }, 2547 },
2548 "file": "src/components/settings/team/TeamDashboard.js", 2548 "file": "src/components/settings/team/TeamDashboard.js",
2549 "id": "settings.team.contentHeadline", 2549 "id": "settings.team.contentHeadline",
2550 "start": { 2550 "start": {
2551 "column": 19, 2551 "column": 19,
2552 "line": 18 2552 "line": 20
2553 } 2553 }
2554 }, 2554 },
2555 { 2555 {
2556 "defaultMessage": "!!!You and your team use Franz? You can now manage Premium subscriptions for as many colleagues, friends or family members as you want, all from within one account.", 2556 "defaultMessage": "!!!You and your team use Franz? You can now manage Premium subscriptions for as many colleagues, friends or family members as you want, all from within one account.",
2557 "end": { 2557 "end": {
2558 "column": 3, 2558 "column": 3,
2559 "line": 25 2559 "line": 27
2560 }, 2560 },
2561 "file": "src/components/settings/team/TeamDashboard.js", 2561 "file": "src/components/settings/team/TeamDashboard.js",
2562 "id": "settings.team.intro", 2562 "id": "settings.team.intro",
2563 "start": { 2563 "start": {
2564 "column": 9, 2564 "column": 9,
2565 "line": 22 2565 "line": 24
2566 } 2566 }
2567 }, 2567 },
2568 { 2568 {
2569 "defaultMessage": "!!!Franz for Teams gives you the option to invite co-workers to your team by sending them email invitations and manage their subscriptions in your account’s preferences. Don’t waste time setting up subscriptions for every team member individually, forget about multiple invoices and different billing cycles - one team to rule them all!", 2569 "defaultMessage": "!!!Franz for Teams gives you the option to invite co-workers to your team by sending them email invitations and manage their subscriptions in your account’s preferences. Don’t waste time setting up subscriptions for every team member individually, forget about multiple invoices and different billing cycles - one team to rule them all!",
2570 "end": { 2570 "end": {
2571 "column": 3, 2571 "column": 3,
2572 "line": 29 2572 "line": 31
2573 }, 2573 },
2574 "file": "src/components/settings/team/TeamDashboard.js", 2574 "file": "src/components/settings/team/TeamDashboard.js",
2575 "id": "settings.team.copy", 2575 "id": "settings.team.copy",
2576 "start": { 2576 "start": {
2577 "column": 8, 2577 "column": 8,
2578 "line": 26 2578 "line": 28
2579 } 2579 }
2580 }, 2580 },
2581 { 2581 {
2582 "defaultMessage": "!!!Manage your Team on meetfranz.com", 2582 "defaultMessage": "!!!Manage your Team on meetfranz.com",
2583 "end": { 2583 "end": {
2584 "column": 3, 2584 "column": 3,
2585 "line": 33 2585 "line": 35
2586 }, 2586 },
2587 "file": "src/components/settings/team/TeamDashboard.js", 2587 "file": "src/components/settings/team/TeamDashboard.js",
2588 "id": "settings.team.manageAction", 2588 "id": "settings.team.manageAction",
2589 "start": { 2589 "start": {
2590 "column": 16, 2590 "column": 16,
2591 "line": 30 2591 "line": 32
2592 } 2592 }
2593 }, 2593 },
2594 { 2594 {
2595 "defaultMessage": "!!!Upgrade your Account", 2595 "defaultMessage": "!!!Upgrade your Account",
2596 "end": { 2596 "end": {
2597 "column": 3, 2597 "column": 3,
2598 "line": 37 2598 "line": 39
2599 }, 2599 },
2600 "file": "src/components/settings/team/TeamDashboard.js", 2600 "file": "src/components/settings/team/TeamDashboard.js",
2601 "id": "settings.team.upgradeAction", 2601 "id": "settings.team.upgradeAction",
2602 "start": { 2602 "start": {
2603 "column": 17, 2603 "column": 17,
2604 "line": 34 2604 "line": 36
2605 } 2605 }
2606 } 2606 }
2607 ], 2607 ],
@@ -2895,6 +2895,63 @@
2895 { 2895 {
2896 "descriptors": [ 2896 "descriptors": [
2897 { 2897 {
2898 "defaultMessage": "!!!Get a Franz Supporter License",
2899 "end": {
2900 "column": 3,
2901 "line": 16
2902 },
2903 "file": "src/components/ui/ActivateTrialButton/index.js",
2904 "id": "feature.delayApp.upgrade.action",
2905 "start": {
2906 "column": 10,
2907 "line": 13
2908 }
2909 },
2910 {
2911 "defaultMessage": "!!!Yes, I want the free 14 day trial of Franz Professional",
2912 "end": {
2913 "column": 3,
2914 "line": 20
2915 },
2916 "file": "src/components/ui/ActivateTrialButton/index.js",
2917 "id": "feature.delayApp.trial.action",
2918 "start": {
2919 "column": 15,
2920 "line": 17
2921 }
2922 },
2923 {
2924 "defaultMessage": "!!!Upgrade account",
2925 "end": {
2926 "column": 3,
2927 "line": 24
2928 },
2929 "file": "src/components/ui/ActivateTrialButton/index.js",
2930 "id": "feature.delayApp.upgrade.actionShort",
2931 "start": {
2932 "column": 15,
2933 "line": 21
2934 }
2935 },
2936 {
2937 "defaultMessage": "!!!Activate the free Franz Professional trial",
2938 "end": {
2939 "column": 3,
2940 "line": 28
2941 },
2942 "file": "src/components/ui/ActivateTrialButton/index.js",
2943 "id": "feature.delayApp.trial.actionShort",
2944 "start": {
2945 "column": 20,
2946 "line": 25
2947 }
2948 }
2949 ],
2950 "path": "src/components/ui/ActivateTrialButton/index.json"
2951 },
2952 {
2953 "descriptors": [
2954 {
2898 "defaultMessage": "!!!Add unlimited services", 2955 "defaultMessage": "!!!Add unlimited services",
2899 "end": { 2956 "end": {
2900 "column": 3, 2957 "column": 3,
@@ -3033,13 +3090,13 @@
3033 "defaultMessage": "!!!Upgrade account", 3090 "defaultMessage": "!!!Upgrade account",
3034 "end": { 3091 "end": {
3035 "column": 3, 3092 "column": 3,
3036 "line": 18 3093 "line": 19
3037 }, 3094 },
3038 "file": "src/components/ui/PremiumFeatureContainer/index.js", 3095 "file": "src/components/ui/PremiumFeatureContainer/index.js",
3039 "id": "premiumFeature.button.upgradeAccount", 3096 "id": "premiumFeature.button.upgradeAccount",
3040 "start": { 3097 "start": {
3041 "column": 10, 3098 "column": 10,
3042 "line": 15 3099 "line": 16
3043 } 3100 }
3044 } 3101 }
3045 ], 3102 ],
@@ -3048,6 +3105,24 @@
3048 { 3105 {
3049 "descriptors": [ 3106 "descriptors": [
3050 { 3107 {
3108 "defaultMessage": "!!!Upgrade to Franz Professional",
3109 "end": {
3110 "column": 3,
3111 "line": 16
3112 },
3113 "file": "src/components/ui/UpgradeButton/index.js",
3114 "id": "global.upgradeButton.upgradeToPro",
3115 "start": {
3116 "column": 16,
3117 "line": 13
3118 }
3119 }
3120 ],
3121 "path": "src/components/ui/UpgradeButton/index.json"
3122 },
3123 {
3124 "descriptors": [
3125 {
3051 "defaultMessage": "!!!Loading", 3126 "defaultMessage": "!!!Loading",
3052 "end": { 3127 "end": {
3053 "column": 3, 3128 "column": 3,
@@ -3720,91 +3795,91 @@
3720 "defaultMessage": "!!!Franz is better together!", 3795 "defaultMessage": "!!!Franz is better together!",
3721 "end": { 3796 "end": {
3722 "column": 3, 3797 "column": 3,
3723 "line": 19 3798 "line": 21
3724 }, 3799 },
3725 "file": "src/features/shareFranz/Component.js", 3800 "file": "src/features/shareFranz/Component.js",
3726 "id": "feature.shareFranz.headline", 3801 "id": "feature.shareFranz.headline",
3727 "start": { 3802 "start": {
3728 "column": 12, 3803 "column": 12,
3729 "line": 16 3804 "line": 18
3730 } 3805 }
3731 }, 3806 },
3732 { 3807 {
3733 "defaultMessage": "!!!Tell your friends and colleagues how awesome Franz is and help us to spread the word.", 3808 "defaultMessage": "!!!Tell your friends and colleagues how awesome Franz is and help us to spread the word.",
3734 "end": { 3809 "end": {
3735 "column": 3, 3810 "column": 3,
3736 "line": 23 3811 "line": 25
3737 }, 3812 },
3738 "file": "src/features/shareFranz/Component.js", 3813 "file": "src/features/shareFranz/Component.js",
3739 "id": "feature.shareFranz.text", 3814 "id": "feature.shareFranz.text",
3740 "start": { 3815 "start": {
3741 "column": 8, 3816 "column": 8,
3742 "line": 20 3817 "line": 22
3743 } 3818 }
3744 }, 3819 },
3745 { 3820 {
3746 "defaultMessage": "!!!Share as email", 3821 "defaultMessage": "!!!Share as email",
3747 "end": { 3822 "end": {
3748 "column": 3, 3823 "column": 3,
3749 "line": 27 3824 "line": 29
3750 }, 3825 },
3751 "file": "src/features/shareFranz/Component.js", 3826 "file": "src/features/shareFranz/Component.js",
3752 "id": "feature.shareFranz.action.email", 3827 "id": "feature.shareFranz.action.email",
3753 "start": { 3828 "start": {
3754 "column": 16, 3829 "column": 16,
3755 "line": 24 3830 "line": 26
3756 } 3831 }
3757 }, 3832 },
3758 { 3833 {
3759 "defaultMessage": "!!!Share on Facebook", 3834 "defaultMessage": "!!!Share on Facebook",
3760 "end": { 3835 "end": {
3761 "column": 3, 3836 "column": 3,
3762 "line": 31 3837 "line": 33
3763 }, 3838 },
3764 "file": "src/features/shareFranz/Component.js", 3839 "file": "src/features/shareFranz/Component.js",
3765 "id": "feature.shareFranz.action.facebook", 3840 "id": "feature.shareFranz.action.facebook",
3766 "start": { 3841 "start": {
3767 "column": 19, 3842 "column": 19,
3768 "line": 28 3843 "line": 30
3769 } 3844 }
3770 }, 3845 },
3771 { 3846 {
3772 "defaultMessage": "!!!Share on Twitter", 3847 "defaultMessage": "!!!Share on Twitter",
3773 "end": { 3848 "end": {
3774 "column": 3, 3849 "column": 3,
3775 "line": 35 3850 "line": 37
3776 }, 3851 },
3777 "file": "src/features/shareFranz/Component.js", 3852 "file": "src/features/shareFranz/Component.js",
3778 "id": "feature.shareFranz.action.twitter", 3853 "id": "feature.shareFranz.action.twitter",
3779 "start": { 3854 "start": {
3780 "column": 18, 3855 "column": 18,
3781 "line": 32 3856 "line": 34
3782 } 3857 }
3783 }, 3858 },
3784 { 3859 {
3785 "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com", 3860 "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com",
3786 "end": { 3861 "end": {
3787 "column": 3, 3862 "column": 3,
3788 "line": 39 3863 "line": 41
3789 }, 3864 },
3790 "file": "src/features/shareFranz/Component.js", 3865 "file": "src/features/shareFranz/Component.js",
3791 "id": "feature.shareFranz.shareText.email", 3866 "id": "feature.shareFranz.shareText.email",
3792 "start": { 3867 "start": {
3793 "column": 18, 3868 "column": 18,
3794 "line": 36 3869 "line": 38
3795 } 3870 }
3796 }, 3871 },
3797 { 3872 {
3798 "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com /cc @FranzMessenger", 3873 "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com /cc @FranzMessenger",
3799 "end": { 3874 "end": {
3800 "column": 3, 3875 "column": 3,
3801 "line": 43 3876 "line": 45
3802 }, 3877 },
3803 "file": "src/features/shareFranz/Component.js", 3878 "file": "src/features/shareFranz/Component.js",
3804 "id": "feature.shareFranz.shareText.twitter", 3879 "id": "feature.shareFranz.shareText.twitter",
3805 "start": { 3880 "start": {
3806 "column": 20, 3881 "column": 20,
3807 "line": 40 3882 "line": 42
3808 } 3883 }
3809 } 3884 }
3810 ], 3885 ],
@@ -4127,104 +4202,104 @@
4127 "defaultMessage": "!!!Your workspaces", 4202 "defaultMessage": "!!!Your workspaces",
4128 "end": { 4203 "end": {
4129 "column": 3, 4204 "column": 3,
4130 "line": 20 4205 "line": 22
4131 }, 4206 },
4132 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 4207 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
4133 "id": "settings.workspaces.headline", 4208 "id": "settings.workspaces.headline",
4134 "start": { 4209 "start": {
4135 "column": 12, 4210 "column": 12,
4136 "line": 17 4211 "line": 19
4137 } 4212 }
4138 }, 4213 },
4139 { 4214 {
4140 "defaultMessage": "!!!You haven't added any workspaces yet.", 4215 "defaultMessage": "!!!You haven't added any workspaces yet.",
4141 "end": { 4216 "end": {
4142 "column": 3, 4217 "column": 3,
4143 "line": 24 4218 "line": 26
4144 }, 4219 },
4145 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 4220 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
4146 "id": "settings.workspaces.noWorkspacesAdded", 4221 "id": "settings.workspaces.noWorkspacesAdded",
4147 "start": { 4222 "start": {
4148 "column": 19, 4223 "column": 19,
4149 "line": 21 4224 "line": 23
4150 } 4225 }
4151 }, 4226 },
4152 { 4227 {
4153 "defaultMessage": "!!!Could not load your workspaces", 4228 "defaultMessage": "!!!Could not load your workspaces",
4154 "end": { 4229 "end": {
4155 "column": 3, 4230 "column": 3,
4156 "line": 28 4231 "line": 30
4157 }, 4232 },
4158 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 4233 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
4159 "id": "settings.workspaces.workspacesRequestFailed", 4234 "id": "settings.workspaces.workspacesRequestFailed",
4160 "start": { 4235 "start": {
4161 "column": 27, 4236 "column": 27,
4162 "line": 25 4237 "line": 27
4163 } 4238 }
4164 }, 4239 },
4165 { 4240 {
4166 "defaultMessage": "!!!Try again", 4241 "defaultMessage": "!!!Try again",
4167 "end": { 4242 "end": {
4168 "column": 3, 4243 "column": 3,
4169 "line": 32 4244 "line": 34
4170 }, 4245 },
4171 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 4246 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
4172 "id": "settings.workspaces.tryReloadWorkspaces", 4247 "id": "settings.workspaces.tryReloadWorkspaces",
4173 "start": { 4248 "start": {
4174 "column": 23, 4249 "column": 23,
4175 "line": 29 4250 "line": 31
4176 } 4251 }
4177 }, 4252 },
4178 { 4253 {
4179 "defaultMessage": "!!!Your changes have been saved", 4254 "defaultMessage": "!!!Your changes have been saved",
4180 "end": { 4255 "end": {
4181 "column": 3, 4256 "column": 3,
4182 "line": 36 4257 "line": 38
4183 }, 4258 },
4184 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 4259 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
4185 "id": "settings.workspaces.updatedInfo", 4260 "id": "settings.workspaces.updatedInfo",
4186 "start": { 4261 "start": {
4187 "column": 15, 4262 "column": 15,
4188 "line": 33 4263 "line": 35
4189 } 4264 }
4190 }, 4265 },
4191 { 4266 {
4192 "defaultMessage": "!!!Workspace has been deleted", 4267 "defaultMessage": "!!!Workspace has been deleted",
4193 "end": { 4268 "end": {
4194 "column": 3, 4269 "column": 3,
4195 "line": 40 4270 "line": 42
4196 }, 4271 },
4197 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 4272 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
4198 "id": "settings.workspaces.deletedInfo", 4273 "id": "settings.workspaces.deletedInfo",
4199 "start": { 4274 "start": {
4200 "column": 15, 4275 "column": 15,
4201 "line": 37 4276 "line": 39
4202 } 4277 }
4203 }, 4278 },
4204 { 4279 {
4205 "defaultMessage": "!!!Info about workspace feature", 4280 "defaultMessage": "!!!Info about workspace feature",
4206 "end": { 4281 "end": {
4207 "column": 3, 4282 "column": 3,
4208 "line": 44 4283 "line": 46
4209 }, 4284 },
4210 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 4285 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
4211 "id": "settings.workspaces.workspaceFeatureInfo", 4286 "id": "settings.workspaces.workspaceFeatureInfo",
4212 "start": { 4287 "start": {
4213 "column": 24, 4288 "column": 24,
4214 "line": 41 4289 "line": 43
4215 } 4290 }
4216 }, 4291 },
4217 { 4292 {
4218 "defaultMessage": "!!!Less is More: Introducing Franz Workspaces", 4293 "defaultMessage": "!!!Less is More: Introducing Franz Workspaces",
4219 "end": { 4294 "end": {
4220 "column": 3, 4295 "column": 3,
4221 "line": 48 4296 "line": 50
4222 }, 4297 },
4223 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 4298 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
4224 "id": "settings.workspaces.workspaceFeatureHeadline", 4299 "id": "settings.workspaces.workspaceFeatureHeadline",
4225 "start": { 4300 "start": {
4226 "column": 28, 4301 "column": 28,
4227 "line": 45 4302 "line": 47
4228 } 4303 }
4229 } 4304 }
4230 ], 4305 ],
@@ -4537,6 +4612,19 @@
4537 "column": 39, 4612 "column": 39,
4538 "line": 24 4613 "line": 24
4539 } 4614 }
4615 },
4616 {
4617 "defaultMessage": "!!!Franz Professional Required",
4618 "end": {
4619 "column": 3,
4620 "line": 31
4621 },
4622 "file": "src/i18n/globalMessages.js",
4623 "id": "global.franzProRequired",
4624 "start": {
4625 "column": 15,
4626 "line": 28
4627 }
4540 } 4628 }
4541 ], 4629 ],
4542 "path": "src/i18n/globalMessages.json" 4630 "path": "src/i18n/globalMessages.json"
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 32e9c743a..e09cca7d6 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -5,8 +5,10 @@
5 "feature.delayApp.headline": "Please purchase a Franz Supporter License to skip waiting", 5 "feature.delayApp.headline": "Please purchase a Franz Supporter License to skip waiting",
6 "feature.delayApp.text": "Franz will continue in {seconds} seconds.", 6 "feature.delayApp.text": "Franz will continue in {seconds} seconds.",
7 "feature.delayApp.trial.action": "Yes, I want the free 14 day trial of Franz Professional", 7 "feature.delayApp.trial.action": "Yes, I want the free 14 day trial of Franz Professional",
8 "feature.delayApp.trial.actionShort": "Activate the free Franz Professional trial",
8 "feature.delayApp.trial.headline": "Get the free Franz Professional 14 day trial and skip the line", 9 "feature.delayApp.trial.headline": "Get the free Franz Professional 14 day trial and skip the line",
9 "feature.delayApp.upgrade.action": "Get a Franz Supporter License", 10 "feature.delayApp.upgrade.action": "Get a Franz Supporter License",
11 "feature.delayApp.upgrade.actionShort": "Upgrade account",
10 "feature.serviceLimit.limitReached": "You have added {amount} out of {limit} services that are included in your plan. Please upgrade your account to add more services.", 12 "feature.serviceLimit.limitReached": "You have added {amount} out of {limit} services that are included in your plan. Please upgrade your account to add more services.",
11 "feature.shareFranz.action.email": "Send as email", 13 "feature.shareFranz.action.email": "Send as email",
12 "feature.shareFranz.action.facebook": "Share on Facebook", 14 "feature.shareFranz.action.facebook": "Share on Facebook",
@@ -19,11 +21,13 @@
19 "feature.todos.premium.rollout": "Franz Todos will be available to everyone soon.", 21 "feature.todos.premium.rollout": "Franz Todos will be available to everyone soon.",
20 "feature.todos.premium.upgrade": "Upgrade Account", 22 "feature.todos.premium.upgrade": "Upgrade Account",
21 "global.api.unhealthy": "Can't connect to Franz online services", 23 "global.api.unhealthy": "Can't connect to Franz online services",
24 "global.franzProRequired": "Franz Professional Required",
22 "global.notConnectedToTheInternet": "You are not connected to the internet.", 25 "global.notConnectedToTheInternet": "You are not connected to the internet.",
23 "global.spellchecker.useDefault": "Use System Default ({default})", 26 "global.spellchecker.useDefault": "Use System Default ({default})",
24 "global.spellchecking.autodetect": "Detect language automatically", 27 "global.spellchecking.autodetect": "Detect language automatically",
25 "global.spellchecking.autodetect.short": "Automatic", 28 "global.spellchecking.autodetect.short": "Automatic",
26 "global.spellchecking.language": "Spell checking language", 29 "global.spellchecking.language": "Spell checking language",
30 "global.upgradeButton.upgradeToPro": "Upgrade to Franz Professional",
27 "import.headline": "Import your Franz 4 services", 31 "import.headline": "Import your Franz 4 services",
28 "import.notSupportedHeadline": "Services not yet supported in Franz 5", 32 "import.notSupportedHeadline": "Services not yet supported in Franz 5",
29 "import.skip.label": "I want to add services manually", 33 "import.skip.label": "I want to add services manually",
diff --git a/src/i18n/messages/src/components/settings/team/TeamDashboard.json b/src/i18n/messages/src/components/settings/team/TeamDashboard.json
index 6bccc25ff..01d033044 100644
--- a/src/i18n/messages/src/components/settings/team/TeamDashboard.json
+++ b/src/i18n/messages/src/components/settings/team/TeamDashboard.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Team", 4 "defaultMessage": "!!!Team",
5 "file": "src/components/settings/team/TeamDashboard.js", 5 "file": "src/components/settings/team/TeamDashboard.js",
6 "start": { 6 "start": {
7 "line": 14, 7 "line": 16,
8 "column": 12 8 "column": 12
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 17, 11 "line": 19,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Franz for Teams", 17 "defaultMessage": "!!!Franz for Teams",
18 "file": "src/components/settings/team/TeamDashboard.js", 18 "file": "src/components/settings/team/TeamDashboard.js",
19 "start": { 19 "start": {
20 "line": 18, 20 "line": 20,
21 "column": 19 21 "column": 19
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 21, 24 "line": 23,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!You and your team use Franz? You can now manage Premium subscriptions for as many colleagues, friends or family members as you want, all from within one account.", 30 "defaultMessage": "!!!You and your team use Franz? You can now manage Premium subscriptions for as many colleagues, friends or family members as you want, all from within one account.",
31 "file": "src/components/settings/team/TeamDashboard.js", 31 "file": "src/components/settings/team/TeamDashboard.js",
32 "start": { 32 "start": {
33 "line": 22, 33 "line": 24,
34 "column": 9 34 "column": 9
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 25, 37 "line": 27,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Franz for Teams gives you the option to invite co-workers to your team by sending them email invitations and manage their subscriptions in your account’s preferences. Don’t waste time setting up subscriptions for every team member individually, forget about multiple invoices and different billing cycles - one team to rule them all!", 43 "defaultMessage": "!!!Franz for Teams gives you the option to invite co-workers to your team by sending them email invitations and manage their subscriptions in your account’s preferences. Don’t waste time setting up subscriptions for every team member individually, forget about multiple invoices and different billing cycles - one team to rule them all!",
44 "file": "src/components/settings/team/TeamDashboard.js", 44 "file": "src/components/settings/team/TeamDashboard.js",
45 "start": { 45 "start": {
46 "line": 26, 46 "line": 28,
47 "column": 8 47 "column": 8
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 29, 50 "line": 31,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Manage your Team on meetfranz.com", 56 "defaultMessage": "!!!Manage your Team on meetfranz.com",
57 "file": "src/components/settings/team/TeamDashboard.js", 57 "file": "src/components/settings/team/TeamDashboard.js",
58 "start": { 58 "start": {
59 "line": 30, 59 "line": 32,
60 "column": 16 60 "column": 16
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 33, 63 "line": 35,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,11 @@
69 "defaultMessage": "!!!Upgrade your Account", 69 "defaultMessage": "!!!Upgrade your Account",
70 "file": "src/components/settings/team/TeamDashboard.js", 70 "file": "src/components/settings/team/TeamDashboard.js",
71 "start": { 71 "start": {
72 "line": 34, 72 "line": 36,
73 "column": 17 73 "column": 17
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 37, 76 "line": 39,
77 "column": 3 77 "column": 3
78 } 78 }
79 } 79 }
diff --git a/src/i18n/messages/src/components/ui/ActivateTrialButton/index.json b/src/i18n/messages/src/components/ui/ActivateTrialButton/index.json
new file mode 100644
index 000000000..08c1a9293
--- /dev/null
+++ b/src/i18n/messages/src/components/ui/ActivateTrialButton/index.json
@@ -0,0 +1,54 @@
1[
2 {
3 "id": "feature.delayApp.upgrade.action",
4 "defaultMessage": "!!!Get a Franz Supporter License",
5 "file": "src/components/ui/ActivateTrialButton/index.js",
6 "start": {
7 "line": 13,
8 "column": 10
9 },
10 "end": {
11 "line": 16,
12 "column": 3
13 }
14 },
15 {
16 "id": "feature.delayApp.trial.action",
17 "defaultMessage": "!!!Yes, I want the free 14 day trial of Franz Professional",
18 "file": "src/components/ui/ActivateTrialButton/index.js",
19 "start": {
20 "line": 17,
21 "column": 15
22 },
23 "end": {
24 "line": 20,
25 "column": 3
26 }
27 },
28 {
29 "id": "feature.delayApp.upgrade.actionShort",
30 "defaultMessage": "!!!Upgrade account",
31 "file": "src/components/ui/ActivateTrialButton/index.js",
32 "start": {
33 "line": 21,
34 "column": 15
35 },
36 "end": {
37 "line": 24,
38 "column": 3
39 }
40 },
41 {
42 "id": "feature.delayApp.trial.actionShort",
43 "defaultMessage": "!!!Activate the free Franz Professional trial",
44 "file": "src/components/ui/ActivateTrialButton/index.js",
45 "start": {
46 "line": 25,
47 "column": 20
48 },
49 "end": {
50 "line": 28,
51 "column": 3
52 }
53 }
54] \ No newline at end of file
diff --git a/src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json b/src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json
index 320d3ca3e..0cde4cee5 100644
--- a/src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json
+++ b/src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Upgrade account", 4 "defaultMessage": "!!!Upgrade account",
5 "file": "src/components/ui/PremiumFeatureContainer/index.js", 5 "file": "src/components/ui/PremiumFeatureContainer/index.js",
6 "start": { 6 "start": {
7 "line": 15, 7 "line": 16,
8 "column": 10 8 "column": 10
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 18, 11 "line": 19,
12 "column": 3 12 "column": 3
13 } 13 }
14 } 14 }
diff --git a/src/i18n/messages/src/components/ui/UpgradeButton/index.json b/src/i18n/messages/src/components/ui/UpgradeButton/index.json
new file mode 100644
index 000000000..28e44cb66
--- /dev/null
+++ b/src/i18n/messages/src/components/ui/UpgradeButton/index.json
@@ -0,0 +1,15 @@
1[
2 {
3 "id": "global.upgradeButton.upgradeToPro",
4 "defaultMessage": "!!!Upgrade to Franz Professional",
5 "file": "src/components/ui/UpgradeButton/index.js",
6 "start": {
7 "line": 13,
8 "column": 16
9 },
10 "end": {
11 "line": 16,
12 "column": 3
13 }
14 }
15] \ No newline at end of file
diff --git a/src/i18n/messages/src/features/shareFranz/Component.json b/src/i18n/messages/src/features/shareFranz/Component.json
index 79b425b15..72150f2b0 100644
--- a/src/i18n/messages/src/features/shareFranz/Component.json
+++ b/src/i18n/messages/src/features/shareFranz/Component.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Franz is better together!", 4 "defaultMessage": "!!!Franz is better together!",
5 "file": "src/features/shareFranz/Component.js", 5 "file": "src/features/shareFranz/Component.js",
6 "start": { 6 "start": {
7 "line": 16, 7 "line": 18,
8 "column": 12 8 "column": 12
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 19, 11 "line": 21,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Tell your friends and colleagues how awesome Franz is and help us to spread the word.", 17 "defaultMessage": "!!!Tell your friends and colleagues how awesome Franz is and help us to spread the word.",
18 "file": "src/features/shareFranz/Component.js", 18 "file": "src/features/shareFranz/Component.js",
19 "start": { 19 "start": {
20 "line": 20, 20 "line": 22,
21 "column": 8 21 "column": 8
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 23, 24 "line": 25,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Share as email", 30 "defaultMessage": "!!!Share as email",
31 "file": "src/features/shareFranz/Component.js", 31 "file": "src/features/shareFranz/Component.js",
32 "start": { 32 "start": {
33 "line": 24, 33 "line": 26,
34 "column": 16 34 "column": 16
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 27, 37 "line": 29,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Share on Facebook", 43 "defaultMessage": "!!!Share on Facebook",
44 "file": "src/features/shareFranz/Component.js", 44 "file": "src/features/shareFranz/Component.js",
45 "start": { 45 "start": {
46 "line": 28, 46 "line": 30,
47 "column": 19 47 "column": 19
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 31, 50 "line": 33,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Share on Twitter", 56 "defaultMessage": "!!!Share on Twitter",
57 "file": "src/features/shareFranz/Component.js", 57 "file": "src/features/shareFranz/Component.js",
58 "start": { 58 "start": {
59 "line": 32, 59 "line": 34,
60 "column": 18 60 "column": 18
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 35, 63 "line": 37,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,11 @@
69 "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com", 69 "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com",
70 "file": "src/features/shareFranz/Component.js", 70 "file": "src/features/shareFranz/Component.js",
71 "start": { 71 "start": {
72 "line": 36, 72 "line": 38,
73 "column": 18 73 "column": 18
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 39, 76 "line": 41,
77 "column": 3 77 "column": 3
78 } 78 }
79 }, 79 },
@@ -82,11 +82,11 @@
82 "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com /cc @FranzMessenger", 82 "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com /cc @FranzMessenger",
83 "file": "src/features/shareFranz/Component.js", 83 "file": "src/features/shareFranz/Component.js",
84 "start": { 84 "start": {
85 "line": 40, 85 "line": 42,
86 "column": 20 86 "column": 20
87 }, 87 },
88 "end": { 88 "end": {
89 "line": 43, 89 "line": 45,
90 "column": 3 90 "column": 3
91 } 91 }
92 } 92 }
diff --git a/src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json b/src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json
index ef8f1bebc..7eb4fab50 100644
--- a/src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json
+++ b/src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Your workspaces", 4 "defaultMessage": "!!!Your workspaces",
5 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 5 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
6 "start": { 6 "start": {
7 "line": 17, 7 "line": 19,
8 "column": 12 8 "column": 12
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 20, 11 "line": 22,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!You haven't added any workspaces yet.", 17 "defaultMessage": "!!!You haven't added any workspaces yet.",
18 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 18 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
19 "start": { 19 "start": {
20 "line": 21, 20 "line": 23,
21 "column": 19 21 "column": 19
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 24, 24 "line": 26,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Could not load your workspaces", 30 "defaultMessage": "!!!Could not load your workspaces",
31 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 31 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
32 "start": { 32 "start": {
33 "line": 25, 33 "line": 27,
34 "column": 27 34 "column": 27
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 28, 37 "line": 30,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Try again", 43 "defaultMessage": "!!!Try again",
44 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 44 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
45 "start": { 45 "start": {
46 "line": 29, 46 "line": 31,
47 "column": 23 47 "column": 23
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 32, 50 "line": 34,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Your changes have been saved", 56 "defaultMessage": "!!!Your changes have been saved",
57 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 57 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
58 "start": { 58 "start": {
59 "line": 33, 59 "line": 35,
60 "column": 15 60 "column": 15
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 36, 63 "line": 38,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,11 @@
69 "defaultMessage": "!!!Workspace has been deleted", 69 "defaultMessage": "!!!Workspace has been deleted",
70 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 70 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
71 "start": { 71 "start": {
72 "line": 37, 72 "line": 39,
73 "column": 15 73 "column": 15
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 40, 76 "line": 42,
77 "column": 3 77 "column": 3
78 } 78 }
79 }, 79 },
@@ -82,11 +82,11 @@
82 "defaultMessage": "!!!Info about workspace feature", 82 "defaultMessage": "!!!Info about workspace feature",
83 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 83 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
84 "start": { 84 "start": {
85 "line": 41, 85 "line": 43,
86 "column": 24 86 "column": 24
87 }, 87 },
88 "end": { 88 "end": {
89 "line": 44, 89 "line": 46,
90 "column": 3 90 "column": 3
91 } 91 }
92 }, 92 },
@@ -95,11 +95,11 @@
95 "defaultMessage": "!!!Less is More: Introducing Franz Workspaces", 95 "defaultMessage": "!!!Less is More: Introducing Franz Workspaces",
96 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 96 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
97 "start": { 97 "start": {
98 "line": 45, 98 "line": 47,
99 "column": 28 99 "column": 28
100 }, 100 },
101 "end": { 101 "end": {
102 "line": 48, 102 "line": 50,
103 "column": 3 103 "column": 3
104 } 104 }
105 } 105 }
diff --git a/src/i18n/messages/src/i18n/globalMessages.json b/src/i18n/messages/src/i18n/globalMessages.json
index 28001614f..f4fd5b8b0 100644
--- a/src/i18n/messages/src/i18n/globalMessages.json
+++ b/src/i18n/messages/src/i18n/globalMessages.json
@@ -76,5 +76,18 @@
76 "line": 27, 76 "line": 27,
77 "column": 3 77 "column": 3
78 } 78 }
79 },
80 {
81 "id": "global.franzProRequired",
82 "defaultMessage": "!!!Franz Professional Required",
83 "file": "src/i18n/globalMessages.js",
84 "start": {
85 "line": 28,
86 "column": 15
87 },
88 "end": {
89 "line": 31,
90 "column": 3
91 }
79 } 92 }
80] \ No newline at end of file 93] \ No newline at end of file
diff --git a/src/stores/UIStore.js b/src/stores/UIStore.js
index 2c785111f..9680c5bcc 100644
--- a/src/stores/UIStore.js
+++ b/src/stores/UIStore.js
@@ -46,7 +46,6 @@ export default class UIStore extends Store {
46 // Actions 46 // Actions
47 @action _openSettings({ path = '/settings' }) { 47 @action _openSettings({ path = '/settings' }) {
48 const settingsPath = path !== '/settings' ? `/settings/${path}` : path; 48 const settingsPath = path !== '/settings' ? `/settings/${path}` : path;
49 console.log(settingsPath);
50 this.stores.router.push(settingsPath); 49 this.stores.router.push(settingsPath);
51 } 50 }
52 51
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js
index f3dfbdbf0..365336cbf 100644
--- a/src/stores/UserStore.js
+++ b/src/stores/UserStore.js
@@ -10,6 +10,8 @@ import Request from './lib/Request';
10import CachedRequest from './lib/CachedRequest'; 10import CachedRequest from './lib/CachedRequest';
11import { gaEvent } from '../lib/analytics'; 11import { gaEvent } from '../lib/analytics';
12import { sleep } from '../helpers/async-helpers'; 12import { sleep } from '../helpers/async-helpers';
13import { getPlan } from '../helpers/plan-helpers';
14import { PLANS } from '../config';
13 15
14const debug = require('debug')('Franz:UserStore'); 16const debug = require('debug')('Franz:UserStore');
15 17
@@ -150,10 +152,34 @@ export default class UserStore extends Store {
150 return this.getUserInfoRequest.execute().result || {}; 152 return this.getUserInfoRequest.execute().result || {};
151 } 153 }
152 154
155 @computed get team() {
156 return this.data.team || null;
157 }
158
153 @computed get isPremium() { 159 @computed get isPremium() {
154 return !!this.data.isPremium; 160 return !!this.data.isPremium;
155 } 161 }
156 162
163 @computed get isPremiumOverride() {
164 return (!this.team.plan && this.isPremium) || (this.team.state === 'expired' && this.isPremium);
165 }
166
167 @computed get isPersonal() {
168 if (!this.team.plan) return false;
169 const plan = getPlan(this.team.plan);
170
171 return plan === PLANS.PERSONAL;
172 }
173
174 @computed get isPro() {
175 if (this.isPremiumOverride) return true;
176
177 if ((!this.team.plan || this.team.state === 'expired')) return false;
178 const plan = getPlan(this.team.plan);
179
180 return plan === PLANS.PRO;
181 }
182
157 @computed get legacyServices() { 183 @computed get legacyServices() {
158 return this.getLegacyServicesRequest.execute() || {}; 184 return this.getLegacyServicesRequest.execute() || {};
159 } 185 }
diff --git a/src/styles/settings.scss b/src/styles/settings.scss
index 0955aaa0c..bb95ab5d2 100644
--- a/src/styles/settings.scss
+++ b/src/styles/settings.scss
@@ -1,45 +1,39 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3%headline { 3%headline {
4 color: $theme-gray-light; 4 color: #FFF;
5 font-size: 20px; 5 font-size: 20px;
6 font-weight: 400; 6 font-weight: 400;
7 letter-spacing: -1px; 7 letter-spacing: -1px;
8 8
9 a { color: $theme-gray-light; } 9 a { color: #FFF }
10} 10}
11 11
12%headline__dark { 12%headline__dark {
13 color: $dark-theme-gray-lightest; 13 color: #FFF;
14 font-size: 20px;
15 font-weight: 400;
16 letter-spacing: -1px;
14 17
15 a { color: $dark-theme-gray-lightest; } 18 a { color: #FFF }
16} 19}
17 20
18.theme__dark { 21.theme__dark {
19 .settings-wrapper { background: rgba($dark-theme-black, .8); } 22 .settings-wrapper { background: rgba($dark-theme-black, .8); }
20 23
21 .settings { 24 .settings {
22 background: $dark-theme-gray-darkest;
23 box-shadow: 0 20px 50px rgba($dark-theme-black, .5);
24
25 .settings__header { 25 .settings__header {
26 background: $dark-theme-gray-darker; 26 .mdi { color: #FFF }
27 27 }
28 h1,
29 .settings__header-item { @extend %headline__dark; }
30 28
31 .separator { border-right: 1px solid $dark-theme-gray-dark; } 29 .settings__main {
32 .mdi { color: $dark-theme-gray-lightest; } 30 background: $dark-theme-gray-darkest;
33 } 31 }
34 32
35 .settings__body::-webkit-scrollbar-thumb { background: $dark-theme-gray; } 33 .settings__body::-webkit-scrollbar-thumb { background: $dark-theme-gray; }
36 34
37 .settings__close { 35 .settings__close {
38 background: $dark-theme-gray-darker; 36 color: #FFF;
39 border-left: none;
40 color: $dark-theme-gray-lightest;
41
42 &:hover { background: darken($dark-theme-gray-darker, 5%); }
43 } 37 }
44 38
45 &__settings-group h3 { color: $dark-theme-gray-lightest; } 39 &__settings-group h3 { color: $dark-theme-gray-lightest; }
@@ -76,6 +70,11 @@
76 70
77 .settings-navigation__link { 71 .settings-navigation__link {
78 color: $dark-theme-gray-lightest; 72 color: $dark-theme-gray-lightest;
73 border-bottom: 1px solid darken($dark-theme-gray-darker, 3%);
74
75 &:last-child {
76 border: 0,
77 }
79 78
80 .badge { 79 .badge {
81 background: $dark-theme-gray-lighter; 80 background: $dark-theme-gray-lighter;
@@ -129,15 +128,14 @@
129} 128}
130 129
131.settings { 130.settings {
132 background: #FFF;
133 border-radius: $theme-border-radius; 131 border-radius: $theme-border-radius;
134 box-shadow: 0 20px 50px rgba(black, .5); 132 // box-shadow: 0 20px 50px rgba(black, .5);
135 display: flex; 133 display: flex;
136 height: 100%; 134 height: 100%;
137 max-height: 720px; 135 max-height: 720px;
138 max-width: 900px; 136 max-width: 900px;
139 min-height: 400px; 137 min-height: 400px;
140 overflow: hidden; 138 // overflow: hidden;
141 position: relative; 139 position: relative;
142 width: 100%; 140 width: 100%;
143 z-index: 9999; 141 z-index: 9999;
@@ -147,25 +145,32 @@
147 flex: 1; 145 flex: 1;
148 flex-direction: column; 146 flex-direction: column;
149 height: auto; 147 height: auto;
148 border-radius: $theme-border-radius;
149 overflow: hidden;
150 box-shadow: 0 20px 50px rgba($dark-theme-black, .5);
151 background: #FFF;
150 } 152 }
151 153
152 .settings__header { 154 .settings__header {
153 align-items: center; 155 align-items: center;
154 background: $theme-gray-lighter; 156 background: $theme-brand-primary;
155 display: flex; 157 display: flex;
156 height: 50px; 158 height: 50px;
157 padding: 0 40px; 159 padding: 0 40px;
158 width: calc(100% - 60px); 160 width: calc(100% - 60px);
161 color: #FFF;
159 162
160 h1 { 163 h1 {
161 @extend %headline; 164 @extend %headline;
162 margin: 0; 165 margin: 0;
163 } 166 }
164 167
165 .settings__header-item { @extend %headline; } 168 .settings__header-item {
169 @extend %headline;
170 }
166 171
167 .separator { 172 .separator {
168 border-right: 1px solid darken($theme-gray-lighter, 10%); 173 border-right: 1px solid darken($theme-brand-primary, 8%);
169 height: 100%; 174 height: 100%;
170 margin: 0 15px; 175 margin: 0 15px;
171 transform: skew(15deg) rotate(2deg); 176 transform: skew(15deg) rotate(2deg);
@@ -218,17 +223,18 @@
218 } 223 }
219 224
220 .settings__close { 225 .settings__close {
221 background: $theme-gray-lighter; 226 background: $theme-brand-primary;
222 border-left: 1px solid darken($theme-gray-lighter, 5%); 227 border-left: 1px solid darken($theme-brand-primary, 8%);
223 color: $theme-gray-light; 228 color: #FFF;
224 font-size: 20px; 229 font-size: 20px;
225 height: 50px; 230 height: 50px;
226 padding: 0 20px; 231 padding: 0 20px;
227 position: absolute; 232 position: absolute;
228 right: 0; 233 right: 0;
229 transition: background $theme-transition-time; 234 transition: background $theme-transition-time;
235 border-top-right-radius: $theme-border-radius;
230 236
231 &:hover { background: darken($theme-gray-lighter, 5%); } 237 &:hover { background: darken($theme-brand-primary, 5%); }
232 } 238 }
233 239
234 .search-input { margin-bottom: 30px; } 240 .search-input { margin-bottom: 30px; }
@@ -414,7 +420,13 @@
414 background: $theme-gray-lightest; 420 background: $theme-gray-lightest;
415 flex-direction: column; 421 flex-direction: column;
416 height: auto; 422 height: auto;
417 width: 200px; 423 width: 240px;
424 height: calc(100% - 100px);
425 align-self: center;
426 border-top-left-radius: $theme-border-radius;;
427 border-bottom-left-radius: $theme-border-radius;;
428 overflow: hidden;
429 box-shadow: 0 20px 50px rgba($dark-theme-black, .5);
418 430
419 .settings-navigation__link { 431 .settings-navigation__link {
420 align-items: center; 432 align-items: center;
@@ -426,6 +438,7 @@
426 padding: 0 20px; 438 padding: 0 20px;
427 text-decoration: none; 439 text-decoration: none;
428 transition: background $theme-transition-time, color $theme-transition-time; 440 transition: background $theme-transition-time, color $theme-transition-time;
441 border-bottom: 1px solid darken($theme-gray-lightest, 3%);
429 442
430 &:hover { 443 &:hover {
431 background: darken($theme-gray-lightest, 5%); 444 background: darken($theme-gray-lightest, 5%);