1import AsyncStorage from '@react-native-async-storage/async-storage';
2import { useFocusEffect } from '@react-navigation/native';
3import format from 'date-format';
4import * as BackgroundFetch from 'expo-background-fetch';
5import * as TaskManager from 'expo-task-manager';
6import React from 'react';
7import { StyleSheet, Text, View } from 'react-native';
8
9import Button from '../components/Button';
10import useAppState from '../utilities/useAppState';
11
12const BACKGROUND_FETCH_TASK = 'background-fetch';
13const LAST_FETCH_DATE_KEY = 'background-fetch-date';
14
15export default function BackgroundFetchScreen() {
16  const [isRegistered, setIsRegistered] = React.useState<boolean>(false);
17  const [fetchDate, setFetchDate] = React.useState<Date | null>(null);
18  const [status, setStatus] = React.useState<BackgroundFetch.Status | null>(null);
19  const appState = useAppState(null);
20
21  React.useEffect(() => {
22    if (appState === 'active') {
23      refreshLastFetchDateAsync();
24    }
25  }, [appState]);
26
27  const onFocus = React.useCallback(() => {
28    refreshLastFetchDateAsync();
29    checkStatusAsync();
30  }, []);
31  useFocusEffect(onFocus);
32
33  const refreshLastFetchDateAsync = async () => {
34    const lastFetchDateStr = await AsyncStorage.getItem(LAST_FETCH_DATE_KEY);
35
36    if (lastFetchDateStr) {
37      setFetchDate(new Date(+lastFetchDateStr));
38    }
39  };
40
41  const checkStatusAsync = async () => {
42    const status = await BackgroundFetch.getStatusAsync();
43    const isRegistered = await TaskManager.isTaskRegisteredAsync(BACKGROUND_FETCH_TASK);
44    setStatus(status);
45    setIsRegistered(isRegistered);
46  };
47
48  const toggle = async () => {
49    if (isRegistered) {
50      await BackgroundFetch.unregisterTaskAsync(BACKGROUND_FETCH_TASK);
51    } else {
52      await BackgroundFetch.registerTaskAsync(BACKGROUND_FETCH_TASK, {
53        minimumInterval: 60, // 1 minute
54        stopOnTerminate: false,
55        startOnBoot: true,
56      });
57    }
58    setIsRegistered(!isRegistered);
59  };
60
61  const renderText = () => {
62    if (!fetchDate) {
63      return <Text>There was no BackgroundFetch call yet.</Text>;
64    }
65    return (
66      <View style={{ flexDirection: 'column', alignItems: 'center' }}>
67        <Text>Last background fetch was invoked at:</Text>
68        <Text style={styles.boldText}>{format('yyyy-MM-dd hh:mm:ss:SSS', fetchDate)}</Text>
69      </View>
70    );
71  };
72
73  return (
74    <View style={styles.screen}>
75      <View style={styles.textContainer}>
76        <Text>
77          Background fetch status:{' '}
78          <Text style={styles.boldText}>{status ? BackgroundFetch.Status[status] : null}</Text>
79        </Text>
80      </View>
81      <View style={styles.textContainer}>{renderText()}</View>
82      <Button
83        buttonStyle={styles.button}
84        title={isRegistered ? 'Unregister BackgroundFetch task' : 'Register BackgroundFetch task'}
85        onPress={toggle}
86      />
87    </View>
88  );
89}
90
91BackgroundFetchScreen.navigationOptions = {
92  title: 'Background Fetch',
93};
94
95TaskManager.defineTask(BACKGROUND_FETCH_TASK, async () => {
96  const now = Date.now();
97
98  console.log(`Got background fetch call at date: ${new Date(now).toISOString()}`);
99  await AsyncStorage.setItem(LAST_FETCH_DATE_KEY, now.toString());
100
101  return BackgroundFetch.Result.NewData;
102});
103
104const styles = StyleSheet.create({
105  screen: {
106    flex: 1,
107    justifyContent: 'center',
108    alignItems: 'center',
109  },
110  button: {
111    padding: 10,
112    marginVertical: 5,
113  },
114  textContainer: {
115    margin: 10,
116  },
117  boldText: {
118    fontWeight: 'bold',
119  },
120});
121