# Wagmi-charts line-chart spike — report

**Agent:** 70-wagmi-charts (nanodev:5)
**Branch:** `feature/nan-20-live-price-websocket`
**Commit:** `167de00a` — local only (not pushed)
**Date:** 2026-04-18

## TL;DR

**Recommendation: GO.** Line-chart swap went cleanly. Signature preserved, all
gates green, no new Jest mocks required. Zero friction. Proceeding to candle
swap is warranted once coordinator greenlights.

One caveat: I have not visually verified on device (agent does not push; EAS
preview is hook-gated on push). Coordinator or user should run local Expo or
EAS preview on commit `167de00a` before final go-for-candle decision.

## What changed

- `src/features/stock-detail/presentation/components/StockLineChart.tsx`
  - Replaced victory-native `CartesianChart + Line` with
    `LineChart.Provider + LineChart + LineChart.Path + LineChart.Tooltip +
    LineChart.CursorCrosshair`
  - Data reshaped once via `useMemo`: `bars.map(b => ({ timestamp, value: b.close }))`
  - `onCurrentIndexChange` → `Haptics.impactAsync(Light)` (once per index change)
  - Chart width measured via `onLayout` (wagmi requires explicit width/height)
  - Public props unchanged: `{ bars, trend, priceHigh, priceLow, chartInteractive }`
  - External price labels + `ChartXAxis` preserved verbatim
- `package.json` / `pnpm-lock.yaml` — added `react-native-wagmi-charts ^2.9.1`
  and `expo-haptics ~15.0.8` (both via `npx expo install` for SDK compat)

## What is NOT yet changed

- `StockCandleChart.tsx` — still victory-native + hand-rolled Skia
- `ChartCrosshair.tsx` — still in place (candle chart still depends on it)
- `StockDetailScreen.tsx:110–116` `ChartSlot` — unchanged
- `getStockAggs` 1D session-bleed bug — separate commit, not yet addressed
- Crossfade polish on line↔candle swap — not yet addressed

## Gates

| Gate | Result |
|---|---|
| `rtk pnpm typecheck` | ✅ clean |
| `rtk pnpm lint --fix` | ✅ zero warnings |
| `rtk pnpm test --testPathPattern=stock-detail` | ✅ 32 suites / 160 tests |
| Visual on device | ⚠️ not verified — requires EAS preview or local Expo run |

## Live-price flow — already works

No new wiring was needed for live-price. `useChartData` already merges WS
ticks into the returned `bars` array via `mergeTickIntoSparkline` for `1D`
timerange (see `useChartData.ts:66–76`). Since `StockLineChart` consumes
`bars` and passes them to wagmi's `LineChart.Provider data={...}` prop, every
flush of `LivePriceStore` triggers a Provider re-render with mutated
`bars[last].close`. Wagmi's `isTransitionEnabled` on the Path animates the
tail smoothly.

The 300ms flush cadence currently in use (commit `b318b198`) feels like it
will be fine — faster than the 1.5s cadence the research doc cited and still
well below any render budget concern for a single Provider.

## Risks / observations

1. **Crosshair UX is different by design.** Our old `ChartCrosshair` activates
   on a 200ms long-press with dashed gray lines and a tooltip fixed at the top
   of the container. Wagmi's `CursorCrosshair` is a pan-follow crosshair
   (finger moves → crosshair snaps to nearest data point immediately) with the
   tooltip floating above the cursor position. This is intentional — it's the
   Robinhood-style UX the research doc cites as the reason to migrate. But it
   IS a perceivable UX change. Coordinator should verify it matches the intent
   before greenlighting candle migration.
2. **Tooltip date formatting.** Wagmi's `LineChart.Tooltip` renders the child
   content — by default uses `LineChart.PriceText` if children are provided,
   or falls back to price-only. I left children off for the spike (only styled
   the tooltip text). If the date needs to appear, we add
   `<LineChart.DatetimeText />` inside `<LineChart.Tooltip>`. Trivial.
3. **Y-axis scaling.** Wagmi auto-scales Y to the `value` range. Our external
   `priceHigh`/`priceLow` labels are derived from bars' `high`/`low`, not
   `close` — so they can sit outside the chart's actual drawn range
   (identical to today's behavior). If we want the labels to hug the line,
   pass `yRange={{ min: priceLow, max: priceHigh }}` to `LineChart.Provider`.
   Deferred — match existing look first, tune later.
4. **X-axis height reservation.** Wagmi's internal `LineChart` reserves 40px
   at the bottom for x-axis labels. We pass `height={220}` (CHART_HEIGHT) and
   our external `ChartXAxis` sits below. There's no visible stacking issue
   because wagmi's reserved area is empty when we don't add
   `<LineChart.Axis />`. Confirmed by reading the Chart.tsx source.
5. **Haptic cadence.** `onCurrentIndexChange` fires on every index transition.
   On a 120-point sparkline, a fast pan triggers ~120 haptics in a swipe,
   which may feel buzzy. Easy fix if needed: debounce or gate on `isActive
   && idx > 0` edge. Not doing it for the spike — ship and feel it first.
6. **New deps are native modules.** `expo-haptics` is native. Fresh install
   means the next `eas build` needs to pick up the change. For EAS preview
   (JS-only), works today via the dev client that already has the module
   available IF a prebuild runs before the next preview. If the dev client
   hasn't been rebuilt since adding the module, haptics will no-op silently
   but the chart will still render.

## Open questions from research doc

The research doc had 3 "user answers" (already resolved): bleed scope, haptics
lib, crossfade scope. No spike-blocking unknowns surfaced during this work.

## If GO: phase 2 plan

1. **Candle swap.** Replace `StockCandleChart` internals with
   `CandlestickChart.Provider + CandlestickChart.Candles +
   CandlestickChart.Crosshair`. Delete hand-rolled Skia `renderCandles`
   function (~30 LOC). Data shape: `bars.map(b => ({ timestamp, open, high,
   low, close }))`.
2. **Delete `ChartCrosshair.tsx`** once both ticker charts migrate. Verify no
   other importers first.
3. **Wire tooltip content consistently** across both charts — same date
   formatting, same price font — via a shared `<ChartTooltipContent />`
   co-located in `components/chart/` if one is extracted; otherwise inline.
4. **Crossfade in `ChartSlot`** — Reanimated `withTiming` opacity on
   `chartType` change wrapping both children (not unmount/remount). ~30 LOC.
5. **1D session-bleed fix** — separate commit on `getStockAggs` (or wherever
   the REST adapter lives; need to locate). Normalize to current trading
   session window.

Total phase-2 budget estimate: 2–3h if candle swap goes like line did. Add
~1h each for crossfade and 1D fix.

## Files touched in spike

- `package.json`
- `pnpm-lock.yaml`
- `src/features/stock-detail/presentation/components/StockLineChart.tsx`
