From fe04d9b14e5db26ca104bd523ce17514c4ff0dcc Mon Sep 17 00:00:00 2001 From: Marek Fajkus Date: Fri, 30 Oct 2020 19:13:55 +0100 Subject: [PATCH] Refactor route structure (#219) --- src/Main.elm | 38 +++++--- src/Page/Options.elm | 14 +-- src/Page/Packages.elm | 14 +-- src/Route.elm | 151 ++++++++++++++--------------- src/Route/SearchQuery.elm | 6 +- src/Search.elm | 196 ++++++++++++-------------------------- 6 files changed, 167 insertions(+), 252 deletions(-) diff --git a/src/Main.elm b/src/Main.elm index dc132d8..6b027e6 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -162,15 +162,27 @@ changeRouteTo currentModel url = let attempteQuery (( newModel, cmd ) as pair) = case ( currentModel.route, newModel.route ) of - ( Route.Packages channel1 query1 _ from1 size1 sort1, Route.Packages channel2 query2 _ from2 size2 sort2 ) -> - if channel1 /= channel2 || query1 /= query2 || from1 /= from2 || size1 /= size2 || sort1 /= sort2 then + ( Route.Packages arg1, Route.Packages arg2 ) -> + if + (arg1.channel /= arg2.channel) + || (arg1.query /= arg2.query) + || (arg1.from /= arg2.from) + || (arg1.size /= arg2.size) + || (arg1.sort /= arg2.sort) + then submitQuery newModel ( newModel, cmd ) else pair - ( Route.Options channel1 query1 _ from1 size1 sort1, Route.Options channel2 query2 _ from2 size2 sort2 ) -> - if channel1 /= channel2 || query1 /= query2 || from1 /= from2 || size1 /= size2 || sort1 /= sort2 then + ( Route.Options arg1, Route.Options arg2 ) -> + if + (arg1.channel /= arg2.channel) + || (arg1.query /= arg2.query) + || (arg1.from /= arg2.from) + || (arg1.size /= arg2.size) + || (arg1.sort /= arg2.sort) + then submitQuery newModel ( newModel, cmd ) else @@ -203,7 +215,7 @@ changeRouteTo currentModel url = -- on the home page ( model, Browser.Navigation.pushUrl model.navKey "/packages" ) - Route.Packages channel query show from size sort -> + Route.Packages searchArgs -> let modelPage = case model.page of @@ -213,11 +225,11 @@ changeRouteTo currentModel url = _ -> Nothing in - Page.Packages.init channel query show from size sort modelPage + Page.Packages.init searchArgs modelPage |> updateWith Packages PackagesMsg model |> attempteQuery - Route.Options channel query show from size sort -> + Route.Options searchArgs -> let modelPage = case model.page of @@ -227,7 +239,7 @@ changeRouteTo currentModel url = _ -> Nothing in - Page.Options.init channel query show from size sort modelPage + Page.Options.init searchArgs modelPage |> updateWith Options OptionsMsg model |> attempteQuery @@ -349,14 +361,14 @@ viewNavigation route = toRoute f = case route of -- Preserve arguments - Route.Packages channel query show from size sort -> - f channel query show from size sort + Route.Packages searchArgs -> + f searchArgs - Route.Options channel query show from size sort -> - f channel query show from size sort + Route.Options searchArgs -> + f searchArgs _ -> - f Nothing Nothing Nothing Nothing Nothing Nothing + f <| Route.SearchArgs Nothing Nothing Nothing Nothing Nothing Nothing in li [] [ a [ href "https://nixos.org" ] [ text "Back to nixos.org" ] ] :: List.map diff --git a/src/Page/Options.elm b/src/Page/Options.elm index ccac5da..41409bd 100644 --- a/src/Page/Options.elm +++ b/src/Page/Options.elm @@ -65,19 +65,11 @@ type alias ResultItemSource = } -init : - Maybe String - -> Maybe String - -> Maybe String - -> Maybe Int - -> Maybe Int - -> Maybe String - -> Maybe Model - -> ( Model, Cmd Msg ) -init channel query show from size sort model = +init : Route.SearchArgs -> Maybe Model -> ( Model, Cmd Msg ) +init searchArgs model = let ( newModel, newCmd ) = - Search.init channel query show from size sort model + Search.init searchArgs model in ( newModel , Cmd.map SearchMsg newCmd diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index a4901fb..99ea000 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -102,19 +102,11 @@ type alias ResultPackageHydraPath = } -init : - Maybe String - -> Maybe String - -> Maybe String - -> Maybe Int - -> Maybe Int - -> Maybe String - -> Maybe Model - -> ( Model, Cmd Msg ) -init channel query show from size sort model = +init : Route.SearchArgs -> Maybe Model -> ( Model, Cmd Msg ) +init searchArgs model = let ( newModel, newCmd ) = - Search.init channel query show from size sort model + Search.init searchArgs model in ( newModel , Cmd.map SearchMsg newCmd diff --git a/src/Route.elm b/src/Route.elm index b67a519..86436d7 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -1,12 +1,20 @@ -module Route exposing (Route(..), fromUrl, href, replaceUrl, routeToString) +module Route exposing + ( Route(..) + , SearchArgs + , SearchRoute + , fromUrl + , href + , replaceUrl + , routeToString + ) import Browser.Navigation import Html import Html.Attributes -import Route.SearchQuery +import Route.SearchQuery exposing (SearchQuery) import Url import Url.Builder exposing (QueryParameter) -import Url.Parser exposing (()) +import Url.Parser exposing ((), ()) import Url.Parser.Query @@ -14,45 +22,66 @@ import Url.Parser.Query -- ROUTING -type Route - = NotFound - | Home - -- route | channel | (search) query | show | from | size | sort - | Packages (Maybe String) (Maybe String) (Maybe String) (Maybe Int) (Maybe Int) (Maybe String) - | Options (Maybe String) (Maybe String) (Maybe String) (Maybe Int) (Maybe Int) (Maybe String) +type alias SearchArgs = + { query : Maybe SearchQuery + , channel : Maybe String + , show : Maybe String + , from : Maybe Int + , size : Maybe Int + + -- TODO: embed sort type + , sort : Maybe String + } -parser : Url.Url -> Url.Parser.Parser (Route -> msg) msg -parser url = +type alias SearchRoute = + SearchArgs -> Route + + +searchQueryParser : Url.Url -> Url.Parser.Parser (SearchArgs -> msg) msg +searchQueryParser url = let rawQuery = Route.SearchQuery.toRawQuery url - withSearchQuery : (a -> Maybe String -> b) -> a -> b - withSearchQuery f channel = - f channel <| - Maybe.andThen Route.SearchQuery.searchQueryToString <| - Maybe.andThen (Route.SearchQuery.searchString "query") rawQuery + maybeQuery = Maybe.andThen (Route.SearchQuery.searchString "query") rawQuery in + Url.Parser.map (SearchArgs maybeQuery) <| + Url.Parser.top + Url.Parser.Query.string "channel" + Url.Parser.Query.string "show" + Url.Parser.Query.int "from" + Url.Parser.Query.int "size" + Url.Parser.Query.string "sort" + + +searchArgsToUrl : SearchArgs -> ( List QueryParameter, Maybe ( String, Route.SearchQuery.SearchQuery ) ) +searchArgsToUrl args = + ( List.filterMap identity + [ Maybe.map (Url.Builder.string "channel") args.channel + , Maybe.map (Url.Builder.string "show") args.show + , Maybe.map (Url.Builder.int "from") args.from + , Maybe.map (Url.Builder.int "size") args.size + , Maybe.map (Url.Builder.string "sort") args.sort + ] + , Maybe.map (Tuple.pair "query") args.query + ) + + +type Route + = NotFound + | Home + | Packages SearchArgs + | Options SearchArgs + + +parser : Url.Url -> Url.Parser.Parser (Route -> msg) msg +parser url = Url.Parser.oneOf [ Url.Parser.map Home Url.Parser.top - , Url.Parser.map NotFound (Url.Parser.s "not-found") - , Url.Parser.map (withSearchQuery Packages) - (Url.Parser.s "packages" - Url.Parser.Query.string "channel" - Url.Parser.Query.string "show" - Url.Parser.Query.int "from" - Url.Parser.Query.int "size" - Url.Parser.Query.string "sort" - ) - , Url.Parser.map (withSearchQuery Options) - (Url.Parser.s "options" - Url.Parser.Query.string "channel" - Url.Parser.Query.string "show" - Url.Parser.Query.int "from" - Url.Parser.Query.int "size" - Url.Parser.Query.string "sort" - ) + , Url.Parser.map NotFound <| Url.Parser.s "not-found" + , Url.Parser.map Packages <| Url.Parser.s "packages" searchQueryParser url + , Url.Parser.map Options <| Url.Parser.s "options" searchQueryParser url ] @@ -96,51 +125,17 @@ routeToString = routeToPieces : Route -> ( List String, List QueryParameter, Maybe ( String, Route.SearchQuery.SearchQuery ) ) routeToPieces page = - let - channelQ = - Maybe.map (Url.Builder.string "channel") + case page of + Home -> + ( [], [], Nothing ) - queryQ = - Maybe.map (Route.SearchQuery.toSearchQuery "query") + NotFound -> + ( [ "not-found" ], [], Nothing ) - showQ = - Maybe.map (Url.Builder.string "show") + Packages searchArgs -> + searchArgsToUrl searchArgs + |> (\( query, raw ) -> ( [ "packages" ], query, raw )) - fromQ = - Maybe.map (Url.Builder.int "from") - - sizeQ = - Maybe.map (Url.Builder.int "size") - - sortQ = - Maybe.map (Url.Builder.string "sort") - in - (\( path, urlQ, searchQuery ) -> ( path, List.filterMap identity urlQ, searchQuery )) <| - case page of - Home -> - ( [], [], Nothing ) - - NotFound -> - ( [ "not-found" ], [], Nothing ) - - Packages channel query show from size sort -> - ( [ "packages" ] - , [ channelQ channel - , showQ show - , fromQ from - , sizeQ size - , sortQ sort - ] - , queryQ query - ) - - Options channel query show from size sort -> - ( [ "options" ] - , [ channelQ channel - , showQ show - , fromQ from - , sizeQ size - , sortQ sort - ] - , queryQ query - ) + Options searchArgs -> + searchArgsToUrl searchArgs + |> (\( query, raw ) -> ( [ "options" ], query, raw )) diff --git a/src/Route/SearchQuery.elm b/src/Route/SearchQuery.elm index f2e385b..b4d6a8f 100644 --- a/src/Route/SearchQuery.elm +++ b/src/Route/SearchQuery.elm @@ -60,9 +60,9 @@ searchQueryToString (SearchQuery str) = Url.percentDecode <| String.replace "+" "%20" str -toSearchQuery : String -> String -> ( String, SearchQuery ) -toSearchQuery name query = - ( name, SearchQuery <| String.replace "%20" "+" <| Url.percentEncode query ) +toSearchQuery : String -> SearchQuery +toSearchQuery query = + SearchQuery <| String.replace "%20" "+" <| Url.percentEncode query {-| Build absolute URL with support for search query strings diff --git a/src/Search.elm b/src/Search.elm index bd7ab64..b8eeebd 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -63,22 +63,13 @@ import Json.Decode import Json.Encode import RemoteData import Route exposing (Route) +import Route.SearchQuery import Set import Task import Url import Url.Builder -type alias SearchRoute = - Maybe String - -> Maybe String - -> Maybe String - -> Maybe Int - -> Maybe Int - -> Maybe String - -> Route - - type alias Model a = { channel : String , query : Maybe String @@ -124,16 +115,8 @@ type Sort | AlphabeticallyDesc -init : - Maybe String - -> Maybe String - -> Maybe String - -> Maybe Int - -> Maybe Int - -> Maybe String - -> Maybe (Model a) - -> ( Model a, Cmd (Msg a) ) -init channel query show from size sort model = +init : Route.SearchArgs -> Maybe (Model a) -> ( Model a, Cmd (Msg a) ) +init args model = let defaultChannel = model @@ -150,17 +133,17 @@ init channel query show from size sort model = |> Maybe.map (\x -> x.size) |> Maybe.withDefault 30 in - ( { channel = Maybe.withDefault defaultChannel channel - , query = query + ( { channel = Maybe.withDefault defaultChannel args.channel + , query = Maybe.andThen Route.SearchQuery.searchQueryToString args.query , result = model |> Maybe.map (\x -> x.result) |> Maybe.withDefault RemoteData.NotAsked - , show = show - , from = Maybe.withDefault defaultFrom from - , size = Maybe.withDefault defaultSize size + , show = args.show + , from = Maybe.withDefault defaultFrom args.from + , size = Maybe.withDefault defaultSize args.size , sort = - sort + args.sort |> Maybe.withDefault "" |> fromSortId |> Maybe.withDefault Relevance @@ -186,7 +169,7 @@ type Msg a update : - SearchRoute + Route.SearchRoute -> Browser.Navigation.Key -> Msg a -> Model a @@ -199,24 +182,14 @@ update toRoute navKey msg model = ) SortChange sortId -> - let - sort = - fromSortId sortId |> Maybe.withDefault Relevance - in - ( { model | sort = sort } - , createUrl - toRoute - model.channel - model.query - model.show - 0 - model.size - sort - |> Browser.Navigation.pushUrl navKey - ) + { model + | sort = fromSortId sortId |> Maybe.withDefault Relevance + , from = 0 + } + |> pushUrl toRoute navKey ChannelChange channel -> - ( { model + { model | channel = channel , result = if model.query == Nothing || model.query == Just "" then @@ -224,21 +197,9 @@ update toRoute navKey msg model = else RemoteData.Loading - } - , if model.query == Nothing || model.query == Just "" then - Cmd.none - - else - createUrl - toRoute - channel - model.query - model.show - 0 - model.size - model.sort - |> Browser.Navigation.pushUrl navKey - ) + , from = 0 + } + |> pushUrl toRoute navKey QueryInput query -> ( { model | query = Just query } @@ -246,21 +207,11 @@ update toRoute navKey msg model = ) QueryInputSubmit -> - if model.query == Nothing || model.query == Just "" then - ( model, Cmd.none ) - - else - ( { model | result = RemoteData.Loading } - , createUrl - toRoute - model.channel - model.query - model.show - 0 - model.size - model.sort - |> Browser.Navigation.pushUrl navKey - ) + { model + | result = RemoteData.Loading + , from = 0 + } + |> pushUrl toRoute navKey QueryResponse result -> ( { model | result = result } @@ -268,38 +219,38 @@ update toRoute navKey msg model = ) ShowDetails selected -> - ( model - , createUrl - toRoute - model.channel - model.query - (if model.show == Just selected then - Nothing + { model + | show = + if model.show == Just selected then + Nothing - else - Just selected - ) - model.from - model.size - model.sort - |> Browser.Navigation.pushUrl navKey - ) + else + Just selected + } + |> pushUrl toRoute navKey -{-| TODO: Sort should be part of Route type --} -createUrl : - SearchRoute - -> String - -> Maybe String - -> Maybe String - -> Int - -> Int - -> Sort - -> String -createUrl toRoute channel query show from size sort = - toRoute (Just channel) query show (Just from) (Just size) (Just <| toSortId sort) - |> Route.routeToString +pushUrl : Route.SearchRoute -> Browser.Navigation.Key -> Model a -> ( Model a, Cmd msg ) +pushUrl toRoute navKey model = + Tuple.pair model <| + if model.query == Nothing || model.query == Just "" then + Cmd.none + + else + Browser.Navigation.pushUrl navKey <| createUrl toRoute model + + +createUrl : Route.SearchRoute -> Model a -> String +createUrl toRoute model = + Route.routeToString <| + toRoute + { channel = Just model.channel + , query = Maybe.map Route.SearchQuery.toSearchQuery model.query + , show = model.show + , from = Just model.from + , size = Just model.size + , sort = Just <| toSortId model.sort + } @@ -448,7 +399,7 @@ fromSortId id = view : - { toRoute : SearchRoute + { toRoute : Route.SearchRoute , categoryName : String } -> String @@ -624,7 +575,7 @@ viewPager : (Msg a -> b) -> Model a -> SearchResult a - -> SearchRoute + -> Route.SearchRoute -> Html b viewPager _ model result toRoute = ul [ class "pager" ] @@ -639,14 +590,7 @@ viewPager _ model result toRoute = "" else - createUrl - toRoute - model.channel - model.query - model.show - 0 - model.size - model.sort + createUrl toRoute { model | from = 0 } ] [ text "First" ] ] @@ -661,14 +605,7 @@ viewPager _ model result toRoute = "" else - createUrl - toRoute - model.channel - model.query - model.show - (model.from - model.size) - model.size - model.sort + createUrl toRoute { model | from = model.from - model.size } ] [ text "Previous" ] ] @@ -683,14 +620,7 @@ viewPager _ model result toRoute = "" else - createUrl - toRoute - model.channel - model.query - model.show - (model.from + model.size) - model.size - model.sort + createUrl toRoute { model | from = model.from + model.size } ] [ text "Next" ] ] @@ -713,14 +643,8 @@ viewPager _ model result toRoute = else 0 in - createUrl - toRoute - model.channel - model.query - model.show - (((result.hits.total.value // model.size) - remainder) * model.size) - model.size - model.sort + createUrl toRoute + { model | from = ((result.hits.total.value // model.size) - remainder) * model.size } ] [ text "Last" ] ]