Use ReadonlyArrays in conversation model and redux

This commit is contained in:
Fedor Indutny 2022-12-21 16:07:02 -08:00 committed by GitHub
parent ecbf84638d
commit dec23725e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 173 additions and 162 deletions

View file

@ -1105,7 +1105,7 @@ export class SignalProtocolStore extends EventEmitter {
async getOpenDevices(
ourUuid: UUID,
identifiers: Array<string>,
identifiers: ReadonlyArray<string>,
{ zone = GLOBAL_ZONE }: SessionTransactionOptions = {}
): Promise<{
devices: Array<DeviceType>;

View file

@ -49,7 +49,7 @@ export type Props = {
noteToSelf?: boolean;
phoneNumber?: string;
profileName?: string;
sharedGroupNames: Array<string>;
sharedGroupNames: ReadonlyArray<string>;
size: AvatarSize;
title: string;
unblurredAvatarPath?: string;

View file

@ -79,7 +79,7 @@ export type Props = Readonly<{
moduleClassName?: string;
theme: ThemeType;
placeholder?: string;
sortedGroupMembers?: Array<ConversationType>;
sortedGroupMembers?: ReadonlyArray<ConversationType>;
scrollerRef?: React.RefObject<HTMLDivElement>;
onDirtyChange?(dirty: boolean): unknown;
onEditorStateChange?(
@ -488,7 +488,9 @@ export function CompositionInput(props: Props): React.ReactElement {
[]
);
const removeStaleMentions = (currentMembers: Array<ConversationType>) => {
const removeStaleMentions = (
currentMembers: ReadonlyArray<ConversationType>
) => {
const quill = quillRef.current;
if (quill === undefined) {

View file

@ -20,12 +20,12 @@ import { offsetDistanceModifier } from '../util/popperUtil';
import { handleOutsideClick } from '../util/handleOutsideClick';
type PropsType = {
draftPreferredReactions: Array<string>;
draftPreferredReactions: ReadonlyArray<string>;
hadSaveError: boolean;
i18n: LocalizerType;
isSaving: boolean;
originalPreferredReactions: Array<string>;
recentEmojis: Array<string>;
originalPreferredReactions: ReadonlyArray<string>;
recentEmojis: ReadonlyArray<string>;
selectedDraftEmojiIndex: undefined | number;
skinTone: number;

View file

@ -40,12 +40,12 @@ import {
} from '../util/shouldNeverBeCalled';
export type DataPropsType = {
attachments?: Array<AttachmentType>;
attachments?: ReadonlyArray<AttachmentType>;
candidateConversations: ReadonlyArray<ConversationType>;
doForwardMessage: (
selectedContacts: Array<string>,
messageBody?: string,
attachments?: Array<AttachmentType>,
attachments?: ReadonlyArray<AttachmentType>,
linkPreview?: LinkPreviewType
) => void;
getPreferredBadge: PreferredBadgeSelectorType;
@ -102,7 +102,7 @@ export function ForwardMessageModal({
filterAndSortConversationsByRecent(candidateConversations, '', regionCode)
);
const [attachmentsToForward, setAttachmentsToForward] = useState<
Array<AttachmentType>
ReadonlyArray<AttachmentType>
>(attachments || []);
const [isEditingMessage, setIsEditingMessage] = useState(false);
const [messageBodyText, setMessageBodyText] = useState(messageBody || '');

View file

@ -29,7 +29,7 @@ export type PropsType = {
getConversation?: (id: string) => ConversationType;
i18n: LocalizerType;
isViewOnce?: boolean;
media: Array<MediaItemType>;
media: ReadonlyArray<MediaItemType>;
saveAttachment: SaveAttachmentActionCreatorType;
selectedIndex?: number;
toggleForwardMessageModal: (messageId: string) => unknown;

View file

@ -69,7 +69,7 @@ export type PropsDataType = {
firstName: string;
i18n: LocalizerType;
isUsernameFlagEnabled: boolean;
userAvatarData: Array<AvatarDataType>;
userAvatarData: ReadonlyArray<AvatarDataType>;
username?: string;
usernameEditState: UsernameEditState;
} & Pick<EmojiButtonProps, 'recentEmojis' | 'skinTone'>;

View file

@ -11,7 +11,7 @@ import type { LocalizerType } from '../types/Util';
type PropsType = {
i18n: LocalizerType;
nameClassName?: string;
sharedGroupNames: Array<string>;
sharedGroupNames: ReadonlyArray<string>;
};
export function SharedGroupNames({

View file

@ -96,9 +96,9 @@ export type PropsType = {
story: StoryViewType
) => unknown;
onUseEmoji: (_: EmojiPickDataType) => unknown;
preferredReactionEmoji: Array<string>;
preferredReactionEmoji: ReadonlyArray<string>;
queueStoryDownload: (storyId: string) => unknown;
recentEmojis?: Array<string>;
recentEmojis?: ReadonlyArray<string>;
renderEmojiPicker: (props: RenderEmojiPickerProps) => JSX.Element;
replyState?: ReplyStateType;
retryMessageSend: (messageId: string) => unknown;

View file

@ -98,14 +98,14 @@ export type PropsType = {
onSetSkinTone: (tone: number) => unknown;
onTextTooLong: () => unknown;
onUseEmoji: (_: EmojiPickDataType) => unknown;
preferredReactionEmoji: Array<string>;
recentEmojis?: Array<string>;
preferredReactionEmoji: ReadonlyArray<string>;
recentEmojis?: ReadonlyArray<string>;
renderEmojiPicker: (props: RenderEmojiPickerProps) => JSX.Element;
replies: ReadonlyArray<ReplyType>;
skinTone?: number;
sortedGroupMembers?: Array<ConversationType>;
sortedGroupMembers?: ReadonlyArray<ConversationType>;
storyPreviewAttachment?: AttachmentType;
views: Array<StorySendStateType>;
views: ReadonlyArray<StorySendStateType>;
viewTarget: StoryViewTargetType;
onChangeViewTarget: (target: StoryViewTargetType) => unknown;
deleteGroupStoryReply: (id: string) => void;

View file

@ -29,7 +29,7 @@ export type Props = {
membersCount?: number;
name?: string;
phoneNumber?: string;
sharedGroupNames?: Array<string>;
sharedGroupNames?: ReadonlyArray<string>;
unblurAvatar: (conversationId: string) => void;
unblurredAvatarPath?: string;
updateSharedGroups: (conversationId: string) => unknown;

View file

@ -43,11 +43,11 @@ const renderChange = (
groupName,
areWeAdmin = true,
}: {
groupMemberships?: Array<{
groupMemberships?: ReadonlyArray<{
uuid: UUIDStringType;
isAdmin: boolean;
}>;
groupBannedMemberships?: Array<UUIDStringType>;
groupBannedMemberships?: ReadonlyArray<UUIDStringType>;
groupName?: string;
areWeAdmin?: boolean;
} = {}

View file

@ -25,11 +25,11 @@ import { ConfirmationDialog } from '../ConfirmationDialog';
export type PropsDataType = {
areWeAdmin: boolean;
conversationId: string;
groupMemberships?: Array<{
groupMemberships?: ReadonlyArray<{
uuid: UUIDStringType;
isAdmin: boolean;
}>;
groupBannedMemberships?: Array<UUIDStringType>;
groupBannedMemberships?: ReadonlyArray<UUIDStringType>;
groupName?: string;
ourACI?: UUIDStringType;
ourPNI?: UUIDStringType;
@ -155,11 +155,11 @@ function GroupV2Detail({
conversationId: string;
detail: GroupV2ChangeDetailType;
isLastText: boolean;
groupMemberships?: Array<{
groupMemberships?: ReadonlyArray<{
uuid: UUIDStringType;
isAdmin: boolean;
}>;
groupBannedMemberships?: Array<UUIDStringType>;
groupBannedMemberships?: ReadonlyArray<UUIDStringType>;
groupName?: string;
i18n: LocalizerType;
fromId?: UUIDStringType;

View file

@ -21,7 +21,7 @@ import type { LocalizerType, ThemeType } from '../../types/Util';
export type DirectionType = 'incoming' | 'outgoing';
export type Props = {
attachments: Array<AttachmentType>;
attachments: ReadonlyArray<AttachmentType>;
bottomOverlay?: boolean;
direction: DirectionType;
isSticker?: boolean;

View file

@ -223,7 +223,7 @@ export type PropsData = {
>;
reducedMotion?: boolean;
conversationType: ConversationTypeType;
attachments?: Array<AttachmentType>;
attachments?: ReadonlyArray<AttachmentType>;
giftBadge?: GiftBadgeType;
payment?: AnyPaymentEvent;
quote?: {
@ -255,7 +255,7 @@ export type PropsData = {
storyId?: string;
text: string;
};
previews: Array<LinkPreviewType>;
previews: ReadonlyArray<LinkPreviewType>;
isTapToView?: boolean;
isTapToViewExpired?: boolean;

View file

@ -28,7 +28,7 @@ export type OwnProps = {
onPick: (emoji: string) => unknown;
onSetSkinTone: (tone: number) => unknown;
openCustomizePreferredReactionsModal?: () => unknown;
preferredReactionEmoji: Array<string>;
preferredReactionEmoji: ReadonlyArray<string>;
renderEmojiPicker: (props: RenderEmojiPickerProps) => React.ReactElement;
};

View file

@ -48,7 +48,7 @@ type StateType = {
maximumGroupSizeModalState: OneTimeModalState;
recommendedGroupSizeModalState: OneTimeModalState;
searchTerm: string;
selectedConversationIds: Array<string>;
selectedConversationIds: ReadonlyArray<string>;
stage: Stage;
};

View file

@ -74,14 +74,14 @@ export type StateProps = {
i18n: LocalizerType;
isAdmin: boolean;
isGroup: boolean;
groupsInCommon: Array<ConversationType>;
groupsInCommon: ReadonlyArray<ConversationType>;
maxGroupSize: number;
maxRecommendedGroupSize: number;
memberships: Array<GroupV2Membership>;
memberships: ReadonlyArray<GroupV2Membership>;
pendingApprovalMemberships: ReadonlyArray<GroupV2RequestingMembership>;
pendingMemberships: ReadonlyArray<GroupV2PendingMembership>;
theme: ThemeType;
userAvatarData: Array<AvatarDataType>;
userAvatarData: ReadonlyArray<AvatarDataType>;
renderChooseGroupMembersModal: (
props: SmartChooseGroupMembersModalPropsType
) => JSX.Element;

View file

@ -15,7 +15,7 @@ import { PanelSection } from './PanelSection';
type Props = {
contactId: string;
i18n: LocalizerType;
groupsInCommon: Array<ConversationType>;
groupsInCommon: ReadonlyArray<ConversationType>;
toggleAddUserToAnotherGroupModal: (contactId?: string) => void;
showConversation: ShowConversationType;
};

View file

@ -24,7 +24,7 @@ export type Props = {
i18n: LocalizerType;
isGroup: boolean;
isMe: boolean;
memberships: Array<GroupV2Membership>;
memberships: ReadonlyArray<GroupV2Membership>;
startEditing: (isGroupTitle: boolean) => void;
theme: ThemeType;
};

View file

@ -19,7 +19,7 @@ export type Props = {
showAllMedia: () => void;
showLightboxWithMedia: (
selectedAttachmentPath: string | undefined,
media: Array<MediaItemType>
media: ReadonlyArray<MediaItemType>
) => void;
};

View file

@ -25,7 +25,7 @@ export type Props = {
getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType;
maxShownMemberCount?: number;
memberships: Array<GroupV2Membership>;
memberships: ReadonlyArray<GroupV2Membership>;
showContactModal: (contactId: string, conversationId?: string) => void;
startAddingNewMembers?: () => void;
theme: ThemeType;

View file

@ -41,7 +41,7 @@ type PropsType = {
deleteAvatarFromDisk: DeleteAvatarFromDiskActionType;
replaceAvatar: ReplaceAvatarActionType;
saveAvatarToDisk: SaveAvatarToDiskActionType;
userAvatarData: Array<AvatarDataType>;
userAvatarData: ReadonlyArray<AvatarDataType>;
};
export function EditConversationAttributesModal({

View file

@ -34,7 +34,7 @@ type PropsActionType = {
) => void;
readonly revokePendingMembershipsFromGroupV2: (
conversationId: string,
memberIds: Array<string>
memberIds: ReadonlyArray<string>
) => void;
};
@ -193,14 +193,14 @@ function MembershipActionConfirmation({
) => void;
conversation: ConversationType;
i18n: LocalizerType;
members: Array<ConversationType>;
members: ReadonlyArray<ConversationType>;
onClose: () => void;
ourUuid: string;
revokePendingMembershipsFromGroupV2: (
conversationId: string,
memberIds: Array<string>
memberIds: ReadonlyArray<string>
) => void;
stagedMemberships: Array<StagedMembershipType>;
stagedMemberships: ReadonlyArray<StagedMembershipType>;
}) {
const revokeStagedMemberships = () => {
if (!stagedMemberships) {
@ -424,7 +424,7 @@ function MembersPendingProfileKey({
conversation: ConversationType;
getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType;
members: Array<ConversationType>;
members: ReadonlyArray<ConversationType>;
memberships: ReadonlyArray<GroupV2PendingMembership>;
ourUuid: string;
setStagedMemberships: (stagedMembership: Array<StagedMembershipType>) => void;

View file

@ -14,7 +14,7 @@ import { missingCaseError } from '../../../util/missingCaseError';
export type Props = {
header?: string;
i18n: LocalizerType;
mediaItems: Array<MediaItemType>;
mediaItems: ReadonlyArray<MediaItemType>;
onItemClick: (event: ItemClickEvent) => unknown;
type: 'media' | 'documents';
};

View file

@ -15,7 +15,7 @@ type YearMonthSectionType = 'yearMonth';
type GenericSection<T> = {
type: T;
mediaItems: Array<MediaItemType>;
mediaItems: ReadonlyArray<MediaItemType>;
};
type StaticSection = GenericSection<StaticSectionType>;
type YearMonthSection = GenericSection<YearMonthSectionType> & {
@ -25,7 +25,7 @@ type YearMonthSection = GenericSection<YearMonthSectionType> & {
export type Section = StaticSection | YearMonthSection;
export const groupMediaItemsByDate = (
timestamp: number,
mediaItems: Array<MediaItemType>
mediaItems: ReadonlyArray<MediaItemType>
): Array<Section> => {
const referenceDateTime = moment.utc(timestamp);

View file

@ -19,7 +19,7 @@ export type GroupListItemConversationType = Pick<
> & {
disabledReason: DisabledReason | undefined;
membersCount: number;
memberships: Array<{
memberships: ReadonlyArray<{
uuid: UUIDStringType;
isAdmin: boolean;
}>;

View file

@ -36,7 +36,7 @@ export type OwnProps = {
readonly doSend?: () => unknown;
readonly skinTone?: number;
readonly onSetSkinTone?: (tone: number) => unknown;
readonly recentEmojis?: Array<string>;
readonly recentEmojis?: ReadonlyArray<string>;
readonly onClickSettings?: () => unknown;
readonly onClose?: () => unknown;
};

View file

@ -1193,7 +1193,7 @@ export function buildDeletePendingMemberChange({
uuids,
group,
}: {
uuids: Array<UUID>;
uuids: ReadonlyArray<UUID>;
group: ConversationAttributesType;
}): Proto.GroupChange.Actions {
const actions = new Proto.GroupChange.Actions();
@ -1452,7 +1452,7 @@ export async function modifyGroupV2({
conversation: ConversationModel;
usingCredentialsFrom: ReadonlyArray<ConversationModel>;
createGroupChange: () => Promise<Proto.GroupChange.Actions | undefined>;
extraConversationsForSend?: Array<string>;
extraConversationsForSend?: ReadonlyArray<string>;
inviteLinkPassword?: string;
name: string;
}): Promise<void> {
@ -1554,7 +1554,7 @@ export async function modifyGroupV2({
type: conversationQueueJobEnum.enum.GroupUpdate,
conversationId: conversation.id,
groupChangeBase64,
recipients: groupV2Info.members,
recipients: groupV2Info.members.slice(),
revision: groupV2Info.revision,
});
});
@ -1733,8 +1733,8 @@ export async function createGroupV2(
name: string;
avatar: undefined | Uint8Array;
expireTimer: undefined | DurationInSeconds;
conversationIds: Array<string>;
avatars?: Array<AvatarDataType>;
conversationIds: ReadonlyArray<string>;
avatars?: ReadonlyArray<AvatarDataType>;
refreshedCredentials?: boolean;
}>
): Promise<ConversationModel> {
@ -1953,7 +1953,7 @@ export async function createGroupV2(
await conversationJobQueue.add({
type: conversationQueueJobEnum.enum.GroupUpdate,
conversationId: conversation.id,
recipients: groupV2Info.members,
recipients: groupV2Info.members.slice(),
revision: groupV2Info.revision,
});
@ -2438,7 +2438,7 @@ export async function initiateMigrationToGroupV2(
await conversationJobQueue.add({
type: conversationQueueJobEnum.enum.GroupUpdate,
conversationId: conversation.id,
recipients: groupV2Info.members,
recipients: groupV2Info.members.slice(),
revision: groupV2Info.revision,
});
}
@ -2466,7 +2466,7 @@ export async function waitThenRespondToGroupV2Migration(
}
export function buildMigrationBubble(
previousGroupV1MembersIds: Array<string>,
previousGroupV1MembersIds: ReadonlyArray<string>,
newAttributes: ConversationAttributesType
): GroupChangeMessageType {
const ourACI = window.storage.user.getCheckedUuid(UUIDKind.ACI);
@ -3880,7 +3880,7 @@ async function integrateGroupChanges({
}: {
group: ConversationAttributesType;
newRevision: number | undefined;
changes: Array<Proto.IGroupChanges>;
changes: ReadonlyArray<Proto.IGroupChanges>;
}): Promise<UpdatesResultType> {
const logId = idForLogging(group.groupId);
let attributes = group;
@ -4715,7 +4715,7 @@ function extractDiffs({
return result;
}
function profileKeysToMembers(items: Array<GroupChangeMemberType>) {
function profileKeysToMembers(items: ReadonlyArray<GroupChangeMemberType>) {
return items.map(item => ({
profileKey: Bytes.toBase64(item.profileKey),
uuid: item.uuid,

View file

@ -17,12 +17,12 @@ export function toggleSelectedContactForGroupAddition(
maximumGroupSizeModalState: OneTimeModalState;
numberOfContactsAlreadyInGroup: number;
recommendedGroupSizeModalState: OneTimeModalState;
selectedConversationIds: Array<string>;
selectedConversationIds: ReadonlyArray<string>;
}>
): {
maximumGroupSizeModalState: OneTimeModalState;
recommendedGroupSizeModalState: OneTimeModalState;
selectedConversationIds: Array<string>;
selectedConversationIds: ReadonlyArray<string>;
} {
const {
maxGroupSize,

View file

@ -741,7 +741,7 @@ export class ConversationModel extends window.Backbone
}: {
usingCredentialsFrom: ReadonlyArray<ConversationModel>;
createGroupChange: () => Promise<Proto.GroupChange.Actions | undefined>;
extraConversationsForSend?: Array<string>;
extraConversationsForSend?: ReadonlyArray<string>;
inviteLinkPassword?: string;
name: string;
}): Promise<void> {
@ -1159,9 +1159,9 @@ export class ConversationModel extends window.Backbone
{ groupChange?: Uint8Array } & (
| {
includePendingMembers?: boolean;
extraConversationsForSend?: Array<string>;
extraConversationsForSend?: ReadonlyArray<string>;
}
| { members: Array<string> }
| { members: ReadonlyArray<string> }
)
> = {}
): GroupV2InfoType | undefined {
@ -3712,7 +3712,7 @@ export class ConversationModel extends window.Backbone
isStoryReply = false,
}: {
includePendingMembers?: boolean;
extraConversationsForSend?: Array<string>;
extraConversationsForSend?: ReadonlyArray<string>;
isStoryReply?: boolean;
} = {}): Array<string> {
return getRecipients(this.attributes, {

View file

@ -45,14 +45,14 @@ export class MemberRepository {
FUSE_OPTIONS
);
constructor(private members: Array<ConversationType> = []) {}
constructor(private members: ReadonlyArray<ConversationType> = []) {}
updateMembers(members: Array<ConversationType>): void {
updateMembers(members: ReadonlyArray<ConversationType>): void {
this.members = members;
this.isFuseReady = false;
}
getMembers(omit?: ConversationType): Array<ConversationType> {
getMembers(omit?: ConversationType): ReadonlyArray<ConversationType> {
if (omit) {
return this.members.filter(({ id }) => id !== omit.id);
}
@ -72,7 +72,10 @@ export class MemberRepository {
: undefined;
}
search(pattern: string, omit?: ConversationType): Array<ConversationType> {
search(
pattern: string,
omit?: ConversationType
): ReadonlyArray<ConversationType> {
if (!this.isFuseReady) {
this.fuse.setCollection(this.members);
this.isFuseReady = true;

View file

@ -31,7 +31,7 @@ export type MentionCompletionOptions = {
const MENTION_REGEX = /(?:^|\W)@([-+\w]*)$/;
export class MentionCompletion {
results: Array<ConversationType>;
results: ReadonlyArray<ConversationType>;
index: number;
@ -105,7 +105,7 @@ export class MentionCompletion {
this.clearResults();
}
possiblyShowMemberResults(): Array<ConversationType> {
possiblyShowMemberResults(): ReadonlyArray<ConversationType> {
const range = this.quill.getSelection();
if (range) {
@ -120,7 +120,7 @@ export class MentionCompletion {
if (leftTokenTextMatch) {
const [, leftTokenText] = leftTokenTextMatch;
let results: Array<ConversationType> = [];
let results: ReadonlyArray<ConversationType> = [];
const memberRepository = this.options.memberRepositoryRef.current;

View file

@ -471,7 +471,7 @@ export type DataInterface = {
options: { forceSave?: boolean; ourUuid: UUIDStringType }
) => Promise<void>;
removeMessage: (id: string) => Promise<void>;
removeMessages: (ids: Array<string>) => Promise<void>;
removeMessages: (ids: ReadonlyArray<string>) => Promise<void>;
getTotalUnreadForConversation: (
conversationId: string,
options: {

View file

@ -1509,7 +1509,7 @@ async function updateConversations(
})();
}
function removeConversationsSync(ids: Array<string>): void {
function removeConversationsSync(ids: ReadonlyArray<string>): void {
const db = getInstance();
// Our node interface doesn't seem to allow you to replace one single ? with an array
@ -2046,7 +2046,7 @@ async function removeMessage(id: string): Promise<void> {
db.prepare<Query>('DELETE FROM messages WHERE id = $id;').run({ id });
}
function removeMessagesSync(ids: Array<string>): void {
function removeMessagesSync(ids: ReadonlyArray<string>): void {
const db = getInstance();
db.prepare<ArrayQuery>(
@ -2057,7 +2057,7 @@ function removeMessagesSync(ids: Array<string>): void {
).run(ids);
}
async function removeMessages(ids: Array<string>): Promise<void> {
async function removeMessages(ids: ReadonlyArray<string>): Promise<void> {
batchMultiVarQuery(getInstance(), ids, removeMessagesSync);
}
@ -2091,7 +2091,7 @@ async function getMessagesById(
return batchMultiVarQuery(
db,
messageIds,
(batch: Array<string>): Array<MessageType> => {
(batch: ReadonlyArray<string>): Array<MessageType> => {
const query = db.prepare<ArrayQuery>(
`SELECT json FROM messages WHERE id IN (${Array(batch.length)
.fill('?')
@ -2325,7 +2325,7 @@ async function getUnreadReactionsAndMarkRead({
});
const idsToUpdate = unreadMessages.map(item => item.rowid);
batchMultiVarQuery(db, idsToUpdate, (ids: Array<number>): void => {
batchMultiVarQuery(db, idsToUpdate, (ids: ReadonlyArray<number>): void => {
db.prepare<ArrayQuery>(
`
UPDATE reactions SET
@ -3408,7 +3408,7 @@ async function getAllUnprocessedAndIncrementAttempts(): Promise<
})();
}
function removeUnprocessedsSync(ids: Array<string>): void {
function removeUnprocessedsSync(ids: ReadonlyArray<string>): void {
const db = getInstance();
db.prepare<ArrayQuery>(
@ -4680,7 +4680,7 @@ function modifyStoryDistributionMembersSync(
});
}
batchMultiVarQuery(db, toRemove, (uuids: Array<UUIDStringType>) => {
batchMultiVarQuery(db, toRemove, (uuids: ReadonlyArray<UUIDStringType>) => {
db.prepare<ArrayQuery>(
`
DELETE FROM storyDistributionMembers
@ -5166,7 +5166,7 @@ async function getKnownMessageAttachments(
const messages = batchMultiVarQuery(
db,
rowids,
(batch: Array<number>): Array<MessageType> => {
(batch: ReadonlyArray<number>): Array<MessageType> => {
const query = db.prepare<ArrayQuery>(
`SELECT json FROM messages WHERE rowid IN (${Array(batch.length)
.fill('?')

View file

@ -57,7 +57,7 @@ export default function updateToSchemaVersion42(
}
});
function deleteReactions(rowids: Array<number>) {
function deleteReactions(rowids: ReadonlyArray<number>) {
db.prepare<ArrayQuery>(
`
DELETE FROM reactions

View file

@ -5,7 +5,7 @@ import type { Database } from '@signalapp/better-sqlite3';
import { isNumber, last } from 'lodash';
export type EmptyQuery = [];
export type ArrayQuery = Array<Array<null | number | bigint | string>>;
export type ArrayQuery = Array<ReadonlyArray<null | number | bigint | string>>;
export type Query = {
[key: string]: null | number | bigint | string | Uint8Array;
};
@ -72,21 +72,21 @@ export function getSQLCipherVersion(db: Database): string | undefined {
export function batchMultiVarQuery<ValueT>(
db: Database,
values: Array<ValueT>,
query: (batch: Array<ValueT>) => void
values: ReadonlyArray<ValueT>,
query: (batch: ReadonlyArray<ValueT>) => void
): [];
export function batchMultiVarQuery<ValueT, ResultT>(
db: Database,
values: Array<ValueT>,
query: (batch: Array<ValueT>) => Array<ResultT>
values: ReadonlyArray<ValueT>,
query: (batch: ReadonlyArray<ValueT>) => Array<ResultT>
): Array<ResultT>;
export function batchMultiVarQuery<ValueT, ResultT>(
db: Database,
values: Array<ValueT>,
values: ReadonlyArray<ValueT>,
query:
| ((batch: Array<ValueT>) => void)
| ((batch: Array<ValueT>) => Array<ResultT>)
| ((batch: ReadonlyArray<ValueT>) => void)
| ((batch: ReadonlyArray<ValueT>) => Array<ResultT>)
): Array<ResultT> {
if (values.length > MAX_VARIABLE_COUNT) {
const result: Array<ResultT> = [];
@ -187,7 +187,7 @@ export function removeById<Key extends string | number>(
throw new Error('removeById: No ids to delete!');
}
const removeByIdsSync = (ids: Array<string | number>): void => {
const removeByIdsSync = (ids: ReadonlyArray<string | number>): void => {
db.prepare<ArrayQuery>(
`
DELETE FROM ${table}

View file

@ -170,7 +170,7 @@ export type ConversationType = {
about?: string;
aboutText?: string;
aboutEmoji?: string;
avatars?: Array<AvatarDataType>;
avatars?: ReadonlyArray<AvatarDataType>;
avatarPath?: string;
avatarHash?: string;
profileAvatarPath?: string;
@ -215,24 +215,24 @@ export type ConversationType = {
announcementsOnly?: boolean;
announcementsOnlyReady?: boolean;
expireTimer?: DurationInSeconds;
memberships?: Array<{
memberships?: ReadonlyArray<{
uuid: UUIDStringType;
isAdmin: boolean;
}>;
pendingMemberships?: Array<{
pendingMemberships?: ReadonlyArray<{
uuid: UUIDStringType;
addedByUserId?: UUIDStringType;
}>;
pendingApprovalMemberships?: Array<{
pendingApprovalMemberships?: ReadonlyArray<{
uuid: UUIDStringType;
}>;
bannedMemberships?: Array<UUIDStringType>;
bannedMemberships?: ReadonlyArray<UUIDStringType>;
muteExpiresAt?: number;
dontNotifyForMentionsIfMuted?: boolean;
isMe: boolean;
lastUpdated?: number;
// This is used by the CompositionInput for @mentions
sortedGroupMembers?: Array<ConversationType>;
sortedGroupMembers?: ReadonlyArray<ConversationType>;
title: string;
titleNoDefault?: string;
searchableTitle?: string;
@ -240,7 +240,7 @@ export type ConversationType = {
isSelected?: boolean;
isFetchingUUID?: boolean;
typingContactId?: string;
recentMediaItems?: Array<MediaItemType>;
recentMediaItems?: ReadonlyArray<MediaItemType>;
profileSharing?: boolean;
shouldShowDraft?: boolean;
@ -248,7 +248,7 @@ export type ConversationType = {
draftBodyRanges?: DraftBodyRangesType;
draftPreview?: string;
sharedGroupNames: Array<string>;
sharedGroupNames: ReadonlyArray<string>;
groupDescription?: string;
groupVersion?: 1 | 2;
groupId?: string;
@ -260,7 +260,7 @@ export type ConversationType = {
profileKey?: string;
voiceNotePlaybackRate?: number;
badges: Array<
badges: ReadonlyArray<
| {
id: string;
}
@ -312,7 +312,7 @@ export type MessageLookupType = {
export type ConversationMessageType = {
isNearBottom?: boolean;
messageChangeCounter: number;
messageIds: Array<string>;
messageIds: ReadonlyArray<string>;
messageLoadingState?: undefined | TimelineMessageLoadingState;
metrics: MessageMetricsType;
scrollToMessageId?: string;
@ -340,8 +340,8 @@ type ComposerGroupCreationState = {
groupExpireTimer: DurationInSeconds;
maximumGroupSizeModalState: OneTimeModalState;
recommendedGroupSizeModalState: OneTimeModalState;
selectedConversationIds: Array<string>;
userAvatarData: Array<AvatarDataType>;
selectedConversationIds: ReadonlyArray<string>;
userAvatarData: ReadonlyArray<AvatarDataType>;
};
type DistributionVerificationData = {
@ -397,7 +397,7 @@ type ContactSpoofingReviewStateType =
export type ConversationsStateType = {
preJoinConversation?: PreJoinConversationType;
invitedUuidsForNewlyCreatedGroup?: Array<string>;
invitedUuidsForNewlyCreatedGroup?: ReadonlyArray<string>;
conversationLookup: ConversationLookupType;
conversationsByE164: ConversationLookupType;
conversationsByUuid: ConversationLookupType;
@ -407,8 +407,9 @@ export type ConversationsStateType = {
selectedMessage: string | undefined;
selectedMessageCounter: number;
selectedMessageSource: SelectedMessageSource | undefined;
selectedConversationPanels: Array<PanelRenderType>;
selectedConversationPanels: ReadonlyArray<PanelRenderType>;
selectedMessageForDetails?: MessageAttributesType;
showArchived: boolean;
composer?: ComposerStateType;
contactSpoofingReview?: ContactSpoofingReviewStateType;
@ -600,7 +601,7 @@ type CreateGroupPendingActionType = {
type CreateGroupFulfilledActionType = {
type: 'CREATE_GROUP_FULFILLED';
payload: {
invitedUuids: Array<UUIDStringType>;
invitedUuids: ReadonlyArray<UUIDStringType>;
};
};
type CreateGroupRejectedActionType = {
@ -655,7 +656,7 @@ export type MessagesAddedActionType = {
isActive: boolean;
isJustSent: boolean;
isNewMessage: boolean;
messages: Array<MessageAttributesType>;
messages: ReadonlyArray<MessageAttributesType>;
};
};
@ -682,7 +683,7 @@ export type MessagesResetActionType = {
type: 'MESSAGES_RESET';
payload: {
conversationId: string;
messages: Array<MessageAttributesType>;
messages: ReadonlyArray<MessageAttributesType>;
metrics: MessageMetricsType;
scrollToMessageId?: string;
// The set of provided messages should be trusted, even if it conflicts with metrics,
@ -776,7 +777,7 @@ type SetRecentMediaItemsActionType = {
type: 'SET_RECENT_MEDIA_ITEMS';
payload: {
id: string;
recentMediaItems: Array<MediaItemType>;
recentMediaItems: ReadonlyArray<MediaItemType>;
};
};
type ToggleComposeEditingAvatarActionType = {
@ -812,7 +813,7 @@ type ReplaceAvatarsActionType = {
type: typeof REPLACE_AVATARS;
payload: {
conversationId: string;
avatars: Array<AvatarDataType>;
avatars: ReadonlyArray<AvatarDataType>;
};
};
@ -1230,7 +1231,7 @@ function filterAvatarData(
return avatars.filter(avatarData => !isSameAvatarData(data, avatarData));
}
function getNextAvatarId(avatars: Array<AvatarDataType>): number {
function getNextAvatarId(avatars: ReadonlyArray<AvatarDataType>): number {
return Math.max(...avatars.map(x => Number(x.id))) + 1;
}
@ -1238,10 +1239,10 @@ async function getAvatarsAndUpdateConversation(
conversations: ConversationsStateType,
conversationId: string,
getNextAvatarsData: (
avatars: Array<AvatarDataType>,
avatars: ReadonlyArray<AvatarDataType>,
nextId: number
) => Array<AvatarDataType>
): Promise<Array<AvatarDataType>> {
) => ReadonlyArray<AvatarDataType>
): Promise<ReadonlyArray<AvatarDataType>> {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
throw new Error('getAvatarsAndUpdateConversation: No conversation found');
@ -2415,7 +2416,7 @@ function messagesAdded({
isActive: boolean;
isJustSent: boolean;
isNewMessage: boolean;
messages: Array<MessageAttributesType>;
messages: ReadonlyArray<MessageAttributesType>;
}): MessagesAddedActionType {
return {
type: 'MESSAGES_ADDED',
@ -2469,7 +2470,7 @@ function reviewMessageRequestNameCollision(
export type MessageResetOptionsType = Readonly<{
conversationId: string;
messages: Array<MessageAttributesType>;
messages: ReadonlyArray<MessageAttributesType>;
metrics: MessageMetricsType;
scrollToMessageId?: string;
unboundedFetch?: boolean;
@ -2705,7 +2706,7 @@ function approvePendingMembershipFromGroupV2(
function revokePendingMembershipsFromGroupV2(
conversationId: string,
memberIds: Array<string>
memberIds: ReadonlyArray<string>
): ThunkAction<void, RootStateType, unknown, NoopActionType> {
return async dispatch => {
const conversation = window.ConversationController.get(conversationId);
@ -3340,7 +3341,7 @@ function leaveGroup(
}
function toggleGroupsForStorySend(
conversationIds: Array<string>
conversationIds: ReadonlyArray<string>
): ThunkAction<Promise<void>, RootStateType, unknown, NoopActionType> {
return async dispatch => {
await Promise.all(
@ -4018,7 +4019,7 @@ export function reducer(
...omit(state, 'contactSpoofingReview'),
selectedConversationId,
selectedConversationPanels: [],
messagesLookup: omit(state.messagesLookup, messageIds),
messagesLookup: omit(state.messagesLookup, [...messageIds]),
messagesByConversation: omit(state.messagesByConversation, [id]),
};
}
@ -4930,7 +4931,7 @@ export function reducer(
}
if (action.type === 'SHOW_CHOOSE_GROUP_MEMBERS') {
let selectedConversationIds: Array<string>;
let selectedConversationIds: ReadonlyArray<string>;
let recommendedGroupSizeModalState: OneTimeModalState;
let maximumGroupSizeModalState: OneTimeModalState;
let groupName: string;

View file

@ -37,7 +37,7 @@ export type ItemsStateType = {
readonly preferredLeftPaneWidth?: number;
readonly preferredReactionEmoji?: Array<string>;
readonly preferredReactionEmoji?: ReadonlyArray<string>;
readonly areWeASubscriber?: boolean;
};

View file

@ -32,7 +32,7 @@ export type LightboxStateType =
| {
isShowingLightbox: true;
isViewOnce: boolean;
media: Array<MediaItemType>;
media: ReadonlyArray<MediaItemType>;
selectedAttachmentPath: string | undefined;
};
@ -47,7 +47,7 @@ type ShowLightboxActionType = {
type: typeof SHOW_LIGHTBOX;
payload: {
isViewOnce: boolean;
media: Array<MediaItemType>;
media: ReadonlyArray<MediaItemType>;
selectedAttachmentPath: string | undefined;
};
};
@ -89,7 +89,7 @@ function closeLightbox(): ThunkAction<
function showLightboxWithMedia(
selectedAttachmentPath: string | undefined,
media: Array<MediaItemType>
media: ReadonlyArray<MediaItemType>
): ShowLightboxActionType {
return {
type: SHOW_LIGHTBOX,

View file

@ -279,7 +279,7 @@ export const _getLeftPaneLists = (
lookup: ConversationLookupType,
comparator: (left: ConversationType, right: ConversationType) => number,
selectedConversation?: string,
pinnedConversationIds?: Array<string>
pinnedConversationIds?: ReadonlyArray<string>
): {
conversations: Array<ConversationType>;
archivedConversations: Array<ConversationType>;
@ -603,7 +603,7 @@ export const getFilteredComposeContacts = createSelector(
getRegionCode,
(
searchTerm: string,
contacts: Array<ConversationType>,
contacts: ReadonlyArray<ConversationType>,
regionCode: string | undefined
): Array<ConversationType> => {
return filterAndSortConversationsByRecent(contacts, searchTerm, regionCode);
@ -616,7 +616,7 @@ export const getFilteredComposeGroups = createSelector(
getRegionCode,
(
searchTerm: string,
groups: Array<ConversationType>,
groups: ReadonlyArray<ConversationType>,
regionCode: string | undefined
): Array<ConversationType> => {
return filterAndSortConversationsByRecent(groups, searchTerm, regionCode);
@ -638,7 +638,7 @@ const getGroupCreationComposerState = createSelector(
groupName: string;
groupAvatar: undefined | Uint8Array;
groupExpireTimer: DurationInSeconds;
selectedConversationIds: Array<string>;
selectedConversationIds: ReadonlyArray<string>;
} => {
switch (composerState?.step) {
case ComposerStep.ChooseGroupMembers:

View file

@ -36,5 +36,6 @@ export const getSelectedIndex = createSelector(
export const getMedia = createSelector(
getLightboxState,
(state): Array<MediaItemType> => (state.isShowingLightbox ? state.media : [])
(state): ReadonlyArray<MediaItemType> =>
state.isShowingLightbox ? state.media : []
);

View file

@ -33,7 +33,7 @@ export function SmartLightbox(): JSX.Element | null {
const isShowingLightbox = useSelector<StateType, boolean>(shouldShowLightbox);
const isViewOnce = useSelector<StateType, boolean>(getIsViewOnce);
const media = useSelector<StateType, Array<MediaItemType>>(getMedia);
const media = useSelector<StateType, ReadonlyArray<MediaItemType>>(getMedia);
const selectedIndex = useSelector<StateType, number>(getSelectedIndex);
if (!isShowingLightbox) {

View file

@ -34,7 +34,7 @@ export const SmartReactionPicker = React.forwardRef<
const i18n = useSelector<StateType, LocalizerType>(getIntl);
const preferredReactionEmoji = useSelector<StateType, Array<string>>(
const preferredReactionEmoji = useSelector<StateType, ReadonlyArray<string>>(
getPreferredReactionEmoji
);

View file

@ -54,7 +54,7 @@ export function SmartStoryViewer(): JSX.Element | null {
const i18n = useSelector<StateType, LocalizerType>(getIntl);
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
const preferredReactionEmoji = useSelector<StateType, Array<string>>(
const preferredReactionEmoji = useSelector<StateType, ReadonlyArray<string>>(
getPreferredReactionEmoji
);

View file

@ -99,7 +99,7 @@ describe('MentionCompletion', () => {
describe('onTextChange', () => {
let possiblyShowMemberResultsStub: sinon.SinonStub<
[],
Array<ConversationType>
ReadonlyArray<ConversationType>
>;
beforeEach(() => {

View file

@ -97,11 +97,11 @@ export type GroupV2InfoType = {
groupChange?: Uint8Array;
masterKey: Uint8Array;
revision: number;
members: Array<string>;
members: ReadonlyArray<string>;
};
export type GroupV1InfoType = {
id: string;
members: Array<string>;
members: ReadonlyArray<string>;
};
type GroupCallUpdateType = {

View file

@ -726,14 +726,16 @@ export function hasFailed(attachment?: AttachmentType): boolean {
return Boolean(resolved && resolved.error);
}
export function hasVideoBlurHash(attachments?: Array<AttachmentType>): boolean {
export function hasVideoBlurHash(
attachments?: ReadonlyArray<AttachmentType>
): boolean {
const firstAttachment = attachments ? attachments[0] : null;
return Boolean(firstAttachment && firstAttachment.blurHash);
}
export function hasVideoScreenshot(
attachments?: Array<AttachmentType>
attachments?: ReadonlyArray<AttachmentType>
): string | null | undefined {
const firstAttachment = attachments ? attachments[0] : null;

View file

@ -118,7 +118,9 @@ const personalDefaultAvatars = PersonalAvatarIcons.map((icon, index) => ({
icon,
}));
export function getDefaultAvatars(isGroup?: boolean): Array<AvatarDataType> {
export function getDefaultAvatars(
isGroup?: boolean
): ReadonlyArray<AvatarDataType> {
if (isGroup) {
return groupDefaultAvatars;
}

22
ts/types/Storage.d.ts vendored
View file

@ -49,8 +49,8 @@ export type StorageAccessType = {
'audio-notification': boolean;
'auto-download-update': boolean;
'badge-count-muted-conversations': boolean;
'blocked-groups': Array<string>;
'blocked-uuids': Array<string>;
'blocked-groups': ReadonlyArray<string>;
'blocked-uuids': ReadonlyArray<string>;
'call-ringtone-notification': boolean;
'call-system-notification': boolean;
'hide-menu-bar': boolean;
@ -64,11 +64,11 @@ export type StorageAccessType = {
'theme-setting': ThemeSettingType;
attachmentMigration_isComplete: boolean;
attachmentMigration_lastProcessedIndex: number;
blocked: Array<string>;
blocked: ReadonlyArray<string>;
defaultConversationColor: DefaultConversationColorType;
customColors: CustomColorsItemType;
device_name: string;
existingOnboardingStoryMessageIds: Array<string> | undefined;
existingOnboardingStoryMessageIds: ReadonlyArray<string> | undefined;
hasRegisterSupportForUnauthenticatedDelivery: boolean;
hasSetMyStoriesPrivacy: boolean;
hasViewedOnboardingStory: boolean;
@ -98,12 +98,12 @@ export type StorageAccessType = {
version: string;
linkPreviews: boolean;
universalExpireTimer: number;
retryPlaceholders: Array<RetryItemType>;
retryPlaceholders: ReadonlyArray<RetryItemType>;
chromiumRegistrationDoneEver: '';
chromiumRegistrationDone: '';
phoneNumberSharingMode: PhoneNumberSharingMode;
phoneNumberDiscoverability: PhoneNumberDiscoverability;
pinnedConversationIds: Array<string>;
pinnedConversationIds: ReadonlyArray<string>;
preferContactAvatars: boolean;
primarySendsSms: boolean;
// Unlike `number_id` (which also includes device id) this field is only
@ -115,18 +115,18 @@ export type StorageAccessType = {
avatarUrl: string | undefined;
manifestVersion: number;
storageCredentials: StorageServiceCredentials;
'storage-service-error-records': Array<UnknownRecord>;
'storage-service-unknown-records': Array<UnknownRecord>;
'storage-service-pending-deletes': Array<ExtendedStorageID>;
'storage-service-error-records': ReadonlyArray<UnknownRecord>;
'storage-service-unknown-records': ReadonlyArray<UnknownRecord>;
'storage-service-pending-deletes': ReadonlyArray<ExtendedStorageID>;
'preferred-video-input-device': string;
'preferred-audio-input-device': AudioDevice;
'preferred-audio-output-device': AudioDevice;
previousAudioDeviceModule: AudioDeviceModule;
remoteConfig: RemoteConfigType;
unidentifiedDeliveryIndicators: boolean;
groupCredentials: Array<GroupCredentialType>;
groupCredentials: ReadonlyArray<GroupCredentialType>;
lastReceivedAtCounter: number;
preferredReactionEmoji: Array<string>;
preferredReactionEmoji: ReadonlyArray<string>;
skinTone: number;
unreadCount: number;
'challenge:conversations': ReadonlyArray<RegisteredChallengeType>;

View file

@ -8,7 +8,7 @@ import type { ConversationAttributesType } from '../model-types.d';
export function getAvatarData(
conversationAttrs: Pick<ConversationAttributesType, 'avatars' | 'type'>
): Array<AvatarDataType> {
): ReadonlyArray<AvatarDataType> {
const { avatars } = conversationAttrs;
if (avatars && avatars.length) {

View file

@ -11,9 +11,9 @@ import type { UUIDStringType } from '../types/UUID';
import { isConversationUnregistered } from './isConversationUnregistered';
export type GroupMemberships = {
memberships: Array<GroupV2Membership>;
pendingApprovalMemberships: Array<GroupV2RequestingMembership>;
pendingMemberships: Array<GroupV2PendingMembership>;
memberships: ReadonlyArray<GroupV2Membership>;
pendingApprovalMemberships: ReadonlyArray<GroupV2RequestingMembership>;
pendingMemberships: ReadonlyArray<GroupV2PendingMembership>;
};
export const getGroupMemberships = (
@ -30,7 +30,7 @@ export const getGroupMemberships = (
getConversationByUuid: (uuid: UUIDStringType) => undefined | ConversationType
): GroupMemberships => ({
memberships: memberships.reduce(
(result: Array<GroupV2Membership>, membership) => {
(result: ReadonlyArray<GroupV2Membership>, membership) => {
const member = getConversationByUuid(membership.uuid);
if (!member) {
return result;
@ -40,7 +40,7 @@ export const getGroupMemberships = (
[]
),
pendingApprovalMemberships: pendingApprovalMemberships.reduce(
(result: Array<GroupV2RequestingMembership>, membership) => {
(result: ReadonlyArray<GroupV2RequestingMembership>, membership) => {
const member = getConversationByUuid(membership.uuid);
if (!member || isConversationUnregistered(member)) {
return result;
@ -50,7 +50,7 @@ export const getGroupMemberships = (
[]
),
pendingMemberships: pendingMemberships.reduce(
(result: Array<GroupV2PendingMembership>, membership) => {
(result: ReadonlyArray<GroupV2PendingMembership>, membership) => {
const member = getConversationByUuid(membership.uuid);
if (!member || isConversationUnregistered(member)) {
return result;

View file

@ -18,7 +18,7 @@ export function getRecipients(
isStoryReply = false,
}: {
includePendingMembers?: boolean;
extraConversationsForSend?: Array<string>;
extraConversationsForSend?: ReadonlyArray<string>;
isStoryReply?: boolean;
} = {}
): Array<string> {

View file

@ -14,9 +14,9 @@ import { getRecipientsByConversation } from './getRecipientsByConversation';
export async function maybeForwardMessage(
messageAttributes: MessageAttributesType,
conversationIds: Array<string>,
conversationIds: ReadonlyArray<string>,
messageBody?: string,
attachments?: Array<AttachmentType>,
attachments?: ReadonlyArray<AttachmentType>,
linkPreview?: LinkPreviewType
): Promise<boolean> {
const idForLogging = getMessageIdForLogging(messageAttributes);

View file

@ -167,7 +167,7 @@ export async function sendContentMessageToGroup({
isPartialSend?: boolean;
messageId: string | undefined;
online?: boolean;
recipients: Array<string>;
recipients: ReadonlyArray<string>;
sendOptions?: SendOptionsType;
sendTarget: SenderKeyTargetType;
sendType: SendTypesType;
@ -254,7 +254,7 @@ export async function sendToGroupViaSenderKey(options: {
isPartialSend?: boolean;
messageId: string | undefined;
online?: boolean;
recipients: Array<string>;
recipients: ReadonlyArray<string>;
recursionCount: number;
sendOptions?: SendOptionsType;
sendTarget: SenderKeyTargetType;
@ -836,7 +836,7 @@ export function _shouldFailSend(error: unknown, logId: string): boolean {
return false;
}
function getRecipients(options: GroupSendOptionsType): Array<string> {
function getRecipients(options: GroupSendOptionsType): ReadonlyArray<string> {
if (options.groupV2) {
return options.groupV2.members;
}