all repos

onasty @ main

a one-time notes service

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
module Shared exposing (Flags, Model, Msg, decoder, init, subscriptions, update)

import Api.Auth
import Auth.User
import Dict
import Effect exposing (Effect)
import Json.Decode
import JwtUtil
import Route exposing (Route)
import Route.Path
import Shared.Model
import Shared.Msg
import Task
import Time



-- FLAGS


type alias Flags =
    { accessToken : Maybe String
    , refreshToken : Maybe String
    , appUrl : String
    }


decoder : Json.Decode.Decoder Flags
decoder =
    Json.Decode.map3 Flags
        (Json.Decode.field "access_token" (Json.Decode.maybe Json.Decode.string))
        (Json.Decode.field "refresh_token" (Json.Decode.maybe Json.Decode.string))
        (Json.Decode.field "app_url" Json.Decode.string)



-- INIT


type alias Model =
    Shared.Model.Model


init : Result Json.Decode.Error Flags -> Route () -> ( Model, Effect Msg )
init flagsResult _ =
    let
        flags =
            flagsResult |> Result.withDefault { accessToken = Nothing, refreshToken = Nothing, appUrl = "" }

        user =
            case
                Maybe.map2 (\access refresh -> { accessToken = access, refreshToken = refresh })
                    flags.accessToken
                    flags.refreshToken
            of
                Just credentials ->
                    Auth.User.SignedIn credentials

                Nothing ->
                    Auth.User.NotSignedIn
    in
    ( { user = user
      , timeZone = Time.utc
      , appURL = flags.appUrl
      }
    , Effect.batch
        [ Time.here |> Task.perform Shared.Msg.GotZone |> Effect.sendCmd
        , Time.now |> Task.perform Shared.Msg.CheckTokenExpiration |> Effect.sendCmd
        ]
    )



-- UPDATE


type alias Msg =
    Shared.Msg.Msg


update : Route () -> Msg -> Model -> ( Model, Effect Msg )
update _ msg model =
    case msg of
        Shared.Msg.GotZone timeZone ->
            ( { model | timeZone = timeZone }, Effect.none )

        Shared.Msg.Logout ->
            ( { model | user = Auth.User.NotSignedIn }, Effect.clearUser )

        Shared.Msg.SignedIn credentials ->
            ( { model | user = Auth.User.SignedIn credentials }
            , Effect.batch
                [ Effect.pushRoute { path = Route.Path.Home_, query = Dict.empty, hash = Nothing }
                , Effect.saveUser credentials.accessToken credentials.refreshToken
                ]
            )

        Shared.Msg.CheckTokenExpiration now ->
            case model.user of
                Auth.User.SignedIn credentials ->
                    if JwtUtil.isExpired now credentials.accessToken then
                        ( model, Effect.refreshTokens )

                    else
                        ( model, Effect.none )

                _ ->
                    ( model, Effect.none )

        Shared.Msg.TriggerTokenRefresh ->
            case model.user of
                Auth.User.SignedIn credentials ->
                    ( { model | user = Auth.User.RefreshingTokens }
                    , Api.Auth.refreshToken
                        { onResponse = Shared.Msg.ApiRefreshTokensResponded
                        , refreshToken = credentials.refreshToken
                        }
                    )

                _ ->
                    ( model, Effect.none )

        Shared.Msg.ApiRefreshTokensResponded (Ok credentials) ->
            ( { model | user = Auth.User.SignedIn credentials }
            , Effect.saveUser credentials.accessToken credentials.refreshToken
            )

        Shared.Msg.ApiRefreshTokensResponded (Err _) ->
            ( { model | user = Auth.User.NotSignedIn }, Effect.clearUser )



-- SUBSCRIPTIONS


subscriptions : Route () -> Model -> Sub Msg
subscriptions _ _ =
    Time.every (30 * 1000) Shared.Msg.CheckTokenExpiration