xcarpentier / rn-tourguide

🚩Make an interactive step by step tour guide for your react-native app (a rewrite of react-native-copilot)
https://xcarpentier.github.io/rn-tourguide/
Other
731 stars 213 forks source link

ERROR while scrolling back outside the refferred scrollview #148

Open nihatt opened 10 months ago

nihatt commented 10 months ago

in my code zone 1 and 2 works perfectly with scroll thanks to @xcarpentier but when i come to zone 3 which is outside the scroll view i get an error which is telling me

`view <RCTShadowView: 0x7f8efd4bdb90; viewName: RCTView; reactTag: 105; frame: {{0, 0}, {234, 24}}> (tag #105) is not a descendant of <RCTShadowView: 0x7f8efd4725c0; viewName: RCTScrollView; reactTag: 669; frame: {{0, 108}, {390, 657}}> (tag #669)

RCTMeasureLayout RCTUIManager.m:1392 -[RCTUIManager measureLayout:relativeTo:errorCallback:callback:] __invoking___ -[NSInvocation invoke] -[NSInvocation invokeWithTarget:] -[RCTModuleMethod invokeWithBridge:module:arguments:] facebook::react::invokeInner(RCTBridge, RCTModuleData, unsigned int, folly::dynamic const&, int, (anonymous namespace)::SchedulingContext) facebook::react::RCTNativeModule::invoke(unsigned int, folly::dynamic&&, int)::$_0::operator()() const invocation function for block in facebook::react::RCTNativeModule::invoke(unsigned int, folly::dynamic&&, int) _dispatch_call_block_and_release _dispatch_client_callout _dispatch_lane_serial_drain _dispatch_lane_invoke _dispatch_workloop_worker_thread _pthread_wqthread start_wqthread `

my code is like that :

` <View style={{ width: '60%' }}> <TourGuideZone zone={3} text={mainTourValues[2].appTutorialResource.title} borderRadius={16}

<TouchableOpacity style={[styles.profileImage]} onPress={() => toggle()}>

{patientDetail?.data?.fullName ?? ''}

{ navigation.navigate('BasketScreen'); }}>
              <IconBasket />
              {userBasketCount > 0 && (
                <LinearGradient
                  colors={['#253FC4', '#000D4F']}
                  style={{
                    position: 'absolute',
                    top: 0,
                    right: 0,
                    width: 22,
                    height: 22,
                    borderRadius: 11,
                    justifyContent: 'center',
                    alignItems: 'center',
                  }}>
                  <Text style={{ ...FONTS.f1, color: 'white' }}>
                    {userBasketCount}
                  </Text>
                </LinearGradient>
              )}
            </TouchableOpacity>
        <TouchableOpacity
          style={{
            paddingTop: 10,
          }}
          onPress={() => {
            navigation.navigate('NotificationScreen');
          }}>
          <IconNotification />
          {userNotificationCount > 0 && (
            <LinearGradient
              colors={['#253FC4', '#000D4F']}
              style={{
                position: 'absolute',
                top: 0,
                right: 0,
                width: 22,
                height: 22,
                borderRadius: 11,
                justifyContent: 'center',
                alignItems: 'center',
              }}>
              <Text style={{ ...FONTS.f1, color: 'white' }}>
                {userNotificationCount}
              </Text>
            </LinearGradient>
          )}
        </TouchableOpacity>

        <TouchableOpacity
          style={{
            paddingTop: 10,
          }}
          onPress={() => {
            handleScaleChange();
          }}>
          <IconMenu />
        </TouchableOpacity>
      </View>
    </View>
    <ScrollView
      ref={(r) => { scrollRef.current = r }}
      contentContainerStyle={{ flexGrow: 1 }}
      scrollEventThrottle={16}
      keyboardShouldPersistTaps={'always'}
      showsVerticalScrollIndicator={false}
      refreshControl={
        <RefreshControl
          refreshing={refreshing}
          onRefresh={loadDashboardDetail}
        />
      }
    >
      <View style={styles.container}>
        <View style={styles.section}>
          <TextInput
            style={styles.input}
            value={search}
            placeholder={strings.searchDasboardPlaceHolder}
            onChangeText={text => setSearch(text)}
            keyboardType="default"
            onSubmitEditing={() => {
              navigation.navigate('SearchHospitalSectionScreen', {
                searchText: search,
              });
              setSearch('');
            }}
          />
        </View>
        <View>
          <TourGuideZone
            zone={1}
            text={mainTourValues[0].appTutorialResource.title}
            borderRadius={16}
          >
            <SwiperFlatList
              autoplay
              autoplayDelay={3}
              autoplayLoop
              index={0}
              contentContainerStyle={{ marginHorizontal: 15 }}
              borderRadius={15}
              showPagination
              keyExtractor={(item, index) => index.toString()}
              data={dashboardData?.sliderList}
              paginationStyleItemActive={styles.paginationStyleItemActive}
              paginationStyleItemInactive={styles.paginationStyleItemInactive}
              paginationActiveColor={'#B1292F'}
              paginationDefaultColor={'#F4888C'}
              renderItem={({ item }) => (
                <Pressable
                  key={item.key}
                  style={styles.child}
                  onPress={async () => {
                    const result = await getSliderDetailById(item.id);
                    navigation.navigate('SliderDetailScreen', {
                      item: result?.data?.sliderResource,
                    });
                  }}>
                  <Image
                    style={styles.imageStyle}
                    resizeMode="contain"
                    source={{
                      uri: item?.sliderResource?.image,
                    }}
                  />
                </Pressable>
              )}
            />
          </TourGuideZone>
        </View>
        {/* Hizmet Paketleri */}
        {serviceData?.length !== 0 && (
          <>

            <View style={styles.serviceView}>
              <Text
                style={[
                  styles.textSizeLarge,
                  styles.textBlue,
                  styles.textBold,
                ]}>
                {strings.servicePackages}
              </Text>
              <Text
                onPress={() => navigation.navigate('PackageScreen')}
                style={[
                  styles.textSizeMedium,
                  styles.textUnderline,
                  styles.textBlue,
                ]}>
                {strings.seeAll}
              </Text>
            </View>
            <View style={styles.shadow}>

              <FlatList
                data={serviceData}
                renderItem={item => (
                  <RenderPackageList {...item} getDetail={getDetail} />
                )}
                contentContainerStyle={styles.flatlistContent}
                keyExtractor={(item, index) => index.toString()}
                horizontal={true}
                scrollEnabled
                showsHorizontalScrollIndicator={false}
              />

            </View>

          </>
        )}

        {/* Randevular */}
        <>
          <View ref={stepRef2} style={styles.serviceView}>

            <Text
              style={[
                styles.textSizeLarge,
                styles.textBlue,
                styles.textBold,
              ]}>
              {strings.upcomingAppointments}
            </Text>

            <TouchableOpacity
              onPress={() => navigation.navigate('AppointmentListScreen')}>

              <Text
                style={[
                  styles.textSizeMedium,
                  styles.textUnderline,
                  styles.textBlue,
                ]}>
                {strings.seeAll}
              </Text>

            </TouchableOpacity>
          </View>
          <View
            style={{
              height:
                dashboardData?.appointments?.futureList.length !== 0
                  ? 240
                  : 'auto',
            }}>
            <>
              {dashboardData?.appointments?.futureList.length === 0 ? (
                <View style={styles.flexDirectionRow}>
                  <TourGuideZone
                    zone={2}
                    text={mainTourValues[1].appTutorialResource.description}
                    borderRadius={16}
                  >
                    <TouchableOpacity
                      style={[
                        styles.appointmentFirstItem,
                        styles.alignSelf,
                        styles.alignItems,
                        styles.shadow,
                      ]}
                      onPress={() =>
                        navigation.navigate('AppointmentScreen')
                      }>

                      <IconMakeAppointment />
                      <Text style={styles.appointmentText}>
                        {strings.appointment}
                      </Text>

                    </TouchableOpacity>
                  </TourGuideZone>`
