feat: JMAP file/folder sharing in Files app #408
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
ChevronRight,
|
||||
ChevronDown,
|
||||
Home,
|
||||
Share2,
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useFileStore, type FileResource } from "@/stores/file-store";
|
||||
@@ -29,6 +30,8 @@ interface FolderTreeSidebarProps {
|
||||
export function FolderTreeSidebar({ currentPath, onNavigate, listByParentId, width = 256, isResizing }: FolderTreeSidebarProps) {
|
||||
const t = useTranslations("files");
|
||||
const client = useFileStore(s => s.client);
|
||||
const sharedRoots = useFileStore(s => s.sharedRoots);
|
||||
const loadSharedRoots = useFileStore(s => s.loadSharedRoots);
|
||||
const [rootChildren, setRootChildren] = useState<FolderNode[] | null>(null);
|
||||
const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set(["root"]));
|
||||
const [loadingIds, setLoadingIds] = useState<Set<string>>(new Set());
|
||||
@@ -81,6 +84,8 @@ export function FolderTreeSidebar({ currentPath, onNavigate, listByParentId, wid
|
||||
useEffect(() => {
|
||||
if (client) {
|
||||
loadChildren(null, "/");
|
||||
// Discover folders shared with the user by other principals.
|
||||
loadSharedRoots();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [client]);
|
||||
@@ -180,6 +185,40 @@ export function FolderTreeSidebar({ currentPath, onNavigate, listByParentId, wid
|
||||
/>
|
||||
))
|
||||
)}
|
||||
|
||||
{/* Shared with me: folders another principal has shared with the user */}
|
||||
{sharedRoots.filter(r => r.isDirectory).length > 0 && (
|
||||
<div className="mt-2 pt-2 border-t border-border/60">
|
||||
<div className="px-3 py-1 text-[11px] font-medium uppercase tracking-wide text-muted-foreground flex items-center gap-1.5">
|
||||
<Share2 className="w-3 h-3" />
|
||||
<span className="truncate">{t("shared_with_me")}</span>
|
||||
</div>
|
||||
{sharedRoots.filter(r => r.isDirectory).map(r => {
|
||||
const path = `/${r.name}`;
|
||||
const isSelected = currentPath === path;
|
||||
return (
|
||||
<div
|
||||
key={r.id}
|
||||
style={{ paddingBlock: "var(--density-sidebar-py)" }}
|
||||
className={cn(
|
||||
"group w-full flex items-center max-lg:min-h-[44px] text-sm transition-all duration-200 px-2",
|
||||
isSelected ? "bg-accent text-accent-foreground" : "hover:bg-muted text-foreground"
|
||||
)}
|
||||
>
|
||||
<button
|
||||
onClick={() => handleFolderClick(path, r.id)}
|
||||
className="flex items-center px-1 rounded transition-colors duration-150 flex-1 text-left min-w-0"
|
||||
style={{ paddingBlock: "var(--density-sidebar-py)", paddingLeft: "24px" }}
|
||||
title={r.ownerName ? t("shared_by", { name: r.ownerName }) : r.name}
|
||||
>
|
||||
<Folder className="w-4 h-4 flex-shrink-0 mr-2 text-primary" />
|
||||
<span className="truncate">{r.name}</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user