@@ -5,6 +5,7 @@
5{-# LANGUAGE EmptyDataDecls #-}
5{-# LANGUAGE EmptyDataDecls #-}
6{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE FlexibleContexts #-}
7{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE FlexibleInstances #-}
8{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9{-# LANGUAGE LambdaCase #-}
10{-# LANGUAGE LambdaCase #-}
10{-# LANGUAGE MultiParamTypeClasses #-}
11{-# LANGUAGE MultiParamTypeClasses #-}
@@ -48,11 +49,11 @@ module Reflex.Dom.Builder.Immediate
48 , Pair1 (..)
49 , Pair1 (..)
49 , Maybe1 (..)
50 , Maybe1 (..)
50 , GhcjsEventSpec (..)
51 , GhcjsEventSpec (..)
52+ , ghcjsEventSpec_filters
53+ , ghcjsEventSpec_handler
51 , GhcjsEventHandler (..)
54 , GhcjsEventHandler (..)
52#ifndef USE_TEMPLATE_HASKELL
55#ifndef USE_TEMPLATE_HASKELL
53 , phantom2
56 , phantom2
54− , ghcjsEventSpec_filters
55− , ghcjsEventSpec_handler
57 , drawChildUpdate
58 , drawChildUpdate
58 , mkHasFocus
59 , mkHasFocus
@@ -74,8 +75,6 @@ module Reflex.Dom.Builder.Immediate
74 , WindowConfig (..)
75 , WindowConfig (..)
75 , Window (..)
76 , Window (..)
76 , wrapWindow
77 , wrapWindow
77− , ghcjsEventSpec_filters
78− , ghcjsEventSpec_handler
81import Foreign.JavaScript.TH
80import Foreign.JavaScript.TH
@@ -83,6 +82,8 @@ import Reflex.Class as Reflex
83import Reflex.Dom.Builder.Class
82import Reflex.Dom.Builder.Class
84import Reflex.Dynamic
83import Reflex.Dynamic
85import Reflex.Host.Class
84import Reflex.Host.Class
85+import qualified Reflex.Patch.DMap as PatchDMap
86+import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
86import Reflex.PerformEvent.Class
87import Reflex.PerformEvent.Class
87import Reflex.PostBuild.Class
88import Reflex.PostBuild.Class
88import Reflex.TriggerEvent.Base hiding (askEvents)
89import Reflex.TriggerEvent.Base hiding (askEvents)
@@ -106,10 +107,12 @@ import Data.Dependent.Sum
106import Data.Functor.Compose
107import Data.Functor.Compose
107import Data.Functor.Constant
108import Data.Functor.Constant
108import Data.Functor.Misc
109import Data.Functor.Misc
110+import Data.Functor.Product
109import Data.IORef
111import Data.IORef
110import qualified Data.Map as Map
112import qualified Data.Map as Map
111import Data.Maybe
113import Data.Maybe
112−import Data.Monoid
114+import Data.Monoid hiding (Product)
115+import Data.Some (Some)
113import qualified Data.Some as Some
116import qualified Data.Some as Some
114import Data.Text (Text)
117import Data.Text (Text)
115import qualified Data.Text as T
118import qualified Data.Text as T
@@ -144,12 +147,14 @@ import Reflex.Requester.Base
144import Reflex.Requester.Class
147import Reflex.Requester.Class
145import Foreign.JavaScript.Internal.Utils
148import Foreign.JavaScript.Internal.Utils
147−data ImmediateDomBuilderEnv t m
150+data ImmediateDomBuilderEnv t
148 = ImmediateDomBuilderEnv { _immediateDomBuilderEnv_document :: Document
151 = ImmediateDomBuilderEnv { _immediateDomBuilderEnv_document :: Document
149 , _immediateDomBuilderEnv_parent :: Node
152 , _immediateDomBuilderEnv_parent :: Node
153+ , _immediateDomBuilderEnv_unreadyChildren :: IORef Word -- Number of children who still aren't fully rendered
154+ , _immediateDomBuilderEnv_commitAction :: JSM () -- Action to take when all children are ready --TODO: we should probably get rid of this once we invoke it
152−newtype ImmediateDomBuilderT t m a = ImmediateDomBuilderT { unImmediateDomBuilderT :: ReaderT (ImmediateDomBuilderEnv t m) (RequesterT t JSM Identity (TriggerEventT t m)) a }
157+newtype ImmediateDomBuilderT t m a = ImmediateDomBuilderT { unImmediateDomBuilderT :: ReaderT (ImmediateDomBuilderEnv t) (RequesterT t JSM Identity (TriggerEventT t m)) a }
153 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
158 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
155#ifndef __GHCJS__
160#ifndef __GHCJS__
@@ -164,7 +169,7 @@ instance PrimMonad m => PrimMonad (ImmediateDomBuilderT x m) where
164instance MonadTrans (ImmediateDomBuilderT t) where
169instance MonadTrans (ImmediateDomBuilderT t) where
165 lift = ImmediateDomBuilderT . lift . lift . lift
170 lift = ImmediateDomBuilderT . lift . lift . lift
167−instance (PerformEvent t m, PrimMonad m) => DomRenderHook t (ImmediateDomBuilderT t m) where
172+instance (Reflex t, PrimMonad m) => DomRenderHook t (ImmediateDomBuilderT t m) where
168 withRenderHook hook (ImmediateDomBuilderT a) = do
173 withRenderHook hook (ImmediateDomBuilderT a) = do
169 e <- ImmediateDomBuilderT ask
174 e <- ImmediateDomBuilderT ask
170 ImmediateDomBuilderT $ lift $ withRequesting $ \rsp -> do
175 ImmediateDomBuilderT $ lift $ withRequesting $ \rsp -> do
@@ -185,7 +190,7 @@ runImmediateDomBuilderT
185 , Ref m ~ IORef
190 , Ref m ~ IORef
187 => ImmediateDomBuilderT t m a
192 => ImmediateDomBuilderT t m a
188− -> ImmediateDomBuilderEnv t m
193+ -> ImmediateDomBuilderEnv t
189 -> Chan [DSum (EventTriggerRef t) TriggerInvocation]
194 -> Chan [DSum (EventTriggerRef t) TriggerInvocation]
191runImmediateDomBuilderT (ImmediateDomBuilderT a) env eventChan = flip runTriggerEventT eventChan $ do
196runImmediateDomBuilderT (ImmediateDomBuilderT a) env eventChan = flip runTriggerEventT eventChan $ do
@@ -215,6 +220,9 @@ askParent = ImmediateDomBuilderT $ asks _immediateDomBuilderEnv_parent
215askEvents :: Monad m => ImmediateDomBuilderT t m (Chan [DSum (EventTriggerRef t) TriggerInvocation])
220askEvents :: Monad m => ImmediateDomBuilderT t m (Chan [DSum (EventTriggerRef t) TriggerInvocation])
216askEvents = ImmediateDomBuilderT . lift . lift $ TriggerEventT.askEvents
221askEvents = ImmediateDomBuilderT . lift . lift $ TriggerEventT.askEvents
223+localEnv :: Monad m => (ImmediateDomBuilderEnv t -> ImmediateDomBuilderEnv t) -> ImmediateDomBuilderT t m a -> ImmediateDomBuilderT t m a
224+localEnv f = ImmediateDomBuilderT . local f . unImmediateDomBuilderT
218{-# INLINABLE append #-}
226{-# INLINABLE append #-}
219append :: (IsNode n, MonadJSM m) => n -> ImmediateDomBuilderT t m ()
227append :: (IsNode n, MonadJSM m) => n -> ImmediateDomBuilderT t m ()
220append n = do
228append n = do
@@ -278,7 +286,7 @@ extractUpTo df s e = liftJSM $ do
279 void $ call f f (df, s, e)
287 void $ call f f (df, s, e)
281−type SupportsImmediateDomBuilder t m = (Reflex t, MonadJSM m, MonadJSM (Performable m), MonadHold t m, MonadFix m, PerformEvent t m, MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref JSM, MonadAdjust t m, PrimMonad m)
289+type SupportsImmediateDomBuilder t m = (Reflex t, MonadJSM m, MonadJSM (Performable m), MonadHold t m, MonadFix m, MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref JSM, MonadAdjust t m, PrimMonad m)
283{-# INLINABLE collectUpTo #-}
291{-# INLINABLE collectUpTo #-}
284collectUpTo :: (MonadJSM m, IsNode start, IsNode end) => start -> end -> m DOM.DocumentFragment
292collectUpTo :: (MonadJSM m, IsNode start, IsNode end) => start -> end -> m DOM.DocumentFragment
@@ -299,9 +307,9 @@ newtype EventFilterTriggerRef t er (en :: EventTag) = EventFilterTriggerRef (IOR
299wrap :: forall m er t. SupportsImmediateDomBuilder t m => RawElement GhcjsDomSpace -> RawElementConfig er t (ImmediateDomBuilderT t m) -> ImmediateDomBuilderT t m (Element er GhcjsDomSpace t)
307wrap :: forall m er t. SupportsImmediateDomBuilder t m => RawElement GhcjsDomSpace -> RawElementConfig er t (ImmediateDomBuilderT t m) -> ImmediateDomBuilderT t m (Element er GhcjsDomSpace t)
300wrap e cfg = do
308wrap e cfg = do
301 events <- askEvents
309 events <- askEvents
302− mapM_ (requestDomAction_ . fmap (imapM_ $ \(AttributeName mAttrNamespace n) mv -> case mAttrNamespace of
310+ forM_ (_rawElementConfig_modifyAttributes cfg) $ \modifyAttrs -> requestDomAction_ $ ffor modifyAttrs $ imapM_ $ \(AttributeName mAttrNamespace n) mv -> case mAttrNamespace of
303 Nothing -> maybe (removeAttribute e n) (setAttribute e n) mv
311 Nothing -> maybe (removeAttribute e n) (setAttribute e n) mv
304− Just ns -> maybe (removeAttributeNS e (Just ns) n) (setAttributeNS e (Just ns) n) mv)) (_rawElementConfig_modifyAttributes cfg)
312+ Just ns -> maybe (removeAttributeNS e (Just ns) n) (setAttributeNS e (Just ns) n) mv
305 eventTriggerRefs :: DMap EventName (EventFilterTriggerRef t er) <- liftJSM $ fmap DMap.fromList $ forM (DMap.toList $ _ghcjsEventSpec_filters $ _rawElementConfig_eventSpec cfg) $ \(en :=> GhcjsEventFilter f) -> do
313 eventTriggerRefs :: DMap EventName (EventFilterTriggerRef t er) <- liftJSM $ fmap DMap.fromList $ forM (DMap.toList $ _ghcjsEventSpec_filters $ _rawElementConfig_eventSpec cfg) $ \(en :=> GhcjsEventFilter f) -> do
306 triggerRef <- liftIO $ newIORef Nothing
314 triggerRef <- liftIO $ newIORef Nothing
307 _ <- elementOnEventName en e $ do
315 _ <- elementOnEventName en e $ do
@@ -334,20 +342,22 @@ wrap e cfg = do
334 --TODO: I don't think this is quite right: if a new trigger is created between when this is enqueued and when it fires, this may not work quite right
342 --TODO: I don't think this is quite right: if a new trigger is created between when this is enqueued and when it fires, this may not work quite right
335 ref <- newIORef $ Just t
343 ref <- newIORef $ Just t
336 writeChan events [EventTriggerRef ref :=> TriggerInvocation v (return ())])
344 writeChan events [EventTriggerRef ref :=> TriggerInvocation v (return ())])
337− return $ Element es e
345+ return $ Element
346+ { _element_events = es
339{-# INLINABLE makeElement #-}
350{-# INLINABLE makeElement #-}
340makeElement :: forall er t m a. SupportsImmediateDomBuilder t m => Text -> ElementConfig er t (ImmediateDomBuilderT t m) -> ImmediateDomBuilderT t m a -> ImmediateDomBuilderT t m ((Element er GhcjsDomSpace t, a), DOM.Element)
351makeElement :: forall er t m a. SupportsImmediateDomBuilder t m => Text -> ElementConfig er t (ImmediateDomBuilderT t m) -> ImmediateDomBuilderT t m a -> ImmediateDomBuilderT t m ((Element er GhcjsDomSpace t, a), DOM.Element)
341makeElement elementTag cfg child = do
352makeElement elementTag cfg child = do
342 doc <- askDocument
353 doc <- askDocument
343− env <- ImmediateDomBuilderT ask
344 e <- liftJSM $ case cfg ^. namespace of
354 e <- liftJSM $ case cfg ^. namespace of
345 Nothing -> createElementUnchecked doc (Just elementTag)
355 Nothing -> createElementUnchecked doc (Just elementTag)
346 Just ens -> createElementNSUnchecked doc (Just ens) (Just elementTag)
356 Just ens -> createElementNSUnchecked doc (Just ens) (Just elementTag)
347 ImmediateDomBuilderT $ iforM_ (cfg ^. initialAttributes) $ \(AttributeName mAttrNamespace n) v -> case mAttrNamespace of
357 ImmediateDomBuilderT $ iforM_ (cfg ^. initialAttributes) $ \(AttributeName mAttrNamespace n) v -> case mAttrNamespace of
348 Nothing -> lift $ setAttribute e n v
358 Nothing -> lift $ setAttribute e n v
349 Just ans -> lift $ setAttributeNS e (Just ans) n v
359 Just ans -> lift $ setAttributeNS e (Just ans) n v
350− result <- ImmediateDomBuilderT $ lift $ runReaderT (unImmediateDomBuilderT child) $ env
360+ result <- flip localEnv child $ \env -> env
351 { _immediateDomBuilderEnv_parent = toNode e
361 { _immediateDomBuilderEnv_parent = toNode e
@@ -435,7 +445,7 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
435 Input.setValue domInputElement $ Just (cfg ^. inputElementConfig_initialValue)
445 Input.setValue domInputElement $ Just (cfg ^. inputElementConfig_initialValue)
436 v0 <- Input.getValueUnchecked domInputElement
446 v0 <- Input.getValueUnchecked domInputElement
437 let getMyValue = fromMaybe "" <$> Input.getValue domInputElement
447 let getMyValue = fromMaybe "" <$> Input.getValue domInputElement
438− valueChangedByUI <- performEvent $ liftJSM getMyValue <$ Reflex.select (_element_events e) (WrapArg Input)
448+ valueChangedByUI <- requestDomAction $ liftJSM getMyValue <$ Reflex.select (_element_events e) (WrapArg Input)
439 valueChangedBySetValue <- case _inputElementConfig_setValue cfg of
449 valueChangedBySetValue <- case _inputElementConfig_setValue cfg of
440 Nothing -> return never
450 Nothing -> return never
441 Just eSetValue -> requestDomAction $ ffor eSetValue $ \v' -> do
451 Just eSetValue -> requestDomAction $ ffor eSetValue $ \v' -> do
@@ -486,7 +496,7 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
486 TextArea.setValue domTextAreaElement $ Just (cfg ^. textAreaElementConfig_initialValue)
496 TextArea.setValue domTextAreaElement $ Just (cfg ^. textAreaElementConfig_initialValue)
487 v0 <- TextArea.getValueUnchecked domTextAreaElement
497 v0 <- TextArea.getValueUnchecked domTextAreaElement
488 let getMyValue = fromMaybe "" <$> TextArea.getValue domTextAreaElement
498 let getMyValue = fromMaybe "" <$> TextArea.getValue domTextAreaElement
489− valueChangedByUI <- performEvent $ liftJSM getMyValue <$ Reflex.select (_element_events e) (WrapArg Input)
499+ valueChangedByUI <- requestDomAction $ liftJSM getMyValue <$ Reflex.select (_element_events e) (WrapArg Input)
490 valueChangedBySetValue <- case _textAreaElementConfig_setValue cfg of
500 valueChangedBySetValue <- case _textAreaElementConfig_setValue cfg of
491 Nothing -> return never
501 Nothing -> return never
492 Just eSetValue -> requestDomAction $ ffor eSetValue $ \v' -> do
502 Just eSetValue -> requestDomAction $ ffor eSetValue $ \v' -> do
@@ -511,7 +521,7 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
511 Select.setValue domSelectElement $ Just (cfg ^. selectElementConfig_initialValue)
521 Select.setValue domSelectElement $ Just (cfg ^. selectElementConfig_initialValue)
512 Just v0 <- Select.getValue domSelectElement
522 Just v0 <- Select.getValue domSelectElement
513 let getMyValue = fromMaybe "" <$> Select.getValue domSelectElement
523 let getMyValue = fromMaybe "" <$> Select.getValue domSelectElement
514− valueChangedByUI <- performEvent $ liftJSM getMyValue <$ Reflex.select (_element_events e) (WrapArg Change)
524+ valueChangedByUI <- requestDomAction $ liftJSM getMyValue <$ Reflex.select (_element_events e) (WrapArg Change)
515 valueChangedBySetValue <- case _selectElementConfig_setValue cfg of
525 valueChangedBySetValue <- case _selectElementConfig_setValue cfg of
516 Nothing -> return never
526 Nothing -> return never
517 Just eSetValue -> requestDomAction $ ffor eSetValue $ \v' -> do
527 Just eSetValue -> requestDomAction $ ffor eSetValue $ \v' -> do
@@ -532,96 +542,304 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
532 return (wrapped, result)
542 return (wrapped, result)
533 placeRawElement = append
543 placeRawElement = append
534 wrapRawElement = wrap
544 wrapRawElement = wrap
545+ notReadyUntil e = do
547+ env <- ImmediateDomBuilderT ask
548+ let unreadyChildren = _immediateDomBuilderEnv_unreadyChildren env
549+ liftIO $ modifyIORef' unreadyChildren succ
551+ old <- liftIO $ readIORef unreadyChildren
553+ liftIO $ writeIORef unreadyChildren $! new
554+ when (new == 0) $ _immediateDomBuilderEnv_commitAction env
555+ requestDomAction_ $ ready <$ eOnce
557+ env <- ImmediateDomBuilderT ask
558+ let unreadyChildren = _immediateDomBuilderEnv_unreadyChildren env
559+ liftIO $ modifyIORef' unreadyChildren succ
562+ = FragmentState_Unmounted
563+ | FragmentState_Mounted (DOM.Text, DOM.Text)
565+data ImmediateDomFragment = ImmediateDomFragment
566+ { _immediateDomFragment_document :: DOM.DocumentFragment
567+ , _immediateDomFragment_state :: IORef FragmentState
570+extractFragment :: MonadJSM m => ImmediateDomFragment -> m ()
571+extractFragment fragment = do
572+ state <- liftIO $ readIORef $ _immediateDomFragment_state fragment
574+ FragmentState_Unmounted -> return ()
575+ FragmentState_Mounted (before, after) -> do
576+ extractBetweenExclusive (_immediateDomFragment_document fragment) before after
577+ liftIO $ writeIORef (_immediateDomFragment_state fragment) FragmentState_Unmounted
579+instance SupportsImmediateDomBuilder t m => MountableDomBuilder t (ImmediateDomBuilderT t m) where
580+ type DomFragment (ImmediateDomBuilderT t m) = ImmediateDomFragment
581+ buildDomFragment w = do
582+ df <- createDocumentFragmentUnchecked =<< askDocument
583+ result <- flip localEnv w $ \env -> env
584+ { _immediateDomBuilderEnv_parent = toNode df
586+ state <- liftIO $ newIORef FragmentState_Unmounted
587+ return (ImmediateDomFragment df state, result)
588+ mountDomFragment fragment setFragment = do
590+ extractFragment fragment
591+ before <- textNodeInternal ("" :: Text)
592+ appendChild_ parent $ Just $ _immediateDomFragment_document fragment
593+ after <- textNodeInternal ("" :: Text)
594+ xs <- foldDyn (\new (previous, _) -> (new, Just previous)) (fragment, Nothing) setFragment
595+ requestDomAction_ $ ffor (updated xs) $ \(childFragment, Just previousFragment) -> do
596+ extractFragment previousFragment
597+ extractFragment childFragment
598+ insertBefore (_immediateDomFragment_document childFragment) after
599+ liftIO $ writeIORef (_immediateDomFragment_state childFragment) $ FragmentState_Mounted (before, after)
600+ liftIO $ writeIORef (_immediateDomFragment_state fragment) $ FragmentState_Mounted (before, after)
536−instance (Reflex t, MonadAdjust t m, MonadJSM m, MonadHold t m, PerformEvent t m, MonadJSM (Performable m), MonadFix m, PrimMonad m) => MonadAdjust t (ImmediateDomBuilderT t m) where
602+instance (Reflex t, MonadAdjust t m, MonadJSM m, MonadHold t m, MonadJSM (Performable m), MonadFix m, PrimMonad m) => MonadAdjust t (ImmediateDomBuilderT t m) where
537 runWithReplace a0 a' = do
603 runWithReplace a0 a' = do
538 initialEnv <- ImmediateDomBuilderT ask
604 initialEnv <- ImmediateDomBuilderT ask
539 before <- textNodeInternal ("" :: Text)
605 before <- textNodeInternal ("" :: Text)
606+ let parentUnreadyChildren = _immediateDomBuilderEnv_unreadyChildren initialEnv
607+ haveEverBeenReady <- liftIO $ newIORef False
608+ currentCohort <- liftIO $ newIORef (-1 :: Int) -- Equal to the cohort currently in the DOM
609+ let myCommitAction = do
610+ liftIO (readIORef haveEverBeenReady) >>= \case
613+ liftIO $ writeIORef haveEverBeenReady True
614+ old <- liftIO $ readIORef parentUnreadyChildren
616+ liftIO $ writeIORef parentUnreadyChildren $! new
617+ when (new == 0) $ _immediateDomBuilderEnv_commitAction initialEnv
540 -- We draw 'after' in this roundabout way to avoid using MonadFix
618 -- We draw 'after' in this roundabout way to avoid using MonadFix
541− after <- createTextNodeUnchecked (_immediateDomBuilderEnv_document initialEnv) ("" :: Text)
620+ after <- createTextNodeUnchecked doc ("" :: Text)
542 let drawInitialChild = do
621 let drawInitialChild = do
622+ unreadyChildren <- liftIO $ newIORef 0
543− result <- a0
624+ result <- a0
544− append after
625+ append after
627+ result <- runReaderT (unImmediateDomBuilderT f) $ initialEnv
628+ { _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
629+ , _immediateDomBuilderEnv_commitAction = myCommitAction
631+ liftIO $ readIORef unreadyChildren >>= \case
632+ 0 -> writeIORef haveEverBeenReady True
633+ _ -> modifyIORef' parentUnreadyChildren succ
545 return result
634 return result
635+ a'' <- numberOccurrences a'
546− (result0, result') <- ImmediateDomBuilderT $ lift $ runWithReplace (runReaderT (unImmediateDomBuilderT drawInitialChild) initialEnv) $ ffor a' $ \child -> do
636+ (result0, result') <- ImmediateDomBuilderT $ lift $ runWithReplace drawInitialChild $ ffor a'' $ \(cohortId, child) -> do
547− df <- createDocumentFragmentUnchecked $ _immediateDomBuilderEnv_document initialEnv
637+ df <- createDocumentFragmentUnchecked doc
638+ unreadyChildren <- liftIO $ newIORef 0
639+ let commitAction = do
640+ c <- liftIO $ readIORef currentCohort
641+ when (c <= cohortId) $ do -- If a newer cohort has already been committed, just ignore this
642+ deleteBetweenExclusive before after
643+ insertBefore df after
644+ liftIO $ writeIORef currentCohort cohortId
548 result <- runReaderT (unImmediateDomBuilderT child) $ initialEnv
646 result <- runReaderT (unImmediateDomBuilderT child) $ initialEnv
549 { _immediateDomBuilderEnv_parent = toNode df
647 { _immediateDomBuilderEnv_parent = toNode df
648+ , _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
649+ , _immediateDomBuilderEnv_commitAction = commitAction
551− let update = do
651+ liftIO (readIORef unreadyChildren) >>= \case
552− deleteBetweenExclusive before after
652+ 0 -> liftJSM commitAction
553− insertBefore df after
653+ _ -> return () -- Whoever decrements it to 0 will handle it
554− return (result, update)
654+ return result
555− requestDomAction_ $ snd <$> result'
556− return (result0, fst <$> result')
557− traverseDMapWithKeyWithAdjust (f :: forall a. k a -> v a -> ImmediateDomBuilderT t m (v' a)) (dm0 :: DMap k v) dm' = do
558− initialEnv <- ImmediateDomBuilderT ask
559− (children0, children') <- ImmediateDomBuilderT $ lift $ traverseDMapWithKeyWithAdjust (\k v -> drawChildUpdate initialEnv $ f k v) dm0 dm'
560− let result0 = DMap.map (\(Compose (_, _, v)) -> v) children0
561− placeholders0 = weakenDMapWith (\(Compose (_, ph, _)) -> ph) children0
562− result' = ffor children' $ mapPatchDMap $ \(Compose (_, _, r)) -> r
563− placeholders' = fforCheap children' $ weakenPatchDMapWith $ \(Compose (_, ph, _)) -> ph
564− placeholders <- current . incrementalToDynamic <$> holdIncremental placeholders0 placeholders'
565− _ <- DMap.traverseWithKey (\_ (Compose (df, _, _)) -> Constant () <$ append df) children0
566− lastPlaceholder <- textNodeInternal ("" :: Text)
567− requestDomAction_ $ flip push children' $ \(PatchDMap p) -> do
568− phs <- sample placeholders
569− if DMap.null p then return Nothing else return $ Just $ do
570− let g :: DSum k (ComposeMaybe (Compose ((,,) DOM.DocumentFragment DOM.Text) v')) -> JSM ()
571− g (k :=> ComposeMaybe mv) = do
572− let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phs
573− forM_ (Map.lookup (Some.This k) phs) $ \thisPlaceholder -> thisPlaceholder `deleteUpTo` nextPlaceholder
574− forM_ mv $ \(Compose (df, _, _)) -> df `insertBefore` nextPlaceholder
575− mapM_ g $ DMap.toList p
576 return (result0, result')
655 return (result0, result')
577− traverseDMapWithKeyWithAdjustWithMove (f :: forall a. k a -> v a -> ImmediateDomBuilderT t m (v' a)) (dm0 :: DMap k v) dm' = do
656+ traverseDMapWithKeyWithAdjust = do
578− initialEnv <- ImmediateDomBuilderT ask
657+ let updateChildUnreadiness (p :: PatchDMap k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v')) old = do
579− (children0, children') <- ImmediateDomBuilderT $ lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> drawChildUpdate initialEnv $ f k v) dm0 dm'
658+ let new :: forall a. k a -> ComposeMaybe (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') a -> IO (ComposeMaybe (Constant (IORef (ChildReadyState k))) a)
580− let result0 = DMap.map (\(Compose (_, _, v)) -> v) children0
659+ new k (ComposeMaybe m) = ComposeMaybe <$> case m of
581− placeholders0 = weakenDMapWith (\(Compose (_, ph, _)) -> ph) children0
660+ Nothing -> return Nothing
582− result' = ffor children' $ mapPatchDMapWithMove $ \(Compose (_, _, r)) -> r
661+ Just (Compose (_, _, sRef, _)) -> do
583− placeholders' = fforCheap children' $ weakenPatchDMapWithMoveWith $ \(Compose (_, ph, _)) -> ph
662+ readIORef sRef >>= \case
584− placeholders <- current . incrementalToDynamic <$> holdIncremental placeholders0 placeholders'
663+ ChildReadyState_Ready -> return Nothing -- Delete this child, since it's ready
585− _ <- DMap.traverseWithKey (\_ (Compose (df, _, _)) -> Constant () <$ append df) children0
664+ ChildReadyState_Unready _ -> do
586− lastPlaceholder <- textNodeInternal ("" :: Text)
665+ writeIORef sRef $ ChildReadyState_Unready $ Just $ Some.This k
587− requestDomAction_ $ flip push children' $ \p_ -> do
666+ return $ Just $ Constant sRef
667+ delete _ (Constant sRef) = do
668+ writeIORef sRef $ ChildReadyState_Unready Nothing
669+ return $ Constant ()
670+ p' <- fmap PatchDMap $ DMap.traverseWithKey new $ unPatchDMap p
671+ _ <- DMap.traverseWithKey delete $ PatchDMap.getDeletions p old
672+ return $ applyAlways p' old
673+ hoistTraverseWithKeyWithAdjust traverseDMapWithKeyWithAdjust mapPatchDMap updateChildUnreadiness $ \placeholders lastPlaceholderRef (PatchDMap p) -> do
674+ phs <- liftIO $ readIORef placeholders
675+ forM_ (DMap.toList p) $ \(k :=> ComposeMaybe mv) -> do
676+ lastPlaceholder <- liftIO $ readIORef lastPlaceholderRef
677+ let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phs
678+ forM_ (Map.lookup (Some.This k) phs) $ \thisPlaceholder -> thisPlaceholder `deleteUpTo` nextPlaceholder
679+ forM_ mv $ \(Compose (df, _, _, _)) -> df `insertBefore` nextPlaceholder
680+ liftIO $ writeIORef placeholders $! fromMaybe phs $ apply (weakenPatchDMapWith (\(Compose (_, ph, _, _)) -> ph) $ PatchDMap p) phs
681+ traverseDMapWithKeyWithAdjustWithMove = do
682+ let updateChildUnreadiness (p :: PatchDMapWithMove k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v')) old = do
683+ let new :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') a -> IO (PatchDMapWithMove.NodeInfo k (Constant (IORef (ChildReadyState k))) a)
684+ new k = PatchDMapWithMove.nodeInfoMapFromM $ \case
685+ PatchDMapWithMove.From_Insert (Compose (_, _, sRef, _)) -> do
686+ readIORef sRef >>= \case
687+ ChildReadyState_Ready -> return PatchDMapWithMove.From_Delete
688+ ChildReadyState_Unready _ -> do
689+ writeIORef sRef $ ChildReadyState_Unready $ Just $ Some.This k
690+ return $ PatchDMapWithMove.From_Insert $ Constant sRef
691+ PatchDMapWithMove.From_Delete -> return PatchDMapWithMove.From_Delete
692+ PatchDMapWithMove.From_Move fromKey -> return $ PatchDMapWithMove.From_Move fromKey
693+ deleteOrMove :: forall a. k a -> Product (Constant (IORef (ChildReadyState k))) (ComposeMaybe k) a -> IO (Constant () a)
694+ deleteOrMove _ (Pair (Constant sRef) (ComposeMaybe mToKey)) = do
695+ writeIORef sRef $ ChildReadyState_Unready $ Some.This <$> mToKey -- This will be Nothing if deleting, and Just if moving, so it works out in both cases
696+ return $ Constant ()
697+ p' <- fmap unsafePatchDMapWithMove $ DMap.traverseWithKey new $ unPatchDMapWithMove p
698+ _ <- DMap.traverseWithKey deleteOrMove $ PatchDMapWithMove.getDeletionsAndMoves p old
699+ return $ applyAlways p' old
700+ hoistTraverseWithKeyWithAdjust traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove updateChildUnreadiness $ \placeholders lastPlaceholderRef (p_ :: PatchDMapWithMove k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v')) -> do
588 let p = unPatchDMapWithMove p_
701 let p = unPatchDMapWithMove p_
589− phsBefore <- sample placeholders
702+ phsBefore <- liftIO $ readIORef placeholders
590− if DMap.null p then return Nothing else return $ Just $ do
703+ lastPlaceholder <- liftIO $ readIORef lastPlaceholderRef
591− let collectIfMoved :: forall a. k a -> DMapEdit k (Compose ((,,) DOM.DocumentFragment DOM.Text) v') a -> JSM (Constant (Maybe DOM.DocumentFragment) a)
704+ let collectIfMoved :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') a -> JSM (Constant (Maybe DOM.DocumentFragment) a)
592− collectIfMoved k e = do
705+ collectIfMoved k e = do
593− let mThisPlaceholder = Map.lookup (Some.This k) phsBefore -- Will be Nothing if this element wasn't present before
706+ let mThisPlaceholder = Map.lookup (Some.This k) phsBefore -- Will be Nothing if this element wasn't present before
594− nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phsBefore
707+ nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phsBefore
595− case dmapEditMoved e of
708+ case isJust $ getComposeMaybe $ PatchDMapWithMove._nodeInfo_to e of
596− False -> do
709+ False -> do
597− mapM_ (`deleteUpTo` nextPlaceholder) mThisPlaceholder
710+ mapM_ (`deleteUpTo` nextPlaceholder) mThisPlaceholder
598− return $ Constant Nothing
711+ return $ Constant Nothing
599− True -> do
712+ True -> do
600− Constant <$> mapM (`collectUpTo` nextPlaceholder) mThisPlaceholder
713+ Constant <$> mapM (`collectUpTo` nextPlaceholder) mThisPlaceholder
601− collected <- DMap.traverseWithKey collectIfMoved p
714+ collected <- DMap.traverseWithKey collectIfMoved p
602− let phsAfter = fromMaybe phsBefore $ apply (weakenPatchDMapWithMoveWith (\(Compose (_, ph, _)) -> ph) p_) phsBefore --TODO: Don't recompute this
715+ let !phsAfter = fromMaybe phsBefore $ apply (weakenPatchDMapWithMoveWith (\(Compose (_, ph, _, _)) -> ph) p_) phsBefore --TODO: Don't recompute this
603− let placeFragment :: forall a. k a -> DMapEdit k (Compose ((,,) DOM.DocumentFragment DOM.Text) v') a -> JSM (Constant () a)
716+ let placeFragment :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') a -> JSM (Constant () a)
604− placeFragment k e = do
717+ placeFragment k e = do
605− let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phsAfter
718+ let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phsAfter
606− case e of
719+ case PatchDMapWithMove._nodeInfo_from e of
607− DMapEdit_Insert _ (Compose (df, _, _)) -> do
720+ PatchDMapWithMove.From_Insert (Compose (df, _, _, _)) -> do
608− df `insertBefore` nextPlaceholder
721+ df `insertBefore` nextPlaceholder
609− DMapEdit_Delete _ -> do
722+ PatchDMapWithMove.From_Delete -> do
610− return ()
723+ return ()
611− DMapEdit_Move _ fromKey -> do
724+ PatchDMapWithMove.From_Move fromKey -> do
612− Just (Constant mdf) <- return $ DMap.lookup fromKey collected
725+ Just (Constant mdf) <- return $ DMap.lookup fromKey collected
613− mapM_ (`insertBefore` nextPlaceholder) mdf
726+ mapM_ (`insertBefore` nextPlaceholder) mdf
614− return $ Constant ()
727+ return $ Constant ()
615− mapM_ (\(k :=> v) -> void $ placeFragment k v) $ DMap.toDescList p -- We need to go in reverse order here, to make sure the placeholders are in the right spot at the right time
728+ mapM_ (\(k :=> v) -> void $ placeFragment k v) $ DMap.toDescList p -- We need to go in reverse order here, to make sure the placeholders are in the right spot at the right time
616− return ()
729+ liftIO $ writeIORef placeholders $! phsAfter
617− return (result0, result')
730+
618−
731+data ChildReadyState k
732+ = ChildReadyState_Ready
733+ | ChildReadyState_Unready !(Maybe (Some k))
734+ deriving (Show, Read, Eq, Ord)
736+hoistTraverseWithKeyWithAdjust :: forall (k :: * -> *) v v' t m p.
740+ , MonadIO (Performable m)
746+ , PatchTarget (p k (Constant Int)) ~ DMap k (Constant Int)
747+ , Monoid (p k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v'))
748+ , Patch (p k (Constant Int))
751+ (forall a. k a -> vv a -> RequesterT t JSM Identity (TriggerEventT t m) (vv' a))
754+ -> RequesterT t JSM Identity (TriggerEventT t m) (DMap k vv', Event t (p k vv'))
755+ ) -- ^ The base monad's traversal
756+ -> (forall vv vv'. (forall a. vv a -> vv' a) -> p k vv -> p k vv') -- ^ A way of mapping over the patch type
757+ -> (p k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') -> DMap k (Constant (IORef (ChildReadyState k))) -> IO (DMap k (Constant (IORef (ChildReadyState k))))) -- ^ Given a patch for the children DOM elements, produce a patch for the childrens' unreadiness state
758+ -> (IORef (Map.Map (Some.Some k) DOM.Text) -> IORef DOM.Text -> p k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') -> JSM ()) -- ^ Apply a patch to the DOM
759+ -> (forall a. k a -> v a -> ImmediateDomBuilderT t m (v' a))
762+ -> ImmediateDomBuilderT t m (DMap k v', Event t (p k v'))
763+hoistTraverseWithKeyWithAdjust base mapPatch updateChildUnreadiness applyDomUpdate_ (f :: forall a. k a -> v a -> ImmediateDomBuilderT t m (v' a)) (dm0 :: DMap k v) dm' = do
764+ initialEnv <- ImmediateDomBuilderT ask
765+ let parentUnreadyChildren = _immediateDomBuilderEnv_unreadyChildren initialEnv
766+ pendingChange :: IORef (DMap k (Constant (IORef (ChildReadyState k))), p k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v')) <- liftIO $ newIORef mempty
767+ haveEverBeenReady <- liftIO $ newIORef False
768+ placeholders <- liftIO $ newIORef $ error "placeholders not yet initialized"
769+ lastPlaceholderRef <- liftIO $ newIORef $ error "lastPlaceholderRef not yet initialized"
770+ let applyDomUpdate p = do
771+ applyDomUpdate_ placeholders lastPlaceholderRef p
773+ liftIO $ writeIORef pendingChange $! mempty
775+ liftIO (readIORef haveEverBeenReady) >>= \case
778+ liftIO $ writeIORef haveEverBeenReady True
779+ old <- liftIO $ readIORef parentUnreadyChildren
781+ liftIO $ writeIORef parentUnreadyChildren $! new
782+ when (new == 0) $ _immediateDomBuilderEnv_commitAction initialEnv
783+ markChildReady :: IORef (ChildReadyState k) -> JSM ()
784+ markChildReady childReadyState = do
785+ liftIO (readIORef childReadyState) >>= \case
786+ ChildReadyState_Ready -> return ()
787+ ChildReadyState_Unready countedAt -> do
788+ liftIO $ writeIORef childReadyState ChildReadyState_Ready
790+ Nothing -> return ()
791+ Just (Some.This k) -> do -- This child has been counted as unready, so we need to remove it from the unready set
792+ (oldUnready, p) <- liftIO $ readIORef pendingChange
793+ when (not $ DMap.null oldUnready) $ do -- This shouldn't actually ever be null
794+ let newUnready = DMap.delete k oldUnready
795+ liftIO $ writeIORef pendingChange (newUnready, p)
796+ when (DMap.null newUnready) $ do
798+ (children0, children') <- ImmediateDomBuilderT $ lift $ base (\k v -> drawChildUpdate initialEnv markChildReady $ f k v) dm0 dm'
799+ let processChild k (Compose (_, _, sRef, _)) = ComposeMaybe <$> do
800+ readIORef sRef >>= \case
801+ ChildReadyState_Ready -> return Nothing
802+ ChildReadyState_Unready _ -> do
803+ writeIORef sRef $ ChildReadyState_Unready $ Just $ Some.This k
804+ return $ Just $ Constant sRef
805+ initialUnready <- liftIO $ DMap.mapMaybeWithKey (\_ -> getComposeMaybe) <$> DMap.traverseWithKey processChild children0
806+ liftIO $ if DMap.null initialUnready
807+ then writeIORef haveEverBeenReady True
809+ modifyIORef' parentUnreadyChildren succ
810+ writeIORef pendingChange (initialUnready, mempty) -- The patch is always empty because it got applied implicitly when we ran the children the first time
811+ let result0 = DMap.map (\(Compose (_, _, _, v)) -> v) children0
812+ placeholders0 = weakenDMapWith (\(Compose (_, ph, _, _)) -> ph) children0
813+ result' = ffor children' $ mapPatch $ \(Compose (_, _, _, r)) -> r
814+ liftIO $ writeIORef placeholders $! placeholders0
815+ _ <- DMap.traverseWithKey (\_ (Compose (df, _, _, _)) -> Constant () <$ append df) children0
816+ liftIO . writeIORef lastPlaceholderRef =<< textNodeInternal ("" :: Text)
817+ requestDomAction_ $ ffor children' $ \p -> do
818+ (oldUnready, oldP) <- liftIO $ readIORef pendingChange
819+ newUnready <- liftIO $ updateChildUnreadiness p oldUnready
820+ let !newP = p <> oldP
821+ liftIO $ writeIORef pendingChange (newUnready, newP)
822+ when (DMap.null newUnready) $ do
824+ return (result0, result')
826+drawChildUpdate :: (MonadIO m, MonadJSM m)
827+ => ImmediateDomBuilderEnv t
828+ -> (IORef (ChildReadyState k) -> JSM ()) -- This will NOT be called if the child is ready at initialization time; instead, the ChildReadyState return value will be ChildReadyState_Ready
829+ -> ImmediateDomBuilderT t m (v' a)
619−drawChildUpdate :: (MonadJSM m, PerformEvent t m, MonadFix m, MonadJSM (Performable m)) => ImmediateDomBuilderEnv t m -> ImmediateDomBuilderT t m (v' a) -> RequesterT t JSM Identity (TriggerEventT t m) (Compose ((,,) DOM.DocumentFragment DOM.Text) v' a)
830+ -> RequesterT t JSM Identity (TriggerEventT t m) (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v' a)
620−drawChildUpdate initialEnv child = do
831+drawChildUpdate initialEnv markReady child = do
832+ childReadyState <- liftIO $ newIORef $ ChildReadyState_Unready Nothing
833+ unreadyChildren <- liftIO $ newIORef 0
621 df <- createDocumentFragmentUnchecked $ _immediateDomBuilderEnv_document initialEnv
834 df <- createDocumentFragmentUnchecked $ _immediateDomBuilderEnv_document initialEnv
622− runReaderT (unImmediateDomBuilderT $ Compose <$> ((,,) <$> pure df <*> textNodeInternal ("" :: Text) <*> child)) $ initialEnv
835+ (placeholder, result) <- runReaderT (unImmediateDomBuilderT $ (,) <$> textNodeInternal ("" :: Text) <*> child) $ initialEnv
623 { _immediateDomBuilderEnv_parent = toNode df
836 { _immediateDomBuilderEnv_parent = toNode df
837+ , _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
838+ , _immediateDomBuilderEnv_commitAction = markReady childReadyState
840+ u <- liftIO $ readIORef unreadyChildren
841+ when (u == 0) $ liftIO $ writeIORef childReadyState ChildReadyState_Ready
842+ return $ Compose (df, placeholder, childReadyState, result)
626mkHasFocus :: (MonadHold t m, Reflex t) => Element er d t -> m (Dynamic t Bool)
844mkHasFocus :: (MonadHold t m, Reflex t) => Element er d t -> m (Dynamic t Bool)
627mkHasFocus e = do
845mkHasFocus e = do