Skip to content

7.1 interaction logging - #23

Merged
flvvius merged 8 commits into
mainfrom
feat/7-bias_balance_streaks
May 2, 2026
Merged

7.1 interaction logging#23
flvvius merged 8 commits into
mainfrom
feat/7-bias_balance_streaks

Conversation

@flvvius

@flvvius flvvius commented May 2, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added comprehensive interaction tracking for bookmarks, shares, source clicks, and page views
    • Device-aware analytics and scroll-depth / time-on-page measurement
    • New Bias Balance meter component to visualize event/article bias
    • Dashboard now shows real user stats: streaks, bookmark count, articles read, and bias balance
    • User menu displays current reading streak next to profile options

@vercel

vercel Bot commented May 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
news Error Error May 2, 2026 3:23pm
news-web Ready Ready Preview, Comment May 2, 2026 3:23pm

@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@flvvius has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 23 minutes and 27 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b2895d5c-c755-44a4-a50b-9338bd870677

📥 Commits

Reviewing files that changed from the base of the PR and between a6694f2 and ad47af4.

📒 Files selected for processing (8)
  • apps/web/src/components/bias-balance-meter.tsx
  • apps/web/src/components/early-access-apply-card.tsx
  • apps/web/src/components/streak-activity-calendar.tsx
  • apps/web/src/components/user-menu.tsx
  • apps/web/src/routes/dashboard.tsx
  • apps/web/src/routes/event.$slug.tsx
  • packages/backend/convex/interactions.ts
  • packages/backend/convex/schema.ts

Walkthrough

Adds end-to-end interaction tracking: client utilities to compute device/scroll/context, client instrumentation (bookmark, share, source click, view logging), backend validation/normalization and centralized persistence, plus unrelated UI updates (bias meter, dashboard stats, user streak display).

Changes

Interaction tracking (client + backend + wiring)

Layer / File(s) Summary
Utility / Data Shape
apps/web/src/lib/interaction-tracking.ts
Adds buildInteractionContextFromSources, getClientDeviceType, getScrollDepthPercentage, and InteractionContextSnapshot type.
Backend Validation & Persistence
packages/backend/convex/interactions.ts
Adds INTERACTION_* validators, normalizeInteractionMetadata, resolveInteractionContext, recordInteraction; updates logInteraction and toggleBookmark to accept/validate context and metadata and route through recordInteraction.
Route-level orchestration
apps/web/src/routes/event.$slug.tsx
Authorized event page: tracks max scroll depth and time spent, logs "view" on unmount with deviceType, scrollDepthPercentage, timeSpentSeconds, computes interactionContext and passes it to buttons and ArticlesList. Also passes eventId to ShareEventButton on public preview.
Component instrumentation
apps/web/src/components/bookmark-button.tsx, apps/web/src/components/share-event-button.tsx, apps/web/src/components/feed/articles-list.tsx, apps/web/src/components/feed/event-card.tsx
BookmarkButton now includes interactionContext and sends metadata.deviceType on toggle; ShareEventButton logs "share" after Web Share or clipboard copy (uses eventId/interactionContext); ArticlesList logs "click_source" on source/profile/external link clicks when authenticated; EventCard computes and passes interactionContext and eventId to buttons.
API wiring (client)
apps/web/... (various components)
Adds useConvexAuth/useConvexMutation and api.interactions.logInteraction usage across components to perform client-side mutations.

UI / Stats updates

Layer / File(s) Summary
New component
apps/web/src/components/bias-balance-meter.tsx
New BiasBalanceMeter component that clamps/rounds value, derives label/copy, and renders a visual meter.
Dashboard wiring
apps/web/src/routes/dashboard.tsx
Authorized dashboard: fetches bookmarkedEvents, derives readingStreak, longestStreak, articlesRead, biasBalance, bookmarkCount; updates Stats Grid layout and replaces placeholders with real metrics, renders BiasBalanceMeter.
User menu
apps/web/src/components/user-menu.tsx
Adds a disabled dropdown item showing user?.stats.currentStreak with flame icon.

Sequence Diagram

