mywork-1.0.0.0: app/Panes/NoteInput.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Panes.NoteInput
(
NoteInputPane
, initNoteInput
, isNoteInputActive
, noteInputResults
)
where
import Brick hiding ( Location )
import Brick.Focus
import Brick.Forms
import Brick.Panes
import qualified Brick.Widgets.Center as C
import Control.Lens
import Control.Monad.IO.Class ( MonadIO, liftIO )
import Data.Maybe ( isJust )
import qualified Data.Sequence as Seq
import Data.Text ( Text )
import qualified Data.Text as T
import Data.Time.Calendar ( Day )
import Data.Time.Clock ( getCurrentTime, utctDay )
import qualified Graphics.Vty as Vty
import Defs
import Panes.Common.Inputs
import Panes.Common.Attrs
data NoteInputPane
data NewNote = NewNote { _nnText :: Text
, _nnDay :: Day
}
makeLenses ''NewNote
blankNewNote :: NewNote
blankNewNote = NewNote "" (toEnum 0)
type NoteForm = Form NewNote MyWorkEvent WName
instance Pane WName MyWorkEvent NoteInputPane where
data (PaneState NoteInputPane MyWorkEvent) = NN { nNF :: Maybe NoteForm
-- Just == pane active
, nNote :: Maybe Note
-- reset to Nothing when
-- nNF transitions Nothing
-- to Just
-- KWQ , nProj :: Text
, nOrig :: Maybe Note
, nErr :: Maybe Text
}
type (EventType NoteInputPane WName MyWorkEvent) = BrickEvent WName MyWorkEvent
initPaneState _ = NN Nothing Nothing Nothing Nothing
drawPane ps _gs =
C.centerLayer
. modalB ((maybe "New" (const "Edit") $ nOrig ps)
-- <> " " <> show (nProj ps)
<> " Note")
. vLimit 25
. hLimitPercent 65
. (\f -> vBox [ renderForm f
, padBottom (Pad 1) $ withAttr a'Error
$ maybe emptyWidget txt (nErr ps)
, emptyWidget
, vLimit 1 (fill ' ' <+> str "Ctrl-D = accept"
<+> fill ' ' <+> str "ESC = abort"
<+> fill ' ')
]) <$> nNF ps
focusable _ ps = case nNF ps of
Nothing -> mempty
Just f -> Seq.fromList $ focusRingToList $ formFocus f
handlePaneEvent _ = \case
VtyEvent (Vty.EvKey Vty.KEsc []) -> nNFL %%~ const (return Nothing)
VtyEvent (Vty.EvKey (Vty.KChar 'd') [Vty.MCtrl]) -> \s ->
let pf = s ^. nNFL
np form = Note { note = form ^. nnText
, notedOn = form ^. nnDay
, noteSource = MyWorkDB
}
in if maybe False allFieldsValid pf
then
return $ s & nNFL .~ Nothing & newNote .~ (np . formState <$> pf)
else
let badflds = maybe "none"
(foldr (\n a -> if T.null a
then T.pack n
else T.pack n <> ", " <> a) ""
. fmap show . invalidFields)
pf
errmsg = "Correct invalid entries before accepting: "
in return $ s { nErr = Just $ errmsg <> badflds }
ev -> \s -> validateForm
$ s { nErr = Nothing }
& (nNFL . _Just %%~ \w -> nestEventM' w (handleFormEvent ev))
nNFL :: Lens' (PaneState NoteInputPane MyWorkEvent) (Maybe NoteForm)
nNFL f s = (\n -> s { nNF = n }) <$> f (nNF s)
isNoteInputActive :: PaneState NoteInputPane MyWorkEvent -> Bool
isNoteInputActive = isJust . nNF
newNote :: Lens' (PaneState NoteInputPane MyWorkEvent) (Maybe Note)
newNote f s = (\n -> s { nNote = n }) <$> f (nNote s)
-- | Returns the original note name (if any) and the new Note
-- specification.
noteInputResults :: PaneState NoteInputPane MyWorkEvent
-> (Maybe NoteTitle, Maybe Note)
noteInputResults ps = (noteTitle <$> nOrig ps, nNote ps)
-- KWQ: remove!?
validateForm :: EventM WName es (PaneState NoteInputPane MyWorkEvent)
-> EventM WName es (PaneState NoteInputPane MyWorkEvent)
validateForm inner = do
s <- inner
case s ^. nNFL of
Nothing -> return s
Just _f -> return s
initNoteInput :: MonadIO m
=> [Note]
-> Maybe Note
-> PaneState NoteInputPane MyWorkEvent
-> m (PaneState NoteInputPane MyWorkEvent)
initNoteInput curNotes mbNote ps = do
now <- utctDay <$> liftIO getCurrentTime
case nNF ps of
Just _ -> return ps
Nothing ->
let label s = padBottom (Pad 1) . label' s
label' s w = (vLimit 1 $ hLimit labelWidth
$ fill ' ' <+> str s <+> str ": ") <+> w
labelWidth = 15
nlForm =
newForm
[
label "Note" @@=
let validate = \case
[] -> Nothing
[""] -> Nothing
o -> let nt = noteTitle' $ T.unlines o
in if (nt `elem` (noteTitle <$> curNotes)
&& (maybe True ((nt /=) . noteTitle) mbNote))
then Nothing -- invalid
else Just $ T.intercalate "\n" o
in editField nnText (WName "New Note")
Nothing
-- (Just 20)
id validate (txt . T.intercalate "\n") id
, label "Date" @@= let validate = \case
("":_) -> Nothing
(l:_) -> textToDay l
_ -> Nothing
dayInit = T.pack . show
dayRender = txt . headText
in editField nnDay (WName "Note Date (Y-M-D)")
(Just 1) dayInit validate dayRender id
]
(case mbNote of
Nothing -> blankNewNote { _nnDay = now }
Just l -> NewNote { _nnText = note l
, _nnDay = notedOn l
}
)
in return $ NN { nNF = Just nlForm
-- , nProj = projName
, nNote = Nothing
, nOrig = mbNote
, nErr = Nothing
}