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,6 +25,9 @@ , query = Dict.empty

, hash = Nothing } + Auth.User.RefreshingTokens -> + Auth.Action.loadCustomPage + Auth.User.SignedIn credentials -> Auth.Action.loadPageWithUser credentials
M web/src/Auth/User.elm

@@ -10,3 +10,4 @@

type SignInStatus = SignedIn User | NotSignedIn + | RefreshingTokens
M web/src/Effect.elm

@@ -317,7 +317,7 @@

else [] - Auth.User.NotSignedIn -> + _ -> [] in Http.request
M web/src/Layouts/Header.elm

@@ -87,11 +87,19 @@ , Html.li [] [ Html.a [ Html.Events.onClick UserClickedLogout ] [ Html.text "logout" ] ]

] Auth.User.NotSignedIn -> - [ Html.li [] [ viewNavLink ( "sign in", Route.Path.Auth ) ] - ] + viewNotSignedInNav + + Auth.User.RefreshingTokens -> + viewNotSignedInNav ) ] ] + + +viewNotSignedInNav : List (Html msg) +viewNotSignedInNav = + [ Html.li [] [ viewNavLink ( "sign in", Route.Path.Auth ) ] + ] viewNavLink : ( String, Route.Path.Path ) -> Html msg
M web/src/Pages/Auth.elm

@@ -57,6 +57,9 @@ Effect.pushRoutePath Route.Path.Home_

Auth.User.NotSignedIn -> Effect.none + + Auth.User.RefreshingTokens -> + Effect.none )
M web/src/Shared.elm

@@ -79,7 +79,6 @@ initModel : Model

initModel = { user = user , timeZone = Time.utc - , isRefreshingTokens = False } in ( initModel

@@ -128,29 +127,29 @@

else ( model, Effect.none ) - Auth.User.NotSignedIn -> + _ -> ( model, Effect.none ) Shared.Msg.TriggerTokenRefresh -> case model.user of Auth.User.SignedIn credentials -> - ( { model | isRefreshingTokens = True } + ( { model | user = Auth.User.RefreshingTokens } , Api.Auth.refreshToken { onResponse = Shared.Msg.ApiRefreshTokensResponded , refreshToken = credentials.refreshToken } ) - Auth.User.NotSignedIn -> + _ -> ( model, Effect.none ) Shared.Msg.ApiRefreshTokensResponded (Ok credentials) -> - ( { model | isRefreshingTokens = False, user = Auth.User.SignedIn credentials } + ( { model | user = Auth.User.SignedIn credentials } , Effect.saveUser credentials.accessToken credentials.refreshToken ) Shared.Msg.ApiRefreshTokensResponded (Err _) -> - ( { model | isRefreshingTokens = False }, Effect.clearUser ) + ( { model | user = Auth.User.NotSignedIn }, Effect.clearUser )
M web/src/Shared/Model.elm

@@ -7,5 +7,4 @@

type alias Model = { user : Auth.User.SignInStatus , timeZone : Time.Zone - , isRefreshingTokens : Bool }
M web/src/interop.js

@@ -1,16 +1,16 @@

import "./styles.css"; export const flags = (_) => { - return { - access_token: JSON.parse(window.localStorage.access_token || 'null'), - refresh_token: JSON.parse(window.localStorage.refresh_token || 'null'), - } -} + return { + access_token: JSON.parse(window.localStorage.access_token || "null"), + refresh_token: JSON.parse(window.localStorage.refresh_token || "null"), + }; +}; export const onReady = ({ app }) => { - if (app.ports?.sendToLocalStorage) { - app.ports.sendToLocalStorage.subscribe(({ key, value }) => { - window.localStorage[key] = JSON.stringify(value); - }) - } -} + if (app.ports?.sendToLocalStorage) { + app.ports.sendToLocalStorage.subscribe(({ key, value }) => { + window.localStorage[key] = JSON.stringify(value); + }); + } +};