all repos

onasty @ 58c535a5aae5dee03a5cbcb2efc82e1426dcea76

a one-time notes service

onasty/web/src/Pages/Auth.elm (view raw)

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
web: add some styles (#136)..., 11 months ago
1
module Pages.Auth exposing (Model, Msg, Variant, page)
2
3
import Api
4
import Api.Auth
5
import Auth.User
6
import Data.Credentials exposing (Credentials)
7
import Effect exposing (Effect)
8
import Html exposing (Html)
9
import Html.Attributes as Attr
10
import Html.Events
11
import Http
12
import Layouts
13
import Page exposing (Page)
14
import Route exposing (Route)
15
import Route.Path
16
import Shared
17
import View exposing (View)
18
19
20
page : Shared.Model -> Route () -> Page Model Msg
21
page shared _ =
22
    Page.new
23
        { init = init shared
24
        , update = update
25
        , subscriptions = subscriptions
26
        , view = view
27
        }
28
        |> Page.withLayout (\_ -> Layouts.Header {})
29
30
31
32
-- INIT
33
34
35
type alias Model =
36
    { email : String
37
    , password : String
38
    , passwordAgain : String
39
    , isSubmittingForm : Bool
40
    , formVariant : Variant
41
    , error : Maybe Http.Error
42
    }
43
44
45
init : Shared.Model -> () -> ( Model, Effect Msg )
46
init shared _ =
47
    ( { isSubmittingForm = False
48
      , email = ""
49
      , password = ""
50
      , passwordAgain = ""
51
      , formVariant = SignIn
52
      , error = Nothing
53
      }
54
    , case shared.user of
55
        Auth.User.SignedIn _ ->
56
            Effect.pushRoutePath Route.Path.Home_
57
58
        Auth.User.NotSignedIn ->
59
            Effect.none
60
    )
61
62
63
64
-- UPDATE
65
66
67
type Msg
68
    = UserUpdatedInput Field String
69
    | UserChangedFormVariant Variant
70
    | UserClickedSubmit
71
    | ApiSignInResponded (Result Http.Error Credentials)
72
    | ApiSignUpResponded (Result Http.Error ())
73
74
75
type Field
76
    = Email
77
    | Password
78
    | PasswordAgain
79
80
81
type Variant
82
    = SignIn
83
    | SignUp
84
85
86
update : Msg -> Model -> ( Model, Effect Msg )
87
update msg model =
88
    case msg of
89
        UserClickedSubmit ->
90
            ( { model | isSubmittingForm = True }
91
            , case model.formVariant of
92
                SignIn ->
93
                    Api.Auth.signin
94
                        { onResponse = ApiSignInResponded
95
                        , email = model.email
96
                        , password = model.password
97
                        }
98
99
                SignUp ->
100
                    Api.Auth.signup
101
                        { onResponse = ApiSignUpResponded
102
                        , email = model.email
103
                        , password = model.password
104
                        }
105
            )
106
107
        UserChangedFormVariant variant ->
108
            ( { model | formVariant = variant }, Effect.none )
109
110
        UserUpdatedInput Email email ->
111
            ( { model | email = email }, Effect.none )
112
113
        UserUpdatedInput Password password ->
114
            ( { model | password = password }, Effect.none )
115
116
        UserUpdatedInput PasswordAgain passwordAgain ->
117
            ( { model | passwordAgain = passwordAgain }, Effect.none )
118
119
        ApiSignInResponded (Ok credentials) ->
120
            ( { model | isSubmittingForm = False }
121
            , Effect.signin credentials
122
            )
123
124
        ApiSignInResponded (Err error) ->
125
            ( { model | isSubmittingForm = False, error = Just error }, Effect.none )
126
127
        ApiSignUpResponded (Ok ()) ->
128
            -- TODO: show banner with that they have to activate account
129
            ( { model | isSubmittingForm = False }, Effect.none )
130
131
        ApiSignUpResponded (Err error) ->
132
            ( { model | isSubmittingForm = False, error = Just error }, Effect.none )
133
134
135
136
-- SUBSCRIPTIONS
137
138
139
subscriptions : Model -> Sub Msg
140
subscriptions _ =
141
    Sub.none
142
143
144
145
-- VIEW
146
147
148
view : Model -> View Msg
149
view model =
150
    { title = "Authentication"
151
    , body =
152
        [ Html.div [ Attr.class "center" ]
153
            -- TODO: add oauth buttons
154
            [ viewError model.error
155
            , viewChangeVariant model.formVariant
156
            , viewForm model
157
            , viewForgotPassword
158
            ]
159
        ]
160
    }
161
162
163
viewChangeVariant : Variant -> Html Msg
164
viewChangeVariant variant =
165
    Html.div [ Attr.class "mb1" ]
166
        [ Html.button
167
            [ Attr.disabled (variant == SignIn)
168
            , Html.Events.onClick (UserChangedFormVariant SignIn)
169
            ]
170
            [ Html.text "Sign In" ]
171
        , Html.button
172
            [ Attr.disabled (variant == SignUp)
173
            , Html.Events.onClick (UserChangedFormVariant SignUp)
174
            ]
175
            [ Html.text "Sign Up" ]
176
        ]
177
178
179
viewForm : Model -> Html Msg
180
viewForm model =
181
    Html.form [ Html.Events.onSubmit UserClickedSubmit ]
182
        (case model.formVariant of
183
            SignIn ->
184
                [ viewFormInput { field = Email, value = model.email }
185
                , viewFormInput { field = Password, value = model.password }
186
                , viewSubmitButton model
187
                ]
188
189
            SignUp ->
190
                [ viewFormInput { field = Email, value = model.email }
191
                , viewFormInput { field = Password, value = model.password }
192
                , viewFormInput { field = PasswordAgain, value = model.passwordAgain }
193
                , viewSubmitButton model
194
                ]
195
        )
196
197
198
viewError : Maybe Http.Error -> Html Msg
199
viewError maybeError =
200
    case maybeError of
201
        Just error ->
202
            Html.div [ Attr.class "box bad" ]
203
                [ Html.strong [ Attr.class "block titlebar" ] [ Html.text "Error" ]
204
                , Html.text (Api.errorToFriendlyMessage error)
205
                ]
206
207
        Nothing ->
208
            Html.text ""
209
210
211
viewFormInput : { field : Field, value : String } -> Html Msg
212
viewFormInput opts =
213
    Html.div [ Attr.class "mb1" ]
214
        [ Html.label [] [ Html.text (fromFieldToLabel opts.field) ]
215
        , Html.div []
216
            [ Html.input
217
                [ Attr.type_ (fromFieldToInputType opts.field)
218
                , Attr.value opts.value
219
                , Html.Events.onInput (UserUpdatedInput opts.field)
220
                ]
221
                []
222
            ]
223
        ]
224
225
226
viewForgotPassword : Html Msg
227
viewForgotPassword =
228
    Html.div []
229
        [ Html.a
230
            [ Attr.href "/forgot-password"
231
            , Attr.class "gray"
232
            ]
233
            [ Html.text "Forgot password?" ]
234
        ]
235
236
237
viewSubmitButton : Model -> Html Msg
238
viewSubmitButton model =
239
    Html.div [ Attr.class "mb1" ]
240
        [ Html.button
241
            [ Attr.disabled (isFormDisabled model) ]
242
            [ Html.text (fromVariantToLabel model.formVariant) ]
243
        ]
244
245
246
isFormDisabled : Model -> Bool
247
isFormDisabled model =
248
    case model.formVariant of
249
        SignIn ->
250
            model.isSubmittingForm
251
                || String.isEmpty model.email
252
                || String.isEmpty model.password
253
254
        SignUp ->
255
            model.isSubmittingForm
256
                || String.isEmpty model.email
257
                || String.isEmpty model.password
258
                || String.isEmpty model.passwordAgain
259
                || (model.password /= model.passwordAgain)
260
261
262
fromVariantToLabel : Variant -> String
263
fromVariantToLabel variant =
264
    case variant of
265
        SignIn ->
266
            "Sign In"
267
268
        SignUp ->
269
            "Sign Up"
270
271
272
fromFieldToLabel : Field -> String
273
fromFieldToLabel field =
274
    case field of
275
        Email ->
276
            "Email address"
277
278
        Password ->
279
            "Password"
280
281
        PasswordAgain ->
282
            "Password again"
283
284
285
fromFieldToInputType : Field -> String
286
fromFieldToInputType field =
287
    case field of
288
        Email ->
289
            "email"
290
291
        Password ->
292
            "password"
293
294
        PasswordAgain ->
295
            "password"