22{-# LANGUAGE CPP #-}
33{-# LANGUAGE LambdaCase #-}
44{-# LANGUAGE DeriveGeneric #-}
5- {-# LANGUAGE RecordWildCards #-}
65{-# LANGUAGE DeriveAnyClass #-}
6+ {-# LANGUAGE TemplateHaskell #-}
7+ {-# LANGUAGE RecordWildCards #-}
78{-# LANGUAGE OverloadedStrings #-}
89{-# LANGUAGE DerivingStrategies #-}
910-----------------------------------------------------------------------------
1718----------------------------------------------------------------------------
1819module Main where
1920----------------------------------------------------------------------------
20- import Control.Monad.State
21+ import Control.Category ((.) )
22+ import qualified Data.IntMap as IM
23+ import Data.IntMap (IntMap )
2124import Data.Bool
2225import GHC.Generics
26+ import Prelude hiding ((.) )
2327----------------------------------------------------------------------------
24- import Miso
28+ import Miso hiding ( at )
2529import Miso.Html
26- import Miso.JSON
30+ import Miso.Lens
31+ import Miso.Lens.TH
2732import Miso.Html.Property hiding (label_ )
2833import qualified Miso.String as S
2934import qualified Miso.CSS as CSS
3035----------------------------------------------------------------------------
3136default (MisoString )
3237----------------------------------------------------------------------------
33- #ifdef WASM
34- foreign export javascript " hs_start" main :: IO ()
35- #endif
36- ----------------------------------------------------------------------------
3738data Model
3839 = Model
39- { entries :: [ Entry ]
40- , field :: MisoString
41- , uid :: Int
42- , visibility :: MisoString
43- , step :: Bool
40+ { _entries :: IntMap Entry
41+ , _field :: MisoString
42+ , _uid :: Int
43+ , _visibility :: MisoString
44+ , _step :: Bool
4445 } deriving stock (Show , Generic , Eq )
45- deriving anyclass (FromJSON , ToJSON )
4646----------------------------------------------------------------------------
4747data Entry
4848 = Entry
49- { description :: MisoString
50- , completed :: Bool
51- , editing :: Bool
52- , eid :: Int
53- , focussed :: Bool
49+ { _description :: MisoString
50+ , _completed :: Bool
51+ , _editing :: Bool
52+ , _focussed :: Bool
5453 } deriving stock (Show , Generic , Eq )
55- deriving anyclass (FromJSON , ToJSON )
54+ ----------------------------------------------------------------------------
55+ $ (makeLenses ''Entry)
56+ $ (makeLenses ''Model)
5657----------------------------------------------------------------------------
5758emptyModel :: Model
5859emptyModel
5960 = Model
60- { entries = []
61- , visibility = " All"
62- , field = mempty
63- , uid = 0
64- , step = False
61+ { _entries = mempty
62+ , _visibility = " All"
63+ , _field = mempty
64+ , _uid = 0
65+ , _step = False
6566 }
6667----------------------------------------------------------------------------
67- newEntry :: MisoString -> Int -> Entry
68- newEntry desc eid
68+ newEntry :: MisoString -> Entry
69+ newEntry desc
6970 = Entry
70- { description = desc
71- , completed = False
72- , editing = False
73- , eid = eid
74- , focussed = False
71+ { _description = desc
72+ , _completed = False
73+ , _editing = False
74+ , _focussed = False
7575 }
7676----------------------------------------------------------------------------
7777data Msg
@@ -89,6 +89,12 @@ data Msg
8989 | FocusOnInput
9090 deriving (Show )
9191----------------------------------------------------------------------------
92+ #ifdef WASM
93+ #ifndef INTERACTIVE
94+ foreign export javascript " hs_start" main :: IO ()
95+ #endif
96+ #endif
97+ ----------------------------------------------------------------------------
9298main :: IO ()
9399main = startApp (defaultEvents <> keyboardEvents) app
94100----------------------------------------------------------------------------
@@ -111,72 +117,48 @@ updateModel = \case
111117 FocusOnInput ->
112118 io_ (focus " input-box" )
113119 CurrentTime time ->
114- io_ $ consoleLog $ S. ms ( show time)
120+ io_ $ consoleLog ( S. ms time)
115121 Add -> do
116- model@ Model {.. } <- get
117- put model
118- { uid = uid + 1
119- , field = mempty
120- , entries = entries <> [newEntry field uid | not $ S. null field]
121- }
122- UpdateField str ->
123- modify update
124- where
125- update m = m { field = str }
126- EditingEntry id' isEditing ->
127- modify $ \ m ->
128- m { entries =
129- filterMap (entries m) (\ t -> eid t == id') $ \ t ->
130- t { editing = isEditing
131- , focussed = isEditing
132- }
133- }
134- UpdateEntry id' task ->
135- modify $ \ m -> m
136- { entries = filterMap (entries m) ((== id') . eid) $ \ t ->
137- t { description = task }
138- }
139- Delete id' ->
140- modify $ \ m -> m
141- { entries = filter (\ t -> eid t /= id') (entries m)
142- }
122+ value <- use field
123+ field .= mempty
124+ uid += 1
125+ nextId <- use uid
126+ entries %= IM. insert nextId (newEntry value)
127+ UpdateField str -> do
128+ field .= str
129+ EditingEntry idx isEditing ->
130+ entries . at idx %?= (\ e ->
131+ e & editing .~ isEditing
132+ & focussed .~ isEditing)
133+ UpdateEntry idx task ->
134+ entries . at idx %?= do
135+ description .~ task
136+ Delete idx ->
137+ entries . at idx .= Nothing
143138 DeleteComplete ->
144- modify $ \ m -> m
145- { entries = filter (not . completed) (entries m)
146- }
147- Check id' isCompleted ->
148- modify $ \ m -> m
149- { entries =
150- filterMap (entries m) (\ t -> eid t == id') $ \ t ->
151- t { completed = isCompleted }
152- }
139+ entries %= IM. filter (\ entry -> not (entry ^. completed))
140+ Check idx isCompleted ->
141+ entries . at idx %?= do completed .~ isCompleted
153142 CheckAll isCompleted ->
154- modify $ \ m -> m
155- { entries =
156- filterMap (entries m) (const True ) $ \ t ->
157- t { completed = isCompleted }
158- }
143+ entries %= IM. map (\ entry -> entry & completed .~ isCompleted)
159144 ChangeVisibility v ->
160- modify $ \ m -> m { visibility = v }
161- ----------------------------------------------------------------------------
162- filterMap :: [a ] -> (a -> Bool ) -> (a -> a ) -> [a ]
163- filterMap xs p f = [ if p x then f x else x | x <- xs ]
145+ visibility .= v
164146----------------------------------------------------------------------------
165147viewModel :: Model -> View model Msg
166- viewModel m@ Model { .. } =
148+ viewModel m =
167149 div_
168150 [ class_ " todomvc-wrapper"
169151 ]
170152 [ section_
171153 [class_ " todoapp" ]
172- [ viewInput m field
173- , viewEntries visibility entries
174- , viewControls m visibility entries
154+ [ viewInput m (m ^. field)
155+ , viewEntries (m ^. visibility) ( IM. toList (m ^. entries))
156+ , viewControls m (m ^. visibility) ( IM. toList (m ^. entries))
175157 ]
176158 , infoFooter
177159 ]
178160----------------------------------------------------------------------------
179- viewEntries :: MisoString -> [Entry ] -> View model Msg
161+ viewEntries :: MisoString -> [( Int , Entry ) ] -> View model Msg
180162viewEntries visibility entries =
181163 section_
182164 [ class_ " main"
@@ -194,40 +176,36 @@ viewEntries visibility entries =
194176 [for_ " toggle-all" ]
195177 [text $ S. pack " Mark all as complete" ]
196178 , ul_ [class_ " todo-list" ] $
197- flip map (filter isVisible entries) $ \ t ->
198- viewKeyedEntry t
179+ filter isVisible entries <&> viewEntry
199180 ]
200181 where
201182 cssVisibility = bool " visible" " hidden" (null entries)
202- allCompleted = all completed entries
203- isVisible Entry {.. } =
183+ allCompleted = all _completed ( snd <$> entries)
184+ isVisible (_, Entry {.. }) =
204185 case visibility of
205- " Completed" -> completed
206- " Active" -> not completed
186+ " Completed" -> _completed
187+ " Active" -> not _completed
207188 _ -> True
208189----------------------------------------------------------------------------
209- viewKeyedEntry :: Entry -> View model Msg
210- viewKeyedEntry = viewEntry
211- ----------------------------------------------------------------------------
212- viewEntry :: Entry -> View model Msg
213- viewEntry Entry {.. } =
190+ viewEntry :: (Int , Entry ) -> View model Msg
191+ viewEntry (eid, Entry {.. }) =
214192 li_
215193 [ class_ $
216194 S. intercalate " " $
217- [" completed" | completed ] <> [" editing" | editing ]
195+ [" completed" | _completed ] <> [" editing" | _editing ]
218196 , key_ eid
219197 ]
220198 [ div_
221199 [class_ " view" ]
222200 [ input_
223201 [ class_ " toggle"
224202 , type_ " checkbox"
225- , checked_ completed
226- , onClick $ Check eid (not completed )
203+ , checked_ _completed
204+ , onClick $ Check eid (not _completed )
227205 ]
228206 , label_
229207 [onDoubleClick (EditingEntry eid True ) ]
230- [text description ]
208+ [text _description ]
231209 , button_
232210 [ class_ " destroy"
233211 , onClick $ Delete eid
@@ -236,7 +214,7 @@ viewEntry Entry{..} =
236214 ]
237215 , input_
238216 [ class_ " edit"
239- , value_ description
217+ , value_ _description
240218 , name_ " title"
241219 , id_ (" todo-" <> S. ms eid)
242220 , onInput (UpdateEntry eid)
@@ -245,7 +223,7 @@ viewEntry Entry{..} =
245223 ]
246224 ]
247225----------------------------------------------------------------------------
248- viewControls :: Model -> MisoString -> [Entry ] -> View model Msg
226+ viewControls :: Model -> MisoString -> [( Int , Entry ) ] -> View model Msg
249227viewControls model visibility entries =
250228 footer_
251229 [ class_ " footer"
@@ -256,7 +234,7 @@ viewControls model visibility entries =
256234 , viewControlsClear model entriesCompleted
257235 ]
258236 where
259- entriesCompleted = length . filter completed $ entries
237+ entriesCompleted = length . filter (_completed . snd ) $ entries
260238 entriesLeft = length entries - entriesCompleted
261239----------------------------------------------------------------------------
262240viewControlsCount :: Int -> View model Msg
0 commit comments