on develop help sign in
Allow prerender to change type of client widget
f1cf26fTom Smalley
reflex-dom-core/src/Reflex/Dom/Builder/Class.hs+2−2
@@ -61,6 +61,7 @@ import Data.String
61import Data.Text (Text)
61import Data.Text (Text)
62import Data.Type.Coercion
62import Data.Type.Coercion
63import GHCJS.DOM.Types (JSM)
63import GHCJS.DOM.Types (JSM)
64+import qualified GHCJS.DOM.Types as DOM
64
65
65class Default (EventSpec d EventResult) => DomSpace d where
66class Default (EventSpec d EventResult) => DomSpace d where
66 type EventSpec d :: (EventTag -> *) -> *
67 type EventSpec d :: (EventTag -> *) -> *
@@ -68,7 +69,6 @@ class Default (EventSpec d EventResult) => DomSpace d where
68 type RawTextNode d :: *
69 type RawTextNode d :: *
69 type RawCommentNode d :: *
70 type RawCommentNode d :: *
70 type RawElement d :: *
71 type RawElement d :: *
71− type RawFile d :: *
72 type RawInputElement d :: *
72 type RawInputElement d :: *
73 type RawTextAreaElement d :: *
73 type RawTextAreaElement d :: *
74 type RawSelectElement d :: *
74 type RawSelectElement d :: *
@@ -323,7 +323,7 @@ data InputElement er d t
323 , _inputElement_hasFocus :: Dynamic t Bool
323 , _inputElement_hasFocus :: Dynamic t Bool
324 , _inputElement_element :: Element er d t
324 , _inputElement_element :: Element er d t
325 , _inputElement_raw :: RawInputElement d
325 , _inputElement_raw :: RawInputElement d
326− , _inputElement_files :: Dynamic t [RawFile d]
326+ , _inputElement_files :: Dynamic t [DOM.File]
327 }
327 }
328
328
329data TextAreaElementConfig er t m
329data TextAreaElementConfig er t m
reflex-dom-core/src/Reflex/Dom/Builder/Hydration.hs+52−25
@@ -4,6 +4,7 @@
4{-# LANGUAGE DataKinds #-}
4{-# LANGUAGE DataKinds #-}
5{-# LANGUAGE DeriveFunctor #-}
5{-# LANGUAGE DeriveFunctor #-}
6{-# LANGUAGE DeriveTraversable #-}
6{-# LANGUAGE DeriveTraversable #-}
7+{-# LANGUAGE EmptyDataDecls #-}
7{-# LANGUAGE FlexibleContexts #-}
8{-# LANGUAGE FlexibleContexts #-}
8{-# LANGUAGE FlexibleInstances #-}
9{-# LANGUAGE FlexibleInstances #-}
9{-# LANGUAGE GADTs #-}
10{-# LANGUAGE GADTs #-}
@@ -59,7 +60,6 @@ import Control.Monad.Primitive
59import Control.Monad.Reader
60import Control.Monad.Reader
60import Control.Monad.Ref
61import Control.Monad.Ref
61import Control.Monad.State.Strict
62import Control.Monad.State.Strict
62−import Data.Default
63import Data.Dependent.Map (DMap)
63import Data.Dependent.Map (DMap)
64import Data.Dependent.Sum
64import Data.Dependent.Sum
65import Data.FastMutableIntMap (PatchIntMap (..))
65import Data.FastMutableIntMap (PatchIntMap (..))
@@ -122,10 +122,10 @@ import Data.Functor.Contravariant (phantom)
122#ifndef ghcjs_HOST_OS
122#ifndef ghcjs_HOST_OS
123import GHCJS.DOM.Types (MonadJSM (..))
123import GHCJS.DOM.Types (MonadJSM (..))
124
124
125−instance MonadJSM m => MonadJSM (HydrationDomBuilderT t m) where
125+instance MonadJSM m => MonadJSM (HydrationRunnerT t m) where
126 liftJSM' = lift . liftJSM'
126 liftJSM' = lift . liftJSM'
127
127
128−instance MonadJSM m => MonadJSM (HydrationRunnerT t m) where
128+instance MonadJSM m => MonadJSM (HydrationDomBuilderT t m) where
129 liftJSM' = lift . liftJSM'
129 liftJSM' = lift . liftJSM'
130
130
131instance MonadJSM m => MonadJSM (DomRenderHookT t m) where
131instance MonadJSM m => MonadJSM (DomRenderHookT t m) where
@@ -366,8 +366,8 @@ type SupportsHydrationDomBuilder t m = (Reflex t, MonadJSM m, MonadHold t m, Mon
366wrap
366wrap
367 :: forall m er t. (Reflex t, MonadJSM m, MonadReflexCreateTrigger t m, DomRenderHook t m)
367 :: forall m er t. (Reflex t, MonadJSM m, MonadReflexCreateTrigger t m, DomRenderHook t m)
368 => Chan [DSum (EventTriggerRef t) TriggerInvocation]
368 => Chan [DSum (EventTriggerRef t) TriggerInvocation]
369− -> RawElement GhcjsDomSpace
369+ -> DOM.Element
370− -> RawElementConfig er t GhcjsDomSpace
370+ -> RawElementConfig er t HydrationDomSpace
371 -> m (DMap EventName (EventFilterTriggerRef t er))
371 -> m (DMap EventName (EventFilterTriggerRef t er))
372wrap events e cfg = do
372wrap events e cfg = do
373 forM_ (_rawElementConfig_modifyAttributes cfg) $ \modifyAttrs -> requestDomAction_ $ ffor modifyAttrs $ imapM_ $ \(AttributeName mAttrNamespace n) mv -> case mAttrNamespace of
373 forM_ (_rawElementConfig_modifyAttributes cfg) $ \modifyAttrs -> requestDomAction_ $ ffor modifyAttrs $ imapM_ $ \(AttributeName mAttrNamespace n) mv -> case mAttrNamespace of
@@ -390,7 +390,7 @@ wrap events e cfg = do
390
390
391triggerBody
391triggerBody
392 :: forall er t x. DOM.JSContextRef
392 :: forall er t x. DOM.JSContextRef
393− -> ElementConfig er t GhcjsDomSpace
393+ -> ElementConfig er t HydrationDomSpace
394 -> Chan [DSum (EventTriggerRef t) TriggerInvocation]
394 -> Chan [DSum (EventTriggerRef t) TriggerInvocation]
395 -> DMap EventName (EventFilterTriggerRef t er)
395 -> DMap EventName (EventFilterTriggerRef t er)
396 -> DOM.Element
396 -> DOM.Element
@@ -421,9 +421,9 @@ triggerBody ctx cfg events eventTriggerRefs e (WrapArg en) t = case DMap.lookup
421makeElement
421makeElement
422 :: forall er t m a. (MonadJSM m, MonadHold t m, MonadFix m, MonadReflexCreateTrigger t m, Adjustable t m, Ref m ~ IORef, PerformEvent t m, MonadJSM (Performable m), MonadRef m)
422 :: forall er t m a. (MonadJSM m, MonadHold t m, MonadFix m, MonadReflexCreateTrigger t m, Adjustable t m, Ref m ~ IORef, PerformEvent t m, MonadJSM (Performable m), MonadRef m)
423 => Text
423 => Text
424− -> ElementConfig er t GhcjsDomSpace
424+ -> ElementConfig er t HydrationDomSpace
425 -> HydrationDomBuilderT t m a
425 -> HydrationDomBuilderT t m a
426− -> HydrationDomBuilderT t m ((Element er GhcjsDomSpace t, a), IORef DOM.Element)
426+ -> HydrationDomBuilderT t m ((Element er HydrationDomSpace t, a), IORef DOM.Element)
427makeElement elementTag cfg child = do
427makeElement elementTag cfg child = do
428 doc <- askDocument
428 doc <- askDocument
429 ctx <- askJSM
429 ctx <- askJSM
@@ -452,7 +452,7 @@ makeElement elementTag cfg child = do
452 eventTriggerRefs <- wrap events e $ extractRawElementConfig cfg
452 eventTriggerRefs <- wrap events e $ extractRawElementConfig cfg
453 es <- newFanEventWithTrigger $ triggerBody ctx cfg events eventTriggerRefs e
453 es <- newFanEventWithTrigger $ triggerBody ctx cfg events eventTriggerRefs e
454 e' <- liftIO $ newIORef e
454 e' <- liftIO $ newIORef e
455− return ((Element es e, result), e')
455+ return ((Element es (), result), e')
456 HydrationMode_Hydrating -> do
456 HydrationMode_Hydrating -> do
457 -- Schedule everything for after postBuild, except for getting the result itself
457 -- Schedule everything for after postBuild, except for getting the result itself
458 parent <- liftIO $ newIORef $ error "Parent not yet initialized"
458 parent <- liftIO $ newIORef $ error "Parent not yet initialized"
@@ -620,16 +620,41 @@ instance SupportsHydrationDomBuilder t m => NotReady t (HydrationDomBuilderT t m
620 unreadyChildren <- askUnreadyChildren
620 unreadyChildren <- askUnreadyChildren
621 liftIO $ modifyIORef' unreadyChildren succ
621 liftIO $ modifyIORef' unreadyChildren succ
622
622
623+data HydrationDomSpace
624+
625+instance DomSpace HydrationDomSpace where
626+ type EventSpec HydrationDomSpace = GhcjsEventSpec
627+ type RawDocument HydrationDomSpace = DOM.Document
628+ type RawTextNode HydrationDomSpace = ()
629+ type RawCommentNode HydrationDomSpace = ()
630+ type RawElement HydrationDomSpace = ()
631+ type RawInputElement HydrationDomSpace = ()
632+ type RawTextAreaElement HydrationDomSpace = ()
633+ type RawSelectElement HydrationDomSpace = ()
634+ addEventSpecFlags _ en f es = es
635+ { _ghcjsEventSpec_filters =
636+ let f' = Just . GhcjsEventFilter . \case
637+ Nothing -> \evt -> do
638+ mEventResult <- unGhcjsEventHandler (_ghcjsEventSpec_handler es) (en, evt)
639+ return (f mEventResult, return mEventResult)
640+ Just (GhcjsEventFilter oldFilter) -> \evt -> do
641+ (oldFlags, oldContinuation) <- oldFilter evt
642+ mEventResult <- oldContinuation
643+ let newFlags = oldFlags <> f mEventResult
644+ return (newFlags, return mEventResult)
645+ in DMap.alter f' en $ _ghcjsEventSpec_filters es
646+ }
647+
623instance (SupportsHydrationDomBuilder t m) => DomBuilder t (HydrationDomBuilderT t m) where
648instance (SupportsHydrationDomBuilder t m) => DomBuilder t (HydrationDomBuilderT t m) where
624− type DomBuilderSpace (HydrationDomBuilderT t m) = GhcjsDomSpace
649+ type DomBuilderSpace (HydrationDomBuilderT t m) = HydrationDomSpace
625 {-# INLINABLE textNode #-}
650 {-# INLINABLE textNode #-}
626 textNode (TextNodeConfig initialContents mSetContents) = do
651 textNode (TextNodeConfig initialContents mSetContents) = do
627 t <- textNodeInternal initialContents mSetContents
652 t <- textNodeInternal initialContents mSetContents
628− return $ TextNode t
653+ return $ TextNode ()
629 {-# INLINABLE commentNode #-}
654 {-# INLINABLE commentNode #-}
630 commentNode (CommentNodeConfig initialContents mSetContents) = do
655 commentNode (CommentNodeConfig initialContents mSetContents) = do
631 c <- commentNodeInternal initialContents mSetContents
656 c <- commentNodeInternal initialContents mSetContents
632− return $ CommentNode c
657+ return $ CommentNode ()
633 {-# INLINABLE element #-}
658 {-# INLINABLE element #-}
634 element elementTag cfg child = do
659 element elementTag cfg child = do
635 fst <$> makeElement elementTag cfg child
660 fst <$> makeElement elementTag cfg child
@@ -833,19 +858,21 @@ instance (SupportsHydrationDomBuilder t m) => DomBuilder t (HydrationDomBuilderT
833 , _selectElement_raw = undefined -- TODO domSelectElement
858 , _selectElement_raw = undefined -- TODO domSelectElement
834 }
859 }
835
860
861+ placeRawElement () = pure () -- TODO
836− placeRawElement e = getHydrationMode >>= \case
862+-- placeRawElement e = getHydrationMode >>= \case
837− HydrationMode_Immediate -> append $ toNode e
863+-- HydrationMode_Immediate -> append $ toNode e
838− HydrationMode_Hydrating -> addHydrationStep $ insertAfterPreviousNode e
864+-- HydrationMode_Hydrating -> addHydrationStep $ insertAfterPreviousNode e
839− wrapRawElement e rawCfg = do
865+ wrapRawElement () _cfg = pure $ Element (EventSelector $ const never) () -- TODO
866+-- wrapRawElement e rawCfg = do
840− ctx <- askJSM
867+-- ctx <- askJSM
841− events <- askEvents
868+-- events <- askEvents
842− let cfg = (def :: ElementConfig EventResult t GhcjsDomSpace)
869+-- let cfg = (def :: ElementConfig EventResult t HydrationDomSpace)
843− { _elementConfig_modifyAttributes = _rawElementConfig_modifyAttributes rawCfg
870+-- { _elementConfig_modifyAttributes = _rawElementConfig_modifyAttributes rawCfg
844− , _elementConfig_eventSpec = _rawElementConfig_eventSpec rawCfg
871+-- , _elementConfig_eventSpec = _rawElementConfig_eventSpec rawCfg
845− }
872+-- }
846− eventTriggerRefs <- wrap events e rawCfg
873+-- eventTriggerRefs <- wrap events e rawCfg
847− es <- newFanEventWithTrigger $ triggerBody ctx cfg events eventTriggerRefs e
874+-- es <- newFanEventWithTrigger $ triggerBody ctx cfg events eventTriggerRefs e
848− return $ Element es e
875+-- return $ Element es e
849
876
850data FragmentState
877data FragmentState
851 = FragmentState_Unmounted
878 = FragmentState_Unmounted
reflex-dom-core/src/Reflex/Dom/Builder/Immediate.hs−1
@@ -394,7 +394,6 @@ instance DomSpace GhcjsDomSpace where
394 type RawTextNode GhcjsDomSpace = DOM.Text
394 type RawTextNode GhcjsDomSpace = DOM.Text
395 type RawCommentNode GhcjsDomSpace = DOM.Comment
395 type RawCommentNode GhcjsDomSpace = DOM.Comment
396 type RawElement GhcjsDomSpace = DOM.Element
396 type RawElement GhcjsDomSpace = DOM.Element
397− type RawFile GhcjsDomSpace = DOM.File
398 type RawInputElement GhcjsDomSpace = DOM.HTMLInputElement
397 type RawInputElement GhcjsDomSpace = DOM.HTMLInputElement
399 type RawTextAreaElement GhcjsDomSpace = DOM.HTMLTextAreaElement
398 type RawTextAreaElement GhcjsDomSpace = DOM.HTMLTextAreaElement
400 type RawSelectElement GhcjsDomSpace = DOM.HTMLSelectElement
399 type RawSelectElement GhcjsDomSpace = DOM.HTMLSelectElement
reflex-dom-core/src/Reflex/Dom/Builder/Static.hs+2−1
@@ -56,6 +56,8 @@ import Reflex.PostBuild.Class
56import Reflex.TriggerEvent.Class
56import Reflex.TriggerEvent.Class
57import System.Random (randomRIO)
57import System.Random (randomRIO)
58
58
59+import qualified GHCJS.DOM.Types as DOM
60+
59data StaticDomBuilderEnv t = StaticDomBuilderEnv
61data StaticDomBuilderEnv t = StaticDomBuilderEnv
60 { _staticDomBuilderEnv_shouldEscape :: Bool
62 { _staticDomBuilderEnv_shouldEscape :: Bool
61 , _staticDomBuilderEnv_selectValue :: Maybe (Behavior t Text)
63 , _staticDomBuilderEnv_selectValue :: Maybe (Behavior t Text)
@@ -152,7 +154,6 @@ instance DomSpace StaticDomSpace where
152 type RawTextNode StaticDomSpace = ()
154 type RawTextNode StaticDomSpace = ()
153 type RawCommentNode StaticDomSpace = ()
155 type RawCommentNode StaticDomSpace = ()
154 type RawElement StaticDomSpace = ()
156 type RawElement StaticDomSpace = ()
155− type RawFile StaticDomSpace = ()
156 type RawInputElement StaticDomSpace = ()
157 type RawInputElement StaticDomSpace = ()
157 type RawTextAreaElement StaticDomSpace = ()
158 type RawTextAreaElement StaticDomSpace = ()
158 type RawSelectElement StaticDomSpace = ()
159 type RawSelectElement StaticDomSpace = ()
reflex-dom-core/src/Reflex/Dom/Prerender.hs+132−85
@@ -9,20 +9,27 @@
9{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE MultiParamTypeClasses #-}
10{-# LANGUAGE Rank2Types #-}
10{-# LANGUAGE Rank2Types #-}
11{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeApplications #-}
12{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeSynonymInstances #-}
14{-# LANGUAGE TypeSynonymInstances #-}
14{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
16+
17+-- | Render the first widget on the server, and the second on the client.
15module Reflex.Dom.Prerender
18module Reflex.Dom.Prerender
16 ( Prerender (..)
19 ( Prerender (..)
17 , prerender
20 , prerender
21+ , prerender_
18 , PrerenderClientConstraint
22 , PrerenderClientConstraint
19 ) where
23 ) where
20
24
21import Control.Lens ((&), (.~))
25import Control.Lens ((&), (.~))
26+import Control.Monad.Primitive (PrimMonad)
22import Control.Monad.Reader
27import Control.Monad.Reader
23−import Data.Constraint
28+import Control.Monad.Ref (MonadRef(..))
29+import Control.Monad.State.Strict
24import Data.Default
30import Data.Default
31+import Data.Foldable (traverse_)
25−import Data.IORef (modifyIORef', readIORef, newIORef, writeIORef)
32+import Data.IORef (IORef, modifyIORef', readIORef, newIORef)
26import Data.String (IsString)
33import Data.String (IsString)
27import qualified GHCJS.DOM.Element as Element
34import qualified GHCJS.DOM.Element as Element
28import qualified GHCJS.DOM.Types as DOM
35import qualified GHCJS.DOM.Types as DOM
@@ -33,67 +40,137 @@ import GHCJS.DOM.Types (MonadJSM)
33import Reflex hiding (askEvents)
40import Reflex hiding (askEvents)
34import Reflex.Dom.Builder.Class
41import Reflex.Dom.Builder.Class
35import Reflex.Dom.Builder.InputDisabled
42import Reflex.Dom.Builder.InputDisabled
36−import Reflex.Dom.Builder.Immediate (GhcjsDomSpace, ImmediateDomBuilderT, SupportsImmediateDomBuilder, insertBefore, deleteBetweenExclusive)
43+import Reflex.Dom.Builder.Immediate (ImmediateDomBuilderT, SupportsImmediateDomBuilder, insertBefore, deleteBetweenExclusive, runImmediateDomBuilderT, ImmediateDomBuilderEnv(..), GhcjsDomSpace)
37import Reflex.Dom.Builder.Hydration
44import Reflex.Dom.Builder.Hydration
38import Reflex.Dom.Builder.Static
45import Reflex.Dom.Builder.Static
39import Reflex.Host.Class
46import Reflex.Host.Class
40
47
41−type PrerenderClientConstraint' js m =
48+type PrerenderClientConstraint t m =
42− ( HasJS js m
49+ ( DomBuilder t m
43− , HasJS js (Performable m)
50+ , DomBuilderSpace m ~ GhcjsDomSpace
44− , MonadJSM m
51+ , HasDocument m
45− , MonadJSM (Performable m)
52+ , TriggerEvent t m
46− , HasJSContext m
53+ , Prerender t m
47− , HasJSContext (Performable m)
54+ , PrerenderBaseConstraints t m
48− , MonadFix m
49− , MonadFix (Performable m)
50 )
55 )
51
56
52−type PrerenderClientConstraint js m =
57+type PrerenderBaseConstraints t m =
53− ( PrerenderClientConstraint' js m
58+ ( HasJSContext (Performable m)
54− , DomBuilderSpace m ~ GhcjsDomSpace
59+ , HasJSContext m
60+ , MonadFix m
61+ , MonadHold t m
62+ , MonadJSM (Performable m)
63+ , MonadJSM m
64+ , MonadRef (Performable m)
65+ , MonadRef m
66+ , MonadReflexCreateTrigger t m
67+ , MonadSample t (Performable m)
68+ , PerformEvent t m
69+ , PostBuild t m
70+ , PrimMonad m
71+ , Ref (Performable m) ~ IORef
72+ , Ref m ~ IORef
55 )
73 )
56
74
75+-- | Render the first widget on the server, and the second on the client. The
76+-- hydration builder will run *both* widgets, updating the result dynamic at
77+-- switchover time.
78+prerender
79+ :: (Functor m, Prerender t m)
80+ => m a -> ((PrerenderClientConstraint t (Client m)) => Client m a) -> m (Dynamic t a)
81+prerender server client = snd <$> prerenderImpl ((,) () <$> server) client
82+
83+-- | Render the first widget on the server, and the second on the client. The
84+-- hydration builder will run *both* widgets.
85+prerender_
86+ :: (Functor m, Reflex t, Prerender t m)
87+ => m () -> ((PrerenderClientConstraint t (Client m)) => Client m ()) -> m ()
88+prerender_ server client = void $ prerenderImpl ((,) () <$> server) client
89+
57−class Prerender js m | m -> js where
90+class Prerender t m | m -> t where
58− prerenderClientDict :: Maybe (Dict (PrerenderClientConstraint js m))
91+ -- | Monad in which the client widget is built
59− prerenderWrapper :: m (m ())
92+ type Client m :: * -> *
60− default prerenderWrapper :: (MonadTrans t, Monad n, m ~ t n, Prerender js n) => m (m ())
93+ -- | This function should be considered internal: it should only be used where
61− prerenderWrapper = lift $ lift <$> prerenderWrapper
94+ -- the resultant 'Maybe' isn't used to change what is built in the DOM, or
62−
95+ -- hydration will fail.
63−-- | Draw one widget when prerendering (e.g. server-side) and another when the
64−-- widget is fully instantiated. In a given execution of this function, there
65−-- will be exactly one invocation of exactly one of the arguments.
66−prerender :: forall js m a. (Prerender js m, Monad m) => m a -> (PrerenderClientConstraint js m => m a) -> m a
96+ prerenderImpl :: m (a, b) -> ((PrerenderClientConstraint t (Client m)) => Client m b) -> m (Maybe a, Dynamic t b)
67−prerender server client = do
97+
68− finalize <- prerenderWrapper
98+instance (Adjustable t m, PrerenderBaseConstraints t m) => Prerender t (ImmediateDomBuilderT t m) where
69− a <- case prerenderClientDict :: Maybe (Dict (PrerenderClientConstraint js m)) of
99+ type Client (ImmediateDomBuilderT t m) = ImmediateDomBuilderT t m
70− Nothing -> server
100+ prerenderImpl _ client = (,) Nothing . pure <$> client
71− Just Dict -> client
101+
72− finalize
73− pure a
74−
75−instance (PrerenderClientConstraint' js m, ReflexHost t) => Prerender js (ImmediateDomBuilderT t m) where
102+instance (Adjustable t m, PrerenderBaseConstraints t m, ReflexHost t) => Prerender t (HydrationDomBuilderT t m) where
76− prerenderClientDict = Just Dict
103+ -- | PostBuildT is needed here because we delay running the immediate builder
77− prerenderWrapper = pure (pure ())
104+ -- until after switchover, at which point the postBuild of @m@ has already fired
78−
105+ type Client (HydrationDomBuilderT t m) = PostBuildT t (ImmediateDomBuilderT t m)
79−instance (PrerenderClientConstraint' js m, SupportsImmediateDomBuilder t m, ReflexHost t, PerformEvent t m) => Prerender js (HydrationDomBuilderT t m) where
106+ -- | Runs the server widget up until switchover, then replaces it with the
80− prerenderClientDict = Just Dict
107+ -- client widget.
81− prerenderWrapper = getHydrationMode >>= \case
108+ prerenderImpl server client = do
82− HydrationMode_Immediate -> pure (pure ())
83− HydrationMode_Hydrating -> do
84− parent <- HydrationDomBuilderT $ asks _hydrationDomBuilderEnv_parent
109+ env <- HydrationDomBuilderT ask
85− hydrationMode <- HydrationDomBuilderT $ asks _hydrationDomBuilderEnv_hydrationMode
110+ events <- askEvents
111+ doc <- askDocument
86− df <- Document.createDocumentFragment =<< askDocument
112+ df <- Document.createDocumentFragment doc
87− initialParent <- liftIO $ readIORef parent
113+ unreadyChildren <- HydrationDomBuilderT $ asks _hydrationDomBuilderEnv_unreadyChildren
88− liftIO $ writeIORef parent $ DOM.toNode df
114+ let immediateEnv = ImmediateDomBuilderEnv
89− liftIO $ writeIORef hydrationMode HydrationMode_Immediate
115+ { _immediateDomBuilderEnv_document = doc
90− pure $ do
116+ , _immediateDomBuilderEnv_parent = DOM.toNode df
91− liftIO $ writeIORef parent initialParent
117+ , _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
92− liftIO $ writeIORef hydrationMode HydrationMode_Hydrating
118+ , _immediateDomBuilderEnv_commitAction = pure ()
119+ }
120+ (b', trigger) <- newTriggerEvent
93− addHydrationStep $ do
121+ addHydrationStep $ do
94− -- Delete up to the end marker
122+ liftIO . trigger <=< lift $ runImmediateDomBuilderT (runPostBuildT client $ void b') immediateEnv events
95− after <- deleteToPrerenderEnd
123+ insertBefore df =<< deleteToPrerenderEnd
96− insertBefore df after
124+ ((a, b0), _) <- lift $ runHydrationDomBuilderT server env events
125+ (,) (Just a) <$> holdDyn b0 b'
126+
127+instance SupportsStaticDomBuilder t m => Prerender t (StaticDomBuilderT t m) where
128+ type Client (StaticDomBuilderT t m) = ImmediateDomBuilderT t m
129+ prerenderImpl server _ = do
130+ _ <- element "script" (def & initialAttributes .~ Map.singleton "type" startMarker) $ pure ()
131+ (a, b) <- server
132+ _ <- element "script" (def & initialAttributes .~ Map.singleton "type" endMarker) $ pure ()
133+ pure (Just a, pure b)
134+
135+instance (Prerender t m, Monad m) => Prerender t (ReaderT r m) where
136+ type Client (ReaderT r m) = Client m
137+ prerenderImpl server client = do
138+ r <- ask
139+ lift $ prerenderImpl (runReaderT server r) client
140+
141+instance (Prerender t m, Monad m, Functor (Client m)) => Prerender t (StateT s m) where
142+ type Client (StateT s m) = Client m
143+ prerenderImpl server client = do
144+ s <- get
145+ (ma, evt) <- lift $ prerenderImpl
146+ (do ((a, b), s') <- runStateT server s; pure ((a, s'), b))
147+ client
148+ traverse_ (put . snd) ma
149+ pure (fst <$> ma, evt)
150+
151+instance (Prerender t m, Monad m, Functor (Client m)) => Prerender t (DynamicWriterT t w m) where
152+ type Client (DynamicWriterT t w m) = Client m
153+ prerenderImpl (DynamicWriterT server) client = DynamicWriterT $ prerenderImpl server client
154+
155+instance (Prerender t m, Monad m, Functor (Client m)) => Prerender t (EventWriterT t w m) where
156+ type Client (EventWriterT t w m) = Client m
157+ prerenderImpl (EventWriterT server) client = EventWriterT $ prerenderImpl server client
158+
159+instance (Prerender t m, Monad m, Functor (Client m)) => Prerender t (RequesterT t request response m) where
160+ type Client (RequesterT t request response m) = Client m
161+ prerenderImpl (RequesterT server) client = RequesterT $ prerenderImpl server client
162+
163+instance (Prerender t m, Monad m, Functor (Client m)) => Prerender t (QueryT t q m) where
164+ type Client (QueryT t q m) = Client m
165+ prerenderImpl (QueryT server) client = QueryT $ prerenderImpl server client
166+
167+instance (Prerender t m, Monad m) => Prerender t (InputDisabledT m) where
168+ type Client (InputDisabledT m) = Client m
169+ prerenderImpl (InputDisabledT server) client = InputDisabledT $ prerenderImpl server client
170+
171+instance (Prerender t m, Monad m) => Prerender t (PostBuildT t m) where
172+ type Client (PostBuildT t m) = Client m
173+ prerenderImpl (PostBuildT server) client = PostBuildT $ prerenderImpl server client
97
174
98startMarker, endMarker :: IsString s => s
175startMarker, endMarker :: IsString s => s
99startMarker = "prerender/start"
176startMarker = "prerender/start"
@@ -125,33 +202,3 @@ deleteToPrerenderEnd = do
125 deleteBetweenExclusive startNode endNode
202 deleteBetweenExclusive startNode endNode
126 setPreviousNode $ Just $ DOM.toNode endNode -- TODO probably not necessary
203 setPreviousNode $ Just $ DOM.toNode endNode -- TODO probably not necessary
127 pure endNode
204 pure endNode
128−
129−data NoJavascript -- This type should never have a HasJS instance
130−
131−instance (js ~ NoJavascript, SupportsStaticDomBuilder t m) => Prerender js (StaticDomBuilderT t m) where
132− prerenderClientDict = Nothing
133− prerenderWrapper = do
134− _ <- element "script" (def & initialAttributes .~ Map.singleton "type" startMarker) $ pure ()
135− pure $ void $ element "script" (def & initialAttributes .~ Map.singleton "type" endMarker) $ pure ()
136−
137−instance (Prerender js m, Monad m, ReflexHost t) => Prerender js (PostBuildT t m) where
138− prerenderClientDict = fmap (\Dict -> Dict) (prerenderClientDict :: Maybe (Dict (PrerenderClientConstraint js m)))
139−
140−instance (Prerender js m, Monad m) => Prerender js (DynamicWriterT t w m) where
141− prerenderClientDict = fmap (\Dict -> Dict) (prerenderClientDict :: Maybe (Dict (PrerenderClientConstraint js m)))
142−
143−instance (Prerender js m, Monad m) => Prerender js (EventWriterT t w m) where
144− prerenderClientDict = fmap (\Dict -> Dict) (prerenderClientDict :: Maybe (Dict (PrerenderClientConstraint js m)))
145−
146−instance (Prerender js m, Monad m) => Prerender js (ReaderT w m) where
147− prerenderClientDict = fmap (\Dict -> Dict) (prerenderClientDict :: Maybe (Dict (PrerenderClientConstraint js m)))
148−
149−instance (Prerender js m, Monad m) => Prerender js (RequesterT t request response m) where
150− prerenderClientDict = fmap (\Dict -> Dict) (prerenderClientDict :: Maybe (Dict (PrerenderClientConstraint js m)))
151−
152−instance (Prerender js m, Monad m) => Prerender js (QueryT t q m) where
153− prerenderClientDict = fmap (\Dict -> Dict) (prerenderClientDict :: Maybe (Dict (PrerenderClientConstraint js m)))
154−
155−instance (Prerender js m, Monad m) => Prerender js (InputDisabledT m) where
156− prerenderClientDict = fmap (\Dict -> Dict) (prerenderClientDict :: Maybe (Dict (PrerenderClientConstraint js m)))
157−