LCOV - code coverage report
Current view: top level - pkg/lib - notifications.ts Coverage Total Hit
Test: cockpit Lines: 100.0 % 39 39
Test Date: 2026-07-17 12:03:54

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2019 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6              : /* NOTIFICATIONS
       7              : 
       8              : A page can broadcast notifications to the rest of Cockpit.  For
       9              : example, the "Software updates" page can send out a notification when
      10              : it detects that software updates are available.  The shell will then
      11              : highlight the menu entry for "Software updates" and the "System"
      12              : overview page will also mention it in its "Operating system" section.
      13              : 
      14              : The details are all still experimental and subject to change.
      15              : 
      16              : As a first step, there are only simple "page status" notifications.
      17              : When we address "event" style notifications, page status notifications
      18              : might become a special case of them.  Or not.
      19              : 
      20              : A page status is either null, or a JSON value with the following
      21              : fields:
      22              : 
      23              :  - type (string, optional)
      24              : 
      25              :  If specified, one of "info", "warning", "error".  The shell will put
      26              :  an appropriate icon next to the navigation entry for this page, for
      27              :  example.
      28              : 
      29              :  Omitting 'type' means that the page has no special status and is the
      30              :  same as using "null" as the whole status value.  This can be used to
      31              :  broadcast values in the 'details' field to other pages without
      32              :  forcing an icon into the navigation menu.
      33              : 
      34              :  - title (string, optional)
      35              : 
      36              :  A short, human readable, localized description of the status,
      37              :  suitable for a tooltip.
      38              : 
      39              :  - details (JSON value, optional)
      40              : 
      41              :  An arbitrary value.  The "System" overview page might monitor a
      42              :  couple of pages for their status and it will use 'details' to display
      43              :  a richer version of the status than possible with just type and
      44              :  title. The recognized properties are:
      45              : 
      46              :    * icon: custom icon name (defaults to standard icon corresponding to type)
      47              :    * pficon: PatternFly icon name; e.g. "enhancement", "bug", "security", "spinner", "check";
      48              :      see get_pficon() in pkg/systemd/page-status.jsx
      49              :    * link: custom link target (defaults to page name); if false, the
      50              :      notification will not be a link
      51              : 
      52              : Usage:
      53              : 
      54              :  import { page_status } from "notifications";
      55              : 
      56              :  - page_status.set_own(STATUS)
      57              : 
      58              :  Sets the status of the page making the call, completely overwriting
      59              :  the current status.  For example,
      60              : 
      61              :     page_status.set_own({
      62              :       type: "info",
      63              :       title: _("Software updates available"),
      64              :       details: {
      65              :         num_updates: 10,
      66              :         num_security_updates: 5
      67              :       }
      68              :     });
      69              : 
      70              :     page_status.set_own({
      71              :       type: null
      72              :       title: _("System is up to date"),
      73              :       details: {
      74              :         last_check: 81236457
      75              :       }
      76              :     });
      77              : 
      78              :  Calling this function with the same STATUS value multiple times is
      79              :  cheap: only the first call will actually broadcast the new status.
      80              : 
      81              :  - page_status.get(PAGE, [HOST])
      82              : 
      83              :  Retrieves the current status of page PAGE of HOST.  When HOST is
      84              :  omitted, it defaults to the default host of the calling page.
      85              : 
      86              :  PAGE is the same string that Cockpit uses in its URLs to identify a
      87              :  page, such as "system/terminal" or "storage".
      88              : 
      89              :  Until the page_status object is fully initialized (see 'valid'
      90              :  below), this function will return 'undefined'.
      91              : 
      92              :  - page_status.addEventListener("changed", event => { ... })
      93              : 
      94              :  The "changed" event is emitted whenever any page status changes.
      95              : 
      96              :  - page_status.valid
      97              : 
      98              :  The page_status objects needs to initialize itself asynchronously and
      99              :  'valid' is false until this is done.  When 'valid' changes to true, a
     100              :  "changed" event is emitted.
     101              : 
     102              : */
     103              : 
     104          148 : import cockpit, { JsonValue, JsonObject } from "cockpit";
     105              : import { dequal } from 'dequal/lite';
     106              : 
     107              : export interface Status {
     108              :     type?: string | null;
     109              :     title?: string;
     110              :     details?: JsonObject;
     111              : }
     112              : 
     113          148 : class PageStatus extends EventTarget {
     114          148 :     valid: boolean = false;
     115          148 :     cur_own: Status | null = null;
     116              : 
     117          148 :     constructor() {
     118          148 :         super();
     119           72 :         window.addEventListener("storage", event => {
     120           43 :             if (event.key == "cockpit:page_status") {
     121           43 :                 this.dispatchEvent(new CustomEvent("changed"));
     122           43 :             }
     123           72 :         });
     124              : 
     125          148 :         cockpit.transport.wait(() => {
     126          148 :             this.valid = true;
     127          148 :             this.dispatchEvent(new CustomEvent("changed"));
     128          148 :         });
     129          148 :     }
     130              : 
     131           94 :     get(page: string, host?: string): Status | null | undefined {
     132           94 :         let page_status;
     133              : 
     134           94 :         if (!this.valid)
     135           18 :             return undefined;
     136              : 
     137           94 :         if (host === undefined)
     138           94 :             host = cockpit.transport.host;
     139              : 
     140           94 :         try {
     141           91 :             page_status = JSON.parse(sessionStorage.getItem("cockpit:page_status") || "{}");
     142           16 :         } catch {
     143           16 :             return null;
     144           16 :         }
     145              : 
     146           94 :         if (page_status?.[host])
     147           23 :             return page_status[host][page] || null;
     148           91 :         return null;
     149           94 :     }
     150              : 
     151           50 :     set_own(status: Status | null) {
     152           33 :         if (!dequal(status, this.cur_own)) {
     153           33 :             this.cur_own = status;
     154           33 :             cockpit.transport.control("notify", { page_status: status as JsonValue });
     155           33 :         }
     156           50 :     }
     157          148 : }
     158              : 
     159          148 : export const page_status = new PageStatus();
        

Generated by: LCOV version 2.0-1