forked from HeinrichApfelmus/threepenny-gui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Chat.hs
106 lines (85 loc) · 3.23 KB
/
Chat.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import Control.Concurrent
import qualified Control.Concurrent.Chan as Chan
import Control.Exception
import Control.Monad
import Data.Functor
import Data.List.Extra
import Data.Time
import Data.IORef
import Prelude hiding (catch)
import Paths
import qualified Graphics.UI.Threepenny as UI
import Graphics.UI.Threepenny.Core hiding (text)
{-----------------------------------------------------------------------------
Chat
------------------------------------------------------------------------------}
main :: IO ()
main = do
static <- getStaticDir
messages <- Chan.newChan
startGUI defaultConfig
{ tpPort = 10000
, tpCustomHTML = Just "chat.html"
, tpStatic = Just static
} $ setup messages
type Message = (UTCTime, String, String)
setup :: Chan Message -> Window -> UI ()
setup globalMsgs window = do
msgs <- liftIO $ Chan.dupChan globalMsgs
return window # set title "Chat"
(nickRef, nickname) <- mkNickname
messageArea <- mkMessageArea msgs nickRef
getBody window #+
[ UI.div #. "header" #+ [string "Threepenny Chat"]
, UI.div #. "gradient"
, viewSource
, element nickname
, element messageArea
]
messageReceiver <- liftIO $ forkIO $ receiveMessages window msgs messageArea
on UI.disconnect window $ const $ liftIO $ do
killThread messageReceiver
now <- getCurrentTime
nick <- readIORef nickRef
Chan.writeChan msgs (now,nick,"( left the conversation )")
receiveMessages w msgs messageArea = do
messages <- Chan.getChanContents msgs
forM_ messages $ \msg -> do
atomic w $ runUI w $ do
-- FIXME: withWindow should include a call to atomic ?
element messageArea #+ [mkMessage msg]
UI.scrollToBottom messageArea
mkMessageArea :: Chan Message -> IORef String -> UI Element
mkMessageArea msgs nickname = do
input <- UI.textarea #. "send-textarea"
on UI.sendValue input $ (. trim) $ \content -> do
element input # set value ""
when (not (null content)) $ liftIO $ do
now <- getCurrentTime
nick <- readIORef nickname
when (not (null nick)) $
Chan.writeChan msgs (now,nick,content)
UI.div #. "message-area" #+ [UI.div #. "send-area" #+ [element input]]
mkNickname :: UI (IORef String, Element)
mkNickname = do
input <- UI.input #. "name-input"
el <- UI.div #. "name-area" #+
[ UI.span #. "name-label" #+ [string "Your name "]
, element input
]
UI.setFocus input
nick <- liftIO $ newIORef ""
on UI.keyup input $ \_ -> liftIO . writeIORef nick . trim =<< get value input
return (nick,el)
mkMessage :: Message -> UI Element
mkMessage (timestamp, nick, content) =
UI.div #. "message" #+
[ UI.div #. "timestamp" #+ [string $ show timestamp]
, UI.div #. "name" #+ [string $ nick ++ " says:"]
, UI.div #. "content" #+ [string content]
]
viewSource :: UI Element
viewSource =
UI.anchor #. "view-source" # set UI.href url #+ [string "View source code"]
where
url = "https://github.com/HeinrichApfelmus/threepenny-gui/blob/master/src/Chat.hs"