Skip to content

Commit

Permalink
Fix: update metrics collection banner to modal with management toggle…
Browse files Browse the repository at this point in the history
… settings (#373)

* fix: 340 - updates to add modal and toggles for telemetry consent

* fix: 340 updated metrics consent text

* fix - 340 updated linter issues

* feat - 340 toggle modal redesign, updates to modal design per pr

* fix: 340 - updated metrics countly to minimal for default

* fix: 340 - updated styles file for linting error

Co-authored-by: Dan McQuillan <daniel.mcquillan@protocol.ai>
  • Loading branch information
0xDanomite and Dan McQuillan authored Jan 18, 2023
1 parent a81d48b commit d925b36
Show file tree
Hide file tree
Showing 4 changed files with 337 additions and 150 deletions.
1 change: 1 addition & 0 deletions src/countly.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ declare module 'countly-sdk-web' {
interface CountlyWebSdk {
group_features: (arg0: Record<string, string[]>) => unknown
add_consent: (consentFeature: string | string[]) => void
remove_consent: (consentFeature: string | string[]) => void
require_consent: boolean
init: (configOptions?: Partial<CountlyWebSdk>) => void
/**
Expand Down
77 changes: 50 additions & 27 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,41 +60,64 @@ <h1 class='f3 fw2 montserrat aqua ttu ma0'>Public Gateways</h1>
<div class="Took">ΔT</div>
</div>
</div>
<div class="hidden js-metrics-notification-decline-warning metrics-notification-wrapper bg-navy bt bw1 border-aqua">
<div class="metrics-notification-spacer"></div>
<div class="metrics-notification-container">
<span id="metrics-notification-decline-warning" class="metrics-notification-text">
We will limit collection of metrics to only necessary features, 'sessions' and 'views'. See <a href="https://support.count.ly/hc/en-us/articles/360037441932-Web-analytics-JavaScript-#features-for-consent">Countly's group_features</a> for more information.

<div class="hidden js-metrics-notification-modal metrics-notification-wrapper bg-navy bt bw1 border-jade">
<div class="metrics-modal-close js-modal-close"></div>
<div class="metrics-notification-content js-metrics-agreement">
<h3 class="js-metrics-notification-heading metrics-notification-heading montserrat">
Metrics and Telemetry Notice
</h3>
<!-- metric agreement -->
<span class="metrics-notification-text montserrat">
This site collects metrics and other telemetry data in accordance with our <a href="https://github.com/ipfs-shipyard/ignite-metrics/blob/main/docs/telemetry/COLLECTION_POLICY.md">metrics collection policy</a>. You can learn more about
how we use this information and customize your preferences by clicking “manage settings” below.
</span>
<div class="metrics-notification-buttons">
<button id="metrics-notification-warning-close" class="js-metrics-notification-warning-close">✕&nbsp;Close</button>
<button id="metrics-notification-manage" class="js-metrics-notification-manage">Manage settings</button>
<button id="metrics-notification-confirm" class="primary js-metrics-notification-confirm">Got it!</button>
</div>
</div>
</div>
<div class="hidden js-metrics-notification metrics-notification-wrapper bg-navy bt bw1 border-aqua">
<div class="metrics-notification-spacer"></div>
<div class="metrics-notification-container">
<span class="metrics-notification-text">We're collecting <a href="https://github.com/ipfs/ipfs-gui/issues/125">web-vitals, pageview, and other metrics</a> in order to improve and prioritize our work on IPFS and its public gateways.
Please consent to the collection of these metrics to assist in our efforts!</span>
<div class="hidden metrics-notification-content metrics-notification-content-preferences js-metrics-preferences">
<h3 class="js-metrics-notification-heading metrics-notification-heading montserrat">
Metrics and Telemetry Settings
</h3>
<!-- manage preferences -->
<span class="metrics-notification-text montserrat">
This site collects metrics and other telemetry data in order to improve and prioritize work on IPFS. It does not collect any personally identifiable information.
</span>
<div class="metrics-notification-customize">
<span class="metrics-notification-text metrics-notification-customize-text montserrat">
Opt-in or out at any time by using the toggle below.
For more details, read the full <a href="https://github.com/ipfs-shipyard/ignite-metrics/blob/main/docs/telemetry/COLLECTION_POLICY.md">metrics collection policy</a>.
</span>
<ul class="metrics-notification-preference-list">
<li class="metrics-notification-preference-list-item">
<span class="preference-list-item-text">Minimal Telemetry</span>
<label class="preference-list-item-switch">
<input class="js-necessary-toggle" type="checkbox">
<span class="toggle-slider"></span>
</label>
</li>
</ul>
</div>
<div class="metrics-notification-buttons">
<button id="metrics-notification-accept" class="js-metrics-notification-accept">Allow</button>
<button id="metrics-notification-decline" class="js-metrics-notification-decline">Decline</button>
<button id="metrics-notification-preferences-save" class="primary js-metrics-notification-preferences-save">
Save and Exit
</button>
</div>
</div>
</div>
<button class="cookieConsentToggle js-cookie-banner-toggle" aria-label="Edit cookie settings">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M510.52 255.82c-69.97-.85-126.47-57.69-126.47-127.86-70.17
0-127-56.49-127.86-126.45-27.26-4.14-55.13.3-79.72 12.82l-69.13
35.22a132.221 132.221 0 0 0-57.79 57.81l-35.1 68.88a132.645 132.645 0 0
0-12.82 80.95l12.08 76.27a132.521 132.521 0 0 0 37.16 72.96l54.77
54.76a132.036 132.036 0 0 0 72.71 37.06l76.71 12.15c27.51 4.36 55.7-.11
80.53-12.76l69.13-35.21a132.273 132.273 0 0 0
57.79-57.81l35.1-68.88c12.56-24.64 17.01-52.58 12.91-79.91zM176
368c-17.67 0-32-14.33-32-32s14.33-32 32-32 32 14.33 32 32-14.33 32-32
32zm32-160c-17.67 0-32-14.33-32-32s14.33-32 32-32 32 14.33 32 32-14.33
32-32 32zm160 128c-17.67 0-32-14.33-32-32s14.33-32 32-32 32 14.33 32
32-14.33 32-32 32z" />

<button class="metricsConsentToggle js-metrics-modal-toggle" aria-label="Edit metrics settings">
<svg width="43" height="43" viewBox="0 0 43 43" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="21.2333" cy="21.4634" r="20.3114" fill="white" stroke="white" />
<rect x="8.35547" y="17.8948" width="6.0781" height="14.5421" fill="#C4C7D6" />
<rect x="18.4336" y="21.7493" width="6.0781" height="10.6877" fill="#C4C7D6" />
<rect x="28.5117" y="15.4712" width="6.0781" height="16.9658" fill="#C4C7D6" />
<circle cx="10.5458" cy="14.0489" r="1.21184" fill="#C4C7D6" />
<circle cx="21.4716" cy="18.2176" r="1.21184" fill="#C4C7D6" />
<circle cx="31.9208" cy="10.5678" r="1.21184" fill="#C4C7D6" />
<path d="M10.5352 14.0284L21.5802 18.3007L32.104 10.6941" stroke="#C4C7D6" />
</svg>
</button>
</div>
Expand Down
131 changes: 72 additions & 59 deletions src/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,82 @@ import Countly from 'countly-sdk-web'

const metricsConsent = localStorage.getItem('metrics_consent')

const banner = document.querySelector('.js-metrics-notification')
const declineWarning = document.querySelector('.js-metrics-notification-decline-warning')
const acceptButton = document.querySelector('.js-metrics-notification-accept')
const declineButton = document.querySelector('.js-metrics-notification-decline')
const declineWarningClose = document.querySelector('.js-metrics-notification-warning-close')
const bannerToggle = document.querySelector('.js-cookie-banner-toggle')
const metricsNotificationModal = document.querySelector('.js-metrics-notification-modal')
const metricsAgreementContent = document.querySelector('.js-metrics-agreement')
const metricsManagePreferencesContent = document.querySelector('.js-metrics-preferences')

const closeNotificationModalX = document.querySelector('.js-modal-close')
const confirmMetricNoticeBtn = document.querySelector('.js-metrics-notification-confirm')

const saveMetricPreferencesBtn = document.querySelector('.js-metrics-notification-preferences-save')

const managePreferencesBtn = document.querySelector('.js-metrics-notification-manage')
const necessaryMetricsToggle = document.querySelector('.js-necessary-toggle') as HTMLInputElement
const metricsModalToggle = document.querySelector('.js-metrics-modal-toggle')

function addConsent (consent: string[]): void {
hideConsentBanner()
Countly.add_consent(consent)

if (Array.isArray(consent)) {
localStorage.setItem('metrics_consent', JSON.stringify(consent))
} else {
localStorage.setItem('metrics_consent', JSON.stringify([consent]))
localStorage.setItem(
'metrics_consent',
JSON.stringify([consent])
)
}
}

function addConsentEventHandler (): void {
acceptButton?.removeEventListener('click', addConsentEventHandler)
metricsNotificationModal?.classList.add('hidden')

addConsent(['all'])
addConsent(['minimal'])
}

function declineConsentEventHandler (): void {
addConsent(['necessary'])
hideConsentBanner()
displayDeclineWarning()
}
function updateNecessaryMetricPreferences (): void {
const necessaryMetricsAccepted = necessaryMetricsToggle.checked

function displayDeclineWarning (): void {
declineWarning?.classList.remove('hidden')
bannerToggle?.setAttribute('disabled', '')
if (necessaryMetricsAccepted) {
addConsent(['minimal'])
} else {
Countly.remove_consent(['minimal'])
localStorage.setItem('metrics_consent', JSON.stringify([]))
}
}

function declineWarningCloseEventHandler (): void {
declineWarningClose?.removeEventListener('click', declineWarningCloseEventHandler)
declineWarning?.classList.add('hidden')
bannerToggle?.removeAttribute('disabled')
function initMetricsModal (): void {
metricsNotificationModal?.classList.remove('hidden')
confirmMetricNoticeBtn?.classList.remove('hidden')
managePreferencesBtn?.classList.remove('hidden')
metricsAgreementContent?.classList.remove('hidden')
closeNotificationModalX?.addEventListener('click', hideMetricsModal)
confirmMetricNoticeBtn?.addEventListener('click', addConsentEventHandler)
managePreferencesBtn?.addEventListener('click', managePreferencesClicked)
}

function hideConsentBanner (): void {
acceptButton?.removeEventListener('click', addConsentEventHandler)
declineButton?.removeEventListener('click', declineConsentEventHandler)
banner?.classList.add('hidden')
bannerToggle?.removeAttribute('disabled')
function hideMetricsModal (): void {
metricsNotificationModal?.classList.add('hidden')
metricsManagePreferencesContent?.classList.add('hidden')
metricsAgreementContent?.classList.remove('hidden')
}

/**
* Display the consent banner and handle the user's choice
*/
function displayConsentBanner (): void {
acceptButton?.addEventListener('click', addConsentEventHandler)
declineButton?.addEventListener('click', declineConsentEventHandler)
declineWarningClose?.addEventListener('click', declineWarningCloseEventHandler)
banner?.classList.remove('hidden')
bannerToggle?.setAttribute('disabled', '')
declineWarning?.classList.add('hidden')
function managePreferencesClicked (): void {
const metricsConsent = localStorage.getItem('metrics_consent')
if (metricsConsent != null) necessaryMetricsToggle.checked = JSON.parse(metricsConsent)[0] === 'minimal'
metricsAgreementContent?.classList.add('hidden')
saveMetricPreferencesBtn?.classList.remove('hidden')
metricsManagePreferencesContent?.classList.remove('hidden')

necessaryMetricsToggle.addEventListener('click', updateNecessaryMetricPreferences)
saveMetricPreferencesBtn?.addEventListener('click', hideMetricsModal)
}

function bannerToggleEventHandler (): void {
displayConsentBanner()
function metricsModalToggleEventHandler (): void {
initMetricsModal()
}

function loadCountly (): void {
bannerToggle?.addEventListener('click', bannerToggleEventHandler)
metricsModalToggle?.addEventListener('click', metricsModalToggleEventHandler)
Countly.init({
app_key: '3c2c0819434074fc4d339ddd8e112a1e741ecb72',
url: 'https://countly.ipfs.io',
Expand All @@ -75,17 +86,26 @@ function loadCountly (): void {
/**
* @see https://support.count.ly/hc/en-us/articles/360037441932-Web-analytics-JavaScript-#features-for-consent
*/
const necessaryFeatures = ['sessions', 'views']
const marketingFeatures = ['attribution', 'users', 'location']
const performanceFeatures = ['events', 'crashes', 'apm']
const trackingFeatures = ['scrolls', 'clicks', 'forms', 'star-rating', 'feedback']

const minimalFeatures = ['sessions', 'views', 'events']
const performanceFeatures = ['crashes', 'apm']
const uxFeatures = ['scrolls', 'clicks', 'forms']
const feedbackFeatures = ['star-rating', 'feedback']
const locationFeatures = ['location']

Countly.group_features({
all: [...necessaryFeatures, ...marketingFeatures, ...performanceFeatures, ...trackingFeatures],
necessary: necessaryFeatures,
marketing: marketingFeatures,
tracking: trackingFeatures,
performance: performanceFeatures
all: [
...minimalFeatures,
...performanceFeatures,
...uxFeatures,
...feedbackFeatures,
...locationFeatures
],
minimal: minimalFeatures,
performance: performanceFeatures,
ux: uxFeatures,
feedback: feedbackFeatures,
location: locationFeatures
})

/**
Expand All @@ -102,17 +122,10 @@ function loadCountly (): void {
Countly.track_view()

if (metricsConsent != null) {
try {
addConsent(JSON.parse(metricsConsent))
} catch {
displayConsentBanner()
}
addConsent(JSON.parse(metricsConsent))
} else {
displayConsentBanner()
addConsent(['minimal'])
}
}

export {
loadCountly,
Countly
}
export { loadCountly, Countly }
Loading

0 comments on commit d925b36

Please sign in to comment.