@react-navigation/stack#useHeaderHeight TypeScript Examples

The following examples show how to use @react-navigation/stack#useHeaderHeight. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: JoinScreen.tsx    From lets-fork-native with MIT License 6 votes vote down vote up
JoinScreen = React.memo((props: Props): React.ReactElement => {
  const { navigation, ws } = props
  const [value, setValue] = React.useState('')
  const headerHeight = useHeaderHeight()

  // joins an existing party
  const handleJoin = (): void => {
    if (value) {
      ws.send(JSON.stringify({ type: 'join', payload: { party_id: value } }))
      navigation.navigate('Party')
      Keyboard.dismiss()
    }
  }

  return (
    <KeyboardAvoidingView
      behavior="height"
      style={{
        ...styles.container,
        height: height - headerHeight,
      }}
    >
      <Text style={styles.text}>Please enter the party code</Text>
      <Input value={value} handleChange={setValue} keyboardType="phone-pad" />
      <Button size="sm" color="purple" onPress={(): void => handleJoin()}>
        JOIN
      </Button>
    </KeyboardAvoidingView>
  )
})
Example #2
Source File: ImageTransformerTest.tsx    From react-native-gallery-toolkit with MIT License 6 votes vote down vote up
export default function StandaloneGalleryBasicScreen() {
  const headerHeight = useHeaderHeight();
  return (
    <ImageTransformer
      windowDimensions={{
        height: height - headerHeight,
        width,
      }}
      width={image.width}
      height={image.height}
      source={image.uri}
    />
  );
}
Example #3
Source File: Map.tsx    From react-native-gallery-toolkit with MIT License 6 votes vote down vote up
export default function StandaloneMap() {
  const initialIndex = 1;
  const headerHeight = useHeaderHeight();

  const totalCount = 3;

  function handleIndexChange(i: number) {
    'worklet';
    console.log(i);
    // you can fetch more items and store it to the map here
  }

  return (
    <SimpleGallery
      initialIndex={initialIndex}
      onIndexChange={handleIndexChange}
      height={height - headerHeight}
      renderPage={(props, i) => <Page {...props} index={i} />}
      getItem={(data, i) => data.get(i)}
      items={images}
      getTotalCount={() => totalCount}
    />
  );
}
Example #4
Source File: ShelfTabs.tsx    From krmanga with MIT License 5 votes vote down vote up
function ShelfTabs() {

    const tabHeight = useHeaderHeight() - getStatusBarHeight();

    return (
        <Tab.Navigator
            lazy
            tabBar={(props) => <TopBar  {...props} />}
            pager={props => <ViewPagerAdapter {...props} />}
            tabBarOptions={{
                scrollEnabled: true,
                tabStyle: {
                    width: 50,
                    height: tabHeight,
                    padding: 0,
                    margin: 0
                },
                labelStyle: {
                    fontSize: 15,
                    fontWeight: "bold",
                    color: Color.white
                },
                indicatorStyle: {
                    height: 3,
                    width: 16,
                    marginLeft: 17,
                    marginBottom: 5,
                    borderRadius: 2,
                    backgroundColor: Color.white
                },
                allowFontScaling: true,
                activeTintColor: Color.dark_title,
                inactiveTintColor: Color.black
            }}>
            <Tab.Screen
                key={"shelf-tab-1"}
                name="收藏"
                component={Shelf}
            />
            <Tab.Screen
                key={"shelf-tab-2"}
                name={"历史"}
                component={History}
            />
            <Tab.Screen
                key={"shelf-tab-3"}
                name={"下载"}
                component={Download}
            />
        </Tab.Navigator>
    );
}
Example #5
Source File: TopBarWrapper.tsx    From krmanga with MIT License 5 votes vote down vote up
function TopBarWrapper({navigation, topBarColor}: IProps) {

    const headerHeight = useHeaderHeight()

    const goSearch = () => {
        navigation.navigate('Search');
    }

    const goGuess = (headerTitle: string) => {
        navigation.navigate('Guess', {
            headerTitle
        });
    }

    return (
        <Animated.View style={[styles.container, {
            height: headerHeight
        }]}>
            <Animated.View style={{
                ...StyleSheet.absoluteFillObject,
                backgroundColor: Color.theme,
                opacity: topBarColor
            }}/>
            <View style={styles.header}>
                <View style={styles.headerLeft}>
                    <Touchable style={styles.headerLeftView} onPress={() => goGuess("榜单")}>
                        <Icon name="icon-paihangbang" size={25}/>
                        <Text style={styles.headerText}>榜单</Text>
                    </Touchable>
                    <Touchable style={styles.headerLeftView} onPress={() => goGuess("更新")}>
                        <Icon name="icon-shizhong" size={25}/>
                        <Text style={styles.headerText}>更新</Text>
                    </Touchable>
                    <Touchable style={styles.headerLeftView} onPress={() => goGuess("书单")}>
                        <Icon name="icon-history" size={25}/>
                        <Text style={styles.headerText}>书单</Text>
                    </Touchable>
                    <Touchable style={styles.headerLeftView} onPress={() => goGuess("VIP")}>
                        <Icon name="icon-VIP" size={25}/>
                        <Text style={styles.headerText}>VIP</Text>
                    </Touchable>
                </View>
                <Touchable onPress={goSearch}>
                    <View style={styles.headerRight}>
                        <Icon name="icon-search" size={25}/>
                    </View>
                </Touchable>
            </View>
        </Animated.View>
    )
}
Example #6
Source File: index.tsx    From lets-fork-native with MIT License 5 votes vote down vote up
// Initially shown while waiting for users to join party
// But can also be accessed through Menu for additional
// users to join after party has started
export default function Share(props: Props): React.ReactElement {
  const [loading, setLoading] = React.useState(false)
  const { party, ws } = props
  const headerHeight = useHeaderHeight()
  const viewHeight = env.ADS ? height - headerHeight - 50 : height - headerHeight

  const handlePress = (): void => {
    Alert.alert(
      '',
      'No matches will be shown until another user joins your party',
      [
        {
          text: 'OK',
          onPress: (): void => {
            if (ws) {
              ws.send(JSON.stringify({ type: 'start-swiping' }))
              setLoading(true)
            }
          },
        },
      ],
      { cancelable: true },
    )
  }

  if (loading) {
    return (
      <View
        style={{
          ...styles.container,
          height: viewHeight,
        }}
      >
        <ActivityIndicator size="large" />
      </View>
    )
  }

  return (
    <View
      style={{
        ...styles.container,
        height: viewHeight,
      }}
    >
      <Text style={styles.text}>Share this code with friends to have them join your party.</Text>
      <Text style={styles.code}>{party.id}</Text>
      <QRCode
        size={200}
        // value={`http://192.168.178.76:8003/party/${party.id}`}
        value={`https://letsfork.app/party/${party.id}`}
      />
      {
        party.status === 'waiting'
        && <Button color="purple" size="lg" onPress={(): void => handlePress()}>START SWIPING</Button>
      }
      <TouchableOpacity
        accessibilityRole="button"
        onPress={(): Promise<ShareAction> => RNShare.share(
          { message: `Join my party on Let's Fork by clicking this link:\nhttps://letsfork.app/party/${party.id}\n\nor by opening the app and entering the code ${party.id}` },
        )}
      >
        {Platform.OS === 'ios' ? (
          <Ionicons name="ios-share-outline" size={32} />
        ) : (
          <MaterialIcons name="share" size={32} />
        )}
      </TouchableOpacity>
    </View>
  )
}
Example #7
Source File: MatchScreen.tsx    From lets-fork-native with MIT License 5 votes vote down vote up
MatchScreen = React.memo((props: Props) => {
  const { navigation, party } = props
  const headerHeight = useHeaderHeight()

  if (!party?.matches) {
    return (
      <View
        style={{
          ...styles.container,
          height: height - headerHeight - 108,
        }}
      >
        <Text style={styles.text}>No matches yet</Text>
        <Text style={styles.text}>Keep swiping!</Text>
      </View>
    )
  }

  return (
    <ScrollView contentContainerStyle={styles.scrollView}>
      {party?.matches.map((restaurant) => (
        <TouchableOpacity
          key={restaurant.id}
          onPress={(): void => navigation.navigate('Restaurant', {
            restaurant,
          })}
        >
          <View style={styles.card}>
            <View style={styles.overlay}>
              <View>
                <Text style={styles.name}>{restaurant.name}</Text>
              </View>
            </View>
            <Image style={styles.image} source={{ uri: restaurant.image_url }} />
          </View>
        </TouchableOpacity>
      ))}
    </ScrollView>
  )
})
Example #8
Source File: Basic.tsx    From react-native-gallery-toolkit with MIT License 5 votes vote down vote up
export default function StandaloneGalleryBasicScreen() {
  const headerHeight = useHeaderHeight();
  return (
    <SimpleGallery height={height - headerHeight} items={images} />
  );
}
Example #9
Source File: CategoryTabs.tsx    From krmanga with MIT License 4 votes vote down vote up
function CategoryTabs({ hideHeader, statusList, myCategoryList }: IProps) {

    const translateY = useRef(new Animated.Value(0)).current;
    const headerHeight = useHeaderHeight();
    const navigationHeight = headerHeight - getStatusBarHeight();

    const getTopOpacity = () => {
        return translateY.interpolate({
            inputRange: [-navigationHeight, 0],
            outputRange: [0, 1],
            extrapolate: "clamp"
        });
    };

    const showTopBar = () => {
        Animated.timing(translateY, {
            toValue: 0,
            useNativeDriver: true
        }).start();
    };

    const hideTopBar = () => {
        Animated.timing(translateY, {
            toValue: -navigationHeight,
            useNativeDriver: true
        }).start();
    };

    const addModel = useCallback((id: number) => {
        statusList.map(item => {
            createCategoryModel(`tab-category-${id}-status-${item.id}`);
        });
    }, []);

    const renderScreen = (item: ICategory) => {
        addModel(item.id);
        return (
            <Tab.Screen
                key={`item-${item.id}`}
                name={item.name}
                component={Category}
                initialParams={{
                    namespace: `tab-category-${item.id}`,
                    category_id: item.id
                }}
                options={{
                    tabBarLabel: item.name
                }}
            />
        );
    };

    if (hideHeader) {
        hideTopBar();
    } else {
        showTopBar();
    }
    return (
        <View style={styles.container}>
            <View style={[styles.statusBar, {
                ...StyleSheet.absoluteFillObject,
                backgroundColor: Color.page_bg
            }]} />
            <Animated.View style={[styles.statusBar, {
                backgroundColor: Color.theme,
                opacity: getTopOpacity()
            }
            ]} />
            <Animated.View style={[styles.tabBarView, {
                height: navigationHeight,
                backgroundColor: Color.theme,
                opacity: getTopOpacity(),
                transform: [{ translateY: translateY }]
            }]}>
                <Animated.Text style={[styles.title]}>漫画分类</Animated.Text>
            </Animated.View>
            <Animated.View style={{
                height: viewportHeight - getStatusBarHeight(),
                transform: [{ translateY: translateY }]
            }}>
                <Tab.Navigator
                    lazy
                    tabBar={(props) => <TopBar {...props} />}
                    pager={(props) => <ViewPagerAdapter {...props} />}
                    tabBarOptions={{
                        scrollEnabled: true,
                        tabStyle: {
                            width: 50,
                            height: 45,
                            padding: 0,
                            margin: 0
                        },
                        labelStyle: {
                            fontSize: 13
                        },
                        indicatorStyle: {
                            height: 3,
                            width: 16,
                            marginLeft: 17,
                            marginBottom: 5,
                            borderRadius: 2,
                            backgroundColor: Color.theme
                        },
                        allowFontScaling: true,
                        activeTintColor: Color.theme,
                        inactiveTintColor: Color.black
                    }}>
                    {myCategoryList.map(item => renderScreen(item))}
                </Tab.Navigator>
            </Animated.View>
        </View>
    );
}
Example #10
Source File: index.tsx    From krmanga with MIT License 4 votes vote down vote up
function Home({ dispatch, commendList, refreshing, navigation, loading, hasMore }: IProps) {

    const headerHeight = useHeaderHeight();
    const scrollY: Animated.Value = useRef(new Animated.Value(0)).current;
    const [endReached, setEndReached] = useState<boolean>(false);

    useEffect(() => {
        SplashScreen.hide();//关闭启动屏
        dispatch({
            type: "home/setState",
            payload: {
                headerHeight
            }
        });
        syncImmediate();
        loadCarouselList();
        loadCommendList(true);
    }, []);

    const syncImmediate = () => {
        if (Platform.OS === "android") {
            codePush.checkForUpdate().then((update) => {
                if (update) {
                    navigation.navigate("AppUpdate");
                }
            });
        }
    };

    const loadCarouselList = () => {
        dispatch({
            type: "home/fetchCarouselList"
        });
    };

    const loadCommendList = (refreshing: boolean, callback?: () => void) => {
        dispatch({
            type: "home/fetchCommendList",
            payload: {
                refreshing
            },
            callback
        });
    };

    const renderSectionHeader = ({ section: { title } }: any) => {
        return (
            <View style={styles.sectionHeader}>
                <View style={styles.cell} />
                <Text style={styles.classifyName}>{title}</Text>
            </View>
        );
    };

    const onRefresh = () => {
        loadCarouselList();
        loadCommendList(true);
    };

    const onEndReached = () => {
        if (!hasMore || loading) {
            return;
        }
        setEndReached(true);
        loadCommendList(false, () => {
            setEndReached(false);
        });
    };

    const renderFooter = () => {
        if (endReached) {
            return <More />;
        }
        if (!hasMore) {
            return <End />;
        }

        return null;
    };

    const goBrief = useCallback((data: IBook) => {
        navigation.navigate("Brief", {
            id: data.id
        });
    }, []);

    const renderItem = ({ item }: SectionListRenderItemInfo<IBook[]>) => {
        return (
            <View style={styles.contentContainer}>
                {item.map(data => {
                    return (
                        <BookCover data={data} goBrief={goBrief} key={data.id} />
                    );
                })}
            </View>
        );
    };

    const getTopBarColor = useCallback(() => {
        return scrollY.interpolate({
            inputRange: [0, maxScroll],
            outputRange: [0, 1],
            extrapolate: "clamp"
        });
    }, []);

    const TopBarColor = getTopBarColor();

    return (
        (loading && refreshing) ? <HomePlaceholder /> :
            <View style={{ flex: 1 }}>
                <CarouselBlurBackground />
                <TopBarWrapper navigation={navigation} topBarColor={TopBarColor} />
                <SectionList
                    keyExtractor={(item, index) => `item-${item["id"]}-key-${index}`}
                    ListHeaderComponent={() => <Carousel />}
                    renderSectionHeader={renderSectionHeader}
                    onRefresh={onRefresh}
                    refreshing={refreshing}
                    sections={commendList}
                    stickySectionHeadersEnabled={true}
                    scrollEventThrottle={1}
                    onScroll={Animated.event(
                        [{
                            nativeEvent: { contentOffset: { y: scrollY } }
                        }],
                        {
                            useNativeDriver: false
                        }
                    )}
                    onEndReached={onEndReached}
                    onEndReachedThreshold={0.1}
                    renderItem={renderItem}
                    extraData={endReached}
                    ListFooterComponent={renderFooter}
                />
            </View>
    );
}
Example #11
Source File: SearchBar.tsx    From krmanga with MIT License 4 votes vote down vote up
function SearchBar({ navigation, dispatch, searchTitle }: IProps) {

    const headerHeight = useHeaderHeight();

    const onSubmitEditing = () => {
        if (searchTitle.length > 0) {
            dispatch({
                type: "search/setState",
                payload: {
                    showBookView: true
                }
            });
            dispatch({
                type: "search/fetchBookList",
                payload: {
                    title: searchTitle,
                    refreshing: true
                },
                addSearch: (isAdd: boolean) => {
                    if (isAdd) {
                        dispatch({
                            type: "search/addSearch",
                            payload: {
                                title: searchTitle
                            }
                        });
                    }
                }
            });
        }
    };

    const debounce = (cb: any, wait: number) => {
        let timeout = tempTimeout;
        if (timeout !== null) {
            clearTimeout(timeout);
        }
        tempTimeout = setTimeout(() => {
            tempTimeout = null;
            cb && cb();
        }, wait);
    };

    const onChangeText = (title: string) => {
        dispatch({
            type: "search/setState",
            payload: {
                searchTitle: title
            }
        });

        if (title && title.length > 0) {
            debounce(() => loadData(title), 500);
        } else {
            dispatch({
                type: "search/setState",
                payload: {
                    showSimpleView: false,
                    showBookView: false
                }
            });
            if (tempTimeout !== null) {
                clearTimeout(tempTimeout);
            }
        }
    };

    const cleanTitle = useCallback(() => {
        dispatch({
            type: "search/setState",
            payload: {
                searchTitle: "",
                showSimpleView: false,
                showBookView: false
            }
        });
    }, []);

    const loadData = (title: string) => {
        dispatch({
            type: "search/fetchSimpleList",
            payload: {
                searchTitle: title
            }
        });
    };

    return (
        <View style={[styles.wrapper, { height: headerHeight + 15 }]}>
            <View style={styles.container}>
                <View style={styles.leftView}>
                    <Icon name="icon-search" style={styles.searchIcon} size={18} />
                    <TextInput style={styles.searchInput}
                               onSubmitEditing={onSubmitEditing}
                               maxLength={20}
                               placeholder={"搜索关键字..."}
                               onChangeText={(text) => {
                                   onChangeText(text);
                               }}
                               value={searchTitle}
                    />
                    <Touchable onPress={cleanTitle}>
                        <Icon name="icon-chacha" style={styles.cleanTitle} size={18} />
                    </Touchable>
                </View>
                <Touchable onPress={() => navigation.goBack()}>
                    <View style={styles.rightView}>
                        <Text>取消</Text>
                    </View>
                </Touchable>
            </View>
        </View>
    );
}
Example #12
Source File: index.tsx    From lets-fork-native with MIT License 4 votes vote down vote up
export default function Details(props: Props): React.ReactElement {
  const headerHeight = useHeaderHeight()
  const { restaurant: defaultRestaurant, photos } = props
  const [restaurant, setRestaurant] = React.useState(defaultRestaurant)

  React.useEffect(() => {
    const fetchData = async (): Promise<void> => {
      // More details about the restaurant can be fetched from
      // the server. This can be triggered off a feature flag in the future.
      // For the time being this saves on api requests to yelp.
      if (false) { // eslint-disable-line
        try {
          const rest = await getRestaurant(defaultRestaurant.id)
          setRestaurant({
            ...rest,
            ...defaultRestaurant,
          })
        } catch (err) {
          console.log(err)
        }
      } else {
        setRestaurant(defaultRestaurant)
      }
    }

    fetchData()
  }, [defaultRestaurant])

  const imageHeight = env.ADS
    ? height - headerHeight - 50
    : height - headerHeight

  const images = [(
    <Image
      key={restaurant.image_url}
      style={{
        ...styles.image,
        height: imageHeight,
      }}
      source={{ uri: restaurant.image_url, cache: 'force-cache' }}
    />
  )]

  if (restaurant.photos?.length) {
    restaurant.photos.forEach((url) => {
      if (url !== restaurant.image_url) {
        images.push(
          <Image
            key={url}
            style={{
              ...styles.image,
              height: imageHeight,
            }}
            source={{ uri: url, cache: 'force-cache' }}
          />,
        )
      }
    })
  }

  return (
    <View
      style={{
        ...styles.container,
        minHeight: (height - headerHeight) * 0.8,
      }}
    >
      {
        photos ? (
          <ScrollView
            horizontal
            alwaysBounceHorizontal={false}
            showsHorizontalScrollIndicator
            scrollEventThrottle={10}
            pagingEnabled
            onScroll={
              Animated.event(
                [{ nativeEvent: { contentOffset: { x: new Animated.Value(0) } } }],
                { useNativeDriver: true },
              )
            }
          >
            {images}
          </ScrollView>
        ) : null
      }
      <View>
        <Text style={[styles.text, styles.name]}>{restaurant.name}</Text>
        <View style={styles.rating}>
          <Rating rating={restaurant.rating} size="sm" />
          <Text style={styles.text}>{`•   ${restaurant.review_count} reviews`}</Text>
        </View>
        <Text
          style={styles.text}
        >
          {
            restaurant.price
              ? `${restaurant.price}   •   ${restaurant?.categories?.map((c) => c.title).join(', ')}`
              : restaurant?.categories?.map((c) => c.title).join(', ')
          }
        </Text>
        { restaurant?.transactions?.length
          ? (
            <Text style={styles.text}>
              {restaurant.transactions.map((tran) => `${tran[0].toUpperCase()}${tran.replace('_', ' ').substring(1)}`).join('   •   ')}
            </Text>
          ) : null}
      </View>
      <View style={styles.section}>
        <TouchableOpacity onPress={(): void => call(restaurant.display_phone)}>
          <MaterialIcons name="phone" size={32} />
        </TouchableOpacity>
        <TouchableOpacity onPress={(): Promise<any> => Linking.openURL(restaurant.url)}>
          <FontAwesome name="yelp" size={32} color={colors.yelpRed} />
        </TouchableOpacity>
      </View>
      {
        restaurant?.coordinates?.latitude && restaurant?.coordinates?.longitude
          ? (
            <View style={styles.mapContainer}>
              <MapView
                region={{
                  latitude: restaurant.coordinates.latitude,
                  longitude: restaurant.coordinates.longitude,
                  latitudeDelta: 0.005,
                  longitudeDelta: 0.05,
                }}
                style={styles.map}
                rotateEnabled={false}
                scrollEnabled={false}
                zoomEnabled={false}
              >
                <Marker
                  coordinate={{
                    latitude: restaurant.coordinates.latitude,
                    longitude: restaurant.coordinates.longitude,
                  }}
                  title={restaurant.name}
                />
              </MapView>
            </View>
          ) : null
      }
      <TouchableOpacity
        style={styles.section}
        onPress={(): void => {
          const url = Platform.select({
            ios: `maps:0,0?q=${restaurant.coordinates.latitude},${restaurant.coordinates.longitude}`,
            android: `geo:0,0?q=${restaurant.coordinates.latitude},${restaurant.coordinates.longitude}`,
          })
          if (url) {
            Linking.openURL(url)
          }
        }}
      >
        <Text style={styles.directionText}>Get Directions</Text>
        <MaterialIcons name="directions" size={32} />
      </TouchableOpacity>
      {
        restaurant.hours
          ? <Hours hours={restaurant.hours} />
          : null
      }
    </View>
  )
}
Example #13
Source File: PartyScreen.tsx    From lets-fork-native with MIT License 4 votes vote down vote up
PartyScreen = React.memo((props: Props) => {
  const {
    navigation, party, route, setParty, ws,
  } = props

  const [snapIndex, setSnapIndex] = React.useState(2)
  const [finished, setFinished] = React.useState<boolean>(false)
  const [restaurants, setRestaurants] = React.useState<Restaurant[]>()
  const headerHeight = useHeaderHeight()
  const viewHeight = env.ADS ? height - headerHeight - 50 : height - headerHeight

  if (party?.error) {
    Alert.alert(
      'Yike! Something went wrong',
      party.error,
      [
        {
          text: 'OK',
          onPress: (): void => {
            navigation.navigate('Home')
            setParty({} as Party)
          },
        },
      ],
      { cancelable: false },
    )
  }

  // Request more cards with 3 remaining to prevent
  // having to show loader
  React.useEffect(() => {
    if (restaurants && restaurants?.length === 3) {
      ws.send(JSON.stringify({ type: 'request-more' }))
    }
  }, [restaurants, restaurants?.length, ws])

  // Deep linking will open the app to the party screen
  // but the party still needs to be joined
  React.useEffect(() => {
    if (route?.params?.id) {
      ws.send(JSON.stringify({ type: 'join', payload: { party_id: route?.params?.id } }))
    }
  }, [route?.params?.id, ws])

  // When anyone requests more cards, they are set in current
  // and this useEffect loads the new cards into the restaurants array
  const prevState = usePrevious(party || {} as Party)
  React.useEffect(() => {
    if (JSON.stringify(prevState.current) !== JSON.stringify(party?.current)) {
      if (party?.current?.length && restaurants) {
        const res = [...restaurants, ...party?.current]
        setRestaurants(res)
      }
    }
  }, [party, prevState, restaurants])

  // Custom android back button
  useFocusEffect( // eslint-disable-line
    React.useCallback(() => {
      const onBackPress = (): boolean => {
        Alert.alert(
          'Are you sure you want to exit?',
          'Exiting will make you lose all data in this party',
          [
            { text: 'Cancel' },
            {
              text: 'OK',
              onPress: (): void => {
                ws.send(JSON.stringify({ type: 'quit' }))
                navigation.navigate('Home')
                setParty({} as Party)
              },
            },
          ],
          { cancelable: true },
        )
        return true
      }

      BackHandler.addEventListener('hardwareBackPress', onBackPress)

      return (): void => BackHandler.removeEventListener('hardwareBackPress', onBackPress)
    }, [navigation, setParty, ws]),
  )

  const handleSwipeRight = (id: string): void => {
    ws.send(JSON.stringify({ type: 'swipe-right', payload: { restaurant_id: id } }))
  }

  if (party?.status === 'waiting') {
    return <Share party={party} ws={ws} />
  }

  if (finished || party?.total === 0) {
    return (
      <View
        style={{
          ...styles.waiting,
          height: viewHeight,
        }}
      >
        <Text style={styles.text}>
          No more restaurants.
          Go through the list again or try expanding your search range.
        </Text>
        <Button
          size="sm"
          color="purple"
          onPress={(): void => {
            setFinished(false)
            setRestaurants(party?.restaurants)
          }}
        >
          START OVER
        </Button>
      </View>
    )
  }

  if (!party || !party.restaurants) {
    return (
      <View
        style={{
          ...styles.waiting,
          height: viewHeight,
        }}
      >
        <ActivityIndicator size="large" />
      </View>
    )
  }

  const current = restaurants?.length
    ? restaurants[0] : party.restaurants[0]

  return (
    <SafeAreaView style={styles.container}>
      <View
        // disable swiping while BottomSheet is open
        pointerEvents={snapIndex !== 2 ? 'none' : 'auto'}
        style={{ height: viewHeight, zIndex: 0 }}
      >
        <SwipeWindow
          handleSwipeRight={handleSwipeRight}
          restaurants={restaurants || party.restaurants}
          setFinished={setFinished}
          setRestaurants={setRestaurants}
        />
      </View>
      <ScrollBottomSheet
        componentType="ScrollView"
        contentContainerStyle={styles.scrollBottomSheet}
        snapPoints={[100, 100, viewHeight - BOTTOM_BAR_HEIGHT]}
        initialSnapIndex={2}
        onSettle={setSnapIndex}
        renderHandle={(): React.ReactElement => <Handle />}
        animationConfig={{
          duration: 100,
        }}
      >
        <Details restaurant={current} />
      </ScrollBottomSheet>
    </SafeAreaView>
  )
})