module Example.Counter where import Data.Text (pack) import Effectful import Effectful.Concurrent.STM import Example.Style as Style import Web.Hyperbole -- We are using a TVar to manage our state -- In normal web applications, state will be managed in a database, abstracted behind a custom Effect. See Example.EFfects.Users for the interface -- Optionally, the count could be stored in a session. See Example.Sessions page :: (Hyperbole :> es, Concurrent :> es) => TVar Int -> Page es Response page var = do handle $ counter var load $ do n <- readTVarIO var pure $ col (pad 20 . gap 10) $ do el h1 "Counter" hyper Counter (viewCount n) data Counter = Counter deriving (Generic, Param) instance HyperView Counter where type Action Counter = Count data Count = Increment | Decrement deriving (Generic, Param) counter :: (Hyperbole :> es, Concurrent :> es) => TVar Int -> Counter -> Count -> Eff es (View Counter ()) counter var _ Increment = do n <- modify var $ \n -> n + 1 pure $ viewCount n counter var _ Decrement = do n <- modify var $ \n -> n - 1 pure $ viewCount n viewCount :: Int -> View Counter () viewCount n = col (gap 10) $ do row id $ do el (bold . fontSize 48 . border 1 . pad (XY 20 0)) $ text $ pack $ show n row (gap 10) $ do button Decrement Style.btn "Decrement" button Increment Style.btn "Increment" modify :: (Concurrent :> es) => TVar Int -> (Int -> Int) -> Eff es Int modify var f = atomically $ do modifyTVar var f readTVar var initCounter :: (Concurrent :> es) => Eff es (TVar Int) initCounter = newTVarIO 0