From 87e91cd6a522363cedcf9aa684c6abdafd52bcf1 Mon Sep 17 00:00:00 2001 From: mavrickdeveloper Date: Wed, 8 Apr 2026 20:10:16 +0100 Subject: [PATCH 1/2] Fix react-native-pdf Android event coalescing --- patches/react-native-pdf/details.md | 19 +++++++++++++++++-- ...disable-topchange-coalescing-android.patch | 16 ++++++++++++++++ src/components/PDFThumbnail/index.native.tsx | 19 ++++++++++++------- 3 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 patches/react-native-pdf/react-native-pdf+7.0.2+003+disable-topchange-coalescing-android.patch diff --git a/patches/react-native-pdf/details.md b/patches/react-native-pdf/details.md index d5404acc40483..de7b3b1583223 100644 --- a/patches/react-native-pdf/details.md +++ b/patches/react-native-pdf/details.md @@ -3,7 +3,7 @@ ### [react-native-pdf+7.0.2+001+fix-onPressLink-android.patch](react-native-pdf+7.0.2+001+fix-onPressLink-android.patch) - Reason: - + ``` This patch fixes the onPressLink callback not working on Android by upgrading the pdfiumandroid library from version 1.0.32 to 1.0.35. @@ -19,7 +19,7 @@ ### [react-native-pdf+7.0.2+002+fix-pdf-crash-already-closed-android.patch](react-native-pdf+7.0.2+002+fix-pdf-crash-already-closed-android.patch) - Reason: - + ``` This patch fixes a fatal Android crash (java.lang.IllegalStateException: Already closed) that occurs when users navigate away from a PDF while it's still rendering. @@ -29,3 +29,18 @@ - Upstream PR/issue: https://github.com/wonday/react-native-pdf/issues/976, https://github.com/wonday/react-native-pdf/issues/882, https://github.com/wonday/react-native-pdf/issues/830, https://github.com/wonday/react-native-pdf/pull/999 - E/App issue: https://github.com/Expensify/App/issues/82570 - PR introducing patch: https://github.com/Expensify/App/pull/82628 + + +### [react-native-pdf+7.0.2+003+disable-topchange-coalescing-android.patch](react-native-pdf+7.0.2+003+disable-topchange-coalescing-android.patch) + +- Reason: + + ``` + This patch prevents Android Fabric from coalescing react-native-pdf's shared topChange events. + + react-native-pdf sends loadComplete and pageChanged through the same topChange event name. Under Fabric, TopChangeEvent inherited React Native's default coalescing behavior, so pageChanged could replace loadComplete for the same PDF view before JS received it. That broke the onLoadComplete contract for hidden PDF validation renders. Marking the event as non-coalescible preserves both native callbacks in JS without adding an app-level success fallback. + ``` + +- Upstream PR/issue: N/A +- E/App issue: https://github.com/Expensify/App/issues/81225 +- PR introducing patch: TBD diff --git a/patches/react-native-pdf/react-native-pdf+7.0.2+003+disable-topchange-coalescing-android.patch b/patches/react-native-pdf/react-native-pdf+7.0.2+003+disable-topchange-coalescing-android.patch new file mode 100644 index 0000000000000..612949a37ee2e --- /dev/null +++ b/patches/react-native-pdf/react-native-pdf+7.0.2+003+disable-topchange-coalescing-android.patch @@ -0,0 +1,16 @@ +diff --git a/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/events/TopChangeEvent.java b/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/events/TopChangeEvent.java +index 6feb97d..aab7738 100644 +--- a/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/events/TopChangeEvent.java ++++ b/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/events/TopChangeEvent.java +@@ -17,6 +17,11 @@ public class TopChangeEvent extends Event { + return "topChange"; + } + ++ @Override ++ public boolean canCoalesce() { ++ return false; ++ } ++ + @Nullable + @Override + protected WritableMap getEventData() { diff --git a/src/components/PDFThumbnail/index.native.tsx b/src/components/PDFThumbnail/index.native.tsx index 0a3474b91bd72..2ea1f341b8a62 100644 --- a/src/components/PDFThumbnail/index.native.tsx +++ b/src/components/PDFThumbnail/index.native.tsx @@ -1,4 +1,4 @@ -import React, {useState} from 'react'; +import React, {useCallback, useRef, useState} from 'react'; import {View} from 'react-native'; import Pdf from 'react-native-pdf'; import LoadingIndicator from '@components/LoadingIndicator'; @@ -13,6 +13,16 @@ function PDFThumbnail({previewSourceURL, style, enabled = true, fitPolicy = 0, o const styles = useThemeStyles(); const sizeStyles = [styles.w100, styles.h100]; const [failedToLoad, setFailedToLoad] = useState(false); + const lastReportedLoadSuccessSource = useRef(undefined); + + const reportLoadSuccess = useCallback(() => { + if (!onLoadSuccess || lastReportedLoadSuccessSource.current === previewSourceURL) { + return; + } + + lastReportedLoadSuccessSource.current = previewSourceURL; + onLoadSuccess(); + }, [onLoadSuccess, previewSourceURL]); return ( @@ -35,12 +45,7 @@ function PDFThumbnail({previewSourceURL, style, enabled = true, fitPolicy = 0, o } setFailedToLoad(true); }} - onLoadComplete={() => { - if (!onLoadSuccess) { - return; - } - onLoadSuccess(); - }} + onLoadComplete={reportLoadSuccess} /> )} {failedToLoad && } From d01d374a8519b2393e2cb5b04bbc2fb1cbed2ec5 Mon Sep 17 00:00:00 2001 From: mavrickdeveloper Date: Wed, 8 Apr 2026 20:36:49 +0100 Subject: [PATCH 2/2] Remove PDFThumbnail change from PR scope --- src/components/PDFThumbnail/index.native.tsx | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/components/PDFThumbnail/index.native.tsx b/src/components/PDFThumbnail/index.native.tsx index 2ea1f341b8a62..0a3474b91bd72 100644 --- a/src/components/PDFThumbnail/index.native.tsx +++ b/src/components/PDFThumbnail/index.native.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useRef, useState} from 'react'; +import React, {useState} from 'react'; import {View} from 'react-native'; import Pdf from 'react-native-pdf'; import LoadingIndicator from '@components/LoadingIndicator'; @@ -13,16 +13,6 @@ function PDFThumbnail({previewSourceURL, style, enabled = true, fitPolicy = 0, o const styles = useThemeStyles(); const sizeStyles = [styles.w100, styles.h100]; const [failedToLoad, setFailedToLoad] = useState(false); - const lastReportedLoadSuccessSource = useRef(undefined); - - const reportLoadSuccess = useCallback(() => { - if (!onLoadSuccess || lastReportedLoadSuccessSource.current === previewSourceURL) { - return; - } - - lastReportedLoadSuccessSource.current = previewSourceURL; - onLoadSuccess(); - }, [onLoadSuccess, previewSourceURL]); return ( @@ -45,7 +35,12 @@ function PDFThumbnail({previewSourceURL, style, enabled = true, fitPolicy = 0, o } setFailedToLoad(true); }} - onLoadComplete={reportLoadSuccess} + onLoadComplete={() => { + if (!onLoadSuccess) { + return; + } + onLoadSuccess(); + }} /> )} {failedToLoad && }