Merge pull request #178 from mikecao/dev

v0.39.0 - Tracker updates
This commit is contained in:
Mike Cao 2020-09-18 14:59:53 -07:00 committed by GitHub
commit 238251a9b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 99 additions and 52 deletions

View File

@ -2,7 +2,7 @@ import React, { useRef } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import Button from 'components/common/Button'; import Button from 'components/common/Button';
import FormLayout, { FormButtons, FormRow } from 'components/layout/FormLayout'; import FormLayout, { FormButtons, FormRow } from 'components/layout/FormLayout';
import CopyButton from '../common/CopyButton'; import CopyButton from 'components/common/CopyButton';
export default function TrackingCodeForm({ values, onClose }) { export default function TrackingCodeForm({ values, onClose }) {
const ref = useRef(); const ref = useRef();

View File

@ -23,6 +23,7 @@ export const useSession = use(async (req, res, next) => {
try { try {
session = await getSession(req); session = await getSession(req);
} catch (e) { } catch (e) {
console.error(e);
return serverError(res, e.message); return serverError(res, e.message);
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "umami", "name": "umami",
"version": "0.38.0", "version": "0.39.0",
"description": "A simple, fast, website analytics alternative to Google Analytics. ", "description": "A simple, fast, website analytics alternative to Google Analytics. ",
"author": "Mike Cao <mike@mikecao.com>", "author": "Mike Cao <mike@mikecao.com>",
"license": "MIT", "license": "MIT",

View File

@ -11,6 +11,12 @@ export default function Test() {
return <h1>No id query specified.</h1>; return <h1>No id query specified.</h1>;
} }
function handleClick() {
window.umami('Custom event');
window.umami.pageView('/fake', 'https://www.google.com');
window.umami.pageEvent('pageEvent', 'custom-type');
}
return ( return (
<> <>
<Head> <Head>
@ -20,7 +26,7 @@ export default function Test() {
</Head> </Head>
<Layout> <Layout>
<p> <p>
Here you can test if your umami installation works. Open the network tab in your browser's Here you can test if your umami installation works. Open the network tab in your browser
developer console and watch for requests to the url <b>collect</b>. The links below should developer console and watch for requests to the url <b>collect</b>. The links below should
trigger page views. Clicking on the button should trigger an event. trigger page views. Clicking on the button should trigger an event.
</p> </p>
@ -40,6 +46,10 @@ export default function Test() {
> >
Button Button
</button> </button>
<h2>Manual trigger</h2>
<button id="manual-button" type="button" onClick={handleClick}>
Button
</button>
</Layout> </Layout>
</> </>
); );

View File

@ -1,6 +1,5 @@
import 'dotenv/config'; import 'dotenv/config';
import buble from '@rollup/plugin-buble'; import buble from '@rollup/plugin-buble';
import replace from '@rollup/plugin-replace';
import resolve from '@rollup/plugin-node-resolve'; import resolve from '@rollup/plugin-node-resolve';
import { terser } from 'rollup-plugin-terser'; import { terser } from 'rollup-plugin-terser';
@ -10,10 +9,5 @@ export default {
file: 'public/umami.js', file: 'public/umami.js',
format: 'iife', format: 'iife',
}, },
plugins: [ plugins: [resolve(), buble(), terser({ compress: { evaluate: false } })],
replace({ __DNT__: !!process.env.ENABLE_DNT }),
resolve(),
buble(),
terser({ compress: { evaluate: false } }),
],
}; };

View File

@ -1,6 +1,4 @@
import 'promise-polyfill/src/polyfill'; import { doNotTrack, hook } from '../lib/web';
import 'unfetch/polyfill';
import { post, hook, doNotTrack } from '../lib/web';
import { removeTrailingSlash } from '../lib/url'; import { removeTrailingSlash } from '../lib/url';
(window => { (window => {
@ -13,28 +11,42 @@ import { removeTrailingSlash } from '../lib/url';
} = window; } = window;
const script = document.querySelector('script[data-website-id]'); const script = document.querySelector('script[data-website-id]');
const attr = key => script && script.getAttribute(key);
// eslint-disable-next-line no-undef const website = attr('data-website-id');
if (!script || (__DNT__ && doNotTrack())) return; const hostUrl = attr('data-host-url');
const autoTrack = attr('data-auto-track') !== 'false';
const dnt = attr('data-do-not-track') === 'true';
if (!script || (dnt && doNotTrack())) return;
const website = script.getAttribute('data-website-id');
const hostUrl = script.getAttribute('data-host-url');
const root = hostUrl const root = hostUrl
? removeTrailingSlash(hostUrl) ? removeTrailingSlash(hostUrl)
: new URL(script.src).href.split('/').slice(0, -1).join('/'); : new URL(script.src).href.split('/').slice(0, -1).join('/');
const screen = `${width}x${height}`; const screen = `${width}x${height}`;
const listeners = []; const listeners = [];
let currentUrl = `${pathname}${search}`; let currentUrl = `${pathname}${search}`;
let currentRef = document.referrer; let currentRef = document.referrer;
/* Collect metrics */ /* Collect metrics */
const collect = (type, params) => { const post = (url, data, callback) => {
const req = new XMLHttpRequest();
req.open('POST', url, true);
req.setRequestHeader('Content-Type', 'application/json');
req.onreadystatechange = () => {
if (req.readyState === 4) {
callback && callback();
}
};
req.send(JSON.stringify(data));
};
const collect = (type, params, uuid) => {
const payload = { const payload = {
url: currentUrl, website: uuid,
referrer: currentRef,
website,
hostname, hostname,
screen, screen,
language, language,
@ -52,14 +64,55 @@ import { removeTrailingSlash } from '../lib/url';
}); });
}; };
const pageView = () => collect('pageview').then(() => setTimeout(loadEvents, 300)); const pageView = (url = currentUrl, referrer = currentRef, uuid = website) =>
collect(
'pageview',
{
url,
referrer,
},
uuid,
);
const pageEvent = (event_type, event_value) => collect('event', { event_type, event_value }); const pageEvent = (event_value, event_type = 'custom', url = currentUrl, uuid = website) =>
collect(
'event',
{
event_type,
event_value,
url,
},
uuid,
);
/* Handle history */ /* Handle events */
const loadEvents = () => {
document.querySelectorAll("[class*='umami--']").forEach(element => {
element.className.split(' ').forEach(className => {
if (/^umami--([a-z]+)--([a-z0-9_]+[a-z0-9-_]+)$/.test(className)) {
const [, type, value] = className.split('--');
const listener = () => pageEvent(value, type);
listeners.push([element, type, listener]);
element.addEventListener(type, listener, true);
}
});
});
};
const removeEvents = () => {
listeners.forEach(([element, type, listener]) => {
element && element.removeEventListener(type, listener, true);
});
listeners.length = 0;
};
/* Handle history changes */
const handlePush = (state, title, url) => { const handlePush = (state, title, url) => {
removeEvents(); removeEvents();
currentRef = currentUrl; currentRef = currentUrl;
const newUrl = url.toString(); const newUrl = url.toString();
@ -70,40 +123,29 @@ import { removeTrailingSlash } from '../lib/url';
currentUrl = newUrl; currentUrl = newUrl;
} }
pageView(); pageView(currentUrl, currentRef);
setTimeout(loadEvents, 300);
}; };
history.pushState = hook(history, 'pushState', handlePush); /* Global */
history.replaceState = hook(history, 'replaceState', handlePush);
/* Handle events */ if (!window.umami) {
const umami = event_value => pageEvent(event_value);
umami.pageView = pageView;
umami.pageEvent = pageEvent;
const removeEvents = () => { window.umami = umami;
listeners.forEach(([element, type, listener]) => { }
element && element.removeEventListener(type, listener, true);
});
listeners.length = 0;
};
const loadEvents = () => {
document.querySelectorAll("[class*='umami--']").forEach(element => {
element.className.split(' ').forEach(className => {
if (/^umami--([a-z]+)--([a-z0-9_]+[a-z0-9-_]+)$/.test(className)) {
const [, type, value] = className.split('--');
const listener = () => pageEvent(type, value);
listeners.push([element, type, listener]);
element.addEventListener(type, listener, true);
}
});
});
};
/* Start */ /* Start */
pageView(); if (autoTrack) {
history.pushState = hook(history, 'pushState', handlePush);
history.replaceState = hook(history, 'replaceState', handlePush);
if (!window.umami) { pageView(currentUrl, currentRef);
window.umami = event_value => collect('event', { event_type: 'custom', event_value });
loadEvents();
} }
})(window); })(window);