about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--metro.config.js14
-rw-r--r--package.json2
-rw-r--r--src/App.native.tsx71
-rw-r--r--src/AppProfiler.tsx29
4 files changed, 82 insertions, 34 deletions
diff --git a/metro.config.js b/metro.config.js
index e6a6676f0..d083f2a84 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 2dc5210b7..30c2be2b3 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 668fb91fc..9b2940aa9 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 (
-    <GeolocationProvider>
-      <A11yProvider>
-        <KeyboardProvider enabled={false} statusBarTranslucent={true}>
-          <SessionProvider>
-            <PrefsStateProvider>
-              <I18nProvider>
-                <ShellStateProvider>
-                  <InvitesStateProvider>
-                    <ModalStateProvider>
-                      <DialogStateProvider>
-                        <LightboxStateProvider>
-                          <PortalProvider>
-                            <BottomSheetProvider>
-                              <StarterPackProvider>
-                                <SafeAreaProvider
-                                  initialMetrics={initialWindowMetrics}>
-                                  <IntentDialogProvider>
-                                    <InnerApp />
-                                  </IntentDialogProvider>
-                                </SafeAreaProvider>
-                              </StarterPackProvider>
-                            </BottomSheetProvider>
-                          </PortalProvider>
-                        </LightboxStateProvider>
-                      </DialogStateProvider>
-                    </ModalStateProvider>
-                  </InvitesStateProvider>
-                </ShellStateProvider>
-              </I18nProvider>
-            </PrefsStateProvider>
-          </SessionProvider>
-        </KeyboardProvider>
-      </A11yProvider>
-    </GeolocationProvider>
+    <AppProfiler>
+      <GeolocationProvider>
+        <A11yProvider>
+          <KeyboardProvider enabled={false} statusBarTranslucent={true}>
+            <SessionProvider>
+              <PrefsStateProvider>
+                <I18nProvider>
+                  <ShellStateProvider>
+                    <InvitesStateProvider>
+                      <ModalStateProvider>
+                        <DialogStateProvider>
+                          <LightboxStateProvider>
+                            <PortalProvider>
+                              <BottomSheetProvider>
+                                <StarterPackProvider>
+                                  <SafeAreaProvider
+                                    initialMetrics={initialWindowMetrics}>
+                                    <IntentDialogProvider>
+                                      <InnerApp />
+                                    </IntentDialogProvider>
+                                  </SafeAreaProvider>
+                                </StarterPackProvider>
+                              </BottomSheetProvider>
+                            </PortalProvider>
+                          </LightboxStateProvider>
+                        </DialogStateProvider>
+                      </ModalStateProvider>
+                    </InvitesStateProvider>
+                  </ShellStateProvider>
+                </I18nProvider>
+              </PrefsStateProvider>
+            </SessionProvider>
+          </KeyboardProvider>
+        </A11yProvider>
+      </GeolocationProvider>
+    </AppProfiler>
   )
 }
 
diff --git a/src/AppProfiler.tsx b/src/AppProfiler.tsx
new file mode 100644
index 000000000..31a4cc54e
--- /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 = `<Profiler> ${id}:${phase} ${
+      actualDuration > 500
+        ? '(╯°□°)╯ '
+        : actualDuration > 100
+        ? '[!!] '
+        : actualDuration > 16
+        ? '[!] '
+        : ''
+    }${Math.round(actualDuration)}ms`
+    log(message)
+  }
+}
+
+export function AppProfiler({children}: {children: React.ReactNode}) {
+  return (
+    <Profiler id="app" onRender={onRender}>
+      {children}
+    </Profiler>
+  )
+}