// // Owns the sidebar's controllers and the polling that keeps them current. // // Apple Container publishes no event stream, so the sidebar polls on an // interval while it is visible. Controllers only reload their tree when the // data actually changed, which keeps selection and expansion intact. // const { SystemController } = require("./TreeViews/System"); const { ContainersController } = require("./TreeViews/Containers"); const { ImagesController } = require("./TreeViews/Images"); const { VolumesController } = require("./TreeViews/Volumes"); const { NetworksController } = require("./TreeViews/Networks"); const { ContainerCLI } = require("../Interfaces/ContainerCLI"); const notify = require("../Utilities/Notify"); class ContainerSidebar { constructor() { this.controllers = null; this.timer = null; this.refreshing = false; this.watchers = new CompositeDisposable(); this.watchers.add( nova.config.onDidChange("apple_container.sidebar.disabled", () => this.restart()) ); this.watchers.add( nova.config.onDidChange("apple_container.sidebar.refresh", () => this.schedule()) ); this.start(); } start() { if (nova.config.get("apple_container.sidebar.disabled", "boolean")) { return; } this.system = new SystemController(this); this.containers = new ContainersController(this); this.images = new ImagesController(this); this.volumes = new VolumesController(this); this.networks = new NetworksController(this); this.controllers = [this.system, this.containers, this.images, this.volumes, this.networks]; this.bootstrap(); this.schedule(); } stop() { if (this.timer) { clearInterval(this.timer); this.timer = null; } if (this.controllers) { for (let controller of this.controllers) { controller.dispose(); } this.controllers = null; } } dispose() { this.stop(); this.watchers.dispose(); } restart() { this.stop(); this.start(); } // First pass: start the services when asked to, then fill every section. async bootstrap() { if (nova.config.get("apple_container.sidebar.autostart", "boolean")) { let status = await ContainerCLI.systemStatus(); if (status.status !== "running") { try { await ContainerCLI.systemStart(); } catch (error) { notify.configFailure( "Unable to start container services", error, "apple-container-autostart" ); } } } return this.refresh({ force: true }); } schedule() { if (this.timer) { clearInterval(this.timer); this.timer = null; } if (!this.controllers) { return; } let seconds = nova.config.get("apple_container.sidebar.refresh", "number"); if (typeof seconds !== "number" || seconds <= 0) { return; } this.timer = setInterval(() => this.refresh(), Math.max(1, seconds) * 1000); } // One refresh at a time, and only for sections the user can actually see. async refresh(options = {}) { if (!this.controllers || this.refreshing) { return; } this.refreshing = true; try { await this.system.refresh(options); if (!this.system.running) { for (let controller of this.sections()) { controller.clear(); } return; } let pending = this.sections() .filter((controller) => options.force || controller.visible) .map((controller) => controller.refresh(options)); await Promise.all(pending); } finally { this.refreshing = false; } } sections() { return [this.containers, this.images, this.volumes, this.networks]; } reload() { if (!this.controllers) { return Promise.resolve(); } for (let controller of this.controllers) { controller.invalidate(); } return this.refresh({ force: true }); } reloadContainers() { if (this.containers) { this.containers.reload(); } } } module.exports = { ContainerSidebar };