Skip to content
This repository has been archived by the owner on Jun 24, 2022. It is now read-only.

Commit

Permalink
Tie Outbreak checks into background tasks. (#1424)
Browse files Browse the repository at this point in the history
* Tie Outbreak checks into background tasks.

* Rename the storage key.

* Remove randomMinutes.

* Refactor to eliminate duplication.

* Lint

Co-authored-by: Stephen McMurtry <[email protected]>
  • Loading branch information
James Eberhardt and smcmurtry authored Feb 18, 2021
1 parent e1323b6 commit 1158cb1
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/screens/testScreen/TestScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const Content = () => {
}, [setOutbreakStatus]);

const onCheckForOutbreak = useCallback(async () => {
checkForOutbreaks();
checkForOutbreaks(true);
}, [checkForOutbreaks]);

const goToCheckInHistory = useCallback(() => navigation.navigate('CheckInHistoryScreen'), [navigation]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import {captureException, captureMessage} from 'shared/log';
import {log} from 'shared/logging/config';
import {DeviceEventEmitter, Platform} from 'react-native';
import {ContagiousDateInfo, ContagiousDateType} from 'shared/DataSharing';
import {EN_API_VERSION} from 'env';
import {EN_API_VERSION, QR_ENABLED} from 'env';
import {EventTypeMetric, FilteredMetricsService} from 'services/MetricsService/FilteredMetricsService';
import {checkNotifications} from 'react-native-permissions';
import {Status} from 'screens/home/components/NotificationPermissionStatus';
import {PollNotifications} from 'services/PollNotificationService';
import {OutbreakService} from 'shared/OutbreakProvider';

import {BackendInterface, SubmissionKeySet} from '../BackendService';
import {PERIODIC_TASK_INTERVAL_IN_MINUTES} from '../BackgroundSchedulerService';
Expand Down Expand Up @@ -263,6 +264,10 @@ export class ExposureNotificationService {
await this.updateExposureStatus();
await this.processNotification();

if (QR_ENABLED) {
OutbreakService.sharedInstance(this.i18n).checkForOutbreaks();
}

const filteredMetricsService = FilteredMetricsService.sharedInstance();

await filteredMetricsService.addEvent({type: EventTypeMetric.BackgroundCheck});
Expand Down
55 changes: 53 additions & 2 deletions src/shared/OutbreakProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,46 @@
import {TEST_MODE} from 'env';
import AsyncStorage from '@react-native-community/async-storage';
import React, {useContext, useEffect, useMemo, useState} from 'react';
import {Key} from 'services/StorageService';
import PushNotification from 'bridge/PushNotification';
import {useI18nRef, I18n} from 'locale';
import PQueue from 'p-queue';

// eslint-disable-next-line @shopify/strict-component-boundaries
import {DefaultSecureKeyValueStore, SecureKeyValueStore} from '../services/MetricsService/SecureKeyValueStorage';

import {Observable} from './Observable';
import {CheckInData, getNewOutbreakStatus, getOutbreakEvents, initialOutbreakStatus, OutbreakStatus} from './qr';
import {createCancellableCallbackPromise} from './cancellablePromise';
import {getCurrentDate, minutesBetween} from './date-fns';

const OutbreaksLastCheckedStorageKey = 'A436ED42-707E-11EB-9439-0242AC130002';

const MIN_OUTBREAKS_CHECK_MINUTES = TEST_MODE ? 15 : 240;

export class OutbreakService implements OutbreakService {
private static instance: OutbreakService;

static sharedInstance(i18n: I18n): OutbreakService {
if (!this.instance) {
this.instance = new this(i18n);
}
return this.instance;
}

class OutbreakService implements OutbreakService {
outbreakStatus: Observable<OutbreakStatus>;
checkInHistory: Observable<CheckInData[]>;
i18n: I18n;
secureKeyValueStore: SecureKeyValueStore;

private serialPromiseQueue: PQueue;

constructor(i18n: I18n) {
this.outbreakStatus = new Observable<OutbreakStatus>(initialOutbreakStatus);
this.checkInHistory = new Observable<CheckInData[]>([]);
this.i18n = i18n;
this.secureKeyValueStore = new DefaultSecureKeyValueStore();
this.serialPromiseQueue = new PQueue({concurrency: 1});
}

setOutbreakStatus = async (value: OutbreakStatus) => {
Expand Down Expand Up @@ -49,11 +73,28 @@ class OutbreakService implements OutbreakService {
this.checkInHistory.set(JSON.parse(checkInHistory));
};

checkForOutbreaks = async () => {
checkForOutbreaks = async (forceCheck?: boolean) => {
return this.serialPromiseQueue.add(() => {
return this.getOutbreaksLastCheckedDateTime().then(async outbreaksLastCheckedDateTime => {
if (forceCheck === false && outbreaksLastCheckedDateTime) {
const today = getCurrentDate();
const minutesSinceLastOutbreaksCheck = minutesBetween(outbreaksLastCheckedDateTime, today);
if (minutesSinceLastOutbreaksCheck > MIN_OUTBREAKS_CHECK_MINUTES) {
await this.getOutbreaksFromServer();
}
} else {
await this.getOutbreaksFromServer();
}
});
});
};

getOutbreaksFromServer = async () => {
const outbreakEvents = await getOutbreakEvents();
const newOutbreakStatusType = getNewOutbreakStatus(this.checkInHistory.get(), outbreakEvents);
this.setOutbreakStatus(newOutbreakStatusType);
this.processOutbreakNotification(newOutbreakStatusType);
this.markOutbreaksLastCheckedDateTime(getCurrentDate());
};

processOutbreakNotification = (status: OutbreakStatus) => {
Expand All @@ -65,6 +106,16 @@ class OutbreakService implements OutbreakService {
});
}
};

private getOutbreaksLastCheckedDateTime(): Promise<Date | null> {
return this.secureKeyValueStore
.retrieve(OutbreaksLastCheckedStorageKey)
.then(value => (value ? new Date(Number(value)) : null));
}

private markOutbreaksLastCheckedDateTime(date: Date): Promise<void> {
return this.secureKeyValueStore.save(OutbreaksLastCheckedStorageKey, `${date.getTime()}`);
}
}

export const createOutbreakService = async (i18n: I18n) => {
Expand Down

0 comments on commit 1158cb1

Please sign in to comment.