Line data Source code
1 : diff --git a/pkg/lib/anaconda/_anaconda.scss b/pkg/lib/anaconda/_anaconda.scss
2 : index 06b99354f..0e4bb8768 100644
3 : --- a/pkg/lib/anaconda/_anaconda.scss
4 : +++ b/pkg/lib/anaconda/_anaconda.scss
5 : @@ -30,9 +30,9 @@
6 : --pf-v6-c-page--BackgroundColor: var(--pf-t--global--background--color--primary--default);
7 : }
8 :
9 : - .pf-v6-c-card {
10 : - --pf-v6-c-card--BorderColor: transparent;
11 : - }
12 : + // .pf-v6-c-card {
13 : + // --pf-v6-c-card--BorderColor: transparent;
14 : + // }
15 :
16 : // Approximates PageSection padding={{ default: "noPadding" }} isFilled={false} (PF .pf-m-no-padding / .pf-m-no-fill)
17 : .pf-v6-c-page__main-section {
18 : diff --git a/pkg/lib/packagekit.js b/pkg/lib/packagekit.js
19 : index 6b2061124..32b61e554 100644
20 : --- a/pkg/lib/packagekit.js
21 : +++ b/pkg/lib/packagekit.js
22 : @@ -186,6 +186,9 @@ export function transaction(method, arglist, signalHandlers, notifyHandler) {
23 :
24 : const watchPromise = watchTransaction(transactionPath, signalHandlers, notifyHandler) || Promise.resolve();
25 : return watchPromise.then(() => {
26 : + // FIX: force set cache to 24h as some PK backends like dnf4 doesn't
27 : + // integrate with PK cache and shows cache as 0.
28 19 : + call(transactionPath, transactionInterface, "SetHints", ["cache-age=1"]);
29 : if (method) {
30 : return call(transactionPath, transactionInterface, method, arglist)
31 : .then(() => transactionPath);
32 : diff --git a/pkg/networkmanager/anaconda-main.css b/pkg/networkmanager/anaconda-main.css
33 : new file mode 100644
34 : index 000000000..b32fa5fa5
35 : --- /dev/null
36 : +++ b/pkg/networkmanager/anaconda-main.css
37 : @@ -0,0 +1,5 @@
38 : +#network-interface {
39 : + .pf-v6-c-card.pf-m-plain .pf-v6-c-card__header, .pf-v6-c-card.pf-m-plain > .pf-v6-c-card__title {
40 : + padding-block-start: 0;
41 : + }
42 : +}
43 : diff --git a/pkg/networkmanager/anaconda-main.tsx b/pkg/networkmanager/anaconda-main.tsx
44 : new file mode 100644
45 : index 000000000..e7da8c3a8
46 : --- /dev/null
47 : +++ b/pkg/networkmanager/anaconda-main.tsx
48 : @@ -0,0 +1,182 @@
49 : +/*
50 : + * Copyright (C) 2021 Red Hat, Inc.
51 : + * SPDX-License-Identifier: LGPL-2.1-or-later
52 : + */
53 : +
54 35 : +import cockpit from "cockpit";
55 35 : +import React, { useState } from 'react';
56 : +
57 : +import { Flex, FlexItem } from "@patternfly/react-core/dist/esm/layouts/Flex/index.js";
58 : +import { Page, } from "@patternfly/react-core/dist/esm/components/Page/index.js";
59 : +
60 : +import { ListingTableRowProps } from "cockpit-components-table.jsx";
61 : +
62 : +import {
63 : + has_group,
64 : + is_loopback,
65 : + is_managed,
66 : + is_wireless,
67 : + render_active_connection,
68 : +} from './interfaces.js';
69 : +import { Content, ContentVariants, SimpleList, SimpleListGroup, SimpleListItem, Split, SplitItem } from "@patternfly/react-core";
70 : +import { NetworkInterfacePage } from "./network-interface.jsx";
71 : +import "./anaconda-main.css";
72 : +
73 35 : +const _ = cockpit.gettext;
74 : +
75 : +interface AnacondaNetworkPageProps {
76 : + privileged: boolean;
77 : + operationInProgress: boolean;
78 : + usage_monitor: any;
79 : + interfaces: any[];
80 : + iface?: any;
81 : +}
82 : +
83 : +interface AnacondaActiveNetwork {
84 : + isWireless?: boolean;
85 : + iface: any;
86 : +}
87 : +
88 0 : +export const AnacondaNetworkPage = ({ privileged, operationInProgress, usage_monitor, interfaces }: AnacondaNetworkPageProps) => {
89 0 : + const [active, setActive] = useState<AnacondaActiveNetwork>();
90 : + // useEvent(usage_monitor.grid, "notify");
91 : +
92 0 : + const managedWired: ListingTableRowProps[] = [];
93 0 : + const managedWireless: ListingTableRowProps[] = [];
94 0 : + let hasDetails = false;
95 : +
96 0 : + interfaces.forEach(iface => {
97 : + // Skip loopback
98 0 : + if (is_loopback(iface))
99 0 : + return;
100 : +
101 : + // Skip members
102 0 : + else if (has_group(iface))
103 0 : + return;
104 : +
105 0 : + const dev = iface.Device;
106 : + // const show_traffic = (dev && (dev.State == 100 || dev.State == 10) && dev.Carrier === true);
107 : +
108 : + // usage_monitor.add(iface.Name);
109 : +
110 0 : + const activeConnection = render_active_connection(dev, false, true);
111 0 : + const isWireless = is_wireless(iface);
112 : +
113 0 : + let connectionStatus;
114 0 : + if (activeConnection) {
115 0 : + connectionStatus = _("Connected")
116 0 : + } else {
117 0 : + connectionStatus = _("Disconnected")
118 0 : + }
119 : +
120 0 : + const row = (
121 0 : + <SimpleListItem key={iface.name} onClick={() => {setActive({isWireless, iface})}}>
122 0 : + <Flex
123 0 : + direction={{ default: 'row' }}
124 0 : + justifyContent={{ default: 'justifyContentSpaceBetween' }}
125 0 : + flexWrap={{ default: 'nowrap' }}
126 : + >
127 0 : + <FlexItem flex={{ default: 'flex_1' }}>{iface.Name}</FlexItem>
128 0 : + <FlexItem>
129 0 : + <Content component={ContentVariants.small}>{connectionStatus}</Content>
130 0 : + </FlexItem>
131 0 : + </Flex>
132 0 : + </SimpleListItem>
133 : + )
134 : +
135 : + // Details column: show type-specific information
136 : + // let detailsColumn = null;
137 : + // if (dev?.DeviceType === '802-11-wireless') {
138 : + // const networkCount = dev.visibleSsids.length;
139 : + // if (networkCount > 0 || dev.ActiveAccessPoint?.Ssid) {
140 : + // hasDetails = true;
141 : + // detailsColumn = (
142 : + // <Flex columnGap={{ default: 'columnGapSm' }}>
143 : + // {networkCount > 0 && (
144 : + // <FlexItem>
145 : + // <Label status="info">
146 : + // {cockpit.format(cockpit.ngettext("$0 network", "$0 networks", networkCount), networkCount)}
147 : + // </Label>
148 : + // </FlexItem>
149 : + // )}
150 : + // {dev.ActiveAccessPoint?.Ssid && (
151 : + // <FlexItem>
152 : + // <Label status="success" icon={<ConnectedIcon />}>{dev.ActiveAccessPoint?.Ssid}</Label>
153 : + // </FlexItem>
154 : + // )}
155 : + // </Flex>
156 : + // );
157 : + // }
158 : + // }
159 : + // row.columns.push({ title: detailsColumn });
160 : +
161 0 : + if (!dev || is_managed(dev)) {
162 0 : + isWireless ? managedWireless.push(row) : managedWired.push(row);
163 0 : + }
164 0 : + });
165 : +
166 : + // TODO: Actions: turn on off action and edit (wired) or join wifi (wireless)
167 0 : + const actions = privileged && (
168 0 : + <>
169 : + {/* <NetworkAction buttonText={_("Add VPN")} type='wg' />
170 : + <NetworkAction buttonText={_("Add bond")} type='bond' />
171 : + <NetworkAction buttonText={_("Add team")} type='team' />
172 : + <NetworkAction buttonText={_("Add bridge")} type='bridge' />
173 : + <NetworkAction buttonText={_("Add VLAN")} type='vlan' /> */}
174 0 : + </>
175 : + );
176 : +
177 0 : + return (
178 0 : + <Page data-test-wait={operationInProgress} id="networking" className="pf-m-no-sidebar anaconda">
179 0 : + <Content component="h1">{_("Networks")}</Content>
180 0 : + <Split hasGutter>
181 0 : + <SplitItem>
182 0 : + <SimpleList>
183 0 : + {managedWireless.length !== 0 && (
184 0 : + <SimpleListGroup title={_("Wireless")} id="wireless-connections">{...managedWireless}</SimpleListGroup>
185 : + )}
186 0 : + {managedWired.length !== 0 && (
187 0 : + <SimpleListGroup title={_("Wired")} id="wired-connections">{...managedWired}</SimpleListGroup>
188 : + )}
189 0 : + {(managedWireless.length === 0 && managedWired.length === 0) && (
190 0 : + <SimpleListItem key="not-found">{_("No networks found")}</SimpleListItem>
191 : + )}
192 0 : + </SimpleList>
193 0 : + </SplitItem>
194 0 : + <SplitItem isFilled>
195 0 : + {active?.iface &&
196 0 : + <NetworkInterfacePage
197 0 : + privileged={privileged}
198 0 : + operationInProgress={operationInProgress}
199 0 : + usage_monitor={usage_monitor}
200 0 : + plot_state={undefined}
201 0 : + interfaces={interfaces}
202 0 : + iface={active.iface} />
203 : + }
204 0 : + </SplitItem>
205 0 : + </Split>
206 : +
207 0 : + </Page>
208 : + );
209 0 : +};
210 : +
211 : +export const AnacondaWirelessDetail = ({active}: {active: AnacondaActiveNetwork}) => {
212 : + return <>
213 : + <Content component="h2">{_("Wireless")}</Content>
214 : + Interface
215 : + {active.iface.Name}
216 : + Status
217 : + Network joined
218 : + Security type
219 : + </>
220 : +}
221 : +
222 : +export const AnacondaWiredDetail = ({active}: {active: AnacondaActiveNetwork}) => {
223 : + return <>
224 : + <Content component="h2">{_("Wired")}</Content>
225 : + Interface
226 : + {active.iface.Name}
227 : + Status
228 : + IP Settings
229 : + </>
230 : +}
231 : diff --git a/pkg/networkmanager/interfaces.js b/pkg/networkmanager/interfaces.js
232 : index 17d5fcf7b..cffac4aa3 100644
233 : --- a/pkg/networkmanager/interfaces.js
234 : +++ b/pkg/networkmanager/interfaces.js
235 : @@ -2107,6 +2107,24 @@ export function apply_group_member(choices, model, apply_group, group_connection
236 : });
237 : }
238 :
239 34 : +export function is_loopback(iface) {
240 34 : + return iface.Name == "lo" || (iface.Device && iface.Device.DeviceType == "loopback");
241 34 : +}
242 : +
243 34 : +export function has_group(iface) {
244 34 : + return (
245 34 : + (iface.Device &&
246 34 : + iface.Device.ActiveConnection &&
247 34 : + iface.Device.ActiveConnection.Group &&
248 6 : + iface.Device.ActiveConnection.Group.Members.length > 0) ||
249 34 : + (iface.MainConnection && iface.MainConnection.Groups.length > 0)
250 : + );
251 34 : +}
252 : +
253 0 : +export function is_wireless(iface) {
254 0 : + return iface.Device?.DeviceType === '802-11-wireless';
255 0 : +}
256 : +
257 : export function init() {
258 : cockpit.translate();
259 : }
260 : diff --git a/pkg/networkmanager/network-interface.jsx b/pkg/networkmanager/network-interface.jsx
261 : index 43aa0dcb1..b241c35d7 100644
262 : --- a/pkg/networkmanager/network-interface.jsx
263 : +++ b/pkg/networkmanager/network-interface.jsx
264 : @@ -21,7 +21,7 @@ import { SearchInput } from "@patternfly/react-core/dist/esm/components/SearchIn
265 : import { Spinner } from "@patternfly/react-core/dist/esm/components/Spinner/index.js";
266 : import { Switch } from "@patternfly/react-core/dist/esm/components/Switch/index.js";
267 : import { Tooltip } from "@patternfly/react-core/dist/esm/components/Tooltip/index.js";
268 : -import { SortByDirection } from '@patternfly/react-table';
269 : +import { ActionsColumn, SortByDirection } from '@patternfly/react-table';
270 : import {
271 : ConnectedIcon,
272 : DisconnectedIcon,
273 : @@ -225,6 +225,8 @@ export const NetworkInterfacePage = ({
274 : const [prevAPCount, setPrevAPCount] = useState(0);
275 : const [networkSearch, setNetworkSearch] = useState("");
276 :
277 29 : + const anaconda = in_anaconda_mode();
278 : +
279 : const dev_name = iface.Name;
280 : const dev = iface.Device;
281 : const isManaged = iface && (!dev || is_managed(dev));
282 : @@ -432,7 +434,14 @@ export const NetworkInterfacePage = ({
283 : mac_desc = mac;
284 : }
285 :
286 : - return mac_desc;
287 29 : + return (
288 29 : + <DescriptionListGroup id="network-interface-mac">
289 29 : + <DescriptionListTerm>{_("MAC")}</DescriptionListTerm>
290 29 : + <DescriptionListDescription data-label="Carrier">
291 29 : + { mac_desc }
292 29 : + </DescriptionListDescription>
293 29 : + </DescriptionListGroup>
294 : + );
295 : }
296 :
297 : function renderCarrierStatusRow() {
298 : @@ -980,47 +989,49 @@ export const NetworkInterfacePage = ({
299 : );
300 : } else {
301 : actionColumn = (
302 : - <>
303 : - <Privileged allowed={privileged}
304 : - tooltipId={"wifi-connect-" + index}
305 : - excuse={_("Not permitted to connect to network")}>
306 : - <Button variant="secondary"
307 : - size="sm"
308 : - icon={<ConnectedIcon />}
309 : - isDisabled={!privileged}
310 : - onClick={() => connectToAP(ap)}
311 : - aria-label={_("Connect")}>
312 : - {_("Connect")}
313 : - </Button>
314 : - </Privileged>
315 : - {ap.Connection && (
316 : - <>
317 : - {" "}
318 : - <KebabDropdown
319 : - toggleButtonId={"wifi-kebab-" + index}
320 : - isDisabled={!privileged}
321 : - dropdownItems={[
322 : - <DropdownItem key="forget"
323 : - className="pf-m-danger"
324 : - onClick={() => forgetNetwork(ap)}
325 : - aria-label={_("Forget")}>
326 : - {_("Forget")}
327 : - </DropdownItem>
328 : - ]} />
329 : - </>
330 : - )}
331 : - </>
332 0 : + <Privileged allowed={privileged}
333 0 : + tooltipId={"wifi-connect-" + index}
334 0 : + excuse={_("Not permitted to connect to network")}>
335 0 : + <Button variant="secondary"
336 0 : + size="sm"
337 0 : + icon={<ConnectedIcon />}
338 0 : + isDisabled={!privileged}
339 0 : + onClick={() => connectToAP(ap)}
340 0 : + aria-label={_("Connect")}>
341 0 : + {_("Connect")}
342 0 : + </Button>
343 0 : + </Privileged>
344 : );
345 : }
346 0 : + let rowActions = <></>;
347 0 : + if (ap.Connection) {
348 0 : + rowActions = <ActionsColumn
349 0 : + items={[
350 0 : + {
351 0 : + title: _("Forget"),
352 0 : + onClick: () => forgetNetwork(ap),
353 0 : + isDanger: true,
354 0 : + "aria-label": _("Forget")
355 0 : + }
356 0 : + ]}
357 0 : + isDisabled={!privileged}
358 0 : + />
359 0 : + }
360 : +
361 0 : + const networkColumns = [
362 0 : + { title: nameColumn, sortKey: ap.Ssid, header: true },
363 0 : + { title: <>{securityIcon} {ap.Mode}</>, sortKey: ap.Mode },
364 0 : + { title: signalColumn, sortKey: String(ap.Strength).padStart(3, '0') },
365 0 : + ];
366 0 : + if (!anaconda) {
367 0 : + networkColumns.push({ title: cockpit.format_bits_per_sec(ap.MaxBitrate * 1000) });
368 0 : + }
369 0 : + networkColumns.push({ title: <Flex justifyContent={{ default: 'justifyContentFlexEnd' }}><FlexItem>{actionColumn}</FlexItem></Flex>, props: { hasAction: true } });
370 0 : + networkColumns.push({ title: rowActions, props: { isActionCell: true } });
371 : +
372 :
373 : return {
374 : - columns: [
375 : - { title: nameColumn, sortKey: ap.Ssid, header: true },
376 : - { title: <>{securityIcon} {ap.Mode}</>, sortKey: ap.Mode },
377 : - { title: signalColumn, sortKey: String(ap.Strength).padStart(3, '0') },
378 : - { title: cockpit.format_bits_per_sec(ap.MaxBitrate * 1000) },
379 : - { title: actionColumn },
380 : - ],
381 0 : + columns: networkColumns,
382 : props: { key: ap.HwAddress, "data-ssid": ap.Ssid, "data-known": !!ap.Connection }
383 : };
384 : });
385 : @@ -1028,18 +1039,38 @@ export const NetworkInterfacePage = ({
386 : // Add aggregated hidden access points row at the bottom
387 : if (dev.hiddenAPCount > 0) {
388 : const hiddenLabel = cockpit.ngettext("$0 hidden network", "$0 hidden networks", dev.hiddenAPCount);
389 : +
390 2 : + const networkHiddenColumns = [
391 2 : + { title: cockpit.format(hiddenLabel, dev.hiddenAPCount), sortKey: "zzz-hidden", header: true },
392 2 : + { title: "" },
393 2 : + { title: "" },
394 2 : + { title: "" },
395 2 : + { title: "" },
396 2 : + ];
397 : +
398 2 : + if (!anaconda) {
399 2 : + networkHiddenColumns.push({ title: "" });
400 2 : + }
401 2 : + networkHiddenColumns.push({ title: "" });
402 : +
403 : +
404 : rows.push({
405 : - columns: [
406 : - { title: cockpit.format(hiddenLabel, dev.hiddenAPCount), sortKey: "zzz-hidden", header: true },
407 : - { title: "" },
408 : - { title: "" },
409 : - { title: "" },
410 : - { title: "" },
411 : - ],
412 2 : + columns: networkHiddenColumns,
413 : props: { key: "hidden-networks", "data-hidden": true }
414 : });
415 : }
416 :
417 2 : + const listingColumns = [
418 2 : + { title: _("Network"), header: true, sortable: true },
419 2 : + { title: _("Mode") },
420 2 : + { title: _("Signal"), sortable: true },
421 2 : + ];
422 : +
423 2 : + if (!anaconda) {
424 2 : + listingColumns.push({ title: _("Rate") });
425 2 : + }
426 2 : + listingColumns.push({ title: "", props: { screenReaderText: _("Actions") } });
427 : +
428 : return (
429 : <Card isPlain id="network-interface-wifi-networks">
430 : <CardHeader actions={{
431 : @@ -1077,13 +1108,7 @@ export const NetworkInterfacePage = ({
432 : </CardHeader>
433 : <ListingTable aria-label={_("Available networks")}
434 : variant='compact'
435 : - columns={[
436 : - { title: _("Network"), header: true, sortable: true },
437 : - { title: _("Mode") },
438 : - { title: _("Signal"), sortable: true },
439 : - { title: _("Rate") },
440 : - { title: "", props: { screenReaderText: _("Actions") } },
441 : - ]}
442 29 : + columns={listingColumns}
443 : sortBy={{ index: 2, direction: SortByDirection.asc }}
444 : sortMethod={networkSort}
445 : rows={rows} />
446 : @@ -1112,7 +1137,7 @@ export const NetworkInterfacePage = ({
447 : };
448 :
449 : const cs = con && connection_settings(con);
450 : - if (!con || (cs.type != "bond" && cs.type != "team" && cs.type != "bridge")) {
451 20 : + if (plot_state && (!con || (cs.type != "bond" && cs.type != "team" && cs.type != "bridge"))) {
452 : plot_state.plot_instances('rx', rx_plot_data, [dev_name], true);
453 : plot_state.plot_instances('tx', tx_plot_data, [dev_name], true);
454 : return null;
455 : @@ -1120,7 +1145,7 @@ export const NetworkInterfacePage = ({
456 :
457 : const plot_ifaces = [];
458 :
459 : - con.Members.forEach(member_con => {
460 13 : + con && con.Members.forEach(member_con => {
461 : member_con.Interfaces.forEach(iface => {
462 : if (iface.MainConnection != member_con)
463 : return;
464 : @@ -1140,8 +1165,10 @@ export const NetworkInterfacePage = ({
465 : });
466 : });
467 :
468 : - plot_state.plot_instances('rx', rx_plot_data, plot_ifaces, true);
469 : - plot_state.plot_instances('tx', tx_plot_data, plot_ifaces, true);
470 14 : + if (plot_state) {
471 14 : + plot_state.plot_instances('rx', rx_plot_data, plot_ifaces, true);
472 14 : + plot_state.plot_instances('tx', tx_plot_data, plot_ifaces, true);
473 14 : + }
474 :
475 : const sorted_members = Object.keys(members).sort()
476 : .map(name => members[name]);
477 : @@ -1208,12 +1235,11 @@ export const NetworkInterfacePage = ({
478 : const settingsRows = renderConnectionSettingsRows(iface.MainConnection, connectionSettings)
479 : .map((component, idx) => <React.Fragment key={idx}>{component}</React.Fragment>);
480 :
481 : - const anaconda = in_anaconda_mode();
482 : -
483 : return (
484 : <Page id="network-interface"
485 : data-test-wait={operationInProgress}
486 : className={"pf-m-no-sidebar" + (anaconda ? " anaconda" : "")}>
487 29 : + { !anaconda ? (
488 : <PageBreadcrumb hasBodyWrapper={false} stickyOnBreakpoint={{ default: "top" }}>
489 : <Breadcrumb>
490 : <BreadcrumbItem to='#/'>
491 : @@ -1224,9 +1250,12 @@ export const NetworkInterfacePage = ({
492 : </BreadcrumbItem>
493 : </Breadcrumb>
494 : </PageBreadcrumb>
495 : - <PageSection hasBodyWrapper={false}>
496 : - <NetworkPlots plot_state={plot_state} />
497 : - </PageSection>
498 2 : + ) : <></> }
499 29 : + { plot_state &&
500 29 : + <PageSection hasBodyWrapper={false}>
501 29 : + <NetworkPlots plot_state={plot_state} />
502 29 : + </PageSection>
503 : + }
504 : <PageSection hasBodyWrapper={false}>
505 : <Gallery hasGutter>
506 : <Card isPlain className="network-interface-details">
507 : @@ -1246,12 +1275,12 @@ export const NetworkInterfacePage = ({
508 : <CardTitle className="network-interface-details-title">
509 : <span id="network-interface-name">{dev_name}</span>
510 : <span id="network-interface-hw">{renderDesc()}</span>
511 : - <span id="network-interface-mac">{renderMac()}</span>
512 : </CardTitle>
513 : </CardHeader>
514 : <CardBody>
515 : <DescriptionList id="network-interface-settings" className="network-interface-settings pf-m-horizontal-on-sm">
516 : {renderActiveStatusRow()}
517 29 : + {renderMac()}
518 : {renderCarrierStatusRow()}
519 : {settingsRows}
520 : </DescriptionList>
521 : @@ -1264,7 +1293,7 @@ export const NetworkInterfacePage = ({
522 : }
523 : </Card>
524 : {renderWiFiNetworks()}
525 : - {renderConnectionMembers(iface.MainConnection)}
526 29 : + { !anaconda && renderConnectionMembers(iface.MainConnection)}
527 : </Gallery>
528 : </PageSection>
529 : </Page>
530 : diff --git a/pkg/networkmanager/network-main.jsx b/pkg/networkmanager/network-main.jsx
531 : index d2870f684..1b75ec3f4 100644
532 : --- a/pkg/networkmanager/network-main.jsx
533 : +++ b/pkg/networkmanager/network-main.jsx
534 : @@ -25,6 +25,8 @@ import { in_anaconda_mode } from "utils";
535 : import firewall from './firewall-client.js';
536 : import {
537 : device_state_text,
538 : + has_group,
539 : + is_loopback,
540 : is_managed,
541 : render_active_connection,
542 : } from './interfaces.js';
543 : @@ -39,23 +41,17 @@ export const NetworkPage = ({ privileged, operationInProgress, usage_monitor, pl
544 : const unmanaged = [];
545 : const plot_ifaces = [];
546 : let hasDetails = false;
547 34 : + const anaconda_mode = JSON.parse(window.sessionStorage.getItem("cockpit_anaconda"));
548 : +
549 :
550 : interfaces.forEach(iface => {
551 : - function hasGroup(iface) {
552 : - return ((iface.Device &&
553 : - iface.Device.ActiveConnection &&
554 : - iface.Device.ActiveConnection.Group &&
555 : - iface.Device.ActiveConnection.Group.Members.length > 0) ||
556 : - (iface.MainConnection &&
557 : - iface.MainConnection.Groups.length > 0));
558 : - }
559 :
560 : // Skip loopback
561 : - if (iface.Name == "lo" || (iface.Device && iface.Device.DeviceType == 'loopback'))
562 34 : + if (is_loopback(iface))
563 : return;
564 :
565 : // Skip members
566 : - if (hasGroup(iface))
567 34 : + else if (has_group(iface))
568 : return;
569 :
570 : const dev = iface.Device;
571 : diff --git a/pkg/networkmanager/networking.scss b/pkg/networkmanager/networking.scss
572 : index bf340a515..823e383d2 100644
573 : --- a/pkg/networkmanager/networking.scss
574 : +++ b/pkg/networkmanager/networking.scss
575 : @@ -323,3 +323,10 @@ th {
576 : }
577 :
578 : /* End Firewall specific CSS */
579 : +
580 : +#networking.anaconda .pf-v6-c-page__main-container {
581 : + border-radius: 0;
582 : + margin: 0;
583 : + border: 0;
584 : + max-block-size: 100%;
585 : +}
586 : diff --git a/pkg/networkmanager/networkmanager.jsx b/pkg/networkmanager/networkmanager.jsx
587 : index 54f4f963d..d34cbd503 100644
588 : --- a/pkg/networkmanager/networkmanager.jsx
589 : +++ b/pkg/networkmanager/networkmanager.jsx
590 : @@ -25,6 +25,7 @@ import { PlotState } from 'plot';
591 :
592 : import { useObject, useEvent, usePageLocation } from "hooks";
593 : import { WithDialogs } from "dialogs.jsx";
594 : +import { AnacondaNetworkPage } from './anaconda-main';
595 :
596 : const _ = cockpit.gettext;
597 :
598 : @@ -99,11 +100,29 @@ const App = () => {
599 :
600 : const interfaces = model.list_interfaces();
601 :
602 35 : + const anaconda_mode = JSON.parse(window.sessionStorage.getItem("cockpit_anaconda"));
603 : +
604 6 : + if (anaconda_mode) {
605 0 : + const iface = path.length == 1 ? interfaces.find(iface => iface.Name == path[0]) : undefined;
606 : +
607 6 : + return (
608 6 : + <ModelContext.Provider value={model}>
609 6 : + <WithDialogs key="networking-anaconda">
610 6 : + <AnacondaNetworkPage privileged={superuser.allowed}
611 6 : + operationInProgress={model.operationInProgress}
612 6 : + usage_monitor={usage_monitor}
613 6 : + interfaces={interfaces}
614 6 : + iface={iface} />
615 6 : + </WithDialogs>
616 6 : + </ModelContext.Provider>
617 : + );
618 6 : + }
619 : +
620 : /* At this point NM is running and the model is ready */
621 : if (path.length == 0) {
622 : return (
623 : <ModelContext.Provider value={model}>
624 : - <WithDialogs key="1">
625 34 : + <WithDialogs key="networking">
626 : <NetworkPage privileged={superuser.allowed}
627 : operationInProgress={model.operationInProgress}
628 : usage_monitor={usage_monitor}
629 : @@ -118,7 +137,7 @@ const App = () => {
630 : if (iface) {
631 : return (
632 : <ModelContext.Provider value={model}>
633 : - <WithDialogs key="2">
634 32 : + <WithDialogs key="networking-interface">
635 : <NetworkInterfacePage privileged={superuser.allowed}
636 : operationInProgress={model.operationInProgress}
637 : usage_monitor={usage_monitor}
638 : diff --git a/pkg/packagekit/updates.jsx b/pkg/packagekit/updates.jsx
639 : index 03a73658f..b74d273f5 100644
640 : --- a/pkg/packagekit/updates.jsx
641 : +++ b/pkg/packagekit/updates.jsx
642 : @@ -1222,12 +1222,17 @@ class OsUpdates extends React.Component {
643 : try {
644 : const seconds = await this.state.packageManager.get_last_refresh_time();
645 : this.setState({ timeSinceRefresh: seconds });
646 18 : + console.log("seconds", seconds)
647 :
648 : // automatically trigger refresh for ≥ 1 day or if never refreshed
649 : - if (seconds >= 24 * 3600 || seconds < 0)
650 2 : + if (seconds >= 60 * 5 || seconds < 0) {
651 7 : + console.log("refreshing")
652 : this.handleRefresh();
653 : - else if (always_load)
654 7 : + }
655 10 : + else if (always_load) {
656 10 : + console.log("always refresh")
657 : this.loadUpdates();
658 10 : + }
659 : } catch (exc) {
660 : this.handleLoadError(exc);
661 : }
662 : diff --git a/test/verify/check-networkmanager-wifi b/test/verify/check-networkmanager-wifi
663 : index 79374d517..835090a5f 100755
664 : --- a/test/verify/check-networkmanager-wifi
665 : +++ b/test/verify/check-networkmanager-wifi
666 : @@ -107,6 +107,7 @@ wpa_passphrase=hidden99""")
667 : # detects our three visible wifis (plus one hidden)
668 : b.wait_in_text("table[aria-label='Available networks'] tbody:first-of-type", "ZE WIFI!")
669 : b.wait_text("tr[data-ssid='ZE WIFI!'] th", "ZE WIFI!")
670 : + testlib.sit()
671 : self.assertEqual(b.get_pf_progress_value("tr[data-ssid='ZE WIFI!'] [data-label='Signal']"), 100)
672 : # sadly, mac80211_hwsim's data rate reporting is flaky, so just ensure at least one of our networks is correct
673 : b.wait_in_text("table[aria-label='Available networks']", "54 Mbps")
|