/*
* Copyright (C) 2024 Red Hat, Inc.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
import cockpit from "cockpit";
import React, { useEffect } from 'react';
import { createRoot } from "react-dom/client";
import {
Modal, ModalBody, ModalFooter, ModalHeader
} from '@patternfly/react-core/dist/esm/components/Modal/index.js';
import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js";
import { WithDialogs } from "dialogs.jsx";
import { useInit, useEvent, useOn, useLoggedInUser } from "hooks";
import { TopNav } from "./topnav.jsx";
import { SidebarToggle, PageNav } from "./nav.jsx";
import { CockpitHosts, CockpitCurrentHost } from "./hosts.jsx";
import { HostModalState, HostModal, connect_host } from "./hosts_dialog.jsx";
import { Frames } from "./frames.jsx";
import { EarlyFailure, Disconnected, MachineTroubleshoot } from "./failures.jsx";
import { ShellState } from "./state.jsx";
import { get_session_controller } from "./session";
import { SkipToContent } from '@patternfly/react-core/dist/esm/components/SkipToContent/index.js';
import 'cockpit-dark-theme'; // once per page
import '../lib/patternfly/patternfly-6-cockpit.scss';
import "./shell.scss";
const _ = cockpit.gettext;
interface SkipLinkProps {
focus_id: string;
children?: React.ReactNode
}
// We create a wrapper specifically to handle edge-cases like headless browsers
// not getting focus when clicking an anchor to a heading. We can also scroll
// it into view in case it wouldn't do that before - just to be safe.
const SkipLink = ({ focus_id, children }: SkipLinkProps) => {
return (
{
const el = document.getElementById(focus_id);
el?.focus();
el?.scrollIntoView();
ev.preventDefault();
}}>
{children}
);
};
const SessionCountdownModal = () => {
const controller = get_session_controller();
useOn(controller, "changed");
controller.inhibit_activity_reporting(controller.countdown > 0);
if (controller.countdown <= 0)
return null;
return (
{ cockpit.format(_("You will be logged out in $0 seconds."), controller.countdown) }
);
};
const Shell = () => {
const current_user = useLoggedInUser()?.name || "";
const state = useInit(() => new ShellState());
const session_controller = get_session_controller();
const host_modal_state = useInit(() => HostModalState());
useOn(state, "update");
useOn(session_controller, "changed");
useEvent(host_modal_state, "changed");
useEffect(() => {
return state.on("connect", () => {
connect_host(host_modal_state, state, state.current_machine);
});
}, [host_modal_state, state]);
const {
ready,
config,
current_machine,
current_manifest_item,
} = state;
const { countdown, problem } = session_controller;
if (problem && !ready)
return ;
if (!ready)
return null;
cockpit.assert(current_machine && current_manifest_item);
const title_parts = [];
if (current_manifest_item.label)
title_parts.push(current_manifest_item.label);
title_parts.push((current_machine.user || current_user) + "@" + current_machine.label);
document.title = title_parts.join(" - ");
if (countdown > 0)
document.title = "(" + countdown + ") " + document.title;
document.documentElement.lang = config.language;
if (config.language_direction)
document.documentElement.dir = config.language_direction;
let failure = null;
if (problem) {
failure = ;
} else if (current_machine.state !== "connected") {
failure = connect_host(host_modal_state, state, current_machine)} />;
}
return (
{_("Skip to content")}{_("Skip main navigation")}
{ failure &&
{ failure }
}
);
};
interface ShellWindow extends Window {
features?: object;
}
function init() {
cockpit.translate();
/* Give us a name. This used to (maybe) indicate at some point to
* child frames that we are a cockpit1 router frame. But they
* actually check for a "cockpit1:" prefix of their own name.
*/
window.name = "cockpit1";
/* Tell the pages about our features. */
(window as ShellWindow).features = {
navbar_is_for_current_machine: true
};
function follow(arg: unknown) {
/* A promise of some sort */
if (arguments.length === 1 && arg && typeof arg == "object" && "then" in arg && typeof arg.then == "function") {
arg.then(function(...args: unknown[]) { console.log(...args) },
function(...args: unknown[]) { console.error(...args) });
if ("stream" in arg && typeof arg.stream == "function")
arg.stream(function(...args: unknown[]) { console.log(...args) });
}
}
let zz_value: unknown;
/* For debugging in the browser console */
Object.defineProperties(window, {
cockpit: { value: cockpit },
zz: {
get: function() { return zz_value },
set: function(val) { zz_value = val; follow(val) }
}
});
const root = createRoot(document.getElementById("shell")!);
root.render();
}
document.addEventListener("DOMContentLoaded", init);