refactor: replace serverdata with suspense query (#1265)

* refactor: replace serverdata with suspense query

* fix: deepsource issues
This commit is contained in:
Meier Lukas
2024-10-11 23:47:07 +02:00
committed by GitHub
parent 511c9a4dbb
commit 0f8d9edb3e
41 changed files with 288 additions and 646 deletions

View File

@@ -21,7 +21,7 @@ import {
Title,
Tooltip,
} from "@mantine/core";
import { useDisclosure, useListState, useTimeout } from "@mantine/hooks";
import { useDisclosure, useTimeout } from "@mantine/hooks";
import type { IconProps } from "@tabler/icons-react";
import {
IconAlertTriangle,
@@ -40,9 +40,6 @@ import { MantineReactTable, useMantineReactTable } from "mantine-react-table";
import { clientApi } from "@homarr/api/client";
import { useIntegrationsWithInteractAccess } from "@homarr/auth/client";
import { humanFileSize } from "@homarr/common";
import type { Modify } from "@homarr/common/types";
import type { Integration } from "@homarr/db/schema/sqlite";
import type { IntegrationKindByCategory } from "@homarr/definitions";
import { getIconUrl, getIntegrationKindsByCategory } from "@homarr/definitions";
import type {
DownloadClientJobsAndStatus,
@@ -91,30 +88,35 @@ export default function DownloadClientsWidget({
isEditMode,
integrationIds,
options,
serverData,
setOptions,
}: WidgetComponentProps<"downloads">) {
const integrationsWithInteractions = useIntegrationsWithInteractAccess().flatMap(({ id }) =>
integrationIds.includes(id) ? [id] : [],
);
const [currentItems, currentItemsHandlers] = useListState<{
integration: Modify<Integration, { kind: IntegrationKindByCategory<"downloadClient"> }>;
timestamp: Date;
data: DownloadClientJobsAndStatus | null;
}>(
//Automatically invalidate data older than 30 seconds
serverData?.initialData?.map((item) =>
dayjs().diff(item.timestamp) < invalidateTime ? item : { ...item, timestamp: new Date(0), data: null },
) ?? [],
const [currentItems] = clientApi.widget.downloads.getJobsAndStatuses.useSuspenseQuery(
{
integrationIds,
},
{
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
retry: false,
select(data) {
return data.map((item) =>
dayjs().diff(item.timestamp) < invalidateTime ? item : { ...item, timestamp: new Date(0), data: null },
);
},
},
);
const utils = clientApi.useUtils();
//Invalidate all data after no update for 30 seconds using timer
const invalidationTimer = useTimeout(
() => {
currentItemsHandlers.applyWhere(
() => true,
(item) => ({ ...item, timestamp: new Date(0), data: null }),
utils.widget.downloads.getJobsAndStatuses.setData({ integrationIds }, (prevData) =>
prevData?.map((item) => ({ ...item, timestamp: new Date(0), data: null })),
);
},
invalidateTime,
@@ -146,20 +148,24 @@ export default function DownloadClientsWidget({
//Don't update already invalid data (new Date (0))
.filter(({ timestamp }) => dayjs().diff(timestamp) > invalidateTime && timestamp > new Date(0))
.map(({ integration }) => integration.id);
currentItemsHandlers.applyWhere(
({ integration }) => invalidIndexes.includes(integration.id),
//Set date to now so it won't update that integration for at least 30 seconds
(item) => ({ ...item, timestamp: new Date(0), data: null }),
utils.widget.downloads.getJobsAndStatuses.setData({ integrationIds }, (prevData) =>
prevData?.map((item) =>
invalidIndexes.includes(item.integration.id) ? item : { ...item, timestamp: new Date(0), data: null },
),
);
//Find id to update
const updateIndex = currentItems.findIndex((pair) => pair.integration.id === data.integration.id);
if (updateIndex >= 0) {
//Update found index
currentItemsHandlers.setItem(updateIndex, data);
} else if (integrationIds.includes(data.integration.id)) {
//Append index not found (new integration)
currentItemsHandlers.append(data);
}
utils.widget.downloads.getJobsAndStatuses.setData({ integrationIds }, (prevData) => {
const updateIndex = currentItems.findIndex((pair) => pair.integration.id === data.integration.id);
if (updateIndex >= 0) {
//Update found index
return prevData?.map((pair, index) => (index === updateIndex ? data : pair));
} else if (integrationIds.includes(data.integration.id)) {
//Append index not found (new integration)
return [...(prevData ?? []), data];
}
return undefined;
});
//Reset no update timer
invalidationTimer.clear();
invalidationTimer.start();

View File

@@ -31,7 +31,7 @@ const columnsSort = columnsList.filter((column) =>
sortingExclusion.some((exclusion) => exclusion !== column),
) as Exclude<typeof columnsList, (typeof sortingExclusion)[number]>;
export const { definition, componentLoader, serverDataLoader } = createWidgetDefinition("downloads", {
export const { definition, componentLoader } = createWidgetDefinition("downloads", {
icon: IconDownload,
options: optionsBuilder.from(
(factory) => ({
@@ -105,6 +105,4 @@ export const { definition, componentLoader, serverDataLoader } = createWidgetDef
},
),
supportedIntegrations: getIntegrationKindsByCategory("downloadClient"),
})
.withServerData(() => import("./serverData"))
.withDynamicImport(() => import("./component"));
}).withDynamicImport(() => import("./component"));

View File

@@ -1,21 +0,0 @@
"use server";
import { api } from "@homarr/api/server";
import type { WidgetProps } from "../definition";
export default async function getServerDataAsync({ integrationIds }: WidgetProps<"downloads">) {
if (integrationIds.length === 0) {
return {
initialData: undefined,
};
}
const jobsAndStatuses = await api.widget.downloads.getJobsAndStatuses({
integrationIds,
});
return {
initialData: jobsAndStatuses,
};
}