diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 035b1701e5..809e3d9cde 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -245,7 +245,7 @@ processChatCommand = \case when (memberStatus membership == GSMemInvited) $ throwChatError (CEGroupNotJoined gInfo) unless (memberActive membership) $ throwChatError CEGroupMemberNotActive let sendInvitation memberId cReq = do - void . sendDirectMessage (contactConn contact) $ + void . sendDirectContactMessage contact $ XGrpInv $ GroupInvitation (MemberIdRole userMemberId userRole) (MemberIdRole memberId memRole) cReq groupProfile setActive $ ActiveG gName pure $ CRSentGroupInvitation gInfo contact @@ -372,7 +372,7 @@ processChatCommand = \case asks currentUser >>= atomically . (`writeTVar` Just user') contacts <- withStore (`getUserContacts` user) withChatLock . procCmd $ do - forM_ contacts $ \ct -> sendDirectMessage (contactConn ct) $ XInfo p + forM_ contacts $ \ct -> sendDirectContactMessage ct $ XInfo p pure $ CRUserProfileUpdated profile p QuitChat -> liftIO exitSuccess ShowVersion -> pure CRVersionInfo @@ -548,12 +548,6 @@ processAgentMessage (Just user@User {userId, profile}) agentConnId agentMessage isMember memId GroupInfo {membership} members = sameMemberId memId membership || isJust (find (sameMemberId memId) members) - contactIsReady :: Contact -> Bool - contactIsReady Contact {activeConn} = connStatus activeConn == ConnReady - - memberIsReady :: GroupMember -> Bool - memberIsReady GroupMember {activeConn} = maybe False ((== ConnReady) . connStatus) activeConn - agentMsgConnStatus :: ACommand 'Agent -> Maybe ConnStatus agentMsgConnStatus = \case CONF {} -> Just ConnRequested @@ -622,8 +616,8 @@ processAgentMessage (Just user@User {userId, profile}) agentConnId agentMessage toView $ CRContactConnected ct setActive $ ActiveC c showToast (c <> "> ") "connected" - Just (gInfo, m) -> do - when (memberIsReady m) $ do + Just (gInfo, m@GroupMember {activeConn}) -> do + when (maybe False ((== ConnReady) . connStatus) activeConn) $ do notifyMemberConnected gInfo m when (memberCategory m == GCPreMember) $ probeMatchingContacts ct SENT msgId -> do @@ -717,8 +711,8 @@ processAgentMessage (Just user@User {userId, profile}) agentConnId agentMessage Nothing -> do notifyMemberConnected gInfo m messageError "implementation error: connected member does not have contact" - Just ct -> - when (contactIsReady ct) $ do + Just ct@Contact {activeConn = Connection {connStatus}} -> + when (connStatus == ConnReady) $ do notifyMemberConnected gInfo m when (memberCategory m == GCPreMember) $ probeMatchingContacts ct MSG msgMeta msgBody -> do @@ -879,14 +873,14 @@ processAgentMessage (Just user@User {userId, profile}) agentConnId agentMessage probeMatchingContacts ct = do gVar <- asks idsDrg (probe, probeId) <- withStore $ \st -> createSentProbe st gVar userId ct - void . sendDirectMessage (contactConn ct) $ XInfoProbe probe + void . sendDirectContactMessage ct $ XInfoProbe probe cs <- withStore (\st -> getMatchingContacts st userId ct) let probeHash = ProbeHash $ C.sha256Hash (unProbe probe) forM_ cs $ \c -> sendProbeHash c probeHash probeId `catchError` const (pure ()) where sendProbeHash :: Contact -> ProbeHash -> Int64 -> m () sendProbeHash c probeHash probeId = do - void . sendDirectMessage (contactConn c) $ XInfoProbeCheck probeHash + void . sendDirectContactMessage c $ XInfoProbeCheck probeHash withStore $ \st -> createSentProbeHash st userId probeId c messageWarning :: Text -> m () @@ -967,7 +961,7 @@ processAgentMessage (Just user@User {userId, profile}) agentConnId agentMessage probeMatch :: Contact -> Contact -> Probe -> m () probeMatch c1@Contact {profile = p1} c2@Contact {profile = p2} probe = when (p1 == p2) $ do - void . sendDirectMessage (contactConn c1) $ XInfoProbeOk probe + void . sendDirectContactMessage c1 $ XInfoProbeOk probe mergeContacts c1 c2 xInfoProbeOk :: Contact -> Probe -> m () @@ -1201,6 +1195,12 @@ deleteMemberConnection m@GroupMember {activeConn} = do -- withStore $ \st -> deleteGroupMemberConnection st userId m forM_ activeConn $ \conn -> withStore $ \st -> updateConnectionStatus st conn ConnDeleted +sendDirectContactMessage :: ChatMonad m => Contact -> ChatMsgEvent -> m MessageId +sendDirectContactMessage ct@Contact {activeConn = conn@Connection {connStatus}} chatMsgEvent = do + if connStatus == ConnReady || connStatus == ConnSndReady + then sendDirectMessage conn chatMsgEvent + else throwChatError $ CEContactNotReady ct + sendDirectMessage :: ChatMonad m => Connection -> ChatMsgEvent -> m MessageId sendDirectMessage conn chatMsgEvent = do (msgId, msgBody) <- createSndMessage chatMsgEvent @@ -1267,8 +1267,8 @@ saveRcvMSG Connection {connId} agentMsgMeta msgBody = do pure (msgId, chatMsgEvent) sendDirectChatItem :: ChatMonad m => UserId -> Contact -> ChatMsgEvent -> CIContent 'MDSnd -> m (ChatItem 'CTDirect 'MDSnd) -sendDirectChatItem userId contact@Contact {activeConn} chatMsgEvent ciContent = do - msgId <- sendDirectMessage activeConn chatMsgEvent +sendDirectChatItem userId contact chatMsgEvent ciContent = do + msgId <- sendDirectContactMessage contact chatMsgEvent createdAt <- liftIO getCurrentTime ciMeta <- saveChatItem userId (CDDirectSnd contact) $ mkNewChatItem ciContent msgId createdAt createdAt pure $ ChatItem CIDirectSnd ciMeta ciContent diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 9d3175f1d9..1dd95dbc86 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -229,6 +229,7 @@ data ChatErrorType | CEChatNotStarted | CEInvalidConnReq | CEInvalidChatMessage {message :: String} + | CEContactNotReady {contact :: Contact} | CEContactGroups {contact :: Contact, groupNames :: [GroupName]} | CEGroupUserRole | CEGroupContactRole {contactName :: ContactName} diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 16f1257538..757b639407 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -2453,9 +2453,18 @@ getContact_ db userId contactId = FROM contacts ct JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id LEFT JOIN connections c ON c.contact_id = ct.contact_id - WHERE ct.user_id = ? AND ct.contact_id = ? AND (c.conn_status = ? OR c.conn_status = ?) - ORDER BY c.connection_id DESC - LIMIT 1 + WHERE ct.user_id = ? AND ct.contact_id = ? + AND c.connection_id = ( + SELECT cc_connection_id FROM ( + SELECT + cc.connection_id AS cc_connection_id, + (CASE WHEN cc.conn_status = ? OR cc.conn_status = ? THEN 1 ELSE 0 END) AS cc_conn_status_ord + FROM connections cc + WHERE cc.user_id = ct.user_id AND cc.contact_id = ct.contact_id + ORDER BY cc_conn_status_ord DESC, cc_connection_id DESC + LIMIT 1 + ) + ) |] (userId, contactId, ConnReady, ConnSndReady) ) diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 01e697dc6c..eb4458570a 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -474,7 +474,8 @@ viewChatError = \case CEChatNotStarted -> ["error: chat not started"] CEInvalidConnReq -> viewInvalidConnReq CEInvalidChatMessage e -> ["chat message error: " <> sShow e] - CEContactGroups Contact {localDisplayName} gNames -> [ttyContact localDisplayName <> ": contact cannot be deleted, it is a member of the group(s) " <> ttyGroups gNames] + CEContactNotReady c -> [ttyContact' c <> ": not ready"] + CEContactGroups c gNames -> [ttyContact' c <> ": contact cannot be deleted, it is a member of the group(s) " <> ttyGroups gNames] CEGroupDuplicateMember c -> ["contact " <> ttyContact c <> " is already in the group"] CEGroupDuplicateMemberId -> ["cannot add member - duplicate member ID"] CEGroupUserRole -> ["you have insufficient permissions for this group command"]