background.ts: Introduce types for redux initialState

This commit is contained in:
Scott Nonnenberg 2022-02-23 10:48:40 -08:00 committed by GitHub
parent 3673b6d101
commit 4763831d3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 315 additions and 238 deletions

View file

@ -155,7 +155,7 @@
"react-sortable-hoc": "1.9.1",
"react-virtualized": "9.21.0",
"read-last-lines": "1.8.0",
"redux": "4.0.2",
"redux": "4.1.2",
"redux-logger": "3.0.6",
"redux-promise-middleware": "6.1.0",
"redux-thunk": "2.3.0",

View file

@ -91,7 +91,6 @@ import { RotateSignedPreKeyListener } from './textsecure/RotateSignedPreKeyListe
import { isDirectConversation, isGroupV2 } from './util/whatTypeOfConversation';
import { BackOff, FIBONACCI_TIMEOUTS } from './util/BackOff';
import { AppViewType } from './state/ducks/app';
import { UsernameSaveState } from './state/ducks/conversationsEnums';
import type { BadgesStateType } from './state/ducks/badges';
import { badgeImageFileDownloader } from './badges/badgeImageFileDownloader';
import { isIncoming } from './state/selectors/message';
@ -116,7 +115,6 @@ import { ReadStatus } from './messages/MessageReadStatus';
import type { SendStateByConversationId } from './messages/MessageSendState';
import { SendStatus } from './messages/MessageSendState';
import * as AttachmentDownloads from './messageModifiers/AttachmentDownloads';
import * as preferredReactions from './state/ducks/preferredReactions';
import * as Conversation from './types/Conversation';
import * as Stickers from './types/Stickers';
import * as Errors from './types/errors';
@ -128,10 +126,7 @@ import { RemoveAllConfiguration } from './types/RemoveAllConfiguration';
import { isValidUuid, UUIDKind } from './types/UUID';
import type { UUID } from './types/UUID';
import * as log from './logging/log';
import {
loadRecentEmojis,
getEmojiReducerState,
} from './util/loadRecentEmojis';
import { loadRecentEmojis } from './util/loadRecentEmojis';
import { deleteAllLogs } from './util/deleteAllLogs';
import { ToastCaptchaFailed } from './components/ToastCaptchaFailed';
import { ToastCaptchaSolved } from './components/ToastCaptchaSolved';
@ -143,6 +138,7 @@ import { deliveryReceiptsJobQueue } from './jobs/deliveryReceiptsJobQueue';
import { updateOurUsername } from './util/updateOurUsername';
import { ReactionSource } from './reactions/ReactionSource';
import { singleProtoJobQueue } from './jobs/singleProtoJobQueue';
import { getInitialState } from './state/getInitialState';
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
@ -890,70 +886,7 @@ export async function startApp(): Promise<void> {
function initializeRedux() {
// Here we set up a full redux store with initial state for our LeftPane Root
const convoCollection = window.getConversations();
const conversations = convoCollection.map(conversation =>
conversation.format()
);
const ourNumber = window.textsecure.storage.user.getNumber();
const ourUuid = window.textsecure.storage.user.getUuid()?.toString();
const ourConversationId =
window.ConversationController.getOurConversationId();
const ourDeviceId = window.textsecure.storage.user.getDeviceId();
const themeSetting = window.Events.getThemeSetting();
const theme = themeSetting === 'system' ? window.systemTheme : themeSetting;
// TODO: DESKTOP-3125
const initialState = {
badges: initialBadgesState,
conversations: {
conversationLookup: window.Signal.Util.makeLookup(conversations, 'id'),
conversationsByE164: window.Signal.Util.makeLookup(
conversations,
'e164'
),
conversationsByUuid: window.Signal.Util.makeLookup(
conversations,
'uuid'
),
conversationsByGroupId: window.Signal.Util.makeLookup(
conversations,
'groupId'
),
conversationsByUsername: window.Signal.Util.makeLookup(
conversations,
'username'
),
messagesByConversation: {},
messagesLookup: {},
verificationDataByConversation: {},
selectedConversationId: undefined,
selectedMessage: undefined,
selectedMessageCounter: 0,
selectedConversationPanelDepth: 0,
selectedConversationTitle: '',
showArchived: false,
usernameSaveState: UsernameSaveState.None,
},
emojis: getEmojiReducerState(),
items: window.storage.getItemsState(),
preferredReactions: preferredReactions.getInitialState(),
stickers: Stickers.getInitialState(),
user: {
attachmentsPath: window.baseAttachmentsPath,
stickersPath: window.baseStickersPath,
tempPath: window.baseTempPath,
regionCode: window.storage.get('regionCode'),
ourConversationId,
ourDeviceId,
ourNumber,
ourUuid,
platform: window.platform,
i18n: window.i18n,
interactionMode: window.getInteractionMode(),
theme,
version: window.getVersion(),
},
};
const initialState = getInitialState({ badges: initialBadgesState });
const store = window.Signal.State.createStore(initialState);
window.reduxStore = store;

View file

@ -40,15 +40,10 @@ import type {
StartCallType,
} from '../state/ducks/calling';
import type { LocalizerType, ThemeType } from '../types/Util';
import type { UUIDStringType } from '../types/UUID';
import { missingCaseError } from '../util/missingCaseError';
const GROUP_CALL_RING_DURATION = 60 * 1000;
type MeType = ConversationType & {
uuid: UUIDStringType;
};
export type PropsType = {
activeCall?: ActiveCallType;
availableCameras: Array<MediaDeviceInfo>;
@ -83,7 +78,7 @@ export type PropsType = {
declineCall: (_: DeclineCallType) => void;
i18n: LocalizerType;
isGroupCallOutboundRingEnabled: boolean;
me: MeType;
me: ConversationType;
notifyForCall: (title: string, isVideoCall: boolean) => unknown;
openSystemPreferencesAction: () => unknown;
playRingtone: () => unknown;

View file

@ -150,14 +150,14 @@ const createProps = (
getPresentingSources: action('get-presenting-sources'),
hangUpActiveCall: action('hang-up'),
i18n,
me: {
me: getDefaultConversation({
color: AvatarColors[1],
id: '6146087e-f7ef-457e-9a8d-47df1fdd6b25',
name: 'Morty Smith',
profileName: 'Morty Smith',
title: 'Morty Smith',
uuid: '3c134598-eecb-42ab-9ad3-2b0873f771b2',
},
}),
openSystemPreferencesAction: action('open-system-preferences-action'),
setGroupCallVideoRequest: action('set-group-call-video-request'),
setLocalAudio: action('set-local-audio'),

View file

@ -28,7 +28,6 @@ import {
GroupCallConnectionState,
GroupCallJoinState,
} from '../types/Calling';
import type { AvatarColorType } from '../types/Colors';
import { AvatarColors } from '../types/Colors';
import type { ConversationType } from '../state/ducks/conversations';
import { CallingToastManager } from './CallingToastManager';
@ -37,7 +36,6 @@ import { GroupCallRemoteParticipants } from './GroupCallRemoteParticipants';
import type { LocalizerType } from '../types/Util';
import { NeedsScreenRecordingPermissionsModal } from './NeedsScreenRecordingPermissionsModal';
import { missingCaseError } from '../util/missingCaseError';
import type { UUIDStringType } from '../types/UUID';
import * as KeyboardLayout from '../services/keyboardLayout';
import { useActivateSpeakerViewOnPresenting } from '../hooks/useActivateSpeakerViewOnPresenting';
@ -49,16 +47,7 @@ export type PropsType = {
hangUpActiveCall: () => void;
i18n: LocalizerType;
joinedAt?: number;
me: {
avatarPath?: string;
color?: AvatarColorType;
id: string;
name?: string;
phoneNumber?: string;
profileName?: string;
title: string;
uuid: UUIDStringType;
};
me: ConversationType;
openSystemPreferencesAction: () => unknown;
setGroupCallVideoRequest: (_: Array<GroupCallVideoRequest>) => void;
setLocalAudio: (_: SetLocalAudioType) => void;

View file

@ -61,11 +61,13 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => {
isGroupCall,
isGroupCallOutboundRingEnabled: true,
isCallFull: boolean('isCallFull', overrideProps.isCallFull || false),
me: overrideProps.me || {
color: AvatarColors[0],
id: UUID.generate().toString(),
uuid: UUID.generate().toString(),
},
me:
overrideProps.me ||
getDefaultConversation({
color: AvatarColors[0],
id: UUID.generate().toString(),
uuid: UUID.generate().toString(),
}),
onCallCanceled: action('on-call-canceled'),
onJoinCall: action('on-join-call'),
outgoingRing: boolean('outgoingRing', Boolean(overrideProps.outgoingRing)),
@ -105,12 +107,12 @@ story.add('No Camera, no avatar', () => {
story.add('No Camera, local avatar', () => {
const props = createProps({
availableCameras: [],
me: {
me: getDefaultConversation({
avatarPath: '/fixtures/kitten-4-112-112.jpg',
color: AvatarColors[0],
id: UUID.generate().toString(),
uuid: UUID.generate().toString(),
},
}),
});
return <CallingLobby {...props} />;
});
@ -146,10 +148,10 @@ story.add('Group Call - 1 peeked participant (self)', () => {
const uuid = UUID.generate().toString();
const props = createProps({
isGroupCall: true,
me: {
me: getDefaultConversation({
id: UUID.generate().toString(),
uuid,
},
}),
peekedParticipants: [fakePeekedParticipant({ title: 'Ash', uuid })],
});
return <CallingLobby {...props} />;

View file

@ -19,9 +19,7 @@ import {
CallingLobbyJoinButton,
CallingLobbyJoinButtonVariant,
} from './CallingLobbyJoinButton';
import type { AvatarColorType } from '../types/Colors';
import type { LocalizerType } from '../types/Util';
import type { UUIDStringType } from '../types/UUID';
import { useIsOnline } from '../hooks/useIsOnline';
import * as KeyboardLayout from '../services/keyboardLayout';
import type { ConversationType } from '../state/ducks/conversations';
@ -51,12 +49,7 @@ export type PropsType = {
isGroupCall: boolean;
isGroupCallOutboundRingEnabled: boolean;
isCallFull?: boolean;
me: {
avatarPath?: string;
id: string;
color?: AvatarColorType;
uuid: UUIDStringType;
};
me: Readonly<Pick<ConversationType, 'avatarPath' | 'color' | 'id' | 'uuid'>>;
onCallCanceled: () => void;
onJoinCall: () => void;
outgoingRing: boolean;

View file

@ -1,4 +1,4 @@
// Copyright 2020-2021 Signal Messenger, LLC
// Copyright 2020-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable react/no-array-index-key */
@ -24,7 +24,7 @@ type ParticipantType = ConversationType & {
export type PropsType = {
readonly i18n: LocalizerType;
readonly onClose: () => void;
readonly ourUuid: string;
readonly ourUuid: string | undefined;
readonly participants: Array<ParticipantType>;
};
@ -113,7 +113,7 @@ export const CallingParticipantsList = React.memo(
sharedGroupNames={participant.sharedGroupNames}
size={32}
/>
{participant.uuid === ourUuid ? (
{ourUuid && participant.uuid === ourUuid ? (
<span className="module-calling-participants-list__name">
{i18n('you')}
</span>

View file

@ -88,7 +88,7 @@ export type PropsType = {
preferredWidthFromStorage: number;
selectedConversationId: undefined | string;
selectedMessageId: undefined | string;
regionCode: string;
regionCode: string | undefined;
challengeStatus: 'idle' | 'required' | 'pending';
setChallengeStatus: (status: 'idle') => void;
crashReportCount: number;

View file

@ -1,4 +1,4 @@
// Copyright 2020-2021 Signal Messenger, LLC
// Copyright 2020-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReactElement } from 'react';
@ -22,7 +22,7 @@ import { Modal } from '../Modal';
export type PropsDataType = {
groupName?: string;
ourUuid: UUIDStringType;
ourUuid?: UUIDStringType;
change: GroupV2ChangeType;
};

View file

@ -24,7 +24,7 @@ export type LeftPaneComposePropsType = {
composeContacts: ReadonlyArray<ContactListItemConversationType>;
composeGroups: ReadonlyArray<ConversationListItemPropsType>;
regionCode: string;
regionCode: string | undefined;
searchTerm: string;
isFetchingUsername: boolean;
isUsernamesEnabled: boolean;
@ -355,7 +355,7 @@ function focusRef(el: HTMLElement | null) {
function parsePhoneNumber(
str: string,
regionCode: string
regionCode: string | undefined
): undefined | PhoneNumber {
let result: PhoneNumber;
try {

View file

@ -1,4 +1,4 @@
// Copyright 2020 Signal Messenger, LLC
// Copyright 2020-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { LocalizerType } from './types/Util';
@ -20,7 +20,7 @@ export type StringRendererType<T> = (
export type RenderOptionsType<T> = {
from?: UUIDStringType;
i18n: LocalizerType;
ourUuid: UUIDStringType;
ourUuid?: UUIDStringType;
renderContact: SmartContactRendererType<T>;
renderString: StringRendererType<T>;
};
@ -47,7 +47,7 @@ export function renderChangeDetail<T>(
options: RenderOptionsType<T>
): T | string {
const { from, i18n, ourUuid, renderContact, renderString } = options;
const fromYou = Boolean(from && from === ourUuid);
const fromYou = Boolean(from && ourUuid && from === ourUuid);
if (detail.type === 'create') {
if (fromYou) {
@ -209,7 +209,7 @@ export function renderChangeDetail<T>(
}
if (detail.type === 'member-add') {
const { uuid } = detail;
const weAreJoiner = uuid === ourUuid;
const weAreJoiner = Boolean(ourUuid && uuid === ourUuid);
if (weAreJoiner) {
if (fromYou) {
@ -239,8 +239,8 @@ export function renderChangeDetail<T>(
}
if (detail.type === 'member-add-from-invite') {
const { uuid, inviter } = detail;
const weAreJoiner = uuid === ourUuid;
const weAreInviter = Boolean(inviter && inviter === ourUuid);
const weAreJoiner = Boolean(ourUuid && uuid === ourUuid);
const weAreInviter = Boolean(inviter && ourUuid && inviter === ourUuid);
if (!from || from !== uuid) {
if (weAreJoiner) {
@ -302,7 +302,7 @@ export function renderChangeDetail<T>(
if (detail.type === 'member-add-from-link') {
const { uuid } = detail;
if (fromYou && uuid === ourUuid) {
if (fromYou && ourUuid && uuid === ourUuid) {
return renderString('GroupV2--member-add-from-link--you--you', i18n);
}
if (from && uuid === from) {
@ -320,7 +320,7 @@ export function renderChangeDetail<T>(
}
if (detail.type === 'member-add-from-admin-approval') {
const { uuid } = detail;
const weAreJoiner = uuid === ourUuid;
const weAreJoiner = Boolean(ourUuid && uuid === ourUuid);
if (weAreJoiner) {
if (from) {
@ -371,7 +371,7 @@ export function renderChangeDetail<T>(
}
if (detail.type === 'member-remove') {
const { uuid } = detail;
const weAreLeaver = uuid === ourUuid;
const weAreLeaver = Boolean(ourUuid && uuid === ourUuid);
if (weAreLeaver) {
if (fromYou) {
@ -407,7 +407,7 @@ export function renderChangeDetail<T>(
}
if (detail.type === 'member-privilege') {
const { uuid, newPrivilege } = detail;
const weAreMember = uuid === ourUuid;
const weAreMember = Boolean(ourUuid && uuid === ourUuid);
if (newPrivilege === RoleEnum.ADMINISTRATOR) {
if (weAreMember) {
@ -493,7 +493,7 @@ export function renderChangeDetail<T>(
}
if (detail.type === 'pending-add-one') {
const { uuid } = detail;
const weAreInvited = uuid === ourUuid;
const weAreInvited = Boolean(ourUuid && uuid === ourUuid);
if (weAreInvited) {
if (from) {
return renderString('GroupV2--pending-add--one--you--other', i18n, [
@ -534,8 +534,8 @@ export function renderChangeDetail<T>(
}
if (detail.type === 'pending-remove-one') {
const { inviter, uuid } = detail;
const weAreInviter = Boolean(inviter && inviter === ourUuid);
const weAreInvited = uuid === ourUuid;
const weAreInviter = Boolean(inviter && ourUuid && inviter === ourUuid);
const weAreInvited = Boolean(ourUuid && uuid === ourUuid);
const sentByInvited = Boolean(from && from === uuid);
const sentByInviter = Boolean(from && inviter && from === inviter);
@ -629,7 +629,7 @@ export function renderChangeDetail<T>(
}
if (detail.type === 'pending-remove-many') {
const { count, inviter } = detail;
const weAreInviter = Boolean(inviter && inviter === ourUuid);
const weAreInviter = Boolean(inviter && ourUuid && inviter === ourUuid);
if (weAreInviter) {
if (fromYou) {
@ -709,7 +709,7 @@ export function renderChangeDetail<T>(
}
if (detail.type === 'admin-approval-add-one') {
const { uuid } = detail;
const weAreJoiner = uuid === ourUuid;
const weAreJoiner = Boolean(ourUuid && uuid === ourUuid);
if (weAreJoiner) {
return renderString('GroupV2--admin-approval-add-one--you', i18n);
@ -720,7 +720,7 @@ export function renderChangeDetail<T>(
}
if (detail.type === 'admin-approval-remove-one') {
const { uuid } = detail;
const weAreJoiner = uuid === ourUuid;
const weAreJoiner = Boolean(ourUuid && uuid === ourUuid);
if (weAreJoiner) {
if (fromYou) {

View file

@ -1,4 +1,4 @@
// Copyright 2021 Signal Messenger, LLC
// Copyright 2021-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { makeEnumParser } from '../util/enum';
@ -150,10 +150,12 @@ export const someSendStatus = (
export const isMessageJustForMe = (
sendStateByConversationId: undefined | Readonly<SendStateByConversationId>,
ourConversationId: string
ourConversationId: string | undefined
): boolean => {
const conversationIds = Object.keys(sendStateByConversationId || {});
return (
conversationIds.length === 1 && conversationIds[0] === ourConversationId
return Boolean(
ourConversationId &&
conversationIds.length === 1 &&
conversationIds[0] === ourConversationId
);
};

View file

@ -389,7 +389,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
},
contactNameColorSelector: (
conversationId: string,
contactId: string
contactId: string | undefined
) => {
const state = window.reduxStore.getState();
const contactNameColorSelector = getContactNameColorSelector(state);

View file

@ -1,9 +1,9 @@
// Copyright 2019-2021 Signal Messenger, LLC
// Copyright 2019-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable no-console */
import type { DeepPartial, Store } from 'redux';
import type { Store } from 'redux';
import { applyMiddleware, createStore as reduxCreateStore } from 'redux';
import promise from 'redux-promise-middleware';
@ -54,5 +54,5 @@ const middlewareList = [
const enhancer = applyMiddleware(...middlewareList);
export const createStore = (
initialState: DeepPartial<StateType>
initialState: Readonly<StateType>
): Store<StateType> => reduxCreateStore(reducer, initialState, enhancer);

View file

@ -48,7 +48,7 @@ function setActiveAudioID(
// Reducer
function getEmptyState(): AudioPlayerStateType {
export function getEmptyState(): AudioPlayerStateType {
return {
activeAudioID: undefined,
activeAudioContext: undefined,

View file

@ -205,7 +205,7 @@ function errorRecording(
// Reducer
function getEmptyState(): AudioPlayerStateType {
export function getEmptyState(): AudioPlayerStateType {
return {
recordingState: RecordingState.Idle,
};

View file

@ -82,12 +82,12 @@ function updateOrCreate(
// Reducer
export function getInitialState(): BadgesStateType {
export function getEmptyState(): BadgesStateType {
return { byId: {} };
}
export function reducer(
state: Readonly<BadgesStateType> = getInitialState(),
state: Readonly<BadgesStateType> = getEmptyState(),
action: Readonly<ImageFileDownloadedActionType | UpdateOrCreateActionType>
): BadgesStateType {
switch (action.type) {

View file

@ -832,11 +832,14 @@ function groupCallStateChange(
didSomeoneStartPresenting = false;
}
const { ourUuid } = getState().user;
strictAssert(ourUuid, 'groupCallStateChange failed to fetch our uuid');
dispatch({
type: GROUP_CALL_STATE_CHANGE,
payload: {
...payload,
ourUuid: getState().user.ourUuid,
ourUuid,
},
});

View file

@ -33,7 +33,7 @@ export const actions = {
// Reducer
function getEmptyState(): ExpirationStateType {
export function getEmptyState(): ExpirationStateType {
return {
hasExpired: false,
};

View file

@ -274,7 +274,7 @@ function savePreferredLeftPaneWidth(
// Reducer
function getEmptyState(): ItemsStateType {
export function getEmptyState(): ItemsStateType {
return {
defaultConversationColor: {
color: ConversationColors[0],

View file

@ -200,12 +200,12 @@ function selectDraftEmojiToBeReplaced(
// Reducer
export function getInitialState(): PreferredReactionsStateType {
export function getEmptyState(): PreferredReactionsStateType {
return {};
}
export function reducer(
state: Readonly<PreferredReactionsStateType> = getInitialState(),
state: Readonly<PreferredReactionsStateType> = getEmptyState(),
action: Readonly<
| CancelCustomizePreferredReactionsModalActionType
| DeselectDraftEmojiActionType

View file

@ -157,7 +157,7 @@ export const actions = {
toggleVerified,
};
function getEmptyState(): SafetyNumberStateType {
export function getEmptyState(): SafetyNumberStateType {
return {
contacts: {},
};

View file

@ -1,4 +1,4 @@
// Copyright 2019-2021 Signal Messenger, LLC
// Copyright 2019-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ThunkAction, ThunkDispatch } from 'redux-thunk';
@ -24,6 +24,7 @@ import type {
} from './conversations';
import { getQuery, getSearchConversation } from '../selectors/search';
import { getIntl, getUserConversationId } from '../selectors/user';
import { strictAssert } from '../../util/assert';
const {
searchConversations: dataSearchConversations,
@ -157,11 +158,16 @@ function updateSearchTerm(
});
const state = getState();
const ourConversationId = getUserConversationId(state);
strictAssert(
ourConversationId,
'updateSearchTerm our conversation is missing'
);
doSearch({
dispatch,
noteToSelf: getIntl(state)('noteToSelf').toLowerCase(),
ourConversationId: getUserConversationId(state),
ourConversationId,
query: getQuery(state),
searchConversationId: getSearchConversation(state)?.id,
});

View file

@ -324,7 +324,7 @@ async function doUseSticker(
// Reducer
function getEmptyState(): StickersStateType {
export function getEmptyState(): StickersStateType {
return {
installedPack: null,
packs: {},

View file

@ -123,7 +123,7 @@ export const actions = {
// Reducer
function getEmptyState(): UpdatesStateType {
export function getEmptyState(): UpdatesStateType {
return {
dialogType: DialogType.None,
didSnooze: false,

View file

@ -14,12 +14,12 @@ export type UserStateType = {
attachmentsPath: string;
stickersPath: string;
tempPath: string;
ourConversationId: string;
ourDeviceId: number;
ourUuid: UUIDStringType;
ourNumber: string;
ourConversationId: string | undefined;
ourDeviceId: number | undefined;
ourUuid: UUIDStringType | undefined;
ourNumber: string | undefined;
platform: string;
regionCode: string;
regionCode: string | undefined;
i18n: LocalizerType;
interactionMode: 'mouse' | 'keyboard';
theme: ThemeType;

108
ts/state/getInitialState.ts Normal file
View file

@ -0,0 +1,108 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { getEmptyState as accounts } from './ducks/accounts';
import { getEmptyState as app } from './ducks/app';
import { getEmptyState as audioPlayer } from './ducks/audioPlayer';
import { getEmptyState as audioRecorder } from './ducks/audioRecorder';
import { getEmptyState as calling } from './ducks/calling';
import { getEmptyState as composer } from './ducks/composer';
import { getEmptyState as conversations } from './ducks/conversations';
import { getEmptyState as crashReports } from './ducks/crashReports';
import { getEmptyState as expiration } from './ducks/expiration';
import { getEmptyState as globalModals } from './ducks/globalModals';
import { getEmptyState as linkPreviews } from './ducks/linkPreviews';
import { getEmptyState as network } from './ducks/network';
import { getEmptyState as preferredReactions } from './ducks/preferredReactions';
import { getEmptyState as safetyNumber } from './ducks/safetyNumber';
import { getEmptyState as search } from './ducks/search';
import { getEmptyState as updates } from './ducks/updates';
import { getEmptyState as user } from './ducks/user';
import type { StateType } from './reducer';
import type { BadgesStateType } from './ducks/badges';
import { getInitialState as stickers } from '../types/Stickers';
import { getEmojiReducerState as emojis } from '../util/loadRecentEmojis';
export function getInitialState({
badges,
}: {
badges: BadgesStateType;
}): StateType {
const items = window.storage.getItemsState();
const convoCollection = window.getConversations();
const formattedConversations = convoCollection.map(conversation =>
conversation.format()
);
const ourNumber = window.textsecure.storage.user.getNumber();
const ourUuid = window.textsecure.storage.user.getUuid()?.toString();
const ourConversationId =
window.ConversationController.getOurConversationId();
const ourDeviceId = window.textsecure.storage.user.getDeviceId();
const themeSetting = window.Events.getThemeSetting();
const theme = themeSetting === 'system' ? window.systemTheme : themeSetting;
return {
accounts: accounts(),
app: app(),
audioPlayer: audioPlayer(),
audioRecorder: audioRecorder(),
badges,
calling: calling(),
composer: composer(),
conversations: {
...conversations(),
conversationLookup: window.Signal.Util.makeLookup(
formattedConversations,
'id'
),
conversationsByE164: window.Signal.Util.makeLookup(
formattedConversations,
'e164'
),
conversationsByUuid: window.Signal.Util.makeLookup(
formattedConversations,
'uuid'
),
conversationsByGroupId: window.Signal.Util.makeLookup(
formattedConversations,
'groupId'
),
conversationsByUsername: window.Signal.Util.makeLookup(
formattedConversations,
'username'
),
},
crashReports: crashReports(),
emojis: emojis(),
expiration: expiration(),
globalModals: globalModals(),
items,
linkPreviews: linkPreviews(),
network: network(),
preferredReactions: preferredReactions(),
safetyNumber: safetyNumber(),
search: search(),
stickers: stickers(),
updates: updates(),
user: {
...user(),
attachmentsPath: window.baseAttachmentsPath,
stickersPath: window.baseStickersPath,
tempPath: window.baseTempPath,
regionCode: window.storage.get('regionCode'),
ourConversationId,
ourDeviceId,
ourNumber,
ourUuid,
platform: window.platform,
i18n: window.i18n,
interactionMode: window.getInteractionMode(),
theme,
version: window.getVersion(),
},
};
}

View file

@ -1,4 +1,4 @@
// Copyright 2020-2021 Signal Messenger, LLC
// Copyright 2020-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { createSelector } from 'reselect';
@ -62,7 +62,12 @@ export const getIncomingCall = createSelector(
getUserUuid,
(
callsByConversation: CallsByConversationType,
ourUuid: UUIDStringType
): undefined | DirectCallStateType | GroupCallStateType =>
getIncomingCallHelper(callsByConversation, ourUuid)
ourUuid: UUIDStringType | undefined
): undefined | DirectCallStateType | GroupCallStateType => {
if (!ourUuid) {
return undefined;
}
return getIncomingCallHelper(callsByConversation, ourUuid);
}
);

View file

@ -365,9 +365,13 @@ export const getMe = createSelector(
[getConversationLookup, getUserConversationId],
(
lookup: ConversationLookupType,
ourConversationId: string
ourConversationId: string | undefined
): ConversationType => {
return lookup[ourConversationId];
if (!ourConversationId) {
return getPlaceholderContact();
}
return lookup[ourConversationId] || getPlaceholderContact();
}
);
@ -654,8 +658,6 @@ export const getConversationSelector = createSelector(
): GetConversationByIdType => {
return (id?: string) => {
if (!id) {
log.warn(`getConversationSelector: Called with a falsey id ${id}`);
// This will return a placeholder contact
return selector(undefined);
}
@ -714,10 +716,10 @@ const getCachedConversationMemberColorsSelector = createSelector(
getUserConversationId,
(
conversationSelector: GetConversationByIdType,
ourConversationId: string
ourConversationId: string | undefined
) => {
return memoizee(
(conversationId: string) => {
(conversationId: string | undefined) => {
const contactNameColors: Map<string, ContactNameColorType> = new Map();
const {
sortedGroupMembers = [],
@ -726,7 +728,9 @@ const getCachedConversationMemberColorsSelector = createSelector(
} = conversationSelector(conversationId);
if (type === 'direct') {
contactNameColors.set(ourConversationId, ContactNameColors[0]);
if (ourConversationId) {
contactNameColors.set(ourConversationId, ContactNameColors[0]);
}
contactNameColors.set(theirId, ContactNameColors[0]);
return contactNameColors;
}
@ -751,7 +755,7 @@ const getCachedConversationMemberColorsSelector = createSelector(
export type ContactNameColorSelectorType = (
conversationId: string,
contactId: string
contactId: string | undefined
) => ContactNameColorType;
export const getContactNameColorSelector = createSelector(
@ -759,8 +763,13 @@ export const getContactNameColorSelector = createSelector(
conversationMemberColorsSelector => {
return (
conversationId: string,
contactId: string
contactId: string | undefined
): ContactNameColorType => {
if (!contactId) {
log.warn('No color generated for missing contactId');
return ContactNameColors[0];
}
const contactNameColors =
conversationMemberColorsSelector(conversationId);
const color = contactNameColors.get(contactId);
@ -792,10 +801,10 @@ export const getMessageSelector = createSelector(
messageLookup: MessageLookupType,
selectedMessage: SelectedMessageType | undefined,
conversationSelector: GetConversationByIdType,
regionCode: string,
ourNumber: string,
ourUuid: UUIDStringType,
ourConversationId: string,
regionCode: string | undefined,
ourNumber: string | undefined,
ourUuid: UUIDStringType | undefined,
ourConversationId: string | undefined,
callSelector: CallSelectorType,
activeCall: undefined | CallStateType,
accountSelector: AccountSelectorType,

View file

@ -104,12 +104,12 @@ type PropsForUnsupportedMessage = {
export type GetPropsForBubbleOptions = Readonly<{
conversationSelector: GetConversationByIdType;
ourConversationId: string;
ourConversationId?: string;
ourNumber?: string;
ourUuid: UUIDStringType;
ourUuid?: UUIDStringType;
selectedMessageId?: string;
selectedMessageCounter?: number;
regionCode: string;
regionCode?: string;
callSelector: CallSelectorType;
activeCall?: CallStateType;
accountSelector: AccountSelectorType;
@ -195,7 +195,7 @@ export function getContactId(
ourNumber,
ourUuid,
}: GetContactOptions
): string {
): string | undefined {
const source = getSource(message, ourNumber);
const sourceUuid = getSourceUuid(message, ourUuid);
@ -1286,7 +1286,7 @@ export function getMessagePropStatus(
MessageWithUIFieldsType,
'type' | 'errors' | 'sendStateByConversationId'
>,
ourConversationId: string
ourConversationId: string | undefined
): LastMessageStatus | undefined {
if (!isOutgoing(message)) {
return undefined;
@ -1298,7 +1298,10 @@ export function getMessagePropStatus(
const { sendStateByConversationId = {} } = message;
if (isMessageJustForMe(sendStateByConversationId, ourConversationId)) {
if (
ourConversationId &&
isMessageJustForMe(sendStateByConversationId, ourConversationId)
) {
const status =
sendStateByConversationId[ourConversationId]?.status ??
SendStatus.Pending;
@ -1313,7 +1316,9 @@ export function getMessagePropStatus(
}
const sendStates = Object.values(
omit(sendStateByConversationId, ourConversationId)
ourConversationId
? omit(sendStateByConversationId, ourConversationId)
: sendStateByConversationId
);
const highestSuccessfulStatus = sendStates.reduce(
(result: SendStatus, { status }) => maxStatus(result, status),
@ -1343,7 +1348,7 @@ export function getMessagePropStatus(
export function getPropsForEmbeddedContact(
message: MessageWithUIFieldsType,
regionCode: string,
regionCode: string | undefined,
accountSelector: (identifier?: string) => boolean
): EmbeddedContactType | undefined {
const contacts = message.contact;
@ -1427,7 +1432,7 @@ function canReplyOrReact(
MessageWithUIFieldsType,
'deletedForEveryone' | 'sendStateByConversationId' | 'type'
>,
ourConversationId: string,
ourConversationId: string | undefined,
conversation: undefined | Readonly<ConversationType>
): boolean {
const { deletedForEveryone, sendStateByConversationId } = message;
@ -1455,7 +1460,12 @@ function canReplyOrReact(
if (isOutgoing(message)) {
return (
isMessageJustForMe(sendStateByConversationId, ourConversationId) ||
someSendStatus(omit(sendStateByConversationId, ourConversationId), isSent)
someSendStatus(
ourConversationId
? omit(sendStateByConversationId, ourConversationId)
: sendStateByConversationId,
isSent
)
);
}
@ -1475,7 +1485,7 @@ export function canReply(
| 'sendStateByConversationId'
| 'type'
>,
ourConversationId: string,
ourConversationId: string | undefined,
conversationSelector: GetConversationByIdType
): boolean {
const conversation = getConversation(message, conversationSelector);
@ -1496,7 +1506,7 @@ export function canReact(
| 'sendStateByConversationId'
| 'type'
>,
ourConversationId: string,
ourConversationId: string | undefined,
conversationSelector: GetConversationByIdType
): boolean {
const conversation = getConversation(message, conversationSelector);

View file

@ -211,7 +211,7 @@ export const getMessageSearchResultSelector = createSelector(
selectedMessageId: string | undefined,
conversationSelector: GetConversationByIdType,
searchConversationId: string | undefined,
ourConversationId: string
ourConversationId: string | undefined
): GetMessageSearchResultByIdType => {
return (id: string) => {
const message = messageSearchResultLookup[id];

View file

@ -15,27 +15,27 @@ export const getUser = (state: StateType): UserStateType => state.user;
export const getUserNumber = createSelector(
getUser,
(state: UserStateType): string => state.ourNumber
(state: UserStateType): string | undefined => state.ourNumber
);
export const getUserDeviceId = createSelector(
getUser,
(state: UserStateType): number => state.ourDeviceId
(state: UserStateType): number | undefined => state.ourDeviceId
);
export const getRegionCode = createSelector(
getUser,
(state: UserStateType): string => state.regionCode
(state: UserStateType): string | undefined => state.regionCode
);
export const getUserConversationId = createSelector(
getUser,
(state: UserStateType): string => state.ourConversationId
(state: UserStateType): string | undefined => state.ourConversationId
);
export const getUserUuid = createSelector(
getUser,
(state: UserStateType): UUIDStringType => state.ourUuid
(state: UserStateType): UUIDStringType | undefined => state.ourUuid
);
export const getIntl = createSelector(

View file

@ -7,7 +7,7 @@ import { memoize } from 'lodash';
import { mapDispatchToProps } from '../actions';
import { CallManager } from '../../components/CallManager';
import { calling as callingService } from '../../services/calling';
import { getUserUuid, getIntl, getTheme } from '../selectors/user';
import { getIntl, getTheme } from '../selectors/user';
import { getMe, getConversationSelector } from '../selectors/conversations';
import { getActiveCall } from '../ducks/calling';
import type { ConversationType } from '../ducks/conversations';
@ -323,12 +323,7 @@ const mapStateToProps = (state: StateType) => ({
i18n: getIntl(state),
isGroupCallOutboundRingEnabled: isGroupCallOutboundRingEnabled(),
incomingCall: mapStateToIncomingCallProp(state),
me: {
...getMe(state),
// `getMe` returns a `ConversationType` which might not have a UUID, at least
// according to the type. This ensures one is set.
uuid: getUserUuid(state),
},
me: getMe(state),
notifyForCall,
playRingtone,
stopRingtone,

View file

@ -1,4 +1,4 @@
// Copyright 2020-2021 Signal Messenger, LLC
// Copyright 2020-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { connect } from 'react-redux';
@ -21,6 +21,7 @@ import { getUserUuid, getIntl, getTheme } from '../selectors/user';
import { getOwn } from '../../util/getOwn';
import { missingCaseError } from '../../util/missingCaseError';
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
import { strictAssert } from '../../util/assert';
export type OwnProps = {
id: string;
@ -46,6 +47,8 @@ const getOutgoingCallButtonStyle = (
state: StateType
): OutgoingCallButtonStyle => {
const { calling } = state;
const ourUuid = getUserUuid(state);
strictAssert(ourUuid, 'getOutgoingCallButtonStyle missing our uuid');
if (getActiveCall(calling)) {
return OutgoingCallButtonStyle.None;
@ -61,7 +64,7 @@ const getOutgoingCallButtonStyle = (
const call = getOwn(calling.callsByConversation, conversation.id);
if (
call?.callMode === CallMode.Group &&
isAnybodyElseInGroupCall(call.peekInfo, getUserUuid(state))
isAnybodyElseInGroupCall(call.peekInfo, ourUuid)
) {
return OutgoingCallButtonStyle.Join;
}

View file

@ -1,4 +1,4 @@
// Copyright 2021 Signal Messenger, LLC
// Copyright 2021-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { connect } from 'react-redux';

View file

@ -222,6 +222,20 @@ describe('message send state utilities', () => {
)
);
});
it('returns false if the message is for you but we have no conversationId', () => {
assert.isFalse(
isMessageJustForMe(
{
[ourConversationId]: {
status: SendStatus.Sent,
updatedAt: 123,
},
},
undefined
)
);
});
});
describe('sendStateReducer', () => {

View file

@ -10,7 +10,7 @@ import { noopAction } from '../../../state/ducks/noop';
import type { PreferredReactionsStateType } from '../../../state/ducks/preferredReactions';
import {
actions,
getInitialState,
getEmptyState,
reducer,
} from '../../../state/ducks/preferredReactions';
@ -26,7 +26,7 @@ describe('preferred reactions duck', () => {
});
const stateWithOpenCustomizationModal = {
...getInitialState(),
...getEmptyState(),
customizePreferredReactionsModal: {
draftPreferredReactions: ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'],
originalPreferredReactions: ['💙', '👍', '👎', '😂', '😮', '😢'],
@ -59,7 +59,7 @@ describe('preferred reactions duck', () => {
it("does nothing if the modal isn't open", () => {
const action = cancelCustomizePreferredReactionsModal();
const result = reducer(getInitialState(), action);
const result = reducer(getEmptyState(), action);
assert.notProperty(result, 'customizePreferredReactionsModal');
});
@ -76,7 +76,7 @@ describe('preferred reactions duck', () => {
const { deselectDraftEmoji } = actions;
it('is a no-op if the customization modal is not open', () => {
const state = getInitialState();
const state = getEmptyState();
const action = deselectDraftEmoji();
const result = reducer(state, action);
@ -167,7 +167,7 @@ describe('preferred reactions duck', () => {
const { replaceSelectedDraftEmoji } = actions;
it('is a no-op if the customization modal is not open', () => {
const state = getInitialState();
const state = getEmptyState();
const action = replaceSelectedDraftEmoji('🦈');
const result = reducer(state, action);
@ -350,7 +350,7 @@ describe('preferred reactions duck', () => {
};
it("does nothing if the modal isn't open", () => {
const result = reducer(getInitialState(), action);
const result = reducer(getEmptyState(), action);
assert.notProperty(result, 'customizePreferredReactionsModal');
});
@ -404,7 +404,7 @@ describe('preferred reactions duck', () => {
};
it("does nothing if the modal isn't open", () => {
const state = getInitialState();
const state = getEmptyState();
const result = reducer(state, action);
assert.strictEqual(result, state);
@ -428,7 +428,7 @@ describe('preferred reactions duck', () => {
const { selectDraftEmojiToBeReplaced } = actions;
it('is a no-op if the customization modal is not open', () => {
const state = getInitialState();
const state = getEmptyState();
const action = selectDraftEmojiToBeReplaced(2);
const result = reducer(state, action);

View file

@ -1,4 +1,4 @@
// Copyright 2019-2021 Signal Messenger, LLC
// Copyright 2019-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { omit } from 'lodash';
@ -86,7 +86,7 @@ const DEFAULT_ADDRESS_TYPE = Proto.DataMessage.Contact.PostalAddress.Type.HOME;
export function embeddedContactSelector(
contact: EmbeddedContactType,
options: {
regionCode: string;
regionCode?: string;
firstNumber?: string;
isNumberOnSignal?: boolean;
getAbsoluteAttachmentPath: (path: string) => string;

View file

@ -1,4 +1,4 @@
// Copyright 2019-2021 Signal Messenger, LLC
// Copyright 2019-2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { isNumber, pick, reject, groupBy, values } from 'lodash';
@ -23,6 +23,7 @@ import type {
import Data from '../sql/Client';
import { SignalService as Proto } from '../protobuf';
import * as log from '../logging/log';
import type { StickersStateType } from '../state/ducks/stickers';
export type RecentStickerType = Readonly<{
stickerId: number;
@ -31,12 +32,6 @@ export type RecentStickerType = Readonly<{
export type BlessedType = Pick<StickerPackType, 'key' | 'status'>;
export type InitialState = {
packs: Record<string, StickerPackType>;
recentStickers: Array<RecentStickerType>;
blessedPacks: Record<string, boolean>;
};
export type DownloadMap = Record<
string,
{
@ -85,7 +80,7 @@ const STICKER_PACK_DEFAULTS: StickerPackType = {
const VALID_PACK_ID_REGEXP = /^[0-9a-f]{32}$/i;
let initialState: InitialState | undefined;
let initialState: StickersStateType | undefined;
let packsToDownload: DownloadMap | undefined;
const downloadQueue = new Queue({ concurrency: 1, timeout: 1000 * 60 * 2 });
@ -104,6 +99,7 @@ export async function load(): Promise<void> {
packs,
recentStickers,
blessedPacks,
installedPack: null,
};
packsToDownload = capturePacksToDownload(packs);
@ -269,7 +265,7 @@ async function getRecentStickersForRedux(): Promise<Array<RecentStickerType>> {
}));
}
export function getInitialState(): InitialState {
export function getInitialState(): StickersStateType {
strictAssert(initialState !== undefined, 'Stickers not initialized');
return initialState;
}

View file

@ -6401,7 +6401,7 @@
{
"rule": "jQuery-wrap(",
"path": "node_modules/regenerator-runtime/runtime.js",
"line": " wrap(innerFn, outerFn, self, tryLocsList)",
"line": " wrap(innerFn, outerFn, self, tryLocsList),",
"reasonCategory": "falseMatch",
"updated": "2019-12-11T01:10:06.091Z"
},
@ -7817,13 +7817,6 @@
"reasonCategory": "usageTrusted",
"updated": "2021-10-22T00:52:39.251Z"
},
{
"rule": "jQuery-load(",
"path": "ts/jobs/helpers/syncHelpers.ts",
"line": " await window.ConversationController.load();",
"reasonCategory": "falseMatch",
"updated": "2021-11-04T16:14:03.477Z"
},
{
"rule": "jQuery-load(",
"path": "ts/jobs/conversationJobQueue.ts",
@ -7838,6 +7831,13 @@
"reasonCategory": "falseMatch",
"updated": "2021-11-04T16:14:03.477Z"
},
{
"rule": "jQuery-load(",
"path": "ts/jobs/helpers/syncHelpers.ts",
"line": " await window.ConversationController.load();",
"reasonCategory": "falseMatch",
"updated": "2021-11-04T16:14:03.477Z"
},
{
"rule": "jQuery-append(",
"path": "ts/logging/uploadDebugLog.ts",
@ -8080,4 +8080,4 @@
"reasonCategory": "usageTrusted",
"updated": "2021-09-17T21:02:59.414Z"
}
]
]

View file

@ -933,6 +933,13 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.9.2":
version "7.17.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941"
integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.1.0", "@babel/template@^7.12.13", "@babel/template@^7.16.0", "@babel/template@^7.4.4":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6"
@ -12677,13 +12684,12 @@ redux-ts-utils@3.2.2:
dependencies:
immer "^2.0.0"
redux@4.0.2, redux@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.2.tgz#597cc660a99f91412e31c96c3da10ed8ace0715d"
integrity sha512-oAiFLWYQhbpSvzjcVfgQ90MlZ0u6uDIHFK41Q0/BnCfjEg96SACzwUFwDVUKz/LP/SwJORGaFY8AM5wOB/zf0A==
redux@4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104"
integrity sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==
dependencies:
loose-envify "^1.4.0"
symbol-observable "^1.2.0"
"@babel/runtime" "^7.9.2"
redux@^3.6.0:
version "3.7.2"
@ -12695,6 +12701,14 @@ redux@^3.6.0:
loose-envify "^1.1.0"
symbol-observable "^1.0.3"
redux@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.2.tgz#597cc660a99f91412e31c96c3da10ed8ace0715d"
integrity sha512-oAiFLWYQhbpSvzjcVfgQ90MlZ0u6uDIHFK41Q0/BnCfjEg96SACzwUFwDVUKz/LP/SwJORGaFY8AM5wOB/zf0A==
dependencies:
loose-envify "^1.4.0"
symbol-observable "^1.2.0"
refractor@^2.4.1:
version "2.10.0"
resolved "https://registry.yarnpkg.com/refractor/-/refractor-2.10.0.tgz#4cc7efc0028a87924a9b31d82d129dec831a287b"