fix: performance improvements
This commit is contained in:
parent
0e1ff2b415
commit
bcf443c5e3
@ -8,7 +8,21 @@ import { eq } from "drizzle-orm";
|
||||
import jwt from "jsonwebtoken";
|
||||
import bcrypt from "bcrypt";
|
||||
|
||||
const JWT_SECRET = process.env.JWT_SECRET!;
|
||||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
if (!JWT_SECRET) {
|
||||
throw new Error("JWT_SECRET is not defined");
|
||||
}
|
||||
|
||||
async function setSessionCookie(token: string) {
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.set("session", token, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24 * 7,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
});
|
||||
}
|
||||
|
||||
export async function login(_prevState: unknown, formData: FormData) {
|
||||
const username = formData.get("username") as string;
|
||||
@ -23,22 +37,20 @@ export async function login(_prevState: unknown, formData: FormData) {
|
||||
const users = await db.select()
|
||||
.from(usersTable)
|
||||
.where(eq(usersTable.username, username));
|
||||
|
||||
if (users.length === 0) {
|
||||
return { error: "Invalid password or username!" };
|
||||
} else if (!bcrypt.compareSync(password, users[0].password)) {
|
||||
return { error: "Invalid password or username!" };
|
||||
}
|
||||
|
||||
const token = jwt.sign({ sub: username }, JWT_SECRET, { expiresIn: "7d" });
|
||||
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.set("session", token, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24 * 7,
|
||||
});
|
||||
const user = users[0];
|
||||
|
||||
const passwordValid = await bcrypt.compare(password, user.password);
|
||||
if (!passwordValid) {
|
||||
return { error: "Invalid password or username!" };
|
||||
}
|
||||
|
||||
const token = jwt.sign({ sub: user.id }, JWT_SECRET!, { expiresIn: "7d" });
|
||||
await setSessionCookie(token);
|
||||
|
||||
redirect("/notes");
|
||||
}
|
||||
|
||||
@ -49,9 +61,7 @@ export async function register(_prevState: unknown, formData: FormData) {
|
||||
|
||||
if (password !== passwordConfirm) {
|
||||
return { error: "Passwords do not match!" };
|
||||
}
|
||||
|
||||
if (username.length < 3) {
|
||||
} else if (username.length < 3) {
|
||||
return { error: "Username is too short!" };
|
||||
} else if (password.length < 8) {
|
||||
return { error: "Password is too short!" };
|
||||
@ -61,23 +71,20 @@ export async function register(_prevState: unknown, formData: FormData) {
|
||||
.from(usersTable)
|
||||
.where(eq(usersTable.username, username));
|
||||
|
||||
if (users.length !== 0) {
|
||||
if (users.length > 0) {
|
||||
return { error: "Username is already taken!" };
|
||||
}
|
||||
|
||||
await db.insert(usersTable).values({
|
||||
username,
|
||||
password: bcrypt.hashSync(password, bcrypt.genSaltSync()),
|
||||
});
|
||||
const [createdUser] = await db
|
||||
.insert(usersTable)
|
||||
.values({
|
||||
username,
|
||||
password: await bcrypt.hash(password, await bcrypt.genSalt()),
|
||||
})
|
||||
.returning({ id: usersTable.id });
|
||||
|
||||
const token = jwt.sign({ sub: username }, JWT_SECRET, { expiresIn: "7d" });
|
||||
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.set("session", token, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
maxAge: 60 * 60 * 24 * 7,
|
||||
});
|
||||
const token = jwt.sign({ sub: createdUser.id }, JWT_SECRET!, { expiresIn: "7d" });
|
||||
await setSessionCookie(token);
|
||||
|
||||
redirect("/notes");
|
||||
}
|
||||
@ -96,15 +103,15 @@ export async function getAuth() {
|
||||
}
|
||||
|
||||
try {
|
||||
const decodedToken = jwt.verify(token, JWT_SECRET) as jwt.JwtPayload;
|
||||
const username = decodedToken.sub;
|
||||
if (!username) {
|
||||
const decodedToken = jwt.verify(token, JWT_SECRET!) as jwt.JwtPayload;
|
||||
const userId = decodedToken.sub;
|
||||
if (!userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const users = await db.select()
|
||||
.from(usersTable)
|
||||
.where(eq(usersTable.username, username));
|
||||
.where(eq(usersTable.id, userId));
|
||||
if (users.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1,29 +1,50 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { notFound } from "next/navigation";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "@/lib/db";
|
||||
import { blocksTable, IBlock, notesTable } from "@/lib/db/schema";
|
||||
import { requireAuth } from "./auth";
|
||||
import { assertNoteOwner } from "./notes";
|
||||
|
||||
export async function assertBlockOwner(blockId: string): Promise<boolean> {
|
||||
const user = await requireAuth();
|
||||
type Transaction = Parameters<Parameters<typeof db.transaction>[0]>[0];
|
||||
|
||||
export async function assertBlockOwner(blockId: string, authorId: string) {
|
||||
const block = await db.query.blocksTable.findFirst({
|
||||
where: eq(blocksTable.id, blockId),
|
||||
with: { note: true },
|
||||
});
|
||||
if (!block) return false;
|
||||
if (!block || block.note.authorId !== authorId) {
|
||||
notFound();
|
||||
}
|
||||
}
|
||||
|
||||
return block.note.authorId === user.id;
|
||||
async function updateLastEdited(tx: Transaction, noteId: string) {
|
||||
await tx
|
||||
.update(notesTable)
|
||||
.set({ lastEdited: new Date() })
|
||||
.where(eq(notesTable.id, noteId));
|
||||
}
|
||||
|
||||
export async function getBlockIfOwned(blockId: string): Promise<IBlock> {
|
||||
const user = await requireAuth();
|
||||
const block = await db.query.blocksTable.findFirst({
|
||||
where: eq(blocksTable.id, blockId),
|
||||
with: { note: true },
|
||||
});
|
||||
if (!block || block.note.authorId !== user.id) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
export async function createBlock(formData: FormData) {
|
||||
const noteId = formData.get("noteId") as string;
|
||||
|
||||
const isAllowed = await assertNoteOwner(noteId);
|
||||
if (!isAllowed) return;
|
||||
const user = await requireAuth();
|
||||
await assertNoteOwner(noteId, user.id);
|
||||
|
||||
const blocks = await getBlocks(noteId);
|
||||
const lastBlock = blocks.pop();
|
||||
@ -33,30 +54,16 @@ export async function createBlock(formData: FormData) {
|
||||
await tx
|
||||
.insert(blocksTable)
|
||||
.values({ noteId, order });
|
||||
await tx
|
||||
.update(notesTable)
|
||||
.set({ lastEdited: new Date() })
|
||||
.where(eq(notesTable.id, noteId));
|
||||
await updateLastEdited(tx, noteId);
|
||||
});
|
||||
|
||||
revalidatePath("/notes/[id]", "page");
|
||||
}
|
||||
|
||||
async function getBlock(blockId: string): Promise<IBlock | null> {
|
||||
const isAllowed = await assertBlockOwner(blockId);
|
||||
if (!isAllowed) return null;
|
||||
|
||||
const blocks = await db
|
||||
.select()
|
||||
.from(blocksTable)
|
||||
.where(eq(blocksTable.id, blockId));
|
||||
|
||||
return blocks.length === 0 ? null : blocks[0];
|
||||
}
|
||||
|
||||
export async function getBlocks(noteId: string): Promise<IBlock[]> {
|
||||
const isAllowed = await assertNoteOwner(noteId);
|
||||
if (!isAllowed) return [];
|
||||
const user = await requireAuth();
|
||||
await assertNoteOwner(noteId, user.id);
|
||||
|
||||
return db
|
||||
.select()
|
||||
.from(blocksTable)
|
||||
@ -66,21 +73,20 @@ export async function getBlocks(noteId: string): Promise<IBlock[]> {
|
||||
|
||||
export async function deleteBlock(formData: FormData) {
|
||||
const blockId = formData.get("blockId") as string;
|
||||
|
||||
const user = await requireAuth();
|
||||
await assertBlockOwner(blockId, user.id);
|
||||
|
||||
const isAllowed = await assertBlockOwner(blockId);
|
||||
if (!isAllowed) return;
|
||||
|
||||
const block = await getBlock(blockId);
|
||||
if (!block) return;
|
||||
const block = await getBlockIfOwned(blockId);
|
||||
if (!block) {
|
||||
return;
|
||||
};
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.delete(blocksTable)
|
||||
.where(eq(blocksTable.id, blockId));
|
||||
await tx
|
||||
.update(notesTable)
|
||||
.set({ lastEdited: new Date() })
|
||||
.where(eq(notesTable.id, block.noteId));
|
||||
await updateLastEdited(tx, block.noteId);
|
||||
});
|
||||
|
||||
revalidatePath("/notes/[id]", "page");
|
||||
@ -88,23 +94,22 @@ export async function deleteBlock(formData: FormData) {
|
||||
|
||||
export async function changeLock(formData: FormData) {
|
||||
const blockId = formData.get("blockId") as string;
|
||||
const isLocked = formData.get("isLocked") === null ? false : true;
|
||||
const isLocked = formData.get("isLocked") === "true";
|
||||
|
||||
const isAllowed = await assertBlockOwner(blockId);
|
||||
if (!isAllowed) return;
|
||||
const user = await requireAuth();
|
||||
await assertBlockOwner(blockId, user.id);
|
||||
|
||||
const block = await getBlock(blockId);
|
||||
if (!block) return;
|
||||
const block = await getBlockIfOwned(blockId);
|
||||
if (!block) {
|
||||
return
|
||||
}
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(blocksTable)
|
||||
.set({ isLocked })
|
||||
.where(eq(blocksTable.id, blockId));
|
||||
await tx
|
||||
.update(notesTable)
|
||||
.set({ lastEdited: new Date() })
|
||||
.where(eq(notesTable.id, block.noteId));
|
||||
await updateLastEdited(tx, block.noteId);
|
||||
});
|
||||
|
||||
revalidatePath("/notes/[id]", "page");
|
||||
@ -113,21 +118,20 @@ export async function changeLock(formData: FormData) {
|
||||
export async function addLine(formData: FormData) {
|
||||
const blockId = formData.get("blockId") as string;
|
||||
|
||||
const isAllowed = await assertBlockOwner(blockId);
|
||||
if (!isAllowed) return;
|
||||
const user = await requireAuth();
|
||||
await assertBlockOwner(blockId, user.id);
|
||||
|
||||
const block = await getBlock(blockId);
|
||||
if (!block) return;
|
||||
const block = await getBlockIfOwned(blockId);
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(blocksTable)
|
||||
.set({ lines: [...block.lines, ""] })
|
||||
.where(eq(blocksTable.id, blockId));
|
||||
await tx
|
||||
.update(notesTable)
|
||||
.set({ lastEdited: new Date() })
|
||||
.where(eq(notesTable.id, block.noteId));
|
||||
await updateLastEdited(tx, block.noteId);
|
||||
});
|
||||
|
||||
revalidatePath("/notes/[id]", "page");
|
||||
@ -136,21 +140,20 @@ export async function addLine(formData: FormData) {
|
||||
export async function deleteLine(formData: FormData) {
|
||||
const blockId = formData.get("blockId") as string;
|
||||
|
||||
const isAllowed = await assertBlockOwner(blockId);
|
||||
if (!isAllowed) return;
|
||||
const user = await requireAuth();
|
||||
await assertBlockOwner(blockId, user.id);
|
||||
|
||||
const block = await getBlock(blockId);
|
||||
if (!block) return;
|
||||
const block = await getBlockIfOwned(blockId);
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(blocksTable)
|
||||
.set({ lines: block.lines.slice(0, -1) })
|
||||
.where(eq(blocksTable.id, blockId));
|
||||
await tx
|
||||
.update(notesTable)
|
||||
.set({ lastEdited: new Date() })
|
||||
.where(eq(notesTable.id, block.noteId));
|
||||
await updateLastEdited(tx, block.noteId);
|
||||
});
|
||||
|
||||
revalidatePath("/notes/[id]", "page");
|
||||
@ -158,18 +161,25 @@ export async function deleteLine(formData: FormData) {
|
||||
|
||||
export async function moveUp(formData: FormData) {
|
||||
const blockId = formData.get("blockId") as string;
|
||||
const isAllowed = await assertBlockOwner(blockId);
|
||||
if (!isAllowed) return;
|
||||
|
||||
const user = await requireAuth();
|
||||
await assertBlockOwner(blockId, user.id);
|
||||
|
||||
const block = await getBlock(blockId);
|
||||
if (!block) return;
|
||||
const block = await getBlockIfOwned(blockId);
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
const siblings = await getBlocks(block.noteId);
|
||||
const currentIndex = siblings.findIndex((b) => b.id === block.id);
|
||||
if (currentIndex <= 0) return;
|
||||
if (currentIndex <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prevBlock = siblings[currentIndex - 1];
|
||||
if (!prevBlock) return;
|
||||
if (!prevBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
@ -180,10 +190,7 @@ export async function moveUp(formData: FormData) {
|
||||
.update(blocksTable)
|
||||
.set({ order: block.order })
|
||||
.where(eq(blocksTable.id, prevBlock.id));
|
||||
await tx
|
||||
.update(notesTable)
|
||||
.set({ lastEdited: new Date() })
|
||||
.where(eq(notesTable.id, block.noteId));
|
||||
await updateLastEdited(tx, block.noteId);
|
||||
});
|
||||
|
||||
revalidatePath("/notes/[id]", "page");
|
||||
@ -191,18 +198,25 @@ export async function moveUp(formData: FormData) {
|
||||
|
||||
export async function moveDown(formData: FormData) {
|
||||
const blockId = formData.get("blockId") as string;
|
||||
const isAllowed = await assertBlockOwner(blockId);
|
||||
if (!isAllowed) return;
|
||||
|
||||
const block = await getBlock(blockId);
|
||||
if (!block) return;
|
||||
const user = await requireAuth();
|
||||
await assertBlockOwner(blockId, user.id);
|
||||
|
||||
const block = await getBlockIfOwned(blockId);
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
const siblings = await getBlocks(block.noteId);
|
||||
const currentIndex = siblings.findIndex((b) => b.id === block.id);
|
||||
if (currentIndex === -1 || currentIndex >= siblings.length - 1) return;
|
||||
if (currentIndex === -1 || currentIndex >= siblings.length - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextBlock = siblings[currentIndex + 1];
|
||||
if (!nextBlock) return;
|
||||
if (!nextBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
@ -213,49 +227,44 @@ export async function moveDown(formData: FormData) {
|
||||
.update(blocksTable)
|
||||
.set({ order: block.order })
|
||||
.where(eq(blocksTable.id, nextBlock.id));
|
||||
await tx
|
||||
.update(notesTable)
|
||||
.set({ lastEdited: new Date() })
|
||||
.where(eq(notesTable.id, block.noteId));
|
||||
await updateLastEdited(tx, block.noteId);
|
||||
});
|
||||
|
||||
revalidatePath("/notes/[id]", "page");
|
||||
}
|
||||
|
||||
export async function setLines(blockId: string, lines: string[]) {
|
||||
const isAllowed = await assertBlockOwner(blockId);
|
||||
if (!isAllowed) return;
|
||||
const user = await requireAuth();
|
||||
await assertBlockOwner(blockId, user.id);
|
||||
|
||||
const block = await getBlock(blockId);
|
||||
if (!block) return;
|
||||
const block = await getBlockIfOwned(blockId);
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(blocksTable)
|
||||
.set({ lines })
|
||||
.where(eq(blocksTable.id, blockId));
|
||||
await tx
|
||||
.update(notesTable)
|
||||
.set({ lastEdited: new Date() })
|
||||
.where(eq(notesTable.id, block.noteId));
|
||||
await updateLastEdited(tx, block.noteId);
|
||||
});
|
||||
}
|
||||
|
||||
export async function setTag(blockId: string, tag: string) {
|
||||
const isAllowed = await assertBlockOwner(blockId);
|
||||
if (!isAllowed) return;
|
||||
const user = await requireAuth();
|
||||
await assertBlockOwner(blockId, user.id);
|
||||
|
||||
const block = await getBlock(blockId);
|
||||
if (!block) return;
|
||||
const block = await getBlockIfOwned(blockId);
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
await db.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(blocksTable)
|
||||
.set({ tag })
|
||||
.where(eq(blocksTable.id, blockId));
|
||||
await tx
|
||||
.update(notesTable)
|
||||
.set({ lastEdited: new Date() })
|
||||
.where(eq(notesTable.id, block.noteId));
|
||||
await updateLastEdited(tx, block.noteId);
|
||||
});
|
||||
}
|
||||
|
@ -1,42 +1,43 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { desc, eq, and } from "drizzle-orm";
|
||||
import { blocksTable, INote, notesTable, usersTable } from "@/lib/db/schema";
|
||||
import { requireAuth } from "./auth";
|
||||
import { db } from "@/lib/db";
|
||||
|
||||
export async function assertNoteOwner(noteId: string): Promise<boolean> {
|
||||
export async function assertNoteOwner(noteId: string, userId: string) {
|
||||
const note = await db.query.notesTable.findFirst({
|
||||
where: eq(notesTable.id, noteId),
|
||||
});
|
||||
if (!note || note.authorId !== userId) {
|
||||
notFound();
|
||||
}
|
||||
}
|
||||
|
||||
export async function getNoteIfOwned(noteId: string): Promise<INote> {
|
||||
const user = await requireAuth();
|
||||
|
||||
const note = await db.query.notesTable.findFirst({
|
||||
where: eq(notesTable.id, noteId),
|
||||
});
|
||||
if (!note) return false;
|
||||
|
||||
return note.authorId === user.id;
|
||||
if (!note || note.authorId !== user.id) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return note;
|
||||
}
|
||||
|
||||
export async function createNote() {
|
||||
const user = await requireAuth();
|
||||
const result = await db
|
||||
const [note] = await db
|
||||
.insert(notesTable)
|
||||
.values({ authorId: user.id })
|
||||
.returning({ id: usersTable.id });
|
||||
const noteId = result[0].id;
|
||||
|
||||
await db.insert(blocksTable).values({ noteId, order: 1 });
|
||||
redirect(`/notes/${noteId}`);
|
||||
}
|
||||
|
||||
export async function getNote(noteId: string): Promise<INote | null> {
|
||||
const user = await requireAuth();
|
||||
const note = await db
|
||||
.select()
|
||||
.from(notesTable)
|
||||
.where(and(eq(notesTable.id, noteId), eq(notesTable.authorId, user.id)));
|
||||
return note.length > 0 ? note[0] : null;
|
||||
await db.insert(blocksTable).values({ noteId: note.id, order: 1 });
|
||||
redirect(`/notes/${note.id}`);
|
||||
}
|
||||
|
||||
export async function getNotes(): Promise<INote[]> {
|
||||
@ -49,19 +50,25 @@ export async function getNotes(): Promise<INote[]> {
|
||||
}
|
||||
|
||||
export async function deleteNote(formData: FormData) {
|
||||
const user = await requireAuth();
|
||||
const noteId = formData.get("noteId") as string;
|
||||
|
||||
const user = await requireAuth();
|
||||
await assertNoteOwner(noteId, user.id);
|
||||
|
||||
await db
|
||||
.delete(notesTable)
|
||||
.where(and(eq(notesTable.id, noteId), eq(notesTable.authorId, user.id)));
|
||||
|
||||
revalidatePath("/notes");
|
||||
}
|
||||
|
||||
export async function setTitle(noteId: string, title: string) {
|
||||
if (title === "") return;
|
||||
|
||||
const isAllowed = await assertNoteOwner(noteId);
|
||||
if (!isAllowed) return;
|
||||
if (title === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await requireAuth();
|
||||
await assertNoteOwner(noteId, user.id);
|
||||
|
||||
await db
|
||||
.update(notesTable)
|
||||
|
@ -1,24 +1,17 @@
|
||||
import { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import { getNote } from "@/app/actions/notes";
|
||||
import { getNoteIfOwned } from "@/app/actions/notes";
|
||||
import { getBlocks } from "@/app/actions/blocks";
|
||||
import Editor from "@/components/editor/Editor";
|
||||
|
||||
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
|
||||
const { id } = await params;
|
||||
const note = await getNote(id);
|
||||
if (!note) {
|
||||
notFound();
|
||||
}
|
||||
const note = await getNoteIfOwned(id);
|
||||
return { title: note.title };
|
||||
}
|
||||
|
||||
export default async function Note({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
const note = await getNote(id);
|
||||
if (!note) {
|
||||
notFound();
|
||||
}
|
||||
const note = await getNoteIfOwned(id);
|
||||
const blocks = await getBlocks(note.id);
|
||||
|
||||
return (
|
||||
|
@ -67,7 +67,7 @@ export default function Block({ block }: { block: IBlock }) {
|
||||
<div className="flex gap-2 ml-auto">
|
||||
<form action={changeLock}>
|
||||
<input type="hidden" name="blockId" value={block.id} />
|
||||
{!block.isLocked && <input type="hidden" name="isLocked" />}
|
||||
{!block.isLocked && <input type="hidden" name="isLocked" value="true" />}
|
||||
<IconOnlyButton
|
||||
type="submit"
|
||||
alwaysOn={block.isLocked}
|
||||
|
@ -5,10 +5,10 @@ import { Copy, Plus } from "lucide-react";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { IBlock, INote } from "@/lib/db/schema";
|
||||
import { createBlock } from "@/app/actions/blocks";
|
||||
import { setTitle } from "@/app/actions/notes";
|
||||
import { useDebounce } from "@/lib/hooks/useDebounce";
|
||||
import IconOnlyButton from "../ui/IconOnlyButton";
|
||||
import Block from "./Block";
|
||||
import { useDebounce } from "@/lib/hooks/useDebounce";
|
||||
import { setTitle } from "@/app/actions/notes";
|
||||
|
||||
const defaultNoteId = uuidv4();
|
||||
|
||||
|
@ -2,6 +2,10 @@ import { InputHTMLAttributes } from "react";
|
||||
|
||||
export default function LineInput(props: InputHTMLAttributes<HTMLInputElement>) {
|
||||
return (
|
||||
<input type="text" className="border-b-2 border-b-neutral-600 focus:border-b-neutral-500 bg-neutral-800/30 p-2 my-2 rounded-t-md text-lg focus:outline-none font-mono w-full" {...props} />
|
||||
<input
|
||||
type="text"
|
||||
className="border-b-2 border-b-neutral-600 focus:border-b-neutral-500 bg-neutral-800/30 p-2 my-2 rounded-t-md text-lg focus:outline-none font-mono w-full"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { DependencyList, useEffect, useState } from "react";
|
||||
import { DependencyList, useEffect, useRef } from "react";
|
||||
|
||||
export function useDebounce(callback: () => void, deps: DependencyList) {
|
||||
const [isFirstTime, setIsFirstTime] = useState(true);
|
||||
const isFirstRun = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (isFirstTime) {
|
||||
setIsFirstTime(false);
|
||||
if (isFirstRun.current) {
|
||||
isFirstRun.current = false;
|
||||
} else {
|
||||
const timeout = setTimeout(callback, 1000);
|
||||
return () => clearTimeout(timeout);
|
||||
|
Loading…
x
Reference in New Issue
Block a user