Skip to content

Commit

Permalink
Merge pull request janhq#1958 from janhq/feat-umami-integration
Browse files Browse the repository at this point in the history
feat: integrate umami script locally
  • Loading branch information
0xHieu01 authored Feb 7, 2024
2 parents 68d7d19 + b864a28 commit a8c1d70
Show file tree
Hide file tree
Showing 2 changed files with 270 additions and 24 deletions.
210 changes: 210 additions & 0 deletions web/public/umami_script.js
Original file line number Diff line number Diff line change
@@ -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)
})()
84 changes: 60 additions & 24 deletions web/utils/umami.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,67 @@
import { useEffect } from 'react'

import Script from 'next/script'

// 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 (!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',
}),
})
}, [])

return <></>
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 && (
<Script
src={analyticsScriptPath}
data-website-id={analyticsId}
data-cache="true"
data-host-url="https://eu.umami.is"
defer
onLoad={() => document.dispatchEvent(new Event('umami:loaded'))}
/>
)}
</>
)
}

export default Umami

0 comments on commit a8c1d70

Please sign in to comment.