ElmのパッケージサイトのSessionを参考にして試したメモ

## 概要
[package.elm-lang.org][*1]を参考に、一度取得したデータを保持して再取得しない仕組みを試した。
もともとはマークダウンを取得して表示する処理。
一度取得したものは取得しない処理を追加。

## ソースの変更点についてメモ


### Session.elm

[package.elm-lang.org][*1]は author project versionをディレクトリのキーにしていたので、
こちらはマークダウンのファイル名とバージョンで一意にするようにする。
マークダウンはほぼ更新しない予定なのでバージョンはオマケ。

```Elm:Session.elm
module Session exposing (Data, Version, addMarkdown, empty, fetchMarkdown, getMarkdown, markdownUrl, toMarkdownKey)

import Dict
import Http
import Json.Decode as Decode
import Url.Builder as Url


type alias Version =
    Float



-- SESSION DATA


type alias Data =
    { markdowns : Dict.Dict String String
    }


empty : Data
empty =
    Data Dict.empty



-- Markdown


toMarkdownKey : String -> Version -> String
toMarkdownKey fileName version =
    fileName ++ "@" ++ String.fromFloat version


getMarkdown : Data -> String -> Version -> Maybe String
getMarkdown data fileName version =
    Dict.get (toMarkdownKey fileName version) data.markdowns


addMarkdown : String -> Version -> String -> Data -> Data
addMarkdown fileName version markdown data =
    let
        newMarkdowns =
            Dict.insert (toMarkdownKey fileName version) markdown data.markdowns
    in
    { data | markdowns = newMarkdowns }


fetchMarkdown : (Result Http.Error String -> msg) -> String -> Cmd msg
fetchMarkdown toMsg fileName =
    Http.get
        { url = markdownUrl fileName
        , expect = Http.expectString toMsg
        }


markdownUrl : String -> String
markdownUrl fileName =
    Url.absolute [ "assets", "markdown", fileName ] []
```

### Page/Markdown.elm

Sessionとバージョンをモデルに追加している。

```diff:Page/Markdown.elm
 import Markdown
+import Session
 import Skeleton exposing (viewLink, viewMain)
```

```diff:Page/Markdown.elm
 type alias Model =
-    { fileName : String
+    { session : Session.Data
+    , fileName : String
+    , version : Session.Version
     , state : State
     , naviState : NaviState
     }
```

initでセッションとバージョンを引数に追加。
また、セッションにデータがあるか問い合わせる処理を追加。
なければ変更前と同様にHttpから取得する。

```diff:Page/Markdown.elm
-init : String -> ( Model, Cmd Msg )
-init fileName =
-    ( Model fileName Init Close
-    , getMarkdown GotMarkdown fileName
-    )
+init : Session.Data -> String -> Session.Version -> ( Model, Cmd Msg )
+init session fileName version =
+    case Session.getMarkdown session fileName version of
+        Just markdown ->
+            let
+                model =
+                    Model session fileName version (Loaded markdown) Close
+            in
+            ( model
+            , Cmd.none
+            )
+        Nothing ->
+            -- ページの初期化
+            -- 最初のModelを作ると同時に、ページの表示に必要なデータをHttpで取得
+            ( Model session fileName version Init Close
+            , Session.fetchMarkdown GotMarkdown fileName
+            )
```

updateでは、取得したデータをセッションにいれる処理を追加。

```diff:Page/Markdown.elm
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    --- init での Http リクエストの結果が得られたら Model を変更する
    case msg of
     case msg of
         GotMarkdown (Ok markdown) ->
-            ( { model | state = Loaded markdown }, Cmd.none )
+            ( { model
+                | state = Loaded markdown
+                , session = Session.addMarkdown model.fileName model.version markdown model.session
+              }
+            , Cmd.none
+            )
        GotMarkdown (Err err) ->
            ( { model | state = Error err }, Cmd.none )
```

### Main.elm

```diff:Main.elm
 import Route exposing (..)
+import Session
 import Skeleton exposing (Details, view)
```

exitで、ページ遷移時にセッションを引き継ぐ処理を行っている。
ここでは暫定的に、マークダウンのページ間でのみセッションを引き継ぐ。
後に、[package.elm-lang.org][*1]のように、すべてのページのモデルにsessionをくみこむ予定。

```diff:Main.elm
+exit : Model -> Session.Data
+exit model =
+    case model.page of
+        MarkdownPage m ->
+            m.session
+
+        _ ->
+            Session.empty


 goTo : Maybe Route -> Model -> ( Model, Cmd Msg )
 goTo maybeRoute model =
+    let
+        session =
+            exit model
+    in
     case maybeRoute of
         Nothing ->
             ( { model | page = NotFound }
```

引数にセッションとバージョンを追加。

```diff:Main.elm
         Just Route.PrivacyPolicy ->
             let
                 ( markdownModel, markdownCmd ) =
-                    Markdown.init "privacy-policy.md"
+                    Markdown.init session "privacy-policy.md" 1.0
             in
             ( { model | page = MarkdownPage markdownModel }
             , Cmd.map MarkdownMsg markdownCmd
            )
         Just Route.About ->
             let
                 ( markdownModel, markdownCmd ) =
-                    Markdown.init "about.md"
+                    Markdown.init session "about.md" 1.0
             in
             ( { model | page = MarkdownPage markdownModel }
             , Cmd.map MarkdownMsg markdownCmd
            )
         Just Route.Agreement ->
             let
                 ( markdownModel, markdownCmd ) =
-                    Markdown.init "agreement.md"
+                    Markdown.init session "agreement.md" 1.0
             in
             ( { model | page = MarkdownPage markdownModel }
             , Cmd.map MarkdownMsg markdownCmd
            )
```

[変更前のソース](https://github.com/hibohiboo/garden/tree/bfd050ba67de7f1b3e2c0aaa4639b82b908cb77f)  

[変更後のソース](https://github.com/hibohiboo/garden/tree/e5bac32617cdadc0c91282f342019d1527e962c3)  


## 参考

[package.elm-lang.org][*1]

[*1]:https://github.com/elm/package.elm-lang.org