From 15c1f9f9c8d9a25524199be819aee9ddeca41c85 Mon Sep 17 00:00:00 2001 From: JRoberts <8711996+jr-simplex@users.noreply.github.com> Date: Mon, 24 Oct 2022 00:18:15 +0400 Subject: [PATCH] core: group link contact connecting(ed) events to avoid adding previews in ui (#1242) * core: group link contact connecting(ed) events to avoid adding ui previews * fix test * refactor * ios types * android types * type in bot --- .../java/chat/simplex/app/model/SimpleXAPI.kt | 28 +++++++++++-------- apps/ios/Shared/Model/SimpleXAPI.swift | 26 +++++++++-------- .../ios/SimpleX NSE/NotificationService.swift | 4 +-- apps/ios/SimpleXChat/APITypes.swift | 12 ++++---- apps/simplex-bot-advanced/Main.hs | 2 +- .../typescript/src/response.ts | 4 +++ src/Simplex/Chat.hs | 14 ++++++---- src/Simplex/Chat/Bot.hs | 2 +- src/Simplex/Chat/Controller.hs | 7 ++--- src/Simplex/Chat/Store.hs | 5 ++++ src/Simplex/Chat/View.hs | 10 ++++--- 11 files changed, 67 insertions(+), 47 deletions(-) diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index 4de5359e05..c24b1dc320 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -923,16 +923,20 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a chatModel.removeChat(r.connection.id) } is CR.ContactConnected -> { - chatModel.updateContact(r.contact) - chatModel.dismissConnReqView(r.contact.activeConn.id) - chatModel.removeChat(r.contact.activeConn.id) - chatModel.updateNetworkStatus(r.contact.id, Chat.NetworkStatus.Connected()) - ntfManager.notifyContactConnected(r.contact) + if (!r.viaGroupLink) { + chatModel.updateContact(r.contact) + chatModel.dismissConnReqView(r.contact.activeConn.id) + chatModel.removeChat(r.contact.activeConn.id) + chatModel.updateNetworkStatus(r.contact.id, Chat.NetworkStatus.Connected()) + ntfManager.notifyContactConnected(r.contact) + } } is CR.ContactConnecting -> { - chatModel.updateContact(r.contact) - chatModel.dismissConnReqView(r.contact.activeConn.id) - chatModel.removeChat(r.contact.activeConn.id) + if (!r.viaGroupLink) { + chatModel.updateContact(r.contact) + chatModel.dismissConnReqView(r.contact.activeConn.id) + chatModel.removeChat(r.contact.activeConn.id) + } } is CR.ReceivedContactRequest -> { val contactRequest = r.contactRequest @@ -1761,8 +1765,8 @@ sealed class CR { @Serializable @SerialName("userContactLinkUpdated") class UserContactLinkUpdated(val contactLink: UserContactLinkRec): CR() @Serializable @SerialName("userContactLinkCreated") class UserContactLinkCreated(val connReqContact: String): CR() @Serializable @SerialName("userContactLinkDeleted") class UserContactLinkDeleted: CR() - @Serializable @SerialName("contactConnected") class ContactConnected(val contact: Contact): CR() - @Serializable @SerialName("contactConnecting") class ContactConnecting(val contact: Contact): CR() + @Serializable @SerialName("contactConnected") class ContactConnected(val contact: Contact, val userCustomProfile: Profile?, val viaGroupLink: Boolean): CR() + @Serializable @SerialName("contactConnecting") class ContactConnecting(val contact: Contact, val viaGroupLink: Boolean): CR() @Serializable @SerialName("receivedContactRequest") class ReceivedContactRequest(val contactRequest: UserContactRequest): CR() @Serializable @SerialName("acceptingContactRequest") class AcceptingContactRequest(val contact: Contact): CR() @Serializable @SerialName("contactRequestRejected") class ContactRequestRejected: CR() @@ -1782,7 +1786,7 @@ sealed class CR { @Serializable @SerialName("contactsList") class ContactsList(val contacts: List): CR() // group events @Serializable @SerialName("groupCreated") class GroupCreated(val groupInfo: GroupInfo): CR() - @Serializable @SerialName("sentGroupInvitation") class SentGroupInvitation(val groupInfo: GroupInfo, val contact: Contact, val member: GroupMember): CR() + @Serializable @SerialName("sentGroupInvitation") class SentGroupInvitation(val groupInfo: GroupInfo, val contact: Contact, val member: GroupMember, val viaGroupLink: Boolean): CR() @Serializable @SerialName("userAcceptedGroupSent") class UserAcceptedGroupSent (val groupInfo: GroupInfo): CR() @Serializable @SerialName("userDeletedMember") class UserDeletedMember(val groupInfo: GroupInfo, val member: GroupMember): CR() @Serializable @SerialName("leftMemberUser") class LeftMemberUser(val groupInfo: GroupInfo): CR() @@ -1971,7 +1975,7 @@ sealed class CR { is ChatItemDeleted -> "deletedChatItem:\n${json.encodeToString(deletedChatItem)}\ntoChatItem:\n${json.encodeToString(toChatItem)}" is ContactsList -> json.encodeToString(contacts) is GroupCreated -> json.encodeToString(groupInfo) - is SentGroupInvitation -> "groupInfo: $groupInfo\ncontact: $contact\nmember: $member" + is SentGroupInvitation -> "groupInfo: $groupInfo\ncontact: $contact\nmember: $member\nviaGroupLink: $viaGroupLink" is UserAcceptedGroupSent -> json.encodeToString(groupInfo) is UserDeletedMember -> "groupInfo: $groupInfo\nmember: $member" is LeftMemberUser -> json.encodeToString(groupInfo) diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 604a6cc7be..d5b710ad39 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -694,7 +694,7 @@ func apiNewGroup(_ p: GroupProfile) throws -> GroupInfo { func apiAddMember(_ groupId: Int64, _ contactId: Int64, _ memberRole: GroupMemberRole) async throws -> GroupMember { let r = await chatSendCmd(.apiAddMember(groupId: groupId, contactId: contactId, memberRole: memberRole)) - if case let .sentGroupInvitation(_, _, member) = r { return member } + if case let .sentGroupInvitation(_, _, member, _) = r { return member } throw r } @@ -881,16 +881,20 @@ func processReceivedMsg(_ res: ChatResponse) async { m.updateContactConnection(connection) case let .contactConnectionDeleted(connection): m.removeChat(connection.id) - case let .contactConnected(contact): - m.updateContact(contact) - m.dismissConnReqView(contact.activeConn.id) - m.removeChat(contact.activeConn.id) - m.updateNetworkStatus(contact.id, .connected) - NtfManager.shared.notifyContactConnected(contact) - case let .contactConnecting(contact): - m.updateContact(contact) - m.dismissConnReqView(contact.activeConn.id) - m.removeChat(contact.activeConn.id) + case let .contactConnected(contact, _, viaGroupLink): + if !viaGroupLink { + m.updateContact(contact) + m.dismissConnReqView(contact.activeConn.id) + m.removeChat(contact.activeConn.id) + m.updateNetworkStatus(contact.id, .connected) + NtfManager.shared.notifyContactConnected(contact) + } + case let .contactConnecting(contact, viaGroupLink): + if !viaGroupLink { + m.updateContact(contact) + m.dismissConnReqView(contact.activeConn.id) + m.removeChat(contact.activeConn.id) + } case let .receivedContactRequest(contactRequest): let cInfo = ChatInfo.contactRequest(contactRequest: contactRequest) if m.hasChat(contactRequest.id) { diff --git a/apps/ios/SimpleX NSE/NotificationService.swift b/apps/ios/SimpleX NSE/NotificationService.swift index 05de795e49..4decfde072 100644 --- a/apps/ios/SimpleX NSE/NotificationService.swift +++ b/apps/ios/SimpleX NSE/NotificationService.swift @@ -207,9 +207,9 @@ func chatRecvMsg() async -> ChatResponse? { func receivedMsgNtf(_ res: ChatResponse) async -> (String, UNMutableNotificationContent)? { logger.debug("NotificationService processReceivedMsg: \(res.responseType)") switch res { - case let .contactConnected(contact): + case let .contactConnected(contact, _, _): return (contact.id, createContactConnectedNtf(contact)) -// case let .contactConnecting(contact): +// case let .contactConnecting(contact, _): // TODO profile update case let .receivedContactRequest(contactRequest): return (UserContact(contactRequest: contactRequest).id, createContactRequestNtf(contactRequest)) diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index dfd0a47fc5..7dbf1aff8e 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -301,8 +301,8 @@ public enum ChatResponse: Decodable, Error { case userContactLinkUpdated(contactLink: UserContactLink) case userContactLinkCreated(connReqContact: String) case userContactLinkDeleted - case contactConnected(contact: Contact) - case contactConnecting(contact: Contact) + case contactConnected(contact: Contact, userCustomProfile: Profile?, viaGroupLink: Bool) + case contactConnecting(contact: Contact, viaGroupLink: Bool) case receivedContactRequest(contactRequest: UserContactRequest) case acceptingContactRequest(contact: Contact) case contactRequestRejected @@ -322,7 +322,7 @@ public enum ChatResponse: Decodable, Error { case contactsList(contacts: [Contact]) // group events case groupCreated(groupInfo: GroupInfo) - case sentGroupInvitation(groupInfo: GroupInfo, contact: Contact, member: GroupMember) + case sentGroupInvitation(groupInfo: GroupInfo, contact: Contact, member: GroupMember, viaGroupLink: Bool) case userAcceptedGroupSent(groupInfo: GroupInfo) case userDeletedMember(groupInfo: GroupInfo, member: GroupMember) case leftMemberUser(groupInfo: GroupInfo) @@ -503,8 +503,8 @@ public enum ChatResponse: Decodable, Error { case let .userContactLinkUpdated(contactLink): return contactLink.responseDetails case let .userContactLinkCreated(connReq): return connReq case .userContactLinkDeleted: return noDetails - case let .contactConnected(contact): return String(describing: contact) - case let .contactConnecting(contact): return String(describing: contact) + case let .contactConnected(contact, _, _): return String(describing: contact) + case let .contactConnecting(contact, _): return String(describing: contact) case let .receivedContactRequest(contactRequest): return String(describing: contactRequest) case let .acceptingContactRequest(contact): return String(describing: contact) case .contactRequestRejected: return noDetails @@ -523,7 +523,7 @@ public enum ChatResponse: Decodable, Error { case let .chatItemDeleted(deletedChatItem, toChatItem): return "deletedChatItem:\n\(String(describing: deletedChatItem))\ntoChatItem:\n\(String(describing: toChatItem))" case let .contactsList(contacts): return String(describing: contacts) case let .groupCreated(groupInfo): return String(describing: groupInfo) - case let .sentGroupInvitation(groupInfo, contact, member): return "groupInfo: \(groupInfo)\ncontact: \(contact)\nmember: \(member)" + case let .sentGroupInvitation(groupInfo, contact, member, viaGroupLink): return "groupInfo: \(groupInfo)\ncontact: \(contact)\nmember: \(member)\nviaGroupLink: \(viaGroupLink)" case let .userAcceptedGroupSent(groupInfo): return String(describing: groupInfo) case let .userDeletedMember(groupInfo, member): return "groupInfo: \(groupInfo)\nmember: \(member)" case let .leftMemberUser(groupInfo): return String(describing: groupInfo) diff --git a/apps/simplex-bot-advanced/Main.hs b/apps/simplex-bot-advanced/Main.hs index f5acebe96a..864c532fae 100644 --- a/apps/simplex-bot-advanced/Main.hs +++ b/apps/simplex-bot-advanced/Main.hs @@ -39,7 +39,7 @@ mySquaringBot _user cc = do race_ (forever $ void getLine) . forever $ do (_, resp) <- atomically . readTBQueue $ outputQ cc case resp of - CRContactConnected contact _ -> do + CRContactConnected contact _ _ -> do contactConnected contact void . sendMsg contact $ "Hello! I am a simple squaring bot - if you send me a number, I will calculate its square" CRNewChatItem (AChatItem _ SMDRcv (DirectChat contact) ChatItem {content}) -> do diff --git a/packages/simplex-chat-client/typescript/src/response.ts b/packages/simplex-chat-client/typescript/src/response.ts index 48fc921da1..2d3b2e227a 100644 --- a/packages/simplex-chat-client/typescript/src/response.ts +++ b/packages/simplex-chat-client/typescript/src/response.ts @@ -383,11 +383,14 @@ export interface CRContactRequestAlreadyAccepted extends CR { export interface CRContactConnecting extends CR { type: "contactConnecting" contact: Contact + viaGroupLink: boolean } export interface CRContactConnected extends CR { type: "contactConnected" contact: Contact + userCustomProfile?: Profile + viaGroupLink: boolean } export interface CRContactAnotherClient extends CR { @@ -566,6 +569,7 @@ export interface CRSentGroupInvitation extends CR { groupInfo: GroupInfo contact: Contact member: GroupMember + viaGroupLink: boolean } export interface CRLeftMemberUser extends CR { diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index db3ffd31df..63700c8971 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -832,11 +832,11 @@ processChatCommand = \case (agentConnId, cReq) <- withAgent $ \a -> createConnection a True SCMInvitation member <- withStore $ \db -> createNewContactMember db gVar user groupId contact memRole agentConnId cReq sendInvitation member cReq - pure $ CRSentGroupInvitation gInfo contact member + pure $ CRSentGroupInvitation gInfo contact member False Just member@GroupMember {groupMemberId, memberStatus} | memberStatus == GSMemInvited -> withStore' (\db -> getMemberInvitation db user groupMemberId) >>= \case - Just cReq -> sendInvitation member cReq $> CRSentGroupInvitation gInfo contact member + Just cReq -> sendInvitation member cReq $> CRSentGroupInvitation gInfo contact member False Nothing -> throwChatError $ CEGroupCantResendInvitation gInfo cName | otherwise -> throwChatError $ CEGroupDuplicateMember cName APIJoinGroup groupId -> withUser $ \user@User {userId} -> do @@ -1685,7 +1685,8 @@ processAgentMessage (Just user@User {userId, profile}) corrId agentConnId agentM Nothing -> do -- [incognito] print incognito profile used for this contact incognitoProfile <- forM customUserProfileId $ \profileId -> withStore (\db -> getProfileById db userId profileId) - toView $ CRContactConnected ct (fmap fromLocalProfile incognitoProfile) + viaGroupLink <- withStore' (\db -> getConnectionViaGroupLinkFlag db user connId) + toView $ CRContactConnected ct (fmap fromLocalProfile incognitoProfile) (fromMaybe False viaGroupLink) setActive $ ActiveC c showToast (c <> "> ") "connected" forM_ viaUserContactLink $ \userContactLinkId -> @@ -1749,7 +1750,7 @@ processAgentMessage (Just user@User {userId, profile}) corrId agentConnId agentM Just ct -> do withStore' $ \db -> setNewContactMemberConnRequest db user m cReq sendGrpInvitation ct m - toView $ CRSentGroupInvitationViaLink gInfo ct m + toView $ CRSentGroupInvitation gInfo ct m True where sendGrpInvitation :: Contact -> GroupMember -> m () sendGrpInvitation ct GroupMember {memberId, memberRole = memRole} = do @@ -2533,12 +2534,13 @@ processAgentMessage (Just user@User {userId, profile}) corrId agentConnId agentM toView $ CRContactsMerged to from saveConnInfo :: Connection -> ConnInfo -> m () - saveConnInfo activeConn connInfo = do + saveConnInfo activeConn@Connection {connId} connInfo = do ChatMessage {chatMsgEvent} <- parseChatMessage connInfo case chatMsgEvent of XInfo p -> do ct <- withStore $ \db -> createDirectContact db userId activeConn p - toView $ CRContactConnecting ct + viaGroupLink <- withStore' (\db -> getConnectionViaGroupLinkFlag db user connId) + toView $ CRContactConnecting ct (fromMaybe False viaGroupLink) -- TODO show/log error, other events in SMP confirmation _ -> pure () diff --git a/src/Simplex/Chat/Bot.hs b/src/Simplex/Chat/Bot.hs index 9368b36450..a209a2eeec 100644 --- a/src/Simplex/Chat/Bot.hs +++ b/src/Simplex/Chat/Bot.hs @@ -24,7 +24,7 @@ chatBotRepl welcome answer _user cc = do race_ (forever $ void getLine) . forever $ do (_, resp) <- atomically . readTBQueue $ outputQ cc case resp of - CRContactConnected contact _ -> do + CRContactConnected contact _ _ -> do contactConnected contact void $ sendMsg contact welcome CRNewChatItem (AChatItem _ SMDRcv (DirectChat contact) ChatItem {content}) -> do diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index b459347128..b8614f34ba 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -281,8 +281,7 @@ data ChatResponse | CRUserAcceptedGroupSent {groupInfo :: GroupInfo} | CRUserDeletedMember {groupInfo :: GroupInfo, member :: GroupMember} | CRGroupsList {groups :: [GroupInfo]} - | CRSentGroupInvitation {groupInfo :: GroupInfo, contact :: Contact, member :: GroupMember} - | CRSentGroupInvitationViaLink {groupInfo :: GroupInfo, contact :: Contact, member :: GroupMember} + | CRSentGroupInvitation {groupInfo :: GroupInfo, contact :: Contact, member :: GroupMember, viaGroupLink :: Bool} | CRFileTransferStatus (FileTransfer, [Integer]) -- TODO refactor this type to FileTransferStatus | CRUserProfile {profile :: Profile} | CRUserProfileNoChange @@ -316,8 +315,8 @@ data ChatResponse | CRUserProfileUpdated {fromProfile :: Profile, toProfile :: Profile} | CRContactAliasUpdated {toContact :: Contact} | CRConnectionAliasUpdated {toConnection :: PendingContactConnection} - | CRContactConnecting {contact :: Contact} - | CRContactConnected {contact :: Contact, userCustomProfile :: Maybe Profile} + | CRContactConnecting {contact :: Contact, viaGroupLink :: Bool} + | CRContactConnected {contact :: Contact, userCustomProfile :: Maybe Profile, viaGroupLink :: Bool} | CRContactAnotherClient {contact :: Contact} | CRSubscriptionEnd {connectionEntity :: ConnectionEntity} | CRContactsDisconnected {server :: SMPServer, contactRefs :: [ContactRef]} diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 0100f54be9..ea51f98675 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -68,6 +68,7 @@ module Simplex.Chat.Store getContactConnections, getConnectionEntity, getConnectionById, + getConnectionViaGroupLinkFlag, getConnectionsContacts, getGroupAndMember, updateConnectionStatus, @@ -1459,6 +1460,10 @@ getConnectionById db User {userId} connId = ExceptT $ do |] (userId, connId) +getConnectionViaGroupLinkFlag :: DB.Connection -> User -> Int64 -> IO (Maybe Bool) +getConnectionViaGroupLinkFlag db User {userId} connId = + maybeFirstRow fromOnly $ DB.query db "SELECT via_group_link FROM connections WHERE user_id = ? AND connection_id = ? LIMIT 1" (userId, connId) + getConnectionsContacts :: DB.Connection -> UserId -> [ConnId] -> IO [ContactRef] getConnectionsContacts db userId agentConnIds = do DB.execute_ db "DROP TABLE IF EXISTS temp.conn_ids" diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index a643dc8110..5bde2cb9d6 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -93,8 +93,10 @@ responseToView testView = \case CRGroupCreated g -> viewGroupCreated g CRGroupMembers g -> viewGroupMembers g CRGroupsList gs -> viewGroupsList gs - CRSentGroupInvitation g c _ -> ["invitation to join the group " <> ttyGroup' g <> " sent to " <> ttyContact' c] - CRSentGroupInvitationViaLink g c _ -> [ttyContact' c <> " invited to group " <> ttyGroup' g <> " via your group link"] + CRSentGroupInvitation g c _ viaLink -> + if viaLink + then [ttyContact' c <> " invited to group " <> ttyGroup' g <> " via your group link"] + else ["invitation to join the group " <> ttyGroup' g <> " sent to " <> ttyContact' c] CRFileTransferStatus ftStatus -> viewFileTransferStatus ftStatus CRUserProfile p -> viewUserProfile p CRUserProfileNoChange -> ["user profile did not change"] @@ -132,8 +134,8 @@ responseToView testView = \case CRSndFileCancelled _ ft -> sendingFile_ "cancelled" ft CRSndFileRcvCancelled _ ft@SndFileTransfer {recipientDisplayName = c} -> [ttyContact c <> " cancelled receiving " <> sndFile ft] - CRContactConnecting _ -> [] - CRContactConnected ct userCustomProfile -> viewContactConnected ct userCustomProfile testView + CRContactConnecting _ _ -> [] + CRContactConnected ct userCustomProfile _ -> viewContactConnected ct userCustomProfile testView CRContactAnotherClient c -> [ttyContact' c <> ": contact is connected to another client"] CRSubscriptionEnd acEntity -> [sShow (connId (entityConnection acEntity :: Connection)) <> ": END"] CRContactsDisconnected srv cs -> [plain $ "server disconnected " <> showSMPServer srv <> " (" <> contactList cs <> ")"]