Add type-alias-readonlydeep rule and make ducks mostly immutable
This commit is contained in:
parent
11ce3c3d59
commit
c58a723f45
47 changed files with 1164 additions and 871 deletions
58
.eslint/rules/type-alias-readonlydeep.js
Normal file
58
.eslint/rules/type-alias-readonlydeep.js
Normal 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',
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
79
.eslint/rules/type-alias-readonlydeep.test.js
Normal file
79
.eslint/rules/type-alias-readonlydeep.test.js
Normal 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',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
|
@ -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: {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
|
||||
module.exports = {
|
||||
'valid-i18n-keys': require('./.eslint/rules/valid-i18n-keys'),
|
||||
'type-alias-readonlydeep': require('./.eslint/rules/type-alias-readonlydeep'),
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 : []
|
||||
);
|
||||
|
|
|
@ -125,7 +125,7 @@ export const getRecentStickers = createSelector(
|
|||
getStickersPath,
|
||||
getTempPath,
|
||||
(
|
||||
recents: Array<RecentStickerType>,
|
||||
recents: ReadonlyArray<RecentStickerType>,
|
||||
packs: Dictionary<StickerPackDBType>,
|
||||
stickersPath: string,
|
||||
tempPath: string
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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]) => {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"lib": [
|
||||
"dom", // Required to access `window`
|
||||
"dom.iterable",
|
||||
"es2020"
|
||||
"es2022"
|
||||
],
|
||||
"incremental": true,
|
||||
// "allowJs": true, // Allow javascript files to be compiled.
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue