add support for query parameters on pages

This commit is contained in:
Ryan Haskell-Glatz 2019-11-19 19:48:09 -06:00
parent 7bafc4ee7c
commit 459eb7a75d
12 changed files with 179 additions and 82 deletions

View File

@ -14,13 +14,19 @@ import Html.Attributes as Attr
import Utils.Spa as Spa
-- TODO: either
-- 1. delete this completely, and only define in Transitions.elm
-- 2. generate the Transitions file, and pull out transitions.layout to main
transition : Transition (Element msg)
transition =
Transition.fadeUi 300
view : Spa.Context msg -> Element msg
view { page, global, toMsg } =
view : Spa.LayoutContext msg -> Element msg
view { page, global, fromGlobalMsg } =
column
[ Font.size 16
, Font.color Styles.colors.jet
@ -36,7 +42,7 @@ view { page, global, toMsg } =
, height fill
, centerX
]
[ Element.map toMsg (viewNavbar global.user)
[ Element.map fromGlobalMsg (viewNavbar global.user)
, page
]

View File

@ -19,8 +19,8 @@ transition =
el
[ alpha 0
, width fill
, rotate (2 * pi)
, moveLeft 1000
, rotate (4 * pi)
, scale 0
, Styles.transition
{ property = "all"
, speed = 500
@ -42,7 +42,7 @@ transition =
}
view : Spa.Context msg -> Element msg
view : Spa.LayoutContext msg -> Element msg
view { page, route } =
column [ width fill ]
[ row [ spacing 16 ] <|

View File

@ -12,7 +12,7 @@ transition =
Transition.none
view : Spa.Context msg -> Element msg
view : Spa.LayoutContext msg -> Element msg
view { page } =
column
[ width fill

View File

@ -12,6 +12,6 @@ transition =
Transition.optOut
view : Spa.Context msg -> Element msg
view : Spa.LayoutContext msg -> Element msg
view { page } =
page

View File

@ -11,6 +11,6 @@ transition =
Transition.optOut
view : Spa.Context msg -> Element msg
view : Spa.LayoutContext msg -> Element msg
view { page } =
page

View File

@ -11,6 +11,6 @@ transition =
Transition.optOut
view : Spa.Context msg -> Element msg
view : Spa.LayoutContext msg -> Element msg
view { page } =
page

View File

@ -2,10 +2,12 @@ module Pages.Docs.Dynamic exposing (Model, Msg, page)
import App.Page
import Components.Hero
import Dict exposing (Dict)
import Element exposing (..)
import Element.Font as Font
import Generated.Docs.Params as Params
import Global
import Utils.Spa exposing (Page)
import Utils.Spa as Spa exposing (Page)
type alias Model =
@ -23,7 +25,7 @@ page =
{ title = always "Dynamic"
, init = always init
, update = always update
, view = view
, view = always view
}
@ -50,8 +52,8 @@ update msg model =
-- VIEW
view : Global.Model -> Model -> Element Msg
view global model =
view : Model -> Element Msg
view model =
column
[ width fill
]
@ -62,9 +64,4 @@ view global model =
[ { label = text "back to docs", action = Components.Hero.Link "/docs" }
]
}
, global.user
|> Maybe.map (\name -> "Oh hey there, " ++ name ++ "!")
|> Maybe.withDefault "Sign in if you want me to say hello!"
|> text
|> el [ centerX ]
]

View File

@ -1,8 +1,9 @@
module Utils.Spa exposing
( Bundle
, Context
, LayoutContext
, Init
, Page
, PageContext
, Recipe
, Update
, layout
@ -44,8 +45,12 @@ type alias Upgrade params model msg layoutModel layoutMsg appMsg =
App.Types.Upgrade Route params model msg (Element msg) layoutModel layoutMsg (Element layoutMsg) Global.Model Global.Msg appMsg (Element appMsg)
type alias Context msg =
App.Types.Context Route msg (Element msg) Global.Model Global.Msg
type alias LayoutContext msg =
App.Types.LayoutContext Route msg (Element msg) Global.Model Global.Msg
type alias PageContext =
App.Types.PageContext Global.Model
layout :

View File

@ -1,6 +1,7 @@
module App exposing
( Program, create
, usingHtml
, queryParameters
)
{-|
@ -59,6 +60,7 @@ Providing `App.create` with these `ui` options will do the trick!
import Browser
import Browser.Navigation as Nav
import Dict exposing (Dict)
import Html exposing (Html)
import Internals.Page as Page
import Internals.Pattern as Pattern exposing (Pattern)
@ -258,7 +260,10 @@ init config flags url key =
flags
( pageModel, pageCmd, pageGlobalCmd ) =
config.init.pages route { global = globalModel }
config.init.pages route
{ global = globalModel
, queryParameters = queryParameters url
}
in
( { flags = flags
, url = url
@ -333,7 +338,12 @@ update config msg model =
FadeInPage url ->
url
|> config.routing.fromUrl
|> (\route -> config.init route { global = model.global })
|> (\route ->
config.init route
{ global = model.global
, queryParameters = queryParameters model.url
}
)
|> (\( pageModel, pageCmd, globalCmd ) ->
( { model
| visibilities = { layout = Transition.visible, page = Transition.visible }
@ -404,7 +414,11 @@ update config msg model =
)
Page pageMsg ->
config.update.pages pageMsg model.page { global = model.global }
config.update.pages pageMsg
model.page
{ global = model.global
, queryParameters = queryParameters model.url
}
|> (\( page, pageCmd, globalCmd ) ->
( { model | page = page }
, Cmd.batch
@ -442,12 +456,14 @@ subscriptions config model =
model.page
{ fromGlobalMsg = Global
, fromPageMsg = Page
, global = model.global
, map = config.map
, transitioningPattern = model.transitioningPattern
, visibility = model.visibilities.page
, route = config.fromUrl model.url
}
{ global = model.global
, queryParameters = queryParameters model.url
}
).subscriptions
, Sub.map Global (config.global model.global)
]
@ -475,12 +491,14 @@ view config model =
model.page
{ fromGlobalMsg = Global
, fromPageMsg = Page
, global = model.global
, map = config.map
, transitioningPattern = model.transitioningPattern
, visibility = model.visibilities.page
, route = config.fromUrl model.url
}
{ global = model.global
, queryParameters = queryParameters model.url
}
in
{ title = bundle.title
, body =
@ -523,3 +541,23 @@ chooseFrom options =
urlPath : Url -> List String
urlPath url =
url.path |> String.dropLeft 1 |> String.split "/"
-- QUERY PARAMETERS
queryParameters : { a | query : Maybe String } -> Dict String String
queryParameters url =
let
toDict : String -> Dict String String
toDict query =
query
|> String.split "&"
|> List.map (String.split "=")
|> List.map (\pieces -> ( List.head pieces, List.drop 1 pieces |> List.head ))
|> List.map (Tuple.mapBoth (Maybe.withDefault "") (Maybe.withDefault ""))
|> List.filter (\( key, _ ) -> not (String.isEmpty key))
|> Dict.fromList
in
Maybe.map toDict url.query |> Maybe.withDefault Dict.empty

View File

@ -124,6 +124,10 @@ import Internals.Transition as Transition exposing (Transition)
import Internals.Utils as Utils
type alias PageContext globalModel =
Internals.Page.PageContext globalModel
type alias Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
Internals.Page.Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
@ -216,7 +220,7 @@ keep model =
-}
static :
{ title : { global : globalModel } -> String
, view : globalModel -> ui_pageMsg
, view : PageContext globalModel -> ui_pageMsg
}
-> Page route pageParams () Never ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
static page =
@ -225,9 +229,16 @@ static page =
{ init = \_ _ -> ( toModel (), Cmd.none, Cmd.none )
, update = \_ model _ -> ( toModel model, Cmd.none, Cmd.none )
, bundle =
\_ context ->
{ title = page.title { global = context.global }
, view = page.view context.global |> map toMsg |> context.map context.fromPageMsg
\_ private context ->
{ title =
page.title
{ global = context.global
}
, view =
page.view
context
|> map toMsg
|> private.map private.fromPageMsg
, subscriptions = Sub.none
}
}
@ -286,9 +297,9 @@ static page =
-}
sandbox :
{ title : { global : globalModel, model : pageModel } -> String
, init : globalModel -> pageParams -> pageModel
, update : globalModel -> pageMsg -> pageModel -> pageModel
, view : globalModel -> pageModel -> ui_pageMsg
, init : PageContext globalModel -> pageParams -> pageModel
, update : PageContext globalModel -> pageMsg -> pageModel -> pageModel
, view : PageContext globalModel -> pageModel -> ui_pageMsg
}
-> Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
sandbox page =
@ -296,20 +307,28 @@ sandbox page =
(\{ toModel, toMsg, map } ->
{ init =
\pageParams context ->
( toModel (page.init context.global pageParams)
( toModel (page.init context pageParams)
, Cmd.none
, Cmd.none
)
, update =
\msg model context ->
( page.update context.global msg model |> toModel
( page.update context msg model
|> toModel
, Cmd.none
, Cmd.none
)
, bundle =
\model context ->
{ title = page.title { global = context.global, model = model }
, view = page.view context.global model |> map toMsg |> context.map context.fromPageMsg
\model private context ->
{ title =
page.title
{ global = context.global
, model = model
}
, view =
page.view context model
|> map toMsg
|> private.map private.fromPageMsg
, subscriptions = Sub.none
}
}
@ -357,10 +376,10 @@ sandbox page =
-}
element :
{ title : { global : globalModel, model : pageModel } -> String
, init : globalModel -> pageParams -> ( pageModel, Cmd pageMsg )
, update : globalModel -> pageMsg -> pageModel -> ( pageModel, Cmd pageMsg )
, view : globalModel -> pageModel -> ui_pageMsg
, subscriptions : globalModel -> pageModel -> Sub pageMsg
, init : PageContext globalModel -> pageParams -> ( pageModel, Cmd pageMsg )
, update : PageContext globalModel -> pageMsg -> pageModel -> ( pageModel, Cmd pageMsg )
, view : PageContext globalModel -> pageModel -> ui_pageMsg
, subscriptions : PageContext globalModel -> pageModel -> Sub pageMsg
}
-> Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
element page =
@ -368,17 +387,26 @@ element page =
(\{ toModel, toMsg, map } ->
{ init =
\pageParams context ->
page.init context.global pageParams
page.init context pageParams
|> tuple toModel toMsg
, update =
\msg model context ->
page.update context.global msg model
page.update context msg model
|> tuple toModel toMsg
, bundle =
\model context ->
{ title = page.title { global = context.global, model = model }
, view = page.view context.global model |> map toMsg |> context.map context.fromPageMsg
, subscriptions = page.subscriptions context.global model |> Sub.map (toMsg >> context.fromPageMsg)
\model private context ->
{ title =
page.title
{ global = context.global
, model = model
}
, view =
page.view context model
|> map toMsg
|> private.map private.fromPageMsg
, subscriptions =
page.subscriptions context model
|> Sub.map (toMsg >> private.fromPageMsg)
}
}
)
@ -428,10 +456,10 @@ element page =
-}
component :
{ title : { global : globalModel, model : pageModel } -> String
, init : globalModel -> pageParams -> ( pageModel, Cmd pageMsg, Cmd globalMsg )
, update : globalModel -> pageMsg -> pageModel -> ( pageModel, Cmd pageMsg, Cmd globalMsg )
, view : globalModel -> pageModel -> ui_pageMsg
, subscriptions : globalModel -> pageModel -> Sub pageMsg
, init : PageContext globalModel -> pageParams -> ( pageModel, Cmd pageMsg, Cmd globalMsg )
, update : PageContext globalModel -> pageMsg -> pageModel -> ( pageModel, Cmd pageMsg, Cmd globalMsg )
, view : PageContext globalModel -> pageModel -> ui_pageMsg
, subscriptions : PageContext globalModel -> pageModel -> Sub pageMsg
}
-> Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
component page =
@ -439,17 +467,26 @@ component page =
(\{ toModel, toMsg, map } ->
{ init =
\pageParams context ->
page.init context.global pageParams
page.init context pageParams
|> truple toModel toMsg
, update =
\msg model context ->
page.update context.global msg model
page.update context msg model
|> truple toModel toMsg
, bundle =
\model context ->
{ title = page.title { global = context.global, model = model }
, view = page.view context.global model |> map toMsg |> context.map context.fromPageMsg
, subscriptions = page.subscriptions context.global model |> Sub.map (toMsg >> context.fromPageMsg)
\model private context ->
{ title =
page.title
{ global = context.global
, model = model
}
, view =
page.view context model
|> map toMsg
|> private.map private.fromPageMsg
, subscriptions =
page.subscriptions context model
|> Sub.map (toMsg >> private.fromPageMsg)
}
}
)
@ -505,20 +542,20 @@ layout map options =
options.recipe.update msg model global
|> truple toModel toMsg
, bundle =
\model context ->
\model private context ->
let
viewLayout page =
options.view
{ page = page
, global = context.global
, toMsg = context.fromGlobalMsg
, route = context.route
, fromGlobalMsg = private.fromGlobalMsg
, route = private.route
}
myLayoutsVisibility : Transition.Visibility
myLayoutsVisibility =
if context.transitioningPattern == options.pattern then
context.visibility
if private.transitioningPattern == options.pattern then
private.visibility
else
Transition.visible
@ -527,14 +564,14 @@ layout map options =
bundle =
options.recipe.bundle
model
{ fromGlobalMsg = context.fromGlobalMsg
, fromPageMsg = toMsg >> context.fromPageMsg
, global = context.global
{ fromGlobalMsg = private.fromGlobalMsg
, fromPageMsg = toMsg >> private.fromPageMsg
, map = map
, transitioningPattern = context.transitioningPattern
, visibility = context.visibility
, route = context.route
, transitioningPattern = private.transitioningPattern
, visibility = private.visibility
, route = private.route
}
context
in
{ title = bundle.title
, view =

View File

@ -5,7 +5,7 @@ module App.Types exposing
, Update
, Bundle
, Layout, Upgrade
, Context
, LayoutContext, PageContext
)
{-|
@ -274,10 +274,16 @@ type alias Layout route pageParams pageModel pageMsg ui_pageMsg globalModel glob
Page.Layout route pageParams pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg
{-| TODO: Context docs
{-| TODO: PageContext docs
-}
type alias Context route msg ui_msg globalModel globalMsg =
Page.Context route msg ui_msg globalModel globalMsg
type alias PageContext globalModel =
Page.PageContext globalModel
{-| TODO: LayoutContext docs
-}
type alias LayoutContext route msg ui_msg globalModel globalMsg =
Page.LayoutContext route msg ui_msg globalModel globalMsg
{-|

View File

@ -1,19 +1,27 @@
module Internals.Page exposing
( Bundle
, Context
, Init
, Layout
, LayoutContext
, Page(..)
, PageContext
, Recipe
, Update
, Upgrade
, upgrade
)
import Dict exposing (Dict)
import Internals.Pattern exposing (Pattern)
import Internals.Transition as Transition exposing (Transition)
type alias PageContext globalModel =
{ global : globalModel
, queryParameters : Dict String String
}
type Page route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
= Page (Page_ route pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg)
@ -57,24 +65,24 @@ upgrade map config =
type alias Init layoutModel layoutMsg globalModel globalMsg =
{ global : globalModel }
PageContext globalModel
-> ( layoutModel, Cmd layoutMsg, Cmd globalMsg )
type alias Update layoutModel layoutMsg globalModel globalMsg =
{ global : globalModel }
PageContext globalModel
-> ( layoutModel, Cmd layoutMsg, Cmd globalMsg )
type alias Bundle route layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
{ global : globalModel
, fromGlobalMsg : globalMsg -> msg
{ fromGlobalMsg : globalMsg -> msg
, fromPageMsg : layoutMsg -> msg
, map : (layoutMsg -> msg) -> ui_layoutMsg -> ui_msg
, route : route
, visibility : Transition.Visibility
, transitioningPattern : Pattern
}
-> PageContext globalModel
->
{ title : String
, view : ui_msg
@ -82,17 +90,17 @@ type alias Bundle route layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
}
type alias Context route msg ui_msg globalModel globalMsg =
type alias LayoutContext route msg ui_msg globalModel globalMsg =
{ page : ui_msg
, route : route
, global : globalModel
, toMsg : globalMsg -> msg
, fromGlobalMsg : globalMsg -> msg
}
type alias Layout route pageParams pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg =
{ pattern : Pattern
, transition : Transition ui_msg
, view : Context route msg ui_msg globalModel globalMsg -> ui_msg
, view : LayoutContext route msg ui_msg globalModel globalMsg -> ui_msg
, recipe : Recipe route pageParams pageModel pageMsg pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg
}