diff --git a/components/RightSidebar.tsx b/components/RightSidebar.tsx index 0b6efab..314498f 100644 --- a/components/RightSidebar.tsx +++ b/components/RightSidebar.tsx @@ -32,6 +32,10 @@ export const RightSidebar: React.FC = ({ song, onClose, onOpe const [shareModalOpen, setShareModalOpen] = useState(false); const [copiedStyle, setCopiedStyle] = useState(false); const [copiedLyrics, setCopiedLyrics] = useState(false); + const [isEditingTitle, setIsEditingTitle] = useState(false); + const [titleDraft, setTitleDraft] = useState(''); + const [titleError, setTitleError] = useState(null); + const [isSavingTitle, setIsSavingTitle] = useState(false); useEffect(() => { if (song) { @@ -39,6 +43,57 @@ export const RightSidebar: React.FC = ({ song, onClose, onOpe } }, [song, user]); + useEffect(() => { + if (song) { + setTitleDraft(song.title || ''); + setIsEditingTitle(false); + setTitleError(null); + setIsSavingTitle(false); + } + }, [song?.id]); + + const startTitleEdit = () => { + if (!song || !isOwner) return; + setTitleDraft(song.title || ''); + setTitleError(null); + setIsEditingTitle(true); + }; + + const cancelTitleEdit = () => { + if (!song) return; + setTitleDraft(song.title || ''); + setTitleError(null); + setIsEditingTitle(false); + }; + + const saveTitleEdit = async () => { + if (!song) return; + if (!token) { + setTitleError('Please sign in to rename.'); + return; + } + const trimmed = titleDraft.trim(); + if (!trimmed) { + setTitleError('Title cannot be empty.'); + return; + } + if (trimmed === song.title) { + setIsEditingTitle(false); + return; + } + setIsSavingTitle(true); + setTitleError(null); + try { + await songsApi.updateSong(song.id, { title: trimmed }, token); + onSongUpdate?.({ ...song, title: trimmed }); + setIsEditingTitle(false); + } catch (err) { + const message = err instanceof Error ? err.message : 'Rename failed'; + setTitleError(message); + } finally { + setIsSavingTitle(false); + } + }; if (!song) return (
@@ -111,14 +166,67 @@ export const RightSidebar: React.FC = ({ song, onClose, onOpe
-

onNavigateToSong?.(song.id)} - className="text-2xl font-bold text-zinc-900 dark:text-white leading-tight tracking-tight cursor-pointer hover:underline" - > - {song.title} -

+ {!isEditingTitle ? ( +

onNavigateToSong?.(song.id)} + className="text-2xl font-bold text-zinc-900 dark:text-white leading-tight tracking-tight cursor-pointer hover:underline" + > + {song.title} +

+ ) : ( +
+ setTitleDraft(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + void saveTitleEdit(); + } + if (e.key === 'Escape') { + e.preventDefault(); + cancelTitleEdit(); + } + }} + className="w-full text-xl font-bold text-zinc-900 dark:text-white bg-white dark:bg-black/30 border border-zinc-200 dark:border-white/10 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-pink-500/40" + maxLength={120} + autoFocus + /> +
+ + + {titleError && ( + {titleError} + )} +
+
+ )}
+ {isOwner && !isEditingTitle && ( + + )} -); \ No newline at end of file +); diff --git a/package-lock.json b/package-lock.json index 02ddbbc..30adeaa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,6 +53,7 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1485,6 +1486,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2136,6 +2138,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -2201,6 +2204,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -2536,6 +2540,7 @@ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4",