Skip to content

Commit

Permalink
add stats for onboarding
Browse files Browse the repository at this point in the history
We have multiple ways to initiate onboarding:
- scan QR in the app
- paste from clipboard in the app
- entering to a textbox in the app
- launching a nrelopenpath:// link from an external app (browser, camera)

We want to keep track of how often these methods are used, as well as how often they are successful.
First I added the 'onboard' stat which represents an attempt to onboard. The reading has 'configUpdated' (to indicate whether the attempt was successful), and 'joinMethod' ('scan', 'paste', 'textbox', or 'external')

I also added "open_qr_scanner" and "paste_token" events (no readings) because the user might open the QR scanner but not scan any QR codes. An 'onboard' stat would not be recorded, but we still want to know if the user clicked "Scan QR"

Lastly, I added 'onboarding_state' as a more general stat for evaluating our onboarding process.
We should be able to compare the timestamp at each reading to see how long users take at each stage of the onboarding process – as well as how long the entire onboarding process takes
JGreenlee committed Oct 23, 2024
1 parent ee85fb0 commit 478815e
Showing 4 changed files with 30 additions and 8 deletions.
7 changes: 5 additions & 2 deletions www/js/App.tsx
Original file line number Diff line number Diff line change
@@ -16,13 +16,15 @@ import { initRemoteNotifyHandler } from './splash/remoteNotifyHandler';
// import { getUserCustomLabels } from './services/commHelper';
import AlertBar from './components/AlertBar';
import Main from './Main';
import { joinWithTokenOrUrl } from './config/opcode';
import { joinWithTokenOrUrl } from './config/dynamicConfig';
import { addStatReading } from './plugin/clientStats';

export const AppContext = createContext<any>({});
const CUSTOM_LABEL_KEYS_IN_DATABASE = ['mode', 'purpose'];
type CustomLabelMap = {
[k: string]: string[];
};
type OnboardingJoinMethod = 'scan' | 'paste' | 'textbox' | 'external';

const App = () => {
// will remain null while the onboarding state is still being determined
@@ -39,8 +41,9 @@ const App = () => {

// handleOpenURL function must be provided globally for cordova-plugin-customurlscheme
// https://www.npmjs.com/package/cordova-plugin-customurlscheme
window['handleOpenURL'] = async (url: string) => {
window['handleOpenURL'] = async (url: string, joinMethod: OnboardingJoinMethod = 'external') => {
const configUpdated = await joinWithTokenOrUrl(url);
addStatReading('onboard', { configUpdated, joinMethod });
if (configUpdated) {
refreshOnboardingState();
}
9 changes: 6 additions & 3 deletions www/js/onboarding/WelcomePage.tsx
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ import { AppContext } from '../App';
import { displayError, logDebug, logWarn } from '../plugin/logger';
import { onboardingStyles } from './OnboardingStack';
import { AlertManager } from '../components/AlertBar';
import { addStatReading } from '../plugin/clientStats';

let barcodeScannerIsOpen = false;

@@ -41,6 +42,7 @@ const WelcomePage = () => {
function scanCode() {
if (barcodeScannerIsOpen) return;
barcodeScannerIsOpen = true;
addStatReading('open_qr_scanner');
window['cordova'].plugins.barcodeScanner.scan(
(result) => {
barcodeScannerIsOpen = false;
@@ -50,7 +52,7 @@ const WelcomePage = () => {
AlertManager.addMessage({ text: 'No QR code found in scan. Please try again.' });
return;
}
handleOpenURL(result.text);
handleOpenURL(result.text, 'scan');
},
(error) => {
barcodeScannerIsOpen = false;
@@ -61,11 +63,12 @@ const WelcomePage = () => {

function pasteCode() {
window['cordova'].plugins.clipboard.paste((clipboardContent: string) => {
addStatReading('paste_token');
try {
if (!clipboardContent?.startsWith('nrelop_') && !clipboardContent?.includes('://')) {
throw new Error('Clipboard content is not a valid token or URL');
}
handleOpenURL(clipboardContent);
handleOpenURL(clipboardContent, 'paste');
} catch (e) {
logWarn(`Tried using clipboard content ${clipboardContent}: ${e}`);
setPasteModalVis(true);
@@ -138,7 +141,7 @@ const WelcomePage = () => {
<Button onPress={() => setPasteModalVis(false)}>{t('login.button-decline')}</Button>
<Button
onPress={() =>
handleOpenURL(existingToken).catch((e) =>
handleOpenURL(existingToken, 'textbox').catch((e) =>
displayError(e, `Tried using token ${existingToken}`),
)
}>
10 changes: 8 additions & 2 deletions www/js/onboarding/onboardingHelper.ts
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import { storageGet, storageSet } from '../plugin/storage';
import { logDebug } from '../plugin/logger';
import { EVENTS, publish } from '../customEventHandler';
import { readConsentState, isConsented } from '../splash/startprefs';
import { addStatReading } from '../plugin/clientStats';

export const INTRO_DONE_KEY = 'intro_done';

@@ -62,13 +63,18 @@ export function getPendingOnboardingState(): Promise<OnboardingState> {
route = OnboardingRoute.SURVEY;
}

const opcode = config?.joined?.opcode;

logDebug(`pending onboarding state is ${route};
isIntroDone = ${isIntroDone};
config = ${config};
isConsented = ${isConsented};
saveQrDone = ${saveQrDone}`);
saveQrDone = ${saveQrDone};
opcode = ${opcode}`);

addStatReading('onboarding_state', { route, opcode });

return { route, opcode: config?.joined?.opcode };
return { route, opcode };
},
);
}
12 changes: 11 additions & 1 deletion www/js/plugin/clientStats.ts
Original file line number Diff line number Diff line change
@@ -4,15 +4,25 @@ const CLIENT_TIME = 'stats/client_time';
const CLIENT_ERROR = 'stats/client_error';

type StatKey =
// app-wide interaction stats
| 'app_state_change'
| 'nav_tab_change'
| 'open_notification'
// onboarding interaction stats
| 'onboard' // { configUpdated (true if success, onboarding began), joinMethod (scan if in-app QR scan, paste if in-app paste, textbox if manually entered, external if launched from browser or external QR scanner)}
| 'onboarding_state' // { route (current OnoardingRoute enum value), opcode}
| 'open_qr_scanner' // user opened in-app qr scanner
| 'paste_token' // user clicked 'paste code'
// 'label' interaction stats
| 'view_trip_details'
| 'multilabel_open'
| 'multilabel_choose'
// 'control' interaction stats
| 'set_reminder_prefs'
| 'force_sync'
| 'open_notification'
// unexpected states for debugging
| 'missing_keys'
// errors
| 'ui_error';

let appVersion;

0 comments on commit 478815e

Please sign in to comment.