sequenceDiagram
    participant User
    participant Client as Client UI
    participant Lib as Tracking Lib<br/>(interaction-tracking.ts)
    participant API as Convex Mutation<br/>(api.interactions)
    participant Backend as Convex Backend<br/>(interactions.ts)
    participant DB as Convex DB

    User->>Client: interact (view / click / share / bookmark)
    Client->>Lib: buildInteractionContextFromSources()
    Client->>Lib: getClientDeviceType(), getScrollDepthPercentage()
    Client->>API: logInteraction / toggleBookmark (with context & metadata)
    API->>Backend: mutation handler
    Backend->>Backend: normalizeMetadata(), resolveContext(), recordInteraction()
    Backend->>DB: insert / patch interaction
    DB-->>Backend: ack
    Backend-->>API: success
    API-->>Client: mutation result
    Client->>User: toast / UI update
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

v0

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '7.1 interaction logging' directly addresses the core changes across multiple files—adding interaction tracking for bookmarks, shares, article clicks, and page views with device metadata.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/7-bias_balance_streaks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 23 minutes and 27 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/routes/event`.$slug.tsx:
- Around line 356-390: The effect is depending on the unstable TanStack mutation
object (logInteraction) which can change between renders and cause premature
cleanup; replace the dependency with a stable mutate function from
useConvexMutation(api.interactions.logInteraction) (e.g., const logInteractionFn
= useConvexMutation(api.interactions.logInteraction)) and call that function
inside the cleanup instead of logInteraction.mutate, then remove the mutation
object from the useEffect dependency array (keep eventData?.event?._id and
isAuthenticated). Ensure you still import/use getClientDeviceType,
getScrollDepthPercentage and preserve the same metadata shape when invoking the
stable mutate function.

In `@packages/backend/convex/interactions.ts`:
- Around line 12-20: INTERACTION_TYPE_VALIDATOR currently includes "bookmark"
and "unbookmark", which lets logInteraction insert bookmark rows and bypass
toggleBookmark's cooldown/dedup logic; remove the v.literal("bookmark") and
v.literal("unbookmark") from INTERACTION_TYPE_VALIDATOR (or alternatively update
logInteraction to explicitly reject these two types) so bookmark actions are
only handled by toggleBookmark; update logInteraction to throw or return early
if it receives "bookmark"/"unbookmark", and ensure resolveBookmarkStatus and
getBookmarkedEvents continue to treat bookmark rows as authoritative and rely on
toggleBookmark for writes.
- Around line 134-162: The code in interactions.ts currently fan-outs on every
interaction write by querying eventArticles, uniqueSourceIds, and sources and
computing totalBias/totalReliability to produce biasRating and
sourceReliability; move this work off the hot write path by reading precomputed
event-level aggregates (e.g., add avgBias and avgReliability fields to the
events record or an eventStats document) or require the caller to pass a cheap
context arg instead of recomputing articles/sources per write, and update the
interaction write logic (the block referencing eventArticles, uniqueSourceIds,
sources, totalBias, totalReliability, biasRating, sourceReliability and the
similar logic at the later block) to use those precomputed fields or the
passed-in context; ensure background jobs or DB triggers maintain the
precomputed aggregates when articles or sources change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 435f699a-437b-4c35-be78-f93d0c0c0c88

📥 Commits

Reviewing files that changed from the base of the PR and between b320382 and d2cbf05.

📒 Files selected for processing (7)
  • apps/web/src/components/bookmark-button.tsx
  • apps/web/src/components/feed/articles-list.tsx
  • apps/web/src/components/feed/event-card.tsx
  • apps/web/src/components/share-event-button.tsx
  • apps/web/src/lib/interaction-tracking.ts
  • apps/web/src/routes/event.$slug.tsx
  • packages/backend/convex/interactions.ts

Comment thread apps/web/src/routes/event.$slug.tsx Outdated
Comment thread packages/backend/convex/interactions.ts
Comment thread packages/backend/convex/interactions.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/bias-balance-meter.tsx`:
- Around line 62-69: The indicator thumb is clipped at extremes because the
element uses left: `${indicatorPosition}%` together with the existing
-translate-x-1/2 transform and the parent has overflow-hidden; update the
indicator positioning logic in the BiasBalanceMeter component (the thumb div
that currently has class "-translate-x-1/2" and style { left:
`${indicatorPosition}%` }) so that when indicatorPosition is 0 or 100 you remove
or override the -50% X-translation (or apply a pixel/offset calc) to keep the
full thumb inside the container (e.g., use conditional transform or left:
`calc(...)` to shift by half the thumb width only for non-extreme values, or
clamp left to a small inset like 0.5rem/99.5% at extremes).

