Migrate schema to service ids
This commit is contained in:
parent
71958f8a01
commit
8b0da36caa
258 changed files with 4795 additions and 2613 deletions
|
@ -40,7 +40,7 @@ export namespace BodyRange {
|
|||
export const { Style } = Proto.DataMessage.BodyRange;
|
||||
|
||||
export type Mention = {
|
||||
mentionUuid: AciString;
|
||||
mentionAci: AciString;
|
||||
};
|
||||
export type Link = {
|
||||
url: string;
|
||||
|
@ -68,7 +68,7 @@ export namespace BodyRange {
|
|||
bodyRange: BodyRange<T>
|
||||
): bodyRange is X {
|
||||
// satisfies keyof Mention
|
||||
return ('mentionUuid' as const) in bodyRange;
|
||||
return ('mentionAci' as const) in bodyRange;
|
||||
}
|
||||
export function isFormatting(
|
||||
bodyRange: BodyRange<object>
|
||||
|
@ -181,14 +181,12 @@ export function filterAndClean(
|
|||
return undefined;
|
||||
}
|
||||
|
||||
let mentionUuid: AciString | undefined;
|
||||
let mentionAci: AciString | undefined;
|
||||
if ('mentionAci' in range && range.mentionAci) {
|
||||
mentionUuid = normalizeAci(range.mentionAci, 'BodyRange.mentionAci');
|
||||
} else if ('mentionUuid' in range && range.mentionUuid) {
|
||||
mentionUuid = normalizeAci(range.mentionUuid, 'BodyRange.mentionUuid');
|
||||
mentionAci = normalizeAci(range.mentionAci, 'BodyRange.mentionAci');
|
||||
}
|
||||
|
||||
if (mentionUuid) {
|
||||
if (mentionAci) {
|
||||
countByTypeRecord[MENTION_NAME] += 1;
|
||||
if (countByTypeRecord[MENTION_NAME] > MAX_PER_TYPE) {
|
||||
return undefined;
|
||||
|
@ -198,7 +196,7 @@ export function filterAndClean(
|
|||
...restOfRange,
|
||||
start,
|
||||
length,
|
||||
mentionUuid,
|
||||
mentionAci,
|
||||
};
|
||||
}
|
||||
if ('style' in range && range.style) {
|
||||
|
@ -230,7 +228,7 @@ export function hydrateRanges(
|
|||
|
||||
return filterAndClean(ranges)?.map(range => {
|
||||
if (BodyRange.isMention(range)) {
|
||||
const conversation = conversationSelector(range.mentionUuid);
|
||||
const conversation = conversationSelector(range.mentionAci);
|
||||
|
||||
return {
|
||||
...range,
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
import { z } from 'zod';
|
||||
import Long from 'long';
|
||||
import { CallMode } from './Calling';
|
||||
import type { ServiceIdString } from './ServiceId';
|
||||
import type { AciString } from './ServiceId';
|
||||
import { aciSchema } from './ServiceId';
|
||||
import { bytesToUuid } from '../util/uuidToBytes';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
import * as Bytes from '../Bytes';
|
||||
|
@ -67,8 +68,8 @@ export type CallStatus = DirectCallStatus | GroupCallStatus;
|
|||
|
||||
export type CallDetails = Readonly<{
|
||||
callId: string;
|
||||
peerId: ServiceIdString | string;
|
||||
ringerId: string | null;
|
||||
peerId: AciString | string;
|
||||
ringerId: AciString | string | null;
|
||||
mode: CallMode;
|
||||
type: CallType;
|
||||
direction: CallDirection;
|
||||
|
@ -95,7 +96,7 @@ export type CallHistoryGroup = Omit<CallHistoryDetails, 'callId' | 'ringerId'> &
|
|||
|
||||
export type GroupCallMeta = Readonly<{
|
||||
callId: string;
|
||||
ringerId: string;
|
||||
ringerId: string | AciString;
|
||||
}>;
|
||||
|
||||
export enum CallHistoryFilterStatus {
|
||||
|
@ -118,7 +119,7 @@ export type CallHistoryPagination = Readonly<{
|
|||
limit: number;
|
||||
}>;
|
||||
|
||||
const ringerIdSchema = z.union([z.string(), z.null()]);
|
||||
const ringerIdSchema = z.union([aciSchema, z.string(), z.null()]);
|
||||
|
||||
const callModeSchema = z.nativeEnum(CallMode);
|
||||
const callTypeSchema = z.nativeEnum(CallType);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import type { AudioDevice } from '@signalapp/ringrtc';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type { ServiceIdString } from './ServiceId';
|
||||
|
||||
// These are strings (1) for the database (2) for Storybook.
|
||||
export enum CallMode {
|
||||
|
@ -59,7 +60,10 @@ export type ActiveDirectCallType = ActiveCallBaseType & {
|
|||
hasRemoteVideo: boolean;
|
||||
presenting: boolean;
|
||||
title: string;
|
||||
uuid?: string;
|
||||
// Note that the field name/type has to match the
|
||||
// GroupCallRemoteParticipantType below (which is based on
|
||||
// ConversationType).
|
||||
serviceId?: ServiceIdString;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
|
|
@ -31,7 +31,7 @@ type GenericEmbeddedContactType<AvatarType> = {
|
|||
|
||||
// Populated by selector
|
||||
firstNumber?: string;
|
||||
uuid?: ServiceIdString;
|
||||
serviceId?: ServiceIdString;
|
||||
};
|
||||
|
||||
export type EmbeddedContactType = GenericEmbeddedContactType<Avatar>;
|
||||
|
@ -149,11 +149,12 @@ export function embeddedContactSelector(
|
|||
options: {
|
||||
regionCode?: string;
|
||||
firstNumber?: string;
|
||||
uuid?: ServiceIdString;
|
||||
serviceId?: ServiceIdString;
|
||||
getAbsoluteAttachmentPath: (path: string) => string;
|
||||
}
|
||||
): EmbeddedContactType {
|
||||
const { getAbsoluteAttachmentPath, firstNumber, uuid, regionCode } = options;
|
||||
const { getAbsoluteAttachmentPath, firstNumber, serviceId, regionCode } =
|
||||
options;
|
||||
|
||||
let { avatar } = contact;
|
||||
if (avatar && avatar.avatar) {
|
||||
|
@ -175,7 +176,7 @@ export function embeddedContactSelector(
|
|||
return {
|
||||
...contact,
|
||||
firstNumber,
|
||||
uuid,
|
||||
serviceId,
|
||||
avatar,
|
||||
number:
|
||||
contact.number &&
|
||||
|
|
|
@ -28,7 +28,7 @@ export type PanelRequestType =
|
|||
contact: EmbeddedContactType;
|
||||
signalAccount?: {
|
||||
phoneNumber: string;
|
||||
uuid: ServiceIdString;
|
||||
serviceId: ServiceIdString;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ export type PanelRenderType =
|
|||
contact: EmbeddedContactType;
|
||||
signalAccount?: {
|
||||
phoneNumber: string;
|
||||
uuid: ServiceIdString;
|
||||
serviceId: ServiceIdString;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { AciString } from './ServiceId';
|
||||
|
||||
export type ReactionType = Readonly<{
|
||||
conversationId: string;
|
||||
emoji: string;
|
||||
fromId: string;
|
||||
messageId: string;
|
||||
messageReceivedAt: number;
|
||||
targetAuthorUuid: string;
|
||||
targetAuthorAci: AciString;
|
||||
targetTimestamp: number;
|
||||
}>;
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
|
||||
import { z } from 'zod';
|
||||
|
||||
import { aciSchema } from './ServiceId';
|
||||
|
||||
export const receiptSchema = z.object({
|
||||
messageId: z.string(),
|
||||
conversationId: z.string(),
|
||||
senderE164: z.string().optional(),
|
||||
senderUuid: z.string().optional(),
|
||||
senderAci: aciSchema.optional(),
|
||||
timestamp: z.number(),
|
||||
isDirectConversation: z.boolean().optional(),
|
||||
});
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { v4 as generateUuid } from 'uuid';
|
||||
import { z } from 'zod';
|
||||
import { Aci, Pni, ServiceId } from '@signalapp/libsignal-client';
|
||||
|
||||
import { isValidUuid } from '../util/isValidUuid';
|
||||
import * as log from '../logging/log';
|
||||
|
@ -14,20 +16,22 @@ export enum ServiceIdKind {
|
|||
}
|
||||
|
||||
export type PniString = string & { __pni: never };
|
||||
export type UntaggedPniString = string & { __pni: never };
|
||||
export type UntaggedPniString = string & { __untagged_pni: never };
|
||||
export type AciString = string & { __aci: never };
|
||||
export type ServiceIdString = PniString | AciString;
|
||||
|
||||
export function isServiceIdString(value?: string): value is ServiceIdString {
|
||||
export function isServiceIdString(
|
||||
value?: string | null
|
||||
): value is ServiceIdString {
|
||||
return isAciString(value) || isPniString(value);
|
||||
}
|
||||
|
||||
export function isAciString(value?: string): value is AciString {
|
||||
export function isAciString(value?: string | null): value is AciString {
|
||||
return isValidUuid(value);
|
||||
}
|
||||
|
||||
export function isPniString(value?: string): value is PniString {
|
||||
if (value === undefined) {
|
||||
export function isPniString(value?: string | null): value is PniString {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -35,12 +39,11 @@ export function isPniString(value?: string): value is PniString {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Legacy IDs
|
||||
return isValidUuid(value);
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isUntaggedPniString(
|
||||
value?: string
|
||||
value?: string | null
|
||||
): value is UntaggedPniString {
|
||||
return isValidUuid(value);
|
||||
}
|
||||
|
@ -52,8 +55,24 @@ export function toTaggedPni(untagged: UntaggedPniString): PniString {
|
|||
export function normalizeServiceId(
|
||||
rawServiceId: string,
|
||||
context: string,
|
||||
logger?: Pick<LoggerType, 'warn'>
|
||||
): ServiceIdString;
|
||||
|
||||
export function normalizeServiceId(
|
||||
rawServiceId: string | undefined | null,
|
||||
context: string,
|
||||
logger?: Pick<LoggerType, 'warn'>
|
||||
): ServiceIdString | undefined;
|
||||
|
||||
export function normalizeServiceId(
|
||||
rawServiceId: string | undefined | null,
|
||||
context: string,
|
||||
logger: Pick<LoggerType, 'warn'> = log
|
||||
): ServiceIdString {
|
||||
): ServiceIdString | undefined {
|
||||
if (rawServiceId == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = rawServiceId.toLowerCase().replace(/^pni:/, 'PNI:');
|
||||
|
||||
if (!isAciString(result) && !isPniString(result)) {
|
||||
|
@ -71,8 +90,24 @@ export function normalizeServiceId(
|
|||
export function normalizeAci(
|
||||
rawAci: string,
|
||||
context: string,
|
||||
logger?: Pick<LoggerType, 'warn'>
|
||||
): AciString;
|
||||
|
||||
export function normalizeAci(
|
||||
rawAci: string | undefined | null,
|
||||
context: string,
|
||||
logger?: Pick<LoggerType, 'warn'>
|
||||
): AciString | undefined;
|
||||
|
||||
export function normalizeAci(
|
||||
rawAci: string | undefined | null,
|
||||
context: string,
|
||||
logger: Pick<LoggerType, 'warn'> = log
|
||||
): AciString {
|
||||
): AciString | undefined {
|
||||
if (rawAci == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = rawAci.toLowerCase();
|
||||
|
||||
if (!isAciString(result)) {
|
||||
|
@ -90,8 +125,24 @@ export function normalizeAci(
|
|||
export function normalizePni(
|
||||
rawPni: string,
|
||||
context: string,
|
||||
logger?: Pick<LoggerType, 'warn'>
|
||||
): PniString;
|
||||
|
||||
export function normalizePni(
|
||||
rawPni: string | undefined | null,
|
||||
context: string,
|
||||
logger?: Pick<LoggerType, 'warn'>
|
||||
): PniString | undefined;
|
||||
|
||||
export function normalizePni(
|
||||
rawPni: string | undefined | null,
|
||||
context: string,
|
||||
logger: Pick<LoggerType, 'warn'> = log
|
||||
): PniString {
|
||||
): PniString | undefined {
|
||||
if (rawPni == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result = rawPni.toLowerCase().replace(/^pni:/, 'PNI:');
|
||||
|
||||
if (!isPniString(result)) {
|
||||
|
@ -122,3 +173,49 @@ export function getAciFromPrefix(prefix: string): AciString {
|
|||
}
|
||||
return `${padded}-0000-4000-8000-${'0'.repeat(12)}` as AciString;
|
||||
}
|
||||
|
||||
export const aciSchema = z
|
||||
.string()
|
||||
.refine(isAciString)
|
||||
.transform(x => {
|
||||
if (!isAciString(x)) {
|
||||
throw new Error('Refine did not throw!');
|
||||
}
|
||||
return x;
|
||||
});
|
||||
|
||||
export const serviceIdSchema = z
|
||||
.string()
|
||||
.refine(isServiceIdString)
|
||||
.transform(x => {
|
||||
if (!isServiceIdString(x)) {
|
||||
throw new Error('Refine did not throw!');
|
||||
}
|
||||
return x;
|
||||
});
|
||||
|
||||
export function toServiceIdObject(serviceId: ServiceIdString): ServiceId {
|
||||
return ServiceId.parseFromServiceIdString(serviceId);
|
||||
}
|
||||
|
||||
export function toAciObject(aci: AciString): Aci {
|
||||
return Aci.parseFromServiceIdString(aci);
|
||||
}
|
||||
|
||||
export function toPniObject(pni: PniString): Pni {
|
||||
return Pni.parseFromServiceIdString(pni);
|
||||
}
|
||||
|
||||
// Note: getServiceIdString() returns normalized string so we can cast it
|
||||
// without normalizing.
|
||||
export function fromServiceIdObject(obj: ServiceId): ServiceIdString {
|
||||
return obj.getServiceIdString() as ServiceIdString;
|
||||
}
|
||||
|
||||
export function fromAciObject(obj: Aci): AciString {
|
||||
return obj.getServiceIdString() as AciString;
|
||||
}
|
||||
|
||||
export function fromPniObject(obj: Pni): PniString {
|
||||
return obj.getServiceIdString() as PniString;
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ export type StoryViewType = {
|
|||
| 'profileName'
|
||||
| 'sharedGroupNames'
|
||||
| 'title'
|
||||
| 'uuid'
|
||||
| 'serviceId'
|
||||
>;
|
||||
sendState?: Array<StorySendStateType>;
|
||||
timestamp: number;
|
||||
|
|
|
@ -67,3 +67,19 @@ export type UnwrapPromise<Value> = Value extends Promise<infer T> ? T : Value;
|
|||
export type BytesToStrings<Value> = Value extends Uint8Array
|
||||
? string
|
||||
: { [Key in keyof Value]: BytesToStrings<Value[Key]> };
|
||||
|
||||
export type JSONWithUnknownFields<Value> = Value extends Record<
|
||||
string | symbol | number,
|
||||
unknown
|
||||
>
|
||||
? Readonly<
|
||||
{
|
||||
[Key in keyof Value]: JSONWithUnknownFields<Value[Key]>;
|
||||
} & {
|
||||
// Make sure that rest property is required to handle.
|
||||
__rest: never;
|
||||
}
|
||||
>
|
||||
: Value extends Array<infer E>
|
||||
? ReadonlyArray<JSONWithUnknownFields<E>>
|
||||
: Value;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue