{-# LANGUAGE MultiWayIf #-} module Events where import Prelude () import Prelude.Compat import Brick import Control.Monad (forM_) import Control.Monad.IO.Class (liftIO) import qualified Data.Set as Set import qualified Data.Text as T import Data.Monoid ((<>)) import qualified Graphics.Vty as Vty import Lens.Micro.Platform import Network.Mattermost import Network.Mattermost.Lenses import Network.Mattermost.WebSocket import Connection import State import State.Common import Types import Types.Channels (ccInfo, cdMentionCount) import Events.ShowHelp import Events.Main import Events.JoinChannel import Events.ChannelScroll import Events.ChannelSelect import Events.LeaveChannelConfirm import Events.DeleteChannelConfirm import Events.UrlSelect import Events.MessageSelect import Events.PostListOverlay onEvent :: ChatState -> BrickEvent Name MHEvent -> EventM Name (Next ChatState) onEvent st ev = runMHEvent st $ case ev of (AppEvent e) -> onAppEvent e (VtyEvent (Vty.EvKey (Vty.KChar 'l') [Vty.MCtrl])) -> do vty <- mh getVtyHandle liftIO $ Vty.refresh vty (VtyEvent e) -> onVtyEvent e _ -> return () onAppEvent :: MHEvent -> MH () onAppEvent RefreshWebsocketEvent = do st <- use id liftIO $ connectWebsockets st onAppEvent WebsocketDisconnect = csConnectionStatus .= Disconnected onAppEvent WebsocketConnect = do csConnectionStatus .= Connected refreshChannelsAndUsers onAppEvent BGIdle = csWorkerIsBusy .= Nothing onAppEvent (BGBusy n) = csWorkerIsBusy .= Just n onAppEvent (WSEvent we) = handleWSEvent we onAppEvent (RespEvent f) = f onAppEvent (AsyncErrEvent e) = do let msg = "An unexpected error has occurred! The exception encountered was:\n " <> T.pack (show e) <> "\nPlease report this error at https://github.com/matterhorn-chat/matterhorn/issues" postErrorMessage msg onVtyEvent :: Vty.Event -> MH () onVtyEvent e = do -- Even if we aren't showing the help UI when a resize occurs, we -- need to invalidate its cache entry anyway in case the new size -- differs from the cached size. case e of (Vty.EvResize _ _) -> do mh $ invalidateCacheEntry HelpText mh $ invalidateCacheEntry ScriptHelpText _ -> return () mode <- use csMode case mode of Main -> onEventMain e ShowHelp _ -> onEventShowHelp e ChannelSelect -> onEventChannelSelect e UrlSelect -> onEventUrlSelect e LeaveChannelConfirm -> onEventLeaveChannelConfirm e JoinChannel -> onEventJoinChannel e ChannelScroll -> onEventChannelScroll e MessageSelect -> onEventMessageSelect e MessageSelectDeleteConfirm -> onEventMessageSelectDeleteConfirm e DeleteChannelConfirm -> onEventDeleteChannelConfirm e PostListOverlay _ -> onEventPostListOverlay e handleWSEvent :: WebsocketEvent -> MH () handleWSEvent we = do myId <- use (csMe.userIdL) myTeamId <- use (csMyTeam.teamIdL) case weEvent we of WMPosted -> case wepPost (weData we) of Just p -> do -- If the message is a header change, also update the channel -- metadata. myUserId <- use (csMe.userIdL) case wepMentions (weData we) of Just lst | myUserId `Set.member` lst -> csChannel(postChannelId p).ccInfo.cdMentionCount += 1 _ -> return () addMessageToState p >>= postProcessMessageAdd Nothing -> return () WMPostEdited -> case wepPost (weData we) of Just p -> editMessage p Nothing -> return () WMPostDeleted -> case wepPost (weData we) of Just p -> deleteMessage p Nothing -> return () WMStatusChange -> case wepStatus (weData we) of Just status -> case wepUserId (weData we) of Just uId -> updateStatus uId status Nothing -> return () Nothing -> return () WMUserAdded -> case webChannelId (weBroadcast we) of Just cId -> if wepUserId (weData we) == Just myId && wepTeamId (weData we) == Just myTeamId then handleChannelInvite cId else return () Nothing -> return () WMUserUpdated -> -- XXX return () WMNewUser -> do let Just newUserId = wepUserId $ weData we handleNewUser newUserId WMUserRemoved -> -- XXX return () WMChannelDeleted -> -- XXX return () WMDirectAdded -> -- XXX return () WMChannelCreated -> -- XXX return () WMGroupAdded -> -- XXX return () WMEmojiAdded -> -- XXX return () WMLeaveTeam -> -- XXX: How do we deal with this one? return () -- An 'ephemeral message' is just Mattermost's version -- of our 'client message'. This can be a little bit -- wacky, e.g. if the user types '/shortcuts' in the -- browser, we'll get an ephemeral message even in -- MatterHorn with the browser shortcuts, but it's -- probably a good idea to handle these messages anyway. WMEphemeralMessage -> case wepPost (weData we) of Just p -> do postInfoMessage (p^.postMessageL) Nothing -> return () -- The only preference we observe right now is flagging WMPreferenceChanged | Just pref <- wepPreferences (weData we) , Just fps <- mapM preferenceToFlaggedPost pref -> forM_ fps $ \f -> updateMessageFlag (flaggedPostId f) (flaggedPostStatus f) | otherwise -> return () WMPreferenceDeleted | Just pref <- wepPreferences (weData we) , Just fps <- mapM preferenceToFlaggedPost pref -> forM_ fps $ \f -> updateMessageFlag (flaggedPostId f) False | otherwise -> return () -- This happens whenever a user connects to the server -- I think all the information we need (about being -- online or away or what-have-you) gets represented -- in StatusChanged messages, so we can ignore it. WMHello -> return () -- right now we don't show typing notifications. maybe -- we should? i dunno. WMTyping -> return () -- Do we need to do anything with this? WMUpdateTeam -> return () WMReactionAdded -> case wepReaction (weData we) of Just r -> case webChannelId (weBroadcast we) of Just cId -> addReactions cId [r] Nothing -> return () Nothing -> return () WMReactionRemoved -> case wepReaction (weData we) of Just r -> case webChannelId (weBroadcast we) of Just cId -> removeReaction r cId Nothing -> return () Nothing -> return () WMAddedToTeam -> return () -- XXX: we need to handle this WMWebRTC -> return () WMAuthenticationChallenge -> return () WMChannelViewed -> case webChannelId $ weBroadcast we of Just cId -> refreshChannelById False cId Nothing -> return () WMChannelUpdated -> case webChannelId $ weBroadcast we of Just cId -> refreshChannelById False cId Nothing -> return ()