From e43c98997b0de6a8da989f68d97f1d4f7fb7bb6f Mon Sep 17 00:00:00 2001 From: kboz Date: Fri, 24 Apr 2026 00:55:26 +0200 Subject: feat(core): implement wallet trade tracking and sqlite market caching - Transitioned primary ESI networking and dataset initialization to `asyncio` and `aiohttp` for non-blocking I/O. - Introduced `aiosqlite` utilizing an in-memory database to significantly speed up arbitrage querying. - Implemented character wallet tracking (added `esi-wallet.read_character_wallet.v1` scope) to automatically deduct filled market volumes internally instead of waiting for full region polling. - Exposed `/settings` endpoint allowing dynamic tweaking of threshold variables (cargo size, limits, tax rate, ship speeds) directly from the React UI. - Decoupled auto-waypoint submission during route planning; users now push routes selectively via a new "Set in Game" frontend action and `/set_waypoints` backend logic. - Optimized order evaluations and triplet generation limits through `LegMetrics` to lower CPU footprint drastically on large dataset passes. - Migrated React frontend completely to Server-Sent Events (SSE), cleanly ditching HTTP polling. --- frontend/src/Options.js | 170 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 157 insertions(+), 13 deletions(-) (limited to 'frontend/src/Options.js') diff --git a/frontend/src/Options.js b/frontend/src/Options.js index 4c8a7e9..b490f67 100644 --- a/frontend/src/Options.js +++ b/frontend/src/Options.js @@ -1,29 +1,173 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import './Options.css'; +import useSSE from './useSSE'; const Options = () => { const [message, setMessage] = useState(''); + const [messageType, setMessageType] = useState('info'); + const [isSettingWaypoints, setIsSettingWaypoints] = useState(false); + const [settings, setSettings] = useState({ + cargo: '', + max_warp_speed: '', + max_subwarp_speed: '', + risk_cost: '', + ship_cost: '', + tax_rate: '', + profit_threshold: '', + max_capital: '', + snipe_profit_threshold: '' + }); + const { data: sseData } = useSSE('http://localhost:8000/data_stream'); + const route = Array.isArray(sseData?.route) ? sseData.route : []; + const hasRouteWaypoints = route.length > 1; - const handleClick = async () => { - setMessage('Sending request...'); + useEffect(() => { + fetch('http://localhost:8000/settings') + .then(res => res.json()) + .then(data => { + setSettings({ + cargo: data.cargo, + max_warp_speed: data.max_warp_speed, + max_subwarp_speed: data.max_subwarp_speed, + risk_cost: data.risk_cost, + ship_cost: data.ship_cost, + tax_rate: data.tax_rate, + profit_threshold: data.profit_threshold, + max_capital: data.max_capital, + snipe_profit_threshold: data.snipe_profit_threshold + }); + }) + .catch(err => console.error("Error fetching settings", err)); + }, []); + + const handleSettingsChange = (e) => { + const { name, value } = e.target; + setSettings(prev => ({ ...prev, [name]: value })); + }; + + const handleSaveSettings = async () => { try { - const response = await fetch('http://localhost:8000/create_route', { method: 'POST' }); - const data = await response.json(); - setMessage(`Response received: ${JSON.stringify(data)}`); + const response = await fetch('http://localhost:8000/settings', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + cargo: Number(settings.cargo), + max_warp_speed: Number(settings.max_warp_speed), + max_subwarp_speed: Number(settings.max_subwarp_speed), + risk_cost: Number(settings.risk_cost), + ship_cost: Number(settings.ship_cost), + tax_rate: Number(settings.tax_rate), + profit_threshold: Number(settings.profit_threshold), + max_capital: Number(settings.max_capital), + snipe_profit_threshold: Number(settings.snipe_profit_threshold) + }) + }); + if (response.ok) { + setMessageType('success'); + setMessage('Settings saved'); + setTimeout(() => setMessage(''), 3000); + } + } catch (error) { + setMessageType('error'); + setMessage(`Error: ${error.message}`); + } + }; + + const handleCreateRoute = async () => { + try { + await fetch('http://localhost:8000/create_route', { method: 'POST' }); } catch (error) { setMessage(`Error: ${error}`); } }; + const handleSetWaypoints = async () => { + if (!hasRouteWaypoints) { + setMessageType('error'); + setMessage('No route exists'); + return; + } + + setIsSettingWaypoints(true); + setMessageType('info'); + setMessage('Sending to EVE...'); + try { + const response = await fetch('http://localhost:8000/set_waypoints', { method: 'POST' }); + const resData = await response.json(); + const nextMessage = resData.message || resData.status || 'Waypoint request completed'; + const nextType = resData.success ? 'success' : resData.status === 'partial' ? 'warning' : 'error'; + setMessageType(nextType); + setMessage(nextMessage); + if (resData.success) { + setTimeout(() => setMessage(''), 4000); + } + } catch (error) { + setMessageType('error'); + setMessage(`Error: ${error}`); + } finally { + setIsSettingWaypoints(false); + } + }; + + const messageClassName = messageType === 'error' + ? 'mb-1 text-red-400' + : messageType === 'warning' + ? 'mb-1 text-yellow-300' + : 'mb-1 text-green-400'; + + const waypointButtonClassName = hasRouteWaypoints && !isSettingWaypoints + ? 'options-button flex-1 px-2 py-1 text-sm bg-green-700 hover:bg-green-600 text-white rounded' + : 'options-button flex-1 px-2 py-1 text-sm bg-green-900 text-white rounded opacity-50 cursor-not-allowed'; + return ( -
- + +
+ + {message &&

{message}

} + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + -

{message}

); } -- cgit v1.3