Tidy up and fix some edge cases
This commit is contained in:
parent
c3a355097d
commit
b01055f962
4 changed files with 58 additions and 68 deletions
|
@ -292,22 +292,23 @@ interface IViewData {
|
|||
|
||||
// Apply fn to all hash path parts after the 1st one
|
||||
async function getViewData(anonymous = true): Promise<IViewData> {
|
||||
const rand = randomString(8);
|
||||
const { origin, hash } = window.location;
|
||||
let { pathname } = window.location;
|
||||
|
||||
// Redact paths which could contain unexpected PII
|
||||
if (origin.startsWith('file://')) {
|
||||
pathname = "/<redacted>/";
|
||||
pathname = `/<redacted_${rand}>/`; // XXX: inject rand because Count.ly doesn't like X->X transitions
|
||||
}
|
||||
|
||||
let [_, screen, ...parts] = hash.split("/");
|
||||
|
||||
if (!knownScreens.has(screen)) {
|
||||
screen = "<redacted>";
|
||||
screen = `<redacted_${rand}>`;
|
||||
}
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
parts[i] = anonymous ? "<redacted>" : await hashHex(parts[i]);
|
||||
parts[i] = anonymous ? `<redacted_${rand}>` : await hashHex(parts[i]);
|
||||
}
|
||||
|
||||
const hashStr = `${_}/${screen}/${parts.join("/")}`;
|
||||
|
@ -342,19 +343,28 @@ const getRoomStats = (roomId: string) => {
|
|||
}
|
||||
}
|
||||
|
||||
export class CountlyAnalytics {
|
||||
export default class CountlyAnalytics {
|
||||
private baseUrl: URL = null;
|
||||
private appKey: string = null;
|
||||
private userKey: string = null;
|
||||
private firstPage = true;
|
||||
private heartbeatIntervalID: number = null;
|
||||
|
||||
private anonymous = true;
|
||||
private pendingEvents: IEvent[] = [];
|
||||
|
||||
private anonymous: boolean;
|
||||
private appPlatform: string;
|
||||
private appVersion = "unknown";
|
||||
|
||||
private initTime = CountlyAnalytics.getTimestamp();
|
||||
private firstPage = true;
|
||||
private heartbeatIntervalId: NodeJS.Timeout;
|
||||
private activityIntervalId: NodeJS.Timeout;
|
||||
private trackTime = true;
|
||||
private lastBeat: number;
|
||||
private storedDuration = 0;
|
||||
private lastView: string;
|
||||
private lastViewTime = 0;
|
||||
private lastViewStoredDuration = 0;
|
||||
private sessionStarted = false;
|
||||
private heartbeatEnabled = false;
|
||||
private inactivityCounter = 0;
|
||||
private pendingEvents: IEvent[] = [];
|
||||
|
||||
private static internalInstance = new CountlyAnalytics();
|
||||
|
||||
|
@ -374,8 +384,8 @@ export class CountlyAnalytics {
|
|||
private async changeUserKey(userKey: string, merge = false) {
|
||||
const oldUserKey = this.userKey;
|
||||
this.userKey = userKey;
|
||||
if (merge) {
|
||||
this.request({ old_device_id: oldUserKey });
|
||||
if (oldUserKey && merge) {
|
||||
await this.request({ old_device_id: oldUserKey });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,26 +393,15 @@ export class CountlyAnalytics {
|
|||
if (!this.disabled && this.anonymous === anonymous) return;
|
||||
if (!this.canEnable()) return;
|
||||
|
||||
if (!this.disabled && this.anonymous !== anonymous) {
|
||||
this.anonymous = anonymous;
|
||||
if (anonymous) {
|
||||
await this.changeUserKey(randomString(64))
|
||||
} else {
|
||||
await this.changeUserKey(await hashHex(MatrixClientPeg.get().getUserId()), true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const config = SdkConfig.get();
|
||||
|
||||
this.baseUrl = new URL("/i", config.countly.url);
|
||||
this.appKey = config.countly.appKey;
|
||||
|
||||
this.anonymous = anonymous;
|
||||
if (this.anonymous) {
|
||||
this.userKey = randomString(64);
|
||||
if (anonymous) {
|
||||
await this.changeUserKey(randomString(64))
|
||||
} else {
|
||||
this.userKey = await hashHex(MatrixClientPeg.get().getUserId());
|
||||
await this.changeUserKey(await hashHex(MatrixClientPeg.get().getUserId()), true);
|
||||
}
|
||||
|
||||
const platform = PlatformPeg.get();
|
||||
|
@ -414,19 +413,26 @@ export class CountlyAnalytics {
|
|||
}
|
||||
|
||||
// start heartbeat
|
||||
this.heartbeatIntervalID = window.setInterval(this.heartbeat.bind(this), HEARTBEAT_INTERVAL);
|
||||
this.trackSessions(); // TODO clear on disable
|
||||
this.trackErrors(); // TODO clear on disable
|
||||
this.heartbeatIntervalId = setInterval(this.heartbeat.bind(this), HEARTBEAT_INTERVAL);
|
||||
this.trackSessions();
|
||||
this.trackErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable Analytics, stop the heartbeat and clear identifiers from localStorage
|
||||
*/
|
||||
public disable() {
|
||||
public async disable() {
|
||||
if (this.disabled) return;
|
||||
this.queue({ key: "Opt-Out" });
|
||||
window.clearInterval(this.heartbeatIntervalID);
|
||||
await this.track("Opt-Out" );
|
||||
this.endSession();
|
||||
window.clearInterval(this.heartbeatIntervalId);
|
||||
window.clearTimeout(this.activityIntervalId)
|
||||
this.baseUrl = null;
|
||||
// remove listeners bound in trackSessions()
|
||||
window.removeEventListener("beforeunload", this.endSession);
|
||||
window.removeEventListener("unload", this.endSession);
|
||||
window.removeEventListener("visibilitychange", this.onVisibilityChange);
|
||||
window.removeEventListener("mousemove", this.onUserActivity);
|
||||
window.removeEventListener("click", this.onUserActivity);
|
||||
window.removeEventListener("keydown", this.onUserActivity);
|
||||
window.removeEventListener("scroll", this.onUserActivity);
|
||||
}
|
||||
|
||||
public reportFeedback(rating: 1 | 2 | 3 | 4 | 5, comment: string) {
|
||||
|
@ -435,12 +441,6 @@ export class CountlyAnalytics {
|
|||
|
||||
public trackPageChange(generationTimeMs?: number) {
|
||||
if (this.disabled) return;
|
||||
|
||||
if (typeof generationTimeMs !== 'number') {
|
||||
console.warn('Analytics.trackPageChange: expected generationTimeMs to be a number');
|
||||
// But continue anyway because we still want to track the change
|
||||
}
|
||||
|
||||
// TODO use generationTimeMs
|
||||
this.trackPageView();
|
||||
}
|
||||
|
@ -491,7 +491,7 @@ export class CountlyAnalytics {
|
|||
}
|
||||
|
||||
public recordError(err: Error | string, fatal = false) {
|
||||
if (this.disabled) return;
|
||||
if (this.disabled || this.anonymous) return;
|
||||
|
||||
let error = "";
|
||||
if (typeof err === "object") {
|
||||
|
@ -534,11 +534,6 @@ export class CountlyAnalytics {
|
|||
|
||||
ob._background = document.hasFocus();
|
||||
|
||||
// if (crashLogs.length > 0) {
|
||||
// ob._logs = crashLogs.join("\n");
|
||||
// }
|
||||
// crashLogs = [];
|
||||
|
||||
this.request({ crash: JSON.stringify(ob) });
|
||||
}
|
||||
|
||||
|
@ -672,13 +667,6 @@ export class CountlyAnalytics {
|
|||
});
|
||||
}
|
||||
|
||||
private trackTime = true;
|
||||
private lastBeat: number;
|
||||
private storedDuration = 0;
|
||||
private lastView: string;
|
||||
private lastViewTime = 0;
|
||||
private lastViewStoredDuration = 0;
|
||||
|
||||
private startTime() {
|
||||
if (!this.trackTime) {
|
||||
this.trackTime = true;
|
||||
|
@ -697,6 +685,7 @@ export class CountlyAnalytics {
|
|||
}
|
||||
|
||||
private getMetrics(): IMetrics {
|
||||
if (this.anonymous) return undefined;
|
||||
const metrics: IMetrics = {};
|
||||
|
||||
// getting app version
|
||||
|
@ -719,13 +708,10 @@ export class CountlyAnalytics {
|
|||
return metrics;
|
||||
}
|
||||
|
||||
private sessionStarted = false;
|
||||
private heartbeatEnabled = false;
|
||||
|
||||
private async beginSession(heartbeat = true) {
|
||||
if (!this.sessionStarted) {
|
||||
this.reportOrientation();
|
||||
window.addEventListener("resize", this.reportOrientation)
|
||||
window.addEventListener("resize", this.reportOrientation);
|
||||
|
||||
this.lastBeat = CountlyAnalytics.getTimestamp();
|
||||
this.sessionStarted = true;
|
||||
|
@ -738,7 +724,7 @@ export class CountlyAnalytics {
|
|||
},
|
||||
};
|
||||
|
||||
this.request({
|
||||
await this.request({
|
||||
begin_session: 1,
|
||||
metrics: JSON.stringify(this.getMetrics()),
|
||||
user_details: JSON.stringify(userDetails),
|
||||
|
@ -761,9 +747,11 @@ export class CountlyAnalytics {
|
|||
if (this.sessionStarted) {
|
||||
window.removeEventListener("resize", this.reportOrientation)
|
||||
|
||||
const sec = CountlyAnalytics.getTimestamp() - this.lastBeat;
|
||||
this.reportViewDuration();
|
||||
this.request({ end_session: 1, session_duration: sec });
|
||||
this.request({
|
||||
end_session: 1,
|
||||
session_duration: CountlyAnalytics.getTimestamp() - this.lastBeat,
|
||||
});
|
||||
}
|
||||
this.sessionStarted = false;
|
||||
}
|
||||
|
@ -783,8 +771,6 @@ export class CountlyAnalytics {
|
|||
this.inactivityCounter = 0;
|
||||
};
|
||||
|
||||
private inactivityCounter = 0;
|
||||
|
||||
private trackSessions() {
|
||||
this.beginSession();
|
||||
this.startTime();
|
||||
|
@ -797,7 +783,7 @@ export class CountlyAnalytics {
|
|||
window.addEventListener("keydown", this.onUserActivity);
|
||||
window.addEventListener("scroll", this.onUserActivity);
|
||||
|
||||
setInterval(() => {
|
||||
this.activityIntervalId = setInterval(() => {
|
||||
this.inactivityCounter++;
|
||||
if (this.inactivityCounter >= INACTIVITY_TIME) {
|
||||
this.stopTime();
|
||||
|
@ -942,10 +928,9 @@ export class CountlyAnalytics {
|
|||
|
||||
// if this event can be sent anonymously and we are disabled then dispatch it right away
|
||||
if (this.disabled && anonymous) {
|
||||
this.request({ device_id: randomString(64) });
|
||||
await this.request({ device_id: randomString(64) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.mxCountlyAnalytics = CountlyAnalytics;
|
||||
export default CountlyAnalytics;
|
||||
|
|
|
@ -47,6 +47,7 @@ import DeviceListener from "./DeviceListener";
|
|||
import {Jitsi} from "./widgets/Jitsi";
|
||||
import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY} from "./BasePlatform";
|
||||
import ThreepidInviteStore from "./stores/ThreepidInviteStore";
|
||||
import CountlyAnalytics from "./CountlyAnalytics";
|
||||
|
||||
const HOMESERVER_URL_KEY = "mx_hs_url";
|
||||
const ID_SERVER_URL_KEY = "mx_is_url";
|
||||
|
@ -580,6 +581,10 @@ let _isLoggingOut = false;
|
|||
*/
|
||||
export function logout(): void {
|
||||
if (!MatrixClientPeg.get()) return;
|
||||
if (!CountlyAnalytics.instance.disabled) {
|
||||
// user has logged out, fall back to anonymous
|
||||
CountlyAnalytics.instance.enable(true);
|
||||
}
|
||||
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
// logout doesn't work for guest sessions
|
||||
|
|
|
@ -103,7 +103,7 @@ export default class SecurityUserSettingsTab extends React.Component {
|
|||
|
||||
_updateAnalytics = (checked) => {
|
||||
checked ? Analytics.enable() : Analytics.disable();
|
||||
checked ? CountlyAnalytics.enable() : CountlyAnalytics.disable();
|
||||
CountlyAnalytics.instance.enable(!checked);
|
||||
};
|
||||
|
||||
_onExportE2eKeysClicked = () => {
|
||||
|
|
|
@ -1705,8 +1705,8 @@
|
|||
"Please go into as much detail as you like, so we can track down the problem.": "Please go into as much detail as you like, so we can track down the problem.",
|
||||
"Add comment": "Add comment",
|
||||
"Comment": "Comment",
|
||||
"Feedback": "Feedback",
|
||||
"There are two ways you can provide feedback and help us improve %(brand)s.": "There are two ways you can provide feedback and help us improve %(brand)s.",
|
||||
"Feedback": "Feedback",
|
||||
"Report a bug": "Report a bug",
|
||||
"Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. No match? <newIssueLink>Start a new one</newIssueLink>.": "Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. No match? <newIssueLink>Start a new one</newIssueLink>.",
|
||||
"PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.": "PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.",
|
||||
|
|
Loading…
Reference in a new issue