fix: leave group v2 as only admin mark it as deleted and pushes to swarm

before removing the wrapper data
pull/2963/head
Audric Ackermann 1 year ago
parent 95cd0e86f1
commit 0a4e3041de

@ -342,7 +342,7 @@
"leaveAndRemoveForEveryone": "Leave Group and Remove for Everyone", "leaveAndRemoveForEveryone": "Leave Group and Remove for Everyone",
"leaveGroupConfirmation": "Are you sure you want to leave <b>$name$</b>?", "leaveGroupConfirmation": "Are you sure you want to leave <b>$name$</b>?",
"leaveGroupConfirmationAdmin": "As you are the admin of this group, if you leave it it will be removed for every current members. Are you sure you want to leave this group?", "leaveGroupConfirmationAdmin": "As you are the admin of this group, if you leave it it will be removed for every current members. Are you sure you want to leave this group?",
"leaveGroupConrirmationOnlyAdminLegacy": "Because you are the creator of this group it will be deleted for everyone. This cannot be undone.", "leaveGroupConfirmationOnlyAdminLegacy": "Because you are the creator of this group it will be deleted for everyone. This cannot be undone.",
"leaveGroupConfirmationOnlyAdmin": "You are the only admin in <b>$name$</b>", "leaveGroupConfirmationOnlyAdmin": "You are the only admin in <b>$name$</b>",
"leaveGroupConfirmationOnlyAdminWarning": "Group settings and members cannot be changed without an admin", "leaveGroupConfirmationOnlyAdminWarning": "Group settings and members cannot be changed without an admin",
"leaveGroupFailed": "Failed to leave Group!", "leaveGroupFailed": "Failed to leave Group!",

@ -397,9 +397,9 @@ export async function showLeaveGroupByConvoId(conversationId: string, name: stri
const isClosedGroup = conversation.isClosedGroup() || false; const isClosedGroup = conversation.isClosedGroup() || false;
const isPublic = conversation.isPublic() || false; const isPublic = conversation.isPublic() || false;
const admins = conversation.get('groupAdmins') || []; const admins = conversation.getGroupAdmins();
const isAdmin = admins.includes(UserUtils.getOurPubKeyStrFromCache()); const isAdmin = admins.includes(UserUtils.getOurPubKeyStrFromCache());
const showOnlyGroupAdminWarning = isClosedGroup && isAdmin && admins.length === 1; const showOnlyGroupAdminWarning = isClosedGroup && isAdmin;
const lastMessageInteractionType = conversation.get('lastMessageInteractionType'); const lastMessageInteractionType = conversation.get('lastMessageInteractionType');
const lastMessageInteractionStatus = conversation.get('lastMessageInteractionStatus'); const lastMessageInteractionStatus = conversation.get('lastMessageInteractionStatus');
@ -432,7 +432,9 @@ export async function showLeaveGroupByConvoId(conversationId: string, name: stri
window?.inboxStore?.dispatch( window?.inboxStore?.dispatch(
updateConfirmModal({ updateConfirmModal({
title: window.i18n('leaveGroup'), title: window.i18n('leaveGroup'),
message: window.i18n('leaveGroupConrirmationOnlyAdminLegacy', name ? [name] : ['']), message: window.i18n('leaveGroupConfirmationOnlyAdminLegacy', [
name || window.i18n('unknown'),
]),
onClickOk, onClickOk,
okText: window.i18n('leave'), okText: window.i18n('leave'),
okTheme: SessionButtonColor.Danger, okTheme: SessionButtonColor.Danger,
@ -440,7 +442,7 @@ export async function showLeaveGroupByConvoId(conversationId: string, name: stri
conversationId, conversationId,
}) })
); );
// TODO Only to be used after the closed group rebuild // TODO AUDRIC this is chunk3 stuff: Only to be used after the closed group rebuild chunk3
// const onClickOkLastAdmin = () => { // const onClickOkLastAdmin = () => {
// /* TODO */ // /* TODO */
// }; // };

@ -1401,7 +1401,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
if (!pubKey) { if (!pubKey) {
throw new Error('isAdmin() pubKey is falsy'); throw new Error('isAdmin() pubKey is falsy');
} }
const groupAdmins = getLibGroupAdminsOutsideRedux(this.id) || this.getGroupAdmins(); const groupAdmins = this.getGroupAdmins();
return Array.isArray(groupAdmins) && groupAdmins.includes(pubKey); return Array.isArray(groupAdmins) && groupAdmins.includes(pubKey);
} }
@ -1956,7 +1956,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
} }
public getGroupAdmins(): Array<string> { public getGroupAdmins(): Array<string> {
const groupAdmins = this.get('groupAdmins'); const groupAdmins = getLibGroupAdminsOutsideRedux(this.id) || this.get('groupAdmins');
return groupAdmins && groupAdmins.length > 0 ? groupAdmins : []; return groupAdmins && groupAdmins.length > 0 ? groupAdmins : [];
} }

