Skip to content

Commit b85eb41

Browse files
Merge branch 'RocketChat:develop' into develop
2 parents ed3ae7d + 047660f commit b85eb41

File tree

15 files changed

+18764
-13991
lines changed

15 files changed

+18764
-13991
lines changed

.github/workflows/prettier.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,5 @@ jobs:
4646
git config user.name "${{ github.actor }}"
4747
git config user.email "${{ github.actor }}@users.noreply.github.com"
4848
git add .
49-
git commit -m "chore: format code and fix lint issues [skip ci]"
49+
git commit -m "chore: format code and fix lint issues"
5050
git push origin ${{ github.ref_name }}

android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ android {
9090
minSdkVersion rootProject.ext.minSdkVersion
9191
targetSdkVersion rootProject.ext.targetSdkVersion
9292
versionCode VERSIONCODE as Integer
93-
versionName "4.69.0"
93+
versionName "4.70.0"
9494
vectorDrawables.useSupportLibrary = true
9595
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
9696
resValue "string", "rn_config_reader_custom_package", "chat.rocket.reactnative"

android/app/src/main/java/chat/rocket/reactnative/notification/NotificationIntentHandler.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,20 @@ class NotificationIntentHandler {
9393
}
9494

9595
try {
96+
val notId = extras.getString("notId")
97+
98+
// Clear the notification messages from the static map to prevent stacking
99+
if (!notId.isNullOrEmpty()) {
100+
try {
101+
val notIdInt = notId.toIntOrNull()
102+
if (notIdInt != null) {
103+
CustomPushNotification.clearMessages(notIdInt)
104+
}
105+
} catch (e: Exception) {
106+
Log.e(TAG, "Error clearing notification messages for ID $notId: ${e.message}", e)
107+
}
108+
}
109+
96110
// Extract all notification data from Intent extras
97111
// Only include serializable types to avoid JSON serialization errors
98112
val notificationData = mutableMapOf<String, Any?>()

app/containers/markdown/components/Katex.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,34 @@ interface IKaTeXProps {
1313
value: KaTeXProps['value'];
1414
}
1515

16+
const BLOCK_ENV_PATTERN = /\\begin\s*\{\s*(array|matrix|pmatrix|bmatrix|Bmatrix|vmatrix|Vmatrix)\s*\}/;
17+
1618
export const KaTeX = ({ value }: IKaTeXProps): React.ReactElement | null => {
1719
const { colors } = useTheme();
1820
const fixAndroidWebviewCrashStyle: StyleProp<ViewStyle> = isAndroid ? { opacity: 0.99, overflow: 'hidden' } : {};
21+
// KaTeX array does not render correctly in MathView (shows gray box).
22+
// MathView does not throw, so renderError is never triggered.
23+
if (BLOCK_ENV_PATTERN.test(value)) {
24+
return (
25+
<Katex
26+
expression={value}
27+
displayMode={true}
28+
style={[{ flex: 1, height: DEFAULT_MESSAGE_HEIGHT }, fixAndroidWebviewCrashStyle]}
29+
/>
30+
);
31+
}
32+
1933
return (
2034
<MathView
2135
math={value}
36+
config={{ inline: false }}
2237
style={{ color: colors.fontDefault }}
2338
renderError={() => (
24-
<Katex expression={value} style={[{ flex: 1, height: DEFAULT_MESSAGE_HEIGHT }, fixAndroidWebviewCrashStyle]} />
39+
<Katex
40+
expression={value}
41+
displayMode={true}
42+
style={[{ flex: 1, height: DEFAULT_MESSAGE_HEIGHT }, fixAndroidWebviewCrashStyle]}
43+
/>
2544
)}
2645
/>
2746
);

app/containers/message/Components/Attachments/Attachments.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@ import Audio from './Audio';
77
import Video from './Video';
88
import CollapsibleQuote from './CollapsibleQuote';
99
import AttachedActions from './AttachedActions';
10+
import Reply from './Reply';
1011
import MessageContext from '../../Context';
1112
import { type IMessageAttachments } from '../../interfaces';
1213
import { type IAttachment } from '../../../../definitions';
1314
import { getMessageFromAttachment } from '../../utils';
1415

1516
const removeQuote = (file?: IAttachment) =>
16-
file?.image_url || file?.audio_url || file?.video_url || (file?.actions?.length || 0) > 0 || file?.collapsed;
17+
file?.image_url ||
18+
file?.audio_url ||
19+
file?.video_url ||
20+
file?.collapsed ||
21+
(file?.actions?.length || 0) > 0 ||
22+
(file?.attachments?.length || 0) > 0;
1723

1824
const Attachments: React.FC<IMessageAttachments> = React.memo(
1925
({ attachments, timeFormat, showAttachment, getCustomEmoji, author }: IMessageAttachments) => {
@@ -68,6 +74,19 @@ const Attachments: React.FC<IMessageAttachments> = React.memo(
6874
return <CollapsibleQuote key={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} />;
6975
}
7076

77+
if (file.attachments?.length) {
78+
return (
79+
<Reply
80+
key={index}
81+
attachment={file}
82+
timeFormat={timeFormat}
83+
getCustomEmoji={getCustomEmoji}
84+
showAttachment={showAttachment}
85+
msg={msg}
86+
/>
87+
);
88+
}
89+
7190
return null;
7291
});
7392
return <View style={{ gap: 4 }}>{attachmentsElements}</View>;

app/containers/message/Components/Attachments/Quote.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ import { getMessageFromAttachment } from '../../utils';
1111
const isQuoteAttachment = (file?: IAttachment): boolean => {
1212
if (!file) return false;
1313

14+
if (file.collapsed) return false;
15+
16+
// Attachments with nested attachments (e.g. message link + quoted image) are rendered only by Attachments as Reply
17+
if (file.attachments?.length) {
18+
return false;
19+
}
20+
1421
if (!file.color && !file.text && (file.image_url || file.audio_url || file.video_url || file.collapsed)) {
1522
return false;
1623
}

app/containers/message/Message.stories.tsx

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,6 +1245,53 @@ export const MessageWithReplyLargeFont = () => (
12451245
</>
12461246
);
12471247

1248+
export const MessageWithNestedReplyAndFile = () => (
1249+
<>
1250+
<Message
1251+
msg='Forwarded message with file inside'
1252+
attachments={[
1253+
{
1254+
author_name: 'rocket.cat',
1255+
message_link: 'https://open.rocket.chat/group/msg-id',
1256+
ts: date,
1257+
timeFormat: 'LT',
1258+
text: '',
1259+
attachments: [
1260+
{
1261+
author_name: 'user',
1262+
ts: date,
1263+
timeFormat: 'LT',
1264+
type: 'file',
1265+
title: 'document.pdf',
1266+
title_link: '/file-upload/abc/document.pdf'
1267+
}
1268+
]
1269+
}
1270+
]}
1271+
/>
1272+
<Message
1273+
msg='Forwarded message with nested image'
1274+
attachments={[
1275+
{
1276+
author_name: 'rocket.cat',
1277+
ts: date,
1278+
timeFormat: 'LT',
1279+
text: '',
1280+
attachments: [
1281+
{
1282+
author_name: 'user',
1283+
ts: date,
1284+
timeFormat: 'LT',
1285+
description: 'Nested image from forwarded message',
1286+
image_url: 'https://octodex.github.com/images/yaktocat.png'
1287+
}
1288+
]
1289+
}
1290+
]}
1291+
/>
1292+
</>
1293+
);
1294+
12481295
export const MessageWithReplyAndFileLargeFont = () => (
12491296
<>
12501297
<MessageLargeFont
@@ -2191,6 +2238,20 @@ const collapsedAttachments = {
21912238
}
21922239
]
21932240
};
2241+
2242+
const collapsibleAttachmentWithText = {
2243+
collapsed: true,
2244+
title: 'Collapsed attachment block',
2245+
text: 'This attachment text should NOT appear as plain text above the message or duplicate before the block.',
2246+
description: 'Attachment description that might also leak as duplicate plain text.',
2247+
color: '#2c3e50',
2248+
fields: [
2249+
{ title: 'Field 1', value: 'Value 1', short: true },
2250+
{ title: 'Field 2', value: 'Value 2', short: true },
2251+
{ title: 'Long field', value: 'This field value could also contribute to duplicate text when expanded.', short: false }
2252+
]
2253+
};
2254+
21942255
export const CollapsedAttachments = () => (
21952256
<>
21962257
<Message msg='Message' attachments={[collapsedAttachments]} />
@@ -2207,6 +2268,23 @@ export const CollapsedAttachmentsLargeFont = () => (
22072268
</>
22082269
);
22092270

2271+
export const CollapsibleAttachmentWithText = () => (
2272+
<>
2273+
<Message msg='This is the main message body.' attachments={[collapsibleAttachmentWithText]} />
2274+
<Message msg='This is the main message body.' attachments={[{ ...collapsibleAttachmentWithText, collapsed: false }]} />
2275+
</>
2276+
);
2277+
2278+
export const CollapsibleAttachmentWithTextLargeFont = () => (
2279+
<>
2280+
<MessageLargeFont msg='This is the main message body.' attachments={[collapsibleAttachmentWithText]} />
2281+
<MessageLargeFont
2282+
msg='This is the main message body.'
2283+
attachments={[{ ...collapsibleAttachmentWithText, collapsed: false }]}
2284+
/>
2285+
</>
2286+
);
2287+
22102288
const attachmentWithTextAndLink = [
22112289
{
22122290
title: 'Rocket.Chat',
@@ -2238,6 +2316,25 @@ export const Katex = () => (
22382316
);
22392317
export const KatexLargeFont = () => <MessageLargeFont {...katex} />;
22402318

2319+
const katexArray = {
2320+
msg: '\\begin{array}{|c|c|c|c|c|c|} \\hline \\text{...} & \\text{...} \\\\ \\hline \\end{array}',
2321+
md: [
2322+
{
2323+
type: 'KATEX',
2324+
value:
2325+
' \\begin{array}{|c|c|c|c|c|c|} \\hline \\text{testing} & \\text{testingII} & \\text{testing III} & \\text{testing IV} & \\text{testV} & \\text{testVI} \\\\ \\hline \\text{TEST} & \\text{TEST} & \\text{TEST} & \\text{TEST} & \\text{TEST} & \\text{TEST} \\\\ \\hline\\text{TEST} & \\text{✅} & \\text{test} & \\text{test} & \\text{test} & \\text{✅} \\\\ \\hline \\end{array} '
2326+
}
2327+
]
2328+
};
2329+
2330+
export const KatexArray = () => (
2331+
<>
2332+
<Message {...katexArray} />
2333+
</>
2334+
);
2335+
2336+
export const KatexArrayLargeFont = () => <MessageLargeFont {...katexArray} />;
2337+
22412338
const inlineKatex = {
22422339
msg: '\\(xˆ2 + yˆ2 - zˆ2\\)',
22432340
md: [

0 commit comments

Comments
 (0)