Minor module reorg.

[?]
Jan 26, 2021, 6:22 AM
NAFJ6RB3KYDBSTSNB3WQSVUQEPUGG2RZCBWRF4XNT2UKSOXDNMDQC

Dependencies

  • [2] QH4UB73N Format with purty.
  • [3] J6S23MDG Use server timestamps for interval start and end.
  • [4] EA5BFM5G Split Login component into its own module.
  • [5] ENNZIQJG Use live signup API for client.
  • [6] PT4276XC Add logout functionality.
  • [7] ZIG57EE6 Fix project selection, end log end on project switch.
  • [8] RSF6UAJK Break out api module for timeline.
  • [9] ARX7SHY5 Begin work on login UI.
  • [10] OUR4PAOT Use local dates for display of intervals.
  • [11] 5R2Z7FSX Initial rendering for signup controls.
  • [12] QMEYU4MW Add display for prior intervals.
  • [13] QAC2QJ32 Add project overview page to client.
  • [14] WRPIYG3E Use project listing functionality to check for whether we have a cookie.
  • [15] QU5FW67R Add project selection to time tracker.
  • [16] B4MTB6UO Persist project across pages.
  • [17] BFZN4SUA Make timeline component work.
  • [18] TKGBRIQT Login component now raises LoginComplete message.
  • [19] 2J37EVJM Check for an open interval on project switch.
  • [20] HO2PFRAB Client login now handles response correctly.
  • [21] JXG3FCXY Upgrade ps + halogen versions.
  • [22] NJNMO72S Add zcash.com submodule and update client to modern halogen.
  • [23] SAESJLLY Initial experiments in hash routing.
  • [24] AAALU5A2 Fix client routing
  • [25] IR75ZMX3 Return actual events for interval ends, not just timestamps.
  • [26] 3LMXT7Z6 preventDefault on login form submission.
  • [*] RB2ETNIF Add skeletal PureScript client project.
  • [*] O2BZOX7M Add signup form, captcha check.

