git.iko.soy/rowtype-yoga/psfp
on master help sign in
Rerenders too much
6c57a55Mark Eibes
blog/.psc-ide-port+1−1
@@ -1 +1 @@
1−15613
1+15549
blog/src/components/PSLayout.purs+36−13
@@ -1,17 +1,20 @@
1module PSLayout where
1module PSLayout where
2
2
3import Prelude
3import Prelude
4+
5+import Data.Array (foldMap)
4import Data.Array as A
6import Data.Array as A
5import Data.Array as Array
7import Data.Array as Array
6import Data.Array.NonEmpty as NEA
8import Data.Array.NonEmpty as NEA
7−import Data.Function.Uncurried (mkFn2)
9+import Data.Either (Either(..))
8import Data.Maybe (Maybe(..), fromMaybe, isJust)
10import Data.Maybe (Maybe(..), fromMaybe, isJust)
9−import Data.Nullable (Nullable, toMaybe)
11+import Data.Nullable (Nullable)
10import Data.Nullable as Nullable
12import Data.Nullable as Nullable
11import Data.String as String
13import Data.String as String
12import Data.Tuple.Nested ((/\))
14import Data.Tuple.Nested ((/\))
13−import Debug.Trace (spy)
14import Effect (Effect)
15import Effect (Effect)
16+import Effect.Aff (launchAff_)
17+import Effect.Class (liftEffect)
15import JSS (jss, jssClasses)
18import JSS (jss, jssClasses)
16import Justifill (justifill)
19import Justifill (justifill)
17import Milkis.Impl (FetchImpl)
20import Milkis.Impl (FetchImpl)
@@ -23,14 +26,16 @@ import React.Basic.Hooks (ReactChildren, component, componentWithChildren, eleme
23import React.Basic.Hooks as React
26import React.Basic.Hooks as React
24import Unsafe.Coerce (unsafeCoerce)
27import Unsafe.Coerce (unsafeCoerce)
25import Yoga.Box.Component as Box
28import Yoga.Box.Component as Box
29+import Yoga.ClickAway.Component as ClickAway
26import Yoga.CompileEditor.Component (mkCompileEditor)
30import Yoga.CompileEditor.Component (mkCompileEditor)
27import Yoga.Compiler.Api (apiCompiler)
31import Yoga.Compiler.Api (apiCompiler)
28import Yoga.Compiler.Types (Compiler)
32import Yoga.Compiler.Types (Compiler)
29import Yoga.FillInTheGaps.Component as FillInTheGaps
33import Yoga.FillInTheGaps.Component as FillInTheGaps
30−import Yoga.FillInTheGaps.Logic (complete, parseSegments)
34+import Yoga.FillInTheGaps.Logic (findResult, parseSegments, toCode)
31import Yoga.Header.Component (mkHeader)
35import Yoga.Header.Component (mkHeader)
32import Yoga.Helpers ((?||))
36import Yoga.Helpers ((?||))
33import Yoga.InlineCode.Component as InlineCode
37import Yoga.InlineCode.Component as InlineCode
38+import Yoga.Modal.Component as Modal
34import Yoga.Theme (fromTheme)
39import Yoga.Theme (fromTheme)
35import Yoga.Theme.CSSBaseline (mkCssBaseline)
40import Yoga.Theme.CSSBaseline (mkCssBaseline)
36import Yoga.Theme.Default (darkTheme)
41import Yoga.Theme.Default (darkTheme)
@@ -97,6 +102,13 @@ mkSecret = do
97 , children: kids
102 , children: kids
98 }
103 }
99
104
105+isQuiz ∷ JSX -> Boolean
106+isQuiz a =
107+ (unsafeCoerce a).props.mdxType == "pre"
108+ && ((unsafeCoerce a).props.children.props.className == "language-purescript")
109+ && ((unsafeCoerce a).props.children.props.mdxType == "code")
110+ && ((unsafeCoerce a).props.children.props.children # parseSegments # isJust)
111+
100mkMdxProviderComponent ∷
112mkMdxProviderComponent ∷
101 ∀ r.
113 ∀ r.
102 { | Compiler r } ->
114 { | Compiler r } ->
@@ -114,6 +126,8 @@ mkMdxProviderComponent compiler = do
114 sidebar <- mkSidebar
126 sidebar <- mkSidebar
115 header <- mkHeader
127 header <- mkHeader
116 yogaInlineCode <- InlineCode.makeComponent
128 yogaInlineCode <- InlineCode.makeComponent
129+ modal <- Modal.makeComponent
130+ clickAway <- ClickAway.makeComponent
117 h <- mkH
131 h <- mkH
118 p <- mkP
132 p <- mkP
119 secret <- mkSecret
133 secret <- mkSecret
@@ -136,6 +150,8 @@ mkMdxProviderComponent compiler = do
136 componentWithChildren "MDXProviderComponent" \{ children, siteInfo } -> React.do
150 componentWithChildren "MDXProviderComponent" \{ children, siteInfo } -> React.do
137 classes <- useStyles {}
151 classes <- useStyles {}
138 visibleThrough /\ updateVisible <- useState 1
152 visibleThrough /\ updateVisible <- useState 1
153+ questions /\ updateQuestions <- useState []
154+ maybeModal /\ updateModal <- useState Nothing
139 let
155 let
140 baseline child = element cssBaseline { kids: child }
156 baseline child = element cssBaseline { kids: child }
141
157
@@ -146,10 +162,7 @@ mkMdxProviderComponent compiler = do
146 if i >= visibleThrough then
162 if i >= visibleThrough then
147 { i, acc }
163 { i, acc }
148 else
164 else
149− if (unsafeCoerce a).props.mdxType == "pre"
165+ if isQuiz a then
150− && ((unsafeCoerce a).props.children.props.className == "language-purescript")
151− && ((unsafeCoerce a).props.children.props.mdxType == "code")
152− && ((unsafeCoerce a).props.children.props.children # parseSegments # isJust) then
153 { i: i + 1, acc: A.snoc acc a }
166 { i: i + 1, acc: A.snoc acc a }
154 else
167 else
155 { i, acc: A.snoc acc a }
168 { i, acc: A.snoc acc a }
@@ -218,11 +231,14 @@ mkMdxProviderComponent compiler = do
218 jsx box {}
231 jsx box {}
219 [ element fillInTheGaps
232 [ element fillInTheGaps
220 { initialSegments
233 { initialSegments
221− , update:
234+ , incantate: \segments -> launchAff_ do
222− \segs ->
235+ let code = toCode segments
223− when (complete segs)
236+ result <- compiler.compileAndRun { code }
237+ liftEffect case result of
238+ Right r | String.stripSuffix (String.Pattern "\n") r.stdout == (findResult $ join segments) ->
224− $ updateVisible
239+ updateVisible (_ + 1)
225− (_ + 1)
240+ _ ->
241+ updateModal (const $ Just { title: "Failed", kids: [R.text "Try again"] })
226 }
242 }
227 ]
243 ]
228 true, _, _ ->
244 true, _, _ ->
@@ -241,6 +257,13 @@ mkMdxProviderComponent compiler = do
241 ]
257 ]
242 , components: mdxComponents
258 , components: mdxComponents
243 }
259 }
260+ , maybeModal # foldMap
261+ (\modalProps ->
262+ fragment [
263+ element clickAway { allowEscape: Just true, onClick: updateModal (const Nothing), style: Nothing },
264+ element modal (justifill modalProps)
265+ ]
266+ )
244 ]
267 ]
245
268
246mkSidebar = do
269mkSidebar = do
components/src/Yoga/FillInTheGaps/Component.purs+25−11
@@ -4,18 +4,20 @@ import Prelude
4import Data.Array as A
4import Data.Array as A
5import Data.Tuple.Nested ((/\))
5import Data.Tuple.Nested ((/\))
6import Effect (Effect)
6import Effect (Effect)
7−import Effect.Aff (Milliseconds(..), delay)
8−import Effect.Class (liftEffect)
9import Justifill (justifill)
7import Justifill (justifill)
10import React.Basic (JSX, ReactComponent, element)
8import React.Basic (JSX, ReactComponent, element)
11import React.Basic.DOM as R
9import React.Basic.DOM as R
10+import React.Basic.Events (handler_)
11+import React.Basic.Helpers (jsx)
12−import React.Basic.Hooks (component, useState)
12+import React.Basic.Hooks (component, useEffect, useState)
13import React.Basic.Hooks as React
13import React.Basic.Hooks as React
14−import React.Basic.Hooks.Aff (useAff)
15import React.Basic.SyntaxHighlighter.Component (HighlighterTheme, syntaxHighlighterImpl)
14import React.Basic.SyntaxHighlighter.Component (HighlighterTheme, syntaxHighlighterImpl)
15+import Yoga.Button.Component (ButtonType(..), mkButton)
16−import Yoga.FillInTheGaps.Logic (Segment(..), updateSegments)
16+import Yoga.FillInTheGaps.Logic (Segment(..), complete, updateSegments)
17import Yoga.Helpers ((?||))
17import Yoga.Helpers ((?||))
18import Yoga.InlineCode.Component as InlineCode
18import Yoga.InlineCode.Component as InlineCode
19+import Yoga.Stack.Component as Stack
20+import Yoga.Cluster.Component as Cluster
19import Yoga.Theme.Styles (useTheme)
21import Yoga.Theme.Styles (useTheme)
20import Yoga.Theme.Syntax (mkHighlighterTheme)
22import Yoga.Theme.Syntax (mkHighlighterTheme)
21
23
@@ -43,13 +45,25 @@ renderSegments highlighterTheme ic update arrs = R.div_ (A.mapWithIndex renderLi
43 else
45 else
44 mempty
46 mempty
45
47
46−makeComponent ∷ Effect (ReactComponent { initialSegments ∷ Array (Array Segment), update ∷ Array (Array Segment) -> Effect Unit })
48+makeComponent ∷ Effect (ReactComponent { initialSegments ∷ Array (Array Segment), incantate ∷ Array (Array Segment) -> Effect Unit })
47makeComponent = do
49makeComponent = do
48 ic <- InlineCode.makeComponent
50 ic <- InlineCode.makeComponent
51+ btn <- mkButton
52+ stack <- Stack.makeComponent
53+ cluster <- Cluster.makeComponent
49− component "FillInTheGaps" \{ initialSegments, update } -> React.do
54+ component "FillInTheGaps" \{ initialSegments, incantate } -> React.do
50 segments /\ modifySegments <- useState initialSegments
55 segments /\ modifySegments <- useState initialSegments
51− useAff segments do
52− delay (200.0 # Milliseconds)
53− update segments # liftEffect
54 highlighterTheme <- useTheme <#> mkHighlighterTheme
56 highlighterTheme <- useTheme <#> mkHighlighterTheme
57+ pure
58+ $ jsx stack {}
55− pure $ renderSegments highlighterTheme ic (modifySegments) segments
59+ [ renderSegments highlighterTheme ic (modifySegments) segments
60+ , jsx cluster {}
61+ [ R.div_
62+ [ jsx btn
63+ { onClick: handler_ (incantate segments)
64+ , buttonType: if complete segments then HighlightedButton else DisabledButton
65+ }
66+ [ R.text "Incantate" ]
67+ ]
68+ ]
69+ ]
components/src/Yoga/FillInTheGaps/Stories.purs+1−1
@@ -14,7 +14,7 @@ stories = do
14 storiesOf "FillInTheGaps" do
14 storiesOf "FillInTheGaps" do
15 addDecorator fullScreenDecorator
15 addDecorator fullScreenDecorator
16 add "The FillInTheGaps" (FillInTheGaps.makeComponent)
16 add "The FillInTheGaps" (FillInTheGaps.makeComponent)
17− [ { initialSegments: parseSegments codeWithHoles ?|| [], update: pure (pure unit) }
17+ [ { initialSegments: parseSegments codeWithHoles ?|| [], incantate: pure (pure unit) }
18 ]
18 ]
19
19
20codeWithHoles =
20codeWithHoles =