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

Rewrite StartPrefs #1072

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0e31d15
remove unused imports from startprefts.js
Oct 17, 2023
4fc3982
convert startprefs from .js to .ts
Oct 17, 2023
416a111
remove all references to angular service, replace with imports
Oct 17, 2023
0584278
add a comment about the notifications
Oct 18, 2023
1d4752d
remove extra line
Oct 18, 2023
e0dc3bb
correct typo
Oct 18, 2023
8a3320d
make a deep copy for storage
Oct 19, 2023
a26536c
updating "keys"
Oct 19, 2023
d8d1a37
update event protocol
Oct 19, 2023
8a99ba9
move plugin calls
Oct 19, 2023
b40761a
Merge remote-tracking branch 'upstream/service_rewrite_2023' into sta…
Oct 19, 2023
1edfc8c
add docstrings for functions
Oct 19, 2023
d4a29b1
first test for startprefs
Oct 20, 2023
40f6b93
test markConsented
Oct 20, 2023
9849fa2
test all functions in sequence
Oct 20, 2023
ef4a9d8
Merge remote-tracking branch 'upstream/onboarding_routing_sept_2023' …
Oct 20, 2023
8ab5bf1
add timeout
Oct 20, 2023
2c92dbf
resolve merge conflicts by merging upstream
Oct 23, 2023
60caee5
remove "protocol id"
Oct 23, 2023
c1d76ad
update consent/no consent messages
Oct 23, 2023
3130741
remove unneeded type declaration
Oct 25, 2023
39aa2f2
move "after consent"
Oct 25, 2023
657ae34
rework "intro done event"
Oct 25, 2023
a7a75d1
remove unneeded object
Oct 25, 2023
c54f939
console.log -> logDebug
Oct 27, 2023
2516b2d
_is_consented assigned but never used
Oct 27, 2023
8bbfb82
add a catch block
Oct 27, 2023
e19a346
less hacky undefined catch
Oct 27, 2023
34b997c
Merge remote-tracking branch 'upstream/service_rewrite_2023' into sta…
Oct 30, 2023
b2d6aa9
add comment and discussion link
Oct 30, 2023
fcbefb1
add comments about naming
Oct 30, 2023
3c23808
remove old imports
Oct 30, 2023
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
31 changes: 31 additions & 0 deletions www/__mocks__/cordovaMocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export const mockFile = () => {
"applicationStorageDirectory" : "../path/to/app/storage/directory"};
}

//for consent document
const _storage = {};

export const mockBEMUserCache = () => {
const _cache = {};
const messages = [];
Expand Down Expand Up @@ -92,9 +95,37 @@ export const mockBEMUserCache = () => {
rs(messages.filter(m => m.key == key).map(m => m.value));
}, 100)
);
},
getDocument: (key: string, withMetadata?: boolean) => {
return new Promise<any[]>((rs, rj) =>
setTimeout(() => {
rs(_storage[key]);
}, 100)
);
},
isEmptyDoc: (doc) => {
if (doc == undefined) { return true }
let string = doc.toString();
if (string.length == 0) {
return true;
} else {
return false;
}
}
}
window['cordova'] ||= {};
window['cordova'].plugins ||= {};
window['cordova'].plugins.BEMUserCache = mockBEMUserCache;
}

export const mockBEMDataCollection = () => {
const mockBEMDataCollection = {
markConsented: (consentDoc) => {
setTimeout(() => {
_storage['config/consent'] = consentDoc;
}, 100)
}
}
window['cordova'] ||= {};
window['cordova'].plugins.BEMDataCollection = mockBEMDataCollection;
}
24 changes: 24 additions & 0 deletions www/__tests__/startprefs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { markConsented, isConsented, readConsentState, getConsentDocument } from '../js/splash/startprefs';

import { mockBEMUserCache, mockBEMDataCollection } from "../__mocks__/cordovaMocks";
import { mockLogger } from "../__mocks__/globalMocks";

mockBEMUserCache();
mockBEMDataCollection();
mockLogger();

global.fetch = (url: string) => new Promise((rs, rj) => {
setTimeout(() => rs({
json: () => new Promise((rs, rj) => {
let myJSON = { "emSensorDataCollectionProtocol": { "protocol_id": "2014-04-6267", "approval_date": "2016-07-14" } };
setTimeout(() => rs(myJSON), 100);
})
}));
}) as any;

