core: update deleteGroupMember logic and its usages (no items & expiration) (#1258)

This commit is contained in:
JRoberts
2022-10-26 13:37:17 +04:00
committed by GitHub
parent a20f0050b9
commit 385ebd2298
3 changed files with 73 additions and 21 deletions

View File

@@ -537,6 +537,8 @@ processChatCommand = \case
maxItemTs_ <- withStore' $ \db -> getGroupMaxItemTs db user gInfo
forM_ filesInfo $ \fileInfo -> deleteFile user fileInfo
withStore' $ \db -> deleteGroupCIs db user gInfo
membersToDelete <- withStore' $ \db -> getGroupMembersForExpiration db user gInfo
forM_ membersToDelete $ \m -> withStore' $ \db -> deleteGroupMember db user m
gInfo' <- case maxItemTs_ of
Just ts -> do
withStore' $ \db -> updateGroupTs db user gInfo ts
@@ -905,7 +907,10 @@ processChatCommand = \case
ci <- saveSndChatItem user (CDGroupSnd gInfo) msg (CISndGroupEvent $ SGEMemberDeleted memberId (fromLocalProfile memberProfile)) Nothing Nothing
toView . CRNewChatItem $ AChatItem SCTGroup SMDSnd (GroupChat gInfo) ci
deleteMemberConnection user m
withStore' $ \db -> updateGroupMemberStatus db userId m GSMemRemoved
withStore' $ \db ->
checkGroupMemberHasItems db user m >>= \case
Just _ -> updateGroupMemberStatus db userId m GSMemRemoved
Nothing -> deleteGroupMember db user m
pure $ CRUserDeletedMember gInfo m {memberStatus = GSMemRemoved}
APILeaveGroup groupId -> withUser $ \user@User {userId} -> do
Group gInfo@GroupInfo {membership} members <- withStore $ \db -> getGroup db user groupId
@@ -1536,6 +1541,8 @@ expireChatItems user ttl sync = do
maxItemTs_ <- withStore' $ \db -> getGroupMaxItemTs db user gInfo
forM_ filesInfo $ \fileInfo -> deleteFile user fileInfo
withStore' $ \db -> deleteGroupExpiredCIs db user gInfo expirationDate createdAtCutoff
membersToDelete <- withStore' $ \db -> getGroupMembersForExpiration db user gInfo
forM_ membersToDelete $ \m -> withStore' $ \db -> deleteGroupMember db user m
withStore' $ \db -> do
ciCount_ <- getGroupCICount db user gInfo
case (maxItemTs_, ciCount_) of

View File

@@ -83,6 +83,7 @@ module Simplex.Chat.Store
getGroupInfoByName,
getGroupMember,
getGroupMembers,
getGroupMembersForExpiration,
deleteGroupConnectionsAndFiles,
deleteGroupItemsAndMembers,
deleteGroup,
@@ -98,6 +99,7 @@ module Simplex.Chat.Store
updateGroupMemberStatus,
updateGroupMemberStatusById,
createNewGroupMember,
checkGroupMemberHasItems,
deleteGroupMember,
deleteGroupMemberConnection,
updateGroupMemberRole,
@@ -1676,9 +1678,7 @@ deleteGroupItemsAndMembers :: DB.Connection -> User -> GroupInfo -> [GroupMember
deleteGroupItemsAndMembers db user@User {userId} GroupInfo {groupId} members = do
DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ?" (userId, groupId)
DB.execute db "DELETE FROM group_members WHERE user_id = ? AND group_id = ?" (userId, groupId)
forM_ members $ \m@GroupMember {groupMemberId, memberContactId, memberContactProfileId} -> unless (isJust memberContactId) $ do
sameProfileMember :: (Maybe GroupMemberId) <- maybeFirstRow fromOnly $ DB.query db "SELECT group_member_id FROM group_members WHERE user_id = ? AND contact_profile_id = ? AND group_member_id != ? LIMIT 1" (userId, memberContactProfileId, groupMemberId)
unless (isJust sameProfileMember) $ deleteMemberProfileAndName_ db user m
forM_ members $ \m -> cleanupMemberContactAndProfile_ db user m
deleteGroup :: DB.Connection -> User -> GroupInfo -> IO ()
deleteGroup db User {userId} GroupInfo {groupId, localDisplayName} = do
@@ -1779,6 +1779,32 @@ getGroupMembers db user@User {userId, userContactId} GroupInfo {groupId} = do
|]
(groupId, userId, userContactId)
getGroupMembersForExpiration :: DB.Connection -> User -> GroupInfo -> IO [GroupMember]
getGroupMembersForExpiration db user@User {userId, userContactId} GroupInfo {groupId} = do
map (toContactMember user)
<$> DB.query
db
[sql|
SELECT
m.group_member_id, m.group_id, m.member_id, m.member_role, m.member_category, m.member_status,
m.invited_by, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.local_alias,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id, c.created_at
FROM group_members m
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
LEFT JOIN connections c ON c.connection_id = (
SELECT max(cc.connection_id)
FROM connections cc
where cc.group_member_id = m.group_member_id
)
WHERE m.group_id = ? AND m.user_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?)
AND m.member_status IN (?, ?, ?)
AND m.group_member_id NOT IN (
SELECT DISTINCT group_member_id FROM chat_items
)
|]
(groupId, userId, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted)
toContactMember :: User -> (GroupMemberRow :. MaybeConnectionRow) -> GroupMember
toContactMember User {userContactId} (memberRow :. connRow) =
(toGroupMember userContactId memberRow) {activeConn = toMaybeConnection connRow}
@@ -1986,19 +2012,30 @@ createNewMember_
groupMemberId <- insertedRowId db
pure GroupMember {groupMemberId, groupId, memberId, memberRole, memberCategory, memberStatus, invitedBy, localDisplayName, memberProfile = toLocalProfile memberContactProfileId memberProfile "", memberContactId, memberContactProfileId, activeConn}
checkGroupMemberHasItems :: DB.Connection -> User -> GroupMember -> IO (Maybe ChatItemId)
checkGroupMemberHasItems db User {userId} GroupMember {groupMemberId, groupId} =
maybeFirstRow fromOnly $ DB.query db "SELECT chat_item_id FROM chat_items WHERE user_id = ? AND group_id = ? AND group_member_id = ? LIMIT 1" (userId, groupId, groupMemberId)
deleteGroupMember :: DB.Connection -> User -> GroupMember -> IO ()
deleteGroupMember db user@User {userId} m@GroupMember {groupMemberId, groupId, memberContactId, memberContactProfileId} = do
deleteGroupMember db user@User {userId} m@GroupMember {groupMemberId, groupId} = do
deleteGroupMemberConnection db user m
DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ? AND group_member_id = ?" (userId, groupId, groupMemberId)
DB.execute db "DELETE FROM group_members WHERE user_id = ? AND group_member_id = ?" (userId, groupMemberId)
unless (isJust memberContactId) $ do
sameProfileMember :: (Maybe GroupMemberId) <- maybeFirstRow fromOnly $ DB.query db "SELECT group_member_id FROM group_members WHERE user_id = ? AND contact_profile_id = ? AND group_member_id != ? LIMIT 1" (userId, memberContactProfileId, groupMemberId)
unless (isJust sameProfileMember) $ deleteMemberProfileAndName_ db user m
cleanupMemberContactAndProfile_ db user m
deleteMemberProfileAndName_ :: DB.Connection -> User -> GroupMember -> IO ()
deleteMemberProfileAndName_ db User {userId} GroupMember {memberContactProfileId, localDisplayName} = do
DB.execute db "DELETE FROM contact_profiles WHERE user_id = ? AND contact_profile_id = ?" (userId, memberContactProfileId)
DB.execute db "DELETE FROM display_names WHERE user_id = ? AND local_display_name = ?" (userId, localDisplayName)
cleanupMemberContactAndProfile_ :: DB.Connection -> User -> GroupMember -> IO ()
cleanupMemberContactAndProfile_ db User {userId} GroupMember {groupMemberId, localDisplayName, memberContactId, memberContactProfileId} =
case memberContactId of
Just contactId ->
runExceptT (getContact db userId contactId) >>= \case
Right ct@Contact {activeConn = Connection {connLevel, viaGroupLink}, contactUsed} ->
unless ((connLevel == 0 && not viaGroupLink) || contactUsed) $ deleteContact db userId ct
_ -> pure ()
Nothing -> do
sameProfileMember :: (Maybe GroupMemberId) <- maybeFirstRow fromOnly $ DB.query db "SELECT group_member_id FROM group_members WHERE user_id = ? AND contact_profile_id = ? AND group_member_id != ? LIMIT 1" (userId, memberContactProfileId, groupMemberId)
unless (isJust sameProfileMember) $ do
DB.execute db "DELETE FROM contact_profiles WHERE user_id = ? AND contact_profile_id = ?" (userId, memberContactProfileId)
DB.execute db "DELETE FROM display_names WHERE user_id = ? AND local_display_name = ?" (userId, localDisplayName)
deleteGroupMemberConnection :: DB.Connection -> User -> GroupMember -> IO ()
deleteGroupMemberConnection db User {userId} GroupMember {groupMemberId} =
@@ -3122,7 +3159,7 @@ getDirectChatPreviews_ db User {userId} = do
) ChatStats ON ChatStats.contact_id = ct.contact_id
LEFT JOIN chat_items ri ON i.quoted_shared_msg_id = ri.shared_msg_id
WHERE ct.user_id = ?
AND ((c.conn_level = 0 AND c.via_group_link = 0) OR i.chat_item_id IS NOT NULL OR ct.contact_used = 1)
AND ((c.conn_level = 0 AND c.via_group_link = 0) OR ct.contact_used = 1)
AND c.connection_id = (
SELECT cc_connection_id FROM (
SELECT

View File

@@ -768,7 +768,13 @@ testGroupDelete =
cath <## "#team: you deleted the group"
alice <##> bob
alice <##> cath
bob <##> cath
-- unused group contacts are deleted
bob ##> "@cath hi"
bob <## "no contact cath"
(cath </)
cath ##> "@bob hi"
cath <## "no contact bob"
(bob </)
testGroupSameName :: IO ()
testGroupSameName =
@@ -3557,8 +3563,10 @@ testGroupLinkDeleteInvitedMemberNoBrokenItem =
alice <## "#team: you removed bob from the group"
alice #$> ("/_get chat #1 count=100", chat, [])
alice @@@ [("#team", "")]
alice <##> bob
alice @@@ [("@bob", "hey"), ("#team", "")]
-- removing member deletes unused group contact
alice ##> "@bob hi"
alice <## "no contact bob"
(bob </)
bob ##> "/j team"
bob <## "error: connection authorization failed - this could happen if connection was deleted, secured with different credentials, or due to a bug - please re-create the connection"
-- repeat request is prohibited because of the re-used XContactId, until contact is deleted
@@ -3568,11 +3576,11 @@ testGroupLinkDeleteInvitedMemberNoBrokenItem =
bob <## "alice: contact is deleted"
bob ##> ("/c " <> gLink)
bob <## "connection request sent!"
alice <## "bob_1 (Bob): accepting request to join group #team..."
alice <## "bob (Bob): accepting request to join group #team..."
concurrentlyN_
[ do
alice <## "bob_1 (Bob): contact is connected"
alice <## "bob_1 invited to group #team via your group link",
alice <## "bob (Bob): contact is connected"
alice <## "bob invited to group #team via your group link",
do
bob <## "alice_1 (Alice): contact is connected"
bob <## "#team_1 (team): alice_1 invites you to join the group as member"
@@ -3580,12 +3588,12 @@ testGroupLinkDeleteInvitedMemberNoBrokenItem =
]
bob ##> "/j team_1"
concurrently_
(alice <## "#team: bob_1 joined the group")
(alice <## "#team: bob joined the group")
(bob <## "#team_1: you joined the group")
alice #> "#team hello"
bob <# "#team_1 alice_1> hello"
bob #> "#team_1 hi there"
alice <# "#team bob_1> hi there"
alice <# "#team bob> hi there"
withTestChatContactConnected :: String -> (TestCC -> IO a) -> IO a
withTestChatContactConnected dbPrefix action =