chore: save snapshot before tag 0.3
This commit is contained in:
@@ -42,6 +42,8 @@ import { PrivateTree } from "@/components/sidebar/private-tree";
|
||||
import { buildSidebarSectionsFromTree, flattenDocumentTree } from "@/lib/sidebar-tree";
|
||||
import { supabaseBrowser } from "@/lib/supabase/client";
|
||||
import { useSearchPaletteStore } from "@/store/search-palette";
|
||||
import { FileTree } from "@/components/sidebar/file-tree";
|
||||
import type { MediaAsset } from "@/types/media";
|
||||
|
||||
const TOP_BUTTONS = [
|
||||
{ id: "search", icon: SearchIcon, label: "搜索" },
|
||||
@@ -74,7 +76,9 @@ interface ContextMenuState {
|
||||
export function Sidebar({ initialData }: SidebarProps) {
|
||||
const { open, setOpen, width, setWidth, collapsedSections, toggleSection, trashConfirm, setTrashConfirm } =
|
||||
useSidebarStore();
|
||||
const openSearchPalette = useSearchPaletteStore((state) => state.openSearch);
|
||||
const viewMode = useSidebarStore((state) => state.viewMode);
|
||||
const setViewMode = useSidebarStore((state) => state.setViewMode);
|
||||
const openSearchPalette = useSearchPaletteStore((state) => state.openSearch);
|
||||
const sidebarQuery = useSidebarData(initialData);
|
||||
const sidebarData = sidebarQuery.data ?? initialData;
|
||||
const segments = useSelectedLayoutSegments();
|
||||
@@ -148,6 +152,18 @@ export function Sidebar({ initialData }: SidebarProps) {
|
||||
);
|
||||
}, [sidebarData.trashedDocuments, trashSearch]);
|
||||
|
||||
const assetsByDoc = useMemo(() => {
|
||||
const map: Record<string, MediaAsset[]> = {};
|
||||
const assets = sidebarData.mediaAssets ?? [];
|
||||
assets.forEach((asset) => {
|
||||
if (!map[asset.document_id]) {
|
||||
map[asset.document_id] = [];
|
||||
}
|
||||
map[asset.document_id].push(asset);
|
||||
});
|
||||
return map;
|
||||
}, [sidebarData.mediaAssets]);
|
||||
|
||||
const activeWorkspace =
|
||||
sidebarData.workspaces.find((workspace) => workspace.id === sidebarData.activeWorkspaceId) ??
|
||||
sidebarData.workspaces[0];
|
||||
@@ -231,6 +247,17 @@ export function Sidebar({ initialData }: SidebarProps) {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleOpenAsset = useCallback((asset: MediaAsset) => {
|
||||
const url = asset.signed_url ?? asset.file_url;
|
||||
if (!url) {
|
||||
window.alert("暂无可用的文件链接");
|
||||
return;
|
||||
}
|
||||
if (typeof window !== "undefined") {
|
||||
window.open(url, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleResizeStart = useCallback(
|
||||
(event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
@@ -577,74 +604,120 @@ export function Sidebar({ initialData }: SidebarProps) {
|
||||
<Library className="h-4 w-4" />
|
||||
页面树
|
||||
</div>
|
||||
<Input
|
||||
className="mt-2 h-8 rounded-md border-[#eeeeee]"
|
||||
placeholder="搜索页面"
|
||||
value={filter}
|
||||
onChange={(event) => setFilter(event.target.value)}
|
||||
/>
|
||||
<div className="mt-2 flex items-center gap-2">
|
||||
<Input
|
||||
className="h-8 flex-1 rounded-md border-[#eeeeee]"
|
||||
placeholder="搜索页面"
|
||||
value={filter}
|
||||
onChange={(event) => setFilter(event.target.value)}
|
||||
/>
|
||||
<div className="flex rounded-md border border-[#e5e7eb] bg-white">
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"px-3 py-1 text-xs",
|
||||
viewMode === "section" ? "bg-[#2563eb] text-white" : "text-gray-600",
|
||||
)}
|
||||
onClick={() => setViewMode("section")}
|
||||
>
|
||||
分组
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"px-3 py-1 text-xs",
|
||||
viewMode === "filesystem" ? "bg-[#2563eb] text-white" : "text-gray-600",
|
||||
)}
|
||||
onClick={() => setViewMode("filesystem")}
|
||||
>
|
||||
文件
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SectionList
|
||||
id="starred"
|
||||
label="星标置顶"
|
||||
icon={SECTION_ICONS.starred}
|
||||
nodes={starredNodes}
|
||||
collapsed={collapsedSections.starred}
|
||||
onToggle={() => toggleSection("starred")}
|
||||
/>
|
||||
<SectionList
|
||||
id="public"
|
||||
label="公共页面"
|
||||
icon={SECTION_ICONS.public}
|
||||
nodes={publicNodes}
|
||||
collapsed={collapsedSections.public}
|
||||
onToggle={() => toggleSection("public")}
|
||||
/>
|
||||
<SectionList
|
||||
id="shared"
|
||||
label="共享页面"
|
||||
icon={SECTION_ICONS.shared}
|
||||
nodes={sharedNodes}
|
||||
collapsed={collapsedSections.shared}
|
||||
onToggle={() => toggleSection("shared")}
|
||||
/>
|
||||
<SectionList
|
||||
id="templates"
|
||||
label="模板中心"
|
||||
icon={SECTION_ICONS.templates}
|
||||
nodes={templateNodes}
|
||||
collapsed={collapsedSections.templates}
|
||||
onToggle={() => toggleSection("templates")}
|
||||
/>
|
||||
{viewMode === "section" ? (
|
||||
<>
|
||||
<SectionList
|
||||
id="starred"
|
||||
label="星标置顶"
|
||||
icon={SECTION_ICONS.starred}
|
||||
nodes={starredNodes}
|
||||
collapsed={collapsedSections.starred}
|
||||
onToggle={() => toggleSection("starred")}
|
||||
/>
|
||||
<SectionList
|
||||
id="public"
|
||||
label="公共页面"
|
||||
icon={SECTION_ICONS.public}
|
||||
nodes={publicNodes}
|
||||
collapsed={collapsedSections.public}
|
||||
onToggle={() => toggleSection("public")}
|
||||
/>
|
||||
<SectionList
|
||||
id="shared"
|
||||
label="共享页面"
|
||||
icon={SECTION_ICONS.shared}
|
||||
nodes={sharedNodes}
|
||||
collapsed={collapsedSections.shared}
|
||||
onToggle={() => toggleSection("shared")}
|
||||
/>
|
||||
<SectionList
|
||||
id="templates"
|
||||
label="模板中心"
|
||||
icon={SECTION_ICONS.templates}
|
||||
nodes={templateNodes}
|
||||
collapsed={collapsedSections.templates}
|
||||
onToggle={() => toggleSection("templates")}
|
||||
/>
|
||||
|
||||
<div className="flex flex-1 flex-col border-t border-[#f1f1f1]">
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center justify-between px-4 py-3 text-sm font-medium text-gray-600"
|
||||
onClick={() => toggleSection("private")}
|
||||
>
|
||||
<span>私有 / 我的页面</span>
|
||||
<MoreHorizontal className="h-4 w-4 text-gray-400" />
|
||||
</button>
|
||||
{!collapsedSections.private ? (
|
||||
<div className="flex flex-1 flex-col border-t border-[#f1f1f1]">
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center justify-between px-4 py-3 text-sm font-medium text-gray-600"
|
||||
onClick={() => toggleSection("private")}
|
||||
>
|
||||
<span>私有 / 我的页面</span>
|
||||
<MoreHorizontal className="h-4 w-4 text-gray-400" />
|
||||
</button>
|
||||
{!collapsedSections.private ? (
|
||||
<div className="flex-1 px-1 pb-2">
|
||||
<div className="h-full rounded-md border border-[#eff2f6] bg-white">
|
||||
<PrivateTree
|
||||
nodes={filteredPrivateTree}
|
||||
expanded={expanded}
|
||||
activeId={activeId}
|
||||
onToggleExpand={toggleExpand}
|
||||
onMove={handleMove}
|
||||
onCreateChild={handleCreate}
|
||||
onContextMenu={openContextMenu}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-4 pb-2 text-xs text-gray-400">已折叠</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-1 flex-col border-t border-[#f1f1f1]">
|
||||
<div className="flex-1 px-1 pb-2">
|
||||
<div className="h-full rounded-md border border-[#eff2f6] bg-white">
|
||||
<PrivateTree
|
||||
<FileTree
|
||||
nodes={filteredPrivateTree}
|
||||
assetsByDoc={assetsByDoc}
|
||||
expanded={expanded}
|
||||
activeId={activeId}
|
||||
onToggleExpand={toggleExpand}
|
||||
onMove={handleMove}
|
||||
onOpenDocument={(id) => handleOpenDocument(id, "main")}
|
||||
onOpenAsset={handleOpenAsset}
|
||||
onCreateChild={handleCreate}
|
||||
onContextMenu={openContextMenu}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-4 pb-2 text-xs text-gray-400">已折叠</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="border-t border-[#f1f1f1] p-3">
|
||||
<Button className="w-full justify-center gap-2" variant="outline" onClick={() => handleCreate(null)}>
|
||||
|
||||
Reference in New Issue
Block a user