diff options
Diffstat (limited to 'src/features/workspaces/store.js')
-rw-r--r-- | src/features/workspaces/store.js | 276 |
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 @@ | |||
1 | import { | ||
2 | computed, | ||
3 | observable, | ||
4 | action, | ||
5 | } from 'mobx'; | ||
6 | import localStorage from 'mobx-localstorage'; | ||
7 | import { matchRoute } from '../../helpers/routing-helpers'; | ||
8 | import { workspaceActions } from './actions'; | ||
9 | import { FeatureStore } from '../utils/FeatureStore'; | ||
10 | import { | ||
11 | createWorkspaceRequest, | ||
12 | deleteWorkspaceRequest, | ||
13 | getUserWorkspacesRequest, | ||
14 | updateWorkspaceRequest, | ||
15 | } from './api'; | ||
16 | import { WORKSPACES_ROUTES } from './index'; | ||
17 | |||
18 | const debug = require('debug')('Franz:feature:workspaces:store'); | ||
19 | |||
20 | export 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 | } | ||