kino/src/Kino/UI.hs

195 lines
6.2 KiB
Haskell
Raw Normal View History

2021-09-20 13:21:48 +02:00
{-# LANGUAGE OverloadedStrings #-}
{-|
Module : Kino.UI
Description : This is the code which interacts with Brick
This is the code which interacts with Brick
-}
2021-09-06 21:02:39 +02:00
module Kino.UI (runApp) where
2021-09-06 21:02:39 +02:00
2021-10-10 16:39:47 +02:00
import Data.Char
2021-09-14 17:06:34 +02:00
import Brick hiding (Direction(..))
import Brick.Widgets.Edit (handleEditorEvent, editorText, editAttr, getEditContents)
import Control.Monad.IO.Class (liftIO)
2021-09-14 17:06:34 +02:00
import Graphics.Vty (defAttr)
import Graphics.Vty.Attributes (withStyle, reverseVideo)
2021-09-20 13:21:48 +02:00
import Graphics.Vty.Attributes.Color (brightBlack)
import Graphics.Vty.Input.Events
2021-09-14 17:06:34 +02:00
import Lens.Micro
2021-09-20 15:30:10 +02:00
import Network.Wreq
import System.Clipboard
import qualified Data.Text as T
2021-09-06 21:02:39 +02:00
import Kino.Types
import Kino.Request
import Kino.Misc
import Kino.UI.Widgets
import Kino.Torrent
2021-09-14 17:06:34 +02:00
-- | The initial state of our application
2021-09-14 17:06:34 +02:00
initialState :: AppS
initialState = AppS
{ _appMode = Browse
, _appCursor = 0
, _appExpanded = False
, _appPage = 1
2021-09-20 13:21:48 +02:00
, _appListing = JSONListMovies 0 0 0 []
2021-09-14 17:06:34 +02:00
, _appDetails = Nothing
, _appMessage = Nothing
, _appContinue = True
2021-09-20 13:21:48 +02:00
, _appEditor = editorText Input (Just 1) mempty
2021-09-20 15:30:10 +02:00
, _appReqOpts = defaultOptions
2021-10-10 16:39:47 +02:00
, _appSort = UploadDate
, _appSortMode = Descending
2021-09-14 17:06:34 +02:00
}
2021-09-06 21:02:39 +02:00
-- | Our brick app specification
2021-09-13 19:00:26 +02:00
app :: App AppS () Ident
2021-09-06 21:02:39 +02:00
app = App
{ appDraw = draw
, appChooseCursor = chooseCursor
2021-09-13 19:00:26 +02:00
, appHandleEvent = eventHandler
, appStartEvent = startEvent
, appAttrMap = attributeMap
2021-09-06 21:02:39 +02:00
}
-- | A small wrapper to run the brick app
2021-09-13 19:00:26 +02:00
runApp :: IO AppS
runApp = defaultMain app initialState
2021-09-06 21:02:39 +02:00
-- | The starting event which grabs the inital listing
2021-09-14 17:06:34 +02:00
startEvent :: AppS -> EventM Ident AppS
2022-06-26 19:09:57 +02:00
startEvent = liftIO . setMovies
2021-09-13 19:00:26 +02:00
-- | The drawing function which defers to the proper function
-- from UI.Widgets
2021-09-13 19:00:26 +02:00
draw :: AppS -> [Widget Ident]
2021-09-14 17:06:34 +02:00
draw s = pure $ case s ^. appMode of
Browse -> browseWidget s
Search -> searchWidget s
Message -> messageWidget s
2021-10-10 16:39:47 +02:00
Sort -> sortWidget
2021-09-06 21:02:39 +02:00
-- | Currently just showFirstCursor
2021-09-13 19:00:26 +02:00
chooseCursor :: AppS -> [CursorLocation Ident] -> Maybe (CursorLocation Ident)
chooseCursor = showFirstCursor
2021-09-06 21:02:39 +02:00
-- | Scrolls our cursor and updates the app details
2021-09-14 17:06:34 +02:00
scroll :: ScrollDirection -> AppS -> AppS
scroll d s = s & appCursor .~ newCursor
2021-09-14 17:06:34 +02:00
& appExpanded .~ False
2021-09-20 13:21:48 +02:00
& appDetails .~ (moviesMovies (s ^. appListing) !? newCursor)
2021-09-14 17:06:34 +02:00
where
2021-09-20 13:21:48 +02:00
upperLimit = length (moviesMovies (s ^. appListing)) - 1
new = case d of { Up -> subtract 1; Down -> (+1) }
newCursor = max 0 (min upperLimit (new (s ^. appCursor)))
2021-09-20 15:30:10 +02:00
-- | Scrolls to the next page
scrollPage :: ScrollDirection -> AppS -> IO AppS
scrollPage d s = do
setMovies $ s & appPage .~ newPage
where
listing = s ^. appListing
upperLimit = moviesCount listing `div` moviesLimit listing
new = case d of { Up -> subtract 1; Down -> (+1) }
newPage = max 1 (min upperLimit (new (s ^. appPage)))
-- | Copy the magnet link of the focused movie at
-- the given index
copyMagnet :: AppS -> Int -> EventM Ident (Next AppS)
copyMagnet s i = case (do
m <- s ^. appDetails
_ <- movieTorrents m !? i - 1
toMagnets m !? i - 1) of
Nothing -> continue s
(Just magnet) -> do
liftIO (setClipboardString magnet)
2021-09-20 13:21:48 +02:00
continue (displayMessage s False "Copied magnet link to clipboard!")
2021-10-10 16:39:47 +02:00
-- | Continue with a new sort mode
setSort :: AppS -> SortOrder -> EventM Ident (Next AppS)
setSort st so = continue =<< (liftIO $ setMovies (st & appSort .~ so
& appMode .~ Browse))
-- | The event handler, takes care of keyboard events.
2021-09-14 17:06:34 +02:00
eventHandler :: AppS -> BrickEvent Ident () -> EventM Ident (Next AppS)
2021-09-20 13:21:48 +02:00
eventHandler s (VtyEvent e@(EvKey k _)) =
2021-09-14 17:06:34 +02:00
case s ^. appMode of
Message -> if s ^. appContinue
then continue (s & appMode .~ Browse
& appMessage .~ Nothing)
else halt s
2021-09-20 15:30:10 +02:00
2021-10-04 11:11:09 +02:00
Sort -> case k of
2021-10-10 16:39:47 +02:00
(KChar c) -> let s' = s & appSortMode .~ if isUpper c then Ascending else Descending
in case toLower c of
't' -> setSort s' Title
'y' -> setSort s' Year
'r' -> setSort s' Rating
's' -> setSort s' Seeds
'd' -> setSort s' Downloads
'l' -> setSort s' Likes
'u' -> setSort s' UploadDate
'q' -> continue (s & appMode .~ Browse)
_ -> continueWithoutRedraw s
_ -> continueWithoutRedraw s
2021-10-04 11:11:09 +02:00
2021-09-20 13:21:48 +02:00
Search -> case k of
(KEnter) -> do
let queryText = T.unlines (getEditContents (s ^. appEditor))
2021-09-20 15:30:10 +02:00
newS <- liftIO (setMovies (s & appReqOpts %~ (\o -> o & param "query_term" .~ [queryText])
& appPage .~ 1))
2021-09-20 13:21:48 +02:00
continue (newS & appMode .~ Browse)
2021-10-10 16:39:47 +02:00
2021-09-20 13:21:48 +02:00
_ -> continue =<< handleEventLensed s appEditor handleEditorEvent e
2021-09-20 15:30:10 +02:00
Browse -> case k of
2021-09-14 17:06:34 +02:00
(KChar 'q') -> halt s
2021-09-14 18:24:05 +02:00
(KEsc) -> halt s
2021-09-14 17:06:34 +02:00
(KChar 'j') -> continue (scroll Down s)
(KChar 'k') -> continue (scroll Up s)
2021-09-14 18:24:05 +02:00
(KDown) -> continue (scroll Down s)
(KUp) -> continue (scroll Up s)
2021-09-20 15:30:10 +02:00
(KChar 'l') -> continue =<< liftIO (scrollPage Down s)
(KChar 'h') -> continue =<< liftIO (scrollPage Up s)
(KRight) -> continue =<< liftIO (scrollPage Down s)
(KLeft) -> continue =<< liftIO (scrollPage Up s)
2021-09-14 17:06:34 +02:00
(KChar ' ') -> continue (s & appExpanded %~ not)
2021-09-14 18:24:05 +02:00
(KEnter) -> continue (s & appExpanded %~ not)
(KChar '1') -> copyMagnet s 1
(KChar '2') -> copyMagnet s 2
(KChar '3') -> copyMagnet s 3
(KChar '4') -> copyMagnet s 4
(KChar '5') -> copyMagnet s 5
(KChar '6') -> copyMagnet s 6
(KChar '7') -> copyMagnet s 7
(KChar '8') -> copyMagnet s 8
(KChar '9') -> copyMagnet s 9
2021-09-14 18:24:05 +02:00
(KChar '/') -> continue (s & appMode .~ Search)
2021-10-10 16:39:47 +02:00
(KChar 'f') -> continue (s & appMode .~ Search)
(KChar 's') -> continue (s & appMode .~ Sort)
2021-09-14 18:24:05 +02:00
2021-09-20 13:21:48 +02:00
_ -> continueWithoutRedraw s
2021-09-14 17:06:34 +02:00
eventHandler s _ = continue s
2021-09-06 21:02:39 +02:00
-- | The attribute map, currently not dependant on state
2021-09-14 17:06:34 +02:00
attributeMap :: AppS -> AttrMap
2021-09-20 13:21:48 +02:00
attributeMap = const $ attrMap defAttr [ (attrName "selected", withStyle defAttr reverseVideo)
, (editAttr, fg brightBlack) ]