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 721fc48df..f6fa70b4e 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 @@ -1480,10 +1480,14 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a if (active(r.user)) { chatModel.upsertGroupMember(r.groupInfo, r.member) } - is CR.ConnectedToGroupMember -> + is CR.ConnectedToGroupMember -> { if (active(r.user)) { chatModel.upsertGroupMember(r.groupInfo, r.member) } + if (r.memberContact != null) { + chatModel.setContactNetworkStatus(r.memberContact, NetworkStatus.Connected()) + } + } is CR.GroupUpdated -> if (active(r.user)) { chatModel.updateGroup(r.toGroup) @@ -3333,7 +3337,7 @@ sealed class CR { @Serializable @SerialName("groupInvitation") class GroupInvitation(val user: User, val groupInfo: GroupInfo): CR() // unused @Serializable @SerialName("userJoinedGroup") class UserJoinedGroup(val user: User, val groupInfo: GroupInfo): CR() @Serializable @SerialName("joinedGroupMember") class JoinedGroupMember(val user: User, val groupInfo: GroupInfo, val member: GroupMember): CR() - @Serializable @SerialName("connectedToGroupMember") class ConnectedToGroupMember(val user: User, val groupInfo: GroupInfo, val member: GroupMember): CR() + @Serializable @SerialName("connectedToGroupMember") class ConnectedToGroupMember(val user: User, val groupInfo: GroupInfo, val member: GroupMember, val memberContact: Contact? = null): CR() @Serializable @SerialName("groupRemoved") class GroupRemoved(val user: User, val groupInfo: GroupInfo): CR() // unused @Serializable @SerialName("groupUpdated") class GroupUpdated(val user: User, val toGroup: GroupInfo): CR() @Serializable @SerialName("groupLinkCreated") class GroupLinkCreated(val user: User, val groupInfo: GroupInfo, val connReqContact: String, val memberRole: GroupMemberRole): CR() @@ -3558,7 +3562,7 @@ sealed class CR { is GroupInvitation -> withUser(user, json.encodeToString(groupInfo)) is UserJoinedGroup -> withUser(user, json.encodeToString(groupInfo)) is JoinedGroupMember -> withUser(user, "groupInfo: $groupInfo\nmember: $member") - is ConnectedToGroupMember -> withUser(user, "groupInfo: $groupInfo\nmember: $member") + is ConnectedToGroupMember -> withUser(user, "groupInfo: $groupInfo\nmember: $member\nmemberContact: $memberContact") is GroupRemoved -> withUser(user, json.encodeToString(groupInfo)) is GroupUpdated -> withUser(user, json.encodeToString(toGroup)) is GroupLinkCreated -> withUser(user, "groupInfo: $groupInfo\nconnReqContact: $connReqContact\nmemberRole: $memberRole") diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index d5a58e659..16eddca9a 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -1345,10 +1345,13 @@ func processReceivedMsg(_ res: ChatResponse) async { if active(user) { _ = m.upsertGroupMember(groupInfo, member) } - case let .connectedToGroupMember(user, groupInfo, member): + case let .connectedToGroupMember(user, groupInfo, member, memberContact): if active(user) { _ = m.upsertGroupMember(groupInfo, member) } + if let contact = memberContact { + m.setContactNetworkStatus(contact, .connected) + } case let .groupUpdated(user, toGroup): if active(user) { m.updateGroup(toGroup) diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index f2759e8ce..7e590a40f 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -456,7 +456,7 @@ public enum ChatResponse: Decodable, Error { case groupInvitation(user: User, groupInfo: GroupInfo) // unused case userJoinedGroup(user: User, groupInfo: GroupInfo) case joinedGroupMember(user: User, groupInfo: GroupInfo, member: GroupMember) - case connectedToGroupMember(user: User, groupInfo: GroupInfo, member: GroupMember) + case connectedToGroupMember(user: User, groupInfo: GroupInfo, member: GroupMember, memberContact: Contact?) case groupRemoved(user: User, groupInfo: GroupInfo) // unused case groupUpdated(user: User, toGroup: GroupInfo) case groupLinkCreated(user: User, groupInfo: GroupInfo, connReqContact: String, memberRole: GroupMemberRole) @@ -692,7 +692,7 @@ public enum ChatResponse: Decodable, Error { case let .groupInvitation(u, groupInfo): return withUser(u, String(describing: groupInfo)) case let .userJoinedGroup(u, groupInfo): return withUser(u, String(describing: groupInfo)) case let .joinedGroupMember(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)") - case let .connectedToGroupMember(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)") + case let .connectedToGroupMember(u, groupInfo, member, memberContact): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)\nmemberContact: \(String(describing: memberContact))") case let .groupRemoved(u, groupInfo): return withUser(u, String(describing: groupInfo)) case let .groupUpdated(u, toGroup): return withUser(u, String(describing: toGroup)) case let .groupLinkCreated(u, groupInfo, connReqContact, memberRole): return withUser(u, "groupInfo: \(groupInfo)\nconnReqContact: \(connReqContact)\nmemberRole: \(memberRole)") diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index bac4e5cb1..8dab2e8d3 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -46,7 +46,6 @@ import Data.Text.Encoding (encodeUtf8) import Data.Time (NominalDiffTime, addUTCTime, defaultTimeLocale, formatTime) import Data.Time.Clock (UTCTime, diffUTCTime, getCurrentTime, nominalDay, nominalDiffTimeToSeconds) import Data.Time.Clock.System (SystemTime, systemToUTCTime) -import Data.Time.LocalTime (getCurrentTimeZone) import Data.Word (Word32) import qualified Database.SQLite.Simple as DB import Simplex.Chat.Archive @@ -69,7 +68,7 @@ import Simplex.Messaging.Agent.Client (AgentStatsKey (..), temporaryAgentError) import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), createAgentStore, defaultAgentConfig) import Simplex.Messaging.Agent.Lock import Simplex.Messaging.Agent.Protocol -import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError, SQLiteStore (dbNew), execSQL, upMigration, withTransactionCtx) +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError, SQLiteStore (dbNew), execSQL, upMigration) import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations import Simplex.Messaging.Client (defaultNetworkConfig) import qualified Simplex.Messaging.Crypto as C @@ -2787,7 +2786,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do _ -> pure () Just (gInfo@GroupInfo {membership}, m@GroupMember {activeConn}) -> when (maybe False ((== ConnReady) . connStatus) activeConn) $ do - notifyMemberConnected gInfo m + notifyMemberConnected gInfo m $ Just ct let connectedIncognito = contactConnIncognito ct || memberIncognito membership when (memberCategory m == GCPreMember) $ probeMatchingContacts ct connectedIncognito SENT msgId -> do @@ -2936,11 +2935,11 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do -- TODO notify member who forwarded introduction - question - where it is stored? There is via_contact but probably there should be via_member in group_members table withStore' (\db -> getViaGroupContact db user m) >>= \case Nothing -> do - notifyMemberConnected gInfo m + notifyMemberConnected gInfo m Nothing messageWarning "connected member does not have contact" Just ct@Contact {activeConn = Connection {connStatus}} -> when (connStatus == ConnReady) $ do - notifyMemberConnected gInfo m + notifyMemberConnected gInfo m $ Just ct let connectedIncognito = contactConnIncognito ct || memberIncognito membership when (memberCategory m == GCPreMember) $ probeMatchingContacts ct connectedIncognito MSG msgMeta _msgFlags msgBody -> do @@ -3278,10 +3277,10 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do groupDescriptionChatItem gInfo m descr = createInternalChatItem user (CDGroupRcv gInfo m) (CIRcvMsgContent $ MCText descr) Nothing - notifyMemberConnected :: GroupInfo -> GroupMember -> m () - notifyMemberConnected gInfo m@GroupMember {localDisplayName = c} = do + notifyMemberConnected :: GroupInfo -> GroupMember -> Maybe Contact -> m () + notifyMemberConnected gInfo m@GroupMember {localDisplayName = c} ct_ = do memberConnectedChatItem gInfo m - toView $ CRConnectedToGroupMember user gInfo m + toView $ CRConnectedToGroupMember user gInfo m ct_ let g = groupName' gInfo whenGroupNtfs user gInfo $ do setActive $ ActiveG g diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 2a9154c51..ce152fe0f 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -491,7 +491,7 @@ data ChatResponse | CRJoinedGroupMemberConnecting {user :: User, groupInfo :: GroupInfo, hostMember :: GroupMember, member :: GroupMember} | CRMemberRole {user :: User, groupInfo :: GroupInfo, byMember :: GroupMember, member :: GroupMember, fromRole :: GroupMemberRole, toRole :: GroupMemberRole} | CRMemberRoleUser {user :: User, groupInfo :: GroupInfo, member :: GroupMember, fromRole :: GroupMemberRole, toRole :: GroupMemberRole} - | CRConnectedToGroupMember {user :: User, groupInfo :: GroupInfo, member :: GroupMember} + | CRConnectedToGroupMember {user :: User, groupInfo :: GroupInfo, member :: GroupMember, memberContact :: Maybe Contact} | CRDeletedMember {user :: User, groupInfo :: GroupInfo, byMember :: GroupMember, deletedMember :: GroupMember} | CRDeletedMemberUser {user :: User, groupInfo :: GroupInfo, member :: GroupMember} | CRLeftMember {user :: User, groupInfo :: GroupInfo, member :: GroupMember} diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 61d4c72fb..f6cec6e3b 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -195,7 +195,7 @@ responseToView user_ ChatConfig {logLevel, showReactions, testView} liveItems ts CRHostConnected p h -> [plain $ "connected to " <> viewHostEvent p h] CRHostDisconnected p h -> [plain $ "disconnected from " <> viewHostEvent p h] CRJoinedGroupMemberConnecting u g host m -> ttyUser u [ttyGroup' g <> ": " <> ttyMember host <> " added " <> ttyFullMember m <> " to the group (connecting...)"] - CRConnectedToGroupMember u g m -> ttyUser u [ttyGroup' g <> ": " <> connectedMember m <> " is connected"] + CRConnectedToGroupMember u g m _ -> ttyUser u [ttyGroup' g <> ": " <> connectedMember m <> " is connected"] CRMemberRole u g by m r r' -> ttyUser u $ viewMemberRoleChanged g by m r r' CRMemberRoleUser u g m r r' -> ttyUser u $ viewMemberRoleUserChanged g m r r' CRDeletedMemberUser u g by -> ttyUser u $ [ttyGroup' g <> ": " <> ttyMember by <> " removed you from the group"] <> groupPreserved g