({ state, onClose }: TradeModalProps)
| 212 | } |
| 213 | |
| 214 | function TradeModalContent({ state, onClose }: TradeModalProps) { |
| 215 | const { t } = useI18n(); |
| 216 | const hasYesNo = !!(state.yesToken && state.noToken); |
| 217 | |
| 218 | const [activeTokenId, setActiveTokenId] = useState(state.tokenId); |
| 219 | const [activePrice, setActivePrice] = useState(state.currentPrice); |
| 220 | const [activeName, setActiveName] = useState(state.outcomeName); |
| 221 | const [orderPlaced, setOrderPlaced] = useState(false); |
| 222 | const [recentOrders, setRecentOrders] = useState<RecentOrder[]>(() => loadRecentOrders()); |
| 223 | const [activeOpenOrders, setActiveOpenOrders] = useState<OpenOrder[]>([]); |
| 224 | const [cancellingOrderId, setCancellingOrderId] = useState<string | null>(null); |
| 225 | |
| 226 | // Read share balances for Yes/No tokens |
| 227 | const CTF_ADDRESS = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045" as const; |
| 228 | const CTF_BALANCE_ABI = [{ name: "balanceOf", type: "function", stateMutability: "view", inputs: [{ name: "account", type: "address" }, { name: "id", type: "uint256" }], outputs: [{ name: "", type: "uint256" }] }] as const; |
| 229 | const { proxyAddress, address, isConnected, tradeSession } = useWalletStore(); |
| 230 | const balanceTarget = (proxyAddress ?? address) as `0x${string}` | undefined; |
| 231 | |
| 232 | const handleKey = useCallback( |
| 233 | (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }, |
| 234 | [onClose] |
| 235 | ); |
| 236 | useEffect(() => { |
| 237 | document.addEventListener("keydown", handleKey); |
| 238 | return () => document.removeEventListener("keydown", handleKey); |
| 239 | }, [handleKey]); |
| 240 | |
| 241 | const [isMobile, setIsMobile] = useState(false); |
| 242 | useEffect(() => { setIsMobile(window.innerWidth <= 768); }, []); |
| 243 | |
| 244 | // On mobile, scroll focused inputs into view when virtual keyboard opens |
| 245 | const modalRef = useRef<HTMLDivElement>(null); |
| 246 | useEffect(() => { |
| 247 | const el = modalRef.current; |
| 248 | if (!el) return; |
| 249 | const handleFocusIn = (e: FocusEvent) => { |
| 250 | if (window.innerWidth > 500) return; // desktop — no virtual keyboard |
| 251 | const target = e.target; |
| 252 | if (target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement) { |
| 253 | setTimeout(() => { |
| 254 | // Only scroll if keyboard actually shrunk the viewport |
| 255 | const vv = window.visualViewport; |
| 256 | if (vv && vv.height < window.innerHeight * 0.85) { |
| 257 | target.scrollIntoView({ behavior: "smooth", block: "center" }); |
| 258 | } |
| 259 | }, 300); // wait for keyboard animation |
| 260 | } |
| 261 | }; |
| 262 | el.addEventListener("focusin", handleFocusIn); |
| 263 | return () => el.removeEventListener("focusin", handleFocusIn); |
| 264 | }, []); |
| 265 | |
| 266 | const handleSuccess = useCallback((info: { side: string; amount: number; price: number }) => { |
| 267 | setOrderPlaced(true); |
| 268 | const newOrder: RecentOrder = { |
| 269 | id: `${Date.now()}-${Math.random().toString(36).slice(2, 6)}`, |
| 270 | side: info.side, |
| 271 | outcome: activeName, |
nothing calls this directly
no test coverage detected