11// Playlist list component
22import { Box , Text } from 'ink' ;
3- import { useTheme } from '../../hooks/useTheme.ts' ;
4- import { usePlaylist } from '../../hooks/usePlaylist.ts' ;
3+ import TextInput from 'ink-text-input' ;
4+ import { useCallback , useState } from 'react' ;
5+ import { useNavigation } from '../../hooks/useNavigation.ts' ;
56import { useKeyBinding } from '../../hooks/useKeyboard.ts' ;
7+ import { usePlayer } from '../../hooks/usePlayer.ts' ;
8+ import { usePlaylist } from '../../hooks/usePlaylist.ts' ;
9+ import { useTheme } from '../../hooks/useTheme.ts' ;
10+ import { useKeyboardBlocker } from '../../hooks/useKeyboardBlocker.tsx' ;
611import { KEYBINDINGS } from '../../utils/constants.ts' ;
7- import { useState , useCallback } from 'react' ;
8- import type { Playlist } from '../../types/youtube-music.types.ts' ;
912
1013export default function PlaylistList ( ) {
1114 const { theme} = useTheme ( ) ;
12- const { playlists, createPlaylist} = usePlaylist ( ) ;
15+ const { play, setQueue} = usePlayer ( ) ;
16+ const { dispatch} = useNavigation ( ) ;
17+ const { playlists, createPlaylist, renamePlaylist} = usePlaylist ( ) ;
18+ const [ selectedIndex , setSelectedIndex ] = useState ( 0 ) ;
1319 const [ lastCreated , setLastCreated ] = useState < string | null > ( null ) ;
20+ const [ renamingPlaylistId , setRenamingPlaylistId ] = useState < string | null > (
21+ null ,
22+ ) ;
23+ const [ renameValue , setRenameValue ] = useState ( '' ) ;
24+ useKeyboardBlocker ( renamingPlaylistId !== null ) ;
1425
1526 const handleCreate = useCallback ( ( ) => {
1627 const name = `Playlist ${ playlists . length + 1 } ` ;
1728 createPlaylist ( name ) ;
1829 setLastCreated ( name ) ;
19- } , [ playlists . length , createPlaylist ] ) ;
30+ setSelectedIndex ( playlists . length ) ;
31+ } , [ createPlaylist , playlists . length ] ) ;
32+
33+ const navigateUp = useCallback ( ( ) => {
34+ setSelectedIndex ( prev => Math . max ( 0 , prev - 1 ) ) ;
35+ } , [ ] ) ;
36+
37+ const navigateDown = useCallback ( ( ) => {
38+ setSelectedIndex ( prev =>
39+ Math . min ( playlists . length === 0 ? 0 : playlists . length - 1 , prev + 1 ) ,
40+ ) ;
41+ } , [ playlists . length ] ) ;
2042
43+ const startPlaylist = useCallback ( ( ) => {
44+ if ( renamingPlaylistId ) return ;
45+ const playlist = playlists [ selectedIndex ] ;
46+ if ( ! playlist || playlist . tracks . length === 0 ) return ;
47+ setQueue ( [ ...playlist . tracks ] ) ;
48+ const firstTrack = playlist . tracks [ 0 ] ;
49+ if ( ! firstTrack ) return ;
50+ play ( firstTrack ) ;
51+ } , [ play , playlists , selectedIndex , renamingPlaylistId , setQueue ] ) ;
52+
53+ const handleRename = useCallback ( ( ) => {
54+ const playlist = playlists [ selectedIndex ] ;
55+ if ( ! playlist ) return ;
56+ setRenamingPlaylistId ( playlist . playlistId ) ;
57+ setRenameValue ( playlist . name ) ;
58+ } , [ playlists , selectedIndex ] ) ;
59+
60+ const handleRenameSubmit = useCallback (
61+ ( value : string ) => {
62+ if ( ! renamingPlaylistId ) return ;
63+ const trimmedValue = value . trim ( ) || `Playlist ${ selectedIndex + 1 } ` ;
64+ renamePlaylist ( renamingPlaylistId , trimmedValue ) ;
65+ setRenamingPlaylistId ( null ) ;
66+ setRenameValue ( '' ) ;
67+ } ,
68+ [ renamePlaylist , renamingPlaylistId , selectedIndex ] ,
69+ ) ;
70+
71+ const handleBack = useCallback ( ( ) => {
72+ if ( renamingPlaylistId ) {
73+ setRenamingPlaylistId ( null ) ;
74+ setRenameValue ( '' ) ;
75+ return ;
76+ }
77+ dispatch ( { category : 'GO_BACK' } ) ;
78+ } , [ dispatch , renamingPlaylistId ] ) ;
79+
80+ useKeyBinding ( KEYBINDINGS . UP , navigateUp ) ;
81+ useKeyBinding ( KEYBINDINGS . DOWN , navigateDown ) ;
82+ useKeyBinding ( KEYBINDINGS . SELECT , startPlaylist ) ;
83+ useKeyBinding ( [ 'r' ] , handleRename ) ;
2184 useKeyBinding ( KEYBINDINGS . CREATE_PLAYLIST , handleCreate ) ;
85+ useKeyBinding ( KEYBINDINGS . BACK , handleBack ) ;
2286
2387 return (
2488 < Box flexDirection = "column" gap = { 1 } >
@@ -34,27 +98,65 @@ export default function PlaylistList() {
3498 </ Text >
3599 </ Box >
36100
37- { /* Playlist List */ }
101+ { /* Playlist entries */ }
38102 { playlists . length === 0 ? (
39103 < Text color = { theme . colors . dim } > No playlists yet</ Text >
40104 ) : (
41- playlists . map ( ( playlist : Playlist , index : number ) => (
42- < Box key = { playlist . playlistId || index } paddingX = { 1 } >
43- < Text color = { theme . colors . primary } > { index + 1 } .</ Text >
44- < Text > </ Text >
45- < Text color = { theme . colors . text } > { playlist . name } </ Text >
46- < Text color = { theme . colors . dim } >
47- < Text > </ Text > ({ playlist . tracks ?. length || 0 } tracks)
48- </ Text >
49- </ Box >
50- ) )
105+ playlists . map ( ( playlist , index ) => {
106+ const isSelected = index === selectedIndex ;
107+ const isRenaming =
108+ renamingPlaylistId === playlist . playlistId && isSelected ;
109+ const rowBackground = isSelected ? theme . colors . secondary : undefined ;
110+
111+ return (
112+ < Box
113+ key = { playlist . playlistId }
114+ paddingX = { 1 }
115+ backgroundColor = { rowBackground }
116+ >
117+ < Text
118+ color = {
119+ isSelected ? theme . colors . background : theme . colors . primary
120+ }
121+ bold = { isSelected }
122+ >
123+ { index + 1 } .
124+ </ Text >
125+ < Text > </ Text >
126+ < Box flexDirection = "column" >
127+ { isRenaming ? (
128+ < TextInput
129+ value = { renameValue }
130+ onChange = { setRenameValue }
131+ onSubmit = { handleRenameSubmit }
132+ placeholder = "Playlist name"
133+ focus
134+ />
135+ ) : (
136+ < Text
137+ color = {
138+ isSelected ? theme . colors . background : theme . colors . text
139+ }
140+ bold = { isSelected }
141+ >
142+ { playlist . name }
143+ </ Text >
144+ ) }
145+ < Text color = { theme . colors . dim } >
146+ { ` (${ playlist . tracks . length } tracks)` }
147+ </ Text >
148+ </ Box >
149+ </ Box >
150+ ) ;
151+ } )
51152 ) }
52153
53154 { /* Instructions */ }
54155 < Box marginTop = { 1 } >
55156 < Text color = { theme . colors . dim } >
56- Press < Text color = { theme . colors . text } > c</ Text > to create playlist
57- < Text > | </ Text >
157+ < Text color = { theme . colors . text } > Enter</ Text > to play playlist |{ ' ' }
158+ < Text color = { theme . colors . text } > r</ Text > to rename |{ ' ' }
159+ < Text color = { theme . colors . text } > c</ Text > to create |{ ' ' }
58160 < Text color = { theme . colors . text } > Esc</ Text > to go back
59161 </ Text >
60162 { lastCreated && (
0 commit comments