react-flux-1.0.2: example/routing/Router.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
#ifdef __GHCJS__
{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI #-}
#endif
-- |
module Router (setLocationHash
, getLocationHash
, onLocationHashChange
, actionRoute
, childRoutePath
, initRouter
, storeRouter
) where
import React.Flux
import qualified Data.Text as T
import qualified Data.ByteString.Char8 as BC
import Control.Monad.IO.Class (liftIO)
import qualified Web.Routes as WR
#ifdef __GHCJS__
import Control.Monad (liftM)
import qualified Data.JSString as JSS
import GHCJS.Foreign.Callback
import GHCJS.Types (JSString, JSVal)
import Unsafe.Coerce
#endif
#ifdef __GHCJS__
foreign import javascript unsafe
"window.onhashchange = function() {$1(location.hash.toString());}"
js_attachtLocationHashCb :: (Callback (JSVal -> IO ())) -> IO ()
foreign import javascript unsafe
"window.location.hash = $1"
js_setLocationHash :: JSString -> IO ()
foreign import javascript unsafe
"window.location.hash.toString()"
js_getLocationHash :: IO JSString
setLocationHash :: String -> IO ()
setLocationHash = js_setLocationHash . JSS.pack
getLocationHash :: IO (Maybe String)
getLocationHash = do
rt <- liftM JSS.unpack js_getLocationHash
return $ case rt of
"" -> Nothing
_ -> Just rt
onLocationHashChange :: (String -> IO ()) -> IO ()
onLocationHashChange fn = do
cb <- syncCallback1 ThrowWouldBlock (fn . JSS.unpack . unsafeCoerce)
js_attachtLocationHashCb cb
# else
setLocationHash :: String -> IO ()
setLocationHash _ = return ()
getLocationHash :: IO (Maybe String)
getLocationHash = return Nothing
onLocationHashChange :: (String -> IO ()) -> IO ()
onLocationHashChange _ = return ()
#endif
childRoutePath :: WR.PathInfo action => action -> [T.Text]
childRoutePath = WR.toPathSegments
actionRoute :: WR.PathInfo action => Maybe ([T.Text] -> T.Text) -> action -> T.Text
actionRoute mparentRouter action =
frag
where
path = maybe (WR.toPathInfo action) ($ childRoutePath action) mparentRouter
frag = if "#" `T.isPrefixOf` path
then path
else T.cons '#' path
initRouter :: ([T.Text] -> IO ()) -> IO ()
initRouter router =
onLocationHashChange $ router . stripHash . WR.decodePathInfo . BC.pack
where
stripHash ("#":path) = path
stripHash path = path
storeRouter :: (StoreData store, WR.PathInfo (StoreAction store)) =>
ReactStore store -> ([T.Text] -> IO ())
storeRouter store =
let site = WR.mkSitePI $ WR.runRouteT $ routerAlterStore
in
\rt -> either (const $ return ()) id $ WR.runSite "" site rt
where
routerAlterStore action =
liftIO $ alterStore store action