Initial rendering for signup controls.

[?]
Sep 2, 2020, 4:53 AM
5R2Z7FSXJD7Z53QSU2NSTEBONTYK43FIJOSOMUST5XMYIWRXY2HQC

Dependencies

  • [2] PT4276XC Add logout functionality.
  • [3] WRPIYG3E Use project listing functionality to check for whether we have a cookie.
  • [*] EA5BFM5G Split Login component into its own module.
  • [*] RB2ETNIF Add skeletal PureScript client project.

Change contents

  • file addition: Signup.purs (----------)
    [5.1]
    module Aftok.Signup where
    import Prelude
    -- import Control.Monad.Trans.Class (lift)
    import Data.Maybe (Maybe(..), fromMaybe)
    -- import Affjax (post, get, printError)
    import Affjax.StatusCode (StatusCode)
    -- import Affjax.RequestBody as RB
    -- import Affjax.ResponseFormat as RF
    import Halogen as H
    import Halogen.HTML.Core (AttrName(..), ClassName(..))
    import Halogen.HTML as HH
    -- import Halogen.HTML.CSS as CSS
    import Halogen.HTML.Events as E
    import Web.UIEvent.MouseEvent as ME
    import Web.Event.Event as WE
    import Halogen.HTML.Properties as P
    -- import CSS (backgroundImage, url)
    import Aftok.Types (System)
    data SignupResponse
    = OK
    | Error { status :: Maybe StatusCode, message :: String }
    data RecoveryType
    = RecoveryEmail
    | RecoveryZAddr
    derive instance recoveryTypeEq :: Eq RecoveryType
    type SignupState =
    { username :: Maybe String
    , password :: Maybe String
    , passwordConfirm :: Maybe String
    , recoveryType :: RecoveryType
    , recoveryEmail :: Maybe String
    , recoveryZAddr :: Maybe String
    , loginResponse :: Maybe SignupResponse
    }
    data SignupAction
    = SetUsername String
    | SetPassword String
    | ConfirmPassword String
    | SetRecoveryType RecoveryType
    | SetRecoveryEmail String
    | SetRecoveryZAddr String
    | Signin ME.MouseEvent
    | Signup WE.Event
    data SignupResult
    = SignupComplete { username :: String }
    | LoginSwitch
    type Slot id = forall query. H.Slot query SignupResult id
    type Capability m =
    { signup :: String -> String -> m SignupResponse
    }
    type Config =
    { recaptchaKey :: String
    }
    component
    :: forall query input m
    . Monad m
    => System m
    -> Capability m
    -> Config
    -> H.Component HH.HTML query input SignupResult m
    component system caps conf = H.mkComponent
    { initialState
    , render
    , eval: H.mkEval $ H.defaultEval { handleAction = eval }
    } where
    initialState :: input -> SignupState
    initialState _ =
    { username: Nothing
    , password: Nothing
    , passwordConfirm: Nothing
    , recoveryType: RecoveryEmail
    , recoveryEmail: Nothing
    , recoveryZAddr: Nothing
    , loginResponse: Nothing
    }
    render :: forall slots. SignupState -> H.ComponentHTML SignupAction slots m
    render st =
    HH.section
    [ P.classes (ClassName <$> ["section-border", "border-primary"]) ]
    [ HH.div
    [ P.classes (ClassName <$> ["container", "d-flex", "flex-column"]) ]
    [ HH.div
    [ P.classes (ClassName <$> ["align-items-center", "pt-6"]) ]
    [ HH.h1
    [ P.classes (ClassName <$> ["mb-0", "font-weight-bold", "text-center"]) ]
    [ HH.text "Sign up" ]
    , HH.p
    [ P.classes (ClassName <$> ["text-center", "text-muted", "col-md-5", "mx-auto"]) ]
    [ HH.text "You can use either an email address or zcash payment address for account recovery." ]
    ]
    , HH.div
    [ P.classes (ClassName <$> ["align-items-center", "justify-content-center", "no-gutters"]) ]
    [ HH.div
    [ P.classes (ClassName <$> ["col-12", "col-lg-4", "py-8", "py-md-0"]) ]
    [ HH.form
    [ P.classes (ClassName <$> ["mb-6"]) ]
    [ HH.div
    [ P.classes (ClassName <$> ["form-group"]) ]
    [ HH.label [ P.for "username" ] [ HH.text "Username" ]
    , HH.input
    [ P.type_ P.InputText
    , P.classes (ClassName <$> ["form-control"])
    , P.id_ "username"
    , P.placeholder "Choose a handle (username)"
    , P.required true
    , P.autofocus true
    , P.value (fromMaybe "" st.username)
    , E.onValueInput (Just <<< SetUsername)
    ]
    ]
    , HH.div
    [ P.classes (ClassName <$> ["form-group"]) ]
    [ HH.label [ P.for "password" ] [ HH.text "Password" ]
    , HH.input
    [ P.type_ P.InputPassword
    , P.classes (ClassName <$> ["form-control"])
    , P.id_ "password"
    , P.placeholder "Enter a unique password"
    , P.required true
    , P.value (fromMaybe "" st.password)
    , E.onValueInput (Just <<< SetPassword)
    ]
    , HH.input
    [ P.type_ P.InputPassword
    , P.classes (ClassName <$> ["form-control"])
    , P.id_ "password"
    , P.placeholder "Enter a unique password"
    , P.required true
    , P.value (fromMaybe "" st.passwordConfirm)
    , E.onValueInput (Just <<< ConfirmPassword)
    ]
    ]
    , recoverySwitch st.recoveryType
    , recoveryField st
    , HH.div
    [ P.classes (ClassName <$> ["form-group", "mb-3"]) ]
    [ HH.div
    [ P.classes (ClassName <$> ["form-group", "mb-3"])
    , P.attr (AttrName "data-sitekey") conf.recaptchaKey
    ] []
    ]
    , HH.button
    [ P.classes (ClassName <$> ["btn", "btn-block", "btn-primary"]) ]
    [ HH.text "Sign up" ]
    ]
    , HH.p
    [ P.classes (ClassName <$> ["mb-0", "font-size-sm", "text-center", "text-muted"]) ]
    [ HH.text "Alreay have an account? "
    , HH.a
    [ P.href "#", E.onClick (Just <<< Signin) ]
    [ HH.text "Sign in" ]
    ]
    ]
    ]
    ]
    ]
    eval :: SignupAction -> H.HalogenM SignupState SignupAction () SignupResult m Unit
    eval = case _ of
    SetUsername user -> H.modify_ (_ { username = Just user })
    SetPassword pass -> H.modify_ (_ { password = Just pass })
    ConfirmPassword pass -> H.modify_ (_ { passwordConfirm = Just pass })
    _ -> pure unit
    recoverySwitch :: forall i. RecoveryType -> HH.HTML i SignupAction
    recoverySwitch rt =
    HH.div
    [ P.classes (ClassName <$> ["form-group", "mb-3"]) ]
    [ HH.label
    [ P.for "recoverySwitch" ]
    [ HH.text "Choose a recovery method" ]
    , HH.div
    [ P.classes (ClassName <$> ["form-group", "mb-3"]) ]
    [ HH.span
    [ P.classes (ClassName <$> [ if rt == RecoveryEmail then "text-success" else "text-muted"]) ]
    [ HH.text "Email" ]
    , HH.div
    [ P.classes (ClassName <$> ["custom-control", "custom-switch", "custom-switch-light", "mx-3"]) ]
    [ HH.input
    [ P.type_ P.InputCheckbox
    , P.classes (ClassName <$> ["custom-control-input"])
    , P.id_ "recoverySwitch"
    , E.onChecked (\b -> Just <<< SetRecoveryType $ if b then RecoveryZAddr else RecoveryEmail)
    ]
    ]
    , HH.span
    [ P.classes (ClassName <$> [if rt == RecoveryZAddr then "text-success" else "text-muted"]) ]
    [ HH.text "Z-Address" ]
    ]
    ]
    recoveryField :: forall i. SignupState -> HH.HTML i SignupAction
    recoveryField st = case st.recoveryType of
    RecoveryEmail ->
    HH.div
    [ P.id_ "recoveryEmail" ]
    [ HH.label [ P.for "email" ] [ HH.text "Email Address" ]
    , HH.input
    [ P.type_ P.InputEmail
    , P.classes (ClassName <$> ["form-control"])
    , P.id_ "email"
    , P.placeholder "name@address.com"
    , P.value (fromMaybe "" st.recoveryEmail)
    , E.onValueInput (Just <<< SetRecoveryEmail)
    ]
    ]
    RecoveryZAddr ->
    HH.div
    [ P.id_ "recoveryZAddr" ]
    [ HH.label
    [ P.for "zaddr" ]
    [ HH.text "Zcash Shielded Address"
    , HH.a
    [ P.attr (AttrName "data-toggle") "modal"
    , P.href "#modalAboutZAddr"
    ]
    [ HH.img [ P.src "./assets/img/icons/duotone-icons/Code/Info-circle.svg" ]
    ]
    ]
    , HH.input
    [ P.type_ P.InputText
    , P.classes (ClassName <$> ["form-control"])
    , P.id_ "email"
    , P.placeholder "Enter a Zcash shielded address"
    , P.value (fromMaybe "" st.recoveryZAddr)
    , E.onValueInput (Just <<< SetRecoveryZAddr)
    ]
    ]
  • edit in client/src/Main.purs at line 129
    [2.2927][2.2927:10706]()
    -- <!-- Navigation -->
    -- <ul class="navbar-nav ml-auto">
    -- <li class="nav-item dropdown">
    -- <a class="nav-link dropdown-toggle" id="navbarAccount" data-toggle="dropdown" href="#" aria-haspopup="true" aria-expanded="false">
    -- Guidebook
    -- </a>
    -- <ul class="dropdown-menu" aria-labelledby="navbarAccount">
    -- <li class="dropdown-item dropright">
    -- <a class="dropdown-link dropdown-toggle" data-toggle="dropdown" href="#">
    -- Getting Started
    -- </a>
    -- <div class="dropdown-menu">
    -- <a class="dropdown-item" href="@@webRoot/guide-foundation.html">
    -- Foundational Principles
    -- </a>
    -- <a class="dropdown-item" href="@@webRoot/guide-time.html">
    -- Measuring Contributions
    -- </a>
    -- <a class="dropdown-item" href="@@webRoot/guide-revenue.html">
    -- Revenue Sharing
    -- </a>
    -- <a class="dropdown-item" href="@@webRoot/guide-tithing.html">
    -- Varying Compensation
    -- </a>
    -- </div>
    -- </li>
    -- <!--
    -- <li class="dropdown-item dropright">
    -- <a class="dropdown-link dropdown-toggle" data-toggle="dropdown" href="#">
    -- Up And Running
    -- </a>
    -- <div class="dropdown-menu">
    -- <a class="dropdown-item" href="@@webRoot/guide-voting.html">
    -- Share-Weighted Voting
    -- </a>
    -- <a class="dropdown-item" href="@@webRoot/guide-auctions.html">
    -- Expense Auctions
    -- </a>
    -- <a class="dropdown-item" href="@@webRoot/guide-forks.html">
    -- Project Forks & Merges
    -- </a>
    -- </div>
    -- </li>
    -- <li class="dropdown-item dropright">
    -- <a class="dropdown-link dropdown-toggle" data-toggle="dropdown" href="#">
    -- Coming Soon!
    -- </a>
    -- <div class="dropdown-menu">
    -- <a class="dropdown-item" href="@@webRoot/guide-debt-contracts.html">
    -- 3rd-party Contracts
    -- </a>
    -- <a class="dropdown-item" href="@@webRoot/guide-delegative-voting.html">
    -- Delegative Voting
    -- </a>
    -- </div>
    -- </li>
    -- <li class="dropdown-item dropright">
    -- <a class="dropdown-item" href="@@webRoot/blog.html">
    -- Blog
    -- </a>
    -- </li>
    -- -->
    -- </ul>
    -- </li>
    -- <li class="nav-item dropdown">
    -- <a class="nav-link dropdown-toggle" id="navbarAccount" data-toggle="dropdown" href="#" aria-haspopup="true" aria-expanded="false">
    -- My Account
    -- </a>
    -- <ul class="dropdown-menu" aria-labelledby="navbarAccount">
    -- <li class="dropdown-item dropright">
    -- <a class="dropdown-item" data-toggle="modal" href="#modalSigninHorizontal">
    -- Sign In
    -- </a>
    -- </li>
    -- <!--
    -- <li class="dropdown-item dropright">
    -- <a class="dropdown-item" href="@@webRoot/account-revenue.html">
    -- Revenue Dashboard
    -- </a>
    -- </li>
    -- <li class="dropdown-item dropright">
    -- <a class="dropdown-item" href="@@webRoot/account-auctions.html">
    -- Active Expense Auctions
    -- </a>
    -- </li>
    -- <li class="dropdown-item dropright">
    -- <a class="dropdown-item" href="@@webRoot/account-general.html">
    -- Project List
    -- </a>
    -- </li>
    -- <li class="dropdown-item dropright">
    -- <a class="dropdown-item" href="@@webRoot/account-tithes.html">
    -- My Tithes
    -- </a>
    -- </li>
    -- <li class="dropdown-item dropright">
    -- <a class="dropdown-item" href="@@webRoot/account-keys.html">
    -- Manage Payment Keys
    -- </a>
    -- </li>
    -- <li class="dropdown-item dropright">
    -- <a class="dropdown-item" href="@@webRoot/account-billing.html">
    -- Billing Center
    -- </a>
    -- </li>
    -- -->
    -- </ul>
    -- </li>
    -- <li class="nav-item dropdown">
    -- <a class="nav-link dropdown-toggle" id="navbarDocumentation" data-toggle="dropdown" href="#" aria-haspopup="true" aria-expanded="false">
    -- Documentation
    -- </a>
    -- <div class="dropdown-menu dropdown-menu-md" aria-labelledby="navbarDocumentation">
    -- <div class="list-group list-group-flush">
    -- <a class="list-group-item" href="@@webRoot/docs/index.html">
    --
    -- <!-- Icon -->
    -- <div class="icon icon-sm text-primary">
    -- @@include("../assets/img/icons/duotone-icons/General/Clipboard.svg")
    -- </div>
    --
    -- <!-- Content -->
    -- <div class="ml-4">
    --
    -- <!-- Heading -->
    -- <h6 class="font-weight-bold text-uppercase text-primary mb-0">
    -- Documentation
    -- </h6>
    --
    -- <!-- Text -->
    -- <p class="font-size-sm text-gray-700 mb-0">
    -- CLI & API user guide
    -- </p>
    --
    -- </div>
    --
    -- </a>
    -- <a class="list-group-item" href="@@webRoot/faq.html">
    --
    -- <!-- Icon -->
    -- <div class="icon icon-sm text-primary">
    -- @@include("../assets/img/icons/duotone-icons/Code/Question-circle.svg")
    -- </div>
    --
    -- <!-- Content -->
    -- <div class="ml-4">
    --
    -- <!-- Heading -->
    -- <h6 class="font-weight-bold text-uppercase text-primary mb-0">
    -- FAQ
    -- </h6>
    --
    -- <!-- Text -->
    -- <p class="font-size-sm text-gray-700 mb-0">
    -- Common problems & solutions
    -- </p>
    --
    -- </div>
    --
    -- </a>
    -- <a class="list-group-item" href="https://discord.gg/wbhCGjw" target="_blank">
    --
    -- <!-- Icon -->
    -- <div class="icon icon-sm text-primary">
    -- @@include("../assets/img/icons/social/discord.svg")
    -- </div>
    --
    -- <!-- Content -->
    -- <div class="ml-4">
    --
    -- <!-- Heading -->
    -- <h6 class="font-weight-bold text-uppercase text-primary mb-0">
    -- Community
    -- </h6>
    --
    -- <!-- Text -->
    -- <p class="font-size-sm text-gray-700 mb-0">
    -- Join our Discord chat
    -- </p>
    --
    -- </div>
    --
    -- </a>
    -- <!--
    -- <a class="list-group-item" href="@@webRoot/docs/changelog.html">
    --
    -- <div class="icon icon-sm text-primary">
    -- @@include("../assets/img/icons/duotone-icons/Files/File.svg")
    -- </div>
    --
    -- <div class="ml-4">
    --
    -- <h6 class="font-weight-bold text-uppercase text-primary mb-0">
    -- Changelog
    -- </h6>
    --
    -- <p class="font-size-sm text-gray-700 mb-0">
    -- Project history
    -- </p>
    -- </div>
    -- </a>
    -- -->
    -- </div>
    -- </div>
    -- </li>
    -- </ul>