Skip to content

Commit 04ab611

Browse files
committed
Adding app state and delete feature
1 parent e5d3ddf commit 04ab611

File tree

1 file changed

+69
-6
lines changed

1 file changed

+69
-6
lines changed

src/Bulletin.hs

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import Network.Wai.Handler.Warp (run, Port)
88
import qualified Data.Text as T
99
import qualified Data.Time.Clock as C
1010
import 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.
1315
main :: IO ()
@@ -30,20 +32,23 @@ runServer port = do
3032
mkApp :: IO Twain.Application
3133
mkApp = 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

Comments
 (0)