mohebifar / mohebifar/react-native-copilot

Expo 54: Scrolling is broken on iOS/Android and positioning is off on Android

Open
#351 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
2.4k
Forks
428
PR merge metrics
No merged PRs in 30d

Description

Hi! 👋

Firstly, thanks for your work on this project! 🙂

As I have clarified in the title, there are scroll and positioning related issues when we use this package in a project built with Expo 54

Here is the diff that solved my problem:

diff --git a/node_modules/react-native-copilot/dist/index.js b/node_modules/react-native-copilot/dist/index.js
index 3aff2e2..ad074f8 100644
--- a/node_modules/react-native-copilot/dist/index.js
+++ b/node_modules/react-native-copilot/dist/index.js
@@ -893,7 +893,7 @@ var CopilotProvider = (_a) => {
   const copilotEvents = (0, import_react8.useRef)((0, import_mitt.default)()).current;
   const modal = (0, import_react8.useRef)(null);
   const [visible, setVisibility] = useStateWithAwait(false);
-  const [scrollView, setScrollView] = (0, import_react8.useState)(null);
+  const scrollViewRef = (0, import_react8.useRef)(null);
   const {
     currentStep,
     currentStepNumber,
@@ -927,20 +927,17 @@ var CopilotProvider = (_a) => {
   );
   const setCurrentStep = (0, import_react8.useCallback)(
     (step, move = true) => __async(void 0, null, function* () {
-      var _a2;
       setCurrentStepState(step);
       copilotEvents.emit("stepChange", step);
-      if (scrollView != null) {
-        const nodeHandle = (0, import_react_native7.findNodeHandle)(scrollView);
-        if (nodeHandle) {
-          (_a2 = step == null ? void 0 : step.wrapperRef.current) == null ? void 0 : _a2.measureLayout(
-            nodeHandle,
-            (_x, y, _w, h) => {
-              const yOffset = y > 0 ? y - h / 2 : 0;
-              scrollView.scrollTo({ y: yOffset, animated: false });
-            }
-          );
-        }
+      if (scrollViewRef.current != null && step?.wrapperRef.current) {
+        step.wrapperRef.current.measure((_x, _y, _w, h, _pageX, pageY) => {
+          if (pageY !== undefined && h !== undefined) {
+            // Scroll so element appears in upper portion of screen
+            // leaving room for tooltip below
+            const yOffset = Math.max(0, pageY - 150);
+            scrollViewRef.current.scrollTo({ y: yOffset, animated: false });
+          }
+        });
       }
       setTimeout(
         () => {
@@ -948,16 +945,17 @@ var CopilotProvider = (_a) => {
             void moveModalToStep(step);
           }
         },
-        scrollView != null ? 100 : 0
+        scrollViewRef.current != null ? 100 : 0
       );
     }),
-    [copilotEvents, moveModalToStep, scrollView, setCurrentStepState]
+    [copilotEvents, moveModalToStep, setCurrentStepState]
   );
   const start = (0, import_react8.useCallback)(
     (fromStep, suppliedScrollView = null) => __async(void 0, null, function* () {
-      if (scrollView == null) {
-        setScrollView(suppliedScrollView);
+      if (suppliedScrollView != null) {
+        scrollViewRef.current = suppliedScrollView;
       }
+      
       const currentStep2 = fromStep ? steps[fromStep] : getFirstStep();
       if (startTries.current > MAX_START_TRIES) {
         startTries.current = 0;
@@ -980,7 +978,6 @@ var CopilotProvider = (_a) => {
       copilotEvents,
       getFirstStep,
       moveModalToStep,
-      scrollView,
       setCurrentStep,
       setVisibility,
       steps
@@ -1071,6 +1068,7 @@ function walkthroughable(WrappedComponent) {
 
 // src/components/CopilotStep.tsx
 var import_react11 = __toESM(require("react"));
+var import_react_native9 = require("react-native");
 var CopilotStep = ({
   name,
   order,
@@ -1086,9 +1084,15 @@ var CopilotStep = ({
       const measure2 = () => {
         if (wrapperRef.current != null && "measure" in wrapperRef.current) {
           wrapperRef.current.measure((_ox, _oy, width, height, x, y) => {
+            // Adjust for Android status bar
+            let adjustedY = y;
+            if (import_react_native9.Platform.OS === 'android') {
+              const statusBarHeight = import_react_native9.StatusBar.currentHeight || 0;
+              adjustedY = y + statusBarHeight;
+            }
             resolve({
               x,
-              y,
+              y: adjustedY,
               width,
               height
             });

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating the source for CopilotProvider corresponding to dist/index.js and read src/components/CopilotStep.tsx, then reproduce the scrolling and positioning behavior in an Expo 54 app on iOS and Android. Compare the reported diff with the source and verify that walkthrough scrolling and step positioning are correct on both platforms.

Written by the indexing model from the issue text.

Assessment

Tech stack
react-native, typescript
Domain
mobile
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.