mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-05-08 08:58:38 +01:00
Make most message attribute uses readonly
Co-authored-by: Jamie Kyle <jamie@signal.org>
This commit is contained in:
@@ -4,7 +4,10 @@
|
||||
import type { SafetyNumberChangeSource } from '../components/SafetyNumberChangeDialog';
|
||||
import * as log from '../logging/log';
|
||||
import { explodePromise } from './explodePromise';
|
||||
import type { RecipientsByConversation } from '../state/ducks/stories';
|
||||
import type {
|
||||
RecipientsByConversation,
|
||||
RecipientEntry,
|
||||
} from '../state/ducks/stories';
|
||||
import { isNotNil } from './isNotNil';
|
||||
import type { ServiceIdString } from '../types/ServiceId';
|
||||
import { waitForAll } from './waitForAll';
|
||||
@@ -105,7 +108,7 @@ export function filterServiceIds(
|
||||
byConversation: RecipientsByConversation,
|
||||
predicate: (serviceId: ServiceIdString) => boolean
|
||||
): RecipientsByConversation {
|
||||
const filteredByConversation: RecipientsByConversation = {};
|
||||
const filteredByConversation: Record<string, RecipientEntry> = {};
|
||||
Object.entries(byConversation).forEach(
|
||||
([conversationId, conversationData]) => {
|
||||
const conversationFiltered = conversationData.serviceIds
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
import { DAY } from './durations';
|
||||
import { isMoreRecentThan } from './timestamp';
|
||||
import { isOutgoing } from '../messages/helpers';
|
||||
|
||||
export const MESSAGE_MAX_EDIT_COUNT = 10;
|
||||
|
||||
export function canEditMessage(message: MessageAttributesType): boolean {
|
||||
export function canEditMessage(
|
||||
message: ReadonlyMessageAttributesType
|
||||
): boolean {
|
||||
const result =
|
||||
!message.deletedForEveryone &&
|
||||
isOutgoing(message) &&
|
||||
@@ -29,6 +31,8 @@ export function canEditMessage(message: MessageAttributesType): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isWithinMaxEdits(message: MessageAttributesType): boolean {
|
||||
export function isWithinMaxEdits(
|
||||
message: ReadonlyMessageAttributesType
|
||||
): boolean {
|
||||
return (message.editHistory?.length ?? 0) <= MESSAGE_MAX_EDIT_COUNT;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import type { WritableDeep } from 'type-fest';
|
||||
|
||||
/**
|
||||
* Takes a readonly object and returns a writable deep clone of it.
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
|
||||
*/
|
||||
export function deepClone<T>(value: T): WritableDeep<T> {
|
||||
return structuredClone(value) as WritableDeep<T>;
|
||||
}
|
||||
@@ -2,10 +2,15 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { isNumber, sortBy } from 'lodash';
|
||||
import type { ReadonlyDeep } from 'type-fest';
|
||||
|
||||
import { strictAssert } from './assert';
|
||||
|
||||
import type { EditHistoryType, MessageAttributesType } from '../model-types';
|
||||
import type {
|
||||
EditHistoryType,
|
||||
MessageAttributesType,
|
||||
ReadonlyMessageAttributesType,
|
||||
} from '../model-types';
|
||||
import type { LoggerType } from '../types/Logging';
|
||||
|
||||
// The tricky bit for this function is if we are on our second+ attempt to send a given
|
||||
@@ -14,7 +19,7 @@ export function getTargetOfThisEditTimestamp({
|
||||
message,
|
||||
targetTimestamp,
|
||||
}: {
|
||||
message: MessageAttributesType;
|
||||
message: ReadonlyMessageAttributesType;
|
||||
targetTimestamp: number;
|
||||
}): number {
|
||||
const { timestamp: originalTimestamp, editHistory } = message;
|
||||
@@ -27,7 +32,7 @@ export function getTargetOfThisEditTimestamp({
|
||||
});
|
||||
const mostRecent = sortBy(
|
||||
sentItems,
|
||||
(item: EditHistoryType) => item.timestamp
|
||||
(item: ReadonlyDeep<EditHistoryType>) => item.timestamp
|
||||
);
|
||||
|
||||
const { length } = mostRecent;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
import type { MessageModel } from '../models/messages';
|
||||
import type { SignalService as Proto } from '../protobuf';
|
||||
import type { AciString } from '../types/ServiceId';
|
||||
@@ -72,12 +72,12 @@ export async function findStoryMessages(
|
||||
}
|
||||
|
||||
function isStoryAMatch(
|
||||
message: MessageAttributesType | null | undefined,
|
||||
message: ReadonlyMessageAttributesType | null | undefined,
|
||||
conversationId: string,
|
||||
ourConversationId: string,
|
||||
authorAci: AciString,
|
||||
sentTimestamp: number
|
||||
): message is MessageAttributesType {
|
||||
): message is ReadonlyMessageAttributesType {
|
||||
if (!message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
import type {
|
||||
ConversationAttributesType,
|
||||
MessageAttributesType,
|
||||
ReadonlyMessageAttributesType,
|
||||
} from '../model-types.d';
|
||||
import { isIncoming, isOutgoing } from '../state/selectors/message';
|
||||
import { getTitle } from './getTitle';
|
||||
|
||||
function getIncomingContact(
|
||||
messageAttributes: MessageAttributesType
|
||||
messageAttributes: ReadonlyMessageAttributesType
|
||||
): ConversationAttributesType | undefined {
|
||||
if (!isIncoming(messageAttributes)) {
|
||||
return undefined;
|
||||
@@ -24,7 +24,7 @@ function getIncomingContact(
|
||||
}
|
||||
|
||||
export function getMessageAuthorText(
|
||||
messageAttributes?: MessageAttributesType
|
||||
messageAttributes?: ReadonlyMessageAttributesType
|
||||
): string | undefined {
|
||||
if (!messageAttributes) {
|
||||
return undefined;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
import type { ConversationModel } from '../models/conversations';
|
||||
|
||||
export function getMessageConversation({
|
||||
conversationId,
|
||||
}: Pick<MessageAttributesType, 'conversationId'>):
|
||||
}: Pick<ReadonlyMessageAttributesType, 'conversationId'>):
|
||||
| ConversationModel
|
||||
| undefined {
|
||||
return window.ConversationController.get(conversationId);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
import type { LoggerType } from '../types/Logging';
|
||||
import { assertDev } from './assert';
|
||||
|
||||
@@ -16,7 +16,7 @@ export function getMessageSentTimestamp(
|
||||
sent_at: sentAt,
|
||||
timestamp,
|
||||
}: Pick<
|
||||
MessageAttributesType,
|
||||
ReadonlyMessageAttributesType,
|
||||
'editMessageTimestamp' | 'sent_at' | 'timestamp'
|
||||
>,
|
||||
{ includeEdits = true, log }: GetMessageSentTimestampOptionsType
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
|
||||
export function getMessageSentTimestampSet({
|
||||
sent_at: sentAt,
|
||||
editHistory,
|
||||
}: Pick<
|
||||
MessageAttributesType,
|
||||
ReadonlyMessageAttributesType,
|
||||
'sent_at' | 'editHistory'
|
||||
>): ReadonlySet<number> {
|
||||
return new Set([
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
|
||||
export function getMessageTimestamp(
|
||||
message: Pick<MessageAttributesType, 'received_at' | 'received_at_ms'>
|
||||
message: Pick<ReadonlyMessageAttributesType, 'received_at' | 'received_at_ms'>
|
||||
): number {
|
||||
return message.received_at_ms || message.received_at;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ReadonlyDeep } from 'type-fest';
|
||||
|
||||
import type { RawBodyRange } from '../types/BodyRange';
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
import type { ICUStringMessageParamsByKeyType } from '../types/Util';
|
||||
import * as Attachment from '../types/Attachment';
|
||||
import * as EmbeddedContact from '../types/EmbeddedContact';
|
||||
@@ -64,9 +66,9 @@ function getNameForNumber(e164: string): string {
|
||||
}
|
||||
|
||||
export function getNotificationDataForMessage(
|
||||
attributes: MessageAttributesType
|
||||
attributes: ReadonlyMessageAttributesType
|
||||
): {
|
||||
bodyRanges?: ReadonlyArray<RawBodyRange>;
|
||||
bodyRanges?: ReadonlyArray<ReadonlyDeep<RawBodyRange>>;
|
||||
emoji?: string;
|
||||
text: string;
|
||||
} {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
import { applyRangesToText, hydrateRanges } from '../types/BodyRange';
|
||||
import { findAndFormatContact } from './findAndFormatContact';
|
||||
import { getNotificationDataForMessage } from './getNotificationDataForMessage';
|
||||
@@ -9,7 +9,7 @@ import { isConversationAccepted } from './isConversationAccepted';
|
||||
import { strictAssert } from './assert';
|
||||
|
||||
export function getNotificationTextForMessage(
|
||||
attributes: MessageAttributesType
|
||||
attributes: ReadonlyMessageAttributesType
|
||||
): string {
|
||||
const { text, emoji, bodyRanges } = getNotificationDataForMessage(attributes);
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
import * as EmbeddedContact from '../types/EmbeddedContact';
|
||||
|
||||
export function getQuoteBodyText(
|
||||
messageAttributes: MessageAttributesType,
|
||||
messageAttributes: ReadonlyMessageAttributesType,
|
||||
id: number
|
||||
): string | undefined {
|
||||
const storyReactionEmoji = messageAttributes.storyReaction?.emoji;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import type { ConversationAttributesType } from '../model-types';
|
||||
import type { RecipientsByConversation } from '../state/ducks/stories';
|
||||
import type { ServiceIdString } from '../types/ServiceId';
|
||||
|
||||
import { getConversationMembers } from './getConversationMembers';
|
||||
import { isNotNil } from './isNotNil';
|
||||
@@ -10,7 +11,12 @@ import { isNotNil } from './isNotNil';
|
||||
export function getRecipientsByConversation(
|
||||
conversations: Array<ConversationAttributesType>
|
||||
): RecipientsByConversation {
|
||||
const recipientsByConversation: RecipientsByConversation = {};
|
||||
const recipientsByConversation: Record<
|
||||
string,
|
||||
{
|
||||
serviceIds: Array<ServiceIdString>;
|
||||
}
|
||||
> = {};
|
||||
|
||||
conversations.forEach(attributes => {
|
||||
recipientsByConversation[attributes.id] = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
|
||||
export function getSenderIdentifier({
|
||||
sent_at: sentAt,
|
||||
@@ -9,7 +9,7 @@ export function getSenderIdentifier({
|
||||
sourceServiceId,
|
||||
sourceDevice,
|
||||
}: Pick<
|
||||
MessageAttributesType,
|
||||
ReadonlyMessageAttributesType,
|
||||
'sent_at' | 'source' | 'sourceServiceId' | 'sourceDevice'
|
||||
>): string {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { partition } from 'lodash';
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
import { isLongMessage } from '../types/MIME';
|
||||
|
||||
// NOTE: If you're modifying this function then you'll likely also need
|
||||
// to modify ./queueAttachmentDownloads
|
||||
export function hasAttachmentDownloads(
|
||||
message: MessageAttributesType
|
||||
message: ReadonlyMessageAttributesType
|
||||
): boolean {
|
||||
const attachments = message.attachments || [];
|
||||
|
||||
@@ -83,7 +83,7 @@ export function hasAttachmentDownloads(
|
||||
}
|
||||
|
||||
function hasPreviewDownloads(
|
||||
previews: MessageAttributesType['preview']
|
||||
previews: ReadonlyMessageAttributesType['preview']
|
||||
): boolean {
|
||||
return (previews || []).some(item => {
|
||||
if (!item.image) {
|
||||
@@ -98,7 +98,7 @@ function hasPreviewDownloads(
|
||||
}
|
||||
|
||||
function hasNormalAttachmentDownloads(
|
||||
attachments: MessageAttributesType['attachments']
|
||||
attachments: ReadonlyMessageAttributesType['attachments']
|
||||
): boolean {
|
||||
return (attachments || []).some(attachment => {
|
||||
if (!attachment) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import type {
|
||||
ConversationAttributesType,
|
||||
MessageAttributesType,
|
||||
ReadonlyMessageAttributesType,
|
||||
} from '../model-types.d';
|
||||
import {
|
||||
getSource,
|
||||
@@ -16,7 +16,7 @@ import type { ConversationType } from '../state/ducks/conversations';
|
||||
|
||||
export function getMessageIdForLogging(
|
||||
message: Pick<
|
||||
MessageAttributesType,
|
||||
ReadonlyMessageAttributesType,
|
||||
'type' | 'sourceServiceId' | 'sourceDevice' | 'sent_at'
|
||||
>
|
||||
): string {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { ReadStatus } from '../messages/MessageReadStatus';
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
|
||||
export const isMessageUnread = (
|
||||
message: Readonly<Pick<MessageAttributesType, 'readStatus'>>
|
||||
message: Pick<ReadonlyMessageAttributesType, 'readStatus'>
|
||||
): boolean => message.readStatus === ReadStatus.Unread;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
import { DAY } from './durations';
|
||||
|
||||
export function isTooOldToModifyMessage(
|
||||
serverTimestamp: number,
|
||||
message: MessageAttributesType
|
||||
message: Pick<ReadonlyMessageAttributesType, 'serverTimestamp' | 'sent_at'>
|
||||
): boolean {
|
||||
const messageTimestamp = message.serverTimestamp || message.sent_at || 0;
|
||||
const delta = Math.abs(serverTimestamp - messageTimestamp);
|
||||
|
||||
+25
-20
@@ -1,17 +1,17 @@
|
||||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
import { createBatcher } from './batcher';
|
||||
import { createWaitBatcher } from './waitBatcher';
|
||||
import { DataWriter } from '../sql/Client';
|
||||
import * as log from '../logging/log';
|
||||
|
||||
const updateMessageBatcher = createBatcher<MessageAttributesType>({
|
||||
const updateMessageBatcher = createBatcher<ReadonlyMessageAttributesType>({
|
||||
name: 'messageBatcher.updateMessageBatcher',
|
||||
wait: 75,
|
||||
maxSize: 50,
|
||||
processBatch: async (messageAttrs: Array<MessageAttributesType>) => {
|
||||
processBatch: async (messageAttrs: Array<ReadonlyMessageAttributesType>) => {
|
||||
log.info('updateMessageBatcher', messageAttrs.length);
|
||||
|
||||
// Grab the latest from the cache in case they've changed
|
||||
@@ -27,7 +27,9 @@ const updateMessageBatcher = createBatcher<MessageAttributesType>({
|
||||
|
||||
let shouldBatch = true;
|
||||
|
||||
export function queueUpdateMessage(messageAttr: MessageAttributesType): void {
|
||||
export function queueUpdateMessage(
|
||||
messageAttr: ReadonlyMessageAttributesType
|
||||
): void {
|
||||
if (shouldBatch) {
|
||||
updateMessageBatcher.add(messageAttr);
|
||||
} else {
|
||||
@@ -41,21 +43,24 @@ export function setBatchingStrategy(keepBatching = false): void {
|
||||
shouldBatch = keepBatching;
|
||||
}
|
||||
|
||||
export const saveNewMessageBatcher = createWaitBatcher<MessageAttributesType>({
|
||||
name: 'messageBatcher.saveNewMessageBatcher',
|
||||
wait: 75,
|
||||
maxSize: 30,
|
||||
processBatch: async (messageAttrs: Array<MessageAttributesType>) => {
|
||||
log.info('saveNewMessageBatcher', messageAttrs.length);
|
||||
export const saveNewMessageBatcher =
|
||||
createWaitBatcher<ReadonlyMessageAttributesType>({
|
||||
name: 'messageBatcher.saveNewMessageBatcher',
|
||||
wait: 75,
|
||||
maxSize: 30,
|
||||
processBatch: async (
|
||||
messageAttrs: Array<ReadonlyMessageAttributesType>
|
||||
) => {
|
||||
log.info('saveNewMessageBatcher', messageAttrs.length);
|
||||
|
||||
// Grab the latest from the cache in case they've changed
|
||||
const messagesToSave = messageAttrs.map(
|
||||
message => window.MessageCache.accessAttributes(message.id) ?? message
|
||||
);
|
||||
// Grab the latest from the cache in case they've changed
|
||||
const messagesToSave = messageAttrs.map(
|
||||
message => window.MessageCache.accessAttributes(message.id) ?? message
|
||||
);
|
||||
|
||||
await DataWriter.saveMessages(messagesToSave, {
|
||||
forceSave: true,
|
||||
ourAci: window.textsecure.storage.user.getCheckedAci(),
|
||||
});
|
||||
},
|
||||
});
|
||||
await DataWriter.saveMessages(messagesToSave, {
|
||||
forceSave: true,
|
||||
ourAci: window.textsecure.storage.user.getCheckedAci(),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ConversationModel } from '../models/conversations';
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import type { ReadonlyMessageAttributesType } from '../model-types.d';
|
||||
import * as log from '../logging/log';
|
||||
import { DataReader } from '../sql/Client';
|
||||
import { isGroup } from './whatTypeOfConversation';
|
||||
import { isMessageUnread } from './isMessageUnread';
|
||||
|
||||
export async function shouldReplyNotifyUser(
|
||||
messageAttributes: Readonly<
|
||||
Pick<MessageAttributesType, 'readStatus' | 'storyId'>
|
||||
messageAttributes: Pick<
|
||||
ReadonlyMessageAttributesType,
|
||||
'readStatus' | 'storyId'
|
||||
>,
|
||||
conversation: ConversationModel
|
||||
): Promise<boolean> {
|
||||
|
||||
Reference in New Issue
Block a user