From 383e2e07bd33d4afef555866442064e383ca41e8 Mon Sep 17 00:00:00 2001 From: HRiggs Date: Thu, 4 Dec 2025 17:41:43 -0500 Subject: [PATCH] Pierre smells like cheese --- electron-app/electron-builder.yml | 2 + electron-app/package.json | 2 +- electron-app/src/main/index.ts | 6 ++ electron-app/src/renderer/App.tsx | 96 +++++++++++++++++-- .../src/renderer/components/TitleBar.tsx | 3 + 5 files changed, 98 insertions(+), 11 deletions(-) diff --git a/electron-app/electron-builder.yml b/electron-app/electron-builder.yml index 7e82874..1ec1d67 100644 --- a/electron-app/electron-builder.yml +++ b/electron-app/electron-builder.yml @@ -9,6 +9,8 @@ files: extraResources: - from: .env to: .env + - from: resources/icons + to: icons win: target: - portable diff --git a/electron-app/package.json b/electron-app/package.json index c07e75b..5a466cf 100644 --- a/electron-app/package.json +++ b/electron-app/package.json @@ -1,6 +1,6 @@ { "name": "rmtpocketwatcher", - "version": "1.0.4", + "version": "1.0.5", "description": "Real-time AUEC price tracking desktop application", "type": "module", "main": "dist/main/index.js", diff --git a/electron-app/src/main/index.ts b/electron-app/src/main/index.ts index c0840a8..61a61c3 100644 --- a/electron-app/src/main/index.ts +++ b/electron-app/src/main/index.ts @@ -9,6 +9,12 @@ import { initDatabase, closeDatabase } from './database.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); +// Set app name for notifications (must be before app ready) +app.setName('rmtPocketWatcher'); +if (process.platform === 'win32') { + app.setAppUserModelId('com.lambdabanking.rmtpocketwatcher'); +} + // Load environment variables from .env file // In dev: __dirname = dist/main, so go up to electron-app root // In prod: __dirname = resources/app.asar/dist/main, .env should be in resources diff --git a/electron-app/src/renderer/App.tsx b/electron-app/src/renderer/App.tsx index bf6bcef..d3dce37 100644 --- a/electron-app/src/renderer/App.tsx +++ b/electron-app/src/renderer/App.tsx @@ -44,6 +44,8 @@ export function App() { const [selectedRange, setSelectedRange] = useState('7d'); const [zoomState, setZoomState] = useState(null); const chartContainerRef = useRef(null); + const [animateChart, setAnimateChart] = useState(true); + const [hoveredSeller, setHoveredSeller] = useState(null); // Price Alert State const [alerts, setAlerts] = useState([]); @@ -262,6 +264,7 @@ export function App() { const handleWheel = (e: WheelEvent) => { e.preventDefault(); e.stopPropagation(); + setAnimateChart(false); const isZoomIn = e.deltaY < 0; const zoomFactor = isZoomIn ? 0.8 : 1.25; // Zoom in = smaller range, zoom out = larger range @@ -334,11 +337,13 @@ export function App() { }, [fullChartData, zoomState, yAxisDomain]); const handleRangeChange = (range: string) => { + setAnimateChart(true); setSelectedRange(range); setZoomState(null); }; const resetZoom = () => { + setAnimateChart(false); setZoomState(null); }; @@ -415,6 +420,7 @@ export function App() { const handleZoomIn = () => { if (!fullChartData.length) return; + setAnimateChart(false); // Keep current X range, only adjust Y max const currentXStart = zoomState?.xStart ?? fullChartData[0].timestamp; @@ -434,6 +440,7 @@ export function App() { const handleZoomOut = () => { if (!fullChartData.length) return; + setAnimateChart(false); // Keep current X range, only adjust Y max const currentXStart = zoomState?.xStart ?? fullChartData[0].timestamp; @@ -453,6 +460,7 @@ export function App() { const handleTimelineCompress = () => { if (!fullChartData.length) return; + setAnimateChart(false); const currentXStart = zoomState?.xStart ?? fullChartData[0].timestamp; const currentXEnd = zoomState?.xEnd ?? fullChartData[fullChartData.length - 1].timestamp; @@ -491,6 +499,7 @@ export function App() { const handleTimelineExpand = () => { if (!fullChartData.length) return; + setAnimateChart(false); const currentXStart = zoomState?.xStart ?? fullChartData[0].timestamp; const currentXEnd = zoomState?.xEnd ?? fullChartData[fullChartData.length - 1].timestamp; @@ -542,6 +551,7 @@ export function App() { const handleSliderChange = (e: React.ChangeEvent) => { if (!fullChartData.length) return; + setAnimateChart(false); const position = parseFloat(e.target.value); const dataXStart = fullChartData[0].timestamp; @@ -892,16 +902,60 @@ export function App() { domain={yAxisDomain} allowDataOverflow={true} /> - { - if (payload && payload.length > 0 && payload[0].payload.fullTime) { - return payload[0].payload.fullTime; + { + if (!active || !payload || payload.length === 0) return null; + const data = payload[0]?.payload; + if (!data) return null; + const seenNames = new Set(); + const validSellers = payload + .filter((p: any) => { + if (p.value === undefined || p.value === null || isNaN(p.value)) return false; + if (!p.name || String(p.name).trim() === '') return false; + if (p.stroke === 'transparent') return false; + // Deduplicate by name + if (seenNames.has(p.name)) return false; + seenNames.add(p.name); + return true; + }) + .sort((a: any, b: any) => a.value - b.value); + if (validSellers.length === 0) return null; + + // If hovering over a specific line, show that seller and others with same price + let displaySellers = validSellers; + if (hoveredSeller) { + const hoveredData = validSellers.find((s: any) => s.name === hoveredSeller); + if (hoveredData) { + const hoveredPrice = hoveredData.value; + // Show hovered seller and any with same price (within 0.0001 tolerance) + displaySellers = validSellers.filter((s: any) => + s.name === hoveredSeller || Math.abs(s.value - hoveredPrice) < 0.0001 + ); + } } - return label; + + return ( +
+
+ {data.fullTime || data.time} +
+ {!hoveredSeller && ( +
+ {validSellers.length} seller{validSellers.length !== 1 ? "s" : ""} - Sorted by price +
+ )} + {displaySellers.slice(0, hoveredSeller ? 20 : 10).map((seller: any, idx: number) => ( +
+ {seller.name} + ${Number(seller.value).toFixed(4)} +
+ ))} + {!hoveredSeller && validSellers.length > 10 && ( +
+{validSellers.length - 10} more sellers
+ )} +
+ ); }} - formatter={(value: any, name: string) => [`$${Number(value).toFixed(9)}`, name]} wrapperStyle={{ zIndex: 1000 }} /> + {/* Invisible wider lines for better hover detection */} + {sellers.map((seller) => ( + setHoveredSeller(seller)} + onMouseLeave={() => setHoveredSeller(null)} + style={{ cursor: 'pointer' }} + legendType="none" + /> + ))} + {/* Visible lines */} {sellers.map((seller) => ( ))} diff --git a/electron-app/src/renderer/components/TitleBar.tsx b/electron-app/src/renderer/components/TitleBar.tsx index 17fbcf9..7827781 100644 --- a/electron-app/src/renderer/components/TitleBar.tsx +++ b/electron-app/src/renderer/components/TitleBar.tsx @@ -56,6 +56,9 @@ export function TitleBar() { WebkitAppRegion: 'drag', userSelect: 'none', padding: '0 15px', + position: 'sticky', + top: 0, + zIndex: 1000, } as any} >