refactor: use suspense query instead of serverdata for app widget (#1143)
* refactor: use suspense query instead of serverdata for app widget * chore: add missing translation for loading tooltip
This commit is contained in:
@@ -1,58 +1,37 @@
|
||||
"use client";
|
||||
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { useState } from "react";
|
||||
import { Box, Center, Flex, Loader, Stack, Text, Tooltip, UnstyledButton } from "@mantine/core";
|
||||
import { IconDeviceDesktopX } from "@tabler/icons-react";
|
||||
import { Suspense } from "react";
|
||||
import { Flex, Text, Tooltip, UnstyledButton } from "@mantine/core";
|
||||
import combineClasses from "clsx";
|
||||
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { parseAppHrefWithVariablesClient } from "@homarr/common/client";
|
||||
import { useRegisterSpotlightActions } from "@homarr/spotlight";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { WidgetComponentProps } from "../definition";
|
||||
import classes from "./app.module.css";
|
||||
import { PingDot } from "./ping/ping-dot";
|
||||
import { PingIndicator } from "./ping/ping-indicator";
|
||||
|
||||
export default function AppWidget({ options, serverData, isEditMode, width }: WidgetComponentProps<"app">) {
|
||||
const t = useScopedI18n("widget.app");
|
||||
const isQueryEnabled = Boolean(options.appId);
|
||||
const {
|
||||
data: app,
|
||||
isPending,
|
||||
isError,
|
||||
} = clientApi.app.byId.useQuery(
|
||||
export default function AppWidget({ options, isEditMode }: WidgetComponentProps<"app">) {
|
||||
const t = useI18n();
|
||||
const [app] = clientApi.app.byId.useSuspenseQuery(
|
||||
{
|
||||
id: options.appId,
|
||||
},
|
||||
{
|
||||
initialData:
|
||||
// We need to check if the id's match because otherwise the same initialData for a changed id will be used
|
||||
serverData?.app?.id === options.appId ? serverData.app : undefined,
|
||||
refetchOnMount: false,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
enabled: isQueryEnabled,
|
||||
},
|
||||
);
|
||||
|
||||
const [pingResult, setPingResult] = useState<RouterOutputs["widget"]["app"]["ping"] | null>(null);
|
||||
|
||||
const shouldRunPing = Boolean(app?.href) && options.pingEnabled;
|
||||
clientApi.widget.app.updatedPing.useSubscription(
|
||||
{ url: parseAppHrefWithVariablesClient(app?.href ?? "") },
|
||||
{
|
||||
enabled: shouldRunPing,
|
||||
onData(data) {
|
||||
setPingResult(data);
|
||||
},
|
||||
retry: false,
|
||||
},
|
||||
);
|
||||
|
||||
useRegisterSpotlightActions(
|
||||
`app-${options.appId}`,
|
||||
app?.href
|
||||
app.href
|
||||
? [
|
||||
{
|
||||
id: `app-${options.appId}`,
|
||||
@@ -69,44 +48,21 @@ export default function AppWidget({ options, serverData, isEditMode, width }: Wi
|
||||
[app, options.appId, options.openInNewTab],
|
||||
);
|
||||
|
||||
if (isPending && isQueryEnabled) {
|
||||
return (
|
||||
<Center h="100%">
|
||||
<Loader />
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError || !isQueryEnabled) {
|
||||
return (
|
||||
<Tooltip.Floating label={t("error.notFound.tooltip")}>
|
||||
<Stack gap="xs" align="center" justify="center" h="100%" w="100%">
|
||||
<IconDeviceDesktopX size={width >= 96 ? "2rem" : "1rem"} />
|
||||
{width >= 96 && (
|
||||
<Text ta="center" size="sm">
|
||||
{t("error.notFound.label")}
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
</Tooltip.Floating>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AppLink
|
||||
href={parseAppHrefWithVariablesClient(app?.href ?? "")}
|
||||
href={parseAppHrefWithVariablesClient(app.href ?? "")}
|
||||
openInNewTab={options.openInNewTab}
|
||||
enabled={Boolean(app?.href) && !isEditMode}
|
||||
enabled={Boolean(app.href) && !isEditMode}
|
||||
>
|
||||
<Tooltip.Floating
|
||||
label={app?.description}
|
||||
label={app.description}
|
||||
position="right-start"
|
||||
multiline
|
||||
disabled={!options.showDescriptionTooltip || !app?.description}
|
||||
disabled={!options.showDescriptionTooltip || !app.description}
|
||||
styles={{ tooltip: { maxWidth: 300 } }}
|
||||
>
|
||||
<Flex
|
||||
className={combineClasses("app-flex-wrapper", app?.name, app?.id)}
|
||||
className={combineClasses("app-flex-wrapper", app.name, app.id)}
|
||||
h="100%"
|
||||
w="100%"
|
||||
direction="column"
|
||||
@@ -115,14 +71,22 @@ export default function AppWidget({ options, serverData, isEditMode, width }: Wi
|
||||
align="center"
|
||||
>
|
||||
{options.showTitle && (
|
||||
<Text className="app-title" fw={700} size="12.5cqmin">
|
||||
{app?.name}
|
||||
<Text className="app-title" fw={700} ta="center" size="12.5cqmin">
|
||||
{app.name}
|
||||
</Text>
|
||||
)}
|
||||
<img src={app?.iconUrl} alt={app?.name} className={combineClasses(classes.appIcon, "app-icon")} />
|
||||
<img src={app.iconUrl} alt={app.name} className={combineClasses(classes.appIcon, "app-icon")} />
|
||||
</Flex>
|
||||
</Tooltip.Floating>
|
||||
{shouldRunPing && <PingIndicator pingResult={pingResult} />}
|
||||
{options.pingEnabled && app.href ? (
|
||||
<Suspense
|
||||
fallback={
|
||||
<PingDot color="blue" tooltip={t("common.rtl", { symbol: "…", value: t("common.action.loading") })} />
|
||||
}
|
||||
>
|
||||
<PingIndicator href={app.href} />
|
||||
</Suspense>
|
||||
) : null}
|
||||
</AppLink>
|
||||
);
|
||||
}
|
||||
@@ -141,31 +105,3 @@ const AppLink = ({ href, openInNewTab, enabled, children }: PropsWithChildren<Ap
|
||||
) : (
|
||||
children
|
||||
);
|
||||
|
||||
interface PingIndicatorProps {
|
||||
pingResult: RouterOutputs["widget"]["app"]["ping"] | null;
|
||||
}
|
||||
|
||||
const PingIndicator = ({ pingResult }: PingIndicatorProps) => {
|
||||
return (
|
||||
<Box bottom={4} right={4} pos="absolute">
|
||||
<Tooltip
|
||||
label={pingResult && "statusCode" in pingResult ? pingResult.statusCode : pingResult?.error}
|
||||
disabled={!pingResult}
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
borderRadius: "100%",
|
||||
backgroundColor: !pingResult
|
||||
? "orange"
|
||||
: "error" in pingResult || pingResult.statusCode >= 500
|
||||
? "red"
|
||||
: "green",
|
||||
}}
|
||||
w={16}
|
||||
h={16}
|
||||
></Box>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user