1{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE RankNTypes #-}
6{-# LANGUAGE RecursiveDo #-}
7{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE TypeApplications #-}
9{-# LANGUAGE TypeOperators #-}
13------------------------------------------------------------------------------
16import Control.Monad.Fix (MonadFix)
17import Data.Monoid (First(..), (<>))
18import Data.Text (Text)
19import qualified Data.Text as T
20import qualified Data.Text.Encoding as T
25import Text.Read (readMaybe)
26import Reflex.Dom.Core hiding (run)
27------------------------------------------------------------------------------
29import Servant.Reflex.Multi
30import qualified Servant.Reflex.RequestAPI as SR
37 "getunit" :> Get '[JSON] ()
38 :<|> "getint" :> Get '[JSON] Int
39 :<|> "dblint" :> Capture "anint" Int :> Get '[JSON] Int
40 :<|> "sayhi" :> QueryParam "username" Text
41 :> QueryParams "greetings" Text
44 :<|> "double" :> ReqBody '[JSON] Double
45 :> Post '[JSON] Double
46 :<|> "a" :> "b" :> QueryFlag "gusto" :> Get '[JSON] Text
47 :<|> "qna" :> ReqBody '[JSON] Question
49 :> Header "is-favorite" Bool
50 :> Post '[JSON] (Headers '[Header "anum" Int, Header "was-favorite" Bool] Answer)
51 :<|> "secret" :> BasicAuth "realm" () :> Get '[JSON] Int
55 divClass "example-tuple" runTuple
56 divClass "example-base" run
57 divClass "example-multi" runMulti
59hiWidget :: (SupportsServantReflex t m,
61 DomBuilderSpace m ~ GhcjsDomSpace,
62 PostBuild t m) => m (Dynamic t (Maybe Text),
69 inp :: Dynamic t Text <- fmap value $ divClass "name-input" $ (textInput def)
70 let checkedName = fmap (\i -> bool (Just i) Nothing (T.null i)) inp
73 text "Greetings (space-separated)"
75 greetings <- fmap (fmap T.words . value) $
76 divClass "greetings-input" $ (textInput def)
80 gusto <- fmap value $ divClass "gusto-input" $ checkbox False def
83 sayHiClicks :: Event t () <- divClass "hi-button" $ button "Say hi"
84 return (checkedName, greetings, gusto, sayHiClicks)
87readWidget :: (SupportsServantReflex t m,
89 DomBuilderSpace m ~ GhcjsDomSpace,
92 ) => m (Dynamic t (Maybe a))
93readWidget = fmap (readMaybe . T.unpack) . value <$> textInput def
95readWidget' :: (SupportsServantReflex t m,
97 DomBuilderSpace m ~ GhcjsDomSpace,
100 ) => a -> m (Dynamic t a)
101readWidget' d = fmap (fromMaybe d . readMaybe . T.unpack) . value <$> textInput def
103basicAuthWidget :: (SupportsServantReflex t m,
105 DomBuilderSpace m ~ GhcjsDomSpace,
107 ) => m (Dynamic t BasicAuthData)
109 un <- fmap value $ text "Username" >> textArea def
110 pw <- fmap value $ text "Unhidden PW" >> textArea def
111 return $ BasicAuthData <$> fmap T.encodeUtf8 un <*> fmap T.encodeUtf8 pw
114runTuple :: forall t m. (SupportsServantReflex t m,
116 DomBuilderSpace m ~ GhcjsDomSpace,
119 MonadHold t m) => m ()
121 el "h1" (text "Tuple API")
124 let (getUnit :<|> getInt :<|> dblInt :<|> sayHi :<|> dbl :<|> ab :<|> question :<|> secret :<|> _) =
125 SR.clientR (Proxy @API) (Proxy @m) (Proxy @t) (Proxy @[]) url
127 u <- divClass "demo-group" $ do
128 unitBtn <- button "get unit"
129 getUnit (fmap (:[]) unitBtn)
131 (giR, diR) <- divClass "demo-group" $ do
133 getIntBtn <- button "get int"
134 dblIntBtn <- button "double int"
135 gi <- getInt (fmap (:[]) getIntBtn)
136 di <- dblInt (fmap (:[]) (fmapMaybe id $ tagPromptlyDyn int dblIntBtn))
139 hiResp <- divClass "demo-group" $ do
140 (nm, greets, gusto, hiClicks) <- hiWidget
141 let hiReqs = tagPromptlyDyn ((,,) <$> nm <*> greets <*> gusto) hiClicks
142 sayHi (fmap (:[]) hiReqs)
144 dblResp <- divClass "demo-group" $ do
145 dblInput <- readWidget
146 dblBtn <- button "double double"
147 dbl $ (fmap (:[])) $ fmapMaybe id $ tagPromptlyDyn dblInput dblBtn
149 twoPathResp <- divClass "demo-group" $ do
150 twoPathInp <- text "Two Path" >> checkbox False def
151 ab (fmap (:[]) $ updated (value twoPathInp))
153 qnaR <- divClass "demo-group" $ do
154 q <- fmap Question . value <$> textInput def
156 f <- readWidget' False
157 question (fmap (:[]) (updated $ (,,) <$> q <*> i <*> f))
159 secretR <- divClass "demo-group" $ do
160 ba <- basicAuthWidget
161 sBtn <- button "Get secret"
162 secret (fmap pure $ tagPromptlyDyn ba sBtn)
164 divClass "demo-group" $ do
165 el "div" $ text "responses"
166 dynText =<< holdDyn "No responses yet"
167 (leftmost [ tshow <$> u , tshow <$> giR
169 , tshow <$> hiResp , tshow <$> dblResp
170 , tshow <$> twoPathResp
171 , T.concat . fmap (either tshow (\x -> tshow (tshow (getHeaders x), tshow (getResponse x)))) <$> qnaR
178runMulti :: forall t m. (SupportsServantReflex t m,
180 DomBuilderSpace m ~ GhcjsDomSpace,
183 MonadHold t m) => m ()
187 let (_ :<|> _ :<|> _ :<|> sayHi :<|> dbl :<|> _ :<|> _ :<|> _ :<|> _ ) =
188 clientA api (Proxy :: Proxy m) (Proxy :: Proxy []) (Proxy :: Proxy Int) url
190 num :: Dynamic t (Either Text Double) <- fmap (note "No read" . readMaybe . T.unpack) . value <$> textInput def
191 num2 :: Dynamic t (Either Text Double) <- fmap (note "No read" . readMaybe . T.unpack) . value <$> textInput def
193 b <- button "Run dbl multi"
194 reqCount :: Dynamic t Int <- count b
195 r <- dbl ((\x y -> [x,y]) <$> num <*> num2) (tag (current reqCount) b)
196 dynText =<< holdDyn "Waiting" (T.pack . show . fmap reqSuccess <$> r)
198 lastInd <- holdDyn [] $ fmap reqTag <$> r
201 divClass "demo-group" $ do
202 nms <- fmap (fmap T.words . value) $ divClass "" $ do
205 grts <- fmap (fmap T.words . value) $ divClass "" $ do
208 gust <- fmap (value) $ divClass "gusto-input" $ checkbox False def
210 r' <- sayHi (fmap QParamSome <$> nms) (fmap (:[]) $ grts)
211 (constDyn [True, False]) (1 <$ b)
213 dynText =<< holdDyn "Waiting" (T.pack . show . catMaybes .
214 fmap reqSuccess <$> r')
219run :: forall t m. (SupportsServantReflex t m,
221 DomBuilderSpace m ~ GhcjsDomSpace,
224 MonadHold t m) => m ()
227 reqCount <- count $ leftmost
228 [() <$ unitBtn, () <$ intBtn, () <$ sayHiClicks, () <$ dblBtn, () <$ mpGo]
229 -- Allow user to choose the url target for the request
230 -- (alternatively we could just `let url = constDyn (BasePath "/")`)
233 dynText $ showBaseUrl <$> url
237 -- Name the computed API client functions
238 let tweakRequest = ClientOptions $ \r -> do
239 return $ r & withCredentials .~ True
240 let (getUnit :<|> getInt :<|> dblInt :<|> sayhi :<|> dbl
241 :<|> multi :<|> qna :<|> secret :<|> doRaw) =
242 clientWithOpts api (Proxy :: Proxy m) (Proxy :: Proxy Int) url tweakRequest
244 c2 = client (Proxy :: Proxy ComprehensiveAPI) (Proxy :: Proxy m) (Proxy :: Proxy ()) url -- Just make sure this compiles for now
246 (unitBtn, intBtn) <- elClass "div" "demo-group" $ do
247 unitBtn <- divClass "unit-button" $ button "Get unit"
248 intBtn <- divClass "int-button" $ button "Get int"
250 unitResponse <- getUnit $ tag (current reqCount) unitBtn
251 intResponse :: Event t (ReqResult Int Int) <- getInt $ tag (current reqCount) intBtn
253 score <- foldDyn (+) 0 (fmapMaybe reqSuccess (intResponse))
255 r <- holdDyn "Waiting" $ fmap showXhrResponse $
256 leftmost [fmapMaybe response (unitResponse)
257 ,fmapMaybe response (intResponse)
259 divClass "unit-int-response" $ el "p" $ dynText r >> el "br" (return ()) >> text "Total: " >> display score
260 return (unitBtn, intBtn)
262 sayHiClicks <- elClass "div" "demo-group" $ do
266 inp :: Dynamic t Text <- fmap value $ divClass "name-input" $ (textInput def)
267 let checkedName = fmap (\i -> bool (QParamSome i) (QParamInvalid "Need a name") (T.null i)) inp
270 text "Greetings (space-separated)"
272 greetings <- fmap (fmap T.words . value) $
273 divClass "greetings-input" $ (textInput def)
277 gusto <- fmap value $ divClass "gusto-input" $ checkbox False def
280 sayHiClicks :: Event t () <- divClass "hi-button" $ button "Say hi"
281 let triggers = leftmost [sayHiClicks, () <$ updated inp]
283 resp <- sayhi checkedName greetings gusto (tag (current reqCount) triggers)
284 divClass "greeting-response" $ dynText =<<
285 holdDyn "No hi yet" (leftmost [ fmapMaybe reqSuccess (resp)
286 , fmapMaybe reqFailure (resp)])
289 dblBtn <- elClass "div" "demo-group" $ do
290 text "A Double to double"
292 dblinp <- fmap value $ divClass "double-input" $ textInput def
293 (dblBtn) <- divClass "double-button" $ button "Double it"
294 dblResp <- dbl (fmap (note "read failure" . readMaybe . T.unpack) $
295 dblinp) (tag (current reqCount) dblBtn)
296 divClass "double-errors" $ dynText =<<
297 holdDyn "(no errors)" (fmapMaybe reqFailure (dblResp))
299 divClass "double-result" $ el "p" $ dynText =<<
300 holdDyn "No number yet" (fmap tshow $
301 fmapMaybe reqSuccess (dblResp))
304 mpGo <- elClass "div" "demo-group" $ do
305 text "Multi-part path"
306 b <- value <$> checkbox False def
307 mpGo <- button "Test"
308 multiResp <- multi b (tag (current reqCount) mpGo)
309 dynText =<< holdDyn "No res yet" (fmap tshow $
310 fmapMaybe reqSuccess $
318 elClass "div" "demo-group" $ do
319 text "JSON Unicode encoding test"
320 txt <- value <$> textInput def
321 ev <- fmap (1 <$) $ button "Question"
322 let dQ = Right . Question <$> traceDyn "will send: " txt
323 -- rr <- qna dQ ev _ _
326 -- dynText =<< holdDyn "No Answer" (unAnswer <$> fmapMaybe reqSuccess rr)
328 divClass "demo-group" $ do
329 un <- fmap value $ text "Username" >> textArea def
330 pw <- fmap value $ text "Unhidden PW" >> textArea def
331 let ba :: Dynamic t BasicAuthData = BasicAuthData
332 <$> fmap T.encodeUtf8 un
333 <*> fmap T.encodeUtf8 pw
334 b <- button "Get secret"
335 r <- secret (Just <$> ba) (0 <$ b)
336 res <- holdDyn Nothing (reqSuccess <$> r)
340showXhrResponse :: XhrResponse -> Text
341showXhrResponse (XhrResponse stat stattxt rbmay rtmay respHeaders) =
342 T.unlines ["stat: " <> tshow stat
343 ,"stattxt: " <> tshow stattxt
344 ,"resp: " <> maybe "" showRB rbmay
345 ,"rtext: " <> tshow rtmay
346 ,"rHeaders: " <> tshow respHeaders]
348tshow :: Show a => a -> Text
351showRB :: XhrResponseBody -> Text
352showRB (XhrResponseBody_Default t) = tshow t
353showRB (XhrResponseBody_Text t) = tshow t
354showRB (XhrResponseBody_Blob t) = "<Blob>"
355showRB (XhrResponseBody_ArrayBuffer t) = tshow t
357note :: e -> Maybe a -> Either e a
358note e = maybe (Left e) Right
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list