Skip to content

Commit

Permalink
manage sessions: login/logout, #10
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonLab committed Mar 5, 2020
1 parent cda5c5f commit 6eb47dd
Show file tree
Hide file tree
Showing 8 changed files with 420 additions and 116 deletions.
349 changes: 271 additions & 78 deletions elm.js

Large diffs are not rendered by default.

25 changes: 23 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,29 @@
<span>This website is using Elm, so it needs js enabled</span>
</noscript>
<script>
flags = localStorage.getItem('store'); // get the person session from the local storage
Elm.Main.init({ flags: flags });
var flags = localStorage.getItem('store'); // get the person session from the local storage
var app = Elm.Main.init({ flags: flags });

// store new session, see Session module
app.ports.storeSession.subscribe(function (val) {

if (val === null) { // logout
localStorage.removeItem('store');
} else {
localStorage.setItem('store', JSON.stringify(val));
}

// send message to Elm to notify session has been saved
// https://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful
setTimeout(function () { app.ports.onSessionChange.send(val); }, 0);
});

// Listen for localStorage changes
window.addEventListener("storage", function (event) {
if (event.storageArea === localStorage && event.key === 'store') {
app.ports.onSessionChange.send(JSON.parse(event.newValue));
}
}, false);
</script>
</body>

Expand Down
18 changes: 16 additions & 2 deletions src/Main.elm
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,24 @@ loadRoute maybeRoute model =
in
( Session subModel, Cmd.map GotPagesSessionMsg subMsg )

Just Route.Logout ->
( model, Session.logout )


subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
subscriptions model =
case model of
Home home ->
Sub.map GotHomeMsg (Home.subscriptions home)

Auth authModel ->
Sub.map GotAuthMsg (Auth.subscriptions authModel)

Session sessionModel ->
Sub.map GotPagesSessionMsg (PagesSession.subscriptions sessionModel)

NotFound _ ->
Sub.none


view : Model -> Browser.Document Msg
Expand Down
13 changes: 12 additions & 1 deletion src/Pages/Auth.elm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Pages.Auth exposing (Model, Msg(..), TypeUrl(..), Url, authUrlsDecoder, getAuthUrls, init, showAuthUrl, toSession, update, urlDecoder, urlTypeDecoder, view)
module Pages.Auth exposing (Model, Msg(..), TypeUrl(..), Url, authUrlsDecoder, getAuthUrls, init, showAuthUrl, subscriptions, toSession, update, urlDecoder, urlTypeDecoder, view)

import Asset exposing (..)
import Endpoint
Expand Down Expand Up @@ -43,6 +43,7 @@ init session =

type Msg
= GotAuthUrls (Result Http.Error (List Url))
| GotSession Session


update : Msg -> Model -> ( Model, Cmd msg )
Expand All @@ -56,6 +57,11 @@ update msg model =
Err _ ->
( model, Cmd.none )

GotSession session ->
( { model | session = session }
, Route.replaceUrl (Session.navKey model.session) Route.Home
)



-- View
Expand Down Expand Up @@ -127,3 +133,8 @@ showAuthUrl url =
toSession : Model -> Session
toSession model =
model.session


subscriptions : Model -> Sub Msg
subscriptions model =
Session.changeSession GotSession (Session.navKey model.session)
20 changes: 15 additions & 5 deletions src/Pages/Home.elm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Pages.Home exposing (Model, Msg(..), init, toSession, update, view)
module Pages.Home exposing (Model, Msg(..), init, subscriptions, toSession, update, view)

import Asset
import Html exposing (..)
Expand Down Expand Up @@ -26,14 +26,16 @@ init session =


type Msg
= None
= GotSession Session


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
None ->
( model, Cmd.none )
GotSession session ->
( session
, Route.replaceUrl (Session.navKey model) Route.Home
)



Expand All @@ -51,11 +53,19 @@ view model =
a [ Route.href (Route.Auth Nothing), class "tc db" ] [ text "login/signup" ]

Session.Session _ person ->
span [ class "tc db" ] [ text <| "logged in with token: " ++ person.email ]
div []
[ span [ class "tc db" ] [ text <| "logged in with: " ++ person.email ]
, a [ Route.href Route.Logout, class "tc db" ] [ text "logout" ]
]
]
}


toSession : Model -> Session
toSession model =
model


subscriptions : Model -> Sub Msg
subscriptions model =
Session.changeSession GotSession (Session.navKey model)
42 changes: 17 additions & 25 deletions src/Pages/Session.elm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Pages.Session exposing (Model, Msg(..), Person, getPersonInfo, init, personDecoder, subscriptions, toSession, update, view)
module Pages.Session exposing (Model, Msg(..), PersonInfo, getPersonInfo, init, personDecoder, subscriptions, toSession, update, view)

import Asset
import Endpoint
Expand All @@ -8,7 +8,7 @@ import Http
import Json.Decode as JD
import Page
import Route
import Session exposing (..)
import Session exposing (Session)


type alias Model =
Expand All @@ -17,7 +17,7 @@ type alias Model =
}


