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