diff --git a/metro.config.js b/metro.config.js
index e6a6676f06..d083f2a840 100644
--- a/metro.config.js
+++ b/metro.config.js
@@ -9,6 +9,11 @@ cfg.resolver.sourceExts = process.env.RN_SRC_EXT
if (cfg.resolver.resolveRequest) {
throw Error('Update this override because it is conflicting now.')
}
+
+if (process.env.BSKY_PROFILE) {
+ cfg.cacheVersion += ':PROFILE'
+}
+
cfg.resolver.resolveRequest = (context, moduleName, platform) => {
// HACK: manually resolve a few packages that use `exports` in `package.json`.
// A proper solution is to enable `unstable_enablePackageExports` but this needs careful testing.
@@ -29,6 +34,15 @@ cfg.resolver.resolveRequest = (context, moduleName, platform) => {
if (moduleName === '@ipld/dag-cbor') {
return context.resolveRequest(context, '@ipld/dag-cbor/src', platform)
}
+ if (process.env.BSKY_PROFILE) {
+ if (moduleName.endsWith('ReactNativeRenderer-prod')) {
+ return context.resolveRequest(
+ context,
+ moduleName.replace('-prod', '-profiling'),
+ platform,
+ )
+ }
+ }
return context.resolveRequest(context, moduleName, platform)
}
diff --git a/package.json b/package.json
index 2dc5210b78..30c2be2b34 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,8 @@
"postinstall": "patch-package && yarn intl:compile",
"prebuild": "expo prebuild --clean",
"android": "expo run:android",
+ "android:prod": "expo run:android --variant release",
+ "android:profile": "BSKY_PROFILE=1 expo run:android --variant release",
"ios": "expo run:ios",
"web": "expo start --web",
"use-build-number": "./scripts/useBuildNumberEnv.sh",
diff --git a/src/App.native.tsx b/src/App.native.tsx
index 668fb91fcd..9b2940aa9d 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -69,6 +69,7 @@ import {Provider as PortalProvider} from '#/components/Portal'
import {Splash} from '#/Splash'
import {BottomSheetProvider} from '../modules/bottom-sheet'
import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider'
+import {AppProfiler} from './AppProfiler'
SplashScreen.preventAutoHideAsync()
@@ -184,40 +185,42 @@ function App() {
* that is set up in the InnerApp component above.
*/
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/src/AppProfiler.tsx b/src/AppProfiler.tsx
new file mode 100644
index 0000000000..31a4cc54ef
--- /dev/null
+++ b/src/AppProfiler.tsx
@@ -0,0 +1,29 @@
+import React, {Profiler} from 'react'
+
+// Don't let it get stripped out in profiling builds (which apply production Babel preset).
+const log = (global as any)['con' + 'sole'].log
+
+function onRender(id: string, phase: string, actualDuration: number) {
+ if (!__DEV__) {
+ // This block of code will exist in the production build of the app.
+ // However, only profiling builds of React call `onRender` so it's dead code in actual production.
+ const message = ` ${id}:${phase} ${
+ actualDuration > 500
+ ? '(╯°□°)╯ '
+ : actualDuration > 100
+ ? '[!!] '
+ : actualDuration > 16
+ ? '[!] '
+ : ''
+ }${Math.round(actualDuration)}ms`
+ log(message)
+ }
+}
+
+export function AppProfiler({children}: {children: React.ReactNode}) {
+ return (
+
+ {children}
+
+ )
+}