type alias Person =
type alias PersonInfo =
{ email : String
, name : String
}
Expand All @@ -33,7 +33,8 @@ init session token =


type Msg
= GotPersonInfo (Result Http.Error Person)
= GotPersonInfo (Result Http.Error PersonInfo)
| GotSession Session


update : Msg -> Model -> ( Model, Cmd Msg )
Expand All @@ -42,21 +43,21 @@ update msg model =
GotPersonInfo result ->
case result of
Ok person ->
-- instead of Cmd.none, call a store cache command
let
_ =
Debug.log "Person" person
session =
{ email = person.email, token = model.token }
in
( model, Route.replaceUrl (navKey model.session) Route.Home )
( model, Session.storeSession (Just <| Session.encode session) )

-- if a 401 redirect to 401 page not authorised
Err e ->
let
_ =
Debug.log "Error" e
in
( model, Cmd.none )

GotSession session ->
( { model | session = session }
, Route.replaceUrl (Session.navKey model.session) Route.Home
)


getPersonInfo : String -> Cmd Msg
getPersonInfo token =
Expand All @@ -72,10 +73,10 @@ getPersonInfo token =
}


personDecoder : JD.Decoder Person
personDecoder : JD.Decoder PersonInfo
personDecoder =
JD.field "data"
(JD.map2 Person
(JD.map2 PersonInfo
(JD.field "email" JD.string)
(JD.field "name" JD.string)
)
Expand All @@ -86,7 +87,7 @@ personDecoder =


view : Model -> Page.PageStructure Msg
view model =
view _ =
{ title = "Auth"
, content =
[ img [ Asset.src Asset.logo, class "center db pt2" ] []
Expand All @@ -101,18 +102,9 @@ view model =

subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
Session.changeSession GotSession (Session.navKey model.session)


toSession : Model -> Session
toSession model =
model.session



-- create port module
-- create storeSession
-- in html.js store the session
-- create subscriptions to listen to new change in store and trigger the GotSession session message
-- redirect to Home page with the new session
-- Create a redirect function in Route module
5 changes: 5 additions & 0 deletions src/Route.elm
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import Url.Parser.Query as Query
type Route
= Home
| Auth (Maybe String)
| Logout


routeParser : Parser.Parser (Route -> a) a
routeParser =
Parser.oneOf
[ Parser.map Home Parser.top
, Parser.map Auth (Parser.s "auth" <?> Query.string "jwt")
, Parser.map Logout (Parser.s "logout")
]


Expand All @@ -47,6 +49,9 @@ routeToString route =
Auth (Just jwt) ->
"/auth?jwt=" ++ jwt

Logout ->
"/logout"


replaceUrl : Nav.Key -> Route -> Cmd msg
replaceUrl key route =
Expand Down
64 changes: 61 additions & 3 deletions src/Session.elm
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module Session exposing (Person, Session(..), decode, navKey)
port module Session exposing (Person, Session(..), changeSession, decode, encode, logout, navKey, onSessionChange, storeSession)

{-| Represent the current user
-}

import Browser.Navigation as Nav
import Json.Decode exposing (..)
import Json.Decode as JD
import Json.Encode as JE


type Session
Expand All @@ -20,14 +21,22 @@ type alias Person =

decode : Nav.Key -> String -> Session
decode key str =
case decodeString (map2 Person (field "email" string) (field "token" string)) str of
case JD.decodeString (JD.map2 Person (JD.field "email" JD.string) (JD.field "token" JD.string)) str of
Ok p ->
Session key p

_ ->
Guest key


encode : Person -> JE.Value
encode person =
JE.object
[ ( "email", JE.string person.email )
, ( "token", JE.string person.token )
]


navKey : Session -> Nav.Key
navKey session =
case session of
Expand All @@ -36,3 +45,52 @@ navKey session =

Session key _ ->
key


port storeSession : Maybe JD.Value -> Cmd msg


port onSessionChange : (JE.Value -> msg) -> Sub msg



-- create function which use onSessionChange: pass transform value to maybe person
-- create change function which transform a maybe person to session


changeSession : (Session -> msg) -> Nav.Key -> Sub msg
changeSession toMsg key =
changePerson (\maybePerson -> toMsg (sessionFromPerson maybePerson key))


changePerson : (Maybe Person -> msg) -> Sub msg
changePerson toMsg =
onSessionChange (\value -> toMsg (decodeFromChange personDecoder value))


sessionFromPerson : Maybe Person -> Nav.Key -> Session
sessionFromPerson maybePerson key =
case maybePerson of
Just person ->
Session key person

Nothing ->
Guest key


personDecoder : JD.Decoder Person
personDecoder =
JD.map2 Person
(JD.field "email" JD.string)
(JD.field "token" JD.string)


decodeFromChange : JD.Decoder Person -> JD.Value -> Maybe Person
decodeFromChange decoder val =
JD.decodeValue decoder val
|> Result.toMaybe


logout : Cmd msg
logout =
storeSession Nothing

0 comments on commit 6eb47dd

Please sign in to comment.