Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move network to TanStack query #1489

Merged
merged 2 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion web/src/client/network/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
ConnectionTypes,
createAccessPoint,
createConnection,
DeviceState,
NetworkState,
securityFromFlags,
} from "./model";
import { formatIp, ipPrefixFor } from "./utils";
Expand Down Expand Up @@ -239,7 +241,26 @@ class NetworkClient {
return this.client.get(`/network/connections/${connection.id}/disconnect`);
}

async loadNetworks(devices, connections, accessPoints) {
networkStateFor(state) {
switch (state) {
case DeviceState.CONFIG:
case DeviceState.IPCHECK:
// TRANSLATORS: Wifi network status
return NetworkState.CONNECTING;
case DeviceState.ACTIVATED:
// TRANSLATORS: Wifi network status
return NetworkState.CONNECTED;
case DeviceState.DEACTIVATING:
case DeviceState.FAILED:
case DeviceState.DISCONNECTED:
// TRANSLATORS: Wifi network status
return NetworkState.DISCONNECTED;
default:
return "";
}
}

loadNetworks(devices, connections, accessPoints) {
const knownSsids = [];

return accessPoints
Expand Down
7 changes: 7 additions & 0 deletions web/src/client/network/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ const DeviceState = Object.freeze({
FAILED: "failed",
});

const NetworkState = Object.freeze({
DISCONNECTED: "disconnected",
CONNECTING: "connecting",
CONNECTED: "connected"
});

/**
* Returns a human readable connection state
*
Expand Down Expand Up @@ -302,6 +308,7 @@ export {
createConnection,
createDevice,
DeviceState,
NetworkState,
securityFromFlags,
SecurityProtocols,
};
3 changes: 2 additions & 1 deletion web/src/components/network/ConnectionsTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export default function ConnectionsTable({ connections, devices, onForget }) {
<Th width={25}>{_("Name")}</Th>
{/* TRANSLATORS: table header */}
<Th>{_("IP addresses")}</Th>
<Th />
{/* TRANSLATORS: table header aria label */}
<Th aria-label={_("Connection actions")} />
</Tr>
</Thead>
<Tbody>
Expand Down
73 changes: 7 additions & 66 deletions web/src/components/network/NetworkPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,67 +21,29 @@

// @ts-check

import React, { useCallback, useEffect, useState } from "react";
import { CardBody, Grid, GridItem, Skeleton, Split, Stack } from "@patternfly/react-core";
import { useLoaderData } from "react-router-dom";
import React from "react";
import { CardBody, Grid, GridItem } from "@patternfly/react-core";
import { ButtonLink, CardField, EmptyState, Page } from "~/components/core";
import { ConnectionsTable } from "~/components/network";
import { NetworkEventTypes } from "~/client/network";
import { useInstallerClient } from "~/context/installer";
import { _ } from "~/i18n";
import { formatIp } from "~/client/network/utils";
import { sprintf } from "sprintf-js";
import { useNetwork, useNetworkConfigChanges } from "~/queries/network";

/**
* Page component holding Network settings
* @component
*/
export default function NetworkPage() {
const { network: client } = useInstallerClient();
// @ts-ignore
const { connections: initialConnections, devices: initialDevices, settings } = useLoaderData();
const [connections, setConnections] = useState(initialConnections);
const [devices, setDevices] = useState(initialDevices);
const [updateState, setUpdateState] = useState(false);

const fetchState = useCallback(async () => {
const devices = await client.devices();
const connections = await client.connections();
setDevices(devices);
setConnections(connections);
}, [client]);

useEffect(() => {
if (!updateState) return;

setUpdateState(false);
fetchState();
}, [fetchState, updateState]);

useEffect(() => {
return client.onNetworkChange(({ type }) => {
if (
[
NetworkEventTypes.DEVICE_ADDED,
NetworkEventTypes.DEVICE_UPDATED,
NetworkEventTypes.DEVICE_REMOVED,
// @ts-ignore
].includes(type)
) {
setUpdateState(true);
}
});
});

const { connections, devices, settings } = useNetwork();
const connectionDevice = ({ id }) => devices?.find(({ connection }) => id === connection);
const connectionAddresses = (connection) => {
const device = connectionDevice(connection);
const addresses = device ? device.addresses : connection.addresses;

return addresses?.map(formatIp).join(", ");
};

const ready = connections !== undefined && devices !== undefined;
useNetworkConfigChanges();

const WifiConnections = () => {
const { wireless_enabled: wifiAvailable } = settings;
Expand Down Expand Up @@ -132,36 +94,15 @@ export default function NetworkPage() {
);
};

const SectionSkeleton = () => (
<Stack hasGutter>
<Skeleton width="45%" />
<Split hasGutter>
<Skeleton width="30%" height="10px" />
<Skeleton width="30%" height="10px" />
<Skeleton width="30%" height="10px" />
</Split>
<Split hasGutter>
<Skeleton width="30%" height="10px" />
<Skeleton width="30%" height="10px" />
<Skeleton width="30%" height="10px" />
</Split>
</Stack>
);

const WiredConnections = () => {
const wiredConnections = connections.filter((c) => !c.wireless);
const total = wiredConnections.length;

return (
<CardField label={total > 0 && _("Wired")}>
<CardBody>
{!ready && <SectionSkeleton />}
{ready && total === 0 && (
<EmptyState title={_("No wired connections found")} icon="warning" />
)}
{ready && total !== 0 && (
<ConnectionsTable connections={wiredConnections} devices={devices} />
)}
{total === 0 && (<EmptyState title={_("No wired connections found")} icon="warning" />)}
{total !== 0 && (<ConnectionsTable connections={wiredConnections} devices={devices} />)}
</CardBody>
</CardField>
);
Expand Down
11 changes: 9 additions & 2 deletions web/src/components/network/WifiConnectionForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* find current contact information at www.suse.com.
*/

import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import {
ActionGroup,
Alert,
Expand All @@ -32,7 +32,9 @@ import {
} from "@patternfly/react-core";
import { PasswordInput } from "~/components/core";
import { useInstallerClient } from "~/context/installer";
import { useNetworkConfigChanges } from "~/queries/network";
import { _ } from "~/i18n";
import { useQueryClient } from "@tanstack/react-query";

/*
* FIXME: it should be moved to the SecurityProtocols enum that already exists or to a class based
Expand All @@ -57,13 +59,16 @@ const securityFrom = (supported) => {

export default function WifiConnectionForm({ network, onCancel, onSubmitCallback }) {
const { network: client } = useInstallerClient();
const queryClient = useQueryClient();
const [error, setError] = useState(false);
const [isConnecting, setIsConnecting] = useState(false);
const [ssid, setSsid] = useState(network?.ssid || "");
const [password, setPassword] = useState(network?.password || "");
const [security, setSecurity] = useState(securityFrom(network?.security || []));
const hidden = network?.hidden || false;

useNetworkConfigChanges();

const accept = async (e) => {
e.preventDefault();
setError(false);
Expand All @@ -76,7 +81,9 @@ export default function WifiConnectionForm({ network, onCancel, onSubmitCallback
client
.addAndConnectTo(ssid, { security, password, hidden })
.catch(() => setError(true))
.finally(() => setIsConnecting(false));
.finally(
() => setIsConnecting(false) && queryClient.invalidateQueries({ queryKey: ["network"] }),
);
};

return (
Expand Down
4 changes: 4 additions & 0 deletions web/src/components/network/WifiConnectionForm.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import { WifiConnectionForm } from "~/components/network";

jest.mock("~/client");

jest.mock("~/queries/network", () => ({
useNetworkConfigChanges: jest.fn(),
}));

Element.prototype.scrollIntoView = jest.fn();

const hiddenNetworkMock = {
Expand Down
35 changes: 19 additions & 16 deletions web/src/components/network/WifiNetworksListPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ import {
} from "@patternfly/react-core";
import { Icon } from "~/components/layout";
import { WifiConnectionForm } from "~/components/network";
import { ButtonLink, EmptyState } from "~/components/core";
import { ButtonLink } from "~/components/core";
import { DeviceState } from "~/client/network/model";
import { useInstallerClient } from "~/context/installer";
import { _ } from "~/i18n";
import { formatIp } from "~/client/network/utils";
import { sprintf } from "sprintf-js";
import { useQueryClient } from "@tanstack/react-query";
import { useSelectedWifi, useSelectedWifiChange } from "~/queries/network";

const HIDDEN_NETWORK = Object.freeze({ hidden: true });

Expand Down Expand Up @@ -85,11 +86,13 @@ const ConnectionData = ({ network }) => {
return <Stack hasGutter>{connectionAddresses(network)}</Stack>;
};

const WifiDrawerPanelBody = ({ network, onCancel, onForget }) => {
const WifiDrawerPanelBody = ({ network, onCancel }) => {
const client = useInstallerClient();
const queryClient = useQueryClient();
const { data } = useSelectedWifi();
const forgetNetwork = async () => {
await client.network.deleteConnection(network.settings.id);
onForget();
queryClient.invalidateQueries({ queryKey: ["network", "connections"] })
};

if (!network) return;
Expand All @@ -98,6 +101,8 @@ const WifiDrawerPanelBody = ({ network, onCancel, onForget }) => {

if (network === HIDDEN_NETWORK) return <Form />;

if (data.needsAuth) return <Form />;

if (network.settings && !network.device) {
return (
<Split hasGutter>
Expand Down Expand Up @@ -180,22 +185,21 @@ const NetworkListName = ({ network }) => {
* @param {function} props.onSelectionCallback - the function to trigger when user selects a network
* @param {function} props.onCancelCallback - the function to trigger when user cancel dismiss before connecting to a network
*/
function WifiNetworksListPage({
selected,
onSelectionChange,
networks = [],
forceUpdateNetworksCallback = () => {},
}) {
const selectHiddenNetwork = () => {
onSelectionChange(HIDDEN_NETWORK);
function WifiNetworksListPage({ networks = [] }) {
const { data } = useSelectedWifi();
const selected = data.ssid === undefined ? HIDDEN_NETWORK : networks.find(n => n.ssid === data.ssid);
const changeSelected = useSelectedWifiChange();

const selectHiddneNetwork = () => {
changeSelected.mutate({ ssid: undefined, needsAuth: null });
};

const selectNetwork = (ssid) => {
onSelectionChange(networks.find((n) => n.ssid === ssid));
changeSelected.mutate({ ssid, needsAuth: null });
};

const unselectNetwork = () => {
onSelectionChange(undefined);
changeSelected.mutate({ ssid: null, needsAuth: null });
};

const renderElements = () => {
Expand Down Expand Up @@ -246,7 +250,6 @@ function WifiNetworksListPage({
<WifiDrawerPanelBody
network={selected}
onCancel={unselectNetwork}
onForget={forceUpdateNetworksCallback}
/>
</DrawerPanelBody>
</DrawerPanelContent>
Expand All @@ -261,7 +264,7 @@ function WifiNetworksListPage({
>
{renderElements()}
</DataList>
<Button variant="link" onClick={selectHiddenNetwork}>
<Button variant="link" onClick={selectHiddneNetwork}>
{_("Connect to hidden network")}
</Button>
</Stack>
Expand Down
Loading
Loading