Introduce Service Id Types
Co-authored-by: Scott Nonnenberg <scott@signal.org>
This commit is contained in:
parent
414c0a58d3
commit
366b875fd2
269 changed files with 5832 additions and 5550 deletions
|
@ -5,14 +5,14 @@ import { createSelector } from 'reselect';
|
|||
|
||||
import type { StateType } from '../reducer';
|
||||
import type { AccountsStateType } from '../ducks/accounts';
|
||||
import type { UUIDStringType } from '../../types/UUID';
|
||||
import type { ServiceIdString } from '../../types/ServiceId';
|
||||
|
||||
export const getAccounts = (state: StateType): AccountsStateType =>
|
||||
state.accounts;
|
||||
|
||||
export type AccountSelectorType = (
|
||||
identifier?: string
|
||||
) => UUIDStringType | undefined;
|
||||
) => ServiceIdString | undefined;
|
||||
export const getAccountSelector = createSelector(
|
||||
getAccounts,
|
||||
(accounts: AccountsStateType): AccountSelectorType => {
|
||||
|
|
|
@ -28,7 +28,7 @@ import { getMessageIdForLogging } from '../../util/idForLogging';
|
|||
import * as Attachment from '../../types/Attachment';
|
||||
import type { ActiveAudioPlayerStateType } from '../ducks/audioPlayer';
|
||||
import { isPlayed } from '../../types/Attachment';
|
||||
import type { UUIDStringType } from '../../types/UUID';
|
||||
import type { ServiceIdString } from '../../types/ServiceId';
|
||||
|
||||
export type VoiceNoteForPlayback = {
|
||||
id: string;
|
||||
|
@ -36,7 +36,7 @@ export type VoiceNoteForPlayback = {
|
|||
url: string | undefined;
|
||||
type: 'incoming' | 'outgoing';
|
||||
source: string | undefined;
|
||||
sourceUuid: UUIDStringType | undefined;
|
||||
sourceUuid: ServiceIdString | undefined;
|
||||
isPlayed: boolean;
|
||||
messageIdForLogging: string;
|
||||
timestamp: number;
|
||||
|
@ -58,12 +58,12 @@ export const selectVoiceNoteTitle = createSelector(
|
|||
getUserConversationId,
|
||||
getConversationSelector,
|
||||
getIntl,
|
||||
(ourNumber, ourACI, ourConversationId, conversationSelector, i18n) => {
|
||||
(ourNumber, ourAci, ourConversationId, conversationSelector, i18n) => {
|
||||
return (
|
||||
message: Pick<MessageAttributesType, 'type' | 'source' | 'sourceUuid'>
|
||||
) => {
|
||||
const source = getSource(message, ourNumber);
|
||||
const sourceUuid = getSourceUuid(message, ourACI);
|
||||
const sourceUuid = getSourceUuid(message, ourAci);
|
||||
|
||||
const conversation =
|
||||
!source && !sourceUuid
|
||||
|
|
|
@ -15,7 +15,7 @@ import { getIncomingCall as getIncomingCallHelper } from '../ducks/callingHelper
|
|||
import { getUserACI } from './user';
|
||||
import { getOwn } from '../../util/getOwn';
|
||||
import { CallViewMode } from '../../types/Calling';
|
||||
import type { UUIDStringType } from '../../types/UUID';
|
||||
import type { AciString } from '../../types/ServiceId';
|
||||
|
||||
export type CallStateType = DirectCallStateType | GroupCallStateType;
|
||||
|
||||
|
@ -70,13 +70,13 @@ export const getIncomingCall = createSelector(
|
|||
getUserACI,
|
||||
(
|
||||
callsByConversation: CallsByConversationType,
|
||||
ourUuid: UUIDStringType | undefined
|
||||
ourAci: AciString | undefined
|
||||
): undefined | DirectCallStateType | GroupCallStateType => {
|
||||
if (!ourUuid) {
|
||||
if (!ourAci) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getIncomingCallHelper(callsByConversation, ourUuid);
|
||||
return getIncomingCallHelper(callsByConversation, ourAci);
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { normalizeStoryDistributionId } from '../../types/StoryDistributionId';
|
||||
import type { ContactsByStory } from '../../components/SafetyNumberChangeDialog';
|
||||
import type { ConversationVerificationData } from '../ducks/conversations';
|
||||
import type { StoryDistributionListDataType } from '../ducks/storyDistributionLists';
|
||||
|
@ -41,22 +42,22 @@ export const getByDistributionListConversationsStoppingSend = createSelector(
|
|||
return;
|
||||
}
|
||||
|
||||
const conversationUuids = new Set(
|
||||
conversationData.uuidsNeedingVerification
|
||||
const conversationServiceIds = new Set(
|
||||
conversationData.serviceIdsNeedingVerification
|
||||
);
|
||||
|
||||
if (conversationData.byDistributionId) {
|
||||
Object.entries(conversationData.byDistributionId).forEach(
|
||||
([distributionId, distributionData]) => {
|
||||
if (distributionData.uuidsNeedingVerification.length === 0) {
|
||||
if (distributionData.serviceIdsNeedingVerification.length === 0) {
|
||||
return;
|
||||
}
|
||||
const currentDistribution =
|
||||
distributionListSelector(distributionId);
|
||||
|
||||
if (!currentDistribution) {
|
||||
distributionData.uuidsNeedingVerification.forEach(uuid => {
|
||||
conversationUuids.add(uuid);
|
||||
distributionData.serviceIdsNeedingVerification.forEach(uuid => {
|
||||
conversationServiceIds.add(uuid);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -64,18 +65,21 @@ export const getByDistributionListConversationsStoppingSend = createSelector(
|
|||
conversations.push({
|
||||
story: {
|
||||
conversationId,
|
||||
distributionId,
|
||||
distributionId: normalizeStoryDistributionId(
|
||||
distributionId,
|
||||
'conversations-extra'
|
||||
),
|
||||
name: currentDistribution.name,
|
||||
},
|
||||
contacts: distributionData.uuidsNeedingVerification.map(uuid =>
|
||||
conversationSelector(uuid)
|
||||
contacts: distributionData.serviceIdsNeedingVerification.map(
|
||||
uuid => conversationSelector(uuid)
|
||||
),
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (conversationUuids.size) {
|
||||
if (conversationServiceIds.size) {
|
||||
const currentConversation = conversationSelector(conversationId);
|
||||
conversations.push({
|
||||
story: isGroup(currentConversation)
|
||||
|
@ -84,7 +88,7 @@ export const getByDistributionListConversationsStoppingSend = createSelector(
|
|||
name: currentConversation.title,
|
||||
}
|
||||
: undefined,
|
||||
contacts: Array.from(conversationUuids).map(uuid =>
|
||||
contacts: Array.from(conversationServiceIds).map(uuid =>
|
||||
conversationSelector(uuid)
|
||||
),
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ import {
|
|||
import type { ContactNameColorType } from '../../types/Colors';
|
||||
import { ContactNameColors } from '../../types/Colors';
|
||||
import type { AvatarDataType } from '../../types/Avatar';
|
||||
import type { UUIDStringType } from '../../types/UUID';
|
||||
import type { ServiceIdString } from '../../types/ServiceId';
|
||||
import { isInSystemContacts } from '../../util/isInSystemContacts';
|
||||
import { isSignalConnection } from '../../util/getSignalConnections';
|
||||
import { sortByTitle } from '../../util/sortByTitle';
|
||||
|
@ -871,7 +871,7 @@ export const getConversationByIdSelector = createSelector(
|
|||
export const getConversationByUuidSelector = createSelector(
|
||||
getConversationsByUuid,
|
||||
conversationsByUuid =>
|
||||
(uuid: UUIDStringType): undefined | ConversationType =>
|
||||
(uuid: ServiceIdString): undefined | ConversationType =>
|
||||
getOwn(conversationsByUuid, uuid)
|
||||
);
|
||||
|
||||
|
@ -1042,9 +1042,9 @@ export const getInvitedContactsForNewlyCreatedGroup = createSelector(
|
|||
getConversations,
|
||||
(
|
||||
conversationLookup,
|
||||
{ invitedUuidsForNewlyCreatedGroup = [] }
|
||||
{ invitedServiceIdsForNewlyCreatedGroup = [] }
|
||||
): Array<ConversationType> =>
|
||||
deconstructLookup(conversationLookup, invitedUuidsForNewlyCreatedGroup)
|
||||
deconstructLookup(conversationLookup, invitedServiceIdsForNewlyCreatedGroup)
|
||||
);
|
||||
|
||||
export const getConversationsWithCustomColorSelector = createSelector(
|
||||
|
@ -1127,20 +1127,20 @@ export const getConversationIdsStoppedForVerification = createSelector(
|
|||
Object.keys(verificationDataByConversation)
|
||||
);
|
||||
|
||||
export const getConversationUuidsStoppingSend = createSelector(
|
||||
export const getConversationServiceIdsStoppingSend = createSelector(
|
||||
getConversationVerificationData,
|
||||
(pendingData): Array<string> => {
|
||||
const result = new Set<string>();
|
||||
(pendingData): Array<ServiceIdString> => {
|
||||
const result = new Set<ServiceIdString>();
|
||||
Object.values(pendingData).forEach(item => {
|
||||
if (item.type === ConversationVerificationState.PendingVerification) {
|
||||
item.uuidsNeedingVerification.forEach(conversationId => {
|
||||
result.add(conversationId);
|
||||
item.serviceIdsNeedingVerification.forEach(serviceId => {
|
||||
result.add(serviceId);
|
||||
});
|
||||
|
||||
if (item.byDistributionId) {
|
||||
Object.values(item.byDistributionId).forEach(distribution => {
|
||||
distribution.uuidsNeedingVerification.forEach(conversationId => {
|
||||
result.add(conversationId);
|
||||
distribution.serviceIdsNeedingVerification.forEach(serviceId => {
|
||||
result.add(serviceId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -1152,12 +1152,14 @@ export const getConversationUuidsStoppingSend = createSelector(
|
|||
|
||||
export const getConversationsStoppingSend = createSelector(
|
||||
getConversationSelector,
|
||||
getConversationUuidsStoppingSend,
|
||||
getConversationServiceIdsStoppingSend,
|
||||
(
|
||||
conversationSelector: GetConversationByIdType,
|
||||
uuids: ReadonlyArray<string>
|
||||
serviceIds: ReadonlyArray<ServiceIdString>
|
||||
): Array<ConversationType> => {
|
||||
const conversations = uuids.map(uuid => conversationSelector(uuid));
|
||||
const conversations = serviceIds.map(serviceId =>
|
||||
conversationSelector(serviceId)
|
||||
);
|
||||
return sortByTitle(conversations);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -14,7 +14,7 @@ import type {
|
|||
ConversationColorType,
|
||||
CustomColorType,
|
||||
} from '../../types/Colors';
|
||||
import type { UUIDStringType } from '../../types/UUID';
|
||||
import type { AciString } from '../../types/ServiceId';
|
||||
import { DEFAULT_CONVERSATION_COLOR } from '../../types/Colors';
|
||||
import { getPreferredReactionEmoji as getPreferredReactionEmojiFromStoredValue } from '../../reactions/preferredReactionEmoji';
|
||||
import { isBeta } from '../../util/version';
|
||||
|
@ -60,7 +60,7 @@ const isRemoteConfigBucketEnabled = (
|
|||
config: Readonly<ConfigMapType>,
|
||||
name: ConfigKeyType,
|
||||
e164: string | undefined,
|
||||
uuid: UUIDStringType | undefined
|
||||
uuid: AciString | undefined
|
||||
): boolean => {
|
||||
const flagValue = config[name]?.value;
|
||||
return innerIsBucketValueEnabled(name, flagValue, e164, uuid);
|
||||
|
@ -140,7 +140,7 @@ export const getStoriesEnabled = createSelector(
|
|||
state: ItemsStateType,
|
||||
remoteConfig: ConfigMapType,
|
||||
e164: string | undefined,
|
||||
aci: UUIDStringType | undefined
|
||||
aci: AciString | undefined
|
||||
): boolean => {
|
||||
if (state.hasStoriesDisabled) {
|
||||
return false;
|
||||
|
|
|
@ -42,7 +42,11 @@ import type { PropsType as ProfileChangeNotificationPropsType } from '../../comp
|
|||
import type { QuotedAttachmentType } from '../../components/conversation/Quote';
|
||||
|
||||
import { getDomain, isStickerPack } from '../../types/LinkPreview';
|
||||
import type { UUIDStringType } from '../../types/UUID';
|
||||
import type {
|
||||
AciString,
|
||||
PniString,
|
||||
ServiceIdString,
|
||||
} from '../../types/ServiceId';
|
||||
|
||||
import type { EmbeddedContactType } from '../../types/EmbeddedContact';
|
||||
import { embeddedContactSelector } from '../../types/EmbeddedContact';
|
||||
|
@ -82,9 +86,9 @@ import {
|
|||
getIntl,
|
||||
getRegionCode,
|
||||
getUserACI,
|
||||
getUserPNI,
|
||||
getUserConversationId,
|
||||
getUserNumber,
|
||||
getUserPNI,
|
||||
} from './user';
|
||||
|
||||
import type {
|
||||
|
@ -161,8 +165,8 @@ export type GetPropsForBubbleOptions = Readonly<{
|
|||
conversationSelector: GetConversationByIdType;
|
||||
ourConversationId?: string;
|
||||
ourNumber?: string;
|
||||
ourACI?: UUIDStringType;
|
||||
ourPNI?: UUIDStringType;
|
||||
ourAci: AciString | undefined;
|
||||
ourPni: PniString | undefined;
|
||||
targetedMessageId?: string;
|
||||
targetedMessageCounter?: number;
|
||||
selectedMessageIds: ReadonlyArray<string> | undefined;
|
||||
|
@ -214,8 +218,8 @@ export function getSourceDevice(
|
|||
|
||||
export function getSourceUuid(
|
||||
message: Pick<MessageAttributesType, 'type' | 'sourceUuid'>,
|
||||
ourACI: string | undefined
|
||||
): string | undefined {
|
||||
ourAci: AciString | undefined
|
||||
): ServiceIdString | undefined {
|
||||
if (isIncoming(message)) {
|
||||
return message.sourceUuid;
|
||||
}
|
||||
|
@ -225,16 +229,12 @@ export function getSourceUuid(
|
|||
);
|
||||
}
|
||||
|
||||
return ourACI;
|
||||
return ourAci;
|
||||
}
|
||||
|
||||
export type GetContactOptions = Pick<
|
||||
GetPropsForBubbleOptions,
|
||||
| 'conversationSelector'
|
||||
| 'ourConversationId'
|
||||
| 'ourNumber'
|
||||
| 'ourACI'
|
||||
| 'ourPNI'
|
||||
'conversationSelector' | 'ourConversationId' | 'ourNumber' | 'ourAci'
|
||||
>;
|
||||
|
||||
export function getContactId(
|
||||
|
@ -243,11 +243,11 @@ export function getContactId(
|
|||
conversationSelector,
|
||||
ourConversationId,
|
||||
ourNumber,
|
||||
ourACI,
|
||||
ourAci,
|
||||
}: GetContactOptions
|
||||
): string | undefined {
|
||||
const source = getSource(message, ourNumber);
|
||||
const sourceUuid = getSourceUuid(message, ourACI);
|
||||
const sourceUuid = getSourceUuid(message, ourAci);
|
||||
|
||||
if (!source && !sourceUuid) {
|
||||
return ourConversationId;
|
||||
|
@ -264,11 +264,11 @@ export function getContact(
|
|||
conversationSelector,
|
||||
ourConversationId,
|
||||
ourNumber,
|
||||
ourACI,
|
||||
ourAci,
|
||||
}: GetContactOptions
|
||||
): ConversationType {
|
||||
const source = getSource(message, ourNumber);
|
||||
const sourceUuid = getSourceUuid(message, ourACI);
|
||||
const sourceUuid = getSourceUuid(message, ourAci);
|
||||
|
||||
if (!source && !sourceUuid) {
|
||||
return conversationSelector(ourConversationId);
|
||||
|
@ -578,8 +578,8 @@ export type GetPropsForMessageOptions = Pick<
|
|||
GetPropsForBubbleOptions,
|
||||
| 'conversationSelector'
|
||||
| 'ourConversationId'
|
||||
| 'ourACI'
|
||||
| 'ourPNI'
|
||||
| 'ourAci'
|
||||
| 'ourPni'
|
||||
| 'ourNumber'
|
||||
| 'targetedMessageId'
|
||||
| 'targetedMessageCounter'
|
||||
|
@ -675,7 +675,7 @@ export const getPropsForMessage = (
|
|||
conversationSelector,
|
||||
ourConversationId,
|
||||
ourNumber,
|
||||
ourACI,
|
||||
ourAci,
|
||||
regionCode,
|
||||
targetedMessageId,
|
||||
targetedMessageCounter,
|
||||
|
@ -706,7 +706,7 @@ export const getPropsForMessage = (
|
|||
conversationSelector,
|
||||
ourConversationId,
|
||||
ourNumber,
|
||||
ourACI,
|
||||
ourAci,
|
||||
});
|
||||
const contactNameColor = contactNameColorSelector(conversationId, authorId);
|
||||
|
||||
|
@ -788,8 +788,8 @@ export const getMessagePropsSelector = createSelector(
|
|||
(
|
||||
conversationSelector,
|
||||
ourConversationId,
|
||||
ourACI,
|
||||
ourPNI,
|
||||
ourAci,
|
||||
ourPni,
|
||||
ourNumber,
|
||||
regionCode,
|
||||
accountSelector,
|
||||
|
@ -804,8 +804,8 @@ export const getMessagePropsSelector = createSelector(
|
|||
conversationSelector,
|
||||
ourConversationId,
|
||||
ourNumber,
|
||||
ourACI,
|
||||
ourPNI,
|
||||
ourAci,
|
||||
ourPni,
|
||||
regionCode,
|
||||
targetedMessageCounter: targetedMessage?.counter,
|
||||
targetedMessageId: targetedMessage?.id,
|
||||
|
@ -1008,7 +1008,7 @@ export function isGroupV2Change(message: MessageWithUIFieldsType): boolean {
|
|||
|
||||
function getPropsForGroupV2Change(
|
||||
message: MessageWithUIFieldsType,
|
||||
{ conversationSelector, ourACI, ourPNI }: GetPropsForBubbleOptions
|
||||
{ conversationSelector, ourAci, ourPni }: GetPropsForBubbleOptions
|
||||
): GroupsV2Props {
|
||||
const change = message.groupV2Change;
|
||||
|
||||
|
@ -1024,8 +1024,8 @@ function getPropsForGroupV2Change(
|
|||
groupName: conversation?.type === 'group' ? conversation?.name : undefined,
|
||||
groupMemberships: conversation.memberships,
|
||||
groupBannedMemberships: conversation.bannedMemberships,
|
||||
ourACI,
|
||||
ourPNI,
|
||||
ourAci,
|
||||
ourPni,
|
||||
change,
|
||||
};
|
||||
}
|
||||
|
@ -1611,7 +1611,7 @@ export function getMessagePropStatus(
|
|||
export function getPropsForEmbeddedContact(
|
||||
message: MessageWithUIFieldsType,
|
||||
regionCode: string | undefined,
|
||||
accountSelector: (identifier?: string) => UUIDStringType | undefined
|
||||
accountSelector: (identifier?: string) => ServiceIdString | undefined
|
||||
): EmbeddedContactType | undefined {
|
||||
const contacts = message.contact;
|
||||
if (!contacts || !contacts.length) {
|
||||
|
@ -1911,8 +1911,8 @@ export const getMessageDetails = createSelector(
|
|||
i18n,
|
||||
regionCode,
|
||||
message,
|
||||
ourACI,
|
||||
ourPNI,
|
||||
ourAci,
|
||||
ourPni,
|
||||
ourConversationId,
|
||||
ourNumber,
|
||||
selectedMessageIds
|
||||
|
@ -1943,7 +1943,7 @@ export const getMessageDetails = createSelector(
|
|||
conversationSelector,
|
||||
ourConversationId,
|
||||
ourNumber,
|
||||
ourACI,
|
||||
ourAci,
|
||||
}),
|
||||
].filter(isNotNil);
|
||||
} else if (!isEmpty(sendStateByConversationId)) {
|
||||
|
@ -1985,15 +1985,15 @@ export const getMessageDetails = createSelector(
|
|||
// If an error has a specific number it's associated with, we'll show it next to
|
||||
// that contact. Otherwise, it will be a standalone entry.
|
||||
const errors = allErrors.filter(error =>
|
||||
Boolean(error.identifier || error.number)
|
||||
Boolean(error.serviceId || error.number)
|
||||
);
|
||||
const errorsGroupedById = groupBy(allErrors, error => {
|
||||
const identifier = error.identifier || error.number;
|
||||
if (!identifier) {
|
||||
const serviceId = error.serviceId || error.number;
|
||||
if (!serviceId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return window.ConversationController.getConversationId(identifier);
|
||||
return window.ConversationController.getConversationId(serviceId);
|
||||
});
|
||||
|
||||
const hasUnidentifiedDeliveryIndicators = window.storage.get(
|
||||
|
@ -2045,10 +2045,10 @@ export const getMessageDetails = createSelector(
|
|||
accountSelector,
|
||||
contactNameColorSelector,
|
||||
conversationSelector,
|
||||
ourACI,
|
||||
ourAci,
|
||||
ourPni,
|
||||
ourConversationId,
|
||||
ourNumber,
|
||||
ourPNI,
|
||||
regionCode,
|
||||
selectedMessageIds,
|
||||
}),
|
||||
|
|
|
@ -31,6 +31,8 @@ export const getDistributionListsWithMembers = createSelector(
|
|||
): Array<StoryDistributionListWithMembersDataType> =>
|
||||
distributionLists.map(list => ({
|
||||
...list,
|
||||
members: list.memberUuids.map(uuid => conversationSelector(uuid)),
|
||||
members: list.memberServiceIds.map(serviceId =>
|
||||
conversationSelector(serviceId)
|
||||
),
|
||||
}))
|
||||
);
|
||||
|
|
|
@ -42,8 +42,8 @@ export const getTimelineItem = (
|
|||
const conversationSelector = getConversationSelector(state);
|
||||
const regionCode = getRegionCode(state);
|
||||
const ourNumber = getUserNumber(state);
|
||||
const ourACI = getUserACI(state);
|
||||
const ourPNI = getUserPNI(state);
|
||||
const ourAci = getUserACI(state);
|
||||
const ourPni = getUserPNI(state);
|
||||
const ourConversationId = getUserConversationId(state);
|
||||
const callSelector = getCallSelector(state);
|
||||
const callHistorySelector = getCallHistorySelector(state);
|
||||
|
@ -56,8 +56,8 @@ export const getTimelineItem = (
|
|||
conversationSelector,
|
||||
ourConversationId,
|
||||
ourNumber,
|
||||
ourACI,
|
||||
ourPNI,
|
||||
ourAci,
|
||||
ourPni,
|
||||
regionCode,
|
||||
targetedMessageId: targetedMessage?.id,
|
||||
targetedMessageCounter: targetedMessage?.counter,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import { createSelector } from 'reselect';
|
||||
|
||||
import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||
import type { UUIDStringType } from '../../types/UUID';
|
||||
import type { AciString, PniString } from '../../types/ServiceId';
|
||||
import type { LocaleMessagesType } from '../../types/I18N';
|
||||
import type { MenuOptionsType } from '../../types/menu';
|
||||
|
||||
|
@ -37,12 +37,12 @@ export const getUserConversationId = createSelector(
|
|||
|
||||
export const getUserACI = createSelector(
|
||||
getUser,
|
||||
(state: UserStateType): UUIDStringType | undefined => state.ourACI
|
||||
(state: UserStateType): AciString | undefined => state.ourAci
|
||||
);
|
||||
|
||||
export const getUserPNI = createSelector(
|
||||
getUser,
|
||||
(state: UserStateType): UUIDStringType | undefined => state.ourPNI
|
||||
(state: UserStateType): PniString | undefined => state.ourPni
|
||||
);
|
||||
|
||||
export const getIntl = createSelector(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue