"use client"; import { useState, useEffect, Suspense, Dispatch, SetStateAction, useRef, } from "react"; import { motion, useAnimation } from "framer-motion"; import { ContentItem } from "@/lib/types/note"; import { useRouter, useSearchParams } from "next/navigation"; import NewPostButton from "@/ui/new-post-button"; import UserDropdown from "@/ui/layout/user-dropdown"; import { Session } from "next-auth"; import { useCollaborationByUserId, useUserShareNotes } from "./request"; import Link from "next/link"; import { exportAsJson, fetcher } from "@/lib/utils"; import { Collaboration, ShareNote } from "@prisma/client"; import SearchInput from "@/ui/search-input"; import { Check, ChevronLeft, ChevronRight, Download, DownloadCloud, Edit, ExternalLink, Minus, Plus, Trash2, Shapes, FolderClosed, FolderOpen, FolderEdit, } from "lucide-react"; import Tooltip from "@/ui/shared/tooltip"; import useWindowSize from "@/lib/hooks/use-window-size"; import toast from "react-hot-toast"; import { addNote, deleteNote, patchNote, updateNote } from "@/store/db.model"; import useLocalStorage from "@/lib/hooks/use-local-storage"; import { Note_Storage_Key } from "@/lib/consts"; export default function Sidebar({ id, session, contents, setShowSignInModal, setShowEditModal, setShowRoomModal, }: { id?: string; session: Session | null; contents: ContentItem[]; setShowSignInModal: Dispatch>; setShowEditModal: Dispatch>; setShowRoomModal: Dispatch>; }) { const router = useRouter(); const params = useSearchParams(); const { isMobile } = useWindowSize(); const [active, setActive] = useState(false); const [showEditInput, setShowEditInput] = useState(false); const [showEditCate, setShowEditCate] = useState(false); const [searchKey, setSearchKey] = useState(""); const controls = useAnimation(); const controlText = useAnimation(); const controlTitleText = useAnimation(); const [contentsCache, setContentsCache] = useState([]); const [categorizedData, setCategorizedData] = useState<{ [key: string]: ContentItem[]; }>(); const { shares, isLoading } = useUserShareNotes(); const [sharesCache, setSharesCache] = useState([]); const { rooms } = useCollaborationByUserId(); const [roomsCache, setRoomsCache] = useState([]); const [openHistory, setOpenHistory] = useState(true); const [openShares, setOpenShares] = useState(false); const [openRooms, setOpenRooms] = useState(false); const editCateRef = useRef(null); const editTitleRef = useRef(null); const [oldContents, setOldContents] = useLocalStorage( Note_Storage_Key, [], ); const showMore = () => { controls.start({ width: "270px", transition: { duration: 0.001 }, }); controlText.start({ opacity: 1, display: "block", transition: { delay: 0.3 }, }); controlTitleText.start({ opacity: 1, transition: { delay: 0.3 }, }); setActive(true); }; const showLess = () => { controls.start({ width: "0px", transition: { duration: 0.001 }, }); controlText.start({ opacity: 0, display: "none", }); controlTitleText.start({ opacity: 0, }); setActive(false); }; // patch useEffect(() => { if (oldContents.length > 0) { patchNote(oldContents); setOldContents([]); } }, [oldContents]); useEffect(() => { showMore(); }, []); useEffect(() => { if (isMobile) { showLess(); } }, [isMobile]); useEffect(() => { if (searchKey === "") { setContentsCache(contents); setSharesCache(shares?.data || []); setCategorizedData(() => { return ( contents // .sort((a, b) => b.updated_at - a.updated_at) .reduce((acc, item) => { const tag = item.tag || ""; // If tag is undefined, default it to an empty string if (!acc[tag]) { acc[tag] = []; } acc[tag].push(item); return acc; }, {} as { [key: string]: ContentItem[] }) ); }); } }, [searchKey, contents, shares]); useEffect(() => { if (shares && shares.data) { setSharesCache(shares.data); } }, [shares]); useEffect(() => { if (rooms && rooms.data) { setRoomsCache(rooms.data); } }, [rooms]); const handleDeleteItem = (_id: string) => { deleteNote(_id); }; const handleDeletePublicItem = async (_id: string) => { const res = await fetcher(`/api/share?id=${_id}`, { method: "DELETE", }); const updatedList = shares.data.filter((item) => item.id !== _id); setSharesCache(updatedList); }; const handleEditTitle = (itemId: string) => { if (showEditInput && id === itemId) { setShowEditInput(false); const index = contents.findIndex((item) => item.id === id); if (index !== -1) { updateNote({ ...contents[index], title: editTitleRef.current.value, }); } } else { setShowEditInput(true); } }; const handleEditCate = (itemId: string) => { if (showEditCate && id === itemId) { setShowEditCate(false); const index = contents.findIndex((item) => item.id === id); if (index !== -1) { updateNote({ ...contents[index], tag: editCateRef.current.value, }); } } else { setShowEditCate(true); } }; const handleExportJson = () => { if (!contents) return; exportAsJson(contents, "Inke-notes-local"); }; const handleInputSearch = (value: string) => { if (value.length > 0) { setSearchKey(value); const local_res = contents.filter((item) => { if ( item.title.includes(value) || JSON.stringify(item.content).includes(value) || (item.tag && item.tag.includes(value)) ) { return item; } }); setContentsCache(local_res); setCategorizedData(() => { return ( local_res // .sort((a, b) => b.updated_at - a.updated_at) .reduce((acc, item) => { const tag = item.tag || ""; // If tag is undefined, default it to an empty string if (!acc[tag]) { acc[tag] = []; } acc[tag].push(item); return acc; }, {} as { [key: string]: ContentItem[] }) ); }); if (shares && shares.data) { const publish_res = shares.data.filter((item) => { if (item.data.includes(value)) { return item; } }); setSharesCache(publish_res); } } else { setSearchKey(""); } }; const handleClickPublishNote = (publishId: string, localId: string) => { const localIndex = contentsCache.findIndex((i) => i.id === localId); if (localIndex !== -1) { router.push(`/post/${localId}`); } else { router.push(`/publish/${localId}`); } }; const handleSyncPublisToLocal = (localId: string, remoteDate: string) => { const data = JSON.parse(remoteDate || "{}"); if (remoteDate && data) { addNote(data); router.push(`/post/${data.id}`); } }; const handleQuitSpace = async (id: string, roomId: string) => { const res = await fetcher(`/api/collaboration?id=${id}`, { method: "DELETE", }); if (res && res.code === 200) { toast("Exit space"); } }; const handleCreateSpace = () => { setShowRoomModal(true); }; const handleToggleCollapse = (tag: string) => { setCategorizedData((prevData) => { const updatedData = { ...prevData }; updatedData[tag].forEach((item) => { item.collapsed = !item.collapsed; }); return updatedData; }); }; return (
{active && ( )} {!active && ( )}
{ setOpenHistory(!openHistory); }} >

