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
  • Loading branch information
JGreenlee committed Oct 23, 2024
1 parent ee85fb0 commit 478815e
Show file tree
Hide file tree
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
Expand Up @@ -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
Expand All @@ -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();
}
Expand Down
9 changes: 6 additions & 3 deletions www/js/onboarding/WelcomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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}`),
)
}>
Expand Down
10 changes: 8 additions & 2 deletions www/js/onboarding/onboardingHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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 };
},
);
}
Expand Down
12 changes: 11 additions & 1 deletion www/js/plugin/clientStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 478815e

Please sign in to comment.