From fd36310bb3a17d27c857125479857b200d343b34 Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Wed, 7 Feb 2024 18:23:35 +0700 Subject: [PATCH 1/3] feat: integrate umami script locally --- web/public/umami_script.js | 148 +++++++++++++++++++++++++++++++++++++ web/utils/umami.tsx | 86 ++++++++++++++------- 2 files changed, 209 insertions(+), 25 deletions(-) create mode 100644 web/public/umami_script.js diff --git a/web/public/umami_script.js b/web/public/umami_script.js new file mode 100644 index 000000000..f9b5b7fcf --- /dev/null +++ b/web/public/umami_script.js @@ -0,0 +1,148 @@ +! function() { + "use strict"; + ! function(t) { + var e = t.screen, + n = e.width, + r = e.height, + a = t.navigator.language, + i = t.location, + o = t.localStorage, + u = t.document, + c = t.history, + f = "jan.ai", + s = "main page", + l = i.search, + d = u.currentScript; + if (d) { + var m = "data-", + h = d.getAttribute.bind(d), + v = h(m + "website-id"), + p = h(m + "host-url"), + g = "false" !== h(m + "auto-track"), + y = h(m + "do-not-track"), + b = h(m + "domains") || "", + S = b.split(",").map((function(t) { + return t.trim() + })), + k = (p ? p.replace(/\/$/, "") : d.src.split("/").slice(0, -1).join("/")) + "/api/send", + w = n + "x" + r, + N = /data-umami-event-([\w-_]+)/, + T = m + "umami-event", + j = 300, + A = function(t, e, n) { + var r = t[e]; + return function() { + for (var e = [], a = arguments.length; a--;) e[a] = arguments[a]; + return n.apply(null, e), r.apply(t, e) + } + }, + x = function() { + return { + website: v, + hostname: f, + screen: w, + language: a, + title: M, + url: I, + referrer: J + } + }, + E = function() { + return o && o.getItem("umami.disabled") || y && function() { + var e = t.doNotTrack, + n = t.navigator, + r = t.external, + a = "msTrackingProtectionEnabled", + i = e || n.doNotTrack || n.msDoNotTrack || r && a in r && r[a](); + return "1" == i || "yes" === i + }() || b && !S.includes(f) + }, + O = function(t, e, n) { + n && (J = I, (I = function(t) { + try { + return new URL(t).pathname + } catch (e) { + return t + } + }(n.toString())) !== J && setTimeout(D, j)) + }, + L = function(t, e) { + if (void 0 === e && (e = "event"), !E()) { + var n = { + "Content-Type": "application/json" + }; + return void 0 !== K && (n["x-umami-cache"] = K), fetch(k, { + method: "POST", + body: JSON.stringify({ + type: e, + payload: t + }), + headers: n + }).then((function(t) { + return t.text() + })).then((function(t) { + return K = t + })).catch((function() {})) + } + }, + D = function(t, e) { + return L("string" == typeof t ? Object.assign({}, x(), { + name: t, + data: "object" == typeof e ? e : void 0 + }) : "object" == typeof t ? t : "function" == typeof t ? t(x()) : x()) + }; + t.umami || (t.umami = { + track: D, + identify: function(t) { + return L(Object.assign({}, x(), { + data: t + }), "identify") + } + }); + var K, P, _, q, C, I = "" + s + l, + J = u.referrer, + M = u.title; + if (g && !E()) { + c.pushState = A(c, "pushState", O), c.replaceState = A(c, "replaceState", O), C = function(t) { + var e = t.getAttribute.bind(t), + n = e(T); + if (n) { + var r = {}; + return t.getAttributeNames().forEach((function(t) { + var n = t.match(N); + n && (r[n[1]] = e(t)) + })), D(n, r) + } + return Promise.resolve() + }, u.addEventListener("click", (function(t) { + var e = t.target, + n = "A" === e.tagName ? e : function(t, e) { + for (var n = t, r = 0; r < e; r++) { + if ("A" === n.tagName) return n; + if (!(n = n.parentElement)) return null + } + return null + }(e, 10); + if (n) { + var r = n.href, + a = "_blank" === n.target || t.ctrlKey || t.shiftKey || t.metaKey || t.button && 1 === t.button; + if (n.getAttribute(T) && r) return a || t.preventDefault(), C(n).then((function() { + a || (i.href = r) + })) + } else C(e) + }), !0), _ = new MutationObserver((function(t) { + var e = t[0]; + M = e && e.target ? e.target.text : void 0 + })), (q = u.querySelector("head > title")) && _.observe(q, { + subtree: !0, + characterData: !0, + childList: !0 + }); + var R = function() { + "complete" !== u.readyState || P || (D(), P = !0) + }; + u.addEventListener("readystatechange", R, !0), R() + } + } + }(window) +}(); \ No newline at end of file diff --git a/web/utils/umami.tsx b/web/utils/umami.tsx index 277ae1223..9e678d77f 100644 --- a/web/utils/umami.tsx +++ b/web/utils/umami.tsx @@ -1,31 +1,67 @@ import { useEffect } from 'react' -const Umami = () => { - useEffect(() => { - if (!VERSION || !ANALYTICS_HOST || !ANALYTICS_ID) return - fetch(ANALYTICS_HOST, { - method: 'POST', - // eslint-disable-next-line @typescript-eslint/naming-convention - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - payload: { - website: ANALYTICS_ID, - hostname: 'jan.ai', - screen: `${screen.width}x${screen.height}`, - language: navigator.language, - referrer: 'index.html', - data: { version: VERSION }, - type: 'event', - title: document.title, - url: 'index.html', - name: VERSION, - }, - type: 'event', - }), - }) - }, []) +import Script from 'next/script' - return <> +// Define the type for the umami data object +interface UmamiData { + version: string +} + +declare global { + interface Window { + umami: + | { + track: (event: string, data?: UmamiData) => void + } + | undefined + } +} + +const Umami = () => { + const appVersion = VERSION + const analyticsScriptPath = './umami_script.js' + const analyticsId = ANALYTICS_ID + + useEffect(() => { + if (!appVersion || !analyticsScriptPath || !analyticsId) return + + const ping = () => { + // Check if umami is defined before ping + if (window.umami !== null && typeof window.umami !== 'undefined') { + window.umami.track(appVersion, { + version: appVersion, + }) + } + } + + // Wait for umami to be defined before ping + if (window.umami !== null && typeof window.umami !== 'undefined') { + ping() + } else { + // Listen for umami script load event + document.addEventListener('umami:loaded', ping) + } + + // Cleanup function to remove event listener if the component unmounts + return () => { + document.removeEventListener('umami:loaded', ping) + } + }, [appVersion, analyticsScriptPath, analyticsId]) + + return ( + <> + {appVersion && analyticsScriptPath && analyticsId && ( +