each command takes lock if it needs it (#273)
This commit is contained in:
parent
5aabf87898
commit
9b67aa537a
@ -112,9 +112,9 @@ execChatCommand :: (MonadUnliftIO m, MonadReader ChatController m) => ByteString
|
|||||||
execChatCommand s = case parseAll chatCommandP $ B.dropWhileEnd isSpace s of
|
execChatCommand s = case parseAll chatCommandP $ B.dropWhileEnd isSpace s of
|
||||||
Left e -> pure . CRChatError . ChatError $ CECommandError e
|
Left e -> pure . CRChatError . ChatError $ CECommandError e
|
||||||
Right cmd -> do
|
Right cmd -> do
|
||||||
ChatController {chatLock = l, smpAgent = a, currentUser} <- ask
|
ChatController {currentUser} <- ask
|
||||||
user <- readTVarIO currentUser
|
user <- readTVarIO currentUser
|
||||||
withAgentLock a . withLock l $ either CRChatCmdError id <$> runExceptT (processChatCommand user cmd)
|
either CRChatCmdError id <$> runExceptT (processChatCommand user cmd)
|
||||||
|
|
||||||
toView :: ChatMonad m => ChatResponse -> m ()
|
toView :: ChatMonad m => ChatResponse -> m ()
|
||||||
toView event = do
|
toView event = do
|
||||||
@ -129,7 +129,7 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
CTGroup -> CRApiChat . AChat SCTGroup <$> withStore (\st -> getGroupChat st user cId pagination)
|
CTGroup -> CRApiChat . AChat SCTGroup <$> withStore (\st -> getGroupChat st user cId pagination)
|
||||||
CTContactRequest -> pure $ CRChatError ChatErrorNotImplemented
|
CTContactRequest -> pure $ CRChatError ChatErrorNotImplemented
|
||||||
APIGetChatItems _count -> pure $ CRChatError ChatErrorNotImplemented
|
APIGetChatItems _count -> pure $ CRChatError ChatErrorNotImplemented
|
||||||
APISendMessage cType chatId mc -> case cType of
|
APISendMessage cType chatId mc -> withChatLock $ case cType of
|
||||||
CTDirect -> do
|
CTDirect -> do
|
||||||
ct@Contact {localDisplayName = c} <- withStore $ \st -> getContact st userId chatId
|
ct@Contact {localDisplayName = c} <- withStore $ \st -> getContact st userId chatId
|
||||||
ci <- sendDirectChatItem userId ct (XMsgNew mc) (CISndMsgContent mc)
|
ci <- sendDirectChatItem userId ct (XMsgNew mc) (CISndMsgContent mc)
|
||||||
@ -148,7 +148,7 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
withStore (\st -> getContactGroupNames st userId ct) >>= \case
|
withStore (\st -> getContactGroupNames st userId ct) >>= \case
|
||||||
[] -> do
|
[] -> do
|
||||||
conns <- withStore $ \st -> getContactConnections st userId ct
|
conns <- withStore $ \st -> getContactConnections st userId ct
|
||||||
procCmd $ do
|
withChatLock . procCmd $ do
|
||||||
withAgent $ \a -> forM_ conns $ \conn ->
|
withAgent $ \a -> forM_ conns $ \conn ->
|
||||||
deleteConnection a (aConnId conn) `catchError` \(_ :: AgentErrorType) -> pure ()
|
deleteConnection a (aConnId conn) `catchError` \(_ :: AgentErrorType) -> pure ()
|
||||||
withStore $ \st -> deleteContact st userId ct
|
withStore $ \st -> deleteContact st userId ct
|
||||||
@ -160,11 +160,11 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
APIAcceptContact connReqId -> do
|
APIAcceptContact connReqId -> do
|
||||||
UserContactRequest {agentInvitationId = AgentInvId invId, localDisplayName = cName, profileId, profile = p} <- withStore $ \st ->
|
UserContactRequest {agentInvitationId = AgentInvId invId, localDisplayName = cName, profileId, profile = p} <- withStore $ \st ->
|
||||||
getContactRequest st userId connReqId
|
getContactRequest st userId connReqId
|
||||||
procCmd $ do
|
withChatLock . procCmd $ do
|
||||||
connId <- withAgent $ \a -> acceptContact a invId . directMessage $ XInfo profile
|
connId <- withAgent $ \a -> acceptContact a invId . directMessage $ XInfo profile
|
||||||
acceptedContact <- withStore $ \st -> createAcceptedContact st userId connId cName profileId p
|
acceptedContact <- withStore $ \st -> createAcceptedContact st userId connId cName profileId p
|
||||||
pure $ CRAcceptingContactRequest acceptedContact
|
pure $ CRAcceptingContactRequest acceptedContact
|
||||||
APIRejectContact connReqId -> do
|
APIRejectContact connReqId -> withChatLock $ do
|
||||||
cReq@UserContactRequest {agentContactConnId = AgentConnId connId, agentInvitationId = AgentInvId invId} <-
|
cReq@UserContactRequest {agentContactConnId = AgentConnId connId, agentInvitationId = AgentInvId invId} <-
|
||||||
withStore $ \st ->
|
withStore $ \st ->
|
||||||
getContactRequest st userId connReqId
|
getContactRequest st userId connReqId
|
||||||
@ -173,29 +173,29 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
pure $ CRContactRequestRejected cReq
|
pure $ CRContactRequestRejected cReq
|
||||||
ChatHelp section -> pure $ CRChatHelp section
|
ChatHelp section -> pure $ CRChatHelp section
|
||||||
Welcome -> pure $ CRWelcome user
|
Welcome -> pure $ CRWelcome user
|
||||||
AddContact -> procCmd $ do
|
AddContact -> withChatLock . procCmd $ do
|
||||||
(connId, cReq) <- withAgent (`createConnection` SCMInvitation)
|
(connId, cReq) <- withAgent (`createConnection` SCMInvitation)
|
||||||
withStore $ \st -> createDirectConnection st userId connId
|
withStore $ \st -> createDirectConnection st userId connId
|
||||||
pure $ CRInvitation cReq
|
pure $ CRInvitation cReq
|
||||||
Connect (Just (ACR SCMInvitation cReq)) -> procCmd $ do
|
Connect (Just (ACR SCMInvitation cReq)) -> withChatLock . procCmd $ do
|
||||||
connect cReq $ XInfo profile
|
connect cReq $ XInfo profile
|
||||||
pure CRSentConfirmation
|
pure CRSentConfirmation
|
||||||
Connect (Just (ACR SCMContact cReq)) -> procCmd $ do
|
Connect (Just (ACR SCMContact cReq)) -> withChatLock . procCmd $ do
|
||||||
connect cReq $ XContact profile Nothing
|
connect cReq $ XContact profile Nothing
|
||||||
pure CRSentInvitation
|
pure CRSentInvitation
|
||||||
Connect Nothing -> throwChatError CEInvalidConnReq
|
Connect Nothing -> throwChatError CEInvalidConnReq
|
||||||
ConnectAdmin -> procCmd $ do
|
ConnectAdmin -> withChatLock . procCmd $ do
|
||||||
connect adminContactReq $ XContact profile Nothing
|
connect adminContactReq $ XContact profile Nothing
|
||||||
pure CRSentInvitation
|
pure CRSentInvitation
|
||||||
DeleteContact cName -> do
|
DeleteContact cName -> do
|
||||||
contactId <- withStore $ \st -> getContactIdByName st userId cName
|
contactId <- withStore $ \st -> getContactIdByName st userId cName
|
||||||
processChatCommand user $ APIDeleteChat CTDirect contactId
|
processChatCommand user $ APIDeleteChat CTDirect contactId
|
||||||
ListContacts -> CRContactsList <$> withStore (`getUserContacts` user)
|
ListContacts -> CRContactsList <$> withStore (`getUserContacts` user)
|
||||||
CreateMyAddress -> procCmd $ do
|
CreateMyAddress -> withChatLock . procCmd $ do
|
||||||
(connId, cReq) <- withAgent (`createConnection` SCMContact)
|
(connId, cReq) <- withAgent (`createConnection` SCMContact)
|
||||||
withStore $ \st -> createUserContactLink st userId connId cReq
|
withStore $ \st -> createUserContactLink st userId connId cReq
|
||||||
pure $ CRUserContactLinkCreated cReq
|
pure $ CRUserContactLinkCreated cReq
|
||||||
DeleteMyAddress -> do
|
DeleteMyAddress -> withChatLock $ do
|
||||||
conns <- withStore $ \st -> getUserContactLinkConnections st userId
|
conns <- withStore $ \st -> getUserContactLinkConnections st userId
|
||||||
procCmd $ do
|
procCmd $ do
|
||||||
withAgent $ \a -> forM_ conns $ \conn ->
|
withAgent $ \a -> forM_ conns $ \conn ->
|
||||||
@ -216,7 +216,7 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
NewGroup gProfile -> do
|
NewGroup gProfile -> do
|
||||||
gVar <- asks idsDrg
|
gVar <- asks idsDrg
|
||||||
CRGroupCreated <$> withStore (\st -> createNewGroup st gVar user gProfile)
|
CRGroupCreated <$> withStore (\st -> createNewGroup st gVar user gProfile)
|
||||||
AddMember gName cName memRole -> do
|
AddMember gName cName memRole -> withChatLock $ do
|
||||||
-- TODO for large groups: no need to load all members to determine if contact is a member
|
-- TODO for large groups: no need to load all members to determine if contact is a member
|
||||||
(group, contact) <- withStore $ \st -> (,) <$> getGroupByName st user gName <*> getContactByName st userId cName
|
(group, contact) <- withStore $ \st -> (,) <$> getGroupByName st user gName <*> getContactByName st userId cName
|
||||||
let Group gInfo@GroupInfo {groupId, groupProfile, membership} members = group
|
let Group gInfo@GroupInfo {groupId, groupProfile, membership} members = group
|
||||||
@ -243,7 +243,7 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
| otherwise -> throwChatError $ CEGroupDuplicateMember cName
|
| otherwise -> throwChatError $ CEGroupDuplicateMember cName
|
||||||
JoinGroup gName -> do
|
JoinGroup gName -> do
|
||||||
ReceivedGroupInvitation {fromMember, connRequest, groupInfo = g} <- withStore $ \st -> getGroupInvitation st user gName
|
ReceivedGroupInvitation {fromMember, connRequest, groupInfo = g} <- withStore $ \st -> getGroupInvitation st user gName
|
||||||
procCmd $ do
|
withChatLock . procCmd $ do
|
||||||
agentConnId <- withAgent $ \a -> joinConnection a connRequest . directMessage . XGrpAcpt $ memberId (membership g :: GroupMember)
|
agentConnId <- withAgent $ \a -> joinConnection a connRequest . directMessage . XGrpAcpt $ memberId (membership g :: GroupMember)
|
||||||
withStore $ \st -> do
|
withStore $ \st -> do
|
||||||
createMemberConnection st userId fromMember agentConnId
|
createMemberConnection st userId fromMember agentConnId
|
||||||
@ -258,14 +258,14 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
Just m@GroupMember {memberId = mId, memberRole = mRole, memberStatus = mStatus} -> do
|
Just m@GroupMember {memberId = mId, memberRole = mRole, memberStatus = mStatus} -> do
|
||||||
let userRole = memberRole (membership :: GroupMember)
|
let userRole = memberRole (membership :: GroupMember)
|
||||||
when (userRole < GRAdmin || userRole < mRole) $ throwChatError CEGroupUserRole
|
when (userRole < GRAdmin || userRole < mRole) $ throwChatError CEGroupUserRole
|
||||||
procCmd $ do
|
withChatLock . procCmd $ do
|
||||||
when (mStatus /= GSMemInvited) . void . sendGroupMessage members $ XGrpMemDel mId
|
when (mStatus /= GSMemInvited) . void . sendGroupMessage members $ XGrpMemDel mId
|
||||||
deleteMemberConnection m
|
deleteMemberConnection m
|
||||||
withStore $ \st -> updateGroupMemberStatus st userId m GSMemRemoved
|
withStore $ \st -> updateGroupMemberStatus st userId m GSMemRemoved
|
||||||
pure $ CRUserDeletedMember gInfo m
|
pure $ CRUserDeletedMember gInfo m
|
||||||
LeaveGroup gName -> do
|
LeaveGroup gName -> do
|
||||||
Group gInfo@GroupInfo {membership} members <- withStore $ \st -> getGroupByName st user gName
|
Group gInfo@GroupInfo {membership} members <- withStore $ \st -> getGroupByName st user gName
|
||||||
procCmd $ do
|
withChatLock . procCmd $ do
|
||||||
void $ sendGroupMessage members XGrpLeave
|
void $ sendGroupMessage members XGrpLeave
|
||||||
mapM_ deleteMemberConnection members
|
mapM_ deleteMemberConnection members
|
||||||
withStore $ \st -> updateGroupMemberStatus st userId membership GSMemLeft
|
withStore $ \st -> updateGroupMemberStatus st userId membership GSMemLeft
|
||||||
@ -277,7 +277,7 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
memberRole (membership :: GroupMember) == GROwner
|
memberRole (membership :: GroupMember) == GROwner
|
||||||
|| (s == GSMemRemoved || s == GSMemLeft || s == GSMemGroupDeleted || s == GSMemInvited)
|
|| (s == GSMemRemoved || s == GSMemLeft || s == GSMemGroupDeleted || s == GSMemInvited)
|
||||||
unless canDelete $ throwChatError CEGroupUserRole
|
unless canDelete $ throwChatError CEGroupUserRole
|
||||||
procCmd $ do
|
withChatLock . procCmd $ do
|
||||||
when (memberActive membership) . void $ sendGroupMessage members XGrpDel
|
when (memberActive membership) . void $ sendGroupMessage members XGrpDel
|
||||||
mapM_ deleteMemberConnection members
|
mapM_ deleteMemberConnection members
|
||||||
withStore $ \st -> deleteGroup st user g
|
withStore $ \st -> deleteGroup st user g
|
||||||
@ -288,7 +288,7 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
groupId <- withStore $ \st -> getGroupIdByName st user gName
|
groupId <- withStore $ \st -> getGroupIdByName st user gName
|
||||||
let mc = MCText $ safeDecodeUtf8 msg
|
let mc = MCText $ safeDecodeUtf8 msg
|
||||||
processChatCommand user $ APISendMessage CTGroup groupId mc
|
processChatCommand user $ APISendMessage CTGroup groupId mc
|
||||||
SendFile cName f -> do
|
SendFile cName f -> withChatLock $ do
|
||||||
(fileSize, chSize) <- checkSndFile f
|
(fileSize, chSize) <- checkSndFile f
|
||||||
contact <- withStore $ \st -> getContactByName st userId cName
|
contact <- withStore $ \st -> getContactByName st userId cName
|
||||||
(agentConnId, fileConnReq) <- withAgent (`createConnection` SCMInvitation)
|
(agentConnId, fileConnReq) <- withAgent (`createConnection` SCMInvitation)
|
||||||
@ -299,7 +299,7 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
withStore $ \st -> updateFileTransferChatItemId st fileId $ chatItemId ci
|
withStore $ \st -> updateFileTransferChatItemId st fileId $ chatItemId ci
|
||||||
setActive $ ActiveC cName
|
setActive $ ActiveC cName
|
||||||
pure . CRNewChatItem $ AChatItem SCTDirect SMDSnd (DirectChat contact) ci
|
pure . CRNewChatItem $ AChatItem SCTDirect SMDSnd (DirectChat contact) ci
|
||||||
SendGroupFile gName f -> do
|
SendGroupFile gName f -> withChatLock $ do
|
||||||
(fileSize, chSize) <- checkSndFile f
|
(fileSize, chSize) <- checkSndFile f
|
||||||
Group gInfo@GroupInfo {membership} members <- withStore $ \st -> getGroupByName st user gName
|
Group gInfo@GroupInfo {membership} members <- withStore $ \st -> getGroupByName st user gName
|
||||||
unless (memberActive membership) $ throwChatError CEGroupMemberUserRemoved
|
unless (memberActive membership) $ throwChatError CEGroupMemberUserRemoved
|
||||||
@ -322,7 +322,7 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
ReceiveFile fileId filePath_ -> do
|
ReceiveFile fileId filePath_ -> do
|
||||||
ft@RcvFileTransfer {fileInvitation = FileInvitation {fileName, fileConnReq}, fileStatus} <- withStore $ \st -> getRcvFileTransfer st userId fileId
|
ft@RcvFileTransfer {fileInvitation = FileInvitation {fileName, fileConnReq}, fileStatus} <- withStore $ \st -> getRcvFileTransfer st userId fileId
|
||||||
unless (fileStatus == RFSNew) . throwChatError $ CEFileAlreadyReceiving fileName
|
unless (fileStatus == RFSNew) . throwChatError $ CEFileAlreadyReceiving fileName
|
||||||
procCmd $ do
|
withChatLock . procCmd $ do
|
||||||
tryError (withAgent $ \a -> joinConnection a fileConnReq . directMessage $ XFileAcpt fileName) >>= \case
|
tryError (withAgent $ \a -> joinConnection a fileConnReq . directMessage $ XFileAcpt fileName) >>= \case
|
||||||
Right agentConnId -> do
|
Right agentConnId -> do
|
||||||
filePath <- getRcvFilePath fileId filePath_ fileName
|
filePath <- getRcvFilePath fileId filePath_ fileName
|
||||||
@ -333,7 +333,7 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
Left e -> throwError e
|
Left e -> throwError e
|
||||||
CancelFile fileId -> do
|
CancelFile fileId -> do
|
||||||
ft' <- withStore (\st -> getFileTransfer st userId fileId)
|
ft' <- withStore (\st -> getFileTransfer st userId fileId)
|
||||||
procCmd $ case ft' of
|
withChatLock . procCmd $ case ft' of
|
||||||
FTSnd fts -> do
|
FTSnd fts -> do
|
||||||
forM_ fts $ \ft -> cancelSndFileTransfer ft
|
forM_ fts $ \ft -> cancelSndFileTransfer ft
|
||||||
pure $ CRSndGroupFileCancelled fts
|
pure $ CRSndGroupFileCancelled fts
|
||||||
@ -350,12 +350,15 @@ processChatCommand user@User {userId, profile} = \case
|
|||||||
let user' = (user :: User) {localDisplayName = displayName, profile = p}
|
let user' = (user :: User) {localDisplayName = displayName, profile = p}
|
||||||
asks currentUser >>= atomically . (`writeTVar` user')
|
asks currentUser >>= atomically . (`writeTVar` user')
|
||||||
contacts <- withStore (`getUserContacts` user)
|
contacts <- withStore (`getUserContacts` user)
|
||||||
procCmd $ do
|
withChatLock . procCmd $ do
|
||||||
forM_ contacts $ \ct -> sendDirectMessage (contactConn ct) $ XInfo p
|
forM_ contacts $ \ct -> sendDirectMessage (contactConn ct) $ XInfo p
|
||||||
pure $ CRUserProfileUpdated profile p
|
pure $ CRUserProfileUpdated profile p
|
||||||
QuitChat -> liftIO exitSuccess
|
QuitChat -> liftIO exitSuccess
|
||||||
ShowVersion -> pure CRVersionInfo
|
ShowVersion -> pure CRVersionInfo
|
||||||
where
|
where
|
||||||
|
withChatLock action = do
|
||||||
|
ChatController {chatLock = l, smpAgent = a} <- ask
|
||||||
|
withAgentLock a . withLock l $ action
|
||||||
-- below code would make command responses asynchronous where they can be slow
|
-- below code would make command responses asynchronous where they can be slow
|
||||||
-- in View.hs `r'` should be defined as `id` in this case
|
-- in View.hs `r'` should be defined as `id` in this case
|
||||||
-- procCmd :: m ChatResponse -> m ChatResponse
|
-- procCmd :: m ChatResponse -> m ChatResponse
|
||||||
|
Loading…
Reference in New Issue
Block a user