feat: add copy button
This commit is contained in:
parent
b3ead435cf
commit
8aaf8bb42d
@ -1,14 +0,0 @@
|
||||
import Header from "@/components/Header";
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<>
|
||||
<Header allowExport />
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import Header from "@/components/Header";
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<div className="m-4">
|
||||
{children}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { Metadata } from "next";
|
||||
import { Noto_Sans_Mono } from "next/font/google";
|
||||
import Header from "@/components/Header";
|
||||
import "./globals.css";
|
||||
|
||||
const notoSansMono = Noto_Sans_Mono({
|
||||
@ -20,7 +21,10 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${notoSansMono.variable} font-mono antialiased`}>
|
||||
{children}
|
||||
<Header />
|
||||
<div className="m-4">
|
||||
{children}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { Metadata } from "next";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getNotes } from "@/lib/notes";
|
||||
import { getAuth } from "@/lib/auth";
|
||||
import NoteCard from "@/components/ui/NoteCard";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@ -8,11 +10,16 @@ export const metadata: Metadata = {
|
||||
};
|
||||
|
||||
export default async function Notes() {
|
||||
const notes = await getNotes();
|
||||
const auth = await getAuth();
|
||||
if (!auth) {
|
||||
redirect("/auth");
|
||||
}
|
||||
|
||||
const notes = await getNotes(auth.id);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="font-bold text-xl mb-4">Notes</h1>
|
||||
<h1 className="font-bold text-xl mb-4">Notes of {auth.username}:</h1>
|
||||
{notes ? (
|
||||
notes.length > 0 ? (
|
||||
notes.map((note) => <NoteCard key={note.id} note={note} />)
|
@ -1,10 +1,10 @@
|
||||
import Link from "next/link";
|
||||
import { ArrowRightFromLine, CircleQuestionMark, List, Plus, UserRound, UserRoundMinus } from "lucide-react";
|
||||
import { CircleQuestionMark, List, Plus, UserRound, UserRoundMinus } from "lucide-react";
|
||||
import { logOut } from "@/app/actions";
|
||||
import { getAuth } from "@/lib/auth";
|
||||
import HeaderButton from "./ui/HeaderButton";
|
||||
|
||||
export default async function Header({ allowExport = false }: { allowExport?: boolean }) {
|
||||
export default async function Header() {
|
||||
const auth = await getAuth();
|
||||
|
||||
return (
|
||||
@ -13,19 +13,16 @@ export default async function Header({ allowExport = false }: { allowExport?: bo
|
||||
<HeaderButton title="rhyme" className="bg-sky-600 hover:bg-sky-500" />
|
||||
</Link>
|
||||
|
||||
<div className="flex gap-2">
|
||||
{auth && (
|
||||
<>
|
||||
{auth && (
|
||||
<div className="flex gap-2">
|
||||
<Link href="/notes/new">
|
||||
<HeaderButton title="new" icon={<Plus size={20} />} />
|
||||
</Link>
|
||||
<Link href="/notes">
|
||||
<HeaderButton title="list" icon={<List size={20} />} />
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
{allowExport && <HeaderButton title="export" icon={<ArrowRightFromLine size={20} />} />}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-2 ml-auto">
|
||||
<Link href="/about">
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useReducer } from "react";
|
||||
import { Plus } from "lucide-react";
|
||||
import { Copy, Plus } from "lucide-react";
|
||||
import { editorReducer } from "@/lib/editorReducer";
|
||||
import IconOnlyButton from "../ui/IconOnlyButton";
|
||||
import Block from "./Block";
|
||||
@ -23,11 +23,18 @@ export default function Editor() {
|
||||
dispatch({ type: "add_block" });
|
||||
}
|
||||
|
||||
const handleCopy = () => {
|
||||
dispatch({ type: "copy" });
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center px-4 max-w-2xl w-full gap-4">
|
||||
<input className="font-bold text-xl w-full text-center focus:outline-none" defaultValue="Untitled" />
|
||||
{state.map((block) => <Block key={block.id} block={block} dispatch={dispatch} /> )}
|
||||
<IconOnlyButton onClick={handleAddBlock} icon={<Plus size={24} />} />
|
||||
<div className="flex gap-2">
|
||||
<IconOnlyButton onClick={handleAddBlock} icon={<Plus size={24} />} />
|
||||
<IconOnlyButton onClick={handleCopy} icon={<Copy size={24} />} title="Copy note to clipboard" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -5,6 +5,15 @@ import { notesTable } from "@/lib/db/schema";
|
||||
import { deleteNote } from "@/lib/notes";
|
||||
import IconOnlyButton from "./IconOnlyButton";
|
||||
|
||||
function makeTimestamp(date: Date) {
|
||||
const day = date.getDate();
|
||||
const month = date.getMonth() + 1;
|
||||
const year = date.getFullYear();
|
||||
const hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
return `${day}.${month}.${year} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
export default function NoteCard({ note }: { note: typeof notesTable.$inferSelect }) {
|
||||
const handleDeleteNote = async () => {
|
||||
"use server";
|
||||
@ -13,11 +22,11 @@ export default function NoteCard({ note }: { note: typeof notesTable.$inferSelec
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center mb-2 gap-4">
|
||||
<Link href={`/notes/${note.id}`} className="flex flex-col border-2 border-neutral-700 py-3 px-4 rounded-lg hover:bg-neutral-800 w-full">
|
||||
<div className="flex items-center mb-3 gap-4">
|
||||
<Link href={`/notes/${note.id}`} className="flex flex-col border border-neutral-500 py-3 px-4 rounded-lg hover:bg-neutral-800 w-full">
|
||||
<h1 className="font-bold">{note.name}</h1>
|
||||
<i className="text-neutral-400 text-sm">Creation date: {note.creationTime.toString()}</i>
|
||||
<i className="text-neutral-400 text-sm">Last time edited: {note.lastEdited.toString()}</i>
|
||||
<i className="text-neutral-400 text-sm">Last time edited: {makeTimestamp(note.lastEdited)}</i>
|
||||
<i className="text-neutral-400 text-sm">Creation date: {makeTimestamp(note.creationTime)}</i>
|
||||
</Link>
|
||||
<IconOnlyButton icon={<X size={24} />} onClick={handleDeleteNote} />
|
||||
</div>
|
||||
|
@ -14,6 +14,7 @@ type EditorState = IBlock[];
|
||||
|
||||
export type Action =
|
||||
| { type: "add_block" }
|
||||
| { type: "copy" }
|
||||
| { type: "delete_block", blockId: string }
|
||||
| { type: "add_line", blockId: string }
|
||||
| { type: "delete_line", blockId: string }
|
||||
@ -36,6 +37,21 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
|
||||
})),
|
||||
}
|
||||
];
|
||||
|
||||
case "copy":
|
||||
let copyText = "";
|
||||
|
||||
state.forEach((block) => {
|
||||
if (block.tag !== "") {
|
||||
copyText += `[${block.tag}]\n`;
|
||||
}
|
||||
|
||||
block.lines.forEach((line) => copyText += line.text + "\n")
|
||||
copyText += "\n";
|
||||
});
|
||||
|
||||
navigator.clipboard.writeText(copyText);
|
||||
return state;
|
||||
|
||||
case "delete_block":
|
||||
return state.filter((block) => block.id !== action.blockId);
|
||||
|
@ -1,17 +1,13 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { desc, eq } from "drizzle-orm";
|
||||
import { notesTable } from "./db/schema";
|
||||
import { getAuth } from "./auth";
|
||||
import { db } from "./db";
|
||||
|
||||
export async function getNotes() {
|
||||
const auth = await getAuth();
|
||||
if (!auth) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function getNotes(authorId: string) {
|
||||
return db.select()
|
||||
.from(notesTable)
|
||||
.where(eq(notesTable.authorId, auth.id));
|
||||
.where(eq(notesTable.authorId, authorId))
|
||||
.orderBy(desc(notesTable.lastEdited));
|
||||
}
|
||||
|
||||
export async function createNote() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user