From 7f68609bea7ae7e657c77ee54437a681c963bf5c Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Fri, 4 Dec 2020 10:39:51 +0100 Subject: [PATCH 01/25] packages aggs fields --- VERSION | 2 +- import-scripts/import_scripts/channel.py | 4 ++ src/Page/Options.elm | 1 + src/Page/Packages.elm | 5 ++ src/Search.elm | 72 +++++++++++++++++++++++- 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index b6a7d89c..98d9bcb7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -16 +17 diff --git a/import-scripts/import_scripts/channel.py b/import-scripts/import_scripts/channel.py index 536cae91..707dc175 100644 --- a/import-scripts/import_scripts/channel.py +++ b/import-scripts/import_scripts/channel.py @@ -148,6 +148,7 @@ "type": "nested", "properties": {"fullName": {"type": "text"}, "url": {"type": "text"}}, }, + "package_license_set": {"type": "keyword"}, "package_maintainers": { "type": "nested", "properties": { @@ -156,6 +157,7 @@ "github": {"type": "text"}, }, }, + "package_maintainers_set": {"type": "keyword"}, "package_platforms": {"type": "keyword"}, "package_position": {"type": "text"}, "package_homepage": {"type": "keyword"}, @@ -486,7 +488,9 @@ def gen(): package_longDescription=package_longDescription, package_longDescription_reverse=field_reverse(package_longDescription), package_license=licenses, + package_license_set=[i["fullName"] for i in licenses], package_maintainers=maintainers, + package_maintainers_set=[i["name"] for i in maintainers if i["name"]], package_platforms=[i for i in platforms if i], package_position=position, package_homepage=data["meta"].get("homepage"), diff --git a/src/Page/Options.elm b/src/Page/Options.elm index ca5f649a..a76aea8f 100644 --- a/src/Page/Options.elm +++ b/src/Page/Options.elm @@ -283,6 +283,7 @@ makeRequest options channel query from size sort = sort "option" "option_name" + [] [ ( "option_name", 6.0 ) , ( "option_name_query", 3.0 ) , ( "option_description", 1.0 ) diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index db6f3fc5..b4ec6395 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -407,6 +407,11 @@ makeRequest options channel query from size sort = sort "package" "package_attr_name" + [ "package_attr_set" + , "package_license_set" + , "package_maintainers_set" + , "package_platform" + ] [ ( "package_attr_name", 9.0 ) , ( "package_pname", 6.0 ) , ( "package_attr_name_query", 4.0 ) diff --git a/src/Search.elm b/src/Search.elm index 38359f40..919f6275 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -346,6 +346,47 @@ sortBy = ] +toAggs : + List String + -> ( String, Json.Encode.Value ) +toAggs aggsFields = + let + fields = + List.map + (\field -> + ( field + , Json.Encode.object + [ ( "terms" + , Json.Encode.object + [ ( "field" + , Json.Encode.string field + ) + ] + ) + ] + ) + ) + aggsFields + + allFields = + [ ( "all" + , Json.Encode.object + [ ( "global" + , Json.Encode.object [] + ) + , ( "aggs" + , Json.Encode.object fields + ) + ] + ) + ] + in + ( "aggs" + , Json.Encode.object <| + List.append fields allFields + ) + + toSortQuery : Sort -> String @@ -751,9 +792,10 @@ makeRequestBody : -> Sort -> String -> String + -> List String -> List ( String, Float ) -> Http.Body -makeRequestBody query from sizeRaw sort type_ sortField fields = +makeRequestBody query from sizeRaw sort type_ sortField aggsFields fields = let -- you can not request more then 10000 results otherwise it will return 404 size = @@ -772,6 +814,34 @@ makeRequestBody query from sizeRaw sort type_ sortField fields = , Json.Encode.int size ) , toSortQuery sort sortField + , toAggs aggsFields + + --, ( "aggs" + -- , Json.Encode.object + -- [ ( "package_attr_set" + -- , Json.Encode.object + -- [ ( "terms" + -- , Json.Encode.object + -- [ ( "field" + -- , Json.Encode.string "package_attr_set" + -- ) + -- ] + -- ) + -- ] + -- ) + -- , ( "package_license_set" + -- , Json.Encode.object + -- [ ( "terms" + -- , Json.Encode.object + -- [ ( "field" + -- , Json.Encode.string "package_license_set" + -- ) + -- ] + -- ) + -- ] + -- ) + -- ] + -- ) , ( "query" , Json.Encode.object [ ( "bool" From 4f50c5b0bcb2d9c08b3b8d05121d785bdc55a95e Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Wed, 6 Jan 2021 15:54:10 +0100 Subject: [PATCH 02/25] initial work --- VERSION | 2 +- src/Page/Options.elm | 40 ++++++++- src/Page/Packages.elm | 131 ++++++++++++++++++++++++---- src/Search.elm | 193 ++++++++++++++++++++++++++++-------------- src/index.less | 18 ++++ 5 files changed, 300 insertions(+), 84 deletions(-) diff --git a/VERSION b/VERSION index 98d9bcb7..3c032078 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -17 +18 diff --git a/src/Page/Options.elm b/src/Page/Options.elm index 21ee4933..aae49050 100644 --- a/src/Page/Options.elm +++ b/src/Page/Options.elm @@ -18,6 +18,8 @@ import Html , div , dl , dt + , h4 + , li , pre , span , table @@ -27,6 +29,7 @@ import Html , th , thead , tr + , ul ) import Html.Attributes exposing @@ -53,7 +56,7 @@ import Search type alias Model = - Search.Model ResultItemSource + Search.Model ResultItemSource ResultAggregations type alias ResultItemSource = @@ -66,6 +69,16 @@ type alias ResultItemSource = } +type alias ResultAggregations = + { all : AggregationsAll + } + + +type alias AggregationsAll = + { doc_count : Int + } + + init : Route.SearchArgs -> Maybe Model -> ( Model, Cmd Msg ) init searchArgs model = let @@ -82,7 +95,7 @@ init searchArgs model = type Msg - = SearchMsg (Search.Msg ResultItemSource) + = SearchMsg (Search.Msg ResultItemSource ResultAggregations) update : @@ -114,13 +127,21 @@ view model = "Search NixOS options" model viewSuccess + viewSearchFaceted SearchMsg +viewSearchFaceted : + Search.SearchResult ResultItemSource ResultAggregations + -> List (Html Msg) +viewSearchFaceted result = + [ ul [ class "nav nav-list" ] [] ] + + viewSuccess : String -> Maybe String - -> Search.SearchResult ResultItemSource + -> Search.SearchResult ResultItemSource ResultAggregations -> Html Msg viewSuccess channel show result = div [ class "search-result" ] @@ -302,6 +323,7 @@ makeRequest options channel query from size sort = ) ("latest-" ++ String.fromInt options.mappingSchemaVersion ++ "-" ++ channel) decodeResultItemSource + decodeResultAggregations options Search.QueryResponse (Just "query-options") @@ -321,3 +343,15 @@ decodeResultItemSource = (Json.Decode.field "option_default" (Json.Decode.nullable Json.Decode.string)) (Json.Decode.field "option_example" (Json.Decode.nullable Json.Decode.string)) (Json.Decode.field "option_source" (Json.Decode.nullable Json.Decode.string)) + + +decodeResultAggregations : Json.Decode.Decoder ResultAggregations +decodeResultAggregations = + Json.Decode.map ResultAggregations + (Json.Decode.field "all" decodeResultAggregationsAll) + + +decodeResultAggregationsAll : Json.Decode.Decoder AggregationsAll +decodeResultAggregationsAll = + Json.Decode.map AggregationsAll + (Json.Decode.field "doc_count" Json.Decode.int) diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index fc418205..a4eb85fc 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -18,8 +18,10 @@ import Html , div , dl , dt + , h4 , li , pre + , span , table , tbody , td @@ -42,7 +44,6 @@ import Html.Events ) import Json.Decode import Json.Decode.Pipeline -import Json.Encode import Regex import Route import Search @@ -53,7 +54,7 @@ import Search type alias Model = - Search.Model ResultItemSource + Search.Model ResultItemSource ResultAggregations type alias ResultItemSource = @@ -103,6 +104,24 @@ type alias ResultPackageHydraPath = } +type alias ResultAggregations = + { all : AggregationsAll + , package_platform : Search.Aggregation + , package_attr_set : Search.Aggregation + , package_maintainers_set : Search.Aggregation + , package_license_set : Search.Aggregation + } + + +type alias AggregationsAll = + { doc_count : Int + , package_platform : Search.Aggregation + , package_attr_set : Search.Aggregation + , package_maintainers_set : Search.Aggregation + , package_license_set : Search.Aggregation + } + + init : Route.SearchArgs -> Maybe Model -> ( Model, Cmd Msg ) init searchArgs model = let @@ -119,7 +138,7 @@ init searchArgs model = type Msg - = SearchMsg (Search.Msg ResultItemSource) + = SearchMsg (Search.Msg ResultItemSource ResultAggregations) update : @@ -151,13 +170,68 @@ view model = "Search NixOS packages" model viewSuccess + viewSearchFaceted SearchMsg +viewSearchFaceted : + Search.SearchResult ResultItemSource ResultAggregations + -> List (Html Msg) +viewSearchFaceted result = + -- [ ( "Packages set", "package_attr_set" ) + -- , ( "Licenses", "package_license_set" ) + -- , ( "Maintainers", "package_maintainers_set" ) + -- , ( "Platforms", "package_platform" ) + -- ] + let + _ = + Debug.log "Result" result.aggregations.package_attr_set + in + [ ul [ class "nav nav-list" ] + ([] + |> viewSearchFacetedSet "Package sets" result.aggregations.package_attr_set + |> viewSearchFacetedSet "Licenses" result.aggregations.package_license_set + |> viewSearchFacetedSet "Platforms" result.aggregations.package_platform + |> viewSearchFacetedSet "Maintainers" result.aggregations.package_maintainers_set + ) + ] + + +viewSearchFacetedSet : + String + -> Search.Aggregation + -> List (Html Msg) + -> List (Html Msg) +viewSearchFacetedSet title aggregation sets = + List.append + sets + (if List.isEmpty aggregation.buckets then + [] + + else + [ li [] + [ h4 [] [ text title ] + , ul [ class "nav nav-tabs nav-stacked" ] + (List.map + (\bucket -> + li [] + [ a [ href "#" ] + [ span [] [ text bucket.key ] + , span [ class "badge badge-info" ] [ text <| String.fromInt bucket.doc_count ] + ] + ] + ) + aggregation.buckets + ) + ] + ] + ) + + viewSuccess : String -> Maybe String - -> Search.SearchResult ResultItemSource + -> Search.SearchResult ResultItemSource ResultAggregations -> Html Msg viewSuccess channel show result = div [ class "search-result" ] @@ -424,6 +498,7 @@ makeRequest options channel query from size sort = ) ("latest-" ++ String.fromInt options.mappingSchemaVersion ++ "-" ++ channel) decodeResultItemSource + decodeResultAggregations options Search.QueryResponse (Just "query-packages") @@ -434,20 +509,6 @@ makeRequest options channel query from size sort = -- JSON -decodeHomepage : Json.Decode.Decoder (List String) -decodeHomepage = - Json.Decode.oneOf - -- null becomes [] (empty list) - [ Json.Decode.null [] - - -- "foo" becomes ["foo"] - , Json.Decode.map List.singleton Json.Decode.string - - -- arrays are decoded to list as expected - , Json.Decode.list Json.Decode.string - ] - - decodeResultItemSource : Json.Decode.Decoder ResultItemSource decodeResultItemSource = Json.Decode.succeed ResultItemSource @@ -465,6 +526,20 @@ decodeResultItemSource = |> Json.Decode.Pipeline.required "package_hydra" (Json.Decode.nullable (Json.Decode.list decodeResultPackageHydra)) +decodeHomepage : Json.Decode.Decoder (List String) +decodeHomepage = + Json.Decode.oneOf + -- null becomes [] (empty list) + [ Json.Decode.null [] + + -- "foo" becomes ["foo"] + , Json.Decode.map List.singleton Json.Decode.string + + -- arrays are decoded to list as expected + , Json.Decode.list Json.Decode.string + ] + + decodeResultPackageLicense : Json.Decode.Decoder ResultPackageLicense decodeResultPackageLicense = Json.Decode.map2 ResultPackageLicense @@ -498,3 +573,23 @@ decodeResultPackageHydraPath = Json.Decode.map2 ResultPackageHydraPath (Json.Decode.field "output" Json.Decode.string) (Json.Decode.field "path" Json.Decode.string) + + +decodeResultAggregations : Json.Decode.Decoder ResultAggregations +decodeResultAggregations = + Json.Decode.map5 ResultAggregations + (Json.Decode.field "all" decodeAggregationsAll) + (Json.Decode.field "package_platform" Search.decodeAggregation) + (Json.Decode.field "package_attr_set" Search.decodeAggregation) + (Json.Decode.field "package_maintainers_set" Search.decodeAggregation) + (Json.Decode.field "package_license_set" Search.decodeAggregation) + + +decodeAggregationsAll : Json.Decode.Decoder AggregationsAll +decodeAggregationsAll = + Json.Decode.map5 AggregationsAll + (Json.Decode.field "doc_count" Json.Decode.int) + (Json.Decode.field "package_platform" Search.decodeAggregation) + (Json.Decode.field "package_attr_set" Search.decodeAggregation) + (Json.Decode.field "package_maintainers_set" Search.decodeAggregation) + (Json.Decode.field "package_license_set" Search.decodeAggregation) diff --git a/src/Search.elm b/src/Search.elm index 8ff00392..81fdc84b 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -1,5 +1,7 @@ module Search exposing - ( Model + ( Aggregation + , AggregationsBucketItem + , Model , Msg(..) , Options , ResultItem @@ -7,6 +9,7 @@ module Search exposing , Sort(..) , channelDetailsFromId , channels + , decodeAggregation , decodeResult , elementId , fromSortId @@ -72,10 +75,10 @@ import Url import Url.Builder -type alias Model a = +type alias Model a b = { channel : String , query : Maybe String - , result : RemoteData.WebData (SearchResult a) + , result : RemoteData.WebData (SearchResult a b) , show : Maybe String , from : Int , size : Int @@ -83,8 +86,9 @@ type alias Model a = } -type alias SearchResult a = +type alias SearchResult a b = { hits : ResultHits a + , aggregations : b } @@ -111,13 +115,29 @@ type alias ResultItem a = } +type alias Aggregation = + { doc_count_error_upper_bound : Int + , sum_other_doc_count : Int + , buckets : List AggregationsBucketItem + } + + +type alias AggregationsBucketItem = + { doc_count : Int + , key : String + } + + type Sort = Relevance | AlphabeticallyAsc | AlphabeticallyDesc -init : Route.SearchArgs -> Maybe (Model a) -> ( Model a, Cmd (Msg a) ) +init : + Route.SearchArgs + -> Maybe (Model a b) + -> ( Model a b, Cmd (Msg a b) ) init args model = let channel = @@ -155,12 +175,16 @@ init args model = ) -shouldLoad : Model a -> Bool +shouldLoad : + Model a b + -> Bool shouldLoad model = model.result == RemoteData.Loading -ensureLoading : Model a -> Model a +ensureLoading : + Model a b + -> Model a b ensureLoading model = if model.query /= Nothing && model.query /= Just "" && List.member model.channel channels then { model | result = RemoteData.Loading } @@ -180,18 +204,20 @@ elementId str = -- --------------------------- -type Msg a +type Msg a b = NoOp | SortChange String | ChannelChange String | QueryInput String | QueryInputSubmit - | QueryResponse (RemoteData.WebData (SearchResult a)) + | QueryResponse (RemoteData.WebData (SearchResult a b)) | ShowDetails String | ChangePage Int -scrollToEntry : Maybe String -> Cmd (Msg a) +scrollToEntry : + Maybe String + -> Cmd (Msg a b) scrollToEntry val = let doScroll id = @@ -205,9 +231,9 @@ scrollToEntry val = update : Route.SearchRoute -> Browser.Navigation.Key - -> Msg a - -> Model a - -> ( Model a, Cmd (Msg a) ) + -> Msg a b + -> Model a b + -> ( Model a b, Cmd (Msg a b) ) update toRoute navKey msg model = case msg of NoOp -> @@ -263,7 +289,11 @@ update toRoute navKey msg model = |> pushUrl toRoute navKey -pushUrl : Route.SearchRoute -> Browser.Navigation.Key -> Model a -> ( Model a, Cmd msg ) +pushUrl : + Route.SearchRoute + -> Browser.Navigation.Key + -> Model a b + -> ( Model a b, Cmd msg ) pushUrl toRoute navKey model = Tuple.pair model <| if model.query == Nothing || model.query == Just "" then @@ -273,7 +303,10 @@ pushUrl toRoute navKey model = Browser.Navigation.pushUrl navKey <| createUrl toRoute model -createUrl : Route.SearchRoute -> Model a -> String +createUrl : + Route.SearchRoute + -> Model a b + -> String createUrl toRoute model = Route.routeToString <| toRoute @@ -477,11 +510,12 @@ view : , categoryName : String } -> String - -> Model a - -> (String -> Maybe String -> SearchResult a -> Html b) - -> (Msg a -> b) - -> Html b -view { toRoute, categoryName } title model viewSuccess outMsg = + -> Model a b + -> (String -> Maybe String -> SearchResult a b -> Html c) + -> (SearchResult a b -> List (Html c)) + -> (Msg a b -> c) + -> Html c +view { toRoute, categoryName } title model viewSuccess viewSearchFaceted outMsg = div [ class "search-page" ] @@ -572,43 +606,19 @@ view { toRoute, categoryName } title model viewSuccess outMsg = if result.hits.total.value == 0 then [ h4 [] [ text <| "No " ++ categoryName ++ " found!" ] , text "How to " - , Html.a [ href "https://nixos.org/manual/nixpkgs/stable/#chap-quick-start"] [ text "add" ] + , Html.a [ href "https://nixos.org/manual/nixpkgs/stable/#chap-quick-start" ] [ text "add" ] , text " or " - , a [ href "https://github.com/NixOS/nixpkgs/issues/new?assignees=&labels=0.kind%3A+packaging+request&template=packaging_request.md&title="] [ text "request" ] + , a [ href "https://github.com/NixOS/nixpkgs/issues/new?assignees=&labels=0.kind%3A+packaging+request&template=packaging_request.md&title=" ] [ text "request" ] , text " package to nixpkgs?" ] else - [ p [] - [ em [] - [ text - ("Showing results " - ++ String.fromInt model.from - ++ "-" - ++ String.fromInt - (if model.from + model.size > result.hits.total.value then - result.hits.total.value - - else - model.from + model.size - ) - ++ " of " - ++ (if result.hits.total.value == 10000 then - "more than 10000 results, please provide more precise search terms." - - else - String.fromInt result.hits.total.value - ++ "." - ) - ) - ] - ] - , div [] - [ viewSortSelection outMsg model - , viewPager outMsg model result.hits.total.value toRoute + [ div [ class "row" ] + [ div [ class "span3 search-faceted" ] + (viewSearchFaceted result) + , div [ class "span9" ] + (viewResults model result viewSuccess toRoute outMsg) ] - , viewSuccess model.channel model.show result - , viewPager outMsg model result.hits.total.value toRoute ] RemoteData.Failure error -> @@ -638,7 +648,48 @@ view { toRoute, categoryName } title model viewSuccess outMsg = ] -viewSortSelection : (Msg a -> b) -> Model a -> Html b +viewResults : + Model a b + -> SearchResult a b + -> (String -> Maybe String -> SearchResult a b -> Html c) + -> Route.SearchRoute + -> (Msg a b -> c) + -> List (Html c) +viewResults model result viewSuccess toRoute outMsg = + [ p [] + [ em [] + [ text + ("Showing results " + ++ String.fromInt model.from + ++ "-" + ++ String.fromInt + (if model.from + model.size > result.hits.total.value then + result.hits.total.value + + else + model.from + model.size + ) + ++ " of " + ++ (if result.hits.total.value == 10000 then + "more than 10000 results, please provide more precise search terms." + + else + String.fromInt result.hits.total.value + ++ "." + ) + ) + ] + ] + , div [] + [ viewSortSelection outMsg model + , viewPager outMsg model result.hits.total.value toRoute + ] + , viewSuccess model.channel model.show result + , viewPager outMsg model result.hits.total.value toRoute + ] + + +viewSortSelection : (Msg a b -> c) -> Model a b -> Html c viewSortSelection outMsg model = form [ class "form-horizontal pull-right sort-form" ] [ div [ class "control-group sort-group" ] @@ -664,11 +715,11 @@ viewSortSelection outMsg model = viewPager : - (Msg a -> b) - -> Model a + (Msg a b -> c) + -> Model a b -> Int -> Route.SearchRoute - -> Html b + -> Html c viewPager outMsg model total toRoute = Html.map outMsg <| ul [ class "pager" ] @@ -921,11 +972,12 @@ makeRequest : Http.Body -> String -> Json.Decode.Decoder a + -> Json.Decode.Decoder b -> Options - -> (RemoteData.WebData (SearchResult a) -> Msg a) + -> (RemoteData.WebData (SearchResult a b) -> Msg a b) -> Maybe String - -> Cmd (Msg a) -makeRequest body index decodeResultItemSource options responseMsg tracker = + -> Cmd (Msg a b) +makeRequest body index decodeResultItemSource decodeResultAggregations options responseMsg tracker = Http.riskyRequest { method = "POST" , headers = @@ -936,7 +988,7 @@ makeRequest body index decodeResultItemSource options responseMsg tracker = , expect = Http.expectJson (RemoteData.fromResult >> responseMsg) - (decodeResult decodeResultItemSource) + (decodeResult decodeResultItemSource decodeResultAggregations) , timeout = Nothing , tracker = tracker } @@ -948,10 +1000,12 @@ makeRequest body index decodeResultItemSource options responseMsg tracker = decodeResult : Json.Decode.Decoder a - -> Json.Decode.Decoder (SearchResult a) -decodeResult decodeResultItemSource = - Json.Decode.map SearchResult + -> Json.Decode.Decoder b + -> Json.Decode.Decoder (SearchResult a b) +decodeResult decodeResultItemSource decodeResultAggregations = + Json.Decode.map2 SearchResult (Json.Decode.field "hits" (decodeResultHits decodeResultItemSource)) + (Json.Decode.field "aggregations" decodeResultAggregations) decodeResultHits : Json.Decode.Decoder a -> Json.Decode.Decoder (ResultHits a) @@ -978,3 +1032,18 @@ decodeResultItem decodeResultItemSource = (Json.Decode.field "_source" decodeResultItemSource) (Json.Decode.maybe (Json.Decode.field "text" Json.Decode.string)) (Json.Decode.maybe (Json.Decode.field "matched_queries" (Json.Decode.list Json.Decode.string))) + + +decodeAggregation : Json.Decode.Decoder Aggregation +decodeAggregation = + Json.Decode.map3 Aggregation + (Json.Decode.field "doc_count_error_upper_bound" Json.Decode.int) + (Json.Decode.field "sum_other_doc_count" Json.Decode.int) + (Json.Decode.field "buckets" (Json.Decode.list decodeAggregationBucketItem)) + + +decodeAggregationBucketItem : Json.Decode.Decoder AggregationsBucketItem +decodeAggregationBucketItem = + Json.Decode.map2 AggregationsBucketItem + (Json.Decode.field "doc_count" Json.Decode.int) + (Json.Decode.field "key" Json.Decode.string) diff --git a/src/index.less b/src/index.less index d197cb3c..65d11742 100644 --- a/src/index.less +++ b/src/index.less @@ -59,6 +59,24 @@ header .navbar.navbar-static-top { margin-left: 0.2em; } } + +.search-faceted { + margin-top: 6.5em; + + & > ul { + padding: 0; + } + + ul.nav-stacked > li > a { + display: flex; + justify-content: space-between; + + & > span:last-child { + align-self: center; + } + } +} + .search-result { tbody > tr { cursor: pointer; From 521f770cfe5b1e09ff81f27f8d630f818b470b61 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Thu, 7 Jan 2021 01:57:31 +0100 Subject: [PATCH 03/25] working with only one selected bucket --- src/Main.elm | 4 +- src/Page/Options.elm | 21 +++-- src/Page/Packages.elm | 164 ++++++++++++++++++++++++++++-------- src/Route.elm | 5 +- src/Search.elm | 191 ++++++++++++++++++++++-------------------- src/Utils.elm | 13 +++ 6 files changed, 263 insertions(+), 135 deletions(-) create mode 100644 src/Utils.elm diff --git a/src/Main.elm b/src/Main.elm index 0fdfb910..f6f67e02 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -32,7 +32,6 @@ import Page.Packages import Route import Search import Url -import Url.Builder @@ -129,6 +128,7 @@ attemptQuery (( model, _ ) as pair) = (Maybe.withDefault "" searchModel.query) searchModel.from searchModel.size + searchModel.selectedBuckets searchModel.sort ] ) @@ -365,7 +365,7 @@ viewNavigation route = f searchArgs _ -> - f <| Route.SearchArgs Nothing Nothing Nothing Nothing Nothing Nothing + f <| Route.SearchArgs Nothing 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 a4267bcd..362c7312 100644 --- a/src/Page/Options.elm +++ b/src/Page/Options.elm @@ -127,23 +127,24 @@ view model = "Search NixOS options" model viewSuccess - viewSearchFaceted + viewBuckets SearchMsg -viewSearchFaceted : - Search.SearchResult ResultItemSource ResultAggregations +viewBuckets : + Maybe String + -> Search.SearchResult ResultItemSource ResultAggregations -> List (Html Msg) -viewSearchFaceted result = - [ ul [ class "nav nav-list" ] [] ] +viewBuckets _ _ = + [] viewSuccess : String -> Maybe String - -> Search.SearchResult ResultItemSource ResultAggregations + -> List (Search.ResultItem ResultItemSource) -> Html Msg -viewSuccess channel show result = +viewSuccess channel show hits = div [ class "search-result" ] [ table [ class "table table-hover" ] [ thead [] @@ -155,7 +156,7 @@ viewSuccess channel show result = [] (List.concatMap (viewResultItem channel show) - result.hits.hits + hits ) ] ] @@ -319,9 +320,10 @@ makeRequest : -> String -> Int -> Int + -> Maybe String -> Search.Sort -> Cmd Msg -makeRequest options channel query from size sort = +makeRequest options channel query from size buckets sort = Search.makeRequest (Search.makeRequestBody (String.trim query) @@ -331,6 +333,7 @@ makeRequest options channel query from size sort = "option" "option_name" [] + [] [ ( "option_name", 6.0 ) , ( "option_name_query", 3.0 ) , ( "option_description", 1.0 ) diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index 24b83dfc..27c9b9f2 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -44,9 +44,11 @@ import Html.Events ) import Json.Decode import Json.Decode.Pipeline +import Json.Encode import Regex import Route import Search +import Utils @@ -106,7 +108,7 @@ type alias ResultPackageHydraPath = type alias ResultAggregations = { all : AggregationsAll - , package_platform : Search.Aggregation + , package_platforms : Search.Aggregation , package_attr_set : Search.Aggregation , package_maintainers_set : Search.Aggregation , package_license_set : Search.Aggregation @@ -115,13 +117,40 @@ type alias ResultAggregations = type alias AggregationsAll = { doc_count : Int - , package_platform : Search.Aggregation + , package_platforms : Search.Aggregation , package_attr_set : Search.Aggregation , package_maintainers_set : Search.Aggregation , package_license_set : Search.Aggregation } +type alias Buckets = + { packageSets : List String + , licenses : List String + , maintainers : List String + , platforms : List String + } + + +emptyBuckets : Buckets +emptyBuckets = + { packageSets = [] + , licenses = [] + , maintainers = [] + , platforms = [] + } + + +initBuckets : + Maybe String + -> Buckets +initBuckets bucketsAsString = + bucketsAsString + |> Maybe.map (Json.Decode.decodeString decodeBuckets) + |> Maybe.andThen Result.toMaybe + |> Maybe.withDefault emptyBuckets + + init : Route.SearchArgs -> Maybe Model -> ( Model, Cmd Msg ) init searchArgs model = let @@ -170,42 +199,57 @@ view model = "Search NixOS packages" model viewSuccess - viewSearchFaceted + viewBuckets SearchMsg -viewSearchFaceted : - Search.SearchResult ResultItemSource ResultAggregations +viewBuckets : + Maybe String + -> Search.SearchResult ResultItemSource ResultAggregations -> List (Html Msg) -viewSearchFaceted result = - -- [ ( "Packages set", "package_attr_set" ) - -- , ( "Licenses", "package_license_set" ) - -- , ( "Maintainers", "package_maintainers_set" ) - -- , ( "Platforms", "package_platform" ) - -- ] +viewBuckets bucketsAsString result = let - _ = - Debug.log "Result" result.aggregations.package_attr_set + initialBuckets = + initBuckets bucketsAsString + + createBucketsMsg getBucket mergeBuckets value = + value + |> Utils.toggleList (getBucket initialBuckets) + |> mergeBuckets initialBuckets + |> encodeBuckets + |> Json.Encode.encode 0 + |> Search.BucketsChange + |> SearchMsg in - [ ul [ class "nav nav-list" ] - ([] - |> viewSearchFacetedSet "Package sets" result.aggregations.package_attr_set - |> viewSearchFacetedSet "Licenses" result.aggregations.package_license_set - |> viewSearchFacetedSet "Platforms" result.aggregations.package_platform - |> viewSearchFacetedSet "Maintainers" result.aggregations.package_maintainers_set - ) - ] + [] + |> viewSearchFacetedSet + "Package sets" + (result.aggregations.package_attr_set.buckets |> List.sortBy .doc_count |> List.reverse) + (createBucketsMsg .packageSets (\s v -> { s | packageSets = v })) + |> viewSearchFacetedSet + "Licenses" + (result.aggregations.package_license_set.buckets |> List.sortBy .doc_count |> List.reverse) + (createBucketsMsg .licenses (\s v -> { s | licenses = v })) + |> viewSearchFacetedSet + "Platforms" + (result.aggregations.package_platforms.buckets |> List.sortBy .doc_count |> List.reverse) + (createBucketsMsg .platforms (\s v -> { s | platforms = v })) + |> viewSearchFacetedSet + "Maintainers" + (result.aggregations.package_maintainers_set.buckets |> List.sortBy .doc_count |> List.reverse) + (createBucketsMsg .maintainers (\s v -> { s | maintainers = v })) viewSearchFacetedSet : String - -> Search.Aggregation + -> List Search.AggregationsBucketItem + -> (String -> Msg) -> List (Html Msg) -> List (Html Msg) -viewSearchFacetedSet title aggregation sets = +viewSearchFacetedSet title buckets searchMsgFor sets = List.append sets - (if List.isEmpty aggregation.buckets then + (if List.isEmpty buckets then [] else @@ -215,13 +259,16 @@ viewSearchFacetedSet title aggregation sets = (List.map (\bucket -> li [] - [ a [ href "#" ] + [ a + [ href "#" + , onClick <| searchMsgFor bucket.key + ] [ span [] [ text bucket.key ] , span [ class "badge badge-info" ] [ text <| String.fromInt bucket.doc_count ] ] ] ) - aggregation.buckets + buckets ) ] ] @@ -231,9 +278,9 @@ viewSearchFacetedSet title aggregation sets = viewSuccess : String -> Maybe String - -> Search.SearchResult ResultItemSource ResultAggregations + -> List (Search.ResultItem ResultItemSource) -> Html Msg -viewSuccess channel show result = +viewSuccess channel show hits = div [ class "search-result" ] [ table [ class "table table-hover" ] [ thead [] @@ -248,7 +295,7 @@ viewSuccess channel show result = [] (List.concatMap (viewResultItem channel show) - result.hits.hits + hits ) ] ] @@ -487,9 +534,40 @@ makeRequest : -> String -> Int -> Int + -> Maybe String -> Search.Sort -> Cmd Msg -makeRequest options channel query from size sort = +makeRequest options channel query from size maybeBuckets sort = + let + filterByBucket field value = + [ ( "term" + , Json.Encode.object + [ ( field + , Json.Encode.object + [ ( "value", Json.Encode.string value ) + , ( "_name", Json.Encode.string <| "filter_bucket_" ++ field ) + ] + ) + ] + ) + ] + + filterByBuckets = + let + buckets = + initBuckets maybeBuckets + in + [ ( "package_attr_set", buckets.packageSets ) + , ( "package_license_set", buckets.licenses ) + , ( "package_maintainers_set", buckets.maintainers ) + , ( "package_platforms", buckets.platforms ) + ] + |> List.map + (\( field, items ) -> + List.map (filterByBucket field) items + ) + |> List.concat + in Search.makeRequest (Search.makeRequestBody (String.trim query) @@ -501,8 +579,9 @@ makeRequest options channel query from size sort = [ "package_attr_set" , "package_license_set" , "package_maintainers_set" - , "package_platform" + , "package_platforms" ] + filterByBuckets [ ( "package_attr_name", 9.0 ) , ( "package_pname", 6.0 ) , ( "package_attr_name_query", 4.0 ) @@ -523,6 +602,25 @@ makeRequest options channel query from size sort = -- JSON +encodeBuckets : Buckets -> Json.Encode.Value +encodeBuckets options = + Json.Encode.object + [ ( "package_attr_set", Json.Encode.list Json.Encode.string options.packageSets ) + , ( "package_license_set", Json.Encode.list Json.Encode.string options.licenses ) + , ( "package_maintainers_set", Json.Encode.list Json.Encode.string options.maintainers ) + , ( "package_platforms", Json.Encode.list Json.Encode.string options.platforms ) + ] + + +decodeBuckets : Json.Decode.Decoder Buckets +decodeBuckets = + Json.Decode.map4 Buckets + (Json.Decode.field "package_attr_set" (Json.Decode.list Json.Decode.string)) + (Json.Decode.field "package_license_set" (Json.Decode.list Json.Decode.string)) + (Json.Decode.field "package_maintainers_set" (Json.Decode.list Json.Decode.string)) + (Json.Decode.field "package_platforms" (Json.Decode.list Json.Decode.string)) + + decodeResultItemSource : Json.Decode.Decoder ResultItemSource decodeResultItemSource = Json.Decode.succeed ResultItemSource @@ -593,7 +691,7 @@ decodeResultAggregations : Json.Decode.Decoder ResultAggregations decodeResultAggregations = Json.Decode.map5 ResultAggregations (Json.Decode.field "all" decodeAggregationsAll) - (Json.Decode.field "package_platform" Search.decodeAggregation) + (Json.Decode.field "package_platforms" Search.decodeAggregation) (Json.Decode.field "package_attr_set" Search.decodeAggregation) (Json.Decode.field "package_maintainers_set" Search.decodeAggregation) (Json.Decode.field "package_license_set" Search.decodeAggregation) @@ -603,7 +701,7 @@ decodeAggregationsAll : Json.Decode.Decoder AggregationsAll decodeAggregationsAll = Json.Decode.map5 AggregationsAll (Json.Decode.field "doc_count" Json.Decode.int) - (Json.Decode.field "package_platform" Search.decodeAggregation) + (Json.Decode.field "package_platforms" Search.decodeAggregation) (Json.Decode.field "package_attr_set" Search.decodeAggregation) (Json.Decode.field "package_maintainers_set" Search.decodeAggregation) (Json.Decode.field "package_license_set" Search.decodeAggregation) diff --git a/src/Route.elm b/src/Route.elm index 2675aba5..39b19b93 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -28,6 +28,7 @@ type alias SearchArgs = , show : Maybe String , from : Maybe Int , size : Maybe Int + , selectedBuckets : Maybe String -- TODO: embed sort type , sort : Maybe String @@ -53,6 +54,7 @@ searchQueryParser url = Url.Parser.Query.string "show" Url.Parser.Query.int "from" Url.Parser.Query.int "size" + Url.Parser.Query.string "selectedBuckets" Url.Parser.Query.string "sort" @@ -63,6 +65,7 @@ searchArgsToUrl args = , 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 "selectedBuckets") args.selectedBuckets , Maybe.map (Url.Builder.string "sort") args.sort ] , Maybe.map (Tuple.pair "query") args.query @@ -82,7 +85,7 @@ parser url = [ Url.Parser.map Home Url.Parser.top , 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 + , Url.Parser.map Options <| Url.Parser.s "selectedBuckets" searchQueryParser url ] diff --git a/src/Search.elm b/src/Search.elm index 81fdc84b..0cbfc1a1 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -71,8 +71,6 @@ import Route exposing (Route) import Route.SearchQuery import Set import Task -import Url -import Url.Builder type alias Model a b = @@ -82,6 +80,7 @@ type alias Model a b = , show : Maybe String , from : Int , size : Int + , selectedBuckets : Maybe String , sort : Sort } @@ -138,32 +137,37 @@ init : Route.SearchArgs -> Maybe (Model a b) -> ( Model a b, Cmd (Msg a b) ) -init args model = +init args maybeModel = let - channel = - model - |> Maybe.map (\x -> x.channel) - |> Maybe.withDefault defaultChannel + getField getFn default = + maybeModel + |> Maybe.map getFn + |> Maybe.withDefault default - from = - model - |> Maybe.map (\x -> x.from) - |> Maybe.withDefault 0 + modelChannel = + getField .channel defaultChannel - size = - model - |> Maybe.map (\x -> x.size) - |> Maybe.withDefault 30 + modelFrom = + getField .from 0 + + modelSize = + getField .size 30 in - ( { channel = Maybe.withDefault channel args.channel - , query = Maybe.andThen Route.SearchQuery.searchQueryToString args.query - , result = - model - |> Maybe.map (\x -> x.result) - |> Maybe.withDefault RemoteData.NotAsked + ( { channel = + args.channel + |> Maybe.withDefault modelChannel + , query = + args.query + |> Maybe.andThen Route.SearchQuery.searchQueryToString + , result = getField .result RemoteData.NotAsked , show = args.show - , from = Maybe.withDefault from args.from - , size = Maybe.withDefault size args.size + , from = + args.from + |> Maybe.withDefault modelFrom + , size = + args.size + |> Maybe.withDefault modelSize + , selectedBuckets = args.selectedBuckets , sort = args.sort |> Maybe.withDefault "" @@ -207,6 +211,7 @@ elementId str = type Msg a b = NoOp | SortChange String + | BucketsChange String | ChannelChange String | QueryInput String | QueryInputSubmit @@ -249,6 +254,19 @@ update toRoute navKey msg model = |> ensureLoading |> pushUrl toRoute navKey + BucketsChange selectedBuckets -> + { model + | selectedBuckets = + if selectedBuckets == "" then + Nothing + + else + Just selectedBuckets + , from = 0 + } + |> ensureLoading + |> pushUrl toRoute navKey + ChannelChange channel -> { model | channel = channel @@ -315,6 +333,7 @@ createUrl toRoute model = , show = model.show , from = Just model.from , size = Just model.size + , selectedBuckets = model.selectedBuckets , sort = Just <| toSortId model.sort } @@ -399,7 +418,7 @@ sortBy = toAggs : List String -> ( String, Json.Encode.Value ) -toAggs aggsFields = +toAggs bucketsFields = let fields = List.map @@ -416,7 +435,7 @@ toAggs aggsFields = ] ) ) - aggsFields + bucketsFields allFields = [ ( "all" @@ -511,11 +530,20 @@ view : } -> String -> Model a b - -> (String -> Maybe String -> SearchResult a b -> Html c) - -> (SearchResult a b -> List (Html c)) + -> + (String + -> Maybe String + -> List (ResultItem a) + -> Html c + ) + -> + (Maybe String + -> SearchResult a b + -> List (Html c) + ) -> (Msg a b -> c) -> Html c -view { toRoute, categoryName } title model viewSuccess viewSearchFaceted outMsg = +view { toRoute, categoryName } title model viewSuccess viewBuckets outMsg = div [ class "search-page" ] @@ -613,13 +641,26 @@ view { toRoute, categoryName } title model viewSuccess viewSearchFaceted outMsg ] else - [ div [ class "row" ] - [ div [ class "span3 search-faceted" ] - (viewSearchFaceted result) - , div [ class "span9" ] - (viewResults model result viewSuccess toRoute outMsg) + let + selectedBuckets = + viewBuckets model.selectedBuckets result + in + if List.length selectedBuckets > 0 then + [ div [ class "row" ] + [ div + [ class "span3 search-faceted" ] + [ ul [ class "nav nav-list" ] selectedBuckets ] + , div [ class "span9" ] + (viewResults model result viewSuccess toRoute outMsg) + ] + ] + + else + [ div [ class "row" ] + [ div [ class "span12" ] + (viewResults model result viewSuccess toRoute outMsg) + ] ] - ] RemoteData.Failure error -> let @@ -651,7 +692,12 @@ view { toRoute, categoryName } title model viewSuccess viewSearchFaceted outMsg viewResults : Model a b -> SearchResult a b - -> (String -> Maybe String -> SearchResult a b -> Html c) + -> + (String + -> Maybe String + -> List (ResultItem a) + -> Html c + ) -> Route.SearchRoute -> (Msg a b -> c) -> List (Html c) @@ -684,7 +730,7 @@ viewResults model result viewSuccess toRoute outMsg = [ viewSortSelection outMsg model , viewPager outMsg model result.hits.total.value toRoute ] - , viewSuccess model.channel model.show result + , viewSuccess model.channel model.show result.hits.hits , viewPager outMsg model result.hits.total.value toRoute ] @@ -790,10 +836,10 @@ type alias Options = } -filter_by_type : +filterByType : String -> List ( String, Json.Encode.Value ) -filter_by_type type_ = +filterByType type_ = [ ( "term" , Json.Encode.object [ ( "type" @@ -867,9 +913,10 @@ makeRequestBody : -> String -> String -> List String + -> List (List ( String, Json.Encode.Value )) -> List ( String, Float ) -> Http.Body -makeRequestBody query from sizeRaw sort type_ sortField aggsFields fields = +makeRequestBody query from sizeRaw sort type_ sortField bucketsFields filterByBuckets fields = let -- you can not request more then 10000 results otherwise it will return 404 size = @@ -888,41 +935,27 @@ makeRequestBody query from sizeRaw sort type_ sortField aggsFields fields = , Json.Encode.int size ) , toSortQuery sort sortField - , toAggs aggsFields - - --, ( "aggs" - -- , Json.Encode.object - -- [ ( "package_attr_set" - -- , Json.Encode.object - -- [ ( "terms" - -- , Json.Encode.object - -- [ ( "field" - -- , Json.Encode.string "package_attr_set" - -- ) - -- ] - -- ) - -- ] - -- ) - -- , ( "package_license_set" - -- , Json.Encode.object - -- [ ( "terms" - -- , Json.Encode.object - -- [ ( "field" - -- , Json.Encode.string "package_license_set" - -- ) - -- ] - -- ) - -- ] - -- ) - -- ] - -- ) + , toAggs bucketsFields , ( "query" , Json.Encode.object [ ( "bool" , Json.Encode.object [ ( "filter" , Json.Encode.list Json.Encode.object - [ filter_by_type type_ ] + [ filterByType type_ + , [ ( "bool" + , Json.Encode.object + [ --( "must" + --, Json.Encode.object (filterByType type_) + --) + ( "should" + , Json.Encode.list Json.Encode.object + filterByBuckets + ) + ] + ) + ] + ] ) , ( "must" , Json.Encode.list Json.Encode.object @@ -932,28 +965,6 @@ makeRequestBody query from sizeRaw sort type_ sortField aggsFields fields = , ( "queries" , Json.Encode.list Json.Encode.object (searchFields query fields) - -- [ [ ( "bool" - -- , Json.Encode.object - -- [ ( "must" - -- , Json.Encode.list Json.Encode.object <| - -- searchFields query fields - -- ) - -- ] - -- ) - -- ] - -- ] - -- , [ ( "bool" - -- , Json.Encode.object - -- [ ( "must" - -- , Json.Encode.list Json.Encode.object <| - -- searchFields - -- 0.8 - -- (String.words query |> List.map String.reverse) - -- ) - -- ] - -- ) - -- ] - --] ) ] ) diff --git a/src/Utils.elm b/src/Utils.elm new file mode 100644 index 00000000..2b0fc4dd --- /dev/null +++ b/src/Utils.elm @@ -0,0 +1,13 @@ +module Utils exposing + ( toggleList + ) + + +toggleList : + List a + -> a + -> List a +toggleList list item = + if List.member item list + then List.filter (\x -> x /= item) list + else List.append list [item] From 9355761c1a174e07561db0d99ce30e0a411f1dda Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Thu, 7 Jan 2021 03:18:36 +0100 Subject: [PATCH 04/25] implement search in post_filter and show checkbox --- src/Main.elm | 2 +- src/Page/Packages.elm | 54 +++++++++++++++++++++++++++++++++++-------- src/Route.elm | 8 +++---- src/Search.elm | 49 +++++++++++++++++---------------------- src/index.less | 6 ++--- 5 files changed, 74 insertions(+), 45 deletions(-) diff --git a/src/Main.elm b/src/Main.elm index f6f67e02..2d21c1b0 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -128,7 +128,7 @@ attemptQuery (( model, _ ) as pair) = (Maybe.withDefault "" searchModel.query) searchModel.from searchModel.size - searchModel.selectedBuckets + searchModel.buckets searchModel.sort ] ) diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index 27c9b9f2..26780ad7 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -19,6 +19,7 @@ import Html , dl , dt , h4 + , input , li , pre , span @@ -33,10 +34,12 @@ import Html ) import Html.Attributes exposing - ( class + ( checked + , class , colspan , href , target + , type_ ) import Html.Events exposing @@ -212,6 +215,16 @@ viewBuckets bucketsAsString result = initialBuckets = initBuckets bucketsAsString + allBuckets = + { packageSets = List.map .key result.aggregations.package_attr_set.buckets + , licenses = List.map .key result.aggregations.package_license_set.buckets + , maintainers = List.map .key result.aggregations.package_platforms.buckets + , platforms = List.map .key result.aggregations.package_maintainers_set.buckets + } + + selectedBucket = + initialBuckets + createBucketsMsg getBucket mergeBuckets value = value |> Utils.toggleList (getBucket initialBuckets) @@ -222,31 +235,36 @@ viewBuckets bucketsAsString result = |> SearchMsg in [] - |> viewSearchFacetedSet + |> viewBucket "Package sets" (result.aggregations.package_attr_set.buckets |> List.sortBy .doc_count |> List.reverse) (createBucketsMsg .packageSets (\s v -> { s | packageSets = v })) - |> viewSearchFacetedSet + selectedBucket.packageSets + |> viewBucket "Licenses" (result.aggregations.package_license_set.buckets |> List.sortBy .doc_count |> List.reverse) (createBucketsMsg .licenses (\s v -> { s | licenses = v })) - |> viewSearchFacetedSet + selectedBucket.licenses + |> viewBucket "Platforms" (result.aggregations.package_platforms.buckets |> List.sortBy .doc_count |> List.reverse) (createBucketsMsg .platforms (\s v -> { s | platforms = v })) - |> viewSearchFacetedSet + selectedBucket.platforms + |> viewBucket "Maintainers" (result.aggregations.package_maintainers_set.buckets |> List.sortBy .doc_count |> List.reverse) (createBucketsMsg .maintainers (\s v -> { s | maintainers = v })) + selectedBucket.maintainers -viewSearchFacetedSet : +viewBucket : String -> List Search.AggregationsBucketItem -> (String -> Msg) + -> List String -> List (Html Msg) -> List (Html Msg) -viewSearchFacetedSet title buckets searchMsgFor sets = +viewBucket title buckets searchMsgFor selectedBucket sets = List.append sets (if List.isEmpty buckets then @@ -263,8 +281,15 @@ viewSearchFacetedSet title buckets searchMsgFor sets = [ href "#" , onClick <| searchMsgFor bucket.key ] - [ span [] [ text bucket.key ] - , span [ class "badge badge-info" ] [ text <| String.fromInt bucket.doc_count ] + [ span [] + [ input + [ type_ "checkbox" + , checked <| List.member bucket.key selectedBucket + ] + [] + ] + , span [] [ text bucket.key ] + , span [] [ span [ class "badge badge-info" ] [ text <| String.fromInt bucket.doc_count ] ] ] ] ) @@ -567,6 +592,17 @@ makeRequest options channel query from size maybeBuckets sort = List.map (filterByBucket field) items ) |> List.concat + |> (\x -> + [ ( "bool" + , Json.Encode.object + [ ( "should" + , Json.Encode.list Json.Encode.object + x + ) + ] + ) + ] + ) in Search.makeRequest (Search.makeRequestBody diff --git a/src/Route.elm b/src/Route.elm index 39b19b93..bfcce39a 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -28,7 +28,7 @@ type alias SearchArgs = , show : Maybe String , from : Maybe Int , size : Maybe Int - , selectedBuckets : Maybe String + , buckets : Maybe String -- TODO: embed sort type , sort : Maybe String @@ -54,7 +54,7 @@ searchQueryParser url = Url.Parser.Query.string "show" Url.Parser.Query.int "from" Url.Parser.Query.int "size" - Url.Parser.Query.string "selectedBuckets" + Url.Parser.Query.string "buckets" Url.Parser.Query.string "sort" @@ -65,7 +65,7 @@ searchArgsToUrl args = , 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 "selectedBuckets") args.selectedBuckets + , Maybe.map (Url.Builder.string "buckets") args.buckets , Maybe.map (Url.Builder.string "sort") args.sort ] , Maybe.map (Tuple.pair "query") args.query @@ -85,7 +85,7 @@ parser url = [ Url.Parser.map Home Url.Parser.top , 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 "selectedBuckets" searchQueryParser url + , Url.Parser.map Options <| Url.Parser.s "buckets" searchQueryParser url ] diff --git a/src/Search.elm b/src/Search.elm index 0cbfc1a1..d76913e6 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -80,7 +80,7 @@ type alias Model a b = , show : Maybe String , from : Int , size : Int - , selectedBuckets : Maybe String + , buckets : Maybe String , sort : Sort } @@ -167,7 +167,7 @@ init args maybeModel = , size = args.size |> Maybe.withDefault modelSize - , selectedBuckets = args.selectedBuckets + , buckets = args.buckets , sort = args.sort |> Maybe.withDefault "" @@ -254,14 +254,14 @@ update toRoute navKey msg model = |> ensureLoading |> pushUrl toRoute navKey - BucketsChange selectedBuckets -> + BucketsChange buckets -> { model - | selectedBuckets = - if selectedBuckets == "" then + | buckets = + if buckets == "" then Nothing else - Just selectedBuckets + Just buckets , from = 0 } |> ensureLoading @@ -281,12 +281,17 @@ update toRoute navKey msg model = ) QueryInputSubmit -> - { model | from = 0 } + { model + | from = 0 + , buckets = Nothing + } |> ensureLoading |> pushUrl toRoute navKey QueryResponse result -> - ( { model | result = result } + ( { model + | result = result + } , scrollToEntry model.show ) @@ -333,7 +338,7 @@ createUrl toRoute model = , show = model.show , from = Just model.from , size = Just model.size - , selectedBuckets = model.selectedBuckets + , buckets = model.buckets , sort = Just <| toSortId model.sort } @@ -642,14 +647,14 @@ view { toRoute, categoryName } title model viewSuccess viewBuckets outMsg = else let - selectedBuckets = - viewBuckets model.selectedBuckets result + buckets = + viewBuckets model.buckets result in - if List.length selectedBuckets > 0 then + if List.length buckets > 0 then [ div [ class "row" ] [ div [ class "span3 search-faceted" ] - [ ul [ class "nav nav-list" ] selectedBuckets ] + [ ul [ class "nav nav-list" ] buckets ] , div [ class "span9" ] (viewResults model result viewSuccess toRoute outMsg) ] @@ -913,7 +918,7 @@ makeRequestBody : -> String -> String -> List String - -> List (List ( String, Json.Encode.Value )) + -> List ( String, Json.Encode.Value ) -> List ( String, Float ) -> Http.Body makeRequestBody query from sizeRaw sort type_ sortField bucketsFields filterByBuckets fields = @@ -936,26 +941,14 @@ makeRequestBody query from sizeRaw sort type_ sortField bucketsFields filterByBu ) , toSortQuery sort sortField , toAggs bucketsFields + , ( "post_filter", Json.Encode.object filterByBuckets ) , ( "query" , Json.Encode.object [ ( "bool" , Json.Encode.object [ ( "filter" , Json.Encode.list Json.Encode.object - [ filterByType type_ - , [ ( "bool" - , Json.Encode.object - [ --( "must" - --, Json.Encode.object (filterByType type_) - --) - ( "should" - , Json.Encode.list Json.Encode.object - filterByBuckets - ) - ] - ) - ] - ] + [ filterByType type_ ] ) , ( "must" , Json.Encode.list Json.Encode.object diff --git a/src/index.less b/src/index.less index 74bfc5bd..e948cd00 100644 --- a/src/index.less +++ b/src/index.less @@ -68,11 +68,11 @@ header .navbar.navbar-static-top { } ul.nav-stacked > li > a { - display: flex; - justify-content: space-between; + display: grid; + grid-template-columns: 2em auto auto; & > span:last-child { - align-self: center; + text-align: right; } } } From fb86e085c5503ce4e6e21502de42f5eb6a4f1d07 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sat, 9 Jan 2021 10:26:46 +0100 Subject: [PATCH 05/25] no need to lowercase since we already do this in edge --- import-scripts/import_scripts/channel.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/import-scripts/import_scripts/channel.py b/import-scripts/import_scripts/channel.py index 1d77814b..336b7d3d 100644 --- a/import-scripts/import_scripts/channel.py +++ b/import-scripts/import_scripts/channel.py @@ -88,42 +88,34 @@ }, "package_attr_name": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "package_attr_name_reverse": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "package_attr_name_query": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "package_attr_name_query_reverse": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "package_attr_set": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "package_attr_set_reverse": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "package_pname": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "package_pname_reverse": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "package_pversion": {"type": "keyword"}, @@ -168,22 +160,18 @@ # Options fields "option_name": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "option_name_reverse": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "option_name_query": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "option_name_query_reverse": { "type": "keyword", - "normalizer": "lowercase", "fields": {"edge": {"type": "text", "analyzer": "edge"}}, }, "option_description": { From b8b3f98dd002ea2cc88f77c45cce3eb47388dd46 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Mon, 11 Jan 2021 13:36:46 +0100 Subject: [PATCH 06/25] change how the packages/options are displayed --- src/Page/Options.elm | 193 +++++------- src/Page/Packages.elm | 557 ++++++++++++++++++----------------- src/Route.elm | 2 +- src/Search.elm | 663 ++++++++++++++++++++++++------------------ src/index.html | 5 +- src/index.js | 2 +- src/index.less | 459 +++++++++++++++++++++++------ 7 files changed, 1111 insertions(+), 770 deletions(-) diff --git a/src/Page/Options.elm b/src/Page/Options.elm index 362c7312..9bc54127 100644 --- a/src/Page/Options.elm +++ b/src/Page/Options.elm @@ -14,27 +14,16 @@ import Html ( Html , a , code - , dd , div - , dl - , dt - , h4 , li , pre - , span - , table - , tbody - , td + , strong , text - , th - , thead - , tr , ul ) import Html.Attributes exposing ( class - , colspan , href , target ) @@ -45,8 +34,6 @@ import Html.Events import Html.Parser import Html.Parser.Util import Json.Decode -import Json.Encode -import Regex import Route import Search @@ -124,7 +111,9 @@ update navKey msg model = view : Model -> Html Msg view model = Search.view { toRoute = Route.Options, categoryName = "options" } - "Search NixOS options" + [ text "Search more than " + , strong [] [ text "10 000 options" ] + ] model viewSuccess viewBuckets @@ -141,118 +130,45 @@ viewBuckets _ _ = viewSuccess : String + -> Bool -> Maybe String -> List (Search.ResultItem ResultItemSource) -> Html Msg -viewSuccess channel show hits = - div [ class "search-result" ] - [ table [ class "table table-hover" ] - [ thead [] - [ tr [] - [ th [] [ text "Option name" ] - ] - ] - , tbody - [] - (List.concatMap - (viewResultItem channel show) - hits - ) - ] - ] +viewSuccess channel showNixOSDetails show hits = + ul [] + (List.concatMap + (viewResultItem channel showNixOSDetails show) + hits + ) viewResultItem : String + -> Bool -> Maybe String -> Search.ResultItem ResultItemSource -> List (Html Msg) -viewResultItem channel show item = - let - packageDetails = - if Just item.source.name == show then - [ td [ colspan 1 ] [ viewResultItemDetails channel item ] - ] - - else - [] - - open = - SearchMsg (Search.ShowDetails item.source.name) - in - [] - -- DEBUG: |> List.append - -- DEBUG: [ tr [] - -- DEBUG: [ td [ colspan 1 ] - -- DEBUG: [ div [] [ text <| "score: " ++ String.fromFloat (Maybe.withDefault 0 item.score) ] - -- DEBUG: , div [] - -- DEBUG: [ text <| - -- DEBUG: "matched queries: " - -- DEBUG: , ul [] - -- DEBUG: (item.matched_queries - -- DEBUG: |> Maybe.withDefault [] - -- DEBUG: |> List.sort - -- DEBUG: |> List.map (\q -> li [] [ text q ]) - -- DEBUG: ) - -- DEBUG: ] - -- DEBUG: ] - -- DEBUG: ] - -- DEBUG: ] - |> List.append - (tr - [ onClick open - , Search.elementId item.source.name - ] - [ td [] - [ Html.button - [ class "search-result-button" - , Html.Events.custom "click" <| - Json.Decode.succeed - { message = open - , stopPropagation = True - , preventDefault = True - } - ] - [ text item.source.name - ] - ] - ] - :: packageDetails - ) - - -viewResultItemDetails : - String - -> Search.ResultItem ResultItemSource - -> Html Msg -viewResultItemDetails channel item = +viewResultItem channel _ show item = let - default = - "Not given" - - asText value = - span [] <| + showHtml value = + [ div [] <| case Html.Parser.run value of Ok nodes -> Html.Parser.Util.toVirtualDom nodes - Err e -> + Err _ -> [] + ] + + default = + "Not given" asPre value = pre [] [ text value ] - asCode value = - code [] [ text value ] - asPreCode value = div [] [ pre [] [ code [] [ text value ] ] ] - encodeHtml value = - value - |> String.replace "<" "<" - |> String.replace ">" ">" - githubUrlPrefix branch = "https://github.com/NixOS/nixpkgs/blob/" ++ branch ++ "/" @@ -275,14 +191,6 @@ viewResultItemDetails channel item = Nothing -> text <| cleanPosition value - wrapped wrapWith value = - case value of - "" -> - wrapWith <| "\"" ++ value ++ "\"" - - _ -> - wrapWith value - withEmpty wrapWith maybe = case maybe of Nothing -> @@ -293,21 +201,54 @@ viewResultItemDetails channel item = Just value -> wrapWith value + + wrapped wrapWith value = + case value of + "" -> + wrapWith <| "\"" ++ value ++ "\"" + + _ -> + wrapWith value + + showDetails = + if Just item.source.name == show then + [ div [] + [ div [] [ text "Default value" ] + , div [] [ withEmpty (wrapped asPreCode) item.source.default ] + , div [] [ text "Type" ] + , div [] [ withEmpty asPre item.source.type_ ] + , div [] [ text "Example" ] + , div [] [ withEmpty (wrapped asPreCode) item.source.example ] + , div [] [ text "Declared in" ] + , div [] [ withEmpty asGithubLink item.source.source ] + ] + ] + + else + [] + + open = + SearchMsg (Search.ShowDetails item.source.name) in - dl [ class "dl-horizontal" ] - [ dt [] [ text "Name" ] - , dd [] [ withEmpty asText (Just (encodeHtml item.source.name)) ] - , dt [] [ text "Description" ] - , dd [] [ withEmpty asText item.source.description ] - , dt [] [ text "Default value" ] - , dd [] [ withEmpty (wrapped asPreCode) item.source.default ] - , dt [] [ text "Type" ] - , dd [] [ withEmpty asPre item.source.type_ ] - , dt [] [ text "Example value" ] - , dd [] [ withEmpty (wrapped asPreCode) item.source.example ] - , dt [] [ text "Declared in" ] - , dd [] [ withEmpty asGithubLink item.source.source ] + [ li + [ class "option" + , onClick open + , Search.elementId item.source.name ] + ([] + |> List.append showDetails + |> List.append + (item.source.description + |> Maybe.map showHtml + |> Maybe.withDefault [] + ) + |> List.append + [ Html.button + [ class "search-result-button" ] + [ text item.source.name ] + ] + ) + ] @@ -323,7 +264,7 @@ makeRequest : -> Maybe String -> Search.Sort -> Cmd Msg -makeRequest options channel query from size buckets sort = +makeRequest options channel query from size _ sort = Search.makeRequest (Search.makeRequestBody (String.trim query) diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index 26780ad7..42b9a25e 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -13,33 +13,24 @@ import Html exposing ( Html , a - , code - , dd , div - , dl - , dt + , em , h4 - , input , li + , p , pre , span - , table - , tbody - , td + , strong , text - , th - , thead - , tr , ul ) import Html.Attributes exposing - ( checked - , class - , colspan + ( class + , classList , href + , id , target - , type_ ) import Html.Events exposing @@ -110,7 +101,7 @@ type alias ResultPackageHydraPath = type alias ResultAggregations = - { all : AggregationsAll + { all : Aggregations , package_platforms : Search.Aggregation , package_attr_set : Search.Aggregation , package_maintainers_set : Search.Aggregation @@ -118,7 +109,7 @@ type alias ResultAggregations = } -type alias AggregationsAll = +type alias Aggregations = { doc_count : Int , package_platforms : Search.Aggregation , package_attr_set : Search.Aggregation @@ -199,7 +190,9 @@ update navKey msg model = view : Model -> Html Msg view model = Search.view { toRoute = Route.Packages, categoryName = "packages" } - "Search NixOS packages" + [ text "Search more than " + , strong [] [ text "80 000 packages" ] + ] model viewSuccess viewBuckets @@ -215,13 +208,6 @@ viewBuckets bucketsAsString result = initialBuckets = initBuckets bucketsAsString - allBuckets = - { packageSets = List.map .key result.aggregations.package_attr_set.buckets - , licenses = List.map .key result.aggregations.package_license_set.buckets - , maintainers = List.map .key result.aggregations.package_platforms.buckets - , platforms = List.map .key result.aggregations.package_maintainers_set.buckets - } - selectedBucket = initialBuckets @@ -272,28 +258,28 @@ viewBucket title buckets searchMsgFor selectedBucket sets = else [ li [] - [ h4 [] [ text title ] - , ul [ class "nav nav-tabs nav-stacked" ] - (List.map - (\bucket -> - li [] - [ a - [ href "#" - , onClick <| searchMsgFor bucket.key - ] - [ span [] - [ input - [ type_ "checkbox" - , checked <| List.member bucket.key selectedBucket + [ ul [] + (List.append + [ li [ class "header" ] [ text title ] ] + (List.map + (\bucket -> + li [] + [ a + [ href "#" + , onClick <| searchMsgFor bucket.key + , classList + [ ( "selected" + , List.member bucket.key selectedBucket + ) ] - [] ] - , span [] [ text bucket.key ] - , span [] [ span [ class "badge badge-info" ] [ text <| String.fromInt bucket.doc_count ] ] + [ span [] [ text bucket.key ] + , span [] [ span [ class "badge" ] [ text <| String.fromInt bucket.doc_count ] ] + ] ] - ] + ) + buckets ) - buckets ) ] ] @@ -302,251 +288,251 @@ viewBucket title buckets searchMsgFor selectedBucket sets = viewSuccess : String + -> Bool -> Maybe String -> List (Search.ResultItem ResultItemSource) -> Html Msg -viewSuccess channel show hits = - div [ class "search-result" ] - [ table [ class "table table-hover" ] - [ thead [] - [ tr [] - [ th [] [ text "Attribute name" ] - , th [] [ text "Name" ] - , th [] [ text "Version" ] - , th [] [ text "Description" ] - ] - ] - , tbody - [] - (List.concatMap - (viewResultItem channel show) - hits - ) - ] - ] +viewSuccess channel showNixOSDetails show hits = + ul [] + (List.concatMap + (viewResultItem channel showNixOSDetails show) + hits + ) viewResultItem : String + -> Bool -> Maybe String -> Search.ResultItem ResultItemSource -> List (Html Msg) -viewResultItem channel show item = +viewResultItem channel showNixOSDetails show item = let - packageDetails = - if Just item.source.attr_name == show then - [ td [ colspan 4 ] [ viewResultItemDetails channel item ] - ] - - else - [] - - open = - SearchMsg (Search.ShowDetails item.source.attr_name) - in - [] - -- DEBUG: |> List.append - -- DEBUG: [ tr [] - -- DEBUG: [ td [ colspan 4 ] - -- DEBUG: [ div [] - -- DEBUG: [ text <| - -- DEBUG: "score: " - -- DEBUG: ++ (item.score - -- DEBUG: |> Maybe.map String.fromFloat - -- DEBUG: |> Maybe.withDefault "No score" - -- DEBUG: ) - -- DEBUG: ] - -- DEBUG: , div [] - -- DEBUG: [ text <| - -- DEBUG: "matched queries: " - -- DEBUG: , ul [] - -- DEBUG: (item.matched_queries - -- DEBUG: |> Maybe.withDefault [] - -- DEBUG: |> List.sort - -- DEBUG: |> List.map (\q -> li [] [ text q ]) - -- DEBUG: ) - -- DEBUG: ] - -- DEBUG: ] - -- DEBUG: ] - -- DEBUG: ] - |> List.append - (tr - [ onClick open - , Search.elementId item.source.attr_name - ] - [ td [] - [ Html.button - [ class "search-result-button" - , Html.Events.custom "click" <| - Json.Decode.succeed - { message = open - , stopPropagation = True - , preventDefault = True - } - ] - [ text item.source.attr_name ] - ] - , td [] [ text item.source.pname ] - , td [] [ text item.source.pversion ] - , td [] [ text <| Maybe.withDefault "" item.source.description ] - ] - :: packageDetails - ) - - -viewResultItemDetails : - String - -> Search.ResultItem ResultItemSource - -> Html Msg -viewResultItemDetails channel item = - let - default = - "Not specified" - - asText = - text - - asLink value = - a [ href value ] [ text value ] - - githubUrlPrefix branch = - "https://github.com/NixOS/nixpkgs/blob/" ++ branch ++ "/" - cleanPosition = Regex.fromString "^[0-9a-f]+\\.tar\\.gz\\/" |> Maybe.withDefault Regex.never >> (\reg -> Regex.replace reg (\_ -> "")) - asGithubLink value = - case Search.channelDetailsFromId channel of - Just channelDetails -> - a - [ href <| githubUrlPrefix channelDetails.branch ++ (value |> String.replace ":" "#L" |> cleanPosition) - , target "_blank" - ] - [ text <| cleanPosition value ] + createGithubUrl branch value = + let + uri = + value + |> String.replace ":" "#L" + |> cleanPosition + in + "https://github.com/NixOS/nixpkgs/blob/" ++ branch ++ "/" ++ uri - Nothing -> - text <| cleanPosition value - - mainPlatforms platform = - List.member platform - [ "x86_64-linux" - , "aarch64-linux" - , "x86_64-darwin" - , "i686-linux" + createShortDetailsItem title url = + [ li [] + [ a + [ href url + , target "_blank" + ] + [ text title ] ] + ] - getHydraDetailsForPlatform hydra platform = - hydra - |> Maybe.andThen - (\hydras -> - hydras - |> List.filter (\x -> x.platform == platform) + shortPackageDetails = + ul [] + ([] + |> List.append + (item.source.position + |> Maybe.map + (\position -> + case Search.channelDetailsFromId channel of + Nothing -> + [] + + Just channelDetails -> + createShortDetailsItem + "source" + (createGithubUrl channelDetails.branch position) + ) + |> Maybe.withDefault [] + ) + |> List.append + (item.source.homepage |> List.head - ) + |> Maybe.map (createShortDetailsItem "homepage") + |> Maybe.withDefault [] + ) + |> List.append + (item.source.licenses + |> List.head + |> Maybe.map + (\license -> + case ( license.fullName, license.url ) of + ( Nothing, Nothing ) -> + [] - showPlatforms hydra platforms = - platforms - |> List.filter mainPlatforms - |> List.map (showPlatform hydra) + ( Just fullName, Nothing ) -> + [ text fullName ] - showPlatform hydra platform = - case - ( getHydraDetailsForPlatform hydra platform - , Search.channelDetailsFromId channel - ) - of - ( Just hydraDetails, _ ) -> - a - [ href <| "https://hydra.nixos.org/build/" ++ String.fromInt hydraDetails.build_id - ] - [ text platform - ] + ( Nothing, Just url ) -> + createShortDetailsItem "license" url - ( Nothing, Just channelDetails ) -> - a - [ href <| "https://hydra.nixos.org/job/" ++ channelDetails.jobset ++ "/nixpkgs." ++ item.source.attr_name ++ "." ++ platform - ] - [ text platform + ( Just fullName, Just url ) -> + createShortDetailsItem fullName url + ) + |> Maybe.withDefault [] + ) + |> List.append + [ li [] [ text item.source.pname ] + , li [] [ text item.source.pversion ] ] - - ( _, _ ) -> - text platform - - showLicence license = - case ( license.fullName, license.url ) of - ( Nothing, Nothing ) -> - text default - - ( Just fullName, Nothing ) -> - text fullName - - ( Nothing, Just url ) -> - a [ href url ] [ text default ] - - ( Just fullName, Just url ) -> - a [ href url ] [ text fullName ] + ) showMaintainer maintainer = - a - [ href <| - case maintainer.github of - Just github -> - "https://github.com/" ++ github - - Nothing -> - "#" + li [] + [ a + [ href <| + case maintainer.github of + Just github -> + "https://github.com/" ++ github + + Nothing -> + "#" + ] + [ text <| Maybe.withDefault "" maintainer.name ++ " <" ++ Maybe.withDefault "" maintainer.email ++ ">" ] ] - [ text <| Maybe.withDefault "" maintainer.name ++ " <" ++ Maybe.withDefault "" maintainer.email ++ ">" ] - asPre value = - pre [] [ text value ] - - asCode value = - code [] [ text value ] + showPlatform platform = + case Search.channelDetailsFromId channel of + Just channelDetails -> + let + url = + "https://hydra.nixos.org/job/" ++ channelDetails.jobset ++ "/nixpkgs." ++ item.source.attr_name ++ "." ++ platform + in + li [] + [ a + [ href url + ] + [ text platform ] + ] - asList list = - case list of - [] -> - asPre default + Nothing -> + li [] [ text platform ] + + maintainersAndPlatforms = + --[ dt [] [ text "Platforms" ] + --, dd [] [ asList (showPlatforms item.source.hydra item.source.platforms) ] + [ div [] + [ div [] + (List.append [ h4 [] [ text "Maintainers" ] ] + (if List.isEmpty item.source.maintainers then + [ p [] [ text "This package has no maintainers." ] ] + + else + [ ul [] (List.map showMaintainer item.source.maintainers) ] + ) + ) + , div [] + (List.append [ h4 [] [ text "Platforms" ] ] + (if List.isEmpty item.source.maintainers then + [ p [] [ text "This package is not available on any platform." ] ] - _ -> - ul [ class "inline" ] <| List.map (\i -> li [] [ i ]) list + else + [ ul [] (List.map showPlatform item.source.platforms) ] + ) + ) + ] + ] - withEmpty wrapWith maybe = - case maybe of - Nothing -> - asPre default + longerPackageDetails = + if Just item.source.attr_name == show then + [ div [] + ([] + |> List.append maintainersAndPlatforms + |> List.append + (item.source.longDescription + |> Maybe.map (\desc -> [ p [] [ text desc ] ]) + |> Maybe.withDefault [] + ) + |> List.append + [ div [] + [ h4 [] + [ text "How to install " + , em [] [ text item.source.attr_name ] + , text "?" + ] + , ul [ class "nav nav-tabs" ] + [ li + [ classList + [ ( "active", showNixOSDetails ) + , ( "pull-right", True ) + ] + ] + [ a + [ href "#" + , onClick <| SearchMsg (Search.ShowNixOSDetails True) + ] + [ text "On NixOS" ] + ] + , li + [ classList + [ ( "active", not showNixOSDetails ) + , ( "pull-right", True ) + ] + ] + [ a + [ href "#" + , onClick <| SearchMsg (Search.ShowNixOSDetails False) + ] + [ text "On non-NixOS" ] + ] + ] + , div [ class "tab-content" ] + [ div + [ classList + [ ( "tab-pane", True ) + , ( "active", not showNixOSDetails ) + ] + , id "package-details-nixpkgs" + ] + [ pre [] + [ text "nix-env -iA nixpkgs." + , strong [] [ text item.source.attr_name ] + ] + ] + , div + [ classList + [ ( "tab-pane", True ) + , ( "active", showNixOSDetails ) + ] + ] + [ pre [] + [ text <| "nix-env -iA nixos." + , strong [] [ text item.source.attr_name ] + ] + ] + ] + ] + ] + ) + ] - Just "" -> - asPre default + else + [] - Just value -> - wrapWith value + open = + SearchMsg (Search.ShowDetails item.source.attr_name) in - dl [ class "dl-horizontal" ] - [ dt [] [ text "Attribute Name" ] - , dd [] [ withEmpty asText (Just item.source.attr_name) ] - , dt [] [ text "Name" ] - , dd [] [ withEmpty asText (Just item.source.pname) ] - , dt [] [ text "Install command" ] - , dd [] [ withEmpty asCode (Just ("nix-env -iA nixos." ++ item.source.attr_name)) ] - , dt [] [ text "Nix expression" ] - , dd [] [ withEmpty asGithubLink item.source.position ] - , dt [] [ text "Platforms" ] - , dd [] [ asList (showPlatforms item.source.hydra item.source.platforms) ] - , dt [] [ text "Homepage" ] - , dd [] <| List.intersperse (Html.text ", ") <| List.map asLink item.source.homepage - , dt [] [ text "Licenses" ] - , dd [] [ asList (List.map showLicence item.source.licenses) ] - , dt [] [ text "Maintainers" ] - , dd [] [ asList (List.map showMaintainer item.source.maintainers) ] - , dt [] [ text "Description" ] - , dd [] [ withEmpty asText item.source.description ] - , dt [] [ text "Long description" ] - , dd [] [ withEmpty asText item.source.longDescription ] + [ li + [ class "package" + , onClick open + , Search.elementId item.source.attr_name ] + ([] + |> List.append longerPackageDetails + |> List.append + [ Html.button + [ class "search-result-button" ] + [ text item.source.attr_name ] + , div [] [ text <| Maybe.withDefault "" item.source.description ] + , shortPackageDetails + ] + ) + ] @@ -564,6 +550,16 @@ makeRequest : -> Cmd Msg makeRequest options channel query from size maybeBuckets sort = let + currentBuckets = + initBuckets maybeBuckets + + aggregations = + [ ( "package_attr_set", currentBuckets.packageSets ) + , ( "package_license_set", currentBuckets.licenses ) + , ( "package_maintainers_set", currentBuckets.maintainers ) + , ( "package_platforms", currentBuckets.platforms ) + ] + filterByBucket field value = [ ( "term" , Json.Encode.object @@ -578,31 +574,30 @@ makeRequest options channel query from size maybeBuckets sort = ] filterByBuckets = - let - buckets = - initBuckets maybeBuckets - in - [ ( "package_attr_set", buckets.packageSets ) - , ( "package_license_set", buckets.licenses ) - , ( "package_maintainers_set", buckets.maintainers ) - , ( "package_platforms", buckets.platforms ) + [ ( "bool" + , Json.Encode.object + [ ( "must" + , Json.Encode.list Json.Encode.object + (List.map + (\( aggregation, buckets ) -> + [ ( "bool" + , Json.Encode.object + [ ( "should" + , Json.Encode.list Json.Encode.object <| + List.map + (filterByBucket aggregation) + buckets + ) + ] + ) + ] + ) + aggregations + ) + ) + ] + ) ] - |> List.map - (\( field, items ) -> - List.map (filterByBucket field) items - ) - |> List.concat - |> (\x -> - [ ( "bool" - , Json.Encode.object - [ ( "should" - , Json.Encode.list Json.Encode.object - x - ) - ] - ) - ] - ) in Search.makeRequest (Search.makeRequestBody @@ -726,16 +721,16 @@ decodeResultPackageHydraPath = decodeResultAggregations : Json.Decode.Decoder ResultAggregations decodeResultAggregations = Json.Decode.map5 ResultAggregations - (Json.Decode.field "all" decodeAggregationsAll) + (Json.Decode.field "all" decodeAggregations) (Json.Decode.field "package_platforms" Search.decodeAggregation) (Json.Decode.field "package_attr_set" Search.decodeAggregation) (Json.Decode.field "package_maintainers_set" Search.decodeAggregation) (Json.Decode.field "package_license_set" Search.decodeAggregation) -decodeAggregationsAll : Json.Decode.Decoder AggregationsAll -decodeAggregationsAll = - Json.Decode.map5 AggregationsAll +decodeAggregations : Json.Decode.Decoder Aggregations +decodeAggregations = + Json.Decode.map5 Aggregations (Json.Decode.field "doc_count" Json.Decode.int) (Json.Decode.field "package_platforms" Search.decodeAggregation) (Json.Decode.field "package_attr_set" Search.decodeAggregation) diff --git a/src/Route.elm b/src/Route.elm index bfcce39a..06f6ff89 100644 --- a/src/Route.elm +++ b/src/Route.elm @@ -85,7 +85,7 @@ parser url = [ Url.Parser.map Home Url.Parser.top , 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 "buckets" searchQueryParser url + , Url.Parser.map Options <| Url.Parser.s "options" searchQueryParser url ] diff --git a/src/Search.elm b/src/Search.elm index d76913e6..1bb8e97a 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -30,16 +30,14 @@ import Html , a , button , div - , em , form , h1 + , h2 , h4 , input - , label , li - , option , p - , select + , span , strong , text , ul @@ -53,7 +51,6 @@ import Html.Attributes , href , id , placeholder - , selected , type_ , value ) @@ -67,7 +64,7 @@ import Http import Json.Decode import Json.Encode import RemoteData -import Route exposing (Route) +import Route import Route.SearchQuery import Set import Task @@ -82,6 +79,7 @@ type alias Model a b = , size : Int , buckets : Maybe String , sort : Sort + , showNixOSDetails : Bool } @@ -151,7 +149,7 @@ init args maybeModel = getField .from 0 modelSize = - getField .size 30 + getField .size 50 in ( { channel = args.channel @@ -173,6 +171,7 @@ init args maybeModel = |> Maybe.withDefault "" |> fromSortId |> Maybe.withDefault Relevance + , showNixOSDetails = False } |> ensureLoading , Browser.Dom.focus "search-query-input" |> Task.attempt (\_ -> NoOp) @@ -218,6 +217,7 @@ type Msg a b | QueryResponse (RemoteData.WebData (SearchResult a b)) | ShowDetails String | ChangePage Int + | ShowNixOSDetails Bool scrollToEntry : @@ -311,6 +311,10 @@ update toRoute navKey msg model = |> ensureLoading |> pushUrl toRoute navKey + ShowNixOSDetails show -> + { model | showNixOSDetails = show } + |> pushUrl toRoute navKey + pushUrl : Route.SearchRoute @@ -420,10 +424,10 @@ sortBy = ] -toAggs : +toAggregations : List String -> ( String, Json.Encode.Value ) -toAggs bucketsFields = +toAggregations bucketsFields = let fields = List.map @@ -448,14 +452,14 @@ toAggs bucketsFields = [ ( "global" , Json.Encode.object [] ) - , ( "aggs" + , ( "aggregations" , Json.Encode.object fields ) ] ) ] in - ( "aggs" + ( "aggregations" , Json.Encode.object <| List.append fields allFields ) @@ -497,7 +501,7 @@ toSortTitle sort = "Alphabetically Descending" Relevance -> - "Relevance" + "Best match" toSortId : Sort -> String @@ -533,10 +537,11 @@ view : { toRoute : Route.SearchRoute , categoryName : String } - -> String + -> List (Html c) -> Model a b -> (String + -> Bool -> Maybe String -> List (ResultItem a) -> Html c @@ -549,149 +554,192 @@ view : -> (Msg a b -> c) -> Html c view { toRoute, categoryName } title model viewSuccess viewBuckets outMsg = - div - [ class "search-page" - ] - [ h1 [ class "page-header" ] [ text title ] - , div - [ class "search-input" - ] - [ form [ onSubmit (outMsg QueryInputSubmit) ] - [ p - [] - ([] - |> List.append - (if List.member model.channel channels then - [] - - else - [ p [ class "alert alert-error" ] - [ h4 [] [ text "Wrong channel selected!" ] - , text <| "Please select one of the channels above!" - ] - ] - ) - |> List.append - [ p [] - [ strong [] - [ text "Channel: " ] - , div - [ class "btn-group" - , attribute "data-toggle" "buttons-radio" - ] - (List.filterMap - (\channel_id -> - channelDetailsFromId channel_id - |> Maybe.map - (\channel -> - button - [ type_ "button" - , classList - [ ( "btn", True ) - , ( "active", channel.id == model.channel ) - ] - , onClick <| outMsg (ChannelChange channel.id) - ] - [ text channel.title ] - ) - ) - channels - ) - ] - ] - ) - , p - [ class "input-append" - ] - [ input - [ type_ "text" - , id "search-query-input" - , autofocus True - , placeholder <| "Search for " ++ categoryName - , onInput (outMsg << QueryInput) - , value <| Maybe.withDefault "" model.query - ] - [] - , div [ class "loader" ] [] - , div [ class "btn-group" ] - [ button [ class "btn", type_ "submit" ] - [ text "Search" ] - ] - ] - ] - ] - , div [] <| + let + resultStatus = case model.result of RemoteData.NotAsked -> - [ text "" ] + "not-asked" RemoteData.Loading -> - [ p [] [ em [] [ text "Searching..." ] ] - , div [] - [ viewSortSelection outMsg model - , viewPager outMsg model 0 toRoute + "loading" + + RemoteData.Success _ -> + "success" + + RemoteData.Failure _ -> + "failure" + in + div [ class <| "search-page " ++ resultStatus ] + [ h1 [] title + , viewSearchInput outMsg categoryName model.channel model.query + , viewResult outMsg toRoute categoryName model viewSuccess viewBuckets + ] + + +viewResult : + (Msg a b -> c) + -> Route.SearchRoute + -> String + -> Model a b + -> + (String + -> Bool + -> Maybe String + -> List (ResultItem a) + -> Html c + ) + -> + (Maybe String + -> SearchResult a b + -> List (Html c) + ) + -> Html c +viewResult outMsg toRoute categoryName model viewSuccess viewBuckets = + case model.result of + RemoteData.NotAsked -> + div [] [ text "" ] + + RemoteData.Loading -> + div [ class "loader-wrapper" ] + [ div [ class "loader" ] [ text "Loading..." ] + , h2 [] [ text "Searching..." ] + ] + + RemoteData.Success result -> + if result.hits.total.value == 0 then + viewNoResults categoryName + + else + let + buckets = + viewBuckets model.buckets result + in + if List.length buckets > 0 then + div [ class "search-results" ] + [ ul [] buckets + , div [] + (viewResults model result viewSuccess toRoute outMsg categoryName) ] - , div [ class "loader-wrapper" ] [ div [ class "loader" ] [ text "Loading..." ] ] - , viewPager outMsg model 0 toRoute - ] - RemoteData.Success result -> - if result.hits.total.value == 0 then - [ h4 [] [ text <| "No " ++ categoryName ++ " found!" ] - , text "How to " - , Html.a [ href "https://nixos.org/manual/nixpkgs/stable/#chap-quick-start" ] [ text "add" ] - , text " or " - , a [ href "https://github.com/NixOS/nixpkgs/issues/new?assignees=&labels=0.kind%3A+packaging+request&template=packaging_request.md&title=" ] [ text "request" ] - , text " package to nixpkgs?" + else + div [ class "search-results" ] + [ div [] + (viewResults model result viewSuccess toRoute outMsg categoryName) ] - else - let - buckets = - viewBuckets model.buckets result - in - if List.length buckets > 0 then - [ div [ class "row" ] - [ div - [ class "span3 search-faceted" ] - [ ul [ class "nav nav-list" ] buckets ] - , div [ class "span9" ] - (viewResults model result viewSuccess toRoute outMsg) - ] - ] + RemoteData.Failure error -> + let + ( errorTitle, errorMessage ) = + case error of + Http.BadUrl text -> + ( "Bad Url!", text ) + + Http.Timeout -> + ( "Timeout!", "Request to the server timeout." ) + + Http.NetworkError -> + ( "Network Error!", "A network request bonsaisearch.net domain failed. This is either due to a content blocker or a networking issue." ) + + Http.BadStatus code -> + ( "Bad Status", "Server returned " ++ String.fromInt code ) + + Http.BadBody text -> + ( "Bad Body", text ) + in + div [] + [ div [ class "alert alert-error" ] + [ h4 [] [ text errorTitle ] + , text errorMessage + ] + ] - else - [ div [ class "row" ] - [ div [ class "span12" ] - (viewResults model result viewSuccess toRoute outMsg) - ] - ] - RemoteData.Failure error -> - let - ( errorTitle, errorMessage ) = - case error of - Http.BadUrl text -> - ( "Bad Url!", text ) +viewNoResults : + String + -> Html c +viewNoResults categoryName = + div [ class "search-no-results" ] + [ h2 [] [ text <| "No " ++ categoryName ++ " found!" ] + , text "How to " + , Html.a [ href "https://nixos.org/manual/nixpkgs/stable/#chap-quick-start" ] [ text "add" ] + , text " or " + , a [ href "https://github.com/NixOS/nixpkgs/issues/new?assignees=&labels=0.kind%3A+packaging+request&template=packaging_request.md&title=" ] [ text "request" ] + , text " package to nixpkgs?" + ] - Http.Timeout -> - ( "Timeout!", "Request to the server timeout." ) - Http.NetworkError -> - ( "Network Error!", "A network request bonsaisearch.net domain failed. This is either due to a content blocker or a networking issue." ) +viewSearchInput : + (Msg a b -> c) + -> String + -> String + -> Maybe String + -> Html c +viewSearchInput outMsg categoryName selectedChannel searchQuery = + form + [ onSubmit (outMsg QueryInputSubmit) + , class "search-input" + ] + [ div [] + [ div [] + [ input + [ type_ "text" + , id "search-query-input" + , autofocus True + , placeholder <| "Search for " ++ categoryName + , onInput (outMsg << QueryInput) + , value <| Maybe.withDefault "" searchQuery + ] + [] + ] + , button [ class "btn", type_ "submit" ] + [ text "Search" ] + ] + , div [] (viewChannels outMsg selectedChannel) + ] - Http.BadStatus code -> - ( "Bad Status", "Server returned " ++ String.fromInt code ) - Http.BadBody text -> - ( "Bad Body", text ) - in - [ div [ class "alert alert-error" ] - [ h4 [] [ text errorTitle ] - , text errorMessage - ] - ] +viewChannels : + (Msg a b -> c) + -> String + -> List (Html c) +viewChannels outMsg selectedChannel = + List.append + [ div [] + [ h4 [] [ text "Channel: " ] + , div + [ class "btn-group" + , attribute "data-toggle" "buttons-radio" + ] + (List.filterMap + (\channelId -> + channelDetailsFromId channelId + |> Maybe.map + (\channel -> + button + [ type_ "button" + , classList + [ ( "btn", True ) + , ( "active", channel.id == selectedChannel ) + ] + , onClick <| outMsg (ChannelChange channel.id) + ] + [ text channel.title ] + ) + ) + channels + ) + ] ] + (if List.member selectedChannel channels then + [] + + else + [ p [ class "alert alert-error" ] + [ h4 [] [ text "Wrong channel selected!" ] + , text <| "Please select one of the channels above!" + ] + ] + ) viewResults : @@ -699,132 +747,190 @@ viewResults : -> SearchResult a b -> (String + -> Bool -> Maybe String -> List (ResultItem a) -> Html c ) -> Route.SearchRoute -> (Msg a b -> c) + -> String -> List (Html c) -viewResults model result viewSuccess toRoute outMsg = - [ p [] - [ em [] - [ text - ("Showing results " - ++ String.fromInt model.from - ++ "-" - ++ String.fromInt - (if model.from + model.size > result.hits.total.value then - result.hits.total.value - - else - model.from + model.size - ) - ++ " of " - ++ (if result.hits.total.value == 10000 then - "more than 10000 results, please provide more precise search terms." - - else - String.fromInt result.hits.total.value - ++ "." - ) +viewResults model result viewSuccess toRoute outMsg categoryName = + let + from = + String.fromInt model.from + + to = + String.fromInt + (if model.from + model.size > result.hits.total.value then + result.hits.total.value + + else + model.from + model.size ) - ] - ] - , div [] - [ viewSortSelection outMsg model - , viewPager outMsg model result.hits.total.value toRoute + + total = + String.fromInt result.hits.total.value + in + [ div [] + [ viewSortSelection toRoute model + , div [] + (List.append + [ text "Showing results " + , text from + , text "-" + , text to + , text " of " + ] + (if result.hits.total.value == 10000 then + [ text "more than 10000." + , p [] [ text "Please provide more precise search terms." ] + ] + + else + [ strong [] + [ text total + , text " " + , text categoryName + ] + , text "." + ] + ) + ) ] - , viewSuccess model.channel model.show result.hits.hits - , viewPager outMsg model result.hits.total.value toRoute + , viewSuccess model.channel model.showNixOSDetails model.show result.hits.hits + , viewPager outMsg model result.hits.total.value ] -viewSortSelection : (Msg a b -> c) -> Model a b -> Html c -viewSortSelection outMsg model = - form [ class "form-horizontal pull-right sort-form" ] - [ div [ class "control-group sort-group" ] - [ label [ class "control-label" ] [ text "Sort by:" ] - , div - [ class "controls" ] - [ select - [ onInput (\x -> outMsg (SortChange x)) - ] - (List.map - (\sort -> - option - [ selected (model.sort == sort) - , value (toSortId sort) +viewSortSelection : + Route.SearchRoute + -> Model a b + -> Html c +viewSortSelection toRoute model = + div [ class "btn-group dropdown pull-right" ] + [ button + [ class "btn" + , attribute "data-toggle" "dropdown" + ] + [ span [] [ text <| "Sort: " ] + , span [ class "selected" ] [ text <| toSortTitle model.sort ] + , span [ class "caret" ] [] + ] + , ul [ class "dropdown-menu pull-right" ] + (List.append + [ li [ class " header" ] [ text "Sort options" ] + , li [ class "divider" ] [] + ] + (List.map + (\sort -> + let + url = + createUrl toRoute + { model | sort = sort } + in + li + [ classList + [ ( "selected", model.sort == sort ) ] + ] + [ a + [ href url ] [ text <| toSortTitle sort ] - ) - sortBy + ] ) - ] - ] + sortBy + ) + ) ] + +--form [ class "form-horizontal pull-right sort-form" ] +-- [ div [ class "control-group sort-group" ] +-- [ label [ class "control-label" ] [ text "Sort by:" ] +-- , div +-- [ class "controls" ] +-- [ select +-- [ onInput (\x -> outMsg (SortChange x)) +-- ] +-- (List.map +-- (\sort -> +-- option +-- [ selected (model.sort == sort) +-- , value (toSortId sort) +-- ] +-- [ text <| toSortTitle sort ] +-- ) +-- sortBy +-- ) +-- ] +-- ] +-- ] + + viewPager : (Msg a b -> c) -> Model a b -> Int - -> Route.SearchRoute -> Html c -viewPager outMsg model total toRoute = +viewPager outMsg model total = Html.map outMsg <| - ul [ class "pager" ] - [ li [ classList [ ( "disabled", model.from == 0 ) ] ] - [ a - [ Html.Events.onClick <| - if model.from == 0 then - NoOp - - else - ChangePage 0 + div [] + [ ul [ class "pager" ] + [ li [ classList [ ( "disabled", model.from == 0 ) ] ] + [ a + [ Html.Events.onClick <| + if model.from == 0 then + NoOp + + else + ChangePage 0 + ] + [ text "First" ] ] - [ text "First" ] - ] - , li [ classList [ ( "disabled", model.from == 0 ) ] ] - [ a - [ Html.Events.onClick <| - if model.from - model.size < 0 then - NoOp - - else - ChangePage <| model.from - model.size + , li [ classList [ ( "disabled", model.from == 0 ) ] ] + [ a + [ Html.Events.onClick <| + if model.from - model.size < 0 then + NoOp + + else + ChangePage <| model.from - model.size + ] + [ text "Previous" ] ] - [ text "Previous" ] - ] - , li [ classList [ ( "disabled", model.from + model.size >= total ) ] ] - [ a - [ Html.Events.onClick <| - if model.from + model.size >= total then - NoOp - - else - ChangePage <| model.from + model.size + , li [ classList [ ( "disabled", model.from + model.size >= total ) ] ] + [ a + [ Html.Events.onClick <| + if model.from + model.size >= total then + NoOp + + else + ChangePage <| model.from + model.size + ] + [ text "Next" ] ] - [ text "Next" ] - ] - , li [ classList [ ( "disabled", model.from + model.size >= total ) ] ] - [ a - [ Html.Events.onClick <| - if model.from + model.size >= total then - NoOp - - else - let - remainder = - if remainderBy model.size total == 0 then - 1 - - else - 0 - in - ChangePage <| ((total // model.size) - remainder) * model.size + , li [ classList [ ( "disabled", model.from + model.size >= total ) ] ] + [ a + [ Html.Events.onClick <| + if model.from + model.size >= total then + NoOp + + else + let + remainder = + if remainderBy model.size total == 0 then + 1 + + else + 0 + in + ChangePage <| ((total // model.size) - remainder) * model.size + ] + [ text "Last" ] ] - [ text "Last" ] ] ] @@ -933,42 +1039,49 @@ makeRequestBody query from sizeRaw sort type_ sortField bucketsFields filterByBu in Http.jsonBody (Json.Encode.object - [ ( "from" - , Json.Encode.int from - ) - , ( "size" - , Json.Encode.int size - ) - , toSortQuery sort sortField - , toAggs bucketsFields - , ( "post_filter", Json.Encode.object filterByBuckets ) - , ( "query" - , Json.Encode.object - [ ( "bool" - , Json.Encode.object - [ ( "filter" - , Json.Encode.list Json.Encode.object - [ filterByType type_ ] - ) - , ( "must" - , Json.Encode.list Json.Encode.object - [ [ ( "dis_max" - , Json.Encode.object - [ ( "tie_breaker", Json.Encode.float 0.7 ) - , ( "queries" - , Json.Encode.list Json.Encode.object - (searchFields query fields) - ) - ] - ) - ] - ] - ) - ] - ) - ] - ) - ] + (List.append + [ ( "from" + , Json.Encode.int from + ) + , ( "size" + , Json.Encode.int size + ) + , toSortQuery sort sortField + , toAggregations bucketsFields + , ( "query" + , Json.Encode.object + [ ( "bool" + , Json.Encode.object + [ ( "filter" + , Json.Encode.list Json.Encode.object + [ filterByType type_ ] + ) + , ( "must" + , Json.Encode.list Json.Encode.object + [ [ ( "dis_max" + , Json.Encode.object + [ ( "tie_breaker", Json.Encode.float 0.7 ) + , ( "queries" + , Json.Encode.list Json.Encode.object + (searchFields query fields) + ) + ] + ) + ] + ] + ) + ] + ) + ] + ) + ] + (if List.isEmpty filterByBuckets then + [] + + else + [ ( "post_filter", Json.Encode.object filterByBuckets ) ] + ) + ) ) diff --git a/src/index.html b/src/index.html index 6284f5da..cd598924 100644 --- a/src/index.html +++ b/src/index.html @@ -16,11 +16,12 @@ - + + + diff --git a/src/index.js b/src/index.js index cd83fede..f32e29a6 100644 --- a/src/index.js +++ b/src/index.js @@ -11,4 +11,4 @@ Elm.Main.init({ elasticsearchUsername : process.env.ELASTICSEARCH_USERNAME || 'z3ZFJ6y2mR', elasticsearchPassword : process.env.ELASTICSEARCH_PASSWORD || 'ds8CEvALPf9pui7XG' } -}); \ No newline at end of file +}); diff --git a/src/index.less b/src/index.less index e948cd00..9bd5f48c 100644 --- a/src/index.less +++ b/src/index.less @@ -1,7 +1,30 @@ +/* ------------------------------------------------------------------------- */ +/* -- Utils ---------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + +.terminal() { + background: #333; + color: #fff; + margin: 0; + + &:before { + content: "$ " + } +} + +/* ------------------------------------------------------------------------- */ +/* -- Layout --------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + body { position: relative; min-height: 100vh; - overflow-y: scroll; + overflow-y: auto; + + & > div:first-child { + position: relative; + min-height: 100vh; + } } #content { @@ -30,109 +53,377 @@ header .navbar.navbar-static-top { } } -.search-input { - .input-append { - input { - font-size: 18px; - height: 40px; - width: 25em; - } - div.loader { - display: none; - z-index: 100; - position: absolute; - margin: 0; - top: 37px; - right: 125px; - font-size: 6px; - color: #999999; - } - button { - font-size: 24px; - height: 50px; - min-width: 4em; - } - } - form > p > strong { - vertical-align: middle; - font-size: 1.2em; - margin-left: 0.2em; - } -} +// Search seatch +.search-page { -.search-faceted { - margin-top: 6.5em; + &.not-asked { - & > ul { - padding: 0; + & > h1 { + margin-top: 2.5em; + margin-bottom: 0.8em; + } } - - ul.nav-stacked > li > a { - display: grid; - grid-template-columns: 2em auto auto; - & > span:last-child { - text-align: right; + // Search section title title + & > h1 { + font-weight: normal; + font-size: 2.3em; + + &:before { + content: "\2315"; + display: inline-block; + font-size: 1.5em; + margin-right: 0.2em; + -moz-transform: scale(-1, 1); + -webkit-transform: scale(-1, 1); + -o-transform: scale(-1, 1); + -ms-transform: scale(-1, 1); + transform: scale(-1, 1); } } -} -.search-result { - tbody > tr { - cursor: pointer; - } - tbody > td > dl > dd > ul.inline { - margin: 0; - li { - margin: 0; - padding: 0; - } - li::after { - content: ", "; - padding-right: 0.5em; + // Search input section + & > .search-input { + + // Search Input and Button + & > div:nth-child(1) { + display: grid; + grid-template-columns: auto 8em; + + & > div > input { + font-size: 18px; + height: 40px; + width: 100%; + } + + & > button { + font-size: 24px; + height: 50px; + min-width: 4em; + } } - li:last-child::after { - content: ""; + + // List of channels + & > div:nth-child(2) { + margin-bottom: 0.5em; + + // "Channels: " label + & > div > h4 { + display: inline; + vertical-align: middle; + font-size: 1.2em; + margin-left: 0.2em; + } } } - tbody > td > dl > dd > pre { - background: transparent; - border: 0; - padding: 0; - line-height: 20px; - margin: 0; - } - tbody > td > dl > dt, - tbody > td > dl > dd { - margin-bottom: 1em; + + // Loader during loading the search results + & > .loader-wrapper > h2 { + position: absolute; + top: 3em; + width: 100%; + text-align: center; } - .search-result-button { - margin: 0; - padding: 0; - border: 0; - background: transparent; - display: inline; - font: inherit; - color: inherit; - text-align: inherit; + & > .search-no-results { + padding: 2em 1em; + text-align: center; + margin-bottom: 2em; + + & > h2 { + margin-top: 0; + } } -} -.sort-form, -.sort-form > .sort-group { - margin-bottom: 0; -} -.pager { - & > li > a { - cursor: pointer; - margin: 0 2px; + & > .search-results { + display: flex; + flex-direction: row; + + // Buckets + & > ul { + list-style: none; + margin: 0 1em 0 0; + + & > li { + margin-bottom: 1em; + border: 1px solid #ccc; + padding: 1em; + border-radius: 4px; + + & > ul { + list-style: none; + margin: 0; + + & > li { + margin-bottom: 0.2em; + + &.header { + font-size: 1.2em; + font-weight: bold; + margin-bottom: 0.5em; + } + + & > a { + display: grid; + grid-template-columns: auto auto; + color: #333; + padding: 0.5em 0.5em 0.5em 1em; + text-decoration: none; + + &:hover { + text-decoration: none; + background: #eee; + border-radius: 4px; + } + + & > span:first-child { + overflow: hidden; + } + & > span:last-child { + text-align: right; + margin-left: 0.3em; + } + + &.selected { + background: #0081c2; + color: #FFF; + border-radius: 4px; + position: relative; + + &:after { + content: "\2715"; + font-size: 1.7em; + color: #fff; + position: absolute; + top: 0.25em; + right: 0.5em; + } + + & > span:last-child { + display: none; + + } + } + } + } + } + } + } + + // Results section + & > div { + width: 100%; + + // Search results header + & > :nth-child(1) { + + // Dropdown to show sorting options + & > div:nth-child(1) { + + & > button { + & > .selected { + margin-right: 0.5em; + } + } + + & > ul > li { + + & > a { + padding: 3px 10px; + } + + & > a:before { + display: inline-block; + content: " "; + width: 24.5px; + } + + &.selected > a:before { + content: "\2714"; + } + } + + & > ul > li.header { + font-weight: bold; + padding: 3px 10px; + } + + & > ul > li.header:before, + & > ul > li.divider:before { + display: none; + } + } + + // Text that displays number of results + & > div:nth-child(2) { + font-size: 1.7em; + line-height: 1.3em; + + & > p { + font-size: 0.7em; + } + } + + } + + // Search results list + & > :nth-child(2) { + list-style: none; + margin: 2em 0 0 0; + + & > li { + border-bottom: 1px solid #ccc; + padding-bottom: 2em; + margin-bottom: 2em; + + &:last-child { + border-bottom: 0; + } + + // Attribute name or option name + & > :nth-child(1) { + background: inherit; + border: 0; + padding: 0; + color: #08c; + font-size: 1.5em; + margin-bottom: 0.5em; + } + + // Description + & > :nth-child(2) { + font-size: 1.2em; + margin-bottom: 0.5em; + + } + + &.package { + + // short details of a pacakge + & > :nth-child(3) { + color: #666; + list-style: none; + margin: 0; + + & > li { + display: inline-block; + margin-right: 1em; + + & > a { + color: #666; + } + } + & > li:last-child { + margin-right: 0; + } + } + + // longer details of a pacakge + & > :nth-child(4) { + margin: 1em 0 1em 1em; + + // how to install a package + & > :nth-child(1) { + + h4 { + font-size: 1.2em; + line-height: 1em; + float: left; + } + + ul.nav-tabs { + margin: 0; + + & > li > a { + margin-right: 0; + } + } + + div.tab-content { + padding: 1em; + border: 1px solid #ddd; + border-top: 0; + } + + pre { + .terminal(); + } + + } + + // long description of a package + & > :nth-child(2) { + margin-top: 1em; + } + + // maintainers and platforms + & > :nth-child(3) { + margin-top: 1em; + display: grid; + grid-template-columns: auto auto; + } + } + } + + &.option { + + // short details of a pacakge + & > :nth-child(3) { + margin-top: 1em; + margin-left: 1em; + display: grid; + grid-template-columns: 100px max-content; + column-gap: 1em; + row-gap: 0.5em; + + & > div:nth-child(2n+1) { + font-weight: bold; + text-align: right; + } + + & > div:nth-child(2n) { + pre { + background: transparent; + margin: 0; + padding: 0; + border: 0; + vertical-align: inherit; + display: inline; + } + + pre code { + background: #333; + color: #fff; + padding: 0.5em + } + } + + } + } + } + } + + // Search results footer + & > :nth-child(3) { + margin-top: 1em; + + & > ul > li > a { + cursor: pointer; + margin: 0 2px; + } + } + } } } +/* ------------------------------------------------------------------------- */ +/* -- Loader --------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ + .loader-wrapper { height: 200px; overflow: hidden; + position: relative; } .loader, .loader:before, From b2b1d59a729390c76e77e184362895184b415222 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Mon, 11 Jan 2021 13:50:54 +0100 Subject: [PATCH 07/25] fix click on links --- src/Page/Packages.elm | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index 42b9a25e..d29cbea2 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -308,6 +308,14 @@ viewResultItem : -> List (Html Msg) viewResultItem channel showNixOSDetails show item = let + onClickStop message = + Html.Events.custom "click" <| + Json.Decode.succeed + { message = message + , stopPropagation = True + , preventDefault = True + } + cleanPosition = Regex.fromString "^[0-9a-f]+\\.tar\\.gz\\/" |> Maybe.withDefault Regex.never @@ -464,7 +472,7 @@ viewResultItem channel showNixOSDetails show item = ] [ a [ href "#" - , onClick <| SearchMsg (Search.ShowNixOSDetails True) + , onClickStop <| SearchMsg (Search.ShowNixOSDetails True) ] [ text "On NixOS" ] ] @@ -476,7 +484,7 @@ viewResultItem channel showNixOSDetails show item = ] [ a [ href "#" - , onClick <| SearchMsg (Search.ShowNixOSDetails False) + , onClickStop <| SearchMsg (Search.ShowNixOSDetails False) ] [ text "On non-NixOS" ] ] From 3dcbd4db626f941b6e39d9b265eb071b51d8c7fc Mon Sep 17 00:00:00 2001 From: Marek Fajkus Date: Sun, 17 Jan 2021 02:42:53 +0100 Subject: [PATCH 08/25] faceted search trap click (#262) * search results trap click events to avoid double actions * trap and improve options * Few small improvements * apply styles to packages --- src/Page/Options.elm | 16 +++++----- src/Page/Packages.elm | 72 +++++++++++++++++++------------------------ src/Search.elm | 22 +++++++++++++ src/index.less | 21 +++++++++++-- 4 files changed, 79 insertions(+), 52 deletions(-) diff --git a/src/Page/Options.elm b/src/Page/Options.elm index 9bc54127..af6f83ff 100644 --- a/src/Page/Options.elm +++ b/src/Page/Options.elm @@ -136,7 +136,7 @@ viewSuccess : -> Html Msg viewSuccess channel showNixOSDetails show hits = ul [] - (List.concatMap + (List.map (viewResultItem channel showNixOSDetails show) hits ) @@ -147,7 +147,7 @@ viewResultItem : -> Bool -> Maybe String -> Search.ResultItem ResultItemSource - -> List (Html Msg) + -> Html Msg viewResultItem channel _ show item = let showHtml value = @@ -167,7 +167,7 @@ viewResultItem channel _ show item = pre [] [ text value ] asPreCode value = - div [] [ pre [] [ code [] [ text value ] ] ] + div [] [ pre [] [ code [ class "code-block" ] [ text value ] ] ] githubUrlPrefix branch = "https://github.com/NixOS/nixpkgs/blob/" ++ branch ++ "/" @@ -186,7 +186,7 @@ viewResultItem channel _ show item = [ href <| githubUrlPrefix channelDetails.branch ++ (value |> String.replace ":" "#L") , target "_blank" ] - [ text <| value ] + [ text value ] Nothing -> text <| cleanPosition value @@ -212,7 +212,7 @@ viewResultItem channel _ show item = showDetails = if Just item.source.name == show then - [ div [] + [ div [ Html.Attributes.map SearchMsg Search.trapClick ] [ div [] [ text "Default value" ] , div [] [ withEmpty (wrapped asPreCode) item.source.default ] , div [] [ text "Type" ] @@ -230,13 +230,12 @@ viewResultItem channel _ show item = open = SearchMsg (Search.ShowDetails item.source.name) in - [ li + li [ class "option" , onClick open , Search.elementId item.source.name ] - ([] - |> List.append showDetails + (showDetails |> List.append (item.source.description |> Maybe.map showHtml @@ -248,7 +247,6 @@ viewResultItem channel _ show item = [ text item.source.name ] ] ) - ] diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index d29cbea2..928a05f4 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -32,10 +32,7 @@ import Html.Attributes , id , target ) -import Html.Events - exposing - ( onClick - ) +import Html.Events exposing (onClick) import Json.Decode import Json.Decode.Pipeline import Json.Encode @@ -294,7 +291,7 @@ viewSuccess : -> Html Msg viewSuccess channel showNixOSDetails show hits = ul [] - (List.concatMap + (List.map (viewResultItem channel showNixOSDetails show) hits ) @@ -305,17 +302,9 @@ viewResultItem : -> Bool -> Maybe String -> Search.ResultItem ResultItemSource - -> List (Html Msg) + -> Html Msg viewResultItem channel showNixOSDetails show item = let - onClickStop message = - Html.Events.custom "click" <| - Json.Decode.succeed - { message = message - , stopPropagation = True - , preventDefault = True - } - cleanPosition = Regex.fromString "^[0-9a-f]+\\.tar\\.gz\\/" |> Maybe.withDefault Regex.never @@ -331,7 +320,7 @@ viewResultItem channel showNixOSDetails show item = "https://github.com/NixOS/nixpkgs/blob/" ++ branch ++ "/" ++ uri createShortDetailsItem title url = - [ li [] + [ li [ Html.Attributes.map SearchMsg Search.trapClick ] [ a [ href url , target "_blank" @@ -342,22 +331,20 @@ viewResultItem channel showNixOSDetails show item = shortPackageDetails = ul [] - ([] - |> List.append - (item.source.position - |> Maybe.map - (\position -> - case Search.channelDetailsFromId channel of - Nothing -> - [] - - Just channelDetails -> - createShortDetailsItem - "source" - (createGithubUrl channelDetails.branch position) - ) - |> Maybe.withDefault [] + ((item.source.position + |> Maybe.map + (\position -> + case Search.channelDetailsFromId channel of + Nothing -> + [] + + Just channelDetails -> + createShortDetailsItem + "source" + (createGithubUrl channelDetails.branch position) ) + |> Maybe.withDefault [] + ) |> List.append (item.source.homepage |> List.head @@ -448,9 +435,8 @@ viewResultItem channel showNixOSDetails show item = longerPackageDetails = if Just item.source.attr_name == show then - [ div [] - ([] - |> List.append maintainersAndPlatforms + [ div [ Html.Attributes.map SearchMsg Search.trapClick ] + (maintainersAndPlatforms |> List.append (item.source.longDescription |> Maybe.map (\desc -> [ p [] [ text desc ] ]) @@ -472,7 +458,9 @@ viewResultItem channel showNixOSDetails show item = ] [ a [ href "#" - , onClickStop <| SearchMsg (Search.ShowNixOSDetails True) + , Search.onClickStop <| + SearchMsg <| + Search.ShowNixOSDetails True ] [ text "On NixOS" ] ] @@ -484,20 +472,23 @@ viewResultItem channel showNixOSDetails show item = ] [ a [ href "#" - , onClickStop <| SearchMsg (Search.ShowNixOSDetails False) + , Search.onClickStop <| + SearchMsg <| + Search.ShowNixOSDetails False ] [ text "On non-NixOS" ] ] ] - , div [ class "tab-content" ] + , div + [ class "tab-content" ] [ div [ classList - [ ( "tab-pane", True ) - , ( "active", not showNixOSDetails ) + [ ( "active", not showNixOSDetails ) ] + , class "tab-pane" , id "package-details-nixpkgs" ] - [ pre [] + [ pre [ class "code-block" ] [ text "nix-env -iA nixpkgs." , strong [] [ text item.source.attr_name ] ] @@ -525,7 +516,7 @@ viewResultItem channel showNixOSDetails show item = open = SearchMsg (Search.ShowDetails item.source.attr_name) in - [ li + li [ class "package" , onClick open , Search.elementId item.source.attr_name @@ -540,7 +531,6 @@ viewResultItem channel showNixOSDetails show item = , shortPackageDetails ] ) - ] diff --git a/src/Search.elm b/src/Search.elm index 1bb8e97a..41554a27 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -16,7 +16,9 @@ module Search exposing , init , makeRequest , makeRequestBody + , onClickStop , shouldLoad + , trapClick , update , view ) @@ -1164,3 +1166,23 @@ decodeAggregationBucketItem = Json.Decode.map2 AggregationsBucketItem (Json.Decode.field "doc_count" Json.Decode.int) (Json.Decode.field "key" Json.Decode.string) + + + +-- Html Event Helpers + + +onClickStop : msg -> Html.Attribute msg +onClickStop message = + Html.Events.custom "click" <| + Json.Decode.succeed + { message = message + , stopPropagation = True + , preventDefault = True + } + + +trapClick : Html.Attribute (Msg a b) +trapClick = + Html.Events.stopPropagationOn "click" <| + Json.Decode.succeed ( NoOp, True ) diff --git a/src/index.less b/src/index.less index 9bd5f48c..cdfbc604 100644 --- a/src/index.less +++ b/src/index.less @@ -12,6 +12,16 @@ } } +.search-result-item() { + &:hover { + cursor: pointer; + + .search-result-button { + text-decoration: underline; + } + } +} + /* ------------------------------------------------------------------------- */ /* -- Layout --------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */ @@ -27,6 +37,11 @@ body { } } +.code-block { + display: block; + cursor: text; +} + #content { padding-bottom: 4rem; } @@ -298,6 +313,7 @@ header .navbar.navbar-static-top { } &.package { + .search-result-item(); // short details of a pacakge & > :nth-child(3) { @@ -341,7 +357,7 @@ header .navbar.navbar-static-top { div.tab-content { padding: 1em; - border: 1px solid #ddd; + border: 1px solid #ddd; border-top: 0; } @@ -366,13 +382,14 @@ header .navbar.navbar-static-top { } &.option { + .search-result-item(); // short details of a pacakge & > :nth-child(3) { margin-top: 1em; margin-left: 1em; display: grid; - grid-template-columns: 100px max-content; + grid-template-columns: 100px 1fr; column-gap: 1em; row-gap: 0.5em; From 65a739f9ba54ced3a46a80c80f0727f56d015259 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sun, 17 Jan 2021 02:57:00 +0100 Subject: [PATCH 09/25] fix sorting problem --- src/Search.elm | 153 ++++++++++++++++++++----------------------------- 1 file changed, 62 insertions(+), 91 deletions(-) diff --git a/src/Search.elm b/src/Search.elm index 41554a27..1d8b6a4f 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -211,7 +211,7 @@ elementId str = type Msg a b = NoOp - | SortChange String + | SortChange Sort | BucketsChange String | ChannelChange String | QueryInput String @@ -248,9 +248,9 @@ update toRoute navKey msg model = , Cmd.none ) - SortChange sortId -> + SortChange sort -> { model - | sort = fromSortId sortId |> Maybe.withDefault Relevance + | sort = sort , from = 0 } |> ensureLoading @@ -776,7 +776,7 @@ viewResults model result viewSuccess toRoute outMsg categoryName = String.fromInt result.hits.total.value in [ div [] - [ viewSortSelection toRoute model + [ Html.map outMsg <| viewSortSelection toRoute model , div [] (List.append [ text "Showing results " @@ -802,14 +802,14 @@ viewResults model result viewSuccess toRoute outMsg categoryName = ) ] , viewSuccess model.channel model.showNixOSDetails model.show result.hits.hits - , viewPager outMsg model result.hits.total.value + , Html.map outMsg <| viewPager model result.hits.total.value ] viewSortSelection : Route.SearchRoute -> Model a b - -> Html c + -> Html (Msg a b) viewSortSelection toRoute model = div [ class "btn-group dropdown pull-right" ] [ button @@ -827,18 +827,15 @@ viewSortSelection toRoute model = ] (List.map (\sort -> - let - url = - createUrl toRoute - { model | sort = sort } - in li [ classList [ ( "selected", model.sort == sort ) ] ] [ a - [ href url ] + [ href "#" + , onClick <| SortChange sort + ] [ text <| toSortTitle sort ] ] ) @@ -848,93 +845,67 @@ viewSortSelection toRoute model = ] - ---form [ class "form-horizontal pull-right sort-form" ] --- [ div [ class "control-group sort-group" ] --- [ label [ class "control-label" ] [ text "Sort by:" ] --- , div --- [ class "controls" ] --- [ select --- [ onInput (\x -> outMsg (SortChange x)) --- ] --- (List.map --- (\sort -> --- option --- [ selected (model.sort == sort) --- , value (toSortId sort) --- ] --- [ text <| toSortTitle sort ] --- ) --- sortBy --- ) --- ] --- ] --- ] - - viewPager : - (Msg a b -> c) - -> Model a b + Model a b -> Int - -> Html c -viewPager outMsg model total = - Html.map outMsg <| - div [] - [ ul [ class "pager" ] - [ li [ classList [ ( "disabled", model.from == 0 ) ] ] - [ a - [ Html.Events.onClick <| - if model.from == 0 then - NoOp - - else - ChangePage 0 - ] - [ text "First" ] + -> Html (Msg a b) +viewPager model total = + div [] + [ ul [ class "pager" ] + [ li [ classList [ ( "disabled", model.from == 0 ) ] ] + [ a + [ onClick <| + if model.from == 0 then + NoOp + + else + ChangePage 0 ] - , li [ classList [ ( "disabled", model.from == 0 ) ] ] - [ a - [ Html.Events.onClick <| - if model.from - model.size < 0 then - NoOp - - else - ChangePage <| model.from - model.size - ] - [ text "Previous" ] + [ text "First" ] + ] + , li [ classList [ ( "disabled", model.from == 0 ) ] ] + [ a + [ onClick <| + if model.from - model.size < 0 then + NoOp + + else + ChangePage <| model.from - model.size ] - , li [ classList [ ( "disabled", model.from + model.size >= total ) ] ] - [ a - [ Html.Events.onClick <| - if model.from + model.size >= total then - NoOp - - else - ChangePage <| model.from + model.size - ] - [ text "Next" ] + [ text "Previous" ] + ] + , li [ classList [ ( "disabled", model.from + model.size >= total ) ] ] + [ a + [ onClick <| + if model.from + model.size >= total then + NoOp + + else + ChangePage <| model.from + model.size ] - , li [ classList [ ( "disabled", model.from + model.size >= total ) ] ] - [ a - [ Html.Events.onClick <| - if model.from + model.size >= total then - NoOp - - else - let - remainder = - if remainderBy model.size total == 0 then - 1 - - else - 0 - in - ChangePage <| ((total // model.size) - remainder) * model.size - ] - [ text "Last" ] + [ text "Next" ] + ] + , li [ classList [ ( "disabled", model.from + model.size >= total ) ] ] + [ a + [ onClick <| + if model.from + model.size >= total then + NoOp + + else + let + remainder = + if remainderBy model.size total == 0 then + 1 + + else + 0 + in + ChangePage <| ((total // model.size) - remainder) * model.size ] + [ text "Last" ] ] ] + ] From 48244cd1f06264e7f1412dcca1b482c85c0edd27 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sun, 17 Jan 2021 03:06:45 +0100 Subject: [PATCH 10/25] only open when clicked on the title of the result item --- src/Page/Packages.elm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index 928a05f4..a4948315 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -499,7 +499,7 @@ viewResultItem channel showNixOSDetails show item = , ( "active", showNixOSDetails ) ] ] - [ pre [] + [ pre [ class "code-block" ] [ text <| "nix-env -iA nixos." , strong [] [ text item.source.attr_name ] ] @@ -518,14 +518,15 @@ viewResultItem channel showNixOSDetails show item = in li [ class "package" - , onClick open , Search.elementId item.source.attr_name ] ([] |> List.append longerPackageDetails |> List.append [ Html.button - [ class "search-result-button" ] + [ class "search-result-button" + , onClick open + ] [ text item.source.attr_name ] , div [] [ text <| Maybe.withDefault "" item.source.description ] , shortPackageDetails From 01559c1dfcf1855208b5988222ebeaf494dbf828 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sun, 17 Jan 2021 03:24:58 +0100 Subject: [PATCH 11/25] only show subset of platforms as before --- src/Page/Packages.elm | 45 +++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index a4948315..9b94ff33 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -184,6 +184,15 @@ update navKey msg model = -- VIEW +allowedPlatforms : List String +allowedPlatforms = + [ "x86_64-linux" + , "aarch64-linux" + , "x86_64-darwin" + , "i686-linux" + ] + + view : Model -> Html Msg view model = Search.view { toRoute = Route.Packages, categoryName = "packages" } @@ -216,28 +225,37 @@ viewBuckets bucketsAsString result = |> Json.Encode.encode 0 |> Search.BucketsChange |> SearchMsg + + sortBuckets items = + items + |> List.sortBy .doc_count + |> List.reverse + + filterPlatforms = + List.filter + (\bucket -> List.member bucket.key allowedPlatforms) in [] |> viewBucket "Package sets" - (result.aggregations.package_attr_set.buckets |> List.sortBy .doc_count |> List.reverse) + (result.aggregations.package_attr_set.buckets |> sortBuckets) (createBucketsMsg .packageSets (\s v -> { s | packageSets = v })) selectedBucket.packageSets |> viewBucket "Licenses" - (result.aggregations.package_license_set.buckets |> List.sortBy .doc_count |> List.reverse) + (result.aggregations.package_license_set.buckets |> sortBuckets) (createBucketsMsg .licenses (\s v -> { s | licenses = v })) selectedBucket.licenses - |> viewBucket - "Platforms" - (result.aggregations.package_platforms.buckets |> List.sortBy .doc_count |> List.reverse) - (createBucketsMsg .platforms (\s v -> { s | platforms = v })) - selectedBucket.platforms |> viewBucket "Maintainers" - (result.aggregations.package_maintainers_set.buckets |> List.sortBy .doc_count |> List.reverse) + (result.aggregations.package_maintainers_set.buckets |> sortBuckets) (createBucketsMsg .maintainers (\s v -> { s | maintainers = v })) selectedBucket.maintainers + |> viewBucket + "Platforms" + (result.aggregations.package_platforms.buckets |> filterPlatforms |> sortBuckets) + (createBucketsMsg .platforms (\s v -> { s | platforms = v })) + selectedBucket.platforms viewBucket : @@ -409,8 +427,6 @@ viewResultItem channel showNixOSDetails show item = li [] [ text platform ] maintainersAndPlatforms = - --[ dt [] [ text "Platforms" ] - --, dd [] [ asList (showPlatforms item.source.hydra item.source.platforms) ] [ div [] [ div [] (List.append [ h4 [] [ text "Maintainers" ] ] @@ -423,11 +439,16 @@ viewResultItem channel showNixOSDetails show item = ) , div [] (List.append [ h4 [] [ text "Platforms" ] ] - (if List.isEmpty item.source.maintainers then + (let + platforms = + List.filter (\platform -> List.member platform allowedPlatforms) + item.source.platforms + in + if List.isEmpty platforms then [ p [] [ text "This package is not available on any platform." ] ] else - [ ul [] (List.map showPlatform item.source.platforms) ] + [ ul [] (List.map showPlatform platforms) ] ) ) ] From 7a841b70cb54b87e44ccbbdba8af77d1e3180fe8 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sun, 17 Jan 2021 03:40:09 +0100 Subject: [PATCH 12/25] make it more obvious what each item in shortPackageDetails is --- src/Page/Packages.elm | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index 9b94ff33..498c102e 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -338,14 +338,13 @@ viewResultItem channel showNixOSDetails show item = "https://github.com/NixOS/nixpkgs/blob/" ++ branch ++ "/" ++ uri createShortDetailsItem title url = - [ li [ Html.Attributes.map SearchMsg Search.trapClick ] + li [ Html.Attributes.map SearchMsg Search.trapClick ] [ a [ href url , target "_blank" ] [ text title ] ] - ] shortPackageDetails = ul [] @@ -357,40 +356,43 @@ viewResultItem channel showNixOSDetails show item = [] Just channelDetails -> - createShortDetailsItem - "source" + [ createShortDetailsItem + "Source" (createGithubUrl channelDetails.branch position) + ] ) |> Maybe.withDefault [] ) |> List.append (item.source.homepage |> List.head - |> Maybe.map (createShortDetailsItem "homepage") + |> Maybe.map (\x -> [ createShortDetailsItem "Homepage" x ]) |> Maybe.withDefault [] ) |> List.append (item.source.licenses - |> List.head - |> Maybe.map + |> List.filterMap (\license -> case ( license.fullName, license.url ) of ( Nothing, Nothing ) -> - [] + Nothing ( Just fullName, Nothing ) -> - [ text fullName ] + Just (text fullName) ( Nothing, Just url ) -> - createShortDetailsItem "license" url + Just (createShortDetailsItem "Unknown" url) ( Just fullName, Just url ) -> - createShortDetailsItem fullName url + Just (createShortDetailsItem fullName url) ) - |> Maybe.withDefault [] + |> List.intersperse (text ", ") + |> List.append [ text "Licenses: " ] ) |> List.append - [ li [] [ text item.source.pname ] + [ text "Name: " + , li [] [ text item.source.pname ] + , text "Version: " , li [] [ text item.source.pversion ] ] ) From 3d8c18b2b7f11b62aa18da89aae38e3c43b3c70b Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sun, 17 Jan 2021 03:43:22 +0100 Subject: [PATCH 13/25] reset opened item when sort/bucket item is clicked --- src/Search.elm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Search.elm b/src/Search.elm index 1d8b6a4f..a9062743 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -251,6 +251,7 @@ update toRoute navKey msg model = SortChange sort -> { model | sort = sort + , show = Nothing , from = 0 } |> ensureLoading @@ -264,6 +265,7 @@ update toRoute navKey msg model = else Just buckets + , show = Nothing , from = 0 } |> ensureLoading @@ -272,6 +274,7 @@ update toRoute navKey msg model = ChannelChange channel -> { model | channel = channel + , show = Nothing , from = 0 } |> ensureLoading @@ -285,6 +288,7 @@ update toRoute navKey msg model = QueryInputSubmit -> { model | from = 0 + , show = Nothing , buckets = Nothing } |> ensureLoading From fb18f67e61fa7f7013d52bcb796f6635af085ae6 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sun, 17 Jan 2021 03:47:24 +0100 Subject: [PATCH 14/25] whole result item no longer opens details --- src/index.less | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/index.less b/src/index.less index cdfbc604..8eb278cd 100644 --- a/src/index.less +++ b/src/index.less @@ -14,8 +14,6 @@ .search-result-item() { &:hover { - cursor: pointer; - .search-result-button { text-decoration: underline; } From 1aabd791d0c52a264038a323f11ed0a36403568f Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sun, 17 Jan 2021 04:33:25 +0100 Subject: [PATCH 15/25] use a (on hover) wedge to show/hide details --- src/Page/Packages.elm | 50 +++++++++++++++++++++++++++++++------------ src/index.less | 39 +++++++++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index 498c102e..7ea2b2ff 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -338,13 +338,11 @@ viewResultItem channel showNixOSDetails show item = "https://github.com/NixOS/nixpkgs/blob/" ++ branch ++ "/" ++ uri createShortDetailsItem title url = - li [ Html.Attributes.map SearchMsg Search.trapClick ] - [ a - [ href url - , target "_blank" - ] - [ text title ] + a + [ href url + , target "_blank" ] + [ text title ] shortPackageDetails = ul [] @@ -356,9 +354,11 @@ viewResultItem channel showNixOSDetails show item = [] Just channelDetails -> - [ createShortDetailsItem - "Source" - (createGithubUrl channelDetails.branch position) + [ li [ trapClick ] + [ createShortDetailsItem + "Source" + (createGithubUrl channelDetails.branch position) + ] ] ) |> Maybe.withDefault [] @@ -366,7 +366,12 @@ viewResultItem channel showNixOSDetails show item = |> List.append (item.source.homepage |> List.head - |> Maybe.map (\x -> [ createShortDetailsItem "Homepage" x ]) + |> Maybe.map + (\x -> + [ li [ trapClick ] + [ createShortDetailsItem "Homepage" x ] + ] + ) |> Maybe.withDefault [] ) |> List.append @@ -387,7 +392,7 @@ viewResultItem channel showNixOSDetails show item = Just (createShortDetailsItem fullName url) ) |> List.intersperse (text ", ") - |> List.append [ text "Licenses: " ] + |> (\x -> [ li [] (List.append [ text "Licenses: " ] x) ]) ) |> List.append [ text "Name: " @@ -458,7 +463,7 @@ viewResultItem channel showNixOSDetails show item = longerPackageDetails = if Just item.source.attr_name == show then - [ div [ Html.Attributes.map SearchMsg Search.trapClick ] + [ div [ trapClick ] (maintainersAndPlatforms |> List.append (item.source.longDescription @@ -536,11 +541,16 @@ viewResultItem channel showNixOSDetails show item = else [] - open = + toggle = SearchMsg (Search.ShowDetails item.source.attr_name) + + trapClick = + Html.Attributes.map SearchMsg Search.trapClick in li [ class "package" + , classList + [ ( "opened", Just item.source.attr_name == show ) ] , Search.elementId item.source.attr_name ] ([] @@ -548,11 +558,23 @@ viewResultItem channel showNixOSDetails show item = |> List.append [ Html.button [ class "search-result-button" - , onClick open + , onClick toggle ] [ text item.source.attr_name ] , div [] [ text <| Maybe.withDefault "" item.source.description ] , shortPackageDetails + , a + [ href "#" + , onClick toggle + ] + [ text + (if Just item.source.attr_name == show then + "▲▲▲ Hide package details ▲▲▲" + + else + "▾▾▾ Show more package details ▾▾▾" + ) + ] ] ) diff --git a/src/index.less b/src/index.less index 8eb278cd..a66cd1c9 100644 --- a/src/index.less +++ b/src/index.less @@ -284,10 +284,12 @@ header .navbar.navbar-static-top { list-style: none; margin: 2em 0 0 0; + // Result item & > li { border-bottom: 1px solid #ccc; padding-bottom: 2em; margin-bottom: 2em; + text-align: center; &:last-child { border-bottom: 0; @@ -301,22 +303,25 @@ header .navbar.navbar-static-top { color: #08c; font-size: 1.5em; margin-bottom: 0.5em; + text-align: left; + display: block; } // Description & > :nth-child(2) { font-size: 1.2em; margin-bottom: 0.5em; - + text-align: left; } &.package { - .search-result-item(); + .search-result-item(); // short details of a pacakge & > :nth-child(3) { color: #666; list-style: none; + text-align: left; margin: 0; & > li { @@ -332,9 +337,34 @@ header .navbar.navbar-static-top { } } - // longer details of a pacakge + // show longer details link & > :nth-child(4) { - margin: 1em 0 1em 1em; + margin: 0 auto; + display: none; + text-align: center; + text-decoration: none; + line-height: 1.5em; + color: #666; + background: #FFF; + padding: 0 1em; + position: relative; + top: 0.75em; + outline: none; + } + &.opened, + &:hover { + padding-bottom: 0; + + & > :nth-child(4) { + display: inline-block; + padding-top: 0.5em; + } + } + + // longer details of a pacakge + & > :nth-child(5) { + margin: 2em 0 1em 1em; + text-align: left; // how to install a package & > :nth-child(1) { @@ -416,6 +446,7 @@ header .navbar.navbar-static-top { } } } + } // Search results footer From 9aec6bec8ed2305da4091827a4e8369c8ae97f70 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sun, 17 Jan 2021 04:36:45 +0100 Subject: [PATCH 16/25] show buckets even when there is no packages to list --- src/Search.elm | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/Search.elm b/src/Search.elm index a9062743..eb6055dd 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -612,26 +612,25 @@ viewResult outMsg toRoute categoryName model viewSuccess viewBuckets = ] RemoteData.Success result -> - if result.hits.total.value == 0 then + let + buckets = + viewBuckets model.buckets result + in + if result.hits.total.value == 0 && List.length buckets == 0 then viewNoResults categoryName - else - let - buckets = - viewBuckets model.buckets result - in - if List.length buckets > 0 then - div [ class "search-results" ] - [ ul [] buckets - , div [] - (viewResults model result viewSuccess toRoute outMsg categoryName) - ] + else if List.length buckets > 0 then + div [ class "search-results" ] + [ ul [] buckets + , div [] + (viewResults model result viewSuccess toRoute outMsg categoryName) + ] - else - div [ class "search-results" ] - [ div [] - (viewResults model result viewSuccess toRoute outMsg categoryName) - ] + else + div [ class "search-results" ] + [ div [] + (viewResults model result viewSuccess toRoute outMsg categoryName) + ] RemoteData.Failure error -> let From 743844d97fe217e6ad624ffa8b7b6d38b3c9fdda Mon Sep 17 00:00:00 2001 From: Marek Fajkus Date: Sun, 17 Jan 2021 16:04:31 +0100 Subject: [PATCH 17/25] Apply "more link" on options and fix CSS issues (#264) * Apply "more link" on options and fix CSS issues * elm fmt --- src/Page/Options.elm | 40 ++++++++++++++++------------- src/Page/Packages.elm | 19 ++++---------- src/Search.elm | 23 +++++++++++++++++ src/Utils.elm | 16 ++++++------ src/index.less | 59 ++++++++++++++++++++++--------------------- 5 files changed, 88 insertions(+), 69 deletions(-) diff --git a/src/Page/Options.elm b/src/Page/Options.elm index af6f83ff..91b505fe 100644 --- a/src/Page/Options.elm +++ b/src/Page/Options.elm @@ -24,6 +24,7 @@ import Html import Html.Attributes exposing ( class + , classList , href , target ) @@ -151,14 +152,13 @@ viewResultItem : viewResultItem channel _ show item = let showHtml value = - [ div [] <| + div [] <| case Html.Parser.run value of Ok nodes -> Html.Parser.Util.toVirtualDom nodes Err _ -> [] - ] default = "Not given" @@ -212,7 +212,7 @@ viewResultItem channel _ show item = showDetails = if Just item.source.name == show then - [ div [ Html.Attributes.map SearchMsg Search.trapClick ] + div [ Html.Attributes.map SearchMsg Search.trapClick ] [ div [] [ text "Default value" ] , div [] [ withEmpty (wrapped asPreCode) item.source.default ] , div [] [ text "Type" ] @@ -222,31 +222,35 @@ viewResultItem channel _ show item = , div [] [ text "Declared in" ] , div [] [ withEmpty asGithubLink item.source.source ] ] - ] + |> Just else - [] + Nothing - open = + toggle = SearchMsg (Search.ShowDetails item.source.name) + + isOpen = + Just item.source.name == show in li [ class "option" - , onClick open + , classList [ ( "opened", isOpen ) ] , Search.elementId item.source.name ] - (showDetails - |> List.append - (item.source.description - |> Maybe.map showHtml - |> Maybe.withDefault [] - ) - |> List.append - [ Html.button - [ class "search-result-button" ] + <| + List.filterMap identity + [ Just <| + Html.button + [ class "search-result-button" + , onClick toggle + ] [ text item.source.name ] - ] - ) + , Maybe.map showHtml item.source.description + , Just <| + Search.showMoreButton toggle isOpen + , showDetails + ] diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index 7ea2b2ff..e6a617cb 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -546,11 +546,13 @@ viewResultItem channel showNixOSDetails show item = trapClick = Html.Attributes.map SearchMsg Search.trapClick + + isOpen = + Just item.source.attr_name == show in li [ class "package" - , classList - [ ( "opened", Just item.source.attr_name == show ) ] + , classList [ ( "opened", isOpen ) ] , Search.elementId item.source.attr_name ] ([] @@ -563,18 +565,7 @@ viewResultItem channel showNixOSDetails show item = [ text item.source.attr_name ] , div [] [ text <| Maybe.withDefault "" item.source.description ] , shortPackageDetails - , a - [ href "#" - , onClick toggle - ] - [ text - (if Just item.source.attr_name == show then - "▲▲▲ Hide package details ▲▲▲" - - else - "▾▾▾ Show more package details ▾▾▾" - ) - ] + , Search.showMoreButton toggle isOpen ] ) diff --git a/src/Search.elm b/src/Search.elm index eb6055dd..70a3227d 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -18,6 +18,7 @@ module Search exposing , makeRequestBody , onClickStop , shouldLoad + , showMoreButton , trapClick , update , view @@ -1143,6 +1144,28 @@ decodeAggregationBucketItem = +-- Html Helper elemetnts + + +showMoreButton : msg -> Bool -> Html msg +showMoreButton toggle isOpen = + div [ class "result-item-show-more-wrapper" ] + [ a + [ href "#" + , onClick toggle + , class "result-item-show-more" + ] + [ text <| + if isOpen then + "▲▲▲ Hide package details ▲▲▲" + + else + "▾▾▾ Show more package details ▾▾▾" + ] + ] + + + -- Html Event Helpers diff --git a/src/Utils.elm b/src/Utils.elm index 2b0fc4dd..9f0956c4 100644 --- a/src/Utils.elm +++ b/src/Utils.elm @@ -1,13 +1,13 @@ -module Utils exposing - ( toggleList - ) +module Utils exposing (toggleList) -toggleList : +toggleList : List a - -> a + -> a -> List a toggleList list item = - if List.member item list - then List.filter (\x -> x /= item) list - else List.append list [item] + if List.member item list then + List.filter (\x -> x /= item) list + + else + List.append list [ item ] diff --git a/src/index.less b/src/index.less index a66cd1c9..2c4f4350 100644 --- a/src/index.less +++ b/src/index.less @@ -18,6 +18,34 @@ text-decoration: underline; } } + + .result-item-show-more-wrapper { + text-align: center; + } + + // show longer details link + .result-item-show-more { + margin: 0 auto; + display: none; + text-align: center; + text-decoration: none; + line-height: 1.5em; + color: #666; + background: #FFF; + padding: 0 1em; + position: relative; + top: 0.75em; + outline: none; + } + &.opened, + &:hover { + padding-bottom: 0; + + .result-item-show-more { + display: inline-block; + padding-top: 0.5em; + } + } } /* ------------------------------------------------------------------------- */ @@ -276,7 +304,6 @@ header .navbar.navbar-static-top { font-size: 0.7em; } } - } // Search results list @@ -289,7 +316,6 @@ header .navbar.navbar-static-top { border-bottom: 1px solid #ccc; padding-bottom: 2em; margin-bottom: 2em; - text-align: center; &:last-child { border-bottom: 0; @@ -337,30 +363,6 @@ header .navbar.navbar-static-top { } } - // show longer details link - & > :nth-child(4) { - margin: 0 auto; - display: none; - text-align: center; - text-decoration: none; - line-height: 1.5em; - color: #666; - background: #FFF; - padding: 0 1em; - position: relative; - top: 0.75em; - outline: none; - } - &.opened, - &:hover { - padding-bottom: 0; - - & > :nth-child(4) { - display: inline-block; - padding-top: 0.5em; - } - } - // longer details of a pacakge & > :nth-child(5) { margin: 2em 0 1em 1em; @@ -413,9 +415,8 @@ header .navbar.navbar-static-top { .search-result-item(); // short details of a pacakge - & > :nth-child(3) { - margin-top: 1em; - margin-left: 1em; + & > :nth-child(4) { + margin: 2em 0 1em 1em; display: grid; grid-template-columns: 100px 1fr; column-gap: 1em; From c909f6fac8b20d95a4ceacbe41c187c5c5bd0462 Mon Sep 17 00:00:00 2001 From: Marek Fajkus Date: Sun, 17 Jan 2021 16:22:39 +0100 Subject: [PATCH 18/25] facet search update item hover interaction (#263) * Improve hover action * fix --- src/index.less | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/index.less b/src/index.less index 2c4f4350..5458e6e2 100644 --- a/src/index.less +++ b/src/index.less @@ -12,13 +12,8 @@ } } -.search-result-item() { - &:hover { - .search-result-button { - text-decoration: underline; - } - } +.search-result-item() { .result-item-show-more-wrapper { text-align: center; } @@ -176,6 +171,13 @@ header .navbar.navbar-static-top { } } + + .search-result-button { + &:hover { + text-decoration: underline; + } + } + & > .search-results { display: flex; flex-direction: row; From 64b07f96eb310c3108f8d15c93db4a3f9b68d987 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Thu, 21 Jan 2021 00:09:22 +0100 Subject: [PATCH 19/25] off by one in "showing results" --- src/Search.elm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Search.elm b/src/Search.elm index 70a3227d..e757c4d3 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -784,7 +784,7 @@ viewResults model result viewSuccess toRoute outMsg categoryName = , div [] (List.append [ text "Showing results " - , text from + , text <| from + 1 , text "-" , text to , text " of " From 6ebeabbe00d25153239f2161f6f5b88e8c140cdc Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Thu, 21 Jan 2021 00:11:42 +0100 Subject: [PATCH 20/25] typo --- src/Search.elm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Search.elm b/src/Search.elm index e757c4d3..2f131e93 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -765,7 +765,7 @@ viewResults : viewResults model result viewSuccess toRoute outMsg categoryName = let from = - String.fromInt model.from + String.fromInt (model.from + 1) to = String.fromInt @@ -784,7 +784,7 @@ viewResults model result viewSuccess toRoute outMsg categoryName = , div [] (List.append [ text "Showing results " - , text <| from + 1 + , text from , text "-" , text to , text " of " From da0d0c0ba257ff1e02d745c5439d0642bafc9d12 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sat, 23 Jan 2021 16:47:22 +0100 Subject: [PATCH 21/25] Bucket name doesn't display under X when bucket is selected --- src/index.less | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/index.less b/src/index.less index 5458e6e2..7d8da92c 100644 --- a/src/index.less +++ b/src/index.less @@ -238,8 +238,12 @@ header .navbar.navbar-static-top { font-size: 1.7em; color: #fff; position: absolute; - top: 0.25em; - right: 0.5em; + top: 4px; + right: 4px; + background: #0081c2; + bottom: 4px; + width: 1em; + padding: 2px; } & > span:last-child { From f06a92fc641370670993b235bb86d86caa52a47e Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sat, 23 Jan 2021 16:49:00 +0100 Subject: [PATCH 22/25] Reset bucket selection when switching between channels --- src/Search.elm | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Search.elm b/src/Search.elm index 2f131e93..7772e0c7 100644 --- a/src/Search.elm +++ b/src/Search.elm @@ -276,6 +276,7 @@ update toRoute navKey msg model = { model | channel = channel , show = Nothing + , buckets = Nothing , from = 0 } |> ensureLoading From 1c910d31d6e11757f06698e76867e86d30d8b00a Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sat, 23 Jan 2021 16:49:41 +0100 Subject: [PATCH 23/25] Add "No packages set"/"No license"/"No maintainers" buckets Also I now filter platforms in import-channel script and only import allowed platforms. --- default.nix | 2 ++ import-scripts/default.nix | 12 ++++++++--- import-scripts/import_scripts/channel.py | 26 ++++++++++++++---------- src/Page/Packages.elm | 24 +++------------------- 4 files changed, 29 insertions(+), 35 deletions(-) diff --git a/default.nix b/default.nix index 4f439d7c..b3440faf 100644 --- a/default.nix +++ b/default.nix @@ -31,6 +31,8 @@ pkgs.stdenv.mkDerivation { name = "${package.name}-${package.version}"; src = pkgs.lib.cleanSource ./.; + preferLocalBuild = true; + buildInputs = [ yarnPkg diff --git a/import-scripts/default.nix b/import-scripts/default.nix index 9fc3d733..84c6df91 100644 --- a/import-scripts/default.nix +++ b/import-scripts/default.nix @@ -13,12 +13,13 @@ mkPoetryApplication { ''; }); }); + preferLocalBuild = true; nativeBuildInputs = with pkgs; [ poetry fd entr + nixStable ]; - #doCheck = false; checkPhase = '' export PYTHONPATH=$PWD:$PYTHONPATH black --diff --check import_scripts/ tests/ @@ -27,8 +28,13 @@ mkPoetryApplication { pytest -vv tests/ ''; postInstall = '' - wrapProgram $out/bin/import-channel --set INDEX_SCHEMA_VERSION "${version}" - wrapProgram $out/bin/channel-diff --set INDEX_SCHEMA_VERSION "${version}" + wrapProgram $out/bin/import-channel \ + --set INDEX_SCHEMA_VERSION "${version}" \ + --prefix PATH : "${pkgs.nixStable}/bin" + wrapProgram $out/bin/channel-diff \ + --set INDEX_SCHEMA_VERSION "${version}" \ + --prefix PATH : "${pkgs.nixStable}/bin" + ''; shellHook = '' cd import-scripts/ diff --git a/import-scripts/import_scripts/channel.py b/import-scripts/import_scripts/channel.py index 336b7d3d..94258a27 100644 --- a/import-scripts/import_scripts/channel.py +++ b/import-scripts/import_scripts/channel.py @@ -36,6 +36,7 @@ "20.03": "nixos/20.03/nixos-20.03.", "20.09": "nixos/20.09/nixos-20.09.", } +ALLOWED_PLATFORMS = ["x86_64-linux", "aarch64-linux", "x86_64-darwin", "i686-linux"] ANALYSIS = { "normalizer": { "lowercase": {"type": "custom", "char_filter": [], "filter": ["lowercase"]} @@ -386,7 +387,7 @@ def remove_attr_set(name): @backoff.on_exception(backoff.expo, subprocess.CalledProcessError) def get_packages_raw(evaluation): logger.debug( - f"get_packages: Retrieving list of packages for '{evaluation['git_revision']}' revision" + f"get_packages_raw: Retrieving list of packages for '{evaluation['git_revision']}' revision" ) result = subprocess.run( shlex.split( @@ -407,7 +408,7 @@ def gen(): licenses = data["meta"].get("license") if licenses: if type(licenses) == str: - licenses = [dict(fullName=licenses)] + licenses = [dict(fullName=licenses, url=None)] elif type(licenses) == dict: licenses = [licenses] licenses = [ @@ -417,24 +418,27 @@ def gen(): for license in licenses ] else: - licenses = [] + licenses = [dict(fullName="No license", url=None)] maintainers = get_maintainer(data["meta"].get("maintainers", [])) + if len(maintainers) == 0: + maintainers = [dict(name="No maintainers", email=None, github=None)] platforms = [ - type(platform) == str and platform or None + platform for platform in data["meta"].get("platforms", []) + if type(platform) == str and platform in ALLOWED_PLATFORMS ] - attr_set = None + attr_set = "No package set" if "." in attr_name: - attr_set = attr_name.split(".")[0] + maybe_attr_set = attr_name.split(".")[0] if ( - not attr_set.endswith("Packages") - and not attr_set.endswith("Plugins") - and not attr_set.endswith("Extensions") + maybe_attr_set.endswith("Packages") + or maybe_attr_set.endswith("Plugins") + or maybe_attr_set.endswith("Extensions") ): - attr_set = None + attr_set = maybe_attr_set hydra = None if data["name"] in evaluation_builds: @@ -485,7 +489,7 @@ def gen(): package_license_set=[i["fullName"] for i in licenses], package_maintainers=maintainers, package_maintainers_set=[i["name"] for i in maintainers if i["name"]], - package_platforms=[i for i in platforms if i], + package_platforms=platforms, package_position=position, package_homepage=data["meta"].get("homepage"), package_system=data["system"], diff --git a/src/Page/Packages.elm b/src/Page/Packages.elm index e6a617cb..443b6fdc 100644 --- a/src/Page/Packages.elm +++ b/src/Page/Packages.elm @@ -184,15 +184,6 @@ update navKey msg model = -- VIEW -allowedPlatforms : List String -allowedPlatforms = - [ "x86_64-linux" - , "aarch64-linux" - , "x86_64-darwin" - , "i686-linux" - ] - - view : Model -> Html Msg view model = Search.view { toRoute = Route.Packages, categoryName = "packages" } @@ -230,10 +221,6 @@ viewBuckets bucketsAsString result = items |> List.sortBy .doc_count |> List.reverse - - filterPlatforms = - List.filter - (\bucket -> List.member bucket.key allowedPlatforms) in [] |> viewBucket @@ -253,7 +240,7 @@ viewBuckets bucketsAsString result = selectedBucket.maintainers |> viewBucket "Platforms" - (result.aggregations.package_platforms.buckets |> filterPlatforms |> sortBuckets) + (result.aggregations.package_platforms.buckets |> sortBuckets) (createBucketsMsg .platforms (\s v -> { s | platforms = v })) selectedBucket.platforms @@ -446,16 +433,11 @@ viewResultItem channel showNixOSDetails show item = ) , div [] (List.append [ h4 [] [ text "Platforms" ] ] - (let - platforms = - List.filter (\platform -> List.member platform allowedPlatforms) - item.source.platforms - in - if List.isEmpty platforms then + (if List.isEmpty item.source.platforms then [ p [] [ text "This package is not available on any platform." ] ] else - [ ul [] (List.map showPlatform platforms) ] + [ ul [] (List.map showPlatform item.source.platforms) ] ) ) ] From ae8332bfdf59db3a1e2761e9149b62dc16c50225 Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sat, 23 Jan 2021 17:01:50 +0100 Subject: [PATCH 24/25] Remove outline when button of result item is focused --- src/index.less | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index.less b/src/index.less index 7d8da92c..9488eaa8 100644 --- a/src/index.less +++ b/src/index.less @@ -268,6 +268,9 @@ header .navbar.navbar-static-top { & > div:nth-child(1) { & > button { + &:focus { + outline: none; + } & > .selected { margin-right: 0.5em; } From 18ac389850b2974639fdc6965390e88f4206c95a Mon Sep 17 00:00:00 2001 From: Rok Garbas Date: Sat, 23 Jan 2021 17:19:25 +0100 Subject: [PATCH 25/25] Revert "Remove outline when button of result item is focused" This reverts commit ae8332bfdf59db3a1e2761e9149b62dc16c50225. --- src/index.less | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/index.less b/src/index.less index 9488eaa8..7d8da92c 100644 --- a/src/index.less +++ b/src/index.less @@ -268,9 +268,6 @@ header .navbar.navbar-static-top { & > div:nth-child(1) { & > button { - &:focus { - outline: none; - } & > .selected { margin-right: 0.5em; }