Skip to content

Live cursor updates the side-by-side left card#4694

Merged
ncarazon merged 8 commits intofeat/question-page-redesign-2nd-iterationfrom
feat/live-cursor-updates-the-side-by-side-left-card
May 11, 2026
Merged

Live cursor updates the side-by-side left card#4694
ncarazon merged 8 commits intofeat/question-page-redesign-2nd-iterationfrom
feat/live-cursor-updates-the-side-by-side-left-card

Conversation

@ncarazon
Copy link
Copy Markdown
Contributor

@ncarazon ncarazon commented May 6, 2026

Related to #4642

This PR implements live cursor updates for the consumer view: hovering the timeline updates the left-panel value, range, and mini distribution chart in real-time. Applies to all consumer continuous charts. Binary timeline/radial is explicitly excluded. Live updates are scoped to consumer view only - forecaster left panel and mobile header remain static.

Implemented features & fixes

  • Consumer continuous left panel - QuestionHeaderCPStatus now accepts a cursorForecast prop; on hover, the center value, confidence interval, and minified area chart all animate to the cursor position; reverts to latest aggregate when cursor leaves
  • Consumer continuous timeline - removed dashed cursor line and floating tooltip (replaced by left-panel live update); added a filled 8px dot on the CP line at cursor position
  • Consumer mobile mini chart - ContinuousQuestionPrediction now reads cursor forecast from ContinuousChartCursorContext and updates both the center value display and the minified distribution chart in real-time on mobile
  • ContinuousChartCursorContext - new context scoped to ConsumerShell that bridges the timeline cursor forecast to the mobile mini chart without prop drilling; DetailedContinuousChartCard writes, ContinuousQuestionPrediction reads
  • Mobile tooltip position - floating tooltip now follows touch position in real-time via useClientPoint x/y override in useChartTooltip; touch offset increased to 50px to clear the finger; onTouchEnd added to Victory chart events so cursor resets on touch lift
  • Forecaster continuous timeline - unchanged; dashed line, floating tooltip, and value label chip are preserved
  • hideCursorValueLabel - added to NumericChart/NumericTimeline to suppress the on-chart value chip when the left panel is already showing the value (consumer continuous only)
  • ContinuousCPBar - added overrideCenter/overrideBounds props so the value/range display can be driven by cursor data without mutating the underlying question object
demo.mp4

Summary by CodeRabbit

  • New Features

    • Enhanced cursor state management for synchronized chart interactions across multiple views.
    • Improved touch handling on mobile devices for better tooltip positioning and cursor tracking.
  • Bug Fixes

    • Refined cursor rendering behavior in consumer views for clearer forecast visualization.
    • Improved forecast data display alignment when interacting with charts via cursor or touch.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a82e6650-3574-4675-a01b-9c48eeb3aa6f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/live-cursor-updates-the-side-by-side-left-card

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

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 6, 2026

Cleanup: Preview Environment Removed

The preview environment for this PR has been destroyed.

Resource Status
🌐 Preview App Deleted
🗄️ PostgreSQL Branch Deleted
⚡ Redis Database Deleted
🔧 GitHub Deployments Removed
📦 Docker Image Retained (auto-cleanup via GHCR policies)

Cleanup triggered by PR close at 2026-05-11T14:32:36Z

Base automatically changed from feat/mobile-introduce-forecaster-tab-bar-styling-parity-pass to feat/question-page-redesign-1st-iteration May 7, 2026 08:27
@ncarazon ncarazon changed the base branch from feat/question-page-redesign-1st-iteration to feat/question-page-redesign-2nd-iteration May 7, 2026 13:13
ncarazon added 4 commits May 7, 2026 16:13
…or continuous consumer view, with dot indicator on timeline and no tooltip/dashed line in consumer mode
@ncarazon ncarazon force-pushed the feat/live-cursor-updates-the-side-by-side-left-card branch from 14a3222 to fbf720b Compare May 7, 2026 13:14
@ncarazon ncarazon marked this pull request as ready for review May 7, 2026 13:15
Comment thread questions/serializers/aggregate_forecasts.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
front_end/src/components/charts/numeric_chart.tsx (1)

