Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 44 additions & 117 deletions apps/desktop/src/session/components/outer-header/folder/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import {
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@hypr/ui/components/ui/breadcrumb";
import { Button } from "@hypr/ui/components/ui/button";

import { SearchableFolderDropdown } from "./searchable-dropdown";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@hypr/ui/components/ui/tooltip";

import { useBillingAccess } from "~/auth/billing";
import { FolderBreadcrumb } from "~/shared/ui/folder-breadcrumb";
import * as main from "~/store/tinybase/store/main";
import { useSessionTitle } from "~/store/zustand/live-title";
import { useTabs } from "~/store/zustand/tabs";

export function FolderChain({ sessionId }: { sessionId: string }) {
Expand All @@ -26,78 +27,57 @@ export function FolderChain({ sessionId }: { sessionId: string }) {
"folder_id",
main.STORE_ID,
);
const storeTitle = main.UI.useCell(
"sessions",
sessionId,
"title",
main.STORE_ID,
) as string | undefined;
const title = useSessionTitle(sessionId, storeTitle);

const handleChangeTitle = main.UI.useSetPartialRowCallback(
"sessions",
sessionId,
(title: string) => ({ title }),
[],
main.STORE_ID,
);

if (!isPro) {
return (
<Breadcrumb className="ml-1.5 w-full min-w-0">
<BreadcrumbList className="w-full flex-nowrap gap-0.5 overflow-hidden text-xs text-neutral-700">
<BreadcrumbItem className="min-w-0 flex-1 overflow-hidden">
<BreadcrumbPage className="block w-full min-w-0">
<TitleInput title={title} handleChangeTitle={handleChangeTitle} />
</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
);
if (!folderId) {
return <UnassignedFolderBreadcrumb />;
}

return (
<Breadcrumb className="ml-1.5 w-full min-w-0">
<BreadcrumbList className="w-full flex-nowrap gap-0.5 overflow-hidden text-xs text-neutral-700">
{folderId && <FolderIcon className="mr-1 h-3 w-3 shrink-0" />}
{!folderId ? (
<RenderIfRootNotExist
title={title}
handleChangeTitle={handleChangeTitle}
sessionId={sessionId}
/>
) : (
<RenderIfRootExist
title={title}
handleChangeTitle={handleChangeTitle}
folderId={folderId}
/>
)}
<BreadcrumbList className="w-full flex-nowrap gap-0.5 overflow-hidden font-mono text-xs text-neutral-700">
<FolderIcon className="mr-1 h-3 w-3 shrink-0" />
<RenderFolderBreadcrumb folderId={folderId} isPro={isPro} />
</BreadcrumbList>
</Breadcrumb>
);
}

function RenderIfRootExist({
function UnassignedFolderBreadcrumb() {
return (
<Breadcrumb className="ml-1.5 w-full min-w-0">
<BreadcrumbList className="w-full flex-nowrap gap-0.5 overflow-hidden font-mono text-xs text-neutral-700">
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<BreadcrumbItem className="flex items-center gap-1.5 text-neutral-400">
<FolderIcon className="h-3 w-3 shrink-0" />
<span className="cursor-default">Unassigned</span>
</BreadcrumbItem>
</TooltipTrigger>
<TooltipContent side="bottom">Coming soon</TooltipContent>
</Tooltip>
</BreadcrumbList>
</Breadcrumb>
);
}

function RenderFolderBreadcrumb({
folderId,
title,
handleChangeTitle,
isPro,
}: {
folderId: string;
title: string;
handleChangeTitle: (title: string) => void;
isPro: boolean;
}) {
const openNew = useTabs((state) => state.openNew);

return (
<>
<FolderBreadcrumb
folderId={folderId}
renderSeparator={({ index }) =>
index > 0 ? <BreadcrumbSeparator className="shrink-0" /> : null
}
renderCrumb={({ id, name }) => (
<BreadcrumbItem className="overflow-hidden">
<FolderBreadcrumb
folderId={folderId}
renderSeparator={({ index }) =>
index > 0 ? <BreadcrumbSeparator className="shrink-0" /> : null
}
renderCrumb={({ id, name }) => (
<BreadcrumbItem className="overflow-hidden">
{isPro ? (
<BreadcrumbLink asChild>
<Button
size="sm"
Expand All @@ -108,64 +88,11 @@ function RenderIfRootExist({
{name}
</Button>
</BreadcrumbLink>
</BreadcrumbItem>
)}
/>
<BreadcrumbSeparator className="shrink-0" />
<BreadcrumbItem className="min-w-0 flex-1 overflow-hidden">
<BreadcrumbPage className="block w-full min-w-0">
<TitleInput title={title} handleChangeTitle={handleChangeTitle} />
</BreadcrumbPage>
</BreadcrumbItem>
</>
);
}

function RenderIfRootNotExist({
title,
handleChangeTitle,
sessionId,
}: {
title: string;
handleChangeTitle: (title: string) => void;
sessionId: string;
}) {
return (
<>
<BreadcrumbItem className="shrink-0">
<SearchableFolderDropdown
sessionId={sessionId}
trigger={
<button className="text-neutral-500 outline-hidden transition-colors hover:text-neutral-700">
Select folder
</button>
}
/>
</BreadcrumbItem>
<BreadcrumbSeparator className="shrink-0" />
<BreadcrumbItem className="min-w-0 flex-1 overflow-hidden">
<BreadcrumbPage className="block w-full min-w-0">
<TitleInput title={title} handleChangeTitle={handleChangeTitle} />
</BreadcrumbPage>
</BreadcrumbItem>
</>
);
}

function TitleInput({
title,
handleChangeTitle,
}: {
title: string;
handleChangeTitle: (title: string) => void;
}) {
return (
<input
type="text"
placeholder="Untitled"
className="block w-full min-w-0 truncate border-none bg-transparent text-neutral-700 focus:underline focus:outline-hidden"
value={title ?? ""}
onChange={(e) => handleChangeTitle(e.target.value)}
) : (
<span className="truncate text-neutral-600">{name}</span>
)}
</BreadcrumbItem>
)}
/>
);
}
Loading