1{-# LANGUAGE CPP, FlexibleInstances, MultiParamTypeClasses, TypeFamilies, GeneralizedNewtypeDeriving, UndecidableInstances, FunctionalDependencies, RecursiveDo, ScopedTypeVariables, LambdaCase, GADTs #-}
2module Reflex.Dom.DynamicWriter where
5import Reflex.Host.Class
7import Reflex.Dom.Builder.Class
8import Reflex.Dom.PerformEvent.Class
9import Reflex.Dom.PostBuild.Class
11import Control.Lens hiding (element)
12import Control.Monad.IO.Class
13import Control.Monad.State.Strict
14import Control.Monad.Reader
15import Control.Monad.Ref
16import Control.Monad.Exception
18import qualified Data.Map as Map
19import Data.Dependent.Map (DMap)
20import Data.List.NonEmpty (NonEmpty, nonEmpty)
21import Data.Functor.Compose
22import Data.Functor.Misc
25import Data.Traversable
27instance MonadTrans (DynamicWriterT t w) where
28 lift = DynamicWriterT . lift
31 PatchMap :: Ord k => Map k (Maybe v) -> PatchMap (Map k v)
33instance Patch PatchMap where
34 apply (PatchMap p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
35 where insertions = Map.mapMaybeWithKey (const id) p
36 deletions = Map.mapMaybeWithKey (const nothingToJust) p
41instance Ord k => Semigroup (PatchMap (Map k v)) where
42 PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
43 -- PatchMap is idempotent, so stimes n is id for every n
44#if MIN_VERSION_semigroups(0,17,0)
45 stimes = stimesIdempotentMonoid
47 times1p n x = case compare n 0 of
48 LT -> error "stimesIdempotentMonoid: negative multiplier"
53instance Ord k => Monoid (PatchMap (Map k v)) where
54 mempty = PatchMap mempty
57mconcatIncremental :: (Reflex t, MonadHold t m, MonadFix m, Monoid v) => Map k v -> Event t (PatchMap (Map k v)) -> m (Dynamic t v)
58mconcatIncremental m0 e = do
59 d <- foldDynMaybe apply m0 e
60 return $ fmap (mconcat . Map.elems) d
62mapIncrementalMapValuesWithKey :: Reflex t => (k -> v -> v') -> Incremental t PatchMap (Map k v) -> Incremental t PatchMap (Map k v')
63mapIncrementalMapValuesWithKey f = unsafeMapIncremental (Map.mapWithKey f) $ \(PatchMap m) -> PatchMap $ Map.mapWithKey (\k mv -> fmap (f k) mv) m
65mapIncrementalMapValues :: Reflex t => (v -> v') -> Incremental t PatchMap (Map k v) -> Incremental t PatchMap (Map k v')
66mapIncrementalMapValues f = mapIncrementalMapValuesWithKey $ const f
68unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (a -> a') -> (p a -> p' a') -> Incremental t p a -> Incremental t p' a'
69unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
71incrementalExtractFunctorDMap :: Reflex t => Incremental t PatchMap (Map k (f v)) -> Incremental t PatchDMap (DMap (Const2 k v) f)
72incrementalExtractFunctorDMap = unsafeMapIncremental mapWithFunctorToDMap $ \(PatchMap m) -> PatchDMap $ mapWithFunctorToDMap $ fmap Compose m
74mergeIncrementalMap :: (Reflex t, Ord k) => Incremental t PatchMap (Map k (Event t v)) -> Event t (Map k v)
75mergeIncrementalMap = fmap dmapToMap . mergeIncremental . incrementalExtractFunctorDMap
77holdIncrementalReplaceableMap :: forall t k v m. (MonadFix m, MonadHold t m, Reflex t, Ord k) => Map k (Replaceable t v) -> Event t (PatchMap (Map k (Replaceable t v))) -> m (Incremental t PatchMap (Map k v))
78holdIncrementalReplaceableMap m0 m' = do
79 rec vals <- holdIncremental m0 $ m' <> valChanges
80 let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
81 return $ mapIncrementalMapValues _replaceable_value vals
83mergeDynIncremental :: (Reflex t, Ord k) => Incremental t PatchMap (Map k (Dynamic t v)) -> Incremental t PatchMap (Map k v)
84mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
85 where changedValues = fmap (PatchMap . fmap Just) $ mergeIncrementalMap $ mapIncrementalMapValues updated a
86 addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
88data Replaceable t w = Replaceable
89 { _replaceable_value :: w
90 , _replaceable_modify :: Event t (Maybe (Replaceable t w)) -- "Please delete" is represented with an event of Nothing; all subsequent events will be ignored
93type DynamicWriterAccumulator t w = [Replaceable t (Dynamic t w)]
95newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT (DynamicWriterAccumulator t w) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t, MonadSample t, MonadAsyncException, MonadException) -- The list is kept in reverse order
97instance HasWebView m => HasWebView (DynamicWriterT t w m) where
98 type WebViewPhantom (DynamicWriterT t w m) = WebViewPhantom m
99 askWebView = lift askWebView
101instance MonadRef m => MonadRef (DynamicWriterT t w m) where
102 type Ref (DynamicWriterT t w m) = Ref m
103 newRef = lift . newRef
104 readRef = lift . readRef
105 writeRef r = lift . writeRef r
107instance MonadAtomicRef m => MonadAtomicRef (DynamicWriterT t w m) where
108 atomicModifyRef r = lift . atomicModifyRef r
110instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWriterT t w m) where
111 newEventWithTrigger = lift . newEventWithTrigger
112 newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
114runDynamicWriterTInternal :: DynamicWriterT t w m a -> m (a, DynamicWriterAccumulator t w)
115runDynamicWriterTInternal (DynamicWriterT a) = runStateT a []
117mconcatIncrementalReplaceableDynMap :: forall m t k v. (MonadFix m, MonadHold t m, Reflex t, Monoid v, Ord k) => Map k (Replaceable t (Dynamic t v)) -> Event t (PatchMap (Map k (Replaceable t (Dynamic t v)))) -> Event t () -> m (Replaceable t (Dynamic t v))
118mconcatIncrementalReplaceableDynMap m0 m' additionsCeased = do
119 rec vals <- holdIncremental m0 $ m' <> valChanges
120 let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
121 let i = mapIncrementalMapValues _replaceable_value vals
122 noMoreAdditions <- holdDyn False $ True <$ additionsCeased
123 let replaceSelf nma x = do
126 [] -> return Nothing -- Delete self
127 [(_, n)] -> return $ Just n --TODO: What if this one is also replacing itself simultaneously?
129 result = fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeDynIncremental i
130 return $ Replaceable result $ fmapMaybe id $ updated $ zipDynWith replaceSelf noMoreAdditions $ incrementalToDynamic vals
132runDynamicWriterT :: (MonadFix m, Reflex t, MonadHold t m, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
133runDynamicWriterT (DynamicWriterT a) = do
134 (result, ws) <- runStateT a []
135 Replaceable w _ <- mconcatIncrementalReplaceableDynMap (Map.fromList $ zip [1 :: Int ..] $ reverse ws) never never
138class Monad m => MonadDynamicWriter t w m | m -> t w where
139 tellDyn :: Dynamic t w -> m ()
141instance (Monad m, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
142 tellDyn w = DynamicWriterT $ modify (Replaceable w never :)
144instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
146 local f (DynamicWriterT a) = DynamicWriterT $ mapStateT (local f) a
147 reader = lift . reader
149liftDynamicWriterTThroughSync :: Monad m' => (m (a, DynamicWriterAccumulator t w) -> m' (b, (a, DynamicWriterAccumulator t w))) -> DynamicWriterT t w m a -> DynamicWriterT t w m' (b, a)
150liftDynamicWriterTThroughSync f (DynamicWriterT child) = DynamicWriterT $ do
152 (b, (a, newS)) <- lift $ f $ runStateT child s
156{-# INLINABLE liftDynamicWriterTElementConfig #-}
157liftDynamicWriterTElementConfig :: ElementConfig er t (DynamicWriterT t w m) -> ElementConfig er t m
158liftDynamicWriterTElementConfig cfg = cfg
159 { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
160 , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
163instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (DynamicWriterT t w m) where
164 type DomBuilderSpace (DynamicWriterT t w m) = DomBuilderSpace m
165 textNode = liftTextNode
166 element elementTag cfg (DynamicWriterT child) = DynamicWriterT $ do
169 { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
170 , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
172 (el, (a, newS)) <- lift $ element elementTag cfg' $ runStateT child s
177 { _placeholderConfig_insertAbove = runDynamicWriterTInternal <$> _placeholderConfig_insertAbove cfg
179 let manageChildren :: Event t (NonEmpty (Replaceable t (Dynamic t w))) -- ^ Add nodes on the right; these are in reverse order
180 -> Event t () -- ^ No more nodes will be added after this event fires
181 -> m (Replaceable t (Dynamic t w))
182 manageChildren newChildren additionsCeased = do
183 rec nextId <- hold (0 :: Int) newNextId -- We assume this will never wrap around
184 let numberedNewChildren :: Event t (Int, PatchMap (Map Int (Replaceable t (Dynamic t w))))
185 numberedNewChildren = flip pushAlways newChildren $ \rcs -> do
186 let cs = reverse $ toList rcs
187 myFirstId <- sample nextId
188 let (myNextId, numbered) = mapAccumL (\n v -> (succ n, (n, Just v))) myFirstId cs
189 return (myNextId, PatchMap $ Map.fromList numbered)
190 newNextId = fst <$> numberedNewChildren
191 mconcatIncrementalReplaceableDynMap Map.empty (snd <$> numberedNewChildren) additionsCeased
192 rec children <- lift $ manageChildren childOutputs $ cfg ^. deleteSelf
193 p <- DynamicWriterT $ do
195 lift $ placeholder cfg'
196 let result = fst <$> _placeholder_insertedAbove p
197 childOutputs = fmapMaybe (nonEmpty . snd) $ _placeholder_insertedAbove p
199 { _placeholder_insertedAbove = result
201 inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
202 textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
204instance (Deletable t m, Reflex t, Monoid w, MonadHold t m, MonadFix m) => Deletable t (DynamicWriterT t w m) where
205 deletable delete child = do
206 (result, output) <- lift $ deletable delete $ runDynamicWriterT child
207 DynamicWriterT $ modify (Replaceable output (Nothing <$ delete) :)
210instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
211 type Performable (DynamicWriterT t w m) = Performable m
212 performEvent_ = lift . performEvent_
213 performEvent = lift . performEvent
215instance TriggerEvent t m => TriggerEvent t (DynamicWriterT t w m) where
216 newTriggerEvent = lift newTriggerEvent
217 newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
218 newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
220instance PostBuild t m => PostBuild t (DynamicWriterT t w m) where
221 getPostBuild = lift getPostBuild
223instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
224 tellDyn = lift . tellDyn
226instance MonadState s m => MonadState s (DynamicWriterT t w m) where
230instance HasJS x m => HasJS x (DynamicWriterT t w m) where
231 type JSM (DynamicWriterT t w m) = JSM m
232 liftJS = lift . liftJS
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list