2{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE RecursiveDo #-}
9{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE TypeFamilies #-}
11{-# LANGUAGE UndecidableInstances #-}
12module Reflex.Dom.DynamicWriter where
15import Reflex.Dom.Builder.Class
16import Reflex.Dom.Class
17import Reflex.Dom.PerformEvent.Class
18import Reflex.Dom.PostBuild.Class
19import Reflex.Host.Class
21import Control.Lens hiding (element)
22import Control.Monad.Exception
23import Control.Monad.IO.Class
24import Control.Monad.Reader
25import Control.Monad.Ref
26import Control.Monad.State.Strict
27import Data.Dependent.Map (DMap)
29import Data.Functor.Compose
30import Data.Functor.Misc
31import Data.List.NonEmpty (NonEmpty, nonEmpty)
33import qualified Data.Map as Map
35import Data.Traversable
37instance MonadTrans (DynamicWriterT t w) where
38 lift = DynamicWriterT . lift
41 PatchMap :: Ord k => Map k (Maybe v) -> PatchMap (Map k v)
43instance Patch PatchMap where
44 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?
45 where insertions = Map.mapMaybeWithKey (const id) p
46 deletions = Map.mapMaybeWithKey (const nothingToJust) p
51instance Ord k => Semigroup (PatchMap (Map k v)) where
52 PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
53 -- PatchMap is idempotent, so stimes n is id for every n
54#if MIN_VERSION_semigroups(0,17,0)
55 stimes = stimesIdempotentMonoid
57 times1p n x = case compare n 0 of
58 LT -> error "stimesIdempotentMonoid: negative multiplier"
63instance Ord k => Monoid (PatchMap (Map k v)) where
64 mempty = PatchMap mempty
67mconcatIncremental :: (Reflex t, MonadHold t m, MonadFix m, Monoid v) => Map k v -> Event t (PatchMap (Map k v)) -> m (Dynamic t v)
68mconcatIncremental m0 e = do
69 d <- foldDynMaybe apply m0 e
70 return $ fmap (mconcat . Map.elems) d
72mapIncrementalMapValuesWithKey :: Reflex t => (k -> v -> v') -> Incremental t PatchMap (Map k v) -> Incremental t PatchMap (Map k v')
73mapIncrementalMapValuesWithKey f = unsafeMapIncremental (Map.mapWithKey f) $ \(PatchMap m) -> PatchMap $ Map.mapWithKey (\k mv -> fmap (f k) mv) m
75mapIncrementalMapValues :: Reflex t => (v -> v') -> Incremental t PatchMap (Map k v) -> Incremental t PatchMap (Map k v')
76mapIncrementalMapValues f = mapIncrementalMapValuesWithKey $ const f
78unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (a -> a') -> (p a -> p' a') -> Incremental t p a -> Incremental t p' a'
79unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
81incrementalExtractFunctorDMap :: Reflex t => Incremental t PatchMap (Map k (f v)) -> Incremental t PatchDMap (DMap (Const2 k v) f)
82incrementalExtractFunctorDMap = unsafeMapIncremental mapWithFunctorToDMap $ \(PatchMap m) -> PatchDMap $ mapWithFunctorToDMap $ fmap Compose m
84mergeIncrementalMap :: (Reflex t, Ord k) => Incremental t PatchMap (Map k (Event t v)) -> Event t (Map k v)
85mergeIncrementalMap = fmap dmapToMap . mergeIncremental . incrementalExtractFunctorDMap
87holdIncrementalReplaceableMap :: 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))
88holdIncrementalReplaceableMap m0 m' = do
89 rec vals <- holdIncremental m0 $ m' <> valChanges
90 let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
91 return $ mapIncrementalMapValues _replaceable_value vals
93mergeDynIncremental :: (Reflex t, Ord k) => Incremental t PatchMap (Map k (Dynamic t v)) -> Incremental t PatchMap (Map k v)
94mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
95 where changedValues = fmap (PatchMap . fmap Just) $ mergeIncrementalMap $ mapIncrementalMapValues updated a
96 addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
98data Replaceable t w = Replaceable
99 { _replaceable_value :: w
100 , _replaceable_modify :: Event t (Maybe (Replaceable t w)) -- "Please delete" is represented with an event of Nothing; all subsequent events will be ignored
103type DynamicWriterAccumulator t w = [Replaceable t (Dynamic t w)]
105newtype 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
107instance HasWebView m => HasWebView (DynamicWriterT t w m) where
108 type WebViewPhantom (DynamicWriterT t w m) = WebViewPhantom m
109 askWebView = lift askWebView
111instance MonadRef m => MonadRef (DynamicWriterT t w m) where
112 type Ref (DynamicWriterT t w m) = Ref m
113 newRef = lift . newRef
114 readRef = lift . readRef
115 writeRef r = lift . writeRef r
117instance MonadAtomicRef m => MonadAtomicRef (DynamicWriterT t w m) where
118 atomicModifyRef r = lift . atomicModifyRef r
120instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWriterT t w m) where
121 newEventWithTrigger = lift . newEventWithTrigger
122 newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
124runDynamicWriterTInternal :: DynamicWriterT t w m a -> m (a, DynamicWriterAccumulator t w)
125runDynamicWriterTInternal (DynamicWriterT a) = runStateT a []
127mconcatIncrementalReplaceableDynMap :: 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))
128mconcatIncrementalReplaceableDynMap m0 m' additionsCeased = do
129 rec vals <- holdIncremental m0 $ m' <> valChanges
130 let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
131 let i = mapIncrementalMapValues _replaceable_value vals
132 noMoreAdditions <- holdDyn False $ True <$ additionsCeased
133 let replaceSelf nma x = do
136 [] -> return Nothing -- Delete self
137 [(_, n)] -> return $ Just n --TODO: What if this one is also replacing itself simultaneously?
139 result = fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeDynIncremental i
140 return $ Replaceable result $ fmapMaybe id $ updated $ zipDynWith replaceSelf noMoreAdditions $ incrementalToDynamic vals
142runDynamicWriterT :: (MonadFix m, Reflex t, MonadHold t m, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
143runDynamicWriterT (DynamicWriterT a) = do
144 (result, ws) <- runStateT a []
145 Replaceable w _ <- mconcatIncrementalReplaceableDynMap (Map.fromList $ zip [1 :: Int ..] $ reverse ws) never never
148class Monad m => MonadDynamicWriter t w m | m -> t w where
149 tellDyn :: Dynamic t w -> m ()
151instance (Monad m, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
152 tellDyn w = DynamicWriterT $ modify (Replaceable w never :)
154instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
156 local f (DynamicWriterT a) = DynamicWriterT $ mapStateT (local f) a
157 reader = lift . reader
159liftDynamicWriterTThroughSync :: Monad m' => (m (a, DynamicWriterAccumulator t w) -> m' (b, (a, DynamicWriterAccumulator t w))) -> DynamicWriterT t w m a -> DynamicWriterT t w m' (b, a)
160liftDynamicWriterTThroughSync f (DynamicWriterT child) = DynamicWriterT $ do
162 (b, (a, newS)) <- lift $ f $ runStateT child s
166{-# INLINABLE liftDynamicWriterTElementConfig #-}
167liftDynamicWriterTElementConfig :: ElementConfig er t (DynamicWriterT t w m) -> ElementConfig er t m
168liftDynamicWriterTElementConfig cfg = cfg
169 { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
170 , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
173instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (DynamicWriterT t w m) where
174 type DomBuilderSpace (DynamicWriterT t w m) = DomBuilderSpace m
175 textNode = liftTextNode
176 element elementTag cfg (DynamicWriterT child) = DynamicWriterT $ do
179 { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
180 , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
182 (el, (a, newS)) <- lift $ element elementTag cfg' $ runStateT child s
187 { _placeholderConfig_insertAbove = runDynamicWriterTInternal <$> _placeholderConfig_insertAbove cfg
189 let manageChildren :: Event t (NonEmpty (Replaceable t (Dynamic t w))) -- ^ Add nodes on the right; these are in reverse order
190 -> Event t () -- ^ No more nodes will be added after this event fires
191 -> m (Replaceable t (Dynamic t w))
192 manageChildren newChildren additionsCeased = do
193 rec nextId <- hold (0 :: Int) newNextId -- We assume this will never wrap around
194 let numberedNewChildren :: Event t (Int, PatchMap (Map Int (Replaceable t (Dynamic t w))))
195 numberedNewChildren = flip pushAlways newChildren $ \rcs -> do
196 let cs = reverse $ toList rcs
197 myFirstId <- sample nextId
198 let (myNextId, numbered) = mapAccumL (\n v -> (succ n, (n, Just v))) myFirstId cs
199 return (myNextId, PatchMap $ Map.fromList numbered)
200 newNextId = fst <$> numberedNewChildren
201 mconcatIncrementalReplaceableDynMap Map.empty (snd <$> numberedNewChildren) additionsCeased
202 rec children <- lift $ manageChildren childOutputs $ cfg ^. deleteSelf
203 p <- DynamicWriterT $ do
205 lift $ placeholder cfg'
206 let result = fst <$> _placeholder_insertedAbove p
207 childOutputs = fmapMaybe (nonEmpty . snd) $ _placeholder_insertedAbove p
209 { _placeholder_insertedAbove = result
211 inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
212 textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
214instance (Deletable t m, Reflex t, Monoid w, MonadHold t m, MonadFix m) => Deletable t (DynamicWriterT t w m) where
215 deletable delete child = do
216 (result, output) <- lift $ deletable delete $ runDynamicWriterT child
217 DynamicWriterT $ modify (Replaceable output (Nothing <$ delete) :)
220instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
221 type Performable (DynamicWriterT t w m) = Performable m
222 performEvent_ = lift . performEvent_
223 performEvent = lift . performEvent
225instance TriggerEvent t m => TriggerEvent t (DynamicWriterT t w m) where
226 newTriggerEvent = lift newTriggerEvent
227 newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
228 newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
230instance PostBuild t m => PostBuild t (DynamicWriterT t w m) where
231 getPostBuild = lift getPostBuild
233instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
234 tellDyn = lift . tellDyn
236instance MonadState s m => MonadState s (DynamicWriterT t w m) where
240instance HasJS x m => HasJS x (DynamicWriterT t w m) where
241 type JSM (DynamicWriterT t w m) = JSM m
242 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