From 075f45986fd4d198292226e64afb71b3515576b4 Mon Sep 17 00:00:00 2001 From: Dhravya Date: Sat, 25 May 2024 18:41:26 -0500 Subject: refactored UI, with shared components and UI, better rules and million lint --- .../web/src/components/Sidebar/AddMemoryDialog.tsx | 480 -------------- .../src/components/Sidebar/DeleteConfirmation.tsx | 47 -- apps/web/src/components/Sidebar/EditNoteDialog.tsx | 155 ----- apps/web/src/components/Sidebar/ExpandedSpace.tsx | 287 --------- apps/web/src/components/Sidebar/FilterCombobox.tsx | 303 --------- apps/web/src/components/Sidebar/MemoriesBar.tsx | 709 --------------------- apps/web/src/components/Sidebar/SettingsTab.tsx | 99 --- apps/web/src/components/Sidebar/index.tsx | 172 ----- 8 files changed, 2252 deletions(-) delete mode 100644 apps/web/src/components/Sidebar/AddMemoryDialog.tsx delete mode 100644 apps/web/src/components/Sidebar/DeleteConfirmation.tsx delete mode 100644 apps/web/src/components/Sidebar/EditNoteDialog.tsx delete mode 100644 apps/web/src/components/Sidebar/ExpandedSpace.tsx delete mode 100644 apps/web/src/components/Sidebar/FilterCombobox.tsx delete mode 100644 apps/web/src/components/Sidebar/MemoriesBar.tsx delete mode 100644 apps/web/src/components/Sidebar/SettingsTab.tsx delete mode 100644 apps/web/src/components/Sidebar/index.tsx (limited to 'apps/web/src/components/Sidebar') diff --git a/apps/web/src/components/Sidebar/AddMemoryDialog.tsx b/apps/web/src/components/Sidebar/AddMemoryDialog.tsx deleted file mode 100644 index 64147b1e..00000000 --- a/apps/web/src/components/Sidebar/AddMemoryDialog.tsx +++ /dev/null @@ -1,480 +0,0 @@ -import { Editor } from "novel"; -import { - DialogClose, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "../ui/dialog"; -import { Input } from "../ui/input"; -import { Label } from "../ui/label"; -import { Markdown } from "tiptap-markdown"; -import { useEffect, useRef, useState } from "react"; -import { FilterMemories, FilterSpaces } from "./FilterCombobox"; -import { useMemory } from "@/contexts/MemoryContext"; -import { Loader, Plus, X } from "lucide-react"; -import { StoredContent, StoredSpace } from "@/server/db/schema"; -import { cleanUrl } from "@/lib/utils"; -import { motion } from "framer-motion"; -import { getMetaData } from "@/server/helpers"; - -export function AddMemoryPage({ - closeDialog, - defaultSpaces, - onAdd, -}: { - closeDialog: () => void; - defaultSpaces?: number[]; - onAdd?: (addedData: StoredContent) => void; -}) { - const { addMemory } = useMemory(); - - const [loading, setLoading] = useState(false); - const [url, setUrl] = useState(""); - const [selectedSpacesId, setSelectedSpacesId] = useState( - defaultSpaces ?? [], - ); - - return ( -
- - Add a web page to memory - - This will fetch the content of the web page and add it to the memory - - - - setUrl(e.target.value)} - disabled={loading} - /> - - - - - Cancel - - -
- ); -} - -export function NoteAddPage({ - closeDialog, - defaultSpaces, - onAdd, -}: { - closeDialog: () => void; - defaultSpaces?: number[]; - onAdd?: (addedData: StoredContent) => void; -}) { - const { addMemory } = useMemory(); - - const [selectedSpacesId, setSelectedSpacesId] = useState( - defaultSpaces ?? [], - ); - - const inputRef = useRef(null); - const [name, setName] = useState(""); - const [content, setContent] = useState(""); - const [loading, setLoading] = useState(false); - - function check(): boolean { - const data = { - name: name.trim(), - content, - }; - if (!data.name || data.name.length < 1) { - if (!inputRef.current) { - alert("Please enter a name for the note"); - return false; - } - inputRef.current.value = ""; - inputRef.current.placeholder = "Please enter a title for the note"; - inputRef.current.dataset["error"] = "true"; - setTimeout(() => { - inputRef.current!.placeholder = "Title of the note"; - inputRef.current!.dataset["error"] = "false"; - }, 500); - inputRef.current.focus(); - return false; - } - return true; - } - - return ( -
- setName(e.target.value)} - /> - { - if (!editor) return; - setContent(editor.storage.markdown.getMarkdown()); - }} - extensions={[Markdown]} - className="novel-editor border-rgray-7 dark mt-5 max-h-[60vh] min-h-[40vh] w-full overflow-y-auto rounded-lg border bg-white md:w-[50vw] [&>div>div]:p-5" - /> - - - - - Cancel - - -
- ); -} - -export function SpaceAddPage({ - closeDialog, - onAdd, -}: { - closeDialog: () => void; - onAdd?: (addedData: StoredSpace) => void; -}) { - const { addSpace } = useMemory(); - - const inputRef = useRef(null); - const [name, setName] = useState(""); - - const [loading, setLoading] = useState(false); - - const [selected, setSelected] = useState([]); - - function check(): boolean { - const data = { - name: name.trim(), - }; - if (!data.name || data.name.length < 1) { - if (!inputRef.current) { - alert("Please enter a name for the note"); - return false; - } - inputRef.current.value = ""; - inputRef.current.placeholder = "Please enter a title for the space"; - inputRef.current.dataset["error"] = "true"; - setTimeout(() => { - inputRef.current!.placeholder = "Enter the name of the space"; - inputRef.current!.dataset["error"] = "false"; - }, 500); - inputRef.current.focus(); - return false; - } - return true; - } - - return ( -
- - Add a space - - - setName(e.target.value)} - className="mt-2 w-full placeholder:transition placeholder:duration-500 data-[error=true]:placeholder:text-red-400 focus-visible:data-[error=true]:ring-red-500/10" - /> - {selected.length > 0 && ( - <> - -
- {selected.map((i) => ( - - setSelected((prev) => prev.filter((p) => p.id !== i.id)) - } - {...i} - /> - ))} -
- - )} - - - - Memory - - - - Cancel - - -
- ); -} - -export function MemorySelectedItem({ - id, - title, - url, - type, - image, - onRemove, -}: StoredContent & { onRemove: () => void }) { - return ( -
- - {title} - - {type === "note" ? "Note" : cleanUrl(url)} - -
- ); -} - -export function AddExistingMemoryToSpace({ - space, - closeDialog, - fromSpaces, - notInSpaces, - onAdd, -}: { - space: { title: string; id: number }; - closeDialog: () => void; - fromSpaces?: number[]; - notInSpaces?: number[]; - onAdd?: () => void; -}) { - const { addMemoriesToSpace } = useMemory(); - - const [loading, setLoading] = useState(false); - - const [selected, setSelected] = useState([]); - - return ( -
- - Add an existing memory to {space.title} - - Pick the memories you want to add to this space - - - {selected.length > 0 && ( - <> - -
- {selected.map((i) => ( - - setSelected((prev) => prev.filter((p) => p.id !== i.id)) - } - {...i} - /> - ))} -
- - )} - - - - Memory - - - - Cancel - - -
- ); -} diff --git a/apps/web/src/components/Sidebar/DeleteConfirmation.tsx b/apps/web/src/components/Sidebar/DeleteConfirmation.tsx deleted file mode 100644 index 7955df0d..00000000 --- a/apps/web/src/components/Sidebar/DeleteConfirmation.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { - Dialog, - DialogContent, - DialogTrigger, - DialogTitle, - DialogDescription, - DialogClose, - DialogFooter, -} from "../ui/dialog"; - -export default function DeleteConfirmation({ - onDelete, - trigger = true, - children, -}: { - trigger?: boolean; - onDelete?: () => void; - children: React.ReactNode; -}) { - return ( - - {trigger ? ( - {children} - ) : ( - <>{children} - )} - - Are you sure? - - You will not be able to recover this it. - - - - Delete - - - Cancel - - - - - ); -} diff --git a/apps/web/src/components/Sidebar/EditNoteDialog.tsx b/apps/web/src/components/Sidebar/EditNoteDialog.tsx deleted file mode 100644 index c0ad716d..00000000 --- a/apps/web/src/components/Sidebar/EditNoteDialog.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { Editor } from "novel"; -import { DialogClose, DialogFooter } from "../ui/dialog"; -import { Input } from "../ui/input"; -import { Markdown } from "tiptap-markdown"; -import { useEffect, useRef, useState } from "react"; -import { FilterSpaces } from "./FilterCombobox"; -import { useMemory } from "@/contexts/MemoryContext"; -import { Loader, Plus, Trash, X } from "lucide-react"; -import { motion } from "framer-motion"; -import { StoredContent } from "@/server/db/schema"; -import { fetchContent } from "@/actions/db"; -import { isArraysEqual } from "@/lib/utils"; -import DeleteConfirmation from "./DeleteConfirmation"; - -export function NoteEdit({ - memory, - closeDialog, - onDelete, -}: { - memory: StoredContent; - closeDialog: () => any; - onDelete?: () => void; -}) { - const { updateMemory, deleteMemory } = useMemory(); - - const [initialSpaces, setInitialSpaces] = useState([]); - const [selectedSpacesId, setSelectedSpacesId] = useState([]); - - const inputRef = useRef(null); - const [name, setName] = useState(memory.title ?? ""); - const [content, setContent] = useState(memory.content); - const [loading, setLoading] = useState(false); - - function check(): boolean { - const data = { - name: name.trim(), - content, - }; - if (!data.name || data.name.length < 1) { - if (!inputRef.current) { - alert("Please enter a name for the note"); - return false; - } - inputRef.current.value = ""; - inputRef.current.placeholder = "Please enter a title for the note"; - inputRef.current.dataset["error"] = "true"; - setTimeout(() => { - inputRef.current!.placeholder = "Title of the note"; - inputRef.current!.dataset["error"] = "false"; - }, 500); - inputRef.current.focus(); - return false; - } - return true; - } - - useEffect(() => { - fetchContent(memory.id).then((data) => { - if (data?.spaces) { - setInitialSpaces(data.spaces); - setSelectedSpacesId(data.spaces); - } - }); - }, []); - - return ( -
- setName(e.target.value)} - /> - { - if (!editor) return; - setContent(editor.storage.markdown.getMarkdown()); - }} - extensions={[Markdown]} - className="novel-editor border-rgray-7 dark mt-5 max-h-[60vh] min-h-[40vh] w-[50vw] overflow-y-auto rounded-lg border bg-white [&>div>div]:p-5" - /> - - - { - deleteMemory(memory.id); - onDelete?.(); - }} - > - - - - - Cancel - - -
- ); -} diff --git a/apps/web/src/components/Sidebar/ExpandedSpace.tsx b/apps/web/src/components/Sidebar/ExpandedSpace.tsx deleted file mode 100644 index 55d3f3f8..00000000 --- a/apps/web/src/components/Sidebar/ExpandedSpace.tsx +++ /dev/null @@ -1,287 +0,0 @@ -import { fetchContentForSpace, getSpace } from "@/actions/db"; -import { useMemory } from "@/contexts/MemoryContext"; -import { StoredContent, StoredSpace } from "@/server/db/schema"; -import { - Edit3, - Loader, - Plus, - Search, - Sparkles, - StickyNote, - Text, - Undo2, -} from "lucide-react"; -import { useEffect, useRef, useState } from "react"; -import { Input, InputWithIcon } from "../ui/input"; -import { useDebounce } from "@/hooks/useDebounce"; -import { useAutoAnimate } from "@formkit/auto-animate/react"; -import { AddMemoryModal, MemoryItem } from "./MemoriesBar"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "../ui/dropdown-menu"; -import { DialogTrigger } from "../ui/dialog"; - -export function ExpandedSpace({ - spaceId, - back, -}: { - spaceId: number; - back: () => void; -}) { - const { updateMemory, updateSpace, search } = useMemory(); - - const [parent, enableAnimations] = useAutoAnimate(); - - const inputRef = useRef(null); - - const [contentForSpace, setContentForSpace] = useState([]); - - const [lastUpdatedTitle, setLastUpdatedTitle] = useState(null); - - const [title, setTitle] = useState(""); - const debouncedTitle = useDebounce(title, 500); - - const [loading, setLoading] = useState(true); - - const [saveLoading, setSaveLoading] = useState(false); - - const [searchQuery, setSearcyQuery] = useState(""); - const [searchLoading, setSearchLoading] = useState(false); - const query = useDebounce(searchQuery, 500); - - const [searchResults, setSearchResults] = useState([]); - - const [addMemoryState, setAddMemoryState] = useState< - "page" | "note" | "existing-memory" | "space" | null - >(null); - const [isDropdownOpen, setIsDropdownOpen] = useState(false); - - useEffect(() => { - (async () => { - const title = (await getSpace(spaceId))?.name ?? ""; - setTitle(title); - setLastUpdatedTitle(title); - setContentForSpace((await fetchContentForSpace(spaceId)) ?? []); - setLoading(false); - })(); - }, []); - - useEffect(() => { - if ( - debouncedTitle.trim().length < 1 || - debouncedTitle.trim() === lastUpdatedTitle?.trim() - ) - return; - (async () => { - setSaveLoading(true); - await updateSpace(spaceId, debouncedTitle.trim()); - setLastUpdatedTitle(debouncedTitle); - setSaveLoading(false); - })(); - }, [debouncedTitle]); - - useEffect(() => { - const q = query.trim(); - if (q.length < 1) { - setSearchResults([]); - return; - } - - setSearchLoading(true); - - (async () => { - setSearchResults( - ( - await search(q, { - filter: { spaces: false }, - memoriesRelativeToSpace: { - fromSpaces: [spaceId], - }, - }) - ).map((i) => i.memory!), - ); - setSearchLoading(false); - })(); - }, [query]); - - if (loading) { - return ( -
- -
- ); - } - - return ( -
-
- - setTitle(e.target.value)} - /> - -
-
- - ) : ( - - ) - } - className="bg-rgray-4 mt-2 w-full" - value={searchQuery} - onChange={(e) => setSearcyQuery(e.target.value)} - /> -
-
- { - if (!data) { - setLoading(true); - (async () => { - const title = (await getSpace(spaceId))?.name ?? ""; - setTitle(title); - setLastUpdatedTitle(title); - setContentForSpace((await fetchContentForSpace(spaceId)) ?? []); - setLoading(false); - })(); - } else if (Object.hasOwn(data, "url")) { - const _data = data as StoredContent; - setContentForSpace((prev) => [...prev, _data]); - } - }} - data={{ space: { title, id: spaceId }, notInSpaces: [spaceId] }} - defaultSpaces={[spaceId]} - type={addMemoryState} - > - - - - - e.preventDefault()}> - - { - setAddMemoryState("existing-memory"); - }} - > - - Existing Memory - - - - { - setAddMemoryState("page"); - }} - > - - Page - - - - { - setAddMemoryState("note"); - }} - > - - Note - - - - - -
-
- {query.trim().length > 0 ? ( - <> - {searchResults.map((memory, i) => ( - { - await updateMemory(memory.id, { - removedFromSpaces: [spaceId], - }); - setContentForSpace((prev) => - prev.filter((s) => s.id !== memory.id), - ); - setSearchResults((prev) => - prev.filter((i) => i.id !== memory.id), - ); - }} - {...memory!} - key={i} - onDelete={() => { - setContentForSpace((prev) => - prev.filter((s) => s.id !== memory.id), - ); - setSearchResults((prev) => - prev.filter((i) => i.id !== memory.id), - ); - }} - /> - ))} - - ) : ( - contentForSpace.map((m) => ( - - setContentForSpace((prev) => prev.filter((s) => s.id !== m.id)) - } - removeFromSpace={async () => { - await updateMemory(m.id, { - removedFromSpaces: [spaceId], - }); - setContentForSpace((prev) => prev.filter((s) => s.id !== m.id)); - }} - /> - )) - )} -
-
- ); -} diff --git a/apps/web/src/components/Sidebar/FilterCombobox.tsx b/apps/web/src/components/Sidebar/FilterCombobox.tsx deleted file mode 100644 index 634a09e3..00000000 --- a/apps/web/src/components/Sidebar/FilterCombobox.tsx +++ /dev/null @@ -1,303 +0,0 @@ -"use client"; - -import * as React from "react"; -import { Check, ChevronsUpDown } from "lucide-react"; - -import { cn } from "@/lib/utils"; -import { Button } from "@/components/ui/button"; -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, -} from "@/components/ui/command"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { SpaceIcon } from "@/assets/Memories"; -import { AnimatePresence, LayoutGroup, motion } from "framer-motion"; -import { SearchResult, useMemory } from "@/contexts/MemoryContext"; -import { useDebounce } from "@/hooks/useDebounce"; -import { StoredContent } from "@/server/db/schema"; - -export interface FilterSpacesProps - extends React.ButtonHTMLAttributes { - side?: "top" | "bottom"; - align?: "end" | "start" | "center"; - onClose?: () => void; - selectedSpaces: number[]; - setSelectedSpaces: ( - spaces: number[] | ((prev: number[]) => number[]), - ) => void; - name: string; -} - -export function FilterSpaces({ - className, - side = "bottom", - align = "center", - onClose, - selectedSpaces, - setSelectedSpaces, - name, - ...props -}: FilterSpacesProps) { - const { spaces } = useMemory(); - const [open, setOpen] = React.useState(false); - - const sortedSpaces = spaces.sort(({ id: a }, { id: b }) => - selectedSpaces.includes(a) && !selectedSpaces.includes(b) - ? -1 - : selectedSpaces.includes(b) && !selectedSpaces.includes(a) - ? 1 - : 0, - ); - - React.useEffect(() => { - if (!open) { - onClose?.(); - } - }, [open]); - - return ( - - - - - e.preventDefault()} - > - - spaces - .find((s) => s.id.toString() === val) - ?.name.toLowerCase() - .includes(search.toLowerCase().trim()) - ? 1 - : 0 - } - > - - - - Nothing found - - {sortedSpaces.map((space) => ( - { - setSelectedSpaces((prev: number[]) => - prev.includes(parseInt(val)) - ? prev.filter((v) => v !== parseInt(val)) - : [...prev, parseInt(val)], - ); - }} - asChild - > - - - {space.name.length > 10 - ? space.name.slice(0, 10) + "..." - : space.name} - {selectedSpaces.includes(space.id)} - - - - ))} - - - - - - - ); -} - -export type FilterMemoriesProps = { - side?: "top" | "bottom"; - align?: "end" | "start" | "center"; - onClose?: () => void; - selected: StoredContent[]; - setSelected: React.Dispatch>; - fromSpaces?: number[]; - notInSpaces?: number[]; -} & React.ButtonHTMLAttributes; - -export function FilterMemories({ - className, - side = "bottom", - align = "center", - onClose, - selected, - setSelected, - fromSpaces, - notInSpaces, - ...props -}: FilterMemoriesProps) { - const { search } = useMemory(); - - const [open, setOpen] = React.useState(false); - const [searchQuery, setSearchQuery] = React.useState(""); - const query = useDebounce(searchQuery, 500); - - const [searchResults, setSearchResults] = React.useState([]); - const [isSearching, setIsSearching] = React.useState(false); - - const results = React.useMemo(() => { - return searchResults.map((r) => r.memory); - }, [searchResults]); - - console.log("memoized", results); - - React.useEffect(() => { - const q = query.trim(); - if (q.length > 0) { - setIsSearching(true); - (async () => { - const results = await search(q, { - filter: { - memories: true, - spaces: false, - }, - memoriesRelativeToSpace: { - fromSpaces, - notInSpaces, - }, - }); - setSearchResults(results); - setIsSearching(false); - })(); - } else { - setSearchResults([]); - } - }, [query]); - - React.useEffect(() => { - if (!open) { - onClose?.(); - } - }, [open]); - - console.log(searchResults); - return ( - - - - - - - e.preventDefault()} - align={align} - side={side} - className="w-[200px] p-0" - > - - - - - - {isSearching - ? "Searching..." - : query.trim().length > 0 - ? "Nothing Found" - : "Search something"} - - {results.map((m) => ( - { - setSelected((prev) => - prev.find((p) => p.id === parseInt(val)) - ? prev.filter((v) => v.id !== parseInt(val)) - : [...prev, m], - ); - }} - asChild - > -
- - {m.title && m.title?.length > 14 - ? m.title?.slice(0, 14) + "..." - : m.title} - i.id === m.id) !== undefined - } - className={cn( - "on:opacity-100 ml-auto h-4 w-4 opacity-0", - )} - /> -
-
- ))} -
-
-
-
-
-
-
- ); -} diff --git a/apps/web/src/components/Sidebar/MemoriesBar.tsx b/apps/web/src/components/Sidebar/MemoriesBar.tsx deleted file mode 100644 index a81d00c0..00000000 --- a/apps/web/src/components/Sidebar/MemoriesBar.tsx +++ /dev/null @@ -1,709 +0,0 @@ -import { Editor } from "novel"; -import { useAutoAnimate } from "@formkit/auto-animate/react"; -import { - MemoryWithImage, - MemoryWithImages3, - MemoryWithImages2, -} from "@/assets/MemoryWithImages"; -import { Input, InputWithIcon } from "../ui/input"; -import { - ArrowUpRight, - Edit3, - Loader, - Minus, - MoreHorizontal, - Plus, - Search, - Sparkles, - Text, - Trash2, -} from "lucide-react"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "../ui/dropdown-menu"; -import { useEffect, useMemo, useRef, useState } from "react"; -import { Variant, useAnimate, motion } from "framer-motion"; -import { SearchResult, useMemory } from "@/contexts/MemoryContext"; -import { SpaceIcon } from "@/assets/Memories"; -import { Dialog, DialogContent } from "../ui/dialog"; -import useViewport from "@/hooks/useViewport"; -import useTouchHold from "@/hooks/useTouchHold"; -import { DialogTrigger } from "@radix-ui/react-dialog"; -import { - AddExistingMemoryToSpace, - AddMemoryPage, - NoteAddPage, - SpaceAddPage, -} from "./AddMemoryDialog"; -import { ExpandedSpace } from "./ExpandedSpace"; -import { StoredContent, StoredSpace } from "@/server/db/schema"; -import { useDebounce } from "@/hooks/useDebounce"; -import { NoteEdit } from "./EditNoteDialog"; -import DeleteConfirmation from "./DeleteConfirmation"; - -export function MemoriesBar({ isOpen }: { isOpen: boolean }) { - const [parent, enableAnimations] = useAutoAnimate(); - const { spaces, deleteSpace, freeMemories, search } = useMemory(); - - const [isDropdownOpen, setIsDropdownOpen] = useState(false); - const [addMemoryState, setAddMemoryState] = useState< - "page" | "note" | "space" | "existing-memory" | null - >(null); - - const [expandedSpace, setExpandedSpace] = useState(null); - - const [searchQuery, setSearcyQuery] = useState(""); - const [searchLoading, setSearchLoading] = useState(false); - const query = useDebounce(searchQuery, 500); - - const [searchResults, setSearchResults] = useState([]); - - useEffect(() => { - const q = query.trim(); - if (q.length < 1) { - setSearchResults([]); - return; - } - - setSearchLoading(true); - - (async () => { - setSearchResults(await search(q)); - setSearchLoading(false); - })(); - }, [query]); - - useEffect(() => { - if (!isOpen) { - setExpandedSpace(null); - } - }, [isOpen]); - - if (expandedSpace) { - return ( - setExpandedSpace(null)} - // close={() => setExpandedSpace(null)} - /> - ); - } - - return ( -
-
-

Your Memories

- - ) : ( - - ) - } - className="bg-rgray-4 mt-2 w-full" - value={searchQuery} - onChange={(e) => setSearcyQuery(e.target.value)} - /> -
-
- - - - - - e.preventDefault()}> - - { - setAddMemoryState("page"); - }} - > - - Page to Memory - - - - { - setAddMemoryState("note"); - }} - > - - Note - - - - { - setAddMemoryState("space"); - }} - > - - Space - - - - - -
-
- {query.trim().length > 0 ? ( - <> - {searchResults.map(({ type, space, memory }, i) => ( - <> - {type === "memory" && ( - { - setSearchResults((prev) => - prev.filter((i) => i.memory?.id !== memory.id), - ); - }} - /> - )} - {type === "space" && ( - { - setSearchResults((prev) => - prev.filter((i) => i.space?.id !== space.id), - ); - deleteSpace(space.id); - }} - /> - )} - - ))} - - ) : ( - <> - {spaces.map((space) => ( - deleteSpace(space.id)} - key={space.id} - onClick={() => setExpandedSpace(space.id)} - {...space} - /> - ))} - {freeMemories.map((m) => ( - - ))} - - )} -
-
- ); -} - -const SpaceExitVariant: Variant = { - opacity: 0, - scale: 0, - borderRadius: "50%", - background: "var(--gray-1)", - transition: { - duration: 0.2, - }, -}; - -export function MemoryItem( - props: StoredContent & { - onDelete?: () => void; - removeFromSpace?: () => Promise; - }, -) { - const { id, title, image, type, url, onDelete, removeFromSpace } = props; - - const { deleteMemory } = useMemory(); - - const name = title - ? title.length > 10 - ? title.slice(0, 10) + "..." - : title - : ""; - - const [isDialogOpen, setIsDialogOpen] = useState(false); - - const [moreDropdownOpen, setMoreDropdownOpen] = useState(false); - - const touchEventProps = useTouchHold({ - onHold() { - setMoreDropdownOpen(true); - }, - }); - return ( - -
- {type === "note" ? ( - - - - ) : ( - - )} - - {type === "page" ? ( - { - deleteMemory(id); - onDelete?.(); - }} - url={url} - /> - ) : type === "note" ? ( - setIsDialogOpen(true)} - onDelete={() => { - deleteMemory(id); - onDelete?.(); - }} - /> - ) : null} - -
- {type === "page" ? ( - window.open(url)} - className="h-16 w-16" - id={id.toString()} - src={image!} - onError={(e) => { - (e.target as HTMLImageElement).src = - "/icons/white_without_bg.png"; - }} - /> - ) : type === "note" ? ( - setIsDialogOpen(true)} className="h-16 w-16" /> - ) : ( - <> - )} -
-
- - setIsDialogOpen(false)} - memory={props} - /> - -
- ); -} - -export function SpaceItem({ - name, - id, - onDelete, - onClick, -}: StoredSpace & { onDelete: () => void; onClick?: () => void }) { - const { cachedMemories } = useMemory(); - - const [itemRef, animateItem] = useAnimate(); - const { width } = useViewport(); - - const [moreDropdownOpen, setMoreDropdownOpen] = useState(false); - - const touchEventProps = useTouchHold({ - onHold() { - setMoreDropdownOpen(true); - }, - }); - - const spaceMemories = useMemo(() => { - return cachedMemories.filter((m) => m.space === id); - }, [cachedMemories]); - - const _name = name.length > 10 ? name.slice(0, 10) + "..." : name; - - return ( - - - { - onDelete(); - return; - if (!itemRef.current || width < 768) { - onDelete(); - return; - } - // const trash = document.querySelector("#trash")! as HTMLDivElement; - // const trashBin = document.querySelector("#trash-button")!; - // const trashRect = trashBin.getBoundingClientRect(); - // const scopeRect = itemRef.current.getBoundingClientRect(); - // const el = document.createElement("div"); - // el.style.position = "fixed"; - // el.style.top = "0"; - // el.style.left = "0"; - // el.style.width = "15px"; - // el.style.height = "15px"; - // el.style.backgroundColor = "var(--gray-7)"; - // el.style.zIndex = "60"; - // el.style.borderRadius = "50%"; - // el.style.transform = "scale(5)"; - // el.style.opacity = "0"; - // trash.dataset["open"] = "true"; - // const initial = { - // x: scopeRect.left + scopeRect.width / 2, - // y: scopeRect.top + scopeRect.height / 2, - // }; - // const delta = { - // x: - // trashRect.left + - // trashRect.width / 2 - - // scopeRect.left + - // scopeRect.width / 2, - // y: - // trashRect.top + - // trashRect.height / 4 - - // scopeRect.top + - // scopeRect.height / 2, - // }; - // const end = { - // x: trashRect.left + trashRect.width / 2, - // y: trashRect.top + trashRect.height / 4, - // }; - // el.style.offsetPath = `path('M ${initial.x} ${initial.y} Q ${delta.x * 0.01} ${delta.y * 0.01} ${end.x} ${end.y}`; - // animateItem(itemRef.current, SpaceExitVariant, { - // duration: 0.2, - // }).then(() => { - // itemRef.current.style.scale = "0"; - // onDelete(); - // }); - // document.body.appendChild(el); - // el.animate( - // { - // transform: ["scale(5)", "scale(1)"], - // opacity: [0, 0.3, 1], - // }, - // { - // duration: 200, - // easing: "cubic-bezier(0.64, 0.57, 0.67, 1.53)", - // fill: "forwards", - // }, - // ); - // el.animate( - // { - // offsetDistance: ["0%", "100%"], - // }, - // { - // duration: 2000, - // easing: "cubic-bezier(0.64, 0.57, 0.67, 1.53)", - // fill: "forwards", - // delay: 200, - // }, - // ).onfinish = () => { - // el.animate( - // { transform: "scale(0)", opacity: 0 }, - // { duration: 200, fill: "forwards" }, - // ).onfinish = () => { - // el.remove(); - // }; - // }; - }} - /> - {spaceMemories.length > 2 ? ( - (c.type === "note" ? "/note.svg" : c.image)) - .reverse() as string[] - } - /> - ) : spaceMemories.length > 1 ? ( - (c.type === "note" ? "/note.svg" : c.image)) - .reverse() as string[] - } - /> - ) : spaceMemories.length === 1 ? ( - - ) : ( -
- )} -
- ); -} - -export function SpaceMoreButton({ - onDelete, - isOpen, - setIsOpen, - onEdit, -}: { - onDelete?: () => void; - isOpen?: boolean; - onEdit?: () => void; - setIsOpen?: (open: boolean) => void; -}) { - return ( - - - - - - - - - Edit - - - - - Delete - - - - - - ); -} - -export function PageMoreButton({ - onDelete, - isOpen, - setIsOpen, - url, - removeFromSpace, -}: { - onDelete?: () => void; - isOpen?: boolean; - url: string; - setIsOpen?: (open: boolean) => void; - removeFromSpace?: () => Promise; -}) { - return ( - - - - - - - window.open(url)}> - - Open - - {removeFromSpace && ( - - - Remove from space - - )} - - - - Delete - - - - - - ); -} - -export function NoteMoreButton({ - onDelete, - isOpen, - setIsOpen, - onEdit, - removeFromSpace, -}: { - onDelete?: () => void; - isOpen?: boolean; - onEdit?: () => void; - setIsOpen?: (open: boolean) => void; - removeFromSpace?: () => Promise; -}) { - return ( - - - - - - - - - Edit - - {removeFromSpace && ( - - - Remove from space - - )} - - - - Delete - - - - - - ); -} - -export function AddMemoryModal({ - type, - children, - defaultSpaces, - onAdd, - data, -}: { - type: "page" | "note" | "space" | "existing-memory" | null; - children?: React.ReactNode | React.ReactNode[]; - defaultSpaces?: number[]; - data?: { - space?: { - title: string; - id: number; - }; - fromSpaces?: number[]; - notInSpaces?: number[]; - }; - onAdd?: (data?: StoredSpace | StoredContent | StoredContent[]) => void; -}) { - const [isDialogOpen, setIsDialogOpen] = useState(false); - - return ( - - {children} - { - e.preventDefault(); - const novel = document.querySelector('[contenteditable="true"]') as - | HTMLDivElement - | undefined; - if (novel) { - novel.autofocus = false; - novel.onfocus = () => { - ( - document.querySelector("[data-modal-autofocus]") as - | HTMLInputElement - | undefined - )?.focus(); - novel.onfocus = null; - }; - } - ( - document.querySelector("[data-modal-autofocus]") as - | HTMLInputElement - | undefined - )?.focus(); - }} - className="w-max max-w-[auto]" - > - {type === "page" ? ( - setIsDialogOpen(false)} - /> - ) : type === "note" ? ( - setIsDialogOpen(false)} - /> - ) : type === "space" ? ( - setIsDialogOpen(false)} - /> - ) : type === "existing-memory" ? ( - setIsDialogOpen(false)} - /> - ) : ( - <> - )} - - - ); -} diff --git a/apps/web/src/components/Sidebar/SettingsTab.tsx b/apps/web/src/components/Sidebar/SettingsTab.tsx deleted file mode 100644 index 31b8380d..00000000 --- a/apps/web/src/components/Sidebar/SettingsTab.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { Box, LogOut } from "lucide-react"; -import { signOut, useSession } from "next-auth/react"; -import { useEffect, useState } from "react"; - -export function SettingsTab({ open }: { open: boolean }) { - const { data: session } = useSession(); - - const [tweetStat, setTweetStat] = useState<[number, number] | null>(); - const [memoryStat, setMemoryStat] = useState<[number, number] | null>(); - - const [loading, setLoading] = useState(true); - - useEffect(() => { - fetch("/api/getCount").then(async (resp) => { - const data = (await resp.json()) as any; - setTweetStat([data.tweetsCount, data.tweetsLimit]); - setMemoryStat([data.pageCount, data.pageLimit]); - setLoading(false); - }); - }, [open]); - - return ( -
-
-

Settings

-
- { - (e.target as HTMLImageElement).src = - "/icons/white_without_bg.png"; - }} - /> -
-

{session?.user?.name}

- {session?.user?.email} - -
-
-
-
-

- - Storage -

- {loading ? ( -
-
-
-
- ) : ( - <> -
-

- Memories -
- {memoryStat?.join("/")} -
-

-
-
0 ? "5%" : "0%", - }} - className="bg-rgray-5 h-full rounded-full" - /> -
-
-
-

- Tweets -
- {tweetStat?.join("/")} -
-

-
-
0 ? "5%" : "0%", - }} - className="h-full rounded-full bg-white" - /> -
-
- - )} -
-
- ); -} diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx deleted file mode 100644 index ae757afe..00000000 --- a/apps/web/src/components/Sidebar/index.tsx +++ /dev/null @@ -1,172 +0,0 @@ -"use client"; -import { MemoryIcon } from "../../assets/Memories"; -import React, { useEffect, useState } from "react"; -import { AnimatePresence, motion } from "framer-motion"; -import { signOut, useSession } from "next-auth/react"; -import MessagePoster from "@/app/MessagePoster"; -import Link from "next/link"; -import { SettingsTab } from "./SettingsTab"; -import { Avatar, AvatarImage } from "@radix-ui/react-avatar"; -import { AvatarFallback } from "../ui/avatar"; - -export type MenuItem = { - icon: React.ReactNode | React.ReactNode[]; - label: string; - content?: React.ReactNode; - labelDisplay?: React.ReactNode; -}; - -export default function Sidebar({ - selectChange, - jwt, -}: { - selectChange?: (selectedItem: string | null) => void; - jwt: string; -}) { - const { data: session } = useSession(); - - const [selectedItem, setSelectedItem] = useState(null); - - const menuItemsTop: Array = []; - - const menuItemsBottom: Array = [ - { - label: "Settings", - content: , - icon: <>, - }, - ]; - - const menuItems = [...menuItemsTop, ...menuItemsBottom]; - - const Subbar = menuItems.find((i) => i.label === selectedItem)?.content ?? ( - <> - ); - - useEffect(() => { - void selectChange?.(selectedItem); - }, [selectedItem]); - - return ( -
-
- setSelectedItem(null)} - className="focus-visible:ring-rgray-7 relative z-[100] flex w-full flex-col items-center justify-center rounded-md px-3 py-3 opacity-80 ring-2 ring-transparent transition hover:bg-stone-300 hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none" - > - - Memories - - -
- - - - - - ), - content: , - }} - selectedItem={selectedItem} - setSelectedItem={setSelectedItem} - /> - {/* */} -
- - - - {session?.user?.name?.split(" ").map((n) => n[0])}{" "} - - - {session?.user?.name?.split(" ")[0]} -
-
- - {selectedItem && {Subbar}} - -
- ); -} - -const MenuItem = ({ - item: { icon, label, labelDisplay }, - selectedItem, - setSelectedItem, - ...props -}: { - item: MenuItem; - selectedItem: string | null; - setSelectedItem: React.Dispatch>; -}) => { - const handleClick = () => - setSelectedItem((prev) => (prev === label ? null : label)); - - return ( - - ); -}; - -export function SubSidebar({ children }: { children?: React.ReactNode }) { - return ( - - - {children} - - - ); -} -- cgit v1.2.3