Skip to content

Commit 5d2554f

Browse files
authored
Merge PR #65 (first part of url-routing feature)
2 parents b929efe + 750d5cc commit 5d2554f

File tree

5 files changed

+337
-55
lines changed

5 files changed

+337
-55
lines changed

src-ui.v3/matrix-ui.cabal

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ executable matrix-ui
2828
, aeson ^>= 1.4.3
2929
, base ^>= 4.9.1.0 || ^>= 4.11.1.0
3030
, containers ^>= 0.5.7.1
31-
--, jsaddle ^>= 0.9.0.0
31+
, jsaddle ^>= 0.9.0.0
32+
, ghcjs-dom ^>= 0.9.1.0
3233
, ghcjs-base ^>= 0.2.0.0
3334
, lens ^>= 4.17.1
3435
, text ^>= 1.2.2.2
@@ -37,6 +38,10 @@ executable matrix-ui
3738
, vector ^>= 0.12.0.1
3839
, uuid-types ^>= 1.0.3
3940
, servant ^>= 0.16
41+
, primitive ^>= 0.6.4.0
42+
, monad-control ^>= 1.0.2.3
43+
, mtl ^>= 2.2.2
44+
, ref-tf ^>= 0.4
4045

4146
-- unreleased Git snapshots of deps; see cabal.project for provenance
4247
-- , ghcjs-dom-jsffi

src-ui.v3/src/API.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ module API
7171
import PkgId
7272

7373
import Control.Monad (fail)
74-
import Data.Aeson (FromJSON, ToJSON, decode)
74+
import Data.Aeson (FromJSON, decode)
7575
import qualified Data.Aeson as J
7676
import qualified Data.Aeson.Types as J
7777
import qualified Data.Char as C

src-ui.v3/src/Main.hs

Lines changed: 41 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ import qualified Data.List as List
3131
import qualified Data.Map.Strict as Map
3232
import qualified Data.Maybe as M
3333
import Data.Monoid (Endo (Endo), appEndo)
34+
import qualified Data.List.NonEmpty as NE
35+
import Data.List.NonEmpty (NonEmpty)
3436
import Data.Proxy
3537
import qualified Data.Set as Set
38+
import Data.Set (Set)
3639
import qualified Data.Text as T
3740
import Data.Text (Text)
3841
import Data.Time (UTCTime)
@@ -45,6 +48,8 @@ import Data.Vector (Vector)
4548
import qualified Data.Vector as V
4649
import qualified Data.Version as Ver
4750
import GHC.Generics (Rep)
51+
import qualified GHCJS.DOM.Types as DOM
52+
import Language.Javascript.JSaddle (jsNull)
4853
import Network.URI
4954
--import Reflex.Dom
5055
import Reflex.Dom.Core
@@ -61,6 +66,7 @@ import Servant.Reflex
6166

6267
import API
6368
import PkgId
69+
import Router
6470

6571

6672
main :: IO ()
@@ -100,13 +106,18 @@ utc2unix :: UTCTime -> Int
100106
utc2unix x = ceiling (realToFrac (utcTimeToPOSIXSeconds x) :: Double)
101107

102108
bodyElement4 :: forall t m . (SupportsServantReflex t m, MonadFix m, MonadIO m, MonadHold t m, PostBuild t m, DomBuilder t m, Adjustable t m, DomBuilderSpace m ~ GhcjsDomSpace) => m ()
103-
bodyElement4 = mdo
104-
dynLoc <- browserHistoryWith getLocationUri
105-
let dynFrag = decodeFrag . T.pack . uriFragment <$> dynLoc
106-
109+
bodyElement4 = do
110+
--dynLoc <- browserHistoryWith getLocationUri
111+
--let dynFrag = decodeFrag . T.pack . uriFragment <$> dynLoc
112+
_ <- runRouteViewT app
113+
pure ()
107114
-- ticker1 <- tickLossy 1 =<< liftIO getCurrentTime
108115
-- ticker1cnt <- count ticker1
109116

