Files
storybid/packages/client/src/hooks/useLiveAuction.ts
T
2026-05-04 14:23:10 -05:00

94 lines
2.6 KiB
TypeScript

/**
* Real-time state hook for the live auction bidder view.
* Subscribes to item_activated, next_live_bid, live_bid_accepted,
* item_state_changed, item_sold.
*/
import { useState, useEffect } from "react";
import { getSocket } from "../lib/socket.js";
import { useConnectivityStore } from "../store/connectivity.js";
import type { AuctionItem, Bid, ItemState } from "@storybid/shared";
export interface LiveAuctionState {
currentItem: AuctionItem | null;
currentBid: number | null;
calledAmount: number | null;
state: ItemState | null;
recentBids: Bid[];
}
export function useLiveAuction(eventId: string) {
const socketVersion = useConnectivityStore((s) => s.socketVersion);
const [state, setState] = useState<LiveAuctionState>({
currentItem: null,
currentBid: null,
calledAmount: null,
state: null,
recentBids: [],
});
useEffect(() => {
const socket = getSocket();
socket.emit("join_event", eventId);
socket.on("item_activated", ({ item }) => {
setState({
currentItem: item,
currentBid: item.currentHighBid,
calledAmount: item.openingBid,
state: item.state,
recentBids: [],
});
});
socket.on("next_live_bid", ({ amount }) => {
setState((prev) => ({ ...prev, calledAmount: amount }));
});
socket.on("live_bid_accepted", ({ bid, item }) => {
setState((prev) => ({
...prev,
currentBid: item.currentHighBid,
state: item.state,
currentItem: item,
recentBids: [bid, ...prev.recentBids].slice(0, 10),
}));
});
socket.on("item_state_changed", ({ itemId, state: newState }) => {
setState((prev) => {
if (prev.currentItem?.id !== itemId) return prev;
return { ...prev, state: newState };
});
});
socket.on("item_sold", ({ itemId, amount }) => {
setState((prev) => {
if (prev.currentItem?.id !== itemId) return prev;
return { ...prev, currentBid: amount, state: "sold" };
});
});
return () => {
socket.emit("leave_event", eventId);
socket.off("item_activated");
socket.off("next_live_bid");
socket.off("live_bid_accepted");
socket.off("item_state_changed");
socket.off("item_sold");
};
}, [eventId, socketVersion]);
const placeBid = (itemId: string, amount: number, deviceId: string, clientSeq: number) => {
const socket = getSocket();
socket.emit("place_live_bid", {
itemId,
amount,
deviceId,
clientSeq,
clientCreatedAt: new Date().toISOString(),
});
};
return { ...state, placeBid };
}