"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
);