aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-03-28 16:23:17 +0100
committerLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-03-28 16:23:17 +0100
commit7941831bf773b49944001c095a1949a1bdec2cf2 (patch)
tree5dbcbf097e340c381617410e032c2db6b811096e /src
parentimprove styling of workspace switch indicator (diff)
downloadferdium-app-7941831bf773b49944001c095a1949a1bdec2cf2.tar.gz
ferdium-app-7941831bf773b49944001c095a1949a1bdec2cf2.tar.zst
ferdium-app-7941831bf773b49944001c095a1949a1bdec2cf2.zip
add workspace premium notice to dashboard
Diffstat (limited to 'src')
-rw-r--r--src/actions/lib/actions.js4
-rw-r--r--src/components/layout/Sidebar.js2
-rw-r--r--src/components/settings/navigation/SettingsNavigation.js2
-rw-r--r--src/components/settings/services/EditServiceForm.js10
-rw-r--r--src/components/settings/settings/EditSettingsForm.js1
-rw-r--r--src/components/ui/PremiumFeatureContainer/index.js21
-rw-r--r--src/components/ui/PremiumFeatureContainer/styles.js3
-rw-r--r--src/features/delayApp/index.js2
-rw-r--r--src/features/utils/FeatureStore.js21
-rw-r--r--src/features/workspaces/components/CreateWorkspaceForm.js1
-rw-r--r--src/features/workspaces/components/WorkspacesDashboard.js42
-rw-r--r--src/features/workspaces/index.js3
-rw-r--r--src/features/workspaces/store.js69
-rw-r--r--src/i18n/locales/defaultMessages.json54
-rw-r--r--src/i18n/locales/en-US.json2
-rw-r--r--src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json4
-rw-r--r--src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json50
-rw-r--r--src/lib/Menu.js4
-rw-r--r--src/stores/FeaturesStore.js18
19 files changed, 230 insertions, 83 deletions
diff --git a/src/actions/lib/actions.js b/src/actions/lib/actions.js
index 6571e9441..2bc7d2711 100644
--- a/src/actions/lib/actions.js
+++ b/src/actions/lib/actions.js
@@ -9,6 +9,10 @@ export const createActionsFromDefinitions = (actionDefinitions, validate) => {
9 actions[actionName] = action; 9 actions[actionName] = action;
10 action.listeners = []; 10 action.listeners = [];
11 action.listen = listener => action.listeners.push(listener); 11 action.listen = listener => action.listeners.push(listener);
12 action.off = (listener) => {
13 const { listeners } = action;
14 listeners.splice(listeners.indexOf(listener), 1);
15 };
12 action.notify = params => action.listeners.forEach(listener => listener(params)); 16 action.notify = params => action.listeners.forEach(listener => listener(params));
13 }); 17 });
14 return actions; 18 return actions;
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js
index 4fa5e79de..327f76392 100644
--- a/src/components/layout/Sidebar.js
+++ b/src/components/layout/Sidebar.js
@@ -90,7 +90,7 @@ export default @observer class Sidebar extends Component {
90 enableToolTip={() => this.enableToolTip()} 90 enableToolTip={() => this.enableToolTip()}
91 disableToolTip={() => this.disableToolTip()} 91 disableToolTip={() => this.disableToolTip()}
92 /> 92 />
93 {workspaceStore.isFeatureActive ? ( 93 {workspaceStore.isFeatureEnabled ? (
94 <button 94 <button
95 type="button" 95 type="button"
96 onClick={() => { 96 onClick={() => {
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js
index a0eb7d796..dc3c1d6f1 100644
--- a/src/components/settings/navigation/SettingsNavigation.js
+++ b/src/components/settings/navigation/SettingsNavigation.js
@@ -69,7 +69,7 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp
69 {' '} 69 {' '}
70 <span className="badge">{serviceCount}</span> 70 <span className="badge">{serviceCount}</span>
71 </Link> 71 </Link>
72 {workspaceStore.isFeatureActive ? ( 72 {workspaceStore.isFeatureEnabled ? (
73 <Link 73 <Link
74 to="/settings/workspaces" 74 to="/settings/workspaces"
75 className="settings-navigation__link" 75 className="settings-navigation__link"
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js
index 21616b5de..4ba2eb844 100644
--- a/src/components/settings/services/EditServiceForm.js
+++ b/src/components/settings/services/EditServiceForm.js
@@ -341,14 +341,20 @@ export default @observer class EditServiceForm extends Component {
341 </div> 341 </div>
342 </div> 342 </div>
343 343
344 <PremiumFeatureContainer condition={isSpellcheckerPremiumFeature}> 344 <PremiumFeatureContainer
345 condition={isSpellcheckerPremiumFeature}
346 gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }}
347 >
345 <div className="settings__settings-group"> 348 <div className="settings__settings-group">
346 <Select field={form.$('spellcheckerLanguage')} /> 349 <Select field={form.$('spellcheckerLanguage')} />
347 </div> 350 </div>
348 </PremiumFeatureContainer> 351 </PremiumFeatureContainer>
349 352
350 {isProxyFeatureEnabled && ( 353 {isProxyFeatureEnabled && (
351 <PremiumFeatureContainer condition={isProxyPremiumFeature}> 354 <PremiumFeatureContainer
355 condition={isProxyPremiumFeature}
356 gaEventInfo={{ category: 'User', event: 'upgrade', label: 'proxy' }}
357 >
352 <div className="settings__settings-group"> 358 <div className="settings__settings-group">
353 <h3> 359 <h3>
354 {intl.formatMessage(messages.headlineProxy)} 360 {intl.formatMessage(messages.headlineProxy)}
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js
index a92e559f3..8429d0ecb 100644
--- a/src/components/settings/settings/EditSettingsForm.js
+++ b/src/components/settings/settings/EditSettingsForm.js
@@ -170,6 +170,7 @@ export default @observer class EditSettingsForm extends Component {
170 <Select field={form.$('locale')} showLabel={false} /> 170 <Select field={form.$('locale')} showLabel={false} />
171 <PremiumFeatureContainer 171 <PremiumFeatureContainer
172 condition={isSpellcheckerPremiumFeature} 172 condition={isSpellcheckerPremiumFeature}
173 gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }}
173 > 174 >
174 <Fragment> 175 <Fragment>
175 <Toggle 176 <Toggle
diff --git a/src/components/ui/PremiumFeatureContainer/index.js b/src/components/ui/PremiumFeatureContainer/index.js
index 67cd6af0b..3c1e0fac3 100644
--- a/src/components/ui/PremiumFeatureContainer/index.js
+++ b/src/components/ui/PremiumFeatureContainer/index.js
@@ -9,6 +9,7 @@ import { oneOrManyChildElements } from '../../../prop-types';
9import UserStore from '../../../stores/UserStore'; 9import UserStore from '../../../stores/UserStore';
10 10
11import styles from './styles'; 11import styles from './styles';
12import { gaEvent } from '../../../lib/analytics';
12 13
13const messages = defineMessages({ 14const messages = defineMessages({
14 action: { 15 action: {
@@ -17,14 +18,21 @@ const messages = defineMessages({
17 }, 18 },
18}); 19});
19 20
20export default @inject('stores', 'actions') @injectSheet(styles) @observer class PremiumFeatureContainer extends Component { 21@inject('stores', 'actions') @injectSheet(styles) @observer
22class PremiumFeatureContainer extends Component {
21 static propTypes = { 23 static propTypes = {
22 classes: PropTypes.object.isRequired, 24 classes: PropTypes.object.isRequired,
23 condition: PropTypes.bool, 25 condition: PropTypes.bool,
26 gaEventInfo: PropTypes.shape({
27 category: PropTypes.string.isRequired,
28 event: PropTypes.string.isRequired,
29 label: PropTypes.string,
30 }),
24 }; 31 };
25 32
26 static defaultProps = { 33 static defaultProps = {
27 condition: true, 34 condition: true,
35 gaEventInfo: null,
28 }; 36 };
29 37
30 static contextTypes = { 38 static contextTypes = {
@@ -38,6 +46,7 @@ export default @inject('stores', 'actions') @injectSheet(styles) @observer class
38 actions, 46 actions,
39 condition, 47 condition,
40 stores, 48 stores,
49 gaEventInfo,
41 } = this.props; 50 } = this.props;
42 51
43 const { intl } = this.context; 52 const { intl } = this.context;
@@ -49,7 +58,13 @@ export default @inject('stores', 'actions') @injectSheet(styles) @observer class
49 <button 58 <button
50 className={classes.actionButton} 59 className={classes.actionButton}
51 type="button" 60 type="button"
52 onClick={() => actions.ui.openSettings({ path: 'user' })} 61 onClick={() => {
62 actions.ui.openSettings({ path: 'user' });
63 if (gaEventInfo) {
64 const { category, event, label } = gaEventInfo;
65 gaEvent(category, event, label);
66 }
67 }}
53 > 68 >
54 {intl.formatMessage(messages.action)} 69 {intl.formatMessage(messages.action)}
55 </button> 70 </button>
@@ -73,3 +88,5 @@ PremiumFeatureContainer.wrappedComponent.propTypes = {
73 }).isRequired, 88 }).isRequired,
74 }).isRequired, 89 }).isRequired,
75}; 90};
91
92export default PremiumFeatureContainer;
diff --git a/src/components/ui/PremiumFeatureContainer/styles.js b/src/components/ui/PremiumFeatureContainer/styles.js
index 81d6666c6..615ed0a79 100644
--- a/src/components/ui/PremiumFeatureContainer/styles.js
+++ b/src/components/ui/PremiumFeatureContainer/styles.js
@@ -6,6 +6,7 @@ export default theme => ({
6 padding: 20, 6 padding: 20,
7 'border-radius': theme.borderRadius, 7 'border-radius': theme.borderRadius,
8 pointerEvents: 'none', 8 pointerEvents: 'none',
9 height: 'auto',
9 }, 10 },
10 titleContainer: { 11 titleContainer: {
11 display: 'flex', 12 display: 'flex',
@@ -26,7 +27,7 @@ export default theme => ({
26 content: { 27 content: {
27 opacity: 0.5, 28 opacity: 0.5,
28 'margin-top': 20, 29 'margin-top': 20,
29 '& :last-child': { 30 '& > :last-child': {
30 'margin-bottom': 0, 31 'margin-bottom': 0,
31 }, 32 },
32 }, 33 },
diff --git a/src/features/delayApp/index.js b/src/features/delayApp/index.js
index abc8274cf..67f0fc5e6 100644
--- a/src/features/delayApp/index.js
+++ b/src/features/delayApp/index.js
@@ -55,7 +55,7 @@ export default function init(stores) {
55 55
56 setVisibility(true); 56 setVisibility(true);
57 gaPage('/delayApp'); 57 gaPage('/delayApp');
58 gaEvent('delayApp', 'show', 'Delay App Feature'); 58 gaEvent('DelayApp', 'show', 'Delay App Feature');
59 59
60 timeLastDelay = moment(); 60 timeLastDelay = moment();
61 shownAfterLaunch = true; 61 shownAfterLaunch = true;
diff --git a/src/features/utils/FeatureStore.js b/src/features/utils/FeatureStore.js
new file mode 100644
index 000000000..66b66a104
--- /dev/null
+++ b/src/features/utils/FeatureStore.js
@@ -0,0 +1,21 @@
1import Reaction from '../../stores/lib/Reaction';
2
3export class FeatureStore {
4 _actions = null;
5
6 _reactions = null;
7
8 _listenToActions(actions) {
9 if (this._actions) this._actions.forEach(a => a[0].off(a[1]));
10 this._actions = [];
11 actions.forEach(a => this._actions.push(a));
12 this._actions.forEach(a => a[0].listen(a[1]));
13 }
14
15 _startReactions(reactions) {
16 if (this._reactions) this._reactions.forEach(r => r.stop());
17 this._reactions = [];
18 reactions.forEach(r => this._reactions.push(new Reaction(r)));
19 this._reactions.forEach(r => r.start());
20 }
21}
diff --git a/src/features/workspaces/components/CreateWorkspaceForm.js b/src/features/workspaces/components/CreateWorkspaceForm.js
index a8f07d0d5..0be2d528f 100644
--- a/src/features/workspaces/components/CreateWorkspaceForm.js
+++ b/src/features/workspaces/components/CreateWorkspaceForm.js
@@ -30,7 +30,6 @@ const styles = () => ({
30 }, 30 },
31 submitButton: { 31 submitButton: {
32 height: 'inherit', 32 height: 'inherit',
33 marginTop: '3px',
34 }, 33 },
35}); 34});
36 35
diff --git a/src/features/workspaces/components/WorkspacesDashboard.js b/src/features/workspaces/components/WorkspacesDashboard.js
index 52c3afdcf..1fad1f71d 100644
--- a/src/features/workspaces/components/WorkspacesDashboard.js
+++ b/src/features/workspaces/components/WorkspacesDashboard.js
@@ -10,6 +10,8 @@ import WorkspaceItem from './WorkspaceItem';
10import CreateWorkspaceForm from './CreateWorkspaceForm'; 10import 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';
14import PremiumFeatureContainer from '../../../components/ui/PremiumFeatureContainer';
13 15
14const messages = defineMessages({ 16const messages = defineMessages({
15 headline: { 17 headline: {
@@ -36,6 +38,14 @@ const messages = defineMessages({
36 id: 'settings.workspaces.deletedInfo', 38 id: 'settings.workspaces.deletedInfo',
37 defaultMessage: '!!!Workspace has been deleted', 39 defaultMessage: '!!!Workspace has been deleted',
38 }, 40 },
41 workspaceFeatureInfo: {
42 id: 'settings.workspaces.workspaceFeatureInfo',
43 defaultMessage: '!!!Info about workspace feature',
44 },
45 workspaceFeatureHeadline: {
46 id: 'settings.workspaces.workspaceFeatureHeadline',
47 defaultMessage: '!!!Less is More: Introducing Franz Workspaces',
48 },
39}); 49});
40 50
41const styles = () => ({ 51const styles = () => ({
@@ -46,6 +56,12 @@ const styles = () => ({
46 appear: { 56 appear: {
47 height: 'auto', 57 height: 'auto',
48 }, 58 },
59 premiumAnnouncement: {
60 padding: '20px',
61 backgroundColor: '#3498db',
62 marginLeft: '-20px',
63 height: 'auto',
64 },
49}); 65});
50 66
51@injectSheet(styles) @observer 67@injectSheet(styles) @observer
@@ -112,14 +128,24 @@ class WorkspacesDashboard extends Component {
112 </Appear> 128 </Appear>
113 )} 129 )}
114 130
115 {/* ===== Create workspace form ===== */} 131 <PremiumFeatureContainer
116 <div className={classes.createForm}> 132 condition={workspaceStore.isPremiumFeature}
117 <CreateWorkspaceForm 133 gaEventInfo={{ category: 'User', event: 'upgrade', label: 'workspaces' }}
118 isSubmitting={createWorkspaceRequest.isExecuting} 134 >
119 onSubmit={onCreateWorkspaceSubmit} 135 {/* ===== Create workspace form ===== */}
120 /> 136 <div className={classes.createForm}>
121 </div> 137 <CreateWorkspaceForm
122 138 isSubmitting={createWorkspaceRequest.isExecuting}
139 onSubmit={onCreateWorkspaceSubmit}
140 />
141 </div>
142 </PremiumFeatureContainer>
143 {workspaceStore.isUpgradeToPremiumRequired && (
144 <div className={classes.premiumAnnouncement}>
145 <h2>{intl.formatMessage(messages.workspaceFeatureHeadline)}</h2>
146 <p>{intl.formatMessage(messages.workspaceFeatureInfo)}</p>
147 </div>
148 )}
123 {getUserWorkspacesRequest.isExecuting ? ( 149 {getUserWorkspacesRequest.isExecuting ? (
124 <Loader /> 150 <Loader />
125 ) : ( 151 ) : (
diff --git a/src/features/workspaces/index.js b/src/features/workspaces/index.js
index 89999ab0f..524a83e3c 100644
--- a/src/features/workspaces/index.js
+++ b/src/features/workspaces/index.js
@@ -4,11 +4,12 @@ import { resetApiRequests } from './api';
4 4
5const debug = require('debug')('Franz:feature:workspaces'); 5const debug = require('debug')('Franz:feature:workspaces');
6 6
7export const GA_CATEGORY_WORKSPACES = 'workspaces'; 7export const GA_CATEGORY_WORKSPACES = 'Workspaces';
8 8
9export const workspaceStore = new WorkspacesStore(); 9export const workspaceStore = new WorkspacesStore();
10 10
11export default function initWorkspaces(stores, actions) { 11export default function initWorkspaces(stores, actions) {
12 stores.workspaces = workspaceStore;
12 const { features, user } = stores; 13 const { features, user } = stores;
13 14
14 // Toggle workspace feature 15 // Toggle workspace feature
diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js
index f7df7b29c..62bf3efb4 100644
--- a/src/features/workspaces/store.js
+++ b/src/features/workspaces/store.js
@@ -3,9 +3,9 @@ import {
3 observable, 3 observable,
4 action, 4 action,
5} from 'mobx'; 5} from 'mobx';
6import Reaction from '../../stores/lib/Reaction';
7import { matchRoute } from '../../helpers/routing-helpers'; 6import { matchRoute } from '../../helpers/routing-helpers';
8import { workspaceActions } from './actions'; 7import { workspaceActions } from './actions';
8import { FeatureStore } from '../utils/FeatureStore';
9import { 9import {
10 createWorkspaceRequest, 10 createWorkspaceRequest,
11 deleteWorkspaceRequest, 11 deleteWorkspaceRequest,
@@ -15,7 +15,11 @@ import {
15 15
16const debug = require('debug')('Franz:feature:workspaces:store'); 16const debug = require('debug')('Franz:feature:workspaces:store');
17 17
18export default class WorkspacesStore { 18export default class WorkspacesStore extends FeatureStore {
19 @observable isFeatureEnabled = false;
20
21 @observable isPremiumFeature = true;
22
19 @observable isFeatureActive = false; 23 @observable isFeatureActive = false;
20 24
21 @observable activeWorkspace = null; 25 @observable activeWorkspace = null;
@@ -33,36 +37,39 @@ export default class WorkspacesStore {
33 return getUserWorkspacesRequest.result || []; 37 return getUserWorkspacesRequest.result || [];
34 } 38 }
35 39
36 constructor() { 40 @computed get isUpgradeToPremiumRequired() {
37 // Wire-up action handlers 41 return this.isFeatureEnabled && !this.isFeatureActive;
38 workspaceActions.edit.listen(this._edit);
39 workspaceActions.create.listen(this._create);
40 workspaceActions.delete.listen(this._delete);
41 workspaceActions.update.listen(this._update);
42 workspaceActions.activate.listen(this._setActiveWorkspace);
43 workspaceActions.deactivate.listen(this._deactivateActiveWorkspace);
44 workspaceActions.toggleWorkspaceDrawer.listen(this._toggleWorkspaceDrawer);
45 workspaceActions.openWorkspaceSettings.listen(this._openWorkspaceSettings);
46
47 // Register and start reactions
48 this._registerReactions([
49 this._updateWorkspaceBeingEdited,
50 this._updateActiveServiceOnWorkspaceSwitch,
51 ]);
52 } 42 }
53 43
54 start(stores, actions) { 44 start(stores, actions) {
55 debug('WorkspacesStore::start'); 45 debug('WorkspacesStore::start');
56 this.stores = stores; 46 this.stores = stores;
57 this.actions = actions; 47 this.actions = actions;
58 this._reactions.forEach(r => r.start()); 48
59 this.isFeatureActive = true; 49 this._listenToActions([
50 [workspaceActions.edit, this._edit],
51 [workspaceActions.create, this._create],
52 [workspaceActions.delete, this._delete],
53 [workspaceActions.update, this._update],
54 [workspaceActions.activate, this._setActiveWorkspace],
55 [workspaceActions.deactivate, this._deactivateActiveWorkspace],
56 [workspaceActions.toggleWorkspaceDrawer, this._toggleWorkspaceDrawer],
57 [workspaceActions.openWorkspaceSettings, this._openWorkspaceSettings],
58 ]);
59
60 this._startReactions([
61 this._setWorkspaceBeingEditedReaction,
62 this._setActiveServiceOnWorkspaceSwitchReaction,
63 this._setFeatureEnabledReaction,
64 this._setIsPremiumFeatureReaction,
65 ]);
66
60 getUserWorkspacesRequest.execute(); 67 getUserWorkspacesRequest.execute();
68 this.isFeatureActive = true;
61 } 69 }
62 70
63 stop() { 71 stop() {
64 debug('WorkspacesStore::stop'); 72 debug('WorkspacesStore::stop');
65 this._reactions.forEach(r => r.stop());
66 this.isFeatureActive = false; 73 this.isFeatureActive = false;
67 this.activeWorkspace = null; 74 this.activeWorkspace = null;
68 this.nextWorkspace = null; 75 this.nextWorkspace = null;
@@ -85,12 +92,6 @@ export default class WorkspacesStore {
85 92
86 // ========== PRIVATE ========= // 93 // ========== PRIVATE ========= //
87 94
88 _reactions = [];
89
90 _registerReactions(reactions) {
91 reactions.forEach(r => this._reactions.push(new Reaction(r)));
92 }
93
94 _getWorkspaceById = id => this.workspaces.find(w => w.id === id); 95 _getWorkspaceById = id => this.workspaces.find(w => w.id === id);
95 96
96 // Actions 97 // Actions
@@ -164,7 +165,17 @@ export default class WorkspacesStore {
164 165
165 // Reactions 166 // Reactions
166 167
167 _updateWorkspaceBeingEdited = () => { 168 _setFeatureEnabledReaction = () => {
169 const { isWorkspaceEnabled } = this.stores.features.features;
170 this.isFeatureEnabled = isWorkspaceEnabled;
171 };
172
173 _setIsPremiumFeatureReaction = () => {
174 const { isWorkspacePremiumFeature } = this.stores.features.features;
175 this.isPremiumFeature = isWorkspacePremiumFeature;
176 };
177
178 _setWorkspaceBeingEditedReaction = () => {
168 const { pathname } = this.stores.router.location; 179 const { pathname } = this.stores.router.location;
169 const match = matchRoute('/settings/workspaces/edit/:id', pathname); 180 const match = matchRoute('/settings/workspaces/edit/:id', pathname);
170 if (match) { 181 if (match) {
@@ -172,7 +183,7 @@ export default class WorkspacesStore {
172 } 183 }
173 }; 184 };
174 185
175 _updateActiveServiceOnWorkspaceSwitch = () => { 186 _setActiveServiceOnWorkspaceSwitchReaction = () => {
176 if (!this.isFeatureActive) return; 187 if (!this.isFeatureActive) return;
177 if (this.activeWorkspace) { 188 if (this.activeWorkspace) {
178 const services = this.stores.services.allDisplayed; 189 const services = this.stores.services.allDisplayed;
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json
index 659b1b361..1747e1976 100644
--- a/src/i18n/locales/defaultMessages.json
+++ b/src/i18n/locales/defaultMessages.json
@@ -2535,13 +2535,13 @@
2535 "defaultMessage": "!!!Upgrade account", 2535 "defaultMessage": "!!!Upgrade account",
2536 "end": { 2536 "end": {
2537 "column": 3, 2537 "column": 3,
2538 "line": 17 2538 "line": 18
2539 }, 2539 },
2540 "file": "src/components/ui/PremiumFeatureContainer/index.js", 2540 "file": "src/components/ui/PremiumFeatureContainer/index.js",
2541 "id": "premiumFeature.button.upgradeAccount", 2541 "id": "premiumFeature.button.upgradeAccount",
2542 "start": { 2542 "start": {
2543 "column": 10, 2543 "column": 10,
2544 "line": 14 2544 "line": 15
2545 } 2545 }
2546 } 2546 }
2547 ], 2547 ],
@@ -3388,78 +3388,104 @@
3388 "defaultMessage": "!!!Your workspaces", 3388 "defaultMessage": "!!!Your workspaces",
3389 "end": { 3389 "end": {
3390 "column": 3, 3390 "column": 3,
3391 "line": 18 3391 "line": 20
3392 }, 3392 },
3393 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 3393 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
3394 "id": "settings.workspaces.headline", 3394 "id": "settings.workspaces.headline",
3395 "start": { 3395 "start": {
3396 "column": 12, 3396 "column": 12,
3397 "line": 15 3397 "line": 17
3398 } 3398 }
3399 }, 3399 },
3400 { 3400 {
3401 "defaultMessage": "!!!You haven't added any workspaces yet.", 3401 "defaultMessage": "!!!You haven't added any workspaces yet.",
3402 "end": { 3402 "end": {
3403 "column": 3, 3403 "column": 3,
3404 "line": 22 3404 "line": 24
3405 }, 3405 },
3406 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 3406 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
3407 "id": "settings.workspaces.noWorkspacesAdded", 3407 "id": "settings.workspaces.noWorkspacesAdded",
3408 "start": { 3408 "start": {
3409 "column": 19, 3409 "column": 19,
3410 "line": 19 3410 "line": 21
3411 } 3411 }
3412 }, 3412 },
3413 { 3413 {
3414 "defaultMessage": "!!!Could not load your workspaces", 3414 "defaultMessage": "!!!Could not load your workspaces",
3415 "end": { 3415 "end": {
3416 "column": 3, 3416 "column": 3,
3417 "line": 26 3417 "line": 28
3418 }, 3418 },
3419 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 3419 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
3420 "id": "settings.workspaces.workspacesRequestFailed", 3420 "id": "settings.workspaces.workspacesRequestFailed",
3421 "start": { 3421 "start": {
3422 "column": 27, 3422 "column": 27,
3423 "line": 23 3423 "line": 25
3424 } 3424 }
3425 }, 3425 },
3426 { 3426 {
3427 "defaultMessage": "!!!Try again", 3427 "defaultMessage": "!!!Try again",
3428 "end": { 3428 "end": {
3429 "column": 3, 3429 "column": 3,
3430 "line": 30 3430 "line": 32
3431 }, 3431 },
3432 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 3432 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
3433 "id": "settings.workspaces.tryReloadWorkspaces", 3433 "id": "settings.workspaces.tryReloadWorkspaces",
3434 "start": { 3434 "start": {
3435 "column": 23, 3435 "column": 23,
3436 "line": 27 3436 "line": 29
3437 } 3437 }
3438 }, 3438 },
3439 { 3439 {
3440 "defaultMessage": "!!!Your changes have been saved", 3440 "defaultMessage": "!!!Your changes have been saved",
3441 "end": { 3441 "end": {
3442 "column": 3, 3442 "column": 3,
3443 "line": 34 3443 "line": 36
3444 }, 3444 },
3445 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 3445 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
3446 "id": "settings.workspaces.updatedInfo", 3446 "id": "settings.workspaces.updatedInfo",
3447 "start": { 3447 "start": {
3448 "column": 15, 3448 "column": 15,
3449 "line": 31 3449 "line": 33
3450 } 3450 }
3451 }, 3451 },
3452 { 3452 {
3453 "defaultMessage": "!!!Workspace has been deleted", 3453 "defaultMessage": "!!!Workspace has been deleted",
3454 "end": { 3454 "end": {
3455 "column": 3, 3455 "column": 3,
3456 "line": 38 3456 "line": 40
3457 }, 3457 },
3458 "file": "src/features/workspaces/components/WorkspacesDashboard.js", 3458 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
3459 "id": "settings.workspaces.deletedInfo", 3459 "id": "settings.workspaces.deletedInfo",
3460 "start": { 3460 "start": {
3461 "column": 15, 3461 "column": 15,
3462 "line": 35 3462 "line": 37
3463 }
3464 },
3465 {
3466 "defaultMessage": "!!!Info about workspace feature",
3467 "end": {
3468 "column": 3,
3469 "line": 44
3470 },
3471 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
3472 "id": "settings.workspaces.workspaceFeatureInfo",
3473 "start": {
3474 "column": 24,
3475 "line": 41
3476 }
3477 },
3478 {
3479 "defaultMessage": "!!!Less is More: Introducing Franz Workspaces",
3480 "end": {
3481 "column": 3,
3482 "line": 48
3483 },
3484 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
3485 "id": "settings.workspaces.workspaceFeatureHeadline",
3486 "start": {
3487 "column": 28,
3488 "line": 45
3463 } 3489 }
3464 } 3490 }
3465 ], 3491 ],
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 5f7254317..987262c35 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -254,6 +254,8 @@
254 "settings.workspaces.noWorkspacesAdded": "You haven't added any workspaces yet.", 254 "settings.workspaces.noWorkspacesAdded": "You haven't added any workspaces yet.",
255 "settings.workspaces.tryReloadWorkspaces": "Try again", 255 "settings.workspaces.tryReloadWorkspaces": "Try again",
256 "settings.workspaces.updatedInfo": "Your changes have been saved", 256 "settings.workspaces.updatedInfo": "Your changes have been saved",
257 "settings.workspaces.workspaceFeatureHeadline": "Less is More: Introducing Franz Workspaces",
258 "settings.workspaces.workspaceFeatureInfo": "Franz Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time. You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.",
257 "settings.workspaces.workspacesRequestFailed": "Could not load your workspaces", 259 "settings.workspaces.workspacesRequestFailed": "Could not load your workspaces",
258 "sidebar.addNewService": "Add new service", 260 "sidebar.addNewService": "Add new service",
259 "sidebar.closeWorkspaceDrawer": "Close workspace drawer", 261 "sidebar.closeWorkspaceDrawer": "Close workspace drawer",
diff --git a/src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json b/src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json
index 582d546fa..320d3ca3e 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": 14, 7 "line": 15,
8 "column": 10 8 "column": 10
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 17, 11 "line": 18,
12 "column": 3 12 "column": 3
13 } 13 }
14 } 14 }
diff --git a/src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json b/src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json
index a957358c8..ef8f1bebc 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": 15, 7 "line": 17,
8 "column": 12 8 "column": 12
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 18, 11 "line": 20,
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": 19, 20 "line": 21,
21 "column": 19 21 "column": 19
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 22, 24 "line": 24,
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": 23, 33 "line": 25,
34 "column": 27 34 "column": 27
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 26, 37 "line": 28,
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": 27, 46 "line": 29,
47 "column": 23 47 "column": 23
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 30, 50 "line": 32,
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": 31, 59 "line": 33,
60 "column": 15 60 "column": 15
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 34, 63 "line": 36,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,37 @@
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": 35, 72 "line": 37,
73 "column": 15 73 "column": 15
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 38, 76 "line": 40,
77 "column": 3
78 }
79 },
80 {
81 "id": "settings.workspaces.workspaceFeatureInfo",
82 "defaultMessage": "!!!Info about workspace feature",
83 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
84 "start": {
85 "line": 41,
86 "column": 24
87 },
88 "end": {
89 "line": 44,
90 "column": 3
91 }
92 },
93 {
94 "id": "settings.workspaces.workspaceFeatureHeadline",
95 "defaultMessage": "!!!Less is More: Introducing Franz Workspaces",
96 "file": "src/features/workspaces/components/WorkspacesDashboard.js",
97 "start": {
98 "line": 45,
99 "column": 28
100 },
101 "end": {
102 "line": 48,
77 "column": 3 103 "column": 3
78 } 104 }
79 } 105 }
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index d19aa9d6e..a4e41c17c 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -323,7 +323,7 @@ const _templateFactory = intl => [
323 { 323 {
324 label: intl.formatMessage(menuItems.workspaces), 324 label: intl.formatMessage(menuItems.workspaces),
325 submenu: [], 325 submenu: [],
326 visible: workspaceStore.isFeatureActive, 326 visible: workspaceStore.isFeatureEnabled,
327 }, 327 },
328 { 328 {
329 label: intl.formatMessage(menuItems.window), 329 label: intl.formatMessage(menuItems.window),
@@ -732,7 +732,7 @@ export default class FranzMenu {
732 tpl[3].submenu = serviceTpl; 732 tpl[3].submenu = serviceTpl;
733 } 733 }
734 734
735 if (workspaceStore.isFeatureActive) { 735 if (workspaceStore.isFeatureEnabled) {
736 tpl[4].submenu = this.workspacesMenu(); 736 tpl[4].submenu = this.workspacesMenu();
737 } 737 }
738 738
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js
index 2bda82e17..52e38ad96 100644
--- a/src/stores/FeaturesStore.js
+++ b/src/stores/FeaturesStore.js
@@ -1,4 +1,4 @@
1import { computed, observable, reaction } from 'mobx'; 1import { computed, observable, reaction, runInAction } from 'mobx';
2 2
3import Store from './lib/Store'; 3import Store from './lib/Store';
4import CachedRequest from './lib/CachedRequest'; 4import CachedRequest from './lib/CachedRequest';
@@ -17,8 +17,11 @@ export default class FeaturesStore extends Store {
17 17
18 @observable featuresRequest = new CachedRequest(this.api.features, 'features'); 18 @observable featuresRequest = new CachedRequest(this.api.features, 'features');
19 19
20 @observable features = Object.assign({}, DEFAULT_FEATURES_CONFIG);
21
20 async setup() { 22 async setup() {
21 this.registerReactions([ 23 this.registerReactions([
24 this._updateFeatures,
22 this._monitorLoginStatus.bind(this), 25 this._monitorLoginStatus.bind(this),
23 ]); 26 ]);
24 27
@@ -37,13 +40,16 @@ export default class FeaturesStore extends Store {
37 return this.defaultFeaturesRequest.execute().result || DEFAULT_FEATURES_CONFIG; 40 return this.defaultFeaturesRequest.execute().result || DEFAULT_FEATURES_CONFIG;
38 } 41 }
39 42
40 @computed get features() { 43 _updateFeatures = () => {
44 const features = Object.assign({}, DEFAULT_FEATURES_CONFIG);
41 if (this.stores.user.isLoggedIn) { 45 if (this.stores.user.isLoggedIn) {
42 return Object.assign({}, DEFAULT_FEATURES_CONFIG, this.featuresRequest.execute().result); 46 const requestResult = this.featuresRequest.execute().result;
47 Object.assign(features, requestResult);
43 } 48 }
44 49 runInAction('FeaturesStore::_updateFeatures', () => {
45 return DEFAULT_FEATURES_CONFIG; 50 this.features = features;
46 } 51 });
52 };
47 53
48 _monitorLoginStatus() { 54 _monitorLoginStatus() {
49 if (this.stores.user.isLoggedIn) { 55 if (this.stores.user.isLoggedIn) {