History({contents.length})

{openHistory && categorizedData && Object.keys(categorizedData).map((tag) => (

i.id === id) !== -1 ? "text-cyan-500" : "text-gray-500" }` + " flex cursor-pointer items-center justify-start gap-1 pt-2 font-mono text-xs font-semibold transition-all hover:text-slate-300" } onClick={() => handleToggleCollapse(tag)} > {categorizedData[tag][0].collapsed ? ( ) : ( )} {tag || "Uncategorized"}

{categorizedData[tag][0].collapsed && categorizedData[tag].map((item) => (
{showEditInput && id === item.id ? ( ) : showEditCate && id === item.id ? ( ) : (

router.push(`/post/${item.id}`)} > {item.title.length > 0 ? item.title : "Untitled"}

)}
{id === item.id && ( )} {id === item.id && ( )} {id !== item.id && ( )}
{sharesCache.length > 0 && sharesCache.find((i) => i.localId === item.id) && ( )}
))}
))} {sharesCache.length > 0 && ( <>
{ setOpenShares(!openShares); }} >

Published({shares.data.length})

{openShares && sharesCache.map((item) => ( {contentsCache.findIndex((i) => i.id === item.localId) === -1 && (

Cross device sync note

Sync your notes from other devices to the current device (history list).

} fullWidth={false} > )} ))} )} {roomsCache.length > 0 && ( <>
{ setOpenRooms(!openRooms); }} >

Collaborations({rooms.data.length})

{openRooms && roomsCache.map((item) => ( ))} )}
{session ? (
) : ( )}
Home Document Pricing
); }