elm-in-action/src/Main.elm
2022-10-17 18:38:57 -05:00

240 lines
5.7 KiB
Elm

module Main exposing (main)
import Browser exposing (Document)
import Browser.Navigation as Nav
import Html exposing (Html, a, footer, h1, li, nav, text, ul)
import Html.Attributes exposing (classList, href)
import Html.Lazy exposing (lazy)
import PhotoFolders as Folders
import PhotoGroove as Gallery
import Url exposing (Url)
import Url.Parser as Parser exposing ((</>), Parser, s)
type alias Model =
{ page : Page
, version : Float
, key : Nav.Key
}
type Page
= GalleryPage Gallery.Model
| FoldersPage Folders.Model
| NotFound
type Route
= Gallery
| Folders
| SelectedPhoto String
view : Model -> Document Msg
view model =
let
title =
case model.page of
FoldersPage _ ->
Folders.pageTitle
GalleryPage _ ->
Gallery.pageTitle
_ ->
"Not Found"
content =
case model.page of
FoldersPage folders ->
Folders.view folders
|> Html.map GotFoldersMsg
GalleryPage gallery ->
Gallery.view gallery
|> Html.map GotGalleryMsg
NotFound ->
text "Not Found"
in
{ title = "Photo Groove | " ++ title
, body =
[ lazy viewHeader model.page
, content
, viewFooter
]
}
viewHeader : Page -> Html Msg
viewHeader page =
let
logo =
h1 [] [ text "Photo Groove" ]
links =
ul []
[ navLink Folders { url = "/", caption = "Folders" }
, navLink Gallery { url = "/gallery", caption = "Gallery" }
]
navLink : Route -> { url : String, caption : String } -> Html Msg
navLink targetRoute { url, caption } =
li [ classList [ ( "active", isActive { link = targetRoute, page = page } ) ] ]
[ a [ href url ] [ text caption ] ]
in
nav [] [ logo, links ]
isActive : { link : Route, page : Page } -> Bool
isActive { link, page } =
case ( link, page ) of
( Gallery, GalleryPage _ ) ->
True
( Gallery, _ ) ->
False
( Folders, FoldersPage _ ) ->
True
( Folders, _ ) ->
False
( SelectedPhoto _, _ ) ->
False
viewFooter : Html Msg
viewFooter =
footer []
[ text "One is never alone with a rubber duck. -Douglas Adams"
]
type Msg
= ClickedLink Browser.UrlRequest
| ChangedUrl Url
| GotGalleryMsg Gallery.Msg
| GotFoldersMsg Folders.Msg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ClickedLink (Browser.External href) ->
( model, Nav.load href )
ClickedLink (Browser.Internal url) ->
( model, Nav.pushUrl model.key (Url.toString url) )
ChangedUrl url ->
updateUrl url model
GotGalleryMsg galleryMsg ->
case model.page of
GalleryPage gallery ->
toGallery model (Gallery.update galleryMsg gallery)
_ ->
( model, Cmd.none )
GotFoldersMsg foldersMsg ->
case model.page of
FoldersPage folders ->
toFolders model (Folders.update foldersMsg folders)
_ ->
( model, Cmd.none )
toGallery : Model -> ( Gallery.Model, Cmd Gallery.Msg ) -> ( Model, Cmd Msg )
toGallery model ( galleryModel, galleryCmd ) =
( { model | page = GalleryPage galleryModel }
, Cmd.map GotGalleryMsg galleryCmd
)
toFolders : Model -> ( Folders.Model, Cmd Folders.Msg ) -> ( Model, Cmd Msg )
toFolders model ( foldersModel, foldersCmd ) =
( { model | page = FoldersPage foldersModel }
, Cmd.map GotFoldersMsg foldersCmd
)
subscriptions : Model -> Sub Msg
subscriptions model =
case model.page of
GalleryPage gallery ->
Gallery.subscriptions gallery
|> Sub.map GotGalleryMsg
_ ->
Sub.none
init : Float -> Url -> Nav.Key -> ( Model, Cmd Msg )
init version url key =
updateUrl
url
{ page = NotFound
, version = version
, key = key
}
mergeFolders : Model -> ( Folders.Model, Cmd Folders.Msg ) -> ( Model, Cmd Msg )
mergeFolders model foldersTuple =
case model.page of
FoldersPage oldModel ->
( { model
| page =
FoldersPage <|
Folders.merge
{ oldModel = oldModel
, newModel = Tuple.first foldersTuple
}
}
, Cmd.none
)
_ ->
toFolders model foldersTuple
updateUrl : Url -> Model -> ( Model, Cmd Msg )
updateUrl url model =
case Parser.parse parser url of
Just Gallery ->
toGallery model <| Gallery.init model.version
Just Folders ->
mergeFolders model <| Folders.init Nothing
Just (SelectedPhoto filename) ->
mergeFolders model <| Folders.init <| Just filename
Nothing ->
( { model | page = NotFound }, Cmd.none )
parser : Parser (Route -> a) a
parser =
Parser.oneOf
[ Parser.map Folders Parser.top
, Parser.map Gallery (s "gallery")
, Parser.map SelectedPhoto (s "photos" </> Parser.string)
]
main : Program Float Model Msg
main =
Browser.application
{ init = init
, subscriptions = subscriptions
, update = update
, view = view
, onUrlRequest = ClickedLink
, onUrlChange = ChangedUrl
}