@@ -1,20 +1,23 @@
1−module Pages.Pursuit
1+module Pages.Packages
22 (
33 -- getServerSideProps
44 -- ,
5− mkPursuit
5+ mkPackages
66 ) where
77
88import Prelude
99
1010import Components.Page as Page
1111import Control.Monad.Except (except)
12+import Data.Array as Array
1213import Data.Either (Either(..))
14+import Data.Eq ((/=))
1315import Data.Eq.Generic (genericEq)
1416import Data.Generic.Rep (class Generic)
15−import Data.Maybe (Maybe)
17+import Data.Maybe (Maybe, maybe)
1618import Data.Show.Generic (genericShow)
1719import Data.Tuple.Nested ((/\))
20+import Debug (spy)
1821import Effect.Aff (Aff)
1922import Effect.Class (liftEffect)
2023import Effect.Class.Console (log)
@@ -22,15 +25,20 @@ import Effect.Exception (Error)
2225import Fetch (Method(..), fetch)
2326import Fetch.Yoga.Json (fromJSON)
2427import Foreign (ForeignError(..))
28+import JSURI (encodeURIComponent)
2529import Network.RemoteData (RemoteData)
2630import Network.RemoteData as RD
2731import Next.Router (query, useRouter)
2832import Next.Router as Router
2933import NextUI.NextUI as NextUI
34+import React.Basic.DOM.Events (targetValue)
3035import React.Basic.DOM.Simplified.Generated as R
31−import React.Basic.Hooks (type (/\), mkReducer, useEffect)
36+import React.Basic.Events (handler)
37+import React.Basic.Hooks (type (/\), JSX, mkReducer, useEffect)
3238import React.Basic.Hooks as React
3339import React.Hooks.UseRemoteData (useRemoteDataDispatch)
40+import React.Icons (icon_)
41+import React.Icons.Tb (tbSearch)
3442import React.Util (el)
3543import Yoga.JSON (class ReadForeign)
3644import Yoga.JSON as YogaJson
@@ -48,11 +56,14 @@ type DeclarationR =
4856
4957type PackageR = (deprecated :: Boolean)
5058
51−data SearchInfo = Declaration { | DeclarationR } | Package { | PackageR }
59+type ModuleR = (module :: String )
60+
61+data SearchInfo = Declaration { | DeclarationR } | Package { | PackageR } | Module { | ModuleR }
5262
5363derive instance Generic SearchInfo _
5464instance Eq SearchInfo where
5565 eq = genericEq
66+
5667instance Show SearchInfo where
5768 show = genericShow
5869
@@ -62,6 +73,7 @@ instance ReadForeign SearchInfo where
6273 case t of
6374 "declaration" -> YogaJson.readImpl f <#> Declaration
6475 "package" -> YogaJson.readImpl f <#> Package
76+ "module" -> YogaJson.readImpl f <#> Module
6577 other -> except $ Left (pure $ ForeignError $ "Invalid search info type " <> other)
6678
6779type SearchResult =
@@ -78,25 +90,29 @@ data SearchError = SearchError
7890derive instance Generic SearchError _
7991instance Eq SearchError where
8092 eq = genericEq
93+
8194instance Show SearchError where
8295 show SearchError = "Failed to get search results"
8396
84−data Action = UpdateSearchResult (RemoteData SearchError (Array SearchResult))
97+data Action = EditSearchField String | UpdateSearchResult (RemoteData SearchError (Array SearchResult))
8598
8699type State =
87100 { searchResult ∷ RemoteData SearchError (Array SearchResult)
101+ , searchInput :: String
88102 }
89103
90104defaultState :: State
91−defaultState = { searchResult: RD.NotAsked }
105+defaultState = { searchResult: RD.NotAsked, searchInput: "" }
92106
93107reduce ∷ State → Action → State
94108reduce s (UpdateSearchResult searchResult) =
95109 s { searchResult = searchResult }
110+reduce s (EditSearchField "") = s { searchInput = "", searchResult = RD.NotAsked }
111+reduce s (EditSearchField searchInput) = s { searchInput = searchInput }
96112
97−getSearchResult :: {} -> Aff (Either SearchError (Array SearchResult))
98−getSearchResult {} = do
99− let url = "http://localhost:3000/search?q=barlow"
113+getSearchResult :: { searchQuery :: String } -> Aff (Either SearchError (Array SearchResult))
114+getSearchResult { searchQuery } = do
115+ let url = "http://localhost:3000/search?q=" <> (maybe searchQuery identity $ encodeURIComponent searchQuery)
100116 { status, text, json } ← fetch url
101117 { method: GET
102118 , headers: { "Accept": "application/json" }
@@ -110,8 +126,8 @@ getSearchResult {} = do
110126 log $ "Got invalid response [" <> (show statusCode) <> "]:\n" <> body
111127 pure $ Left SearchError
112128
113−mkPursuit :: Page.Component Props
114−mkPursuit = do
129+mkPackages :: Page.Component Props
130+mkPackages = do
115131
116132 reducer ← mkReducer reduce # liftEffect
117133 Page.component "Pursuit" \env props -> React.do
@@ -126,17 +142,49 @@ mkPursuit = do
126142
127143 remoteSearchResult ← useRemoteDataDispatch (dispatch <<< UpdateSearchResult) getSearchResult
128144
129− useEffect unit do
130− remoteSearchResult.load {}
145+ useEffect state.searchInput do
146+ if state.searchInput /= "" then remoteSearchResult.load { searchQuery: state.searchInput } else pure unit
131147 mempty
132148 -- http://localhost:3000/search\?q
133149 pure $ el NextUI.container {} $
134− [ el NextUI.row {}
135− [ R.h1' "Pursuit"
136− , el NextUI.text {} $ show state.searchResult
137− ]
150+ [ el NextUI.row {} $ R.h1' "Packages"
151+ , el NextUI.row {} $ el NextUI.input
152+ { initialValue: spy "searchInput" state.searchInput
153+ , clearable: true
154+ , bordered: true
155+ , contentLeft: icon_ tbSearch
156+ , placeholder: "Search"
157+ , size: "xl"
158+ , type: "search"
159+ , width: "600px"
160+ , onChange: handler targetValue (maybe (pure unit) (EditSearchField >>> dispatch))
161+ }
162+ React.empty
163+ , el NextUI.row {} $ el NextUI.container {} $
164+ renderSearchResults state.searchResult
165+
138166 ]
139167
168+ where
169+ renderSearchResult :: SearchResult -> JSX
170+ renderSearchResult { info: Declaration { "module": m, title }, package } = el NextUI.row {} [
171+ el NextUI.col {} m
172+ , el NextUI.col {} title
173+ ]
174+ renderSearchResult { info: Package { deprecated }, package } = el NextUI.row {} [
175+ el NextUI.col {} package
176+ , el NextUI.col {} $ if deprecated then "deprecated" else ""
177+ ]
178+ renderSearchResult { info: Module { "module": m }, package } = el NextUI.row {} [
179+ el NextUI.col {} package
180+ , el NextUI.col {} $ m
181+ ]
182+
183+ renderSearchResults :: RD.RemoteData SearchError (Array SearchResult) -> Array JSX
184+ renderSearchResults (RD.Success searchResults) = searchResults <#> renderSearchResult
185+ renderSearchResults (RD.Failure err) = Array.singleton $ el NextUI.row {} $ el NextUI.text {} "Uh oh"
186+ renderSearchResults RD.Loading = Array.singleton $ el NextUI.row {} $ el NextUI.loading {} React.empty
187+ renderSearchResults RD.NotAsked = Array.singleton $ el NextUI.row {} $ el NextUI.text {} ""
140188-- getServerSideProps :: forall ctx. EffectFn1 ctx (Promise { props :: Props })
141189-- getServerSideProps =
142190-- mkEffectFn1 $ fromAff