feat: complete automatic mobile board UX

This commit is contained in:
ajnart committed 2026-08-14 11:32:09 +02:00
1 parent b217d8e026
commit e6bd1adfc2
50 files changed
+1279 -343

No files matched your search

@@ -4,14 +4,18 @@ import { shouldPrefetchWidgetForRequest } from "./_prefetch-policy";
describe("shouldPrefetchWidgetForRequest", () => {
test.each(["mobile", "tablet"])("defers downloads for %s requests", (deviceType) => {
expect(shouldPrefetchWidgetForRequest("downloads", deviceType)).toBe(false);
expect(shouldPrefetchWidgetForRequest("downloads", deviceType, true)).toBe(false);
});
test("keeps downloads prefetched for desktop requests", () => {
expect(shouldPrefetchWidgetForRequest("downloads", undefined)).toBe(true);
expect(shouldPrefetchWidgetForRequest("downloads", undefined, true)).toBe(true);
});
test("keeps downloads prefetched for legacy responsive mobile boards", () => {
expect(shouldPrefetchWidgetForRequest("downloads", "mobile", false)).toBe(true);
});
test.each(["app", "bookmarks"] as const)("keeps eager %s data prefetched on mobile", (kind) => {
expect(shouldPrefetchWidgetForRequest(kind, "mobile")).toBe(true);
expect(shouldPrefetchWidgetForRequest(kind, "mobile", true)).toBe(true);
});
});
@@ -1,7 +1,11 @@
import type { WidgetKind } from "@homarr/definitions";
export const shouldPrefetchWidgetForRequest = (kind: WidgetKind, deviceType: string | undefined) => {
export const shouldPrefetchWidgetForRequest = (
kind: WidgetKind,
deviceType: string | undefined,
enableAutomaticMobileLayout: boolean,
) => {
const isMobileDevice = deviceType === "mobile" || deviceType === "tablet";
return kind !== "downloads" || !isMobileDevice;
return kind !== "downloads" || !isMobileDevice || !enableAutomaticMobileLayout;
};
@@ -53,7 +53,7 @@ export default async function BoardSettingsPage(props: Props) {
}
return (
<Container size="xl">
<Container size="xl" px={{ base: "md", md: "xl" }}>
<Stack>
<Title>{t("title", { boardName: capitalize(board.name) })}</Title>
<BoardSettingsForm
@@ -48,13 +48,22 @@ export const BoardSwitcherLogo = () => {
<ScrollArea.Autosize mah={320}>
{boards.map((availableBoard) => {
const isCurrent = availableBoard.id === board.id;
if (isCurrent) {
return (
<Menu.Item key={availableBoard.id} leftSection={<IconCheck size={20} />} aria-current="page" disabled>
<Text truncate title={availableBoard.name}>
{availableBoard.name}
</Text>
</Menu.Item>
);
}
return (
<Menu.Item
key={availableBoard.id}
component={Link}
href={`/boards/${availableBoard.name}`}
leftSection={isCurrent ? <IconCheck size={20} /> : <IconLayoutBoard size={20} />}
aria-current={isCurrent ? "page" : undefined}
leftSection={<IconLayoutBoard size={20} />}
>
<Text truncate title={availableBoard.name}>
{availableBoard.name}
+1
View File
@@ -129,6 +129,7 @@ export default async function Layout(props: {
board: {
homeBoardId: serverSettings.board.homeBoardId,
mobileHomeBoardId: serverSettings.board.mobileHomeBoardId,
enableAutomaticMobileLayout: serverSettings.board.enableAutomaticMobileLayout,
enableStatusByDefault: serverSettings.board.enableStatusByDefault,
forceDisableStatus: serverSettings.board.forceDisableStatus,
},