OUR4PAOTXXKXQPMAR5TIYX7MBRRJS2WVTZS7SN4SOGML7SPJIJGQC RSF6UAJKG7CEKILSVXI6C4YZXY7PIYZM2EMA2IXKQ7SADKNVSH7QC NJNMO72S7VIUV22JXB4IFPZMHWTJAOTP6EC6Z4QSKYIROSXT52MQC BFZN4SUAGYNFFYVAP36BAX32DMO622PK4EPEVQQEAGC2IHTEAAPQC JXG3FCXYBDKMUD77DOM7RCIJYKB7BILC43OHHDZBE7YQRGAMUCCAC QMEYU4MWLTSWPWEEOFRLK2IKE64BY3V5X73323WPLCGPP3TPDYGAC J6S23MDGHVSCVVIRB6XRNSY3EGTDNWFJHV7RYLIEHBUK5KU63CFQC ZIG57EE6RB22JGB3CT33EN2HVYCHCXBT5GROBTBMBLEMDTGQOOBQC QU5FW67RGCWOWT2YFM4NYMJFFHWIRPQANQBBAHBKZUY7UYMCSIMQC WRPIYG3EUHZR6N6T74ZXZDXATRMIRLXAQ24UNUNSVTVYGMT2VDSQC RB2ETNIFLQUA6OA66DAEOXZ25ENMQGNKX5CZRSKEYHTD6BQ6NTFQC PT4276XCOP5NJ3GRFJLIBZKVNVAOATAY5PLWV7FWK6RZW5FTEP5AC import Data.Traversable (traverse_)import Data.Tuple (Tuple(..), fst)import Data.Unfoldable (fromMaybe)import Math (abs)
import Data.Traversable (traverse_, traverse)import Data.Tuple (Tuple(..))import Data.Unfoldable as U
import CSS (backgroundColor, clear, clearBoth, border, rgb, solid, borderRadius, left)import CSS.Display (position, absolute)
import CSS (backgroundColor, clear, clearBoth, border, rgb, solid, borderRadius, marginLeft)import CSS.Display (display, flex)
eval = case _ ofInitialize -> dodt@(DateTime today t) <- lift system.nowDateTimeH.put $ { limits : { bounds: dateBounds today, current: fromDateTime dt}, history : M.empty, active : Nothing, selectedProject: Nothing}_ <- H.subscribe caps.timerpure unit
eval action = docase action ofInitialize -> dovoid $ H.subscribe caps.timer
ProjectSelected p -> doactive <- isJust <$> H.gets (_.active)currentProject <- H.gets (_.selectedProject)when (active && any (\p' -> (unwrap p').projectId /= (unwrap p).projectId) currentProject)(traverse_ logEnd currentProject)timeSpan <- TL.Before <$> lift system.nowDateTime -- FIXME, should come from a form controlintervals' <- lift $ caps.listIntervals (unwrap p).projectId timeSpanlet intervals = case intervals' ofLeft err -> [] -- FIXMERight ivals -> ivalsH.modify_ (_ { selectedProject = Just p, history = toHistory intervals })
ProjectSelected p -> doactive <- isJust <$> H.gets (_.active)currentProject <- H.gets (_.selectedProject)when (active && any (\p' -> (unwrap p').projectId /= (unwrap p).projectId) currentProject)(traverse_ logEnd currentProject)timeSpan <- TL.Before <$> lift system.nowDateTime -- FIXME, should come from a form controlintervals' <- lift $ caps.listIntervals (unwrap p).projectId timeSpanintervals <- lift $ case intervals' ofLeft err ->(system.log $ "Error occurred listing intervals") *>pure []Right ivals ->(system.log $ "Got " <> show (length ivals :: Int) <> " intervals for project " <> pidStr (unwrap p).projectId) *>pure ivalshistory' <- lift <<< runMaybeT $ toHistory system intervalshist <- case history' ofNothing -> lift $ system.log "Project history was empty." *> pure M.emptyJust h -> pure hH.modify_ (_ { selectedProject = Just p, history = hist })
Start -> doproject <- H.gets (_.selectedProject)traverse_ logStart project
Start -> doproject <- H.gets (_.selectedProject)traverse_ logStart projectStop -> docurrentProject <- H.gets (_.selectedProject)traverse_ logEnd currentProject
Refresh -> dot <- lift $ system.nowH.modify_ (refresh t)
-- common updates, irrespective of actionactive <- H.gets (_.active)activeHistory <- lift <<< map (fromMaybe M.empty) <<< runMaybeT $ toHistory system (U.fromMaybe active)H.modify_ (_ { activeHistory = activeHistory })
dateBounds :: Date -> IntervaldateBounds date =let startOfDay = DateTime date bottomendOfDay = adjust (Days 1.0) startOfDaystartInstant = fromDateTime startOfDayin TL.interval startInstant (maybe startInstant fromDateTime endOfDay)
historyLine:: forall w i. Tuple Date DayIntervals-> HH.HTML w ihistoryLine (Tuple d xs) =datedLine d xs.dayBounds xs.loggedIntervals
currentHistory st =let currentDate = date $ toDateTime st.limits.currentin maybe [] identity (M.lookup currentDate st.history) <> fromMaybe st.activepriorHistory:: TimelineState-> Array (Tuple Date (Array Interval))priorHistory st =let currentDate = date $ toDateTime st.limits.currentin reverse <<< filter (not <<< (currentDate == _) <<< fst) $ M.toUnfoldable st.historydateLine:: forall action slots m. TimelineState-> Date-> Array Interval-> H.ComponentHTML action slots mdateLine st d xs =
-> HH.HTML w idatedLine d dateBounds xs =
, lineHtml (intervalHtml (dateBounds d) <$> xs)
, HH.div[ CSS.style doborder solid (px 3.0) (rgb 0x00 0xFF 0x00)borderRadius px5 px5 px5 px5height (px $ 44.0)display flex, P.classes (ClassName <$> ["my-2"])](evalState (traverse (intervalHtml dateBounds) xs) 0.0)
lineHtml:: forall action slots m. Array (H.ComponentHTML action slots m)-> H.ComponentHTML action slots mlineHtml contents =let px5 = px 5.0in HH.div[ CSS.style doclear clearBothborder solid (px 3.0) (rgb 0x00 0xFF 0x00)height (px 50.0)borderRadius px5 px5 px5 px5, P.classes (ClassName <$> ["my-2"])]contents
start :: Instant -> TimelineState -> TimelineStatestart t s =s { active = s.active <|> Just (TL.interval t t)}
updateStart :: Instant -> TimelineState -> TimelineStateupdateStart t s =s { active = s.active <|> Just (TL.interval t t) }
stop :: Instant -> TimelineState -> TimelineStatestop t s =s { history = maybes.history(\i -> M.unionWith (<>) (toHistory [TL.interval (unwrap i).start t]) s.history)s.active, active = Nothing}
updateStop:: forall m. Monad m=> System m-> Instant-> TimelineState-> m TimelineStateupdateStop system t st = donewHistory <- join <$> traverse (\i -> runMaybeT $ toHistory system [TL.interval (start i) t]) st.activepure { selectedProject: st.selectedProject, history: maybe st.history (unionHistories st.history) newHistory, active: Nothing, activeHistory: M.empty}
intervalDate :: Interval -> DateintervalDate = date <<< toDateTime <<< (_.end) <<< unwrap
utcDayBounds :: Instant -> IntervalutcDayBounds i =let startOfDay = DateTime (date $ toDateTime i) bottomendOfDay = DT.adjust (Days 1.0) startOfDaystartInstant = fromDateTime startOfDayin TL.interval startInstant (maybe startInstant fromDateTime endOfDay)
toHistory :: Array Interval -> M.Map Date (Array Interval)toHistory = M.fromFoldableWith (<>) <<< map (\i -> Tuple (intervalDate i) [i])
localDayBounds:: forall m. Monad m=> System m-> Instant-> MaybeT m (Tuple Date Interval)localDayBounds system t = doTuple date start <- MaybeT $ system.dateFFI.midnightLocal tend <- MaybeT <<< pure $ fromDateTime <$> DT.adjust (Days 1.0) (toDateTime start)pure $ Tuple date (interval start end)incrementDayBounds :: Tuple Date Interval -> Maybe (Tuple Date Interval)incrementDayBounds (Tuple d i) =let nextEnd = fromDateTime <$> (DT.adjust (Days 1.0) $ toDateTime (end i))in Tuple <$> D.adjust (Days 1.0) d<*> (interval (end i) <$> nextEnd)splitInterval:: forall m. Monad m=> System m-> Interval-> MaybeT m (Array (Tuple Date DayIntervals))splitInterval system i = dolift <<< system.log $ "Splitting interval " <> show idayBounds@(Tuple date bounds) <- localDayBounds system (start i)split <- if end i < (end bounds)then dopure [Tuple date { dayBounds: bounds, loggedIntervals: [i] }]else dolet firstFragment = [ Tuple date { dayBounds: bounds, loggedIntervals: [interval (start i) (end bounds)]} ]append firstFragment <$> splitInterval system (interval (end bounds) (end i))lift <<< system.log $ "Split result: " <> show splitpure splittoHistory:: forall m. Monad m=> System m-> Array Interval-> MaybeT m (M.Map Date DayIntervals)toHistory system xs = dosplitIntervals <- join <$> traverse (splitInterval system) xspure $ M.fromFoldableWith unionDayIntervals splitIntervals
unionDayIntervals :: DayIntervals -> DayIntervals -> DayIntervalsunionDayIntervals d1 d2 ={ dayBounds: d1.dayBounds -- FIXME, need to be sure these match, loggedIntervals: d1.loggedIntervals <> d2.loggedIntervals}unionHistories :: History -> History -> HistoryunionHistories = M.unionWith unionDayIntervals
jsDateFFI :: DateFFI EffectjsDateFFI ={ midnightLocal}midnightLocal :: Instant -> Effect (Maybe (Tuple Date Instant))midnightLocal i = dolet jsDate = JD.fromInstant iyear <- JD.getFullYear jsDatemonth <- JD.getMonth jsDateday <- JD.getDate jsDatejsMidnight <- midnightLocalJS year month daylet date = JD.toDate jsMidnightpure $ Tuple <$> date <*> JD.toInstant jsMidnightmidnightLocalJS :: Number -> Number -> Number -> Effect JD.JSDatemidnightLocalJS year month day = JD.jsdateLocal{ year, month, day, hour: 0.0, minute: 0.0, second: 0.0, millisecond: 0.0}hoistDateFFI :: forall m n. (forall a. m a -> n a) -> DateFFI m -> DateFFI nhoistDateFFI nt ffi ={ midnightLocal: \i -> nt (ffi.midnightLocal i)}