|
1 | 1 | module Concur.Core.DevTools where
|
2 | 2 |
|
3 |
| -import Data.Unit (Unit) |
| 3 | +import Prelude |
| 4 | + |
| 5 | +import Control.Alt (class Alt, (<|>)) |
| 6 | +import Data.Either (Either(..)) |
4 | 7 | import Effect (Effect)
|
| 8 | +import Effect.AVar as EVar |
| 9 | +import Effect.Aff (Aff) |
| 10 | +import Effect.Aff.AVar as AVar |
| 11 | +import Effect.Aff.Class (class MonadAff, liftAff) |
| 12 | +import Effect.Class (liftEffect) |
| 13 | +import Effect.Exception (error) |
5 | 14 |
|
6 | 15 | data DevToolsConnection
|
7 | 16 | foreign import connectDevTools :: Effect DevToolsConnection
|
8 | 17 | foreign import disconnectDevTools :: Effect Unit
|
9 | 18 | foreign import sendToDevTools :: forall action state. DevToolsConnection -> action -> state -> Effect Unit
|
| 19 | + |
| 20 | +data DevToolsSubscription |
| 21 | +foreign import subscribeDevTools :: forall state. DevToolsConnection -> (state -> Effect Unit) -> Effect DevToolsSubscription |
| 22 | +foreign import unsubscribeDevTools :: DevToolsSubscription -> Effect Unit |
| 23 | + |
| 24 | +data StateSubscription a = StateSubscription DevToolsConnection DevToolsSubscription (EVar.AVar a) |
| 25 | + |
| 26 | +subscribe :: forall a. DevToolsConnection -> Effect (StateSubscription a) |
| 27 | +subscribe conn = do |
| 28 | + v <- EVar.empty |
| 29 | + subs <- subscribeDevTools conn \st -> do |
| 30 | + _ <- EVar.tryPut st v |
| 31 | + pure unit |
| 32 | + pure (StateSubscription conn subs v) |
| 33 | + |
| 34 | +unsubscribe :: forall a. StateSubscription a -> Effect Unit |
| 35 | +unsubscribe (StateSubscription _ subs v) = do |
| 36 | + unsubscribeDevTools subs |
| 37 | + EVar.kill (error "Unsubscribed") v |
| 38 | + |
| 39 | +awaitState :: forall a. StateSubscription a -> Aff a |
| 40 | +awaitState (StateSubscription _ _ v) = AVar.take v |
| 41 | + |
| 42 | +sendState :: forall a. StateSubscription a -> String -> a -> Effect Unit |
| 43 | +sendState (StateSubscription conn _ _) label st = sendToDevTools conn label st |
| 44 | + |
| 45 | +-- Wrap a state getter, so that all outputs from the getter are sent to the devtools |
| 46 | +-- And also, any state sent back from the devtools overrides the local state |
| 47 | +withStateful :: forall m a. MonadAff m => Alt m => StateSubscription a -> String -> m a -> m a |
| 48 | +withStateful subs label axn = do |
| 49 | + est <- map Left axn <|> map Right (liftAff (awaitState subs)) |
| 50 | + case est of |
| 51 | + Left st -> do |
| 52 | + liftEffect $ sendState subs label st |
| 53 | + pure st |
| 54 | + Right st -> pure st |
0 commit comments