diff --git a/ts/components/dialog/DeleteAccountModal.tsx b/ts/components/dialog/DeleteAccountModal.tsx index ce7e42f77..4d5582a96 100644 --- a/ts/components/dialog/DeleteAccountModal.tsx +++ b/ts/components/dialog/DeleteAccountModal.tsx @@ -12,6 +12,10 @@ import { SessionWrapperModal } from '../SessionWrapperModal'; import { Data } from '../../data/data'; import { deleteAllLogs } from '../../node/logs'; import { SessionRadioGroup } from '../basic/SessionRadioGroup'; +import { clearInbox } from '../../session/apis/open_group_api/sogsv3/sogsV3ClearInbox'; +import { OpenGroupData } from '../../data/opengroups'; +import { getOpenGroupV2ConversationId } from '../../session/apis/open_group_api/utils/OpenGroupUtils'; +import { getOpenGroupManager } from '../../session/apis/open_group_api/opengroupV2/OpenGroupManagerV2'; const deleteDbLocally = async () => { window?.log?.info('last message sent successfully. Deleting everything'); @@ -57,6 +61,44 @@ async function deleteEverythingAndNetworkData() { // DELETE EVERYTHING ON NETWORK, AND THEN STUFF LOCALLY STORED // a bit of duplicate code below, but it's easier to follow every case like that (helped with returns) + // send delete for all sogs message requests + // TODO extract function from within startPollingBouncy() to replace some of the code below. + // fetch all open group conversations + const allConvos = await OpenGroupData.getAllOpenGroupV2Conversations(); + let allRoomInfosMap = OpenGroupData.getAllV2OpenGroupRoomsMap(); + // this is time for some cleanup! + // We consider the conversations are our source-of-truth, + // so if there is a roomInfo without an associated convo, we remove it + if (allRoomInfosMap) { + await Promise.all( + [...allRoomInfosMap.values()].map(async infos => { + try { + const roomConvoId = getOpenGroupV2ConversationId(infos.serverUrl, infos.roomId); + if (!allConvos.get(roomConvoId)) { + // remove the roomInfos locally for this open group room + await OpenGroupData.removeV2OpenGroupRoom(roomConvoId); + getOpenGroupManager().removeRoomFromPolledRooms(infos); + // no need to remove it from the ConversationController, the convo is already not there + } + } catch (e) { + window?.log?.warn('cleanup roomInfos error', e); + } + }) + ); + } + + allRoomInfosMap = OpenGroupData.getAllV2OpenGroupRoomsMap(); + if (allRoomInfosMap) { + const allRoomInfos = Object.values(allRoomInfosMap); + // clear each inbox per sogs + for (let i = 0; i < allRoomInfos.length; i++) { + // TODO handle the response to confirm this works + // TODO CONTINUE + // TODO will need to test with a dummy account with some message requests and then if we restore from seed there should be no message requests. + await clearInbox(allRoomInfos[i]); + } + } + // send deletion message to the network const potentiallyMaliciousSnodes = await forceNetworkDeletion(); if (potentiallyMaliciousSnodes === null) { diff --git a/ts/session/apis/open_group_api/sogsv3/sogsV3BatchPoll.ts b/ts/session/apis/open_group_api/sogsv3/sogsV3BatchPoll.ts index e73feb65c..f326eab6c 100644 --- a/ts/session/apis/open_group_api/sogsv3/sogsV3BatchPoll.ts +++ b/ts/session/apis/open_group_api/sogsv3/sogsV3BatchPoll.ts @@ -144,6 +144,13 @@ export type SubRequestPollInfoType = { export type SubRequestInboxType = { type: 'inbox'; + inbox?: { + /** + * Deletes all of the user's received messages. + @returns a JSON object with one key "deleted" set to the number of deleted messages. + */ + type: 'delete'; + }; inboxSince?: { id?: number; }; @@ -247,6 +254,14 @@ const makeBatchRequestPayload = ( break; case 'inbox': + const isDelete = Boolean(options.inbox?.type === 'delete'); + if (isDelete) { + return { + method: 'DELETE', + path: '/inbox', + }; + } + return { method: 'GET', path: diff --git a/ts/session/apis/open_group_api/sogsv3/sogsV3ClearInbox.ts b/ts/session/apis/open_group_api/sogsv3/sogsV3ClearInbox.ts new file mode 100644 index 000000000..258481e82 --- /dev/null +++ b/ts/session/apis/open_group_api/sogsv3/sogsV3ClearInbox.ts @@ -0,0 +1,65 @@ +import AbortController from 'abort-controller'; +import { Data } from '../../../../data/data'; +import { OpenGroupRequestCommonType } from '../opengroupV2/ApiUtil'; +import { getOpenGroupV2ConversationId } from '../utils/OpenGroupUtils'; +import { + batchFirstSubIsSuccess, + batchGlobalIsSuccess, + OpenGroupBatchRow, + sogsBatchSend, +} from './sogsV3BatchPoll'; + +type OpenGroupClearInboxResponse = { + deleted: number; +}; + +export const clearInbox = async (roomInfos: OpenGroupRequestCommonType): Promise => { + const converationId = getOpenGroupV2ConversationId(roomInfos.serverUrl, roomInfos.roomId); + const conversation = await Data.getConversationById(converationId); + + if (!conversation) { + window.log.warn(`clear inbox Matching conversation not found in db`); + // we failed + return false; + } + + const options: Array = [ + { + type: 'inbox', + inbox: { + type: 'delete', + }, + }, + ]; + + const result = await sogsBatchSend( + roomInfos.serverUrl, + new Set([roomInfos.roomId]), + new AbortController().signal, + options, + 'batch' + ); + + if (!result) { + throw new Error('Could not clearInbox, res is invalid'); + } + + const rawMessage = (result.body && (result.body[0].body as OpenGroupClearInboxResponse)) || null; + if (!rawMessage) { + throw new Error('clearInbox parsing failed'); + } + + try { + if (batchGlobalIsSuccess(result) && batchFirstSubIsSuccess(result)) { + // we succeeded + return true; + } else { + // we failed + return false; + } + } catch (e) { + window?.log?.error("clearInbox Can't decode JSON body"); + } + + return false; +};