it('checks state of consent before and after marking consent', async () => {
expect(await readConsentState().then(isConsented)).toBeFalsy();
let marked = await markConsented();
expect(await readConsentState().then(isConsented)).toBeTruthy();
expect(await getConsentDocument()).toEqual({"approval_date": "2016-07-14", "protocol_id": "2014-04-6267"});
});
3 changes: 2 additions & 1 deletion www/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@
"confirm": "Confirm",
"user-data-erased": "User data erased.",
"consent-not-found": "Consent for data collection not found, consent now?",
"no-consent-logout": "Consent for data collection not found, please save your opcode, log out, and log back in with the same opcode. Note that you won't get any personalized stats until you do!",
"no-consent-message": "OK! Note that you won't get any personalized stats until you do!",
"consent-found": "Consent found!",
"consented-to": "Consented to protocol {{protocol_id}}, {{approval_date}}",
"consented-to": "Consented to protocol last updated on {{approval_date}}",
"consented-ok": "OK",
"qrcode": "My OPcode",
"qrcode-share-title": "You can save your OPcode to login easily in the future!"
Expand Down
1 change: 0 additions & 1 deletion www/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import 'leaflet/dist/leaflet.css';

import './js/ngApp.js';
import './js/splash/referral.js';
import './js/splash/startprefs.js';
import './js/splash/pushnotify.js';
import './js/splash/storedevicesettings.js';
import './js/splash/localnotify.js';
Expand Down
2 changes: 0 additions & 2 deletions www/js/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ const App = () => {
const { colors } = useTheme();
const { t } = useTranslation();

const StartPrefs = getAngularService('StartPrefs');

const routes = useMemo(() => {
const showMetrics = appConfig?.survey_info?.['trip-labels'] == 'MULTILABEL';
return showMetrics ? defaultRoutes(t) : defaultRoutes(t).filter(r => r.key != 'metrics');
Expand Down
22 changes: 10 additions & 12 deletions www/js/control/ProfileSettings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { AppContext } from "../App";
import { shareQR } from "../components/QrCode";
import { storageClear } from "../plugin/storage";
import { getAppVersion } from "../plugin/clientStats";
import { getConsentDocument } from "../splash/startprefs";
import { logDebug } from "../plugin/logger";

//any pure functions can go outside
const ProfileSettings = () => {
Expand All @@ -39,7 +41,6 @@ const ProfileSettings = () => {
const EmailHelper = getAngularService('EmailHelper');
const NotificationScheduler = getAngularService('NotificationScheduler');
const ControlHelper = getAngularService('ControlHelper');
const StartPrefs = getAngularService('StartPrefs');

//functions that come directly from an Angular service
const editCollectionConfig = () => setEditCollectionVis(true);
Expand Down Expand Up @@ -314,8 +315,9 @@ const ProfileSettings = () => {

//in ProfileSettings in DevZone (above two functions are helpers)
async function checkConsent() {
StartPrefs.getConsentDocument().then(function(resultDoc){
getConsentDocument().then(function(resultDoc){
setConsentDoc(resultDoc);
logDebug("In profile settings, consent doc found", resultDoc);
if (resultDoc == null) {
setNoConsentVis(true);
} else {
Expand Down Expand Up @@ -482,17 +484,13 @@ const ProfileSettings = () => {
onDismiss={()=>setNoConsentVis(false)}
style={settingStyles.dialog(colors.elevation.level3)}>
<Dialog.Title>{t('general-settings.consent-not-found')}</Dialog.Title>
<Dialog.Content>
<Text variant="">{t('general-settings.no-consent-logout')}</Text>
</Dialog.Content>
<Dialog.Actions>
<Button onPress={()=>{
setNoConsentVis(false);
setNoConsentMessageVis(true)}}>
{t('general-settings.cancel')}
</Button>
<Button onPress={()=>{
setNoConsentVis(false);
// $state.go("root.reconsent"); //don't know how to do this yet
}}>
{t('general-settings.confirm')}
setNoConsentVis(false); }}>
{t('general-settings.consented-ok')}
shankari marked this conversation as resolved.
Show resolved Hide resolved
</Button>
</Dialog.Actions>
</Dialog>
Expand All @@ -503,7 +501,7 @@ const ProfileSettings = () => {
<Dialog visible={consentVis}
onDismiss={()=>setConsentVis(false)}
style={settingStyles.dialog(colors.elevation.level3)}>
<Dialog.Title>{t('general-settings.consented-to', {protocol_id: consentDoc.protocol_id, approval_date: consentDoc.approval_date})}</Dialog.Title>
<Dialog.Title>{t('general-settings.consented-to', {approval_date: consentDoc.approval_date})}</Dialog.Title>
<Dialog.Actions>
<Button onPress={()=>{
setConsentDoc({});
Expand Down
8 changes: 4 additions & 4 deletions www/js/controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import angular from 'angular';
import { addStatError, addStatReading, statKeys } from './plugin/clientStats';
import { getPendingOnboardingState } from './onboarding/onboardingHelper';

angular.module('emission.controllers', ['emission.splash.startprefs',
'emission.splash.pushnotify',
angular.module('emission.controllers', ['emission.splash.pushnotify',
'emission.splash.storedevicesettings',
'emission.splash.localnotify',
'emission.splash.remotenotify'])
Expand All @@ -14,7 +14,7 @@ angular.module('emission.controllers', ['emission.splash.startprefs',
.controller('DashCtrl', function($scope) {})

.controller('SplashCtrl', function($scope, $state, $interval, $rootScope,
StartPrefs, PushNotify, StoreDeviceSettings,
PushNotify, StoreDeviceSettings,
LocalNotify, RemoteNotify) {
console.log('SplashCtrl invoked');
// alert("attach debugger!");
Expand Down Expand Up @@ -49,7 +49,7 @@ angular.module('emission.controllers', ['emission.splash.startprefs',
'root.main.metrics']
if (isInList(toState.name, personalTabs)) {
// toState is in the personalTabs list
StartPrefs.getPendingOnboardingState().then(function(result) {
getPendingOnboardingState().then(function(result) {
if (result != null) {
event.preventDefault();
$state.go(result);
Expand Down
2 changes: 1 addition & 1 deletion www/js/onboarding/ProtocolPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { View, ScrollView } from 'react-native';
import { Button, Surface } from 'react-native-paper';
import { resetDataAndRefresh } from '../config/dynamicConfig';
import { AppContext } from '../App';
import { getAngularService } from '../angular-react-helper';
import PrivacyPolicy from './PrivacyPolicy';
import { onboardingStyles } from './OnboardingStack';
import { markConsented } from '../splash/startprefs';
import { setProtocolDone } from './onboardingHelper';

const ProtocolPage = () => {
Expand Down
21 changes: 11 additions & 10 deletions www/js/onboarding/SaveQrPage.tsx
Abby-Wheelis marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { preloadDemoSurveyResponse } from "./SurveyPage";
import { storageSet } from "../plugin/storage";
import { registerUser } from "../commHelper";
import { resetDataAndRefresh } from "../config/dynamicConfig";
import { markConsented } from "../splash/startprefs";
import i18next from "i18next";

const SaveQrPage = ({ }) => {
Expand All @@ -22,16 +23,16 @@ const SaveQrPage = ({ }) => {

useEffect(() => {
if (overallStatus == true && !registerUserDone) {
const StartPrefs = getAngularService('StartPrefs');
StartPrefs.markConsented().then((response) => {
logDebug('permissions done, going to log in');
login(onboardingState.opcode).then((response) => {
logDebug('login done, refreshing onboarding state');
setRegisterUserDone(true);
preloadDemoSurveyResponse();
refreshOnboardingState();
});
});
logDebug('permissions done, going to log in');
markConsented()
.then(login(onboardingState.opcode)
.then((response) => {
logDebug('login done, refreshing onboarding state');
setRegisterUserDone(true);
preloadDemoSurveyResponse();
refreshOnboardingState();
})
);
} else {
logDebug('permissions not done, waiting');
}
Expand Down
18 changes: 13 additions & 5 deletions www/js/onboarding/onboardingHelper.ts
Abby-Wheelis marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { DateTime } from "luxon";
import { getAngularService } from "../angular-react-helper";
import { getConfig, resetDataAndRefresh } from "../config/dynamicConfig";
import { storageGet, storageSet } from "../plugin/storage";
import { logDebug } from "../plugin/logger";
import { readConsentState, isConsented } from "../splash/startprefs";
import { getAngularService } from "../angular-react-helper";

export const INTRO_DONE_KEY = 'intro_done';

Expand Down Expand Up @@ -61,15 +62,22 @@ export function getPendingOnboardingState(): Promise<OnboardingState> {
};

async function readConsented() {
const StartPrefs = getAngularService('StartPrefs');
return StartPrefs.readConsentState().then(StartPrefs.isConsented) as Promise<boolean>;
return readConsentState().then(isConsented) as Promise<boolean>;
}

async function readIntroDone() {
export async function readIntroDone() {
return storageGet(INTRO_DONE_KEY).then((read_val) => !!read_val) as Promise<boolean>;
}

export async function markIntroDone() {
const currDateTime = DateTime.now().toISO();
return storageSet(INTRO_DONE_KEY, currDateTime);
return storageSet(INTRO_DONE_KEY, currDateTime)
.then(() => {
//handle "on intro" events
shankari marked this conversation as resolved.
Show resolved Hide resolved
logDebug("intro done, calling registerPush and storeDeviceSettings");
const PushNotify = getAngularService("PushNotify");
const StoreSeviceSettings = getAngularService("StoreDeviceSettings");
shankari marked this conversation as resolved.
Show resolved Hide resolved
PushNotify.registerPush();
StoreSeviceSettings.storeDeviceSettings();
});
}
7 changes: 6 additions & 1 deletion www/js/plugin/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ const unmungeValue = (key, retData) => {
}

const localStorageSet = (key: string, value: {[k: string]: any}) => {
localStorage.setItem(key, JSON.stringify(value));
//checking for a value to prevent storing undefined
//case where local was null and native was undefined stored "undefined"
//see discussion: https://github.com/e-mission/e-mission-phone/pull/1072#discussion_r1373753945
if (value) {
localStorage.setItem(key, JSON.stringify(value));
}
Abby-Wheelis marked this conversation as resolved.
Show resolved Hide resolved
}

const localStorageGet = (key: string) => {
Expand Down
1 change: 0 additions & 1 deletion www/js/splash/localnotify.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import angular from 'angular';

angular.module('emission.splash.localnotify', ['emission.plugin.logger',
'emission.splash.startprefs',
'ionic-toast'])
.factory('LocalNotify', function($window, $ionicPlatform, $ionicPopup,
$state, $rootScope, ionicToast, Logger) {
Expand Down
28 changes: 9 additions & 19 deletions www/js/splash/pushnotify.js
shankari marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//naming of this file can be a little confusing - "pushnotifysettings" for rewritten file
//https://github.com/e-mission/e-mission-phone/pull/1072#discussion_r1375360832


/*
* This module deals with the interaction with the push plugin, the redirection
* of silent push notifications and the re-parsing of iOS pushes. It then
Expand All @@ -15,12 +19,12 @@

import angular from 'angular';
import { updateUser } from '../commHelper';
import { readConsentState, isConsented } from './startprefs';

angular.module('emission.splash.pushnotify', ['emission.plugin.logger',
'emission.services',
'emission.splash.startprefs'])
'emission.services'])
.factory('PushNotify', function($window, $state, $rootScope, $ionicPlatform,
$ionicPopup, Logger, StartPrefs) {
$ionicPopup, Logger) {

var pushnotify = {};
var push = null;
Expand Down Expand Up @@ -159,8 +163,8 @@ angular.module('emission.splash.pushnotify', ['emission.plugin.logger',

$ionicPlatform.ready().then(function() {
pushnotify.datacollect = $window.cordova.plugins.BEMDataCollection;
StartPrefs.readConsentState()
.then(StartPrefs.isConsented)
readConsentState()
.then(isConsented)
.then(function(consentState) {
if (consentState == true) {
pushnotify.registerPush();
Expand All @@ -172,19 +176,5 @@ angular.module('emission.splash.pushnotify', ['emission.plugin.logger',
Logger.log("pushnotify startup done");
});

$rootScope.$on(StartPrefs.CONSENTED_EVENT, function(event, data) {
console.log("got consented event "+JSON.stringify(event.name)
+" with data "+ JSON.stringify(data));
if (StartPrefs.isIntroDone()) {
console.log("intro is done -> reconsent situation, we already have a token -> register");
pushnotify.registerPush();
}
});

$rootScope.$on(StartPrefs.INTRO_DONE_EVENT, function(event, data) {
console.log("intro is done -> original consent situation, we should have a token by now -> register");
pushnotify.registerPush();
});

return pushnotify;
});
6 changes: 4 additions & 2 deletions www/js/splash/remotenotify.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//naming of this module can be confusing "remotenotifyhandler" for rewritten file
//https://github.com/e-mission/e-mission-phone/pull/1072#discussion_r1375360832

/*
* This module deals with handling specific push messages that open web pages
* or popups. It does not interface with the push plugin directly. Instead, it
Expand All @@ -15,8 +18,7 @@
import angular from 'angular';
import { addStatEvent, statKeys } from '../plugin/clientStats';

angular.module('emission.splash.remotenotify', ['emission.plugin.logger',
'emission.splash.startprefs'])
angular.module('emission.splash.remotenotify', ['emission.plugin.logger'])

.factory('RemoteNotify', function($http, $window, $ionicPopup, $rootScope, Logger) {

Expand Down
Loading
Loading