In `@apps/web/src/components/user-menu.tsx`:
- Around line 60-63: The streak label always shows "day streak"; change it to
pluralize based on the current streak value by reading user?.stats.currentStreak
(use a local const like streak = user?.stats.currentStreak ?? 0) and render
`${streak} day${streak === 1 ? '' : 's'} streak` inside the DropdownMenuItem
(component DropdownMenuItem, variable user?.stats.currentStreak) so 0 and >1
show "days" and 1 shows "day".

In `@apps/web/src/routes/dashboard.tsx`:
- Line 312: The dashboard currently calls
useQuery(api.interactions.getBookmarkedEvents) (bookmarkedEvents) which fetches
full hydrated bookmark objects just to read .length; replace this with a
lightweight count endpoint or use the already-available currentUser.stats field
instead: add or switch to an API method like api.interactions.getBookmarkedCount
(or read currentUser.stats.bookmarks) and update the useQuery call and any
references to bookmarkedEvents.length to use the numeric count, ensuring you
remove/stop calling getBookmarkedEvents from the dashboard card to avoid
hydrating full articles/sources.

In `@apps/web/src/routes/event`.$slug.tsx:
- Around line 373-390: The cleanup callback currently calls
logInteractionFn(...) with void which can still produce unhandled promise
rejections; update the return cleanup to call logInteractionFn(...) and append a
.catch handler to swallow or log errors so failures don't surface as
unhandledrejection events. Locate the block that removes the "scroll" listener
(handleScroll) and the logInteractionFn invocation that uses
eventData.event._id, interactionContext, getClientDeviceType(),
getScrollDepthPercentage(), maxScrollDepth, and startedAt, and add a .catch(...)
to the returned promise to handle/recover from any rejection.

In `@packages/backend/convex/interactions.ts`:
- Around line 115-121: The current early return when
ctx.db.query("userStats").withIndex("by_user", ...).unique() yields no stats
prevents initializing tracking for new users; instead, create a new userStats
record with the same zero/default shape used by packages/backend/convex/user.ts
(include userId and default fields like streak, articlesRead, biasBalance,
lastReadAt or similar) so the first tracked view updates counts/streaks; locate
the lookup for "userStats" in interactions.ts, and if stats is null call the
appropriate db insert/transaction to persist the default userStats before
proceeding with the view handling logic.
- Around line 25-36: INTERACTION_METADATA_VALIDATOR currently allows any string
for deviceType which can fragment analytics; update the validator so deviceType
is validated as an optional closed enum of the three allowed values ("mobile",
"tablet", "desktop") instead of v.string(); modify the deviceType entry in
INTERACTION_METADATA_VALIDATOR to use the validation combinator that enforces
exact literals (e.g., v.optional of a union of v.literal("mobile"),
v.literal("tablet"), v.literal("desktop")) so only those three values are
accepted at the mutation boundary.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7e1d3cb4-5dc9-49ed-9979-d505ded7f5e2

📥 Commits

Reviewing files that changed from the base of the PR and between d2cbf05 and a6694f2.

📒 Files selected for processing (10)
  • apps/web/src/components/bias-balance-meter.tsx
  • apps/web/src/components/bookmark-button.tsx
  • apps/web/src/components/feed/articles-list.tsx
  • apps/web/src/components/feed/event-card.tsx
  • apps/web/src/components/share-event-button.tsx
  • apps/web/src/components/user-menu.tsx
  • apps/web/src/lib/interaction-tracking.ts
  • apps/web/src/routes/dashboard.tsx
  • apps/web/src/routes/event.$slug.tsx
  • packages/backend/convex/interactions.ts

Comment thread apps/web/src/components/bias-balance-meter.tsx
Comment thread apps/web/src/components/user-menu.tsx
Comment thread apps/web/src/routes/dashboard.tsx Outdated
Comment thread apps/web/src/routes/event.$slug.tsx
Comment thread packages/backend/convex/interactions.ts
Comment thread packages/backend/convex/interactions.ts Outdated
@flvvius
flvvius merged commit cf17167 into main May 2, 2026
3 of 4 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant