all repos

onasty @ 5bdd1f8

a one-time notes service
2 files changed, 28 insertions(+), 16 deletions(-)
web: prompt non verified user to activate their account on sing in (#160)

* web: add helper for checking if a user is not verified while logging in

* refactor(web): show verification banner on successful registration, and
if user is not verified and trying to sign in
Author: Olexandr Smirnov ss2316544@gmail.com
Committed by: GitHub noreply@github.com
Committed at: 2025-07-07 13:19:30 +0300
Parent: f537564
M web/src/Api.elm
···
        1
        
        -module Api exposing (Error(..), Response(..), errorMessage, is404)

      
        
        1
        +module Api exposing (Error(..), Response(..), errorMessage, is404, isNotVerified)

      
        2
        2
         

      
        3
        3
         import Http

      
        4
        4
         import Json.Decode

      ···
        39
        39
         

      
        40
        40
                 _ ->

      
        41
        41
                     False

      
        
        42
        +

      
        
        43
        +

      
        
        44
        +isNotVerified : Error -> Bool

      
        
        45
        +isNotVerified error =

      
        
        46
        +    case error of

      
        
        47
        +        HttpError { reason, message } ->

      
        
        48
        +            (reason == Http.BadStatus 400) && String.contains "user is not activated" message

      
        
        49
        +

      
        
        50
        +        _ ->

      
        
        51
        +            False

      
M web/src/Pages/Auth.elm
···
        41
        41
             , passwordAgain : String

      
        42
        42
             , isSubmittingForm : Bool

      
        43
        43
             , formVariant : Variant

      
        44
        
        -    , gotSignedUp : Bool

      
        
        44
        +    , showVerifyBanner : Bool

      
        45
        45
             , lastClicked : Maybe Posix

      
        46
        46
             , apiError : Maybe Api.Error

      
        47
        47
             , now : Maybe Posix

      ···
        50
        50
         

      
        51
        51
         init : Shared.Model -> () -> ( Model, Effect Msg )

      
        52
        52
         init shared _ =

      
        53
        
        -    ( { email = ""

      
        
        53
        +    ( { formVariant = SignIn

      
        
        54
        +      , isSubmittingForm = False

      
        
        55
        +      , email = ""

      
        54
        56
               , password = ""

      
        55
        57
               , passwordAgain = ""

      
        56
        
        -      , isSubmittingForm = False

      
        57
        
        -      , formVariant = SignIn

      
        58
        
        -      , gotSignedUp = False

      
        
        58
        +      , showVerifyBanner = False

      
        59
        59
               , lastClicked = Nothing

      
        60
        60
               , apiError = Nothing

      
        61
        61
               , now = Nothing

      ···
        144
        144
                     ( { model | isSubmittingForm = False }, Effect.signin credentials )

      
        145
        145
         

      
        146
        146
                 ApiSignInResponded (Err error) ->

      
        147
        
        -            -- TODO: check if error is Unauthorized and prompt use to activate account

      
        148
        
        -            ( { model | isSubmittingForm = False, apiError = Just error }, Effect.none )

      
        
        147
        +            if Api.isNotVerified error then

      
        
        148
        +                ( { model | isSubmittingForm = False, apiError = Nothing, showVerifyBanner = True }, Effect.none )

      
        
        149
        +

      
        
        150
        +            else

      
        
        151
        +                ( { model | isSubmittingForm = False, apiError = Just error }, Effect.none )

      
        149
        152
         

      
        150
        153
                 ApiSignUpResponded (Ok ()) ->

      
        151
        
        -            ( { model | isSubmittingForm = False, gotSignedUp = True }, Effect.none )

      
        
        154
        +            ( { model | isSubmittingForm = False, showVerifyBanner = True }, Effect.none )

      
        152
        155
         

      
        153
        156
                 ApiSignUpResponded (Err error) ->

      
        154
        157
                     ( { model | isSubmittingForm = False, apiError = Just error }, Effect.none )

      ···
        166
        169
         

      
        167
        170
         subscriptions : Model -> Sub Msg

      
        168
        171
         subscriptions model =

      
        169
        
        -    if model.gotSignedUp then

      
        
        172
        +    if model.showVerifyBanner then

      
        170
        173
                 Time.every 1000 Tick

      
        171
        174
         

      
        172
        175
             else

      ···
        199
        202
         

      
        200
        203
         viewBanner : Model -> Html Msg

      
        201
        204
         viewBanner model =

      
        202
        
        -    case ( model.apiError, model.gotSignedUp ) of

      
        
        205
        +    case ( model.apiError, model.showVerifyBanner ) of

      
        203
        206
                 ( Just error, False ) ->

      
        204
        207
                     Components.Error.error (Api.errorMessage error)

      
        205
        208
         

      
        206
        209
                 ( Nothing, True ) ->

      
        207
        
        -            viewBannerSuccess model.now model.lastClicked

      
        
        210
        +            viewVerificationBanner model.now model.lastClicked

      
        208
        211
         

      
        209
        212
                 _ ->

      
        210
        213
                     H.text ""

      
        211
        214
         

      
        212
        215
         

      
        213
        
        -viewBannerSuccess : Maybe Posix -> Maybe Posix -> Html Msg

      
        214
        
        -viewBannerSuccess now lastClicked =

      
        
        216
        +viewVerificationBanner : Maybe Posix -> Maybe Posix -> Html Msg

      
        
        217
        +viewVerificationBanner now lastClicked =

      
        215
        218
             let

      
        216
        219
                 buttonClassesBase =

      
        217
        220
                     "w-full px-4 py-2 rounded-md focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 transition-colors mt-3"

      ···
        223
        226
                     else

      
        224
        227
                         buttonClassesBase ++ " border border-gray-300 text-gray-400 cursor-not-allowed"

      
        225
        228
         

      
        226
        
        -        timeLeftSeconds : Int

      
        227
        229
                 timeLeftSeconds =

      
        228
        230
                     case ( now, lastClicked ) of

      
        229
        231
                         ( Just now_, Just last ) ->

      ···
        242
        244
             in

      
        243
        245
             H.div [ A.class "bg-green-50 border border-green-200 rounded-md p-4 mb-4" ]

      
        244
        246
                 [ H.div [ A.class "font-medium text-green-800 mb-2" ] [ H.text "Check your email!" ]

      
        245
        
        -        , H.p [ A.class "text-green-800 text-sm" ] [ H.text "We've sent you a verification link. Please check your email and click the link to activate your account." ]

      
        
        247
        +        , H.p [ A.class "text-green-800 text-sm" ] [ H.text "Please verify your account to continue. We've sent a verification link to your email — click it to activate your account." ]

      
        246
        248
                 , H.button

      
        247
        249
                     [ A.class (buttonClasses canClick)

      
        248
        250
                     , E.onClick UserClickedResendActivationEmail