3 files changed,
89 insertions(+),
30 deletions(-)
Author:
Smirnov Oleksandr
ss2316544@gmail.com
Committed by:
GitHub
noreply@github.com
Committed at:
2025-06-21 14:53:11 +0300
Parent:
dfbb8b4
jump to
| M | web/src/Api/Auth.elm |
| M | web/src/Effect.elm |
| M | web/src/Pages/Auth.elm |
M
web/src/Api/Auth.elm
··· 1 -module Api.Auth exposing (refreshToken, signin, signup) 1 +module Api.Auth exposing (refreshToken, resendVerificationEmail, signin, signup) 2 2 3 3 import Api 4 4 import Data.Credentials as Credentials exposing (Credentials) ··· 75 75 , onResponse = options.onResponse 76 76 , decoder = Credentials.decode 77 77 } 78 + 79 + 80 +resendVerificationEmail : 81 + { onResponse : Result Api.Error () -> msg 82 + , email : String 83 + , password : String 84 + } 85 + -> Effect msg 86 +resendVerificationEmail options = 87 + let 88 + body : Encode.Value 89 + body = 90 + Encode.object 91 + [ ( "email", Encode.string options.email ) 92 + , ( "password", Encode.string options.password ) 93 + ] 94 + in 95 + Effect.sendApiRequest 96 + { endpoint = "/api/v1/auth/resend-verification-email" 97 + , method = "POST" 98 + , body = Http.jsonBody body 99 + , onResponse = options.onResponse 100 + , decoder = Decode.succeed () 101 + }
M
web/src/Effect.elm
··· 176 176 -> Effect msg 177 177 sendApiRequest opts = 178 178 let 179 - onSuccess : value -> msg 180 - onSuccess value = 181 - opts.onResponse (Ok value) 182 - 183 179 onHttpError : Api.Error -> msg 184 180 onHttpError err = 185 181 opts.onResponse (Err err) 182 + 183 + decoder : Json.Decode.Decoder msg 184 + decoder = 185 + opts.decoder 186 + |> Json.Decode.map Ok 187 + |> Json.Decode.map opts.onResponse 186 188 in 187 189 SendApiRequest 188 190 { endpoint = opts.endpoint 189 191 , method = opts.method 190 192 , body = opts.body 191 193 , onHttpError = onHttpError 192 - , decoder = Json.Decode.map onSuccess opts.decoder 194 + , decoder = decoder 193 195 } 194 196 195 197 ··· 353 355 fromHttpResponseToCustomError decoder response = 354 356 case response of 355 357 Http.GoodStatus_ _ body -> 356 - case Json.Decode.decodeString decoder body of 357 - Ok data -> 358 - Ok data 358 + case 359 + Json.Decode.decodeString decoder 360 + (if String.isEmpty body then 361 + "\"\"" 362 + 363 + else 364 + body 365 + ) 366 + of 367 + Ok value -> 368 + Ok value 359 369 360 370 Err err -> 361 - Err (Api.JsonDecodeError { message = "Something unexpected happened", reason = err }) 371 + Err (Api.JsonDecodeError { message = "Failed to decode JSON response", reason = err }) 362 372 363 373 Http.BadStatus_ { statusCode } body -> 364 374 case Json.Decode.decodeString Data.Error.decode body of
M
web/src/Pages/Auth.elm
··· 37 37 , passwordAgain : String 38 38 , isSubmittingForm : Bool 39 39 , formVariant : Variant 40 - , error : Maybe Api.Error 40 + , gotSignedUp : Bool 41 + , apiError : Maybe Api.Error 41 42 } 42 43 43 44 ··· 48 49 , password = "" 49 50 , passwordAgain = "" 50 51 , formVariant = SignIn 51 - , error = Nothing 52 + , apiError = Nothing 53 + , gotSignedUp = False 52 54 } 53 55 , case shared.user of 54 56 Auth.User.SignedIn _ -> 55 57 Effect.pushRoutePath Route.Path.Home_ 56 58 57 - Auth.User.NotSignedIn -> 58 - Effect.none 59 - 60 - Auth.User.RefreshingTokens -> 59 + _ -> 61 60 Effect.none 62 61 ) 63 62 ··· 70 69 = UserUpdatedInput Field String 71 70 | UserChangedFormVariant Variant 72 71 | UserClickedSubmit 72 + | UserClickedResendActivationEmail 73 73 | ApiSignInResponded (Result Api.Error Credentials) 74 74 | ApiSignUpResponded (Result Api.Error ()) 75 + | ApiResendVerificationEmail (Result Api.Error ()) 75 76 76 77 77 78 type Field ··· 89 90 update msg model = 90 91 case msg of 91 92 UserClickedSubmit -> 92 - ( { model | isSubmittingForm = True } 93 + ( { model | isSubmittingForm = True, apiError = Nothing } 93 94 , case model.formVariant of 94 95 SignIn -> 95 96 Api.Auth.signin ··· 106 107 } 107 108 ) 108 109 110 + UserClickedResendActivationEmail -> 111 + ( model 112 + , Api.Auth.resendVerificationEmail 113 + { onResponse = ApiResendVerificationEmail 114 + , email = model.email 115 + , password = model.password 116 + } 117 + ) 118 + 109 119 UserChangedFormVariant variant -> 110 120 ( { model | formVariant = variant }, Effect.none ) 111 121 ··· 119 129 ( { model | passwordAgain = passwordAgain }, Effect.none ) 120 130 121 131 ApiSignInResponded (Ok credentials) -> 122 - ( { model | isSubmittingForm = False } 123 - , Effect.signin credentials 124 - ) 132 + ( { model | isSubmittingForm = False }, Effect.signin credentials ) 125 133 126 134 ApiSignInResponded (Err error) -> 127 - ( { model | isSubmittingForm = False, error = Just error }, Effect.none ) 135 + ( { model | isSubmittingForm = False, apiError = Just error }, Effect.none ) 128 136 129 137 ApiSignUpResponded (Ok ()) -> 130 - -- TODO: show banner with that they have to activate account 131 - ( { model | isSubmittingForm = False }, Effect.none ) 138 + ( { model | isSubmittingForm = False, gotSignedUp = True }, Effect.none ) 132 139 133 140 ApiSignUpResponded (Err error) -> 134 - ( { model | isSubmittingForm = False, error = Just error }, Effect.none ) 141 + ( { model | isSubmittingForm = False, apiError = Just error }, Effect.none ) 142 + 143 + ApiResendVerificationEmail (Ok ()) -> 144 + ( { model | apiError = Nothing }, Effect.none ) 145 + 146 + ApiResendVerificationEmail (Err err) -> 147 + ( { model | apiError = Just err }, Effect.none ) 135 148 136 149 137 150 ··· 153 166 , body = 154 167 [ Html.div [ Attr.class "center" ] 155 168 -- TODO: add oauth buttons 156 - [ viewError model.error 169 + [ viewBanner model.apiError model.gotSignedUp 157 170 , viewChangeVariant model.formVariant 158 171 , viewForm model 159 172 , viewForgotPassword ··· 197 210 ) 198 211 199 212 200 -viewError : Maybe Api.Error -> Html Msg 201 -viewError maybeError = 202 - case maybeError of 203 - Just error -> 213 +viewBanner : Maybe Api.Error -> Bool -> Html Msg 214 +viewBanner maybeError gotSignedUp = 215 + case ( maybeError, gotSignedUp ) of 216 + ( Just error, _ ) -> 204 217 Html.div [ Attr.class "box bad" ] 205 218 [ Html.strong [ Attr.class "block titlebar" ] [ Html.text "Error" ] 206 219 , Html.text (Api.errorMessage error) 207 220 ] 208 221 209 - Nothing -> 222 + ( Nothing, True ) -> 223 + Html.div [ Attr.class "box ok" ] 224 + [ Html.strong [ Attr.class "block titlebar" ] [ Html.text "Successfully signed up!" ] 225 + , Html.p [] 226 + [ Html.text "Please check your email to activate your account." 227 + , Html.text " If you don't see the email, please check your spam folder." 228 + , Html.button [ Html.Events.onClick UserClickedResendActivationEmail ] 229 + [ Html.text "Resend activation email" 230 + ] 231 + ] 232 + ] 233 + 234 + ( Nothing, False ) -> 210 235 Html.text "" 211 236 212 237