117+
app :: forall t m. (SetRoute t FragRoute m, SupportsServantReflex t m, MonadFix m, MonadIO m, MonadHold t m, PostBuild t m, DomBuilder t m, Adjustable t m, DomBuilderSpace m ~ GhcjsDomSpace)
118+
=> Dynamic t FragRoute
119+
-> m ()
120+
app dynFrag = do
110121
-- top-level PB event
111122
evPB0 <- getPostBuild
112123

@@ -129,11 +140,11 @@ bodyElement4 = mdo
129140
-- pseudo navbar
130141
el "nav" $ do
131142
text "[ "
132-
elAttr "a" ("href" =: "#/") $ text "HOME"
143+
routeLink False "#/" (text "HOME")
133144
text " | "
134-
elAttr "a" ("href" =: "#/queue") $ text "Build Queue"
145+
routeLink False "#/queue" (text "Build Queue")
135146
text " | "
136-
elAttr "a" ("href" =: "#/packages") $ text "Packages"
147+
routeLink False "#/packages" (text "Packages")
137148
text " ]"
138149
text " (current index-state: "
139150
dynText (pkgIdxTsToText <$> dynIdxStLast)
@@ -319,21 +330,23 @@ bodyElement4 = mdo
319330
let dynPkgTags = pkgTagList <$> dynTagPkgs
320331
packagesPageWidget dynPackages0 dynTags dynPkgTags
321332

322-
RoutePackage pn -> pure $ do
333+
RoutePackage (pn, idxSt) -> pure $ do
334+
323335
el "h2" $ text (pkgNToText pn)
324336
el "p" $ el "em" $ elAttr "a" ("href" =: ("https://hackage.haskell.org/package/" <> pkgNToText pn)) $
325337
do text "(view on Hackage)"
326338

327339
evPB <- getPostBuild
328-
340+
let
341+
dynIdxStLast' = fmap (\x -> M.fromMaybe x idxSt) dynIdxStLast
329342
-- single-shot requests
330343
evReports <- getPackageReports (constDyn $ Right pn) evPB
331344
dynReports <- holdDyn mempty evReports
332345

333346
evInfo <- getInfo evPB
334347
dynInfo <- holdDyn (ControllerInfo mempty) evInfo
335348