Change contents

  • edit in client/spago.dhall at line 10
    [3.295116]
    [3.157]
    , "rationals"
  • file addition: Json.purs (----------)
    [3.1]
    module Aftok.Api.Json where
    import Prelude
    import Control.Monad.Error.Class (throwError)
    import Control.Monad.Except.Trans (ExceptT, except, withExceptT)
    import Control.Monad.Trans.Class (lift)
    import Data.Argonaut.Core (Json)
    import Data.Argonaut.Decode (class DecodeJson, decodeJson)
    import Data.DateTime (DateTime)
    import Data.DateTime.Instant (Instant, fromDateTime)
    import Data.Functor.Compose (Compose(..))
    import Data.Either (Either(..), note)
    import Data.Foldable (class Foldable, foldr, foldl, foldMap)
    import Data.JSDate as JD
    import Data.Maybe (Maybe(..))
    import Data.Newtype (class Newtype, unwrap, over)
    import Data.Traversable (class Traversable, traverse)
    import Effect (Effect)
    import Affjax as AJAX
    import Affjax (Response, printError)
    import Affjax.StatusCode (StatusCode(..))
    import Aftok.Api.Types (APIError(..))
    newtype JsonCompose f g a
    = JsonCompose (Compose f g a)
    derive instance jsonComposeNewtype :: Newtype (JsonCompose f g a) _
    instance jsonComposeFunctor :: (Functor f, Functor g) => Functor (JsonCompose f g) where
    map f = over JsonCompose (map f)
    instance jsonComposeFoldable :: (Foldable f, Foldable g) => Foldable (JsonCompose f g) where
    foldr f b = foldr f b <<< unwrap
    foldl f b = foldl f b <<< unwrap
    foldMap f = foldMap f <<< unwrap
    instance jsonComposeTraversable :: (Traversable f, Traversable g) => Traversable (JsonCompose f g) where
    traverse f = map JsonCompose <<< traverse f <<< unwrap
    sequence = traverse identity
    instance jsonComposeDecodeJson :: (DecodeJson (f (g a))) => DecodeJson (JsonCompose f g a) where
    decodeJson json = JsonCompose <<< Compose <$> decodeJson json
    decompose :: forall f g a. JsonCompose f g a -> f (g a)
    decompose (JsonCompose (Compose fga)) = fga
    parseJsonDate :: Json -> ExceptT String Effect DateTime
    parseJsonDate json = do
    str <- except $ decodeJson json
    parseDate str
    parseDate :: String -> ExceptT String Effect DateTime
    parseDate str = do
    jsDate <- lift $ JD.parse str
    except
    $ note ("Unable to convert date " <> show jsDate <> " to a valid DateTime value.")
    (JD.toDateTime jsDate)
    decodeDatedJson :: forall t. Traversable t => DecodeJson (t String) => Json -> ExceptT String Effect (t DateTime)
    decodeDatedJson json = do
    decoded <- except $ decodeJson json
    traverse parseDate decoded
    parseDatedResponse ::
    forall t.
    Traversable t =>
    DecodeJson (t String) =>
    Either AJAX.Error (Response Json) ->
    ExceptT APIError Effect (t Instant)
    parseDatedResponse = case _ of
    Left err -> throwError $ Error { status: Nothing, message: printError err }
    Right r -> case r.status of
    StatusCode 403 -> throwError $ Forbidden
    StatusCode 200 -> withExceptT (ParseFailure r.body) $ map fromDateTime <$> decodeDatedJson r.body
    other -> throwError $ Error { status: Just other, message: r.statusText }
  • file addition: Project.purs (----------)
    [3.1]
    module Aftok.Api.Project where
    import Prelude
    import Control.Monad.Except.Trans (ExceptT, runExceptT, except, withExceptT)
    import Control.Monad.Error.Class (throwError)
    import Data.Argonaut.Core (Json)
    import Data.Argonaut.Decode (class DecodeJson, decodeJson, (.:))
    import Data.Bifunctor (lmap)
    import Data.DateTime (DateTime)
    import Data.Either (Either(..), note)
    import Data.Maybe (Maybe(..))
    import Data.Newtype (class Newtype)
    import Data.Rational (Rational)
    import Data.Time.Duration (Hours)
    import Data.Traversable (traverse)
    import Data.UUID (UUID, parseUUID)
    import Effect (Effect)
    import Effect.Aff (Aff)
    import Effect.Class as EC
    import Affjax (get, printError)
    import Affjax.StatusCode (StatusCode(..))
    import Affjax.ResponseFormat as RF
    import Aftok.Types
    ( UserId
    , ProjectId(..)
    )
    import Aftok.Api.Types
    ( APIError(..) )
    import Aftok.Api.Json (parseDate)
    newtype Project' date
    = Project'
    { projectId :: ProjectId
    , projectName :: String
    , inceptionDate :: date
    , initiator :: UUID
    }
    derive instance newtypeProject :: Newtype (Project' a) _
    type Project
    = Project' DateTime
    data ProjectEvent
    = ProjectChange Project
    instance decodeJsonProject :: DecodeJson (Project' String) where
    decodeJson json = do
    x <- decodeJson json
    project <- x .: "project"
    projectIdStr <- x .: "projectId"
    projectId <- ProjectId <$> (note "Failed to decode project UUID" $ parseUUID projectIdStr)
    projectName <- project .: "projectName"
    inceptionDate <- project .: "inceptionDate"
    initiatorStr <- project .: "initiator"
    initiator <- note "Failed to decode initiator UUID" $ parseUUID initiatorStr
    pure $ Project' { projectId, projectName, inceptionDate, initiator }
    newtype Member' date
    = Member'
    { userId :: UserId
    , handle :: String
    , joinedOn :: date
    , timeDevoted :: Hours
    , revShareFrac :: Rational
    }
    listProjects :: Aff (Either APIError (Array Project))
    listProjects = do
    result <- get RF.json "/api/projects"
    EC.liftEffect <<< runExceptT
    $ case result of
    Left err -> throwError $ Error { status: Nothing, message: printError err }
    Right r -> case r.status of
    StatusCode 403 -> throwError Forbidden
    StatusCode 200 -> do
    records <- except $ lmap (ParseFailure r.body) (decodeJson r.body)
    traverse parseProject records
    other -> throwError $ Error { status: Just other, message: r.statusText }
    parseProject :: Json -> ExceptT APIError Effect Project
    parseProject json = do
    Project' p <- except <<< lmap (ParseFailure json) $ decodeJson json
    pdate <- withExceptT (ParseFailure json) $ parseDate p.inceptionDate
    pure $ Project' (p { inceptionDate = pdate })
  • replacement in client/src/Aftok/Api/Timeline.purs at line 28
    [3.912][3.5:89]()
    import Aftok.Types (APIError, decompose, parseDatedResponse, ProjectId(..), pidStr)
    [3.911]
    [3.1031]
    import Aftok.Types (ProjectId(..), pidStr)
    import Aftok.Api.Types (APIError)
    import Aftok.Api.Json (decompose, parseDatedResponse)
  • file addition: Types.purs (----------)
    [3.1]
    module Aftok.Api.Types where
    import Prelude
    import Affjax.StatusCode (StatusCode)
    import Data.Argonaut.Core (Json, stringify)
    import Data.Maybe (Maybe)
    data APIError
    = Forbidden
    | ParseFailure Json String
    | Error { status :: Maybe StatusCode, message :: String }
    instance showAPIError :: Show APIError where
    show = case _ of
    Forbidden -> "Forbidden"
    ParseFailure js e -> "ParseFailure (" <> show (stringify js) <> ") " <> show e
    Error r -> "Error { status: " <> show r.status <> ", message: " <> r.message <> "}"
  • replacement in client/src/Aftok/Overview.purs at line 14
    [3.534][3.534:613]()
    -- import Data.DateTime.Instant (Instant, unInstant, fromDateTime, toDateTime)
    [3.534]
    [3.613]
    import Data.DateTime.Instant (Instant)
  • replacement in client/src/Aftok/Overview.purs at line 55
    [3.1984][3.1984:2016]()
    import Aftok.Project as Project
    [3.1984]
    [3.2016]
    import Aftok.ProjectList as ProjectList
  • replacement in client/src/Aftok/Overview.purs at line 57
    [3.2087][3.2087:2142]()
    import Aftok.Types (System, Project, ProjectEvent(..))
    [3.2087]
    [3.2143]
    import Aftok.Types (System, ProjectId)
    import Aftok.Api.Project (Project, ProjectEvent(..), Member')
  • replacement in client/src/Aftok/Overview.purs at line 80
    [2.9840][2.9840:9890]()
    = ( projectList :: Project.ProjectListSlot Unit
    [2.9840]
    [2.9890]
    = ( projectList :: ProjectList.Slot Unit
  • replacement in client/src/Aftok/Overview.purs at line 86
    [2.9933][2.9933:9939]()
    = {
    [2.9933]
    [2.9939]
    = { getProjectMembers :: ProjectId -> m (Array (Member' Instant))
  • replacement in client/src/Aftok/Overview.purs at line 94
    [2.10022][2.10022:10048]()
    Project.Capability m ->
    [2.10022]
    [2.10048]
    ProjectList.Capability m ->
  • replacement in client/src/Aftok/Overview.purs at line 124
    [2.10977][2.10977:11027]()
    [ HH.text "Your project timeline" ]
    [2.10977]
    [2.11027]
    [ HH.text "Your project details" ]
  • replacement in client/src/Aftok/Overview.purs at line 126
    [2.11047][2.11047:11181]()
    [ HH.slot _projectList unit (Project.projectListComponent system pcaps) st.selectedProject (Just <<< ProjectSelected) ]
    [2.11047]
    [2.11181]
    [ HH.slot _projectList unit (ProjectList.component system pcaps) st.selectedProject (Just <<< ProjectSelected) ]
  • replacement in client/src/Aftok/Overview.purs at line 148
    [3.4470][2.11828:11847]()
    apiCapability = {}
    [3.4470]
    [3.4490]
    apiCapability =
    { getProjectMembers: \_ -> pure []
    }
  • replacement in client/src/Aftok/Overview.purs at line 153
    [3.4524][2.11848:11868]()
    mockCapability = {}
    [3.4524]
    mockCapability =
    { getProjectMembers: \_ -> pure []
    }
  • file move: Project.purs (----------)ProjectList.purs (----------)
    [3.1]
    [3.532]
  • replacement in client/src/Aftok/ProjectList.purs at line 1
    [3.532][3.533:560]()
    module Aftok.Project where
    [3.532]
    [3.560]
    module Aftok.ProjectList where
  • edit in client/src/Aftok/ProjectList.purs at line 5
    [3.617][3.4:81](),[3.81][3.681:727](),[3.681][3.681:727]()
    import Control.Monad.Except.Trans (ExceptT, runExceptT, except, withExceptT)
    import Control.Monad.Error.Class (throwError)
  • replacement in client/src/Aftok/ProjectList.purs at line 6
    [3.107][3.728:761](),[3.728][3.728:761](),[3.761][3.4585:4626](),[3.4626][3.826:855](),[3.826][3.826:855]()
    import Data.Argonaut.Core (Json)
    import Data.Argonaut.Decode (decodeJson)
    import Data.Bifunctor (lmap)
    [3.107]
    [3.4627]
    -- import Data.Bifunctor (lmap)
  • replacement in client/src/Aftok/ProjectList.purs at line 10
    [3.70][3.108:154](),[3.329][3.108:154](),[3.1026][3.108:154](),[3.1097][3.1097:1120]()
    import Data.Traversable (traverse, traverse_)
    import Effect (Effect)
    [3.329]
    [3.1120]
    import Data.Traversable (traverse_)
  • edit in client/src/Aftok/ProjectList.purs at line 12
    [3.1144][3.155:181](),[3.181][3.1177:1286](),[3.1177][3.1177:1286]()
    import Effect.Class as EC
    import Affjax (get, printError)
    import Affjax.StatusCode (StatusCode(..))
    import Affjax.ResponseFormat as RF
  • replacement in client/src/Aftok/ProjectList.purs at line 13
    [2.11889][3.4680:4722](),[3.4680][3.4680:4722]()
    ( APIError(..)
    , System
    , parseDate
    [2.11889]
    [3.4722]
    ( System
  • replacement in client/src/Aftok/ProjectList.purs at line 15
    [3.4733][3.4733:4750]()
    , Project'(..)
    [3.4733]
    [3.4750]
    )
    import Aftok.Api.Types
    ( APIError
    )
    import Aftok.Api.Project
    ( Project'(..)
  • edit in client/src/Aftok/ProjectList.purs at line 22
    [3.4762]
    [3.4762]
    , listProjects
  • replacement in client/src/Aftok/ProjectList.purs at line 30
    [3.1524][2.11890:11908]()
    type ProjectInput
    [3.1524]
    [2.11908]
    type Input
  • replacement in client/src/Aftok/ProjectList.purs at line 33
    [3.365][2.11927:11946]()
    type ProjectCState
    [3.365]
    [2.11946]
    type Output
    = Project
    type Slot id
    = forall query. H.Slot query Project id
    type CState
  • replacement in client/src/Aftok/ProjectList.purs at line 44
    [3.583][2.12024:12043]()
    data ProjectAction
    [3.583]
    [3.603]
    data Action
  • edit in client/src/Aftok/ProjectList.purs at line 48
    [3.634][2.12044:12110](),[3.698][3.1669:1670](),[2.12110][3.1669:1670](),[3.1669][3.1669:1670]()
    type ProjectListSlot id
    = forall query. H.Slot query Project id
  • replacement in client/src/Aftok/ProjectList.purs at line 52
    [3.1751][2.12194:12242]()
    projectListComponent ::
    forall query input m.
    [3.1751]
    [2.12242]
    component ::
    forall query m.
  • replacement in client/src/Aftok/ProjectList.purs at line 57
    [2.12287][2.12287:12374]()
    H.Component HH.HTML query ProjectInput Project m
    projectListComponent console caps =
    [2.12287]
    [2.12374]
    H.Component HH.HTML query Input Output m
    component console caps =
  • replacement in client/src/Aftok/ProjectList.purs at line 70
    [2.12588][2.12588:12636]()
    initialState :: ProjectInput -> ProjectCState
    [2.12588]
    [2.12636]
    initialState :: Input -> CState
  • replacement in client/src/Aftok/ProjectList.purs at line 73
    [3.1107][2.12701:12782]()
    render :: forall slots. ProjectCState -> H.ComponentHTML ProjectAction slots m
    [3.1107]
    [2.12782]
    render :: forall slots. CState -> H.ComponentHTML Action slots m
  • replacement in client/src/Aftok/ProjectList.purs at line 99
    [3.1519][2.13496:13580]()
    eval :: ProjectAction -> H.HalogenM ProjectCState ProjectAction () Project m Unit
    [3.1519]
    [2.13580]
    eval :: Action -> H.HalogenM CState Action () Project m Unit
  • edit in client/src/Aftok/ProjectList.purs at line 111
    [3.2248][3.2248:2320](),[3.2320][2.13988:14485](),[2.14485][3.2801:2951](),[3.2801][3.2801:2951](),[3.2951][3.128:199](),[3.199][3.3130:3179](),[3.3130][3.3130:3179]()
    listProjects :: Aff (Either APIError (Array Project))
    listProjects = do
    result <- get RF.json "/api/projects"
    EC.liftEffect <<< runExceptT
    $ case result of
    Left err -> throwError $ Error { status: Nothing, message: printError err }
    Right r -> case r.status of
    StatusCode 403 -> throwError Forbidden
    StatusCode 200 -> do
    records <- except $ lmap (ParseFailure r.body) (decodeJson r.body)
    traverse parseProject records
    other -> throwError $ Error { status: Just other, message: r.statusText }
    parseProject :: Json -> ExceptT APIError Effect Project
    parseProject json = do
    Project' p <- except <<< lmap (ParseFailure json) $ decodeJson json
    pdate <- withExceptT (ParseFailure json) $ parseDate p.inceptionDate
    pure $ Project' (p { inceptionDate = pdate })
  • replacement in client/src/Aftok/Timeline.purs at line 58
    [3.924][3.2875:2907](),[3.5960][3.2875:2907](),[3.6787][3.2875:2907](),[2.25738][3.2875:2907](),[3.301880][3.2875:2907]()
    import Aftok.Project as Project
    [2.25738]
    [2.25739]
    import Aftok.ProjectList as ProjectList
  • edit in client/src/Aftok/Timeline.purs at line 61
    [2.25769]
    [2.25769]
    , ProjectId
    )
    import Aftok.Api.Project
    ( Project
    , Project'(..)
  • edit in client/src/Aftok/Timeline.purs at line 67
    [2.25790][2.25790:25833]()
    , Project
    , Project'(..)
    , ProjectId
  • replacement in client/src/Aftok/Timeline.purs at line 96
    [3.1433][2.26074:26093]()
    type TimelineInput
    [3.1433]
    [2.26093]
    type Input
  • replacement in client/src/Aftok/Timeline.purs at line 117
    [2.26384][2.26384:26434]()
    = ( projectList :: Project.ProjectListSlot Unit
    [2.26384]
    [2.26434]
    = ( projectList :: ProjectList.Slot Unit
  • replacement in client/src/Aftok/Timeline.purs at line 135
    [2.26942][2.26942:27025]()
    Project.Capability m ->
    H.Component HH.HTML query TimelineInput ProjectEvent m
    [2.26942]
    [2.27025]
    ProjectList.Capability m ->
    H.Component HH.HTML query Input ProjectEvent m
  • replacement in client/src/Aftok/Timeline.purs at line 149
    [2.27269][2.27269:27318]()
    initialState :: TimelineInput -> TimelineState
    [2.27269]
    [2.27318]
    initialState :: Input -> TimelineState
  • replacement in client/src/Aftok/Timeline.purs at line 170
    [2.28037][2.28037:28171]()
    [ HH.slot _projectList unit (Project.projectListComponent system pcaps) st.selectedProject (Just <<< ProjectSelected) ]
    [2.28037]
    [2.28171]
    [ HH.slot _projectList unit (ProjectList.component system pcaps) st.selectedProject (Just <<< ProjectSelected) ]
  • edit in client/src/Aftok/Types.purs at line 4
    [3.3259][3.10750:10861](),[3.10861][3.3259:3299](),[3.3259][3.3259:3299](),[3.7427][3.3352:3396](),[3.3396][3.5525:5590]()
    import Control.Monad.Error.Class (throwError)
    import Control.Monad.Except.Trans (ExceptT, except, withExceptT)
    import Control.Monad.Trans.Class (lift)
    import Data.Argonaut.Core (Json, stringify)
    import Data.Argonaut.Decode (class DecodeJson, decodeJson, (.:))
  • replacement in client/src/Aftok/Types.purs at line 6
    [3.3469][3.10922:11116]()
    import Data.DateTime.Instant (Instant, fromDateTime)
    import Data.Functor.Compose (Compose(..))
    import Data.Either (Either(..), note)
    import Data.Foldable (class Foldable, foldr, foldl, foldMap)
    [3.3469]
    [3.7206]
    import Data.DateTime.Instant (Instant)
  • replacement in client/src/Aftok/Types.purs at line 8
    [3.7231][3.11117:11251](),[3.3524][3.11117:11251]()
    import Data.Maybe (Maybe(..))
    import Data.Newtype (class Newtype, unwrap, over)
    import Data.Traversable (class Traversable, traverse)
    [3.7231]
    [3.7232]
    import Data.Maybe (Maybe)
    import Data.Newtype (class Newtype)
  • replacement in client/src/Aftok/Types.purs at line 11
    [3.7262][3.5591:5636]()
    import Data.UUID (UUID, toString, parseUUID)
    [3.7262]
    [3.3526]
    import Data.UUID (UUID, toString)
  • edit in client/src/Aftok/Types.purs at line 16
    [3.11346][3.11346:11447]()
    import Affjax as AJAX
    import Affjax (Response, printError)
    import Affjax.StatusCode (StatusCode(..))
  • replacement in client/src/Aftok/Types.purs at line 81
    [3.8201][2.37101:37115](),[2.37115][3.7540:7643](),[3.7540][3.7540:7643]()
    data APIError
    = Forbidden
    | ParseFailure Json String
    | Error { status :: Maybe StatusCode, message :: String }
    [3.8201]
    [3.7643]
    newtype UserId
    = UserId UUID
    derive instance userIdEq :: Eq UserId
  • replacement in client/src/Aftok/Types.purs at line 86
    [3.7644][3.3550:3643](),[3.3643][2.37116:37199](),[2.37199][3.3727:3815](),[3.3727][3.3727:3815]()
    instance showAPIError :: Show APIError where
    show = case _ of
    Forbidden -> "Forbidden"
    ParseFailure js e -> "ParseFailure (" <> show (stringify js) <> ") " <> show e
    Error r -> "Error { status: " <> show r.status <> ", message: " <> r.message <> "}"
    [3.7644]
    [3.5637]
    derive instance userIdNewtype :: Newtype UserId _
  • edit in client/src/Aftok/Types.purs at line 97
    [3.5844][3.5844:5845](),[3.5845][2.37241:37276](),[2.37276][3.5878:5983](),[3.5878][3.5878:5983](),[3.5983][2.37277:37278](),[2.37278][3.5983:6041](),[3.5983][3.5983:6041](),[3.6041][2.37279:37314](),[2.37314][3.6074:6263](),[3.6074][3.6074:6263](),[3.6264][3.6264:6301](),[3.6301][2.37315:37454](),[2.37454][3.6446:6494](),[3.6446][3.6446:6494](),[3.6494][2.37455:37579](),[2.37579][3.6623:6697](),[3.6623][3.6623:6697](),[3.6697][2.37580:37638]()
    newtype Project' date
    = Project'
    { projectId :: ProjectId
    , projectName :: String
    , inceptionDate :: date
    , initiator :: UUID
    }
    derive instance newtypeProject :: Newtype (Project' a) _
    type Project
    = Project' DateTime
    data ProjectEvent
    = ProjectChange Project
    instance decodeJsonProject :: DecodeJson (Project' String) where
    decodeJson json = do
    x <- decodeJson json
    project <- x .: "project"
    projectIdStr <- x .: "projectId"
    projectId <- ProjectId <$> (note "Failed to decode project UUID" $ parseUUID projectIdStr)
    projectName <- project .: "projectName"
    inceptionDate <- project .: "inceptionDate"
    initiatorStr <- project .: "initiator"
    initiator <- note "Failed to decode initiator UUID" $ parseUUID initiatorStr
    pure $ Project' { projectId, projectName, inceptionDate, initiator }
    newtype JsonCompose f g a
    = JsonCompose (Compose f g a)
  • edit in client/src/Aftok/Types.purs at line 98
    [2.37639][3.11965:12158](),[3.11965][3.11965:12158](),[3.12158][3.3815:3816](),[3.3815][3.3815:3816](),[3.3816][3.12159:12815](),[3.12815][3.3816:3896](),[3.3816][3.3816:3896](),[3.3896][2.37640:37674](),[2.37674][3.3933:3949](),[3.3933][3.3933:3949](),[3.3949][3.7644:7645](),[3.7644][3.7644:7645](),[3.7645][3.3950:4023](),[3.4023][3.8202:8234](),[3.8234][2.37675:37802](),[3.8274][3.12816:13024](),[2.37802][3.12816:13024](),[3.4195][3.12816:13024](),[3.13024][3.4195:4196](),[3.4195][3.4195:4196](),[3.4196][2.37803:37960](),[2.37960][3.13187:13218](),[3.13187][3.13187:13218](),[3.13218][2.37961:38039](),[2.38039][3.13301:13331](),[3.13301][3.13301:13331](),[3.13331][2.38040:38265]()
    derive instance jsonComposeNewtype :: Newtype (JsonCompose f g a) _
    instance jsonComposeFunctor :: (Functor f, Functor g) => Functor (JsonCompose f g) where
    map f = over JsonCompose (map f)
    instance jsonComposeFoldable :: (Foldable f, Foldable g) => Foldable (JsonCompose f g) where
    foldr f b = foldr f b <<< unwrap
    foldl f b = foldl f b <<< unwrap
    foldMap f = foldMap f <<< unwrap
    instance jsonComposeTraversable :: (Traversable f, Traversable g) => Traversable (JsonCompose f g) where
    traverse f = map JsonCompose <<< traverse f <<< unwrap
    sequence = traverse identity
    instance jsonComposeDecodeJson :: (DecodeJson (f (g a))) => DecodeJson (JsonCompose f g a) where
    decodeJson json = JsonCompose <<< Compose <$> decodeJson json
    decompose :: forall f g a. JsonCompose f g a -> f (g a)
    decompose (JsonCompose (Compose fga)) = fga
    parseJsonDate :: Json -> ExceptT String Effect DateTime
    parseJsonDate json = do
    str <- except $ decodeJson json
    parseDate str
    parseDate :: String -> ExceptT String Effect DateTime
    parseDate str = do
    jsDate <- lift $ JD.parse str
    except
    $ note ("Unable to convert date " <> show jsDate <> " to a valid DateTime value.")
    (JD.toDateTime jsDate)
    decodeDatedJson :: forall t. Traversable t => DecodeJson (t String) => Json -> ExceptT String Effect (t DateTime)
    decodeDatedJson json = do
    decoded <- except $ decodeJson json
    traverse parseDate decoded
    parseDatedResponse ::
    forall t.
    Traversable t =>
    DecodeJson (t String) =>
    Either AJAX.Error (Response Json) ->
    ExceptT APIError Effect (t Instant)
    parseDatedResponse = case _ of
    Left err -> throwError $ Error { status: Nothing, message: printError err }
    Right r -> case r.status of
    StatusCode 403 -> throwError $ Forbidden
    StatusCode 200 -> withExceptT (ParseFailure r.body) $ map fromDateTime <$> decodeDatedJson r.body
    other -> throwError $ Error { status: Just other, message: r.statusText }
  • replacement in client/src/Main.purs at line 22
    [3.1095][3.6746:6813]()
    import Aftok.Types (System, Project, ProjectEvent(..), liveSystem)
    [3.400]
    [3.307686]
    import Aftok.Types (System, liveSystem)
  • edit in client/src/Main.purs at line 25
    [29.9265]
    [3.1541]
    import Aftok.Api.Project (Project, ProjectEvent(ProjectChange))
  • replacement in client/src/Main.purs at line 29
    [3.6848][3.3755:3787](),[3.307748][3.3755:3787]()
    import Aftok.Project as Project
    [3.6848]
    [3.1189]
    import Aftok.ProjectList as ProjectList
  • replacement in client/src/Main.purs at line 42
    [3.7722][3.3788:3826](),[2.38718][3.3788:3826](),[3.1658][3.3788:3826]()
    project = Project.apiCapability
    [2.38718]
    [2.38719]
    project = ProjectList.apiCapability
  • replacement in client/src/Main.purs at line 120
    [2.39666][2.39666:39692]()
    Project.Capability m ->
    [2.39666]
    [2.39692]
    ProjectList.Capability m ->