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