sdk: debounce screen views 50ms to avoid duplicated screen views

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-05-05 20:06:39 +02:00
parent 3aecbfe627
commit 2e2ee1422f
3 changed files with 16 additions and 10 deletions

View File

@@ -1,2 +1,2 @@
"use strict";(()=>{function v(s){return Promise.all(Object.entries(s).map(async([e,i])=>[e,await i??""])).then(e=>Object.fromEntries(e))}function m(s){let e={"Content-Type":"application/json"};return{headers:e,async fetch(i,t,n){let r=`${s}${i}`,o,d=await v(e);return new Promise(a=>{let p=c=>{clearTimeout(o),fetch(r,{headers:d,method:"POST",body:JSON.stringify(t??{}),keepalive:!0,...n??{}}).then(async l=>{if(l.status===401)return null;if(l.status!==200&&l.status!==202)return f(c,a);let g=await l.text();if(!g)return a(null);a(g)}).catch(()=>f(c,a))};function f(c,l){if(c>1)return l(null);o=setTimeout(()=>{p(c+1)},Math.pow(2,c)*500)}p(0)})}}}var u=class{constructor(e){this.state={properties:{}};this.options=e,this.api=m(e.url??"https://api.openpanel.dev"),this.api.headers["openpanel-client-id"]=e.clientId,this.options.clientSecret&&(this.api.headers["openpanel-client-secret"]=this.options.clientSecret)}setProfileId(e){this.state.profileId=e}setProfile(e){this.setProfileId(e.profileId),this.api.fetch("/profile",{...e,properties:{...this.state.properties,...e.properties}})}increment(e,i,t){let n=t?.profileId??this.state.profileId;if(!n)return console.log("No profile id");this.api.fetch("/profile/increment",{profileId:n,property:e,value:i})}decrement(e,i,t){let n=t?.profileId??this.state.profileId;if(!n)return console.log("No profile id");this.api.fetch("/profile/decrement",{profileId:n,property:e,value:i})}event(e,i){let t=i?.profileId??this.state.profileId;delete i?.profileId,this.api.fetch("/event",{name:e,properties:{...this.state.properties,...i??{}},timestamp:this.timestamp(),deviceId:this.getDeviceId(),profileId:t}).then(n=>{this.options.setDeviceId&&n&&this.options.setDeviceId(n)})}setGlobalProperties(e){this.state.properties={...this.state.properties,...e}}clear(){this.state.deviceId=void 0,this.state.profileId=void 0,this.options.removeDeviceId&&this.options.removeDeviceId()}timestamp(){return new Date().toISOString()}getDeviceId(){if(this.state.deviceId)return this.state.deviceId;this.options.getDeviceId&&(this.state.deviceId=this.options.getDeviceId()||void 0)}};function b(s){return s.replace(/([-_][a-z])/gi,e=>e.toUpperCase().replace("-","").replace("_",""))}var h=class extends u{constructor(i){super(i);this.lastPath="";this.isServer()||(this.setGlobalProperties({__referrer:document.referrer}),this.options.trackOutgoingLinks&&this.trackOutgoingLinks(),this.options.trackScreenViews&&this.trackScreenViews(),this.options.trackAttributes&&this.trackAttributes())}isServer(){return typeof document>"u"}trackOutgoingLinks(){this.isServer()||document.addEventListener("click",i=>{let t=i.target,n=t.closest("a");if(n&&t){let r=n.getAttribute("href");r?.startsWith("http")&&super.event("link_out",{href:r,text:n.innerText||n.getAttribute("title")||t.getAttribute("alt")||t.getAttribute("title")})}})}trackScreenViews(){if(this.isServer())return;let i=history.pushState;history.pushState=function(...r){let o=i.apply(this,r);return window.dispatchEvent(new Event("pushstate")),window.dispatchEvent(new Event("locationchange")),o};let t=history.replaceState;history.replaceState=function(...r){let o=t.apply(this,r);return window.dispatchEvent(new Event("replacestate")),window.dispatchEvent(new Event("locationchange")),o},window.addEventListener("popstate",()=>window.dispatchEvent(new Event("locationchange"))),this.options.hash?window.addEventListener("hashchange",()=>this.screenView()):window.addEventListener("locationchange",()=>this.screenView()),setTimeout(()=>{this.screenView()},50)}trackAttributes(){this.isServer()||document.addEventListener("click",i=>{let t=i.target,n=t.closest("button"),r=t.closest("a"),o=n?.getAttribute("data-event")?n:r?.getAttribute("data-event")?r:null;if(o){let d={};for(let p of o.attributes)p.name.startsWith("data-")&&p.name!=="data-event"&&(d[b(p.name.replace(/^data-/,""))]=p.value);let a=o.getAttribute("data-event");a&&super.event(a,d)}})}screenView(i){if(this.isServer())return;let t=window.location.href;this.lastPath!==t&&(this.lastPath=t,super.event("screen_view",{...i??{},__path:t,__title:document.title}))}};(s=>{if(s.op&&"q"in s.op){let e=s.op.q||[],i=new h(e.shift()[1]);e.forEach(t=>{t[0]in i&&i[t[0]](...t.slice(1))}),s.op=(t,...n)=>{let r=i[t].bind(i);typeof r=="function"&&r(...n)}}})(window);})();
"use strict";(()=>{function v(r){return Promise.all(Object.entries(r).map(async([e,i])=>[e,await i??""])).then(e=>Object.fromEntries(e))}function m(r){let e={"Content-Type":"application/json"};return{headers:e,async fetch(i,t,n){let s=`${r}${i}`,o,a=await v(e);return new Promise(p=>{let c=l=>{clearTimeout(o),fetch(s,{headers:a,method:"POST",body:JSON.stringify(t??{}),keepalive:!0,...n??{}}).then(async d=>{if(d.status===401)return null;if(d.status!==200&&d.status!==202)return h(l,p);let g=await d.text();if(!g)return p(null);p(g)}).catch(()=>h(l,p))};function h(l,d){if(l>1)return d(null);o=setTimeout(()=>{c(l+1)},Math.pow(2,l)*500)}c(0)})}}}var u=class{constructor(e){this.state={properties:{}};this.options=e,this.api=m(e.url??"https://api.openpanel.dev"),this.api.headers["openpanel-client-id"]=e.clientId,this.options.clientSecret&&(this.api.headers["openpanel-client-secret"]=this.options.clientSecret)}setProfileId(e){this.state.profileId=e}setProfile(e){this.setProfileId(e.profileId),this.api.fetch("/profile",{...e,properties:{...this.state.properties,...e.properties}})}increment(e,i,t){let n=t?.profileId??this.state.profileId;if(!n)return console.log("No profile id");this.api.fetch("/profile/increment",{profileId:n,property:e,value:i})}decrement(e,i,t){let n=t?.profileId??this.state.profileId;if(!n)return console.log("No profile id");this.api.fetch("/profile/decrement",{profileId:n,property:e,value:i})}event(e,i){let t=i?.profileId??this.state.profileId;delete i?.profileId,this.api.fetch("/event",{name:e,properties:{...this.state.properties,...i??{}},timestamp:this.timestamp(),deviceId:this.getDeviceId(),profileId:t}).then(n=>{this.options.setDeviceId&&n&&this.options.setDeviceId(n)})}setGlobalProperties(e){this.state.properties={...this.state.properties,...e}}clear(){this.state.deviceId=void 0,this.state.profileId=void 0,this.options.removeDeviceId&&this.options.removeDeviceId()}timestamp(){return new Date().toISOString()}getDeviceId(){if(this.state.deviceId)return this.state.deviceId;this.options.getDeviceId&&(this.state.deviceId=this.options.getDeviceId()||void 0)}};function b(r){return r.replace(/([-_][a-z])/gi,e=>e.toUpperCase().replace("-","").replace("_",""))}var f=class extends u{constructor(i){super(i);this.lastPath="";this.isServer()||(this.setGlobalProperties({__referrer:document.referrer}),this.options.trackOutgoingLinks&&this.trackOutgoingLinks(),this.options.trackScreenViews&&this.trackScreenViews(),this.options.trackAttributes&&this.trackAttributes())}debounce(i,t){clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(i,t)}isServer(){return typeof document>"u"}trackOutgoingLinks(){this.isServer()||document.addEventListener("click",i=>{let t=i.target,n=t.closest("a");if(n&&t){let s=n.getAttribute("href");s?.startsWith("http")&&super.event("link_out",{href:s,text:n.innerText||n.getAttribute("title")||t.getAttribute("alt")||t.getAttribute("title")})}})}trackScreenViews(){if(this.isServer())return;let i=history.pushState;history.pushState=function(...o){let a=i.apply(this,o);return window.dispatchEvent(new Event("pushstate")),window.dispatchEvent(new Event("locationchange")),a};let t=history.replaceState;history.replaceState=function(...o){let a=t.apply(this,o);return window.dispatchEvent(new Event("replacestate")),window.dispatchEvent(new Event("locationchange")),a},window.addEventListener("popstate",function(){window.dispatchEvent(new Event("locationchange"))});let n=()=>this.debounce(()=>this.screenView(),50);this.options.hash?window.addEventListener("hashchange",n):window.addEventListener("locationchange",n),setTimeout(()=>n(),50)}trackAttributes(){this.isServer()||document.addEventListener("click",i=>{let t=i.target,n=t.closest("button"),s=t.closest("a"),o=n?.getAttribute("data-event")?n:s?.getAttribute("data-event")?s:null;if(o){let a={};for(let c of o.attributes)c.name.startsWith("data-")&&c.name!=="data-event"&&(a[b(c.name.replace(/^data-/,""))]=c.value);let p=o.getAttribute("data-event");p&&super.event(p,a)}})}screenView(i){if(this.isServer())return;let t=window.location.href;this.lastPath!==t&&(this.lastPath=t,super.event("screen_view",{...i??{},__path:t,__title:document.title}))}};(r=>{if(r.op&&"q"in r.op){let e=r.op.q||[],i=new f(e.shift()[1]);e.forEach(t=>{t[0]in i&&i[t[0]](...t.slice(1))}),r.op=(t,...n)=>{let s=i[t].bind(i);typeof s=="function"&&s(...n)}}})(window);})();
//# sourceMappingURL=cdn.global.js.map

