@@ -8,6 +8,8 @@ import Network.Wai.Handler.Warp (run, Port)
88import qualified Data.Text as T
99import qualified Data.Time.Clock as C
1010import qualified Data.Map as M
11+ import qualified Control.Concurrent.STM as STM
12+ import Control.Monad.IO.Class (liftIO )
1113
1214-- | Entry point. Starts a bulletin-board server at port 3000.
1315main :: IO ()
@@ -30,20 +32,23 @@ runServer port = do
3032mkApp :: IO Twain. Application
3133mkApp = do
3234 dummyPosts <- makeDummyPosts
35+ appstateVar <- STM. newTVarIO AppState {asNextId = 1 , asPosts = dummyPosts}
3336 pure $ foldr ($)
3437 (Twain. notFound $ Twain. send $ Twain. text " Error: not found." )
35- (routes dummyPosts )
38+ (routes appstateVar )
3639
3740-- | Bulletin board routing.
38- routes :: Posts -> [Twain. Middleware ]
39- routes posts =
41+ routes :: STM. TVar AppState -> [Twain. Middleware ]
42+ routes appstateVar =
4043 -- Our main page, which will display all of the bulletins
41- [ Twain. get " /" $
44+ [ Twain. get " /" $ do
45+ posts <- liftIO $ asPosts <$> STM. readTVarIO appstateVar
4246 Twain. send (displayAllPosts posts)
4347
4448 -- A page for a specific post
4549 , Twain. get " /post/:id" $ do
4650 pid <- Twain. param " id"
51+ posts <- liftIO $ asPosts <$> STM. readTVarIO appstateVar
4752 Twain. send (displayPost pid posts)
4853
4954 -- A page for creating a new post
@@ -55,8 +60,10 @@ routes posts =
5560 Twain. send $ Twain. text " not yet implemented"
5661
5762 -- A request to delete a specific post
58- , Twain. post " /post/:id/delete" $
59- Twain. send $ Twain. text " not yet implemented"
63+ , Twain. post " /post/:id/delete" $ do
64+ pid <- Twain. param " id"
65+ response <- liftIO $ handleDeletePost pid appstateVar
66+ Twain. send response
6067 ]
6168
6269-- ** Business logic
@@ -79,6 +86,30 @@ displayPost pid posts =
7986 [(" Content-Type" , " text/plain; charset=utf-8" )]
8087 " 404 Not found."
8188
89+ -- | Delete a post and respond to the user.
90+ handleDeletePost :: Integer -> STM. TVar AppState -> IO Twain. Response
91+ handleDeletePost pid appstateVar = do
92+ found <- deletePost pid appstateVar
93+ pure $
94+ if found
95+ then
96+ Twain. redirect302 " /"
97+
98+ else
99+ Twain. raw
100+ Twain. status404
101+ [(" Content-Type" , " text/html; charset=utf-8" )]
102+ " 404 Not Found."
103+
104+ -- ** Application state
105+
106+ -- | Application state.
107+ data AppState
108+ = AppState
109+ { asNextId :: Integer -- ^ The id for the next post
110+ , asPosts :: Posts -- ^ All posts
111+ }
112+
82113-- ** Posts
83114
84115-- | A mapping from a post id to a post.
@@ -129,3 +160,35 @@ ppPost post =
129160 , pContent post
130161 , seperator
131162 ]
163+
164+ -- | Add a new post to the store.
165+ newPost :: Post -> STM. TVar AppState -> IO Integer
166+ newPost post appstateVar = do
167+ STM. atomically $ do
168+ appstate <- STM. readTVar appstateVar
169+ STM. writeTVar
170+ appstateVar
171+ ( appstate
172+ { asNextId = asNextId appstate + 1
173+ , asPosts = M. insert (asNextId appstate) post (asPosts appstate)
174+ }
175+ )
176+ pure (asNextId appstate)
177+
178+ -- | Delete a post from the store.
179+ deletePost :: Integer -> STM. TVar AppState -> IO Bool
180+ deletePost pid appstateVar =
181+ STM. atomically $ do
182+ appstate <- STM. readTVar appstateVar
183+ case M. lookup pid (asPosts appstate) of
184+ Just {} -> do
185+ STM. writeTVar
186+ appstateVar
187+ ( appstate
188+ { asPosts = M. delete pid (asPosts appstate)
189+ }
190+ )
191+ pure True
192+
193+ Nothing ->
194+ pure False
0 commit comments