diff options
author | Dominik Guzei <dominik.guzei@gmail.com> | 2019-03-23 14:15:57 +0100 |
---|---|---|
committer | Dominik Guzei <dominik.guzei@gmail.com> | 2019-03-23 14:15:57 +0100 |
commit | 0af622e6e81a5aee64f839eeadd23b4a62b3cf62 (patch) | |
tree | 0e2264b83c3f57f2850062f6b7293bf06416bb49 /src/features/workspaces/store.js | |
parent | fix merge conflicts with latest develop (diff) | |
download | ferdium-app-0af622e6e81a5aee64f839eeadd23b4a62b3cf62.tar.gz ferdium-app-0af622e6e81a5aee64f839eeadd23b4a62b3cf62.tar.zst ferdium-app-0af622e6e81a5aee64f839eeadd23b4a62b3cf62.zip |
refactor state management for workspace feature
Diffstat (limited to 'src/features/workspaces/store.js')
-rw-r--r-- | src/features/workspaces/store.js | 196 |
1 files changed, 121 insertions, 75 deletions
diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js index f6b9b2ff4..883f36ffb 100644 --- a/src/features/workspaces/store.js +++ b/src/features/workspaces/store.js | |||
@@ -1,54 +1,39 @@ | |||
1 | import { observable, reaction, action } from 'mobx'; | 1 | import { |
2 | import Store from '../../stores/lib/Store'; | 2 | computed, |
3 | import CachedRequest from '../../stores/lib/CachedRequest'; | 3 | observable, |
4 | import Workspace from './models/Workspace'; | 4 | action, |
5 | } from 'mobx'; | ||
6 | import Reaction from '../../stores/lib/Reaction'; | ||
5 | import { matchRoute } from '../../helpers/routing-helpers'; | 7 | import { matchRoute } from '../../helpers/routing-helpers'; |
6 | import { workspaceActions } from './actions'; | 8 | import { workspaceActions } from './actions'; |
9 | import { | ||
10 | createWorkspaceRequest, | ||
11 | deleteWorkspaceRequest, | ||
12 | getUserWorkspacesRequest, | ||
13 | updateWorkspaceRequest, | ||
14 | } from './api'; | ||
7 | 15 | ||
8 | const debug = require('debug')('Franz:feature:workspaces'); | 16 | const debug = require('debug')('Franz:feature:workspaces:store'); |
9 | 17 | ||
10 | export default class WorkspacesStore extends Store { | 18 | export default class WorkspacesStore { |
11 | @observable allWorkspacesRequest = new CachedRequest(this.api, 'getUserWorkspaces'); | 19 | @observable isFeatureActive = false; |
12 | 20 | ||
13 | constructor(stores, api, actions, state) { | 21 | @observable activeWorkspace = null; |
14 | super(stores, api, actions); | 22 | |
15 | this.state = state; | 23 | @observable nextWorkspace = null; |
16 | } | 24 | |
25 | @observable workspaceBeingEdited = null; | ||
26 | |||
27 | @observable isSwitchingWorkspace = false; | ||
17 | 28 | ||
18 | setup() { | 29 | @observable isWorkspaceDrawerOpen = false; |
19 | debug('fetching workspaces'); | ||
20 | this.allWorkspacesRequest.execute(); | ||
21 | |||
22 | /** | ||
23 | * Update the state workspaces array when workspaces request has results. | ||
24 | */ | ||
25 | reaction( | ||
26 | () => this.allWorkspacesRequest.result, | ||
27 | workspaces => this._setWorkspaces(workspaces), | ||
28 | ); | ||
29 | /** | ||
30 | * Update the loading state when workspace request is executing. | ||
31 | */ | ||
32 | reaction( | ||
33 | () => this.allWorkspacesRequest.isExecuting, | ||
34 | isExecuting => this._setIsLoadingWorkspaces(isExecuting), | ||
35 | ); | ||
36 | /** | ||
37 | * Update the state with the workspace to be edited when route matches. | ||
38 | */ | ||
39 | reaction( | ||
40 | () => ({ | ||
41 | pathname: this.stores.router.location.pathname, | ||
42 | workspaces: this.state.workspaces, | ||
43 | }), | ||
44 | ({ pathname }) => { | ||
45 | const match = matchRoute('/settings/workspaces/edit/:id', pathname); | ||
46 | if (match) { | ||
47 | this.state.workspaceBeingEdited = this._getWorkspaceById(match.id); | ||
48 | } | ||
49 | }, | ||
50 | ); | ||
51 | 30 | ||
31 | @computed get workspaces() { | ||
32 | return getUserWorkspacesRequest.execute().result || []; | ||
33 | } | ||
34 | |||
35 | constructor() { | ||
36 | // Wire-up action handlers | ||
52 | workspaceActions.edit.listen(this._edit); | 37 | workspaceActions.edit.listen(this._edit); |
53 | workspaceActions.create.listen(this._create); | 38 | workspaceActions.create.listen(this._create); |
54 | workspaceActions.delete.listen(this._delete); | 39 | workspaceActions.delete.listen(this._delete); |
@@ -57,28 +42,62 @@ export default class WorkspacesStore extends Store { | |||
57 | workspaceActions.deactivate.listen(this._deactivateActiveWorkspace); | 42 | workspaceActions.deactivate.listen(this._deactivateActiveWorkspace); |
58 | workspaceActions.toggleWorkspaceDrawer.listen(this._toggleWorkspaceDrawer); | 43 | workspaceActions.toggleWorkspaceDrawer.listen(this._toggleWorkspaceDrawer); |
59 | workspaceActions.openWorkspaceSettings.listen(this._openWorkspaceSettings); | 44 | workspaceActions.openWorkspaceSettings.listen(this._openWorkspaceSettings); |
45 | |||
46 | // Register and start reactions | ||
47 | this._registerReactions([ | ||
48 | this._updateWorkspaceBeingEdited, | ||
49 | this._updateActiveServiceOnWorkspaceSwitch, | ||
50 | ]); | ||
60 | } | 51 | } |
61 | 52 | ||
62 | _getWorkspaceById = id => this.state.workspaces.find(w => w.id === id); | 53 | start(stores, actions) { |
54 | debug('WorkspacesStore::start'); | ||
55 | this.stores = stores; | ||
56 | this.actions = actions; | ||
57 | this._reactions.forEach(r => r.start()); | ||
58 | this.isFeatureActive = true; | ||
59 | } | ||
63 | 60 | ||
64 | @action _setWorkspaces = (workspaces) => { | 61 | stop() { |
65 | debug('setting user workspaces', workspaces.slice()); | 62 | debug('WorkspacesStore::stop'); |
66 | this.state.workspaces = workspaces.map(data => new Workspace(data)); | 63 | this._reactions.forEach(r => r.stop()); |
67 | }; | 64 | this.isFeatureActive = false; |
65 | } | ||
68 | 66 | ||
69 | @action _setIsLoadingWorkspaces = (isLoading) => { | 67 | filterServicesByActiveWorkspace = (services) => { |
70 | this.state.isLoadingWorkspaces = isLoading; | 68 | const { activeWorkspace, isFeatureActive } = this; |
69 | |||
70 | if (!isFeatureActive) return services; | ||
71 | if (activeWorkspace) { | ||
72 | return services.filter(s => ( | ||
73 | activeWorkspace.services.includes(s.id) | ||
74 | )); | ||
75 | } | ||
76 | return services; | ||
71 | }; | 77 | }; |
72 | 78 | ||
79 | // ========== PRIVATE ========= // | ||
80 | |||
81 | _reactions = []; | ||
82 | |||
83 | _registerReactions(reactions) { | ||
84 | reactions.forEach(r => this._reactions.push(new Reaction(r))); | ||
85 | } | ||
86 | |||
87 | _getWorkspaceById = id => this.workspaces.find(w => w.id === id); | ||
88 | |||
89 | // Actions | ||
90 | |||
73 | @action _edit = ({ workspace }) => { | 91 | @action _edit = ({ workspace }) => { |
74 | this.stores.router.push(`/settings/workspaces/edit/${workspace.id}`); | 92 | this.stores.router.push(`/settings/workspaces/edit/${workspace.id}`); |
75 | }; | 93 | }; |
76 | 94 | ||
77 | @action _create = async ({ name }) => { | 95 | @action _create = async ({ name }) => { |
78 | try { | 96 | try { |
79 | const result = await this.api.createWorkspace(name); | 97 | const workspace = await createWorkspaceRequest.execute(name); |
80 | const workspace = new Workspace(result); | 98 | await getUserWorkspacesRequest.patch((result) => { |
81 | this.state.workspaces.push(workspace); | 99 | result.push(workspace); |
100 | }); | ||
82 | this._edit({ workspace }); | 101 | this._edit({ workspace }); |
83 | } catch (error) { | 102 | } catch (error) { |
84 | throw error; | 103 | throw error; |
@@ -87,8 +106,10 @@ export default class WorkspacesStore extends Store { | |||
87 | 106 | ||
88 | @action _delete = async ({ workspace }) => { | 107 | @action _delete = async ({ workspace }) => { |
89 | try { | 108 | try { |
90 | await this.api.deleteWorkspace(workspace); | 109 | await deleteWorkspaceRequest.execute(workspace); |
91 | this.state.workspaces.remove(workspace); | 110 | await getUserWorkspacesRequest.patch((result) => { |
111 | result.remove(workspace); | ||
112 | }); | ||
92 | this.stores.router.push('/settings/workspaces'); | 113 | this.stores.router.push('/settings/workspaces'); |
93 | } catch (error) { | 114 | } catch (error) { |
94 | throw error; | 115 | throw error; |
@@ -97,9 +118,11 @@ export default class WorkspacesStore extends Store { | |||
97 | 118 | ||
98 | @action _update = async ({ workspace }) => { | 119 | @action _update = async ({ workspace }) => { |
99 | try { | 120 | try { |
100 | await this.api.updateWorkspace(workspace); | 121 | await updateWorkspaceRequest.execute(workspace); |
101 | const localWorkspace = this.state.workspaces.find(ws => ws.id === workspace.id); | 122 | await getUserWorkspacesRequest.patch((result) => { |
102 | Object.assign(localWorkspace, workspace); | 123 | const localWorkspace = result.find(ws => ws.id === workspace.id); |
124 | Object.assign(localWorkspace, workspace); | ||
125 | }); | ||
103 | this.stores.router.push('/settings/workspaces'); | 126 | this.stores.router.push('/settings/workspaces'); |
104 | } catch (error) { | 127 | } catch (error) { |
105 | throw error; | 128 | throw error; |
@@ -107,33 +130,56 @@ export default class WorkspacesStore extends Store { | |||
107 | }; | 130 | }; |
108 | 131 | ||
109 | @action _setActiveWorkspace = ({ workspace }) => { | 132 | @action _setActiveWorkspace = ({ workspace }) => { |
110 | Object.assign(this.state, { | 133 | // Indicate that we are switching to another workspace |
111 | isSwitchingWorkspace: true, | 134 | this.isSwitchingWorkspace = true; |
112 | nextWorkspace: workspace, | 135 | this.nextWorkspace = workspace; |
113 | }); | 136 | // Delay switching to next workspace so that the services loading does not drag down UI |
114 | setTimeout(() => { this.state.activeWorkspace = workspace; }, 100); | 137 | setTimeout(() => { this.activeWorkspace = workspace; }, 100); |
138 | // Indicate that we are done switching to the next workspace | ||
115 | setTimeout(() => { | 139 | setTimeout(() => { |
116 | Object.assign(this.state, { | 140 | this.isSwitchingWorkspace = false; |
117 | isSwitchingWorkspace: false, | 141 | this.nextWorkspace = null; |
118 | nextWorkspace: null, | ||
119 | }); | ||
120 | }, 1000); | 142 | }, 1000); |
121 | }; | 143 | }; |
122 | 144 | ||
123 | @action _deactivateActiveWorkspace = () => { | 145 | @action _deactivateActiveWorkspace = () => { |
124 | Object.assign(this.state, { | 146 | // Indicate that we are switching to default workspace |
125 | isSwitchingWorkspace: true, | 147 | this.isSwitchingWorkspace = true; |
126 | nextWorkspace: null, | 148 | this.nextWorkspace = null; |
127 | }); | 149 | // Delay switching to next workspace so that the services loading does not drag down UI |
128 | setTimeout(() => { this.state.activeWorkspace = null; }, 100); | 150 | setTimeout(() => { this.activeWorkspace = null; }, 100); |
129 | setTimeout(() => { this.state.isSwitchingWorkspace = false; }, 1000); | 151 | // Indicate that we are done switching to the default workspace |
152 | setTimeout(() => { this.isSwitchingWorkspace = false; }, 1000); | ||
130 | }; | 153 | }; |
131 | 154 | ||
132 | @action _toggleWorkspaceDrawer = () => { | 155 | @action _toggleWorkspaceDrawer = () => { |
133 | this.state.isWorkspaceDrawerOpen = !this.state.isWorkspaceDrawerOpen; | 156 | this.isWorkspaceDrawerOpen = !this.isWorkspaceDrawerOpen; |
134 | }; | 157 | }; |
135 | 158 | ||
136 | @action _openWorkspaceSettings = () => { | 159 | @action _openWorkspaceSettings = () => { |
137 | this.actions.ui.openSettings({ path: 'workspaces' }); | 160 | this.actions.ui.openSettings({ path: 'workspaces' }); |
138 | }; | 161 | }; |
162 | |||
163 | // Reactions | ||
164 | |||
165 | _updateWorkspaceBeingEdited = () => { | ||
166 | const { pathname } = this.stores.router.location; | ||
167 | const match = matchRoute('/settings/workspaces/edit/:id', pathname); | ||
168 | if (match) { | ||
169 | this.workspaceBeingEdited = this._getWorkspaceById(match.id); | ||
170 | } | ||
171 | }; | ||
172 | |||
173 | _updateActiveServiceOnWorkspaceSwitch = () => { | ||
174 | if (!this.isFeatureActive) return; | ||
175 | if (this.activeWorkspace) { | ||
176 | const services = this.stores.services.allDisplayed; | ||
177 | const activeService = services.find(s => s.isActive); | ||
178 | const workspaceServices = this.filterServicesByActiveWorkspace(services); | ||
179 | const isActiveServiceInWorkspace = workspaceServices.includes(activeService); | ||
180 | if (!isActiveServiceInWorkspace) { | ||
181 | this.actions.service.setActive({ serviceId: workspaceServices[0].id }); | ||
182 | } | ||
183 | } | ||
184 | }; | ||
139 | } | 185 | } |