perf: optimize dashboard loading and builds
This commit is contained in:
135 files changed
+8521
-1718
No files matched your search
@@ -5,27 +5,8 @@ import {
|
||||
hiddenFromOnboarding,
|
||||
integrationDefs,
|
||||
integrationKinds,
|
||||
getWidgetKindsForIntegration,
|
||||
} from "@homarr/definitions";
|
||||
import { loadAllWidgetDefinitions } from "@homarr/widgets/manifest";
|
||||
|
||||
const buildWidgetsByIntegration = async () => {
|
||||
const map = Object.fromEntries(integrationKinds.map((kind) => [kind, [] as WidgetKind[]])) as Record<
|
||||
IntegrationKind,
|
||||
WidgetKind[]
|
||||
>;
|
||||
const definitions = await loadAllWidgetDefinitions();
|
||||
|
||||
for (const [widgetKind, definition] of definitions) {
|
||||
const supported =
|
||||
"supportedIntegrations" in definition ? (definition.supportedIntegrations as IntegrationKind[]) : [];
|
||||
for (const kind of supported) {
|
||||
if (kind in map) {
|
||||
map[kind].push(widgetKind);
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
};
|
||||
|
||||
export const categoryTranslationKeys: Record<string, string> = {
|
||||
dnsHole: "integration.category.dnsHole",
|
||||
@@ -64,14 +45,13 @@ export interface IntegrationGridItem {
|
||||
widgets: WidgetKind[];
|
||||
}
|
||||
|
||||
export const buildSortedIntegrations = async (
|
||||
export const buildSortedIntegrations = (
|
||||
options: { enableMockIntegration?: boolean; onboarding?: boolean } = {},
|
||||
): Promise<IntegrationGridItem[]> => {
|
||||
const widgetsByIntegration = await buildWidgetsByIntegration();
|
||||
): IntegrationGridItem[] => {
|
||||
return integrationKinds
|
||||
.filter((kind) => {
|
||||
if (options.onboarding && hiddenFromOnboarding.has(kind)) return false;
|
||||
if (options.onboarding && widgetsByIntegration[kind].length === 0) return false;
|
||||
if (options.onboarding && getWidgetKindsForIntegration(kind).length === 0) return false;
|
||||
if (!options.enableMockIntegration && kind === "mock") return false;
|
||||
return true;
|
||||
})
|
||||
@@ -79,7 +59,7 @@ export const buildSortedIntegrations = async (
|
||||
kind,
|
||||
name: getIntegrationName(kind),
|
||||
categories: [...new Set(integrationDefs[kind].category.flat())] as string[],
|
||||
widgets: widgetsByIntegration[kind],
|
||||
widgets: getWidgetKindsForIntegration(kind),
|
||||
}))
|
||||
.toSorted((left, right) => {
|
||||
const leftIdx = featuredIntegrations.indexOf(left.kind);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { Badge, Card, Center, Group, Loader, Stack, Text, Tooltip } from "@mantine/core";
|
||||
import { Badge, Card, Center, Group, Stack, Text, Tooltip } from "@mantine/core";
|
||||
import { IconBrandDocker, IconCheck, IconPuzzle } from "@tabler/icons-react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import type { IntegrationKind } from "@homarr/definitions";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
@@ -43,14 +42,10 @@ export const IntegrationMultiSelectGrid = ({
|
||||
[selectedKindsArray, onSelectionChange],
|
||||
);
|
||||
|
||||
const {
|
||||
data: integrations = [],
|
||||
isPending,
|
||||
isError,
|
||||
} = useQuery({
|
||||
queryKey: ["widget-integrations", { enableMockIntegration, onboarding }],
|
||||
queryFn: () => buildSortedIntegrations({ enableMockIntegration, onboarding }),
|
||||
});
|
||||
const integrations = useMemo(
|
||||
() => buildSortedIntegrations({ enableMockIntegration, onboarding }),
|
||||
[enableMockIntegration, onboarding],
|
||||
);
|
||||
|
||||
const sorted = useMemo(() => {
|
||||
if (!detectedKinds || detectedKinds.size === 0) return integrations;
|
||||
@@ -69,11 +64,6 @@ export const IntegrationMultiSelectGrid = ({
|
||||
disableScroll={onboarding}
|
||||
disableAutoFocus={onboarding}
|
||||
>
|
||||
{isPending && (
|
||||
<Center p="xl">
|
||||
<Loader size="sm" />
|
||||
</Center>
|
||||
)}
|
||||
{filtered.map((integration) => {
|
||||
const isSelected = selectedKinds.has(integration.kind);
|
||||
const isDetected = detectedKinds?.has(integration.kind) ?? false;
|
||||
@@ -140,14 +130,7 @@ export const IntegrationMultiSelectGrid = ({
|
||||
);
|
||||
})}
|
||||
|
||||
{isError && (
|
||||
<Center p="xl">
|
||||
<Text c="red" role="alert">
|
||||
{t("common.error")}
|
||||
</Text>
|
||||
</Center>
|
||||
)}
|
||||
{!isPending && !isError && filtered.length === 0 && (
|
||||
{filtered.length === 0 && (
|
||||
<Center p="xl">
|
||||
<Text c="dimmed">{t("common.noResults")}</Text>
|
||||
</Center>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo, useState } from "react";
|
||||
import { Badge, Card, Center, Group, Loader, Stack, Text, Tooltip } from "@mantine/core";
|
||||
import { Badge, Card, Center, Group, Stack, Text, Tooltip } from "@mantine/core";
|
||||
import { IconPuzzle } from "@tabler/icons-react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import type { IntegrationKind } from "@homarr/definitions";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
@@ -20,14 +19,7 @@ export const IntegrationSelectGrid = ({ onSelect, enableMockIntegration = false
|
||||
const [search, setSearch] = useState("");
|
||||
const t = useI18n();
|
||||
|
||||
const {
|
||||
data: integrations = [],
|
||||
isPending,
|
||||
isError,
|
||||
} = useQuery({
|
||||
queryKey: ["widget-integrations", { enableMockIntegration }],
|
||||
queryFn: () => buildSortedIntegrations({ enableMockIntegration }),
|
||||
});
|
||||
const integrations = useMemo(() => buildSortedIntegrations({ enableMockIntegration }), [enableMockIntegration]);
|
||||
const filtered = useMemo(() => filterIntegrations(integrations, search), [integrations, search]);
|
||||
|
||||
return (
|
||||
@@ -36,11 +28,6 @@ export const IntegrationSelectGrid = ({ onSelect, enableMockIntegration = false
|
||||
onSearchChange={setSearch}
|
||||
placeholder={`${t("integration.page.list.search")}...`}
|
||||
>
|
||||
{isPending && (
|
||||
<Center p="xl">
|
||||
<Loader size="sm" />
|
||||
</Center>
|
||||
)}
|
||||
{filtered.map((integration) => (
|
||||
<Card
|
||||
key={integration.kind}
|
||||
@@ -93,14 +80,7 @@ export const IntegrationSelectGrid = ({ onSelect, enableMockIntegration = false
|
||||
</Card>
|
||||
))}
|
||||
|
||||
{isError && (
|
||||
<Center p="xl">
|
||||
<Text c="red" role="alert">
|
||||
{t("common.error")}
|
||||
</Text>
|
||||
</Center>
|
||||
)}
|
||||
{!isPending && !isError && filtered.length === 0 && (
|
||||
{filtered.length === 0 && (
|
||||
<Center p="xl">
|
||||
<Text c="dimmed">{t("common.noResults")}</Text>
|
||||
</Center>
|
||||
|
||||
Reference in new issue
Block a user