Skip to content

Commit

Permalink
Move network to TanStack query (#1489)
Browse files Browse the repository at this point in the history
Adapted the UI using TanStack query instead of route loaders and
useEffect hooks.


---

Related to #1439,
#1452,
#1470, and
#1483
  • Loading branch information
teclator committed Jul 23, 2024
2 parents 2976ebf + c82cc1e commit 55afa6e
Show file tree
Hide file tree
Showing 10 changed files with 340 additions and 229 deletions.
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

0 comments on commit 55afa6e

Please sign in to comment.