|
| 1 | +# Link Analytics — Global Click Tracking, Likes & Live Presence |
| 2 | + |
| 3 | +- Added `js/link-analytics.js` — new module for global cross-user analytics via Firestore |
| 4 | +- Added `css/link-analytics.css` — styles for like widget, click badges, and analytics panel |
| 5 | +- Click count badges appear inline next to each `<a>` link in the preview pane |
| 6 | +- Badge counts are global (shared across ALL users) via Firestore `link_clicks` collection using `FieldValue.increment()` |
| 7 | +- Badges seeded instantly from localStorage display cache; refreshed from Firestore in background |
| 8 | +- Added floating 👍 Like button (fixed, bottom-right) — one like per session, saved to Firestore |
| 9 | +- Like button disappears with a spring-bump → fade-out animation after being clicked |
| 10 | +- Fixed: like button lifted above shared-view pill when `body.shared-view-active` to prevent overlap |
| 11 | +- Fixed: like button lifts above composer FAB on mobile (`bottom: 80px` on ≤768px) |
| 12 | +- Added live "X reading now" presence tracking via Firestore heartbeat/onSnapshot (20s heartbeat, 60s TTL) |
| 13 | +- Added document view counter — increments once per session for shared docs (`link_clicks/views_{docId}`) |
| 14 | +- Added analytics panel (slide-in from right) showing ranked link list with bar chart, opened via toolbar button |
| 15 | +- Panel shows: total opens, live reader count, per-link click count + last clicked date |
| 16 | +- Added `link_clicks` Firestore collection rules (open read/write for analytics, presence subcollection) |
| 17 | +- Fixed: moved `link-analytics.js` out of `Promise.all` Phase 3a into its own `try/catch` await after Phase 3a |
| 18 | +- Fixed: replaced fragile `readyState` bootstrap with `waitForPreview()` poll (100ms × 100 retries) to handle async module load order |
| 19 | +- Updated `src/main.js` to import `link-analytics.css` and load `link-analytics.js` |
| 20 | +- Updated `firestore.rules` to allow `link_clicks` collection and `readers` subcollection |
| 21 | + |
| 22 | +--- |
| 23 | + |
| 24 | +## Summary |
| 25 | +Implements a complete global analytics system for TextAgent shared documents. All metrics (link clicks, likes, document views, live readers) are stored in Firestore and shared across every user — not scoped to a single browser session. |
| 26 | + |
| 27 | +--- |
| 28 | + |
| 29 | +## 1. Global Link Click Tracking |
| 30 | +**Files:** `js/link-analytics.js`, `css/link-analytics.css` |
| 31 | +**What:** Intercepts clicks on all `<a>` links in the markdown preview. Each click atomically increments a Firestore counter (`FieldValue.increment(1)`) under `link_clicks/{urlSHA256}`. A small pill badge renders inline after the link text showing the global click count. |
| 32 | +**Impact:** Any user clicking a link in any TextAgent document contributes to a shared click count visible to everyone. |
| 33 | + |
| 34 | +## 2. Floating Like Button |
| 35 | +**Files:** `js/link-analytics.js`, `css/link-analytics.css` |
| 36 | +**What:** Injects a fixed-position 👍 button (bottom-right) into the preview pane. One like per browser session (memory-only dedup). Saves to `link_clicks/likes_{shareDocId}` with Firestore realtime listener for live count. On click: spring animation → fade-out → `display:none`. |
| 37 | +**Impact:** Readers of shared documents can express appreciation. Like count updates in realtime across all viewers. |
| 38 | + |
| 39 | +## 3. Live Presence (Reading Now) |
| 40 | +**Files:** `js/link-analytics.js` |
| 41 | +**What:** When a shared doc is opened, writes a heartbeat document to `link_clicks/presence_{docId}/readers/{sessionId}` every 20s. A Firestore `onSnapshot` listener counts sessions with `lastSeen` within 60s and updates the green pulsing "X reading now" pill in the analytics panel header. |
| 42 | +**Impact:** Shows how many people are actively reading a shared document right now, in realtime. |
| 43 | + |
| 44 | +## 4. Document View Counter |
| 45 | +**Files:** `js/link-analytics.js` |
| 46 | +**What:** On first load of a shared doc per session, increments `link_clicks/views_{docId}.views` in Firestore. Displayed in the analytics panel as "N total opens". |
| 47 | +**Impact:** Authors can see total lifetime opens of their shared documents. |
| 48 | + |
| 49 | +## 5. Analytics Panel |
| 50 | +**Files:** `js/link-analytics.js`, `css/link-analytics.css` |
| 51 | +**What:** Slide-in right panel (420px wide, keyboard-dismissable) opened via a bar chart toolbar button. Fetches all click data from Firestore, renders a ranked list with proportional bars (gold/silver/bronze for top 3), total opens, and live reader count. |
| 52 | +**Impact:** Central dashboard for all link and document engagement metrics. |
| 53 | + |
| 54 | +## 6. Firestore Rules |
| 55 | +**Files:** `firestore.rules` |
| 56 | +**What:** Added `link_clicks/{docId}` collection rules (open read/write) and `readers/{sessionId}` subcollection rules (write limited to `{lastSeen: int}` shape, delete allowed for cleanup). |
| 57 | +**Impact:** Enables anonymous read/write for analytics without touching the protected `shares` collection rules. |
| 58 | + |
| 59 | +## 7. Module Load Fix |
| 60 | +**Files:** `src/main.js`, `js/link-analytics.js` |
| 61 | +**What:** Extracted from `Promise.all` Phase 3a into a standalone `try/catch await` after Phase 3a. Replaced `readyState` bootstrap with `waitForPreview()` — a 100ms polling loop that waits up to 10s for `M.markdownPreview`, `M.renderMarkdown`, and `M.db` to be available. |
| 62 | +**Impact:** Eliminates silent initialization failures when the module loads before the preview element is wired. |
| 63 | + |
| 64 | +--- |
| 65 | + |
| 66 | +## Files Changed (4 total) |
| 67 | + |
| 68 | +| File | Type | Description | |
| 69 | +|------|------|-------------| |
| 70 | +| `js/link-analytics.js` | NEW | Core analytics module (~570 lines) | |
| 71 | +| `css/link-analytics.css` | NEW | All styles for badges, like widget, panel | |
| 72 | +| `src/main.js` | MODIFIED | CSS import + standalone module load | |
| 73 | +| `firestore.rules` | MODIFIED | `link_clicks` collection + presence rules | |
0 commit comments