{-#LANGUAGE RankNTypes#-} -- | module Hum.Views.Playlists where import Hum.Types import Brick.Types import Graphics.Vty.Input.Events import Brick.Main import Brick.Widgets.Core import Brick.Widgets.Center import Brick.Widgets.Border import Brick.Widgets.Border.Style import Control.Lens import Brick.Widgets.List import Hum.Attributes import Hum.Views.Common import Hum.Rebuild import qualified Data.Text as T import Network.MPD ( withMPD ) import qualified Network.MPD as MPD import qualified Data.Map.Strict as Map import Hum.Utils drawPlaylistLeft :: HState -> Widget Name drawPlaylistLeft st = let vsize = case join $ Map.lookup PlaylistLeft $ extentMap st of Just e -> snd . extentSize $ e Nothing -> 20 in reportExtent PlaylistLeft $ hCenter ( viewport PlaylistLeft Vertical . visible . vLimit vsize . center $ hBorder <=> hCenter (renderList (const $ playlistRow st) ((focPlay . focus $ st) == FocPlaylists) (MPD.toText <$> st ^. playlistsL . plListL) ) ) drawPlaylistRight :: HState -> Widget Name drawPlaylistRight st = let vsize = case join $ Map.lookup PlaylistRight $ extentMap st of Just e -> snd . extentSize $ e Nothing -> 20 in reportExtent PlaylistRight $ hCenter ( viewport PlaylistRight Vertical . visible . vLimit vsize . center $ (if st ^. editableL then withDefAttr listHighlightedAttr . withBorderStyle (borderStyleFromChar '=') $ hBorderWithLabel $ txt "editing" else hBorder ) <=> hCenter (renderList (const $ playlistSongRow st) ((focPlay . focus $ st) == FocPSongs) (st ^. playlistsL . plSongsL) ) ) playlistRow :: HState -> T.Text -> Widget n playlistRow _ val = withAttr queueAlbumAttr $ column Nothing (Pad 1) Max $ txt val playlistSongRow :: HState -> (MPD.Song,Highlight) -> Widget n playlistSongRow st (song,hl) = let pathsInQueue = (MPD.sgFilePath <$>) . (fst <$>) . listElements . queue $ st in (if hl then highlightOverQueueAttrs else id) . withAttr (if MPD.sgFilePath song `elem` pathsInQueue then queueTitleBoldAttr else queueTitleAttr ) $ column Nothing (Pad 1) Max $ txt (meta (MPD.toText . MPD.sgFilePath $ song) MPD.Title song) drawViewPlaylists :: HState -> Widget Name drawViewPlaylists st = hLimitPercent 25 (drawPlaylistLeft st) <+> drawPlaylistRight st playlistsMove :: (forall e . List Name e -> List Name e) -> HState -> EventM Name HState playlistsMove moveFunc s = let playfoc = s ^. focusL . focPlayL in case playfoc of FocPlaylists -> rebuildPlList $ s & playlistsL . plListL %~ moveFunc FocPSongs -> do pure $ s & playlistsL . plSongsL %~ moveFunc playlistsAddtoQ :: Bool -> HState -> EventM Name HState playlistsAddtoQ play s = let playfoc = s ^. focusL . focPlayL in case playfoc of FocPlaylists -> do songBulkAddtoQ play (fst <$> listElements (s ^. playlistsL . plSongsL)) s FocPSongs -> do let maybeFilePath = MPD.sgFilePath . fst . snd <$> listSelectedElement (s ^. playlistsL . plSongsL) traverse_ (\sel -> liftIO (withMPD $ MPD.addId sel Nothing >>= if play then MPD.playId else const pass ) ) maybeFilePath song <- liftIO (withMPD MPD.currentSong) pure s { currentSong = fromRight Nothing song, queue = queue s } playlistsSearch :: Bool -> HState -> EventM Name HState playlistsSearch direction s = let playfoc = s ^. focusL . focPlayL dir = if direction then id else listReverse searchkey = fromMaybe "" ((s ^. exL . searchHistoryL) !!? 0) in if searchkey == "" then pure s else case playfoc of FocPlaylists -> do extentMap <- updateExtentMap rebuildPlList $ (s { extentMap }) & playlistsL . plListL %~ (dir . listFindBy (stringySearch searchkey) . dir) FocPSongs -> do extentMap <- updateExtentMap pure $ s { extentMap } & playlistsL . plSongsL %~ (dir . listFindBy (songSearch searchkey [MPD.Title] . fst) . dir) handleEventPlaylists :: HState -> BrickEvent Name HumEvent -> EventM Name (Next HState) handleEventPlaylists s e = case e of VtyEvent vtye -> case vtye of EvKey (KChar 'j') [] -> continue =<< playlistsMove listMoveDown s EvKey (KChar 'k') [] -> continue =<< playlistsMove listMoveUp s EvKey (KChar 'l') [] -> continue $ s & focusL . focPlayL .~ FocPSongs EvKey (KChar 'h') [] -> continue $ s & focusL . focPlayL .~ FocPlaylists EvKey (KChar 'n') [] -> continue =<< playlistsSearch (s ^. exL . searchDirectionL) s EvKey (KChar 'N') [] -> continue =<< playlistsSearch (s ^. exL . searchDirectionL & not) s EvKey KEnter [] -> continue =<< playlistsMove listMoveDown =<< playlistsAddtoQ True s EvKey (KChar ' ') [] -> if s ^. editableL then continue $ s & playlistsL . plSongsL %~ (listMoveDown . listToggleHighlight) else continue =<< playlistsMove listMoveDown =<< playlistsAddtoQ False s EvKey (KChar 'd') [] | s ^. editableL -> continue $ deleteHighlighted s (playlistsL . plSongsL) | s ^. focusL . focPlayL == FocPlaylists -> continue $ s & modeL .~ PromptMode & promptsL . currentPromptL .~ YNPrompt & promptsL . promptTitleL .~ ("DELETE " <> selectedPl <> "?\nYou can't paste it back yet") & promptsL . exitPromptL .~ deleteSelectedPl | otherwise -> continue s EvKey (KChar 'y') [] | s ^. editableL -> continue $ s & clipboardL . clSongsL .~ (s ^. playlistsL . plSongsL & getHighlighted) | s ^. focusL . focPlayL == FocPlaylists -> continue $ s & clipboardL . clPlNameL .~ (s ^. playlistsL . plListL & listSelectedElement <&> snd) | otherwise -> continue s EvKey (KChar 'p') [] | s ^. editableL -> continue $ s & playlistsL . plSongsL %~ listPaste (s^. clipboardL. clSongsL) | s ^. focusL . focPlayL == FocPlaylists -> continue =<< pastePlaylist s | otherwise -> continue s EvKey (KChar 'G') [] -> continue =<< playlistsMove (\ls -> listMoveBy (length ls) ls) s EvKey (KChar 'g') [] -> continue =<< playlistsMove (listMoveTo 0) s -- TODO change this to 'gg', somehow EvKey (KChar 'e') [] -> if s ^. editableL then continue $ s & editableL %~ not & modeL .~ PromptMode & promptsL . currentPromptL .~ YNPrompt & promptsL . promptTitleL .~ ("Save changes to " <> selectedPl <> "?") & promptsL . exitPromptL .~ saveEditedPl else continue =<< rebuildPlList (s & editableL %~ not) _ -> continue s _ -> continue s where selectedPl = s ^. playlistsL . plListL & listSelectedElement ? maybe "" snd ? MPD.toText