View File

@@ -20,6 +20,7 @@ function toCamelCase(str: string) {
export class Openpanel extends OpenpanelSdk<OpenpanelOptions> {
private lastPath = '';
private debounceTimer: number | undefined;
constructor(options: OpenpanelOptions) {
super(options);
@@ -43,6 +44,11 @@ export class Openpanel extends OpenpanelSdk<OpenpanelOptions> {
}
}
private debounce(func: () => void, delay: number) {
clearTimeout(this.debounceTimer);
this.debounceTimer = setTimeout(func, delay);
}
private isServer() {
return typeof document === 'undefined';
}
@@ -92,20 +98,20 @@ export class Openpanel extends OpenpanelSdk<OpenpanelOptions> {
return ret;
};
window.addEventListener('popstate', () =>
window.dispatchEvent(new Event('locationchange'))
);
window.addEventListener('popstate', function () {
window.dispatchEvent(new Event('locationchange'));
});
const eventHandler = () => this.debounce(() => this.screenView(), 50);
if (this.options.hash) {
window.addEventListener('hashchange', () => this.screenView());
window.addEventListener('hashchange', eventHandler);
} else {
window.addEventListener('locationchange', () => this.screenView());
window.addEventListener('locationchange', eventHandler);
}
// give time for setProfile to be called
setTimeout(() => {
this.screenView();
}, 50);
setTimeout(() => eventHandler(), 50);
}
public trackAttributes() {

View File

@@ -4,7 +4,7 @@
"module": "index.ts",
"scripts": {
"build": "rm -rf dist && tsup",
"build-for-openpanel": "pnpm build && cp dist/cdn.global.js ../../../apps/public/public/op.js && cp dist/cdn.global.js ../../../apps/test/public/op.js",
"build-for-openpanel": "pnpm build && cp dist/cdn.global.js ../../../apps/public/public/op.js",
"lint": "eslint .",
"format": "prettier --check \"**/*.{mjs,ts,md,json}\"",
"typecheck": "tsc --noEmit"