Skip to content

Commit

Permalink
feat: all notes page and minor bug fixes and title changes
Browse files Browse the repository at this point in the history
  • Loading branch information
maheshpaulj committed Jan 7, 2025
1 parent f0353d9 commit 63fd3a9
Show file tree
Hide file tree
Showing 25 changed files with 2,972 additions and 60 deletions.
7 changes: 7 additions & 0 deletions app/(main)/allNotes/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Metadata } from "next";
import Page from "./page"; // import your Demo's page

export const metadata: Metadata = {
title: 'NoteScape - All Notes'
};
export default Page;
219 changes: 219 additions & 0 deletions app/(main)/allNotes/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
"use client";

import { useEffect, useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import { Search, Plus, Filter } from "lucide-react";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useCollection } from "react-firebase-hooks/firestore";
import { db } from "@/firebase";
import { query, collectionGroup, where, DocumentData, Timestamp } from "firebase/firestore";
import { useUser } from "@clerk/nextjs";
import { formatDistanceToNow } from 'date-fns';
import { createNewNote } from "@/actions/actions";
import { toast } from "sonner";

interface RoomDocument extends DocumentData {
title: string;
createdAt: Timestamp;
updatedAt: Timestamp;
role: "owner" | "editor";
roomId: string;
userId: string;
parentNoteId: string | null;
archived: boolean;
icon: string;
coverImage: string;
}

export default function NotesPage() {
const router = useRouter();
const { user } = useUser();
const [searchQuery, setSearchQuery] = useState("");
const [sortCriterion, setSortCriterion] = useState<"updatedAt" | "title" | "createdAt">("updatedAt");
const [groupedData, setGroupedData] = useState<{
owner: RoomDocument[];
editor: RoomDocument[];
}>({ owner: [], editor: [] });

const [ isPending, startTransition ] = useTransition();

const [data] = useCollection(
user &&
query(
collectionGroup(db, "rooms"),
where("userId", "==", user.emailAddresses[0].toString())
)
);

useEffect(() => {
if (!data) return;

const sortNotes = (notes: RoomDocument[]) => {
return [...notes].sort((a, b) => {
if (sortCriterion === "title") {
return a.title.localeCompare(b.title);
}
const aDate = sortCriterion === "createdAt" ? a.createdAt.toDate() : a.updatedAt.toDate();
const bDate = sortCriterion === "createdAt" ? b.createdAt.toDate() : b.updatedAt.toDate();
return bDate.getTime() - aDate.getTime(); // Descending order
});
};

const filterNotes = (notes: RoomDocument[]) => {
return notes.filter((note) =>
note.title.toLowerCase().includes(searchQuery.toLowerCase())
);
};

const grouped = data.docs.reduce<{
owner: RoomDocument[];
editor: RoomDocument[];
}>(
(acc, doc) => {
const roomData = doc.data() as RoomDocument;
if (roomData.role === "owner") {
acc.owner.push({
id: doc.id,
...roomData,
});
} else {
acc.editor.push({
id: doc.id,
...roomData,
});
}
return acc;
},
{ owner: [], editor: [] }
);

setGroupedData({
owner: sortNotes(filterNotes(grouped.owner)),
editor: sortNotes(filterNotes(grouped.editor)),
});
}, [data, sortCriterion, searchQuery]);



const onRedirect = (noteId: string) => {
router.push(`/notes/${noteId}`);
};

const renderDocuments = (notes: RoomDocument[], depth = 0) => { // Replace with your timestamp
return notes.map((note) => (
<div
key={note.roomId}
className={`group flex items-center gap-2 w-full p-2 rounded-lg cursor-pointer
hover:bg-accent transition-colors duration-200`}
style={{ paddingLeft: `${depth * 24 + 12}px` }}
onClick={() => onRedirect(note.roomId)}
>
<div className="flex items-center justify-between flex-1 gap-2">
<p>
{note.icon || "📄"}
<span className="truncate">{note.title}</span>
</p>
<p className="text-xs text-muted-foreground">
{formatDistanceToNow(note.updatedAt.toDate(), { addSuffix: true })}
</p>
</div>
</div>
));
};

const handleCreateNewNote = () => {
try {
startTransition(async() => {
const {noteId} = await createNewNote();
router.push(`/notes/${noteId}`);
})
toast.success("New note created");
} catch (error) {
toast.error("failed to create a new note");
console.error(error);
}
}

return (
<div className="h-full flex flex-col">
<Card className="flex-1 border-none shadow-none">
<CardHeader className="space-y-4">
<div className="flex items-center justify-between">
<CardTitle className="text-xl font-medium">All Notes</CardTitle>
<Button onClick={handleCreateNewNote} className="gap-2" disabled={isPending}>
<Plus size={16} />
{isPending ? "Creating New Note" : "New Note" }
</Button>
</div>
<div className="flex items-center gap-2">
<div className="relative flex-1">
<Search
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground"
size={18}
/>
<Input
placeholder="Search notes..."
className="pl-10"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Filter size={18} />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Sort By</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => setSortCriterion("updatedAt")}>
Last Updated
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setSortCriterion("title")}>
Title
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setSortCriterion("createdAt")}>
Created Date
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</CardHeader>
<CardContent>
<div className="space-y-4">
{groupedData.owner.length > 0 && (
<div>
<h2 className="text-lg font-semibold">My Notes</h2>
<div className="space-y-1">{renderDocuments(groupedData.owner)}</div>
</div>
)}
{groupedData.editor.length > 0 && (
<div>
<h2 className="text-lg font-semibold">Shared with Me</h2>
<div className="space-y-1">
{renderDocuments(groupedData.editor)}
</div>
</div>
)}
</div>
</CardContent>
</Card>
</div>
);
}
8 changes: 8 additions & 0 deletions app/(main)/home/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Metadata } from "next";
import Page from "./page"; // import your Demo's page

