Fix backup import for unused admin call links
This commit is contained in:
parent
1e7d259909
commit
79d3a0f1ee
4 changed files with 116 additions and 44 deletions
|
@ -134,7 +134,10 @@ import { getBackupCdnInfo } from './util/mediaId';
|
|||
import { calculateExpirationTimestamp } from '../../util/expirationTimer';
|
||||
import { ReadStatus } from '../../messages/MessageReadStatus';
|
||||
import { CallLinkRestrictions } from '../../types/CallLink';
|
||||
import { toAdminKeyBytes } from '../../util/callLinks';
|
||||
import {
|
||||
isCallHistoryForUnusedCallLink,
|
||||
toAdminKeyBytes,
|
||||
} from '../../util/callLinks';
|
||||
import { getRoomIdFromRootKey } from '../../util/callLinksRingrtc';
|
||||
import { SeenStatus } from '../../MessageSeenStatus';
|
||||
import { migrateAllMessages } from '../../messages/migrateMessageData';
|
||||
|
@ -512,7 +515,7 @@ export class BackupExportStream extends Readable {
|
|||
for (const item of allCallHistoryItems) {
|
||||
const { callId, type, peerId: roomId, status, timestamp } = item;
|
||||
|
||||
if (type !== CallType.Adhoc) {
|
||||
if (type !== CallType.Adhoc || isCallHistoryForUnusedCallLink(item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ import { assertDev, strictAssert } from '../../util/assert';
|
|||
import {
|
||||
getCheckedTimestampFromLong,
|
||||
getCheckedTimestampOrUndefinedFromLong,
|
||||
getTimestampOrUndefinedFromLong,
|
||||
} from '../../util/timestampLongUtils';
|
||||
import { MAX_SAFE_DATE } from '../../util/timestamp';
|
||||
import { DurationInSeconds, SECOND } from '../../util/durations';
|
||||
|
@ -108,10 +109,13 @@ import {
|
|||
GroupCallStatus,
|
||||
} from '../../types/CallDisposition';
|
||||
import type { CallHistoryDetails } from '../../types/CallDisposition';
|
||||
import { CallLinkRestrictions } from '../../types/CallLink';
|
||||
import { CallLinkRestrictions, isCallLinkAdmin } from '../../types/CallLink';
|
||||
import type { CallLinkType } from '../../types/CallLink';
|
||||
import type { RawBodyRange } from '../../types/BodyRange';
|
||||
import { fromAdminKeyBytes } from '../../util/callLinks';
|
||||
import {
|
||||
fromAdminKeyBytes,
|
||||
toCallHistoryFromUnusedCallLink,
|
||||
} from '../../util/callLinks';
|
||||
import { getRoomIdFromRootKey } from '../../util/callLinksRingrtc';
|
||||
import { loadAllAndReinitializeRedux } from '../allLoaders';
|
||||
import {
|
||||
|
@ -209,6 +213,7 @@ export class BackupImportStream extends Writable {
|
|||
ConversationAttributesType
|
||||
>();
|
||||
private readonly recipientIdToCallLink = new Map<number, CallLinkType>();
|
||||
private readonly adminCallLinksToHasCall = new Map<CallLinkType, boolean>();
|
||||
private readonly chatIdToConvo = new Map<
|
||||
number,
|
||||
ConversationAttributesType
|
||||
|
@ -326,6 +331,15 @@ export class BackupImportStream extends Writable {
|
|||
// Store sticker packs and schedule downloads
|
||||
await createPacksFromBackup(this.stickerPacks);
|
||||
|
||||
// Add placeholder call history for unused admin call links to show in calls tab
|
||||
for (const [callLink, hasCall] of this.adminCallLinksToHasCall) {
|
||||
if (!hasCall) {
|
||||
const callHistory = toCallHistoryFromUnusedCallLink(callLink);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await this.saveCallHistory(callHistory);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset and reload conversations and storage again
|
||||
window.ConversationController.reset();
|
||||
|
||||
|
@ -1223,12 +1237,19 @@ export class BackupImportStream extends Writable {
|
|||
name,
|
||||
restrictions: fromCallLinkRestrictionsProto(restrictions),
|
||||
revoked: false,
|
||||
expiration: getCheckedTimestampOrUndefinedFromLong(expirationMs) ?? null,
|
||||
expiration: getTimestampOrUndefinedFromLong(expirationMs) ?? null,
|
||||
storageNeedsSync: false,
|
||||
};
|
||||
|
||||
this.recipientIdToCallLink.set(recipientId, callLink);
|
||||
|
||||
if (
|
||||
isCallLinkAdmin(callLink) &&
|
||||
!this.adminCallLinksToHasCall.has(callLink)
|
||||
) {
|
||||
this.adminCallLinksToHasCall.set(callLink, false);
|
||||
}
|
||||
|
||||
await DataWriter.insertCallLink(callLink);
|
||||
}
|
||||
|
||||
|
@ -3172,6 +3193,10 @@ export class BackupImportStream extends Writable {
|
|||
};
|
||||
|
||||
await this.saveCallHistory(callHistory);
|
||||
|
||||
if (isCallLinkAdmin(callLink)) {
|
||||
this.adminCallLinksToHasCall.set(callLink, true);
|
||||
}
|
||||
}
|
||||
|
||||
private async fromCustomChatColors(
|
||||
|
|
|
@ -39,7 +39,6 @@ const GROUP_ID_STRING = Bytes.toBase64(deriveGroupID(GROUP_SECRET_PARAMS));
|
|||
describe('backup/calling', () => {
|
||||
let contactA: ConversationModel;
|
||||
let groupA: ConversationModel;
|
||||
let callLink: CallLinkType;
|
||||
|
||||
beforeEach(async () => {
|
||||
await DataWriter.removeAll();
|
||||
|
@ -64,24 +63,6 @@ describe('backup/calling', () => {
|
|||
}
|
||||
);
|
||||
|
||||
const rootKey = CallLinkRootKey.generate();
|
||||
const adminKey = CallLinkRootKey.generateAdminPassKey();
|
||||
callLink = {
|
||||
rootKey: rootKey.toString(),
|
||||
roomId: getRoomIdFromRootKey(rootKey),
|
||||
adminKey: fromAdminKeyBytes(adminKey),
|
||||
name: "Let's Talk Rocks",
|
||||
restrictions: CallLinkRestrictions.AdminApproval,
|
||||
revoked: false,
|
||||
expiration: null,
|
||||
storageID: undefined,
|
||||
storageVersion: undefined,
|
||||
storageUnknownFields: undefined,
|
||||
storageNeedsSync: false,
|
||||
};
|
||||
|
||||
await DataWriter.insertCallLink(callLink);
|
||||
|
||||
await loadAllAndReinitializeRedux();
|
||||
});
|
||||
after(async () => {
|
||||
|
@ -137,6 +118,7 @@ describe('backup/calling', () => {
|
|||
assert.deepEqual(callHistory, allCallHistory[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Group calls', () => {
|
||||
it('roundtrips with a missed call', async () => {
|
||||
const now = Date.now();
|
||||
|
@ -187,22 +169,28 @@ describe('backup/calling', () => {
|
|||
});
|
||||
});
|
||||
describe('Call Links', () => {
|
||||
it('roundtrips with a link with admin details', async () => {
|
||||
const allCallLinksBefore = await DataReader.getAllCallLinks();
|
||||
assert.strictEqual(allCallLinksBefore.length, 1);
|
||||
let callLink: CallLinkType;
|
||||
let adminCallLink: CallLinkType;
|
||||
|
||||
await symmetricRoundtripHarness([]);
|
||||
|
||||
const allCallLinks = await DataReader.getAllCallLinks();
|
||||
assert.strictEqual(allCallLinks.length, 1);
|
||||
|
||||
assert.deepEqual(callLink, allCallLinks[0]);
|
||||
});
|
||||
it('roundtrips with a link without admin details', async () => {
|
||||
await DataWriter._removeAllCallLinks();
|
||||
beforeEach(async () => {
|
||||
const adminRootKey = CallLinkRootKey.generate();
|
||||
const adminKey = CallLinkRootKey.generateAdminPassKey();
|
||||
adminCallLink = {
|
||||
rootKey: adminRootKey.toString(),
|
||||
roomId: getRoomIdFromRootKey(adminRootKey),
|
||||
adminKey: fromAdminKeyBytes(adminKey),
|
||||
name: "Let's Talk Rocks",
|
||||
restrictions: CallLinkRestrictions.AdminApproval,
|
||||
revoked: false,
|
||||
expiration: null,
|
||||
storageID: undefined,
|
||||
storageVersion: undefined,
|
||||
storageUnknownFields: undefined,
|
||||
storageNeedsSync: false,
|
||||
};
|
||||
|
||||
const rootKey = CallLinkRootKey.generate();
|
||||
const callLinkNoAdmin = {
|
||||
callLink = {
|
||||
rootKey: rootKey.toString(),
|
||||
roomId: getRoomIdFromRootKey(rootKey),
|
||||
adminKey: null,
|
||||
|
@ -215,7 +203,15 @@ describe('backup/calling', () => {
|
|||
storageUnknownFields: undefined,
|
||||
storageNeedsSync: false,
|
||||
};
|
||||
await DataWriter.insertCallLink(callLinkNoAdmin);
|
||||
await DataWriter.insertCallLink(callLink);
|
||||
|
||||
await loadAllAndReinitializeRedux();
|
||||
});
|
||||
|
||||
it('roundtrips with a link with admin details', async () => {
|
||||
await DataWriter._removeAllCallLinks();
|
||||
|
||||
await DataWriter.insertCallLink(adminCallLink);
|
||||
|
||||
const allCallLinksBefore = await DataReader.getAllCallLinks();
|
||||
assert.strictEqual(allCallLinksBefore.length, 1);
|
||||
|
@ -225,11 +221,37 @@ describe('backup/calling', () => {
|
|||
const allCallLinks = await DataReader.getAllCallLinks();
|
||||
assert.strictEqual(allCallLinks.length, 1);
|
||||
|
||||
assert.deepEqual(callLinkNoAdmin, allCallLinks[0]);
|
||||
assert.deepEqual(adminCallLink, allCallLinks[0]);
|
||||
});
|
||||
it('creates placeholder call history for a link with admin details', async () => {
|
||||
await DataWriter._removeAllCallLinks();
|
||||
|
||||
await DataWriter.insertCallLink(adminCallLink);
|
||||
|
||||
const allCallHistoryBefore = await DataReader.getAllCallHistory();
|
||||
assert.strictEqual(allCallHistoryBefore.length, 0);
|
||||
|
||||
await symmetricRoundtripHarness([]);
|
||||
|
||||
const allCallHistory = await DataReader.getAllCallHistory();
|
||||
assert.strictEqual(allCallHistory.length, 1);
|
||||
});
|
||||
describe('Adhoc calls', () => {
|
||||
it('roundtrips with a joined call', async () => {
|
||||
it('roundtrips with a link without admin details', async () => {
|
||||
await DataWriter._removeAllCallLinks();
|
||||
|
||||
await DataWriter.insertCallLink(callLink);
|
||||
|
||||
const allCallLinksBefore = await DataReader.getAllCallLinks();
|
||||
assert.strictEqual(allCallLinksBefore.length, 1);
|
||||
|
||||
await symmetricRoundtripHarness([]);
|
||||
|
||||
const allCallLinks = await DataReader.getAllCallLinks();
|
||||
assert.strictEqual(allCallLinks.length, 1);
|
||||
|
||||
assert.deepEqual(callLink, allCallLinks[0]);
|
||||
});
|
||||
it('roundtrips with a joined adhoc call', async () => {
|
||||
const now = Date.now();
|
||||
const callId = '333333';
|
||||
const callHistory: CallHistoryDetails = {
|
||||
|
@ -254,8 +276,7 @@ describe('backup/calling', () => {
|
|||
|
||||
assert.deepEqual(callHistory, allCallHistory[0]);
|
||||
});
|
||||
|
||||
it('does not roundtrip call with missing call link', async () => {
|
||||
it('does not roundtrip adhoc call with missing call link', async () => {
|
||||
const now = Date.now();
|
||||
const callId = '44444';
|
||||
const callHistory: CallHistoryDetails = {
|
||||
|
|
|
@ -116,3 +116,26 @@ export function toCallHistoryFromUnusedCallLink(
|
|||
status: AdhocCallStatus.Pending,
|
||||
};
|
||||
}
|
||||
|
||||
export function isCallHistoryForUnusedCallLink(
|
||||
callHistory: CallHistoryDetails
|
||||
): boolean {
|
||||
const {
|
||||
ringerId,
|
||||
startedById,
|
||||
endedTimestamp,
|
||||
mode,
|
||||
type,
|
||||
direction,
|
||||
status,
|
||||
} = callHistory;
|
||||
return (
|
||||
ringerId == null &&
|
||||
startedById == null &&
|
||||
endedTimestamp == null &&
|
||||
mode === CallMode.Adhoc &&
|
||||
type === CallType.Adhoc &&
|
||||
direction === CallDirection.Incoming &&
|
||||
status === AdhocCallStatus.Pending
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue