aux-search/src/Main.elm
2021-12-31 16:37:02 +01:00

486 lines
14 KiB
Elm
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module Main exposing (main)
import Browser
import Browser.Navigation
import Html
exposing
( Html
, a
, button
, div
, footer
, header
, img
, li
, span
, text
, ul
)
import Html.Attributes
exposing
( attribute
, class
, classList
, href
, id
, src
, type_
)
import Page.Flakes exposing (Model(..))
import Page.Home
import Page.Options
import Page.Packages
import Route exposing (SearchType(..))
import Search
import Url
import Search exposing (defaultFlakeId)
import Search exposing (channels)
import Html exposing (sup)
import Html exposing (small)
import RemoteData exposing (RemoteData(..))
-- MODEL
type alias Flags =
{ elasticsearchMappingSchemaVersion : Int
, elasticsearchUrl : String
, elasticsearchUsername : String
, elasticsearchPassword : String
}
type alias Model =
{ navKey : Browser.Navigation.Key
, route : Route.Route
, elasticsearch : Search.Options
, page : Page
}
type Page
= NotFound
| Home Page.Home.Model
| Packages Page.Packages.Model
| Options Page.Options.Model
| Flakes Page.Flakes.Model
init :
Flags
-> Url.Url
-> Browser.Navigation.Key
-> ( Model, Cmd Msg )
init flags url navKey =
let
model =
{ navKey = navKey
, elasticsearch =
Search.Options
flags.elasticsearchMappingSchemaVersion
flags.elasticsearchUrl
flags.elasticsearchUsername
flags.elasticsearchPassword
, page = NotFound
, route = Route.Home
}
in
changeRouteTo model url
-- UPDATE
type Msg
= ChangedUrl Url.Url
| ClickedLink Browser.UrlRequest
| HomeMsg Page.Home.Msg
| PackagesMsg Page.Packages.Msg
| OptionsMsg Page.Options.Msg
| FlakesMsg Page.Flakes.Msg
updateWith :
(subModel -> Page)
-> (subMsg -> Msg)
-> Model
-> ( subModel, Cmd subMsg )
-> ( Model, Cmd Msg )
updateWith toPage toMsg model ( subModel, subCmd ) =
( { model | page = toPage subModel }
, Cmd.map toMsg subCmd
)
attemptQuery : ( Model, Cmd Msg ) -> ( Model, Cmd Msg )
attemptQuery (( model, _ ) as pair) =
let
-- We intentially throw away Cmd
-- because we don't want to perform any effects
-- in this cases where route itself doesn't change
noEffects =
Tuple.mapSecond (always Cmd.none)
submitQuery msg makeRequest searchModel =
Tuple.mapSecond
(\cmd ->
Cmd.batch
[ cmd
, Cmd.map msg <|
makeRequest
model.elasticsearch
searchModel.searchType
searchModel.channel
(Maybe.withDefault "" searchModel.query)
searchModel.from
searchModel.size
searchModel.buckets
searchModel.sort
]
)
pair
in
case model.page of
Packages searchModel ->
if Search.shouldLoad searchModel then
submitQuery PackagesMsg Page.Packages.makeRequest { searchModel | searchType = PackageSearch }
else
noEffects pair
Options searchModel ->
if Search.shouldLoad searchModel then
submitQuery OptionsMsg Page.Options.makeRequest { searchModel | searchType = OptionSearch }
else
noEffects pair
Flakes (OptionModel searchModel) ->
if Search.shouldLoad searchModel then
submitQuery FlakesMsg Page.Flakes.makeRequest {searchModel | channel = defaultFlakeId }
else
noEffects pair
Flakes (PackagesModel searchModel) ->
if Search.shouldLoad searchModel then
-- let
-- _ = Debug.log "main" "submit flake message"
-- in
submitQuery FlakesMsg Page.Flakes.makeRequest {searchModel | channel = defaultFlakeId}
else
-- let _ = Debug.log "main" "should not load flakes" in
noEffects pair
_ ->
-- let
-- _ = Debug.log "pair" <| Debug.toString pair
-- in
pair
pageMatch : Page -> Page -> Bool
pageMatch m1 m2 =
case ( m1, m2 ) of
( NotFound, NotFound ) ->
True
( Home _, Home _ ) ->
True
( Packages model_a, Packages model_b ) ->
{model_a | show = Nothing, result = NotAsked } == {model_b | show = Nothing, result = NotAsked}
( Options model_a, Options model_b ) ->
{model_a | show = Nothing, result = NotAsked } == {model_b | show = Nothing, result = NotAsked}
( Flakes (OptionModel model_a), Flakes (OptionModel model_b) ) ->
{model_a | show = Nothing, result = NotAsked } == {model_b | show = Nothing, result = NotAsked}
( Flakes (PackagesModel model_a), Flakes (PackagesModel model_b) ) ->
{model_a | show = Nothing, result = NotAsked } == {model_b | show = Nothing, result = NotAsked}
_ ->
False
changeRouteTo :
Model
-> Url.Url
-> ( Model, Cmd Msg )
changeRouteTo currentModel url =
case Route.fromUrl url of
Nothing ->
( { currentModel | page = NotFound }
, Cmd.none
)
Just route ->
let
model =
{ currentModel | route = route }
avoidReinit ( newModel, cmd ) =
if pageMatch currentModel.page newModel.page then
( model, Cmd.none )
else
( newModel, cmd )
in
case route of
Route.NotFound ->
( { model | page = NotFound }, Cmd.none )
Route.Home ->
-- Always redirect to /packages until we have something to show
-- on the home page
( model, Browser.Navigation.replaceUrl model.navKey "/packages" )
Route.Packages searchArgs ->
let
modelPage =
case model.page of
Packages x ->
Just x
_ ->
Nothing
in
Page.Packages.init searchArgs modelPage
|> updateWith Packages PackagesMsg model
|> avoidReinit
|> attemptQuery
Route.Options searchArgs ->
let
modelPage =
case model.page of
Options x ->
Just x
_ ->
Nothing
in
Page.Options.init searchArgs modelPage
|> updateWith Options OptionsMsg model
|> avoidReinit
|> attemptQuery
Route.Flakes searchArgs ->
let
-- _ = Debug.log "changeRouteTo" "flakes"
modelPage =
case model.page of
Flakes x ->
Just x
_ ->
Nothing
in
Page.Flakes.init searchArgs modelPage
|> updateWith Flakes FlakesMsg model
|> avoidReinit
|> attemptQuery
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
-- let _ = Debug.log "main" "update"
-- in
case ( msg, model.page ) of
( ClickedLink urlRequest, _ ) ->
case urlRequest of
Browser.Internal url ->
( model
, Browser.Navigation.pushUrl model.navKey <| Url.toString url
)
Browser.External href ->
( model
, case href of
-- ignore links with no `href` attribute
"" ->
Cmd.none
_ ->
Browser.Navigation.load href
)
( ChangedUrl url, _ ) ->
changeRouteTo model url
( HomeMsg subMsg, Home subModel ) ->
Page.Home.update subMsg subModel
|> updateWith Home HomeMsg model
( PackagesMsg subMsg, Packages subModel ) ->
Page.Packages.update model.navKey subMsg subModel
|> updateWith Packages PackagesMsg model
( OptionsMsg subMsg, Options subModel ) ->
Page.Options.update model.navKey subMsg subModel
|> updateWith Options OptionsMsg model
( FlakesMsg subMsg, Flakes subModel ) ->
Page.Flakes.update model.navKey subMsg subModel
|> updateWith Flakes FlakesMsg model
( _, _ ) ->
-- Disregard messages that arrived for the wrong page.
( model, Cmd.none )
-- VIEW
view :
Model
->
{ title : String
, body : List (Html Msg)
}
view model =
let
title =
case model.page of
Packages _ ->
"NixOS Search - Packages"
Options _ ->
"NixOS Search - Options"
Flakes _ ->
"NixOS Search - Flakes (Experimental)"
_ ->
"NixOS Search"
in
{ title = title
, body =
[ div []
[ header []
[ div [ class "navbar navbar-static-top" ]
[ div [ class "navbar-inner" ]
[ div [ class "container" ]
[ a [ class "brand", href "https://nixos.org" ]
[ img [ src "https://nixos.org/logo/nix-wiki.png", class "logo" ] []
]
, div []
[ ul [ class "nav pull-left" ]
(viewNavigation model.route)
]
]
]
]
]
, div [ class "container main" ]
[ div [ id "content" ] [ viewPage model ]
, footer
[ class "container text-center" ]
[ div []
[ span [] [ text "Please help us improve the search by " ]
, a
[ href "https://github.com/NixOS/nixos-search/issues"
]
[ text "reporting issues" ]
, span [] [ text "." ]
]
, div []
[ span [] [ text " " ]
, span [] [ text "Elasticsearch instance graciously provided by " ]
, a [ href "https://bonsai.io" ] [ text "Bonsai" ]
, span [] [ text ". Thank you! " ]
]
]
]
]
]
}
viewNavigation : Route.Route -> List (Html Msg)
viewNavigation route =
let
toRoute f =
case route of
-- Preserve arguments
Route.Packages searchArgs ->
f searchArgs
Route.Options searchArgs ->
f searchArgs
Route.Flakes searchArgs ->
f searchArgs
_ ->
f <| Route.SearchArgs Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
in
li [] [ a [ href "https://nixos.org" ] [ text "Back to nixos.org" ] ]
:: List.map
(viewNavigationItem route)
[ ( toRoute Route.Packages, text "Packages" )
, ( toRoute Route.Options, text "Options" )
, ( toRoute Route.Flakes, span [] [ text "Flakes", sup [] [span [class "label label-info"][small [] [text "Experimental"]]]] )
]
viewNavigationItem :
Route.Route
-> ( Route.Route, Html Msg )
-> Html Msg
viewNavigationItem currentRoute ( route, title ) =
li
[ classList [ ( "active", currentRoute == route ) ] ]
[ a [ Route.href route ] [ title ] ]
viewPage : Model -> Html Msg
viewPage model =
case model.page of
NotFound ->
div [] [ text "Not Found" ]
Home _ ->
div [] [ text "Welcome" ]
Packages packagesModel ->
Html.map (\m -> PackagesMsg m) <| Page.Packages.view packagesModel
Options optionsModel ->
Html.map (\m -> OptionsMsg m) <| Page.Options.view optionsModel
Flakes flakesModel ->
Html.map (\m -> FlakesMsg m) <| Page.Flakes.view flakesModel
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
-- MAIN
main : Program Flags Model Msg
main =
Browser.application
{ init = init
, onUrlRequest = ClickedLink
, onUrlChange = ChangedUrl
, subscriptions = subscriptions
, update = update
, view = view
}