diff --git a/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx b/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx index 7d06a8951..3e167dd8c 100644 --- a/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx +++ b/apps/nextjs/src/app/[locale]/_client-providers/trpc.tsx @@ -2,6 +2,7 @@ import type { PropsWithChildren } from "react"; import { useState } from "react"; +import type { QueryKey } from "@tanstack/react-query"; import { QueryClient } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { ReactQueryStreamedHydration } from "@tanstack/react-query-next-experimental"; @@ -33,6 +34,7 @@ import { import { createHeadersCallbackForSource, getTrpcUrl } from "@homarr/api/shared"; import { env } from "@homarr/common/env"; import { showWarningNotification } from "@homarr/notifications"; +import { widgetImports } from "@homarr/widgets"; import { createWidgetQueryPersister } from "./query-cache-persister"; @@ -90,6 +92,13 @@ export function TRPCReactProvider(props: PropsWithChildren) { }, }); client.setQueryDefaults([["widget"]], { refetchInterval: queryCacheDefaultRefetchIntervalMs }); + for (const { definition } of Object.values(widgetImports)) { + const def = definition as { refetchInterval?: number | null; queryKey?: QueryKey; kind: string }; + if (def.refetchInterval === undefined) continue; + const key = def.queryKey ?? [["widget", def.kind]]; + const interval = def.refetchInterval === null ? false : def.refetchInterval * 1000; + client.setQueryDefaults(key, { refetchInterval: interval }); + } return client; }); diff --git a/packages/widgets/src/definition.ts b/packages/widgets/src/definition.ts index aaf60269f..3ba53987e 100644 --- a/packages/widgets/src/definition.ts +++ b/packages/widgets/src/definition.ts @@ -70,6 +70,7 @@ export const createWidgetDefinition = ({ showToggleAllButtons: factory.switch({ diff --git a/packages/widgets/src/dns-hole/summary/index.ts b/packages/widgets/src/dns-hole/summary/index.ts index fe1c8b8a1..0d605e8e9 100644 --- a/packages/widgets/src/dns-hole/summary/index.ts +++ b/packages/widgets/src/dns-hole/summary/index.ts @@ -9,6 +9,8 @@ export const widgetKind = "dnsHoleSummary"; export const { definition, componentLoader } = createWidgetDefinition(widgetKind, { icon: IconAd, + queryKey: [["widget", "dnsHole"]], + refetchInterval: 5, createOptions() { return optionsBuilder.from((factory) => ({ usePiHoleColors: factory.switch({ diff --git a/packages/widgets/src/docker/component.tsx b/packages/widgets/src/docker/component.tsx index bbd961c1d..e56cd5cf7 100644 --- a/packages/widgets/src/docker/component.tsx +++ b/packages/widgets/src/docker/component.tsx @@ -184,13 +184,7 @@ export default function DockerWidget({ options, width, isEditMode }: WidgetCompo const t = useScopedI18n("docker"); const isTiny = width <= 256; - const { - data, - refetch, - isFetching, - } = clientApi.docker.getContainers.useQuery(undefined, { - refetchInterval: 30_000, - }); + const { data, refetch, isFetching } = clientApi.docker.getContainers.useQuery(); const containers = data?.containers ?? []; const timestamp = useMemo(() => data?.timestamp ?? new Date(), [data?.timestamp]); const relativeTime = useTimeAgo(timestamp); @@ -304,9 +298,7 @@ export default function DockerWidget({ options, width, isEditMode }: WidgetCompo {t("table.totalMemory", { memory: formatBytes(totals.memory) })} - + export const { definition, componentLoader } = createWidgetDefinition("downloads", { icon: IconDownload, + refetchInterval: 5, createOptions() { return optionsBuilder.from( (factory) => ({ diff --git a/packages/widgets/src/firewall/index.ts b/packages/widgets/src/firewall/index.ts index 2278411de..e154fd3ac 100644 --- a/packages/widgets/src/firewall/index.ts +++ b/packages/widgets/src/firewall/index.ts @@ -7,6 +7,7 @@ import { optionsBuilder } from "../options"; export const { definition, componentLoader } = createWidgetDefinition("firewall", { icon: IconWall, + refetchInterval: 5, createOptions() { return optionsBuilder.from(() => ({})); }, diff --git a/packages/widgets/src/health-monitoring/index.ts b/packages/widgets/src/health-monitoring/index.ts index fe31514de..ff8eb87ab 100644 --- a/packages/widgets/src/health-monitoring/index.ts +++ b/packages/widgets/src/health-monitoring/index.ts @@ -8,6 +8,7 @@ import { createStorageVolumeMultiSelectOptions } from "../storage-volume-options export const { definition, componentLoader } = createWidgetDefinition("healthMonitoring", { icon: IconHeartRateMonitor, + refetchInterval: 5, createOptions() { return optionsBuilder.from( (factory) => ({ diff --git a/packages/widgets/src/immich/album-carousel/index.ts b/packages/widgets/src/immich/album-carousel/index.ts index 9f8110ffc..0ae11ef79 100644 --- a/packages/widgets/src/immich/album-carousel/index.ts +++ b/packages/widgets/src/immich/album-carousel/index.ts @@ -8,6 +8,8 @@ import { optionsBuilder } from "../../options"; export const { definition, componentLoader } = createWidgetDefinition("immich-albumCarousel", { icon: IconPhoto, + queryKey: [["widget", "immich"]], + refetchInterval: null, supportedIntegrations: ["immich"], integrationsRequired: true, createOptions() { diff --git a/packages/widgets/src/immich/server-stats/index.ts b/packages/widgets/src/immich/server-stats/index.ts index 61b07289a..ed4e05a20 100644 --- a/packages/widgets/src/immich/server-stats/index.ts +++ b/packages/widgets/src/immich/server-stats/index.ts @@ -5,6 +5,8 @@ import { optionsBuilder } from "../../options"; export const { definition, componentLoader } = createWidgetDefinition("immich-serverStats", { icon: IconGraphFilled, + queryKey: [["widget", "immich"]], + refetchInterval: null, supportedIntegrations: ["immich"], integrationsRequired: true, createOptions() { diff --git a/packages/widgets/src/indexer-manager/index.ts b/packages/widgets/src/indexer-manager/index.ts index c2160999f..e515cd697 100644 --- a/packages/widgets/src/indexer-manager/index.ts +++ b/packages/widgets/src/indexer-manager/index.ts @@ -7,6 +7,7 @@ import { optionsBuilder } from "../options"; export const { definition, componentLoader } = createWidgetDefinition("indexerManager", { icon: IconReportSearch, + refetchInterval: null, createOptions() { return optionsBuilder.from((factory) => ({ openIndexerSiteInNewTab: factory.switch({ diff --git a/packages/widgets/src/media-releases/index.ts b/packages/widgets/src/media-releases/index.ts index af650eef7..3e6cccbe5 100644 --- a/packages/widgets/src/media-releases/index.ts +++ b/packages/widgets/src/media-releases/index.ts @@ -5,6 +5,8 @@ import { optionsBuilder } from "../options"; export const { definition, componentLoader } = createWidgetDefinition("mediaReleases", { icon: IconTicket, + queryKey: [["widget", "mediaRelease"]], + refetchInterval: null, createOptions() { return optionsBuilder.from((factory) => ({ layout: factory.select({ diff --git a/packages/widgets/src/media-server/index.ts b/packages/widgets/src/media-server/index.ts index b85d09857..54f00476c 100644 --- a/packages/widgets/src/media-server/index.ts +++ b/packages/widgets/src/media-server/index.ts @@ -7,6 +7,7 @@ import { optionsBuilder } from "../options"; export const { componentLoader, definition } = createWidgetDefinition("mediaServer", { icon: IconVideo, + refetchInterval: 5, createOptions() { return optionsBuilder.from((factory) => ({ showOnlyPlaying: factory.switch({ defaultValue: true, withDescription: true }), diff --git a/packages/widgets/src/media-transcoding/index.ts b/packages/widgets/src/media-transcoding/index.ts index 2753925d8..ff4922dad 100644 --- a/packages/widgets/src/media-transcoding/index.ts +++ b/packages/widgets/src/media-transcoding/index.ts @@ -11,6 +11,7 @@ export const views = ["workers", "queue", "statistics"] as const; export const { componentLoader, definition } = createWidgetDefinition("mediaTranscoding", { icon: IconTransform, + refetchInterval: null, createOptions() { return optionsBuilder.from((factory) => ({ defaultView: factory.select({ diff --git a/packages/widgets/src/minecraft/server-status/index.ts b/packages/widgets/src/minecraft/server-status/index.ts index c5cd0e068..028393955 100644 --- a/packages/widgets/src/minecraft/server-status/index.ts +++ b/packages/widgets/src/minecraft/server-status/index.ts @@ -6,6 +6,8 @@ import { optionsBuilder } from "../../options"; export const { componentLoader, definition } = createWidgetDefinition("minecraftServerStatus", { icon: IconBrandMinecraft, + queryKey: [["widget", "minecraft"]], + refetchInterval: null, createOptions() { return optionsBuilder.from((factory) => ({ title: factory.text({ defaultValue: "" }), diff --git a/packages/widgets/src/network-controller/network-status/index.ts b/packages/widgets/src/network-controller/network-status/index.ts index 4af2fbaf0..3ab66da82 100644 --- a/packages/widgets/src/network-controller/network-status/index.ts +++ b/packages/widgets/src/network-controller/network-status/index.ts @@ -7,6 +7,8 @@ import { optionsBuilder } from "../../options"; export const { definition, componentLoader } = createWidgetDefinition("networkControllerStatus", { icon: IconTopologyFull, + queryKey: [["widget", "networkController"]], + refetchInterval: null, createOptions() { return optionsBuilder.from((factory) => ({ content: factory.select({ diff --git a/packages/widgets/src/network-controller/summary/index.ts b/packages/widgets/src/network-controller/summary/index.ts index dd541024a..cadc7b5f8 100644 --- a/packages/widgets/src/network-controller/summary/index.ts +++ b/packages/widgets/src/network-controller/summary/index.ts @@ -7,6 +7,8 @@ import { optionsBuilder } from "../../options"; export const { definition, componentLoader } = createWidgetDefinition("networkControllerSummary", { icon: IconTopologyFull, + queryKey: [["widget", "networkController"]], + refetchInterval: null, createOptions() { return optionsBuilder.from(() => ({})); }, diff --git a/packages/widgets/src/notifications/index.ts b/packages/widgets/src/notifications/index.ts index 553ef5d9f..17fd13393 100644 --- a/packages/widgets/src/notifications/index.ts +++ b/packages/widgets/src/notifications/index.ts @@ -7,6 +7,7 @@ import { optionsBuilder } from "../options"; export const { componentLoader, definition } = createWidgetDefinition("notifications", { icon: IconMessage, + refetchInterval: null, createOptions() { return optionsBuilder.from((factory) => ({ hideLogos: factory.switch({ defaultValue: false }), diff --git a/packages/widgets/src/paperless-ngx/index.ts b/packages/widgets/src/paperless-ngx/index.ts index 59af7ca59..ef6fd2e5a 100644 --- a/packages/widgets/src/paperless-ngx/index.ts +++ b/packages/widgets/src/paperless-ngx/index.ts @@ -5,6 +5,7 @@ import { optionsBuilder } from "../options"; export const { definition, componentLoader } = createWidgetDefinition("paperlessNgx", { icon: IconFileText, + refetchInterval: null, supportedIntegrations: ["paperlessNgx"], integrationsRequired: true, createOptions() { diff --git a/packages/widgets/src/releases/index.ts b/packages/widgets/src/releases/index.ts index 2c3d1d3a2..62fa7ebab 100644 --- a/packages/widgets/src/releases/index.ts +++ b/packages/widgets/src/releases/index.ts @@ -14,6 +14,7 @@ const relativeDateSchema = z export const { definition, componentLoader } = createWidgetDefinition("releases", { icon: IconRocket, + refetchInterval: null, createOptions() { return optionsBuilder.from((factory) => ({ newReleaseWithin: factory.text({ diff --git a/packages/widgets/src/rssFeed/index.ts b/packages/widgets/src/rssFeed/index.ts index 079c761a9..eee7028cb 100644 --- a/packages/widgets/src/rssFeed/index.ts +++ b/packages/widgets/src/rssFeed/index.ts @@ -12,6 +12,7 @@ import { optionsBuilder } from "../options"; */ export const { definition, componentLoader } = createWidgetDefinition("rssFeed", { icon: IconRss, + refetchInterval: null, createOptions() { return optionsBuilder.from((factory) => ({ feedUrls: factory.multiText({ diff --git a/packages/widgets/src/speedtest-tracker/index.ts b/packages/widgets/src/speedtest-tracker/index.ts index ea3c0c5d2..14cc5d118 100644 --- a/packages/widgets/src/speedtest-tracker/index.ts +++ b/packages/widgets/src/speedtest-tracker/index.ts @@ -5,6 +5,7 @@ import { optionsBuilder } from "../options"; export const { definition, componentLoader } = createWidgetDefinition("speedtestTracker", { icon: IconSpeedboat, + refetchInterval: null, createOptions() { return optionsBuilder.from( (factory) => ({ diff --git a/packages/widgets/src/stocks/index.ts b/packages/widgets/src/stocks/index.ts index fa64bf3a4..ba44f481e 100644 --- a/packages/widgets/src/stocks/index.ts +++ b/packages/widgets/src/stocks/index.ts @@ -13,6 +13,7 @@ const timeIntervalOptions = stockPriceTimeFrames.interval; export const { definition, componentLoader } = createWidgetDefinition("stockPrice", { icon: IconBuildingBank, + refetchInterval: null, createOptions() { return optionsBuilder.from((factory) => ({ stock: factory.text({ diff --git a/packages/widgets/src/system-disks/index.ts b/packages/widgets/src/system-disks/index.ts index 61e4a5c05..66ffcc286 100644 --- a/packages/widgets/src/system-disks/index.ts +++ b/packages/widgets/src/system-disks/index.ts @@ -6,6 +6,8 @@ import { createStorageVolumeMultiSelectOptions } from "../storage-volume-options export const { definition, componentLoader } = createWidgetDefinition("systemDisks", { icon: IconServer2, + queryKey: [["widget", "healthMonitoring"]], + refetchInterval: 5, supportedIntegrations: ["dashDot", "openmediavault", "truenas", "unraid", "synology"], createOptions() { return optionsBuilder.from( diff --git a/packages/widgets/src/system-resources/index.ts b/packages/widgets/src/system-resources/index.ts index 8d4837187..b35bab42f 100644 --- a/packages/widgets/src/system-resources/index.ts +++ b/packages/widgets/src/system-resources/index.ts @@ -14,6 +14,8 @@ const labelDisplayModeOptions = { export const { definition, componentLoader } = createWidgetDefinition("systemResources", { icon: IconGraphFilled, + queryKey: [["widget", "healthMonitoring"]], + refetchInterval: 5, supportedIntegrations: ["dashDot", "openmediavault", "truenas", "unraid", "glances", "synology"], createOptions() { return optionsBuilder.from((factory) => ({ diff --git a/packages/widgets/src/tracearr/index.ts b/packages/widgets/src/tracearr/index.ts index 44936cb67..9c34eb548 100644 --- a/packages/widgets/src/tracearr/index.ts +++ b/packages/widgets/src/tracearr/index.ts @@ -5,6 +5,7 @@ import { optionsBuilder } from "../options"; export const { definition, componentLoader } = createWidgetDefinition("tracearr", { icon: IconActivityHeartbeat, + refetchInterval: 5, createOptions() { return optionsBuilder.from((factory) => ({ showStreams: factory.switch({ diff --git a/packages/widgets/src/umami/index.ts b/packages/widgets/src/umami/index.ts index c55ecd261..badbef369 100644 --- a/packages/widgets/src/umami/index.ts +++ b/packages/widgets/src/umami/index.ts @@ -8,6 +8,7 @@ export const timeFrameValues = ["today", "24h", "7d", "30d", "month", "lastMonth export const { definition, componentLoader } = createWidgetDefinition("umami", { icon: IconChartBar, + refetchInterval: null, supportedIntegrations: ["umami"], createOptions() { return optionsBuilder.from( diff --git a/packages/widgets/src/vpn/index.ts b/packages/widgets/src/vpn/index.ts index 426366a66..b3cbe8c73 100644 --- a/packages/widgets/src/vpn/index.ts +++ b/packages/widgets/src/vpn/index.ts @@ -7,6 +7,7 @@ import { optionsBuilder } from "../options"; export const { componentLoader, definition } = createWidgetDefinition("vpn", { icon: IconShieldLock, + refetchInterval: null, createOptions() { return optionsBuilder.from(() => ({})); },