whisper-money/resources/js/hooks
Víctor Falcón 6a0c00dce8
refactor(landing): extract the scroll-progress hook every preview duplicated (#768)
## Why

`welcome.tsx` was the single biggest source of duplication in the repo:
**8 clone pairs, ~200 duplicated lines**, all of them the same thing
copied nine times.

Every scroll-animated preview on the landing page carried its own copy
of:

```tsx
const [scrollProgress, setScrollProgress] = useState(0);
const containerRef = useRef<HTMLDivElement | null>(null);

useEffect(() => {
    const updateProgress = () => { /* measure, clamp 0..1, setState */ };
    updateProgress();
    window.addEventListener('scroll', updateProgress, { passive: true });
    window.addEventListener('resize', updateProgress);
    return () => { /* remove both */ };
}, []);
```

Two of them also duplicated a second effect that measures how far a list
can travel before its last row clears the container.

## What changed

New `resources/js/hooks/use-scroll-progress.ts`:

- **`useScrollProgress()`** — returns `{ ref, progress }`, where
progress goes 0 → 1 as the element crosses the viewport. Nine call
sites.
- **`useScrollTranslate({ speed, padding })`** — the self-scrolling
lists (banks, transactions): adds the travel measurement and returns
`translateY` derived from progress instead of keeping it in a second
piece of state. Two call sites.

`welcome.tsx` goes from 2912 to 2647 lines. No visual or behavioural
change intended: same listeners, same passive flag, same clamping, same
values.

## Metrics

| | before | after |
|---|---|---|
| duplicated lines (tsx) | 5.82% | 5.45% |
| duplicated lines (total) | 5.34% | 5.13% |
| clones | 322 | 313 |
| clones in `welcome.tsx` | 8 | 0 |

## Testing

**Unit** — `use-scroll-progress.test.ts` pins the clamped progress at
three positions (below the fold → 0, passed above → 1, mid-viewport →
the expected ratio). Nothing covered this before.

**Browser** — loaded the landing locally and scrolled every preview into
view, asserting each one's inline styles actually change between "just
entering" and "in view". All nine still animate: the eight that move
`transform` / `opacity` / `top`, plus the cashflow chart, which animates
bar `height`. No JS console errors (only external gravatar/placeholder
404s, pre-existing).
2026-08-11 14:34:24 +02:00
..
use-admin.tsx feat: Add privacy mode to hide monetary amounts (#28) 2025-12-14 16:03:02 +01:00
use-appearance.tsx fix(ui): stop unusable web storage from white-screening the app (#743) 2026-08-09 15:33:22 +00:00
use-cashflow-data.ts feat: expand Sankey subcategories inline (#485) 2026-06-04 10:31:40 +02:00
use-categorize-transactions.ts refactor(encryption): strip client-side transaction encryption (#514) 2026-06-20 16:13:26 +00:00
use-chart-color-scheme.tsx fix(ui): stop unusable web storage from white-screening the app (#743) 2026-08-09 15:33:22 +00:00
use-chart-views.ts fix(dashboard): treat loans as debt in net worth (#238) 2026-03-20 09:55:53 +00:00
use-clipboard.ts Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
use-connect-flow.ts feat(banking): enable Interactive Brokers for all users (#593) 2026-06-26 11:03:21 +02:00
use-count-up.ts User Onboarding Flow (#23) 2025-12-12 13:06:08 +01:00
use-dashboard-data.test.ts feat(settings): centralize currency options and split profile/account support (#256) 2026-04-02 19:23:10 +02:00
use-dashboard-data.ts fix(dashboard): stop negative asset balances inflating net worth (#660) 2026-07-08 15:22:16 +00:00
use-decrypt-account-names.ts Remove encryption from bank account names (#104) 2026-02-09 14:15:26 +01:00
use-decrypt-transactions.test.tsx refactor(encryption): strip client-side transaction encryption (#514) 2026-06-20 16:13:26 +00:00
use-decrypt-transactions.ts fix: address remaining security audit findings (round 2) (#628) 2026-07-03 15:04:03 +00:00
use-expandable-categories.ts feat: expand parent categories inline in breakdowns (#486) 2026-06-04 11:19:21 +02:00
use-initials.tsx Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
use-locale.ts feat: Spanish localization (#74) 2026-02-08 11:58:08 +01:00
use-mobile-navigation.ts Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
use-mobile.tsx fix(appearance): support MediaQueryList change events on legacy Safari (PHP-LARAVEL-41) (#646) 2026-07-05 17:03:35 +00:00
use-onboarding-state.test.tsx feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
use-onboarding-state.ts feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
use-online-status.ts Fix CI: Update bun.lock and fix linting errors 2025-12-01 15:16:06 +01:00
use-poll-job-status.ts feat(ai): manage AI consent outside onboarding with live backfill (#591) 2026-06-25 10:50:35 +02:00
use-pwa-install.ts feat: Show PWA install button on mobile landing page (#99) 2026-02-07 09:33:12 +01:00
use-re-evaluate-all-transactions.tsx fix(security): scope job-status endpoints to owner + feature-area fixes (#627) 2026-07-03 14:49:32 +02:00
use-scroll-progress.test.ts refactor(landing): extract the scroll-progress hook every preview duplicated (#768) 2026-08-11 14:34:24 +02:00
use-scroll-progress.ts refactor(landing): extract the scroll-progress hook every preview duplicated (#768) 2026-08-11 14:34:24 +02:00
use-two-factor-auth.ts Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
use-web-haptics.test.tsx fix(haptics): use a local WebHaptics wrapper (#225) 2026-03-16 11:26:04 +00:00
use-web-haptics.ts fix(haptics): use a local WebHaptics wrapper (#225) 2026-03-16 11:26:04 +00:00