feat: add title to the editor state

This commit is contained in:
Kirill Siukhin 2025-07-11 00:30:47 +05:00
parent 8b1de14f79
commit 2ad220e525
6 changed files with 155 additions and 88 deletions

23
src/app/actions/blocks.ts Normal file
View File

@ -0,0 +1,23 @@
"use server";
import { eq } from "drizzle-orm";
import { blocksTable } from "@/lib/db/schema";
import { db } from "@/lib/db";
export async function updateBlock({
id,
tag,
lines,
}: {
id: string;
tag?: string;
lines?: string[];
}) {
await db
.update(blocksTable)
.set({
...(tag !== undefined ? { tag } : {}),
...(lines !== undefined ? { lines } : {}),
})
.where(eq(blocksTable.id, id));
}

View File

@ -1,6 +1,6 @@
import Link from "next/link"; import Link from "next/link";
import { CircleQuestionMark, List, Plus, UserRound, UserRoundMinus } from "lucide-react"; import { CircleQuestionMark, List, Plus, UserRound, UserRoundMinus } from "lucide-react";
import { logOut } from "@/app/actions"; import { logOut } from "@/app/actions/auth";
import { getAuth } from "@/lib/auth"; import { getAuth } from "@/lib/auth";
import HeaderButton from "./ui/HeaderButton"; import HeaderButton from "./ui/HeaderButton";

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { useReducer } from "react"; import { ChangeEvent, useReducer } from "react";
import { Copy, Plus } from "lucide-react"; import { Copy, Plus } from "lucide-react";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { editorReducer, IBlock } from "@/lib/editorReducer"; import { editorReducer, IBlock } from "@/lib/editorReducer";
@ -25,7 +25,14 @@ const defaultBlocks = [
]; ];
export default function Editor(props: EditorProps) { export default function Editor(props: EditorProps) {
const [state, dispatch] = useReducer(editorReducer, props.defaultBlocks || defaultBlocks); const [state, dispatch] = useReducer(editorReducer, {
title: props.defaultTitle || "Untitled",
blocks: props.defaultBlocks || defaultBlocks,
});
const handleUpdateTitle = (e: ChangeEvent<HTMLInputElement>) => {
dispatch({ type: "update_title", title: e.target.value });
}
const handleAddBlock = () => { const handleAddBlock = () => {
dispatch({ type: "add_block" }); dispatch({ type: "add_block" });
@ -37,8 +44,8 @@ export default function Editor(props: EditorProps) {
return ( return (
<div className="flex flex-col items-center px-4 max-w-2xl w-full gap-4"> <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={props.defaultTitle || "Untitled"} /> <input className="font-bold text-xl w-full text-center focus:outline-none" defaultValue={state.title} onChange={handleUpdateTitle} />
{state.map((block) => <Block key={block.id} block={block} dispatch={dispatch} /> )} {state.blocks.map((block) => <Block key={block.id} block={block} dispatch={dispatch} /> )}
<div className="flex gap-2"> <div className="flex gap-2">
<IconOnlyButton onClick={handleAddBlock} icon={<Plus size={24} />} /> <IconOnlyButton onClick={handleAddBlock} icon={<Plus size={24} />} />
<IconOnlyButton onClick={handleCopy} icon={<Copy size={24} />} title="Copy note to clipboard" /> <IconOnlyButton onClick={handleCopy} icon={<Copy size={24} />} title="Copy note to clipboard" />

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import { useActionState } from "react"; import { useActionState } from "react";
import { login, register } from "@/app/actions"; import { login, register } from "@/app/actions/auth";
export default function AuthForm({ isRegister = false }: { isRegister?: boolean }) { export default function AuthForm({ isRegister = false }: { isRegister?: boolean }) {
const [state, formAction] = useActionState(isRegister ? register : login, null); const [state, formAction] = useActionState(isRegister ? register : login, null);

View File

@ -12,7 +12,10 @@ export type IBlock = {
lines: ILine[]; lines: ILine[];
}; };
type EditorState = IBlock[]; type EditorState = {
title: string;
blocks: IBlock[];
};
export type Action = export type Action =
| { type: "add_block" } | { type: "add_block" }
@ -20,6 +23,7 @@ export type Action =
| { type: "delete_block", blockId: string } | { type: "delete_block", blockId: string }
| { type: "add_line", blockId: string } | { type: "add_line", blockId: string }
| { type: "delete_line", blockId: string } | { type: "delete_line", blockId: string }
| { type: "update_title", title: string }
| { type: "update_line_text"; blockId: string; lineId: string; text: string } | { type: "update_line_text"; blockId: string; lineId: string; text: string }
| { type: "update_tag"; blockId: string; tag: string } | { type: "update_tag"; blockId: string; tag: string }
| { type: "move_block_up", blockId: string } | { type: "move_block_up", blockId: string }
@ -28,9 +32,17 @@ export type Action =
export function editorReducer(state: EditorState, action: Action): EditorState { export function editorReducer(state: EditorState, action: Action): EditorState {
switch (action.type) { switch (action.type) {
case "add_block": case "update_title":
return [ return {
...state, ...state,
title: action.title,
};
case "add_block":
return {
...state,
blocks: [
...state.blocks,
{ {
id: uuidv4(), id: uuidv4(),
tag: "", tag: "",
@ -40,12 +52,13 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
text: "", text: "",
})), })),
} }
]; ]
};
case "copy": case "copy":
let copyText = ""; let copyText = "";
state.forEach((block) => { state.blocks.forEach((block) => {
if (block.tag !== "") { if (block.tag !== "") {
copyText += `[${block.tag}]\n`; copyText += `[${block.tag}]\n`;
} }
@ -62,10 +75,15 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
return state; return state;
case "delete_block": case "delete_block":
return state.filter((block) => block.id !== action.blockId); return {
...state,
blocks: state.blocks.filter((block) => block.id !== action.blockId),
};
case "add_line": case "add_line":
return state.map((block) => { return {
...state,
blocks: state.blocks.map((block) => {
if (block.id === action.blockId) { if (block.id === action.blockId) {
return { return {
...block, ...block,
@ -74,10 +92,13 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
} else { } else {
return block; return block;
} }
}); }),
};
case "delete_line": case "delete_line":
return state.map((block) => { return {
...state,
blocks: state.blocks.map((block) => {
if (block.id === action.blockId && block.lines.length > 0) { if (block.id === action.blockId && block.lines.length > 0) {
return { return {
...block, ...block,
@ -86,10 +107,13 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
} else { } else {
return block; return block;
} }
}); }),
};
case "update_line_text": case "update_line_text":
return state.map((block) => { return {
...state,
blocks: state.blocks.map((block) => {
if (block.id === action.blockId) { if (block.id === action.blockId) {
return { return {
...block, ...block,
@ -107,10 +131,13 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
} else { } else {
return block; return block;
} }
}); }),
};
case "update_tag": case "update_tag":
return state.map((block) => { return {
...state,
blocks: state.blocks.map((block) => {
if (block.id === action.blockId) { if (block.id === action.blockId) {
return { return {
...block, ...block,
@ -119,26 +146,35 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
} else { } else {
return block; return block;
} }
}); }),
};
case "move_block_up": { case "move_block_up": {
const index = state.findIndex((b) => b.id === action.blockId); const index = state.blocks.findIndex((b) => b.id === action.blockId);
if (index <= 0) return state; if (index <= 0) return state;
const newState = [...state]; const newBlocks = [...state.blocks];
[newState[index - 1], newState[index]] = [newState[index], newState[index - 1]]; [newBlocks[index - 1], newBlocks[index]] = [newBlocks[index], newBlocks[index - 1]];
return newState; return {
...state,
blocks: newBlocks,
};
} }
case "move_block_down": { case "move_block_down": {
const index = state.findIndex((b) => b.id === action.blockId); const index = state.blocks.findIndex((b) => b.id === action.blockId);
if (index === state.length - 1) return state; if (index === state.blocks.length - 1) return state;
const newState = [...state]; const newBlocks = [...state.blocks];
[newState[index], newState[index + 1]] = [newState[index + 1], newState[index]]; [newBlocks[index], newBlocks[index + 1]] = [newBlocks[index + 1], newBlocks[index]];
return newState; return {
...state,
blocks: newBlocks,
};
} }
case "toggle_lock": case "toggle_lock":
return state.map((block) => { return {
...state,
blocks: state.blocks.map((block) => {
if (block.id === action.blockId) { if (block.id === action.blockId) {
return { return {
...block, ...block,
@ -147,7 +183,8 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
} else { } else {
return block; return block;
} }
}); }),
};
default: default:
return state; return state;