/* * Copyright (C) 2019 Red Hat, Inc. * SPDX-License-Identifier: LGPL-2.1-or-later */ /* NOTIFICATIONS A page can broadcast notifications to the rest of Cockpit. For example, the "Software updates" page can send out a notification when it detects that software updates are available. The shell will then highlight the menu entry for "Software updates" and the "System" overview page will also mention it in its "Operating system" section. The details are all still experimental and subject to change. As a first step, there are only simple "page status" notifications. When we address "event" style notifications, page status notifications might become a special case of them. Or not. A page status is either null, or a JSON value with the following fields: - type (string, optional) If specified, one of "info", "warning", "error". The shell will put an appropriate icon next to the navigation entry for this page, for example. Omitting 'type' means that the page has no special status and is the same as using "null" as the whole status value. This can be used to broadcast values in the 'details' field to other pages without forcing an icon into the navigation menu. - title (string, optional) A short, human readable, localized description of the status, suitable for a tooltip. - details (JSON value, optional) An arbitrary value. The "System" overview page might monitor a couple of pages for their status and it will use 'details' to display a richer version of the status than possible with just type and title. The recognized properties are: * icon: custom icon name (defaults to standard icon corresponding to type) * pficon: PatternFly icon name; e.g. "enhancement", "bug", "security", "spinner", "check"; see get_pficon() in pkg/systemd/page-status.jsx * link: custom link target (defaults to page name); if false, the notification will not be a link Usage: import { page_status } from "notifications"; - page_status.set_own(STATUS) Sets the status of the page making the call, completely overwriting the current status. For example, page_status.set_own({ type: "info", title: _("Software updates available"), details: { num_updates: 10, num_security_updates: 5 } }); page_status.set_own({ type: null title: _("System is up to date"), details: { last_check: 81236457 } }); Calling this function with the same STATUS value multiple times is cheap: only the first call will actually broadcast the new status. - page_status.get(PAGE, [HOST]) Retrieves the current status of page PAGE of HOST. When HOST is omitted, it defaults to the default host of the calling page. PAGE is the same string that Cockpit uses in its URLs to identify a page, such as "system/terminal" or "storage". Until the page_status object is fully initialized (see 'valid' below), this function will return 'undefined'. - page_status.addEventListener("changed", event => { ... }) The "changed" event is emitted whenever any page status changes. - page_status.valid The page_status objects needs to initialize itself asynchronously and 'valid' is false until this is done. When 'valid' changes to true, a "changed" event is emitted. */ import cockpit, { JsonValue, JsonObject } from "cockpit"; import { dequal } from 'dequal/lite'; export interface Status { type?: string | null; title?: string; details?: JsonObject; } class PageStatus extends EventTarget { valid: boolean = false; cur_own: Status | null = null; constructor() { super(); window.addEventListener("storage", event => { if (event.key === "cockpit:page_status") { this.dispatchEvent(new CustomEvent("changed")); } }); cockpit.transport.wait(() => { this.valid = true; this.dispatchEvent(new CustomEvent("changed")); }); } get(page: string, host?: string): Status | null | undefined { let page_status; if (!this.valid) return undefined; if (host === undefined) host = cockpit.transport.host; try { page_status = JSON.parse(sessionStorage.getItem("cockpit:page_status") || "{}"); } catch { return null; } if (page_status?.[host]) return page_status[host][page] || null; return null; } set_own(status: Status | null) { if (!dequal(status, this.cur_own)) { this.cur_own = status; cockpit.transport.control("notify", { page_status: status as JsonValue }); } } } export const page_status = new PageStatus();