@ -224,15 +224,14 @@ class ConvoController {
await leaveClosedGroup(groupId, fromSyncMessage); await leaveClosedGroup(groupId, fromSyncMessage);
window.log.info(`deleteClosedGroup: ${groupId}, sendLeaveMessage?:${sendLeaveMessage}`); window.log.info(`deleteClosedGroup: ${groupId}, sendLeaveMessage?:${sendLeaveMessage}`);
} }
// if we were kicked or sent our left message, we have nothing to do more with that group.
// Just delete everything related to it, not trying to add update message or send a left message.
await this.removeGroupOrCommunityFromDBAndRedux(groupId);
if (PubKey.is03Pubkey(groupId)) { if (PubKey.is03Pubkey(groupId)) {
await remove03GroupFromWrappers(groupId); await remove03GroupFromWrappers(groupId);
} else { } else {
await removeLegacyGroupFromWrappers(groupId); await removeLegacyGroupFromWrappers(groupId);
} }
// if we were kicked or sent our left message, we have nothing to do more with that group.
// Just delete everything related to it, not trying to add update message or send a left message.
await this.removeGroupOrCommunityFromDBAndRedux(groupId);
if (!fromSyncMessage) { if (!fromSyncMessage) {
await UserSync.queueNewJobIfNeeded(); await UserSync.queueNewJobIfNeeded();
@ -456,6 +455,7 @@ class ConvoController {
/** /**
* You most likely don't want to call this function directly, but instead use the deleteLegacyGroup() from the ConversationController as it will take care of more cleaningup. * You most likely don't want to call this function directly, but instead use the deleteLegacyGroup() from the ConversationController as it will take care of more cleaningup.
* This throws if a leaveMessage needs to be sent, but fails to be sent.
* *
* Note: `fromSyncMessage` is used to know if we need to send a leave group message to the group first. * Note: `fromSyncMessage` is used to know if we need to send a leave group message to the group first.
* So if the user made the action on this device, fromSyncMessage should be false, but if it happened from a linked device polled update, set this to true. * So if the user made the action on this device, fromSyncMessage should be false, but if it happened from a linked device polled update, set this to true.
@ -512,20 +512,17 @@ async function leaveClosedGroup(groupPk: PubkeyType | GroupPubkeyType, fromSyncM
// We might not be able to send our leaving messages (no encryption keypair, we were already removed, no network, etc). // We might not be able to send our leaving messages (no encryption keypair, we were already removed, no network, etc).
// If that happens, we should just remove everything from our current user. // If that happens, we should just remove everything from our current user.
try { const wasSent = await getMessageQueue().sendToGroupV2NonDurably({
const wasSent = await getMessageQueue().sendToGroupV2NonDurably({ message: ourLeavingMessage,
message: ourLeavingMessage, });
}); if (!wasSent) {
if (!wasSent) { throw new Error(
throw new Error( `Even with the retries, leaving message for group ${ed25519Str(
`Even with the retries, leaving message for group ${ed25519Str( groupPk
groupPk )} failed to be sent...`
)} failed to be sent... Still deleting everything` );
);
}
} catch (e) {
window.log.warn('leaving groupv2 error:', e.message);
} }
// the rest of the cleaning of that conversation is done in the `deleteClosedGroup()` // the rest of the cleaning of that conversation is done in the `deleteClosedGroup()`
return; return;
@ -579,12 +576,7 @@ async function removeLegacyGroupFromWrappers(groupId: string) {
} }
async function remove03GroupFromWrappers(groupPk: GroupPubkeyType) { async function remove03GroupFromWrappers(groupPk: GroupPubkeyType) {
getSwarmPollingInstance().removePubkey(groupPk, 'remove03GroupFromWrappers');
await UserGroupsWrapperActions.eraseGroup(groupPk);
await SessionUtilConvoInfoVolatile.removeGroupFromWrapper(groupPk);
window?.inboxStore?.dispatch(groupInfoActions.destroyGroupDetails({ groupPk })); window?.inboxStore?.dispatch(groupInfoActions.destroyGroupDetails({ groupPk }));
window.log.info(`removed 03 from metagroup wrapper ${ed25519Str(groupPk)}`);
} }
async function removeCommunityFromWrappers(conversationId: string) { async function removeCommunityFromWrappers(conversationId: string) {

@ -30,6 +30,7 @@ import { ClosedGroup } from '../../session/group/closed-group';
import { GroupUpdateInfoChangeMessage } from '../../session/messages/outgoing/controlMessage/group_v2/to_group/GroupUpdateInfoChangeMessage'; import { GroupUpdateInfoChangeMessage } from '../../session/messages/outgoing/controlMessage/group_v2/to_group/GroupUpdateInfoChangeMessage';
import { GroupUpdateMemberChangeMessage } from '../../session/messages/outgoing/controlMessage/group_v2/to_group/GroupUpdateMemberChangeMessage'; import { GroupUpdateMemberChangeMessage } from '../../session/messages/outgoing/controlMessage/group_v2/to_group/GroupUpdateMemberChangeMessage';
import { GroupUpdateDeleteMessage } from '../../session/messages/outgoing/controlMessage/group_v2/to_user/GroupUpdateDeleteMessage'; import { GroupUpdateDeleteMessage } from '../../session/messages/outgoing/controlMessage/group_v2/to_user/GroupUpdateDeleteMessage';
import { ed25519Str } from '../../session/onions/onionPath';
import { PubKey } from '../../session/types'; import { PubKey } from '../../session/types';
import { UserUtils } from '../../session/utils'; import { UserUtils } from '../../session/utils';
import { PreConditionFailed } from '../../session/utils/errors'; import { PreConditionFailed } from '../../session/utils/errors';
@ -38,6 +39,7 @@ import { GroupSync } from '../../session/utils/job_runners/jobs/GroupSyncJob';
import { UserSync } from '../../session/utils/job_runners/jobs/UserSyncJob'; import { UserSync } from '../../session/utils/job_runners/jobs/UserSyncJob';
import { RunJobResult } from '../../session/utils/job_runners/PersistedJob'; import { RunJobResult } from '../../session/utils/job_runners/PersistedJob';
import { LibSessionUtil } from '../../session/utils/libsession/libsession_utils'; import { LibSessionUtil } from '../../session/utils/libsession/libsession_utils';
import { SessionUtilConvoInfoVolatile } from '../../session/utils/libsession/libsession_utils_convo_info_volatile';
import { getUserED25519KeyPairBytes } from '../../session/utils/User'; import { getUserED25519KeyPairBytes } from '../../session/utils/User';
import { stringify, toFixedUint8ArrayOfLength } from '../../types/sqlSharedTypes'; import { stringify, toFixedUint8ArrayOfLength } from '../../types/sqlSharedTypes';
import { import {
@ -382,14 +384,36 @@ const refreshGroupDetailsFromWrapper = createAsyncThunk(
const destroyGroupDetails = createAsyncThunk( const destroyGroupDetails = createAsyncThunk(
'group/destroyGroupDetails', 'group/destroyGroupDetails',
async ({ groupPk }: { groupPk: GroupPubkeyType }) => { async ({ groupPk }: { groupPk: GroupPubkeyType }) => {
try { debugger;
await UserGroupsWrapperActions.eraseGroup(groupPk); const us = UserUtils.getOurPubKeyStrFromCache();
await ConfigDumpData.deleteDumpFor(groupPk); const weAreAdmin = await checkWeAreAdmin(groupPk);
const allMembers = await MetaGroupWrapperActions.memberGetAll(groupPk);
const otherAdminsCount = allMembers
.filter(m => m.admin || m.promoted)
.filter(m => m.pubkeyHex !== us).length;
// we are the last admin promoted
if (weAreAdmin && otherAdminsCount === 0) {
// this marks the group info as deleted. We need to push those details
await MetaGroupWrapperActions.infoDestroy(groupPk); await MetaGroupWrapperActions.infoDestroy(groupPk);
getSwarmPollingInstance().removePubkey(groupPk, 'destroyGroupDetails'); const lastPushResult = await GroupSync.pushChangesToGroupSwarmIfNeeded({
} catch (e) { groupPk,
window.log.warn(`destroyGroupDetails for ${groupPk} failed with ${e.message}`); revokeSubRequest: null,
unrevokeSubRequest: null,
supplementKeys: [],
});
if (lastPushResult !== RunJobResult.Success) {
throw new Error(`Failed to destroyGroupDetails for pk ${ed25519Str(groupPk)}`);
}
} }
// this deletes the secretKey if we had it. If we need it for something, it has to be done before this call.
await UserGroupsWrapperActions.eraseGroup(groupPk);
await SessionUtilConvoInfoVolatile.removeGroupFromWrapper(groupPk);
await ConfigDumpData.deleteDumpFor(groupPk);
getSwarmPollingInstance().removePubkey(groupPk, 'destroyGroupDetails');
return { groupPk }; return { groupPk };
} }
); );
@ -1073,11 +1097,13 @@ const handleMemberLeftMessage = createAsyncThunk(
); );
} }
await handleMemberRemovedFromUI({ if (await checkWeAreAdmin(groupPk)) {
groupPk, await handleMemberRemovedFromUI({
removeMembers: [memberLeft], groupPk,
fromMemberLeftMessage: true, removeMembers: [memberLeft],
}); fromMemberLeftMessage: true,
});
}
return { return {
groupPk, groupPk,
@ -1303,7 +1329,7 @@ const metaGroupSlice = createSlice({
}); });
builder.addCase(destroyGroupDetails.fulfilled, (state, action) => { builder.addCase(destroyGroupDetails.fulfilled, (state, action) => {
const { groupPk } = action.payload; const { groupPk } = action.payload;
// FIXME destroyGroupDetails marks the info as destroyed, but does not really remove the wrapper currently window.log.info(`removed 03 from metagroup wrapper ${ed25519Str(groupPk)}`);
deleteGroupPkEntriesFromState(state, groupPk); deleteGroupPkEntriesFromState(state, groupPk);
}); });
builder.addCase(destroyGroupDetails.rejected, (_state, action) => { builder.addCase(destroyGroupDetails.rejected, (_state, action) => {

@ -291,7 +291,7 @@ export type LocalizerKeys =
| 'leaveGroupConfirmationAdmin' | 'leaveGroupConfirmationAdmin'
| 'leaveGroupConfirmationOnlyAdmin' | 'leaveGroupConfirmationOnlyAdmin'
| 'leaveGroupConfirmationOnlyAdminWarning' | 'leaveGroupConfirmationOnlyAdminWarning'
| 'leaveGroupConrirmationOnlyAdminLegacy' | 'leaveGroupConfirmationOnlyAdminLegacy'
| 'leaveGroupFailed' | 'leaveGroupFailed'
| 'leaveGroupFailedPleaseTryAgain' | 'leaveGroupFailedPleaseTryAgain'
| 'leaving' | 'leaving'

Loading…
Cancel
Save