Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | 3x 3x 3x 3x 3x 3x 3x 3x 87x 20x 20x 20x 20x 20x 20x 3x 20x 20x 20x 20x 3x 39x 3x 15x 3x 84x 84x 3x 200x 3x 17x 17x 17x 17x 3x 20x 20x 2x 2x 2x 2x 20x 20x 20x 20x 3x 3x 17x 17x 17x 17x 15x 15x 15x 15x 15x | import analytics from './segmentWrapper' import {getConfig, setConfig} from './config' /** * Cookie to extract the already saved consents for the user * @type {string} */ const TCF_COOKIE_KEY = 'borosTcf' /** * TCF Api Version to use * @type {number} */ const TCF_API_VERSION = 2 /** * Default properties to send with every TCF tracking event */ const TCF_TRACK_PROPERTIES = { channel: 'GDPR' } /** * List of purpose ids needed to be able to track with all info * @type {Array<number>} */ const NEEDED_PURPOSES = [1, 8, 10] /** * TCF events */ const TCF_EVENTS = { // Event that determines that the tcData has been loaded LOADED: 'tcloaded', // Event that determines that the user has performed an action USER_ACTION_COMPLETE: 'useractioncomplete' } /** * State of user according to GDPR regarding tracking */ export const USER_GDPR = { ACCEPTED: 'accepted', DECLINED: 'declined', UNKNOWN: 'unknown' } /** * Events info according * @type {{ [event: string]: [eventId: string, gdprValue: string]}} */ const EVENT_INFO = { [USER_GDPR.ACCEPTED]: ['Privacy Accepted', USER_GDPR.ACCEPTED], [USER_GDPR.DECLINED]: ['Privacy Declined', USER_GDPR.DECLINED] } /** * Define the user GDPR consents state. This value will be updated with new values * when the consents of the users changes. */ const gdprState = { listeners: [], value: undefined, addListener: callback => gdprState.listeners.push(callback), get: () => gdprState.value, set: value => { gdprState.value = value gdprState.listeners.forEach(fn => fn(value)) gdprState.listeners = [] } } /** * Read cookie by using a key * @returns {string} */ function readCookie(key) { const re = new RegExp(key + '=([^;]+)') const value = re.exec(document.cookie) return value !== null ? unescape(value[1]) : null } /** * Check if we're on client and tcfApi is available on window object * @returns {Boolean} */ const checkTcfIsAvailable = () => { // if we're on the client, check we haven't already initialized it Iif (getConfig('initialized')) return false // if we're on client, check if we have the tcfapi available const isTcfApiAvailable = !!window.__tcfapi !isTcfApiAvailable && console.warn( "[tcfTracking] window.__tcfapi is not available on client and TCF won't be tracked." ) return isTcfApiAvailable } /** * Check from a list of consents if user has accepted being tracked * @param {{[purposeId: string]: boolean}} userConsents * @returns {Boolean} */ const checkHasUserConsentedTracking = userConsents => NEEDED_PURPOSES.every(purposeId => userConsents[`${purposeId}`]) /** * Track a specific TCF event * @param {object} params * @param {string} params.eventId Event ID to be sent with the TCF Tracking * @param {string} params.gdprPrivacy Send a string telling if the gdpr has been accepted or reject * @return {Promise} */ const trackTcf = ({eventId, gdprPrivacy}) => analytics.track( 'Event Fired', { ...TCF_TRACK_PROPERTIES, event_id: eventId, ...getConfig('tcfTrackDefaultProperties') }, { gdpr_privacy: gdprPrivacy } ) /** * Get if we have user consents * @return {Promise<string>} */ export const getGdprPrivacyValue = () => { // try to get the actual gdprPrivacyValue and just return it const gdprPrivacyValue = gdprState.get() Eif (gdprPrivacyValue !== undefined) return Promise.resolve(gdprPrivacyValue) // // if we don't have a gdprPrivacyValue, then subscribe to it until we have a value return new Promise(resolve => { gdprState.addListener(gdprPrivacyValue => resolve(gdprPrivacyValue)) }) } /** * Check if gdprPrivacyValue is accepted * @return {boolean} */ export const checkGdprIsAccepted = gdprPrivacyValue => gdprPrivacyValue === USER_GDPR.ACCEPTED /** * Set gdprState according to list of purpose consents * @returns {string} */ const setGdprStateBy = purposeConsents => { const hasConsents = checkHasUserConsentedTracking(purposeConsents) // get the state key according to the gdprPrivacyValue const gdprStateKey = hasConsents ? USER_GDPR.ACCEPTED : USER_GDPR.DECLINED // update gdprState with the new value for user gdpr gdprState.set(gdprStateKey) // return the new gdprState return gdprStateKey } /** * Read and decode the cookie and set the correct initial gdprState */ const initializeGdprState = () => { const cookieValue = readCookie(TCF_COOKIE_KEY) if (!cookieValue) return try { const {purpose} = JSON.parse(window.atob(cookieValue)) const {consents} = purpose setGdprStateBy(consents) } catch (e) { console.error(e) } } /** * Init TCF Tracking User Consents with Segment */ export default function initTcfTracking() { // first check if we're on server, as this doesn't work on server-side const isClient = typeof window !== 'undefined' Iif (!isClient) return // read the cookie and put the correct usergdprValue before listening events initializeGdprState() // do some checks before initializing tcf tracking as we do that only if available if (!checkTcfIsAvailable()) { // if we don't have a gdpr state and tcf is not available // we should assume we don't known if we have consents Eif (gdprState.get() === undefined) gdprState.set(USER_GDPR.UNKNOWN) // and we stop executing as we can't track tcf return } // add a flag to the segmentWrapper config to know it's already initialized setConfig('initialized', true) // listen events from tcf api window.__tcfapi( 'addEventListener', TCF_API_VERSION, ({eventStatus, purpose}, success) => { Iif (!success) return Promise.resolve() // if we've already user consents or the user is accepting or declining now // we change the state of the GDPR to use in our tracking if ( eventStatus === TCF_EVENTS.USER_ACTION_COMPLETE || eventStatus === TCF_EVENTS.LOADED ) { const {consents} = purpose const gdprStateKey = setGdprStateBy(consents) // if it's a user action, then we will track it Eif (eventStatus === TCF_EVENTS.USER_ACTION_COMPLETE) { // extract the eventId and gdprPrivacy string to send with the track const [eventId, gdprPrivacy] = EVENT_INFO[gdprStateKey] return trackTcf({eventId, gdprPrivacy}) } } } ) } |