diff --git a/_locales/en/messages.json b/_locales/en/messages.json index a51d923e26..58ae5e783c 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -2891,6 +2891,10 @@ "messageformat": "Read more", "description": "When a message is too long this is the affordance to expand the message" }, + "icu:MessageBody--message-too-long": { + "messageformat": "Message too long to display more", + "description": "When an incoming message is too long, and we refused to download it" + }, "icu:Message--unsupported-message": { "messageformat": "{contact} sent you a message that can't be processed or displayed because it uses a new Signal feature." }, diff --git a/stylesheets/components/MessageBody.scss b/stylesheets/components/MessageBody.scss index 2f26fb8baa..6028b8a3d8 100644 --- a/stylesheets/components/MessageBody.scss +++ b/stylesheets/components/MessageBody.scss @@ -19,6 +19,10 @@ } } + &__message-too-long { + font-weight: bold; + } + &__at-mention { border-radius: 4px; cursor: pointer; diff --git a/ts/RemoteConfig.ts b/ts/RemoteConfig.ts index 48e0c310d7..4f75b6f98c 100644 --- a/ts/RemoteConfig.ts +++ b/ts/RemoteConfig.ts @@ -35,7 +35,8 @@ export type ConfigKeyType = | 'global.groupsv2.groupSizeHardLimit' | 'global.groupsv2.maxGroupSize' | 'global.nicknames.max' - | 'global.nicknames.min'; + | 'global.nicknames.min' + | 'global.textAttachmentLimitBytes'; type ConfigValueType = { name: ConfigKeyType; diff --git a/ts/components/conversation/MessageBody.stories.tsx b/ts/components/conversation/MessageBody.stories.tsx index be793090d7..4cb30b80e0 100644 --- a/ts/components/conversation/MessageBody.stories.tsx +++ b/ts/components/conversation/MessageBody.stories.tsx @@ -118,6 +118,17 @@ export function TextPending(): JSX.Element { return ; } +export function MessageTooLong(): JSX.Element { + const props = createProps({ + text: 'Check out https://www.signal.org', + textAttachment: { + wasTooBig: true, + }, + }); + + return ; +} + export function Mention(): JSX.Element { const props = createProps({ bodyRanges: [ diff --git a/ts/components/conversation/MessageBody.tsx b/ts/components/conversation/MessageBody.tsx index 2389397391..2998c7e960 100644 --- a/ts/components/conversation/MessageBody.tsx +++ b/ts/components/conversation/MessageBody.tsx @@ -33,7 +33,10 @@ export type Props = { renderLocation: RenderLocation; showConversation?: ShowConversationType; text: string; - textAttachment?: Pick; + textAttachment?: Pick< + AttachmentType, + 'pending' | 'digest' | 'key' | 'wasTooBig' + >; }; /** @@ -59,19 +62,36 @@ export function MessageBody({ text, textAttachment, }: Props): JSX.Element { - const hasReadMore = Boolean(onIncreaseTextLength); - const shouldDisableLinks = disableLinks || !shouldLinkifyMessage(text); const textWithSuffix = - textAttachment?.pending || hasReadMore ? `${text}...` : text; + textAttachment?.pending || onIncreaseTextLength || textAttachment?.wasTooBig + ? `${text}...` + : text; const sizeClass = disableJumbomoji ? undefined : getSizeClass(text); - let pendingContent: React.ReactNode; - if (hasReadMore) { - pendingContent = null; + let endNotification: React.ReactNode; + if (onIncreaseTextLength) { + endNotification = ( + + ); } else if (textAttachment?.pending) { - pendingContent = ( + endNotification = ( {i18n('icu:downloading')} ); } else if ( @@ -79,7 +99,7 @@ export function MessageBody({ canBeDownloaded(textAttachment) && kickOffBodyDownload ) { - pendingContent = ( + endNotification = ( {' '} - ) : null} + {endNotification} ); } diff --git a/ts/messageModifiers/AttachmentDownloads.ts b/ts/messageModifiers/AttachmentDownloads.ts index 22ea453b0f..cc45248aa9 100644 --- a/ts/messageModifiers/AttachmentDownloads.ts +++ b/ts/messageModifiers/AttachmentDownloads.ts @@ -29,6 +29,7 @@ import * as log from '../logging/log'; import { KIBIBYTE, getMaximumIncomingAttachmentSizeInKb, + getMaximumIncomingTextAttachmentSizeInKb, } from '../types/AttachmentSize'; const { @@ -281,14 +282,23 @@ async function _runJob(job?: AttachmentDownloadJobType): Promise { let downloaded: AttachmentType | null = null; try { - const { size } = attachment; const maxInKib = getMaximumIncomingAttachmentSizeInKb(getValue); + const maxTextAttachmentSizeInKib = + getMaximumIncomingTextAttachmentSizeInKb(getValue); + + const { size } = attachment; const sizeInKib = size / KIBIBYTE; + if (!size || sizeInKib > maxInKib) { throw new AttachmentSizeError( `Attachment Job ${id}: Attachment was ${sizeInKib}kib, max is ${maxInKib}kib` ); } + if (type === 'long-message' && sizeInKib > maxTextAttachmentSizeInKib) { + throw new AttachmentSizeError( + `Attachment Job ${id}: Text attachment was ${sizeInKib}kib, max is ${maxTextAttachmentSizeInKib}kib` + ); + } await _addAttachmentToMessage( message, diff --git a/ts/types/AttachmentSize.ts b/ts/types/AttachmentSize.ts index 23ea7b245a..e599919bc6 100644 --- a/ts/types/AttachmentSize.ts +++ b/ts/types/AttachmentSize.ts @@ -43,6 +43,22 @@ export const getMaximumIncomingAttachmentSizeInKb = ( } }; +export const getMaximumIncomingTextAttachmentSizeInKb = ( + getValue: typeof RemoteConfig.getValue +): number => { + try { + return ( + parseIntOrThrow( + getValue('global.textAttachmentLimitBytes'), + 'getMaximumIncomingTextAttachmentSizeInKb' + ) / KIBIBYTE + ); + } catch (_error) { + // TODO: DESKTOP-6314. We're not gonna log until the new flag is fully deployed + return KIBIBYTE * 5; + } +}; + export function getRenderDetailsForLimit(limitKb: number): { limit: number; units: string;