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-sortable-hoc": "1.9.1",
"react-virtualized": "9.21.0", "react-virtualized": "9.21.0",
"read-last-lines": "1.8.0", "read-last-lines": "1.8.0",
"redux": "4.0.2", "redux": "4.1.2",
"redux-logger": "3.0.6", "redux-logger": "3.0.6",
"redux-promise-middleware": "6.1.0", "redux-promise-middleware": "6.1.0",
"redux-thunk": "2.3.0", "redux-thunk": "2.3.0",

View file

@ -91,7 +91,6 @@ import { RotateSignedPreKeyListener } from './textsecure/RotateSignedPreKeyListe
import { isDirectConversation, isGroupV2 } from './util/whatTypeOfConversation'; import { isDirectConversation, isGroupV2 } from './util/whatTypeOfConversation';
import { BackOff, FIBONACCI_TIMEOUTS } from './util/BackOff'; import { BackOff, FIBONACCI_TIMEOUTS } from './util/BackOff';
import { AppViewType } from './state/ducks/app'; import { AppViewType } from './state/ducks/app';
import { UsernameSaveState } from './state/ducks/conversationsEnums';
import type { BadgesStateType } from './state/ducks/badges'; import type { BadgesStateType } from './state/ducks/badges';
import { badgeImageFileDownloader } from './badges/badgeImageFileDownloader'; import { badgeImageFileDownloader } from './badges/badgeImageFileDownloader';
import { isIncoming } from './state/selectors/message'; import { isIncoming } from './state/selectors/message';
@ -116,7 +115,6 @@ import { ReadStatus } from './messages/MessageReadStatus';
import type { SendStateByConversationId } from './messages/MessageSendState'; import type { SendStateByConversationId } from './messages/MessageSendState';
import { SendStatus } from './messages/MessageSendState'; import { SendStatus } from './messages/MessageSendState';
import * as AttachmentDownloads from './messageModifiers/AttachmentDownloads'; import * as AttachmentDownloads from './messageModifiers/AttachmentDownloads';
import * as preferredReactions from './state/ducks/preferredReactions';
import * as Conversation from './types/Conversation'; import * as Conversation from './types/Conversation';
import * as Stickers from './types/Stickers'; import * as Stickers from './types/Stickers';
import * as Errors from './types/errors'; import * as Errors from './types/errors';
@ -128,10 +126,7 @@ import { RemoveAllConfiguration } from './types/RemoveAllConfiguration';
import { isValidUuid, UUIDKind } from './types/UUID'; import { isValidUuid, UUIDKind } from './types/UUID';
import type { UUID } from './types/UUID'; import type { UUID } from './types/UUID';
import * as log from './logging/log'; import * as log from './logging/log';
import { import { loadRecentEmojis } from './util/loadRecentEmojis';
loadRecentEmojis,
getEmojiReducerState,
} from './util/loadRecentEmojis';
import { deleteAllLogs } from './util/deleteAllLogs'; import { deleteAllLogs } from './util/deleteAllLogs';
import { ToastCaptchaFailed } from './components/ToastCaptchaFailed'; import { ToastCaptchaFailed } from './components/ToastCaptchaFailed';
import { ToastCaptchaSolved } from './components/ToastCaptchaSolved'; import { ToastCaptchaSolved } from './components/ToastCaptchaSolved';
@ -143,6 +138,7 @@ import { deliveryReceiptsJobQueue } from './jobs/deliveryReceiptsJobQueue';
import { updateOurUsername } from './util/updateOurUsername'; import { updateOurUsername } from './util/updateOurUsername';
import { ReactionSource } from './reactions/ReactionSource'; import { ReactionSource } from './reactions/ReactionSource';
import { singleProtoJobQueue } from './jobs/singleProtoJobQueue'; import { singleProtoJobQueue } from './jobs/singleProtoJobQueue';
import { getInitialState } from './state/getInitialState';
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000; const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
@ -890,70 +886,7 @@ export async function startApp(): Promise<void> {
function initializeRedux() { function initializeRedux() {
// Here we set up a full redux store with initial state for our LeftPane Root // Here we set up a full redux store with initial state for our LeftPane Root
const convoCollection = window.getConversations(); const convoCollection = window.getConversations();
const conversations = convoCollection.map(conversation => const initialState = getInitialState({ badges: initialBadgesState });
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 store = window.Signal.State.createStore(initialState); const store = window.Signal.State.createStore(initialState);
window.reduxStore = store; window.reduxStore = store;

View file

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

View file

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

View file

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

View file

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

View file

@ -19,9 +19,7 @@ import {
CallingLobbyJoinButton, CallingLobbyJoinButton,
CallingLobbyJoinButtonVariant, CallingLobbyJoinButtonVariant,
} from './CallingLobbyJoinButton'; } from './CallingLobbyJoinButton';
import type { AvatarColorType } from '../types/Colors';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType } from '../types/Util';
import type { UUIDStringType } from '../types/UUID';
import { useIsOnline } from '../hooks/useIsOnline'; import { useIsOnline } from '../hooks/useIsOnline';
import * as KeyboardLayout from '../services/keyboardLayout'; import * as KeyboardLayout from '../services/keyboardLayout';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
@ -51,12 +49,7 @@ export type PropsType = {
isGroupCall: boolean; isGroupCall: boolean;
isGroupCallOutboundRingEnabled: boolean; isGroupCallOutboundRingEnabled: boolean;
isCallFull?: boolean; isCallFull?: boolean;
me: { me: Readonly<Pick<ConversationType, 'avatarPath' | 'color' | 'id' | 'uuid'>>;
avatarPath?: string;
id: string;
color?: AvatarColorType;
uuid: UUIDStringType;
};
onCallCanceled: () => void; onCallCanceled: () => void;
onJoinCall: () => void; onJoinCall: () => void;
outgoingRing: boolean; 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 // SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable react/no-array-index-key */ /* eslint-disable react/no-array-index-key */
@ -24,7 +24,7 @@ type ParticipantType = ConversationType & {
export type PropsType = { export type PropsType = {
readonly i18n: LocalizerType; readonly i18n: LocalizerType;
readonly onClose: () => void; readonly onClose: () => void;
readonly ourUuid: string; readonly ourUuid: string | undefined;
readonly participants: Array<ParticipantType>; readonly participants: Array<ParticipantType>;
}; };
@ -113,7 +113,7 @@ export const CallingParticipantsList = React.memo(
sharedGroupNames={participant.sharedGroupNames} sharedGroupNames={participant.sharedGroupNames}
size={32} size={32}
/> />
{participant.uuid === ourUuid ? ( {ourUuid && participant.uuid === ourUuid ? (
<span className="module-calling-participants-list__name"> <span className="module-calling-participants-list__name">
{i18n('you')} {i18n('you')}
</span> </span>

View file

@ -88,7 +88,7 @@ export type PropsType = {
preferredWidthFromStorage: number; preferredWidthFromStorage: number;
selectedConversationId: undefined | string; selectedConversationId: undefined | string;
selectedMessageId: undefined | string; selectedMessageId: undefined | string;
regionCode: string; regionCode: string | undefined;
challengeStatus: 'idle' | 'required' | 'pending'; challengeStatus: 'idle' | 'required' | 'pending';
setChallengeStatus: (status: 'idle') => void; setChallengeStatus: (status: 'idle') => void;
crashReportCount: number; 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 // SPDX-License-Identifier: AGPL-3.0-only
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
@ -22,7 +22,7 @@ import { Modal } from '../Modal';
export type PropsDataType = { export type PropsDataType = {
groupName?: string; groupName?: string;
ourUuid: UUIDStringType; ourUuid?: UUIDStringType;
change: GroupV2ChangeType; change: GroupV2ChangeType;
}; };

View file

@ -24,7 +24,7 @@ export type LeftPaneComposePropsType = {
composeContacts: ReadonlyArray<ContactListItemConversationType>; composeContacts: ReadonlyArray<ContactListItemConversationType>;
composeGroups: ReadonlyArray<ConversationListItemPropsType>; composeGroups: ReadonlyArray<ConversationListItemPropsType>;
regionCode: string; regionCode: string | undefined;
searchTerm: string; searchTerm: string;
isFetchingUsername: boolean; isFetchingUsername: boolean;
isUsernamesEnabled: boolean; isUsernamesEnabled: boolean;
@ -355,7 +355,7 @@ function focusRef(el: HTMLElement | null) {
function parsePhoneNumber( function parsePhoneNumber(
str: string, str: string,
regionCode: string regionCode: string | undefined
): undefined | PhoneNumber { ): undefined | PhoneNumber {
let result: PhoneNumber; let result: PhoneNumber;
try { 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 // SPDX-License-Identifier: AGPL-3.0-only
import type { LocalizerType } from './types/Util'; import type { LocalizerType } from './types/Util';
@ -20,7 +20,7 @@ export type StringRendererType<T> = (
export type RenderOptionsType<T> = { export type RenderOptionsType<T> = {
from?: UUIDStringType; from?: UUIDStringType;
i18n: LocalizerType; i18n: LocalizerType;
ourUuid: UUIDStringType; ourUuid?: UUIDStringType;
renderContact: SmartContactRendererType<T>; renderContact: SmartContactRendererType<T>;
renderString: StringRendererType<T>; renderString: StringRendererType<T>;
}; };
@ -47,7 +47,7 @@ export function renderChangeDetail<T>(
options: RenderOptionsType<T> options: RenderOptionsType<T>
): T | string { ): T | string {
const { from, i18n, ourUuid, renderContact, renderString } = options; 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 (detail.type === 'create') {
if (fromYou) { if (fromYou) {
@ -209,7 +209,7 @@ export function renderChangeDetail<T>(
} }
if (detail.type === 'member-add') { if (detail.type === 'member-add') {
const { uuid } = detail; const { uuid } = detail;
const weAreJoiner = uuid === ourUuid; const weAreJoiner = Boolean(ourUuid && uuid === ourUuid);
if (weAreJoiner) { if (weAreJoiner) {
if (fromYou) { if (fromYou) {
@ -239,8 +239,8 @@ export function renderChangeDetail<T>(
} }
if (detail.type === 'member-add-from-invite') { if (detail.type === 'member-add-from-invite') {
const { uuid, inviter } = detail; const { uuid, inviter } = detail;
const weAreJoiner = uuid === ourUuid; const weAreJoiner = Boolean(ourUuid && uuid === ourUuid);
const weAreInviter = Boolean(inviter && inviter === ourUuid); const weAreInviter = Boolean(inviter && ourUuid && inviter === ourUuid);
if (!from || from !== uuid) { if (!from || from !== uuid) {
if (weAreJoiner) { if (weAreJoiner) {
@ -302,7 +302,7 @@ export function renderChangeDetail<T>(
if (detail.type === 'member-add-from-link') { if (detail.type === 'member-add-from-link') {
const { uuid } = detail; const { uuid } = detail;
if (fromYou && uuid === ourUuid) { if (fromYou && ourUuid && uuid === ourUuid) {
return renderString('GroupV2--member-add-from-link--you--you', i18n); return renderString('GroupV2--member-add-from-link--you--you', i18n);
} }
if (from && uuid === from) { if (from && uuid === from) {
@ -320,7 +320,7 @@ export function renderChangeDetail<T>(
} }
if (detail.type === 'member-add-from-admin-approval') { if (detail.type === 'member-add-from-admin-approval') {
const { uuid } = detail; const { uuid } = detail;
const weAreJoiner = uuid === ourUuid; const weAreJoiner = Boolean(ourUuid && uuid === ourUuid);
if (weAreJoiner) { if (weAreJoiner) {
if (from) { if (from) {
@ -371,7 +371,7 @@ export function renderChangeDetail<T>(
} }
if (detail.type === 'member-remove') { if (detail.type === 'member-remove') {
const { uuid } = detail; const { uuid } = detail;
const weAreLeaver = uuid === ourUuid; const weAreLeaver = Boolean(ourUuid && uuid === ourUuid);
if (weAreLeaver) { if (weAreLeaver) {
if (fromYou) { if (fromYou) {
@ -407,7 +407,7 @@ export function renderChangeDetail<T>(
} }
if (detail.type === 'member-privilege') { if (detail.type === 'member-privilege') {
const { uuid, newPrivilege } = detail; const { uuid, newPrivilege } = detail;
const weAreMember = uuid === ourUuid; const weAreMember = Boolean(ourUuid && uuid === ourUuid);
if (newPrivilege === RoleEnum.ADMINISTRATOR) { if (newPrivilege === RoleEnum.ADMINISTRATOR) {
if (weAreMember) { if (weAreMember) {
@ -493,7 +493,7 @@ export function renderChangeDetail<T>(
} }
if (detail.type === 'pending-add-one') { if (detail.type === 'pending-add-one') {
const { uuid } = detail; const { uuid } = detail;
const weAreInvited = uuid === ourUuid; const weAreInvited = Boolean(ourUuid && uuid === ourUuid);
if (weAreInvited) { if (weAreInvited) {
if (from) { if (from) {
return renderString('GroupV2--pending-add--one--you--other', i18n, [ return renderString('GroupV2--pending-add--one--you--other', i18n, [
@ -534,8 +534,8 @@ export function renderChangeDetail<T>(
} }
if (detail.type === 'pending-remove-one') { if (detail.type === 'pending-remove-one') {
const { inviter, uuid } = detail; const { inviter, uuid } = detail;
const weAreInviter = Boolean(inviter && inviter === ourUuid); const weAreInviter = Boolean(inviter && ourUuid && inviter === ourUuid);
const weAreInvited = uuid === ourUuid; const weAreInvited = Boolean(ourUuid && uuid === ourUuid);
const sentByInvited = Boolean(from && from === uuid); const sentByInvited = Boolean(from && from === uuid);
const sentByInviter = Boolean(from && inviter && from === inviter); const sentByInviter = Boolean(from && inviter && from === inviter);
@ -629,7 +629,7 @@ export function renderChangeDetail<T>(
} }
if (detail.type === 'pending-remove-many') { if (detail.type === 'pending-remove-many') {
const { count, inviter } = detail; const { count, inviter } = detail;
const weAreInviter = Boolean(inviter && inviter === ourUuid); const weAreInviter = Boolean(inviter && ourUuid && inviter === ourUuid);
if (weAreInviter) { if (weAreInviter) {
if (fromYou) { if (fromYou) {
@ -709,7 +709,7 @@ export function renderChangeDetail<T>(
} }
if (detail.type === 'admin-approval-add-one') { if (detail.type === 'admin-approval-add-one') {
const { uuid } = detail; const { uuid } = detail;
const weAreJoiner = uuid === ourUuid; const weAreJoiner = Boolean(ourUuid && uuid === ourUuid);
if (weAreJoiner) { if (weAreJoiner) {
return renderString('GroupV2--admin-approval-add-one--you', i18n); return renderString('GroupV2--admin-approval-add-one--you', i18n);
@ -720,7 +720,7 @@ export function renderChangeDetail<T>(
} }
if (detail.type === 'admin-approval-remove-one') { if (detail.type === 'admin-approval-remove-one') {
const { uuid } = detail; const { uuid } = detail;
const weAreJoiner = uuid === ourUuid; const weAreJoiner = Boolean(ourUuid && uuid === ourUuid);
if (weAreJoiner) { if (weAreJoiner) {
if (fromYou) { 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 // SPDX-License-Identifier: AGPL-3.0-only
import { makeEnumParser } from '../util/enum'; import { makeEnumParser } from '../util/enum';
@ -150,10 +150,12 @@ export const someSendStatus = (
export const isMessageJustForMe = ( export const isMessageJustForMe = (
sendStateByConversationId: undefined | Readonly<SendStateByConversationId>, sendStateByConversationId: undefined | Readonly<SendStateByConversationId>,
ourConversationId: string ourConversationId: string | undefined
): boolean => { ): boolean => {
const conversationIds = Object.keys(sendStateByConversationId || {}); const conversationIds = Object.keys(sendStateByConversationId || {});
return ( return Boolean(
conversationIds.length === 1 && conversationIds[0] === ourConversationId ourConversationId &&
conversationIds.length === 1 &&
conversationIds[0] === ourConversationId
); );
}; };

View file

@ -389,7 +389,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}, },
contactNameColorSelector: ( contactNameColorSelector: (
conversationId: string, conversationId: string,
contactId: string contactId: string | undefined
) => { ) => {
const state = window.reduxStore.getState(); const state = window.reduxStore.getState();
const contactNameColorSelector = getContactNameColorSelector(state); 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 // SPDX-License-Identifier: AGPL-3.0-only
/* eslint-disable no-console */ /* eslint-disable no-console */
import type { DeepPartial, Store } from 'redux'; import type { Store } from 'redux';
import { applyMiddleware, createStore as reduxCreateStore } from 'redux'; import { applyMiddleware, createStore as reduxCreateStore } from 'redux';
import promise from 'redux-promise-middleware'; import promise from 'redux-promise-middleware';
@ -54,5 +54,5 @@ const middlewareList = [
const enhancer = applyMiddleware(...middlewareList); const enhancer = applyMiddleware(...middlewareList);
export const createStore = ( export const createStore = (
initialState: DeepPartial<StateType> initialState: Readonly<StateType>
): Store<StateType> => reduxCreateStore(reducer, initialState, enhancer); ): Store<StateType> => reduxCreateStore(reducer, initialState, enhancer);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -157,7 +157,7 @@ export const actions = {
toggleVerified, toggleVerified,
}; };
function getEmptyState(): SafetyNumberStateType { export function getEmptyState(): SafetyNumberStateType {
return { return {
contacts: {}, 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 // SPDX-License-Identifier: AGPL-3.0-only
import type { ThunkAction, ThunkDispatch } from 'redux-thunk'; import type { ThunkAction, ThunkDispatch } from 'redux-thunk';
@ -24,6 +24,7 @@ import type {
} from './conversations'; } from './conversations';
import { getQuery, getSearchConversation } from '../selectors/search'; import { getQuery, getSearchConversation } from '../selectors/search';
import { getIntl, getUserConversationId } from '../selectors/user'; import { getIntl, getUserConversationId } from '../selectors/user';
import { strictAssert } from '../../util/assert';
const { const {
searchConversations: dataSearchConversations, searchConversations: dataSearchConversations,
@ -157,11 +158,16 @@ function updateSearchTerm(
}); });
const state = getState(); const state = getState();
const ourConversationId = getUserConversationId(state);
strictAssert(
ourConversationId,
'updateSearchTerm our conversation is missing'
);
doSearch({ doSearch({
dispatch, dispatch,
noteToSelf: getIntl(state)('noteToSelf').toLowerCase(), noteToSelf: getIntl(state)('noteToSelf').toLowerCase(),
ourConversationId: getUserConversationId(state), ourConversationId,
query: getQuery(state), query: getQuery(state),
searchConversationId: getSearchConversation(state)?.id, searchConversationId: getSearchConversation(state)?.id,
}); });

View file

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

View file

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

View file

@ -14,12 +14,12 @@ export type UserStateType = {
attachmentsPath: string; attachmentsPath: string;
stickersPath: string; stickersPath: string;
tempPath: string; tempPath: string;
ourConversationId: string; ourConversationId: string | undefined;
ourDeviceId: number; ourDeviceId: number | undefined;
ourUuid: UUIDStringType; ourUuid: UUIDStringType | undefined;
ourNumber: string; ourNumber: string | undefined;
platform: string; platform: string;
regionCode: string; regionCode: string | undefined;
i18n: LocalizerType; i18n: LocalizerType;
interactionMode: 'mouse' | 'keyboard'; interactionMode: 'mouse' | 'keyboard';
theme: ThemeType; 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 // SPDX-License-Identifier: AGPL-3.0-only
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
@ -62,7 +62,12 @@ export const getIncomingCall = createSelector(
getUserUuid, getUserUuid,
( (
callsByConversation: CallsByConversationType, callsByConversation: CallsByConversationType,
ourUuid: UUIDStringType ourUuid: UUIDStringType | undefined
): undefined | DirectCallStateType | GroupCallStateType => ): undefined | DirectCallStateType | GroupCallStateType => {
getIncomingCallHelper(callsByConversation, ourUuid) if (!ourUuid) {
return undefined;
}
return getIncomingCallHelper(callsByConversation, ourUuid);
}
); );

View file

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

View file

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

View file

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

View file

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

View file

@ -7,7 +7,7 @@ import { memoize } from 'lodash';
import { mapDispatchToProps } from '../actions'; import { mapDispatchToProps } from '../actions';
import { CallManager } from '../../components/CallManager'; import { CallManager } from '../../components/CallManager';
import { calling as callingService } from '../../services/calling'; 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 { getMe, getConversationSelector } from '../selectors/conversations';
import { getActiveCall } from '../ducks/calling'; import { getActiveCall } from '../ducks/calling';
import type { ConversationType } from '../ducks/conversations'; import type { ConversationType } from '../ducks/conversations';
@ -323,12 +323,7 @@ const mapStateToProps = (state: StateType) => ({
i18n: getIntl(state), i18n: getIntl(state),
isGroupCallOutboundRingEnabled: isGroupCallOutboundRingEnabled(), isGroupCallOutboundRingEnabled: isGroupCallOutboundRingEnabled(),
incomingCall: mapStateToIncomingCallProp(state), incomingCall: mapStateToIncomingCallProp(state),
me: { me: getMe(state),
...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),
},
notifyForCall, notifyForCall,
playRingtone, playRingtone,
stopRingtone, 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 // SPDX-License-Identifier: AGPL-3.0-only
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -21,6 +21,7 @@ import { getUserUuid, getIntl, getTheme } from '../selectors/user';
import { getOwn } from '../../util/getOwn'; import { getOwn } from '../../util/getOwn';
import { missingCaseError } from '../../util/missingCaseError'; import { missingCaseError } from '../../util/missingCaseError';
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly'; import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
import { strictAssert } from '../../util/assert';
export type OwnProps = { export type OwnProps = {
id: string; id: string;
@ -46,6 +47,8 @@ const getOutgoingCallButtonStyle = (
state: StateType state: StateType
): OutgoingCallButtonStyle => { ): OutgoingCallButtonStyle => {
const { calling } = state; const { calling } = state;
const ourUuid = getUserUuid(state);
strictAssert(ourUuid, 'getOutgoingCallButtonStyle missing our uuid');
if (getActiveCall(calling)) { if (getActiveCall(calling)) {
return OutgoingCallButtonStyle.None; return OutgoingCallButtonStyle.None;
@ -61,7 +64,7 @@ const getOutgoingCallButtonStyle = (
const call = getOwn(calling.callsByConversation, conversation.id); const call = getOwn(calling.callsByConversation, conversation.id);
if ( if (
call?.callMode === CallMode.Group && call?.callMode === CallMode.Group &&
isAnybodyElseInGroupCall(call.peekInfo, getUserUuid(state)) isAnybodyElseInGroupCall(call.peekInfo, ourUuid)
) { ) {
return OutgoingCallButtonStyle.Join; 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 // SPDX-License-Identifier: AGPL-3.0-only
import { connect } from 'react-redux'; 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', () => { describe('sendStateReducer', () => {

View file

@ -10,7 +10,7 @@ import { noopAction } from '../../../state/ducks/noop';
import type { PreferredReactionsStateType } from '../../../state/ducks/preferredReactions'; import type { PreferredReactionsStateType } from '../../../state/ducks/preferredReactions';
import { import {
actions, actions,
getInitialState, getEmptyState,
reducer, reducer,
} from '../../../state/ducks/preferredReactions'; } from '../../../state/ducks/preferredReactions';
@ -26,7 +26,7 @@ describe('preferred reactions duck', () => {
}); });
const stateWithOpenCustomizationModal = { const stateWithOpenCustomizationModal = {
...getInitialState(), ...getEmptyState(),
customizePreferredReactionsModal: { customizePreferredReactionsModal: {
draftPreferredReactions: ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'], draftPreferredReactions: ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'],
originalPreferredReactions: ['💙', '👍', '👎', '😂', '😮', '😢'], originalPreferredReactions: ['💙', '👍', '👎', '😂', '😮', '😢'],
@ -59,7 +59,7 @@ describe('preferred reactions duck', () => {
it("does nothing if the modal isn't open", () => { it("does nothing if the modal isn't open", () => {
const action = cancelCustomizePreferredReactionsModal(); const action = cancelCustomizePreferredReactionsModal();
const result = reducer(getInitialState(), action); const result = reducer(getEmptyState(), action);
assert.notProperty(result, 'customizePreferredReactionsModal'); assert.notProperty(result, 'customizePreferredReactionsModal');
}); });
@ -76,7 +76,7 @@ describe('preferred reactions duck', () => {
const { deselectDraftEmoji } = actions; const { deselectDraftEmoji } = actions;
it('is a no-op if the customization modal is not open', () => { it('is a no-op if the customization modal is not open', () => {
const state = getInitialState(); const state = getEmptyState();
const action = deselectDraftEmoji(); const action = deselectDraftEmoji();
const result = reducer(state, action); const result = reducer(state, action);
@ -167,7 +167,7 @@ describe('preferred reactions duck', () => {
const { replaceSelectedDraftEmoji } = actions; const { replaceSelectedDraftEmoji } = actions;
it('is a no-op if the customization modal is not open', () => { it('is a no-op if the customization modal is not open', () => {
const state = getInitialState(); const state = getEmptyState();
const action = replaceSelectedDraftEmoji('🦈'); const action = replaceSelectedDraftEmoji('🦈');
const result = reducer(state, action); const result = reducer(state, action);
@ -350,7 +350,7 @@ describe('preferred reactions duck', () => {
}; };
it("does nothing if the modal isn't open", () => { it("does nothing if the modal isn't open", () => {
const result = reducer(getInitialState(), action); const result = reducer(getEmptyState(), action);
assert.notProperty(result, 'customizePreferredReactionsModal'); assert.notProperty(result, 'customizePreferredReactionsModal');
}); });
@ -404,7 +404,7 @@ describe('preferred reactions duck', () => {
}; };
it("does nothing if the modal isn't open", () => { it("does nothing if the modal isn't open", () => {
const state = getInitialState(); const state = getEmptyState();
const result = reducer(state, action); const result = reducer(state, action);
assert.strictEqual(result, state); assert.strictEqual(result, state);
@ -428,7 +428,7 @@ describe('preferred reactions duck', () => {
const { selectDraftEmojiToBeReplaced } = actions; const { selectDraftEmojiToBeReplaced } = actions;
it('is a no-op if the customization modal is not open', () => { it('is a no-op if the customization modal is not open', () => {
const state = getInitialState(); const state = getEmptyState();
const action = selectDraftEmojiToBeReplaced(2); const action = selectDraftEmojiToBeReplaced(2);
const result = reducer(state, action); 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 // SPDX-License-Identifier: AGPL-3.0-only
import { omit } from 'lodash'; import { omit } from 'lodash';
@ -86,7 +86,7 @@ const DEFAULT_ADDRESS_TYPE = Proto.DataMessage.Contact.PostalAddress.Type.HOME;
export function embeddedContactSelector( export function embeddedContactSelector(
contact: EmbeddedContactType, contact: EmbeddedContactType,
options: { options: {
regionCode: string; regionCode?: string;
firstNumber?: string; firstNumber?: string;
isNumberOnSignal?: boolean; isNumberOnSignal?: boolean;
getAbsoluteAttachmentPath: (path: string) => string; 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 // SPDX-License-Identifier: AGPL-3.0-only
import { isNumber, pick, reject, groupBy, values } from 'lodash'; import { isNumber, pick, reject, groupBy, values } from 'lodash';
@ -23,6 +23,7 @@ import type {
import Data from '../sql/Client'; import Data from '../sql/Client';
import { SignalService as Proto } from '../protobuf'; import { SignalService as Proto } from '../protobuf';
import * as log from '../logging/log'; import * as log from '../logging/log';
import type { StickersStateType } from '../state/ducks/stickers';
export type RecentStickerType = Readonly<{ export type RecentStickerType = Readonly<{
stickerId: number; stickerId: number;
@ -31,12 +32,6 @@ export type RecentStickerType = Readonly<{
export type BlessedType = Pick<StickerPackType, 'key' | 'status'>; 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< export type DownloadMap = Record<
string, string,
{ {
@ -85,7 +80,7 @@ const STICKER_PACK_DEFAULTS: StickerPackType = {
const VALID_PACK_ID_REGEXP = /^[0-9a-f]{32}$/i; const VALID_PACK_ID_REGEXP = /^[0-9a-f]{32}$/i;
let initialState: InitialState | undefined; let initialState: StickersStateType | undefined;
let packsToDownload: DownloadMap | undefined; let packsToDownload: DownloadMap | undefined;
const downloadQueue = new Queue({ concurrency: 1, timeout: 1000 * 60 * 2 }); const downloadQueue = new Queue({ concurrency: 1, timeout: 1000 * 60 * 2 });
@ -104,6 +99,7 @@ export async function load(): Promise<void> {
packs, packs,
recentStickers, recentStickers,
blessedPacks, blessedPacks,
installedPack: null,
}; };
packsToDownload = capturePacksToDownload(packs); 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'); strictAssert(initialState !== undefined, 'Stickers not initialized');
return initialState; return initialState;
} }

View file

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

View file

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