aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/workspaces/store.js
diff options
context:
space:
mode:
authorLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-04-11 16:54:01 +0200
committerLibravatar Stefan Malzner <stefan@adlk.io>2019-04-11 16:54:01 +0200
commit47c1c99d893517efc679ab29d675cc0bf44be8be (patch)
tree9cab9697096bef0ce56d8ee8709bc1c2c3a42deb /src/features/workspaces/store.js
parenttest package order (diff)
downloadferdium-app-47c1c99d893517efc679ab29d675cc0bf44be8be.tar.gz
ferdium-app-47c1c99d893517efc679ab29d675cc0bf44be8be.tar.zst
ferdium-app-47c1c99d893517efc679ab29d675cc0bf44be8be.zip
feat(App): Added Workspaces for all your daily routines 🥳
* merge default and fetched feature configs * ignore intellij project files * basic setup for workspaces feature * define workspaces as premium feature * add workspaces menu item in settings dialog * basic setup of workspaces settings screen * fix eslint error * assign react key prop to workspace items * add styles for workspace table * setup logic to display workspace edit page * consolidate workspace feature for further development * prepare basic workspace edit form * add on enter key handler for form input component * add form for creating workspaces * small fixes * adds flow for deleting workspaces * stop tracking google analytics in components * pin gulp-sass-variables version to 1.1.1 * fix merge conflict * fix bug in form input library * improve workspace form setup * finish basic workspace settings * finish workspaces mvp * fix eslint issues * remove dev logs * detach service when underlying webview unmounts * disable no-param-reassign eslint rule * add workspace drawer * change workspace switch shortcuts to start with zero * add workspace drawer toggle menu item and shortcut * improve workspace switching ux * style add workspace icon in drawer like the sidebar icons * improve workspace drawer layout * add i18n messages for service loading and workspace switching * small fixes * add tooltip to add workspace button in drawer * add workspaces count badge in settings navigation * fix merge conflicts with latest develop * refactor state management for workspace feature * reset api requests when workspace feature is stopped * hide workspace feature if it is disabled * handle get workspaces request errors in the ui * show infobox when updating workspaces * indicate any server interaction with spinners and infoboxes * add analytic events for workspace actions * improve styling of workspace switch indicator * add workspace premium notice to dashboard * add workspace feature info in drawer for free users * add workspace premium badge in settings nav * fix premium workspace badge in settings menu for light theme * fix active workspaces settings premium badge in light theme * give upgrade account button a bit more padding * add open last used workspace logic * use mobx-localstorage directly in the store * fix wrong workspace tooltip shortcut in sidebar * fix bug in workspace feature initialization * show workspaces intro in drawer when user has none yet * fix issues for users that have workspace but downgraded to free * border radius for premium intro in workspace settings * close workspace drawer after clicking on a workspace * add hover effect for drawer workspace items * ensure drawer is open on workspace settings routes * add small text label for adding new workspace to drawer * make workspace settings list items taller * refactor workspace table css away from legacy styles * render workspace service list like services + toggle * change plus icon in workspace drawer to settings icon * autofocus create workspace input field * add css transition to drawer workspace item hover * fix drawer add workspace label styles * refactors workspace theme vars into object structure * improve contrast of workspace switching indicator * added generic pro badge component for settings nav * add premium badge to workspace drawer headline * add context menu for workspace drawer items * handle deleted services that are attached to workspaces
Diffstat (limited to 'src/features/workspaces/store.js')
-rw-r--r--src/features/workspaces/store.js276
1 files changed, 276 insertions, 0 deletions
diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js
new file mode 100644
index 000000000..ea601700e
--- /dev/null
+++ b/src/features/workspaces/store.js
@@ -0,0 +1,276 @@
1import {
2 computed,
3 observable,
4 action,
5} from 'mobx';
6import localStorage from 'mobx-localstorage';
7import { matchRoute } from '../../helpers/routing-helpers';
8import { workspaceActions } from './actions';
9import { FeatureStore } from '../utils/FeatureStore';
10import {
11 createWorkspaceRequest,
12 deleteWorkspaceRequest,
13 getUserWorkspacesRequest,
14 updateWorkspaceRequest,
15} from './api';
16import { WORKSPACES_ROUTES } from './index';
17
18const debug = require('debug')('Franz:feature:workspaces:store');
19
20export default class WorkspacesStore extends FeatureStore {
21 @observable isFeatureEnabled = false;
22
23 @observable isFeatureActive = false;
24
25 @observable isPremiumFeature = true;
26
27 @observable isPremiumUpgradeRequired = true;
28
29 @observable activeWorkspace = null;
30
31 @observable nextWorkspace = null;
32
33 @observable workspaceBeingEdited = null;
34
35 @observable isSwitchingWorkspace = false;
36
37 @observable isWorkspaceDrawerOpen = false;
38
39 @observable isSettingsRouteActive = null;
40
41 @computed get workspaces() {
42 if (!this.isFeatureActive) return [];
43 return getUserWorkspacesRequest.result || [];
44 }
45
46 @computed get settings() {
47 return localStorage.getItem('workspaces') || {};
48 }
49
50 @computed get userHasWorkspaces() {
51 return getUserWorkspacesRequest.wasExecuted && this.workspaces.length > 0;
52 }
53
54 start(stores, actions) {
55 debug('WorkspacesStore::start');
56 this.stores = stores;
57 this.actions = actions;
58
59 this._listenToActions([
60 [workspaceActions.edit, this._edit],
61 [workspaceActions.create, this._create],
62 [workspaceActions.delete, this._delete],
63 [workspaceActions.update, this._update],
64 [workspaceActions.activate, this._setActiveWorkspace],
65 [workspaceActions.deactivate, this._deactivateActiveWorkspace],
66 [workspaceActions.toggleWorkspaceDrawer, this._toggleWorkspaceDrawer],
67 [workspaceActions.openWorkspaceSettings, this._openWorkspaceSettings],
68 ]);
69
70 this._startReactions([
71 this._setWorkspaceBeingEditedReaction,
72 this._setActiveServiceOnWorkspaceSwitchReaction,
73 this._setFeatureEnabledReaction,
74 this._setIsPremiumFeatureReaction,
75 this._activateLastUsedWorkspaceReaction,
76 this._openDrawerWithSettingsReaction,
77 this._cleanupInvalidServiceReferences,
78 ]);
79
80 getUserWorkspacesRequest.execute();
81 this.isFeatureActive = true;
82 }
83
84 stop() {
85 debug('WorkspacesStore::stop');
86 this.isFeatureActive = false;
87 this.activeWorkspace = null;
88 this.nextWorkspace = null;
89 this.workspaceBeingEdited = null;
90 this.isSwitchingWorkspace = false;
91 this.isWorkspaceDrawerOpen = false;
92 }
93
94 filterServicesByActiveWorkspace = (services) => {
95 const { activeWorkspace, isFeatureActive } = this;
96 if (isFeatureActive && activeWorkspace) {
97 return this.getWorkspaceServices(activeWorkspace);
98 }
99 return services;
100 };
101
102 getWorkspaceServices(workspace) {
103 const { services } = this.stores;
104 return workspace.services.map(id => services.one(id)).filter(s => !!s);
105 }
106
107 // ========== PRIVATE ========= //
108
109 _wasDrawerOpenBeforeSettingsRoute = null;
110
111 _getWorkspaceById = id => this.workspaces.find(w => w.id === id);
112
113 _updateSettings = (changes) => {
114 localStorage.setItem('workspaces', {
115 ...this.settings,
116 ...changes,
117 });
118 };
119
120 // Actions
121
122 @action _edit = ({ workspace }) => {
123 this.stores.router.push(`/settings/workspaces/edit/${workspace.id}`);
124 };
125
126 @action _create = async ({ name }) => {
127 try {
128 const workspace = await createWorkspaceRequest.execute(name);
129 await getUserWorkspacesRequest.result.push(workspace);
130 this._edit({ workspace });
131 } catch (error) {
132 throw error;
133 }
134 };
135
136 @action _delete = async ({ workspace }) => {
137 try {
138 await deleteWorkspaceRequest.execute(workspace);
139 await getUserWorkspacesRequest.result.remove(workspace);
140 this.stores.router.push('/settings/workspaces');
141 } catch (error) {
142 throw error;
143 }
144 };
145
146 @action _update = async ({ workspace }) => {
147 try {
148 await updateWorkspaceRequest.execute(workspace);
149 // Path local result optimistically
150 const localWorkspace = this._getWorkspaceById(workspace.id);
151 Object.assign(localWorkspace, workspace);
152 this.stores.router.push('/settings/workspaces');
153 } catch (error) {
154 throw error;
155 }
156 };
157
158 @action _setActiveWorkspace = ({ workspace }) => {
159 // Indicate that we are switching to another workspace
160 this.isSwitchingWorkspace = true;
161 this.nextWorkspace = workspace;
162 // Delay switching to next workspace so that the services loading does not drag down UI
163 setTimeout(() => {
164 this.activeWorkspace = workspace;
165 this._updateSettings({ lastActiveWorkspace: workspace.id });
166 }, 100);
167 // Indicate that we are done switching to the next workspace
168 setTimeout(() => {
169 this.isSwitchingWorkspace = false;
170 this.nextWorkspace = null;
171 }, 1000);
172 };
173
174 @action _deactivateActiveWorkspace = () => {
175 // Indicate that we are switching to default workspace
176 this.isSwitchingWorkspace = true;
177 this.nextWorkspace = null;
178 this._updateSettings({ lastActiveWorkspace: null });
179 // Delay switching to next workspace so that the services loading does not drag down UI
180 setTimeout(() => {
181 this.activeWorkspace = null;
182 }, 100);
183 // Indicate that we are done switching to the default workspace
184 setTimeout(() => { this.isSwitchingWorkspace = false; }, 1000);
185 };
186
187 @action _toggleWorkspaceDrawer = () => {
188 this.isWorkspaceDrawerOpen = !this.isWorkspaceDrawerOpen;
189 };
190
191 @action _openWorkspaceSettings = () => {
192 this.actions.ui.openSettings({ path: 'workspaces' });
193 };
194
195 // Reactions
196
197 _setFeatureEnabledReaction = () => {
198 const { isWorkspaceEnabled } = this.stores.features.features;
199 this.isFeatureEnabled = isWorkspaceEnabled;
200 };
201
202 _setIsPremiumFeatureReaction = () => {
203 const { features, user } = this.stores;
204 const { isPremium } = user.data;
205 const { isWorkspacePremiumFeature } = features.features;
206 this.isPremiumFeature = isWorkspacePremiumFeature;
207 this.isPremiumUpgradeRequired = isWorkspacePremiumFeature && !isPremium;
208 };
209
210 _setWorkspaceBeingEditedReaction = () => {
211 const { pathname } = this.stores.router.location;
212 const match = matchRoute('/settings/workspaces/edit/:id', pathname);
213 if (match) {
214 this.workspaceBeingEdited = this._getWorkspaceById(match.id);
215 }
216 };
217
218 _setActiveServiceOnWorkspaceSwitchReaction = () => {
219 if (!this.isFeatureActive) return;
220 if (this.activeWorkspace) {
221 const services = this.stores.services.allDisplayed;
222 const activeService = services.find(s => s.isActive);
223 const workspaceServices = this.getWorkspaceServices(this.activeWorkspace);
224 if (workspaceServices.length <= 0) return;
225 const isActiveServiceInWorkspace = workspaceServices.includes(activeService);
226 if (!isActiveServiceInWorkspace) {
227 this.actions.service.setActive({ serviceId: workspaceServices[0].id });
228 }
229 }
230 };
231
232 _activateLastUsedWorkspaceReaction = () => {
233 if (!this.activeWorkspace && this.userHasWorkspaces) {
234 const { lastActiveWorkspace } = this.settings;
235 if (lastActiveWorkspace) {
236 const workspace = this._getWorkspaceById(lastActiveWorkspace);
237 if (workspace) this._setActiveWorkspace({ workspace });
238 }
239 }
240 };
241
242 _openDrawerWithSettingsReaction = () => {
243 const { router } = this.stores;
244 const isWorkspaceSettingsRoute = router.location.pathname.includes(WORKSPACES_ROUTES.ROOT);
245 const isSwitchingToSettingsRoute = !this.isSettingsRouteActive && isWorkspaceSettingsRoute;
246 const isLeavingSettingsRoute = !isWorkspaceSettingsRoute && this.isSettingsRouteActive;
247
248 if (isSwitchingToSettingsRoute) {
249 this.isSettingsRouteActive = true;
250 this._wasDrawerOpenBeforeSettingsRoute = this.isWorkspaceDrawerOpen;
251 if (!this._wasDrawerOpenBeforeSettingsRoute) {
252 workspaceActions.toggleWorkspaceDrawer();
253 }
254 } else if (isLeavingSettingsRoute) {
255 this.isSettingsRouteActive = false;
256 if (!this._wasDrawerOpenBeforeSettingsRoute && this.isWorkspaceDrawerOpen) {
257 workspaceActions.toggleWorkspaceDrawer();
258 }
259 }
260 };
261
262 _cleanupInvalidServiceReferences = () => {
263 const { services } = this.stores;
264 let invalidServiceReferencesExist = false;
265 this.workspaces.forEach((workspace) => {
266 workspace.services.forEach((serviceId) => {
267 if (!services.one(serviceId)) {
268 invalidServiceReferencesExist = true;
269 }
270 });
271 });
272 if (invalidServiceReferencesExist) {
273 getUserWorkspacesRequest.execute();
274 }
275 };
276}