dorhuri123 commented 9 months ago

Hey i wasn’t able to fix it but i made some hacky solution that work for me if you want to use. the only changes are made in TourGuideProvider.tsx change the if in setCurrentStep to enter the block only if the step order is one of thus in the scrollView also check step. i will note that there is very small dealy when going to the first element in the scroll but not something that seems to be a problem. for example my code look like this:

const setCurrentStep = async (key: string, step?: IStep) =>
    new Promise<void>(async (resolve) => {
    if (step && (step.order === 3 || step.order === 4)) {
        await step.wrapper.measureLayout(
          findNodeHandle(scrollRef.current),
          (_x: number, y: number, _w: number, h: number) => {
            const yOffsett = y > 0 ? y - h / 2 : 0
            scrollRef.current.scrollTo({ y: yOffsett, animated: false })
          },
        )
        setTimeout(() => {
          updateCurrentStep((currentStep) => {
            const newStep = { ...currentStep }
            newStep[key] = step
            eventEmitter[key]?.emit('stepChange', step)
            return newStep
          })
          resolve()
        }, 0);
      }
      else {
        updateCurrentStep((currentStep) => {
          const newStep = { ...currentStep }
          newStep[key] = step
          eventEmitter[key]?.emit('stepChange', step)
          return newStep
        })
        resolve()
      }

    })

if you have multiple tours you can add the tourkey to the if statment and adjust it so it work for each tour(didnt try this yet). after this i notice when i restared the tour i get an unhandle promise that stuck my simulator that is because scrollRef got undefined on the second tour i am gussing it because the poor handling of my part or something to do to the code structure to solve this i removed the if

if (!scrollRef) {
      setScrollRef(_scrollRef)
     }

to just

setScrollRef(_scrollRef)

this seems to work for me if you want to use it or have suggestions to make it better it much appreciated.

nihatt commented 9 months ago

i solved it by using multiple tour guides , but i am surprised tour guide doesnt work by only position . React should release a core package about it i think :D

navaspavil commented 9 months ago

@nihatt Could you fix it?

nihatt commented 9 months ago

@navaspavil yes , i used multiple guides.

Drzaln commented 4 months ago

and call next guides after the first guide is finished? @nihatt

nihatt commented 4 months ago

@Drzaln Yes , giving them each id's and calling them 1by1 helped me