import { workspacesAtom } from "@yaakapp-internal/models"; import classNames from "classnames"; import { useAtomValue } from "jotai"; import * as m from "motion/react-m"; import type { CSSProperties } from "react"; import { useCallback, useMemo, useRef, useState } from "react"; import { useEnsureActiveCookieJar, useSubscribeActiveCookieJarId, } from "../hooks/useActiveCookieJar"; import { activeEnvironmentAtom, useSubscribeActiveEnvironmentId, } from "../hooks/useActiveEnvironment"; import { activeFolderAtom } from "../hooks/useActiveFolder"; import { useSubscribeActiveFolderId } from "../hooks/useActiveFolderId"; import { activeRequestAtom } from "../hooks/useActiveRequest"; import { useSubscribeActiveRequestId } from "../hooks/useActiveRequestId"; import { activeWorkspaceAtom } from "../hooks/useActiveWorkspace"; import { useFloatingSidebarHidden } from "../hooks/useFloatingSidebarHidden"; import { useHotKey } from "../hooks/useHotKey"; import { useSubscribeRecentCookieJars } from "../hooks/useRecentCookieJars"; import { useSubscribeRecentEnvironments } from "../hooks/useRecentEnvironments"; import { useSubscribeRecentRequests } from "../hooks/useRecentRequests"; import { useSubscribeRecentWorkspaces } from "../hooks/useRecentWorkspaces"; import { useShouldFloatSidebar } from "../hooks/useShouldFloatSidebar"; import { useSidebarHidden } from "../hooks/useSidebarHidden"; import { useSidebarWidth } from "../hooks/useSidebarWidth"; import { useSyncWorkspaceRequestTitle } from "../hooks/useSyncWorkspaceRequestTitle"; import { duplicateRequestOrFolderAndNavigate } from "../lib/duplicateRequestOrFolderAndNavigate"; import { importData } from "../lib/importData"; import { jotaiStore } from "../lib/jotai"; import { CreateDropdown } from "./CreateDropdown"; import { Banner } from "./core/Banner"; import { Button } from "./core/Button"; import { HotkeyList } from "./core/HotkeyList"; import { FeedbackLink } from "./core/Link"; import { HStack } from "./core/Stacks"; import { ErrorBoundary } from "./ErrorBoundary"; import { FolderLayout } from "./FolderLayout"; import { GrpcConnectionLayout } from "./GrpcConnectionLayout"; import { HeaderSize } from "./HeaderSize"; import { HttpRequestLayout } from "./HttpRequestLayout"; import { Overlay } from "./Overlay"; import type { ResizeHandleEvent } from "./ResizeHandle"; import { ResizeHandle } from "./ResizeHandle"; import Sidebar from "./Sidebar"; import { SidebarActions } from "./SidebarActions"; import { WebsocketRequestLayout } from "./WebsocketRequestLayout"; import { WorkspaceHeader } from "./WorkspaceHeader"; const side = { gridArea: "side" }; const head = { gridArea: "head" }; const body = { gridArea: "body" }; const drag = { gridArea: "drag" }; export function Workspace() { // First, subscribe to some things applicable to workspaces useGlobalWorkspaceHooks(); const workspaces = useAtomValue(workspacesAtom); const [width, setWidth, resetWidth] = useSidebarWidth(); const [sidebarHidden, setSidebarHidden] = useSidebarHidden(); const [floatingSidebarHidden, setFloatingSidebarHidden] = useFloatingSidebarHidden(); const activeEnvironment = useAtomValue(activeEnvironmentAtom); const floating = useShouldFloatSidebar(); const [isResizing, setIsResizing] = useState(false); const startWidth = useRef(null); const handleResizeMove = useCallback( async ({ x, xStart }: ResizeHandleEvent) => { if (width == null || startWidth.current == null) return; const newWidth = startWidth.current + (x - xStart); if (newWidth < 50) { if (!sidebarHidden) await setSidebarHidden(true); resetWidth(); } else { if (sidebarHidden) await setSidebarHidden(false); setWidth(newWidth); } }, [width, sidebarHidden, setSidebarHidden, resetWidth, setWidth], ); const handleResizeStart = useCallback(() => { startWidth.current = width ?? null; setIsResizing(true); }, [width]); const handleResizeEnd = useCallback(() => { setIsResizing(false); startWidth.current = null; }, []); const sideWidth = sidebarHidden ? 0 : width; const styles = useMemo( () => ({ gridTemplate: floating ? ` ' ${head.gridArea}' auto ' ${body.gridArea}' minmax(0,1fr) / 1fr` : ` ' ${head.gridArea} ${head.gridArea} ${head.gridArea}' auto ' ${side.gridArea} ${drag.gridArea} ${body.gridArea}' minmax(0,1fr) / ${sideWidth}px 0 1fr`, }), [sideWidth, floating], ); const environmentBgStyle = useMemo(() => { if (activeEnvironment?.color == null) return undefined; const background = `linear-gradient(to right, ${activeEnvironment.color} 15%, transparent 40%)`; return { background }; }, [activeEnvironment?.color]); // We're loading still if (workspaces.length === 0) { return null; } return (
{floating ? ( setFloatingSidebarHidden(true)} zIndex={20} > ) : ( <>
)}
); } function WorkspaceBody() { const activeRequest = useAtomValue(activeRequestAtom); const activeFolder = useAtomValue(activeFolderAtom); const activeWorkspace = useAtomValue(activeWorkspaceAtom); if (activeWorkspace == null) { return ( The active workspace was not found. Select a workspace from the header menu or report this bug to ); } if (activeRequest?.model === "grpc_request") { return ; } if (activeRequest?.model === "websocket_request") { return ; } if (activeRequest?.model === "http_request") { return ; } if (activeFolder != null) { return ; } return ( } /> ); } function useGlobalWorkspaceHooks() { useEnsureActiveCookieJar(); useSubscribeActiveRequestId(); useSubscribeActiveFolderId(); useSubscribeActiveEnvironmentId(); useSubscribeActiveCookieJarId(); useSubscribeRecentRequests(); useSubscribeRecentWorkspaces(); useSubscribeRecentEnvironments(); useSubscribeRecentCookieJars(); useSyncWorkspaceRequestTitle(); useHotKey("model.duplicate", () => duplicateRequestOrFolderAndNavigate(jotaiStore.get(activeRequestAtom)), ); }