hstorchat-0.1.0.0: src/Network/HSTorChat/GUI.hs
{-# LANGUAGE DeriveDataTypeable, TypeFamilies, OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Network.HSTorChat.GUI where
import Control.Concurrent
import qualified Data.Text as T
import Data.Proxy
import Data.Typeable
import Graphics.QML
import System.IO
import System.Random
import Network.HSTorChat.Protocol
-- Signals
data BuddiesChanged deriving Typeable
data NewChatMsg deriving Typeable
data BuddyChanged deriving Typeable
instance DefaultClass TorChat where
classMembers = [
defPropertySigRO "buddies" (Proxy :: Proxy BuddiesChanged) buddies
-- | Return Onion address for this instance of HSTorChat.
, defMethod "onion" (return . _myonion . fromObjRef :: ObjRef TorChat -> IO Onion)
-- | Send a message to a buddy.
, defMethod "sendMsg" sendMsg
-- | Add a new buddy.
, defMethod "newBuddy" newBuddy
, defMethod "setStatus" statusChanged
]
where
buddies :: ObjRef TorChat -> IO [ObjRef Buddy]
buddies tc = return . buddylist =<< (readMVar . _buddies $ fromObjRef tc)
instance DefaultClass ChatMsg where
classMembers = [
defPropertyRO "buddy" (return . buddy . fromObjRef)
, defPropertyRO "text" (return . text . fromObjRef)
, defPropertyRO "fromme" (return . fromme . fromObjRef)
]
instance DefaultClass Buddy where
classMembers = [
defPropertyRO "onion" (return . _onion . fromObjRef)
, defPropertySigRO "status" (Proxy :: Proxy BuddyChanged) status
, defPropertySigRO "msgs" (Proxy :: Proxy NewChatMsg) messages
]
where
messages :: ObjRef Buddy -> IO [ObjRef ChatMsg]
messages = readMVar . _msgs . fromObjRef
status = return . T.pack . show . _status . fromObjRef
instance Marshal ChatMsg where
type MarshalMode ChatMsg c d = ModeObjFrom ChatMsg c
marshaller = fromMarshaller fromObjRef
instance Marshal Buddy where
type MarshalMode Buddy c d = ModeObjFrom Buddy c
marshaller = fromMarshaller fromObjRef
instance SignalKeyClass BuddiesChanged where
type SignalParams BuddiesChanged = IO ()
instance SignalKeyClass NewChatMsg where
type SignalParams NewChatMsg = IO ()
instance SignalKeyClass BuddyChanged where
type SignalParams BuddyChanged = IO ()
-- | This function is called when the user enters
-- a msg in a chat window. The handle for the buddy
-- is accessed and used to send the message.
sendMsg :: ObjRef TorChat -> ObjRef Buddy -> T.Text -> IO ()
sendMsg _ bud msg
| null (T.unpack msg) = putStrLn "Ignoring empty request."
-- Check if buddy is offline
| (_status . fromObjRef) bud == Offline = putStrLn "[delayed] msg not supported yet."
-- buddy should be able to receive the message
-- TODO: Implement gaurd to check _outConn status.
| otherwise = do
saveMsg $ ChatMsg msg (_onion $ fromObjRef bud) True
fireSignal (Proxy :: Proxy NewChatMsg) bud
hPutStrLn (_outConn $ fromObjRef bud) $ formatMsg $ Message msg
where
saveMsg cmsg = modifyMVar_ (_msgs $ fromObjRef bud) (\ms -> do m <- newObjectDC cmsg
return (m:ms))
newBuddy :: ObjRef TorChat -> T.Text -> IO ()
newBuddy tc onion
| T.length onion /= 16 = putStrLn $ T.unpack onion ++ " is not 16 characters long."
| otherwise = putStrLn ("Requesting buddy connection: " ++ T.unpack onion) >>
hstorchatOutConn (onion `T.append` ".onion") >>= ping
where ping Nothing = putStrLn $ "Error attempting to connect to " ++ T.unpack onion
ping (Just oHdl) = do gen <- getStdGen
let cky = gencookie gen
tc' = fromObjRef tc
-- Add to list of pending connection.
modifyMVar_ (_pending tc')
$ \p -> return $ PendingConnection cky onion oHdl : filter ((/= onion) . _ponion) p
hPutStrLn oHdl $ formatMsg $ Ping (_myonion tc') cky
statusChanged :: ObjRef TorChat -> T.Text -> IO ()
statusChanged tc status
| T.unpack status == "Away" = alert Away
| T.unpack status == "Extended Away" = alert Xa
| otherwise = alert Available
where
alert st = do bs' <- readMVar . _buddies $ fromObjRef tc
bl <- return . map fromObjRef $ buddylist bs'
-- tell online buddies status.
tell (online bl) $ Status st
online = filter $ (/= Offline) . _status
tell [] _ = return ()
tell (Buddy _ _ oHdl _ _ _:bs) st = hPutStrLn oHdl (formatMsg st) >> tell bs st