react-native-gesture-handler#TouchableWithoutFeedback TypeScript Examples

The following examples show how to use react-native-gesture-handler#TouchableWithoutFeedback. 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: TabBar.tsx    From react-native-meetio with MIT License 6 votes vote down vote up
Tab: React.FC<TabProps> = ({ title, active, onPress }) => {
  return (
    <TouchableWithoutFeedback {...{ onPress }}>
      <Box
        borderRadius={30}
        backgroundColor={active ? "lightBlueMagenta400" : "white"}
        marginHorizontal="m"
        style={{ paddingHorizontal: 10 }}
      >
        <Text
          variant="title3"
          color={active ? "white" : "blueMagenta"}
          lineHeight={26}
        >
          {title}
        </Text>
      </Box>
    </TouchableWithoutFeedback>
  );
}
Example #2
Source File: index.tsx    From frontatish with MIT License 6 votes vote down vote up
render() {
    const {
      label,
      error,
      containerStyle,
      labelStyle,
      Colors,
      disabled,
      disabledTextStyle,
      inputTextStyle,
    } = this.props;
    styles = getStyles(Colors);
    const bottomBorderStyles = this.getBottomBorderStyles();
    const textInputStyle = disabled
      ? { ...styles.disabledTextStyle, ...disabledTextStyle }
      : { ...styles.inputTextStyle, ...inputTextStyle };
    return (
      <TouchableWithoutFeedback onPress={this.setFocus}>
        <View style={[styles.containerStyle, containerStyle]}>
          <Text style={[styles.lableStyle, labelStyle]}>{label}</Text>
          <TextInput
            {...this.props}
            style={textInputStyle}
            // eslint-disable-next-line no-return-assign
            ref={this.textInput}
            editable={!disabled}
            onFocus={() => this.setActive(true)}
            onBlur={() => this.setActive(false)}
          />
          <View style={bottomBorderStyles} />
          {error && <Text style={styles.errorStyle}>{error}</Text>}
        </View>
      </TouchableWithoutFeedback>
    );
  }
Example #3
Source File: FeedTypeChooser.tsx    From react-native-tiktok-clone with MIT License 6 votes vote down vote up
FeedTypeChooser: React.FC<Props> = (props) => {
  const loadFollowing = useCallback(() => { props.onChange(FOLLOWING_FEEDS); }, []);
  const loadRecommend = useCallback(() => { props.onChange(RECOMMEND_FEEDS); }, []);
  return (
    <View style={[styles.main, props.style]}>
      <View style={styles.type}>
        <TouchableWithoutFeedback onPress={loadFollowing}>
          <Text
            style={[
              styles.text,
              styles.leftText,
              props.type == FOLLOWING_FEEDS && styles.active,
            ]}>
            Following
          </Text>
        </TouchableWithoutFeedback>
      </View>
      <Text style={styles.text}>︲</Text>
      <View style={styles.type}>
        <TouchableWithoutFeedback onPress={loadRecommend}>
          <Text
            style={[
              styles.text,
              styles.rightText,
              props.type == RECOMMEND_FEEDS && styles.active,
            ]}>
            For You
          </Text>
        </TouchableWithoutFeedback>
      </View>
    </View>
  );
}
Example #4
Source File: CustomModal.tsx    From hive-keychain-mobile with MIT License 6 votes vote down vote up
render() {
    let modalHeight = this.props.bottomHalf ? this.height / 2 : this.height;
    const styles = StyleSheetFactory.getSheet({
      modalHeight: modalHeight,
      height: this.height,
      width: this.width,
    });
    return (
      <KeyboardAvoidingView
        style={styles.fullHeight}
        behavior={Platform.OS === 'ios' ? 'padding' : null}>
        <View style={styles.mainContainer}>
          <TouchableWithoutFeedback
            style={{height: '100%'}}
            onPress={() => {
              this.props.outsideClick();
            }}></TouchableWithoutFeedback>
          <View style={styles.modalWrapper}>
            <View style={styles.modalContainer}>
              <LinearGradient
                start={{x: 0, y: 0}}
                end={{x: 0, y: 1}}
                colors={['white', '#B9C9D6']}
                style={styles.gradient}>
                {this.props.children}
              </LinearGradient>
            </View>
          </View>
        </View>
      </KeyboardAvoidingView>
    );
  }
