From 079b0fdaed8234e332e134a672483ebf259c842d Mon Sep 17 00:00:00 2001 From: Vitali Zaidman Date: Mon, 22 Sep 2025 09:52:38 -0700 Subject: [PATCH 1/2] Align Android with iOS in displaying HMR "refreshing" in color (#53846) Summary: Changelog: [Android][Added] - hot reload banner is now displayed in blue background like on iOS iOS has a colorful "Refreshing..." banner: {F1982086204} While android doesn't respect the color HMR asks it to set up for the banner: {F1982086218} Reviewed By: cortinico Differential Revision: D82726743 --- .../DefaultDevLoadingViewImplementation.kt | 14 ++++++++++++-- .../devsupport/interfaces/DevLoadingViewManager.kt | 2 ++ .../react/modules/devloading/DevLoadingModule.kt | 4 +++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevLoadingViewImplementation.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevLoadingViewImplementation.kt index 7e06ff82c4ce..330c2e89acde 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevLoadingViewImplementation.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevLoadingViewImplementation.kt @@ -33,10 +33,14 @@ public class DefaultDevLoadingViewImplementation( private var devLoadingPopup: PopupWindow? = null override fun showMessage(message: String) { + showMessage(message, color = null, backgroundColor = null) + } + + override fun showMessage(message: String, color: Double?, backgroundColor: Double?) { if (!isEnabled) { return } - UiThreadUtil.runOnUiThread { showInternal(message) } + UiThreadUtil.runOnUiThread { showInternal(message, color, backgroundColor) } } override fun updateProgress(status: String?, done: Int?, total: Int?) { @@ -59,7 +63,7 @@ public class DefaultDevLoadingViewImplementation( } } - private fun showInternal(message: String) { + private fun showInternal(message: String, color: Double?, backgroundColor: Double?) { if (devLoadingPopup?.isShowing == true) { // already showing return @@ -84,6 +88,12 @@ public class DefaultDevLoadingViewImplementation( currentActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater val view = inflater.inflate(R.layout.dev_loading_view, null) as TextView view.text = message + if (color != null) { + view.setTextColor(color.toInt()) + } + if (backgroundColor != null) { + view.setBackgroundColor(backgroundColor.toInt()) + } val popup = PopupWindow( view, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevLoadingViewManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevLoadingViewManager.kt index 08d4757a4c5b..9366cfb3b300 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevLoadingViewManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevLoadingViewManager.kt @@ -11,6 +11,8 @@ package com.facebook.react.devsupport.interfaces public interface DevLoadingViewManager { public fun showMessage(message: String) + public fun showMessage(message: String, color: Double?, backgroundColor: Double?) + public fun updateProgress(status: String?, done: Int?, total: Int?) public fun hide() diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/devloading/DevLoadingModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/devloading/DevLoadingModule.kt index 7344fcdad8dd..78a3a84146d9 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/devloading/DevLoadingModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/devloading/DevLoadingModule.kt @@ -31,7 +31,9 @@ internal class DevLoadingModule(reactContext: ReactApplicationContext) : } override fun showMessage(message: String, color: Double?, backgroundColor: Double?) { - UiThreadUtil.runOnUiThread { devLoadingViewManager?.showMessage(message) } + UiThreadUtil.runOnUiThread { + devLoadingViewManager?.showMessage(message, color, backgroundColor) + } } override fun hide() { From 29921eaf2d3dbd39faf177fedd20d9290a3fd465 Mon Sep 17 00:00:00 2001 From: Vitali Zaidman Date: Mon, 22 Sep 2025 09:52:38 -0700 Subject: [PATCH 2/2] add disconnection message Summary: Changelog: [General][Added] - new banner added to indicate that Fast Refresh has lost connection, and that the app needs to be restarted to reconnect When HMR were getting disconnected, we were not giving any indication for the user. This lead to situations were the connection is re-established, but HMR is still disconnected, which is unexpected for users. Instead, raise a banner hinting the user to reload the app to reconnect. Reviewed By: huntie Differential Revision: D82726853 --- .../Libraries/Utilities/DevLoadingView.js | 20 +++++++++++++------ .../Libraries/Utilities/HMRClient.js | 11 ++++++---- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/react-native/Libraries/Utilities/DevLoadingView.js b/packages/react-native/Libraries/Utilities/DevLoadingView.js index f43bb86b2969..338a5614df35 100644 --- a/packages/react-native/Libraries/Utilities/DevLoadingView.js +++ b/packages/react-native/Libraries/Utilities/DevLoadingView.js @@ -14,29 +14,37 @@ import NativeDevLoadingView from './NativeDevLoadingView'; const COLOR_SCHEME = { dark: { + load: { + backgroundColor: '#fafafa', + textColor: '#242526', + }, refresh: { backgroundColor: '#2584e8', textColor: '#ffffff', }, - load: { - backgroundColor: '#fafafa', - textColor: '#242526', + error: { + backgroundColor: '#1065AF', + textColor: '#ffffff', }, }, default: { + load: { + backgroundColor: '#404040', + textColor: '#ffffff', + }, refresh: { backgroundColor: '#2584e8', textColor: '#ffffff', }, - load: { - backgroundColor: '#404040', + error: { + backgroundColor: '#1065AF', textColor: '#ffffff', }, }, }; export default { - showMessage(message: string, type: 'load' | 'refresh') { + showMessage(message: string, type: 'load' | 'refresh' | 'error') { if (NativeDevLoadingView) { const colorScheme = getColorScheme() === 'dark' ? COLOR_SCHEME.dark : COLOR_SCHEME.default; diff --git a/packages/react-native/Libraries/Utilities/HMRClient.js b/packages/react-native/Libraries/Utilities/HMRClient.js index 6d16b6b973f3..f466f7a7f99a 100644 --- a/packages/react-native/Libraries/Utilities/HMRClient.js +++ b/packages/react-native/Libraries/Utilities/HMRClient.js @@ -232,8 +232,6 @@ Error: ${e.message}`; }); client.on('error', data => { - DevLoadingView.hide(); - if (data.type === 'GraphNotFoundError') { client.close(); setHMRUnavailableReason( @@ -253,8 +251,6 @@ Error: ${e.message}`; }); client.on('close', closeEvent => { - DevLoadingView.hide(); - // https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1 // https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.5 const isNormalOrUnsetCloseReason = @@ -296,10 +292,17 @@ function setHMRUnavailableReason(reason: string) { } hmrUnavailableReason = reason; + const DevLoadingView = require('./DevLoadingView').default; + DevLoadingView.hide(); + // We only want to show a warning if Fast Refresh is on *and* if we ever // previously managed to connect successfully. We don't want to show // the warning to native engineers who use cached bundles without Metro. if (hmrClient.isEnabled() && didConnect) { + DevLoadingView.showMessage( + 'Fast Refresh disconnected. Reload app to reconnect.', + 'error', + ); console.warn(reason); // (Not using the `warning` module to prevent a Buck cycle.) }