chore: add telemetry
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useWizard, Wizard } from "react-use-wizard";
|
||||
import clsx from "clsx";
|
||||
@@ -14,6 +14,7 @@ import SelectLanguage from "../../components/Language";
|
||||
import { useGetAutoTunnelInfoQuery, useGetServerVersionQuery } from "@/app/services/server";
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import { compareVersion } from "@/utils";
|
||||
import { trackUmamiEvent } from "@/utils/umami";
|
||||
import { shallowEqual } from "react-redux";
|
||||
|
||||
const TUNNEL_MIN_VERSION = "0.5.19";
|
||||
@@ -58,6 +59,12 @@ const Navigator = ({ showTunnelStep }: { showTunnelStep: boolean }) => {
|
||||
export default function OnboardingPage() {
|
||||
const { t } = useTranslation("welcome");
|
||||
const [serverName, setServerName] = useState("");
|
||||
|
||||
// Load Umami and fire "installed" once when the onboarding page mounts.
|
||||
// forceLoad=true so the script is fetched here and only here.
|
||||
useEffect(() => {
|
||||
trackUmamiEvent("installed");
|
||||
}, []);
|
||||
const { isLoading: versionLoading } = useGetServerVersionQuery();
|
||||
const currentVersion = useAppSelector((store) => store.server.version, shallowEqual);
|
||||
const versionOk = !!currentVersion && compareVersion(currentVersion, TUNNEL_MIN_VERSION) >= 0;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { BASE_ORIGIN, tokenHeader } from "@/app/config";
|
||||
import { getLocalAuthData, compareVersion } from "@/utils";
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import { useGetAutoTunnelInfoQuery } from "@/app/services/server";
|
||||
import { trackUmamiEvent } from "@/utils/umami";
|
||||
|
||||
const REQUIRED_VERSION = "0.5.19";
|
||||
|
||||
@@ -66,6 +67,7 @@ export default function GetPublicDomain() {
|
||||
}
|
||||
|
||||
const handleYes = async () => {
|
||||
trackUmamiEvent("create_tunnel");
|
||||
setPhase("starting");
|
||||
setLogs([]);
|
||||
setErrorMsg(null);
|
||||
|
||||
@@ -9,6 +9,7 @@ import ServerVersionChecker from "@/components/ServerVersionChecker";
|
||||
import Button from "@/components/styled/Button";
|
||||
import { getLocalAuthData, compareVersion } from "@/utils";
|
||||
import { useAppSelector } from "@/app/store";
|
||||
import { trackUmamiEvent } from "@/utils/umami";
|
||||
|
||||
const TUNNEL_MIN_VERSION = "0.5.19";
|
||||
|
||||
@@ -80,6 +81,7 @@ export default function Cloudflared() {
|
||||
|
||||
const startTunnel = async () => {
|
||||
if (streaming) return;
|
||||
trackUmamiEvent("create_tunnel");
|
||||
setStreaming(true);
|
||||
setStatus("starting");
|
||||
setLogs([]);
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Umami analytics — lazy loader.
|
||||
*
|
||||
* The script is injected into <head> at most once per page lifetime,
|
||||
* loaded on demand the first time trackUmamiEvent() is called.
|
||||
* Automatic pageview tracking is disabled; all events are explicit.
|
||||
*/
|
||||
|
||||
const UMAMI_SCRIPT_URL = "https://install.voce.chat/script.js";
|
||||
const UMAMI_WEBSITE_ID = "121f1469-bdce-4326-8fa3-7b820837c916";
|
||||
|
||||
type UmamiWindow = Window & { umami?: { track: (event: string, data?: Record<string, unknown>) => void } };
|
||||
|
||||
let loaded = false;
|
||||
let loading = false;
|
||||
const queue: Array<() => void> = [];
|
||||
|
||||
function loadScript(): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
if (loaded) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
queue.push(resolve);
|
||||
|
||||
if (loading) return;
|
||||
loading = true;
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.async = true;
|
||||
script.defer = true;
|
||||
script.src = UMAMI_SCRIPT_URL;
|
||||
script.setAttribute("data-website-id", UMAMI_WEBSITE_ID);
|
||||
script.setAttribute("data-auto-track", "false"); // disable automatic pageview tracking
|
||||
script.onload = () => {
|
||||
loaded = true;
|
||||
loading = false;
|
||||
queue.splice(0).forEach((cb) => cb());
|
||||
};
|
||||
script.onerror = () => {
|
||||
// Don't block callers on network failure; just drain the queue
|
||||
loading = false;
|
||||
queue.splice(0).forEach((cb) => cb());
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the Umami script (if not already loaded) then fire a named event.
|
||||
* The script is fetched on first call and reused for all subsequent calls.
|
||||
* Safe to call from anywhere — silently no-ops if the script fails to load.
|
||||
*
|
||||
* @param eventName The event name shown in the Umami dashboard.
|
||||
* @param data Optional key/value payload.
|
||||
*/
|
||||
export async function trackUmamiEvent(
|
||||
eventName: string,
|
||||
data?: Record<string, unknown>
|
||||
): Promise<void> {
|
||||
await loadScript();
|
||||
|
||||
const umami = (window as UmamiWindow).umami;
|
||||
if (umami?.track) {
|
||||
umami.track(eventName, data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user