@@ -26,6 +26,7 @@ import Control.Monad hiding (sequence, sequence_)
2626import Control.Monad.Fix
2727import Control.Monad.IO.Class
2828import Control.Monad.Primitive
29+import Control.Monad.Reader
2930import Control.Monad.Ref
3031import Control.Monad.Trans.Class
3132import Data.ByteString (ByteString)
@@ -50,7 +51,7 @@ import qualified Reflex.TriggerEvent.Base as TriggerEvent
5051
5152makePrisms ''Sum
5253
53−type ObeliskWidget t x route m =
54+type ObeliskWidget t route m =
5455 ( DomBuilder t m
5556 , MonadFix m
5657 , MonadHold t m
@@ -58,8 +59,6 @@ type ObeliskWidget t x route m =
5859 , MonadReflexCreateTrigger t m
5960 , PostBuild t m
6061 , PerformEvent t m
61− , MonadIO m
62− , MonadIO (Performable m)
6362 , TriggerEvent t m
6463 , HasDocument m
6564 , MonadRef m
@@ -68,91 +67,19 @@ type ObeliskWidget t x route m =
6867 , Ref (Performable m) ~ Ref IO
6968 , MonadFix (Performable m)
7069 , PrimMonad m
71− , Prerender x m
70+ , Prerender t m
7271 , SetRoute t route m
72+ , SetRoute t route (Client m)
7373 , RouteToUrl route m
74+ , RouteToUrl route (Client m)
75+ , Monad (Client m)
7476 )
7577
7678data Frontend route = Frontend
77− { _frontend_head :: !(forall t m x. ObeliskWidget t x route m => RoutedT t route m ())
78− , _frontend_body :: !(forall t m x. ObeliskWidget t x route m => RoutedT t route m ())
79+ { _frontend_head :: !(forall t m. ObeliskWidget t route m => RoutedT t route m ())
80+ , _frontend_body :: !(forall t m. ObeliskWidget t route m => RoutedT t route m ())
7981 }
8082
81−type Widget' x a = HydrationDomBuilderT DomTimeline (DomCoreWidget x) a
82−
83−-- | A widget that isn't attached to any particular part of the DOM hierarchy
84−type FloatingWidget x = TriggerEventT DomTimeline (DomCoreWidget x)
85−
86−type DomCoreWidget x = PostBuildT DomTimeline (WithJSContextSingleton x (PerformEventT DomTimeline DomHost))
87−
88−--TODO: Rename
89−{-# INLINABLE attachWidget''' #-}
90−attachWidget'''
91− :: IORef HydrationMode
92− -> IORef [(Node, HydrationRunnerT DomTimeline (PostBuildT DomTimeline (WithJSContextSingleton () (PerformEventT DomTimeline DomHost))) ())]
93− -> JSContextSingleton ()
94− -> (EventChannel -> Event DomTimeline () -> PerformEventT DomTimeline DomHost (IORef (Maybe (EventTrigger DomTimeline ()))))
95− -> IO ()
96−attachWidget''' hydrationMode rootNodesRef jsSing w = do
97− events <- newChan
98− runDomHost $ flip runTriggerEventT events $ mdo
99− (syncEvent, fireSync) <- newTriggerEvent
100− liftIO $ putStrLn "-------------------------------------- Running host"
101− (postBuildTriggerRef, fc@(FireCommand fire)) <- lift $ hostPerformEventT $ do
102− a <- w events syncEvent
103− runWithReplace (return ()) $ delayedAction <$ syncEvent
104− pure a
105− mPostBuildTrigger <- readRef postBuildTriggerRef
106− lift $ forM_ mPostBuildTrigger $ \postBuildTrigger -> fire [postBuildTrigger :=> Identity ()] $ return ()
107− liftIO $ fireSync ()
108− rootNodes <- liftIO $ readIORef rootNodesRef
109− -- TODO: DomHost is SpiderHost Global only if we're not in profiling mode
110− let delayedAction = do
111− for_ (reverse rootNodes) $ \(rootNode, runner) -> do
112− liftIO $ putStrLn "-------------------------------------- Performing delayed actions"
113− void $ runWithJSContextSingleton (runPostBuildT (runHydrationRunnerT runner Nothing rootNode events) never) jsSing
114− liftIO $ putStrLn "-------------------------------------- Performed delayed actions"
115− liftIO $ writeIORef hydrationMode HydrationMode_Immediate
116− liftIO $ processAsyncEvents events fc
117−
118−--TODO: This is a collection of random stuff; we should make it make some more sense and then upstream to reflex-dom-core
119−runWithHeadAndBody
120− :: ( (forall c. Widget' () c -> FloatingWidget () c) -- "Append to head"
121− -> (forall c. Widget' () c -> FloatingWidget () c) -- "Append to body"
122− -> FloatingWidget () ()
123− )
124− -> JSM ()
125−runWithHeadAndBody app = withJSContextSingletonMono $ \jsSing -> do
126− globalDoc <- currentDocumentUnchecked
127− headElement <- getHeadUnchecked globalDoc
128− bodyElement <- getBodyUnchecked globalDoc
129− unreadyChildren <- liftIO $ newIORef 0
130− hydrationMode <- liftIO $ newIORef HydrationMode_Hydrating
131− hydrationResult <- liftIO $ newIORef []
132− liftIO $ attachWidget''' hydrationMode hydrationResult jsSing $ \events switchover -> flip runWithJSContextSingleton jsSing $ do
133− (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
134− let hydrateDom :: DOM.Node -> Widget' () c -> FloatingWidget () c
135− hydrateDom n w = do
136− events' <- TriggerEvent.askEvents
137− parent <- liftIO $ newIORef $ toNode n
138− lift $ do
139− let builderEnv = HydrationDomBuilderEnv
140− { _hydrationDomBuilderEnv_document = globalDoc
141− , _hydrationDomBuilderEnv_parent = parent
142− , _hydrationDomBuilderEnv_unreadyChildren = unreadyChildren
143− , _hydrationDomBuilderEnv_commitAction = pure ()
144− , _hydrationDomBuilderEnv_hydrationMode = hydrationMode
145− , _hydrationDomBuilderEnv_switchover = switchover
146− }
147− (a, res) <- runHydrationDomBuilderT w builderEnv events'
148− liftIO $ modifyIORef' hydrationResult ((n, res) :)
149− pure a
150− flip runPostBuildT postBuild $ flip runTriggerEventT events $ app (hydrateDom $ toNode headElement) (hydrateDom $ toNode bodyElement)
151−-- liftIO (readIORef unreadyChildren) >>= \case
152−-- 0 -> DOM.liftJSM commit
153−-- _ -> return ()
154− return postBuildTriggerRef
155−
15683runFrontend :: forall backendRoute route. Encoder Identity Identity (R (Sum backendRoute (ObeliskRoute route))) PageName -> Frontend (R route) -> JSM ()
15784runFrontend validFullEncoder frontend = do
15885 let ve = validFullEncoder . hoistParse errorLeft (prismEncoder (rPrism $ _InR . _ObeliskRoute_App))
@@ -168,12 +95,21 @@ runFrontend validFullEncoder frontend = do
16895 , MonadFix m
16996 , MonadFix (Performable m)
17097 )
171− => RoutedT t (R route) (SetRouteT t (R route) (RouteToUrlT (R route) m)) a
98+ => Event t ()
99+ -> RoutedT t (R route) (SetRouteT t (R route) (RouteToUrlT (R route) m)) a
172100 -> m a
173101 runMyRouteViewT = runRouteViewT ve
174− runWithHeadAndBody $ \appendHead appendBody -> runMyRouteViewT $ do
175− mapRoutedT (mapSetRouteT (mapRouteToUrlT appendHead)) $ _frontend_head frontend
176− mapRoutedT (mapSetRouteT (mapRouteToUrlT appendBody)) $ _frontend_body frontend
102+ runHydrationWidgetWithHeadAndBody (liftIO $ putStrLn "SWITCHOVER") $ \appendHead appendBody -> do
103+ rec switchover <- runMyRouteViewT switchover $ do
104+ (switchover, fire) <- newTriggerEvent
105+ mapRoutedT (mapSetRouteT (mapRouteToUrlT appendHead)) $ do
106+ _frontend_head frontend
107+ mapRoutedT (mapSetRouteT (mapRouteToUrlT appendBody)) $ do
108+ _frontend_body frontend
109+ switchover' <- lift $ lift $ lift $ HydrationDomBuilderT $ asks _hydrationDomBuilderEnv_switchover
110+ performEvent_ $ liftIO (fire ()) <$ switchover'
111+ pure switchover
112+ pure ()
177113
178114renderFrontendHtml
179115 :: (t ~ DomTimeline)