Add way to track posthog user properties and send numSpaces (#7716)

pull/21833/head
Michael Telatynski 2022-02-09 09:21:56 +00:00 committed by GitHub
parent c01d46d7ff
commit 91ccbe4395
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 43 additions and 22 deletions

View File

@ -90,7 +90,7 @@
"linkifyjs": "^4.0.0-beta.4", "linkifyjs": "^4.0.0-beta.4",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"maplibre-gl": "^1.15.2", "maplibre-gl": "^1.15.2",
"matrix-analytics-events": "github:matrix-org/matrix-analytics-events.git#dfa6feaa12bcfc8e99b05a148e12fff7f9d62f08", "matrix-analytics-events": "github:matrix-org/matrix-analytics-events.git#53844e3f6f9fefa88384a996b2bf5e60bb301b94",
"matrix-events-sdk": "^0.0.1-beta.6", "matrix-events-sdk": "^0.0.1-beta.6",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-widget-api": "^0.1.0-beta.18", "matrix-widget-api": "^0.1.0-beta.18",

View File

@ -17,6 +17,7 @@ limitations under the License.
import posthog, { PostHog } from 'posthog-js'; import posthog, { PostHog } from 'posthog-js';
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { UserProperties } from "matrix-analytics-events/types/typescript/UserProperties";
import PlatformPeg from './PlatformPeg'; import PlatformPeg from './PlatformPeg';
import SdkConfig from './SdkConfig'; import SdkConfig from './SdkConfig';
@ -42,6 +43,10 @@ import SettingsStore from "./settings/SettingsStore";
interface IEvent { interface IEvent {
// The event name that will be used by PostHog. Event names should use camelCase. // The event name that will be used by PostHog. Event names should use camelCase.
eventName: string; eventName: string;
// do not allow these to be sent manually, we enqueue them all for caching purposes
"$set"?: void;
"$set_once"?: void;
} }
export enum Anonymity { export enum Anonymity {
@ -109,6 +114,8 @@ export class PosthogAnalytics {
private static _instance = null; private static _instance = null;
private platformSuperProperties = {}; private platformSuperProperties = {};
private static ANALYTICS_EVENT_TYPE = "im.vector.analytics"; private static ANALYTICS_EVENT_TYPE = "im.vector.analytics";
private propertiesForNextEvent: Partial<Record<"$set" | "$set_once", UserProperties>> = {};
private userPropertyCache: UserProperties = {};
public static get instance(): PosthogAnalytics { public static get instance(): PosthogAnalytics {
if (!this._instance) { if (!this._instance) {
@ -198,7 +205,11 @@ export class PosthogAnalytics {
} }
const { origin, hash, pathname } = window.location; const { origin, hash, pathname } = window.location;
properties['$redacted_current_url'] = getRedactedCurrentLocation(origin, hash, pathname); properties['$redacted_current_url'] = getRedactedCurrentLocation(origin, hash, pathname);
this.posthog.capture(eventName, properties); this.posthog.capture(eventName, {
...this.propertiesForNextEvent,
...properties,
});
this.propertiesForNextEvent = {};
} }
public isEnabled(): boolean { public isEnabled(): boolean {
@ -260,13 +271,29 @@ export class PosthogAnalytics {
this.setAnonymity(Anonymity.Disabled); this.setAnonymity(Anonymity.Disabled);
} }
public trackEvent<E extends IEvent>( public trackEvent<E extends IEvent>({ eventName, ...properties }: E): void {
event: E,
): void {
if (this.anonymity == Anonymity.Disabled || this.anonymity == Anonymity.Anonymous) return; if (this.anonymity == Anonymity.Disabled || this.anonymity == Anonymity.Anonymous) return;
const eventWithoutName = { ...event }; this.capture(eventName, properties);
delete eventWithoutName.eventName; }
this.capture(event.eventName, eventWithoutName);
public setProperty<K extends keyof UserProperties>(key: K, value: UserProperties[K]): void {
if (this.userPropertyCache[key] === value) return; // nothing to do
this.userPropertyCache[key] = value;
if (!this.propertiesForNextEvent["$set"]) {
this.propertiesForNextEvent["$set"] = {};
}
this.propertiesForNextEvent["$set"][key] = value;
}
public setPropertyOnce<K extends keyof UserProperties>(key: K, value: UserProperties[K]): void {
if (this.userPropertyCache[key]) return; // nothing to do
this.userPropertyCache[key] = value;
if (!this.propertiesForNextEvent["$set_once"]) {
this.propertiesForNextEvent["$set_once"] = {};
}
this.propertiesForNextEvent["$set_once"][key] = value;
} }
public async updatePlatformSuperProperties(): Promise<void> { public async updatePlatformSuperProperties(): Promise<void> {

View File

@ -22,8 +22,6 @@ import { IRoomTimelineData } from "matrix-js-sdk/src/models/event-timeline-set";
import dis from "../dispatcher/dispatcher"; import dis from "../dispatcher/dispatcher";
import { ActionPayload } from "../dispatcher/payloads"; import { ActionPayload } from "../dispatcher/payloads";
// TODO: migrate from sync_state to MatrixActions.sync so that more js-sdk events
// become dispatches in the same place.
/** /**
* Create a MatrixActions.sync action that represents a MatrixClient `sync` event, * Create a MatrixActions.sync action that represents a MatrixClient `sync` event,
* each parameter mapping to a key-value in the action. * each parameter mapping to a key-value in the action.

View File

@ -1523,13 +1523,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}); });
cli.on('sync', (state: SyncState, prevState?: SyncState, data?: ISyncStateData) => { cli.on('sync', (state: SyncState, prevState?: SyncState, data?: ISyncStateData) => {
// LifecycleStore and others cannot directly subscribe to matrix client for
// events because flux only allows store state changes during flux dispatches.
// So dispatch directly from here. Ideally we'd use a SyncStateStore that
// would do this dispatch and expose the sync state itself (by listening to
// its own dispatch).
dis.dispatch({ action: 'sync_state', prevState, state });
if (state === SyncState.Error || state === SyncState.Reconnecting) { if (state === SyncState.Error || state === SyncState.Reconnecting) {
if (data.error instanceof InvalidStoreError) { if (data.error instanceof InvalidStoreError) {
Lifecycle.handleInvalidStoreError(data.error); Lifecycle.handleInvalidStoreError(data.error);

View File

@ -879,10 +879,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
}); });
} }
break; break;
case 'sync_state': case 'MatrixActions.sync':
if (!this.state.matrixClientIsReady) { if (!this.state.matrixClientIsReady) {
this.setState({ this.setState({
matrixClientIsReady: this.context && this.context.isInitialSyncComplete(), matrixClientIsReady: this.context?.isInitialSyncComplete(),
}, () => { }, () => {
// send another "initial" RVS update to trigger peeking if needed // send another "initial" RVS update to trigger peeking if needed
this.onRoomViewStoreUpdate(true); this.onRoomViewStoreUpdate(true);

View File

@ -56,7 +56,7 @@ class LifecycleStore extends Store<ActionPayload> {
deferredAction: null, deferredAction: null,
}); });
break; break;
case 'sync_state': { case 'MatrixActions.sync': {
if (payload.state !== 'PREPARED') { if (payload.state !== 'PREPARED') {
break; break;
} }

View File

@ -53,6 +53,7 @@ import {
} from "."; } from ".";
import { getCachedRoomIDForAlias } from "../../RoomAliasCache"; import { getCachedRoomIDForAlias } from "../../RoomAliasCache";
import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership"; import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
import { PosthogAnalytics } from "../../PosthogAnalytics";
interface IState {} interface IState {}
@ -490,6 +491,8 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
this.parentMap.getOrCreate(child.roomId, new Set()).add(space.roomId); this.parentMap.getOrCreate(child.roomId, new Set()).add(space.roomId);
}); });
}); });
PosthogAnalytics.instance.setProperty("numSpaces", joinedSpaces.length);
}; };
private rebuildHomeSpace = () => { private rebuildHomeSpace = () => {

View File

@ -6249,9 +6249,9 @@ mathml-tag-names@^2.1.3:
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
"matrix-analytics-events@github:matrix-org/matrix-analytics-events.git#dfa6feaa12bcfc8e99b05a148e12fff7f9d62f08": "matrix-analytics-events@github:matrix-org/matrix-analytics-events.git#53844e3f6f9fefa88384a996b2bf5e60bb301b94":
version "0.0.1" version "0.0.1"
resolved "https://codeload.github.com/matrix-org/matrix-analytics-events/tar.gz/dfa6feaa12bcfc8e99b05a148e12fff7f9d62f08" resolved "https://codeload.github.com/matrix-org/matrix-analytics-events/tar.gz/53844e3f6f9fefa88384a996b2bf5e60bb301b94"
matrix-events-sdk@^0.0.1-beta.6: matrix-events-sdk@^0.0.1-beta.6:
version "0.0.1-beta.6" version "0.0.1-beta.6"