diff --git a/web/public/umami_script.js b/web/public/umami_script.js new file mode 100644 index 000000000..b9db0b024 --- /dev/null +++ b/web/public/umami_script.js @@ -0,0 +1,210 @@ +!(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 = 'mainpage', + 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 = { + // eslint-disable-next-line @typescript-eslint/naming-convention + '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) +})() diff --git a/web/utils/umami.tsx b/web/utils/umami.tsx index 277ae1223..dc406a7d2 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 && ( +