export const metadata: Metadata = {
title: 'NoteScape - Home'
};

export default Page;
7 changes: 6 additions & 1 deletion app/(main)/notes/[noteId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import RoomProviderWrapper from "@/components/Providers/RoomProviderWrapper";
import { auth } from "@clerk/nextjs/server"
import { auth } from "@clerk/nextjs/server";
import { Metadata } from "next";

export const metadata: Metadata = {
title: 'NoteScape - Note'
};

function NoteLayout({children, params: {noteId}}:{children: React.ReactNode; params: {noteId: string}}) {
auth.protect();
Expand Down
14 changes: 11 additions & 3 deletions app/(main)/notes/[noteId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
"use client";
import Editor from "@/components/Editor"
import { useDocumentData } from "react-firebase-hooks/firestore";
import { doc, DocumentData, DocumentReference } from "firebase/firestore";
import { doc, DocumentData, DocumentReference, Timestamp } from "firebase/firestore";
import { db } from "@/firebase";
import { useUser } from "@clerk/nextjs";
import { Toolbar } from "./_components/Toolbar";
import { Cover } from "./_components/Cover";
import { useEffect } from "react";

interface RoomDocument extends DocumentData {
title: string;
createdAt: string;
updatedAt: string;
createdAt: Timestamp;
updatedAt: Timestamp;
role: "owner" | "editor";
roomId: string;
userId: string;
Expand All @@ -23,6 +24,13 @@ interface RoomDocument extends DocumentData {
function Page({params: {noteId}}: {params: {noteId: string}}) {
const { user } = useUser();
const [data] = useDocumentData<RoomDocument>(doc(db, "users", user?.emailAddresses[0].toString()!, "rooms", noteId) as DocumentReference<RoomDocument>); // eslint-disable-line @typescript-eslint/no-non-null-asserted-optional-chain

useEffect(() => {
if (data?.title) {
document.title = "NoteScape - " + data.title;
}
}, [data]);

return (
<div className="pb-40 mt-14">
<Cover url={data?.coverImage} />
Expand Down
5 changes: 5 additions & 0 deletions app/(root)/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Github, Users, Sparkles, Cpu } from "lucide-react";
import Link from "next/link";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "NoteScape - About",
};

export default function AboutPage() {
const features = [
Expand Down
5 changes: 5 additions & 0 deletions app/(root)/features/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Spotlight } from "@/components/ui/spotlight";
import { Footer } from "../_components/Footer";
import { LucideIcon, NotebookPen, Users, Languages, Image, KeyRound, Cpu } from "lucide-react";
import { Metadata } from "next";

export const metadata: Metadata = {
title: "NoteScape - Features",
};

interface FeatureSectionProps {
title: string;
Expand Down
Binary file modified app/favicon.ico
Binary file not shown.
26 changes: 23 additions & 3 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ body,
}

.dark {
--background: 0 0% 3.9%;
--background: 0 0% 12%;
--foreground: 0 0% 98%;

--card: 0 0% 3.9%;
--card: 0 0% 9%;
--card-foreground: 0 0% 98%;

--popover: 0 0% 3.9%;
--popover: 0 0% 9%;
--popover-foreground: 0 0% 98%;

--primary: 0 0% 98%;
Expand Down Expand Up @@ -83,4 +83,24 @@ body,

.dark .bn-editor {
background-color: #1f1f1f !important;
}

.dark .bn-shadcn {
--background: 0 0% 9% !important;
--foreground: 0 0% 98% !important;
/* --card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%; */
--popover: 0 0% 9% !important;
--popover-foreground: 0 0% 98% !important;
/* --primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%; */
--muted: 0 0% 15% !important;
--muted-foreground: 0 0% 90% !important;
--accent: 0 0% 20% !important;
--accent-foreground: 0 0% 98% !important;
--border: 217.2 32.6% 17.5%;
--input: 0 0% 17.5% !important;
--ring: 0 0% 18% !important;
}
9 changes: 3 additions & 6 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,14 @@ export const viewport: Viewport = {
initialScale: 1,
maximumScale: 1,
colorScheme: "dark light",
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "#ffffff" },
{ media: "(prefers-color-scheme: dark)", color: "#1e1e1e" }
]
}

export const metadata: Metadata = {
title: "NoteScape - AI-Powered Note-Taking Application",
description: "Transform your note-taking experience with NoteScape's AI-powered features including real-time collaboration, smart translation, and context-aware Q&A. Built with Next.js and Meta's Llama model.",
keywords: "note-taking, AI, collaboration, Llama model, real-time, translation, Next.js",
manifest: "/manifest.json",
icons: "logo.png",
openGraph: {
title: "NoteScape - AI-Powered Note-Taking Application",
description: "Transform your note-taking experience with NoteScape's AI-powered features including real-time collaboration, smart translation, and context-aware Q&A.",
Expand All @@ -43,7 +40,7 @@ export const metadata: Metadata = {
siteName: "NoteScape",
images: [
{
url: "/ss.png", // You'll need to add your actual OG image
url: "/assets/ss.png", // You'll need to add your actual OG image
width: 1200,
height: 630,
alt: "NoteScape Preview"
Expand All @@ -54,7 +51,7 @@ export const metadata: Metadata = {
card: "summary_large_image",
title: "NoteScape - AI-Powered Note-Taking Application",
description: "Transform your note-taking experience with NoteScape's AI-powered features including real-time collaboration, smart translation, and context-aware Q&A.",
images: ["/ss.png"], // You'll need to add your actual Twitter card image
images: ["/assets/ss.png"], // You'll need to add your actual Twitter card image
},
robots: {
index: true,
Expand Down
Loading

0 comments on commit 63fd3a9

Please sign in to comment.