git.iko.soy/purestack-dev/todomvc
on master help sign in
3 files changed+71−28
since splitsnapshots
unifiedsplit
flake.nix+2 src/Frontend.purs+19−13 src/Plum.purs+50−15
flake.nix+2
@@ -38,6 +38,8 @@
3838 project-m36.command = "${
3939 project-m36-rest.packages.${system}.default
4040 }/bin/project-m36-rest";
41+ proxy.command =
42+ "${pkgs.reproxy}/bin/reproxy -l=localhost:3001 --static.enabled --static.rule='*,^/api/(.*),http://localhost:3000/$$1' --assets.location=. --assets.spa --logger.stdout";
4143 };
4244 });
4345 in {
src/Frontend.purs+19−13
@@ -7,26 +7,36 @@ import Prim
77
88import Data.Array ((:))
99import Data.Foldable (for_)
10−import Data.Maybe (Maybe(..))
1110import Data.URL as URL
1211import Effect (Effect)
1312import Plum as Plum
1413import PureStack.Client (client)
15−import Unsafe.Coerce (unsafeCoerce)
1614
17−type Model = { newTodo :: String, todos :: Array String }
15+type Model = { newTodo :: String, todos :: Array TodoItem }
1816
1917data Msg
2018 = UpdateNewTodo String
19+ | SetTodos (Array TodoItem)
2120 | AddNewTodo
21+ | TodoAdded
2222
2323main :: Effect Unit
2424main = do
25+ origin <- Plum.currentOrigin
26+ let api = getApi (origin `URL.addSegment` "api")
2527 Plum.run @Model @Msg "plum"
26− { init: pure { newTodo: "", todos: [] }
27− , update: \msg model -> case msg of
28+ { init: \runMsg -> do
29+ runMsg $ SetTodos <$> api.getTodos
30+ pure { newTodo: "", todos: [] }
31+ , update: \runMsg msg model -> case msg of
2832 UpdateNewTodo newTodo -> pure model { newTodo = newTodo }
29− AddNewTodo -> pure model { newTodo = "", todos = model.newTodo : model.todos }
33+ AddNewTodo -> do
34+ runMsg $ api.createTodo (TodoItem { text: model.newTodo, done: false }) $> TodoAdded
35+ pure model
36+ TodoAdded -> do
37+ runMsg $ SetTodos <$> api.getTodos
38+ pure model
39+ SetTodos todos -> pure model { todos = todos }
3040 , view: \model -> column do
3141 font "sans-serif"
3242
@@ -42,13 +52,9 @@ main = do
4252
4353 onKey "Enter" AddNewTodo
4454
45− for_ model.todos $ \todo -> do
46− text todo mempty
55+ for_ model.todos $ \(TodoItem todo) -> do
56+ text todo.text mempty
4757 }
4858
49−url = case URL.fromString "localhost" of
50− Just x -> x
51− Nothing -> unsafeCoerce unit
52−
53−x = client @API url
59+getApi = client @API
5460
src/Plum.purs+50−15
@@ -1,25 +1,33 @@
1−module Plum (Plum, run) where
1+module Plum (Plum, run, currentOrigin) where
22
33import Prelude
44
55import Data.Array as Array
66import Data.Maybe (Maybe(..))
7+import Data.URL (URL)
8+import Data.URL as URL
79import Effect (Effect)
10+import Effect.Aff (Aff, launchAff_)
11+import Effect.Class (liftEffect)
812import Effect.Console (log)
13+import Effect.Exception (throw)
914import Effect.Ref as Ref
1015import Foreign.Object as Object
1116import Literals.Undefined (undefined)
1217import Plum.View (GenericUI(..), UI, grow)
1318import Snabbdom as Snabbdom
19+import Unsafe.Coerce (unsafeCoerce)
1420import Untagged.Union (asOneOf)
1521import Web.DOM.NonElementParentNode (getElementById) as Web
1622import Web.HTML (window) as Web
23+import Web.HTML.Event.EventTypes (invalid)
1724import Web.HTML.HTMLDocument (toNonElementParentNode) as Web
18−import Web.HTML.Window (document) as Web
25+import Web.HTML.Location as Location
26+import Web.HTML.Window (document, location) as Web
1927
2028type Plum msg model =
21− { init :: Effect model
22− , update :: msg -> model -> Effect model
29+ { init :: (Aff msg -> Effect Unit) -> Effect model
30+ , update :: (Aff msg -> Effect Unit) -> msg -> model -> Effect model
2331 , view :: model -> UI msg Unit
2432 }
2533
@@ -28,25 +36,36 @@ run id plum = do
2836 Web.window >>= Web.document >>= Web.getElementById id <<< Web.toNonElementParentNode >>= case _ of
2937 Just element -> do
3038 let node = Snabbdom.toVNode element
39+ modelRef <- Ref.new { model: unsafeCoerce undefined, lastNode: node }
3140 patch <- Snabbdom.init
32− initModel <- plum.init
33− modelRef <- Ref.new { model: initModel, lastNode: node }
3441 let
42+ fire =
43+ ( \msg -> do
44+ log "in"
45+ { model: m, lastNode } <- Ref.read modelRef
46+ m_ <- plum.update
47+ ( \aff ->
48+ launchAff_
49+ ( do
50+ msg' <- aff
51+ liftEffect $ fire msg'
52+ pure unit
53+ )
54+ )
55+ msg
56+ m
57+ node' <- view m_
58+ newNode <- patch lastNode node'
59+ Ref.write { model: m_, lastNode: newNode } modelRef
60+ pure unit
61+ )
3562 view model = do
3663 let UI { children } _ = plum.view model
3764 case Array.uncons children of
3865 Just { head: child } -> do
3966 let
4067 { node, style } = grow
41− ( \msg -> do
42− log "in"
43− { model: m, lastNode } <- Ref.read modelRef
44− m_ <- plum.update msg m
45− node <- view m_
46− newNode <- patch lastNode node
47− Ref.write { model: m_, lastNode: newNode } modelRef
48− pure unit
49− )
68+ fire
5069 child
5170 pure $
5271 Snabbdom.h "div"
@@ -75,6 +94,15 @@ run id plum = do
7594 }
7695 []
7796 (asOneOf undefined)
97+ initModel <- plum.init
98+ ( \aff ->
99+ launchAff_
100+ ( do
101+ msg' <- aff
102+ liftEffect $ fire msg'
103+ pure unit
104+ )
105+ )
78106 newNode <- view initModel
79107 newNode_ <- patch node newNode
80108 log "patched"
@@ -84,3 +112,10 @@ run id plum = do
84112
85113outerStyle :: String
86114outerStyle = "html,body{height:100%;padding:0;margin:0;}"
115+
116+currentOrigin :: Effect URL
117+currentOrigin = do
118+ s <- Web.window >>= Web.location >>= Location.origin
119+ case URL.fromString s of
120+ Nothing -> throw "window.location.origin returned an invalid url"
121+ Just url -> pure url