diff --git a/docs/html/_config.yml b/docs/html/_config.yml
index a14dddc8..95da5bc1 100755
--- a/docs/html/_config.yml
+++ b/docs/html/_config.yml
@@ -3,7 +3,7 @@ title: taskell
tagline: Command-line Kanban board/task managment
baseurl: ""
locale: "en"
-version: 1.2.2
+version: 1.2.3
destination: _site/public
exclude: [deployment, Capfile, log, Gemfile, Gemfile.lock]
diff --git a/package.yaml b/package.yaml
index c8620d13..18111958 100644
--- a/package.yaml
+++ b/package.yaml
@@ -1,5 +1,5 @@
name: taskell
-version: '1.2.2.0'
+version: '1.2.3.0'
category: CLI
author: Mark Wales
maintainer: mark@smallhadroncollider.com
diff --git a/roadmap.md b/roadmap.md
index 459583e0..26d258df 100644
--- a/roadmap.md
+++ b/roadmap.md
@@ -62,7 +62,6 @@
> Use a `# Title` at top of file and display title somewhere in taskell
- Add tags/labels with `t`
- URL field - plus config to run specific command when selected (e.g. `open -a Chrome.app #{url}`)
-- Should change list numbering to letters when in move list mode
- Redo functionality
- Make trello token UX better
* Open link automatically?
@@ -79,10 +78,6 @@
## In Progress
-- Improve Trello checklist import
- * ~Take checklist fetch errors into account~
- * ~Refactor code~
- * Use Reader to pass around trello token?
## Done
@@ -218,3 +213,8 @@
* ~Add due date support~
* ~Add sub-tasks support~
* ~Add card summary support~
+- Improve Trello checklist import
+ * ~Take checklist fetch errors into account~
+ * ~Refactor code~
+ * ~Use Reader to pass around trello token?~
+- Should change list numbering to letters when in move list mode
diff --git a/src/App.hs b/src/App.hs
index 2e8287e5..f701ce7c 100644
--- a/src/App.hs
+++ b/src/App.hs
@@ -9,9 +9,9 @@ import Graphics.Vty (Mode(BracketedPaste), outputIface, supportsMode, setMode)
import Graphics.Vty.Input.Events (Event(..))
import Data.Taskell.Lists (Lists)
-import Data.Taskell.Date (currentDay, deadline)
+import Data.Taskell.Date (currentDay)
import Events.Actions (event)
-import Events.State (State, Mode(..), continue, path, mode, io, current)
+import Events.State (State, Mode(..), ModalType(..), continue, path, mode, io, current, lists)
import IO.Config (Config, layout, generateAttrMap)
import IO.Taskell (writeData)
import UI.Draw (draw, chooseCursor)
@@ -34,16 +34,25 @@ clearCache state = do
invalidateCacheEntry (RNList li)
invalidateCacheEntry (RNTask (li, ti))
+clearAllTitles :: State -> EventM ResourceName ()
+clearAllTitles state = do
+ let count = length (lists state)
+ let range = [0 .. (count - 1)]
+ void . sequence $ invalidateCacheEntry . RNList <$> range
+ void . sequence $ invalidateCacheEntry . (\x -> RNTask (x, -1)) <$> range
+
handleVtyEvent :: Config -> State -> Event -> EventM ResourceName (Next State)
handleVtyEvent config previousState e = do
let state = event e previousState
case mode previousState of
Search _ _ -> invalidateCache
- _ -> return ()
+ (Modal MoveTo) -> clearAllTitles previousState
+ _ -> return ()
case mode state of
Shutdown -> Brick.halt state
+ (Modal MoveTo) -> clearAllTitles state >> next config state
_ -> clearCache previousState >> clearCache state >> next config state
-- App code
@@ -63,9 +72,9 @@ appStart state = do
go :: Config -> State -> IO ()
go config initial = do
attrMap' <- const <$> generateAttrMap
- deadlineFn <- deadline <$> currentDay
+ today <- currentDay
let app = App {
- appDraw = draw (layout config) deadlineFn
+ appDraw = draw (layout config) today
, appChooseCursor = chooseCursor
, appHandleEvent = handleEvent config
, appStartEvent = appStart
diff --git a/src/Config.hs b/src/Config.hs
index 66f2f182..013ae23a 100644
--- a/src/Config.hs
+++ b/src/Config.hs
@@ -8,7 +8,7 @@ import ClassyPrelude
import Data.FileEmbed (embedFile)
version :: Text
-version = "1.2.2"
+version = "1.2.3"
usage :: Text
usage = decodeUtf8 $(embedFile "templates/usage.txt")
diff --git a/src/Data/Taskell/Date.hs b/src/Data/Taskell/Date.hs
index c8a0d61a..4a374e85 100644
--- a/src/Data/Taskell/Date.hs
+++ b/src/Data/Taskell/Date.hs
@@ -17,14 +17,17 @@ import ClassyPrelude
import Data.Time (Day)
import Data.Time.Clock (secondsToDiffTime)
import Data.Time.Format (parseTimeM, formatTime)
-import Data.Time.Calendar (diffDays)
+import Data.Time.Calendar (toGregorian, diffDays)
import Data.Time.LocalTime (TimeZone, utcToZonedTime, zonedTimeToLocalTime, localDay)
data Deadline = Passed | Today | Tomorrow | ThisWeek | Plenty deriving (Show, Eq)
type DeadlineFn = Day -> Deadline
-dayToText :: Day -> Text
-dayToText day = pack $ formatTime defaultTimeLocale "%d-%b" (UTCTime day (secondsToDiffTime 0))
+dayToText :: Day -> Day -> Text
+dayToText today day = pack $ formatTime defaultTimeLocale format (UTCTime day (secondsToDiffTime 0))
+ where (currentYear, _, _) = toGregorian today
+ (dateYear, _, _) = toGregorian day
+ format = if currentYear == dateYear then "%d-%b" else "%d-%b %Y"
dayToOutput :: Day -> Text
dayToOutput day = pack $ formatTime defaultTimeLocale "%Y-%m-%d" (UTCTime day (secondsToDiffTime 0))
diff --git a/src/IO/Taskell.hs b/src/IO/Taskell.hs
index c39a941c..56429992 100644
--- a/src/IO/Taskell.hs
+++ b/src/IO/Taskell.hs
@@ -57,7 +57,7 @@ createTrello boardID path = do
case maybeToken of
Nothing -> return $ Output $ decodeUtf8 $(embedFile "templates/token.txt")
Just trelloToken -> do
- lists <- lift $ getCards trelloToken boardID
+ lists <- lift $ runReaderT (getCards boardID) trelloToken
case lists of
Left txt -> return $ Output txt
Right ls -> do
diff --git a/src/IO/Trello.hs b/src/IO/Trello.hs
index 5db9a657..9e6aa522 100644
--- a/src/IO/Trello.hs
+++ b/src/IO/Trello.hs
@@ -11,12 +11,14 @@ import ClassyPrelude
import Network.HTTP.Simple (parseRequest, httpBS, getResponseBody, getResponseStatusCode)
import Data.Aeson
-import IO.Trello.List (List, trelloListToList, cards)
+import IO.Trello.List (List, trelloListToList, setCards, cards)
import IO.Trello.Card (Card, idChecklists, setChecklists)
import IO.Trello.ChecklistItem (ChecklistItem, checkItems)
import Data.Taskell.Lists (Lists)
import Data.Time.LocalTime (TimeZone, getCurrentTimeZone)
+type ReaderTrelloToken a = ReaderT TrelloToken IO a
+
type TrelloToken = Text
type TrelloBoardID = Text
type TrelloChecklistID = Text
@@ -27,10 +29,12 @@ key = "80dbcf6f88f62cc5639774e13342c20b"
root :: Text
root = "https://api.trello.com/1/"
-fullURL :: Text -> TrelloToken -> String
-fullURL uri token = unpack $ concat [root, uri, "&key=", key, "&token=", token]
+fullURL :: Text -> ReaderTrelloToken String
+fullURL uri = do
+ token <- ask
+ return . unpack $ concat [root, uri, "&key=", key, "&token=", token]
-boardURL :: TrelloBoardID -> TrelloToken -> String
+boardURL :: TrelloBoardID -> ReaderTrelloToken String
boardURL board = fullURL $ concat [
"boards/", board, "/lists",
"?cards=open",
@@ -38,7 +42,7 @@ boardURL board = fullURL $ concat [
"&fields=id,name,cards"
]
-checklistURL :: TrelloChecklistID -> TrelloToken -> String
+checklistURL :: TrelloChecklistID -> ReaderTrelloToken String
checklistURL checklist = fullURL $ concat [
"checklists/", checklist,
"?fields=id",
@@ -54,9 +58,10 @@ fetch url = do
response <- httpBS request
return (getResponseStatusCode response, getResponseBody response)
-getChecklist :: TrelloToken -> TrelloChecklistID -> IO (Either Text [ChecklistItem])
-getChecklist token checklist = do
- (status, body) <- fetch (checklistURL checklist token)
+getChecklist :: TrelloChecklistID -> ReaderTrelloToken (Either Text [ChecklistItem])
+getChecklist checklist = do
+ url <- checklistURL checklist
+ (status, body) <- lift $ fetch url
return $ case status of
200 -> case checkItems <$> decodeStrict body of
@@ -65,35 +70,27 @@ getChecklist token checklist = do
429 -> Left "Too many checklists"
_ -> Left $ tshow status ++ " error while fetching checklist " ++ checklist
-updateCard :: TrelloToken -> Card -> IO (Either Text Card)
-updateCard token card = do
- let ids = idChecklists card
- checklists <- sequence $ getChecklist token <$> ids
- return $ setChecklists card . concat <$> sequence checklists
+updateCard :: Card -> ReaderTrelloToken (Either Text Card)
+updateCard card = (setChecklists card . concat <$>) . sequence <$> checklists
+ where checklists = sequence $ getChecklist <$> idChecklists card
-updateList :: TrelloToken -> List -> IO (Either Text List)
-updateList token l = do
- let set c = l { cards = c }
- cs <- sequence $ updateCard token <$> cards l
- return $ set <$> sequence cs
+updateList :: List -> ReaderTrelloToken (Either Text List)
+updateList l = (setCards l <$>) . sequence <$> sequence (updateCard <$> cards l)
-getChecklists :: TrelloToken -> [List] -> IO (Either Text [List])
-getChecklists token ls = do
- lists <- sequence $ updateList token <$> ls
- return $ sequence lists
+getChecklists :: [List] -> ReaderTrelloToken (Either Text [List])
+getChecklists ls = sequence <$> sequence (updateList <$> ls)
-getCards :: TrelloToken -> TrelloBoardID -> IO (Either Text Lists)
-getCards token board = do
- (status, body) <- fetch (boardURL board token)
- timezone <- getCurrentTimeZone
+getCards :: TrelloBoardID -> ReaderTrelloToken (Either Text Lists)
+getCards board = do
+ url <- boardURL board
+ (status, body) <- lift $ fetch url
+ timezone <- lift getCurrentTimeZone
putStrLn "Fetching from Trello..."
case status of
200 -> case decodeStrict body of
- Just raw -> do
- lists <- getChecklists token raw
- return $ trelloListsToLists timezone <$> lists
+ Just raw -> fmap (trelloListsToLists timezone) <$> getChecklists raw
Nothing -> return $ Left "Could not parse response. Please file an Issue on GitHub."
404 -> return . Left $ "Could not find Trello board " ++ board ++ ". Make sure the ID is correct"
401 -> return . Left $ "You do not have permission to view Trello board " ++ board
diff --git a/src/IO/Trello/List.hs b/src/IO/Trello/List.hs
index 4b2b3a30..ecfa918c 100644
--- a/src/IO/Trello/List.hs
+++ b/src/IO/Trello/List.hs
@@ -2,6 +2,7 @@
{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
module IO.Trello.List (
List(..)
+ , setCards
, trelloListToList
) where
@@ -18,6 +19,9 @@ data List = List {
, cards :: [Card]
} deriving (Eq, Show, Generic, ToJSON, FromJSON)
+setCards :: List -> [Card] -> List
+setCards list cs = list { cards = cs }
+
trelloListToList :: TimeZone -> List -> TL.List
trelloListToList tz ls = TL.List {
TL.title = name ls
diff --git a/src/UI/Draw.hs b/src/UI/Draw.hs
index bced9c4b..d0535afc 100644
--- a/src/UI/Draw.hs
+++ b/src/UI/Draw.hs
@@ -7,11 +7,14 @@ module UI.Draw (
import ClassyPrelude
+import Data.Char (ord, chr)
import Data.Sequence (mapWithIndex)
+import Control.Monad.Reader (runReader)
import Brick
-import Data.Taskell.Date (Day, DeadlineFn, dayToText)
+import Data.Taskell.Date (Day, dayToText, deadline)
+import Data.Taskell.Lists (Lists)
import Data.Taskell.List (List, tasks, title)
import Data.Taskell.Task (Task, description, hasSubTasks, countSubTasks, countCompleteSubTasks, summary, due)
import Events.State (lists, current, mode, normalise)
@@ -22,114 +25,157 @@ import UI.Modal (showModal)
import UI.Theme
import UI.Types (ResourceName(..))
-renderDate :: DeadlineFn -> Maybe Day -> Maybe (Widget ResourceName)
-renderDate deadlineFn day = do
- attr <- withAttr . dlToAttr . deadlineFn <$> day
- widget <- txt . dayToText <$> day
- return $ attr widget
-
+-- | Draw needs to know various pieces of information, so keep track of them in a record
+data DrawState = DrawState {
+ dsLists :: Lists
+ , dsMode :: Mode
+ , dsLayout :: LayoutConfig
+ , dsToday :: Day
+ , dsCurrent :: Pointer
+ , dsField :: Maybe Field
+ , dsEditingTitle :: Bool
+}
+
+-- | Use a Reader to pass around DrawState
+type ReaderDrawState = ReaderT DrawState Identity
+
+-- | Takes a task's 'due' property and renders a date with appropriate styling (e.g. red if overdue)
+renderDate :: Maybe Day -> ReaderDrawState (Maybe (Widget ResourceName))
+renderDate dueDay = do
+ today <- dsToday <$> ask -- get the value of `today` from DrawState
+ let attr = withAttr . dlToAttr . deadline today <$> dueDay -- create a `Maybe (Widget -> Widget)` attribute function
+ widget = txt . dayToText today <$> dueDay -- get the formatted due date `Maybe Text`
+ return $ attr <*> widget
+
+-- | Renders the appropriate completed sub task count e.g. "[2/3]"
renderSubTaskCount :: Task -> Widget ResourceName
-renderSubTaskCount t = str $ concat [
- "["
- , show $ countCompleteSubTasks t
- , "/"
- , show $ countSubTasks t
- , "]"
- ]
-
-indicators :: DeadlineFn -> Task -> Widget ResourceName
-indicators deadlineFn t = hBox $ padRight (Pad 1) <$> catMaybes [
- const (txt "≡") <$> summary t
- , bool Nothing (Just (renderSubTaskCount t)) (hasSubTasks t)
- , renderDate deadlineFn (due t)
- ]
-
-renderTask :: DeadlineFn -> Maybe Field -> Bool -> Pointer -> Int -> Int -> Task -> Widget ResourceName
-renderTask deadlineFn f eTitle p li ti t =
- cached name
- . (if not eTitle && cur then visible else id)
- . padBottom (Pad 1)
- . (<=> withAttr disabledAttr after)
- . withAttr (if cur then taskCurrentAttr else taskAttr)
- $ if cur && not eTitle then widget' else widget
-
- where cur = (li, ti) == p
- text = description t
- after = indicators deadlineFn t
- name = RNTask (li, ti)
- widget = textField text
- widget' = widgetFromMaybe widget f
-
-columnNumber :: Int -> Text
-columnNumber i = if col >= 1 && col <= 9 then pack (show col) ++ ". " else ""
- where col = i + 1
-
-renderTitle :: Maybe Field -> Bool -> Pointer -> Int -> List -> Widget ResourceName
-renderTitle f eTitle (p, i) li l =
- if cur || p /= li || i == 0
- then visible title'
- else title'
-
- where cur = p == li && eTitle
- text = title l
- col = txt $ columnNumber li
- attr = if p == li then titleCurrentAttr else titleAttr
- title' = padBottom (Pad 1) . withAttr attr . (col <+>) $ if cur then widget' else widget
- widget = textField text
- widget' = widgetFromMaybe widget f
-
-renderList :: LayoutConfig -> DeadlineFn -> Maybe Field -> Bool -> Pointer -> Int -> List -> Widget ResourceName
-renderList layout deadlineFn f eTitle p li l = if fst p == li then visible list else list
- where list =
- (if not eTitle then cached (RNList li) else id)
+renderSubTaskCount task = txt $ concat ["[" , tshow $ countCompleteSubTasks task , "/" , tshow $ countSubTasks task , "]"]
+
+-- | Renders the appropriate indicators: summary, sub task count, and due date
+indicators :: Task -> ReaderDrawState (Widget ResourceName)
+indicators task = do
+ dateWidget <- renderDate (due task) -- get the due date widget
+ return . hBox $ padRight (Pad 1) <$> catMaybes [
+ const (txt "≡") <$> summary task -- show the summary indicator if one is set
+ , bool Nothing (Just (renderSubTaskCount task)) (hasSubTasks task) -- if it has subtasks, render the sub task count
+ , dateWidget
+ ]
+
+-- | Renders an individual task
+renderTask :: Int -> Int -> Task -> ReaderDrawState (Widget ResourceName)
+renderTask listIndex taskIndex task = do
+ eTitle <- dsEditingTitle <$> ask -- is the title being edited? (for visibility)
+ selected <- (== (listIndex, taskIndex)) . dsCurrent <$>ask -- is the current task selected?
+ taskField <- dsField <$> ask -- get the field, if it's being edited
+ after <- indicators task -- get the indicators widget
+
+ let text = description task
+ name = RNTask (listIndex, taskIndex)
+ widget = textField text
+ widget' = widgetFromMaybe widget taskField
+
+ return $ cached name
+ . (if selected && not eTitle then visible else id)
+ . padBottom (Pad 1)
+ . (<=> withAttr disabledAttr after)
+ . withAttr (if selected then taskCurrentAttr else taskAttr)
+ $ if selected && not eTitle then widget' else widget
+
+
+-- | Gets the relevant column prefix - number in normal mode, letter in moveTo
+columnPrefix :: Int -> Int -> ReaderDrawState Text
+columnPrefix selectedList i = do
+ m <- dsMode <$> ask
+ if moveTo m
+ then do
+ let col = chr (i + ord 'a')
+ return $ if i /= selectedList && i >= 0 && i <= 26 then singleton col ++ ". " else ""
+ else do
+ let col = i + 1
+ return $ if col >= 1 && col <= 9 then tshow col ++ ". " else ""
+
+-- | Renders the title for a list
+renderTitle :: Int -> List -> ReaderDrawState (Widget ResourceName)
+renderTitle listIndex list = do
+ (selectedList, selectedTask) <- dsCurrent <$> ask
+ editing <- (selectedList == listIndex &&) . dsEditingTitle <$> ask
+ titleField <- dsField <$> ask
+ col <- txt <$> columnPrefix selectedList listIndex
+
+ let text = title list
+ attr = if selectedList == listIndex then titleCurrentAttr else titleAttr
+ widget = textField text
+ widget' = widgetFromMaybe widget titleField
+ title' = padBottom (Pad 1) . withAttr attr . (col <+>) $ if editing then widget' else widget
+
+ return $ if editing || selectedList /= listIndex || selectedTask == 0 then visible title' else title'
+
+-- | Renders a list
+renderList :: Int -> List -> ReaderDrawState (Widget ResourceName)
+renderList listIndex list = do
+ layout <- dsLayout <$> ask
+ eTitle <- dsEditingTitle <$> ask
+ titleWidget <- renderTitle listIndex list
+ (currentList, _) <- dsCurrent <$> ask
+ taskWidgets <- sequence $ renderTask listIndex `mapWithIndex` tasks list
+
+ let widget = (if not eTitle then cached (RNList listIndex) else id)
. padLeftRight (columnPadding layout)
. hLimit (columnWidth layout)
- . viewport (RNList li) Vertical
+ . viewport (RNList listIndex) Vertical
. vBox
- . (renderTitle f eTitle p li l :)
- . toList
- $ renderTask deadlineFn f eTitle p li `mapWithIndex` tasks l
-
-searchImage :: LayoutConfig -> State -> Widget ResourceName -> Widget ResourceName
-searchImage layout s i = case mode s of
- Search ent f ->
- let attr = if ent then taskCurrentAttr else taskAttr
- in
- i <=> (
- withAttr attr
- . padTopBottom 1
- . padLeftRight (columnPadding layout)
- $ txt "/" <+> field f
- )
- _ -> i
-
-main :: LayoutConfig -> DeadlineFn -> State -> Widget ResourceName
-main layout deadlineFn s =
- searchImage layout s
- . viewport RNLists Horizontal
- . padTopBottom 1
- . hBox
- . toList
- $ renderList layout deadlineFn (getField s) (editingTitle s) (current s) `mapWithIndex` ls
-
- where ls = lists s
-
-getField :: State -> Maybe Field
-getField state = case mode state of
- Insert _ _ f -> Just f
- _ -> Nothing
-
-
-editingTitle :: State -> Bool
-editingTitle state = case mode state of
- Insert IList _ _ -> True
- _ -> False
+ . (titleWidget :)
+ $ toList taskWidgets
+
+ return $ if currentList == listIndex then visible widget else widget
+
+-- | Renders the search area
+renderSearch :: Widget ResourceName -> ReaderDrawState (Widget ResourceName)
+renderSearch mainWidget = do
+ m <- dsMode <$> ask
+ case m of
+ Search editing searchField -> do
+ colPad <- columnPadding . dsLayout <$> ask
+ let attr = withAttr $ if editing then taskCurrentAttr else taskAttr
+ let widget = attr . padTopBottom 1 . padLeftRight colPad $ txt "/" <+> field searchField
+ return $ mainWidget <=> widget
+ _ -> return mainWidget
+
+-- | Renders the main widget
+main :: ReaderDrawState (Widget ResourceName)
+main = do
+ ls <- dsLists <$> ask
+ listWidgets <- toList <$> sequence (renderList `mapWithIndex` ls)
+ let mainWidget = viewport RNLists Horizontal . padTopBottom 1 $ hBox listWidgets
+ renderSearch mainWidget
+
+getField :: Mode -> Maybe Field
+getField (Insert _ _ f) = Just f
+getField _ = Nothing
+
+editingTitle :: Mode -> Bool
+editingTitle (Insert IList _ _) = True
+editingTitle _ = False
+
+moveTo :: Mode -> Bool
+moveTo (Modal MoveTo) = True
+moveTo _ = False
-- draw
-draw :: LayoutConfig -> DeadlineFn -> State -> [Widget ResourceName]
-draw layout deadlineFn state =
- let s = normalise state in
- showModal s deadlineFn [main layout deadlineFn s]
+draw :: LayoutConfig -> Day -> State -> [Widget ResourceName]
+draw layout today state =
+ showModal normalisedState today [runReader main DrawState {
+ dsLists = lists normalisedState
+ , dsMode = stateMode
+ , dsLayout = layout
+ , dsToday = today
+ , dsField = getField stateMode
+ , dsCurrent = current normalisedState
+ , dsEditingTitle = editingTitle stateMode
+ }]
+
+ where normalisedState = normalise state
+ stateMode = mode state
-- cursors
chooseCursor :: State -> [CursorLocation ResourceName] -> Maybe (CursorLocation ResourceName)
diff --git a/src/UI/Modal.hs b/src/UI/Modal.hs
index 669afbfb..21a84a12 100644
--- a/src/UI/Modal.hs
+++ b/src/UI/Modal.hs
@@ -9,7 +9,7 @@ import Brick
import Brick.Widgets.Center
import Brick.Widgets.Border
-import Data.Taskell.Date (DeadlineFn)
+import Data.Taskell.Date (Day)
import Events.State (State, Mode(..), ModalType(..), mode)
import UI.Field (textField)
import UI.Modal.Help (help)
@@ -32,9 +32,9 @@ surround (title, widget) =
where t = padBottom (Pad 1) . withAttr titleAttr $ textField title
-showModal :: State -> DeadlineFn -> [Widget ResourceName] -> [Widget ResourceName]
-showModal s deadlineFn view = case mode s of
+showModal :: State -> Day -> [Widget ResourceName] -> [Widget ResourceName]
+showModal s today view = case mode s of
Modal Help -> surround help : view
- Modal Detail {} -> surround (detail s deadlineFn) : view
+ Modal Detail {} -> surround (detail s today) : view
Modal MoveTo -> surround (moveTo s) : view
_ -> view
diff --git a/src/UI/Modal/Detail.hs b/src/UI/Modal/Detail.hs
index 4ab84952..c0fbcf86 100644
--- a/src/UI/Modal/Detail.hs
+++ b/src/UI/Modal/Detail.hs
@@ -10,7 +10,7 @@ import Data.Sequence (mapWithIndex)
import Brick
-import Data.Taskell.Date (DeadlineFn, dayToOutput)
+import Data.Taskell.Date (Day, dayToOutput, deadline)
import Data.Taskell.Task (Task, SubTask, description, subTasks, name, complete, summary, due)
import Events.State (State, getCurrentTask)
import Events.State.Types (DetailItem(..))
@@ -40,18 +40,18 @@ renderSummary f i task = padTop (Pad 1) $ padBottom (Pad 2) w'
DetailDescription -> visible $ widgetFromMaybe w f
_ -> w
-renderDate :: DeadlineFn -> Maybe Field -> DetailItem -> Task -> Widget ResourceName
-renderDate deadlineFn field item task = case item of
+renderDate :: Day -> Maybe Field -> DetailItem -> Task -> Widget ResourceName
+renderDate today field item task = case item of
DetailDate -> visible $ prefix <+> widgetFromMaybe widget field
_ -> case day of
- Just d -> prefix <+> withAttr (dlToAttr (deadlineFn d)) widget
+ Just d -> prefix <+> withAttr (dlToAttr (deadline today d)) widget
Nothing -> emptyWidget
where day = due task
prefix = txt "Due: "
widget = textField $ maybe "" dayToOutput day
-detail :: State -> DeadlineFn -> (Text, Widget ResourceName)
-detail state deadlineFn = fromMaybe ("Error", txt "Oops") $ do
+detail :: State -> Day -> (Text, Widget ResourceName)
+detail state today = fromMaybe ("Error", txt "Oops") $ do
task <- getCurrentTask state
i <- getCurrentItem state
let f = getField state
@@ -60,4 +60,4 @@ detail state deadlineFn = fromMaybe ("Error", txt "Oops") $ do
w | null sts = withAttr disabledAttr $ txt "No sub-tasks"
| otherwise = vBox . toList $ renderSubTask f i `mapWithIndex` sts
- return (description task, renderDate deadlineFn f i task <=> renderSummary f i task <=> w)
+ return (description task, renderDate today f i task <=> renderSummary f i task <=> w)
diff --git a/test/Data/Taskell/DateTest.hs b/test/Data/Taskell/DateTest.hs
index 37a25187..5b9f1a24 100644
--- a/test/Data/Taskell/DateTest.hs
+++ b/test/Data/Taskell/DateTest.hs
@@ -26,12 +26,26 @@ test_date =
(Just "2018-05-18")
(dayToOutput <$> fromGregorianValid 2018 05 18)
)
- , testCase "dayToText" (
- assertEqual
- "Date in 18-May format"
- (Just "18-May")
- (dayToText <$> fromGregorianValid 2018 05 18)
- )
+ , testGroup "dayToText" [
+ testCase "same year" (
+ assertEqual
+ "Date in 18-May format"
+ (Just "18-May")
+ (dayToText <$> fromGregorianValid 2018 08 18 <*> fromGregorianValid 2018 05 18)
+ )
+ , testCase "different year" (
+ assertEqual
+ "Date in 18-May 2019 format"
+ (Just "18-May 2019")
+ (dayToText <$> fromGregorianValid 2018 08 18 <*> fromGregorianValid 2019 05 18)
+ )
+ , testCase "different year" (
+ assertEqual
+ "Date in 18-May 2017 format"
+ (Just "18-May 2017")
+ (dayToText <$> fromGregorianValid 2018 08 18 <*> fromGregorianValid 2017 05 18)
+ )
+ ]
, testCase "textToDay" (
assertEqual
"A valid Day"