[PULSE-51] Visitor ID storage: optional localStorage for cross-tab unique visitors #21
@@ -40,7 +40,7 @@ export default function ScriptSetupBlock({
|
|||||||
const [scriptCopied, setScriptCopied] = useState(false)
|
const [scriptCopied, setScriptCopied] = useState(false)
|
||||||
|
|
||||||
const copyScript = useCallback(() => {
|
const copyScript = useCallback(() => {
|
||||||
const script = `<script defer data-domain="${site.domain}" data-api="${API_URL}" src="${APP_URL}/script.js"></script>`
|
const script = `<script defer data-domain="${site.domain}" data-api="${API_URL}" data-storage="local" data-storage-ttl="24" src="${APP_URL}/script.js"></script>`
|
||||||
navigator.clipboard.writeText(script)
|
navigator.clipboard.writeText(script)
|
||||||
setScriptCopied(true)
|
setScriptCopied(true)
|
||||||
toast.success('Script copied to clipboard')
|
toast.success('Script copied to clipboard')
|
||||||
@@ -87,10 +87,10 @@ export default function ScriptSetupBlock({
|
|||||||
|
|
||||||
<div className="rounded-xl bg-neutral-100 dark:bg-neutral-800 p-4 relative group">
|
<div className="rounded-xl bg-neutral-100 dark:bg-neutral-800 p-4 relative group">
|
||||||
<code className="text-xs text-neutral-900 dark:text-white break-all font-mono block pr-10">
|
<code className="text-xs text-neutral-900 dark:text-white break-all font-mono block pr-10">
|
||||||
{`<script defer data-domain="${site.domain}" data-api="${API_URL}" src="${APP_URL}/script.js"></script>`}
|
{`<script defer data-domain="${site.domain}" data-api="${API_URL}" data-storage="local" data-storage-ttl="24" src="${APP_URL}/script.js"></script>`}
|
||||||
</code>
|
</code>
|
||||||
<p className="mt-2 text-xs text-neutral-500 dark:text-neutral-400">
|
<p className="mt-2 text-xs text-neutral-500 dark:text-neutral-400">
|
||||||
Optional: <code className="rounded px-1 bg-neutral-200 dark:bg-neutral-700">data-storage="local"</code> for cross-tab unique visitors; <code className="rounded px-1 bg-neutral-200 dark:bg-neutral-700">data-storage-ttl="24"</code> (hours) to expire the ID.
|
Optional: <code className="rounded px-1 bg-neutral-200 dark:bg-neutral-700">data-storage="session"</code> for per-tab (ephemeral) visitor counting.
|
||||||
</p>
|
</p>
|
||||||
|
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
|
Default behavior changed from sessionStorage to localStorage, contradicting PR description which states "keep sessionStorage as default". Comments say "Default: cross-tab visitor ID (localStorage)" but PR says "Default remains sessionStorage". Prompt To Fix With AIDefault behavior changed from sessionStorage to localStorage, contradicting PR description which states "keep sessionStorage as default". Comments say "Default: cross-tab visitor ID (localStorage)" but PR says "Default remains sessionStorage".
<details><summary>Prompt To Fix With AI</summary>
`````markdown
This is a comment left during a code review.
Path: public/script.js
Line: 4:5
Comment:
Default behavior changed from sessionStorage to localStorage, contradicting PR description which states "keep sessionStorage as default". Comments say "Default: cross-tab visitor ID (localStorage)" but PR says "Default remains sessionStorage".
How can I resolve this? If you propose a fix, please make it concise.
`````
</details>
Issue: Default was documented as sessionStorage while the PR said “keep sessionStorage as default.” Issue: Default was documented as sessionStorage while the PR said “keep sessionStorage as default.”
Fix: Default is cross-tab (localStorage). File header now says: “Default: cross-tab visitor ID (localStorage), optional data-storage-ttl in hours. Optional: data-storage=\"session\" for per-tab (ephemeral) counting.”
Why: Product decision is to use cross-tab as the default; docs and code are aligned with that.
|
|||||||
* Pulse - Privacy-First Tracking Script
|
* Pulse - Privacy-First Tracking Script
|
||||||
* Lightweight, no cookies, GDPR compliant.
|
* Lightweight, no cookies, GDPR compliant.
|
||||||
* Default: ephemeral session ID (sessionStorage, per-tab). Optional: data-storage="local"
|
* Default: cross-tab visitor ID (localStorage, optional data-storage-ttl in hours).
|
||||||
* and data-storage-ttl (hours) for a persistent cross-tab visitor ID with optional expiry.
|
* Optional: data-storage="session" for per-tab (ephemeral) counting.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
|
|
||||||
|
Prompt To Fix With AI`parseFloat` with two arguments (second argument is radix) is incorrect - `parseFloat` only accepts one argument. The radix parameter is for `parseInt`, not `parseFloat`.
```suggestion
const ttlHours = storageMode === 'local' ? parseFloat(script.getAttribute('data-storage-ttl') || '24') : 0;
```
<details><summary>Prompt To Fix With AI</summary>
`````markdown
This is a comment left during a code review.
Path: public/script.js
Line: 27:27
Comment:
`parseFloat` with two arguments (second argument is radix) is incorrect - `parseFloat` only accepts one argument. The radix parameter is for `parseInt`, not `parseFloat`.
```suggestion
const ttlHours = storageMode === 'local' ? parseFloat(script.getAttribute('data-storage-ttl') || '24') : 0;
```
How can I resolve this? If you propose a fix, please make it concise.
`````
</details>
Issue: parseFloat was called with two arguments; only parseInt accepts a radix. Issue: parseFloat was called with two arguments; only parseInt accepts a radix.
Fix: Removed the second argument so the call is parseFloat(script.getAttribute('data-storage-ttl') || '24').
Why: Correct use of the API.
|
|||||||
const domain = script.getAttribute('data-domain');
|
const domain = script.getAttribute('data-domain');
|
||||||
const apiUrl = script.getAttribute('data-api') || 'https://pulse-api.ciphera.net';
|
const apiUrl = script.getAttribute('data-api') || 'https://pulse-api.ciphera.net';
|
||||||
// * Visitor ID storage: "session" (default, ephemeral per-tab) or "local" (persistent, cross-tab)
|
// * Visitor ID storage: "local" (default, cross-tab) or "session" (ephemeral per-tab)
|
||||||
const storageMode = (script.getAttribute('data-storage') || 'session').toLowerCase() === 'local' ? 'local' : 'session';
|
const storageMode = (script.getAttribute('data-storage') || 'local').toLowerCase() === 'session' ? 'session' : 'local';
|
||||||
// * When storage is "local", optional TTL in hours; after TTL the ID is regenerated (e.g. 24 = one day)
|
// * When storage is "local", optional TTL in hours; after TTL the ID is regenerated (e.g. 24 = one day)
|
||||||
const ttlHours = storageMode === 'local' ? parseFloat(script.getAttribute('data-storage-ttl') || '24', 10) : 0;
|
const ttlHours = storageMode === 'local' ? parseFloat(script.getAttribute('data-storage-ttl') || '24', 10) : 0;
|
||||||
const ttlMs = ttlHours > 0 ? ttlHours * 60 * 60 * 1000 : 0;
|
const ttlMs = ttlHours > 0 ? ttlHours * 60 * 60 * 1000 : 0;
|
||||||
@@ -151,7 +151,7 @@
|
|||||||
return cachedSessionId;
|
return cachedSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// * Default: session storage (ephemeral, per-tab)
|
// * data-storage="session": session storage (ephemeral, per-tab)
|
||||||
try {
|
try {
|
||||||
cachedSessionId = sessionStorage.getItem(key);
|
cachedSessionId = sessionStorage.getItem(key);
|
||||||
if (!cachedSessionId && legacyKey) {
|
if (!cachedSessionId && legacyKey) {
|
||||||
|
|||||||
Documentation says
data-storage="session"is "Optional" but according to the implementation, this should be the default. If localStorage is now the default, this text should clarify thatdata-storage="session"opts OUT of cross-tab tracking.Prompt To Fix With AI
Issue: Copy said “Optional: data-storage="session"” without stating that the default is cross-tab and that session opts out.
Fix: Copy updated to: “Default: cross-tab (localStorage). Optional: data-storage="session" to opt out (per-tab, ephemeral).”
Why: Makes it explicit that the default is cross-tab and that data-storage="session" opts out of it.
Missing documentation for
data-storage-ttlattribute. The script supports an optional TTL parameter (default 24 hours) but this isn't mentioned here. Consider adding: "Optional:data-storage-ttl=\"48\"to set expiry in hours (default: 24)."Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
Fix: Already added on line 93: “Optional: data-storage-ttl="48" to set expiry in hours (default: 24).”
Why: So users know they can change the cross-tab visitor ID expiry.
If the comment was from a branch that doesn’t have this yet, merge or cherry-pick the commit that added it, or re-apply that sentence in the same paragraph.