336-
evHist <- getPackageHistory (constDyn $ Right pn) (leftmost [updated dynIdxStLast $> (), evPB])
349+
evHist <- getPackageHistory (constDyn $ Right pn) (leftmost [updated dynIdxStLast' $> (), evPB])
337350
dynHist <- holdDyn mempty evHist
338351

339352
evPkgTags <- getPackageTags (constDyn $ Right pn) evPB
@@ -351,11 +364,13 @@ bodyElement4 = mdo
351364
text " for latest index-state "
352365
dynText (pkgIdxTsToText <$> dynIdxStLast)
353366

354-
putQueue (constDyn $ Right pn) (Right <$> dynIdxStLast) (constDyn $ Right (QEntryUpd (-1))) evQButton
367+
putQueue (constDyn $ Right pn) (Right <$> dynIdxStLast') (constDyn $ Right (QEntryUpd (-1))) evQButton
355368

356369

357370
let xs = Map.fromList . fmap (\x -> (x, pkgIdxTsToText x)) . Set.toList <$> dynReports
358-
x0 = (\s -> if Set.null s then PkgIdxTs 0 else Set.findMax s) <$> dynReports
371+
x0 = (\s -> if Set.null s then PkgIdxTs 0
372+
else (findInitialDropDown idxSt s)) <$> dynReports
373+
359374

360375
let ddCfg = DropdownConfig (updated x0) (constDyn mempty)
361376

@@ -365,7 +380,8 @@ bodyElement4 = mdo
365380
ddReports <- el "p" $ do
366381
evQButton <- button "Queue a build"
367382
text " for the index-state "
368-
tmp <- dropdown (PkgIdxTs 0) xs ddCfg
383+
uniqReport <- holdUniqDyn dynReports
384+
tmp <- routePkgIdxTs pn (PkgIdxTs 0) uniqReport xs ddCfg
369385
text " shown below"
370386

371387
_ <- putQueue (constDyn $ Right pn) (Right <$> _dropdown_value tmp) (constDyn $ Right (QEntryUpd (-1))) evQButton
@@ -396,12 +412,14 @@ bodyElement4 = mdo
396412
pure $ tagPromptlyDyn tVal addResult
397413
pure ()
398414

399-
let evReports' = updated (_dropdown_value ddReports)
400-
dynIdxSt = ddReports ^. dropdown_value
401-
415+
let dynIdxSt = ddReports ^. dropdown_value
416+
evReports' = updated (_dropdown_value ddReports)
417+
--evIdxChange = updated dynIdxSt --ddReports ^. dropdown_change
418+
419+
--display $ holdDyn (PkgIdxTs 0) evIdxChange
402420
evRepSum <- getPackageReportSummary (constDyn $ Right pn) (Right <$> dynIdxSt) (leftmost [evReports' $> (), ticker4 $> ()])
403421
dynRepSum <- holdUniqDyn =<< holdDyn (PkgIdxTsReport pn (PkgIdxTs 0) [] mempty) evRepSum
404-
422+
405423
el "hr" blank
406424

407425
evCellClick <- reportTableWidget dynRepSum dynQRows dynWorkers dynHist dynInfo
@@ -454,35 +472,6 @@ bodyElement4 = mdo
454472
delResult <- deleteTags (constDyn $ Right (TagN tId)) (constDyn $ Right pn) rmTag
455473
pure $ (TagN tId) <$ delResult
456474

457-
data FragRoute = RouteHome
458-
| RouteQueue
459-
| RoutePackages
460-
| RoutePackage PkgN
461-
| RouteUser UserName
462-
| RouteUnknown T.Text
463-
deriving (Eq)
464-
465-
decodeFrag :: T.Text -> FragRoute
466-
decodeFrag frag = case frag of
467-
"" -> RouteHome
468-
"#" -> RouteHome
469-
"#/" -> RouteHome
470-
"#/queue" -> RouteQueue
471-
"#/packages" -> RoutePackages
472-
473-
_ | Just sfx <- T.stripPrefix "#/package/" frag
474-
, not (T.null frag)
475-
, Just pn <- pkgNFromText sfx
476-
-> RoutePackage pn
477-
478-
| Just sfx <- T.stripPrefix "#/user/" frag
479-
, not (T.null frag)
480-
, T.all (\c -> C.isAsciiLower c || C.isAsciiUpper c || C.isDigit c || c == '_') sfx
481-
-> RouteUser sfx
482-
483-
| otherwise -> RouteUnknown frag
484-
485-
486475
-- | Renders alpha-tabbed package index
487476
packagesPageWidget :: forall t m. (MonadFix m, MonadHold t m, PostBuild t m, DomBuilder t m)
488477
=> Dynamic t (Vector PkgN)
@@ -533,7 +522,6 @@ packagesPageWidget dynPackages dynTags dynPkgTags = do
533522
case Map.lookup pn dpt of
534523
Just tags -> forM tags $ \(tag0) -> elAttr "a" (("class" =: "tag-item") <> ("data-tag-name" =: (tagNToText tag0))) $ text (tagNToText tag0)
535524
Nothing -> pure ([])
536-
537525
pure ()
538526
where
539527
evalPkgFilter '*' = V.takeWhile (\(PkgN t) -> T.head t < 'A')
@@ -714,6 +702,12 @@ applyLR (L:xs) (l:ls) rs = l : applyLR xs ls rs
714702
applyLR (R:xs) ls (r:rs) = r : applyLR xs ls rs
715703
applyLR _ _ _ = error "applyLR"
716704

705+
findInitialDropDown :: Maybe PkgIdxTs -> Set PkgIdxTs -> PkgIdxTs
706+
findInitialDropDown (Just idx) pkgSet = if Set.member idx pkgSet
707+
then Set.foldr (\a b -> if a == b then a else b) idx pkgSet
708+
else Set.findMax pkgSet
709+
findInitialDropDown Nothing pkgSet = Set.findMax pkgSet
710+
717711
toggleTagSet :: TagN -> Set.Set TagN -> Set.Set TagN
718712
toggleTagSet tn st = if Set.member tn st then Set.delete tn st else Set.insert tn st
719713

src-ui.v3/src/PkgId.hs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+
{-# LANGUAGE OverloadedStrings #-}
23

34
-- |
45
-- Copyright: © 2018 Herbert Valerio Riedel
@@ -28,7 +29,7 @@ module PkgId
2829
, compilerIdFromText
2930
, compilerIdToText
3031

31-
, PkgIdxTs(..), pkgIdxTsToText -- , unPkgIdxTs
32+
, PkgIdxTs(..), pkgIdxTsToText, idxTsToText -- , unPkgIdxTs
3233
, PkgRev
3334

3435
, UserName
@@ -49,6 +50,7 @@ import qualified Data.Version as Ver
4950
import Servant.API (FromHttpApiData (..),
5051
ToHttpApiData (..))
5152
import Text.ParserCombinators.ReadP (readP_to_S)
53+
import qualified Text.Read as R
5254

5355
type UserName = Text
5456
type PkgRev = Word
@@ -64,16 +66,25 @@ instance Show PkgN where
6466
| otherwise = (("PkgN "<>show x) <>)
6567

6668
-- NB: this assumes the Hackage ascii-only policy
67-
pkgNFromText :: Text -> Maybe PkgN
69+
pkgNFromText :: Text -> (Maybe PkgN, Maybe PkgIdxTs)
6870
pkgNFromText t0
69-
| isValid t0 = Just (PkgN t0)
70-
| otherwise = Nothing
71+
| Just (p0,ts0) <- parsingUrlText t0
72+
, Just intTs <- R.readMaybe (T.unpack ts0) :: Maybe Int
73+
, isValid p0 = (Just (PkgN p0), Just (PkgIdxTs intTs))--R.readMaybe (T.unpack ts0) :: Maybe PkgIdxTs)
74+
| otherwise = (Just (PkgN t0), Nothing)
7175
where
7276
isValid t
7377
| T.null t = False
7478
| not (T.all (\c -> C.isAsciiLower c || C.isAsciiUpper c || C.isDigit c || c == '-') t) = False
7579
| otherwise = and [ T.any C.isAlpha x | x <- T.split (=='-') t ]
7680

81+
parsingUrlText :: Text -> Maybe (Text, Text)
82+
parsingUrlText t0 = case T.any (=='@') t0 of
83+
True -> Just (T.takeWhile (/='@') t0, T.takeWhileEnd (/='@') t0)
84+
False -> Just (t0, T.empty)
85+
-- | Just prefix <- T.stripSuffix "@" t0
86+
-- , Just suffix <- T.stripPrefix "@" t0 = Just (prefix,suffix)
87+
-- | otherwise = Just (t0,T.empty)
7788
----------------------------------------------------------------------------
7889

7990
newtype CompilerID = CompilerID {- ghc/ghcjs/ghcvm -} Ver
@@ -111,11 +122,14 @@ instance FromHttpApiData CompilerID where
111122
----------------------------------------------------------------------------
112123

113124
newtype PkgIdxTs = PkgIdxTs Int
114-
deriving (Show,Ord,Eq,FromJSON,ToJSON,FromHttpApiData,ToHttpApiData)
125+
deriving (Show,Ord,Eq,FromJSON,ToJSON,FromHttpApiData,ToHttpApiData,Read)
115126

116127
pkgIdxTsToText :: PkgIdxTs -> Text
117128
pkgIdxTsToText (PkgIdxTs t) = T.pack $ formatTime defaultTimeLocale "%Y-%m-%dT%TZ" (posixSecondsToUTCTime (fromIntegral t :: POSIXTime))
118129

130+
idxTsToText :: PkgIdxTs -> Text
131+
idxTsToText (PkgIdxTs t) = (T.pack . show) t
132+
119133
----------------------------------------------------------------------------
120134

121135
newtype Ver = Ver [Int]
@@ -157,6 +171,11 @@ data Matches = Matches
157171
deriving (Eq,Ord)
158172

159173
matchesEmpty :: Matches
160-
matchesEmpty = Matches { matchesInput = T.empty, matchesExact = Map.empty, matchesInfix = Map.empty}
174+
matchesEmpty = Matches { matchesInput = T.empty, matchesExact = Map.empty, matchesInfix = Map.empty}
175+
176+
---------------------------------
177+
178+
179+
161180

162181

0 commit comments

Comments
 (0)