diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 5ec22602fd..329285a6ec 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -1069,7 +1069,7 @@ processChatCommand = \case callState = CallInvitationSent {localCallType = callType, localDhPrivKey = snd <$> dhKeyPair} (msg, _) <- sendDirectContactMessage ct (XCallInv callId invitation) ci <- saveSndChatItem user (CDDirectSnd ct) msg (CISndCall CISCallPending 0) - let call' = Call {contactId, callId, chatItemId = chatItemId' ci, callState, callTs = chatItemTs' ci} + let call' = Call {remoteHostId = Nothing, contactId, callId, chatItemId = chatItemId' ci, callState, callTs = chatItemTs' ci} call_ <- atomically $ TM.lookupInsert contactId call' calls forM_ call_ $ \call -> updateCallItemStatus user ct call WCSDisconnected Nothing toView $ CRNewChatItem user (AChatItem SCTDirect SMDSnd (DirectChat ct) ci) @@ -1141,7 +1141,7 @@ processChatCommand = \case rcvCallInvitation (contactId, callTs, peerCallType, sharedKey) = runExceptT . withStore $ \db -> do user <- getUserByContactId db contactId contact <- getContact db user contactId - pure RcvCallInvitation {user, contact, callType = peerCallType, sharedKey, callTs} + pure RcvCallInvitation {remoteHostId = Nothing, user, contact, callType = peerCallType, sharedKey, callTs} APIGetNetworkStatuses -> withUser $ \_ -> CRNetworkStatuses Nothing . map (uncurry ConnNetworkStatus) . M.toList <$> chatReadVar connNetworkStatuses APICallStatus contactId receivedStatus -> @@ -1186,7 +1186,7 @@ processChatCommand = \case servers' = fromMaybe (L.map toServerCfg defServers) $ nonEmpty servers pure $ CRUserProtoServers user $ AUPS $ UserProtoServers p servers' defServers where - toServerCfg server = ServerCfg {server, preset = True, tested = Nothing, enabled = True} + toServerCfg server = ServerCfg {remoteHostId = Nothing, server, preset = True, tested = Nothing, enabled = True} GetUserProtoServers aProtocol -> withUser $ \User {userId} -> processChatCommand $ APIGetUserProtoServers userId aProtocol APISetUserProtoServers userId (APSC p (ProtoServersConfig servers)) -> withUserId userId $ \user -> withServerProtocol p $ @@ -4766,7 +4766,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do ci <- saveCallItem CISCallPending let sharedKey = C.Key . C.dhBytes' <$> (C.dh' <$> callDhPubKey <*> (snd <$> dhKeyPair)) callState = CallInvitationReceived {peerCallType = callType, localDhPubKey = fst <$> dhKeyPair, sharedKey} - call' = Call {contactId, callId, chatItemId = chatItemId' ci, callState, callTs = chatItemTs' ci} + call' = Call {remoteHostId = Nothing, contactId, callId, chatItemId = chatItemId' ci, callState, callTs = chatItemTs' ci} calls <- asks currentCalls -- theoretically, the new call invitation for the current contact can mark the in-progress call as ended -- (and replace it in ChatController) @@ -4774,7 +4774,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do withStore' $ \db -> createCall db user call' $ chatItemTs' ci call_ <- atomically (TM.lookupInsert contactId call' calls) forM_ call_ $ \call -> updateCallItemStatus user ct call WCSDisconnected Nothing - toView $ CRCallInvitation RcvCallInvitation {user, contact = ct, callType, sharedKey, callTs = chatItemTs' ci} + toView $ CRCallInvitation RcvCallInvitation {remoteHostId = Nothing, user, contact = ct, callType, sharedKey, callTs = chatItemTs' ci} toView $ CRNewChatItem user $ AChatItem SCTDirect SMDRcv (DirectChat ct) ci else featureRejected CFCalls where @@ -6314,7 +6314,7 @@ chatCommandP = (Just <$> (AutoAccept <$> (" incognito=" *> onOffP <|> pure False) <*> optional (A.space *> msgContentP))) (pure Nothing) srvCfgP = strP >>= \case AProtocolType p -> APSC p <$> (A.space *> jsonP) - toServerCfg server = ServerCfg {server, preset = False, tested = Nothing, enabled = True} + toServerCfg server = ServerCfg {remoteHostId = Nothing, server, preset = False, tested = Nothing, enabled = True} char_ = optional . A.char adminContactReq :: ConnReqContact diff --git a/src/Simplex/Chat/Call.hs b/src/Simplex/Chat/Call.hs index 313442838e..39481db4af 100644 --- a/src/Simplex/Chat/Call.hs +++ b/src/Simplex/Chat/Call.hs @@ -20,14 +20,15 @@ import Data.Text (Text) import Data.Time.Clock (UTCTime) import Database.SQLite.Simple.FromField (FromField (..)) import Database.SQLite.Simple.ToField (ToField (..)) -import Simplex.Chat.Types (Contact, ContactId, User) +import Simplex.Chat.Types (Contact, ContactId, User, RemoteHostId) import Simplex.Chat.Types.Util (decodeJSON, encodeJSON) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Encoding.String import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, fromTextField_, fstToLower, singleFieldJSON) data Call = Call - { contactId :: ContactId, + { remoteHostId :: Maybe RemoteHostId, + contactId :: ContactId, callId :: CallId, chatItemId :: Int64, callState :: CallState, @@ -107,7 +108,8 @@ instance FromField CallId where fromField f = CallId <$> fromField f instance ToField CallId where toField (CallId m) = toField m data RcvCallInvitation = RcvCallInvitation - { user :: User, + { remoteHostId :: Maybe RemoteHostId, + user :: User, contact :: Contact, callType :: CallType, sharedKey :: Maybe C.Key, diff --git a/src/Simplex/Chat/Messages.hs b/src/Simplex/Chat/Messages.hs index 9e4c309910..88ad946058 100644 --- a/src/Simplex/Chat/Messages.hs +++ b/src/Simplex/Chat/Messages.hs @@ -266,7 +266,8 @@ data NewChatItem d = NewChatItem -- | type to show one chat with messages data Chat c = Chat - { chatInfo :: ChatInfo c, + { remoteHostId :: Maybe RemoteHostId, + chatInfo :: ChatInfo c, chatItems :: [CChatItem c], chatStats :: ChatStats } diff --git a/src/Simplex/Chat/Remote.hs b/src/Simplex/Chat/Remote.hs index d9ef5bd648..da1b0f9d78 100644 --- a/src/Simplex/Chat/Remote.hs +++ b/src/Simplex/Chat/Remote.hs @@ -225,7 +225,7 @@ startRemoteHost rh_ = do pollEvents rhId rhClient = do oq <- asks outputQ forever $ do - r_ <- liftRH rhId $ remoteRecv rhClient 10000000 + r_ <- liftRH rhId $ remoteRecv rhId rhClient 10000000 forM r_ $ \r -> atomically $ writeTBQueue oq (Nothing, Just rhId, r) httpError :: RemoteHostId -> HTTP2ClientError -> ChatError httpError rhId = ChatErrorRemoteHost (RHId rhId) . RHEProtocolError . RPEHTTP2 . tshow @@ -359,13 +359,13 @@ processRemoteCommand :: ChatMonad m => RemoteHostId -> RemoteHostClient -> ChatC processRemoteCommand remoteHostId c cmd s = case cmd of SendFile chatName f -> sendFile "/f" chatName f SendImage chatName f -> sendFile "/img" chatName f - _ -> liftRH remoteHostId $ remoteSend c s + _ -> liftRH remoteHostId $ remoteSend remoteHostId c s where sendFile cmdName chatName (CryptoFile path cfArgs) = do -- don't encrypt in host if already encrypted locally CryptoFile path' cfArgs' <- storeRemoteFile remoteHostId (cfArgs $> False) path let f = CryptoFile path' (cfArgs <|> cfArgs') -- use local or host encryption - liftRH remoteHostId $ remoteSend c $ B.unwords [cmdName, B.pack (chatNameStr chatName), cryptoFileStr f] + liftRH remoteHostId $ remoteSend remoteHostId c $ B.unwords [cmdName, B.pack (chatNameStr chatName), cryptoFileStr f] cryptoFileStr CryptoFile {filePath, cryptoArgs} = maybe "" (\(CFArgs key nonce) -> "key=" <> strEncode key <> " nonce=" <> strEncode nonce <> " ") cryptoArgs <> encodeUtf8 (T.pack filePath) diff --git a/src/Simplex/Chat/Remote/Protocol.hs b/src/Simplex/Chat/Remote/Protocol.hs index c1acee1e0f..e70041e9c7 100644 --- a/src/Simplex/Chat/Remote/Protocol.hs +++ b/src/Simplex/Chat/Remote/Protocol.hs @@ -8,6 +8,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TupleSections #-} +{-# OPTIONS_GHC -Wno-ambiguous-fields #-} module Simplex.Chat.Remote.Protocol where @@ -34,9 +35,12 @@ import Data.Word (Word32) import qualified Network.HTTP.Types as N import qualified Network.HTTP2.Client as H import Network.Transport.Internal (decodeWord32, encodeWord32) +import Simplex.Chat.Call (RcvCallInvitation (..)) import Simplex.Chat.Controller +import Simplex.Chat.Messages (AChat (..), Chat (..)) import Simplex.Chat.Remote.Transport import Simplex.Chat.Remote.Types +import Simplex.Chat.Types (RemoteHostId, User (..), UserInfo (..), ServerCfg (..)) import Simplex.FileTransfer.Description (FileDigest (..)) import Simplex.Messaging.Agent.Client (agentDRG) import qualified Simplex.Messaging.Crypto as C @@ -105,18 +109,232 @@ closeRemoteHostClient RemoteHostClient {httpClient} = liftIO $ closeHTTP2Client -- ** Commands -remoteSend :: RemoteHostClient -> ByteString -> ExceptT RemoteProtocolError IO ChatResponse -remoteSend c cmd = +remoteSend :: RemoteHostId -> RemoteHostClient -> ByteString -> ExceptT RemoteProtocolError IO ChatResponse +remoteSend rhId c cmd = sendRemoteCommand' c Nothing RCSend {command = decodeUtf8 cmd} >>= \case - RRChatResponse cr -> pure cr + RRChatResponse cr -> pure $ enrichCRRemoteHostId cr rhId r -> badResponse r -remoteRecv :: RemoteHostClient -> Int -> ExceptT RemoteProtocolError IO (Maybe ChatResponse) -remoteRecv c ms = +remoteRecv :: RemoteHostId -> RemoteHostClient -> Int -> ExceptT RemoteProtocolError IO (Maybe ChatResponse) +remoteRecv rhId c ms = sendRemoteCommand' c Nothing RCRecv {wait = ms} >>= \case - RRChatEvent cr_ -> pure cr_ + RRChatEvent cr_ -> pure $ (`enrichCRRemoteHostId` rhId) <$> cr_ r -> badResponse r +enrichCRRemoteHostId :: ChatResponse -> RemoteHostId -> ChatResponse +enrichCRRemoteHostId cr rhId = case cr of + CRActiveUser {user} -> CRActiveUser {user = enrichUser user} + CRUsersList {users} -> CRUsersList {users = map enrichUserInfo users} + CRChatStarted -> cr + CRChatRunning -> cr + CRChatStopped -> cr + CRChatSuspended -> cr + CRApiChats {user, chats} -> CRApiChats {user = enrichUser user, chats = map enrichAChat chats} + CRChats {chats} -> CRChats {chats = map enrichAChat chats} + CRApiChat {user, chat} -> CRApiChat {user = enrichUser user, chat = enrichAChat chat} + CRChatItems {user, chatName_, chatItems} -> CRChatItems {user = enrichUser user, chatName_, chatItems} + CRChatItemInfo {user, chatItem, chatItemInfo} -> CRChatItemInfo {user = enrichUser user, chatItem, chatItemInfo} + CRChatItemId user chatItemId_ -> CRChatItemId (enrichUser user) chatItemId_ + CRApiParsedMarkdown {formattedText = _ft} -> cr + CRUserProtoServers {user, servers} -> CRUserProtoServers {user = enrichUser user, servers = enrichAUserProtoServers servers} + CRServerTestResult {user, testServer, testFailure} -> CRServerTestResult {user = enrichUser user, testServer, testFailure} + CRChatItemTTL {user, chatItemTTL} -> CRChatItemTTL {user = enrichUser user, chatItemTTL} + CRNetworkConfig {networkConfig = _nc} -> cr + CRContactInfo {user, contact, connectionStats_, customUserProfile} -> CRContactInfo {user = enrichUser user, contact, connectionStats_, customUserProfile} + CRGroupInfo {user, groupInfo, groupSummary} -> CRGroupInfo {user = enrichUser user, groupInfo, groupSummary} + CRGroupMemberInfo {user, groupInfo, member, connectionStats_} -> CRGroupMemberInfo {user = enrichUser user, groupInfo, member, connectionStats_} + CRContactSwitchStarted {user, contact, connectionStats} -> CRContactSwitchStarted {user = enrichUser user, contact, connectionStats} + CRGroupMemberSwitchStarted {user, groupInfo, member, connectionStats} -> CRGroupMemberSwitchStarted {user = enrichUser user, groupInfo, member, connectionStats} + CRContactSwitchAborted {user, contact, connectionStats} -> CRContactSwitchAborted {user = enrichUser user, contact, connectionStats} + CRGroupMemberSwitchAborted {user, groupInfo, member, connectionStats} -> CRGroupMemberSwitchAborted {user = enrichUser user, groupInfo, member, connectionStats} + CRContactSwitch {user, contact, switchProgress} -> CRContactSwitch {user = enrichUser user, contact, switchProgress} + CRGroupMemberSwitch {user, groupInfo, member, switchProgress} -> CRGroupMemberSwitch {user = enrichUser user, groupInfo, member, switchProgress} + CRContactRatchetSyncStarted {user, contact, connectionStats} -> CRContactRatchetSyncStarted {user = enrichUser user, contact, connectionStats} + CRGroupMemberRatchetSyncStarted {user, groupInfo, member, connectionStats} -> CRGroupMemberRatchetSyncStarted {user = enrichUser user, groupInfo, member, connectionStats} + CRContactRatchetSync {user, contact, ratchetSyncProgress} -> CRContactRatchetSync {user = enrichUser user, contact, ratchetSyncProgress} + CRGroupMemberRatchetSync {user, groupInfo, member, ratchetSyncProgress} -> CRGroupMemberRatchetSync {user = enrichUser user, groupInfo, member, ratchetSyncProgress} + CRContactVerificationReset {user, contact} -> CRContactVerificationReset {user = enrichUser user, contact} + CRGroupMemberVerificationReset {user, groupInfo, member} -> CRGroupMemberVerificationReset {user = enrichUser user, groupInfo, member} + CRContactCode {user, contact, connectionCode} -> CRContactCode {user = enrichUser user, contact, connectionCode} + CRGroupMemberCode {user, groupInfo, member, connectionCode} -> CRGroupMemberCode {user = enrichUser user, groupInfo, member, connectionCode} + CRConnectionVerified {user, verified, expectedCode} -> CRConnectionVerified {user = enrichUser user, verified, expectedCode} + CRNewChatItem {user, chatItem} -> CRNewChatItem {user = enrichUser user, chatItem} + CRChatItemStatusUpdated {user, chatItem} -> CRChatItemStatusUpdated {user = enrichUser user, chatItem} + CRChatItemUpdated {user, chatItem} -> CRChatItemUpdated {user = enrichUser user, chatItem} + CRChatItemNotChanged {user, chatItem} -> CRChatItemNotChanged {user = enrichUser user, chatItem} + CRChatItemReaction {user, added, reaction} -> CRChatItemReaction {user = enrichUser user, added, reaction} + CRChatItemDeleted {user, deletedChatItem, toChatItem, byUser, timed} -> CRChatItemDeleted {user = enrichUser user, deletedChatItem, toChatItem, byUser, timed} + CRChatItemDeletedNotFound {user, contact, sharedMsgId} -> CRChatItemDeletedNotFound {user = enrichUser user, contact, sharedMsgId} + CRBroadcastSent {user, msgContent, successes, failures, timestamp} -> CRBroadcastSent {user = enrichUser user, msgContent, successes, failures, timestamp} + CRMsgIntegrityError {user, msgError} -> CRMsgIntegrityError {user = enrichUser user, msgError} + CRCmdAccepted {corr = _corr} -> cr + CRCmdOk {user_} -> CRCmdOk {user_ = enrichUser <$> user_} + CRChatHelp {helpSection = _hs} -> cr + CRWelcome {user} -> CRWelcome {user = enrichUser user} + CRGroupCreated {user, groupInfo} -> CRGroupCreated {user = enrichUser user, groupInfo} + CRGroupMembers {user, group} -> CRGroupMembers {user = enrichUser user, group} + CRContactsList {user, contacts} -> CRContactsList {user = enrichUser user, contacts} + CRUserContactLink {user, contactLink} -> CRUserContactLink {user = enrichUser user, contactLink} + CRUserContactLinkUpdated {user, contactLink} -> CRUserContactLinkUpdated {user = enrichUser user, contactLink} + CRContactRequestRejected {user, contactRequest} -> CRContactRequestRejected {user = enrichUser user, contactRequest} + CRUserAcceptedGroupSent {user, groupInfo, hostContact} -> CRUserAcceptedGroupSent {user = enrichUser user, groupInfo, hostContact} + CRGroupLinkConnecting {user, groupInfo, hostMember} -> CRGroupLinkConnecting {user = enrichUser user, groupInfo, hostMember} + CRUserDeletedMember {user, groupInfo, member} -> CRUserDeletedMember {user = enrichUser user, groupInfo, member} + CRGroupsList {user, groups} -> CRGroupsList {user = enrichUser user, groups} + CRSentGroupInvitation {user, groupInfo, contact, member} -> CRSentGroupInvitation {user = enrichUser user, groupInfo, contact, member} + CRFileTransferStatus user ftStatus -> CRFileTransferStatus (enrichUser user) ftStatus + CRFileTransferStatusXFTP user chatItem -> CRFileTransferStatusXFTP (enrichUser user) chatItem + CRUserProfile {user, profile} -> CRUserProfile {user = enrichUser user, profile} + CRUserProfileNoChange {user} -> CRUserProfileNoChange {user = enrichUser user} + CRUserPrivacy {user, updatedUser} -> CRUserPrivacy {user = enrichUser user, updatedUser = enrichUser updatedUser} + CRVersionInfo {versionInfo = _v, chatMigrations = _cm, agentMigrations = _am} -> cr + CRInvitation {user, connReqInvitation, connection} -> CRInvitation {user = enrichUser user, connReqInvitation, connection} + CRConnectionIncognitoUpdated {user, toConnection} -> CRConnectionIncognitoUpdated {user = enrichUser user, toConnection} + CRConnectionPlan {user, connectionPlan} -> CRConnectionPlan {user = enrichUser user, connectionPlan} + CRSentConfirmation {user} -> CRSentConfirmation {user = enrichUser user} + CRSentInvitation {user, customUserProfile} -> CRSentInvitation {user = enrichUser user, customUserProfile} + CRSentInvitationToContact {user, contact, customUserProfile} -> CRSentInvitationToContact {user = enrichUser user, contact, customUserProfile} + CRContactUpdated {user, fromContact, toContact} -> CRContactUpdated {user = enrichUser user, fromContact, toContact} + CRGroupMemberUpdated {user, groupInfo, fromMember, toMember} -> CRGroupMemberUpdated {user = enrichUser user, groupInfo, fromMember, toMember} + CRContactsMerged {user, intoContact, mergedContact, updatedContact} -> CRContactsMerged {user = enrichUser user, intoContact, mergedContact, updatedContact} + CRContactDeleted {user, contact} -> CRContactDeleted {user = enrichUser user, contact} + CRContactDeletedByContact {user, contact} -> CRContactDeletedByContact {user = enrichUser user, contact} + CRChatCleared {user, chatInfo} -> CRChatCleared {user = enrichUser user, chatInfo} + CRUserContactLinkCreated {user, connReqContact} -> CRUserContactLinkCreated {user = enrichUser user, connReqContact} + CRUserContactLinkDeleted {user} -> CRUserContactLinkDeleted {user = enrichUser user} + CRReceivedContactRequest {user, contactRequest} -> CRReceivedContactRequest {user = enrichUser user, contactRequest} + CRAcceptingContactRequest {user, contact} -> CRAcceptingContactRequest {user = enrichUser user, contact} + CRContactAlreadyExists {user, contact} -> CRContactAlreadyExists {user = enrichUser user, contact} + CRContactRequestAlreadyAccepted {user, contact} -> CRContactRequestAlreadyAccepted {user = enrichUser user, contact} + CRLeftMemberUser {user, groupInfo} -> CRLeftMemberUser {user = enrichUser user, groupInfo} + CRGroupDeletedUser {user, groupInfo} -> CRGroupDeletedUser {user = enrichUser user, groupInfo} + CRRcvFileDescrReady {user, chatItem} -> CRRcvFileDescrReady {user = enrichUser user, chatItem} + CRRcvFileAccepted {user, chatItem} -> CRRcvFileAccepted {user = enrichUser user, chatItem} + CRRcvFileAcceptedSndCancelled {user, rcvFileTransfer} -> CRRcvFileAcceptedSndCancelled {user = enrichUser user, rcvFileTransfer} + CRRcvFileDescrNotReady {user, chatItem} -> CRRcvFileDescrNotReady {user = enrichUser user, chatItem} + CRRcvFileStart {user, chatItem} -> CRRcvFileStart {user = enrichUser user, chatItem} + CRRcvFileProgressXFTP {user, chatItem, receivedSize, totalSize} -> CRRcvFileProgressXFTP {user = enrichUser user, chatItem, receivedSize, totalSize} + CRRcvFileComplete {user, chatItem} -> CRRcvFileComplete {user = enrichUser user, chatItem} + CRRcvFileCancelled {user, chatItem, rcvFileTransfer} -> CRRcvFileCancelled {user = enrichUser user, chatItem, rcvFileTransfer} + CRRcvFileSndCancelled {user, chatItem, rcvFileTransfer} -> CRRcvFileSndCancelled {user = enrichUser user, chatItem, rcvFileTransfer} + CRRcvFileError {user, chatItem, agentError} -> CRRcvFileError {user = enrichUser user, chatItem, agentError} + CRSndFileStart {user, chatItem, sndFileTransfer} -> CRSndFileStart {user = enrichUser user, chatItem, sndFileTransfer} + CRSndFileComplete {user, chatItem, sndFileTransfer} -> CRSndFileComplete {user = enrichUser user, chatItem, sndFileTransfer} + CRSndFileRcvCancelled {user, chatItem, sndFileTransfer} -> CRSndFileRcvCancelled {user = enrichUser user, chatItem, sndFileTransfer} + CRSndFileCancelled {user, chatItem, fileTransferMeta, sndFileTransfers} -> CRSndFileCancelled {user = enrichUser user, chatItem, fileTransferMeta, sndFileTransfers} + CRSndFileStartXFTP {user, chatItem, fileTransferMeta} -> CRSndFileStartXFTP {user = enrichUser user, chatItem, fileTransferMeta} + CRSndFileProgressXFTP {user, chatItem, fileTransferMeta, sentSize, totalSize} -> CRSndFileProgressXFTP {user = enrichUser user, chatItem, fileTransferMeta, sentSize, totalSize} + CRSndFileCompleteXFTP {user, chatItem, fileTransferMeta} -> CRSndFileCompleteXFTP {user = enrichUser user, chatItem, fileTransferMeta} + CRSndFileCancelledXFTP {user, chatItem, fileTransferMeta} -> CRSndFileCancelledXFTP {user = enrichUser user, chatItem, fileTransferMeta} + CRSndFileError {user, chatItem} -> CRSndFileError {user = enrichUser user, chatItem} + CRUserProfileUpdated {user, fromProfile, toProfile, updateSummary} -> CRUserProfileUpdated {user = enrichUser user, fromProfile, toProfile, updateSummary} + CRUserProfileImage {user, profile} -> CRUserProfileImage {user = enrichUser user, profile} + CRContactAliasUpdated {user, toContact} -> CRContactAliasUpdated {user = enrichUser user, toContact} + CRConnectionAliasUpdated {user, toConnection} -> CRConnectionAliasUpdated {user = enrichUser user, toConnection} + CRContactPrefsUpdated {user, fromContact, toContact} -> CRContactPrefsUpdated {user = enrichUser user, fromContact, toContact} + CRContactConnecting {user, contact} -> CRContactConnecting {user = enrichUser user, contact} + CRContactConnected {user, contact, userCustomProfile} -> CRContactConnected {user = enrichUser user, contact, userCustomProfile} + CRContactAnotherClient {user, contact} -> CRContactAnotherClient {user = enrichUser user, contact} + CRSubscriptionEnd {user, connectionEntity} -> CRSubscriptionEnd {user = enrichUser user, connectionEntity} + CRContactsDisconnected {server = _srv, contactRefs = _cr} -> cr + CRContactsSubscribed {server = _srv, contactRefs = _cr} -> cr + CRContactSubError {user, contact, chatError} -> CRContactSubError {user = enrichUser user, contact, chatError} + CRContactSubSummary {user, contactSubscriptions} -> CRContactSubSummary {user = enrichUser user, contactSubscriptions} + CRUserContactSubSummary {user, userContactSubscriptions} -> CRUserContactSubSummary {user = enrichUser user, userContactSubscriptions} + CRNetworkStatus {networkStatus = _ns, connections = _cns} -> cr + CRNetworkStatuses {user_, networkStatuses} -> CRNetworkStatuses {user_ = enrichUser <$> user_, networkStatuses} + CRHostConnected {protocol = _p, transportHost = _th} -> cr + CRHostDisconnected {protocol = _p, transportHost = _th} -> cr + CRGroupInvitation {user, groupInfo} -> CRGroupInvitation {user = enrichUser user, groupInfo} + CRReceivedGroupInvitation {user, groupInfo, contact, fromMemberRole, memberRole} -> CRReceivedGroupInvitation {user = enrichUser user, groupInfo, contact, fromMemberRole, memberRole} + CRUserJoinedGroup {user, groupInfo, hostMember} -> CRUserJoinedGroup {user = enrichUser user, groupInfo, hostMember} + CRJoinedGroupMember {user, groupInfo, member} -> CRJoinedGroupMember {user = enrichUser user, groupInfo, member} + CRJoinedGroupMemberConnecting {user, groupInfo, hostMember, member} -> CRJoinedGroupMemberConnecting {user = enrichUser user, groupInfo, hostMember, member} + CRMemberRole {user, groupInfo, byMember, member, fromRole, toRole} -> CRMemberRole {user = enrichUser user, groupInfo, byMember, member, fromRole, toRole} + CRMemberRoleUser {user, groupInfo, member, fromRole, toRole} -> CRMemberRoleUser {user = enrichUser user, groupInfo, member, fromRole, toRole} + CRConnectedToGroupMember {user, groupInfo, member, memberContact} -> CRConnectedToGroupMember {user = enrichUser user, groupInfo, member, memberContact} + CRDeletedMember {user, groupInfo, byMember, deletedMember} -> CRDeletedMember {user = enrichUser user, groupInfo, byMember, deletedMember} + CRDeletedMemberUser {user, groupInfo, member} -> CRDeletedMemberUser {user = enrichUser user, groupInfo, member} + CRLeftMember {user, groupInfo, member} -> CRLeftMember {user = enrichUser user, groupInfo, member} + CRGroupEmpty {user, groupInfo} -> CRGroupEmpty {user = enrichUser user, groupInfo} + CRGroupRemoved {user, groupInfo} -> CRGroupRemoved {user = enrichUser user, groupInfo} + CRGroupDeleted {user, groupInfo, member} -> CRGroupDeleted {user = enrichUser user, groupInfo, member} + CRGroupUpdated {user, fromGroup, toGroup, member_} -> CRGroupUpdated {user = enrichUser user, fromGroup, toGroup, member_} + CRGroupProfile {user, groupInfo} -> CRGroupProfile {user = enrichUser user, groupInfo} + CRGroupDescription {user, groupInfo} -> CRGroupDescription {user = enrichUser user, groupInfo} + CRGroupLinkCreated {user, groupInfo, connReqContact, memberRole} -> CRGroupLinkCreated {user = enrichUser user, groupInfo, connReqContact, memberRole} + CRGroupLink {user, groupInfo, connReqContact, memberRole} -> CRGroupLink {user = enrichUser user, groupInfo, connReqContact, memberRole} + CRGroupLinkDeleted {user, groupInfo} -> CRGroupLinkDeleted {user = enrichUser user, groupInfo} + CRAcceptingGroupJoinRequest {user, groupInfo, contact} -> CRAcceptingGroupJoinRequest {user = enrichUser user, groupInfo, contact} + CRAcceptingGroupJoinRequestMember {user, groupInfo, member} -> CRAcceptingGroupJoinRequestMember {user = enrichUser user, groupInfo, member} + CRNoMemberContactCreating {user, groupInfo, member} -> CRNoMemberContactCreating {user = enrichUser user, groupInfo, member} + CRNewMemberContact {user, contact, groupInfo, member} -> CRNewMemberContact {user = enrichUser user, contact, groupInfo, member} + CRNewMemberContactSentInv {user, contact, groupInfo, member} -> CRNewMemberContactSentInv {user = enrichUser user, contact, groupInfo, member} + CRNewMemberContactReceivedInv {user, contact, groupInfo, member} -> CRNewMemberContactReceivedInv {user = enrichUser user, contact, groupInfo, member} + CRContactAndMemberAssociated {user, contact, groupInfo, member, updatedContact} -> CRContactAndMemberAssociated {user = enrichUser user, contact, groupInfo, member, updatedContact} + CRMemberSubError {user, groupInfo, member, chatError} -> CRMemberSubError {user = enrichUser user, groupInfo, member, chatError} + CRMemberSubSummary {user, memberSubscriptions} -> CRMemberSubSummary {user = enrichUser user, memberSubscriptions} + CRGroupSubscribed {user, groupInfo} -> CRGroupSubscribed {user = enrichUser user, groupInfo} + CRPendingSubSummary {user, pendingSubscriptions} -> CRPendingSubSummary {user = enrichUser user, pendingSubscriptions} + CRSndFileSubError {user, sndFileTransfer, chatError} -> CRSndFileSubError {user = enrichUser user, sndFileTransfer, chatError} + CRRcvFileSubError {user, rcvFileTransfer, chatError} -> CRRcvFileSubError {user = enrichUser user, rcvFileTransfer, chatError} + CRCallInvitation {callInvitation} -> CRCallInvitation {callInvitation = enrichRcvCallInvitation callInvitation} + CRCallOffer {user, contact, callType, offer, sharedKey, askConfirmation} -> CRCallOffer {user = enrichUser user, contact, callType, offer, sharedKey, askConfirmation} + CRCallAnswer {user, contact, answer} -> CRCallAnswer {user = enrichUser user, contact, answer} + CRCallExtraInfo {user, contact, extraInfo} -> CRCallExtraInfo {user = enrichUser user, contact, extraInfo} + CRCallEnded {user, contact} -> CRCallEnded {user = enrichUser user, contact} + CRCallInvitations {callInvitations} -> CRCallInvitations {callInvitations = map enrichRcvCallInvitation callInvitations} + CRUserContactLinkSubscribed -> cr + CRUserContactLinkSubError {chatError = _ce} -> cr + CRNtfTokenStatus {status = _s} -> cr + CRNtfToken {token = _t, status = _s, ntfMode = _nm} -> cr + CRNtfMessages {user_, connEntity, msgTs, ntfMessages} -> CRNtfMessages {user_ = enrichUser <$> user_, connEntity, msgTs, ntfMessages} + CRNewContactConnection {user, connection} -> CRNewContactConnection {user = enrichUser user, connection} + CRContactConnectionDeleted {user, connection} -> CRContactConnectionDeleted {user = enrichUser user, connection} + CRRemoteHostList {remoteHosts = _rh} -> cr + CRCurrentRemoteHost {remoteHost_ = _rh} -> cr + CRRemoteHostStarted {remoteHost_ = _rh, invitation = _i} -> cr + CRRemoteHostSessionCode {remoteHost_ = _rh, sessionCode = _sc} -> cr + CRNewRemoteHost {remoteHost = _rh} -> cr + CRRemoteHostConnected {remoteHost = _rh} -> cr + CRRemoteHostStopped {remoteHostId_ = _rh} -> cr + CRRemoteFileStored {remoteHostId = _rhId, remoteFileSource = _rfs} -> cr + CRRemoteCtrlList {remoteCtrls = _rc} -> cr + CRRemoteCtrlFound {remoteCtrl = _rc} -> cr + CRRemoteCtrlConnecting {remoteCtrl_ = _rc, ctrlAppInfo = _cai, appVersion = _av} -> cr + CRRemoteCtrlSessionCode {remoteCtrl_ = _rc, sessionCode = _sc} -> cr + CRRemoteCtrlConnected {remoteCtrl = _rc} -> cr + CRRemoteCtrlStopped -> cr + CRSQLResult {rows = _r} -> cr + CRSlowSQLQueries {chatQueries = _cq, agentQueries = _aq} -> cr + CRDebugLocks {chatLockName = _cl, agentLocks = _al} -> cr + CRAgentStats {agentStats = _as} -> cr + CRAgentSubs {activeSubs = _as, pendingSubs = _ps, removedSubs = _rs} -> cr + CRAgentSubsDetails {agentSubs = _as} -> cr + CRConnectionDisabled {connectionEntity = _ce} -> cr + CRAgentRcvQueueDeleted {agentConnId = _acId, server = _srv, agentQueueId = _aqId, agentError_ = _ae} -> cr + CRAgentConnDeleted {agentConnId = _acId} -> cr + CRAgentUserDeleted {agentUserId = _auId} -> cr + CRMessageError {user, severity, errorMessage} -> CRMessageError {user = enrichUser user, severity, errorMessage} + CRChatCmdError {user_, chatError} -> CRChatCmdError {user_ = enrichUser <$> user_, chatError} + CRChatError {user_, chatError} -> CRChatError {user_ = enrichUser <$> user_, chatError} + CRArchiveImported {archiveErrors = _ae} -> cr + CRTimedAction {action = _a, durationMilliseconds = _dm} -> cr + where + enrichUser :: User -> User + enrichUser u = u {remoteHostId = Just rhId} + enrichUserInfo :: UserInfo -> UserInfo + enrichUserInfo uInfo@UserInfo {user} = uInfo {user = enrichUser user} + enrichAChat :: AChat -> AChat + enrichAChat (AChat cType chat) = AChat cType chat {remoteHostId = Just rhId} + enrichServerCfg :: ServerCfg p -> ServerCfg p + enrichServerCfg cfg = cfg {remoteHostId = Just rhId} + enrichAUserProtoServers :: AUserProtoServers -> AUserProtoServers + enrichAUserProtoServers (AUPS ups@UserProtoServers {protoServers}) = AUPS ups {protoServers = fmap enrichServerCfg protoServers} + enrichRcvCallInvitation :: RcvCallInvitation -> RcvCallInvitation + enrichRcvCallInvitation rci = rci {remoteHostId = Just rhId} + + remoteStoreFile :: RemoteHostClient -> FilePath -> FilePath -> ExceptT RemoteProtocolError IO FilePath remoteStoreFile c localPath fileName = do (fileSize, fileDigest) <- getFileInfo localPath @@ -140,7 +358,7 @@ sendRemoteCommand' c attachment_ rc = snd <$> sendRemoteCommand c attachment_ rc sendRemoteCommand :: RemoteHostClient -> Maybe (Handle, Word32) -> RemoteCommand -> ExceptT RemoteProtocolError IO (Int -> IO ByteString, RemoteResponse) sendRemoteCommand RemoteHostClient {httpClient, hostEncoding, encryption} file_ cmd = do - encFile_ <- mapM (prepareEncryptedFile encryption) file_ + encFile_ <- mapM (prepareEncryptedFile encryption) file_ req <- httpRequest encFile_ <$> encryptEncodeHTTP2Body encryption (J.encode cmd) HTTP2Response {response, respBody} <- liftEitherError (RPEHTTP2 . tshow) $ sendRequestDirect httpClient req Nothing (header, getNext) <- parseDecryptHTTP2Body encryption response respBody diff --git a/src/Simplex/Chat/Remote/Types.hs b/src/Simplex/Chat/Remote/Types.hs index 783a083e55..12b5862813 100644 --- a/src/Simplex/Chat/Remote/Types.hs +++ b/src/Simplex/Chat/Remote/Types.hs @@ -19,7 +19,7 @@ import Data.ByteString (ByteString) import Data.Int (Int64) import Data.Text (Text) import Simplex.Chat.Remote.AppVersion -import Simplex.Chat.Types (verificationCode) +import Simplex.Chat.Types (verificationCode, RemoteHostId) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Crypto.SNTRUP761 (KEMHybridSecret) import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, sumTypeJSON) @@ -118,8 +118,6 @@ data RemoteProtocolError | RPEException {someException :: Text} deriving (Show, Exception) -type RemoteHostId = Int64 - data RHKey = RHNew | RHId {remoteHostId :: RemoteHostId} deriving (Eq, Ord, Show) diff --git a/src/Simplex/Chat/Store/Messages.hs b/src/Simplex/Chat/Store/Messages.hs index b6d455fe86..859ce638e0 100644 --- a/src/Simplex/Chat/Store/Messages.hs +++ b/src/Simplex/Chat/Store/Messages.hs @@ -572,7 +572,7 @@ getDirectChatPreviews_ db user@User {userId} = do let contact = toContact user $ contactRow :. connRow ci_ = toDirectChatItemList currentTs ciRow_ stats = toChatStats statsRow - in AChat SCTDirect $ Chat (DirectChat contact) ci_ stats + in AChat SCTDirect $ Chat Nothing (DirectChat contact) ci_ stats getGroupChatPreviews_ :: DB.Connection -> User -> IO [AChat] getGroupChatPreviews_ db User {userId, userContactId} = do @@ -645,7 +645,7 @@ getGroupChatPreviews_ db User {userId, userContactId} = do let groupInfo = toGroupInfo userContactId groupInfoRow ci_ = toGroupChatItemList currentTs userContactId ciRow_ stats = toChatStats statsRow - in AChat SCTGroup $ Chat (GroupChat groupInfo) ci_ stats + in AChat SCTGroup $ Chat Nothing (GroupChat groupInfo) ci_ stats getContactRequestChatPreviews_ :: DB.Connection -> User -> IO [AChat] getContactRequestChatPreviews_ db User {userId} = @@ -669,7 +669,7 @@ getContactRequestChatPreviews_ db User {userId} = toContactRequestChatPreview cReqRow = let cReq = toContactRequest cReqRow stats = ChatStats {unreadCount = 0, minUnreadItemId = 0, unreadChat = False} - in AChat SCTContactRequest $ Chat (ContactRequest cReq) [] stats + in AChat SCTContactRequest $ Chat Nothing (ContactRequest cReq) [] stats getContactConnectionChatPreviews_ :: DB.Connection -> User -> Bool -> IO [AChat] getContactConnectionChatPreviews_ _ _ False = pure [] @@ -688,7 +688,7 @@ getContactConnectionChatPreviews_ db User {userId} _ = toContactConnectionChatPreview connRow = let conn = toPendingContactConnection connRow stats = ChatStats {unreadCount = 0, minUnreadItemId = 0, unreadChat = False} - in AChat SCTContactConnection $ Chat (ContactConnection conn) [] stats + in AChat SCTContactConnection $ Chat Nothing (ContactConnection conn) [] stats getDirectChat :: DB.Connection -> User -> Int64 -> ChatPagination -> Maybe String -> ExceptT StoreError IO (Chat 'CTDirect) getDirectChat db user contactId pagination search_ = do @@ -703,7 +703,7 @@ getDirectChatLast_ :: DB.Connection -> User -> Contact -> Int -> String -> Excep getDirectChatLast_ db user ct@Contact {contactId} count search = do let stats = ChatStats {unreadCount = 0, minUnreadItemId = 0, unreadChat = False} chatItems <- getDirectChatItemsLast db user contactId count search - pure $ Chat (DirectChat ct) (reverse chatItems) stats + pure $ Chat Nothing (DirectChat ct) (reverse chatItems) stats -- the last items in reverse order (the last item in the conversation is the first in the returned list) getDirectChatItemsLast :: DB.Connection -> User -> ContactId -> Int -> String -> ExceptT StoreError IO [CChatItem 'CTDirect] @@ -733,7 +733,7 @@ getDirectChatAfter_ :: DB.Connection -> User -> Contact -> ChatItemId -> Int -> getDirectChatAfter_ db User {userId} ct@Contact {contactId} afterChatItemId count search = do let stats = ChatStats {unreadCount = 0, minUnreadItemId = 0, unreadChat = False} chatItems <- ExceptT getDirectChatItemsAfter_ - pure $ Chat (DirectChat ct) chatItems stats + pure $ Chat Nothing (DirectChat ct) chatItems stats where getDirectChatItemsAfter_ :: IO (Either StoreError [CChatItem 'CTDirect]) getDirectChatItemsAfter_ = do @@ -763,7 +763,7 @@ getDirectChatBefore_ :: DB.Connection -> User -> Contact -> ChatItemId -> Int -> getDirectChatBefore_ db User {userId} ct@Contact {contactId} beforeChatItemId count search = do let stats = ChatStats {unreadCount = 0, minUnreadItemId = 0, unreadChat = False} chatItems <- ExceptT getDirectChatItemsBefore_ - pure $ Chat (DirectChat ct) (reverse chatItems) stats + pure $ Chat Nothing (DirectChat ct) (reverse chatItems) stats where getDirectChatItemsBefore_ :: IO (Either StoreError [CChatItem 'CTDirect]) getDirectChatItemsBefore_ = do @@ -803,7 +803,7 @@ getGroupChatLast_ db user@User {userId} g@GroupInfo {groupId} count search = do let stats = ChatStats {unreadCount = 0, minUnreadItemId = 0, unreadChat = False} chatItemIds <- liftIO getGroupChatItemIdsLast_ chatItems <- mapM (getGroupCIWithReactions db user g) chatItemIds - pure $ Chat (GroupChat g) (reverse chatItems) stats + pure $ Chat Nothing (GroupChat g) (reverse chatItems) stats where getGroupChatItemIdsLast_ :: IO [ChatItemId] getGroupChatItemIdsLast_ = @@ -841,7 +841,7 @@ getGroupChatAfter_ db user@User {userId} g@GroupInfo {groupId} afterChatItemId c afterChatItem <- getGroupChatItem db user groupId afterChatItemId chatItemIds <- liftIO $ getGroupChatItemIdsAfter_ (chatItemTs afterChatItem) chatItems <- mapM (getGroupCIWithReactions db user g) chatItemIds - pure $ Chat (GroupChat g) chatItems stats + pure $ Chat Nothing (GroupChat g) chatItems stats where getGroupChatItemIdsAfter_ :: UTCTime -> IO [ChatItemId] getGroupChatItemIdsAfter_ afterChatItemTs = @@ -864,7 +864,7 @@ getGroupChatBefore_ db user@User {userId} g@GroupInfo {groupId} beforeChatItemId beforeChatItem <- getGroupChatItem db user groupId beforeChatItemId chatItemIds <- liftIO $ getGroupChatItemIdsBefore_ (chatItemTs beforeChatItem) chatItems <- mapM (getGroupCIWithReactions db user g) chatItemIds - pure $ Chat (GroupChat g) (reverse chatItems) stats + pure $ Chat Nothing (GroupChat g) (reverse chatItems) stats where getGroupChatItemIdsBefore_ :: UTCTime -> IO [ChatItemId] getGroupChatItemIdsBefore_ beforeChatItemTs = diff --git a/src/Simplex/Chat/Store/Profiles.hs b/src/Simplex/Chat/Store/Profiles.hs index 611faf90c6..010bcf2bc3 100644 --- a/src/Simplex/Chat/Store/Profiles.hs +++ b/src/Simplex/Chat/Store/Profiles.hs @@ -504,7 +504,7 @@ getProtocolServers db User {userId} = toServerCfg :: (NonEmpty TransportHost, String, C.KeyHash, Maybe Text, Bool, Maybe Bool, Bool) -> ServerCfg p toServerCfg (host, port, keyHash, auth_, preset, tested, enabled) = let server = ProtoServerWithAuth (ProtocolServer protocol host port keyHash) (BasicAuth . encodeUtf8 <$> auth_) - in ServerCfg {server, preset, tested, enabled} + in ServerCfg {remoteHostId = Nothing, server, preset, tested, enabled} overwriteProtocolServers :: forall p. ProtocolTypeI p => DB.Connection -> User -> [ServerCfg p] -> ExceptT StoreError IO () overwriteProtocolServers db User {userId} servers = @@ -555,7 +555,7 @@ getCalls db = |] where toCall :: (ContactId, CallId, ChatItemId, CallState, UTCTime) -> Call - toCall (contactId, callId, chatItemId, callState, callTs) = Call {contactId, callId, chatItemId, callState, callTs} + toCall (contactId, callId, chatItemId, callState, callTs) = Call {remoteHostId = Nothing, contactId, callId, chatItemId, callState, callTs} createCommand :: DB.Connection -> User -> Maybe Int64 -> CommandFunction -> IO CommandId createCommand db User {userId} connId commandFunction = do diff --git a/src/Simplex/Chat/Store/Remote.hs b/src/Simplex/Chat/Store/Remote.hs index ec84860379..4127ebce4b 100644 --- a/src/Simplex/Chat/Store/Remote.hs +++ b/src/Simplex/Chat/Store/Remote.hs @@ -18,6 +18,7 @@ import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Crypto as C import Simplex.RemoteControl.Types import UnliftIO +import Simplex.Chat.Types (RemoteHostId) insertRemoteHost :: DB.Connection -> Text -> FilePath -> RCHostPairing -> ExceptT StoreError IO RemoteHostId insertRemoteHost db hostDeviceName storePath RCHostPairing {caKey, caCert, idPrivKey, knownHost = kh_} = do diff --git a/src/Simplex/Chat/Store/Shared.hs b/src/Simplex/Chat/Store/Shared.hs index af8220d8ea..78807fc038 100644 --- a/src/Simplex/Chat/Store/Shared.hs +++ b/src/Simplex/Chat/Store/Shared.hs @@ -316,7 +316,7 @@ userQuery = toUser :: (UserId, UserId, ContactId, ProfileId, Bool, ContactName, Text, Maybe ImageData, Maybe ConnReqContact, Maybe Preferences) :. (Bool, Bool, Bool, Maybe B64UrlByteString, Maybe B64UrlByteString) -> User toUser ((userId, auId, userContactId, profileId, activeUser, displayName, fullName, image, contactLink, userPreferences) :. (showNtfs, sendRcptsContacts, sendRcptsSmallGroups, viewPwdHash_, viewPwdSalt_)) = - User {userId, agentUserId = AgentUserId auId, userContactId, localDisplayName = displayName, profile, activeUser, fullPreferences, showNtfs, sendRcptsContacts, sendRcptsSmallGroups, viewPwdHash} + User {remoteHostId = Nothing, userId, agentUserId = AgentUserId auId, userContactId, localDisplayName = displayName, profile, activeUser, fullPreferences, showNtfs, sendRcptsContacts, sendRcptsSmallGroups, viewPwdHash} where profile = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences = userPreferences, localAlias = ""} fullPreferences = mergePreferences Nothing userPreferences diff --git a/src/Simplex/Chat/Terminal/Output.hs b/src/Simplex/Chat/Terminal/Output.hs index 98d4285a2e..629054b474 100644 --- a/src/Simplex/Chat/Terminal/Output.hs +++ b/src/Simplex/Chat/Terminal/Output.hs @@ -26,7 +26,6 @@ import Simplex.Chat.Messages import Simplex.Chat.Messages.CIContent (CIContent(..), SMsgDirection (..)) import Simplex.Chat.Options import Simplex.Chat.Protocol (MsgContent (..), msgContentText) -import Simplex.Chat.Remote.Types (RemoteHostId) import Simplex.Chat.Styled import Simplex.Chat.Terminal.Notification (Notification (..), initializeNotifications) import Simplex.Chat.Types diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index d96bddb8b7..536384f796 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -100,11 +100,14 @@ instance FromField AgentUserId where fromField f = AgentUserId <$> fromField f instance ToField AgentUserId where toField (AgentUserId uId) = toField uId +type RemoteHostId = Int64 + aUserId :: User -> UserId aUserId User {agentUserId = AgentUserId uId} = uId data User = User - { userId :: UserId, + { remoteHostId :: Maybe RemoteHostId, + userId :: UserId, agentUserId :: AgentUserId, userContactId :: ContactId, localDisplayName :: ContactName, @@ -1521,7 +1524,8 @@ data XGrpMemIntroCont = XGrpMemIntroCont deriving (Show) data ServerCfg p = ServerCfg - { server :: ProtoServerWithAuth p, + { remoteHostId :: Maybe RemoteHostId, + server :: ProtoServerWithAuth p, preset :: Bool, tested :: Maybe Bool, enabled :: Bool diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 119d19e620..80ba9e5349 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -387,10 +387,10 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe testViewChats chats = [sShow $ map toChatView chats] where toChatView :: AChat -> (Text, Text, Maybe ConnStatus) - toChatView (AChat _ (Chat (DirectChat Contact {localDisplayName, activeConn}) items _)) = ("@" <> localDisplayName, toCIPreview items Nothing, connStatus <$> activeConn) - toChatView (AChat _ (Chat (GroupChat GroupInfo {membership, localDisplayName}) items _)) = ("#" <> localDisplayName, toCIPreview items (Just membership), Nothing) - toChatView (AChat _ (Chat (ContactRequest UserContactRequest {localDisplayName}) items _)) = ("<@" <> localDisplayName, toCIPreview items Nothing, Nothing) - toChatView (AChat _ (Chat (ContactConnection PendingContactConnection {pccConnId, pccConnStatus}) items _)) = (":" <> T.pack (show pccConnId), toCIPreview items Nothing, Just pccConnStatus) + toChatView (AChat _ (Chat _ (DirectChat Contact {localDisplayName, activeConn}) items _)) = ("@" <> localDisplayName, toCIPreview items Nothing, connStatus <$> activeConn) + toChatView (AChat _ (Chat _ (GroupChat GroupInfo {membership, localDisplayName}) items _)) = ("#" <> localDisplayName, toCIPreview items (Just membership), Nothing) + toChatView (AChat _ (Chat _ (ContactRequest UserContactRequest {localDisplayName}) items _)) = ("<@" <> localDisplayName, toCIPreview items Nothing, Nothing) + toChatView (AChat _ (Chat _ (ContactConnection PendingContactConnection {pccConnId, pccConnStatus}) items _)) = (":" <> T.pack (show pccConnId), toCIPreview items Nothing, Just pccConnStatus) toCIPreview :: [CChatItem c] -> Maybe GroupMember -> Text toCIPreview (ci : _) membership_ = testViewItem ci membership_ toCIPreview _ _ = "" @@ -496,7 +496,7 @@ viewHostEvent p h = map toUpper (B.unpack $ strEncode p) <> " host " <> B.unpack viewChats :: CurrentTime -> TimeZone -> [AChat] -> [StyledString] viewChats ts tz = concatMap chatPreview . reverse where - chatPreview (AChat _ (Chat chat items _)) = case items of + chatPreview (AChat _ (Chat _ chat items _)) = case items of CChatItem _ ci : _ -> case viewChatItem chat ci True ts tz of s : _ -> [let s' = sTake 120 s in if sLength s' < sLength s then s' <> "..." else s'] _ -> chatName