From 322ab094224652e27bba5f6fe23e4d56d569112a Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Thu, 6 Nov 2025 13:16:29 +0400 Subject: [PATCH] active on con, fix send --- bots/api/TYPES.md | 2 +- bots/src/API/Docs/Commands.hs | 2 - bots/src/API/Docs/Events.hs | 1 + .../types/typescript/src/types.ts | 2 +- src/Simplex/Chat/Controller.hs | 2 +- src/Simplex/Chat/Library/Internal.hs | 6 +- src/Simplex/Chat/Library/Subscriber.hs | 101 +++++++++--------- src/Simplex/Chat/Store/Connections.hs | 4 +- src/Simplex/Chat/Store/Groups.hs | 56 +++++----- src/Simplex/Chat/Store/Messages.hs | 8 +- .../Migrations/M20251018_chat_relays.hs | 4 +- .../Migrations/M20251018_chat_relays.hs | 6 +- .../SQLite/Migrations/chat_query_plans.txt | 34 +++--- .../Store/SQLite/Migrations/chat_schema.sql | 2 +- src/Simplex/Chat/Store/Shared.hs | 8 +- src/Simplex/Chat/Types.hs | 10 +- src/Simplex/Chat/View.hs | 15 ++- 17 files changed, 133 insertions(+), 130 deletions(-) diff --git a/bots/api/TYPES.md b/bots/api/TYPES.md index a2de11aa94..6d277a44b3 100644 --- a/bots/api/TYPES.md +++ b/bots/api/TYPES.md @@ -2221,7 +2221,7 @@ Known: - createdAt: UTCTime - updatedAt: UTCTime - supportChat: [GroupSupportChat](#groupsupportchat)? -- isChatRelay: bool +- isRelay: bool - relayData: [GroupRelay](#grouprelay)? diff --git a/bots/src/API/Docs/Commands.hs b/bots/src/API/Docs/Commands.hs index cff0e093a7..85a70d7f0e 100644 --- a/bots/src/API/Docs/Commands.hs +++ b/bots/src/API/Docs/Commands.hs @@ -307,7 +307,6 @@ undocumentedCommands = "APIActivateChat", "APIAddGroupShortLink", "APIAddMyAddressShortLink", - "APIAddRelays", "APIArchiveReceivedReports", "APICallStatus", "APIChangeConnectionUser", @@ -326,7 +325,6 @@ undocumentedCommands = "APICreateChatItems", "APICreateChatTag", "APICreateMemberContact", - "APICreateRelayedGroupLink", "APISendMemberContactInvitation", "APIAcceptMemberContact", "APIDeleteChatTag", diff --git a/bots/src/API/Docs/Events.hs b/bots/src/API/Docs/Events.hs index 4a49551a54..8c8d3ade20 100644 --- a/bots/src/API/Docs/Events.hs +++ b/bots/src/API/Docs/Events.hs @@ -187,6 +187,7 @@ undocumentedEvents = "CEvtRcvFileAcceptedSndCancelled", -- only sent with legacy SMP files when they are cancelled "CEvtRcvFileProgressXFTP", "CEvtRcvStandaloneFileComplete", + "CEvtRelayJoined", "CEvtRemoteCtrlFound", "CEvtRemoteCtrlSessionCode", "CEvtRemoteCtrlStopped", diff --git a/packages/simplex-chat-client/types/typescript/src/types.ts b/packages/simplex-chat-client/types/typescript/src/types.ts index 0246d3e736..41a0641746 100644 --- a/packages/simplex-chat-client/types/typescript/src/types.ts +++ b/packages/simplex-chat-client/types/typescript/src/types.ts @@ -2511,7 +2511,7 @@ export interface GroupMember { createdAt: string // ISO-8601 timestamp updatedAt: string // ISO-8601 timestamp supportChat?: GroupSupportChat - isChatRelay: boolean + isRelay: boolean relayData?: GroupRelay } diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 01d2dcd219..0a783ee76c 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -800,7 +800,6 @@ data ChatEvent | CEvtChatItemDeletedNotFound {user :: User, contact :: Contact, sharedMsgId :: SharedMsgId} | CEvtUserAcceptedGroupSent {user :: User, groupInfo :: GroupInfo, hostContact :: Maybe Contact} -- there is the same command response | CEvtGroupLinkConnecting {user :: User, groupInfo :: GroupInfo, hostMember :: GroupMember} - | CEvtRelayAddedToLink {user :: User, groupInfo :: GroupInfo, relayMember :: GroupMember, groupLink :: GroupLink, groupRelays :: [GroupRelay]} | CEvtBusinessLinkConnecting {user :: User, groupInfo :: GroupInfo, hostMember :: GroupMember, fromContact :: Contact} | CEvtSentGroupInvitation {user :: User, groupInfo :: GroupInfo, contact :: Contact, member :: GroupMember} -- there is the same command response | CEvtContactUpdated {user :: User, fromContact :: Contact, toContact :: Contact} @@ -842,6 +841,7 @@ data ChatEvent | CEvtHostDisconnected {protocol :: AProtocolType, transportHost :: TransportHost} | CEvtReceivedGroupInvitation {user :: User, groupInfo :: GroupInfo, contact :: Contact, fromMemberRole :: GroupMemberRole, memberRole :: GroupMemberRole} | CEvtUserJoinedGroup {user :: User, groupInfo :: GroupInfo, hostMember :: GroupMember} + | CEvtRelayJoined {user :: User, groupInfo :: GroupInfo, relayMember :: GroupMember, groupLink :: GroupLink, groupRelays :: [GroupRelay]} | CEvtJoinedGroupMember {user :: User, groupInfo :: GroupInfo, member :: GroupMember} -- there is the same command response | CEvtJoinedGroupMemberConnecting {user :: User, groupInfo :: GroupInfo, hostMember :: GroupMember, member :: GroupMember} | CEvtMemberAcceptedByOther {user :: User, groupInfo :: GroupInfo, acceptingMember :: GroupMember, member :: GroupMember} diff --git a/src/Simplex/Chat/Library/Internal.hs b/src/Simplex/Chat/Library/Internal.hs index 8ff7ecce90..72fdb5d3f2 100644 --- a/src/Simplex/Chat/Library/Internal.hs +++ b/src/Simplex/Chat/Library/Internal.hs @@ -1501,7 +1501,7 @@ getChatScopeInfo vr user = \case getGroupRecipients :: VersionRangeChat -> User -> GroupInfo -> Maybe GroupChatScopeInfo -> VersionChat -> CM [GroupMember] getGroupRecipients vr user gInfo@GroupInfo {membership} scopeInfo modsCompatVersion - | useRelays' gInfo && not (isMemberRelay membership) = do + | useRelays' gInfo && not (isRelay' membership) = do unless (memberCurrent membership && memberActive membership) $ throwChatError $ CECommandError "not current member" withFastStore' $ \db -> getGroupRelayMembers db vr user gInfo | otherwise = case scopeInfo of @@ -2043,9 +2043,9 @@ memberSendAction gInfo@GroupInfo {membership} events members m@GroupMember {memb | useRelays' gInfo = if -- if user is chat relay, send to all non chat relay members - | isMemberRelay membership && not (isMemberRelay m) -> MSASendBatched . snd <$> readyMemberConn m + | isRelay' membership && not (isRelay' m) -> MSASendBatched . snd <$> readyMemberConn m -- if user is not chat relay, send only to chat relays - | not (isMemberRelay membership) && isMemberRelay m -> MSASendBatched . snd <$> readyMemberConn m + | not (isRelay' membership) && isRelay' m -> MSASendBatched . snd <$> readyMemberConn m | otherwise -> Nothing -- TODO [channels fwd] MSAForwarded to create GSSForwarded snd statuses? | otherwise = case memberConn m of Nothing -> pendingOrForwarded diff --git a/src/Simplex/Chat/Library/Subscriber.hs b/src/Simplex/Chat/Library/Subscriber.hs index 3734872e3f..c2fd6db031 100644 --- a/src/Simplex/Chat/Library/Subscriber.hs +++ b/src/Simplex/Chat/Library/Subscriber.hs @@ -730,26 +730,15 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = | otherwise -> messageError "x.grp.acpt: memberId is different from expected" -- TODO [relays] owner: XGrpRelayAcpt processing branch -- TODO - TBC relay category - -- TODO - set relay status RSActive on CON? XGrpRelayAcpt relayLink | memberRole' membership == GROwner -> case relayData m of Just relay -> do -- TODO [relays] owner: check current relay status? - relay' <- withStore' $ \db -> do + withStore' $ \db -> do updateGroupMemberStatus db userId m GSMemAccepted - setRelayLinkAccepted db relay relayLink - let m' = m {relayData = Just relay', memberStatus = GSMemAccepted} - -- TODO [relays] owner: set data on continuation / on CON? + void $ setRelayLinkAccepted db relay relayLink allowAgentConnectionAsync user conn' confId XOk - -- TODO [relays] owner: agent async setConnShortLink api - setGroupLinkData' NRMBackground user gInfo >>= \case - Just gLink -> do - (relay'', relays) <- withStore' $ \db -> - (,) <$> updateRelayStatusFromTo db relay' RSAccepted RSActive <*> getGroupRelays db gInfo - let m'' = m' {relayData = Just relay''} - toView $ CEvtRelayAddedToLink user gInfo m'' gLink relays - Nothing -> messageError "x.grp.relay.acpt: group link not updated" Nothing -> messageError "x.grp.relay.acpt: member is not saved as relay" | otherwise -> messageError "x.grp.relay.acpt: only owner can add relay" _ -> messageError "CONF from invited member must have x.grp.acpt" @@ -823,40 +812,55 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = memberConnectedChatItem gInfo'' scopeInfo m'' let welcomeMsgId_ = (\PreparedGroup {welcomeSharedMsgId = mId} -> mId) <$> prepared unless (memberPending membership || isJust welcomeMsgId_) $ maybeCreateGroupDescrLocal gInfo'' m'' - GCInviteeMember -> do - -- TODO [relays] relay: don't introduce new member to other members - (gInfo', mStatus) <- - if not (memberPending m) - then do - mStatus <- withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected $> GSMemConnected - pure (gInfo, mStatus) - else do - gInfo' <- withStore' $ \db -> increaseGroupMembersRequireAttention db user gInfo - pure (gInfo', memberStatus m) - (gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo' m - memberConnectedChatItem gInfo'' scopeInfo m' - case scopeInfo of - Just (GCSIMemberSupport _) -> do - createInternalChatItem user (CDGroupRcv gInfo'' scopeInfo m') (CIRcvGroupEvent RGENewMemberPendingReview) Nothing - _ -> pure () - toView $ CEvtJoinedGroupMember user gInfo'' m' {memberStatus = mStatus} - let Connection {viaUserContactLink} = conn - when (isJust viaUserContactLink && isNothing (memberContactId m')) $ sendXGrpLinkMem gInfo'' - when (connChatVersion < batchSend2Version) $ getAutoReplyMsg >>= mapM_ (\mc -> sendGroupAutoReply mc Nothing) - case mStatus of - GSMemPendingApproval -> pure () - GSMemPendingReview -> introduceToModerators vr user gInfo'' m' - _ -> do - introduceToAll vr user gInfo'' m' - let memberIsCustomer = case businessChat gInfo'' of - Just BusinessChatInfo {chatType = BCCustomer, customerId} -> memberId' m' == customerId - _ -> False - when (groupFeatureAllowed SGFHistory gInfo'' && not memberIsCustomer) $ sendHistory user gInfo'' m' - where - sendXGrpLinkMem gInfo'' = do - let incognitoProfile = ExistingIncognito <$> incognitoMembershipProfile gInfo'' - profileToSend = userProfileInGroup user gInfo (fromIncognitoProfile <$> incognitoProfile) - void $ sendDirectMemberMessage conn (XGrpLinkMem profileToSend) groupId + GCInviteeMember -> + case relayData m of + -- TODO [relays] owner: relay CON processing branch + -- TODO - TBC relay category + Just relay -> do + -- TODO [relays] owner: agent async setConnShortLink api + withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected + let m' = m {memberStatus = GSMemConnected} + setGroupLinkData' NRMBackground user gInfo >>= \case + Just gLink -> do + (relay', relays) <- withStore' $ \db -> + (,) <$> updateRelayStatus db relay RSActive <*> getGroupRelays db gInfo + let m'' = m' {relayData = Just relay'} + -- TODO [relays] owner: relay added chat item? + toView $ CEvtRelayJoined user gInfo m'' gLink relays + Nothing -> messageError "x.grp.relay.acpt: group link not updated" + Nothing -> do -- TODO [relays] relay: don't introduce new member to other members + (gInfo', mStatus) <- + if not (memberPending m) + then do + mStatus <- withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected $> GSMemConnected + pure (gInfo, mStatus) + else do + gInfo' <- withStore' $ \db -> increaseGroupMembersRequireAttention db user gInfo + pure (gInfo', memberStatus m) + (gInfo'', m', scopeInfo) <- mkGroupChatScope gInfo' m + memberConnectedChatItem gInfo'' scopeInfo m' + case scopeInfo of + Just (GCSIMemberSupport _) -> do + createInternalChatItem user (CDGroupRcv gInfo'' scopeInfo m') (CIRcvGroupEvent RGENewMemberPendingReview) Nothing + _ -> pure () + toView $ CEvtJoinedGroupMember user gInfo'' m' {memberStatus = mStatus} + let Connection {viaUserContactLink} = conn + when (isJust viaUserContactLink && isNothing (memberContactId m')) $ sendXGrpLinkMem gInfo'' + when (connChatVersion < batchSend2Version) $ getAutoReplyMsg >>= mapM_ (\mc -> sendGroupAutoReply mc Nothing) + case mStatus of + GSMemPendingApproval -> pure () + GSMemPendingReview -> introduceToModerators vr user gInfo'' m' + _ -> do + introduceToAll vr user gInfo'' m' + let memberIsCustomer = case businessChat gInfo'' of + Just BusinessChatInfo {chatType = BCCustomer, customerId} -> memberId' m' == customerId + _ -> False + when (groupFeatureAllowed SGFHistory gInfo'' && not memberIsCustomer) $ sendHistory user gInfo'' m' + where + sendXGrpLinkMem gInfo'' = do + let incognitoProfile = ExistingIncognito <$> incognitoMembershipProfile gInfo'' + profileToSend = userProfileInGroup user gInfo (fromIncognitoProfile <$> incognitoProfile) + void $ sendDirectMemberMessage conn (XGrpLinkMem profileToSend) groupId _ -> do unless (memberPending m) $ withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected notifyMemberConnected gInfo m Nothing @@ -2875,10 +2879,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = event = XGrpMsgForward memberId memberName chatMsg brokerTs sendGroupMemberMessage gInfo member event Nothing (pure ()) - -- TODO [channels fwd] base on differentiation between groups and channels isUserGrpFwdRelay :: GroupInfo -> Bool isUserGrpFwdRelay gInfo@GroupInfo {membership = membership@GroupMember {memberRole}} - | useRelays' gInfo = isMemberRelay membership + | useRelays' gInfo = isRelay' membership | otherwise = memberRole >= GRAdmin xGrpLeave :: GroupInfo -> GroupMember -> RcvMessage -> UTCTime -> CM (Maybe DeliveryJobScope) diff --git a/src/Simplex/Chat/Store/Connections.hs b/src/Simplex/Chat/Store/Connections.hs index 08dfd4645d..6c7424dc71 100644 --- a/src/Simplex/Chat/Store/Connections.hs +++ b/src/Simplex/Chat/Store/Connections.hs @@ -149,13 +149,13 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, - mu.is_chat_relay, NULL, NULL, NULL, NULL, + mu.is_relay, NULL, NULL, NULL, NULL, -- from GroupMember m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link FROM group_members m JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id) LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs index ddcc08249d..84fdb452c4 100644 --- a/src/Simplex/Chat/Store/Groups.hs +++ b/src/Simplex/Chat/Store/Groups.hs @@ -79,6 +79,7 @@ module Simplex.Chat.Store.Groups getGroupRelays, createRelayMemberRecord, createRelayConnection, + updateRelayStatus, updateRelayStatusFromTo, setRelayLinkAccepted, createGroupRelayInvitation, @@ -204,8 +205,8 @@ import Database.SQLite.Simple.QQ (sql) type MaybeGroupMemberRow = (Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. (Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, Maybe LocalAlias, Maybe Preferences) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime) :. (Maybe BoolInt, Maybe Int64, Maybe Int64, Maybe RelayStatus, Maybe ShortLinkContact) toMaybeGroupMember :: Int64 -> MaybeGroupMemberRow -> Maybe GroupMember -toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. (Just profileId, Just displayName, Just fullName, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs) :. (Just isChatRelay, groupRelayId, chatRelayId, relayStatus, relayLink)) = - Just $ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs) :. (isChatRelay, groupRelayId, chatRelayId, relayStatus, relayLink)) +toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. (Just profileId, Just displayName, Just fullName, shortDescr, image, contactLink, peerType, Just localAlias, contactPreferences) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs) :. (Just isRelay, groupRelayId, chatRelayId, relayStatus, relayLink)) = + Just $ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked') :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. (profileId, displayName, fullName, shortDescr, image, contactLink, peerType, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs) :. (isRelay, groupRelayId, chatRelayId, relayStatus, relayLink)) toMaybeGroupMember _ _ = Nothing createGroupLink :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink @@ -344,7 +345,7 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile useRe (BI useRelays, ldn, userId, profileId, BI True, currentTs, currentTs, currentTs, currentTs) insertedRowId db memberId <- liftIO $ encodedRandomBytes gVar 12 - membership <- createContactMemberInv_ db user groupId Nothing user (MemberIdRole (MemberId memberId) GROwner) GCUserMember GSMemCreator IBUser customUserProfileId currentTs vr + membership <- createContactMemberInv_ db user groupId Nothing user (MemberIdRole (MemberId memberId) GROwner) GCUserMember GSMemCreator IBUser customUserProfileId False currentTs vr let chatSettings = ChatSettings {enableNtfs = MFAll, sendRcpts = Nothing, favorite = False} pure GroupInfo @@ -419,8 +420,8 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ ((profileId, localDisplayName, connRequest, userId, BI True, currentTs, currentTs, currentTs, currentTs) :. businessChatInfoRow business) insertedRowId db let hostVRange = adjustedMemberVRange vr peerChatVRange - GroupMember {groupMemberId} <- createContactMemberInv_ db user groupId Nothing contact fromMember GCHostMember GSMemInvited IBUnknown Nothing currentTs hostVRange - membership <- createContactMemberInv_ db user groupId (Just groupMemberId) user invitedMember GCUserMember GSMemInvited (IBContact contactId) incognitoProfileId currentTs vr + GroupMember {groupMemberId} <- createContactMemberInv_ db user groupId Nothing contact fromMember GCHostMember GSMemInvited IBUnknown Nothing False currentTs hostVRange + membership <- createContactMemberInv_ db user groupId (Just groupMemberId) user invitedMember GCUserMember GSMemInvited (IBContact contactId) incognitoProfileId False currentTs vr let chatSettings = ChatSettings {enableNtfs = MFAll, sendRcpts = Nothing, favorite = False} pure ( GroupInfo @@ -465,8 +466,8 @@ getHostMemberId_ db User {userId} groupId = ExceptT . firstRow fromOnly (SEHostMemberIdNotFound groupId) $ DB.query db "SELECT group_member_id FROM group_members WHERE user_id = ? AND group_id = ? AND member_category = ?" (userId, groupId, GCHostMember) -createContactMemberInv_ :: IsContact a => DB.Connection -> User -> GroupId -> Maybe GroupMemberId -> a -> MemberIdRole -> GroupMemberCategory -> GroupMemberStatus -> InvitedBy -> Maybe ProfileId -> UTCTime -> VersionRangeChat -> ExceptT StoreError IO GroupMember -createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMemberId userOrContact MemberIdRole {memberId, memberRole} memberCategory memberStatus invitedBy incognitoProfileId createdAt vr = do +createContactMemberInv_ :: IsContact a => DB.Connection -> User -> GroupId -> Maybe GroupMemberId -> a -> MemberIdRole -> GroupMemberCategory -> GroupMemberStatus -> InvitedBy -> Maybe ProfileId -> Bool -> UTCTime -> VersionRangeChat -> ExceptT StoreError IO GroupMember +createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMemberId userOrContact MemberIdRole {memberId, memberRole} memberCategory memberStatus invitedBy incognitoProfileId isRelay createdAt vr = do incognitoProfile <- forM incognitoProfileId $ \profileId -> getProfileById db userId profileId (localDisplayName, memberProfile) <- case (incognitoProfile, incognitoProfileId) of (Just profile@LocalProfile {displayName}, Just profileId) -> @@ -494,7 +495,7 @@ createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMe createdAt, updatedAt = createdAt, supportChat = Nothing, - isChatRelay = BoolDef False, + isRelay = BoolDef isRelay, relayData = Nothing } where @@ -508,12 +509,12 @@ createContactMemberInv_ db User {userId, userContactId} groupId invitedByGroupMe INSERT INTO group_members ( group_id, member_id, member_role, member_category, member_status, invited_by, invited_by_group_member_id, user_id, local_display_name, contact_id, contact_profile_id, created_at, updated_at, - peer_chat_min_version, peer_chat_max_version) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) + peer_chat_min_version, peer_chat_max_version, is_relay) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) |] ( (groupId, memberId, memberRole, memberCategory, memberStatus, fromInvitedBy userContactId invitedBy, invitedByGroupMemberId) :. (userId, localDisplayName' userOrContact, contactId' userOrContact, localProfileId $ profile' userOrContact, createdAt, createdAt) - :. (minV, maxV) + :. (minV, maxV, BI isRelay) ) pure localDisplayName insertMemberIncognitoProfile_ :: ContactName -> ProfileId -> ExceptT StoreError IO ContactName @@ -547,7 +548,7 @@ createPreparedGroup db vr user@User {userId, userContactId} groupProfile busines (groupId, groupLDN) <- createGroup_ db userId groupProfile prepared Nothing Nothing currentTs hostMemberId <- insertHost_ currentTs groupId groupLDN let userMember = MemberIdRole (MemberId $ encodeUtf8 groupLDN <> "_user_unknown_id") GRMember - membership <- createContactMemberInv_ db user groupId (Just hostMemberId) user userMember GCUserMember GSMemUnknown IBUnknown Nothing currentTs vr + membership <- createContactMemberInv_ db user groupId (Just hostMemberId) user userMember GCUserMember GSMemUnknown IBUnknown Nothing False currentTs vr hostMember <- getGroupMember db vr user groupId hostMemberId when business $ liftIO $ setGroupBusinessChatInfo groupId membership hostMember g <- getGroupInfo db vr user groupId @@ -745,7 +746,7 @@ createGroupViaLink' hostMemberId <- insertHost_ currentTs groupId liftIO $ DB.execute db "UPDATE connections SET conn_type = ?, group_member_id = ?, updated_at = ? WHERE connection_id = ?" (ConnMember, hostMemberId, currentTs, connId) -- using IBUnknown since host is created without contact - void $ createContactMemberInv_ db user groupId (Just hostMemberId) user invitedMember GCUserMember membershipStatus IBUnknown customUserProfileId currentTs vr + void $ createContactMemberInv_ db user groupId (Just hostMemberId) user invitedMember GCUserMember membershipStatus IBUnknown customUserProfileId False currentTs vr liftIO $ setViaGroupLinkUri db groupId connId (,) <$> getGroupInfo db vr user groupId <*> getGroupMemberById db vr user hostMemberId where @@ -1023,14 +1024,13 @@ getGroupModerators db vr user@User {userId, userContactId} GroupInfo {groupId} = (groupMemberQuery <> " WHERE m.user_id = ? AND m.group_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?) AND m.member_role IN (?,?,?)") (userId, groupId, userContactId, GRModerator, GRAdmin, GROwner) --- TODO [channels fwd] retrieve relays based on knowledge about member from protocol, not role (isMemberRelay) getGroupRelayMembers :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> IO [GroupMember] getGroupRelayMembers db vr user@User {userId, userContactId} GroupInfo {groupId} = do map (toContactMember vr user) <$> DB.query db - (groupMemberQuery <> " WHERE m.user_id = ? AND m.group_id = ? AND m.contact_id IS DISTINCT FROM ? AND m.member_role = ?") - (userId, groupId, userContactId, GRAdmin) + (groupMemberQuery <> " WHERE m.user_id = ? AND m.group_id = ? AND m.contact_id IS DISTINCT FROM ? AND m.is_relay = 1") + (userId, groupId, userContactId) getGroupMembersForExpiration :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> IO [GroupMember] getGroupMembersForExpiration db vr user@User {userId, userContactId} GroupInfo {groupId} = do @@ -1114,7 +1114,7 @@ createNewContactMember db gVar User {userId, userContactId} GroupInfo {groupId, createdAt, updatedAt = createdAt, supportChat = Nothing, - isChatRelay = BoolDef False, + isRelay = BoolDef False, relayData = Nothing } where @@ -1191,7 +1191,7 @@ createRelayMemberRecord db vr gVar user@User {userId, userContactId} GroupInfo { INSERT INTO group_members ( group_id, member_id, member_role, member_category, member_status, invited_by, invited_by_group_member_id, user_id, local_display_name, contact_profile_id, created_at, updated_at, - is_chat_relay, group_relay_id + is_relay, group_relay_id ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?) |] @@ -1222,6 +1222,10 @@ createRelayConnection db vr user@User {userId} groupMemberId agentConnId connSta connId <- liftIO $ insertedRowId db getConnectionById db vr user connId +updateRelayStatus :: DB.Connection -> GroupRelay -> RelayStatus -> IO GroupRelay +updateRelayStatus db relay@GroupRelay {groupRelayId} relayStatus = + updateRelayStatus_ db groupRelayId relayStatus $> relay {relayStatus} + updateRelayStatusFromTo :: DB.Connection -> GroupRelay -> RelayStatus -> RelayStatus -> IO GroupRelay updateRelayStatusFromTo db relay@GroupRelay {groupRelayId} fromStatus toStatus = do maybeFirstRow fromOnly (DB.query db "SELECT relay_status FROM group_relays WHERE group_relay_id = ?" (Only groupRelayId)) >>= \case @@ -1251,7 +1255,7 @@ createGroupRelayInvitation db vr user@User {userId} groupProfile GroupRelayInvit currentTs <- liftIO getCurrentTime (groupId, _groupLDN) <- createGroup_ db userId groupProfile Nothing Nothing (Just RSInvited) currentTs ownerMemberId <- insertOwner_ currentTs groupId - _membership <- createContactMemberInv_ db user groupId (Just ownerMemberId) user invitedMember GCUserMember GSMemAccepted IBUnknown Nothing currentTs vr + _membership <- createContactMemberInv_ db user groupId (Just ownerMemberId) user invitedMember GCUserMember GSMemAccepted IBUnknown Nothing True currentTs vr ownerMember <- getGroupMember db vr user groupId ownerMemberId g <- getGroupInfo db vr user groupId pure (g, ownerMember) @@ -1407,7 +1411,7 @@ createBusinessRequestGroup (groupProfileId, ldn, userId, BI True, currentTs, currentTs, currentTs, currentTs, BCCustomer) groupId <- liftIO $ insertedRowId db memberId <- liftIO $ encodedRandomBytes gVar 12 - membership <- createContactMemberInv_ db user groupId Nothing user (MemberIdRole (MemberId memberId) GROwner) GCUserMember GSMemCreator IBUser Nothing currentTs vr + membership <- createContactMemberInv_ db user groupId Nothing user (MemberIdRole (MemberId memberId) GROwner) GCUserMember GSMemCreator IBUser Nothing False currentTs vr pure (groupId, membership) VersionRange minV maxV = cReqChatVRange insertClientMember_ currentTs groupId membership = ExceptT $ do @@ -1584,7 +1588,7 @@ createNewGroupMember db user gInfo invitingMember memInfo@MemberInfo {profile} m localDisplayName, memContactId = Nothing, memProfileId, - isChatRelay = False + isRelay = False } liftIO $ createNewMember_ db user gInfo newMember currentTs @@ -1613,7 +1617,7 @@ createNewMember_ localDisplayName, memContactId = memberContactId, memProfileId = memberContactProfileId, - isChatRelay + isRelay } createdAt = do let invitedById = fromInvitedBy userContactId invitedBy @@ -1625,12 +1629,12 @@ createNewMember_ INSERT INTO group_members (group_id, member_id, member_role, member_category, member_status, member_restriction, invited_by, invited_by_group_member_id, user_id, local_display_name, contact_id, contact_profile_id, created_at, updated_at, - peer_chat_min_version, peer_chat_max_version, is_chat_relay) + peer_chat_min_version, peer_chat_max_version, is_relay) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) |] ( (groupId, memberId, memberRole, memberCategory, memberStatus, memRestriction, invitedById, memInvitedByGroupMemberId) :. (userId, localDisplayName, memberContactId, memberContactProfileId, createdAt, createdAt) - :. (minV, maxV, BI isChatRelay) + :. (minV, maxV, BI isRelay) ) groupMemberId <- insertedRowId db pure @@ -1654,7 +1658,7 @@ createNewMember_ createdAt, updatedAt = createdAt, supportChat = Nothing, - isChatRelay = BoolDef isChatRelay, + isRelay = BoolDef isRelay, relayData = Nothing } @@ -1875,7 +1879,7 @@ createIntroReMember memRestriction = restriction <$> memRestrictions_ currentTs <- liftIO getCurrentTime (localDisplayName, memProfileId) <- createNewMemberProfile_ db user memberProfile currentTs - let newMember = NewGroupMember {memInfo, memCategory = GCPreMember, memStatus = GSMemIntroduced, memRestriction, memInvitedBy = IBUnknown, memInvitedByGroupMemberId = Nothing, localDisplayName, memContactId = Nothing, memProfileId, isChatRelay = False} + let newMember = NewGroupMember {memInfo, memCategory = GCPreMember, memStatus = GSMemIntroduced, memRestriction, memInvitedBy = IBUnknown, memInvitedByGroupMemberId = Nothing, localDisplayName, memContactId = Nothing, memProfileId, isRelay = False} liftIO $ do member <- createNewMember_ db user gInfo newMember currentTs conn@Connection {connId = groupConnId} <- createMemberConnection_ db userId (groupMemberId' member) groupAgentConnId chatV mcvr memberContactId cLevel currentTs subMode diff --git a/src/Simplex/Chat/Store/Messages.hs b/src/Simplex/Chat/Store/Messages.hs index 62541773d4..2f7828e493 100644 --- a/src/Simplex/Chat/Store/Messages.hs +++ b/src/Simplex/Chat/Store/Messages.hs @@ -679,7 +679,7 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link FROM group_members m JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id) LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id @@ -3005,7 +3005,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, -- quoted ChatItem ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent, -- quoted GroupMember @@ -3014,14 +3014,14 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences, rm.created_at, rm.updated_at, rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, - rm.is_chat_relay, rr.group_relay_id, rr.chat_relay_id, rr.relay_status, rr.relay_link, + rm.is_relay, rr.group_relay_id, rr.chat_relay_id, rr.relay_status, rr.relay_link, -- deleted by GroupMember dbm.group_member_id, dbm.group_id, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category, dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id, dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences, dbm.created_at, dbm.updated_at, dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, - dbm.is_chat_relay, dbr.group_relay_id, dbr.chat_relay_id, dbr.relay_status, dbr.relay_link + dbm.is_relay, dbr.group_relay_id, dbr.chat_relay_id, dbr.relay_status, dbr.relay_link FROM chat_items i LEFT JOIN files f ON f.chat_item_id = i.chat_item_id LEFT JOIN group_members m ON m.group_member_id = i.group_member_id diff --git a/src/Simplex/Chat/Store/Postgres/Migrations/M20251018_chat_relays.hs b/src/Simplex/Chat/Store/Postgres/Migrations/M20251018_chat_relays.hs index efd43a3a3c..542dde5707 100644 --- a/src/Simplex/Chat/Store/Postgres/Migrations/M20251018_chat_relays.hs +++ b/src/Simplex/Chat/Store/Postgres/Migrations/M20251018_chat_relays.hs @@ -29,14 +29,14 @@ CREATE INDEX idx_chat_relays_user_id ON chat_relays(user_id); ALTER TABLE users ADD COLUMN is_user_chat_relay SMALLINT NOT NULL DEFAULT 0; -ALTER TABLE group_members ADD COLUMN is_chat_relay SMALLINT NOT NULL DEFAULT 0; +ALTER TABLE group_members ADD COLUMN is_relay SMALLINT NOT NULL DEFAULT 0; |] down_m20251018_chat_relays :: Text down_m20251018_chat_relays = T.pack [r| -ALTER TABLE group_members DROP COLUMN is_chat_relay; +ALTER TABLE group_members DROP COLUMN is_relay; ALTER TABLE users DROP COLUMN is_user_chat_relay; diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/M20251018_chat_relays.hs b/src/Simplex/Chat/Store/SQLite/Migrations/M20251018_chat_relays.hs index 17ec38ace1..34356c0092 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/M20251018_chat_relays.hs +++ b/src/Simplex/Chat/Store/SQLite/Migrations/M20251018_chat_relays.hs @@ -14,7 +14,7 @@ import Database.SQLite.Simple.QQ (sql) -- - group_relays.chat_relay_id - associates group_relays record with a chat_relays record, -- chat_relays.deleted is to keep associated record if user removes chat relay from configuration, -- but has group relays using it --- - group_members.is_chat_relay - indicates that the member is a chat relay (to all group members) +-- - group_members.is_relay - indicates that the member is a chat relay (to all group members) -- - group_members.group_relay_id - associates group_members record with a group_relays record for a group owner; -- receiving event to member connection, owner can match it to the relay; -- TBC inverse association - from group_relays to group_members? @@ -60,7 +60,7 @@ CREATE TABLE group_relays( CREATE INDEX idx_group_relays_group_id ON group_relays(group_id); CREATE INDEX idx_group_relays_chat_relay_id ON group_relays(chat_relay_id); -ALTER TABLE group_members ADD COLUMN is_chat_relay INTEGER NOT NULL DEFAULT 0; +ALTER TABLE group_members ADD COLUMN is_relay INTEGER NOT NULL DEFAULT 0; ALTER TABLE group_members ADD COLUMN group_relay_id INTEGER REFERENCES group_relays ON DELETE SET NULL; CREATE INDEX idx_group_members_group_relay_id ON group_members(group_relay_id); @@ -84,7 +84,7 @@ DROP INDEX idx_group_relays_group_id; DROP INDEX idx_group_relays_chat_relay_id; DROP TABLE group_relays; -ALTER TABLE group_members DROP COLUMN is_chat_relay; +ALTER TABLE group_members DROP COLUMN is_relay; DROP INDEX idx_group_members_group_relay_id; ALTER TABLE group_members DROP COLUMN group_relay_id; diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt index a04eb11be3..0a311fef38 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt @@ -120,13 +120,13 @@ Query: pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, - mu.is_chat_relay, NULL, NULL, NULL, NULL, + mu.is_relay, NULL, NULL, NULL, NULL, -- from GroupMember m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link FROM group_members m JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id) LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id @@ -854,7 +854,7 @@ Query: p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link FROM group_members m JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id) LEFT JOIN group_relays r ON r.group_relay_id = m.group_relay_id @@ -1086,7 +1086,7 @@ Query: p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, -- quoted ChatItem ri.chat_item_id, i.quoted_shared_msg_id, i.quoted_sent_at, i.quoted_content, i.quoted_sent, -- quoted GroupMember @@ -1095,14 +1095,14 @@ Query: rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.chat_peer_type, rp.local_alias, rp.preferences, rm.created_at, rm.updated_at, rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts, - rm.is_chat_relay, rr.group_relay_id, rr.chat_relay_id, rr.relay_status, rr.relay_link, + rm.is_relay, rr.group_relay_id, rr.chat_relay_id, rr.relay_status, rr.relay_link, -- deleted by GroupMember dbm.group_member_id, dbm.group_id, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category, dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id, dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.chat_peer_type, dbp.local_alias, dbp.preferences, dbm.created_at, dbm.updated_at, dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts, - dbm.is_chat_relay, dbr.group_relay_id, dbr.chat_relay_id, dbr.relay_status, dbr.relay_link + dbm.is_relay, dbr.group_relay_id, dbr.chat_relay_id, dbr.relay_status, dbr.relay_link FROM chat_items i LEFT JOIN files f ON f.chat_item_id = i.chat_item_id LEFT JOIN group_members m ON m.group_member_id = i.group_member_id @@ -1670,7 +1670,7 @@ Query: INSERT INTO group_members (group_id, member_id, member_role, member_category, member_status, member_restriction, invited_by, invited_by_group_member_id, user_id, local_display_name, contact_id, contact_profile_id, created_at, updated_at, - peer_chat_min_version, peer_chat_max_version, is_chat_relay) + peer_chat_min_version, peer_chat_max_version, is_relay) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) Plan: @@ -5007,7 +5007,7 @@ Query: pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, - mu.is_chat_relay, NULL, NULL, NULL, NULL + mu.is_relay, NULL, NULL, NULL, NULL FROM groups g JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id @@ -5043,7 +5043,7 @@ Query: pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, - mu.is_chat_relay, NULL, NULL, NULL, NULL + mu.is_relay, NULL, NULL, NULL, NULL FROM groups g JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id @@ -5072,7 +5072,7 @@ Query: pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, - mu.is_chat_relay, NULL, NULL, NULL, NULL + mu.is_relay, NULL, NULL, NULL, NULL FROM groups g JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id @@ -5121,7 +5121,7 @@ Query: m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -5151,7 +5151,7 @@ Query: m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -5173,7 +5173,7 @@ Query: m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -5195,7 +5195,7 @@ Query: m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -5217,7 +5217,7 @@ Query: m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -5239,7 +5239,7 @@ Query: m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -5261,7 +5261,7 @@ Query: m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql index f1611da074..052d506c81 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql @@ -199,7 +199,7 @@ CREATE TABLE group_members( support_chat_last_msg_from_member_ts TEXT, member_xcontact_id BLOB, member_welcome_shared_msg_id BLOB, - is_chat_relay INTEGER NOT NULL DEFAULT 0, + is_relay INTEGER NOT NULL DEFAULT 0, group_relay_id INTEGER REFERENCES group_relays ON DELETE SET NULL, FOREIGN KEY(user_id, local_display_name) REFERENCES display_names(user_id, local_display_name) diff --git a/src/Simplex/Chat/Store/Shared.hs b/src/Simplex/Chat/Store/Shared.hs index 05dddc61eb..291102cb78 100644 --- a/src/Simplex/Chat/Store/Shared.hs +++ b/src/Simplex/Chat/Store/Shared.hs @@ -680,7 +680,7 @@ toPreparedGroup = \case _ -> Nothing toGroupMember :: Int64 -> GroupMemberRow -> GroupMember -toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs) :. (BI isCRelay, groupRelayId_, chatRelayId_, relayStatus_, relayLink)) = +toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs) :. (BI isRel, groupRelayId_, chatRelayId_, relayStatus_, relayLink)) = let memberProfile = rowToLocalProfile profileRow memberSettings = GroupMemberSettings {showMessages} blockedByAdmin = maybe False mrsBlocked memberRestriction_ @@ -698,7 +698,7 @@ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, lastMsgFromMemberTs = supportChatLastMsgFromMemberTs } _ -> Nothing - isChatRelay = BoolDef isCRelay + isRelay = BoolDef isRel relayData = case (groupRelayId_, chatRelayId_, relayStatus_) of (Just groupRelayId, Just userChatRelayId, Just relayStatus) -> Just GroupRelay {groupRelayId, userChatRelayId, relayStatus, relayLink} _ -> Nothing @@ -712,7 +712,7 @@ groupMemberQuery = m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.chat_peer_type, p.local_alias, p.preferences, m.created_at, m.updated_at, m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts, - m.is_chat_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, + m.is_relay, r.group_relay_id, r.chat_relay_id, r.relay_status, r.relay_link, c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id, c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.user_contact_link_id, c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter, @@ -757,7 +757,7 @@ groupInfoQueryFields = pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.chat_peer_type, pu.local_alias, pu.preferences, mu.created_at, mu.updated_at, mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts, - mu.is_chat_relay, NULL, NULL, NULL, NULL + mu.is_relay, NULL, NULL, NULL, NULL |] groupInfoQueryFrom :: Query diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index d5377ff7cd..771425aa0d 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -962,7 +962,7 @@ data GroupMember = GroupMember createdAt :: UTCTime, updatedAt :: UTCTime, supportChat :: Maybe GroupSupportChat, - isChatRelay :: BoolDef, -- marker for all members that this member is a chat relay + isRelay :: BoolDef, -- marker for all members that this member is a chat relay relayData :: Maybe GroupRelay -- owner's additional data for a chat relay } deriving (Eq, Show) @@ -1032,10 +1032,8 @@ groupMemberRef :: GroupMember -> GroupMemberRef groupMemberRef GroupMember {groupMemberId, memberProfile = p} = GroupMemberRef {groupMemberId, profile = fromLocalProfile p} --- TODO [channels fwd] knowledge whether member is a relay should come from protocol, not implicitly via role --- TODO - in channels members should directly connect only to relays -isMemberRelay :: GroupMember -> Bool -isMemberRelay GroupMember {memberRole} = memberRole == GRAdmin +isRelay' :: GroupMember -> Bool +isRelay' GroupMember {isRelay} = isTrue isRelay memberConn :: GroupMember -> Maybe Connection memberConn GroupMember {activeConn} = activeConn @@ -1088,7 +1086,7 @@ data NewGroupMember = NewGroupMember localDisplayName :: ContactName, memProfileId :: Int64, memContactId :: Maybe Int64, - isChatRelay :: Bool + isRelay :: Bool } newtype MemberId = MemberId {unMemberId :: ByteString} diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index f6eb5e64bf..31c6076f7b 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -418,7 +418,6 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView} CEvtContactRequestAlreadyAccepted u c -> ttyUser u [ttyFullContact c <> ": sent you a duplicate contact request, but you are already connected, no action needed"] CEvtBusinessRequestAlreadyAccepted u g -> ttyUser u [ttyFullGroup g <> ": sent you a duplicate connection request, but you are already connected, no action needed"] CEvtGroupLinkConnecting u g _ -> ttyUser u [ttyGroup' g <> ": joining the group..."] - CEvtRelayAddedToLink u g relayMem groupLink relays -> ttyUser u $ viewRelayAddedToLink g relayMem groupLink relays CEvtBusinessLinkConnecting u g _ _ -> ttyUser u [ttyGroup' g <> ": joining the group..."] CEvtUnknownMemberCreated u g fwdM um -> ttyUser u [ttyGroup' g <> ": " <> ttyMember fwdM <> " forwarded a message from an unknown member, creating unknown member record " <> ttyMember um] CEvtUnknownMemberBlocked u g byM um -> ttyUser u [ttyGroup' g <> ": " <> ttyMember byM <> " blocked an unknown member, creating unknown member record " <> ttyMember um] @@ -461,6 +460,7 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView} CEvtSubscriptionStatus srv status conns -> [plain $ subStatusStr status <> " " <> show (length conns) <> " connections on server " <> showSMPServer srv] CEvtReceivedGroupInvitation {user = u, groupInfo = g, contact = c, memberRole = r} -> ttyUser u $ viewReceivedGroupInvitation g c r CEvtUserJoinedGroup u g _ -> ttyUser u $ viewUserJoinedGroup g + CEvtRelayJoined u g relayMem groupLink relays -> ttyUser u $ viewRelayJoined g relayMem groupLink relays CEvtJoinedGroupMember u g m -> ttyUser u $ viewJoinedGroupMember g m CEvtHostConnected p h -> [plain $ "connected to " <> viewHostEvent p h] CEvtHostDisconnected p h -> [plain $ "disconnected from " <> viewHostEvent p h] @@ -1148,9 +1148,9 @@ viewReceivedContactRequest c Profile {fullName, shortDescr} = "to reject: " <> highlight ("/rc " <> viewName c) <> " (the sender will NOT be notified)" ] -viewRelayAddedToLink :: GroupInfo -> GroupMember -> GroupLink -> [GroupRelay] -> [StyledString] -viewRelayAddedToLink g relayMem groupLink relays = - [ ttyFullGroup g <> ": relay " <> ttyMember relayMem <> " added to group link", +viewRelayJoined :: GroupInfo -> GroupMember -> GroupLink -> [GroupRelay] -> [StyledString] +viewRelayJoined g relayMem groupLink relays = + [ ttyFullGroup g <> ": relay " <> ttyMember relayMem <> " joined and added to group link", "current relays:" ] <> map showRelay relays @@ -1160,13 +1160,13 @@ viewRelayAddedToLink g relayMem groupLink relays = ] where showRelay GroupRelay {groupRelayId, relayStatus} = - " - relay id " <> sShow groupRelayId <> ": " <> sShow (relayStatusText relayStatus) + " - relay id " <> sShow groupRelayId <> ": " <> plain (relayStatusText relayStatus) GroupLink {connLinkContact = CCLink cReq shortLink} = groupLink cReqStr = strEncode $ simplexChatContact cReq -- TODO [relays] operator: CLI specific apis based on name viewGroupCreated :: GroupInfo -> Bool -> [StyledString] -viewGroupCreated g@GroupInfo {groupId} testView = +viewGroupCreated g testView = case incognitoMembershipProfile g of Just localProfile | testView -> incognitoProfile' profile : message @@ -1189,8 +1189,7 @@ viewGroupCreated g@GroupInfo {groupId} testView = | useRelays' g = relaysInstruction | otherwise = "to add members use " <> highlight ("/a " <> viewGroupName g <> " ") <> " or " <> highlight ("/create link #" <> viewGroupName g) where - relaysInstruction = - "to add members use " <> highlight ("/_create relayed link #" <> show groupId <> " auto_choose_relays=on/off") + relaysInstruction = "wait for selected relay(s) to join, then you can invite members via group link" viewCannotResendInvitation :: GroupInfo -> ContactName -> [StyledString] viewCannotResendInvitation g c =