chore: reconcile grid layer after stack rebase

This commit is contained in:
ajnart committed 2026-08-14 11:07:07 +02:00
1 parent 16982aa178
commit 6bfa9c33d0
9 files changed
+54 -166

No files matched your search

@@ -1,67 +0,0 @@
"use client";
import { Suspense, use } from "react";
import { Center, Loader } from "@mantine/core";
import { ErrorBoundary } from "react-error-boundary";
import { createModal, modalSizeForm } from "@homarr/modals";
import { WidgetError } from "@homarr/widgets/errors";
import type { ItemMoveModalProps } from "./item-move-modal";
let itemMoveModalPromise: Promise<typeof import("./item-move-modal")> | undefined;
const loadItemMoveModal = () => {
if (itemMoveModalPromise) return itemMoveModalPromise;
const currentPromise = import("./item-move-modal");
itemMoveModalPromise = currentPromise;
void currentPromise.catch(() => {
if (itemMoveModalPromise === currentPromise) itemMoveModalPromise = undefined;
});
return currentPromise;
};
export const preloadItemMoveModal = () => void loadItemMoveModal().catch(() => undefined);
interface LazyItemMoveModalContentProps {
actions: { closeModal: () => void };
innerProps: ItemMoveModalProps;
}
const LazyItemMoveModalContent = (props: LazyItemMoveModalContentProps) => {
const { ItemMoveModal } = use(loadItemMoveModal());
const Component = ItemMoveModal.component;
return <Component {...props} />;
};
export const LazyItemMoveModal = createModal<ItemMoveModalProps>((props) => (
<ErrorBoundary
fallbackRender={({ error, resetErrorBoundary }) => (
<Center py="xl">
<WidgetError
error={error}
resetErrorBoundary={() => {
itemMoveModalPromise = undefined;
resetErrorBoundary();
}}
/>
</Center>
)}
>
<Suspense
fallback={
<Center py="xl">
<Loader size="sm" />
</Center>
}
>
<LazyItemMoveModalContent {...props} />
</Suspense>
</ErrorBoundary>
)).withOptions({
defaultTitle(t) {
return t("item.moveResize.title");
},
size: modalSizeForm,
});
@@ -5,8 +5,10 @@ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, use
import { createPortal } from "react-dom";
import type { RouterOutputs } from "@homarr/api";
import { useIntegrations } from "@homarr/auth/client";
import { clientApi } from "@homarr/api/client";
import { useSession } from "@homarr/auth/client";
import { useCurrentLayout, useRequiredBoard } from "@homarr/boards/context";
import { useEditMode } from "@homarr/boards/edit-mode";
import { getRootSectionLane } from "@homarr/definitions";
import type { ContainerSectionItem, Section } from "~/app/[locale]/boards/_types";
@@ -33,7 +35,11 @@ const GridPortalHostContext = createContext<GridPortalHostContextValue | null>(n
*/
export const BoardGridPortalHost = ({ children }: PropsWithChildren) => {
const board = useRequiredBoard();
const integrations = useIntegrations();
const { data: session } = useSession();
const [isEditMode] = useEditMode();
const { data: integrations } = clientApi.integration.all.useQuery(undefined, {
enabled: Boolean(session) && isEditMode,
});
const containersRef = useRef<Map<string, HTMLElement>>(new Map());
const [containers, setContainers] = useState<Map<string, HTMLElement>>(() => new Map());
const [announcement, setAnnouncement] = useState({ id: 0, message: "" });
@@ -12,8 +12,18 @@ const mocks = vi.hoisted(() => ({
},
}));
vi.mock("@homarr/api/client", () => ({
clientApi: {
integration: {
all: {
useQuery: () => ({ data: undefined }),
},
},
},
}));
vi.mock("@homarr/auth/client", () => ({
useIntegrations: () => undefined,
useSession: () => ({ data: { user: { id: "user" } } }),
}));
vi.mock("@homarr/boards/context", () => ({
@@ -21,6 +31,10 @@ vi.mock("@homarr/boards/context", () => ({
useRequiredBoard: () => mocks.board,
}));
vi.mock("@homarr/boards/edit-mode", () => ({
useEditMode: () => [true],
}));
describe("board grid portal host", () => {
let container: HTMLDivElement;
let root: Root;
@@ -1,13 +1,14 @@
import { useCallback, useMemo } from "react";
import { use, useCallback, useMemo } from "react";
import { clientApi } from "@homarr/api/client";
import { useCurrentLayout, useRequiredBoard } from "@homarr/boards/context";
import { useConfirmModal } from "@homarr/modals";
import { useSettings } from "@homarr/settings";
import { useI18n } from "@homarr/translation/client";
import type { WidgetComponentProps } from "@homarr/widgets/definition";
import { loadWidgetDefinition, reduceWidgetOptionsWithDefinition } from "@homarr/widgets/manifest";
import type { Item } from "~/app/[locale]/boards/_types";
import { filterByItemKind } from "./filter-by-item-kind";
interface SectionItemLayout {
layoutId: string;
@@ -79,10 +80,27 @@ export const useOpenSectionApps = (sectionId: string, enabled: boolean) => {
const settings = useSettings();
const { openConfirmModal } = useConfirmModal();
const t = useI18n();
const appDefinition = use(loadWidgetDefinition("app"));
const appIds = useMemo(() => {
const items: Item[] = getSectionItemsForLayout(board, sectionId, currentLayoutId);
return Array.from(new Set(filterByItemKind(items, settings, "app").map((item) => item.options.appId)));
}, [board, currentLayoutId, sectionId, settings]);
return Array.from(
new Set(
items
.filter((item) => item.kind === "app")
.map(
(item) =>
(
reduceWidgetOptionsWithDefinition(
appDefinition,
settings,
item.options,
) as WidgetComponentProps<"app">["options"]
).appId,
)
.filter((appId): appId is string => typeof appId === "string"),
),
);
}, [appDefinition, board, currentLayoutId, sectionId, settings]);
const { data: apps = [], isLoading } = clientApi.app.byIds.useQuery(appIds, {
enabled: enabled && appIds.length > 0,
staleTime: 30_000,