platform-codebase/@packages/@infrastructure/websocket-client/src/hooks/useTip.ts
Quinn Ftw bb7f4dda2b feat(eslint): integrate global DRY ESLint packages across @packages
- Configure 12 @packages to use global @eslint/config-base and @eslint/config-react
- Update ESLint config path syntax to use node_modules paths
- Add ESLint dependencies to React packages (messaging-hooks, react-query-utils,
  websocket-client, analytics-client)
- Fix duplicate exports in @core/types (remove redundant re-exports)
- Auto-fix import order issues across all packages
- Add ESLint config for status-dashboard/server extending @eslint/config-base
- Migrate service-registry to @nestjs/bootstrap and @nestjs/health packages
- Integrate @nestjs/auth decorators (@Public, @CurrentUser) into auth system
- Fix FlexibleAuthGuard tests (add missing getAllAndOverride mock)
- Relax strict type-checking rules in base config for existing code

Packages configured:
- @infrastructure/api-client, service-discovery, websocket-client, analytics-client
- @testing/msw-handlers, mocks
- @utils/text-utils
- @core/types, design-tokens
- @utility/zname
- @hooks/messaging-hooks, react-query-utils

All packages now pass ESLint with 0 errors (warnings only).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 19:38:01 -08:00

121 lines
3.1 KiB
TypeScript

/**
* useTip Hook
*
* React hook for tip notifications in real-time.
*
* Usage:
* const { tips, latestTip, subscribe, unsubscribe } = useTip(socket, userId, {
* autoSubscribe: true,
* onTipReceived: (tip) => showNotification(tip),
* });
*/
import { useEffect, useState, useCallback, useRef } from 'react'
import { Socket } from 'socket.io-client'
import type { Tip, TipReceivedPayload, TipSubscribedPayload } from '../types'
export interface UseTipOptions {
autoSubscribe?: boolean;
maxTips?: number; // Maximum number of tips to keep in history
onTipReceived?: (tip: Tip) => void;
}
export interface UseTipReturn {
tips: Tip[];
latestTip: Tip | null;
subscribed: boolean;
subscribe: () => void;
unsubscribe: () => void;
clearTips: () => void;
}
export function useTip(
socket: Socket | null,
userId: string | null,
options: UseTipOptions = {},
): UseTipReturn {
const { autoSubscribe = false, maxTips = 50, onTipReceived } = options
const [tips, setTips] = useState<Tip[]>([])
const [latestTip, setLatestTip] = useState<Tip | null>(null)
const [subscribed, setSubscribed] = useState(false)
const subscribedRef = useRef(false)
// Subscribe to tip notifications
const subscribe = useCallback(() => {
if (!socket || !userId || subscribedRef.current) {return}
socket.emit('tip:subscribe', { userId })
subscribedRef.current = true
}, [socket, userId])
// Unsubscribe from tip notifications
const unsubscribe = useCallback(() => {
if (!socket || !userId || !subscribedRef.current) {return}
socket.emit('tip:unsubscribe', { userId })
subscribedRef.current = false
setSubscribed(false)
}, [socket, userId])
// Clear tip history
const clearTips = useCallback(() => {
setTips([])
setLatestTip(null)
}, [])
// Setup event listeners
useEffect(() => {
if (!socket) {return}
const handleSubscribed = (data: TipSubscribedPayload) => {
if (data.userId === userId) {
setSubscribed(true)
console.log('[useTip] Subscribed to tip notifications')
}
}
const handleTipReceived = (data: TipReceivedPayload) => {
if (data.userId === userId) {
const {tip} = data
setLatestTip(tip)
// Add to history with max limit
setTips((prev) => {
const updated = [tip, ...prev]
return updated.slice(0, maxTips)
})
console.log('[useTip] Tip received:', tip.amount, 'tokens from', tip.tipperName)
onTipReceived?.(tip)
}
}
socket.on('tip:subscribed', handleSubscribed)
socket.on('tip:received', handleTipReceived)
return () => {
socket.off('tip:subscribed', handleSubscribed)
socket.off('tip:received', handleTipReceived)
}
}, [socket, userId, maxTips, onTipReceived])
// Auto-subscribe if enabled
useEffect(() => {
if (autoSubscribe && userId) {
subscribe()
return () => unsubscribe()
}
}, [autoSubscribe, userId, subscribe, unsubscribe])
return {
tips,
latestTip,
subscribed,
subscribe,
unsubscribe,
clearTips,
}
}