/* * Copyright (C) 2022 Kristóf Marussy * * This file is part of Sophie. * * Sophie is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * SPDX-License-Identifier: AGPL-3.0-only */ import { reaction } from 'mobx'; import { addDisposer } from 'mobx-state-tree'; import type { MainWindow, Partition, ServiceView, ViewFactory, } from '../infrastructure/electron/types'; import type MainStore from '../stores/MainStore'; import type Service from '../stores/Service'; import getLogger from '../utils/getLogger'; const log = getLogger('loadServices'); export default function loadServices( store: MainStore, viewFactory: ViewFactory, ): void { const profilesToPartitions = new Map(); const servicesToViews = new Map(); type ReactionArgs = [ Map, MainWindow | undefined, Service | undefined, unknown, ]; const disposer = reaction( (): ReactionArgs => [ new Map(store.shared.servicesById), store.mainWindow, store.visibleService, [...store.shared.servicesById.values()].map((service) => [ service.settings.profile, service.shouldBeLoaded, ]), ], ([servicesById, mainWindow, visibleService]: ReactionArgs) => { log.debug('Loading service partitions and views'); const partitionsToDispose = new Map(profilesToPartitions); servicesById.forEach((service) => { const { settings: { profile }, } = service; const { id: profileId } = profile; partitionsToDispose.delete(profileId); if (!profilesToPartitions.has(profileId)) { log.debug('Creating partition for profile', profileId); profilesToPartitions.set( profileId, viewFactory.createPartition(profile), ); } }); const viewsToDispose = new Map(servicesToViews); servicesById.forEach((service, serviceId) => { if (!service.shouldBeLoaded) { return; } let view = servicesToViews.get(serviceId); const { settings: { profile: { id: profileId }, }, } = service; if (view === undefined || view.partitionId !== profileId) { log.debug('Creating view for service', serviceId); const partition = profilesToPartitions.get(profileId); if (partition === undefined) { throw new Error(`Missing Partition ${profileId}`); } view = viewFactory.createServiceView(service, partition); servicesToViews.set(serviceId, view); service.setServiceView(view); const { urlToLoad } = service; view.loadURL(urlToLoad); } else { viewsToDispose.delete(serviceId); } }); mainWindow?.setServiceView(visibleService?.serviceView); log.debug('Visible service is', visibleService?.serviceView?.id); viewsToDispose.forEach((view, serviceId) => { const currentView = servicesToViews.get(serviceId); if (currentView === view) { servicesToViews.delete(serviceId); const service = store.shared.servicesById.get(serviceId); if (service !== undefined) { service.setServiceView(undefined); } log.debug('Disposing view for service', serviceId); } else { log.debug('Changed partition for service', serviceId); } view.dispose(); }); partitionsToDispose.forEach((partition, profileId) => { log.debug('Disposing partition for profile', profileId); profilesToPartitions.delete(profileId); partition.dispose(); }); }, { fireImmediately: true, }, ); addDisposer(store, () => { disposer(); store.mainWindow?.setServiceView(undefined); servicesToViews.forEach((serviceView, serviceId) => { store.shared.servicesById.get(serviceId)?.setServiceView(undefined); serviceView.dispose(); }); profilesToPartitions.forEach((partition) => partition.dispose()); }); }