Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add version check service #653

Merged
merged 6 commits into from
Apr 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions App.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import SplashScreen from 'react-native-splash-screen';

import { Theme } from './app/constants/themes';
import Entry from './app/Entry';
import VersionCheckService from './app/services/VersionCheckService';

const App = () => {
useEffect(() => {
SplashScreen.hide();
VersionCheckService.start();
}, []);

return (
Expand Down
5 changes: 5 additions & 0 deletions app/Util.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ export function nowStr() {
return dayjs().format('H:mm');
}

export const isVersionGreater = (source, target) =>
source
.split('.')
.some((val, index) => parseInt(val) > parseInt(target.split('.')[index]));

export default {
isPlatformiOS,
isPlatformAndroid,
Expand Down
6 changes: 6 additions & 0 deletions app/constants/versionCheck.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const APPSTORE_URL = 'itms-apps://itunes.apple.com/us/app/id1508266966';
export const PLAYSTORE_URL = 'market://details?id=org.pathcheck.covidsafepaths';
export const VERSION_URL =
'https://rawcdn.githack.com/tripleblindmarket/covid-safe-paths/0008e269c355e14bb216bb6e69cf4b89e25012b2/versions.yaml';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change to https://rawcdn.githack.com/tripleblindmarket/covid-safe-paths/master/versions.yaml. This will still use the CDN with longer caching, but won't include a tag or commit hash

export const YAML_MANDATORY_VERSION_KEY = 'mandatory update for less than';
export const YAML_LATEST_VERSION_KEY = 'latest version';
10 changes: 9 additions & 1 deletion app/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,5 +109,13 @@
"what_does_this_mean": "What does this mean?",
"what_if_no_symptoms_para": "If you have no symptoms but still would like to be tested you can go to your nearest testing site.\n\nIndividuals who don't exhibit symptoms can sometimes still carry the infection and infect others. Being careful about social distancing and coming in contact with large groups or at risk individuals (the elderly, those with significant other medical issues) is important to manage both your risk and the risk to others.",
"what_if_no_symptoms": "What if I’m not showing symptoms?"
},
"version_update": {
"push_notification_title": "Application is outdated",
"push_notification_message": "Please update your application",
"alert_label": "COVID Safe Paths is outdated",
"alert_sublabel": "Update to a new version?",
"update": "Update",
"later": "Later"
}
}
}
104 changes: 104 additions & 0 deletions app/services/VersionCheckService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import Yaml from 'js-yaml';
import { Alert, Linking } from 'react-native';
import BackgroundTimer from 'react-native-background-timer';
import PushNotification from 'react-native-push-notification';

import { version as currentVersion } from '../../package.json';
import {
APPSTORE_URL,
PLAYSTORE_URL,
VERSION_URL,
YAML_LATEST_VERSION_KEY,
YAML_MANDATORY_VERSION_KEY,
} from '../constants/versionCheck';
import languages from '../locales/languages';
import { isPlatformiOS, isVersionGreater } from '../Util';

const POLL_INTERVAL = 24 * 60 * 60 * 1000; //one day

const updateLink = isPlatformiOS() ? APPSTORE_URL : PLAYSTORE_URL;
let isPollStarted = false;
//make sure we show only one notification
let notificationShown = false;

export default class VersionCheckService {
static start() {
if (!isPollStarted) {
this.checkUpdate();
// currenly BackgroundTimer is not used anywhere else.
// But this thing should be called only once, otherwise it behaves not as expected.
// It worths making a separate service.
BackgroundTimer.runBackgroundTimer(
() => this.checkUpdate(),
POLL_INTERVAL,
);
isPollStarted = true;
}
}

static stop() {
BackgroundTimer.stopBackgroundTimer();
}

static async checkUpdate() {
if (notificationShown) return;
let mandatoryVersion, latestVersion;
try {
const response = await fetch(VERSION_URL);
const responseText = await response.text();
const parsedFile = Yaml.safeLoad(responseText);
mandatoryVersion = parsedFile[YAML_MANDATORY_VERSION_KEY];
latestVersion = parsedFile[YAML_LATEST_VERSION_KEY];
} catch (err) {
console.log(err);
return;
}

if (!isVersionGreater(latestVersion, currentVersion)) return;

const isMandatoryUpdate = isVersionGreater(
mandatoryVersion,
currentVersion,
);
this.showNotifications(isMandatoryUpdate);
}

static showNotifications(isMandatoryUpdate) {
const updateOption = {
text: languages.t('version_update.update'),
onPress: () => {
notificationShown = false;
Linking.canOpenURL(updateLink).then(
supported => supported && Linking.openURL(updateLink),
err => console.log(err),
);
},
};
const laterOption = {
text: languages.t('version_update.later'),
onPress: () => {
notificationShown = false;
console.log('User does not want to update the app');
},
style: 'cancel',
};

const alertOptions = [updateOption];

if (isMandatoryUpdate) {
PushNotification.localNotification({
title: languages.t('version_update.push_notification_title'),
message: languages.t('version_update.push_notification_message'),
});
} else {
alertOptions.push(laterOption);
}

notificationShown = true;
Alert.alert(
languages.t('version_update.alert_label'),
languages.t('version_update.alert_sublabel'),
alertOptions,
);
}
}
6 changes: 6 additions & 0 deletions ios/COVIDSafePaths/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>itms-apps</string>
</array>
<key>LSApplicationCategoryType</key>
<string></string>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.transistorsoft.fetch</string>
Expand Down
5 changes: 4 additions & 1 deletion jestSetupFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ jest.mock(
);
jest.mock('react-native-share', () => 'Share');
jest.mock('rn-fetch-blob', () => 'Blob');
jest.mock('react-native-background-timer', () => 'BackgroundTimer');
jest.mock('react-native-background-timer', () => ({
runBackgroundTimer: () => {},
stopBackgroundTimer: () => {},
}));
jest.mock('react-native-popup-menu', () => ({
Menu: 'Menu',
MenuProvider: 'MenuProvider',
Expand Down