"use client"; import { Content, StoredSpace } from "@repo/db/schema"; import { MemoriesIcon, NextIcon, UrlIcon } from "@repo/ui/icons"; import { ArrowLeftIcon, MenuIcon, MoveIcon, NotebookIcon, PaperclipIcon, TrashIcon, } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; import React, { useMemo, useState } from "react"; import Masonry from "react-layout-masonry"; import { getRawTweet } from "@repo/shared-types/utils"; import { MyTweet } from "../../../components/twitter/render-tweet"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@repo/ui/shadcn/dropdown-menu"; import { Button } from "@repo/ui/shadcn/button"; import { addUserToSpace, deleteItem, deleteSpace, moveItem, } from "@/app/actions/doers"; import { toast } from "sonner"; import { Input } from "@repo/ui/shadcn/input"; import { motion } from "framer-motion"; import { useSearchParams } from "next/navigation"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@repo/ui/shadcn/alert-dialog"; import { BookOpenIcon, DocumentTextIcon, FolderIcon, } from "@heroicons/react/24/outline"; type TMemoriesPage = { memoriesAndSpaces: { memories: Content[]; spaces: StoredSpace[] }; title?: string; currentSpace?: StoredSpace; usersWithAccess?: string[]; }; export function MemoriesPage({ memoriesAndSpaces, title = "Your Memories", currentSpace, usersWithAccess, }: TMemoriesPage) { const searchParams = useSearchParams(); const tab = searchParams.get("tab"); const initialFilter = useMemo(() => { if (tab === "spaces") return "Spaces"; if (tab === "pages") return "Pages"; if (tab === "notes") return "Notes"; if (tab === "tweet") return "Tweet"; return "All"; }, [tab]); const [filter, setFilter] = useState(initialFilter); const [spaces, setSpaces] = useState(memoriesAndSpaces.spaces); const handleDeleteSpace = async (id: number) => { const response = await deleteSpace(id); if (response?.success) { setSpaces(spaces.filter((space) => space.id !== id)); toast.success("Space deleted"); } else { toast.error("Failed to delete the space"); } }; const handleExport = () => { const dataToExport = sortedItems.map((item) => ({ type: item.item, date: new Date(item.date).toISOString(), data: item.data, })); const json = JSON.stringify(dataToExport, null, 2); const blob = new Blob([json], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "memories_and_spaces.json"; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; const sortedItems = useMemo(() => { // merge spaces & memories to { item: "memory" | "space", date: Date, data: Content | StoredSpace } const unifiedItems = [ ...memoriesAndSpaces.memories.map((memory) => ({ item: "memory", date: new Date(memory.savedAt), data: memory, })), ...spaces.map((space) => ({ item: "space", date: new Date(space.createdAt), data: space, })), ].map((item) => ({ ...item, date: Number(item.date), })); // Sort the merged list return unifiedItems .filter((item) => { if (filter === "All") return true; if (filter === "Spaces" && item.item === "space") { return true; } if (filter === "Pages") return ( item.item === "memory" && (item.data as Content).type === "page" ); if (filter === "Notes") return ( item.item === "memory" && (item.data as Content).type === "note" ); if (filter === "Tweet") return ( item.item === "memory" && (item.data as Content).type === "tweet" ); return false; }) .sort((a, b) => b.date - a.date); }, [memoriesAndSpaces.memories, spaces, filter]); return (
{currentSpace && ( )}

{title}

{currentSpace && (
{usersWithAccess && usersWithAccess.length > 0 && (
Users with access
{usersWithAccess.map((user) => (
Spaces icon {user}
))}
)}
{ const email = e.get("email")?.toString(); if (!email) { toast.error("Please enter an email"); return; } const resp = await addUserToSpace(email, currentSpace.id); if (resp.success) { toast.success("User added to space"); } else { toast.error("Failed to add user to space"); } }} className="flex gap-2 max-w-xl mt-2" >
)} {sortedItems.map((item) => { if (item.item === "memory") { return ( ); } if (item.item === "space") { return ( ); } return null; })}
); } function SpaceComponent({ title, description, id, handleDeleteSpace, }: { title: string; description: string; id: number; handleDeleteSpace: (id: number) => void; }) { return (
Space
{title.slice(0, 2).toUpperCase()} {id}
{title}
{description}
Search icon
{ handleDeleteSpace(id); }} />
); } function MemoryComponent({ type, content, title, url, image, description, spaces, id, }: { type: string; content: string; title: string; url: string; image?: string; description: string; spaces: StoredSpace[]; id: number; }) { return ( {type === "page" ? ( <>
Page
{/* remove `<---chunkId: ${vector.id}\n${content}\n---->` pattern from title */}
{title.replace(/(<---chunkId: .*?\n.*?\n---->)/g, "")}
{url.replace("https://supermemory.ai", "").split("#")[0] ?? "/"}
) : type === "note" ? ( <>
Note
{title.replace(/(<---chunkId: .*?\n.*?\n---->)/g, "")}
{content.replace(title, "")}
) : type === "tweet" ? ( ) : null} {spaces.length > 0 && ( Add to space {spaces.map((space) => ( ))} )}
); } const MemoriesFilterMethods = ["All", "Spaces", "Pages", "Notes", "Tweet"]; const SpaceFilterMethods = ["All", "Pages", "Notes", "Tweet"]; function Filters({ setFilter, filter, filterMethods, }: { setFilter: (i: string) => void; filter: string; filterMethods: string[]; }) { return (
{filterMethods.map((i) => { return (
{i === filter && ( )}
); })}
); } function SpaceDeleteAlert({ onClick }: { onClick: () => void }) { return ( {" "} Are you absolutely sure? This is irreversible. This will delete the space and all memories inside it. Cancel Delete ); } export default MemoriesPage;