react-native#BackHandler TypeScript Examples

The following examples show how to use react-native#BackHandler. 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: BatteryModal.tsx    From hamagen-react-native with MIT License 6 votes vote down vote up
BatteryModal: FunctionComponent<Props> = ({ navigation }) => {
  const dispatch = useDispatch();

  useEffect(() => {
    BackHandler.addEventListener('hardwareBackPress', handleExit);

    return () => {
      BackHandler.removeEventListener('hardwareBackPress', handleExit);
    };
  });

  const handleExit = async () => {
    dispatch({ type: USER_DISABLED_BATTERY, payload: false });
    await AsyncStorage.setItem(USER_AGREED_TO_BATTERY, 'false');
    navigation.goBack();
    return true;
  };

  return (
    <View style={styles.container}>
      <HeaderButton type="close" onPress={handleExit} />
      <BatteryPermission
        onEnd={() => {
          navigation.goBack();
        }}
      />
    </View>
  );
}
Example #2
Source File: QuestionaireForm.tsx    From SQUID with MIT License 6 votes vote down vote up
FormBackHandler = ({ onBack }) => {
  useEffect(() => {
    const backHandler = BackHandler.addEventListener(
      "hardwareBackPress",
      () => {
        onBack()
        return true
      }
    )

    return () => backHandler.remove();
  }, [onBack])
  return null
}
Example #3
Source File: Toolbar.tsx    From mobile with Apache License 2.0 6 votes vote down vote up
Toolbar = ({title, navText, onIconClicked, accessibilityTitleAutoFocus}: ToolbarProps) => {
  useEffect(() => {
    if (Platform.OS !== 'android') {
      return;
    }
    const subscription = BackHandler.addEventListener('hardwareBackPress', () => {
      onIconClicked();
      return true;
    });
    return () => subscription.remove();
  }, [onIconClicked]);

  return (
    <Box flexDirection="row" alignItems="center" justifyContent="space-between" minHeight={56}>
      <Box style={styles.invisible}>
        <Button disabled text={navText} variant="text" onPress={() => {}} />
      </Box>
      {title !== '' && (
        <Box flex={1} justifyContent="center" minWidth={100}>
          <Text
            variant="bodySubTitle"
            color="overlayBodyText"
            textAlign="center"
            accessibilityAutoFocus={accessibilityTitleAutoFocus}
          >
            {title}
          </Text>
        </Box>
      )}
      <Box>
        <Button testID="toolbarCloseButton" text={navText} variant="close" onPress={onIconClicked} />
      </Box>
    </Box>
  );
}
Example #4
Source File: useBackButton.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
export default function useBackButton(
  ref: React.RefObject<NavigationContainerRef>
) {
  React.useEffect(() => {
    const subscription = BackHandler.addEventListener(
      'hardwareBackPress',
      () => {
        const navigation = ref.current;

        if (navigation == null) {
          return false;
        }

        if (navigation.canGoBack()) {
          navigation.goBack();

          return true;
        }

        return false;
      }
    );

    return () => subscription.remove();
  }, [ref]);
}
Example #5
Source File: DataUpload.tsx    From ito-app with GNU General Public License v3.0 6 votes vote down vote up
DataUpload: React.FC<{
  navigation: DataUploadScreenNavigationProp;
}> = ({navigation}) => {
  const {t} = useTranslation();

  useFocusEffect(
    useCallback(() => {
      const onBackPress = (): boolean => {
        navigation.navigate('Home');
        return true;
      };
      BackHandler.addEventListener('hardwareBackPress', onBackPress);
      return (): void =>
        BackHandler.removeEventListener('hardwareBackPress', onBackPress);
    }, [navigation]),
  );

  return (
    <View style={global.container}>
      <Header
        navigationButton={{
          title: 'home',
          fn: (): void => navigation.navigate('Home'),
        }}
        showHelp={true}
      />
      <Text style={styles.thanks}>{t('dataUpload.thanks')}</Text>
    </View>
  );
}
Example #6
Source File: Start.tsx    From save-food with MIT License 6 votes vote down vote up
Start = ({ navigation }: Props) => {
	const { useSubscribe } = useSettingsContext()
	const settings = useSubscribe((s) => s.settings)
	const translations = useSubscribe((s) => s.translations.Start)

	useEffect(() => {
		BackHandler.addEventListener('hardwareBackPress', () => true)

		return () => {
			BackHandler.removeEventListener('hardwareBackPress', () => true)
		}
	}, [])

	const doneBtnHandle = async () => {
		await AsyncStorage.removeItem('start')
		navigation.replace('Home')
	}

	return (
		<Onboarding
			showSkip={false}
			onDone={doneBtnHandle}
			pages={getPages(translations, settings.lang)}
		/>
	)
}
Example #7
Source File: Toolbar.tsx    From mobile with Apache License 2.0 5 votes vote down vote up
Toolbar = ({title, navText, navIcon, navLabel, onIconClicked}: ToolbarProps) => {
  useEffect(() => {
    if (Platform.OS !== 'android') {
      return;
    }
    const subscription = BackHandler.addEventListener('hardwareBackPress', () => {
      onIconClicked();
      return true;
    });
    return () => subscription.remove();
  }, [onIconClicked]);

  if (Platform.OS === 'android') {
    return (
      <Box flex={1} flexDirection="row" alignItems="center" justifyContent="flex-start" padding="none" maxHeight={56}>
        {navIcon && <TouchableIcon iconName={navIcon} label={navLabel} onPress={onIconClicked} />}
        <Box padding="m">
          <Text variant="bodySubTitle" color="overlayBodyText" accessibilityRole="header">
            {title}
          </Text>
        </Box>
      </Box>
    );
  }
  return (
    <Box flexDirection="row" alignItems="center" minHeight={56}>
      <Box>
        <Button text={navText} variant="text" onPress={onIconClicked} />
      </Box>
      {title !== '' && (
        <Box flex={1} justifyContent="center" minWidth={100}>
          <Text variant="bodySubTitle" color="overlayBodyText" textAlign="center" accessibilityRole="header">
            {title}
          </Text>
        </Box>
      )}
      <Box style={styles.invisible}>
        <Button disabled text={navText} variant="text" onPress={() => {}} />
      </Box>
    </Box>
  );
}
Example #8
Source File: AllSet.tsx    From hamagen-react-native with MIT License 5 votes vote down vote up
AllSet = ({ navigation, strings: { allSet: { allGood } }, locale, notificationData, setOnboardingRoutes }: Props) => {
  useEffect(() => {
    setTimeout(() => {
      onboardingDoneActions();
    }, 3000);
  }, []);

  useFocusEffect(
    React.useCallback(() => {
      const onBackPress = () => {
        return true;
      };

      BackHandler.addEventListener('hardwareBackPress', onBackPress);

      return () => {
        BackHandler.removeEventListener('hardwareBackPress', onBackPress);
      };
    }, [])
  );

  const onboardingDoneActions = async () => {
    try {
      await AsyncStorage.multiSet([
        [IS_FIRST_TIME, 'true'],
        [DID_CLUSTER_LOCATIONS, 'true'],
        [SICK_DB_UPDATED, 'true'],
        [MENU_DOT_LAST_SEEN, VERSION_BUILD]
      ]);
      // TODO: figure out why replace crash android on first upload
      setOnboardingRoutes(false);

      await startForegroundTimer();
      await startSampling(locale, notificationData);
      await scheduleTask();
    } catch (error) {
      onError({ error });
    }
  };

  return (
    <View style={styles.container}>
      <LottieView
        style={styles.loader}
        source={require('../../assets/lottie/loader no mask.json')}
        resizeMode="cover"
        autoPlay
        loop
      />
      <Text style={styles.text} black>{allGood}</Text>
    </View>
  );
}
Example #9
Source File: FullscreenImageZoom.tsx    From vsinder with Apache License 2.0 5 votes vote down vote up
FullscreenImageZoomProvider: React.FC = ({ children }) => {
  const srcRef = useRef("");
  const [src, setSrc] = useState("");
  srcRef.current = src;

  useEffect(() => {
    const onBackPress = () => {
      if (srcRef.current) {
        setSrc("");
        return true;
      } else {
        return false;
      }
    };
    BackHandler.addEventListener("hardwareBackPress", onBackPress);

    return () =>
      BackHandler.removeEventListener("hardwareBackPress", onBackPress);
  }, []);

  return (
    <FullscreenImageZoomContext.Provider value={setSrc}>
      {src ? (
        <SafeAreaView
          style={{
            position: "absolute",
            top: 0,
            zIndex: 50,
            flex: 1,
            backgroundColor: "#000",
          }}
        >
          <MyButton onPress={() => setSrc("")}>close</MyButton>
          <ImageZoom
            cropWidth={width}
            cropHeight={height}
            imageWidth={codeImageWidth}
            imageHeight={codeImageHeight}
          >
            <ScrollView>
              <Image
                style={{
                  borderRadius: 9,
                  height: codeImageHeight,
                  width: codeImageWidth,
                }}
                resizeMode="contain"
                source={{ uri: src }}
              />
            </ScrollView>
          </ImageZoom>
        </SafeAreaView>
      ) : null}
      {children}
    </FullscreenImageZoomContext.Provider>
  );
}
Example #10
Source File: registration.tsx    From iotc-cpm-sample with MIT License 5 votes vote down vote up
export function Registration() {
  const {state, dispatch} = useContext(ConfigContext);
  const [user] = useUser();
  const [numeric, setNumeric] = useState(false);
  const [qr, setQR] = useState(false);

  const onVerify = async (data: string) => {
    if (user == null) {
      throw new Error('User not logged in');
    }
    const creds = DecryptCredentials(data, user.id);
    // connect to IoTCentral before passing over
    let iotc = new IoTCClient(
      creds.deviceId,
      creds.scopeId,
      IOTC_CONNECT.DEVICE_KEY,
      creds.deviceKey,
    );
    iotc.setModelId(creds.modelId);
    iotc.setLogging(IOTC_LOGGING.ALL);
    await iotc.connect();
    dispatch({
      type: 'CONNECT',
      payload: iotc,
    });
  };

  const onBack = () => {
    setQR(false);
    setNumeric(false);
    return true;
  };

  useEffect(() => {
    BackHandler.addEventListener('hardwareBackPress', onBack);
    return () => {
      BackHandler.removeEventListener('hardwareBackPress', onBack);
    };
  }, []);

  if (!user || state.centralClient !== undefined) {
    return null;
  }

  if (numeric) {
    return <NumericCode onVerify={onVerify} onClose={onBack} />;
  }
  if (qr) {
    return <QRCode onVerify={onVerify} onClose={onBack} />;
  }
  return (
    <View style={{flex: 4, ...style.container}}>
      <View style={{flex: 1, justifyContent: 'center'}}>
        <Name style={style.title}>{title}</Name>
      </View>
      <View style={{flex: 1, justifyContent: 'center'}}>
        <Name style={style.instructions}>{instructions}</Name>
      </View>
      <View style={{flex: 2}}>
        <CPMButton
          mode="outlined"
          style={style.button}
          onPress={() => setNumeric(true)}>
          {code}
        </CPMButton>
        <CPMButton
          mode="contained"
          style={{marginBottom: 50, ...style.button}}
          onPress={() => setQR(true)}>
          {scan}{' '}
        </CPMButton>
        <SimulatedButton />
      </View>
      <Footer text={footerText} />
    </View>
  );
}
Example #11
Source File: useBackgroundOverlay.tsx    From react-native-template with MIT License 5 votes vote down vote up
function useBackgroundOverlay(visible: boolean, onTouchStart: () => void) {
  const isFirstRender = useRef(true)
  const opacity = useMemo(
    () =>
      isFirstRender.current
        ? 0
        : timing({
            from: visible ? 0 : 0.2,
            to: visible ? 0.2 : 0,
          }),
    [visible]
  )

  useEffect(() => {
    isFirstRender.current = false
  }, [])

  useFocusEffect(
    React.useCallback(() => {
      const onBackPress = () => {
        if (visible) {
          onTouchStart()
          return true
        } else {
          return false
        }
      }
      BackHandler.addEventListener("hardwareBackPress", onBackPress)

      return () =>
        BackHandler.removeEventListener("hardwareBackPress", onBackPress)
    }, [onTouchStart, visible])
  )

  return (
    <>
      {visible && <StatusBar backgroundColor="grey" animated />}
      <Animated.View
        pointerEvents={visible ? "auto" : "none"}
        onTouchStart={() => {
          Keyboard.dismiss()
          onTouchStart()
        }}
        style={[
          styles.overlay,
          {
            opacity,
          },
        ]}
      />
    </>
  )
}
Example #12
Source File: ExposureInstructions.tsx    From hamagen-react-native with MIT License 4 votes vote down vote up
ExposureInstructions = ({ navigation, route }: Props) => {
  const dispatch = useDispatch();
  const {
    isRTL,
    locale,
    languages,
    externalUrls,
    strings: {
      scanHome: {
        atPlace,
        betweenHours,
        inDate,
        fromHour,
        deviceCloseTag,
        locationCloseTag
      },
      exposureInstructions: {
        title,
        editBtn,
        subTitle,
        showLess,
        showMore,
        finishBtn,
        reportSite,
        updateTitle,
        updateSubTitle,
        goIntoIsolation,
        reportIsolation,
        allInstructions,
        themInstructions,
      }
    },
  } = useSelector<Store, LocaleReducer>(state => state.locale);

  const exposures = useSelector<Store, Exposure[]>(state => state.exposures.pastExposures.filter((exposure: Exposure) => exposure.properties.wasThere));
  const [shouldShowMore, setShowMore] = useState(false);

  useEffect(() => {
    SplashScreen.hide();
    // if edit button need to be shown then Exposure Instructions don't need to persists
    AsyncStorage.setItem(INIT_ROUTE_NAME, 'ExposureInstructions');
    BackHandler.addEventListener('hardwareBackPress', () => true);

    return () => {
      BackHandler.removeEventListener('hardwareBackPress', () => true);
    };
  }, []);

  const [furtherInstructions, reportForm] = useMemo(() => {
    const relevantLocale: string = Object.keys(languages.short).includes(locale) ? locale : 'he';

    return [
      externalUrls.furtherInstructions[relevantLocale],
      externalUrls.reportForm[relevantLocale]
    ];
  }, [languages.short, locale]);

  const ExposureList = useMemo(() => exposures.map((exposure: Exposure) => {
    let ListText;

    if (exposure.properties.BLETimestamp) {
      const time = moment(exposure.properties.BLETimestamp).startOf('hour');

      const exposureDate = time.format('DD.MM.YY');
      const exposureStartHour = time.format('HH:mm');
      const exposureEndHour = time.add(1, 'hour').format('HH:mm');

      ListText = (<Text>{`${deviceCloseTag}: ${inDate} ${exposureDate} ${betweenHours} ${exposureStartHour}-${exposureEndHour}`}</Text>);
    } else {
      const { Place, fromTime } = exposure.properties;
      const time = moment();
      ListText = (<Text>{`${locationCloseTag}: ${atPlace}${Place} ${inDate} ${moment(fromTime).format('DD.MM.YY')} ${fromHour} ${moment(fromTime).format('HH:mm')}`}</Text>);
    }


    return (
      <Text style={{ fontSize: IS_SMALL_SCREEN ? 14 : 16, lineHeight: 17, marginVertical: IS_SMALL_SCREEN ? 5 : 10, letterSpacing: 0.2, textAlign: isRTL ? 'right' : 'left' }} key={exposure.properties.OBJECTID}>
        <Text bold>• </Text>
        {ListText}

      </Text>
    );
  }), [exposures, locale]);

  const renderActionButton = (icon: number, text: string, buttonText: string, action: () => void) => (
    <View style={[styles.actionButtonContainer, IS_SMALL_SCREEN ? styles.actionButtonContainerSmall : styles.actionButtonContainerBig, IS_SMALL_SCREEN && { flexDirection: isRTL ? 'row-reverse' : 'row' }]}>
      <Icon source={icon} width={22} height={35} />

      <Text style={styles.actionText}>{text}</Text>

      <TouchableOpacity style={styles.button} onPress={action}>
        <Text style={styles.buttonText} bold>{buttonText}</Text>
      </TouchableOpacity>
    </View>
  );

  const RenderHeader = useMemo(() => route.params?.update
    ? (
      <>
        <Icon source={require('../../assets/main/exposureRefresh.png')} width={86} customStyles={{ marginBottom: 15 }} />
        <Text style={styles.title} bold>{updateTitle}</Text>
        <Text style={{ marginBottom: 3 }}>{updateSubTitle}</Text>
      </>
    )
    : (
      <>
        <Text style={styles.title} bold>{title}</Text>
        <Text style={{ marginBottom: 3 }}>{subTitle}</Text>
        {shouldShowMore ? ExposureList : ExposureList.slice(0, 4)}
        {exposures.length > 4 && (
          <TouchableOpacity
            style={{
              flexDirection: isRTL ? 'row' : 'row-reverse',
              alignItems: 'center'
            }}
            onPress={() => {
              LayoutAnimation.create(
                300,
                LayoutAnimation.Types.spring,
                LayoutAnimation.Properties.scaleXY
              );
              setShowMore(!shouldShowMore);
            }}
          >
            <Icon source={require('../../assets/main/showMore.png')} width={9} height={5} customStyles={{ marginHorizontal: 7, transform: [{ rotateZ: shouldShowMore ? '180deg' : '0deg' }] }} />
            <Text style={{ color: MAIN_COLOR, fontSize: 13 }} bold>{shouldShowMore ? showLess : showMore}</Text>
          </TouchableOpacity>
        )}
      </>
    ),

  [route.params?.update, shouldShowMore]);

  return (

    <ScrollView
      bounces={false}
      contentContainerStyle={styles.subContainer}
      showsVerticalScrollIndicator={false}
    >
      {route.params?.showEdit && (
        <TouchableOpacity
          hitSlop={HIT_SLOP}
          style={{
            alignContent: 'center',
            alignItems: 'center',
            position: 'absolute',
            top: PADDING_TOP(IS_SMALL_SCREEN ? 10 : 28),
            flexDirection: isRTL ? 'row' : 'row-reverse',
            [!isRTL ? 'right' : 'left']: IS_SMALL_SCREEN ? 10 : 25,
          }}
          onPress={() => navigation.navigate('ExposureDetected')}
        >
          <Icon
            width={IS_SMALL_SCREEN ? 20 : 24}
            source={require('../../assets/main/back.png')}
            customStyles={{
              transform: [{ rotate: !isRTL ? '0deg' : '180deg' }]
            }}
          />
          <Text
            bold
            style={{
              fontSize: IS_SMALL_SCREEN ? 13 : 15,
              color: MAIN_COLOR,
              marginHorizontal: IS_SMALL_SCREEN ? 5 : 8
            }}
          >
            {editBtn}
          </Text>
        </TouchableOpacity>
      )}


      <View style={{ justifyContent: 'flex-start', alignItems: 'center' }}>

        {RenderHeader}

      </View>
      <View style={{ justifyContent: 'space-between' }}>
        <Text style={{ marginBottom: IS_SMALL_SCREEN ? 12 : 25 }} bold>{themInstructions}</Text>
        <View style={!IS_SMALL_SCREEN && { width: SCREEN_WIDTH - (23 * 2), flexDirection: isRTL ? 'row-reverse' : 'row', flexWrap: 'wrap', justifyContent: 'space-between', }}>
          {renderActionButton(require('../../assets/main/isolation.png'), goIntoIsolation, allInstructions, () => Linking.openURL(furtherInstructions))}
          {renderActionButton(require('../../assets/main/report.png'), reportIsolation, reportSite, () => Linking.openURL(reportForm))}
        </View>
        <Text
          bold
          onPress={() => {
            navigation.navigate('ScanHome');
            dispatch(moveAllToPastExposures());
            AsyncStorage.removeItem(INIT_ROUTE_NAME);
          }}
          style={{
            color: MAIN_COLOR,
            marginTop: IS_SMALL_SCREEN ? 22 : 32,
            fontSize: IS_SMALL_SCREEN ? 14 : 16
          }}
        >
          {finishBtn}
        </Text>
      </View>
    </ScrollView>

  );
}
Example #13
Source File: ExposuresDetected.tsx    From hamagen-react-native with MIT License 4 votes vote down vote up
ExposuresDetected = ({ navigation }: ExposuresDetectedProps) => {
  const dispatch = useDispatch();
  const { isRTL, strings: { scanHome: { inDate, fromHour, wereYouThere, wasNotMe, wasMe, doneBtn, suspectedExposure, events, possibleExposure, atPlace, showOnMap, betweenHours, possibleExposureBLE, locationCloseTag, deviceCloseTag, wasMeBle, wasMeOnly } } } = useSelector<Store, LocaleReducer>(state => state.locale);
  const { exposures } = useSelector<Store, ExposuresReducer>(state => state.exposures);

  const [anim] = useState(new Animated.Value(SCREEN_HEIGHT * 0.08));
  const isOneBle = useMemo(() => exposures.length === 1 && exposures[0].properties.BLETimestamp !== null, [exposures]);
  const flatListRef = useRef(null);

  useEffect(() => {
    if (exposures.length === 0) {
      navigation.navigate('ScanHome');
    } else {
      SplashScreen.hide();
      AsyncStorage.setItem(INIT_ROUTE_NAME, 'ExposureDetected');
      BackHandler.addEventListener('hardwareBackPress', () => true);

      return () => {
        BackHandler.removeEventListener('hardwareBackPress', () => true);
      };
    }
  }, []);


  const showButton = (duration: number = 300) => {
    Animated.timing(anim, {
      toValue: 0,
      duration,
      useNativeDriver: true,
      delay: 300
    }).start();
  };

  // show button when moving to another page
  //  use case for single exposure. the user moves on click but if he returns for edit
  useFocusEffect(
    useCallback(() => {
      if (!isOneBle
        && exposures.every(exposure => exposure.properties.wasThere !== null)) {
        showButton(0);
      }
    }, [])
  );

  const setSelected = (index: number, wasThere: boolean) => {
    dispatch(setExposureSelected({ index, wasThere }));
    if (exposures.length === 1) {
      editDone();
    } else {
      // find index of first card user didn't checked(was or not) and go to there˝
      const emptyIndex = exposures.findIndex(exposure => exposure.properties.wasThere === null || exposure.properties.wasThere === undefined);

      if (emptyIndex === -1) {
        showButton();
      } else if (index + 1 < exposures.length) {
        setTimeout(() => {
          if (flatListRef?.current) {
            flatListRef?.current?.scrollToIndex({
              index: index + 1,
              viewOffset: 10
            });
          }
        }, 300);
      } else {
        // all selected show finish button and findIndex get me last index
        if (emptyIndex === -1 || exposures.length - 1 === emptyIndex) {
          showButton();
        } else {
          flatListRef?.current?.scrollToIndex({
            index: emptyIndex,
            viewOffset: 10
          });
        }
      }
    }
  };

  const editDone = () => {
    dispatch(dismissExposures());
    // check if at least one exposure was checked a been there
    const isExposed = exposures.some((exposure: Exposure) => exposure.properties.wasThere);

    if (isExposed) {
      // move to ExposureInstructions
      const showEdit = exposures.some((exposure: Exposure) => !exposure.properties.BLETimestamp);
      navigation.navigate('ExposureInstructions', { showEdit });
    } else {
      // move to ExposureRelief
      navigation.navigate('ExposureRelief');
      AsyncStorage.removeItem(INIT_ROUTE_NAME);
    }
  };

  const RenderBleExposure = ({ index, exposure: { properties: { BLETimestamp, OBJECTID, Place } } }) => {
    const [exposureDate, exposureStartHour, exposureEndHour] = useMemo(() => {
      const time = moment(BLETimestamp).startOf('hour');

      return [
        time.format('DD.MM.YY'),
        time.format('HH:mm'),
        time.add(1, 'hour').format('HH:mm')
      ];
    }, [BLETimestamp]);


    let LocationText = null;

    if (OBJECTID) {
      LocationText = (
        <>
          <Text style={styles.exposureCardPlace} bold>
            {`${atPlace}${Place}`}
          </Text>
          <View style={styles.exposureCardMapContainer}>
            <Text style={styles.exposureCardMapText} onPress={() => dispatch(showMapModal(exposures[index]))}>{showOnMap}</Text>
          </View>
        </>
      );
    }

    return (
      <Animated.View style={[styles.detailsContainer]}>
        <View style={{ flex: 1, alignItems: 'center' }}>
          <Text style={styles.exposureLength}>{`${index + 1}/${exposures.length}`}</Text>
          <Text style={styles.exposureCardTitle}>{possibleExposureBLE}</Text>
          <Text style={{ fontSize: 17 }} bold>{`${inDate} ${exposureDate}${OBJECTID ? ' ' : '\n'}${betweenHours} ${exposureStartHour}-${exposureEndHour}`}</Text>
          {LocationText}
        </View>
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>

          <TouchableOpacity
            style={[styles.bleActionBtn]}
            onPress={() => setSelected(index, true)}
          >
            <Text style={[styles.actionBtnText, styles.actionBtnSelectedText]} bold>{exposures.length === 1 ? wasMeOnly : wasMeBle}</Text>
          </TouchableOpacity>
        </View>
        <CardIdentifyTag isRTL={isRTL} text={deviceCloseTag} color="rgba(44,191,220,0.5)" />
      </Animated.View>
    );
  };


  const RenderGeoExposure = ({ index, exposure: { properties: { Place, fromTime, OBJECTID, wasThere } } }: RenderExposureProps) => {
    const [wasThereSelected, wasNotThereSelected] = useMemo(() => {
      if (wasThere === null) { return [false, false]; }
      return [wasThere, !wasThere];
    }, [wasThere]);

    const [exposureDate, exposureHour] = useMemo(() => {
      const time = moment(fromTime);
      return [time.format('DD.MM.YY'), time.format('HH:mm')];
    }, [fromTime]);

    return (
      <Animated.View style={[styles.detailsContainer]}>
        <View style={{ alignItems: 'center' }}>
          <Text style={styles.exposureLength}>{`${index + 1}/${exposures.length}`}</Text>
          <Text style={styles.exposureCardTitle}>{possibleExposure}</Text>
          <Text style={styles.exposureCardPlace} bold>
            {`${atPlace}${Place} ${inDate} ${exposureDate} ${fromHour} ${exposureHour}`}
          </Text>
          <View style={styles.exposureCardMapContainer}>
            <Text style={styles.exposureCardMapText} onPress={() => dispatch(showMapModal(exposures[index]))}>{showOnMap}</Text>
          </View>
        </View>
        <View>
          <Text style={styles.actionBtnTitle}>{wereYouThere}</Text>
          <View style={styles.actionBtnContainer}>
            <TouchableOpacity
              style={[styles.actionBtnTouch, wasThereSelected && styles.actionBtnSelected]}
              onPress={() => setSelected(index, true)}
            >
              <Text style={[styles.actionBtnText, wasThereSelected && styles.actionBtnSelectedText]} bold={wasThereSelected}>{wasMe}</Text>
            </TouchableOpacity>
            <TouchableOpacity
              style={[styles.actionBtnTouch, wasNotThereSelected && styles.actionBtnSelected]}
              onPress={() => setSelected(index, false)}
            >
              <Text style={[styles.actionBtnText, wasNotThereSelected && styles.actionBtnSelectedText]} bold={wasNotThereSelected}>{wasNotMe}</Text>
            </TouchableOpacity>
          </View>
        </View>
        <CardIdentifyTag isRTL={isRTL} text={locationCloseTag} color="rgba(217,228,140,0.6)" />
      </Animated.View>
    );
  };

  return (
    <>
      <ScrollView
        contentContainerStyle={styles.container}
        bounces={false}
        showsVerticalScrollIndicator={false}
      >
        <View style={{ alignItems: 'center', marginBottom: IS_SMALL_SCREEN ? 10 : 42 }}>
          <Icon source={require('../../assets/main/exposures.png')} width={IS_SMALL_SCREEN ? 66 : 99} height={IS_SMALL_SCREEN ? 40 : 59} customStyles={{ marginBottom: 33 }} />
          <Text style={styles.title} bold>{`${suspectedExposure} ${exposures.length} ${events}`}</Text>
        </View>

        <FlatList
          horizontal
          bounces={false}
          ref={flatListRef}
          data={exposures}
          nestedScrollEnabled
          keyExtractor={(item: Exposure) => {
            if (item?.properties?.BLETimestamp) { return item.properties.BLETimestamp.toString(); }
            return item.properties.OBJECTID.toString();
          }}
          renderItem={({ item, index }) => (item.properties?.BLETimestamp ? <RenderBleExposure exposure={item} index={index} /> : <RenderGeoExposure exposure={item} index={index} />)}
          showsHorizontalScrollIndicator={false}
          contentContainerStyle={{ paddingLeft: 14, paddingRight: 5 }}
        />
      </ScrollView>
      <Animated.View style={{ transform: [{ translateY: anim }] }}>
        <TouchableOpacity
          onPress={editDone}
          style={{
            width: SCREEN_WIDTH,
            height: SCREEN_HEIGHT * 0.08,
            backgroundColor: MAIN_COLOR,
            justifyContent: 'center',
            flexDirection: isRTL ? 'row-reverse' : 'row',
            alignItems: 'center',
            paddingBottom: PADDING_BOTTOM()
          }}
        >
          <Icon
            source={require('../../assets/main/imDoneUpdate.png')}
            width={IS_SMALL_SCREEN ? 12 : 14}
            height={IS_SMALL_SCREEN ? 10 : 12}
          />
          <Text style={{ fontSize: IS_SMALL_SCREEN ? 15 : 18, color: 'white', marginHorizontal: 6 }} bold>{doneBtn}</Text>
        </TouchableOpacity>
      </Animated.View>
    </>
  );
}
Example #14
Source File: ScanHome.tsx    From hamagen-react-native with MIT License 4 votes vote down vote up
ScanHome: FunctionComponent<ScanHomeProps> = (
  {
    navigation,
    route,
    isRTL,
    strings,
    locale,
    languages,
    externalUrls,
    exposures,
    pastExposures,
    firstPoint,
    enableBle,
    batteryDisabled,
    hideLocationHistory,
    checkForceUpdate,
    checkIfHideLocationHistory,
    checkIfBleEnabled,
    checkIfBatteryDisabled
  }
) => {
  const appStateStatus = useRef<AppStateStatus>('active');
  const [{ hasLocation, hasNetwork, hasGPS }, setIsConnected] = useState({ hasLocation: true, hasNetwork: true, hasGPS: true });

  useEffect(() => {
    init();
  }, []);

  const init = async () => {
    checkIfHideLocationHistory();
    checkIfBatteryDisabled();
    checkConnectionStatusOnLoad();
    checkIfBleEnabled();
    SplashScreen.hide();

    if (exposures.length > 0) {
      navigation.navigate('ExposureDetected');
    } else {
      checkForceUpdate();

      await goToFilterDrivingIfNeeded(navigation);

      const url = await Linking.getInitialURL();


      if (url) {
        return onOpenedFromDeepLink(url, navigation);
      }

      await syncLocationsDBOnLocationEvent();
    }
  };

  useEffect(() => {
    AppState.addEventListener('change', onAppStateChange);
    NetInfo.addEventListener((state: NetInfoState) => setIsConnected({ hasLocation, hasNetwork: state.isConnected, hasGPS }));
    DeviceEventEmitter.addListener(RNSettings.GPS_PROVIDER_EVENT, handleGPSProviderEvent);

    return () => {
      AppState.removeEventListener('change', onAppStateChange);
      DeviceEventEmitter.removeListener(RNSettings.GPS_PROVIDER_EVENT, handleGPSProviderEvent);
    };
  }, [hasLocation, hasNetwork, hasGPS]);

  useFocusEffect(
    React.useCallback(() => {
      const onBackPress = () => {
        BackHandler.exitApp();
        return true;
      };

      BackHandler.addEventListener('hardwareBackPress', onBackPress);

      return () => {
        BackHandler.removeEventListener('hardwareBackPress', onBackPress);
      };
    }, [])
  );

  const checkConnectionStatusOnLoad = async () => {
    const locationPermission = await checkLocationPermissions();
    const networkStatus = await NetInfo.fetch();
    const GPSStatus = await RNSettings.getSetting(RNSettings.LOCATION_SETTING);

    setIsConnected({ hasLocation: locationPermission === RESULTS.GRANTED, hasNetwork: networkStatus.isConnected, hasGPS: GPSStatus === RNSettings.ENABLED });
  };

  const onAppStateChange = async (state: AppStateStatus) => {
    if (state === 'active' && appStateStatus.current !== 'active') {
      checkIfHideLocationHistory();
      checkConnectionStatusOnLoad();
      checkIfBatteryDisabled();
    }

    appStateStatus.current = state;
  };

  const handleGPSProviderEvent = (e: any) => {
    setIsConnected({ hasLocation, hasNetwork, hasGPS: e[RNSettings.LOCATION_SETTING] === RNSettings.ENABLED });
  };

  const exposureState = () => {
    // user never got any exposure detected
    if (exposures.length + pastExposures.length === 0) {
      return 'pristine';
    }

    // check if user past exposures are relevant
    // ie: is less then 14 days old
    if (exposures.some(isAfter14Days) || pastExposures.some(isAfter14Days)) {
      return 'relevant';
    }

    return 'notRelevant';
  };


  const RelevantState = () => {
    if (!hasGPS || !hasLocation) return (<NoGPS {...strings.scanHome.noGPS} />);
    if (!hasNetwork) return (<NoNetwork {...strings.scanHome.noNetwork} />);
    return (
      <NoExposures
        isRTL={isRTL}
        strings={strings}
        firstPoint={firstPoint}
        exposureState={exposureState()}
        hideLocationHistory={hideLocationHistory}
        enableBle={enableBle}
        batteryDisabled={batteryDisabled}
        locale={locale}
        languages={languages}
        externalUrls={externalUrls}
        goToLocationHistory={() => navigation.navigate('LocationHistory')}
        goToBluetoothPermission={() => navigation.navigate('Bluetooth')}
        goToBatteryPermission={() => navigation.navigate('Battery')}
        showBleInfo={route.params?.showBleInfo}
      />
    );
  };


  return (
    <View style={styles.container}>
      <ScanHomeHeader
        enableBle={enableBle}
        languages={languages}
        isRTL={isRTL}
        locale={locale}
        externalUrls={externalUrls}
        strings={strings}
        openDrawer={navigation.openDrawer}
      />
      {RelevantState()}
    </View>
  );
}
Example #15
Source File: NetworkLogger.tsx    From react-native-network-logger with MIT License 4 votes vote down vote up
NetworkLogger: React.FC<Props> = ({ theme = 'light', sort = 'desc' }) => {
  const [requests, setRequests] = useState(
    sortRequests(logger.getRequests(), sort)
  );
  const [request, setRequest] = useState<NetworkRequestInfo>();
  const [showDetails, _setShowDetails] = useState(false);
  const [mounted, setMounted] = useState(false);

  const setShowDetails = useCallback((shouldShow: boolean) => {
    _setShowDetails(shouldShow);

    if (shouldShow) {
      setBackHandler(() => setShowDetails(false));
    } else {
      setBackHandler(undefined);
    }
  }, []);

  useEffect(() => {
    logger.setCallback((updatedRequests: NetworkRequestInfo[]) => {
      setRequests(sortRequests(updatedRequests, sort));
    });

    logger.enableXHRInterception();
    setMounted(true);

    return () => {
      // no-op if component is unmounted
      logger.setCallback(() => {});
    };
  }, [sort]);

  useEffect(() => {
    const onBack = () => {
      if (showDetails) {
        setShowDetails(false);
        return true;
      }

      // Let default back handler take over
      return false;
    };

    const backHandler = BackHandler.addEventListener(
      'hardwareBackPress',
      onBack
    );

    return () => backHandler.remove();
  }, [showDetails, setShowDetails]);

  const showMore = () => {
    Alert.alert('More Options', undefined, [
      {
        text: 'Clear Logs',
        onPress: () => logger.clearRequests(),
        style: 'destructive',
      },
      { text: 'Cancel', style: 'cancel' },
    ]);
  };

  return (
    <ThemeContext.Provider value={theme}>
      <View style={styles.visible}>
        {showDetails && !!request && (
          <View style={styles.visible}>
            <RequestDetails
              onClose={() => setShowDetails(false)}
              request={request}
            />
          </View>
        )}
        <View style={showDetails && !!request ? styles.hidden : styles.visible}>
          {mounted && !logger.enabled && !requests.length ? (
            <Unmounted />
          ) : (
            <RequestList
              requests={requests}
              onShowMore={showMore}
              showDetails={showDetails && !!request}
              onPressItem={(item) => {
                setRequest(item);
                setShowDetails(true);
              }}
            />
          )}
        </View>
      </View>
    </ThemeContext.Provider>
  );
}
Example #16
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>
  )
})