on master help sign in
Merge remote-tracking branch 'origin/develop' into add-obelisk-profiling-feature
ab48708Matthew Bauer
.github/pull_request_template.md+1−1
@@ -1,4 +1,4 @@
1−[Provide a clear overview of your changes.]
1+<!-- Provide a clear overview of your changes. -->
2
2
3I have:
3I have:
4
4
ChangeLog.md+6
@@ -4,10 +4,16 @@ This project's release branch is `master`. This log is written from the perspect
4
4
5## Unreleased
5## Unreleased
6
6
7+* Fix a bug in `Obelisk.Route.Frontend` where `routeLink`, `routeLinkDynAttr`, and `dynRouteLink` would not behave exactly like `<a href="...">` when run by JavaScript. These functions now scroll to the top of the page when the link is clicked. ([#540](https://github.com/obsidiansystems/obelisk/pull/540))
8+
9+## v0.5.0.0 - 2020-02-07
10+
7* Add `Obelisk.Route.(?/)`, a convenience function for constructing routes nested in `Maybe`. ([#457](https://github.com/obsidiansystems/obelisk/pull/457))
11* Add `Obelisk.Route.(?/)`, a convenience function for constructing routes nested in `Maybe`. ([#457](https://github.com/obsidiansystems/obelisk/pull/457))
8* Add local unpacked packages to the `ob run`, `ob watch`, and `ob repl` sessions. Any `.cabal` or hpack package inside the current obelisk project will be loaded into the session. For `ob run`/`ob watch` this means the session will automatically reload when you save a source file in any of those packages. For `ob repl` it means that `:r` will reload changes to any of those packages. There are some edge cases where this integration is still rough. Report any issues you encounter. ([#489](https://github.com/obsidiansystems/obelisk/pull/489))
12* Add local unpacked packages to the `ob run`, `ob watch`, and `ob repl` sessions. Any `.cabal` or hpack package inside the current obelisk project will be loaded into the session. For `ob run`/`ob watch` this means the session will automatically reload when you save a source file in any of those packages. For `ob repl` it means that `:r` will reload changes to any of those packages. There are some edge cases where this integration is still rough. Report any issues you encounter. ([#489](https://github.com/obsidiansystems/obelisk/pull/489))
9* Add `ob hoogle` command to start a local [Hoogle](https://hoogle.haskell.org/) server for the project. ([#628](https://github.com/obsidiansystems/obelisk/pull/628))
13* Add `ob hoogle` command to start a local [Hoogle](https://hoogle.haskell.org/) server for the project. ([#628](https://github.com/obsidiansystems/obelisk/pull/628))
10* `ob thunk pack` will now attempt to automatically detect if the thunk is a private or public repo. To avoid this detection, specify `--private` or `--public` manually. ([#607](https://github.com/obsidiansystems/obelisk/pull/607))
14* `ob thunk pack` will now attempt to automatically detect if the thunk is a private or public repo. To avoid this detection, specify `--private` or `--public` manually. ([#607](https://github.com/obsidiansystems/obelisk/pull/607))
15+* Fix a bug in the plain git thunk loader for thunks marked as 'private' when the revision is not in the default branch. ([#648](https://github.com/obsidiansystems/obelisk/pull/648))
16+* Improve handling of runtime nix dependencies. This may fix some issues encountered particularly by users on systems other than NixOS.
11* Add `ob profile` command to run Obelisk project with profiling. `ob
17* Add `ob profile` command to run Obelisk project with profiling. `ob
12profile` works like ob run, but instead of using ghci, it builds an
18profile` works like ob run, but instead of using ghci, it builds an
13executable that is built with profiling enabled.
19executable that is built with profiling enabled.
LICENSEnew file+30
@@ -0,0 +1,30 @@
1+Copyright (c) 2020 Obsidian Systems
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions are met:
7+
8+ * Redistributions of source code must retain the above copyright
9+ notice, this list of conditions and the following disclaimer.
10+
11+ * Redistributions in binary form must reproduce the above
12+ copyright notice, this list of conditions and the following
13+ disclaimer in the documentation and/or other materials provided
14+ with the distribution.
15+
16+ * Neither the name of Elliot Cameron nor the names of other
17+ contributors may be used to endorse or promote products derived
18+ from this software without specific prior written permission.
19+
20+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README.md+4
@@ -1,5 +1,9 @@
1# Obelisk
1# Obelisk
2
2
3+[![Haskell Programming Language](https://img.shields.io/badge/language-Haskell-blue.svg)](http://www.haskell.org)
4+[![BSD3 License](http://img.shields.io/badge/license-BSD3-brightgreen.svg)](https://tldrlegal.com/license/bsd-3-clause-license-%28revised%29)
5+
6+
3Obelisk provides an easy way to develop and deploy your [Reflex](https://github.com/reflex-frp/reflex) project as web apps and as mobile apps.
7Obelisk provides an easy way to develop and deploy your [Reflex](https://github.com/reflex-frp/reflex) project as web apps and as mobile apps.
4
8
5- [Installing Obelisk](#installing-obelisk)
9- [Installing Obelisk](#installing-obelisk)
lib/command/obelisk-command.cabal+1−2
@@ -1,5 +1,5 @@
1name: obelisk-command
1name: obelisk-command
2−version: 0.4
2+version: 0.5
3cabal-version: >= 1.8
3cabal-version: >= 1.8
4build-type: Simple
4build-type: Simple
5
5
@@ -20,7 +20,6 @@ library
20 , either
20 , either
21 , errors
21 , errors
22 , exceptions
22 , exceptions
23− , extra
24 , filepath
23 , filepath
25 , git
24 , git
26 , github
25 , github
lib/command/src/Obelisk/Command/Run.hs+13−16
@@ -15,7 +15,6 @@ import Control.Monad.Reader (MonadIO)
15import Data.Coerce (coerce)
15import Data.Coerce (coerce)
16import Data.Either
16import Data.Either
17import Data.Foldable (for_, toList)
17import Data.Foldable (for_, toList)
18−import Data.List.Extra (dropPrefix)
19import qualified Data.List.NonEmpty as NE
18import qualified Data.List.NonEmpty as NE
20import Data.Map (Map)
19import Data.Map (Map)
21import qualified Data.Map as Map
20import qualified Data.Map as Map
@@ -53,8 +52,7 @@ import System.Process (waitForProcess)
53
52
54import Obelisk.App (MonadObelisk)
53import Obelisk.App (MonadObelisk)
55import Obelisk.CliApp (Severity (..) , failWith, putLog, proc, readCreateProcessWithExitCode, setCwd, setDelegateCtlc, createProcess_)
54import Obelisk.CliApp (Severity (..) , failWith, putLog, proc, readCreateProcessWithExitCode, setCwd, setDelegateCtlc, createProcess_)
56−import Obelisk.Command.Project (obeliskDirName, toObeliskDir, withProjectRoot, nixShellWithPkgs, findProjectAssets)
55+import Obelisk.Command.Project (withProjectRoot, nixShellWithPkgs, findProjectAssets)
57−import Obelisk.Command.Thunk (attrCacheFileName)
58import Obelisk.Command.Utils (findExePath, ghcidExePath)
56import Obelisk.Command.Utils (findExePath, ghcidExePath)
59
57
60data CabalPackageInfo = CabalPackageInfo
58data CabalPackageInfo = CabalPackageInfo
@@ -162,20 +160,19 @@ runWatch = withProjectRoot "." $ \root -> do
162-- to the Nix @project@ function.
160-- to the Nix @project@ function.
163getLocalPkgs :: MonadObelisk m => FilePath -> m [FilePath]
161getLocalPkgs :: MonadObelisk m => FilePath -> m [FilePath]
164getLocalPkgs root = do
162getLocalPkgs root = do
165− (_exitCode, out, err) <- readCreateProcessWithExitCode $
163+ obeliskPaths <- runFind ["-L", root, "-name", ".obelisk", "-type", "d"]
166− proc findExePath ["-L", root, "(", "-name", "*.cabal", "-o", "-name", Hpack.packageConfig, ")", "-a", "-type", "f"]
167− putLog Debug $ T.strip $ T.pack err
168
164
169− let
165+ -- We do not want to find packages that are embedded inside other obelisk projects, unless that
170− -- We ignore any path that has ".obelisk" in it, but keep the root ".obelisk" paths
166+ -- obelisk project is our own.
167+ let exclusions = filter (/= root) $ map takeDirectory obeliskPaths
168+ runFind $
169+ ["-L", root, "(", "-name", "*.cabal", "-o", "-name", Hpack.packageConfig, ")", "-a", "-type", "f"]
170+ <> concat [["-not", "-path", p </> "*"] | p <- exclusions]
171+ where
172+ runFind args = do
173+ (_exitCode, out, err) <- readCreateProcessWithExitCode $ proc findExePath args
174+ putLog Debug $ T.strip $ T.pack err
171− packagePaths = filter (not . isIgnored) $ map T.unpack $ T.lines $ T.strip $ T.pack out
175+ pure $ map T.unpack $ T.lines $ T.strip $ T.pack out
172− isIgnored path =
173− obeliskDirName `elem` dropPrefix (splitPath $ toObeliskDir root) pathSegments
174− && attrCacheFileName `notElem` pathSegments
175− where
176− pathSegments = splitPath path
177−
178− pure packagePaths
179
176
180data GuessPackageFileError = GuessPackageFileError_Ambiguous [FilePath] | GuessPackageFileError_NotFound
177data GuessPackageFileError = GuessPackageFileError_Ambiguous [FilePath] | GuessPackageFileError_NotFound
181 deriving (Eq, Ord, Show)
178 deriving (Eq, Ord, Show)
lib/command/src/Obelisk/Command/Thunk.hs+22−2
@@ -513,9 +513,10 @@ gitHubStandaloneLoaderV4 = T.unlines
513
513
514plainGitStandaloneLoaders :: NonEmpty Text
514plainGitStandaloneLoaders :: NonEmpty Text
515plainGitStandaloneLoaders =
515plainGitStandaloneLoaders =
516− plainGitStandaloneLoaderV3 :|
516+ plainGitStandaloneLoaderV4 :|
517 [ plainGitStandaloneLoaderV1
517 [ plainGitStandaloneLoaderV1
518 , plainGitStandaloneLoaderV2
518 , plainGitStandaloneLoaderV2
519+ , plainGitStandaloneLoaderV3
519 ]
520 ]
520
521
521plainGitStandaloneLoaderV1 :: Text
522plainGitStandaloneLoaderV1 :: Text
@@ -536,6 +537,8 @@ plainGitStandaloneLoaderV2 = T.unlines
536 , "in import (fetchGit (builtins.fromJSON (builtins.readFile ./git.json)))"
537 , "in import (fetchGit (builtins.fromJSON (builtins.readFile ./git.json)))"
537 ]
538 ]
538
539
540+-- This loader has a bug because @builtins.fetchGit@ is not given a @ref@
541+-- and will fail to find commits without this because it does shallow clones.
539plainGitStandaloneLoaderV3 :: Text
542plainGitStandaloneLoaderV3 :: Text
540plainGitStandaloneLoaderV3 = T.unlines
543plainGitStandaloneLoaderV3 = T.unlines
541 [ "# DO NOT HAND-EDIT THIS FILE"
544 [ "# DO NOT HAND-EDIT THIS FILE"
@@ -552,6 +555,23 @@ plainGitStandaloneLoaderV3 = T.unlines
552 , "in import (fetch (builtins.fromJSON (builtins.readFile ./git.json)))"
555 , "in import (fetch (builtins.fromJSON (builtins.readFile ./git.json)))"
553 ]
556 ]
554
557
558+plainGitStandaloneLoaderV4 :: Text
559+plainGitStandaloneLoaderV4 = T.unlines
560+ [ "# DO NOT HAND-EDIT THIS FILE"
561+ , "let fetch = {url, rev, branch ? null, sha256 ? null, fetchSubmodules ? false, private ? false, ...}:"
562+ , " let realUrl = let firstChar = builtins.substring 0 1 url; in"
563+ , " if firstChar == \"/\" then /. + url"
564+ , " else if firstChar == \".\" then ./. + url"
565+ , " else url;"
566+ , " in if !fetchSubmodules && private then builtins.fetchGit {"
567+ , " url = realUrl; inherit rev;"
568+ , " ${if branch == null then null else \"ref\"} = branch;"
569+ , " } else (import <nixpkgs> {}).fetchgit {"
570+ , " url = realUrl; inherit rev sha256;"
571+ , " };"
572+ , "in import (fetch (builtins.fromJSON (builtins.readFile ./git.json)))"
573+ ]
574+
555thunkFileNames :: [FilePath]
575thunkFileNames :: [FilePath]
556thunkFileNames = ["default.nix", "github.json", "git.json"]
576thunkFileNames = ["default.nix", "github.json", "git.json"]
557
577
@@ -937,7 +957,7 @@ guessGitRepoIsPrivate uri = flip fix urisToTry $ \loop -> \case
937 urisToTry = nubOrd $
957 urisToTry = nubOrd $
938 -- Include the original URI if it isn't using SSH because SSH will certainly fail.
958 -- Include the original URI if it isn't using SSH because SSH will certainly fail.
939 [uri | fmap URI.unRText (URI.uriScheme (unGitUri uri)) /= Just "ssh"] <>
959 [uri | fmap URI.unRText (URI.uriScheme (unGitUri uri)) /= Just "ssh"] <>
940− [changeScheme "https" uri, changeScheme "http" uri, changeScheme "git" uri]
960+ [changeScheme "https" uri, changeScheme "http" uri]
941 changeScheme scheme (GitUri u) = GitUri $ u
961 changeScheme scheme (GitUri u) = GitUri $ u
942 { URI.uriScheme = URI.mkScheme scheme
962 { URI.uriScheme = URI.mkScheme scheme
943 , URI.uriAuthority = (\x -> x { URI.authUserInfo = Nothing }) <$> URI.uriAuthority u
963 , URI.uriAuthority = (\x -> x { URI.authUserInfo = Nothing }) <$> URI.uriAuthority u
lib/route/src/Obelisk/Route/Frontend.hs+80−16
@@ -7,15 +7,11 @@
7{-# LANGUAGE FlexibleContexts #-}
7{-# LANGUAGE FlexibleContexts #-}
8{-# LANGUAGE FlexibleInstances #-}
8{-# LANGUAGE FlexibleInstances #-}
9{-# LANGUAGE GADTs #-}
9{-# LANGUAGE GADTs #-}
10−{-# LANGUAGE KindSignatures #-}
11{-# LANGUAGE LambdaCase #-}
10{-# LANGUAGE LambdaCase #-}
12−{-# LANGUAGE MultiParamTypeClasses #-}
13{-# LANGUAGE OverloadedStrings #-}
11{-# LANGUAGE OverloadedStrings #-}
14{-# LANGUAGE PatternSynonyms #-}
12{-# LANGUAGE PatternSynonyms #-}
15{-# LANGUAGE RankNTypes #-}
13{-# LANGUAGE RankNTypes #-}
16{-# LANGUAGE ScopedTypeVariables #-}
14{-# LANGUAGE ScopedTypeVariables #-}
17−{-# LANGUAGE TemplateHaskell #-}
18−{-# LANGUAGE TupleSections #-}
19{-# LANGUAGE TypeFamilies #-}
15{-# LANGUAGE TypeFamilies #-}
20{-# LANGUAGE TypeOperators #-}
16{-# LANGUAGE TypeOperators #-}
21{-# LANGUAGE ViewPatterns #-}
17{-# LANGUAGE ViewPatterns #-}
@@ -64,7 +60,7 @@ import Prelude hiding ((.), id)
64import Obelisk.Route
60import Obelisk.Route
65
61
66import Control.Category (Category (..), (.))
62import Control.Category (Category (..), (.))
67−import Control.Category.Cartesian
63+import Control.Category.Cartesian ((&&&))
68import Control.Lens hiding (Bifunctor, bimap, universe, element)
64import Control.Lens hiding (Bifunctor, bimap, universe, element)
69import Control.Monad ((<=<))
65import Control.Monad ((<=<))
70import Control.Monad.Fix
66import Control.Monad.Fix
@@ -94,7 +90,9 @@ import Reflex.Dom.Builder.Class
94import Data.Type.Coercion
90import Data.Type.Coercion
95import Language.Javascript.JSaddle --TODO: Get rid of this - other platforms can also be routed
91import Language.Javascript.JSaddle --TODO: Get rid of this - other platforms can also be routed
96import Reflex.Dom.Core
92import Reflex.Dom.Core
93+import qualified GHCJS.DOM as DOM
97import qualified GHCJS.DOM.Types as DOM
94import qualified GHCJS.DOM.Types as DOM
95+import qualified GHCJS.DOM.Window as Window
98import Network.URI
96import Network.URI
99import Data.Maybe (fromMaybe)
97import Data.Maybe (fromMaybe)
100import qualified Data.List as L
98import qualified Data.List as L
@@ -511,59 +509,125 @@ runRouteViewT routeEncoder switchover useHash a = do
511-- | A link widget that, when clicked, sets the route to the provided route. In non-javascript
509-- | A link widget that, when clicked, sets the route to the provided route. In non-javascript
512-- contexts, this widget falls back to using @href@s to control navigation
510-- contexts, this widget falls back to using @href@s to control navigation
513routeLink
511routeLink
514− :: forall t m a route.
512+ :: forall t m a route js.
515 ( DomBuilder t m
513 ( DomBuilder t m
516 , RouteToUrl route m
514 , RouteToUrl route m
517 , SetRoute t route m
515 , SetRoute t route m
516+ , Prerender js t m
518 )
517 )
519 => route -- ^ Target route
518 => route -- ^ Target route
520 -> m a -- ^ Child widget
519 -> m a -- ^ Child widget
521 -> m a
520 -> m a
522routeLink r w = do
521routeLink r w = do
522+ (e, a) <- routeLinkImpl r w
523+ scrollToTop e
524+ return a
525+
526+-- | Raw implementation of 'routeLink'. Does not scroll to the top of the page on clicks.
527+routeLinkImpl
528+ :: forall t m a route.
529+ ( DomBuilder t m
530+ , RouteToUrl route m
531+ , SetRoute t route m
532+ )
533+ => route -- ^ Target route
534+ -> m a -- ^ Child widget
535+ -> m (Event t (), a)
536+routeLinkImpl r w = do
523 enc <- askRouteToUrl
537 enc <- askRouteToUrl
524 let cfg = (def :: ElementConfig EventResult t (DomBuilderSpace m))
538 let cfg = (def :: ElementConfig EventResult t (DomBuilderSpace m))
525 & elementConfig_eventSpec %~ addEventSpecFlags (Proxy :: Proxy (DomBuilderSpace m)) Click (\_ -> preventDefault)
539 & elementConfig_eventSpec %~ addEventSpecFlags (Proxy :: Proxy (DomBuilderSpace m)) Click (\_ -> preventDefault)
526 & elementConfig_initialAttributes .~ "href" =: enc r
540 & elementConfig_initialAttributes .~ "href" =: enc r
527 (e, a) <- element "a" cfg w
541 (e, a) <- element "a" cfg w
528 setRoute $ r <$ domEvent Click e
542 setRoute $ r <$ domEvent Click e
529− return a
543+ return (domEvent Click e, a)
544+
545+scrollToTop :: forall m t js. (Prerender js t m, Monad m) => Event t () -> m ()
546+scrollToTop e = prerender_ blank $ performEvent_ $ ffor e $ \_ -> liftJSM $ DOM.currentWindow >>= \case
547+ Nothing -> pure ()
548+ Just win -> Window.scrollTo win 0 0
530
549
531-- | Like 'routeLinkDynAttr' but without custom attributes.
550-- | Like 'routeLinkDynAttr' but without custom attributes.
532dynRouteLink
551dynRouteLink
533− :: forall t m a route.
552+ :: forall t m a route js.
534 ( DomBuilder t m
553 ( DomBuilder t m
535 , PostBuild t m
554 , PostBuild t m
536− , RouteToUrl (R route) m
555+ , RouteToUrl route m
537− , SetRoute t (R route) m
556+ , SetRoute t route m
557+ , Prerender js t m
538 )
558 )
539− => Dynamic t (R route) -- ^ Target route
559+ => Dynamic t route -- ^ Target route
540− -> m a -- ^ Child widget of the @a@ element
560+ -> m a -- ^ Child widget
541 -> m a
561 -> m a
542−dynRouteLink = routeLinkDynAttr mempty
562+dynRouteLink r w = do
563+ (e, a) <- dynRouteLinkImpl r w
564+ scrollToTop e
565+ return a
566+
567+-- | Raw implementation of 'dynRouteLink'. Does not scroll to the top of the page on clicks.
568+dynRouteLinkImpl
569+ :: forall t m a route.
570+ ( DomBuilder t m
571+ , PostBuild t m
572+ , RouteToUrl route m
573+ , SetRoute t route m
574+ )
575+ => Dynamic t route -- ^ Target route
576+ -> m a -- ^ Child widget
577+ -> m (Event t (), a)
578+dynRouteLinkImpl dr w = do
579+ enc <- askRouteToUrl
580+ er <- dynamicAttributesToModifyAttributes $ ("href" =:) . enc <$> dr
581+ let cfg = (def :: ElementConfig EventResult t (DomBuilderSpace m))
582+ & elementConfig_eventSpec %~ addEventSpecFlags (Proxy :: Proxy (DomBuilderSpace m)) Click (\_ -> preventDefault)
583+ & elementConfig_modifyAttributes .~ er
584+ (e, a) <- element "a" cfg w
585+ let clk = domEvent Click e
586+ setRoute $ tag (current dr) clk
587+ return (clk, a)
543
588
544-- | An @a@-tag link widget that, when clicked, sets the route to current value of the
589-- | An @a@-tag link widget that, when clicked, sets the route to current value of the
545-- provided dynamic route. In non-JavaScript contexts the value of the dynamic post
590-- provided dynamic route. In non-JavaScript contexts the value of the dynamic post
546-- build is used so the link still works like 'routeLink'.
591-- build is used so the link still works like 'routeLink'.
547routeLinkDynAttr
592routeLinkDynAttr
548− :: forall t m a route.
593+ :: forall t m a route js.
549 ( DomBuilder t m
594 ( DomBuilder t m
550 , PostBuild t m
595 , PostBuild t m
551 , RouteToUrl (R route) m
596 , RouteToUrl (R route) m
552 , SetRoute t (R route) m
597 , SetRoute t (R route) m
598+ , Prerender js t m
553 )
599 )
554 => Dynamic t (Map AttributeName Text) -- ^ Attributes for @a@ element. Note that if @href@ is present it will be ignored
600 => Dynamic t (Map AttributeName Text) -- ^ Attributes for @a@ element. Note that if @href@ is present it will be ignored
555 -> Dynamic t (R route) -- ^ Target route
601 -> Dynamic t (R route) -- ^ Target route
556 -> m a -- ^ Child widget of the @a@ element
602 -> m a -- ^ Child widget of the @a@ element
557 -> m a
603 -> m a
558routeLinkDynAttr dAttr dr w = do
604routeLinkDynAttr dAttr dr w = do
605+ (e, a) <- routeLinkDynAttrImpl dAttr dr w
606+ scrollToTop e
607+ return a
608+
609+-- | Raw implementation of 'routeLinkDynAttr'. Does not scroll to the top of the page on clicks.
610+routeLinkDynAttrImpl
611+ :: forall t m a route.
612+ ( DomBuilder t m
613+ , PostBuild t m
614+ , RouteToUrl (R route) m
615+ , SetRoute t (R route) m
616+ )
617+ => Dynamic t (Map AttributeName Text) -- ^ Attributes for @a@ element. Note that if @href@ is present it will be ignored
618+ -> Dynamic t (R route) -- ^ Target route
619+ -> m a -- ^ Child widget of the @a@ element
620+ -> m (Event t (), a)
621+routeLinkDynAttrImpl dAttr dr w = do
559 enc <- askRouteToUrl
622 enc <- askRouteToUrl
560 er <- dynamicAttributesToModifyAttributes $ zipDynWith (<>) (("href" =:) . enc <$> dr) dAttr
623 er <- dynamicAttributesToModifyAttributes $ zipDynWith (<>) (("href" =:) . enc <$> dr) dAttr
561 let cfg = (def :: ElementConfig EventResult t (DomBuilderSpace m))
624 let cfg = (def :: ElementConfig EventResult t (DomBuilderSpace m))
562 & elementConfig_eventSpec %~ addEventSpecFlags (Proxy :: Proxy (DomBuilderSpace m)) Click (\_ -> preventDefault)
625 & elementConfig_eventSpec %~ addEventSpecFlags (Proxy :: Proxy (DomBuilderSpace m)) Click (\_ -> preventDefault)
563 & elementConfig_modifyAttributes .~ er
626 & elementConfig_modifyAttributes .~ er
564 (e, a) <- element "a" cfg w
627 (e, a) <- element "a" cfg w
628+ let clk = domEvent Click e
565− setRoute $ tag (current dr) $ domEvent Click e
629+ setRoute $ tag (current dr) clk
566− return a
630+ return (clk, a)
567
631
568-- On ios due to sandboxing when loading the page from a file adapt the
632-- On ios due to sandboxing when loading the page from a file adapt the
569-- path to be based on the hash.
633-- path to be based on the hash.
lib/selftest/src/Obelisk/SelfTest.hs−12
@@ -27,7 +27,6 @@ import Shelly hiding (cp)
27import System.Directory (getCurrentDirectory, withCurrentDirectory, getDirectoryContents)
27import System.Directory (getCurrentDirectory, withCurrentDirectory, getDirectoryContents)
28import System.Environment
28import System.Environment
29import System.Exit (ExitCode (..))
29import System.Exit (ExitCode (..))
30−import System.FilePath (replaceBaseName, takeBaseName)
31import System.Which (staticWhich)
30import System.Which (staticWhich)
32import qualified System.Info
31import qualified System.Info
33import System.IO (Handle, hClose, hIsEOF, hGetContents)
32import System.IO (Handle, hClose, hIsEOF, hGetContents)
@@ -217,9 +216,6 @@ main = do
217 describe "ob run" $ {- NOT parallel $ -} do
216 describe "ob run" $ {- NOT parallel $ -} do
218 it "works in root directory" $ inTmpObInit $ \_ -> testObRunInDir' Nothing httpManager
217 it "works in root directory" $ inTmpObInit $ \_ -> testObRunInDir' Nothing httpManager
219 it "works in sub directory" $ inTmpObInit $ \_ -> testObRunInDir' (Just "frontend") httpManager
218 it "works in sub directory" $ inTmpObInit $ \_ -> testObRunInDir' (Just "frontend") httpManager
220− it "works with differently named cabal files" $ inTmpObInit $ \_ -> do
221− changeCabalPackageName "backend/backend.cabal" "new-backend"
222− testObRunInDir' Nothing httpManager
223
219
224 describe "obelisk project" $ parallel $ do
220 describe "obelisk project" $ parallel $ do
225 it "can build obelisk command" $ inTmpObInit $ \_ -> nixBuild ["-A", "command" , toTextIgnore obeliskImplDirty]
221 it "can build obelisk command" $ inTmpObInit $ \_ -> nixBuild ["-A", "command" , toTextIgnore obeliskImplDirty]
@@ -426,14 +422,6 @@ obRunCheck httpManager _stdout frontendUri = do
426 req frontendUri >>= \r -> when (HTTP.responseStatus r /= HTTP.ok200) $ errorExit $
422 req frontendUri >>= \r -> when (HTTP.responseStatus r /= HTTP.ok200) $ errorExit $
427 "Request to frontend server failed: " <> T.pack (show r)
423 "Request to frontend server failed: " <> T.pack (show r)
428
424
429−-- | Rename a cabal file and do a really dumb, brittle search/replace in its content to update the name.
430−changeCabalPackageName :: FilePath -> Text -> Sh ()
431−changeCabalPackageName cabalFile newName = do
432− contents <- readfile cabalFile
433− writefile (replaceBaseName cabalFile (T.unpack newName)) $
434− T.replace (" " <> T.pack (takeBaseName cabalFile)) (" " <> newName) contents -- WARNING: Super brittle
435− rm cabalFile
436−
437getFreePorts :: Int -> IO [Socket.PortNumber]
425getFreePorts :: Int -> IO [Socket.PortNumber]
438getFreePorts 0 = pure []
426getFreePorts 0 = pure []
439getFreePorts n = Socket.withSocketsDo $ do
427getFreePorts n = Socket.withSocketsDo $ do