all repos

onasty @ 9c8b9eae5400bed303e3892d640786b0cb0b3554

a one-time notes service

onasty/web/src/Shared.elm (view raw)

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
refactor(web): make use of types for handling auth (#137)..., 11 months ago
1
module Shared exposing
2
    ( Flags, decoder
3
    , Model, Msg
4
    , init, update, subscriptions
5
    )
6
7
{-|
8
9
@docs Flags, decoder
10
@docs Model, Msg
11
@docs init, update, subscriptions
12
13
-}
14
15
import Api.Auth
16
import Auth.User
17
import Data.Credentials exposing (Credentials)
18
import Dict
19
import Effect exposing (Effect)
20
import Json.Decode
21
import JwtUtil
22
import Route exposing (Route)
23
import Route.Path
24
import Shared.Model
25
import Shared.Msg
26
import Task
27
import Time
28
29
30
31
-- FLAGS
32
33
34
type alias Flags =
35
    { accessToken : Maybe String
36
    , refreshToken : Maybe String
37
    }
38
39
40
decoder : Json.Decode.Decoder Flags
41
decoder =
42
    Json.Decode.map2 Flags
43
        (Json.Decode.field "access_token" (Json.Decode.maybe Json.Decode.string))
44
        (Json.Decode.field "refresh_token" (Json.Decode.maybe Json.Decode.string))
45
46
47
48
-- INIT
49
50
51
type alias Model =
52
    Shared.Model.Model
53
54
55
init : Result Json.Decode.Error Flags -> Route () -> ( Model, Effect Msg )
56
init flagsResult _ =
57
    let
58
        flags : Flags
59
        flags =
60
            flagsResult |> Result.withDefault { accessToken = Nothing, refreshToken = Nothing }
61
62
        maybeCredentials : Maybe Credentials
63
        maybeCredentials =
64
            Maybe.map2
65
                (\access refresh -> { accessToken = access, refreshToken = refresh })
66
                flags.accessToken
67
                flags.refreshToken
68
69
        user : Auth.User.SignInStatus
70
        user =
71
            case maybeCredentials of
72
                Just credentials ->
73
                    Auth.User.SignedIn credentials
74
75
                Nothing ->
76
                    Auth.User.NotSignedIn
77
78
        initModel : Model
79
        initModel =
80
            { user = user
81
            , timeZone = Time.utc
82
            }
83
    in
84
    ( initModel
85
    , Effect.batch
86
        [ Time.now |> Task.perform Shared.Msg.CheckTokenExpiration |> Effect.sendCmd
87
        , Time.here |> Task.perform Shared.Msg.GotZone |> Effect.sendCmd
88
        ]
89
    )
90
91
92
93
-- UPDATE
94
95
96
type alias Msg =
97
    Shared.Msg.Msg
98
99
100
update : Route () -> Msg -> Model -> ( Model, Effect Msg )
101
update _ msg model =
102
    case msg of
103
        Shared.Msg.GotZone timeZone ->
104
            ( { model | timeZone = timeZone }, Effect.none )
105
106
        Shared.Msg.Logout ->
107
            ( { model | user = Auth.User.NotSignedIn }, Effect.clearUser )
108
109
        Shared.Msg.SignedIn credentials ->
110
            ( { model | user = Auth.User.SignedIn credentials }
111
            , Effect.batch
112
                [ Effect.pushRoute
113
                    { path = Route.Path.Home_
114
                    , query = Dict.empty
115
                    , hash = Nothing
116
                    }
117
                , Effect.saveUser credentials.accessToken credentials.refreshToken
118
                ]
119
            )
120
121
        Shared.Msg.CheckTokenExpiration now ->
122
            case model.user of
123
                Auth.User.SignedIn credentials ->
124
                    if JwtUtil.isExpired now credentials.accessToken then
125
                        ( model, Effect.refreshTokens )
126
127
                    else
128
                        ( model, Effect.none )
129
130
                _ ->
131
                    ( model, Effect.none )
132
133
        Shared.Msg.TriggerTokenRefresh ->
134
            case model.user of
135
                Auth.User.SignedIn credentials ->
136
                    ( { model | user = Auth.User.RefreshingTokens }
137
                    , Api.Auth.refreshToken
138
                        { onResponse = Shared.Msg.ApiRefreshTokensResponded
139
                        , refreshToken = credentials.refreshToken
140
                        }
141
                    )
142
143
                _ ->
144
                    ( model, Effect.none )
145
146
        Shared.Msg.ApiRefreshTokensResponded (Ok credentials) ->
147
            ( { model | user = Auth.User.SignedIn credentials }
148
            , Effect.saveUser credentials.accessToken credentials.refreshToken
149
            )
150
151
        Shared.Msg.ApiRefreshTokensResponded (Err _) ->
152
            ( { model | user = Auth.User.NotSignedIn }, Effect.clearUser )
153
154
155
156
-- SUBSCRIPTIONS
157
158
159
subscriptions : Route () -> Model -> Sub Msg
160
subscriptions _ _ =
161
    Time.every (30 * 1000) Shared.Msg.CheckTokenExpiration