* Add oxlint and fix all lint errors
- Install oxlint, add .oxlintrc.json with rules for TypeScript/React
- Add npm run lint / lint:fix scripts
- Add Claude PostToolUse hook to run oxlint on every edited file
- Fix 101 errors: unused vars/imports, eqeqeq, prefer-const, no-new-array
- Fix no-array-index-key (use stable keys or suppress positional cases)
- Fix exhaustive-deps missing dependency in useEffect
- Promote exhaustive-deps and no-array-index-key to errors
- Fix Map.get() !== null bug in $leagueId.server.ts (should be !== undefined)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix no-explicit-any warnings and upgrade tsconfig to ES2023
- Replace all `any` types with proper types or `unknown` across ~20 files
- Add typed socket payload interfaces in draft route and useDraftSocket
- Use any[] with eslint-disable for socket.io callbacks (legitimate escape hatch)
- Bump all tsconfigs from ES2022 → ES2023 to support toSorted/toReversed
- Fix cascading type errors uncovered by removing any: Map.get narrowing,
participant relation types, ChartDataPoint, Partial<NewSeason> indexing
- Add ParticipantResultWithParticipant type to participant-result model
- Fix test fixtures to match updated interfaces (DraftCell, ParticipantResult)
- Fix duplicate getQPStandings import in sportsSeasonId.server.ts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Promote no-explicit-any to error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Primary fix: setTeamTimers now bails out with `return prev` when the
value hasn't changed, preventing a full DraftRoom re-render on every
1-second timer tick (was 33% of profiler samples).
Memoization: wrap AvailableParticipantsSection, TeamsDraftedGrid,
QueueSection, SidebarRecentPicks, and DraftGridSection in React.memo
so timer ticks don't cascade into heavy components that don't use
timer state.
Stable refs: wrap nine action handlers in useCallback and extract two
inline arrow functions from props objects so memo() comparisons
actually bail out. Memoize the { numFlexPicks } object passed to
TeamsDraftedGrid.
socketVersion: expose an incrementing counter from useDraftSocket so
the socket handler effect re-registers on socket recreation.
Async cleanup: add abort flag + in-flight guard to the visibilitychange
JWT refresh handler to prevent concurrent executions and stale state
updates after unmount. Add abort flag to useDraftNotifications
permissions.query() to prevent dangling onchange if unmounted
mid-promise.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix draft state not updating when returning from backgrounded mobile app
Mobile browsers suspend JavaScript and silently drop WebSocket connections
when the user switches to another app, without firing "offline"/"online"
events. Add a visibilitychange handler so that when the user returns:
- If the socket is disconnected, reconnect immediately (skipping backoff)
- If the socket appears connected but JS was suspended, rejoin the draft
room and trigger a loader revalidation to catch any missed picks/state
https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt
* Simplify reconnect handlers and fix connect_error overlay flicker
- Merge handleOnline and the shared branch of handleVisibilityChange into a
single handleReturn function. visibilitychange is now a thin guard that calls
it only on show. Both events share the same logic: reconnect if socket dropped,
or restore UI state + rejoin room + revalidate if the socket survived.
- Remove setIsReconnecting(false) from connect_error: reconnect_attempt fires
immediately after and resets it to true anyway, causing the "Reconnecting"
overlay to flicker off and back on during every retry cycle.
https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt
* Fix four issues from useDraftSocket code review
- Manager listener leak: add socket.io.off() for reconnect_attempt and
reconnect_failed in cleanup — socket.disconnect() only tears down the
socket, not the Manager listeners, causing them to accumulate on re-mounts.
- reconnect_failed dead code: add reconnectionAttempts: 10 to io() config
so the handler is actually reachable after exhausting retries.
- connectionError flicker: remove setConnectionError from connect_error —
reconnect_attempt fires immediately after and clears it anyway, causing the
error overlay to flash on every retry cycle. Error now only appears via
reconnect_failed once all attempts are exhausted. connect_error instead
ensures setIsReconnecting(true) so the reconnecting overlay shows instead
of the initial "Connecting to Draft" spinner.
- Add comment to on/off/emit noting they are no-ops if called before the
effect runs (socketRef.current === null).
https://claude.ai/code/session_016tCZVFjSeHdQsdKktbDHEt
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Reduce ping detection window from ~45s to ~15s (pingTimeout 5s, pingInterval 10s)
- Listen to window offline/online events for instant UI feedback on network drop
- Force immediate reconnect on online event instead of waiting for backoff
- Fix stuck-reconnecting bug when network returns within ping timeout window
- Wrap on/off/emit in useCallback to prevent listener re-registration every second
- Expose reconnectCount to trigger revalidator.revalidate() after each reconnect
- Reset hasConnectedOnce ref per socket instance to avoid spurious revalidations
- Remove stale socket ref from hook return value; add emit helper instead
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>