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 = (
{' '}
);
+ } else if (textAttachment?.wasTooBig) {
+ endNotification = (
+
+ {' '}
+ {i18n('icu:MessageBody--message-too-long')}
+
+ );
}
-
return (
{author && (
@@ -135,25 +161,7 @@ export function MessageBody({
textLength={text.length}
/>
- {pendingContent}
- {onIncreaseTextLength ? (
-
- ) : 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;