mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-08-25 21:04:04 +02:00
Open Settings in a dialog on the web build (#581)
This commit is contained in:
@@ -1,33 +1,42 @@
|
|||||||
import { platform } from "@yaakapp-internal/platform";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import type { SettingsTab } from "../components/Settings/Settings";
|
import type { SettingsTab, SettingsTabWithSubtab } from "../components/Settings/Settings";
|
||||||
import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace";
|
import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace";
|
||||||
import { createFastMutation } from "../hooks/useFastMutation";
|
import { createFastMutation } from "../hooks/useFastMutation";
|
||||||
|
import { showDialog } from "../lib/dialog";
|
||||||
import { jotaiStore } from "../lib/jotai";
|
import { jotaiStore } from "../lib/jotai";
|
||||||
import { router } from "../lib/router";
|
import { router } from "../lib/router";
|
||||||
import { rpc } from "../lib/rpc";
|
import { rpc } from "../lib/rpc";
|
||||||
|
|
||||||
// Allow tab with optional subtab (e.g., "plugins:installed")
|
export const openSettings = createFastMutation<void, string, SettingsTabWithSubtab | null>({
|
||||||
type SettingsTabWithSubtab = SettingsTab | `${SettingsTab}:${string}` | null;
|
|
||||||
|
|
||||||
export const openSettings = createFastMutation<void, string, SettingsTabWithSubtab>({
|
|
||||||
mutationKey: ["open_settings"],
|
mutationKey: ["open_settings"],
|
||||||
mutationFn: async (tab) => {
|
mutationFn: async (tab) => {
|
||||||
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||||
if (workspaceId == null) return;
|
if (workspaceId == null) return;
|
||||||
|
|
||||||
const to = "/workspaces/$workspaceId/settings" as const;
|
|
||||||
const params = { workspaceId };
|
|
||||||
const search = { tab: (tab ?? undefined) as SettingsTab | undefined };
|
|
||||||
|
|
||||||
// Settings is its own window where the host has windows to give. Where it
|
// Settings is its own window where the host has windows to give. Where it
|
||||||
// doesn't — a browser tab — the same route opens in place, which is the
|
// doesn't — a browser tab — it's a dialog like any other, so opening it
|
||||||
// whole difference: it is already a route, not a separate app.
|
// doesn't take you away from the request you were working on.
|
||||||
if (!platform.capabilities.multiWindow) {
|
if (!platform.capabilities.multiWindow) {
|
||||||
await router.navigate({ to, params, search });
|
// Imported here so Settings stays out of the startup bundle, the way the
|
||||||
|
// route that renders it on desktop already keeps it
|
||||||
|
const { default: Settings } = await import("../components/Settings/Settings");
|
||||||
|
showDialog({
|
||||||
|
id: "settings",
|
||||||
|
size: "md",
|
||||||
|
className: "h-[calc(100vh-5rem)] max-h-150! overflow-hidden",
|
||||||
|
noPadding: true,
|
||||||
|
noScroll: true,
|
||||||
|
// Keyed so opening a specific tab while the dialog is already up moves to it
|
||||||
|
render: ({ hide }) => <Settings key={tab ?? "general"} tab={tab} hide={hide} />,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const location = router.buildLocation({ to, params, search });
|
const location = router.buildLocation({
|
||||||
|
to: "/workspaces/$workspaceId/settings",
|
||||||
|
params: { workspaceId },
|
||||||
|
search: { tab: (tab ?? undefined) as SettingsTab | undefined },
|
||||||
|
});
|
||||||
|
|
||||||
await rpc("cmd_new_child_window", {
|
await rpc("cmd_new_child_window", {
|
||||||
url: location.href,
|
url: location.href,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { useSearch } from "@tanstack/react-router";
|
|
||||||
import { platform } from "@yaakapp-internal/platform";
|
import { platform } from "@yaakapp-internal/platform";
|
||||||
import { useLicense } from "@yaakapp-internal/license";
|
import { useLicense } from "@yaakapp-internal/license";
|
||||||
import { pluginsAtom, settingsAtom } from "@yaakapp-internal/models";
|
import { pluginsAtom, settingsAtom } from "@yaakapp-internal/models";
|
||||||
@@ -20,6 +19,8 @@ import { SettingsProxy } from "./SettingsProxy";
|
|||||||
import { SettingsTheme } from "./SettingsTheme";
|
import { SettingsTheme } from "./SettingsTheme";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
tab?: SettingsTabWithSubtab | null;
|
||||||
|
/** Set when Settings is in a dialog rather than owning a window. */
|
||||||
hide?: () => void;
|
hide?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,25 +43,19 @@ const tabs = [
|
|||||||
TAB_LICENSE,
|
TAB_LICENSE,
|
||||||
] as const;
|
] as const;
|
||||||
export type SettingsTab = (typeof tabs)[number];
|
export type SettingsTab = (typeof tabs)[number];
|
||||||
|
export type SettingsTabWithSubtab = SettingsTab | `${SettingsTab}:${string}`;
|
||||||
|
|
||||||
export default function Settings({ hide }: Props) {
|
export default function Settings({ tab, hide }: Props) {
|
||||||
const { tab: tabFromQuery } = useSearch({ from: "/workspaces/$workspaceId/settings" });
|
|
||||||
// Parse tab and subtab (e.g., "plugins:installed")
|
// Parse tab and subtab (e.g., "plugins:installed")
|
||||||
const [mainTab, subtab] = tabFromQuery?.split(":") ?? [];
|
const [mainTab, subtab] = tab?.split(":") ?? [];
|
||||||
const settings = useAtomValue(settingsAtom);
|
const settings = useAtomValue(settingsAtom);
|
||||||
const plugins = useAtomValue(pluginsAtom);
|
const plugins = useAtomValue(pluginsAtom);
|
||||||
const licenseCheck = useLicense();
|
const licenseCheck = useLicense();
|
||||||
|
|
||||||
// Close settings window on escape
|
// Close settings window on escape. In a dialog, the dialog handles Escape itself.
|
||||||
// TODO: Could this be put in a better place? Eg. in Rust key listener when creating the window
|
// TODO: Could this be put in a better place? Eg. in Rust key listener when creating the window
|
||||||
useKeyPressEvent("Escape", async () => {
|
useKeyPressEvent("Escape", async () => {
|
||||||
if (hide != null) {
|
if (hide == null) await platform.window.close();
|
||||||
// It's being shown in a dialog, so close the dialog
|
|
||||||
hide();
|
|
||||||
} else {
|
|
||||||
// It's being shown in a window, so close the window
|
|
||||||
await platform.window.close();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -90,7 +85,7 @@ export default function Settings({ hide }: Props) {
|
|||||||
)}
|
)}
|
||||||
<Tabs
|
<Tabs
|
||||||
layout="horizontal"
|
layout="horizontal"
|
||||||
defaultValue={mainTab || tabFromQuery}
|
defaultValue={mainTab}
|
||||||
addBorders
|
addBorders
|
||||||
tabListClassName="min-w-40 bg-surface x-theme-sidebar border-r border-border pl-3"
|
tabListClassName="min-w-40 bg-surface x-theme-sidebar border-r border-border pl-3"
|
||||||
label="Settings"
|
label="Settings"
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ export const Route = createFileRoute("/workspaces/$workspaceId/settings")({
|
|||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
return <Settings />;
|
const { tab } = Route.useSearch();
|
||||||
|
return <Settings tab={tab} />;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user