all repos

onasty @ 9de58ad

a one-time notes service
8 files changed, 34 insertions(+), 21 deletions(-)
refactor(web): make use of types for handling auth (#137)

* refactor(web/auth): make impossible states impossible

While reading elm patterns I decided to drop Bools in models
https://sporto.github.io/elm-patterns/basic/impossible-states.html#make-impossible-states-impossible

* fixup! refactor(web/auth): make impossible states impossible

* web: if refresh failed, log user out

* web: fix formatting of interop.js

* fixup! web: fix formatting of interop.js
Author: Smirnov Oleksandr ss2316544@gmail.com
Committed by: GitHub noreply@github.com
Committed at: 2025-06-20 14:36:59 +0300
Parent: 58c535a
M web/src/Auth.elm
···
        25
        25
                         , hash = Nothing

      
        26
        26
                         }

      
        27
        27
         

      
        
        28
        +        Auth.User.RefreshingTokens ->

      
        
        29
        +            Auth.Action.loadCustomPage

      
        
        30
        +

      
        28
        31
                 Auth.User.SignedIn credentials ->

      
        29
        32
                     Auth.Action.loadPageWithUser credentials

      
        30
        33
         

      
M web/src/Auth/User.elm
···
        10
        10
         type SignInStatus

      
        11
        11
             = SignedIn User

      
        12
        12
             | NotSignedIn

      
        
        13
        +    | RefreshingTokens

      
M web/src/Effect.elm
···
        317
        317
                                     else

      
        318
        318
                                         []

      
        319
        319
         

      
        320
        
        -                        Auth.User.NotSignedIn ->

      
        
        320
        +                        _ ->

      
        321
        321
                                     []

      
        322
        322
                     in

      
        323
        323
                     Http.request

      
M web/src/Layouts/Header.elm
···
        87
        87
                                 ]

      
        88
        88
         

      
        89
        89
                             Auth.User.NotSignedIn ->

      
        90
        
        -                        [ Html.li [] [ viewNavLink ( "sign in", Route.Path.Auth ) ]

      
        91
        
        -                        ]

      
        
        90
        +                        viewNotSignedInNav

      
        
        91
        +

      
        
        92
        +                    Auth.User.RefreshingTokens ->

      
        
        93
        +                        viewNotSignedInNav

      
        92
        94
                         )

      
        93
        95
                     ]

      
        94
        96
                 ]

      
        
        97
        +

      
        
        98
        +

      
        
        99
        +viewNotSignedInNav : List (Html msg)

      
        
        100
        +viewNotSignedInNav =

      
        
        101
        +    [ Html.li [] [ viewNavLink ( "sign in", Route.Path.Auth ) ]

      
        
        102
        +    ]

      
        95
        103
         

      
        96
        104
         

      
        97
        105
         viewNavLink : ( String, Route.Path.Path ) -> Html msg

      
M web/src/Pages/Auth.elm
···
        57
        57
         

      
        58
        58
                 Auth.User.NotSignedIn ->

      
        59
        59
                     Effect.none

      
        
        60
        +

      
        
        61
        +        Auth.User.RefreshingTokens ->

      
        
        62
        +            Effect.none

      
        60
        63
             )

      
        61
        64
         

      
        62
        65
         

      
M web/src/Shared.elm
···
        79
        79
                 initModel =

      
        80
        80
                     { user = user

      
        81
        81
                     , timeZone = Time.utc

      
        82
        
        -            , isRefreshingTokens = False

      
        83
        82
                     }

      
        84
        83
             in

      
        85
        84
             ( initModel

      ···
        128
        127
                             else

      
        129
        128
                                 ( model, Effect.none )

      
        130
        129
         

      
        131
        
        -                Auth.User.NotSignedIn ->

      
        
        130
        +                _ ->

      
        132
        131
                             ( model, Effect.none )

      
        133
        132
         

      
        134
        133
                 Shared.Msg.TriggerTokenRefresh ->

      
        135
        134
                     case model.user of

      
        136
        135
                         Auth.User.SignedIn credentials ->

      
        137
        
        -                    ( { model | isRefreshingTokens = True }

      
        
        136
        +                    ( { model | user = Auth.User.RefreshingTokens }

      
        138
        137
                             , Api.Auth.refreshToken

      
        139
        138
                                 { onResponse = Shared.Msg.ApiRefreshTokensResponded

      
        140
        139
                                 , refreshToken = credentials.refreshToken

      
        141
        140
                                 }

      
        142
        141
                             )

      
        143
        142
         

      
        144
        
        -                Auth.User.NotSignedIn ->

      
        
        143
        +                _ ->

      
        145
        144
                             ( model, Effect.none )

      
        146
        145
         

      
        147
        146
                 Shared.Msg.ApiRefreshTokensResponded (Ok credentials) ->

      
        148
        
        -            ( { model | isRefreshingTokens = False, user = Auth.User.SignedIn credentials }

      
        
        147
        +            ( { model | user = Auth.User.SignedIn credentials }

      
        149
        148
                     , Effect.saveUser credentials.accessToken credentials.refreshToken

      
        150
        149
                     )

      
        151
        150
         

      
        152
        151
                 Shared.Msg.ApiRefreshTokensResponded (Err _) ->

      
        153
        
        -            ( { model | isRefreshingTokens = False }, Effect.clearUser )

      
        
        152
        +            ( { model | user = Auth.User.NotSignedIn }, Effect.clearUser )

      
        154
        153
         

      
        155
        154
         

      
        156
        155
         

      
M web/src/Shared/Model.elm
···
        7
        7
         type alias Model =

      
        8
        8
             { user : Auth.User.SignInStatus

      
        9
        9
             , timeZone : Time.Zone

      
        10
        
        -    , isRefreshingTokens : Bool

      
        11
        10
             }

      
M web/src/interop.js
···
        1
        1
         import "./styles.css";

      
        2
        2
         

      
        3
        3
         export const flags = (_) => {

      
        4
        
        -    return {

      
        5
        
        -        access_token: JSON.parse(window.localStorage.access_token || 'null'),

      
        6
        
        -        refresh_token: JSON.parse(window.localStorage.refresh_token || 'null'),

      
        7
        
        -    }

      
        8
        
        -}

      
        
        4
        +  return {

      
        
        5
        +    access_token: JSON.parse(window.localStorage.access_token || "null"),

      
        
        6
        +    refresh_token: JSON.parse(window.localStorage.refresh_token || "null"),

      
        
        7
        +  };

      
        
        8
        +};

      
        9
        9
         

      
        10
        10
         export const onReady = ({ app }) => {

      
        11
        
        -    if (app.ports?.sendToLocalStorage) {

      
        12
        
        -        app.ports.sendToLocalStorage.subscribe(({ key, value }) => {

      
        13
        
        -            window.localStorage[key] = JSON.stringify(value);

      
        14
        
        -        })

      
        15
        
        -    }

      
        16
        
        -}

      
        
        11
        +  if (app.ports?.sendToLocalStorage) {

      
        
        12
        +    app.ports.sendToLocalStorage.subscribe(({ key, value }) => {

      
        
        13
        +      window.localStorage[key] = JSON.stringify(value);

      
        
        14
        +    });

      
        
        15
        +  }

      
        
        16
        +};