aboutsummaryrefslogtreecommitdiffstats
path: root/packages/main/src/reactions
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-02-08 21:40:40 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-02-14 12:13:22 +0100
commitcc214bb1afd37068c2bbc93f33990ca93f9a900f (patch)
tree7707415d5c4e42b2542d3f482060d4935c892fe1 /packages/main/src/reactions
parentfeat: Unread message badges (diff)
downloadsophie-cc214bb1afd37068c2bbc93f33990ca93f9a900f.tar.gz
sophie-cc214bb1afd37068c2bbc93f33990ca93f9a900f.tar.zst
sophie-cc214bb1afd37068c2bbc93f33990ca93f9a900f.zip
feat: Load and switch services
Signed-off-by: Kristóf Marussy <kristof@marussy.com>
Diffstat (limited to 'packages/main/src/reactions')
-rw-r--r--packages/main/src/reactions/loadServices.ts157
1 files changed, 157 insertions, 0 deletions
diff --git a/packages/main/src/reactions/loadServices.ts b/packages/main/src/reactions/loadServices.ts
new file mode 100644
index 0000000..533ef95
--- /dev/null
+++ b/packages/main/src/reactions/loadServices.ts
@@ -0,0 +1,157 @@
1/*
2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import { autorun, reaction } from 'mobx';
22import { addDisposer } from 'mobx-state-tree';
23
24import type {
25 MainWindow,
26 Partition,
27 ServiceView,
28 ViewFactory,
29} from '../infrastructure/electron/types';
30import type MainStore from '../stores/MainStore';
31import type Service from '../stores/Service';
32import { getLogger } from '../utils/log';
33
34const log = getLogger('loadServices');
35
36export default function loadServices(
37 store: MainStore,
38 viewFactory: ViewFactory,
39): void {
40 const profilesToPartitions = new Map<string, Partition>();
41 const servicesToViews = new Map<string, ServiceView>();
42
43 type ReactionArgs = [
44 Map<string, Service>,
45 MainWindow | undefined,
46 Service | undefined,
47 unknown,
48 ];
49
50 const disposer = reaction(
51 (): ReactionArgs => [
52 new Map(store.shared.servicesById),
53 store.mainWindow,
54 store.visibleService,
55 [...store.shared.servicesById.values()].map((service) => [
56 service.settings.profile,
57 service.shouldBeLoaded,
58 ]),
59 ],
60 ([servicesById, mainWindow, visibleService]: ReactionArgs) => {
61 log.debug('Loading service partitions and views');
62
63 const partitionsToDispose = new Map(profilesToPartitions);
64 servicesById.forEach((service) => {
65 const {
66 settings: { profile },
67 } = service;
68 const { id: profileId } = profile;
69 partitionsToDispose.delete(profileId);
70 if (!profilesToPartitions.has(profileId)) {
71 log.debug('Creating partition for profile', profileId);
72 profilesToPartitions.set(
73 profileId,
74 viewFactory.createPartition(profile),
75 );
76 }
77 });
78
79 const viewsToDispose = new Map(servicesToViews);
80 servicesById.forEach((service, serviceId) => {
81 if (service.shouldBeLoaded) {
82 let view = servicesToViews.get(serviceId);
83 const {
84 settings: {
85 profile: { id: profileId },
86 },
87 } = service;
88 if (view === undefined || view.partitionId !== profileId) {
89 log.debug('Creating view for service', serviceId);
90 const partition = profilesToPartitions.get(profileId);
91 if (partition === undefined) {
92 throw new Error(`Missing Partition ${profileId}`);
93 }
94 view = viewFactory.createServiceView(service, partition);
95 view.setBounds(store.browserViewBounds);
96 servicesToViews.set(serviceId, view);
97 service.setServiceView(view);
98 const { urlToLoad } = service;
99 view.loadURL(urlToLoad).catch((error) => {
100 log.warn(
101 'Cannot URL',
102 urlToLoad,
103 'for service',
104 serviceId,
105 error,
106 );
107 });
108 } else {
109 viewsToDispose.delete(serviceId);
110 }
111 }
112 });
113
114 mainWindow?.setServiceView(visibleService?.serviceView);
115 log.debug('Visible service is', visibleService?.serviceView?.id);
116
117 viewsToDispose.forEach((view, serviceId) => {
118 const currentView = servicesToViews.get(serviceId);
119 if (currentView === view) {
120 servicesToViews.delete(serviceId);
121 const service = store.shared.servicesById.get(serviceId);
122 if (service !== undefined) {
123 service.setServiceView(undefined);
124 }
125 log.debug('Disposing view for service', serviceId);
126 } else {
127 log.debug('Changed partition for service', serviceId);
128 }
129 view.dispose();
130 });
131
132 partitionsToDispose.forEach((partition, profileId) => {
133 log.debug('Disposing partition for profile', profileId);
134 profilesToPartitions.delete(profileId);
135 partition.dispose();
136 });
137 },
138 {
139 fireImmediately: true,
140 },
141 );
142
143 const resizeDisposer = autorun(() => {
144 store.visibleService?.serviceView?.setBounds(store.browserViewBounds);
145 });
146
147 addDisposer(store, () => {
148 resizeDisposer();
149 disposer();
150 store.mainWindow?.setServiceView(undefined);
151 servicesToViews.forEach((serviceView, serviceId) => {
152 store.shared.servicesById.get(serviceId)?.setServiceView(undefined);
153 serviceView.dispose();
154 });
155 profilesToPartitions.forEach((partition) => partition.dispose());
156 });
157}