Enhance time-spent tracking
Summary: Previously, at 1-minute intervals, if the flipper window was focused on, it would report the currently active plugin. We'd sum all those "ping" events and that would approximate the number of full minutes spent in total across all users. It's quite coarse grained, if you're focused on the window for 30 seconds, there's a 50% change your ping will get used. While being reasonable across many users, it doesn't allow analysis like how many plugins do people typically use in a session, because we probably won't see all the plugins they use. New approach, for every minute flipper is open, report the focused and unfocused time spent in each plugin, as well as the total across all plugins. This should give us the previous data but with much more precision. Should be especially helpful for plugins with low numbers of users, you typically interact with emulators while using a plugin, so it's not continually in focus, so you miss a lot of usage events. enhance_bladerunner Reviewed By: nikoant Differential Revision: D19392796 fbshipit-source-id: af9244e993edff9b381144ca587c3a77fdf8c98a
This commit is contained in:
committed by
Facebook Github Bot
parent
84302f109b
commit
a96931c43f
@@ -90,7 +90,6 @@ type BooleanActionType =
|
||||
| 'leftSidebarVisible'
|
||||
| 'rightSidebarVisible'
|
||||
| 'rightSidebarAvailable'
|
||||
| 'windowIsFocused'
|
||||
| 'downloadingImportData';
|
||||
|
||||
export type Action =
|
||||
@@ -98,6 +97,10 @@ export type Action =
|
||||
type: BooleanActionType;
|
||||
payload?: boolean;
|
||||
}
|
||||
| {
|
||||
type: 'windowIsFocused';
|
||||
payload: {isFocused: boolean; time: number};
|
||||
}
|
||||
| {
|
||||
type: 'SET_ACTIVE_SHEET';
|
||||
payload: ActiveSheet;
|
||||
@@ -169,6 +172,7 @@ export const initialState: () => State = () => ({
|
||||
},
|
||||
statusMessages: [],
|
||||
xcodeCommandLineToolsDetected: false,
|
||||
trackingTimeline: [],
|
||||
});
|
||||
|
||||
function statusMessage(sender: string, msg: string): string {
|
||||
@@ -191,7 +195,6 @@ export default function reducer(
|
||||
action.type === 'leftSidebarVisible' ||
|
||||
action.type === 'rightSidebarVisible' ||
|
||||
action.type === 'rightSidebarAvailable' ||
|
||||
action.type === 'windowIsFocused' ||
|
||||
action.type === 'downloadingImportData'
|
||||
) {
|
||||
const newValue =
|
||||
@@ -208,6 +211,11 @@ export default function reducer(
|
||||
[action.type]: newValue,
|
||||
};
|
||||
}
|
||||
} else if (action.type === 'windowIsFocused') {
|
||||
return {
|
||||
...state,
|
||||
windowIsFocused: action.payload.isFocused,
|
||||
};
|
||||
} else if (action.type === 'SET_ACTIVE_SHEET') {
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -88,6 +88,7 @@ export type Action =
|
||||
selectedApp?: null | string;
|
||||
deepLinkPayload: null | string;
|
||||
selectedDevice?: null | BaseDevice;
|
||||
time: number;
|
||||
};
|
||||
}
|
||||
| {
|
||||
@@ -461,9 +462,10 @@ export const selectPlugin = (payload: {
|
||||
selectedApp?: null | string;
|
||||
selectedDevice?: BaseDevice | null;
|
||||
deepLinkPayload: null | string;
|
||||
time?: number;
|
||||
}): Action => ({
|
||||
type: 'SELECT_PLUGIN',
|
||||
payload,
|
||||
payload: {...payload, time: payload.time ?? Date.now()},
|
||||
});
|
||||
|
||||
export const starPlugin = (payload: {
|
||||
|
||||
@@ -52,6 +52,10 @@ import healthchecks, {
|
||||
Action as HealthcheckAction,
|
||||
State as HealthcheckState,
|
||||
} from './healthchecks';
|
||||
import usageTracking, {
|
||||
Action as TrackingAction,
|
||||
State as TrackingState,
|
||||
} from './usageTracking';
|
||||
import user, {State as UserState, Action as UserAction} from './user';
|
||||
import JsonFileStorage from '../utils/jsonFileReduxPersistStorage';
|
||||
import LauncherSettingsStorage from '../utils/launcherSettingsStorage';
|
||||
@@ -78,6 +82,7 @@ export type Actions =
|
||||
| SupportFormAction
|
||||
| PluginManagerAction
|
||||
| HealthcheckAction
|
||||
| TrackingAction
|
||||
| {type: 'INIT'};
|
||||
|
||||
export type State = {
|
||||
@@ -93,6 +98,7 @@ export type State = {
|
||||
supportForm: SupportFormState;
|
||||
pluginManager: PluginManagerState;
|
||||
healthchecks: HealthcheckState & PersistPartial;
|
||||
usageTracking: TrackingState;
|
||||
};
|
||||
|
||||
export type Store = ReduxStore<State, Actions>;
|
||||
@@ -167,4 +173,5 @@ export default combineReducers<State, Actions>({
|
||||
},
|
||||
healthchecks,
|
||||
),
|
||||
usageTracking,
|
||||
});
|
||||
|
||||
86
src/reducers/usageTracking.tsx
Normal file
86
src/reducers/usageTracking.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {produce} from 'immer';
|
||||
import {remote} from 'electron';
|
||||
import {Actions} from './';
|
||||
|
||||
export type TrackingEvent =
|
||||
| {
|
||||
type: 'WINDOW_FOCUS_CHANGE';
|
||||
time: number;
|
||||
isFocused: boolean;
|
||||
}
|
||||
| {type: 'PLUGIN_SELECTED'; time: number; plugin: string | null}
|
||||
| {type: 'TIMELINE_START'; time: number; isFocused: boolean};
|
||||
|
||||
export type State = {
|
||||
timeline: TrackingEvent[];
|
||||
};
|
||||
const INITAL_STATE: State = {
|
||||
timeline: [
|
||||
{
|
||||
type: 'TIMELINE_START',
|
||||
time: Date.now(),
|
||||
isFocused: remote.getCurrentWindow().isFocused(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export type Action =
|
||||
| {
|
||||
type: 'windowIsFocused';
|
||||
payload: {isFocused: boolean; time: number};
|
||||
}
|
||||
| {type: 'CLEAR_TIMELINE'; payload: {time: number; isFocused: boolean}};
|
||||
|
||||
export default function reducer(
|
||||
state: State = INITAL_STATE,
|
||||
action: Actions,
|
||||
): State {
|
||||
if (action.type === 'CLEAR_TIMELINE') {
|
||||
return {
|
||||
...state,
|
||||
timeline: [
|
||||
{
|
||||
type: 'TIMELINE_START',
|
||||
time: action.payload.time,
|
||||
isFocused: action.payload.isFocused,
|
||||
},
|
||||
],
|
||||
};
|
||||
} else if (action.type === 'windowIsFocused') {
|
||||
return produce(state, draft => {
|
||||
draft.timeline.push({
|
||||
type: 'WINDOW_FOCUS_CHANGE',
|
||||
time: action.payload.time,
|
||||
isFocused: action.payload.isFocused,
|
||||
});
|
||||
});
|
||||
} else if (action.type === 'SELECT_PLUGIN') {
|
||||
return produce(state, draft => {
|
||||
draft.timeline.push({
|
||||
type: 'PLUGIN_SELECTED',
|
||||
time: action.payload.time,
|
||||
plugin: action.payload.selectedPlugin || null,
|
||||
});
|
||||
});
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
export function clearTimeline(time: number): Action {
|
||||
return {
|
||||
type: 'CLEAR_TIMELINE',
|
||||
payload: {
|
||||
time,
|
||||
isFocused: remote.getCurrentWindow().isFocused(),
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user