Skip to content

Commit 1ddda6b

Browse files
authored
Merge pull request #121 from adjust/DSM-2999-third-party-sharing
[DSM-2999] Enhance marketing Opt-out
2 parents d09f1a5 + ba278b5 commit 1ddda6b

22 files changed

+706
-688
lines changed

src/assets/scss/index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
@import "flex-grid";
44
@import "../../demo/tabs/tabs";
55
@import "../../demo/key-value-params/key-value-params";
6+
@import "../../demo/dynamic-params/dynamic-params";
67

78
html, body {
89
margin: 0;

src/demo/disable-third-party-sharing/disable-third-party-sharing.html

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/demo/disable-third-party-sharing/disable-third-party-sharing.js

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import { capitalize } from '../utils'
2+
3+
const ACTION_SYMBOLS = {
4+
add: '+',
5+
remove: '×'
6+
}
7+
8+
function _createNode(name, classes = [], content, attrs = {}) {
9+
const element = document.createElement(name)
10+
11+
element.classList.add(...classes)
12+
13+
if (content) {
14+
element.appendChild(document.createTextNode(content))
15+
}
16+
17+
Object.keys(attrs)
18+
.map(key => [key, attrs[key]])
19+
.map(([key, value]) => element[key] = value)
20+
21+
return element
22+
}
23+
24+
function _createInput(inputCls, label, value = '') {
25+
const keyParent = _createNode('div', ['flex-box-column'])
26+
keyParent.style.height = '45px'
27+
const keyLabel = _createNode('label', ['flex-auto'], label)
28+
const keyInput = _createNode('input', ['flex-one', inputCls], null, { type: 'text', value })
29+
30+
keyParent.appendChild(keyLabel)
31+
keyParent.appendChild(keyInput)
32+
33+
return keyParent
34+
}
35+
36+
function _append(parent, handle, isLast, fields, param = {}) {
37+
const group = _createNode('div', ['flex-box-row', 'form-row-group'])
38+
39+
const elements = []
40+
fields.forEach(field => {
41+
elements.push(_createInput(`${field}-input`, capitalize(field), param[field]))
42+
})
43+
group.append(...elements)
44+
45+
const actionName = isLast ? 'add' : 'remove'
46+
const action = _createNode('button', ['flex-auto', actionName], ACTION_SYMBOLS[actionName], { type: 'button' })
47+
48+
action.addEventListener('click', handle, false)
49+
group.appendChild(action)
50+
51+
parent.appendChild(group)
52+
}
53+
54+
const DynamicParams = (id, fields = ['key', 'value'], params = [], onChange) => {
55+
const _id = id
56+
const _fields = fields
57+
const _params = params
58+
let _parent = null
59+
60+
function init() {
61+
_parent = document.getElementById(_id)
62+
63+
if (_params.length) {
64+
_params.forEach((param, idx) => {
65+
const isLast = idx === params.length - 1
66+
const handle = isLast ? _handleAdd : _handleRemove
67+
_append(_parent, handle, isLast, _fields, param)
68+
})
69+
} else {
70+
_append(_parent, _handleAdd, true, _fields)
71+
}
72+
}
73+
74+
function query() {
75+
const rows = _parent.querySelectorAll('.form-row-group')
76+
return Array.from(rows)
77+
.map(row => {
78+
const pairs = _fields
79+
.map(field => [field, row.querySelector(`.${field}-input`).value])
80+
81+
if (pairs.some(([, value]) => value === '')) {
82+
return null
83+
}
84+
85+
return pairs.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
86+
})
87+
.filter(row => !!row)
88+
}
89+
90+
function reset() {
91+
Array.from(_parent.querySelectorAll('.form-row-group'))
92+
.map(row => {
93+
const action = row.querySelector('button')
94+
const handle = action.classList.contains('add') ? _handleAdd : _handleRemove
95+
action.removeEventListener('click', handle, false)
96+
row.remove()
97+
})
98+
99+
_append(_parent, _handleAdd, true, _fields)
100+
}
101+
102+
function _handleRemove(e) {
103+
const action = e.target
104+
action.removeEventListener('click', _handleRemove, false)
105+
action.parentNode.remove()
106+
107+
if (typeof onChange === 'function') {
108+
onChange()
109+
}
110+
}
111+
112+
function _handleAdd(e) {
113+
const row = e.target.parentNode
114+
115+
const pairs = _fields
116+
.map(field => [field, row.querySelector(`.${field}-input`).value])
117+
118+
if (pairs.some(([, value]) => value === '')) {
119+
return
120+
}
121+
122+
_append(_parent, _handleAdd, true, _fields)
123+
_swapAction(row)
124+
125+
if (typeof onChange === 'function') {
126+
onChange()
127+
}
128+
}
129+
130+
function _swapAction(parent) {
131+
const removeAction = _createNode('button', ['flex-auto', 'remove'], ACTION_SYMBOLS.remove, { type: 'button' })
132+
const addAction = parent.querySelector('.add')
133+
134+
removeAction.addEventListener('click', _handleRemove, false)
135+
addAction.removeEventListener('click', _handleAdd, false)
136+
137+
addAction.remove()
138+
parent.appendChild(removeAction)
139+
}
140+
141+
return {
142+
init,
143+
query,
144+
reset
145+
}
146+
}
147+
148+
export default DynamicParams
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.dynamic-params {
2+
h3 {
3+
font-weight: normal;
4+
color: $fontColor;
5+
font-size: 15px;
6+
margin-bottom: 10px;
7+
}
8+
9+
.form-row-group {
10+
margin-bottom: 10px;
11+
align-items: end;
12+
justify-content: space-between;
13+
gap: 3px;
14+
}
15+
16+
button {
17+
padding: 5px 10px;
18+
margin-left: 5px;
19+
}
20+
}

src/demo/main.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import switchBackToOnlineModeInit from './switch-back-to-online-mode/switch-back
1212
import stopInit from './stop/stop'
1313
import restartInit from './restart/restart'
1414
import gdprForgetMeInit from './gdpr-forget-me/gdpr-forget-me'
15-
import disableThirdPartySharingInit from './disable-third-party-sharing/disable-third-party-sharing'
15+
import trackThirdPartySharingInit from './track-third-party-sharing/track-third-party-sharing'
1616
import getWebUUID from './get-web-uuid/get-web-uuid'
1717
import getAttribution from './get-attribution/get-attribution'
1818
import setReferrer from './set-referrer/set-referrer'
@@ -32,7 +32,7 @@ function init (defaultAppConfig, defaultEventConfig) {
3232
stopInit()
3333
restartInit()
3434
gdprForgetMeInit()
35-
disableThirdPartySharingInit()
35+
trackThirdPartySharingInit()
3636
getWebUUID()
3737
getAttribution()
3838
setReferrer()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<div class="action-block">
2+
<div class="action-wrap flex-box-row">
3+
<button id="track-tps-button" class="action flex-one">Track Third Party Sharing</button>
4+
<button type="button" id="tps-side-form-toggle" class="form-toggle flex-auto">...</button>
5+
</div>
6+
7+
<div class="side-form fixed" id="track-tps">
8+
<div class="flex-box-row">
9+
<form class="flex-one" id="tps-config-form">
10+
<div class="flex-box-column flex-two">
11+
<div class="flex-box-row" style="margin-bottom: 10px;">
12+
<label for="enable-tps">Enable/disable third party sharing</label>
13+
<input type="checkbox" id="enable-tps" checked />
14+
</div>
15+
16+
<div id="tps-granular" class="form-row flex-box-column dynamic-params">
17+
<h3>Granular options</h3>
18+
</div>
19+
20+
<div id="tps-partner-sharing" class="form-row flex-box-column dynamic-params">
21+
<h3>Partner sharing settings</h3>
22+
</div>
23+
24+
</div>
25+
26+
<button type="submit">Save and Run</button>
27+
</form>
28+
29+
<pre class="flex-one config" id="tps-config-json"></pre>
30+
</div>
31+
</div>
32+
</div>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import Adjust from '../../sdk/main'
2+
import { getItem, setItem } from '../storage'
3+
import DynamicParams from '../dynamic-params/dynamic-params'
4+
5+
const _ui = {}
6+
let _tpsOptions = {}
7+
let _disabled = false
8+
let _timeoutId = null
9+
let _granularOptions = null
10+
let _partnerSharingSettings = null
11+
12+
function init() {
13+
_tpsOptions = getItem('tpsOptions') || { isEnabled: true, granularOptions: [], partnerSharingSettings: [] }
14+
15+
_ui.tpsOptionsForm = document.getElementById('tps-config-form')
16+
_ui.tpsOptionsJson = document.getElementById('tps-config-json')
17+
_ui.trackTPSButton = document.getElementById('track-tps-button')
18+
_ui.toggleButton = document.getElementById('tps-side-form-toggle')
19+
_ui.enableTPS = document.getElementById('enable-tps')
20+
_ui.submitButton = _ui.tpsOptionsForm.querySelector('button[type="submit"]')
21+
22+
_ui.tpsOptionsForm.addEventListener('submit', _handleSave)
23+
_ui.toggleButton.addEventListener('click', _handleToggle)
24+
_ui.trackTPSButton.addEventListener('click', _handleTrackTPS)
25+
26+
_ui.enableTPS.addEventListener('change', () => {
27+
_tpsOptions.isEnabled = _ui.enableTPS.checked
28+
setItem('tpsOptions', _tpsOptions)
29+
_setJson(_tpsOptions)
30+
})
31+
32+
_granularOptions = DynamicParams('tps-granular', ['partnerName', 'key', 'value'], _tpsOptions.granularOptions,
33+
() => {
34+
_tpsOptions.granularOptions = _granularOptions.query()
35+
_setJson(_tpsOptions)
36+
setItem('tpsOptions', _tpsOptions)
37+
})
38+
_granularOptions.init()
39+
40+
_partnerSharingSettings = DynamicParams('tps-partner-sharing', ['partnerName', 'key', 'value'], _tpsOptions.partnerSharingSettings,
41+
() => {
42+
_tpsOptions.partnerSharingSettings = _partnerSharingSettings.query()
43+
_setJson(_tpsOptions)
44+
setItem('tpsOptions', _tpsOptions)
45+
})
46+
_partnerSharingSettings.init()
47+
48+
_setJson(_tpsOptions)
49+
}
50+
51+
function _trackThirdPartySharing(tpsOptions) {
52+
const options = new Adjust.ThirdPartySharing(tpsOptions.isEnabled)
53+
54+
for (const option of tpsOptions.granularOptions) {
55+
options.addGranularOption(option.partnerName, option.key, option.value)
56+
}
57+
58+
for (const option of tpsOptions.partnerSharingSettings) {
59+
const value = option.value === 'false' ? false : !!option.value
60+
options.addPartnerSharingSetting(option.partnerName, option.key, value)
61+
}
62+
63+
Adjust.trackThirdPartySharing(options)
64+
}
65+
66+
function _handleSave(e) {
67+
e.preventDefault()
68+
69+
if (_disabled) {
70+
return
71+
}
72+
73+
_disabled = true
74+
_ui.submitButton.classList.add('loading')
75+
_ui.submitButton.disabled = true
76+
77+
clearTimeout(_timeoutId)
78+
_timeoutId = setTimeout(() => {
79+
_disabled = false
80+
_ui.submitButton.classList.remove('loading')
81+
_ui.submitButton.disabled = false
82+
83+
_trackThirdPartySharing(_tpsOptions)
84+
}, 1000)
85+
}
86+
87+
function _handleTrackTPS() {
88+
if (_disabled) {
89+
return
90+
}
91+
92+
_disabled = true
93+
_ui.trackTPSButton.classList.add('loading')
94+
_ui.trackTPSButton.disabled = true
95+
96+
clearTimeout(_timeoutId)
97+
_timeoutId = setTimeout(() => {
98+
_disabled = false
99+
_ui.trackTPSButton.classList.remove('loading')
100+
_ui.trackTPSButton.disabled = false
101+
102+
_trackThirdPartySharing(_tpsOptions)
103+
}, 1000)
104+
}
105+
106+
function _handleToggle(e) {
107+
const target = e.target
108+
const sideForm = target.parentNode.nextElementSibling
109+
110+
sideForm.classList.toggle('show')
111+
target.classList.toggle('active')
112+
}
113+
114+
function _setJson(tpsOptions) {
115+
let text = `const options = new Adjust.ThirdPartySharingOptions(${tpsOptions.isEnabled});\n`
116+
117+
for (const option of tpsOptions.granularOptions) {
118+
text += `option.addGranularOption('${option.partnerName}', '${option.key}', '${option.value}')\n`
119+
}
120+
121+
for (const option of tpsOptions.partnerSharingSettings) {
122+
text += `option.addPartnerSharingSetting('${option.partnerName}', '${option.key}', '${option.value}')\n`
123+
}
124+
125+
text += 'Adjust.trackThirdPartySharing(options);'
126+
127+
_ui.tpsOptionsJson.textContent = text
128+
}
129+
130+
export default init

src/demo/utils.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ function hyphenToCamelCase (string) {
22
return string.replace(/(-\w)/g, ([, m]) => m.toUpperCase())
33
}
44

5+
function capitalize(string) {
6+
return string.charAt(0).toUpperCase() + string.slice(1);
7+
}
8+
59
function debounce (fn, wait = 500) {
610
let timeout
711
return function () {
@@ -12,5 +16,6 @@ function debounce (fn, wait = 500) {
1216

1317
export {
1418
hyphenToCamelCase,
19+
capitalize,
1520
debounce
1621
}

0 commit comments

Comments
 (0)