aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
Diffstat (limited to 'src/features')
-rw-r--r--src/features/announcements/actions.js10
-rw-r--r--src/features/announcements/api.js33
-rw-r--r--src/features/announcements/components/AnnouncementScreen.js286
-rw-r--r--src/features/announcements/index.js30
-rw-r--r--src/features/announcements/store.js133
-rw-r--r--src/features/utils/ActionBinding.js29
-rw-r--r--src/features/utils/FeatureStore.js43
-rw-r--r--src/features/workspaces/components/CreateWorkspaceForm.js4
-rw-r--r--src/features/workspaces/store.js69
9 files changed, 613 insertions, 24 deletions
diff --git a/src/features/announcements/actions.js b/src/features/announcements/actions.js
new file mode 100644
index 000000000..bab496314
--- /dev/null
+++ b/src/features/announcements/actions.js
@@ -0,0 +1,10 @@
1import PropTypes from 'prop-types';
2import { createActionsFromDefinitions } from '../../actions/lib/actions';
3
4export const announcementActions = createActionsFromDefinitions({
5 show: {
6 targetVersion: PropTypes.string,
7 },
8}, PropTypes.checkPropTypes);
9
10export default announcementActions;
diff --git a/src/features/announcements/api.js b/src/features/announcements/api.js
new file mode 100644
index 000000000..a581bd8de
--- /dev/null
+++ b/src/features/announcements/api.js
@@ -0,0 +1,33 @@
1import { remote } from 'electron';
2import Request from '../../stores/lib/Request';
3import { API, API_VERSION } from '../../environment';
4
5const debug = require('debug')('Franz:feature:announcements:api');
6
7export const announcementsApi = {
8 async getCurrentVersion() {
9 debug('getting current version of electron app');
10 return Promise.resolve(remote.app.getVersion());
11 },
12
13 async getChangelog(version) {
14 debug('fetching release changelog from Github');
15 const url = `https://api.github.com/repos/meetfranz/franz/releases/tags/v${version}`;
16 const request = await window.fetch(url, { method: 'GET' });
17 if (!request.ok) return null;
18 const data = await request.json();
19 return data.body;
20 },
21
22 async getAnnouncement(version) {
23 debug('fetching release announcement from api');
24 const url = `${API}/${API_VERSION}/announcements/${version}`;
25 const response = await window.fetch(url, { method: 'GET' });
26 if (!response.ok) return null;
27 return response.json();
28 },
29};
30
31export const getCurrentVersionRequest = new Request(announcementsApi, 'getCurrentVersion');
32export const getChangelogRequest = new Request(announcementsApi, 'getChangelog');
33export const getAnnouncementRequest = new Request(announcementsApi, 'getAnnouncement');
diff --git a/src/features/announcements/components/AnnouncementScreen.js b/src/features/announcements/components/AnnouncementScreen.js
new file mode 100644
index 000000000..dfce6cdd5
--- /dev/null
+++ b/src/features/announcements/components/AnnouncementScreen.js
@@ -0,0 +1,286 @@
1import React, { Component } from 'react';
2import marked from 'marked';
3import PropTypes from 'prop-types';
4import { inject, observer } from 'mobx-react';
5import { defineMessages, intlShape } from 'react-intl';
6import injectSheet from 'react-jss';
7import { Button } from '@meetfranz/forms';
8
9import { announcementsStore } from '../index';
10import UIStore from '../../../stores/UIStore';
11import { gaEvent } from '../../../lib/analytics';
12
13const renderer = new marked.Renderer();
14
15renderer.link = (href, title, text) => `<a target="_blank" href="${href}" title="${title}">${text}</a>`;
16
17const markedOptions = { sanitize: true, renderer };
18
19const messages = defineMessages({
20 headline: {
21 id: 'feature.announcements.changelog.headline',
22 defaultMessage: '!!!Changes in Franz {version}',
23 },
24});
25
26const smallScreen = '1000px';
27
28const styles = theme => ({
29 container: {
30 background: theme.colorBackground,
31 position: 'absolute',
32 top: 0,
33 zIndex: 140,
34 width: '100%',
35 height: '100%',
36 overflowY: 'auto',
37 },
38 headline: {
39 color: theme.colorHeadline,
40 margin: [25, 0, 40],
41 // 'max-width': 500,
42 'text-align': 'center',
43 'line-height': '1.3em',
44 },
45 announcement: {
46 height: 'auto',
47
48 [`@media(min-width: ${smallScreen})`]: {
49 display: 'flex',
50 flexDirection: 'column',
51 justifyContent: 'center',
52 height: '100vh',
53 },
54 },
55 main: {
56 display: 'flex',
57 flexDirection: 'column',
58 flexGrow: 1,
59 justifyContent: 'center',
60
61 '& h1': {
62 margin: [40, 0, 15],
63 fontSize: 70,
64 color: theme.styleTypes.primary.accent,
65 textAlign: 'center',
66
67 [`@media(min-width: ${smallScreen})`]: {
68 marginTop: 0,
69 },
70 },
71 '& h2': {
72 fontSize: 30,
73 fontWeight: 300,
74 color: theme.colorText,
75 textAlign: 'center',
76 marginBottom: 60,
77 },
78 },
79 mainBody: {
80 display: 'flex',
81 flexDirection: 'column',
82 alignItems: 'center',
83 width: 'calc(100% - 80px)',
84 height: 'auto',
85 margin: '0 auto',
86 [`@media(min-width: ${smallScreen})`]: {
87 flexDirection: 'row',
88 justifyContent: 'center',
89 },
90 },
91 mainImage: {
92 minWidth: 250,
93 maxWidth: 400,
94 margin: '0 auto',
95 marginBottom: 40,
96 '& img': {
97 width: '100%',
98 },
99 [`@media(min-width: ${smallScreen})`]: {
100 margin: 0,
101 },
102 },
103 mainText: {
104 height: 'auto',
105 maxWidth: 600,
106 textAlign: 'center',
107 '& p': {
108 lineHeight: '1.5em',
109 },
110 [`@media(min-width: ${smallScreen})`]: {
111 textAlign: 'left',
112 },
113 },
114 mainCtaButton: {
115 textAlign: 'center',
116 marginTop: 40,
117 [`@media(min-width: ${smallScreen})`]: {
118 textAlign: 'left',
119 },
120 },
121 spotlight: {
122 height: 'auto',
123 background: theme.announcements.spotlight.background,
124 padding: [40, 0],
125 marginTop: 80,
126 [`@media(min-width: ${smallScreen})`]: {
127 marginTop: 0,
128 justifyContent: 'center',
129 alignItems: 'flex-start',
130 display: 'flex',
131 flexDirection: 'row',
132 },
133 },
134 spotlightTopicContainer: {
135 textAlign: 'center',
136 marginBottom: 20,
137
138 [`@media(min-width: ${smallScreen})`]: {
139 marginBottom: 0,
140 minWidth: 250,
141 maxWidth: 330,
142 width: '100%',
143 textAlign: 'right',
144 marginRight: 60,
145 },
146 },
147 spotlightContentContainer: {
148 textAlign: 'center',
149 [`@media(min-width: ${smallScreen})`]: {
150 height: 'auto',
151 maxWidth: 600,
152 paddingRight: 40,
153 textAlign: 'left',
154 },
155 '& p': {
156 lineHeight: '1.5em',
157 },
158 },
159 spotlightTopic: {
160 fontSize: 20,
161 marginBottom: 5,
162 letterSpacing: 0,
163 fontWeight: 100,
164 },
165 spotlightSubject: {
166 fontSize: 20,
167 },
168 changelog: {
169 padding: [0, 60],
170 maxWidth: 700,
171 margin: [100, 'auto'],
172 height: 'auto',
173
174 '& h3': {
175 fontSize: '24px',
176 margin: '1.5em 0 1em 0',
177 },
178 '& li': {
179 marginBottom: '1em',
180 lineHeight: '1.4em',
181 },
182 '& div': {
183 height: 'auto',
184 },
185 },
186});
187
188
189@inject('stores', 'actions') @injectSheet(styles) @observer
190class AnnouncementScreen extends Component {
191 static propTypes = {
192 classes: PropTypes.object.isRequired,
193 stores: PropTypes.shape({
194 ui: PropTypes.instanceOf(UIStore).isRequired,
195 }).isRequired,
196 };
197
198 static contextTypes = {
199 intl: intlShape,
200 };
201
202 render() {
203 const { classes, stores } = this.props;
204 const { intl } = this.context;
205 const { changelog, announcement } = announcementsStore;
206 const themeImage = stores.ui.isDarkThemeActive ? 'dark' : 'light';
207 return (
208 <div className={classes.container}>
209 {announcement && (
210 <div className={classes.announcement}>
211 <div className={classes.main}>
212 <h1>{announcement.main.headline}</h1>
213 <h2>{announcement.main.subHeadline}</h2>
214 <div className={classes.mainBody}>
215 <div className={classes.mainImage}>
216 <img
217 src={announcement.main.image[themeImage]}
218 alt=""
219 />
220 </div>
221 <div className={classes.mainText}>
222 <div
223 dangerouslySetInnerHTML={{
224 __html: marked(announcement.main.text, markedOptions),
225 }}
226 />
227 <div className={classes.mainCtaButton}>
228 <Button
229 label={announcement.main.cta.label}
230 onClick={() => {
231 const { analytics } = announcement.main.cta;
232 window.location.href = `#${announcement.main.cta.href}`;
233 gaEvent(analytics.category, analytics.action, announcement.main.cta.label);
234 }}
235 />
236 </div>
237 </div>
238 </div>
239 </div>
240 {announcement.spotlight && (
241 <div className={classes.spotlight}>
242 <div className={classes.spotlightTopicContainer}>
243 <h2 className={classes.spotlightTopic}>{announcement.spotlight.title}</h2>
244 <h3 className={classes.spotlightSubject}>{announcement.spotlight.subject}</h3>
245 </div>
246 <div className={classes.spotlightContentContainer}>
247 <div
248 dangerouslySetInnerHTML={{
249 __html: marked(announcement.spotlight.text, markedOptions),
250 }}
251 />
252 <div className={classes.mainCtaButton}>
253 <Button
254 label={announcement.spotlight.cta.label}
255 onClick={() => {
256 const { analytics } = announcement.spotlight.cta;
257 window.location.href = `#${announcement.spotlight.cta.href}`;
258 gaEvent(analytics.category, analytics.action, announcement.spotlight.cta.label);
259 }}
260 />
261 </div>
262 </div>
263 </div>
264 )}
265 </div>
266 )}
267 {changelog && (
268 <div className={classes.changelog}>
269 <h1 className={classes.headline}>
270 {intl.formatMessage(messages.headline, {
271 version: announcementsStore.currentVersion,
272 })}
273 </h1>
274 <div
275 dangerouslySetInnerHTML={{
276 __html: marked(changelog, markedOptions),
277 }}
278 />
279 </div>
280 )}
281 </div>
282 );
283 }
284}
285
286export default AnnouncementScreen;
diff --git a/src/features/announcements/index.js b/src/features/announcements/index.js
new file mode 100644
index 000000000..c087689a7
--- /dev/null
+++ b/src/features/announcements/index.js
@@ -0,0 +1,30 @@
1import { reaction } from 'mobx';
2import { AnnouncementsStore } from './store';
3
4const debug = require('debug')('Franz:feature:announcements');
5
6export const announcementsStore = new AnnouncementsStore();
7
8export default function initAnnouncements(stores, actions) {
9 // const { features } = stores;
10
11 // Toggle workspace feature
12 reaction(
13 () => (
14 true
15 // features.features.isAnnouncementsEnabled
16 ),
17 (isEnabled) => {
18 if (isEnabled) {
19 debug('Initializing `announcements` feature');
20 announcementsStore.start(stores, actions);
21 } else if (announcementsStore.isFeatureActive) {
22 debug('Disabling `announcements` feature');
23 announcementsStore.stop();
24 }
25 },
26 {
27 fireImmediately: true,
28 },
29 );
30}
diff --git a/src/features/announcements/store.js b/src/features/announcements/store.js
new file mode 100644
index 000000000..87cf08544
--- /dev/null
+++ b/src/features/announcements/store.js
@@ -0,0 +1,133 @@
1import {
2 action,
3 computed,
4 observable,
5 reaction,
6} from 'mobx';
7import semver from 'semver';
8import localStorage from 'mobx-localstorage';
9
10import { FeatureStore } from '../utils/FeatureStore';
11import { getAnnouncementRequest, getChangelogRequest, getCurrentVersionRequest } from './api';
12import { announcementActions } from './actions';
13import { createActionBindings } from '../utils/ActionBinding';
14import { createReactions } from '../../stores/lib/Reaction';
15
16const LOCAL_STORAGE_KEY = 'announcements';
17
18const debug = require('debug')('Franz:feature:announcements:store');
19
20export class AnnouncementsStore extends FeatureStore {
21 @observable targetVersion = null;
22
23 @observable isAnnouncementVisible = false;
24
25 @observable isFeatureActive = false;
26
27 @computed get changelog() {
28 return getChangelogRequest.result;
29 }
30
31 @computed get announcement() {
32 return getAnnouncementRequest.result;
33 }
34
35 @computed get settings() {
36 return localStorage.getItem(LOCAL_STORAGE_KEY) || {};
37 }
38
39 @computed get lastSeenAnnouncementVersion() {
40 return this.settings.lastSeenAnnouncementVersion || null;
41 }
42
43 @computed get currentVersion() {
44 return getCurrentVersionRequest.result;
45 }
46
47 @computed get isNewUser() {
48 return this.stores.settings.stats.appStarts <= 1;
49 }
50
51 async start(stores, actions) {
52 debug('AnnouncementsStore::start');
53 this.stores = stores;
54 this.actions = actions;
55 getCurrentVersionRequest.execute();
56
57 this._registerActions(createActionBindings([
58 [announcementActions.show, this._showAnnouncement],
59 ]));
60
61 this._reactions = createReactions([
62 this._fetchAnnouncements,
63 this._showAnnouncementToUsersWhoUpdatedApp,
64 ]);
65 this._registerReactions(this._reactions);
66 this.isFeatureActive = true;
67 }
68
69 stop() {
70 super.stop();
71 debug('AnnouncementsStore::stop');
72 this.isFeatureActive = false;
73 this.isAnnouncementVisible = false;
74 }
75
76 // ======= HELPERS ======= //
77
78 _updateSettings = (changes) => {
79 localStorage.setItem(LOCAL_STORAGE_KEY, {
80 ...this.settings,
81 ...changes,
82 });
83 };
84
85 // ======= ACTIONS ======= //
86
87 @action _showAnnouncement = ({ targetVersion } = {}) => {
88 this.targetVersion = targetVersion || this.currentVersion;
89 this.isAnnouncementVisible = true;
90 this.actions.service.blurActive();
91 this._updateSettings({
92 lastSeenAnnouncementVersion: this.currentVersion,
93 });
94 const dispose = reaction(
95 () => this.stores.services.active,
96 () => {
97 this._hideAnnouncement();
98 dispose();
99 },
100 );
101 };
102
103 @action _hideAnnouncement() {
104 this.isAnnouncementVisible = false;
105 }
106
107 // ======= REACTIONS ========
108
109 _showAnnouncementToUsersWhoUpdatedApp = () => {
110 const { announcement, isNewUser } = this;
111 // Check if there is an announcement and on't show announcements to new users
112 if (!announcement || isNewUser) return;
113
114 // Check if the user has already used current version (= has seen the announcement)
115 const { currentVersion, lastSeenAnnouncementVersion } = this;
116 if (semver.gt(currentVersion, lastSeenAnnouncementVersion)) {
117 debug(`${currentVersion} < ${lastSeenAnnouncementVersion}: announcement is shown`);
118 this._showAnnouncement();
119 }
120 };
121
122 _fetchAnnouncements = () => {
123 const targetVersion = this.targetVersion || this.currentVersion;
124 if (!targetVersion) return;
125 getChangelogRequest.execute(targetVersion);
126 // We only fetch announcements for current / older versions
127 if (targetVersion <= this.currentVersion) {
128 getAnnouncementRequest.execute(targetVersion);
129 } else {
130 getAnnouncementRequest.reset();
131 }
132 }
133}
diff --git a/src/features/utils/ActionBinding.js b/src/features/utils/ActionBinding.js
new file mode 100644
index 000000000..497aa071b
--- /dev/null
+++ b/src/features/utils/ActionBinding.js
@@ -0,0 +1,29 @@
1export default class ActionBinding {
2 action;
3
4 isActive = false;
5
6 constructor(action) {
7 this.action = action;
8 }
9
10 start() {
11 if (!this.isActive) {
12 const { action } = this;
13 action[0].listen(action[1]);
14 this.isActive = true;
15 }
16 }
17
18 stop() {
19 if (this.isActive) {
20 const { action } = this;
21 action[0].off(action[1]);
22 this.isActive = false;
23 }
24 }
25}
26
27export const createActionBindings = actions => (
28 actions.map(a => new ActionBinding(a))
29);
diff --git a/src/features/utils/FeatureStore.js b/src/features/utils/FeatureStore.js
index 66b66a104..b6e0fbce3 100644
--- a/src/features/utils/FeatureStore.js
+++ b/src/features/utils/FeatureStore.js
@@ -1,21 +1,42 @@
1import Reaction from '../../stores/lib/Reaction'; 1import { union } from 'lodash';
2 2
3export class FeatureStore { 3export class FeatureStore {
4 _actions = null; 4 _actions = null;
5 5
6 _reactions = null; 6 _reactions = null;
7 7
8 _listenToActions(actions) { 8 stop() {
9 if (this._actions) this._actions.forEach(a => a[0].off(a[1])); 9 this._stopActions();
10 this._actions = []; 10 this._stopReactions();
11 actions.forEach(a => this._actions.push(a));
12 this._actions.forEach(a => a[0].listen(a[1]));
13 } 11 }
14 12
15 _startReactions(reactions) { 13 // ACTIONS
16 if (this._reactions) this._reactions.forEach(r => r.stop()); 14
17 this._reactions = []; 15 _registerActions(actions) {
18 reactions.forEach(r => this._reactions.push(new Reaction(r))); 16 this._actions = union(this._actions, actions);
19 this._reactions.forEach(r => r.start()); 17 this._startActions();
18 }
19
20 _startActions(actions = this._actions) {
21 actions.forEach(a => a.start());
22 }
23
24 _stopActions(actions = this._actions) {
25 actions.forEach(a => a.stop());
26 }
27
28 // REACTIONS
29
30 _registerReactions(reactions) {
31 this._reactions = union(this._reactions, reactions);
32 this._startReactions();
33 }
34
35 _startReactions(reactions = this._reactions) {
36 reactions.forEach(r => r.start());
37 }
38
39 _stopReactions(reactions = this._reactions) {
40 reactions.forEach(r => r.stop());
20 } 41 }
21} 42}
diff --git a/src/features/workspaces/components/CreateWorkspaceForm.js b/src/features/workspaces/components/CreateWorkspaceForm.js
index 2c00ea63c..cddbb2b04 100644
--- a/src/features/workspaces/components/CreateWorkspaceForm.js
+++ b/src/features/workspaces/components/CreateWorkspaceForm.js
@@ -7,7 +7,7 @@ import injectSheet from 'react-jss';
7import Form from '../../../lib/Form'; 7import Form from '../../../lib/Form';
8import { required } from '../../../helpers/validation-helpers'; 8import { required } from '../../../helpers/validation-helpers';
9import { gaEvent } from '../../../lib/analytics'; 9import { gaEvent } from '../../../lib/analytics';
10import { GA_CATEGORY_WORKSPACES } from '../index'; 10import { GA_CATEGORY_WORKSPACES, workspaceStore } from '../index';
11 11
12const messages = defineMessages({ 12const messages = defineMessages({
13 submitButton: { 13 submitButton: {
@@ -82,7 +82,7 @@ class CreateWorkspaceForm extends Component {
82 {...form.$('name').bind()} 82 {...form.$('name').bind()}
83 showLabel={false} 83 showLabel={false}
84 onEnterKey={this.submitForm.bind(this, form)} 84 onEnterKey={this.submitForm.bind(this, form)}
85 focus 85 focus={workspaceStore.isUserAllowedToUseFeature}
86 /> 86 />
87 <Button 87 <Button
88 className={classes.submitButton} 88 className={classes.submitButton}
diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js
index ea601700e..e11513d1f 100644
--- a/src/features/workspaces/store.js
+++ b/src/features/workspaces/store.js
@@ -14,6 +14,8 @@ import {
14 updateWorkspaceRequest, 14 updateWorkspaceRequest,
15} from './api'; 15} from './api';
16import { WORKSPACES_ROUTES } from './index'; 16import { WORKSPACES_ROUTES } from './index';
17import { createReactions } from '../../stores/lib/Reaction';
18import { createActionBindings } from '../utils/ActionBinding';
17 19
18const debug = require('debug')('Franz:feature:workspaces:store'); 20const debug = require('debug')('Franz:feature:workspaces:store');
19 21
@@ -51,37 +53,74 @@ export default class WorkspacesStore extends FeatureStore {
51 return getUserWorkspacesRequest.wasExecuted && this.workspaces.length > 0; 53 return getUserWorkspacesRequest.wasExecuted && this.workspaces.length > 0;
52 } 54 }
53 55
56 @computed get isUserAllowedToUseFeature() {
57 return !this.isPremiumUpgradeRequired;
58 }
59
60 // ========== PRIVATE PROPERTIES ========= //
61
62 _wasDrawerOpenBeforeSettingsRoute = null;
63
64 _freeUserActions = [];
65
66 _premiumUserActions = [];
67
68 _allActions = [];
69
70 _freeUserReactions = [];
71
72 _premiumUserReactions = [];
73
74 _allReactions = [];
75
76 // ========== PUBLIC API ========= //
77
54 start(stores, actions) { 78 start(stores, actions) {
55 debug('WorkspacesStore::start'); 79 debug('WorkspacesStore::start');
56 this.stores = stores; 80 this.stores = stores;
57 this.actions = actions; 81 this.actions = actions;
58 82
59 this._listenToActions([ 83 // ACTIONS
84
85 this._freeUserActions = createActionBindings([
86 [workspaceActions.toggleWorkspaceDrawer, this._toggleWorkspaceDrawer],
87 [workspaceActions.openWorkspaceSettings, this._openWorkspaceSettings],
88 ]);
89 this._premiumUserActions = createActionBindings([
60 [workspaceActions.edit, this._edit], 90 [workspaceActions.edit, this._edit],
61 [workspaceActions.create, this._create], 91 [workspaceActions.create, this._create],
62 [workspaceActions.delete, this._delete], 92 [workspaceActions.delete, this._delete],
63 [workspaceActions.update, this._update], 93 [workspaceActions.update, this._update],
64 [workspaceActions.activate, this._setActiveWorkspace], 94 [workspaceActions.activate, this._setActiveWorkspace],
65 [workspaceActions.deactivate, this._deactivateActiveWorkspace], 95 [workspaceActions.deactivate, this._deactivateActiveWorkspace],
66 [workspaceActions.toggleWorkspaceDrawer, this._toggleWorkspaceDrawer],
67 [workspaceActions.openWorkspaceSettings, this._openWorkspaceSettings],
68 ]); 96 ]);
97 this._allActions = this._freeUserActions.concat(this._premiumUserActions);
98 this._registerActions(this._allActions);
69 99
70 this._startReactions([ 100 // REACTIONS
71 this._setWorkspaceBeingEditedReaction, 101
72 this._setActiveServiceOnWorkspaceSwitchReaction, 102 this._freeUserReactions = createReactions([
103 this._stopPremiumActionsAndReactions,
104 this._openDrawerWithSettingsReaction,
73 this._setFeatureEnabledReaction, 105 this._setFeatureEnabledReaction,
74 this._setIsPremiumFeatureReaction, 106 this._setIsPremiumFeatureReaction,
75 this._activateLastUsedWorkspaceReaction,
76 this._openDrawerWithSettingsReaction,
77 this._cleanupInvalidServiceReferences, 107 this._cleanupInvalidServiceReferences,
78 ]); 108 ]);
109 this._premiumUserReactions = createReactions([
110 this._setActiveServiceOnWorkspaceSwitchReaction,
111 this._activateLastUsedWorkspaceReaction,
112 this._setWorkspaceBeingEditedReaction,
113 ]);
114 this._allReactions = this._freeUserReactions.concat(this._premiumUserReactions);
115
116 this._registerReactions(this._allReactions);
79 117
80 getUserWorkspacesRequest.execute(); 118 getUserWorkspacesRequest.execute();
81 this.isFeatureActive = true; 119 this.isFeatureActive = true;
82 } 120 }
83 121
84 stop() { 122 stop() {
123 super.stop();
85 debug('WorkspacesStore::stop'); 124 debug('WorkspacesStore::stop');
86 this.isFeatureActive = false; 125 this.isFeatureActive = false;
87 this.activeWorkspace = null; 126 this.activeWorkspace = null;
@@ -104,9 +143,7 @@ export default class WorkspacesStore extends FeatureStore {
104 return workspace.services.map(id => services.one(id)).filter(s => !!s); 143 return workspace.services.map(id => services.one(id)).filter(s => !!s);
105 } 144 }
106 145
107 // ========== PRIVATE ========= // 146 // ========== PRIVATE METHODS ========= //
108
109 _wasDrawerOpenBeforeSettingsRoute = null;
110 147
111 _getWorkspaceById = id => this.workspaces.find(w => w.id === id); 148 _getWorkspaceById = id => this.workspaces.find(w => w.id === id);
112 149
@@ -273,4 +310,14 @@ export default class WorkspacesStore extends FeatureStore {
273 getUserWorkspacesRequest.execute(); 310 getUserWorkspacesRequest.execute();
274 } 311 }
275 }; 312 };
313
314 _stopPremiumActionsAndReactions = () => {
315 if (!this.isUserAllowedToUseFeature) {
316 this._stopActions(this._premiumUserActions);
317 this._stopReactions(this._premiumUserReactions);
318 } else {
319 this._startActions(this._premiumUserActions);
320 this._startReactions(this._premiumUserReactions);
321 }
322 }
276} 323}