231-307: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add isContinuousConsumerView to containerComponent memo dependencies.

isContinuousConsumerView is used in the memo body (line 263) but missing from the dependency array (lines 298-307). When isConsumerView or questionType changes, the cursor rendering mode won't update until another dependency changes.

Suggested fix
   }, [
     defaultCursor,
     xScale,
     height,
     hasExternalTheme,
     getThemeColor,
     handleCursorChange,
     nonInteractive,
     isCursorActive,
+    isContinuousConsumerView,
   ]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@front_end/src/components/charts/numeric_chart.tsx` around lines 231 - 307,
The useMemo producing containerComponent references isContinuousConsumerView but
it is not included in the dependency array, causing stale cursor rendering;
update the dependency list for the useMemo that defines containerComponent to
include isContinuousConsumerView (alongside defaultCursor, xScale, height,
hasExternalTheme, getThemeColor, handleCursorChange, nonInteractive,
isCursorActive) so the memo re-evaluates when isContinuousConsumerView changes.
🧹 Nitpick comments (2)
questions/serializers/aggregate_forecasts.py (1)

63-64: ⚡ Quick win

full_forecast_values is now a no-op; remove or deprecate it.

With history now hardcoded to full=True (Line 131) and latest already hardcoded full=True (Line 135), full_forecast_values no longer changes behavior. Keeping it in the signature/docstring is misleading for callers.

Suggested cleanup
 def serialize_question_aggregations(
     question: Question,
     aggregate_forecasts: list[AggregateForecast],
-    full_forecast_values: bool = False,
     minimize: bool = True,
 ) -> dict:

If backward compatibility is needed, keep the argument temporarily but mark it deprecated and ignored.

Also applies to: 130-133

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@questions/serializers/aggregate_forecasts.py` around lines 63 - 64, The
parameter full_forecast_values in the function signature is now a no-op because
history and latest are being constructed with full=True unconditionally; remove
full_forecast_values from the signature and docstring and delete any references
to it, or if you need backward compatibility keep the parameter but mark it
deprecated (add a DeprecationWarning via warnings.warn when full_forecast_values
is passed and ignore its value) and update the docstring to state it is
deprecated/ignored; ensure any callers/tests are updated to stop relying on it
and that any internal mentions (e.g., where history and latest are created with
full=True) are left consistent.
front_end/src/app/(main)/questions/[id]/components/question_view/consumer_question_view/prediction/single_question_prediction/continuous_question_prediction.tsx (1)

43-54: ⚡ Quick win

Cursor area chart data derivation is duplicated across two components.

The cursorForecastValues extraction + useMemo(cdfToPmf(...)) pattern is copy-pasted almost verbatim in question_header_cp_status.tsx. The only difference is the ContinuousAreaType selection (that file includes "community_resolved"; this one does not, relying on the early return for resolved questions). A shared hook—e.g., useCursorAreaChartData(cursorForecast, questionStatus)—would centralise the CDF→PMF conversion and make future changes easier.

♻️ Suggested shared hook
// e.g. front_end/src/hooks/use_cursor_area_chart_data.ts
import { useMemo } from "react";
import { ContinuousAreaGraphInput } from "@/components/charts/continuous_area_chart";
import { ContinuousAreaType } from "@/types/charts";
import { QuestionStatus } from "@/types/post";
import { NumericAggregateForecast } from "@/types/question";
import { cdfToPmf } from "@/utils/math";