Example #5
Source File: CheckboxExample.tsx    From frontatish with MIT License 5 votes vote down vote up
CheckboxExample = () => {
  const [tick, setTick] = useState(false);
  const Colors = useColors();
  return (
    <SafeAreaView
      style={{
        flex: 1,
        backgroundColor: Colors.white,
        justifyContent: 'center',
        alignItems: 'center',
      }}
    >
      <TouchableWithoutFeedback onPress={() => setTick(!tick)}>
        <CheckBox checked={tick} containerStyle={{ marginVertical: 20 }} />
      </TouchableWithoutFeedback>
      <TouchableWithoutFeedback onPress={() => setTick(!tick)}>
        <CheckBox
          checked={tick}
          containerStyle={{ marginVertical: 20 }}
          size="md"
        />
      </TouchableWithoutFeedback>
      <TouchableWithoutFeedback onPress={() => setTick(!tick)}>
        <CheckBox
          checked={tick}
          containerStyle={{ marginVertical: 20 }}
          size="lg"
        />
      </TouchableWithoutFeedback>
      <TouchableWithoutFeedback onPress={() => setTick(!tick)}>
        <CheckBox
          checked
          disabled
          containerStyle={{ marginVertical: 20 }}
          size="lg"
        />
      </TouchableWithoutFeedback>
      <TouchableWithoutFeedback onPress={() => setTick(!tick)}>
        <CheckBox
          checked={false}
          disabled
          containerStyle={{ marginVertical: 20 }}
          size="lg"
        />
      </TouchableWithoutFeedback>
      <TouchableWithoutFeedback onPress={() => setTick(!tick)}>
        <CheckBox
          checked={tick}
          checkColor={Colors.primary}
          containerStyle={{
            marginVertical: 20,
            backgroundColor: Colors.white,
            borderWidth: 2,
            borderColor: Colors.primary,
          }}
          size="lg"
        />
      </TouchableWithoutFeedback>
    </SafeAreaView>
  );
}
Example #6
Source File: BatterySettings.tsx    From hamagen-react-native with MIT License 5 votes vote down vote up
BatteryPermission: FunctionComponent<Props> = ({ navigation }) => {
  // const [userPressed,setUserPressed] = useState(false)
  const dispatch = useDispatch();
  const { isRTL, strings: {
    batterySettings: { title, description, recommendation, batteryOptimizationOn, batteryOptimizationOff, settingsBtn }
  } } = useSelector<Store, LocaleReducer>(state => state.locale);
  const { batteryDisabled } = useSelector<Store, GeneralReducer>(state => state.general);


  return (
    <View style={[styles.container]}>
      <HeaderButton onPress={navigation.goBack} />
      <View style={{ alignItems: 'center', }}>

        <Icon
          width={106}
          customStyles={{ marginBottom: 20 }}
          source={require('../../../assets/onboarding/batteryBig.png')}
        />

        <Text style={styles.title} bold>{title}</Text>
        <Text style={styles.description}>{description}</Text>
        <Text style={{ fontSize: 16, color: 'rgb(98,98,98)', lineHeight: 24, }} bold>{recommendation}</Text>

      </View>

      <View
        style={[{
          ...BASIC_SHADOW_STYLES,
          paddingVertical: IS_SMALL_SCREEN ? 16 : 24,
          paddingHorizontal: IS_SMALL_SCREEN ? 8 : 16,
          borderRadius: 13,
          flexDirection: isRTL ? 'row-reverse' : 'row',

        }]}
      >

        <View style={[styles.dot, { backgroundColor: batteryDisabled ? 'rgb(195,219,110)' : 'rgb(255,130,130)' }]} />

        <TouchableWithoutFeedback
          hitSlop={HIT_SLOP}
          onPress={async () => {
            const request: DeviceSettingsRequest = await BackgroundGeolocation.deviceSettings.showIgnoreBatteryOptimizations();
            BackgroundGeolocation.deviceSettings.show(request);
          }}
          style={{ paddingHorizontal: IS_SMALL_SCREEN ? 8 : 16, backgroundColor: WHITE }}
        >
          <Text style={{ fontSize: 14, color: 'rgb(98,98,98)', textAlign: isRTL ? 'right' : 'left' }}>{batteryDisabled ? batteryOptimizationOn : batteryOptimizationOff }</Text>
          <View style={{ flexDirection: isRTL ? 'row-reverse' : 'row', alignItems: 'center', marginTop: 8 }}>
            <Icon source={require('../../../assets/main/settingsCog.png')} width={9} />
            <Text style={{ fontSize: 12, color: 'rgb(0,120,214)', textAlign: isRTL ? 'right' : 'left', marginHorizontal: 6 }} bold>{settingsBtn}</Text>
          </View>
        </TouchableWithoutFeedback>
      </View>

    </View>


  );
}
Example #7
Source File: form.tsx    From THUInfo with MIT License 5 votes vote down vote up
StarRating = (props: starRatingProps) => {
	let starTotal: number = props.starTotal || 7;
	let starSize: number = props.starSize || 26;
	let starSpacing: number = props.starSpacing || 3;
	let starColor: string = props.starColor || "gold";

	const changeVal = (x: number) => {
		props.scoreRef.value = x.toString();
	};

	const [rating, setRating] = useState(parseInt(props.scoreRef.value, 10));

	let stars = [];
	for (let i = 1; i <= rating; ++i) {
		stars.push(
			<TouchableWithoutFeedback
				key={i}
				onPress={() => {
					setRating(i);
					changeVal(i);
				}}
				style={{marginHorizontal: starSpacing}}>
				<FontAwesome name="star" size={starSize} color={starColor} />
			</TouchableWithoutFeedback>,
		);
	}
	for (let j = rating + 1; j <= starTotal; ++j) {
		stars.push(
			<TouchableWithoutFeedback
				key={j}
				onPress={() => {
					setRating(j);
					changeVal(j);
				}}
				style={{marginHorizontal: starSpacing}}>
				<FontAwesome name="star-o" size={starSize} color="lightgray" />
			</TouchableWithoutFeedback>,
		);
	}

	return (
		<View
			style={{
				flexDirection: "row",
				justifyContent: "flex-end",
				marginVertical: 2,
			}}>
			{stars}
		</View>
	);
}
Example #8
Source File: CreateStepTwo.tsx    From BitcoinWalletMobile with MIT License 5 votes vote down vote up
CreateStepTwo: React.FC<Props> = (props) => {

    const dispatch = useDispatch()
    const languageSelector = (state: WalletState) => state.language
    const language = useSelector(languageSelector)
    const [words, setWords] = useState([""])

    const [didWriteDown, setDidWriteDown] = useState(false)

    const insets = useSafeAreaInsets()

    const setUpWallet = async () => {
        dispatch(setNewlyCreated(true))
        props.navigation.popToTop()
    }

    const getSeed = async () => {
        let s = await RNSecureKeyStore.get('WALLET_SEED')
        setWords(s.split(' '))
    } 

    useEffect(() => {
        getSeed()
    }, [])

    return (
        <View style={styles.rootContainer}>
            <View style={{ flex: 1 }}>
                <Header screen={getTranslated(language).seed_phrase} action={() => { props.navigation.goBack() }} />
                <Screen>
                    <View style={styles.viewContainer}>
                        <ScrollView contentContainerStyle={{ flexGrow: 1, justifyContent: 'space-between' }} style={{ height: '100%' }}>
                            <View style={{ flex: 1 }}>
                                <View style={styles.warningContainer}>
                                    <View style={styles.iconWithText}>
                                        <Image style={styles.icon} source={require('../../assets/images/warning.png')} />
                                        <Text style={styles.warningTextInner}><Text style={styles.warningText}>{getTranslated(language).warning}! </Text>{getTranslated(language).warning_text_2}</Text>
                                    </View>
                                </View>
                                <RecoveryWords screen="CreateStepTwo" words={words} />
                                <View style={{ marginBottom: insets.bottom + 30 }}>
                                    <View style={styles.hasSavedContainer}>
                                        <TouchableWithoutFeedback style={{ width: 32, height: 32 }} onPress={() => { setDidWriteDown(!didWriteDown) }}>
                                            <CheckBox tintColors={{ true: '#F7931A', false: '#F7931A' }} style={styles.checkBox} tintColor="#F7931A" animationDuration={0} onFillColor="#F7931A" onTintColor="#F7931A" onCheckColor="#fff" boxType="square" disabled={false} value={didWriteDown} onValueChange={(newVal) => setDidWriteDown(newVal)} />
                                        </TouchableWithoutFeedback>
                                        <Text style={styles.hasSavedText}>{getTranslated(language).have_saved}</Text>
                                    </View>
                                    <View style={styles.buttonContainer}>
                                        {didWriteDown &&
                                            <ButtonPrimary text={getTranslated(language).wrote_it_down} action={setUpWallet} />
                                        }
                                        {!didWriteDown &&
                                            <ButtonPrimaryDisabled text={getTranslated(language).wrote_it_down} action={() => { }} />
                                        }
                                    </View>
                                </View>
                            </View>
                        </ScrollView>
                    </View>
                </Screen>
            </View>
        </View>
    );

}
Example #9
Source File: index.tsx    From react-native-meetio with MIT License 4 votes vote down vote up
Home: React.FC<HomeProps> = ({ navigation }) => {
  const goToComponents = () => {
    navigation.navigate(AppRoute.COMPONENTS);
  };

  const goToOnboarding = () => {
    navigation.navigate(AppRoute.OBOARDING);
  };

  return (
    <Box flex={1} backgroundColor="lightBlue300">
      <ScrollView
        contentContainerStyle={{
          paddingBottom: 200,
          height: height,
        }}
        showsVerticalScrollIndicator={false}
      >
        <Box flex={1}>
          <Box
            {...StyleSheet.absoluteFillObject}
            top={526}
            height={246}
            borderBottomLeftRadius={BORDER_BOTTOM_LEFT_RADIUS}
            backgroundColor="white"
          >
            <Box flex={1} borderBottomLeftRadius={BORDER_BOTTOM_LEFT_RADIUS}>
              <Image
                source={Images.BG4}
                style={styles.activityProfilesImageStyle}
              />
              <TouchableWithoutFeedback
                style={[styles.categoryButton, { height: 246 }]}
              >
                <Box style={styles.categoryTitleContainer}>
                  <Text variant="title1" color="white">
                    Activities & Profiles
                  </Text>
                </Box>
              </TouchableWithoutFeedback>
            </Box>
          </Box>
          <Box
            {...StyleSheet.absoluteFillObject}
            top={385}
            height={233}
            borderBottomLeftRadius={BORDER_BOTTOM_LEFT_RADIUS}
          >
            <Box flex={1} borderBottomLeftRadius={BORDER_BOTTOM_LEFT_RADIUS}>
              <Image source={Images.BG3} style={styles.onboardingImageStyle} />
              <TouchableWithoutFeedback
                style={[styles.categoryButton, { height: 233 }]}
                onPress={goToOnboarding}
              >
                <Box style={styles.categoryTitleContainer}>
                  <Text variant="title1" color="white">
                    Onboarding
                  </Text>
                </Box>
              </TouchableWithoutFeedback>
            </Box>
          </Box>
          <Box
            {...StyleSheet.absoluteFillObject}
            top={244}
            height={219}
            borderBottomLeftRadius={BORDER_BOTTOM_LEFT_RADIUS}
          >
            <Box flex={1} borderBottomLeftRadius={BORDER_BOTTOM_LEFT_RADIUS}>
              <Image source={Images.BG2} style={styles.componentsImageStyle} />
              <TouchableWithoutFeedback
                style={[styles.categoryButton, { height: 219 }]}
                onPress={goToComponents}
              >
                <Box style={styles.categoryTitleContainer}>
                  <Text variant="title1" color="white">
                    Components
                  </Text>
                </Box>
              </TouchableWithoutFeedback>
            </Box>
          </Box>
          <Box
            {...StyleSheet.absoluteFillObject}
            top={63}
            height={247}
            borderBottomLeftRadius={BORDER_BOTTOM_LEFT_RADIUS}
          >
            <Box flex={1} borderBottomLeftRadius={BORDER_BOTTOM_LEFT_RADIUS}>
              <Image
                source={Images.BG1}
                style={{ ...StyleSheet.absoluteFillObject, left: LEFT }}
              />
              <TouchableWithoutFeedback
                style={[styles.categoryButton, { height: 247 }]}
              >
                <Box style={styles.categoryTitleContainer}>
                  <Text variant="title1" color="white">
                    Main App
                  </Text>
                </Box>
              </TouchableWithoutFeedback>
            </Box>
          </Box>
        </Box>
      </ScrollView>
      <Box style={styles.headerContainer}>
        <Header
          title="Categories"
          height={180}
          borderBottomLeftRadius={80}
          leftIcon={
            <Box marginLeft="m">
              <Icons.ArrowLeft />
            </Box>
          }
          rightIcon={
            <Box marginRight="m">
              <Icons.Filter />
            </Box>
          }
        />
      </Box>
      <View style={styles.addButton}>
        <Icons.Add />
      </View>
    </Box>
  );
}
Example #10
Source File: dashboard.tsx    From protect-scotland with Apache License 2.0 4 votes vote down vote up
Dashboard: FC = () => {
  const {t} = useTranslation();
  const {
    initialised,
    enabled,
    status,
    contacts,
    getCloseContacts,
    permissions,
    readPermissions
  } = useExposure();
  const [appState] = useAppState();
  const {checked, paused} = useReminder();
  const navigation = useNavigation();
  const {onboarded, setContext, loadAppData} = useApplication();
  const {
    isolationDuration,
    isolationCompleteDuration,
    latestVersion: appLatestVersion
  } = useSettings();
  const [refreshing, setRefreshing] = useState(false);
  const {
    focusRef: tourFocus,
    focusA11yElement: focusTourElem
  } = useA11yElement();
  const {
    focusRef: dashboardFocus,
    focusA11yElement: focusDashboardElem
  } = useA11yElement();
  const isFocused = useIsFocused();
  const messageOpacity = useRef(new Animated.Value(0)).current;
  const contentOpacity = useRef(new Animated.Value(0)).current;
  const gridOpacity = useRef(new Animated.Value(0)).current;
  const exposureEnabled = useRef(enabled);
  const bluetoothDisabled = useRef(
    status.state === 'disabled' && status.type?.includes(StatusType.bluetooth)
  );
  const pushNotificationsDisabled = useRef(
    permissions.notifications.status === 'not_allowed'
  );

  const [state, setState] = useState<{
    stage: number;
    exposurePrompt: boolean;
    bluetoothPrompt: boolean;
    pushNotificationsPrompt: boolean;
    disabled: boolean;
    current: string;
    isolationMessage: string | null;
    isolationComplete: boolean;
    default: string;
    messages: string[];
  }>({
    stage: onboarded ? -1 : 0,
    exposurePrompt: false,
    bluetoothPrompt: false,
    pushNotificationsPrompt: false,
    disabled: false,
    current: t(
      getMessage({
        onboarded,
        enabled,
        status,
        messages: t('dashboard:tour', {returnObjects: true}),
        stage: onboarded ? -1 : 0,
        paused
      })
    ),
    isolationMessage: null,
    isolationComplete: false,
    messages: t('dashboard:tour', {returnObjects: true}),
    default: t('dashboard:message:standard')
  });

  const version = useVersion();

  const resetToNormal = () =>
    setState((s) => ({
      ...s,
      isolationComplete: false,
      isolationMessage: null
    }));

  const setExposed = () =>
    setState((s) => ({
      ...s,
      isolationComplete: false,
      isolationMessage: t('dashboard:exposed')
    }));

  const setIsolationComplete = () =>
    setState((s) => ({
      ...s,
      isolationComplete: true,
      isolationMessage: t('dashboard:isolationComplete')
    }));

  const processContactsForMessaging = async () => {
    let currentStatus = null;
    try {
      currentStatus = await SecureStore.getItemAsync('niexposuredate');
    } catch (err) {
      await SecureStore.deleteItemAsync('niexposuredate');
      console.log('processContactsForMessaging', err);
    }

    if (currentStatus) {
      const daysDiff = differenceInCalendarDays(
        new Date(),
        new Date(Number(currentStatus))
      );

      const withIsolation = isolationDuration + isolationCompleteDuration;

      if (daysDiff >= withIsolation) {
        await SecureStore.deleteItemAsync('niexposuredate');
        return resetToNormal();
      }

      if (daysDiff >= isolationDuration && daysDiff < withIsolation) {
        return setIsolationComplete();
      }

      if (contacts && contacts.length > 0) {
        return setExposed();
      }
    }

    return resetToNormal();
  };

  const checkLatestExposure = async () => {
    const latestExposure = getExposureDate(contacts);

    if (latestExposure) {
      await SecureStore.setItemAsync(
        'niexposuredate',
        String(latestExposure.getTime())
      );
    }

    processContactsForMessaging();
  };

  const onRefresh = () => {
    setRefreshing(true);
    loadAppData().then(() => setRefreshing(false));
  };

  useEffect(() => {
    onRefresh();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    getCloseContacts();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [status]);

  useEffect(() => {
    checkLatestExposure();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [contacts, status]);

  useFocusEffect(
    React.useCallback(() => {
      if (!isFocused || appState !== 'active') {
        return;
      }

      readPermissions();
    }, [isFocused, appState, readPermissions])
  );

  useEffect(() => {
    setState((s) => ({
      ...s,
      current: t(
        getMessage({
          onboarded,
          enabled,
          status,
          messages: state.messages,
          stage: state.stage,
          paused
        })
      )
    }));

    exposureEnabled.current = enabled;
    bluetoothDisabled.current =
      status.state === 'disabled' &&
      status.type?.includes(StatusType.bluetooth);
    pushNotificationsDisabled.current =
      permissions.notifications.status === 'not_allowed';

    if (!exposureEnabled.current && onboarded) {
      setTimeout(() => {
        if (!exposureEnabled.current) {
          setState((s) => ({
            ...s,
            exposurePrompt: true
          }));
        }
      }, PROMPT_OFFSET);
    } else if (bluetoothDisabled.current && onboarded) {
      setTimeout(() => {
        if (bluetoothDisabled.current) {
          setState((s) => ({
            ...s,
            bluetoothPrompt: true
          }));
        }
      }, PROMPT_OFFSET);
    } else if (pushNotificationsDisabled.current && onboarded) {
      setTimeout(() => {
        if (
          pushNotificationsDisabled.current &&
          exposureEnabled.current &&
          !bluetoothDisabled.current
        ) {
          setState((s) => ({
            ...s,
            pushNotificationsPrompt: true
          }));
        }
      }, PROMPT_OFFSET);
    } else if (onboarded && exposureEnabled.current) {
      setState((s) => ({
        ...s,
        exposurePrompt: false
      }));
    }

    setTimeout(() => checkLatestExposure(), 100);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [enabled, onboarded, status, permissions]);

  const animateTourIn = () => {
    setState((s) => ({...s, disabled: true}));
    Animated.parallel([
      Animated.timing(messageOpacity, {
        toValue: 1,
        duration: ANIMATION_DURATION,
        useNativeDriver: true
      }),
      Animated.timing(gridOpacity, {
        toValue: 1,
        duration: ANIMATION_DURATION,
        useNativeDriver: true
      })
    ]).start(() => {
      setState((s) => ({...s, disabled: false}));
    });
  };

  const animateTourOut = () => {
    setState((s) => ({...s, disabled: true}));
    Animated.parallel([
      Animated.timing(messageOpacity, {
        toValue: 0,
        duration: ANIMATION_DURATION,
        useNativeDriver: true
      }),
      Animated.timing(gridOpacity, {
        toValue: 0,
        duration: ANIMATION_DURATION,
        useNativeDriver: true
      })
    ]).start(() => {
      if (state.stage < state.messages.length - 1) {
        setState((s) => ({
          ...s,
          stage: state.stage + 1,
          current: getMessage({
            onboarded,
            enabled,
            status,
            messages: state.messages,
            stage: state.stage + 1
          })
        }));
        animateTourIn();
      } else {
        setState((s) => ({
          ...s,
          stage: -1,
          current: s.default
        }));
        setContext({onboarded: true});
        animateDashboard();
      }
    });
  };

  const animateDashboard = () => {
    setState((s) => ({...s, disabled: true}));
    Animated.parallel([
      Animated.timing(messageOpacity, {
        toValue: 1,
        duration: ANIMATION_DURATION,
        useNativeDriver: true
      }),
      Animated.timing(contentOpacity, {
        toValue: 1,
        duration: ANIMATION_DURATION,
        useNativeDriver: true
      }),
      Animated.timing(gridOpacity, {
        toValue: 1,
        duration: ANIMATION_DURATION,
        useNativeDriver: true
      })
    ]).start(() => {
      AsyncStorage.setItem('scot.onboarded', 'true');
      setState((s) => ({...s, disabled: false}));
    });
  };

  useEffect(() => {
    if (onboarded) {
      setTimeout(() => animateDashboard(), 200);
    } else {
      setTimeout(() => animateTourIn(), 200);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    if (!state.disabled) {
      focusTourElem();
    }
  }, [focusTourElem, state.disabled]);

  useEffect(() => {
    if (onboarded && !state.disabled) {
      focusDashboardElem();
    }
  }, [focusDashboardElem, onboarded, state.disabled]);

  const handleTour = () => {
    animateTourOut();
  };

  if (!initialised || !checked) {
    return (
      <>
        <Container center="both">
          <ActivityIndicator color={colors.darkGrey} size="large" />
        </Container>
      </>
    );
  }

  return (
    <>
      <Header />
      <ScrollView
        refreshControl={
          <RefreshControl
            refreshing={onboarded && refreshing}
            onRefresh={onRefresh}
          />
        }>
        {onboarded &&
          appLatestVersion &&
          version &&
          appLatestVersion !== version?.display && (
            <View style={blockStyles.block}>
              <NewVersionCard />
              <Spacing s={16} />
            </View>
          )}
        <Spacing s={onboarded ? 15 : 65} />
        {!onboarded && state.stage > -1 && (
          <>
            <View style={styles.dots}>
              {[-1, 0, 1, 2].map((x) => (
                <Animated.View
                  key={`step-${x}`}
                  style={[
                    styles.dot,
                    {
                      backgroundColor:
                        state.stage > x
                          ? colors.primaryPurple
                          : colors.lighterPurple
                    }
                  ]}
                />
              ))}
            </View>
            <A11yView
              ref={tourFocus}
              accessible
              accessibilityHint={
                t('dashboard:tourA11y', {returnObjects: true})[state.stage]
              }
            />
            <Animated.View style={{opacity: messageOpacity}}>
              <TouchableWithoutFeedback onPress={() => handleTour()}>
                <Markdown markdownStyles={markDownStyles}>
                  {state.current}
                </Markdown>
              </TouchableWithoutFeedback>
              <Spacing s={30} />
              <ArrowLink
                onPress={() => {
                  if (!state.disabled) {
                    handleTour();
                  }
                }}
                accessibilityHint={t('dashboard:tourActionHint')}
                invert>
                <Text variant="h3" color="pink" style={styles.nextLink}>
                  {t('dashboard:tourAction')}
                </Text>
              </ArrowLink>
            </Animated.View>
          </>
        )}

        {onboarded && state.isolationMessage && (
          <>
            <Animated.View
              style={{
                opacity: messageOpacity
              }}>
              <View accessible ref={dashboardFocus}>
                <Markdown
                  markdownStyles={
                    state.isolationComplete
                      ? markDownStyles
                      : markDownStylesExposed
                  }>
                  {state.isolationMessage}
                </Markdown>
              </View>
              {!state.isolationComplete && (
                <>
                  <Spacing s={30} />
                  <ArrowLink
                    onPress={() =>
                      navigation.navigate(ScreenNames.closeContact)
                    }
                    accessibilityHint={t('dashboard:exposedAction')}
                    invert>
                    <Text variant="h3" color="pink" style={styles.nextLink}>
                      {t('dashboard:tourAction')}
                    </Text>
                  </ArrowLink>
                </>
              )}
              {state.isolationComplete && (
                <>
                  <Spacing s={20} />
                  <Text style={blockStyles.block} inline color="darkGrey">
                    {t('dashboard:isolationCompleteSupplemental')}
                  </Text>
                </>
              )}
            </Animated.View>
            <Spacing s={30} />
            <Animated.View
              style={[{opacity: contentOpacity}, blockStyles.block]}>
              <Message />
            </Animated.View>
          </>
        )}

        {onboarded && !state.isolationMessage && (
          <Animated.View style={{opacity: messageOpacity}}>
            <View accessible ref={dashboardFocus}>
              <Markdown markdownStyles={markDownStyles}>
                {state.current}
              </Markdown>
            </View>
            {state.stage === -1 && !paused && (
              <>
                <Spacing s={20} />
                <Text style={blockStyles.block} inline color="darkGrey">
                  {t(`dashboard:message:bluetooth:${Platform.OS}`)}
                </Text>
              </>
            )}
            {state.stage === -1 && paused && (
              <>
                <Spacing s={20} />
                <Text style={blockStyles.block} inline color="darkGrey">
                  {t('dashboard:message:pausedSupplemental')}
                </Text>
              </>
            )}
          </Animated.View>
        )}
        <Spacing s={30} />
        <Grid
          onboarded={onboarded}
          stage={state.stage}
          opacity={gridOpacity}
          onboardingCallback={() => handleTour()}
        />
        {state.isolationMessage && <Spacing s={34} />}
        {onboarded && !state.isolationMessage && (
          <>
            <Animated.View
              style={[{opacity: contentOpacity}, blockStyles.block]}>
              <Spacing s={30} />
              <Message />
              <Spacing s={16} />
              <Message
                image={RestrictionsImage}
                markdown={t('restrictions:message')}
                accessibilityLabel={t('restrictions:a11y:label')}
                accessibilityHint={t('restrictions:a11y:hint')}
                link={t('links:r')}
              />
              <Spacing s={45} />
            </Animated.View>
          </>
        )}
        {onboarded && (
          <Text variant="h4" color="primaryPurple" align="center">
            {t('dashboard:thanks')}
          </Text>
        )}
        <Spacing s={60} />
      </ScrollView>
      {checked && !paused && state.exposurePrompt && (
        <ExposureNotificationsModal
          isVisible={state.exposurePrompt}
          onBackdropPress={() =>
            setState((s) => ({...s, exposurePrompt: false}))
          }
          onClose={() => setState((s) => ({...s, exposurePrompt: false}))}
        />
      )}
      {checked && !paused && state.bluetoothPrompt && (
        <BluetoothNotificationsModal
          isVisible={state.bluetoothPrompt}
          onBackdropPress={() =>
            setState((s) => ({...s, bluetoothPrompt: false}))
          }
          onClose={() => setState((s) => ({...s, bluetoothPrompt: false}))}
        />
      )}
      {checked && !paused && state.pushNotificationsPrompt && (
        <PushNotificationsModal
          isVisible={state.pushNotificationsPrompt}
          onBackdropPress={() =>
            setState((s) => ({...s, pushNotificationsPrompt: false}))
          }
          onClose={() =>
            setState((s) => ({...s, pushNotificationsPrompt: false}))
          }
        />
      )}
    </>
  );
}