"use client"; import type { PropsWithChildren } from "react"; import { Fragment, Suspense } from "react"; import { Box, Flex, Stack, Text, Tooltip, UnstyledButton } from "@mantine/core"; import { IconMinus } from "@tabler/icons-react"; import combineClasses from "clsx"; import { clientApi } from "@homarr/api/client"; import { useRequiredBoard } from "@homarr/boards/context"; import { useSettings } from "@homarr/settings"; import { useRegisterSpotlightContextResults } from "@homarr/spotlight"; import { useI18n } from "@homarr/translation/client"; import { MaskedOrNormalImage } from "@homarr/ui"; import { WidgetEmptyState } from "../common/empty-state"; import { getSafeAppHref, SAFE_NEW_TAB_REL } from "../common/application-url"; import { getUsableWidgetQueryData, isInitialWidgetQueryPending } from "../common/query-state"; import { WidgetQueryErrorIndicator, WidgetQueryLoadingState } from "../common/query-state-indicator"; 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, isEditMode, height, width }: WidgetComponentProps<"app">) { const t = useI18n(); const settings = useSettings(); const board = useRequiredBoard(); const appQuery = clientApi.app.byId.useQuery({ id: options.appId }, { enabled: Boolean(options.appId) }); const app = getUsableWidgetQueryData(appQuery); const href = getSafeAppHref(app?.href); useRegisterSpotlightContextResults( `app-${app?.id ?? options.appId}`, app && href ? [ { id: app.id, name: app.name, icon: app.iconUrl, interaction() { return { type: "link", href, newTab: options.openInNewTab, }; }, }, ] : [], [app, href, options.openInNewTab], ); if (!options.appId) return ; if (isInitialWidgetQueryPending(appQuery)) return ; if (!app) return ; const isTiny = height < 100 || width < 100; const isColumnLayout = options.layout.startsWith("column"); return ( e.preventDefault() : undefined} > {options.showTitle && ( {app.name} )} {options.descriptionDisplayMode === "normal" && !isTiny && ( {app.description?.split("\n").map((line, index) => ( {line}
))}
)}
{options.pingEnabled && !settings.forceDisableStatus && !board.disableStatus && Boolean(app.pingUrl ?? app.href) ? ( }> ) : null}
{appQuery.error && ( )}
); } interface AppLinkProps { href: string | undefined; openInNewTab: boolean; enabled: boolean; } const AppDescriptionTooltip = ({ description, enabled, children, }: PropsWithChildren<{ description?: string | null; enabled: boolean }>) => enabled ? ( ( {line}
))} position="right-start" multiline styles={{ tooltip: { maxWidth: 300 } }} > {children}
) : ( children ); const AppLink = ({ href, openInNewTab, enabled, children }: PropsWithChildren) => enabled ? ( {children} ) : ( children );