From ed28943b5481d9355db9c7d4515769f797240d97 Mon Sep 17 00:00:00 2001 From: misterkirill Date: Tue, 15 Jul 2025 23:54:31 +0500 Subject: [PATCH] feat: add line saves --- src/app/actions/blocks.ts | 16 +++++++++++++++- src/app/actions/notes.ts | 10 +++++++++- src/components/editor/Block.tsx | 18 ++++++++++++++++-- src/components/editor/Editor.tsx | 12 +++++++++++- src/lib/hooks/useDebounce.ts | 15 ++++++++++----- 5 files changed, 61 insertions(+), 10 deletions(-) diff --git a/src/app/actions/blocks.ts b/src/app/actions/blocks.ts index cfacb80..72dab66 100644 --- a/src/app/actions/blocks.ts +++ b/src/app/actions/blocks.ts @@ -89,7 +89,6 @@ export async function moveUp(formData: FormData) { const block = await getBlock(blockId); if (!block) return; - // Найти блок, который перед текущим const siblings = await getBlocks(block.noteId); const currentIndex = siblings.findIndex((b) => b.id === block.id); if (currentIndex <= 0) return; @@ -136,3 +135,18 @@ export async function moveDown(formData: FormData) { revalidatePath("/notes/[id]", "page"); } + +export async function setLines(blockId: string, lines: string[]) { + console.log(lines) + await db + .update(blocksTable) + .set({ lines }) + .where(eq(blocksTable.id, blockId)); +} + +export async function setTag(blockId: string, tag: string) { + await db + .update(blocksTable) + .set({ tag }) + .where(eq(blocksTable.id, blockId)); +} diff --git a/src/app/actions/notes.ts b/src/app/actions/notes.ts index be8f6c3..d5d6990 100644 --- a/src/app/actions/notes.ts +++ b/src/app/actions/notes.ts @@ -4,8 +4,8 @@ import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { desc, eq, and } from "drizzle-orm"; import { blocksTable, INote, notesTable, usersTable } from "@/lib/db/schema"; -import { db } from "@/lib/db"; import { requireAuth } from "./auth"; +import { db } from "@/lib/db"; export async function createNote() { const user = await requireAuth(); @@ -45,3 +45,11 @@ export async function deleteNote(formData: FormData) { .where(and(eq(notesTable.id, noteId), eq(notesTable.authorId, user.id))); revalidatePath("/notes"); } + +export async function setTitle(noteId: string, title: string) { + await db + .update(notesTable) + .set({ title }) + .where(eq(notesTable.id, noteId)); + revalidatePath("/notes/[id]", "page"); +} diff --git a/src/components/editor/Block.tsx b/src/components/editor/Block.tsx index ed10b3a..8447212 100644 --- a/src/components/editor/Block.tsx +++ b/src/components/editor/Block.tsx @@ -1,10 +1,24 @@ +import { ChangeEvent, useState } from "react"; import { ArrowDown, ArrowUp, LockOpen, Lock, Minus, Plus, X } from "lucide-react"; -import { addLine, changeLock, deleteBlock, deleteLine, moveDown, moveUp } from "@/app/actions/blocks"; +import { addLine, changeLock, deleteBlock, deleteLine, moveDown, moveUp, setLines } from "@/app/actions/blocks"; +import { useDebounce } from "@/lib/hooks/useDebounce"; import { IBlock } from "@/lib/db/schema"; import IconOnlyButton from "../ui/IconOnlyButton"; import LineInput from "./LineInput"; export default function Block({ block }: { block: IBlock }) { + const [lines, setLinesState] = useState(block.lines); + + useDebounce(() => { + setLines(block.id, lines); + }, [lines]); + + const lineChangeHandler = (i: number, e: ChangeEvent) => { + const newLines = [...lines]; + newLines[i] = e.target.value; + setLinesState(newLines); + } + return (
@@ -16,7 +30,7 @@ export default function Block({ block }: { block: IBlock }) { disabled={block.isLocked} /> {block.lines.map((line, i) => ( - + lineChangeHandler(i, e)} /> ))}
diff --git a/src/components/editor/Editor.tsx b/src/components/editor/Editor.tsx index 143b3b5..5b6bf4b 100644 --- a/src/components/editor/Editor.tsx +++ b/src/components/editor/Editor.tsx @@ -1,11 +1,14 @@ "use client"; +import { useState } from "react"; 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 IconOnlyButton from "../ui/IconOnlyButton"; import Block from "./Block"; +import { useDebounce } from "@/lib/hooks/useDebounce"; +import { setTitle } from "@/app/actions/notes"; const defaultNoteId = uuidv4(); @@ -35,6 +38,8 @@ export default function Editor({ note?: INote; blocks?: IBlock[]; }) { + const [title, setTitleState] = useState(note.title); + const copyHandler = () => { let copyText = ""; blocks.forEach((block) => { @@ -46,11 +51,16 @@ export default function Editor({ navigator.clipboard.writeText(copyText); } + useDebounce(() => { + setTitle(note.id, title); + }, [title]); + return (
setTitleState(e.target.value)} + value={title} /> {blocks.map((block) => )}
diff --git a/src/lib/hooks/useDebounce.ts b/src/lib/hooks/useDebounce.ts index e22b902..363e20f 100644 --- a/src/lib/hooks/useDebounce.ts +++ b/src/lib/hooks/useDebounce.ts @@ -1,10 +1,15 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { useEffect } from "react"; +import { DependencyList, useEffect, useState } from "react"; + +export function useDebounce(callback: () => void, deps: DependencyList) { + const [isFirstTime, setIsFirstTime] = useState(true); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function useDebounce(callback: () => void, deps: any[]) { useEffect(() => { - const timeout = setTimeout(callback, 1000); - return () => clearTimeout(timeout); + if (isFirstTime) { + setIsFirstTime(false); + } else { + const timeout = setTimeout(callback, 1000); + return () => clearTimeout(timeout); + } }, deps); }