Add type-alias-readonlydeep rule and make ducks mostly immutable

This commit is contained in:
Jamie Kyle 2023-01-13 12:07:26 -08:00 committed by GitHub
parent 11ce3c3d59
commit c58a723f45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 1164 additions and 871 deletions

View File

@ -0,0 +1,58 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
function isReadOnlyDeep(node, scope) {
if (node.type !== 'TSTypeReference') {
return false;
}
let reference = scope.references.find(reference => {
return reference.identifier === node.typeName;
});
let variable = reference.resolved;
if (variable == null) {
return false;
}
let defs = variable.defs;
if (defs.length !== 1) {
return false;
}
let [def] = defs;
return (
def.type === 'ImportBinding' &&
def.parent.type === 'ImportDeclaration' &&
def.parent.source.type === 'Literal' &&
def.parent.source.value === 'type-fest'
);
}
/** @type {import("eslint").Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
hasSuggestions: false,
fixable: false,
schema: [],
},
create(context) {
return {
TSTypeAliasDeclaration(node) {
let scope = context.getScope(node);
if (isReadOnlyDeep(node.typeAnnotation, scope)) {
return;
}
context.report({
node: node.id,
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
});
},
};
},
};

View File

@ -0,0 +1,79 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
const rule = require('./type-alias-readonlydeep');
const RuleTester = require('eslint').RuleTester;
// avoid triggering mocha's global leak detection
require('@typescript-eslint/parser');
const ruleTester = new RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
});
ruleTester.run('type-alias-readonlydeep', rule, {
valid: [
{
code: `import type { ReadonlyDeep } from "type-fest"; type Foo = ReadonlyDeep<{}>`,
},
{
code: `import { ReadonlyDeep } from "type-fest"; type Foo = ReadonlyDeep<{}>`,
},
],
invalid: [
{
code: `type Foo = {}`,
errors: [
{
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
type: 'Identifier',
},
],
},
{
code: `type Foo = Bar<{}>`,
errors: [
{
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
type: 'Identifier',
},
],
},
{
code: `type Foo = ReadonlyDeep<{}>`,
errors: [
{
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
type: 'Identifier',
},
],
},
{
code: `interface ReadonlyDeep<T> {}; type Foo = ReadonlyDeep<{}>`,
errors: [
{
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
type: 'Identifier',
},
],
},
{
code: `import type { ReadonlyDeep } from "foo"; type Foo = ReadonlyDeep<{}>`,
errors: [
{
message:
'Type aliases must be wrapped with ReadonlyDeep from type-fest',
type: 'Identifier',
},
],
},
],
});

View File

@ -274,6 +274,12 @@ module.exports = {
'react/no-array-index-key': 'off',
},
},
{
files: ['ts/state/ducks/**/*.ts'],
rules: {
'local-rules/type-alias-readonlydeep': 'error',
},
},
],
rules: {

View File

@ -2480,6 +2480,10 @@ Signal Desktop makes use of the following open source projects.
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
## type-fest
License: (MIT OR CC0-1.0)
## typeface-inter
Copyright (c) 2016-2020 The Inter Project Authors.

View File

@ -4,4 +4,5 @@
module.exports = {
'valid-i18n-keys': require('./.eslint/rules/valid-i18n-keys'),
'type-alias-readonlydeep': require('./.eslint/rules/type-alias-readonlydeep'),
};

View File

@ -40,7 +40,7 @@
"test-release": "node ts/scripts/test-release.js",
"test-node": "electron-mocha --timeout 10000 --file test/setup-test-node.js --recursive test/modules ts/test-node ts/test-both",
"test-mock": "mocha ts/test-mock/**/*_test.js",
"test-eslint": "mocha .eslint/rules/**/*.test.js",
"test-eslint": "mocha .eslint/rules/**/*.test.js --ignore-leaks",
"test-node-coverage": "nyc --reporter=lcov --reporter=text mocha --recursive test/modules ts/test-node ts/test-both",
"test-lint-intl": "ts-node ./build/intl-linter/linter.ts --test",
"eslint": "eslint --cache . --max-warnings 0",
@ -174,6 +174,7 @@
"semver": "5.4.1",
"split2": "4.0.0",
"testcheck": "1.0.0-rc.2",
"type-fest": "3.5.0",
"typeface-inter": "3.18.1",
"uuid": "3.3.2",
"websocket": "1.0.34",

View File

@ -9,6 +9,7 @@ import { createPortal } from 'react-dom';
import { noop } from 'lodash';
import { useSpring, animated, to } from '@react-spring/web';
import type { ReadonlyDeep } from 'type-fest';
import type {
ConversationType,
SaveAttachmentActionCreatorType,
@ -29,7 +30,7 @@ export type PropsType = {
getConversation?: (id: string) => ConversationType;
i18n: LocalizerType;
isViewOnce?: boolean;
media: ReadonlyArray<MediaItemType>;
media: ReadonlyArray<ReadonlyDeep<MediaItemType>>;
saveAttachment: SaveAttachmentActionCreatorType;
selectedIndex?: number;
toggleForwardMessageModal: (messageId: string) => unknown;
@ -682,7 +683,7 @@ function LightboxHeader({
}: {
getConversation: (id: string) => ConversationType;
i18n: LocalizerType;
message: MediaItemMessageType;
message: ReadonlyDeep<MediaItemMessageType>;
}): JSX.Element {
const conversation = getConversation(message.conversationId);

View File

@ -7,6 +7,7 @@ import type { ReactChild, ReactNode, RefObject } from 'react';
import React from 'react';
import Measure from 'react-measure';
import type { ReadonlyDeep } from 'type-fest';
import { ScrollDownButton } from './ScrollDownButton';
import type { LocalizerType, ThemeType } from '../../types/Util';
@ -49,7 +50,7 @@ const MIN_ROW_HEIGHT = 18;
const SCROLL_DOWN_BUTTON_THRESHOLD = 8;
const LOAD_NEWER_THRESHOLD = 5;
export type WarningType =
export type WarningType = ReadonlyDeep<
| {
type: ContactSpoofingType.DirectConversationWithSameTitle;
safeConversation: ConversationType;
@ -58,7 +59,8 @@ export type WarningType =
type: ContactSpoofingType.MultipleGroupMembersWithSameTitle;
acknowledgedGroupNameCollisions: GroupNameCollisionsWithIdsByTitle;
groupNameCollisions: GroupNameCollisionsWithIdsByTitle;
};
}
>;
export type ContactSpoofingReviewPropType =
| {
@ -139,7 +141,7 @@ export type PropsActionsType = {
// From Backbone
acknowledgeGroupMemberNameCollisions: (
conversationId: string,
groupNameCollisions: Readonly<GroupNameCollisionsWithIdsByTitle>
groupNameCollisions: ReadonlyDeep<GroupNameCollisionsWithIdsByTitle>
) => void;
clearInvitedUuidsForNewlyCreatedGroup: () => void;
clearSelectedMessage: () => unknown;

View File

@ -3,6 +3,7 @@
import React from 'react';
import type { ReadonlyDeep } from 'type-fest';
import type { LocalizerType } from '../../../types/Util';
import type { MediaItemType } from '../../../types/MediaItem';
@ -19,7 +20,7 @@ export type Props = {
showAllMedia: () => void;
showLightboxWithMedia: (
selectedAttachmentPath: string | undefined,
media: ReadonlyArray<MediaItemType>
media: ReadonlyArray<ReadonlyDeep<MediaItemType>>
) => void;
};

View File

@ -4,6 +4,7 @@
import React from 'react';
import classNames from 'classnames';
import type { ReadonlyDeep } from 'type-fest';
import {
isImageTypeSupported,
isVideoTypeSupported,
@ -13,7 +14,7 @@ import type { MediaItemType } from '../../../types/MediaItem';
import * as log from '../../../logging/log';
export type Props = {
mediaItem: MediaItemType;
mediaItem: ReadonlyDeep<MediaItemType>;
onClick?: () => void;
i18n: LocalizerType;
};

View File

@ -14,6 +14,7 @@ import { batch as batchDispatch } from 'react-redux';
import { v4 as generateGuid } from 'uuid';
import PQueue from 'p-queue';
import type { ReadonlyDeep } from 'type-fest';
import type {
ConversationAttributesType,
ConversationLastProfileType,
@ -5512,7 +5513,7 @@ export class ConversationModel extends window.Backbone
}
acknowledgeGroupMemberNameCollisions(
groupNameCollisions: Readonly<GroupNameCollisionsWithIdsByTitle>
groupNameCollisions: ReadonlyDeep<GroupNameCollisionsWithIdsByTitle>
): void {
this.set('acknowledgedGroupNameCollisions', groupNameCollisions);
window.Signal.Data.updateConversation(this.attributes);

View File

@ -3,6 +3,7 @@
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import * as Errors from '../../types/errors';
import * as log from '../../logging/log';
@ -16,21 +17,21 @@ import type { NoopActionType } from './noop';
// State
export type AccountsStateType = {
export type AccountsStateType = ReadonlyDeep<{
accounts: Record<string, UUIDStringType | undefined>;
};
}>;
// Actions
type AccountUpdateActionType = {
type AccountUpdateActionType = ReadonlyDeep<{
type: 'accounts/UPDATE';
payload: {
phoneNumber: string;
uuid?: UUIDStringType;
};
};
}>;
export type AccountsActionType = AccountUpdateActionType;
export type AccountsActionType = ReadonlyDeep<AccountUpdateActionType>;
// Action Creators

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import type { StateType as RootStateType } from '../reducer';
import * as log from '../../logging/log';
@ -14,10 +15,10 @@ export enum AppViewType {
Standalone = 'Standalone',
}
export type AppStateType = {
export type AppStateType = ReadonlyDeep<{
appView: AppViewType;
hasInitialLoadCompleted: boolean;
};
}>;
// Actions
@ -26,27 +27,28 @@ const OPEN_INBOX = 'app/OPEN_INBOX';
const OPEN_INSTALLER = 'app/OPEN_INSTALLER';
const OPEN_STANDALONE = 'app/OPEN_STANDALONE';
type InitialLoadCompleteActionType = {
type InitialLoadCompleteActionType = ReadonlyDeep<{
type: typeof INITIAL_LOAD_COMPLETE;
};
}>;
type OpenInboxActionType = {
type OpenInboxActionType = ReadonlyDeep<{
type: typeof OPEN_INBOX;
};
}>;
type OpenInstallerActionType = {
type OpenInstallerActionType = ReadonlyDeep<{
type: typeof OPEN_INSTALLER;
};
}>;
type OpenStandaloneActionType = {
type OpenStandaloneActionType = ReadonlyDeep<{
type: typeof OPEN_STANDALONE;
};
}>;
export type AppActionType =
export type AppActionType = ReadonlyDeep<
| InitialLoadCompleteActionType
| OpenInboxActionType
| OpenInstallerActionType
| OpenStandaloneActionType;
| OpenStandaloneActionType
>;
export const actions = {
initialLoadComplete,

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import { useBoundActions } from '../../hooks/useBoundActions';
import { Sound } from '../../util/Sound';
@ -35,25 +36,25 @@ import { getMessagePropStatus } from '../selectors/message';
// State
export type ActiveAudioPlayerStateType = {
readonly playing: boolean;
readonly currentTime: number;
readonly playbackRate: number;
readonly duration: number;
};
export type ActiveAudioPlayerStateType = ReadonlyDeep<{
playing: boolean;
currentTime: number;
playbackRate: number;
duration: number;
}>;
export type AudioPlayerStateType = {
readonly active:
export type AudioPlayerStateType = ReadonlyDeep<{
active:
| (ActiveAudioPlayerStateType & { id: string; context: string })
| undefined;
};
}>;
// Actions
/**
* Sets the current "active" message audio for a particular rendering "context"
*/
export type SetMessageAudioAction = {
export type SetMessageAudioAction = ReadonlyDeep<{
type: 'audioPlayer/SET_MESSAGE_AUDIO';
payload:
| {
@ -63,39 +64,40 @@ export type SetMessageAudioAction = {
duration: number;
}
| undefined;
};
}>;
type SetPlaybackRate = {
type SetPlaybackRate = ReadonlyDeep<{
type: 'audioPlayer/SET_PLAYBACK_RATE';
payload: number;
};
}>;
type SetIsPlayingAction = {
type SetIsPlayingAction = ReadonlyDeep<{
type: 'audioPlayer/SET_IS_PLAYING';
payload: boolean;
};
}>;
type CurrentTimeUpdated = {
type CurrentTimeUpdated = ReadonlyDeep<{
type: 'audioPlayer/CURRENT_TIME_UPDATED';
payload: number;
};
}>;
type MessageAudioEnded = {
type MessageAudioEnded = ReadonlyDeep<{
type: 'audioPlayer/MESSAGE_AUDIO_ENDED';
};
}>;
type DurationChanged = {
type DurationChanged = ReadonlyDeep<{
type: 'audioPlayer/DURATION_CHANGED';
payload: number;
};
}>;
type AudioPlayerActionType =
type AudioPlayerActionType = ReadonlyDeep<
| SetMessageAudioAction
| SetIsPlayingAction
| SetPlaybackRate
| MessageAudioEnded
| CurrentTimeUpdated
| DurationChanged;
| DurationChanged
>;
// Action Creators

View File

@ -3,6 +3,7 @@
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import * as log from '../../logging/log';
import type { InMemoryAttachmentDraftType } from '../../types/Attachment';
import { SignalService as Proto } from '../../protobuf';
@ -28,10 +29,10 @@ export enum RecordingState {
Idle = 'idle',
}
export type AudioPlayerStateType = {
readonly recordingState: RecordingState;
readonly errorDialogAudioRecorderType?: ErrorDialogAudioRecorderType;
};
export type AudioPlayerStateType = ReadonlyDeep<{
recordingState: RecordingState;
errorDialogAudioRecorderType?: ErrorDialogAudioRecorderType;
}>;
// Actions
@ -41,33 +42,34 @@ const ERROR_RECORDING = 'audioRecorder/ERROR_RECORDING';
const NOW_RECORDING = 'audioRecorder/NOW_RECORDING';
const START_RECORDING = 'audioRecorder/START_RECORDING';
type CancelRecordingAction = {
type CancelRecordingAction = ReadonlyDeep<{
type: typeof CANCEL_RECORDING;
payload: undefined;
};
type CompleteRecordingAction = {
}>;
type CompleteRecordingAction = ReadonlyDeep<{
type: typeof COMPLETE_RECORDING;
payload: undefined;
};
type ErrorRecordingAction = {
}>;
type ErrorRecordingAction = ReadonlyDeep<{
type: typeof ERROR_RECORDING;
payload: ErrorDialogAudioRecorderType;
};
type StartRecordingAction = {
}>;
type StartRecordingAction = ReadonlyDeep<{
type: typeof START_RECORDING;
payload: undefined;
};
type NowRecordingAction = {
}>;
type NowRecordingAction = ReadonlyDeep<{
type: typeof NOW_RECORDING;
payload: undefined;
};
}>;
type AudioPlayerActionType =
type AudioPlayerActionType = ReadonlyDeep<
| CancelRecordingAction
| CompleteRecordingAction
| ErrorRecordingAction
| NowRecordingAction
| StartRecordingAction;
| StartRecordingAction
>;
// Action Creators

View File

@ -3,6 +3,7 @@
import type { ThunkAction } from 'redux-thunk';
import { mapValues } from 'lodash';
import type { ReadonlyDeep } from 'type-fest';
import type { StateType as RootStateType } from '../reducer';
import type { BadgeType, BadgeImageType } from '../../badges/types';
import { getOwn } from '../../util/getOwn';
@ -22,27 +23,27 @@ import { badgeImageFileDownloader } from '../../badges/badgeImageFileDownloader'
// State
export type BadgesStateType = {
export type BadgesStateType = ReadonlyDeep<{
byId: Record<string, BadgeType>;
};
}>;
// Actions
const IMAGE_FILE_DOWNLOADED = 'badges/IMAGE_FILE_DOWNLOADED';
const UPDATE_OR_CREATE = 'badges/UPDATE_OR_CREATE';
type ImageFileDownloadedActionType = {
type ImageFileDownloadedActionType = ReadonlyDeep<{
type: typeof IMAGE_FILE_DOWNLOADED;
payload: {
url: string;
localPath: string;
};
};
}>;
type UpdateOrCreateActionType = {
type UpdateOrCreateActionType = ReadonlyDeep<{
type: typeof UPDATE_OR_CREATE;
payload: ReadonlyArray<BadgeType>;
};
payload: Array<BadgeType>;
}>;
// Action creators

View File

@ -9,6 +9,7 @@ import {
openSystemPreferences,
} from 'mac-screen-capture-permissions';
import { has, omit } from 'lodash';
import type { ReadonlyDeep } from 'type-fest';
import { getOwn } from '../../util/getOwn';
import * as Errors from '../../types/errors';
import { getPlatform } from '../selectors/user';
@ -57,14 +58,15 @@ import MessageSender from '../../textsecure/SendMessage';
// State
export type GroupCallPeekInfoType = {
export type GroupCallPeekInfoType = ReadonlyDeep<{
uuids: Array<UUIDStringType>;
creatorUuid?: UUIDStringType;
eraId?: string;
maxDevices: number;
deviceCount: number;
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type GroupCallParticipantInfoType = {
uuid: UUIDStringType;
demuxId: number;
@ -76,6 +78,7 @@ export type GroupCallParticipantInfoType = {
videoAspectRatio: number;
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type DirectCallStateType = {
callMode: CallMode.Direct;
conversationId: string;
@ -87,7 +90,7 @@ export type DirectCallStateType = {
hasRemoteVideo?: boolean;
};
type GroupCallRingStateType =
type GroupCallRingStateType = ReadonlyDeep<
| {
ringId?: undefined;
ringerUuid?: undefined;
@ -95,8 +98,10 @@ type GroupCallRingStateType =
| {
ringId: bigint;
ringerUuid: UUIDStringType;
};
}
>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type GroupCallStateType = {
callMode: CallMode.Group;
conversationId: string;
@ -107,6 +112,7 @@ export type GroupCallStateType = {
remoteAudioLevels?: Map<number, number>;
} & GroupCallRingStateType;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type ActiveCallStateType = {
conversationId: string;
hasLocalAudio: boolean;
@ -124,21 +130,23 @@ export type ActiveCallStateType = {
showParticipantsList: boolean;
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type CallsByConversationType = {
[conversationId: string]: DirectCallStateType | GroupCallStateType;
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type CallingStateType = MediaDeviceSettings & {
callsByConversation: CallsByConversationType;
activeCallState?: ActiveCallStateType;
};
export type AcceptCallType = {
export type AcceptCallType = ReadonlyDeep<{
conversationId: string;
asVideoCall: boolean;
};
}>;
export type CallStateChangeType = {
export type CallStateChangeType = ReadonlyDeep<{
remoteUserId: string; // TODO: Remove
callId: string; // TODO: Remove
conversationId: string;
@ -148,21 +156,22 @@ export type CallStateChangeType = {
isIncoming: boolean;
isVideoCall: boolean;
title: string;
};
}>;
export type CancelCallType = {
export type CancelCallType = ReadonlyDeep<{
conversationId: string;
};
}>;
type CancelIncomingGroupCallRingType = {
type CancelIncomingGroupCallRingType = ReadonlyDeep<{
conversationId: string;
ringId: bigint;
};
}>;
export type DeclineCallType = {
export type DeclineCallType = ReadonlyDeep<{
conversationId: string;
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type GroupCallStateChangeArgumentType = {
connectionState: GroupCallConnectionState;
conversationId: string;
@ -173,77 +182,81 @@ type GroupCallStateChangeArgumentType = {
remoteParticipants: Array<GroupCallParticipantInfoType>;
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type GroupCallStateChangeActionPayloadType =
GroupCallStateChangeArgumentType & {
ourUuid: UUIDStringType;
};
type HangUpActionPayloadType = {
type HangUpActionPayloadType = ReadonlyDeep<{
conversationId: string;
};
}>;
type KeyChangedType = {
type KeyChangedType = ReadonlyDeep<{
uuid: UUIDStringType;
};
}>;
export type KeyChangeOkType = {
export type KeyChangeOkType = ReadonlyDeep<{
conversationId: string;
};
}>;
export type IncomingDirectCallType = {
export type IncomingDirectCallType = ReadonlyDeep<{
conversationId: string;
isVideoCall: boolean;
};
}>;
type IncomingGroupCallType = {
type IncomingGroupCallType = ReadonlyDeep<{
conversationId: string;
ringId: bigint;
ringerUuid: UUIDStringType;
};
}>;
type PeekNotConnectedGroupCallType = {
type PeekNotConnectedGroupCallType = ReadonlyDeep<{
conversationId: string;
};
}>;
type StartDirectCallType = {
type StartDirectCallType = ReadonlyDeep<{
conversationId: string;
hasLocalAudio: boolean;
hasLocalVideo: boolean;
};
}>;
export type StartCallType = StartDirectCallType & {
callMode: CallMode.Direct | CallMode.Group;
};
export type StartCallType = ReadonlyDeep<
StartDirectCallType & {
callMode: CallMode.Direct | CallMode.Group;
}
>;
export type RemoteVideoChangeType = {
export type RemoteVideoChangeType = ReadonlyDeep<{
conversationId: string;
hasVideo: boolean;
};
}>;
type RemoteSharingScreenChangeType = {
type RemoteSharingScreenChangeType = ReadonlyDeep<{
conversationId: string;
isSharingScreen: boolean;
};
}>;
export type SetLocalAudioType = {
export type SetLocalAudioType = ReadonlyDeep<{
enabled: boolean;
};
}>;
export type SetLocalVideoType = {
export type SetLocalVideoType = ReadonlyDeep<{
enabled: boolean;
};
}>;
export type SetGroupCallVideoRequestType = {
export type SetGroupCallVideoRequestType = ReadonlyDeep<{
conversationId: string;
resolutions: Array<GroupCallVideoRequest>;
speakerHeight: number;
};
}>;
export type StartCallingLobbyType = {
export type StartCallingLobbyType = ReadonlyDeep<{
conversationId: string;
isVideoCall: boolean;
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type StartCallingLobbyPayloadType =
| {
callMode: CallMode.Direct;
@ -263,10 +276,12 @@ type StartCallingLobbyPayloadType =
remoteParticipants: Array<GroupCallParticipantInfoType>;
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type SetLocalPreviewType = {
element: React.RefObject<HTMLVideoElement> | undefined;
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type SetRendererCanvasType = {
element: React.RefObject<HTMLCanvasElement> | undefined;
};
@ -441,76 +456,79 @@ const TOGGLE_SPEAKER_VIEW = 'calling/TOGGLE_SPEAKER_VIEW';
const SWITCH_TO_PRESENTATION_VIEW = 'calling/SWITCH_TO_PRESENTATION_VIEW';
const SWITCH_FROM_PRESENTATION_VIEW = 'calling/SWITCH_FROM_PRESENTATION_VIEW';
type AcceptCallPendingActionType = {
type AcceptCallPendingActionType = ReadonlyDeep<{
type: 'calling/ACCEPT_CALL_PENDING';
payload: AcceptCallType;
};
}>;
type CancelCallActionType = {
type CancelCallActionType = ReadonlyDeep<{
type: 'calling/CANCEL_CALL';
};
}>;
type CancelIncomingGroupCallRingActionType = {
type CancelIncomingGroupCallRingActionType = ReadonlyDeep<{
type: 'calling/CANCEL_INCOMING_GROUP_CALL_RING';
payload: CancelIncomingGroupCallRingType;
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type StartCallingLobbyActionType = {
type: 'calling/START_CALLING_LOBBY';
payload: StartCallingLobbyPayloadType;
};
type CallStateChangeFulfilledActionType = {
type CallStateChangeFulfilledActionType = ReadonlyDeep<{
type: 'calling/CALL_STATE_CHANGE_FULFILLED';
payload: CallStateChangeType;
};
}>;
type ChangeIODeviceFulfilledActionType = {
type ChangeIODeviceFulfilledActionType = ReadonlyDeep<{
type: 'calling/CHANGE_IO_DEVICE_FULFILLED';
payload: ChangeIODevicePayloadType;
};
}>;
type CloseNeedPermissionScreenActionType = {
type CloseNeedPermissionScreenActionType = ReadonlyDeep<{
type: 'calling/CLOSE_NEED_PERMISSION_SCREEN';
payload: null;
};
}>;
type DeclineCallActionType = {
type DeclineCallActionType = ReadonlyDeep<{
type: 'calling/DECLINE_DIRECT_CALL';
payload: DeclineCallType;
};
}>;
type GroupCallAudioLevelsChangeActionPayloadType = Readonly<{
type GroupCallAudioLevelsChangeActionPayloadType = ReadonlyDeep<{
conversationId: string;
localAudioLevel: number;
remoteDeviceStates: ReadonlyArray<{ audioLevel: number; demuxId: number }>;
}>;
type GroupCallAudioLevelsChangeActionType = {
type GroupCallAudioLevelsChangeActionType = ReadonlyDeep<{
type: 'calling/GROUP_CALL_AUDIO_LEVELS_CHANGE';
payload: GroupCallAudioLevelsChangeActionPayloadType;
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type GroupCallStateChangeActionType = {
type: 'calling/GROUP_CALL_STATE_CHANGE';
payload: GroupCallStateChangeActionPayloadType;
};
type HangUpActionType = {
type HangUpActionType = ReadonlyDeep<{
type: 'calling/HANG_UP';
payload: HangUpActionPayloadType;
};
}>;
type IncomingDirectCallActionType = {
type IncomingDirectCallActionType = ReadonlyDeep<{
type: 'calling/INCOMING_DIRECT_CALL';
payload: IncomingDirectCallType;
};
}>;
type IncomingGroupCallActionType = {
type IncomingGroupCallActionType = ReadonlyDeep<{
type: 'calling/INCOMING_GROUP_CALL';
payload: IncomingGroupCallType;
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type KeyChangedActionType = {
type: 'calling/MARK_CALL_UNTRUSTED';
payload: {
@ -518,101 +536,104 @@ type KeyChangedActionType = {
};
};
type KeyChangeOkActionType = {
type KeyChangeOkActionType = ReadonlyDeep<{
type: 'calling/MARK_CALL_TRUSTED';
payload: null;
};
}>;
type OutgoingCallActionType = {
type OutgoingCallActionType = ReadonlyDeep<{
type: 'calling/OUTGOING_CALL';
payload: StartDirectCallType;
};
}>;
export type PeekGroupCallFulfilledActionType = {
export type PeekGroupCallFulfilledActionType = ReadonlyDeep<{
type: 'calling/PEEK_GROUP_CALL_FULFILLED';
payload: {
conversationId: string;
peekInfo: GroupCallPeekInfoType;
};
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type RefreshIODevicesActionType = {
type: 'calling/REFRESH_IO_DEVICES';
payload: MediaDeviceSettings;
};
type RemoteSharingScreenChangeActionType = {
type RemoteSharingScreenChangeActionType = ReadonlyDeep<{
type: 'calling/REMOTE_SHARING_SCREEN_CHANGE';
payload: RemoteSharingScreenChangeType;
};
}>;
type RemoteVideoChangeActionType = {
type RemoteVideoChangeActionType = ReadonlyDeep<{
type: 'calling/REMOTE_VIDEO_CHANGE';
payload: RemoteVideoChangeType;
};
}>;
type ReturnToActiveCallActionType = {
type ReturnToActiveCallActionType = ReadonlyDeep<{
type: 'calling/RETURN_TO_ACTIVE_CALL';
};
}>;
type SetLocalAudioActionType = {
type SetLocalAudioActionType = ReadonlyDeep<{
type: 'calling/SET_LOCAL_AUDIO_FULFILLED';
payload: SetLocalAudioType;
};
}>;
type SetLocalVideoFulfilledActionType = {
type SetLocalVideoFulfilledActionType = ReadonlyDeep<{
type: 'calling/SET_LOCAL_VIDEO_FULFILLED';
payload: SetLocalVideoType;
};
}>;
type SetPresentingFulfilledActionType = {
type SetPresentingFulfilledActionType = ReadonlyDeep<{
type: 'calling/SET_PRESENTING';
payload?: PresentedSource;
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type SetPresentingSourcesActionType = {
type: 'calling/SET_PRESENTING_SOURCES';
payload: Array<PresentableSource>;
};
type SetOutgoingRingActionType = {
type SetOutgoingRingActionType = ReadonlyDeep<{
type: 'calling/SET_OUTGOING_RING';
payload: boolean;
};
}>;
type StartDirectCallActionType = {
type StartDirectCallActionType = ReadonlyDeep<{
type: 'calling/START_DIRECT_CALL';
payload: StartDirectCallType;
};
}>;
type ToggleNeedsScreenRecordingPermissionsActionType = {
type ToggleNeedsScreenRecordingPermissionsActionType = ReadonlyDeep<{
type: 'calling/TOGGLE_NEEDS_SCREEN_RECORDING_PERMISSIONS';
};
}>;
type ToggleParticipantsActionType = {
type ToggleParticipantsActionType = ReadonlyDeep<{
type: 'calling/TOGGLE_PARTICIPANTS';
};
}>;
type TogglePipActionType = {
type TogglePipActionType = ReadonlyDeep<{
type: 'calling/TOGGLE_PIP';
};
}>;
type ToggleSettingsActionType = {
type ToggleSettingsActionType = ReadonlyDeep<{
type: 'calling/TOGGLE_SETTINGS';
};
}>;
type ToggleSpeakerViewActionType = {
type ToggleSpeakerViewActionType = ReadonlyDeep<{
type: 'calling/TOGGLE_SPEAKER_VIEW';
};
}>;
type SwitchToPresentationViewActionType = {
type SwitchToPresentationViewActionType = ReadonlyDeep<{
type: 'calling/SWITCH_TO_PRESENTATION_VIEW';
};
}>;
type SwitchFromPresentationViewActionType = {
type SwitchFromPresentationViewActionType = ReadonlyDeep<{
type: 'calling/SWITCH_FROM_PRESENTATION_VIEW';
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type CallingActionType =
| AcceptCallPendingActionType
| CancelCallActionType
@ -1545,7 +1566,7 @@ export const actions = {
toggleSpeakerView,
};
export type ActionsType = typeof actions;
export type ActionsType = ReadonlyDeep<typeof actions>;
// Reducer

View File

@ -6,6 +6,7 @@ import path from 'path';
import { debounce } from 'lodash';
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import type {
AddLinkPreviewActionType,
RemoveLinkPreviewActionType,
@ -86,7 +87,7 @@ import { drop } from '../../util/drop';
import { strictAssert } from '../../util/assert';
// State
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type ComposerStateByConversationType = {
attachments: ReadonlyArray<AttachmentDraftType>;
focusCounter: number;
@ -98,11 +99,13 @@ type ComposerStateByConversationType = {
shouldSendHighQualityAttachments?: boolean;
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type QuotedMessageType = Pick<
MessageAttributesType,
'conversationId' | 'quote'
>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type ComposerStateType = {
conversations: Record<string, ComposerStateByConversationType>;
};
@ -134,14 +137,15 @@ const SET_HIGH_QUALITY_SETTING = 'composer/SET_HIGH_QUALITY_SETTING';
const SET_QUOTED_MESSAGE = 'composer/SET_QUOTED_MESSAGE';
const SET_COMPOSER_DISABLED = 'composer/SET_COMPOSER_DISABLED';
type AddPendingAttachmentActionType = {
type AddPendingAttachmentActionType = ReadonlyDeep<{
type: typeof ADD_PENDING_ATTACHMENT;
payload: {
conversationId: string;
attachment: AttachmentDraftType;
};
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type ReplaceAttachmentsActionType = {
type: typeof REPLACE_ATTACHMENTS;
payload: {
@ -150,36 +154,37 @@ export type ReplaceAttachmentsActionType = {
};
};
export type ResetComposerActionType = {
export type ResetComposerActionType = ReadonlyDeep<{
type: typeof RESET_COMPOSER;
payload: {
conversationId: string;
};
};
}>;
type SetComposerDisabledStateActionType = {
type SetComposerDisabledStateActionType = ReadonlyDeep<{
type: typeof SET_COMPOSER_DISABLED;
payload: {
conversationId: string;
value: boolean;
};
};
}>;
export type SetFocusActionType = {
export type SetFocusActionType = ReadonlyDeep<{
type: typeof SET_FOCUS;
payload: {
conversationId: string;
};
};
}>;
type SetHighQualitySettingActionType = {
type SetHighQualitySettingActionType = ReadonlyDeep<{
type: typeof SET_HIGH_QUALITY_SETTING;
payload: {
conversationId: string;
value: boolean;
};
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type SetQuotedMessageActionType = {
type: typeof SET_QUOTED_MESSAGE;
payload: {
@ -188,6 +193,7 @@ export type SetQuotedMessageActionType = {
};
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type ComposerActionType =
| AddLinkPreviewActionType
| AddPendingAttachmentActionType

View File

@ -12,6 +12,7 @@ import {
without,
} from 'lodash';
import type { ReadonlyDeep } from 'type-fest';
import type { AttachmentType } from '../../types/Attachment';
import type { StateType as RootStateType } from '../reducer';
import * as groups from '../../groups';
@ -152,27 +153,31 @@ import {
// State
export type DBConversationType = {
export type DBConversationType = ReadonlyDeep<{
id: string;
activeAt?: number;
lastMessage?: string | null;
type: string;
};
}>;
export const InteractionModes = ['mouse', 'keyboard'] as const;
export type InteractionModeType = typeof InteractionModes[number];
export type InteractionModeType = ReadonlyDeep<typeof InteractionModes[number]>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type MessageType = MessageAttributesType & {
interactionType?: InteractionModeType;
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type MessageWithUIFieldsType = MessageAttributesType & {
displayLimit?: number;
};
export const ConversationTypes = ['direct', 'group'] as const;
export type ConversationTypeType = typeof ConversationTypes[number];
export type ConversationTypeType = ReadonlyDeep<
typeof ConversationTypes[number]
>;
export type LastMessageType = Readonly<
export type LastMessageType = ReadonlyDeep<
| {
status?: LastMessageStatus;
text: string;
@ -182,154 +187,161 @@ export type LastMessageType = Readonly<
| { deletedForEveryone: true }
>;
export type ConversationType = {
id: string;
uuid?: UUIDStringType;
pni?: UUIDStringType;
e164?: string;
name?: string;
systemGivenName?: string;
systemFamilyName?: string;
familyName?: string;
firstName?: string;
profileName?: string;
username?: string;
about?: string;
aboutText?: string;
aboutEmoji?: string;
avatars?: ReadonlyArray<AvatarDataType>;
avatarPath?: string;
avatarHash?: string;
profileAvatarPath?: string;
unblurredAvatarPath?: string;
areWeAdmin?: boolean;
areWePending?: boolean;
areWePendingApproval?: boolean;
canChangeTimer?: boolean;
canEditGroupInfo?: boolean;
canAddNewMembers?: boolean;
color?: AvatarColorType;
conversationColor?: ConversationColorType;
customColor?: CustomColorType;
customColorId?: string;
discoveredUnregisteredAt?: number;
hideStory?: boolean;
isArchived?: boolean;
isBlocked?: boolean;
isGroupV1AndDisabled?: boolean;
isPinned?: boolean;
isUntrusted?: boolean;
isVerified?: boolean;
activeAt?: number;
timestamp?: number;
inboxPosition?: number;
left?: boolean;
lastMessage?: LastMessageType;
markedUnread?: boolean;
phoneNumber?: string;
membersCount?: number;
hasMessages?: boolean;
accessControlAddFromInviteLink?: number;
accessControlAttributes?: number;
accessControlMembers?: number;
announcementsOnly?: boolean;
announcementsOnlyReady?: boolean;
expireTimer?: DurationInSeconds;
memberships?: ReadonlyArray<{
uuid: UUIDStringType;
isAdmin: boolean;
}>;
pendingMemberships?: ReadonlyArray<{
uuid: UUIDStringType;
addedByUserId?: UUIDStringType;
}>;
pendingApprovalMemberships?: ReadonlyArray<{
uuid: UUIDStringType;
}>;
bannedMemberships?: ReadonlyArray<UUIDStringType>;
muteExpiresAt?: number;
dontNotifyForMentionsIfMuted?: boolean;
isMe: boolean;
lastUpdated?: number;
// This is used by the CompositionInput for @mentions
sortedGroupMembers?: ReadonlyArray<ConversationType>;
title: string;
titleNoDefault?: string;
searchableTitle?: string;
unreadCount?: number;
isSelected?: boolean;
isFetchingUUID?: boolean;
typingContactId?: string;
recentMediaItems?: ReadonlyArray<MediaItemType>;
profileSharing?: boolean;
export type ConversationType = ReadonlyDeep<
{
id: string;
uuid?: UUIDStringType;
pni?: UUIDStringType;
e164?: string;
name?: string;
systemGivenName?: string;
systemFamilyName?: string;
familyName?: string;
firstName?: string;
profileName?: string;
username?: string;
about?: string;
aboutText?: string;
aboutEmoji?: string;
avatars?: ReadonlyArray<AvatarDataType>;
avatarPath?: string;
avatarHash?: string;
profileAvatarPath?: string;
unblurredAvatarPath?: string;
areWeAdmin?: boolean;
areWePending?: boolean;
areWePendingApproval?: boolean;
canChangeTimer?: boolean;
canEditGroupInfo?: boolean;
canAddNewMembers?: boolean;
color?: AvatarColorType;
conversationColor?: ConversationColorType;
customColor?: CustomColorType;
customColorId?: string;
discoveredUnregisteredAt?: number;
hideStory?: boolean;
isArchived?: boolean;
isBlocked?: boolean;
isGroupV1AndDisabled?: boolean;
isPinned?: boolean;
isUntrusted?: boolean;
isVerified?: boolean;
activeAt?: number;
timestamp?: number;
inboxPosition?: number;
left?: boolean;
lastMessage?: LastMessageType;
markedUnread?: boolean;
phoneNumber?: string;
membersCount?: number;
hasMessages?: boolean;
accessControlAddFromInviteLink?: number;
accessControlAttributes?: number;
accessControlMembers?: number;
announcementsOnly?: boolean;
announcementsOnlyReady?: boolean;
expireTimer?: DurationInSeconds;
memberships?: ReadonlyArray<{
uuid: UUIDStringType;
isAdmin: boolean;
}>;
pendingMemberships?: ReadonlyArray<{
uuid: UUIDStringType;
addedByUserId?: UUIDStringType;
}>;
pendingApprovalMemberships?: ReadonlyArray<{
uuid: UUIDStringType;
}>;
bannedMemberships?: ReadonlyArray<UUIDStringType>;
muteExpiresAt?: number;
dontNotifyForMentionsIfMuted?: boolean;
isMe: boolean;
lastUpdated?: number;
// This is used by the CompositionInput for @mentions
sortedGroupMembers?: ReadonlyArray<ConversationType>;
title: string;
titleNoDefault?: string;
searchableTitle?: string;
unreadCount?: number;
isSelected?: boolean;
isFetchingUUID?: boolean;
typingContactId?: string;
recentMediaItems?: ReadonlyArray<MediaItemType>;
profileSharing?: boolean;
shouldShowDraft?: boolean;
draftText?: string;
draftBodyRanges?: DraftBodyRangesType;
draftPreview?: string;
shouldShowDraft?: boolean;
draftText?: string;
draftBodyRanges?: DraftBodyRangesType;
draftPreview?: string;
sharedGroupNames: ReadonlyArray<string>;
groupDescription?: string;
groupVersion?: 1 | 2;
groupId?: string;
groupLink?: string;
messageRequestsEnabled?: boolean;
acceptedMessageRequest: boolean;
secretParams?: string;
publicParams?: string;
profileKey?: string;
voiceNotePlaybackRate?: number;
sharedGroupNames: ReadonlyArray<string>;
groupDescription?: string;
groupVersion?: 1 | 2;
groupId?: string;
groupLink?: string;
messageRequestsEnabled?: boolean;
acceptedMessageRequest: boolean;
secretParams?: string;
publicParams?: string;
profileKey?: string;
voiceNotePlaybackRate?: number;
badges: ReadonlyArray<
badges: ReadonlyArray<
| {
id: string;
}
| {
id: string;
expiresAt: number;
isVisible: boolean;
}
>;
} & (
| {
id: string;
type: 'direct';
storySendMode?: undefined;
acknowledgedGroupNameCollisions?: undefined;
}
| {
id: string;
expiresAt: number;
isVisible: boolean;
type: 'group';
storySendMode: StorySendMode;
acknowledgedGroupNameCollisions: GroupNameCollisionsWithIdsByTitle;
}
>;
} & (
| {
type: 'direct';
storySendMode?: undefined;
acknowledgedGroupNameCollisions?: undefined;
}
| {
type: 'group';
storySendMode: StorySendMode;
acknowledgedGroupNameCollisions: GroupNameCollisionsWithIdsByTitle;
}
);
export type ProfileDataType = {
firstName: string;
} & Pick<ConversationType, 'aboutEmoji' | 'aboutText' | 'familyName'>;
)
>;
export type ProfileDataType = ReadonlyDeep<
{
firstName: string;
} & Pick<ConversationType, 'aboutEmoji' | 'aboutText' | 'familyName'>
>;
export type ConversationLookupType = {
export type ConversationLookupType = ReadonlyDeep<{
[key: string]: ConversationType;
};
export type CustomError = Error & {
identifier?: string;
number?: string;
};
}>;
export type CustomError = ReadonlyDeep<
Error & {
identifier?: string;
number?: string;
}
>;
type MessagePointerType = {
type MessagePointerType = ReadonlyDeep<{
id: string;
received_at: number;
sent_at?: number;
};
type MessageMetricsType = {
}>;
type MessageMetricsType = ReadonlyDeep<{
newest?: MessagePointerType;
oldest?: MessagePointerType;
oldestUnseen?: MessagePointerType;
totalUnseen: number;
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type MessageLookupType = {
[key: string]: MessageWithUIFieldsType;
};
export type ConversationMessageType = {
export type ConversationMessageType = ReadonlyDeep<{
isNearBottom?: boolean;
messageChangeCounter: number;
messageIds: ReadonlyArray<string>;
@ -337,13 +349,13 @@ export type ConversationMessageType = {
metrics: MessageMetricsType;
scrollToMessageId?: string;
scrollToMessageCounter: number;
};
}>;
export type MessagesByConversationType = {
export type MessagesByConversationType = ReadonlyDeep<{
[key: string]: ConversationMessageType | undefined;
};
}>;
export type PreJoinConversationType = {
export type PreJoinConversationType = ReadonlyDeep<{
avatar?: {
loading?: boolean;
url?: string;
@ -352,9 +364,9 @@ export type PreJoinConversationType = {
memberCount: number;
title: string;
approvalRequired: boolean;
};
}>;
type ComposerGroupCreationState = {
type ComposerGroupCreationState = ReadonlyDeep<{
groupAvatar: undefined | Uint8Array;
groupName: string;
groupExpireTimer: DurationInSeconds;
@ -362,13 +374,13 @@ type ComposerGroupCreationState = {
recommendedGroupSizeModalState: OneTimeModalState;
selectedConversationIds: ReadonlyArray<string>;
userAvatarData: ReadonlyArray<AvatarDataType>;
};
}>;
type DistributionVerificationData = {
uuidsNeedingVerification: ReadonlyArray<UUIDStringType>;
};
type DistributionVerificationData = ReadonlyDeep<{
uuidsNeedingVerification: Array<UUIDStringType>;
}>;
export type ConversationVerificationData =
export type ConversationVerificationData = ReadonlyDeep<
| {
type: ConversationVerificationState.PendingVerification;
uuidsNeedingVerification: ReadonlyArray<UUIDStringType>;
@ -378,14 +390,14 @@ export type ConversationVerificationData =
| {
type: ConversationVerificationState.VerificationCancelled;
canceledAt: number;
};
type VerificationDataByConversation = Record<
string,
ConversationVerificationData
}
>;
type ComposerStateType =
type VerificationDataByConversation = ReadonlyDeep<
Record<string, ConversationVerificationData>
>;
type ComposerStateType = ReadonlyDeep<
| {
step: ComposerStep.StartDirectConversation;
searchTerm: string;
@ -403,9 +415,10 @@ type ComposerStateType =
(
| { isCreating: false; hasError: boolean }
| { isCreating: true; hasError: false }
));
))
>;
type ContactSpoofingReviewStateType =
type ContactSpoofingReviewStateType = ReadonlyDeep<
| {
type: ContactSpoofingType.DirectConversationWithSameTitle;
safeConversationId: string;
@ -413,9 +426,11 @@ type ContactSpoofingReviewStateType =
| {
type: ContactSpoofingType.MultipleGroupMembersWithSameTitle;
groupConversationId: string;
};
}
>;
export type ConversationsStateType = {
// eslint-disable-next-line local-rules/type-alias-readonlydeep -- FIXME
export type ConversationsStateType = Readonly<{
preJoinConversation?: PreJoinConversationType;
invitedUuidsForNewlyCreatedGroup?: ReadonlyArray<string>;
conversationLookup: ConversationLookupType;
@ -444,7 +459,7 @@ export type ConversationsStateType = {
// Note: it's very important that both of these locations are always kept up to date
messagesLookup: MessageLookupType;
messagesByConversation: MessagesByConversationType;
};
}>;
// Helpers
@ -502,35 +517,37 @@ export const SET_VOICE_NOTE_PLAYBACK_RATE =
'conversations/SET_VOICE_NOTE_PLAYBACK_RATE';
export const CONVERSATION_UNLOADED = 'CONVERSATION_UNLOADED';
export type CancelVerificationDataByConversationActionType = {
export type CancelVerificationDataByConversationActionType = ReadonlyDeep<{
type: typeof CANCEL_CONVERSATION_PENDING_VERIFICATION;
payload: {
canceledAt: number;
};
};
type ClearGroupCreationErrorActionType = { type: 'CLEAR_GROUP_CREATION_ERROR' };
type ClearInvitedUuidsForNewlyCreatedGroupActionType = {
}>;
type ClearGroupCreationErrorActionType = ReadonlyDeep<{
type: 'CLEAR_GROUP_CREATION_ERROR';
}>;
type ClearInvitedUuidsForNewlyCreatedGroupActionType = ReadonlyDeep<{
type: 'CLEAR_INVITED_UUIDS_FOR_NEWLY_CREATED_GROUP';
};
type ClearVerificationDataByConversationActionType = {
}>;
type ClearVerificationDataByConversationActionType = ReadonlyDeep<{
type: typeof CLEAR_CONVERSATIONS_PENDING_VERIFICATION;
};
type ClearCancelledVerificationActionType = {
}>;
type ClearCancelledVerificationActionType = ReadonlyDeep<{
type: typeof CLEAR_CANCELLED_VERIFICATION;
payload: {
conversationId: string;
};
};
type CloseContactSpoofingReviewActionType = {
}>;
type CloseContactSpoofingReviewActionType = ReadonlyDeep<{
type: 'CLOSE_CONTACT_SPOOFING_REVIEW';
};
type CloseMaximumGroupSizeModalActionType = {
}>;
type CloseMaximumGroupSizeModalActionType = ReadonlyDeep<{
type: 'CLOSE_MAXIMUM_GROUP_SIZE_MODAL';
};
type CloseRecommendedGroupSizeModalActionType = {
}>;
type CloseRecommendedGroupSizeModalActionType = ReadonlyDeep<{
type: 'CLOSE_RECOMMENDED_GROUP_SIZE_MODAL';
};
type ColorsChangedActionType = {
}>;
type ColorsChangedActionType = ReadonlyDeep<{
type: typeof COLORS_CHANGED;
payload: {
conversationColor?: ConversationColorType;
@ -539,41 +556,41 @@ type ColorsChangedActionType = {
value: CustomColorType;
};
};
};
type ColorSelectedPayloadType = {
}>;
type ColorSelectedPayloadType = ReadonlyDeep<{
conversationId: string;
conversationColor?: ConversationColorType;
customColorData?: {
id: string;
value: CustomColorType;
};
};
export type ColorSelectedActionType = {
}>;
export type ColorSelectedActionType = ReadonlyDeep<{
type: typeof COLOR_SELECTED;
payload: ColorSelectedPayloadType;
};
type ComposeDeleteAvatarActionType = {
}>;
type ComposeDeleteAvatarActionType = ReadonlyDeep<{
type: typeof COMPOSE_REMOVE_AVATAR;
payload: AvatarDataType;
};
type ComposeReplaceAvatarsActionType = {
}>;
type ComposeReplaceAvatarsActionType = ReadonlyDeep<{
type: typeof COMPOSE_REPLACE_AVATAR;
payload: {
curr: AvatarDataType;
prev?: AvatarDataType;
};
};
type ComposeSaveAvatarActionType = {
}>;
type ComposeSaveAvatarActionType = ReadonlyDeep<{
type: typeof COMPOSE_ADD_AVATAR;
payload: AvatarDataType;
};
type CustomColorRemovedActionType = {
}>;
type CustomColorRemovedActionType = ReadonlyDeep<{
type: typeof CUSTOM_COLOR_REMOVED;
payload: {
colorId: string;
};
};
type DiscardMessagesActionType = {
}>;
type DiscardMessagesActionType = ReadonlyDeep<{
type: typeof DISCARD_MESSAGES;
payload: Readonly<
| {
@ -582,71 +599,72 @@ type DiscardMessagesActionType = {
}
| { conversationId: string; numberToKeepAtTop: number }
>;
};
type SetPreJoinConversationActionType = {
}>;
type SetPreJoinConversationActionType = ReadonlyDeep<{
type: 'SET_PRE_JOIN_CONVERSATION';
payload: {
data: PreJoinConversationType | undefined;
};
};
}>;
type ConversationAddedActionType = {
type ConversationAddedActionType = ReadonlyDeep<{
type: 'CONVERSATION_ADDED';
payload: {
id: string;
data: ConversationType;
};
};
export type ConversationChangedActionType = {
}>;
export type ConversationChangedActionType = ReadonlyDeep<{
type: 'CONVERSATION_CHANGED';
payload: {
id: string;
data: ConversationType;
};
};
export type ConversationRemovedActionType = {
}>;
export type ConversationRemovedActionType = ReadonlyDeep<{
type: 'CONVERSATION_REMOVED';
payload: {
id: string;
};
};
export type ConversationUnloadedActionType = {
}>;
export type ConversationUnloadedActionType = ReadonlyDeep<{
type: typeof CONVERSATION_UNLOADED;
payload: {
conversationId: string;
};
};
type CreateGroupPendingActionType = {
}>;
type CreateGroupPendingActionType = ReadonlyDeep<{
type: 'CREATE_GROUP_PENDING';
};
type CreateGroupFulfilledActionType = {
}>;
type CreateGroupFulfilledActionType = ReadonlyDeep<{
type: 'CREATE_GROUP_FULFILLED';
payload: {
invitedUuids: ReadonlyArray<UUIDStringType>;
};
};
type CreateGroupRejectedActionType = {
}>;
type CreateGroupRejectedActionType = ReadonlyDeep<{
type: 'CREATE_GROUP_REJECTED';
};
export type RemoveAllConversationsActionType = {
}>;
export type RemoveAllConversationsActionType = ReadonlyDeep<{
type: 'CONVERSATIONS_REMOVE_ALL';
payload: null;
};
export type MessageSelectedActionType = {
}>;
export type MessageSelectedActionType = ReadonlyDeep<{
type: 'MESSAGE_SELECTED';
payload: {
messageId: string;
conversationId: string;
};
};
type ConversationStoppedByMissingVerificationActionType = {
}>;
type ConversationStoppedByMissingVerificationActionType = ReadonlyDeep<{
type: typeof CONVERSATION_STOPPED_BY_MISSING_VERIFICATION;
payload: {
conversationId: string;
distributionId?: string;
untrustedUuids: ReadonlyArray<UUIDStringType>;
};
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep -- FIXME
export type MessageChangedActionType = {
type: typeof MESSAGE_CHANGED;
payload: {
@ -655,22 +673,23 @@ export type MessageChangedActionType = {
data: MessageAttributesType;
};
};
export type MessageDeletedActionType = {
export type MessageDeletedActionType = ReadonlyDeep<{
type: typeof MESSAGE_DELETED;
payload: {
id: string;
conversationId: string;
};
};
export type MessageExpandedActionType = {
}>;
export type MessageExpandedActionType = ReadonlyDeep<{
type: 'MESSAGE_EXPANDED';
payload: {
id: string;
displayLimit: number;
};
};
}>;
export type MessagesAddedActionType = {
// eslint-disable-next-line local-rules/type-alias-readonlydeep -- FIXME
export type MessagesAddedActionType = Readonly<{
type: 'MESSAGES_ADDED';
payload: {
conversationId: string;
@ -679,27 +698,28 @@ export type MessagesAddedActionType = {
isNewMessage: boolean;
messages: ReadonlyArray<MessageAttributesType>;
};
};
}>;
export type MessageExpiredActionType = {
export type MessageExpiredActionType = ReadonlyDeep<{
type: typeof MESSAGE_EXPIRED;
payload: {
id: string;
};
};
}>;
export type RepairNewestMessageActionType = {
export type RepairNewestMessageActionType = ReadonlyDeep<{
type: 'REPAIR_NEWEST_MESSAGE';
payload: {
conversationId: string;
};
};
export type RepairOldestMessageActionType = {
}>;
export type RepairOldestMessageActionType = ReadonlyDeep<{
type: 'REPAIR_OLDEST_MESSAGE';
payload: {
conversationId: string;
};
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type MessagesResetActionType = {
type: 'MESSAGES_RESET';
payload: {
@ -712,132 +732,135 @@ export type MessagesResetActionType = {
unboundedFetch: boolean;
};
};
export type SetMessageLoadingStateActionType = {
export type SetMessageLoadingStateActionType = ReadonlyDeep<{
type: 'SET_MESSAGE_LOADING_STATE';
payload: {
conversationId: string;
messageLoadingState: undefined | TimelineMessageLoadingState;
};
};
export type SetIsNearBottomActionType = {
}>;
export type SetIsNearBottomActionType = ReadonlyDeep<{
type: 'SET_NEAR_BOTTOM';
payload: {
conversationId: string;
isNearBottom: boolean;
};
};
export type ScrollToMessageActionType = {
}>;
export type ScrollToMessageActionType = ReadonlyDeep<{
type: 'SCROLL_TO_MESSAGE';
payload: {
conversationId: string;
messageId: string;
};
};
export type ClearSelectedMessageActionType = {
}>;
export type ClearSelectedMessageActionType = ReadonlyDeep<{
type: 'CLEAR_SELECTED_MESSAGE';
payload: null;
};
export type ClearUnreadMetricsActionType = {
}>;
export type ClearUnreadMetricsActionType = ReadonlyDeep<{
type: 'CLEAR_UNREAD_METRICS';
payload: {
conversationId: string;
};
};
export type SelectedConversationChangedActionType = {
}>;
export type SelectedConversationChangedActionType = ReadonlyDeep<{
type: typeof SELECTED_CONVERSATION_CHANGED;
payload: {
conversationId?: string;
messageId?: string;
switchToAssociatedView?: boolean;
};
};
type ReviewGroupMemberNameCollisionActionType = {
}>;
type ReviewGroupMemberNameCollisionActionType = ReadonlyDeep<{
type: 'REVIEW_GROUP_MEMBER_NAME_COLLISION';
payload: {
groupConversationId: string;
};
};
type ReviewMessageRequestNameCollisionActionType = {
}>;
type ReviewMessageRequestNameCollisionActionType = ReadonlyDeep<{
type: 'REVIEW_MESSAGE_REQUEST_NAME_COLLISION';
payload: {
safeConversationId: string;
};
};
type ShowInboxActionType = {
}>;
type ShowInboxActionType = ReadonlyDeep<{
type: 'SHOW_INBOX';
payload: null;
};
export type ShowArchivedConversationsActionType = {
}>;
export type ShowArchivedConversationsActionType = ReadonlyDeep<{
type: 'SHOW_ARCHIVED_CONVERSATIONS';
payload: null;
};
type SetComposeGroupAvatarActionType = {
}>;
type SetComposeGroupAvatarActionType = ReadonlyDeep<{
type: 'SET_COMPOSE_GROUP_AVATAR';
payload: { groupAvatar: undefined | Uint8Array };
};
type SetComposeGroupNameActionType = {
}>;
type SetComposeGroupNameActionType = ReadonlyDeep<{
type: 'SET_COMPOSE_GROUP_NAME';
payload: { groupName: string };
};
type SetComposeGroupExpireTimerActionType = {
}>;
type SetComposeGroupExpireTimerActionType = ReadonlyDeep<{
type: 'SET_COMPOSE_GROUP_EXPIRE_TIMER';
payload: { groupExpireTimer: DurationInSeconds };
};
type SetComposeSearchTermActionType = {
}>;
type SetComposeSearchTermActionType = ReadonlyDeep<{
type: 'SET_COMPOSE_SEARCH_TERM';
payload: { searchTerm: string };
};
type SetIsFetchingUUIDActionType = {
}>;
type SetIsFetchingUUIDActionType = ReadonlyDeep<{
type: 'SET_IS_FETCHING_UUID';
payload: {
identifier: UUIDFetchStateKeyType;
isFetching: boolean;
};
};
type SetRecentMediaItemsActionType = {
}>;
type SetRecentMediaItemsActionType = ReadonlyDeep<{
type: 'SET_RECENT_MEDIA_ITEMS';
payload: {
id: string;
recentMediaItems: ReadonlyArray<MediaItemType>;
};
};
type ToggleComposeEditingAvatarActionType = {
}>;
type ToggleComposeEditingAvatarActionType = ReadonlyDeep<{
type: typeof COMPOSE_TOGGLE_EDITING_AVATAR;
};
type StartComposingActionType = {
}>;
type StartComposingActionType = ReadonlyDeep<{
type: 'START_COMPOSING';
};
type ShowChooseGroupMembersActionType = {
}>;
type ShowChooseGroupMembersActionType = ReadonlyDeep<{
type: 'SHOW_CHOOSE_GROUP_MEMBERS';
};
type StartSettingGroupMetadataActionType = {
}>;
type StartSettingGroupMetadataActionType = ReadonlyDeep<{
type: 'START_SETTING_GROUP_METADATA';
};
export type ToggleConversationInChooseMembersActionType = {
}>;
export type ToggleConversationInChooseMembersActionType = ReadonlyDeep<{
type: 'TOGGLE_CONVERSATION_IN_CHOOSE_MEMBERS';
payload: {
conversationId: string;
maxRecommendedGroupSize: number;
maxGroupSize: number;
};
};
type PushPanelActionType = {
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep -- FIXME
type PushPanelActionType = Readonly<{
type: typeof PUSH_PANEL;
payload: PanelRenderType;
};
type PopPanelActionType = {
}>;
type PopPanelActionType = ReadonlyDeep<{
type: typeof POP_PANEL;
payload: null;
};
}>;
type ReplaceAvatarsActionType = {
type ReplaceAvatarsActionType = ReadonlyDeep<{
type: typeof REPLACE_AVATARS;
payload: {
conversationId: string;
avatars: ReadonlyArray<AvatarDataType>;
};
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep -- FIXME
export type ConversationActionType =
| CancelVerificationDataByConversationActionType
| ClearCancelledVerificationActionType
@ -1109,7 +1132,7 @@ function onMoveToInbox(conversationId: string): ShowToastActionType {
function acknowledgeGroupMemberNameCollisions(
conversationId: string,
groupNameCollisions: Readonly<GroupNameCollisionsWithIdsByTitle>
groupNameCollisions: ReadonlyDeep<GroupNameCollisionsWithIdsByTitle>
): NoopActionType {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
@ -1907,10 +1930,10 @@ function kickOffAttachmentDownload(
};
}
type AttachmentOptions = {
type AttachmentOptions = ReadonlyDeep<{
messageId: string;
attachment: AttachmentType;
};
}>;
function markAttachmentAsCorrupted(
options: AttachmentOptions
@ -2512,13 +2535,14 @@ function reviewMessageRequestNameCollision(
return { type: 'REVIEW_MESSAGE_REQUEST_NAME_COLLISION', payload };
}
export type MessageResetOptionsType = Readonly<{
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type MessageResetOptionsType = {
conversationId: string;
messages: ReadonlyArray<MessageAttributesType>;
metrics: MessageMetricsType;
scrollToMessageId?: string;
unboundedFetch?: boolean;
}>;
};
function messagesReset({
conversationId,
@ -2583,9 +2607,9 @@ function setIsFetchingUUID(
};
}
export type PushPanelForConversationActionType = (
panel: PanelRequestType
) => unknown;
export type PushPanelForConversationActionType = ReadonlyDeep<
(panel: PanelRequestType) => unknown
>;
function pushPanelForConversation(
panel: PanelRequestType
@ -2619,7 +2643,7 @@ function pushPanelForConversation(
};
}
export type PopPanelForConversationActionType = () => unknown;
export type PopPanelForConversationActionType = ReadonlyDeep<() => unknown>;
function popPanelForConversation(): ThunkAction<
void,
@ -3016,11 +3040,9 @@ function loadRecentMediaItems(
};
}
export type SaveAttachmentActionCreatorType = (
attachment: AttachmentType,
timestamp?: number,
index?: number
) => unknown;
export type SaveAttachmentActionCreatorType = ReadonlyDeep<
(attachment: AttachmentType, timestamp?: number, index?: number) => unknown
>;
function saveAttachment(
attachment: AttachmentType,
@ -3475,14 +3497,14 @@ function showInbox(): ShowInboxActionType {
};
}
type ShowConversationArgsType = {
type ShowConversationArgsType = ReadonlyDeep<{
conversationId?: string;
messageId?: string;
switchToAssociatedView?: boolean;
};
export type ShowConversationType = (
options: ShowConversationArgsType
) => unknown;
}>;
export type ShowConversationType = ReadonlyDeep<
(options: ShowConversationArgsType) => unknown
>;
function showConversation({
conversationId,
@ -3858,10 +3880,12 @@ function getVerificationDataForConversation({
}
// Return same data, and we do nothing. Return undefined, and we'll delete the list.
type DistributionVisitor = (
id: string,
data: DistributionVerificationData
) => DistributionVerificationData | undefined;
type DistributionVisitor = ReadonlyDeep<
(
id: string,
data: DistributionVerificationData
) => DistributionVerificationData | undefined
>;
function visitListsInVerificationData(
existing: VerificationDataByConversation,

View File

@ -1,6 +1,7 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReadonlyDeep } from 'type-fest';
import * as log from '../../logging/log';
import { showToast } from '../../util/showToast';
import * as Errors from '../../types/errors';
@ -10,10 +11,10 @@ import type { PromiseAction } from '../util';
// State
export type CrashReportsStateType = {
export type CrashReportsStateType = ReadonlyDeep<{
count: number;
isPending: boolean;
};
}>;
// Actions
@ -21,15 +22,16 @@ const SET_COUNT = 'crashReports/SET_COUNT';
const UPLOAD = 'crashReports/UPLOAD';
const ERASE = 'crashReports/ERASE';
type SetCrashReportCountActionType = {
type SetCrashReportCountActionType = ReadonlyDeep<{
type: typeof SET_COUNT;
payload: number;
};
}>;
type CrashReportsActionType =
type CrashReportsActionType = ReadonlyDeep<
| SetCrashReportCountActionType
| PromiseAction<typeof UPLOAD>
| PromiseAction<typeof ERASE>;
| PromiseAction<typeof ERASE>
>;
// Action Creators

View File

@ -3,6 +3,7 @@
import { take, uniq } from 'lodash';
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import type { EmojiPickDataType } from '../../components/emoji/EmojiPicker';
import dataInterface from '../../sql/Client';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
@ -12,18 +13,18 @@ const { updateEmojiUsage } = dataInterface;
// State
export type EmojisStateType = {
readonly recents: Array<string>;
};
export type EmojisStateType = ReadonlyDeep<{
recents: Array<string>;
}>;
// Actions
type UseEmojiAction = {
type UseEmojiAction = ReadonlyDeep<{
type: 'emojis/USE_EMOJI';
payload: string;
};
}>;
type EmojisActionType = UseEmojiAction;
type EmojisActionType = ReadonlyDeep<UseEmojiAction>;
// Action Creators
@ -64,8 +65,8 @@ function getEmptyState(): EmojisStateType {
}
export function reducer(
state: Readonly<EmojisStateType> = getEmptyState(),
action: Readonly<EmojisActionType>
state: EmojisStateType = getEmptyState(),
action: EmojisActionType
): EmojisStateType {
if (action.type === 'emojis/USE_EMOJI') {
const { payload } = action;

View File

@ -1,22 +1,25 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReadonlyDeep } from 'type-fest';
// State
export type ExpirationStateType = {
export type ExpirationStateType = ReadonlyDeep<{
hasExpired: boolean;
};
}>;
// Actions
const HYDRATE_EXPIRATION_STATUS = 'expiration/HYDRATE_EXPIRATION_STATUS';
type HyrdateExpirationStatusActionType = {
type HyrdateExpirationStatusActionType = ReadonlyDeep<{
type: 'expiration/HYDRATE_EXPIRATION_STATUS';
payload: boolean;
};
}>;
export type ExpirationActionType = HyrdateExpirationStatusActionType;
export type ExpirationActionType =
ReadonlyDeep<HyrdateExpirationStatusActionType>;
// Action Creators

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import type { ExplodePromiseResultType } from '../../util/explodePromise';
import type { GroupV2PendingMemberType } from '../../model-types.d';
import type { PropsForMessage } from '../selectors/message';
@ -21,24 +22,23 @@ import { getGroupMigrationMembers } from '../../groups';
// State
export type ForwardMessagePropsType = Omit<
PropsForMessage,
'renderingContext' | 'menu' | 'contextMenu'
export type ForwardMessagePropsType = ReadonlyDeep<
Omit<PropsForMessage, 'renderingContext' | 'menu' | 'contextMenu'>
>;
export type SafetyNumberChangedBlockingDataType = Readonly<{
export type SafetyNumberChangedBlockingDataType = ReadonlyDeep<{
promiseUuid: UUIDStringType;
source?: SafetyNumberChangeSource;
}>;
type MigrateToGV2PropsType = {
type MigrateToGV2PropsType = ReadonlyDeep<{
areWeInvited: boolean;
conversationId: string;
droppedMemberIds: ReadonlyArray<string>;
droppedMemberIds: Array<string>;
hasMigrated: boolean;
invitedMemberIds: ReadonlyArray<string>;
};
invitedMemberIds: Array<string>;
}>;
export type GlobalModalsStateType = Readonly<{
export type GlobalModalsStateType = ReadonlyDeep<{
addUserToAnotherGroupModalContactId?: string;
contactModalState?: ContactModalStateType;
errorModalProps?: {
@ -90,12 +90,12 @@ const SHOW_ERROR_MODAL = 'globalModals/SHOW_ERROR_MODAL';
const CLOSE_SHORTCUT_GUIDE_MODAL = 'globalModals/CLOSE_SHORTCUT_GUIDE_MODAL';
const SHOW_SHORTCUT_GUIDE_MODAL = 'globalModals/SHOW_SHORTCUT_GUIDE_MODAL';
export type ContactModalStateType = {
export type ContactModalStateType = ReadonlyDeep<{
contactId: string;
conversationId?: string;
};
}>;
export type UserNotFoundModalStateType =
export type UserNotFoundModalStateType = ReadonlyDeep<
| {
type: 'phoneNumber';
phoneNumber: string;
@ -103,119 +103,120 @@ export type UserNotFoundModalStateType =
| {
type: 'username';
username: string;
};
}
>;
type HideContactModalActionType = {
type HideContactModalActionType = ReadonlyDeep<{
type: typeof HIDE_CONTACT_MODAL;
};
}>;
type ShowContactModalActionType = {
type ShowContactModalActionType = ReadonlyDeep<{
type: typeof SHOW_CONTACT_MODAL;
payload: ContactModalStateType;
};
}>;
type HideWhatsNewModalActionType = {
type HideWhatsNewModalActionType = ReadonlyDeep<{
type: typeof HIDE_WHATS_NEW_MODAL;
};
}>;
type ShowWhatsNewModalActionType = {
type ShowWhatsNewModalActionType = ReadonlyDeep<{
type: typeof SHOW_WHATS_NEW_MODAL;
};
}>;
type HideUserNotFoundModalActionType = {
type HideUserNotFoundModalActionType = ReadonlyDeep<{
type: typeof HIDE_UUID_NOT_FOUND_MODAL;
};
}>;
export type ShowUserNotFoundModalActionType = {
export type ShowUserNotFoundModalActionType = ReadonlyDeep<{
type: typeof SHOW_UUID_NOT_FOUND_MODAL;
payload: UserNotFoundModalStateType;
};
}>;
type ToggleForwardMessageModalActionType = {
type ToggleForwardMessageModalActionType = ReadonlyDeep<{
type: typeof TOGGLE_FORWARD_MESSAGE_MODAL;
payload: ForwardMessagePropsType | undefined;
};
}>;
type ToggleProfileEditorActionType = {
type ToggleProfileEditorActionType = ReadonlyDeep<{
type: typeof TOGGLE_PROFILE_EDITOR;
};
}>;
export type ToggleProfileEditorErrorActionType = {
export type ToggleProfileEditorErrorActionType = ReadonlyDeep<{
type: typeof TOGGLE_PROFILE_EDITOR_ERROR;
};
}>;
type ToggleSafetyNumberModalActionType = {
type ToggleSafetyNumberModalActionType = ReadonlyDeep<{
type: typeof TOGGLE_SAFETY_NUMBER_MODAL;
payload: string | undefined;
};
}>;
type ToggleAddUserToAnotherGroupModalActionType = {
type ToggleAddUserToAnotherGroupModalActionType = ReadonlyDeep<{
type: typeof TOGGLE_ADD_USER_TO_ANOTHER_GROUP_MODAL;
payload: string | undefined;
};
}>;
type ToggleSignalConnectionsModalActionType = {
type ToggleSignalConnectionsModalActionType = ReadonlyDeep<{
type: typeof TOGGLE_SIGNAL_CONNECTIONS_MODAL;
};
}>;
type ShowStoriesSettingsActionType = {
type ShowStoriesSettingsActionType = ReadonlyDeep<{
type: typeof SHOW_STORIES_SETTINGS;
};
}>;
type HideStoriesSettingsActionType = {
type HideStoriesSettingsActionType = ReadonlyDeep<{
type: typeof HIDE_STORIES_SETTINGS;
};
}>;
type StartMigrationToGV2ActionType = {
type StartMigrationToGV2ActionType = ReadonlyDeep<{
type: typeof SHOW_GV2_MIGRATION_DIALOG;
payload: MigrateToGV2PropsType;
};
}>;
type CloseGV2MigrationDialogActionType = {
type CloseGV2MigrationDialogActionType = ReadonlyDeep<{
type: typeof CLOSE_GV2_MIGRATION_DIALOG;
};
}>;
export type ShowSendAnywayDialogActionType = {
export type ShowSendAnywayDialogActionType = ReadonlyDeep<{
type: typeof SHOW_SEND_ANYWAY_DIALOG;
payload: SafetyNumberChangedBlockingDataType & {
untrustedByConversation: RecipientsByConversation;
};
};
}>;
type HideSendAnywayDialogActiontype = {
type HideSendAnywayDialogActiontype = ReadonlyDeep<{
type: typeof HIDE_SEND_ANYWAY_DIALOG;
};
}>;
export type ShowStickerPackPreviewActionType = {
export type ShowStickerPackPreviewActionType = ReadonlyDeep<{
type: typeof SHOW_STICKER_PACK_PREVIEW;
payload: string;
};
}>;
type CloseStickerPackPreviewActionType = {
type CloseStickerPackPreviewActionType = ReadonlyDeep<{
type: typeof CLOSE_STICKER_PACK_PREVIEW;
};
}>;
type CloseErrorModalActionType = {
type CloseErrorModalActionType = ReadonlyDeep<{
type: typeof CLOSE_ERROR_MODAL;
};
}>;
type ShowErrorModalActionType = {
type ShowErrorModalActionType = ReadonlyDeep<{
type: typeof SHOW_ERROR_MODAL;
payload: {
description?: string;
title?: string;
};
};
}>;
type CloseShortcutGuideModalActionType = {
type CloseShortcutGuideModalActionType = ReadonlyDeep<{
type: typeof CLOSE_SHORTCUT_GUIDE_MODAL;
};
}>;
type ShowShortcutGuideModalActionType = {
type ShowShortcutGuideModalActionType = ReadonlyDeep<{
type: typeof SHOW_SHORTCUT_GUIDE_MODAL;
};
}>;
export type GlobalModalsActionType =
export type GlobalModalsActionType = ReadonlyDeep<
| StartMigrationToGV2ActionType
| CloseGV2MigrationDialogActionType
| HideContactModalActionType
@ -239,7 +240,8 @@ export type GlobalModalsActionType =
| ToggleProfileEditorErrorActionType
| ToggleSafetyNumberModalActionType
| ToggleAddUserToAnotherGroupModalActionType
| ToggleSignalConnectionsModalActionType;
| ToggleSignalConnectionsModalActionType
>;
// Action Creators

View File

@ -4,6 +4,7 @@
import { omit } from 'lodash';
import { v4 as getGuid } from 'uuid';
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import type { StateType as RootStateType } from '../reducer';
import * as storageShim from '../../shims/storage';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
@ -23,60 +24,61 @@ import type { ConfigMapType as RemoteConfigType } from '../../RemoteConfig';
// State
export type ItemsStateType = {
readonly universalExpireTimer?: number;
export type ItemsStateType = ReadonlyDeep<{
universalExpireTimer?: number;
readonly [key: string]: unknown;
[key: string]: unknown;
readonly remoteConfig?: RemoteConfigType;
remoteConfig?: RemoteConfigType;
// This property should always be set and this is ensured in background.ts
readonly defaultConversationColor?: DefaultConversationColorType;
defaultConversationColor?: DefaultConversationColorType;
readonly customColors?: CustomColorsItemType;
customColors?: CustomColorsItemType;
readonly preferredLeftPaneWidth?: number;
preferredLeftPaneWidth?: number;
readonly preferredReactionEmoji?: ReadonlyArray<string>;
preferredReactionEmoji?: Array<string>;
readonly areWeASubscriber?: boolean;
};
areWeASubscriber?: boolean;
}>;
// Actions
type ItemPutAction = {
type ItemPutAction = ReadonlyDeep<{
type: 'items/PUT';
payload: null;
};
}>;
type ItemPutExternalAction = {
type ItemPutExternalAction = ReadonlyDeep<{
type: 'items/PUT_EXTERNAL';
payload: {
key: string;
value: unknown;
};
};
}>;
type ItemRemoveAction = {
type ItemRemoveAction = ReadonlyDeep<{
type: 'items/REMOVE';
payload: null;
};
}>;
type ItemRemoveExternalAction = {
type ItemRemoveExternalAction = ReadonlyDeep<{
type: 'items/REMOVE_EXTERNAL';
payload: string;
};
}>;
type ItemsResetAction = {
type ItemsResetAction = ReadonlyDeep<{
type: 'items/RESET';
};
}>;
export type ItemsActionType =
export type ItemsActionType = ReadonlyDeep<
| ItemPutAction
| ItemPutExternalAction
| ItemRemoveAction
| ItemRemoveExternalAction
| ItemsResetAction;
| ItemsResetAction
>;
// Action Creators

View File

@ -3,6 +3,7 @@
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import type { AttachmentType } from '../../types/Attachment';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import type { MediaItemType } from '../../types/MediaItem';
@ -34,6 +35,7 @@ import {
import { showStickerPackPreview } from './globalModals';
import { useBoundActions } from '../../hooks/useBoundActions';
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type LightboxStateType =
| {
isShowingLightbox: false;
@ -41,26 +43,28 @@ export type LightboxStateType =
| {
isShowingLightbox: true;
isViewOnce: boolean;
media: ReadonlyArray<MediaItemType>;
media: ReadonlyArray<ReadonlyDeep<MediaItemType>>;
selectedAttachmentPath: string | undefined;
};
const CLOSE_LIGHTBOX = 'lightbox/CLOSE';
const SHOW_LIGHTBOX = 'lightbox/SHOW';
type CloseLightboxActionType = {
type CloseLightboxActionType = ReadonlyDeep<{
type: typeof CLOSE_LIGHTBOX;
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type ShowLightboxActionType = {
type: typeof SHOW_LIGHTBOX;
payload: {
isViewOnce: boolean;
media: ReadonlyArray<MediaItemType>;
media: ReadonlyArray<ReadonlyDeep<MediaItemType>>;
selectedAttachmentPath: string | undefined;
};
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type LightboxActionType =
| CloseLightboxActionType
| MessageChangedActionType
@ -100,7 +104,7 @@ function closeLightbox(): ThunkAction<
function showLightboxWithMedia(
selectedAttachmentPath: string | undefined,
media: ReadonlyArray<MediaItemType>
media: ReadonlyArray<ReadonlyDeep<MediaItemType>>
): ShowLightboxActionType {
return {
type: SHOW_LIGHTBOX,

View File

@ -3,6 +3,7 @@
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import type { LinkPreviewType } from '../../types/message/LinkPreviews';
import type { MaybeGrabLinkPreviewOptionsType } from '../../types/LinkPreview';
@ -16,35 +17,35 @@ import { useBoundActions } from '../../hooks/useBoundActions';
// State
export type LinkPreviewsStateType = {
readonly linkPreview?: LinkPreviewType;
readonly source?: LinkPreviewSourceType;
};
export type LinkPreviewsStateType = ReadonlyDeep<{
linkPreview?: LinkPreviewType;
source?: LinkPreviewSourceType;
}>;
// Actions
export const ADD_PREVIEW = 'linkPreviews/ADD_PREVIEW';
export const REMOVE_PREVIEW = 'linkPreviews/REMOVE_PREVIEW';
export type AddLinkPreviewActionType = {
export type AddLinkPreviewActionType = ReadonlyDeep<{
type: 'linkPreviews/ADD_PREVIEW';
payload: {
conversationId?: string;
linkPreview: LinkPreviewType;
source: LinkPreviewSourceType;
};
};
}>;
export type RemoveLinkPreviewActionType = {
export type RemoveLinkPreviewActionType = ReadonlyDeep<{
type: 'linkPreviews/REMOVE_PREVIEW';
payload: {
conversationId?: string;
};
};
}>;
type LinkPreviewsActionType =
| AddLinkPreviewActionType
| RemoveLinkPreviewActionType;
type LinkPreviewsActionType = ReadonlyDeep<
AddLinkPreviewActionType | RemoveLinkPreviewActionType
>;
// Action Creators

View File

@ -27,6 +27,7 @@ import { isDownloading, hasFailed } from '../../types/Attachment';
import { isNotNil } from '../../util/isNotNil';
import { useBoundActions } from '../../hooks/useBoundActions';
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type MediaType = {
path: string;
objectURL: string;
@ -44,6 +45,7 @@ type MediaType = {
};
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type MediaGalleryStateType = {
documents: Array<MediaItemType>;
media: Array<MediaType>;
@ -51,6 +53,7 @@ export type MediaGalleryStateType = {
const LOAD_MEDIA_ITEMS = 'mediaGallery/LOAD_MEDIA_ITEMS';
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type LoadMediaItemslActionType = {
type: typeof LOAD_MEDIA_ITEMS;
payload: {
@ -59,6 +62,7 @@ type LoadMediaItemslActionType = {
};
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type MediaGalleryActionType =
| ConversationUnloadedActionType
| LoadMediaItemslActionType

View File

@ -1,18 +1,19 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReadonlyDeep } from 'type-fest';
import { SocketStatus } from '../../types/SocketStatus';
import { trigger } from '../../shims/events';
import { assignWithNoUnnecessaryAllocation } from '../../util/assignWithNoUnnecessaryAllocation';
// State
export type NetworkStateType = {
export type NetworkStateType = ReadonlyDeep<{
isOnline: boolean;
socketStatus: SocketStatus;
withinConnectingGracePeriod: boolean;
challengeStatus: 'required' | 'pending' | 'idle';
};
}>;
// Actions
@ -21,36 +22,37 @@ const CLOSE_CONNECTING_GRACE_PERIOD = 'network/CLOSE_CONNECTING_GRACE_PERIOD';
const RELINK_DEVICE = 'network/RELINK_DEVICE';
const SET_CHALLENGE_STATUS = 'network/SET_CHALLENGE_STATUS';
export type CheckNetworkStatusPayloadType = {
export type CheckNetworkStatusPayloadType = ReadonlyDeep<{
isOnline: boolean;
socketStatus: SocketStatus;
};
}>;
type CheckNetworkStatusAction = {
type CheckNetworkStatusAction = ReadonlyDeep<{
type: 'network/CHECK_NETWORK_STATUS';
payload: CheckNetworkStatusPayloadType;
};
}>;
type CloseConnectingGracePeriodActionType = {
type CloseConnectingGracePeriodActionType = ReadonlyDeep<{
type: 'network/CLOSE_CONNECTING_GRACE_PERIOD';
};
}>;
type RelinkDeviceActionType = {
type RelinkDeviceActionType = ReadonlyDeep<{
type: 'network/RELINK_DEVICE';
};
}>;
type SetChallengeStatusActionType = {
type SetChallengeStatusActionType = ReadonlyDeep<{
type: 'network/SET_CHALLENGE_STATUS';
payload: {
challengeStatus: NetworkStateType['challengeStatus'];
};
};
}>;
export type NetworkActionType =
export type NetworkActionType = ReadonlyDeep<
| CheckNetworkStatusAction
| CloseConnectingGracePeriodActionType
| RelinkDeviceActionType
| SetChallengeStatusActionType;
| SetChallengeStatusActionType
>;
// Action Creators

View File

@ -1,10 +1,12 @@
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
export type NoopActionType = {
import type { ReadonlyDeep } from 'type-fest';
export type NoopActionType = ReadonlyDeep<{
type: 'NOOP';
payload: null;
};
}>;
export function noopAction(): NoopActionType {
return {

View File

@ -3,6 +3,7 @@
import type { ThunkAction } from 'redux-thunk';
import { omit } from 'lodash';
import type { ReadonlyDeep } from 'type-fest';
import * as log from '../../logging/log';
import * as Errors from '../../types/errors';
import { replaceIndex } from '../../util/replaceIndex';
@ -16,7 +17,7 @@ import { convertShortName } from '../../components/emoji/lib';
// State
export type PreferredReactionsStateType = {
export type PreferredReactionsStateType = ReadonlyDeep<{
customizePreferredReactionsModal?: {
draftPreferredReactions: Array<string>;
originalPreferredReactions: Array<string>;
@ -25,7 +26,7 @@ export type PreferredReactionsStateType = {
| { isSaving: true; hadSaveError: false }
| { isSaving: false; hadSaveError: boolean }
);
};
}>;
// Actions
@ -46,45 +47,47 @@ const SAVE_PREFERRED_REACTIONS_REJECTED =
const SELECT_DRAFT_EMOJI_TO_BE_REPLACED =
'preferredReactions/SELECT_DRAFT_EMOJI_TO_BE_REPLACED';
type CancelCustomizePreferredReactionsModalActionType = {
type CancelCustomizePreferredReactionsModalActionType = ReadonlyDeep<{
type: typeof CANCEL_CUSTOMIZE_PREFERRED_REACTIONS_MODAL;
};
}>;
type DeselectDraftEmojiActionType = { type: typeof DESELECT_DRAFT_EMOJI };
type DeselectDraftEmojiActionType = ReadonlyDeep<{
type: typeof DESELECT_DRAFT_EMOJI;
}>;
type OpenCustomizePreferredReactionsModalActionType = {
type OpenCustomizePreferredReactionsModalActionType = ReadonlyDeep<{
type: typeof OPEN_CUSTOMIZE_PREFERRED_REACTIONS_MODAL;
payload: {
originalPreferredReactions: Array<string>;
};
};
}>;
type ReplaceSelectedDraftEmojiActionType = {
type ReplaceSelectedDraftEmojiActionType = ReadonlyDeep<{
type: typeof REPLACE_SELECTED_DRAFT_EMOJI;
payload: string;
};
}>;
type ResetDraftEmojiActionType = {
type ResetDraftEmojiActionType = ReadonlyDeep<{
type: typeof RESET_DRAFT_EMOJI;
payload: { skinTone: number };
};
}>;
type SavePreferredReactionsFulfilledActionType = {
type SavePreferredReactionsFulfilledActionType = ReadonlyDeep<{
type: typeof SAVE_PREFERRED_REACTIONS_FULFILLED;
};
}>;
type SavePreferredReactionsPendingActionType = {
type SavePreferredReactionsPendingActionType = ReadonlyDeep<{
type: typeof SAVE_PREFERRED_REACTIONS_PENDING;
};
}>;
type SavePreferredReactionsRejectedActionType = {
type SavePreferredReactionsRejectedActionType = ReadonlyDeep<{
type: typeof SAVE_PREFERRED_REACTIONS_REJECTED;
};
}>;
type SelectDraftEmojiToBeReplacedActionType = {
type SelectDraftEmojiToBeReplacedActionType = ReadonlyDeep<{
type: typeof SELECT_DRAFT_EMOJI_TO_BE_REPLACED;
payload: number;
};
}>;
// Action creators

View File

@ -1,6 +1,7 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReadonlyDeep } from 'type-fest';
import { generateSecurityNumberBlock } from '../../util/safetyNumber';
import type { ConversationType } from './conversations';
import {
@ -10,17 +11,17 @@ import {
import * as log from '../../logging/log';
import * as Errors from '../../types/errors';
export type SafetyNumberContactType = {
export type SafetyNumberContactType = ReadonlyDeep<{
safetyNumber: string;
safetyNumberChanged?: boolean;
verificationDisabled: boolean;
};
}>;
export type SafetyNumberStateType = {
export type SafetyNumberStateType = ReadonlyDeep<{
contacts: {
[key: string]: SafetyNumberContactType;
};
};
}>;
const GENERATE = 'safetyNumber/GENERATE';
const GENERATE_FULFILLED = 'safetyNumber/GENERATE_FULFILLED';
@ -28,51 +29,52 @@ const TOGGLE_VERIFIED = 'safetyNumber/TOGGLE_VERIFIED';
const TOGGLE_VERIFIED_FULFILLED = 'safetyNumber/TOGGLE_VERIFIED_FULFILLED';
const TOGGLE_VERIFIED_PENDING = 'safetyNumber/TOGGLE_VERIFIED_PENDING';
type GenerateAsyncActionType = {
type GenerateAsyncActionType = ReadonlyDeep<{
contact: ConversationType;
safetyNumber: string;
};
}>;
type GenerateActionType = {
type GenerateActionType = ReadonlyDeep<{
type: 'safetyNumber/GENERATE';
payload: Promise<GenerateAsyncActionType>;
};
}>;
type GenerateFulfilledActionType = {
type GenerateFulfilledActionType = ReadonlyDeep<{
type: 'safetyNumber/GENERATE_FULFILLED';
payload: GenerateAsyncActionType;
};
}>;
type ToggleVerifiedAsyncActionType = {
type ToggleVerifiedAsyncActionType = ReadonlyDeep<{
contact: ConversationType;
safetyNumber?: string;
safetyNumberChanged?: boolean;
};
}>;
type ToggleVerifiedActionType = {
type ToggleVerifiedActionType = ReadonlyDeep<{
type: 'safetyNumber/TOGGLE_VERIFIED';
payload: {
data: { contact: ConversationType };
promise: Promise<ToggleVerifiedAsyncActionType>;
};
};
}>;
type ToggleVerifiedPendingActionType = {
type ToggleVerifiedPendingActionType = ReadonlyDeep<{
type: 'safetyNumber/TOGGLE_VERIFIED_PENDING';
payload: ToggleVerifiedAsyncActionType;
};
}>;
type ToggleVerifiedFulfilledActionType = {
type ToggleVerifiedFulfilledActionType = ReadonlyDeep<{
type: 'safetyNumber/TOGGLE_VERIFIED_FULFILLED';
payload: ToggleVerifiedAsyncActionType;
};
}>;
export type SafetyNumberActionType =
export type SafetyNumberActionType = ReadonlyDeep<
| GenerateActionType
| GenerateFulfilledActionType
| ToggleVerifiedActionType
| ToggleVerifiedPendingActionType
| ToggleVerifiedFulfilledActionType;
| ToggleVerifiedFulfilledActionType
>;
function generate(contact: ConversationType): GenerateActionType {
return {

View File

@ -4,6 +4,7 @@
import type { ThunkAction, ThunkDispatch } from 'redux-thunk';
import { debounce, omit, reject } from 'lodash';
import type { ReadonlyDeep } from 'type-fest';
import type { StateType as RootStateType } from '../reducer';
import { cleanSearchTerm } from '../../util/cleanSearchTerm';
import { filterAndSortConversationsByRecent } from '../../util/filterAndSortConversations';
@ -43,15 +44,17 @@ const {
// State
export type MessageSearchResultType = MessageType & {
snippet?: string;
};
export type MessageSearchResultType = ReadonlyDeep<
MessageType & {
snippet?: string;
}
>;
export type MessageSearchResultLookupType = {
export type MessageSearchResultLookupType = ReadonlyDeep<{
[id: string]: MessageSearchResultType;
};
}>;
export type SearchStateType = {
export type SearchStateType = ReadonlyDeep<{
startSearchCounter: number;
searchConversationId?: string;
contactIds: Array<string>;
@ -64,49 +67,49 @@ export type SearchStateType = {
// Loading state
discussionsLoading: boolean;
messagesLoading: boolean;
};
}>;
// Actions
type SearchMessagesResultsFulfilledActionType = {
type SearchMessagesResultsFulfilledActionType = ReadonlyDeep<{
type: 'SEARCH_MESSAGES_RESULTS_FULFILLED';
payload: {
messages: Array<MessageSearchResultType>;
query: string;
};
};
type SearchDiscussionsResultsFulfilledActionType = {
}>;
type SearchDiscussionsResultsFulfilledActionType = ReadonlyDeep<{
type: 'SEARCH_DISCUSSIONS_RESULTS_FULFILLED';
payload: {
conversationIds: Array<string>;
contactIds: Array<string>;
query: string;
};
};
type UpdateSearchTermActionType = {
}>;
type UpdateSearchTermActionType = ReadonlyDeep<{
type: 'SEARCH_UPDATE';
payload: {
query: string;
};
};
type StartSearchActionType = {
}>;
type StartSearchActionType = ReadonlyDeep<{
type: 'SEARCH_START';
payload: null;
};
type ClearSearchActionType = {
}>;
type ClearSearchActionType = ReadonlyDeep<{
type: 'SEARCH_CLEAR';
payload: null;
};
type ClearConversationSearchActionType = {
}>;
type ClearConversationSearchActionType = ReadonlyDeep<{
type: 'CLEAR_CONVERSATION_SEARCH';
payload: null;
};
type SearchInConversationActionType = {
}>;
type SearchInConversationActionType = ReadonlyDeep<{
type: 'SEARCH_IN_CONVERSATION';
payload: { searchConversationId: string };
};
}>;
export type SearchActionType =
export type SearchActionType = ReadonlyDeep<
| SearchMessagesResultsFulfilledActionType
| SearchDiscussionsResultsFulfilledActionType
| UpdateSearchTermActionType
@ -118,7 +121,8 @@ export type SearchActionType =
| RemoveAllConversationsActionType
| SelectedConversationChangedActionType
| ShowArchivedConversationsActionType
| ConversationUnloadedActionType;
| ConversationUnloadedActionType
>;
// Action Creators

View File

@ -3,6 +3,7 @@
import type { Dictionary } from 'lodash';
import { omit, reject } from 'lodash';
import type { ReadonlyDeep } from 'type-fest';
import type {
StickerPackStatusType,
StickerType as StickerDBType,
@ -24,23 +25,23 @@ const { getRecentStickers, updateStickerLastUsed } = dataInterface;
// State
export type StickersStateType = {
readonly installedPack: string | null;
readonly packs: Dictionary<StickerPackDBType>;
readonly recentStickers: Array<RecentStickerType>;
readonly blessedPacks: Dictionary<boolean>;
};
export type StickersStateType = ReadonlyDeep<{
installedPack: string | null;
packs: Dictionary<StickerPackDBType>;
recentStickers: Array<RecentStickerType>;
blessedPacks: Dictionary<boolean>;
}>;
// These are for the React components
export type StickerType = {
readonly id: number;
readonly packId: string;
readonly emoji?: string;
readonly url: string;
};
export type StickerType = ReadonlyDeep<{
id: number;
packId: string;
emoji?: string;
url: string;
}>;
export type StickerPackType = Readonly<{
export type StickerPackType = ReadonlyDeep<{
id: string;
key: string;
title: string;
@ -56,76 +57,76 @@ export type StickerPackType = Readonly<{
// Actions
type StickerPackAddedAction = {
type StickerPackAddedAction = ReadonlyDeep<{
type: 'stickers/STICKER_PACK_ADDED';
payload: StickerPackDBType;
};
}>;
type StickerAddedAction = {
type StickerAddedAction = ReadonlyDeep<{
type: 'stickers/STICKER_ADDED';
payload: StickerDBType;
};
}>;
type InstallStickerPackPayloadType = {
type InstallStickerPackPayloadType = ReadonlyDeep<{
packId: string;
fromSync: boolean;
status: 'installed';
installedAt: number;
recentStickers: Array<RecentStickerType>;
};
type InstallStickerPackAction = {
}>;
type InstallStickerPackAction = ReadonlyDeep<{
type: 'stickers/INSTALL_STICKER_PACK';
payload: Promise<InstallStickerPackPayloadType>;
};
type InstallStickerPackFulfilledAction = {
}>;
type InstallStickerPackFulfilledAction = ReadonlyDeep<{
type: 'stickers/INSTALL_STICKER_PACK_FULFILLED';
payload: InstallStickerPackPayloadType;
};
type ClearInstalledStickerPackAction = {
}>;
type ClearInstalledStickerPackAction = ReadonlyDeep<{
type: 'stickers/CLEAR_INSTALLED_STICKER_PACK';
};
}>;
type UninstallStickerPackPayloadType = {
type UninstallStickerPackPayloadType = ReadonlyDeep<{
packId: string;
fromSync: boolean;
status: 'downloaded';
installedAt?: undefined;
recentStickers: Array<RecentStickerType>;
};
type UninstallStickerPackAction = {
}>;
type UninstallStickerPackAction = ReadonlyDeep<{
type: 'stickers/UNINSTALL_STICKER_PACK';
payload: Promise<UninstallStickerPackPayloadType>;
};
type UninstallStickerPackFulfilledAction = {
}>;
type UninstallStickerPackFulfilledAction = ReadonlyDeep<{
type: 'stickers/UNINSTALL_STICKER_PACK_FULFILLED';
payload: UninstallStickerPackPayloadType;
};
}>;
type StickerPackUpdatedAction = {
type StickerPackUpdatedAction = ReadonlyDeep<{
type: 'stickers/STICKER_PACK_UPDATED';
payload: { packId: string; patch: Partial<StickerPackDBType> };
};
}>;
type StickerPackRemovedAction = {
type StickerPackRemovedAction = ReadonlyDeep<{
type: 'stickers/REMOVE_STICKER_PACK';
payload: string;
};
}>;
type UseStickerPayloadType = {
type UseStickerPayloadType = ReadonlyDeep<{
packId: string;
stickerId: number;
time: number;
};
type UseStickerAction = {
}>;
type UseStickerAction = ReadonlyDeep<{
type: 'stickers/USE_STICKER';
payload: Promise<UseStickerPayloadType>;
};
type UseStickerFulfilledAction = {
}>;
type UseStickerFulfilledAction = ReadonlyDeep<{
type: 'stickers/USE_STICKER_FULFILLED';
payload: UseStickerPayloadType;
};
}>;
export type StickersActionType =
export type StickersActionType = ReadonlyDeep<
| ClearInstalledStickerPackAction
| StickerAddedAction
| StickerPackAddedAction
@ -134,7 +135,8 @@ export type StickersActionType =
| StickerPackUpdatedAction
| StickerPackRemovedAction
| UseStickerFulfilledAction
| NoopActionType;
| NoopActionType
>;
// Action Creators

View File

@ -4,6 +4,7 @@
import type { ThunkAction, ThunkDispatch } from 'redux-thunk';
import { isEqual, pick } from 'lodash';
import type { ReadonlyDeep } from 'type-fest';
import * as Errors from '../../types/errors';
import type { AttachmentType } from '../../types/Attachment';
import type { DraftBodyRangesType } from '../../types/Util';
@ -60,44 +61,46 @@ import { SHOW_TOAST } from './toast';
import { ToastType } from '../../types/Toast';
import type { ShowToastActionType } from './toast';
export type StoryDataType = {
attachment?: AttachmentType;
hasReplies?: boolean;
hasRepliesFromSelf?: boolean;
messageId: string;
startedDownload?: boolean;
} & Pick<
MessageAttributesType,
| 'canReplyToStory'
| 'conversationId'
| 'deletedForEveryone'
| 'reactions'
| 'readAt'
| 'readStatus'
| 'sendStateByConversationId'
| 'source'
| 'sourceUuid'
| 'storyDistributionListId'
| 'timestamp'
| 'type'
| 'storyRecipientsVersion'
> & {
// don't want the fields to be optional as in MessageAttributesType
expireTimer: DurationInSeconds | undefined;
expirationStartTimestamp: number | undefined;
sourceDevice: number;
};
export type StoryDataType = ReadonlyDeep<
{
attachment?: AttachmentType;
hasReplies?: boolean;
hasRepliesFromSelf?: boolean;
messageId: string;
startedDownload?: boolean;
} & Pick<
MessageAttributesType,
| 'canReplyToStory'
| 'conversationId'
| 'deletedForEveryone'
| 'reactions'
| 'readAt'
| 'readStatus'
| 'sendStateByConversationId'
| 'source'
| 'sourceUuid'
| 'storyDistributionListId'
| 'timestamp'
| 'type'
| 'storyRecipientsVersion'
> & {
// don't want the fields to be optional as in MessageAttributesType
expireTimer: DurationInSeconds | undefined;
expirationStartTimestamp: number | undefined;
sourceDevice: number;
}
>;
export type SelectedStoryDataType = {
export type SelectedStoryDataType = ReadonlyDeep<{
currentIndex: number;
messageId: string;
numStories: number;
storyViewMode: StoryViewModeType;
unviewedStoryConversationIdsSorted: Array<string>;
viewTarget?: StoryViewTargetType;
};
}>;
export type AddStoryData =
export type AddStoryData = ReadonlyDeep<
| {
type: 'Media';
file: File;
@ -107,8 +110,10 @@ export type AddStoryData =
type: 'Text';
sending?: boolean;
}
| undefined;
| undefined
>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type RecipientsByConversation = Record<
string, // conversationId
{
@ -125,6 +130,7 @@ export type RecipientsByConversation = Record<
// State
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type StoriesStateType = Readonly<{
addStoryData: AddStoryData;
hasAllStoriesUnmuted: boolean;
@ -157,20 +163,21 @@ const SET_ADD_STORY_DATA = 'stories/SET_ADD_STORY_DATA';
const SET_STORY_SENDING = 'stories/SET_STORY_SENDING';
const SET_HAS_ALL_STORIES_UNMUTED = 'stories/SET_HAS_ALL_STORIES_UNMUTED';
type DOEStoryActionType = {
type DOEStoryActionType = ReadonlyDeep<{
type: typeof DOE_STORY;
payload: string;
};
}>;
type ListMembersVerified = {
type ListMembersVerified = ReadonlyDeep<{
type: typeof LIST_MEMBERS_VERIFIED;
payload: {
conversationId: string;
distributionId: string | undefined;
uuids: Array<UUIDStringType>;
};
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
type LoadStoryRepliesActionType = {
type: typeof LOAD_STORY_REPLIES;
payload: {
@ -179,62 +186,63 @@ type LoadStoryRepliesActionType = {
};
};
type MarkStoryReadActionType = {
type MarkStoryReadActionType = ReadonlyDeep<{
type: typeof MARK_STORY_READ;
payload: {
messageId: string;
readAt: number;
};
};
}>;
type QueueStoryDownloadActionType = {
type QueueStoryDownloadActionType = ReadonlyDeep<{
type: typeof QUEUE_STORY_DOWNLOAD;
payload: string;
};
}>;
type SendStoryModalOpenStateChanged = {
type SendStoryModalOpenStateChanged = ReadonlyDeep<{
type: typeof SEND_STORY_MODAL_OPEN_STATE_CHANGED;
payload: number | undefined;
};
}>;
type StoryChangedActionType = {
type StoryChangedActionType = ReadonlyDeep<{
type: typeof STORY_CHANGED;
payload: StoryDataType;
};
}>;
type ToggleViewActionType = {
type ToggleViewActionType = ReadonlyDeep<{
type: typeof TOGGLE_VIEW;
};
}>;
type ViewStoryActionType = {
type ViewStoryActionType = ReadonlyDeep<{
type: typeof VIEW_STORY;
payload: SelectedStoryDataType | undefined;
};
}>;
type StoryReplyDeletedActionType = {
type StoryReplyDeletedActionType = ReadonlyDeep<{
type: typeof STORY_REPLY_DELETED;
payload: string;
};
}>;
type RemoveAllStoriesActionType = {
type RemoveAllStoriesActionType = ReadonlyDeep<{
type: typeof REMOVE_ALL_STORIES;
};
}>;
type SetAddStoryDataType = {
type SetAddStoryDataType = ReadonlyDeep<{
type: typeof SET_ADD_STORY_DATA;
payload: AddStoryData;
};
}>;
type SetStorySendingType = {
type SetStorySendingType = ReadonlyDeep<{
type: typeof SET_STORY_SENDING;
payload: boolean;
};
}>;
type SetHasAllStoriesUnmutedType = {
type SetHasAllStoriesUnmutedType = ReadonlyDeep<{
type: typeof SET_HAS_ALL_STORIES_UNMUTED;
payload: boolean;
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type StoriesActionType =
| DOEStoryActionType
| ListMembersVerified
@ -817,11 +825,13 @@ const getSelectedStoryDataForConversationId = (
};
};
export type ViewUserStoriesActionCreatorType = (opts: {
conversationId: string;
storyViewMode?: StoryViewModeType;
viewTarget?: StoryViewTargetType;
}) => unknown;
export type ViewUserStoriesActionCreatorType = ReadonlyDeep<
(opts: {
conversationId: string;
storyViewMode?: StoryViewModeType;
viewTarget?: StoryViewTargetType;
}) => unknown
>;
const viewUserStories: ViewUserStoriesActionCreatorType = ({
conversationId,
@ -886,7 +896,7 @@ function removeAllStories(): RemoveAllStoriesActionType {
};
}
type ViewStoryOptionsType =
type ViewStoryOptionsType = ReadonlyDeep<
| {
closeViewer: true;
}
@ -895,15 +905,18 @@ type ViewStoryOptionsType =
storyViewMode: StoryViewModeType;
viewDirection?: StoryViewDirectionType;
viewTarget?: StoryViewTargetType;
};
}
>;
export type ViewStoryActionCreatorType = (
opts: ViewStoryOptionsType
) => unknown;
export type ViewStoryActionCreatorType = ReadonlyDeep<
(opts: ViewStoryOptionsType) => unknown
>;
export type DispatchableViewStoryType = (
opts: ViewStoryOptionsType
) => ThunkAction<void, RootStateType, unknown, ViewStoryActionType>;
export type DispatchableViewStoryType = ReadonlyDeep<
(
opts: ViewStoryOptionsType
) => ThunkAction<void, RootStateType, unknown, ViewStoryActionType>
>;
const viewStory: ViewStoryActionCreatorType = (
opts

View File

@ -4,6 +4,7 @@
import { omit } from 'lodash';
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import type { StateType as RootStateType } from '../reducer';
import type { StoryDistributionWithMembersType } from '../../sql/Interface';
import type { UUIDStringType } from '../../types/UUID';
@ -19,18 +20,18 @@ import { useBoundActions } from '../../hooks/useBoundActions';
// State
export type StoryDistributionListDataType = {
export type StoryDistributionListDataType = ReadonlyDeep<{
id: UUIDStringType;
deletedAtTimestamp?: number;
name: string;
allowsReplies: boolean;
isBlockList: boolean;
memberUuids: Array<UUIDStringType>;
};
}>;
export type StoryDistributionListStateType = {
export type StoryDistributionListStateType = ReadonlyDeep<{
distributionLists: Array<StoryDistributionListDataType>;
};
}>;
// Actions
@ -43,65 +44,65 @@ export const MODIFY_LIST = 'storyDistributionLists/MODIFY_LIST';
const RESET_MY_STORIES = 'storyDistributionLists/RESET_MY_STORIES';
export const VIEWERS_CHANGED = 'storyDistributionLists/VIEWERS_CHANGED';
type AllowRepliesChangedActionType = {
type AllowRepliesChangedActionType = ReadonlyDeep<{
type: typeof ALLOW_REPLIES_CHANGED;
payload: {
listId: string;
allowsReplies: boolean;
};
};
}>;
type CreateListActionType = {
type CreateListActionType = ReadonlyDeep<{
type: typeof CREATE_LIST;
payload: StoryDistributionListDataType;
};
}>;
type DeleteListActionType = {
type DeleteListActionType = ReadonlyDeep<{
type: typeof DELETE_LIST;
payload: {
listId: string;
deletedAtTimestamp: number;
};
};
}>;
type HideMyStoriesFromActionType = {
type HideMyStoriesFromActionType = ReadonlyDeep<{
type: typeof HIDE_MY_STORIES_FROM;
payload: Array<UUIDStringType>;
};
}>;
type ModifyDistributionListType = Omit<
StoryDistributionListDataType,
'memberUuids'
> & {
membersToAdd: Array<UUIDStringType>;
membersToRemove: Array<UUIDStringType>;
};
type ModifyDistributionListType = ReadonlyDeep<
Omit<StoryDistributionListDataType, 'memberUuids'> & {
membersToAdd: Array<UUIDStringType>;
membersToRemove: Array<UUIDStringType>;
}
>;
export type ModifyListActionType = {
export type ModifyListActionType = ReadonlyDeep<{
type: typeof MODIFY_LIST;
payload: ModifyDistributionListType;
};
}>;
type ResetMyStoriesActionType = {
type ResetMyStoriesActionType = ReadonlyDeep<{
type: typeof RESET_MY_STORIES;
};
}>;
type ViewersChangedActionType = {
type ViewersChangedActionType = ReadonlyDeep<{
type: typeof VIEWERS_CHANGED;
payload: {
listId: string;
memberUuids: Array<UUIDStringType>;
};
};
}>;
export type StoryDistributionListsActionType =
export type StoryDistributionListsActionType = ReadonlyDeep<
| AllowRepliesChangedActionType
| CreateListActionType
| DeleteListActionType
| HideMyStoriesFromActionType
| ModifyListActionType
| ResetMyStoriesActionType
| ViewersChangedActionType;
| ViewersChangedActionType
>;
// Action Creators
@ -503,7 +504,7 @@ export function getEmptyState(): StoryDistributionListStateType {
}
function replaceDistributionListData(
distributionLists: Array<StoryDistributionListDataType>,
distributionLists: ReadonlyArray<StoryDistributionListDataType>,
listId: string,
getNextDistributionListData: (
list: StoryDistributionListDataType

View File

@ -3,6 +3,7 @@
import { ipcRenderer } from 'electron';
import type { ReadonlyDeep } from 'type-fest';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
import type { NoopActionType } from './noop';
import type { ReplacementValuesType } from '../../types/Util';
@ -11,6 +12,7 @@ import type { ToastType } from '../../types/Toast';
// State
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type ToastStateType = {
toast?: {
toastType: ToastType;
@ -23,10 +25,11 @@ export type ToastStateType = {
const HIDE_TOAST = 'toast/HIDE_TOAST';
export const SHOW_TOAST = 'toast/SHOW_TOAST';
type HideToastActionType = {
type HideToastActionType = ReadonlyDeep<{
type: typeof HIDE_TOAST;
};
}>;
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type ShowToastActionType = {
type: typeof SHOW_TOAST;
payload: {
@ -35,6 +38,7 @@ export type ShowToastActionType = {
};
};
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type ToastActionType = HideToastActionType | ShowToastActionType;
// Action Creators
@ -53,10 +57,12 @@ function openFileInFolder(target: string): NoopActionType {
};
}
export type ShowToastActionCreatorType = (
toastType: ToastType,
parameters?: ReplacementValuesType
) => ShowToastActionType;
export type ShowToastActionCreatorType = ReadonlyDeep<
(
toastType: ToastType,
parameters?: ReplacementValuesType
) => ShowToastActionType
>;
export const showToast: ShowToastActionCreatorType = (
toastType,

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import * as updateIpc from '../../shims/updateIpc';
import { DialogType } from '../../types/Dialogs';
import { DAY } from '../../util/durations';
@ -9,14 +10,14 @@ import type { StateType as RootStateType } from '../reducer';
// State
export type UpdatesStateType = {
export type UpdatesStateType = ReadonlyDeep<{
dialogType: DialogType;
didSnooze: boolean;
downloadSize?: number;
downloadedSize?: number;
showEventsCount: number;
version?: string;
};
}>;
// Actions
@ -26,43 +27,44 @@ const SNOOZE_UPDATE = 'updates/SNOOZE_UPDATE';
const START_UPDATE = 'updates/START_UPDATE';
const UNSNOOZE_UPDATE = 'updates/UNSNOOZE_UPDATE';
export type UpdateDialogOptionsType = {
export type UpdateDialogOptionsType = ReadonlyDeep<{
downloadSize?: number;
downloadedSize?: number;
version?: string;
};
}>;
type DismissDialogActionType = {
type DismissDialogActionType = ReadonlyDeep<{
type: typeof DISMISS_DIALOG;
};
}>;
export type ShowUpdateDialogActionType = {
export type ShowUpdateDialogActionType = ReadonlyDeep<{
type: typeof SHOW_UPDATE_DIALOG;
payload: {
dialogType: DialogType;
otherState: UpdateDialogOptionsType;
};
};
}>;
type SnoozeUpdateActionType = {
type SnoozeUpdateActionType = ReadonlyDeep<{
type: typeof SNOOZE_UPDATE;
};
}>;
type StartUpdateActionType = {
type StartUpdateActionType = ReadonlyDeep<{
type: typeof START_UPDATE;
};
}>;
type UnsnoozeUpdateActionType = {
type UnsnoozeUpdateActionType = ReadonlyDeep<{
type: typeof UNSNOOZE_UPDATE;
payload: DialogType;
};
}>;
export type UpdatesActionType =
export type UpdatesActionType = ReadonlyDeep<
| DismissDialogActionType
| ShowUpdateDialogActionType
| SnoozeUpdateActionType
| StartUpdateActionType
| UnsnoozeUpdateActionType;
| UnsnoozeUpdateActionType
>;
// Action Creators

View File

@ -1,6 +1,7 @@
// Copyright 2019 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReadonlyDeep } from 'type-fest';
import { trigger } from '../../shims/events';
import type { LocaleMessagesType } from '../../types/I18N';
@ -13,7 +14,8 @@ import { ThemeType } from '../../types/Util';
// State
export type UserStateType = {
// eslint-disable-next-line local-rules/type-alias-readonlydeep
export type UserStateType = Readonly<{
attachmentsPath: string;
i18n: LocalizerType;
interactionMode: 'mouse' | 'keyboard';
@ -33,11 +35,11 @@ export type UserStateType = {
tempPath: string;
theme: ThemeType;
version: string;
};
}>;
// Actions
type UserChangedActionType = {
type UserChangedActionType = ReadonlyDeep<{
type: 'USER_CHANGED';
payload: {
ourConversationId?: string;
@ -52,9 +54,9 @@ type UserChangedActionType = {
isMainWindowFullScreen?: boolean;
menuOptions?: MenuOptionsType;
};
};
}>;
export type UserActionType = UserChangedActionType;
export type UserActionType = ReadonlyDeep<UserChangedActionType>;
// Action Creators

View File

@ -3,6 +3,7 @@
import type { ThunkAction } from 'redux-thunk';
import type { ReadonlyDeep } from 'type-fest';
import type { UsernameReservationType } from '../../types/Username';
import { ReserveUsernameError } from '../../types/Username';
import * as usernameServices from '../../services/username';
@ -27,14 +28,14 @@ import { showToast } from './toast';
import { ToastType } from '../../types/Toast';
import type { ToastActionType } from './toast';
export type UsernameReservationStateType = Readonly<{
export type UsernameReservationStateType = ReadonlyDeep<{
state: UsernameReservationState;
reservation?: UsernameReservationType;
error?: UsernameReservationError;
abortController?: AbortController;
}>;
export type UsernameStateType = Readonly<{
export type UsernameStateType = ReadonlyDeep<{
// ProfileEditor
editState: UsernameEditState;
@ -52,44 +53,51 @@ const RESERVE_USERNAME = 'username/RESERVE_USERNAME';
const CONFIRM_USERNAME = 'username/CONFIRM_USERNAME';
const DELETE_USERNAME = 'username/DELETE_USERNAME';
type SetUsernameEditStateActionType = {
type SetUsernameEditStateActionType = ReadonlyDeep<{
type: typeof SET_USERNAME_EDIT_STATE;
payload: {
editState: UsernameEditState;
};
};
}>;
type OpenUsernameReservationModalActionType = {
type OpenUsernameReservationModalActionType = ReadonlyDeep<{
type: typeof OPEN_USERNAME_RESERVATION_MODAL;
};
}>;
type CloseUsernameReservationModalActionType = {
type CloseUsernameReservationModalActionType = ReadonlyDeep<{
type: typeof CLOSE_USERNAME_RESERVATION_MODAL;
};
}>;
type SetUsernameReservationErrorActionType = {
type SetUsernameReservationErrorActionType = ReadonlyDeep<{
type: typeof SET_USERNAME_RESERVATION_ERROR;
payload: {
error: UsernameReservationError | undefined;
};
};
}>;
type ReserveUsernameActionType = PromiseAction<
typeof RESERVE_USERNAME,
ReserveUsernameResultType | undefined,
{ abortController: AbortController }
type ReserveUsernameActionType = ReadonlyDeep<
PromiseAction<
typeof RESERVE_USERNAME,
ReserveUsernameResultType | undefined,
{ abortController: AbortController }
>
>;
type ConfirmUsernameActionType = ReadonlyDeep<
PromiseAction<typeof CONFIRM_USERNAME, void>
>;
type DeleteUsernameActionType = ReadonlyDeep<
PromiseAction<typeof DELETE_USERNAME, void>
>;
type ConfirmUsernameActionType = PromiseAction<typeof CONFIRM_USERNAME, void>;
type DeleteUsernameActionType = PromiseAction<typeof DELETE_USERNAME, void>;
export type UsernameActionType =
export type UsernameActionType = ReadonlyDeep<
| SetUsernameEditStateActionType
| OpenUsernameReservationModalActionType
| CloseUsernameReservationModalActionType
| SetUsernameReservationErrorActionType
| ReserveUsernameActionType
| ConfirmUsernameActionType
| DeleteUsernameActionType;
| DeleteUsernameActionType
>;
export const actions = {
setUsernameEditState,
@ -133,7 +141,7 @@ export function setUsernameReservationError(
const INPUT_DELAY_MS = 500;
export type ReserveUsernameOptionsType = Readonly<{
export type ReserveUsernameOptionsType = ReadonlyDeep<{
doReserveUsername?: typeof usernameServices.reserveUsername;
delay?: number;
}>;
@ -194,7 +202,7 @@ export function reserveUsername(
};
}
export type ConfirmUsernameOptionsType = Readonly<{
export type ConfirmUsernameOptionsType = ReadonlyDeep<{
doConfirmUsername?: typeof usernameServices.confirmUsername;
}>;
@ -221,7 +229,7 @@ export function confirmUsername({
};
}
export type DeleteUsernameOptionsType = Readonly<{
export type DeleteUsernameOptionsType = ReadonlyDeep<{
doDeleteUsername?: typeof usernameServices.deleteUsername;
// Only for testing

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { createSelector } from 'reselect';
import type { ReadonlyDeep } from 'type-fest';
import type { MediaItemType } from '../../types/MediaItem';
import type { StateType } from '../reducer';
import type { LightboxStateType } from '../ducks/lightbox';
@ -36,6 +37,6 @@ export const getSelectedIndex = createSelector(
export const getMedia = createSelector(
getLightboxState,
(state): ReadonlyArray<MediaItemType> =>
(state): ReadonlyArray<ReadonlyDeep<MediaItemType>> =>
state.isShowingLightbox ? state.media : []
);

View File

@ -125,7 +125,7 @@ export const getRecentStickers = createSelector(
getStickersPath,
getTempPath,
(
recents: Array<RecentStickerType>,
recents: ReadonlyArray<RecentStickerType>,
packs: Dictionary<StickerPackDBType>,
stickersPath: string,
tempPath: string

View File

@ -8,6 +8,7 @@ import { MediaGallery } from '../../components/conversation/media-gallery/MediaG
import { getMediaGalleryState } from '../selectors/mediaGallery';
import { useConversationsActions } from '../ducks/conversations';
import { useLightboxActions } from '../ducks/lightbox';
import { useMediaGalleryActions } from '../ducks/mediaGallery';
export type PropsType = {

View File

@ -4,6 +4,7 @@
import React from 'react';
import { useSelector } from 'react-redux';
import type { ReadonlyDeep } from 'type-fest';
import type { GetConversationByIdType } from '../selectors/conversations';
import type { LocalizerType } from '../../types/Util';
import type { MediaItemType } from '../../types/MediaItem';
@ -33,7 +34,10 @@ export function SmartLightbox(): JSX.Element | null {
const isShowingLightbox = useSelector<StateType, boolean>(shouldShowLightbox);
const isViewOnce = useSelector<StateType, boolean>(getIsViewOnce);
const media = useSelector<StateType, ReadonlyArray<MediaItemType>>(getMedia);
const media = useSelector<
StateType,
ReadonlyArray<ReadonlyDeep<MediaItemType>>
>(getMedia);
const selectedIndex = useSelector<StateType, number>(getSelectedIndex);
if (!isShowingLightbox) {

View File

@ -6,6 +6,7 @@ import type { RefObject } from 'react';
import React from 'react';
import { connect } from 'react-redux';
import type { ReadonlyDeep } from 'type-fest';
import { mapDispatchToProps } from '../actions';
import type {
ContactSpoofingReviewPropType,
@ -103,7 +104,7 @@ function renderTypingBubble(id: string): JSX.Element {
}
const getWarning = (
conversation: Readonly<ConversationType>,
conversation: ReadonlyDeep<ConversationType>,
state: Readonly<StateType>
): undefined | TimelineWarningType => {
switch (conversation.type) {

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { mapValues, pickBy } from 'lodash';
import type { ReadonlyDeep } from 'type-fest';
import { groupBy, map, filter } from './iterables';
import { getOwn } from './getOwn';
import type { ConversationType } from '../state/ducks/conversations';
@ -51,8 +52,8 @@ export function getCollisionsFromMemberships(
* haven't dismissed.
*/
export const hasUnacknowledgedCollisions = (
previous: Readonly<GroupNameCollisionsWithIdsByTitle>,
current: Readonly<GroupNameCollisionsWithIdsByTitle>
previous: ReadonlyDeep<GroupNameCollisionsWithIdsByTitle>,
current: ReadonlyDeep<GroupNameCollisionsWithIdsByTitle>
): boolean =>
Object.entries(current).some(([title, currentIds]) => {
const previousIds = new Set(getOwn(previous, title) || []);
@ -60,7 +61,7 @@ export const hasUnacknowledgedCollisions = (
});
export const invertIdsByTitle = (
idsByTitle: Readonly<GroupNameCollisionsWithIdsByTitle>
idsByTitle: ReadonlyDeep<GroupNameCollisionsWithIdsByTitle>
): GroupNameCollisionsWithTitlesById => {
const result: GroupNameCollisionsWithTitlesById = Object.create(null);
Object.entries(idsByTitle).forEach(([title, ids]) => {

View File

@ -7,7 +7,7 @@
"lib": [
"dom", // Required to access `window`
"dom.iterable",
"es2020"
"es2022"
],
"incremental": true,
// "allowJs": true, // Allow javascript files to be compiled.

View File

@ -17726,6 +17726,11 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8:
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
type-fest@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.5.0.tgz#df7b2ef54ea775163c56d087b33e901ce9d657f7"
integrity sha512-bI3zRmZC8K0tUz1HjbIOAGQwR2CoPQG68N5IF7gm0LBl8QSNXzkmaWnkWccCUL5uG9mCsp4sBwC8SBrNSISWew==
type-fest@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"