Skip to content

Commit cd2d8c7

Browse files
committed
Implement withStateful. WIP.
1 parent ff22ca4 commit cd2d8c7

File tree

3 files changed

+82
-11
lines changed

3 files changed

+82
-11
lines changed

Diff for: examples/Test/Hello.purs

+11-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module Test.Hello where
33
import Prelude
44

55
import Concur.Core (Widget)
6-
import Concur.Core.DevTools (connectDevTools, sendToDevTools)
6+
import Concur.Core.DevTools (StateSubscription, connectDevTools, subscribe, withStateful)
77
import Concur.React (HTML)
88
import Concur.React.DOM (button, text, div')
99
import Concur.React.Props (onClick)
@@ -15,19 +15,21 @@ import Effect.Class (liftEffect)
1515
import Effect.Console (log)
1616

1717
helloWidget :: Widget HTML Int
18-
helloWidget = snd <$> runStateT helloWidgetS 0
19-
20-
helloWidgetS :: forall a. StateT Int (Widget HTML) a
21-
helloWidgetS = do
18+
helloWidget = do
2219
conn <- liftEffect connectDevTools
20+
subs <- liftEffect $ subscribe conn
21+
snd <$> runStateT (helloWidgetS subs) 0
22+
23+
helloWidgetS :: forall a. StateSubscription Int -> StateT Int (Widget HTML) a
24+
helloWidgetS subs = do
2325
count <- get
24-
liftEffect $ sendToDevTools conn "Increment" count
25-
e <- lift $ div'
26+
-- liftEffect $ sendState subs "Increment" count
27+
newCount <- lift $ withStateful subs "Increment" $ map (const (count + 1)) $ div'
2628
[ but "Say Hello!"
2729
, but $ "For the " <> show count <> " time, hello sailor!"
2830
]
29-
put (count + 1)
31+
put newCount -- (count + 1)
3032
liftEffect (log "You said Hello!")
31-
helloWidgetS
33+
helloWidgetS subs
3234
where
3335
but s = button [onClick] [text s]

Diff for: src/Concur/Core/DevTools.js

+25-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ exports.sendToDevTools = function(connection) {
2727
return function(state) {
2828
return function() {
2929
if(hasDevTools()) {
30-
return window.__REDUX_DEVTOOLS_EXTENSION__.send(action, state);
30+
return connection.send(action, state);
3131
} else {
3232
// ??
3333
return null;
@@ -36,3 +36,27 @@ exports.sendToDevTools = function(connection) {
3636
};
3737
};
3838
};
39+
40+
exports.subscribeDevTools = function(connection) {
41+
return function(handler) {
42+
return function() {
43+
if(hasDevTools()) {
44+
return connection.subscribe(function(message) {
45+
if (message.type === 'DISPATCH' && message.state) {
46+
// Extra () due to handler being a State -> Effect
47+
handler(message.state)();
48+
}
49+
});
50+
} else {
51+
// ??
52+
return null;
53+
}
54+
};
55+
};
56+
};
57+
58+
exports.unsubscribeDevTools = function(connection) {
59+
return function() {
60+
connection.unsubscribe();
61+
};
62+
};

Diff for: src/Concur/Core/DevTools.purs

+46-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,54 @@
11
module Concur.Core.DevTools where
22

3-
import Data.Unit (Unit)
3+
import Prelude
4+
5+
import Control.Alt (class Alt, (<|>))
6+
import Data.Either (Either(..))
47
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)
514

615
data DevToolsConnection
716
foreign import connectDevTools :: Effect DevToolsConnection
817
foreign import disconnectDevTools :: Effect Unit
918
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

Comments
 (0)