@@ -5,6 +5,7 @@
55{-# LANGUAGE EmptyDataDecls #-}
66{-# LANGUAGE FlexibleContexts #-}
77{-# LANGUAGE FlexibleInstances #-}
8+{-# LANGUAGE GADTs #-}
89{-# LANGUAGE GeneralizedNewtypeDeriving #-}
910{-# LANGUAGE LambdaCase #-}
1011{-# LANGUAGE MultiParamTypeClasses #-}
@@ -48,11 +49,11 @@ module Reflex.Dom.Builder.Immediate
4849 , Pair1 (..)
4950 , Maybe1 (..)
5051 , GhcjsEventSpec (..)
52+ , ghcjsEventSpec_filters
53+ , ghcjsEventSpec_handler
5154 , GhcjsEventHandler (..)
5255#ifndef USE_TEMPLATE_HASKELL
5356 , phantom2
54− , ghcjsEventSpec_filters
55− , ghcjsEventSpec_handler
5657#endif
5758 , drawChildUpdate
5859 , mkHasFocus
@@ -74,8 +75,6 @@ module Reflex.Dom.Builder.Immediate
7475 , WindowConfig (..)
7576 , Window (..)
7677 , wrapWindow
77− , ghcjsEventSpec_filters
78− , ghcjsEventSpec_handler
7978 ) where
8079
8180import Foreign.JavaScript.TH
@@ -83,6 +82,8 @@ import Reflex.Class as Reflex
8382import Reflex.Dom.Builder.Class
8483import Reflex.Dynamic
8584import Reflex.Host.Class
85+import qualified Reflex.Patch.DMap as PatchDMap
86+import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
8687import Reflex.PerformEvent.Class
8788import Reflex.PostBuild.Class
8889import Reflex.TriggerEvent.Base hiding (askEvents)
@@ -106,10 +107,12 @@ import Data.Dependent.Sum
106107import Data.Functor.Compose
107108import Data.Functor.Constant
108109import Data.Functor.Misc
110+import Data.Functor.Product
109111import Data.IORef
110112import qualified Data.Map as Map
111113import Data.Maybe
112−import Data.Monoid
114+import Data.Monoid hiding (Product)
115+import Data.Some (Some)
113116import qualified Data.Some as Some
114117import Data.Text (Text)
115118import qualified Data.Text as T
@@ -144,12 +147,14 @@ import Reflex.Requester.Base
144147import Reflex.Requester.Class
145148import Foreign.JavaScript.Internal.Utils
146149
147−data ImmediateDomBuilderEnv t m
150+data ImmediateDomBuilderEnv t
148151 = ImmediateDomBuilderEnv { _immediateDomBuilderEnv_document :: Document
149152 , _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
150155 }
151156
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 }
153158 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
154159
155160#ifndef __GHCJS__
@@ -164,7 +169,7 @@ instance PrimMonad m => PrimMonad (ImmediateDomBuilderT x m) where
164169instance MonadTrans (ImmediateDomBuilderT t) where
165170 lift = ImmediateDomBuilderT . lift . lift . lift
166171
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
168173 withRenderHook hook (ImmediateDomBuilderT a) = do
169174 e <- ImmediateDomBuilderT ask
170175 ImmediateDomBuilderT $ lift $ withRequesting $ \rsp -> do
@@ -185,7 +190,7 @@ runImmediateDomBuilderT
185190 , Ref m ~ IORef
186191 )
187192 => ImmediateDomBuilderT t m a
188− -> ImmediateDomBuilderEnv t m
193+ -> ImmediateDomBuilderEnv t
189194 -> Chan [DSum (EventTriggerRef t) TriggerInvocation]
190195 -> m a
191196runImmediateDomBuilderT (ImmediateDomBuilderT a) env eventChan = flip runTriggerEventT eventChan $ do
@@ -215,6 +220,9 @@ askParent = ImmediateDomBuilderT $ asks _immediateDomBuilderEnv_parent
215220askEvents :: Monad m => ImmediateDomBuilderT t m (Chan [DSum (EventTriggerRef t) TriggerInvocation])
216221askEvents = ImmediateDomBuilderT . lift . lift $ TriggerEventT.askEvents
217222
223+localEnv :: Monad m => (ImmediateDomBuilderEnv t -> ImmediateDomBuilderEnv t) -> ImmediateDomBuilderT t m a -> ImmediateDomBuilderT t m a
224+localEnv f = ImmediateDomBuilderT . local f . unImmediateDomBuilderT
225+
218226{-# INLINABLE append #-}
219227append :: (IsNode n, MonadJSM m) => n -> ImmediateDomBuilderT t m ()
220228append n = do
@@ -278,7 +286,7 @@ extractUpTo df s e = liftJSM $ do
278286 ]
279287 void $ call f f (df, s, e)
280288
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)
282290
283291{-# INLINABLE collectUpTo #-}
284292collectUpTo :: (MonadJSM m, IsNode start, IsNode end) => start -> end -> m DOM.DocumentFragment
@@ -299,9 +307,9 @@ newtype EventFilterTriggerRef t er (en :: EventTag) = EventFilterTriggerRef (IOR
299307wrap :: forall m er t. SupportsImmediateDomBuilder t m => RawElement GhcjsDomSpace -> RawElementConfig er t (ImmediateDomBuilderT t m) -> ImmediateDomBuilderT t m (Element er GhcjsDomSpace t)
300308wrap e cfg = do
301309 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
303311 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
305313 eventTriggerRefs :: DMap EventName (EventFilterTriggerRef t er) <- liftJSM $ fmap DMap.fromList $ forM (DMap.toList $ _ghcjsEventSpec_filters $ _rawElementConfig_eventSpec cfg) $ \(en :=> GhcjsEventFilter f) -> do
306314 triggerRef <- liftIO $ newIORef Nothing
307315 _ <- elementOnEventName en e $ do
@@ -334,20 +342,22 @@ wrap e cfg = do
334342 --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
335343 ref <- newIORef $ Just t
336344 writeChan events [EventTriggerRef ref :=> TriggerInvocation v (return ())])
337− return $ Element es e
345+ return $ Element
346+ { _element_events = es
347+ , _element_raw = e
348+ }
338349
339350{-# INLINABLE makeElement #-}
340351makeElement :: 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)
341352makeElement elementTag cfg child = do
342353 doc <- askDocument
343− env <- ImmediateDomBuilderT ask
344354 e <- liftJSM $ case cfg ^. namespace of
345355 Nothing -> createElementUnchecked doc (Just elementTag)
346356 Just ens -> createElementNSUnchecked doc (Just ens) (Just elementTag)
347357 ImmediateDomBuilderT $ iforM_ (cfg ^. initialAttributes) $ \(AttributeName mAttrNamespace n) v -> case mAttrNamespace of
348358 Nothing -> lift $ setAttribute e n v
349359 Just ans -> lift $ setAttributeNS e (Just ans) n v
350− result <- ImmediateDomBuilderT $ lift $ runReaderT (unImmediateDomBuilderT child) $ env
360+ result <- flip localEnv child $ \env -> env
351361 { _immediateDomBuilderEnv_parent = toNode e
352362 }
353363 append e
@@ -435,7 +445,7 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
435445 Input.setValue domInputElement $ Just (cfg ^. inputElementConfig_initialValue)
436446 v0 <- Input.getValueUnchecked domInputElement
437447 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)
439449 valueChangedBySetValue <- case _inputElementConfig_setValue cfg of
440450 Nothing -> return never
441451 Just eSetValue -> requestDomAction $ ffor eSetValue $ \v' -> do
@@ -486,7 +496,7 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
486496 TextArea.setValue domTextAreaElement $ Just (cfg ^. textAreaElementConfig_initialValue)
487497 v0 <- TextArea.getValueUnchecked domTextAreaElement
488498 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)
490500 valueChangedBySetValue <- case _textAreaElementConfig_setValue cfg of
491501 Nothing -> return never
492502 Just eSetValue -> requestDomAction $ ffor eSetValue $ \v' -> do
@@ -511,7 +521,7 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
511521 Select.setValue domSelectElement $ Just (cfg ^. selectElementConfig_initialValue)
512522 Just v0 <- Select.getValue domSelectElement
513523 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)
515525 valueChangedBySetValue <- case _selectElementConfig_setValue cfg of
516526 Nothing -> return never
517527 Just eSetValue -> requestDomAction $ ffor eSetValue $ \v' -> do
@@ -532,96 +542,304 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
532542 return (wrapped, result)
533543 placeRawElement = append
534544 wrapRawElement = wrap
545+ notReadyUntil e = do
546+ eOnce <- headE e
547+ env <- ImmediateDomBuilderT ask
548+ let unreadyChildren = _immediateDomBuilderEnv_unreadyChildren env
549+ liftIO $ modifyIORef' unreadyChildren succ
550+ let ready = do
551+ old <- liftIO $ readIORef unreadyChildren
552+ let new = pred old
553+ liftIO $ writeIORef unreadyChildren $! new
554+ when (new == 0) $ _immediateDomBuilderEnv_commitAction env
555+ requestDomAction_ $ ready <$ eOnce
556+ notReady = do
557+ env <- ImmediateDomBuilderT ask
558+ let unreadyChildren = _immediateDomBuilderEnv_unreadyChildren env
559+ liftIO $ modifyIORef' unreadyChildren succ
560+
561+data FragmentState
562+ = FragmentState_Unmounted
563+ | FragmentState_Mounted (DOM.Text, DOM.Text)
564+
565+data ImmediateDomFragment = ImmediateDomFragment
566+ { _immediateDomFragment_document :: DOM.DocumentFragment
567+ , _immediateDomFragment_state :: IORef FragmentState
568+ }
535569
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
570+extractFragment :: MonadJSM m => ImmediateDomFragment -> m ()
571+extractFragment fragment = do
572+ state <- liftIO $ readIORef $ _immediateDomFragment_state fragment
573+ case state of
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
578+
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
585+ }
586+ state <- liftIO $ newIORef FragmentState_Unmounted
587+ return (ImmediateDomFragment df state, result)
588+ mountDomFragment fragment setFragment = do
589+ parent <- askParent
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)
601+
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
537603 runWithReplace a0 a' = do
538604 initialEnv <- ImmediateDomBuilderT ask
539605 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
611+ True -> return ()
612+ False -> do
613+ liftIO $ writeIORef haveEverBeenReady True
614+ old <- liftIO $ readIORef parentUnreadyChildren
615+ let new = pred old
616+ liftIO $ writeIORef parentUnreadyChildren $! new
617+ when (new == 0) $ _immediateDomBuilderEnv_commitAction initialEnv
540618 -- We draw 'after' in this roundabout way to avoid using MonadFix
541− after <- createTextNodeUnchecked (_immediateDomBuilderEnv_document initialEnv) ("" :: Text)
619+ doc <- askDocument
620+ after <- createTextNodeUnchecked doc ("" :: Text)
542621 let drawInitialChild = do
543− result <- a0
544− append after
622+ unreadyChildren <- liftIO $ newIORef 0
623+ let f = do
624+ result <- a0
625+ append after
626+ return result
627+ result <- runReaderT (unImmediateDomBuilderT f) $ initialEnv
628+ { _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
629+ , _immediateDomBuilderEnv_commitAction = myCommitAction
630+ }
631+ liftIO $ readIORef unreadyChildren >>= \case
632+ 0 -> writeIORef haveEverBeenReady True
633+ _ -> modifyIORef' parentUnreadyChildren succ
545634 return result
546− (result0, result') <- ImmediateDomBuilderT $ lift $ runWithReplace (runReaderT (unImmediateDomBuilderT drawInitialChild) initialEnv) $ ffor a' $ \child -> do
547− df <- createDocumentFragmentUnchecked $ _immediateDomBuilderEnv_document initialEnv
635+ a'' <- numberOccurrences a'
636+ (result0, result') <- ImmediateDomBuilderT $ lift $ runWithReplace drawInitialChild $ ffor a'' $ \(cohortId, child) -> do
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
645+ myCommitAction
548646 result <- runReaderT (unImmediateDomBuilderT child) $ initialEnv
549647 { _immediateDomBuilderEnv_parent = toNode df
648+ , _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
649+ , _immediateDomBuilderEnv_commitAction = commitAction
550650 }
551− let update = do
552− deleteBetweenExclusive before after
553− insertBefore df after
554− return (result, update)
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
651+ liftIO (readIORef unreadyChildren) >>= \case
652+ 0 -> liftJSM commitAction
653+ _ -> return () -- Whoever decrements it to 0 will handle it
654+ return result
576655 return (result0, result')
577− traverseDMapWithKeyWithAdjustWithMove (f :: forall a. k a -> v a -> ImmediateDomBuilderT t m (v' a)) (dm0 :: DMap k v) dm' = do
578− initialEnv <- ImmediateDomBuilderT ask
579− (children0, children') <- ImmediateDomBuilderT $ lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> drawChildUpdate initialEnv $ f k v) dm0 dm'
580− let result0 = DMap.map (\(Compose (_, _, v)) -> v) children0
581− placeholders0 = weakenDMapWith (\(Compose (_, ph, _)) -> ph) children0
582− result' = ffor children' $ mapPatchDMapWithMove $ \(Compose (_, _, r)) -> r
583− placeholders' = fforCheap children' $ weakenPatchDMapWithMoveWith $ \(Compose (_, ph, _)) -> ph
584− placeholders <- current . incrementalToDynamic <$> holdIncremental placeholders0 placeholders'
585− _ <- DMap.traverseWithKey (\_ (Compose (df, _, _)) -> Constant () <$ append df) children0
586− lastPlaceholder <- textNodeInternal ("" :: Text)
587− requestDomAction_ $ flip push children' $ \p_ -> do
656+ traverseDMapWithKeyWithAdjust = do
657+ let updateChildUnreadiness (p :: PatchDMap k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v')) old = do
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)
659+ new k (ComposeMaybe m) = ComposeMaybe <$> case m of
660+ Nothing -> return Nothing
661+ Just (Compose (_, _, sRef, _)) -> do
662+ readIORef sRef >>= \case
663+ ChildReadyState_Ready -> return Nothing -- Delete this child, since it's ready
664+ ChildReadyState_Unready _ -> do
665+ writeIORef sRef $ ChildReadyState_Unready $ Just $ Some.This k
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
588701 let p = unPatchDMapWithMove p_
589− phsBefore <- sample placeholders
590− if DMap.null p then return Nothing else return $ Just $ do
591− let collectIfMoved :: forall a. k a -> DMapEdit k (Compose ((,,) DOM.DocumentFragment DOM.Text) v') a -> JSM (Constant (Maybe DOM.DocumentFragment) a)
592− collectIfMoved k e = do
593− 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
595− case dmapEditMoved e of
596− False -> do
597− mapM_ (`deleteUpTo` nextPlaceholder) mThisPlaceholder
598− return $ Constant Nothing
599− True -> do
600− Constant <$> mapM (`collectUpTo` nextPlaceholder) mThisPlaceholder
601− collected <- DMap.traverseWithKey collectIfMoved p
602− 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)
604− placeFragment k e = do
605− let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phsAfter
606− case e of
607− DMapEdit_Insert _ (Compose (df, _, _)) -> do
608− df `insertBefore` nextPlaceholder
609− DMapEdit_Delete _ -> do
610− return ()
611− DMapEdit_Move _ fromKey -> do
612− Just (Constant mdf) <- return $ DMap.lookup fromKey collected
613− mapM_ (`insertBefore` nextPlaceholder) mdf
614− 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
616− return ()
617− return (result0, result')
618−
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)
620−drawChildUpdate initialEnv child = do
702+ phsBefore <- liftIO $ readIORef placeholders
703+ lastPlaceholder <- liftIO $ readIORef lastPlaceholderRef
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)
705+ collectIfMoved k e = do
706+ let mThisPlaceholder = Map.lookup (Some.This k) phsBefore -- Will be Nothing if this element wasn't present before
707+ nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phsBefore
708+ case isJust $ getComposeMaybe $ PatchDMapWithMove._nodeInfo_to e of
709+ False -> do
710+ mapM_ (`deleteUpTo` nextPlaceholder) mThisPlaceholder
711+ return $ Constant Nothing
712+ True -> do
713+ Constant <$> mapM (`collectUpTo` nextPlaceholder) mThisPlaceholder
714+ collected <- DMap.traverseWithKey collectIfMoved p
715+ let !phsAfter = fromMaybe phsBefore $ apply (weakenPatchDMapWithMoveWith (\(Compose (_, ph, _, _)) -> ph) p_) phsBefore --TODO: Don't recompute this
716+ let placeFragment :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v') a -> JSM (Constant () a)
717+ placeFragment k e = do
718+ let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phsAfter
719+ case PatchDMapWithMove._nodeInfo_from e of
720+ PatchDMapWithMove.From_Insert (Compose (df, _, _, _)) -> do
721+ df `insertBefore` nextPlaceholder
722+ PatchDMapWithMove.From_Delete -> do
723+ return ()
724+ PatchDMapWithMove.From_Move fromKey -> do
725+ Just (Constant mdf) <- return $ DMap.lookup fromKey collected
726+ mapM_ (`insertBefore` nextPlaceholder) mdf
727+ return $ Constant ()
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
729+ liftIO $ writeIORef placeholders $! phsAfter
730+
731+data ChildReadyState k
732+ = ChildReadyState_Ready
733+ | ChildReadyState_Unready !(Maybe (Some k))
734+ deriving (Show, Read, Eq, Ord)
735+
736+hoistTraverseWithKeyWithAdjust :: forall (k :: * -> *) v v' t m p.
737+ ( MonadAdjust t m
738+ , MonadHold t m
739+ , DMap.GCompare k
740+ , MonadIO (Performable m)
741+ , MonadIO m
742+ , MonadJSM m
743+ , PrimMonad m
744+ , MonadFix m
745+ , Patch (p k v)
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))
749+ )
750+ => (forall vv vv'.
751+ (forall a. k a -> vv a -> RequesterT t JSM Identity (TriggerEventT t m) (vv' a))
752+ -> DMap k vv
753+ -> Event t (p k vv)
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))
760+ -> DMap k v
761+ -> Event t (p k v)
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
772+ markSelfReady
773+ liftIO $ writeIORef pendingChange $! mempty
774+ markSelfReady = do
775+ liftIO (readIORef haveEverBeenReady) >>= \case
776+ True -> return ()
777+ False -> do
778+ liftIO $ writeIORef haveEverBeenReady True
779+ old <- liftIO $ readIORef parentUnreadyChildren
780+ let new = pred old
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
789+ case countedAt of
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
797+ applyDomUpdate p
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
808+ else do
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
823+ applyDomUpdate newP
824+ return (result0, result')
825+
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)
830+ -> RequesterT t JSM Identity (TriggerEventT t m) (Compose ((,,,) DOM.DocumentFragment DOM.Text (IORef (ChildReadyState k))) v' a)
831+drawChildUpdate initialEnv markReady child = do
832+ childReadyState <- liftIO $ newIORef $ ChildReadyState_Unready Nothing
833+ unreadyChildren <- liftIO $ newIORef 0
621834 df <- createDocumentFragmentUnchecked $ _immediateDomBuilderEnv_document initialEnv
622− runReaderT (unImmediateDomBuilderT $ Compose <$> ((,,) <$> pure df <*> textNodeInternal ("" :: Text) <*> child)) $ initialEnv
835+ (placeholder, result) <- runReaderT (unImmediateDomBuilderT $ (,) <$> textNodeInternal ("" :: Text) <*> child) $ initialEnv
623836 { _immediateDomBuilderEnv_parent = toNode df
837+ , _immediateDomBuilderEnv_unreadyChildren = unreadyChildren
838+ , _immediateDomBuilderEnv_commitAction = markReady childReadyState
624839 }
840+ u <- liftIO $ readIORef unreadyChildren
841+ when (u == 0) $ liftIO $ writeIORef childReadyState ChildReadyState_Ready
842+ return $ Compose (df, placeholder, childReadyState, result)
625843
626844mkHasFocus :: (MonadHold t m, Reflex t) => Element er d t -> m (Dynamic t Bool)
627845mkHasFocus e = do