Use local dates for display of intervals.

[?]
Aug 24, 2020, 4:59 AM
OUR4PAOTXXKXQPMAR5TIYX7MBRRJS2WVTZS7SN4SOGML7SPJIJGQC

Dependencies

  • [2] RSF6UAJK Break out api module for timeline.
  • [3] WRPIYG3E Use project listing functionality to check for whether we have a cookie.
  • [4] ZIG57EE6 Fix project selection, end log end on project switch.
  • [5] QMEYU4MW Add display for prior intervals.
  • [6] BFZN4SUA Make timeline component work.
  • [7] PT4276XC Add logout functionality.
  • [8] JXG3FCXY Upgrade ps + halogen versions.
  • [9] QU5FW67R Add project selection to time tracker.
  • [10] NJNMO72S Add zcash.com submodule and update client to modern halogen.
  • [11] J6S23MDG Use server timestamps for interval start and end.
  • [*] RB2ETNIF Add skeletal PureScript client project.

Change contents

  • edit in client/src/Aftok/Api/Timeline.purs at line 82
    [2.2322]
    [2.2322]
    instance showInterval :: Show i => Show (Interval' i) where
    show (Interval i) = "Interval {start: " <> show i.start <> ", end: " <> show i.end <> "}"
  • edit in client/src/Aftok/Api/Timeline.purs at line 106
    [2.3007]
    [2.3007]
    start :: forall i. Interval' i -> i
    start (Interval i) = i.start
    end :: forall i. Interval' i -> i
    end (Interval i) = i.end
  • edit in client/src/Aftok/Timeline.purs at line 7
    [3.301277]
    [3.4]
    import Control.Monad.State (State, put, get, evalState)
  • edit in client/src/Aftok/Timeline.purs at line 9
    [3.44]
    [3.2520]
    import Control.Monad.Maybe.Trans (MaybeT(..), runMaybeT)
  • replacement in client/src/Aftok/Timeline.purs at line 11
    [3.2521][3.723:759]()
    import Data.Array (reverse, filter)
    [3.2521]
    [3.760]
    import Data.Array (reverse)
    import Data.Date as D
  • replacement in client/src/Aftok/Timeline.purs at line 14
    [3.802][3.802:852]()
    import Data.DateTime (DateTime(..), adjust, date)
    [3.802]
    [3.852]
    import Data.DateTime as DT
    import Data.DateTime (DateTime(..), date)
  • replacement in client/src/Aftok/Timeline.purs at line 21
    [3.487][3.998:1054](),[3.1098][3.998:1054](),[3.998][3.998:1054]()
    import Data.Maybe (Maybe(..), maybe, isJust, isNothing)
    [3.1098]
    [2.6611]
    import Data.Maybe (Maybe(..), maybe, isJust, isNothing, fromMaybe)
  • replacement in client/src/Aftok/Timeline.purs at line 25
    [3.2626][2.6641:6677](),[2.6677][3.1144:1179](),[3.553][3.1144:1179](),[3.553][3.45:80](),[3.1179][3.45:80](),[3.2662][3.45:80](),[3.2903][3.45:80](),[3.2904][3.2904:2922]()
    import Data.Traversable (traverse_)
    import Data.Tuple (Tuple(..), fst)
    import Data.Unfoldable (fromMaybe)
    import Math (abs)
    [3.2626]
    [3.1210]
    import Data.Traversable (traverse_, traverse)
    import Data.Tuple (Tuple(..))
    import Data.Unfoldable as U
  • replacement in client/src/Aftok/Timeline.purs at line 46
    [3.301702][3.1319:1406](),[3.183][3.301777:301817](),[3.1406][3.301777:301817](),[3.301777][3.301777:301817]()
    import CSS (backgroundColor, clear, clearBoth, border, rgb, solid, borderRadius, left)
    import CSS.Display (position, absolute)
    [3.301702]
    [3.301817]
    import CSS (backgroundColor, clear, clearBoth, border, rgb, solid, borderRadius, marginLeft)
    import CSS.Display (display, flex)
  • replacement in client/src/Aftok/Timeline.purs at line 52
    [2.6710][2.6710:6787]()
    import Aftok.Api.Timeline (TimelineError, Interval'(..), Interval, TimeSpan)
    [2.6710]
    [3.2875]
    import Aftok.Api.Timeline (TimelineError, Interval'(..), Interval, TimeSpan, start, end, interval)
  • replacement in client/src/Aftok/Timeline.purs at line 54
    [3.2907][2.6788:6844]()
    import Aftok.Project (Project, Project'(..), ProjectId)
    [3.2907]
    [2.6844]
    import Aftok.Project (Project, Project'(..), ProjectId, pidStr)
  • edit in client/src/Aftok/Timeline.purs at line 62
    [3.3422]
    [3.1124]
    type DayIntervals =
    { dayBounds :: Interval
    , loggedIntervals :: Array Interval
    }
    type History = M.Map Date DayIntervals
  • replacement in client/src/Aftok/Timeline.purs at line 70
    [3.1145][3.301976:302006](),[3.3444][3.301976:302006](),[3.302006][3.4013:4056]()
    { limits :: TimelineLimits
    , history :: M.Map Date (Array Interval)
    [3.1145]
    [3.302007]
    { selectedProject :: Maybe Project
    , history :: M.Map Date DayIntervals
  • replacement in client/src/Aftok/Timeline.purs at line 73
    [3.302037][3.3037:3074]()
    , selectedProject :: Maybe Project
    [3.302037]
    [3.3532]
    , activeHistory :: M.Map Date DayIntervals
  • replacement in client/src/Aftok/Timeline.purs at line 115
    [3.1336][2.6873:6944]()
    { limits: { bounds: TL.interval bottom bottom, current: bottom }
    [3.1336]
    [3.4560]
    { selectedProject: Nothing
  • replacement in client/src/Aftok/Timeline.purs at line 118
    [3.3683][3.3683:3716]()
    , selectedProject: Nothing
    [3.3683]
    [3.3716]
    , activeHistory: M.empty
  • replacement in client/src/Aftok/Timeline.purs at line 151
    [3.865][3.5023:5176]()
    , lineHtml $ intervalHtml st.limits.bounds <$> currentHistory st
    ] <> ((\(Tuple d xs) -> dateLine st d xs) <$> priorHistory st))
    [3.865]
    [3.5176]
    ] <> (historyLine <$> reverse (M.toUnfoldable $ unionHistories st.history st.activeHistory))
    )
  • replacement in client/src/Aftok/Timeline.purs at line 157
    [3.5140][3.303693:303737](),[3.5279][3.303693:303737](),[3.303693][3.303693:303737](),[3.303737][3.5280:5511](),[3.5511][3.304192:304227](),[3.304192][3.304192:304227](),[3.304227][3.5141:5184](),[3.5184][3.304227:304245](),[3.304227][3.304227:304245](),[3.304245][3.5512:5548](),[3.5548][3.998:1016](),[3.998][3.998:1016]()
    eval = case _ of
    Initialize -> do
    dt@(DateTime today t) <- lift system.nowDateTime
    H.put $ { limits : { bounds: dateBounds today
    , current: fromDateTime dt
    }
    , history : M.empty
    , active : Nothing
    , selectedProject: Nothing
    }
    _ <- H.subscribe caps.timer
    pure unit
    [3.5279]
    [3.5214]
    eval action = do
    case action of
    Initialize -> do
    void $ H.subscribe caps.timer
  • replacement in client/src/Aftok/Timeline.purs at line 162
    [3.5215][3.2204:2334](),[3.2506][3.2506:2652](),[3.2652][2.6945:7045](),[2.7045][3.5646:5920](),[3.5646][3.5646:5920]()
    ProjectSelected p -> do
    active <- 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 control
    intervals' <- lift $ caps.listIntervals (unwrap p).projectId timeSpan
    let intervals = case intervals' of
    Left err -> [] -- FIXME
    Right ivals -> ivals
    H.modify_ (_ { selectedProject = Just p, history = toHistory intervals })
    [3.5215]
    [3.5264]
    ProjectSelected p -> do
    active <- 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 control
    intervals' <- lift $ caps.listIntervals (unwrap p).projectId timeSpan
    intervals <- lift $ case intervals' of
    Left 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 ivals
    history' <- lift <<< runMaybeT $ toHistory system intervals
    hist <- case history' of
    Nothing -> lift $ system.log "Project history was empty." *> pure M.empty
    Just h -> pure h
    H.modify_ (_ { selectedProject = Just p, history = hist })
  • replacement in client/src/Aftok/Timeline.purs at line 182
    [3.5265][3.304246:304266](),[3.5215][3.304246:304266](),[3.5497][3.5497:5543](),[3.5543][3.2653:2688]()
    Start -> do
    project <- H.gets (_.selectedProject)
    traverse_ logStart project
    [3.5265]
    [3.304322]
    Start -> do
    project <- H.gets (_.selectedProject)
    traverse_ logStart project
    Stop -> do
    currentProject <- H.gets (_.selectedProject)
    traverse_ logEnd currentProject
  • replacement in client/src/Aftok/Timeline.purs at line 190
    [3.304323][3.304323:304343](),[3.304343][3.2689:2782]()
    Stop -> do
    currentProject <- H.gets (_.selectedProject)
    traverse_ logEnd currentProject
    [3.304323]
    [3.304398]
    Refresh -> do
    t <- lift $ system.now
    H.modify_ (refresh t)
  • replacement in client/src/Aftok/Timeline.purs at line 194
    [3.304399][3.304399:304419](),[3.304419][3.5921:5952](),[3.5952][3.304447:304477](),[3.304447][3.304447:304477]()
    Refresh -> do
    t <- lift $ system.now
    H.modify_ (refresh t)
    [3.304399]
    [3.304477]
    -- common updates, irrespective of action
    active <- H.gets (_.active)
    activeHistory <- lift <<< map (fromMaybe M.empty) <<< runMaybeT $ toHistory system (U.fromMaybe active)
    H.modify_ (_ { activeHistory = activeHistory })
  • replacement in client/src/Aftok/Timeline.purs at line 204
    [3.869][3.3020:3059](),[3.6121][3.3020:3059](),[3.3020][3.3020:3059]()
    Right t -> H.modify_ (start t)
    [3.6121]
    [3.3059]
    Right t -> H.modify_ (updateStart t)
  • replacement in client/src/Aftok/Timeline.purs at line 211
    [3.935][3.3290:3328](),[3.6287][3.3290:3328](),[3.3290][3.3290:3328]()
    Right t -> H.modify_ (stop t)
    [3.6287]
    [3.6288]
    Right t -> do
    currentState <- H.get
    updatedState <- lift $ updateStop system t currentState
    H.put updatedState
  • replacement in client/src/Aftok/Timeline.purs at line 216
    [3.6289][3.6289:6469](),[3.6469][2.7046:7120]()
    dateBounds :: Date -> Interval
    dateBounds date =
    let startOfDay = DateTime date bottom
    endOfDay = adjust (Days 1.0) startOfDay
    startInstant = fromDateTime startOfDay
    in TL.interval startInstant (maybe startInstant fromDateTime endOfDay)
    [3.6289]
    [3.6540]
    historyLine
    :: forall w i
    . Tuple Date DayIntervals
    -> HH.HTML w i
    historyLine (Tuple d xs) =
    datedLine d xs.dayBounds xs.loggedIntervals
  • replacement in client/src/Aftok/Timeline.purs at line 223
    [3.6541][3.6541:6576]()
    currentHistory
    :: TimelineState
    [3.6541]
    [3.6576]
    datedLine
    :: forall w i
    . Date
    -> Interval
  • replacement in client/src/Aftok/Timeline.purs at line 228
    [3.6596][3.6596:7135]()
    currentHistory st =
    let currentDate = date $ toDateTime st.limits.current
    in maybe [] identity (M.lookup currentDate st.history) <> fromMaybe st.active
    priorHistory
    :: TimelineState
    -> Array (Tuple Date (Array Interval))
    priorHistory st =
    let currentDate = date $ toDateTime st.limits.current
    in reverse <<< filter (not <<< (currentDate == _) <<< fst) $ M.toUnfoldable st.history
    dateLine
    :: forall action slots m
    . TimelineState
    -> Date
    -> Array Interval
    -> H.ComponentHTML action slots m
    dateLine st d xs =
    [3.6596]
    [3.7135]
    -> HH.HTML w i
    datedLine d dateBounds xs =
  • replacement in client/src/Aftok/Timeline.purs at line 231
    [3.7144][3.7144:7151]()
    []
    [3.7144]
    [3.7151]
    [ CSS.style do
    clear clearBoth
    ]
  • replacement in client/src/Aftok/Timeline.purs at line 235
    [3.7212][3.7212:7264]()
    , lineHtml (intervalHtml (dateBounds d) <$> xs)
    [3.7212]
    [3.7264]
    , HH.div
    [ CSS.style do
    border solid (px 3.0) (rgb 0x00 0xFF 0x00)
    borderRadius px5 px5 px5 px5
    height (px $ 44.0)
    display flex
    , P.classes (ClassName <$> ["my-2"])
    ]
    (evalState (traverse (intervalHtml dateBounds) xs) 0.0)
  • edit in client/src/Aftok/Timeline.purs at line 245
    [3.7270]
    [3.7270]
    where
    px5 = px 5.0
  • edit in client/src/Aftok/Timeline.purs at line 253
    [3.3329][3.3329:3338](),[3.3338][3.304488:304615](),[3.304488][3.304488:304615](),[3.304615][3.3447:3466](),[3.3466][3.304643:304675](),[3.304643][3.304643:304675](),[3.304675][3.7437:7461](),[3.7461][3.3467:3543](),[3.304675][3.3467:3543](),[3.3543][3.304769:304866](),[3.304769][3.304769:304866](),[3.304866][3.5480:5481](),[3.5480][3.5480:5481]()
    lineHtml
    :: forall action slots m
    . Array (H.ComponentHTML action slots m)
    -> H.ComponentHTML action slots m
    lineHtml contents =
    let px5 = px 5.0
    in HH.div
    [ CSS.style do
    clear clearBoth
    border solid (px 3.0) (rgb 0x00 0xFF 0x00)
    height (px 50.0)
    borderRadius px5 px5 px5 px5
    , P.classes (ClassName <$> ["my-2"])
    ]
    contents
  • replacement in client/src/Aftok/Timeline.purs at line 254
    [3.3352][3.304881:304908](),[3.304881][3.304881:304908]()
    :: forall action slots m
    [3.3352]
    [3.7462]
    :: forall w i
  • replacement in client/src/Aftok/Timeline.purs at line 257
    [3.3387][3.304965:305001](),[3.304965][3.304965:305001](),[3.305001][3.7477:7523]()
    -> H.ComponentHTML action slots m
    intervalHtml (Interval limits) (Interval i) =
    [3.3387]
    [3.1082]
    -> State Number (HH.HTML w i)
    intervalHtml (Interval limits) (Interval i) = do
    offset <- get
  • replacement in client/src/Aftok/Timeline.purs at line 261
    [3.1128][3.3413:3453]()
    ileft = ilen limits.start i.start
    [3.1128]
    [3.3453]
    ileft = ilen limits.start i.start
  • replacement in client/src/Aftok/Timeline.purs at line 265
    [3.973][3.305220:305233](),[3.1255][3.305220:305233](),[3.3608][3.305220:305233](),[3.305220][3.305220:305233]()
    in HH.div
    [3.973]
    [3.3488]
    put $ toPct (ilen limits.start i.end)
    pure $ HH.div
  • edit in client/src/Aftok/Timeline.purs at line 268
    [3.3507][3.305254:305280](),[3.305254][3.305254:305280]()
    position absolute
  • replacement in client/src/Aftok/Timeline.purs at line 269
    [3.305325][3.3609:3636](),[3.3636][3.974:1007]()
    height (px $ 44.0)
    left (pct $ toPct ileft)
    [3.305325]
    [3.1007]
    marginLeft (pct $ toPct ileft - offset)
  • replacement in client/src/Aftok/Timeline.purs at line 285
    [3.6224][3.6224:6275](),[3.6275][3.3508:3520](),[3.3520][2.7121:7172](),[2.7172][3.305818:305824](),[3.7572][3.305818:305824](),[3.305818][3.305818:305824]()
    start :: Instant -> TimelineState -> TimelineState
    start t s =
    s { active = s.active <|> Just (TL.interval t t)
    }
    [3.6224]
    [3.6387]
    updateStart :: Instant -> TimelineState -> TimelineState
    updateStart t s =
    s { active = s.active <|> Just (TL.interval t t) }
  • replacement in client/src/Aftok/Timeline.purs at line 289
    [3.6388][3.6388:6438](),[3.6438][3.3521:3532](),[3.3532][3.7573:7615](),[3.7615][2.7173:7262](),[2.7262][3.7701:7718](),[3.7701][3.7701:7718](),[3.7718][3.305918:305947](),[3.305918][3.305918:305947]()
    stop :: Instant -> TimelineState -> TimelineState
    stop t s =
    s { history = maybe
    s.history
    (\i -> M.unionWith (<>) (toHistory [TL.interval (unwrap i).start t]) s.history)
    s.active
    , active = Nothing
    }
    [3.6388]
    [3.6585]
    updateStop
    :: forall m
    . Monad m
    => System m
    -> Instant
    -> TimelineState
    -> m TimelineState
    updateStop system t st = do
    newHistory <- join <$> traverse (\i -> runMaybeT $ toHistory system [TL.interval (start i) t]) st.active
    pure { selectedProject: st.selectedProject
    , history: maybe st.history (unionHistories st.history) newHistory
    , active: Nothing
    , activeHistory: M.empty
    }
  • replacement in client/src/Aftok/Timeline.purs at line 306
    [3.3547][3.1347:1387](),[3.6654][3.1347:1387](),[3.1387][2.7263:7332]()
    s { limits = s.limits { current = t }
    , active = map (\(Interval i) -> TL.interval i.start t) s.active
    [3.3547]
    [3.306012]
    s { active = map (\(Interval i) -> TL.interval i.start t) s.active
  • replacement in client/src/Aftok/Timeline.purs at line 312
    [3.6842][3.306040:306094]()
    in abs $ n (unInstant _end) - n (unInstant _start)
    [3.6842]
    [3.6889]
    in n (unInstant _end) - n (unInstant _start)
  • replacement in client/src/Aftok/Timeline.purs at line 330
    [3.10517][3.10517:10608]()
    intervalDate :: Interval -> Date
    intervalDate = date <<< toDateTime <<< (_.end) <<< unwrap
    [3.10517]
    [3.10608]
    utcDayBounds :: Instant -> Interval
    utcDayBounds i =
    let startOfDay = DateTime (date $ toDateTime i) bottom
    endOfDay = DT.adjust (Days 1.0) startOfDay
    startInstant = fromDateTime startOfDay
    in TL.interval startInstant (maybe startInstant fromDateTime endOfDay)
  • replacement in client/src/Aftok/Timeline.purs at line 337
    [3.10609][3.10609:10747]()
    toHistory :: Array Interval -> M.Map Date (Array Interval)
    toHistory = M.fromFoldableWith (<>) <<< map (\i -> Tuple (intervalDate i) [i])
    [3.10609]
    [3.10747]
    localDayBounds
    :: forall m
    . Monad m
    => System m
    -> Instant
    -> MaybeT m (Tuple Date Interval)
    localDayBounds system t = do
    Tuple date start <- MaybeT $ system.dateFFI.midnightLocal t
    end <- 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 = do
    lift <<< system.log $ "Splitting interval " <> show i
    dayBounds@(Tuple date bounds) <- localDayBounds system (start i)
    split <- if end i < (end bounds)
    then do
    pure [Tuple date { dayBounds: bounds, loggedIntervals: [i] }]
    else do
    let 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 split
    pure split
    toHistory
    :: forall m
    . Monad m
    => System m
    -> Array Interval
    -> MaybeT m (M.Map Date DayIntervals)
    toHistory system xs = do
    splitIntervals <- join <$> traverse (splitInterval system) xs
    pure $ M.fromFoldableWith unionDayIntervals splitIntervals
  • edit in client/src/Aftok/Timeline.purs at line 384
    [3.10748]
    unionDayIntervals :: DayIntervals -> DayIntervals -> DayIntervals
    unionDayIntervals d1 d2 =
    { dayBounds: d1.dayBounds -- FIXME, need to be sure these match
    , loggedIntervals: d1.loggedIntervals <> d2.loggedIntervals
    }
    unionHistories :: History -> History -> History
    unionHistories = M.unionWith unionDayIntervals
  • edit in client/src/Aftok/Types.purs at line 11
    [3.10921]
    [3.3437]
    import Data.Date (Date)
  • replacement in client/src/Aftok/Types.purs at line 17
    [3.11116][3.3495:3524](),[3.3495][3.3495:3524]()
    import Data.JSDate as JSDate
    [3.11116]
    [3.11117]
    import Data.JSDate as JD
  • edit in client/src/Aftok/Types.purs at line 21
    [3.11251]
    [3.3525]
    import Data.Tuple (Tuple(..))
  • edit in client/src/Aftok/Types.purs at line 40
    [3.11677]
    [3.11677]
    , dateFFI :: DateFFI m
  • edit in client/src/Aftok/Types.purs at line 50
    [3.11902]
    [3.11902]
    , dateFFI: hoistDateFFI liftEffect jsDateFFI
    }
    type DateFFI m =
    { midnightLocal :: Instant -> m (Maybe (Tuple Date Instant))
  • edit in client/src/Aftok/Types.purs at line 57
    [3.11907]
    [3.7525]
    jsDateFFI :: DateFFI Effect
    jsDateFFI =
    { midnightLocal
    }
    midnightLocal :: Instant -> Effect (Maybe (Tuple Date Instant))
    midnightLocal i = do
    let jsDate = JD.fromInstant i
    year <- JD.getFullYear jsDate
    month <- JD.getMonth jsDate
    day <- JD.getDate jsDate
    jsMidnight <- midnightLocalJS year month day
    let date = JD.toDate jsMidnight
    pure $ Tuple <$> date <*> JD.toInstant jsMidnight
    midnightLocalJS :: Number -> Number -> Number -> Effect JD.JSDate
    midnightLocalJS 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 n
    hoistDateFFI nt ffi =
    { midnightLocal: \i -> nt (ffi.midnightLocal i)
    }
  • replacement in client/src/Aftok/Types.purs at line 127
    [3.4023][3.4023:4059]()
    jsDate <- lift $ JSDate.parse str
    [3.4023]
    [3.4059]
    jsDate <- lift $ JD.parse str
  • replacement in client/src/Aftok/Types.purs at line 129
    [3.4152][3.4152:4195]()
    (JSDate.toDateTime jsDate)
    [3.4152]
    [3.12816]
    (JD.toDateTime jsDate)
  • replacement in client/src/Main.purs at line 72
    [3.308567][3.308567:308598]()
    initialState _ = LoggedOut
    [3.308567]
    [3.308598]
    initialState _ = Loading
  • replacement in client/src/Main.purs at line 75
    [3.13988][3.308663:308688](),[3.308663][3.308663:308688]()
    render s = case s of
    [3.13988]
    [3.1614]
    render = case _ of
  • replacement in client/src/Main.purs at line 95
    [3.4408][3.4408:4457]()
    LoginComplete (Login.LoginComplete xs) ->
    [3.4408]
    [3.4457]
    LoginComplete (Login.LoginComplete _) ->