Simplify redux ducks and avoid reexport
This commit is contained in:
parent
bd41d7b216
commit
d34d187f1e
5 changed files with 144 additions and 94 deletions
|
@ -27,6 +27,8 @@ export type ConfigKeyType =
|
|||
| 'desktop.messageCleanup'
|
||||
| 'desktop.messageRequests'
|
||||
| 'desktop.pnp'
|
||||
| 'desktop.safetyNumberUUID'
|
||||
| 'desktop.safetyNumberUUID.timestamp'
|
||||
| 'desktop.retryReceiptLifespan'
|
||||
| 'desktop.retryRespondMaxAge'
|
||||
| 'desktop.senderKey.retry'
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ReadonlyDeep } from 'type-fest';
|
||||
import type { ThunkAction } from 'redux-thunk';
|
||||
|
||||
import { generateSecurityNumberBlock } from '../../util/safetyNumber';
|
||||
import type { ConversationType } from './conversations';
|
||||
import {
|
||||
|
@ -10,6 +12,8 @@ import {
|
|||
} from '../../shims/contactVerification';
|
||||
import * as log from '../../logging/log';
|
||||
import * as Errors from '../../types/errors';
|
||||
import type { StateType as RootStateType } from '../reducer';
|
||||
import { getSecurityNumberIdentifierType } from '../selectors/items';
|
||||
|
||||
export type SafetyNumberContactType = ReadonlyDeep<{
|
||||
safetyNumber: string;
|
||||
|
@ -23,83 +27,108 @@ export type SafetyNumberStateType = ReadonlyDeep<{
|
|||
};
|
||||
}>;
|
||||
|
||||
const GENERATE = 'safetyNumber/GENERATE';
|
||||
const GENERATE_FULFILLED = 'safetyNumber/GENERATE_FULFILLED';
|
||||
const TOGGLE_VERIFIED = 'safetyNumber/TOGGLE_VERIFIED';
|
||||
const TOGGLE_VERIFIED_FULFILLED = 'safetyNumber/TOGGLE_VERIFIED_FULFILLED';
|
||||
const TOGGLE_VERIFIED_PENDING = 'safetyNumber/TOGGLE_VERIFIED_PENDING';
|
||||
|
||||
type GenerateAsyncActionType = ReadonlyDeep<{
|
||||
contact: ConversationType;
|
||||
safetyNumber: string;
|
||||
}>;
|
||||
|
||||
type GenerateActionType = ReadonlyDeep<{
|
||||
type: 'safetyNumber/GENERATE';
|
||||
payload: Promise<GenerateAsyncActionType>;
|
||||
}>;
|
||||
|
||||
type GenerateFulfilledActionType = ReadonlyDeep<{
|
||||
type: 'safetyNumber/GENERATE_FULFILLED';
|
||||
payload: GenerateAsyncActionType;
|
||||
}>;
|
||||
|
||||
type ToggleVerifiedAsyncActionType = ReadonlyDeep<{
|
||||
contact: ConversationType;
|
||||
safetyNumber?: string;
|
||||
safetyNumberChanged?: boolean;
|
||||
}>;
|
||||
|
||||
type ToggleVerifiedActionType = ReadonlyDeep<{
|
||||
type: 'safetyNumber/TOGGLE_VERIFIED';
|
||||
payload: {
|
||||
data: { contact: ConversationType };
|
||||
promise: Promise<ToggleVerifiedAsyncActionType>;
|
||||
contact: ConversationType;
|
||||
safetyNumber: string;
|
||||
};
|
||||
}>;
|
||||
|
||||
type ToggleVerifiedPendingActionType = ReadonlyDeep<{
|
||||
type: 'safetyNumber/TOGGLE_VERIFIED_PENDING';
|
||||
payload: ToggleVerifiedAsyncActionType;
|
||||
payload: {
|
||||
contact: ConversationType;
|
||||
};
|
||||
}>;
|
||||
|
||||
type ToggleVerifiedFulfilledActionType = ReadonlyDeep<{
|
||||
type: 'safetyNumber/TOGGLE_VERIFIED_FULFILLED';
|
||||
payload: ToggleVerifiedAsyncActionType;
|
||||
payload: {
|
||||
contact: ConversationType;
|
||||
safetyNumber?: string;
|
||||
safetyNumberChanged?: boolean;
|
||||
};
|
||||
}>;
|
||||
|
||||
export type SafetyNumberActionType = ReadonlyDeep<
|
||||
| GenerateActionType
|
||||
| GenerateFulfilledActionType
|
||||
| ToggleVerifiedActionType
|
||||
| ToggleVerifiedPendingActionType
|
||||
| ToggleVerifiedFulfilledActionType
|
||||
>;
|
||||
|
||||
function generate(contact: ConversationType): GenerateActionType {
|
||||
return {
|
||||
type: GENERATE,
|
||||
payload: doGenerate(contact),
|
||||
};
|
||||
}
|
||||
|
||||
async function doGenerate(
|
||||
function generate(
|
||||
contact: ConversationType
|
||||
): Promise<GenerateAsyncActionType> {
|
||||
const securityNumberBlock = await generateSecurityNumberBlock(contact);
|
||||
return {
|
||||
contact,
|
||||
safetyNumber: securityNumberBlock.join(' '),
|
||||
): ThunkAction<void, RootStateType, unknown, GenerateFulfilledActionType> {
|
||||
return async (dispatch, getState) => {
|
||||
try {
|
||||
const securityNumberBlock = await generateSecurityNumberBlock(
|
||||
contact,
|
||||
getSecurityNumberIdentifierType(getState(), { now: Date.now() })
|
||||
);
|
||||
dispatch({
|
||||
type: GENERATE_FULFILLED,
|
||||
payload: {
|
||||
contact,
|
||||
safetyNumber: securityNumberBlock.join(' '),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
log.error(
|
||||
'failed to generate security number:',
|
||||
Errors.toLogFormat(error)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function toggleVerified(contact: ConversationType): ToggleVerifiedActionType {
|
||||
return {
|
||||
type: TOGGLE_VERIFIED,
|
||||
payload: {
|
||||
data: { contact },
|
||||
promise: doToggleVerified(contact),
|
||||
},
|
||||
function toggleVerified(
|
||||
contact: ConversationType
|
||||
): ThunkAction<
|
||||
void,
|
||||
RootStateType,
|
||||
unknown,
|
||||
ToggleVerifiedPendingActionType | ToggleVerifiedFulfilledActionType
|
||||
> {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: TOGGLE_VERIFIED_PENDING,
|
||||
payload: {
|
||||
contact,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await alterVerification(contact);
|
||||
|
||||
dispatch({
|
||||
type: TOGGLE_VERIFIED_FULFILLED,
|
||||
payload: {
|
||||
contact,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.name === 'OutgoingIdentityKeyError') {
|
||||
await reloadProfiles(contact.id);
|
||||
const securityNumberBlock = await generateSecurityNumberBlock(
|
||||
contact,
|
||||
getSecurityNumberIdentifierType(getState(), { now: Date.now() })
|
||||
);
|
||||
|
||||
dispatch({
|
||||
type: TOGGLE_VERIFIED_FULFILLED,
|
||||
payload: {
|
||||
contact,
|
||||
safetyNumber: securityNumberBlock.join(' '),
|
||||
safetyNumberChanged: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -128,27 +157,6 @@ async function alterVerification(contact: ConversationType): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
async function doToggleVerified(
|
||||
contact: ConversationType
|
||||
): Promise<ToggleVerifiedAsyncActionType> {
|
||||
try {
|
||||
await alterVerification(contact);
|
||||
} catch (err) {
|
||||
if (err.name === 'OutgoingIdentityKeyError') {
|
||||
await reloadProfiles(contact.id);
|
||||
const securityNumberBlock = await generateSecurityNumberBlock(contact);
|
||||
|
||||
return {
|
||||
contact,
|
||||
safetyNumber: securityNumberBlock.join(' '),
|
||||
safetyNumberChanged: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { contact };
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
generateSafetyNumber: generate,
|
||||
toggleVerified,
|
||||
|
|
|
@ -5,6 +5,7 @@ import { createSelector } from 'reselect';
|
|||
import { isInteger } from 'lodash';
|
||||
|
||||
import { ITEM_NAME as UNIVERSAL_EXPIRE_TIMER_ITEM } from '../../util/universalExpireTimer';
|
||||
import { SecurityNumberIdentifierType } from '../../util/safetyNumber';
|
||||
import { innerIsBucketValueEnabled } from '../../RemoteConfig';
|
||||
import type { ConfigKeyType, ConfigMapType } from '../../RemoteConfig';
|
||||
import type { StateType } from '../reducer';
|
||||
|
@ -145,6 +146,25 @@ export const getContactManagementEnabled = createSelector(
|
|||
}
|
||||
);
|
||||
|
||||
export const getSecurityNumberIdentifierType = createSelector(
|
||||
getRemoteConfig,
|
||||
(_state: StateType, { now }: { now: number }) => now,
|
||||
(remoteConfig: ConfigMapType, now: number): SecurityNumberIdentifierType => {
|
||||
if (isRemoteConfigFlagEnabled(remoteConfig, 'desktop.safetyNumberUUID')) {
|
||||
return SecurityNumberIdentifierType.UUIDIdentifier;
|
||||
}
|
||||
|
||||
const timestamp = remoteConfig['desktop.safetyNumberUUID.timestamp']?.value;
|
||||
if (typeof timestamp !== 'number') {
|
||||
return SecurityNumberIdentifierType.E164Identifier;
|
||||
}
|
||||
|
||||
return now >= timestamp
|
||||
? SecurityNumberIdentifierType.UUIDIdentifier
|
||||
: SecurityNumberIdentifierType.E164Identifier;
|
||||
}
|
||||
);
|
||||
|
||||
export const getDefaultConversationColor = createSelector(
|
||||
getItems,
|
||||
(
|
||||
|
|
|
@ -9,7 +9,6 @@ import { createBatcher } from './batcher';
|
|||
import { createWaitBatcher } from './waitBatcher';
|
||||
import { deleteForEveryone } from './deleteForEveryone';
|
||||
import { downloadAttachment } from './downloadAttachment';
|
||||
import { generateSecurityNumber } from './safetyNumber';
|
||||
import { getStringForProfileChange } from './getStringForProfileChange';
|
||||
import { getTextWithMentions } from './getTextWithMentions';
|
||||
import { getUuidsForE164s } from './getUuidsForE164s';
|
||||
|
@ -53,7 +52,6 @@ export {
|
|||
downloadAttachment,
|
||||
flushMessageCounter,
|
||||
fromWebSafeBase64,
|
||||
generateSecurityNumber,
|
||||
getStringForProfileChange,
|
||||
getTextWithMentions,
|
||||
getUserAgent,
|
||||
|
|
|
@ -6,17 +6,18 @@ import type { ConversationType } from '../state/ducks/conversations';
|
|||
import { UUID } from '../types/UUID';
|
||||
|
||||
import { assertDev } from './assert';
|
||||
import { missingCaseError } from './missingCaseError';
|
||||
import * as log from '../logging/log';
|
||||
|
||||
export async function generateSecurityNumber(
|
||||
ourNumber: string,
|
||||
function generateSecurityNumber(
|
||||
ourId: string,
|
||||
ourKey: Uint8Array,
|
||||
theirNumber: string,
|
||||
theirId: string,
|
||||
theirKey: Uint8Array
|
||||
): Promise<string> {
|
||||
const ourNumberBuf = Buffer.from(ourNumber);
|
||||
): string {
|
||||
const ourNumberBuf = Buffer.from(ourId);
|
||||
const ourKeyObj = PublicKey.deserialize(Buffer.from(ourKey));
|
||||
const theirNumberBuf = Buffer.from(theirNumber);
|
||||
const theirNumberBuf = Buffer.from(theirId);
|
||||
const theirKeyObj = PublicKey.deserialize(Buffer.from(theirKey));
|
||||
|
||||
const fingerprint = Fingerprint.new(
|
||||
|
@ -28,13 +29,21 @@ export async function generateSecurityNumber(
|
|||
theirKeyObj
|
||||
);
|
||||
|
||||
const fingerprintString = fingerprint.displayableFingerprint().toString();
|
||||
return Promise.resolve(fingerprintString);
|
||||
return fingerprint.displayableFingerprint().toString();
|
||||
}
|
||||
|
||||
export enum SecurityNumberIdentifierType {
|
||||
UUIDIdentifier = 'UUIDIdentifier',
|
||||
E164Identifier = 'E164Identifier',
|
||||
}
|
||||
|
||||
export async function generateSecurityNumberBlock(
|
||||
contact: ConversationType
|
||||
contact: ConversationType,
|
||||
identifierType: SecurityNumberIdentifierType
|
||||
): Promise<Array<string>> {
|
||||
const logId = `generateSecurityNumberBlock(${contact.id}, ${identifierType})`;
|
||||
log.info(`${logId}: starting`);
|
||||
|
||||
const { storage } = window.textsecure;
|
||||
const ourNumber = storage.user.getNumber();
|
||||
const ourUuid = storage.user.getCheckedUuid();
|
||||
|
@ -56,20 +65,33 @@ export async function generateSecurityNumberBlock(
|
|||
throw new Error('Could not load their key');
|
||||
}
|
||||
|
||||
if (!contact.e164) {
|
||||
log.error(
|
||||
'generateSecurityNumberBlock: Attempted to generate security number for contact with no e164'
|
||||
);
|
||||
return [];
|
||||
}
|
||||
let securityNumber: string;
|
||||
if (identifierType === SecurityNumberIdentifierType.E164Identifier) {
|
||||
if (!contact.e164) {
|
||||
log.error(
|
||||
`${logId}: Attempted to generate security number for contact with no e164`
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
assertDev(ourNumber, 'Should have our number');
|
||||
const securityNumber = await generateSecurityNumber(
|
||||
ourNumber,
|
||||
ourKey,
|
||||
contact.e164,
|
||||
theirKey
|
||||
);
|
||||
assertDev(ourNumber, 'Should have our number');
|
||||
securityNumber = generateSecurityNumber(
|
||||
ourNumber,
|
||||
ourKey,
|
||||
contact.e164,
|
||||
theirKey
|
||||
);
|
||||
} else if (identifierType === SecurityNumberIdentifierType.UUIDIdentifier) {
|
||||
assertDev(theirUuid, 'Should have their uuid');
|
||||
securityNumber = generateSecurityNumber(
|
||||
ourUuid.toString(),
|
||||
ourKey,
|
||||
theirUuid.toString(),
|
||||
theirKey
|
||||
);
|
||||
} else {
|
||||
throw missingCaseError(identifierType);
|
||||
}
|
||||
|
||||
const chunks = [];
|
||||
for (let i = 0; i < securityNumber.length; i += 5) {
|
||||
|
|
Loading…
Add table
Reference in a new issue