diff --git a/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/LinkDetail.tsx b/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/LinkDetail.tsx index 7436f2eb..bbf3d051 100644 --- a/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/LinkDetail.tsx +++ b/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/LinkDetail.tsx @@ -1,17 +1,20 @@ import { Trans } from "@lingui/macro"; import { useState } from "preact/hooks"; +import { useCallback } from "react"; import { Warning } from "components/icons/status"; import Tabs from "components/tabs"; +import { useToast } from "components/toast/toastProvider"; import { StatusAndButton } from "plugins/lime-plugin-mesh-wide/src/components/Components"; -import { useSetReferenceState } from "plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/SetReferenceStateBtn"; +import { useSetLinkReferenceStateModal } from "plugins/lime-plugin-mesh-wide/src/components/configPage/modals"; import { getQueryByLinkType, usePointToPointErrors, } from "plugins/lime-plugin-mesh-wide/src/hooks/useLocatedLinks"; import { MacToMacLink } from "plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink"; import { readableBytes } from "plugins/lime-plugin-mesh-wide/src/lib/utils"; +import { useSetLinkReferenceState } from "plugins/lime-plugin-mesh-wide/src/meshWideQueries"; import { BaseMacToMacLink, BatmanLinkErrorCodes, @@ -221,6 +224,8 @@ export const LinkReferenceStatus = ({ reference }: LinkMapFeature) => { type: reference.type, }); + const isDown = !errors.linkUp; + // Check if there are errors of global reference state to shown const { reference: fetchDataReference } = getQueryByLinkType( reference.type @@ -232,8 +237,63 @@ export const LinkReferenceStatus = ({ reference }: LinkMapFeature) => { referenceError = true; } + const { toggleModal, confirmModal, isModalOpen } = + useSetLinkReferenceStateModal(); + const { showToast } = useToast(); + // Mutation to update the reference state - const { mutate, btnText } = useSetReferenceState(reference.type); + const nodesToUpdate = reference.nodes.reduce((acc, node) => { + acc[node.ipv4] = node.hostname; + return acc; + }, {}); + const { callMutations } = useSetLinkReferenceState({ + linkType: reference.type, + linkToUpdate: reference, + isDown, + nodesToUpdate, + params: { + onSuccess: () => { + showToast({ + text: New reference state set!, + }); + }, + onError: () => { + showToast({ + text: Error setting new reference state!, + }); + }, + onSettled: () => { + if (isModalOpen) toggleModal(); + }, + }, + }); + + const setReferenceState = useCallback(async () => { + confirmModal( + reference.type, + Object.values(nodesToUpdate), + isDown, + async () => { + await callMutations(); + } + ); + }, [callMutations, confirmModal, isDown, nodesToUpdate, reference.type]); + + let btnText = ( + + Set reference state for this +
{reference.type} link +
+ ); + if (isDown) { + btnText = ( + + Delete this {reference.type} link +
+ from reference state +
+ ); + } let errorMessage = Same status as in the reference state; if (referenceError) { @@ -252,7 +312,7 @@ export const LinkReferenceStatus = ({ reference }: LinkMapFeature) => { {errorMessage} diff --git a/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/NodeDetail.tsx b/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/NodeDetail.tsx index 0357bc4f..d7ddde77 100644 --- a/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/NodeDetail.tsx +++ b/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/NodeDetail.tsx @@ -1,16 +1,22 @@ import { Trans } from "@lingui/macro"; +import { useCallback } from "react"; + +import { useToast } from "components/toast/toastProvider"; import { StatusAndButton } from "plugins/lime-plugin-mesh-wide/src/components/Components"; import RemoteRebootBtn from "plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/RebootNodeBtn"; -import { useSetReferenceState } from "plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/SetReferenceStateBtn"; import UpdateNodeInfoBtn from "plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/UpdateNodeInfoBtn"; import { Row, TitleAndText, } from "plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/index"; +import { useSetNoeInfoReferenceStateModal } from "plugins/lime-plugin-mesh-wide/src/components/configPage/modals"; import { useSingleNodeErrors } from "plugins/lime-plugin-mesh-wide/src/hooks/useSingleNodeErrors"; import { getArrayDifference } from "plugins/lime-plugin-mesh-wide/src/lib/utils"; -import { useMeshWideNodesReference } from "plugins/lime-plugin-mesh-wide/src/meshWideQueries"; +import { + useMeshWideNodesReference, + useSetNodeInfoReferenceState, +} from "plugins/lime-plugin-mesh-wide/src/meshWideQueries"; import { NodeErrorCodes, NodeMapFeature, @@ -111,12 +117,49 @@ export const NodeReferenceStatus = ({ actual, reference }: NodeMapFeature) => { reference, }); + const hostname = isDown ? reference.hostname : actual.hostname; + const ip = isDown ? reference.ipv4 : actual.ipv4; + // Check if there are errors of global reference state to shown const { data: meshWideNodesReference, isError: isReferenceError } = useMeshWideNodesReference({}); + const { toggleModal, confirmModal, isModalOpen } = + useSetNoeInfoReferenceStateModal(); + const { showToast } = useToast(); + // Mutation to update the reference state - const { mutate, btnText } = useSetReferenceState("node_info"); + const { mutateAsync } = useSetNodeInfoReferenceState({ + ip, + hostname, + isDown, + params: { + onSuccess: () => { + showToast({ + text: New reference state set!, + }); + }, + onError: () => { + showToast({ + text: Error setting new reference state!, + }); + }, + onSettled: () => { + if (isModalOpen) toggleModal(); + }, + }, + }); + + const setReferenceState = useCallback(async () => { + confirmModal(hostname, isDown, async () => { + await mutateAsync(); + }); + }, [confirmModal, hostname, isDown, mutateAsync]); + + let btnText = Set reference state for this node; + if (isDown) { + btnText = Delete this this node from reference state; + } let referenceError = false; if ( @@ -144,7 +187,7 @@ export const NodeReferenceStatus = ({ actual, reference }: NodeMapFeature) => { {errorMessage} diff --git a/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/SetReferenceStateBtn.tsx b/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/SetReferenceStateBtn.tsx deleted file mode 100644 index fda44759..00000000 --- a/plugins/lime-plugin-mesh-wide/src/components/FeatureDetail/SetReferenceStateBtn.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Trans } from "@lingui/macro"; -import { useCallback } from "react"; - -import { useToast } from "components/toast/toastProvider"; - -import { useSetReferenceStateModal } from "plugins/lime-plugin-mesh-wide/src/components/configPage/modals"; -import { - useSetBatmanLinksInfoReferenceState, - useSetNodeInfoReferenceState, - useSetWifiLinksInfoReferenceState, -} from "plugins/lime-plugin-mesh-wide/src/meshWideQueries"; -import { DataTypes } from "plugins/lime-plugin-mesh-wide/src/meshWideTypes"; - -const toastDuration = 5000; - -export const useSetReferenceState = (dataType: T) => { - const { toggleModal, confirmModal, isModalOpen } = - useSetReferenceStateModal(); - const { showToast } = useToast(); - - const mutationOpts = { - onSuccess: () => { - showToast({ - text: New reference state set!, - duration: toastDuration, - }); - }, - onError: () => { - showToast({ - text: Error setting new reference state!, - duration: toastDuration, - }); - }, - onSettled: () => { - if (isModalOpen) toggleModal(); - }, - }; - - const { mutateAsync: nodesMutation } = - useSetNodeInfoReferenceState(mutationOpts); - const { mutateAsync: wifiMutation } = - useSetWifiLinksInfoReferenceState(mutationOpts); - const { mutateAsync: batmanMutation } = - useSetBatmanLinksInfoReferenceState(mutationOpts); - - const btnText = Set {dataType} reference state; - - const mutate = useCallback(async () => { - switch (dataType) { - case "node_info": - await confirmModal(dataType, async () => { - await nodesMutation(); - }); - break; - case "wifi_links_info": - confirmModal(dataType, async () => { - await wifiMutation(); - }); - break; - case "bat_links_info": - confirmModal(dataType, async () => { - await batmanMutation(); - }); - break; - } - }, [batmanMutation, confirmModal, dataType, nodesMutation, wifiMutation]); - - return { mutate, btnText }; -}; diff --git a/plugins/lime-plugin-mesh-wide/src/components/configPage/modals.tsx b/plugins/lime-plugin-mesh-wide/src/components/configPage/modals.tsx index e0f2f40b..c4ce68c7 100644 --- a/plugins/lime-plugin-mesh-wide/src/components/configPage/modals.tsx +++ b/plugins/lime-plugin-mesh-wide/src/components/configPage/modals.tsx @@ -85,18 +85,77 @@ export const useAddNewSectionModal = () => { return { actionModal, toggleModal }; }; -export const useSetReferenceStateModal = () => { +export const useSetNoeInfoReferenceStateModal = () => { const { toggleModal, setModalState, isModalOpen } = useModal(); const confirmModal = useCallback( - (dataType: DataTypes, cb: () => Promise) => { + (nodeName: string, isDown: boolean, cb: () => Promise) => { + let title = Set reference state for {nodeName}; + let content = Set the reference state for this node.; + if (isDown) { + title = ( + Remove {nodeName} from the reference state + ); + content = ( + + This node seems down, remove them from the reference + state? + + ); + } setModalState({ - title: Set reference state for {dataType}, - content: ( + title, + content, + successCb: cb, + successBtnText: Continue, + }); + toggleModal(); + }, + [setModalState, toggleModal] + ); + return { confirmModal, toggleModal, isModalOpen }; +}; + +export const useSetLinkReferenceStateModal = () => { + const { toggleModal, setModalState, isModalOpen } = useModal(); + + const confirmModal = useCallback( + ( + dataType: DataTypes, + nodes: string[], + isDown: boolean, + cb: () => Promise + ) => { + let title = ( + Set reference state for this {dataType} link? + ); + let content = ( + This will set the reference state of this link: + ); + if (isDown) { + title = ( + + Remove this {dataType} from the reference state + + ); + content = ( - Are you sure you want to set this reference state for{" "} - {dataType} + This link seems down, remove them from the reference + state? + ); + } + setModalState({ + title, + content: ( +
+ {content} +
+
+
{nodes[0]}
+
{nodes[1]}
+
+
), successCb: cb, successBtnText: Continue, diff --git a/plugins/lime-plugin-mesh-wide/src/hooks/useLocatedLinks.tsx b/plugins/lime-plugin-mesh-wide/src/hooks/useLocatedLinks.tsx index 4be5cb17..12894ec9 100644 --- a/plugins/lime-plugin-mesh-wide/src/hooks/useLocatedLinks.tsx +++ b/plugins/lime-plugin-mesh-wide/src/hooks/useLocatedLinks.tsx @@ -35,16 +35,19 @@ interface getQueryByLinkTypeReturnType { export const getQueryByLinkType = ( type: T ): getQueryByLinkTypeReturnType => { - if (type === "bat_links_info") { - return { - state: useMeshWideBatman, - reference: useMeshWideBatmanReference, - } as getQueryByLinkTypeReturnType; + switch (type) { + case "bat_links_info": + return { + state: useMeshWideBatman, + reference: useMeshWideBatmanReference, + } as getQueryByLinkTypeReturnType; + case "wifi_links_info": + default: + return { + state: useMeshWideLinks, + reference: useMeshWideLinksReference, + } as getQueryByLinkTypeReturnType; } - return { - state: useMeshWideLinks, - reference: useMeshWideLinksReference, - } as getQueryByLinkTypeReturnType; }; interface IUselocatedLinks { diff --git a/plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink.ts b/plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink.ts index 067c458f..dae62fd9 100644 --- a/plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink.ts +++ b/plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink.ts @@ -2,6 +2,7 @@ import { BaseMacToMacLink, Coordinates, ILocatedLink, + INodeInfo, LinkDataTypes, LinkType, MacToMacLinkId, @@ -15,11 +16,15 @@ import { */ export class PontToPointLink { private _links: BaseMacToMacLink[] = []; + private _nodes: INodeInfo[] = []; public readonly id: PointToPointLinkId; public readonly coordinates: Coordinates[] = []; - constructor(coord1: Coordinates, coord2: Coordinates) { + constructor(node1: INodeInfo, node2: INodeInfo) { + const coord1 = node1.coordinates; + const coord2 = node2.coordinates; this.id = PontToPointLink.generateId(coord1, coord2); + this.nodes.push(node1, node2); this.coordinates.push(coord1, coord2); } @@ -50,19 +55,14 @@ export class PontToPointLink { return false; } - get names(): string[] { - return [ - ...this._links.reduce((acc, link) => { - Object.keys(link).forEach((key) => acc.add(key)); - return acc; - }, new Set()), - ] as string[]; - } - get links() { return this._links; } + get nodes() { + return this._nodes; + } + /** * Generate a deterministic unique id based on the coordinates of a node. * @param coord1 diff --git a/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.ts b/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.ts index e620c716..8736f9ec 100644 --- a/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.ts +++ b/plugins/lime-plugin-mesh-wide/src/lib/links/getLinksCoordinates.ts @@ -55,8 +55,8 @@ export const mergeLinksAndCoordinates = ( // If this point to point link no exists, instantiate it if (!result[linkKey]) { result[linkKey] = new PontToPointLink( - nodes[linkNodeName].coordinates, - nodes[dstNodeName!].coordinates + nodes[linkNodeName], + nodes[dstNodeName] ); } // If the link PontToPointLink already exists and the link is already added, ignore it diff --git a/plugins/lime-plugin-mesh-wide/src/meshWideQueries.tsx b/plugins/lime-plugin-mesh-wide/src/meshWideQueries.tsx index aa86658a..9c8ddc3a 100644 --- a/plugins/lime-plugin-mesh-wide/src/meshWideQueries.tsx +++ b/plugins/lime-plugin-mesh-wide/src/meshWideQueries.tsx @@ -1,5 +1,8 @@ import { useMutation, useQuery } from "@tanstack/react-query"; +import { meshUpgradeQueryKeys } from "plugins/lime-plugin-mesh-wide-upgrade/src/meshUpgradeQueriesKeys"; +import { getQueryByLinkType } from "plugins/lime-plugin-mesh-wide/src/hooks/useLocatedLinks"; +import { PontToPointLink } from "plugins/lime-plugin-mesh-wide/src/lib/links/PointToPointLink"; import { doSharedStateApiCall, syncAllDataTypes, @@ -15,9 +18,11 @@ import { IMeshWideConfig, INodes, IWifiLinks, + LinkType, SelectedMapFeature, } from "plugins/lime-plugin-mesh-wide/src/meshWideTypes"; +import { useMeshWideSyncCall } from "utils/meshWideSyncCall"; import { useSharedData } from "utils/useSharedData"; const refetchInterval = 60000; @@ -110,21 +115,28 @@ export function useMeshWideNodes(params) { * to unify criterias and add a confirmation modal */ -interface IShatedStateRemoteQueryProps { +interface ISharedStateRemoteQueryProps { ip: string; - hostname?: string; params?: any; } +type ISharedStateSetReferenceQueryProps = { + isDown: boolean; + hostname?: string; +} & ISharedStateRemoteQueryProps; + export const useSetNodeInfoReferenceState = ({ ip, hostname, + isDown, params, -}: IShatedStateRemoteQueryProps) => { +}: ISharedStateSetReferenceQueryProps) => { const type = "node_info"; const { data } = useMeshWideNodes({}); + // Ignore the types here because it to delete a node you have to pass an empty object + // @ts-ignore const queryKey = getFromSharedStateKeys.insertIntoReferenceState(type, { - [hostname]: data[hostname], + [hostname]: isDown ? null : data[hostname], }); return useMutation( queryKey, @@ -135,42 +147,50 @@ export const useSetNodeInfoReferenceState = ({ ); }; -export const useSetWifiLinksInfoReferenceState = ({ - ip, - hostname, - params, -}: IShatedStateRemoteQueryProps) => { - const type = "wifi_links_info"; - const { data } = useMeshWideLinks({}); - const queryKey = getFromSharedStateKeys.insertIntoReferenceState(type, { - [hostname]: data[hostname], - }); - return useMutation( - queryKey, - () => doSharedStateApiCall(queryKey, ip), - { - ...params, - } - ); -}; +interface IUseSetLinkReferenceState { + linkType: LinkType; + linkToUpdate: PontToPointLink; + nodesToUpdate: { [ip: string]: string }; // { ip: hostname } + params: any; + isDown: boolean; +} -export const useSetBatmanLinksInfoReferenceState = ({ - ip, - hostname, +export const useSetLinkReferenceState = ({ + linkType, + linkToUpdate, + isDown, + nodesToUpdate, params, -}: IShatedStateRemoteQueryProps) => { - const type = "bat_links_info"; - const { data } = useMeshWideBatman({}); - const queryKey = getFromSharedStateKeys.insertIntoReferenceState(type, { - [hostname]: data[hostname], +}: IUseSetLinkReferenceState) => { + const { state, reference } = getQueryByLinkType(linkType); + const { data } = state({}); + const { data: referenceData } = reference({}); + + return useMeshWideSyncCall({ + mutationKey: meshUpgradeQueryKeys.remoteConfirmUpgrade(), + mutationFn: ({ ip }) => { + const hostname = nodesToUpdate[ip]; + const referenceLinks = referenceData[hostname]; + for (const mactomac of linkToUpdate.links) { + if (isDown) { + delete referenceLinks[mactomac.id]; + continue; + } + referenceLinks[mactomac.id] = data[hostname][mactomac.id]; + } + const queryKey = getFromSharedStateKeys.insertIntoReferenceState( + linkType, + // For some reason I have to ignore the types here because it not infers properly. + // Using the same code but for a specific link type, it works. + // For some reason with the use of getQueryByLinkType it doesn't work. + // @ts-ignore + { [hostname]: referenceLinks } + ); + return doSharedStateApiCall(queryKey, ip); + }, + ips: Object.keys(nodesToUpdate), + options: params, }); - return useMutation( - queryKey, - () => doSharedStateApiCall(queryKey, ip), - { - ...params, - } - ); }; /** @@ -179,9 +199,8 @@ export const useSetBatmanLinksInfoReferenceState = ({ export const usePublishOnRemoteNode = ({ ip, - hostname, ...opts -}: IShatedStateRemoteQueryProps) => { +}: ISharedStateRemoteQueryProps) => { return useMutation({ mutationFn: ({ ip }) => doSharedStateApiCall( @@ -195,11 +214,11 @@ export const usePublishOnRemoteNode = ({ export const useSyncDataTypes = ({ ip, - ...opts -}: IShatedStateRemoteQueryProps) => { + params, +}: ISharedStateRemoteQueryProps) => { return useMutation(syncAllDataTypes, { mutationKey: [syncFromSharedStateKey, ip], - ...opts, + ...params, }); };