Skip to content

Commit

Permalink
WIP - saner state
Browse files Browse the repository at this point in the history
  • Loading branch information
mvollmer committed Sep 25, 2024
1 parent e1535e9 commit d38e9df
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 221 deletions.
13 changes: 5 additions & 8 deletions pkg/shell/active-pages-modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,12 @@ export const ActivePagesDialog = ({ dialogResult, state }) => {
for (const n in state.frames) {
const f = state.frames[n];
if (f) {
const active = state.last_fullpath_for_host(f.host) == f.fullpath;
const active = f == state.current_frame || state.last_path_for_host(f.host) == f.path;
result.push({
frame: f,
component: f.fullpath,
address: f.host,
name: f.name,
active,
selected: active,
displayName: f.host === "localhost" ? "/" + f.fullpath : f.host + ":/" + f.fullpath,
displayName: f.host === "localhost" ? "/" + f.path : f.host + ":/" + f.path,
});
}
}
Expand All @@ -63,7 +60,7 @@ export const ActivePagesDialog = ({ dialogResult, state }) => {
function onRemove() {
pages.forEach(element => {
if (element.selected) {
state.remove_frame(element.name);
state.remove_frame(element.frame.name);
}
});
dialogResult.resolve();
Expand All @@ -82,8 +79,8 @@ export const ActivePagesDialog = ({ dialogResult, state }) => {
}];
return ({
props: {
key: page.name,
'data-row-id': page.name
key: page.frame.name,
'data-row-id': page.frame.name
},
columns,
selected: page.selected,
Expand Down
34 changes: 17 additions & 17 deletions pkg/shell/base_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,24 @@ function Index() {
return false;
};

const last_hash_for_host_fullpath = { };
const last_hash_for_host_path = { };

function lookup_component_hash(address, component) {
function lookup_path_hash(address, path) {
if (!address)
address = "localhost";

if (last_hash_for_host_fullpath[address])
return last_hash_for_host_fullpath[address][component] || null;
if (last_hash_for_host_path[address])
return last_hash_for_host_path[address][path] || null;
return null;
}

self.track_hash = function track_hash(address, component, hash) {
if (!last_hash_for_host_fullpath[address])
last_hash_for_host_fullpath[address] = { };
last_hash_for_host_fullpath[address][component] = hash;
self.track_hash = function track_hash(address, path, hash) {
if (!last_hash_for_host_path[address])
last_hash_for_host_path[address] = { };
last_hash_for_host_path[address][path] = hash;
};

self.last_component_for_host = { };
self.last_path_for_host = { };

/* Jumps to a given navigate state */
self.jump = function (location, replace) {
Expand All @@ -94,21 +94,21 @@ function Index() {
location.host = current.host || "localhost";

// When switching hosts, check if we left from some page
if (!location.component && location.host !== current.host)
location.component = self.last_component_for_host[location.host];
if (!location.path && location.host !== current.host)
location.path = self.last_path_for_host[location.host];

if (!("component" in location))
location.component = current.component || "";
if (!("path" in location))
location.path = current.path || "";

self.last_component_for_host[location.host] = location.component;
self.last_path_for_host[location.host] = location.path;

const frame_change = (location.host !== current.host ||
location.component !== current.component);
location.path !== current.path);

if (frame_change && !location.hash)
location.hash = lookup_component_hash(location.host, location.component);
location.hash = lookup_path_hash(location.host, location.path);

self.track_hash(location.host, location.component, location.hash);
self.track_hash(location.host, location.path, location.hash);

if (frame_change || location.hash !== current.hash) {
console.log("PUSH", JSON.stringify(location));
Expand Down
6 changes: 5 additions & 1 deletion pkg/shell/failures.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ import { codes } from "./hosts_dialog.jsx";

const _ = cockpit.gettext;

export const EarlyFailure = ({ ca_cert_url }) => {
export const EarlyFailure = ({ }) => {
let ca_cert_url = null;
if (window.navigator.userAgent.indexOf("Safari") >= 0)
ca_cert_url = window.sessionStorage.getItem("CACertUrl");

return (
<Page>
<PageSection variant={PageSectionVariants.light}>
Expand Down
18 changes: 14 additions & 4 deletions pkg/shell/frames.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ function poll_frame_ready(state, frame, elt, count, setupFrameWindow) {
ready = true;

if (ready) {
frame.ready = true;
state.update();

if (elt.getAttribute("data-ready") != "1") {
elt.setAttribute("data-ready", "1");
}
Expand All @@ -89,9 +92,9 @@ function poll_frame_ready(state, frame, elt, count, setupFrameWindow) {
}
}

export const Frames = ({ state, idle_state, current_frame }) => {
export const Frames = ({ state, idle_state, hidden }) => {
const content_ref = useRef(null);
const { frames } = state;
const { frames, current_frame } = state;

useEffect(() => {
const content = document.getElementById("content");
Expand All @@ -108,9 +111,11 @@ export const Frames = ({ state, idle_state, current_frame }) => {
console.log("REMOVE", elt.getAttribute('name'));
state.index.router.unregister_name(elt.getAttribute('name'));
elt.removeAttribute('name');
elt.removeAttribute('title');
elt.removeAttribute('src');
elt.removeAttribute('data-host');
elt.removeAttribute("data-ready");
elt.removeAttribute("data-loaded");
elt.removeAttribute('class');
elt.style.display = "none";
free_iframes.push(elt);
Expand Down Expand Up @@ -163,7 +168,7 @@ export const Frames = ({ state, idle_state, current_frame }) => {
if (iframe.getAttribute("title") != frame.title)
iframe.setAttribute("title", frame.title);

const src = frame.url + frame.hash;
const src = frame.url + "#" + frame.hash;

if (iframe.getAttribute('src') != src) {
console.log("SRC", name, iframe.getAttribute('src'), "->", src);
Expand Down Expand Up @@ -206,5 +211,10 @@ export const Frames = ({ state, idle_state, current_frame }) => {
}
});

return <div ref={content_ref} id="content" className="area-ct-content" role="main" tabIndex="-1" />;
return <div ref={content_ref}
id="content"
className="area-ct-content"
role="main"
tabIndex="-1"
hidden={hidden} />;
};
88 changes: 49 additions & 39 deletions pkg/shell/hosts.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class CockpitHosts extends React.Component {
opened: false,
editing: false,
current_user: "",
current_key: props.machine.key,
current_key: props.state.current_machine.key,
modal_properties: null,
modal_callback: null,
};
Expand All @@ -96,18 +96,18 @@ export class CockpitHosts extends React.Component {
// XXX - Only because trigger_connection_flow isn't available
// until we are mounted. If that is fixed, remove this
// here.
this.props.index.ensure_connection();
this.props.state.index.ensure_connection();
}

componentWillUnmount() {
window.trigger_connection_flow = null;
}

static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.machine.key !== prevState.current_key) {
if (nextProps.state.current_machine.key !== prevState.current_key) {
document.getElementById(nextProps.selector).classList.toggle("interact", false);
return {
current_key: nextProps.machine.key,
current_key: nextProps.state.current_machine.key,
opened: false,
editing: false,
};
Expand All @@ -130,9 +130,10 @@ export class CockpitHosts extends React.Component {

showModal(properties) {
return new Promise((resolve, reject) => {
this.setState({ modal_properties: properties,
modal_callback: result => { resolve(result); return Promise.resolve() },
});
this.setState({
modal_properties: properties,
modal_callback: result => { resolve(result); return Promise.resolve() },
});
});
}

Expand All @@ -141,18 +142,23 @@ export class CockpitHosts extends React.Component {
}

async onHostEdit(event, machine) {
const { state } = this.props;
const { current_machine } = state;

const connection_string = await this.showModal({ address: machine.address });
if (connection_string) {
const parts = this.props.machines.split_connection_string(connection_string);
const parts = state.machines.split_connection_string(connection_string);
const addr = build_href({ host: parts.address });
if (machine == this.props.machine && parts.address != machine.address) {
this.props.loader.connect(parts.address);
this.props.jump(addr);
if (machine == current_machine && parts.address != machine.address) {
state.loader.connect(parts.address);
state.index.jump(addr);
}
}
}

async connectHost(machine) {
const { state } = this.props;

// We need to trigger the loader for machines that already
// have state "connected". The state of a machine object
// survives a full shell reload, but the loader of course has
Expand All @@ -167,7 +173,7 @@ export class CockpitHosts extends React.Component {
if (machine.connection_string == "localhost" ||
machine.state == "connected" ||
machine.state == "connecting") {
this.props.loader.connect(machine.address);
state.loader.connect(machine.address);
return machine.connection_string;
}

Expand All @@ -188,7 +194,7 @@ export class CockpitHosts extends React.Component {
} else {
// Try to connect without any dialog
try {
await try2Connect(this.props.machines, machine.connection_string);
await try2Connect(state.machines, machine.connection_string);
connection_string = machine.connection_string;
} catch (err) {
// continue with troubleshooting in the dialog
Expand All @@ -202,20 +208,22 @@ export class CockpitHosts extends React.Component {

if (connection_string) {
// make the rest of the shell aware that the machine is now connected
const parts = this.props.machines.split_connection_string(connection_string);
this.props.loader.connect(parts.address);
this.props.index.navigate();
const parts = state.machines.split_connection_string(connection_string);
state.loader.connect(parts.address);
state.update();
}

return connection_string;
}

async onHostSwitch(machine) {
const { state } = this.props;

const connection_string = await this.connectHost(machine);
if (connection_string) {
const parts = this.props.machines.split_connection_string(connection_string);
const parts = state.machines.split_connection_string(connection_string);
const addr = build_href({ host: parts.address });
this.props.jump(addr);
state.index.jump(addr);
}
}

Expand All @@ -224,17 +232,20 @@ export class CockpitHosts extends React.Component {
}

onRemove(event, machine) {
const { state } = this.props;
const { current_machine } = state;

event.preventDefault();

if (this.props.machine === machine) {
if (current_machine === machine) {
// Removing machine underneath ourself - jump to localhost
const addr = build_href({ host: "localhost" });
this.props.jump(addr);
state.index.jump(addr);
}

if (this.props.machines.list.length <= 2)
if (state.machines.list.length <= 2)
this.setState({ editing: false });
this.props.machines.change(machine.key, { visible: false });
state.machines.change(machine.key, { visible: false });
}

filterHosts(host, term) {
Expand All @@ -259,16 +270,19 @@ export class CockpitHosts extends React.Component {
// 1. It does not change the arrow when opened/closed
// 2. It closes the dropdown even when trying to search... and cannot tell it not to
render() {
const { state } = this.props;
const { current_machine } = state;

const editing = this.state.editing;
const groups = [{
name: _("Hosts"),
items: this.props.machines.list,
items: state.machines.list,
}];
const render = (m, term) => <CockpitNavItem
term={term}
keyword={m.keyword}
to={build_href({ host: m.address })}
active={m === this.props.machine}
active={m === current_machine}
key={m.key}
name={m.label}
header={(m.user ? m.user : this.state.current_user) + " @"}
Expand All @@ -284,8 +298,8 @@ export class CockpitHosts extends React.Component {
</Tooltip>
</>}
/>;
const label = this.props.machine.label || "";
const user = this.props.machine.user || this.state.current_user;
const label = current_machine.label || "";
const user = current_machine.user || this.state.current_user;

const add_host_action = <Button variant="secondary" onClick={this.onAddNewHost}>{_("Add new host")}</Button>;

Expand Down Expand Up @@ -316,7 +330,7 @@ export class CockpitHosts extends React.Component {

{ this.state.opened &&
<HostsSelector>
<PageSidebar isSidebarOpen={this.props.opened} theme="dark" className={"sidebar-hosts" + (this.state.editing ? " edit-hosts" : "")}>
<PageSidebar theme="dark" className={"sidebar-hosts" + (this.state.editing ? " edit-hosts" : "")}>
<CockpitNav
selector={this.props.selector}
groups={groups}
Expand All @@ -327,31 +341,27 @@ export class CockpitHosts extends React.Component {
jump={() => console.error("internal error: jump not supported in hosts selector")}
/>
<div className="nav-hosts-actions">
{this.props.machines.list.length > 1 && <Button variant="secondary" onClick={this.onEditHosts}>{this.state.editing ? _("Stop editing hosts") : _("Edit hosts")}</Button>}
{state.machines.list.length > 1 && <Button variant="secondary" onClick={this.onEditHosts}>{this.state.editing ? _("Stop editing hosts") : _("Edit hosts")}</Button>}
{add_host_action}
</div>
</PageSidebar>
</HostsSelector>
}
</div>
{this.state.modal_properties &&
<HostModal machines_ins={this.props.machines}
onClose={() => this.setState({ modal_properties: null })}
{...this.state.modal_properties}
caller_callback={this.state.modal_callback}
caller_cancelled={() => this.state.modal_callback(null)}
/>
<HostModal machines_ins={state.machines}
onClose={() => this.setState({ modal_properties: null })}
{...this.state.modal_properties}
caller_callback={this.state.modal_callback}
caller_cancelled={() => this.state.modal_callback(null)}
/>
}
</>
);
}
}

CockpitHosts.propTypes = {
machine: PropTypes.object.isRequired,
machines: PropTypes.object.isRequired,
index: PropTypes.object.isRequired,
loader: PropTypes.object.isRequired,
state: PropTypes.object.isRequired,
selector: PropTypes.string.isRequired,
jump: PropTypes.func.isRequired,
};
Loading

0 comments on commit d38e9df

Please sign in to comment.