export function useCursorAreaChartData(
  cursorForecast: NumericAggregateForecast | null | undefined,
  questionStatus: QuestionStatus
): ContinuousAreaGraphInput | null {
  const cursorForecastValues = cursorForecast?.forecast_values ?? null;
  return useMemo<ContinuousAreaGraphInput | null>(() => {
    if (!cursorForecastValues) return null;
    const type = (
      questionStatus === QuestionStatus.RESOLVED
        ? "community_resolved"
        : questionStatus === QuestionStatus.CLOSED
          ? "community_closed"
          : "community"
    ) as ContinuousAreaType;
    return [{ pmf: cdfToPmf(cursorForecastValues), cdf: cursorForecastValues, type }];
  }, [cursorForecastValues, questionStatus]);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@front_end/src/app/`(main)/questions/[id]/components/question_view/consumer_question_view/prediction/single_question_prediction/continuous_question_prediction.tsx
around lines 43 - 54, Extract the duplicated cursorForecastValues +
useMemo(cdfToPmf(...)) logic into a shared hook named useCursorAreaChartData
that accepts (cursorForecast, questionStatus) and returns
ContinuousAreaGraphInput | null; inside the hook derive cursorForecastValues
from cursorForecast?.forecast_values, memoize the conversion using cdfToPmf, and
compute the ContinuousAreaType mapping (RESOLVED -> "community_resolved", CLOSED
-> "community_closed", else "community"), then replace the duplicated logic in
both continuous_question_prediction (where cursorForecastValues and useMemo are
currently used) and question_header_cp_status by calling
useCursorAreaChartData(cursorForecast, question.status). Ensure you reference
ContinuousAreaGraphInput, ContinuousAreaType, cdfToPmf, and QuestionStatus types
in the new hook.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@front_end/src/components/charts/numeric_chart.tsx`:
- Around line 818-833: The VictoryScatter marker uses colorOverride directly and
currently passes String(colorOverride) which can serialize ThemeColor objects to
invalid CSS; instead resolve colorOverride with resolveToCssColor before passing
to VictoryScatter's fill: when computing the style.data.fill for VictoryScatter
(the component rendering [highlightedPoint]), call
resolveToCssColor(colorOverride) when colorOverride is present, otherwise fall
back to getThemeColor(colorPalette.chip); update the style block inside the
VictoryScatter rendering to use resolveToCssColor so it matches other uses in
this file.

In
`@front_end/src/components/detailed_question_card/detailed_question_card/continuous_chart_card.tsx`:
- Around line 217-221: The current effect causes churn because including
activeForecast in dependencies runs cleanup that sets
cursorCtx.setActiveForecast(null) before every update; split into two effects:
(1) an effect that watches [activeForecast, question, cursorCtx] and only calls
cursorCtx.setActiveForecast(activeForecast) when isContinuousQuestion(question)
is true and calls cursorCtx.setActiveForecast(null) immediately if the question
becomes non-continuous, and (2) a mount/unmount effect that depends only on
cursorCtx and returns a cleanup that sets cursorCtx.setActiveForecast(null) on
unmount; keep using the existing isContinuousQuestion and
cursorCtx.setActiveForecast symbols.

---

Outside diff comments:
In `@front_end/src/components/charts/numeric_chart.tsx`:
- Around line 231-307: The useMemo producing containerComponent references
isContinuousConsumerView but it is not included in the dependency array, causing
stale cursor rendering; update the dependency list for the useMemo that defines
containerComponent to include isContinuousConsumerView (alongside defaultCursor,
xScale, height, hasExternalTheme, getThemeColor, handleCursorChange,
nonInteractive, isCursorActive) so the memo re-evaluates when
isContinuousConsumerView changes.

---

Nitpick comments:
In
`@front_end/src/app/`(main)/questions/[id]/components/question_view/consumer_question_view/prediction/single_question_prediction/continuous_question_prediction.tsx:
- Around line 43-54: Extract the duplicated cursorForecastValues +
useMemo(cdfToPmf(...)) logic into a shared hook named useCursorAreaChartData
that accepts (cursorForecast, questionStatus) and returns
ContinuousAreaGraphInput | null; inside the hook derive cursorForecastValues
from cursorForecast?.forecast_values, memoize the conversion using cdfToPmf, and
compute the ContinuousAreaType mapping (RESOLVED -> "community_resolved", CLOSED
-> "community_closed", else "community"), then replace the duplicated logic in
both continuous_question_prediction (where cursorForecastValues and useMemo are
currently used) and question_header_cp_status by calling
useCursorAreaChartData(cursorForecast, question.status). Ensure you reference
ContinuousAreaGraphInput, ContinuousAreaType, cdfToPmf, and QuestionStatus types
in the new hook.

In `@questions/serializers/aggregate_forecasts.py`:
- Around line 63-64: The parameter full_forecast_values in the function
signature is now a no-op because history and latest are being constructed with
full=True unconditionally; remove full_forecast_values from the signature and
docstring and delete any references to it, or if you need backward compatibility
keep the parameter but mark it deprecated (add a DeprecationWarning via
warnings.warn when full_forecast_values is passed and ignore its value) and
update the docstring to state it is deprecated/ignored; ensure any callers/tests
are updated to stop relying on it and that any internal mentions (e.g., where
history and latest are created with full=True) are left consistent.
🪄 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: CHILL

Plan: Pro

Run ID: 54f10cd1-4b21-4497-9afb-ebff6c012bf5

📥 Commits

Reviewing files that changed from the base of the PR and between 02684fd and fbf720b.

📒 Files selected for processing (12)
  • front_end/src/app/(main)/questions/[id]/components/question_page_shell/index.tsx
  • front_end/src/app/(main)/questions/[id]/components/question_view/consumer_question_view/prediction/single_question_prediction/continuous_question_prediction.tsx
  • front_end/src/app/(main)/questions/[id]/components/question_view/forecaster_question_view/question_header/question_header_cp_status.tsx
  • front_end/src/components/charts/numeric_chart.tsx
  • front_end/src/components/charts/numeric_timeline.tsx
  • front_end/src/components/charts/primitives/line_cursor_points.tsx
  • front_end/src/components/consumer_post_card/consumer_question_tile/consumer_continuous_tile.tsx
  • front_end/src/components/detailed_question_card/detailed_question_card/continuous_chart_card.tsx
  • front_end/src/components/post_card/question_tile/continuous_cp_bar.tsx
  • front_end/src/contexts/continuous_chart_cursor_context.tsx
  • front_end/src/hooks/use_chart_tooltip.ts
  • questions/serializers/aggregate_forecasts.py

Comment thread front_end/src/components/charts/numeric_chart.tsx
Copy link
Copy Markdown
Contributor

@cemreinanc cemreinanc left a comment

Choose a reason for hiding this comment

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

LGTM on frontend, left a comment about BE.

Copy link
Copy Markdown
Contributor

@hlbmtc hlbmtc left a comment

Choose a reason for hiding this comment

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

Blocking not to miss this for now

Comment thread questions/serializers/aggregate_forecasts.py Outdated
ncarazon added 2 commits May 11, 2026 12:58
…orer on pointer enter for continuous consumer cursor animation, keeping posts endpoint lightweight
… stored aggregation bot semantics, format aggregate_forecasts.py
@ncarazon ncarazon requested review from cemreinanc and hlbmtc May 11, 2026 09:59
Copy link
Copy Markdown
Contributor

@cemreinanc cemreinanc left a comment

Choose a reason for hiding this comment

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

Using aggregation explorer without changing default backend is good now!

I got this react error while testing / hovering around chart. (while logged-in and on consumer view)

Image

And last one maybe a design question but: why we dont use the same cursor updates in forecaster view too?

@ncarazon
Copy link
Copy Markdown
Contributor Author

Using aggregation explorer without changing default backend is good now!

I got this react error while testing / hovering around chart. (while logged-in and on consumer view)

Image And last one maybe a design question but: why we dont use the same cursor updates in forecaster view too?

Good catch! Fixed the bug.

Before

1.mp4

After

2.mp4

cursorCtx gets a new object reference on every state update, so the effect that called setActiveForecast immediately re-triggered itself.
Extracted setActiveForecast into its own variable. It's a stable useCallback reference that never changes, so the effect no longer re-triggers


About forecaster view, Atakan mentioned in Figma that this change should be applied on consumer views and additionally should be disabled for binary questions

image

@ncarazon ncarazon requested a review from cemreinanc May 11, 2026 11:50
@ncarazon ncarazon merged commit 624b350 into feat/question-page-redesign-2nd-iteration May 11, 2026
9 checks passed
@ncarazon ncarazon deleted the feat/live-cursor-updates-the-side-by-side-left-card branch May 11, 2026 14:32
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.

3 participants