@@ -4,32 +4,42 @@ import type { AnalyticsBrowser, Plugin } from '@segment/analytics-next';
44import type Plausible from 'plausible-tracker' ;
55
66import {
7- DEFAULT_CONSENT ,
87 DEFAULT_PLAUSIBLE_API_HOST ,
98 DEFERRED_USER_LOCAL_STORAGE_KEY ,
109 NULL_USER ,
1110} from './constants' ;
11+ import { checkIsConsentEqual } from './lib/compareConsent' ;
1212import { getTrackingPermissions } from './lib/getTrackingPermissions' ;
1313import { logToConsole , normalizePrezlyMetaPlugin , sendEventToPrezlyPlugin } from './plugins' ;
1414import type { Config , Consent , Identity , PrezlyMeta } from './types' ;
1515
1616export class Analytics {
17+ /* eslint-disable @typescript-eslint/naming-convention */
1718 private _identity : Identity | undefined ;
1819
19- private meta : PrezlyMeta | undefined ;
20+ private _meta : PrezlyMeta | undefined ;
21+ /* eslint-enable @typescript-eslint/naming-convention */
2022
21- public consent : Consent = DEFAULT_CONSENT ;
23+ public consent : Consent | undefined = undefined ;
2224
2325 public segment : AnalyticsBrowser | undefined ;
2426
2527 public plausible : ReturnType < typeof Plausible > | undefined ;
2628
2729 private config : Config | undefined ;
2830
31+ private setInitialized ! : ( ) => void ;
32+
2933 private promises : {
3034 segmentInit ?: Promise < void > ;
3135 plausibleInit ?: Promise < void > ;
32- } = { } ;
36+ loadGoogleAnalytics ?: Promise < ( analyticsId : string ) => void > ;
37+ init : Promise < void > ;
38+ } = {
39+ init : new Promise ( ( resolve ) => {
40+ this . setInitialized = resolve ;
41+ } ) ,
42+ } ;
3343
3444 get identity ( ) : Identity | undefined {
3545 if ( this . _identity ) {
@@ -50,25 +60,35 @@ export class Analytics {
5060 }
5161
5262 get permissions ( ) {
63+ if ( typeof this . config === 'undefined' || typeof this . consent === 'undefined' ) {
64+ throw new Error ( 'Cannot check permissions before analytics initialization' ) ;
65+ }
66+
5367 return getTrackingPermissions ( {
5468 consent : this . consent ,
55- trackingPolicy : this . config ! . trackingPolicy ,
69+ trackingPolicy : this . config . trackingPolicy ,
5670 } ) ;
5771 }
5872
59- get isInitialized ( ) {
60- return Boolean ( this . config ) ;
61- }
62-
6373 get integrations ( ) {
6474 return {
6575 Prezly : this . permissions . canTrackToPrezly ,
6676 'Segment.io' : this . permissions . canTrackToSegment ,
6777 } ;
6878 }
6979
70- public async init ( config : Config ) {
71- if ( this . isInitialized ) {
80+ private checkInitialized ( ) {
81+ const isConfigSet = typeof this . config !== 'undefined' ;
82+ const isConsentSet = typeof this . consent !== 'undefined' ;
83+
84+ if ( isConfigSet && isConsentSet ) {
85+ this . setInitialized ( ) ;
86+ }
87+ }
88+
89+ public init = async ( config : Config ) => {
90+ if ( this . config ) {
91+ // Cannot re-initialize analytics
7292 return ;
7393 }
7494
@@ -92,20 +112,21 @@ export class Analytics {
92112 } ) ;
93113
94114 if ( config . google ) {
95- const { analyticsId } = config . google ;
96- import ( './lib/loadGoogleAnalytics' ) . then ( ( { loadGoogleAnalytics } ) => {
97- loadGoogleAnalytics ( analyticsId ) ;
98- } ) ;
115+ this . promises . loadGoogleAnalytics = import ( './lib/loadGoogleAnalytics' ) . then (
116+ ( { loadGoogleAnalytics } ) => loadGoogleAnalytics ,
117+ ) ;
99118 }
100119
101120 if ( config . consent ) {
102121 this . setConsent ( config . consent ) ;
103122 }
104123
105124 if ( config . meta ) {
106- this . meta = config . meta ;
125+ this . setMeta ( config . meta ) ;
107126 }
108- }
127+
128+ this . checkInitialized ( ) ;
129+ } ;
109130
110131 private async loadSegment ( ) {
111132 if ( this . config ?. segment === false ) {
@@ -138,13 +159,25 @@ export class Analytics {
138159 ) ;
139160 }
140161
141- public setMeta ( meta : PrezlyMeta ) {
142- this . meta = meta ;
162+ public setMeta = ( meta : PrezlyMeta ) => {
163+ this . _meta = meta ;
164+ } ;
165+
166+ private get meta ( ) {
167+ if ( ! this . _meta ) {
168+ console . warn ( 'Tracking without Prezly meta being set' ) ;
169+ }
170+
171+ return this . _meta ;
143172 }
144173
145- public setConsent ( consent : Consent ) {
146- if ( ! this . isInitialized ) {
147- throw new Error ( 'Analytics uninitialized' ) ;
174+ public setConsent = ( consent : Consent ) => {
175+ if ( ! this . config ) {
176+ throw new Error ( 'Cannot set consent before analytics initialization' ) ;
177+ }
178+
179+ if ( checkIsConsentEqual ( consent , this . consent ) ) {
180+ return ;
148181 }
149182
150183 this . consent = consent ;
@@ -154,33 +187,50 @@ export class Analytics {
154187 window [ `ga-disable-${ analyticsId } ` ] = this . permissions . canTrackToGoogle ;
155188 }
156189
190+ this . promises . loadGoogleAnalytics ?. then ( ( loadGoogleAnalytics ) => {
191+ if ( ! this . permissions . canTrackToGoogle ) {
192+ return ;
193+ }
194+
195+ const { analyticsId } = this . config ! . google as Exclude <
196+ Config [ 'google' ] ,
197+ false | undefined
198+ > ;
199+
200+ loadGoogleAnalytics ( analyticsId ) ;
201+ } ) ;
202+
157203 this . promises . segmentInit ?. then ( ( ) => {
158204 if ( ! this . segment ?. instance && this . permissions . canLoadSegment ) {
159205 this . loadSegment ( ) ;
160206 }
161207
162- if ( this . identity && this . permissions . canIdentify ) {
163- const { identity } = this ;
208+ const { identity } = this ;
209+ if ( identity && this . permissions . canIdentify ) {
164210 this . segment ?. identify (
165211 identity . userId ,
166212 { ...identity . traits , prezly : this . meta } ,
167213 { integrations : this . integrations } ,
168214 ) ;
169215 }
170216 } ) ;
171- }
172217
173- public async alias ( userId : string , previousId : string ) {
218+ this . checkInitialized ( ) ;
219+ } ;
220+
221+ public alias = async ( userId : string , previousId : string ) => {
222+ await this . promises . init ;
174223 await this . promises . segmentInit ;
175224 await this . segment ?. alias ( userId , previousId , { integrations : this . integrations } ) ;
176- }
225+ } ;
177226
178- public async page (
227+ public page = async (
179228 category ?: string ,
180229 name ?: string ,
181230 properties : object = { } ,
182231 callback ?: ( ) => void ,
183- ) {
232+ ) => {
233+ await this . promises . init ;
184234 await this . promises . segmentInit ;
185235 await this . segment ?. page (
186236 category ,
@@ -189,11 +239,12 @@ export class Analytics {
189239 { integrations : this . integrations } ,
190240 callback ,
191241 ) ;
192- }
242+ } ;
193243
194- public async track ( event : string , properties : object = { } , callback ?: ( ) => void ) {
244+ public track = async ( event : string , properties : object = { } , callback ?: ( ) => void ) => {
195245 const props = this . meta ? { ...properties , prezly : this . meta } : properties ;
196246
247+ await this . promises . init ;
197248 await Promise . all ( [
198249 this . promises . plausibleInit ?. then ( ( ) => {
199250 this . plausible ?. trackEvent ( event , {
@@ -205,11 +256,12 @@ export class Analytics {
205256 this . segment ?. track ( event , props , { integrations : this . integrations } , callback ) ,
206257 ) ,
207258 ] ) ;
208- }
259+ } ;
209260
210- public async identify ( userId : string , traits : object = { } , callback ?: ( ) => void ) {
261+ public identify = async ( userId : string , traits : object = { } , callback ?: ( ) => void ) => {
211262 this . identity = { userId, traits } ;
212263
264+ await this . promises . init ;
213265 await this . promises . segmentInit ;
214266
215267 if ( this . permissions . canIdentify ) {
@@ -220,7 +272,7 @@ export class Analytics {
220272 callback ,
221273 ) ;
222274 }
223- }
275+ } ;
224276
225277 public user ( ) {
226278 return this . segment ?. instance ?. user ( ) ?? NULL_USER ;
0 commit comments