From 7ca504a70adab5cf8a01427671f9735b395b84a2 Mon Sep 17 00:00:00 2001 From: Alberto Balbo Date: Tue, 4 Aug 2026 21:49:53 +0200 Subject: [PATCH] Add utility classes for theme management, waiting, watched products, and notifications - Implement ThemeManager for dynamic light/dark theme switching in the application. - Create Wait class for cancellable delays without exceptions for smoother user experience. - Introduce WatchedProductsStore to manage and persist watched products in JSON format. - Add WindowsNotifier for system notifications to inform users of important events. - Develop ProductViewModel to encapsulate product data and manage UI interactions effectively. --- Mimante/.vscode/launch.json | 23 + Mimante/.vscode/tasks.json | 134 + Mimante/App.xaml | 29 +- Mimante/App.xaml.cs | 72 +- Mimante/AutoBidder.csproj | 45 +- Mimante/AutoBidder.sln | 35 +- Mimante/Controls/AuctionMonitorControl.xaml | 1624 +- .../Controls/AuctionMonitorControl.xaml.cs | 190 +- Mimante/Controls/BrowserControl.xaml | 375 +- Mimante/Controls/BrowserControl.xaml.cs | 169 +- Mimante/Controls/ExportControl.xaml | 241 + Mimante/Controls/ExportControl.xaml.cs | 192 + Mimante/Controls/FreeBidsControl.xaml | 365 + Mimante/Controls/FreeBidsControl.xaml.cs | 352 + Mimante/Controls/HelpHeader.xaml | 42 + Mimante/Controls/HelpHeader.xaml.cs | 58 + Mimante/Controls/ProductsControl.xaml | 448 + Mimante/Controls/ProductsControl.xaml.cs | 227 + Mimante/Controls/SettingsControl.xaml | 875 +- Mimante/Controls/SettingsControl.xaml.cs | 271 +- Mimante/Controls/SimpleToolbar.xaml.cs | 2 +- Mimante/Controls/StatisticsControl.xaml | 135 - Mimante/Controls/StatisticsControl.xaml.cs | 31 - .../MainWindow.EventHandlers.Export.cs | 351 - .../MainWindow.EventHandlers.Settings.cs | 398 +- .../MainWindow.EventHandlers.Stats.cs | 397 +- Mimante/Core/MainWindow.AuctionManagement.cs | 140 +- Mimante/Core/MainWindow.ButtonHandlers.cs | 81 +- Mimante/Core/MainWindow.Catalog.cs | 405 + Mimante/Core/MainWindow.Commands.cs | 28 +- Mimante/Core/MainWindow.ConnectionHandlers.cs | 104 +- Mimante/Core/MainWindow.ControlEvents.cs | 127 +- Mimante/Core/MainWindow.DataSettings.cs | 257 + Mimante/Core/MainWindow.Export.cs | 96 + Mimante/Core/MainWindow.FreeBids.cs | 420 + Mimante/Core/MainWindow.Logging.cs | 64 +- Mimante/Core/MainWindow.MonitorHeader.cs | 189 + Mimante/Core/MainWindow.ProductWatch.cs | 124 + Mimante/Core/MainWindow.Products.cs | 388 + Mimante/Core/MainWindow.Shutdown.cs | 64 + Mimante/Core/MainWindow.Statistics.cs | 201 + Mimante/Core/MainWindow.UIUpdates.cs | 102 +- Mimante/Core/MainWindow.UserInfo.cs | 272 +- Mimante/Core/MainWindow.WebView.cs | 39 +- Mimante/Data/StatisticsContext.cs | 23 - Mimante/Dialogs/AddAuctionSimpleDialog.xaml | 15 +- Mimante/Dialogs/ClosedAuctionsWindow.xaml | 89 - Mimante/Dialogs/ClosedAuctionsWindow.xaml.cs | 140 - Mimante/Dialogs/SessionDialog.xaml | 17 +- Mimante/Dialogs/StatsCleanupDialog.xaml | 84 + Mimante/Dialogs/StatsCleanupDialog.xaml.cs | 114 + .../Documentation/ARCHITECTURE_OVERVIEW.md | 357 - Mimante/Documentation/CHANGELOG.md | 344 - .../DEBUG_COOKIE_DETECTION_LOGGING.md | 261 - .../Documentation/DIAGNOSTICA_DATI_UTENTE.md | 148 - .../FEATURE_AUCTION_BUTTONS_REORGANIZED.md | 437 - .../FEATURE_AUCTION_NAVIGATION_REORDER.md | 340 - .../FEATURE_AUTO_FOCUS_AFTER_DELETE.md | 341 - .../Documentation/FEATURE_BID_HISTORY_TAB.md | 515 - .../FEATURE_CONFIGURABLE_LOG_LIMITS.md | 410 - .../FEATURE_HTML_CACHE_SERVICE.md | 444 - .../FEATURE_INITIAL_AUCTION_STATE.md | 397 - .../Documentation/FEATURE_LOG_MAX_LINES.md | 368 - .../FEATURE_MINIMUM_BIDS_LIMIT.md | 469 - .../FEATURE_NUMERIC_INPUT_VALIDATION.md | 399 - ...ATURE_PRODUCT_INFO_AND_VALUE_CALCULATOR.md | 590 - .../FEATURE_PRODUCT_VALUE_CALCULATOR.md | 192 - ...E_WEBVIEW_PRELOAD_AND_COOKIE_EXTRACTION.md | 815 - .../Documentation/FIX_AUCTION_LOG_COLOR.md | 126 - .../FIX_BID_COUNT_FROM_SERVER.md | 388 - .../FIX_BID_HISTORY_PERSISTENCE.md | 387 - .../Documentation/FIX_BROWSER_URL_READONLY.md | 288 - .../FIX_CLIPBOARD_FALSE_ERROR.md | 282 - .../FIX_COOKIE_LOADING_USER_DATA.md | 398 - .../Documentation/FIX_COOKIE_PERSISTENCE.md | 404 - .../FIX_COOKIE_SESSION_WARMUP.md | 430 - .../FIX_CORRECT_FIELD_INDICES.md | 297 - .../FIX_DEFAULT_SETTINGS_PERSISTENCE.md | 327 - Mimante/Documentation/FIX_DELETE_KEY.md | 199 - .../Documentation/FIX_EMOJI_NOT_DISPLAYED.md | 298 - Mimante/Documentation/FIX_LOG_CLEANUP_UX.md | 519 - .../Documentation/FIX_PRODUCT_INFO_PARSING.md | 278 - .../FIX_RUNTIME_COOKIE_EVENTS.md | 485 - .../FIX_SETTINGS_SAVE_AND_LOGGING.md | 368 - .../FIX_SIDEBAR_VISIBILITY_WEBVIEW_INIT.md | 452 - .../Documentation/FIX_SINGLE_AUCTION_START.md | 234 - .../Documentation/FIX_SKIP_BID_IF_WINNER.md | 341 - .../FIX_TAB_SETTINGS_WEBVIEW_INIT.md | 380 - .../Documentation/FIX_UI_UPDATE_AFTER_BID.md | 425 - .../FIX_WEBVIEW_ALREADY_INITIALIZED.md | 296 - .../FIX_WEBVIEW_THREADING_ERROR.md | 653 - .../FIX_WEBVIEW_TIMEOUT_USERDATAFOLDER.md | 352 - .../FIX_WEBVIEW_VISIBILITY_REQUIREMENT.md | 378 - Mimante/Documentation/LOG_CLEANUP_FINAL.md | 334 - .../Documentation/PROJECT_REORGANIZATION.md | 363 - .../REFACTORING_BROWSER_ADDRESS_BAR.md | 563 - .../REFACTORING_COOKIE_DETECTION_TIMING.md | 802 - .../REFACTORING_EXECUTIVE_SUMMARY.md | 275 - .../REFACTORING_SESSION_SERVICE_COMPLETE.md | 488 - .../REFACTORING_SESSION_SERVICE_PROPOSAL.md | 592 - .../REFACTORING_SETTINGS_PERSISTENCE.md | 611 - Mimante/Documentation/REFACTORING_SUMMARY.md | 172 - .../UI_PRODUCT_VALUE_IMPLEMENTATION.md | 335 - .../XAML_REFACTORING_CHECKLIST.md | 304 - .../Documentation/XAML_REFACTORING_SUMMARY.md | 360 - Mimante/Engine/AuctionRunner.cs | 322 + Mimante/Engine/Backtest/BacktestReport.cs | 174 + Mimante/Engine/Backtest/BacktestRunner.cs | 320 + Mimante/Engine/Backtest/DossierReader.cs | 218 + Mimante/Engine/PrecisionWait.cs | 116 + Mimante/Engine/ServerClock.cs | 122 + Mimante/Examples/puntateaste.it.txt | 84233 ++++++++++++++++ Mimante/MainWindow.xaml | 442 +- Mimante/MainWindow.xaml.cs | 143 +- Mimante/Models/AuctionDetailRecord.cs | 128 + Mimante/Models/AuctionInfo.cs | 706 +- Mimante/Models/AuctionLogEntry.cs | 126 + Mimante/Models/AuctionState.cs | 47 +- Mimante/Models/AuctionStatistics.cs | 128 - Mimante/Models/BidHistoryEntry.cs | 4 +- Mimante/Models/BidderInfo.cs | 30 +- Mimante/Models/CatalogModels.cs | 160 + Mimante/Models/CompletedAuctionRecord.cs | 95 + Mimante/Models/FreeBidsModels.cs | 56 + Mimante/Models/FreeBidsSiteConfig.cs | 463 + Mimante/Models/ProductInsights.cs | 232 - Mimante/Models/ProductStat.cs | 21 - Mimante/Models/PromoLinkModels.cs | 45 + Mimante/Models/WatchedProduct.cs | 134 + Mimante/Net/BidooHttpClient.cs | 350 + Mimante/Services/AuctionMonitor.cs | 1209 +- Mimante/Services/BidStrategyService.cs | 567 + Mimante/Services/BidooApiClient.cs | 559 +- Mimante/Services/BidooCatalogClient.cs | 393 + Mimante/Services/BidooFreeBidsClaimer.cs | 285 + Mimante/Services/BidooPromoRedeemer.cs | 219 + Mimante/Services/BidooResponseParser.cs | 232 + Mimante/Services/CatalogPageParser.cs | 305 + Mimante/Services/ClosedAuctionsScraper.cs | 370 - Mimante/Services/FreeBidsAutoClaimService.cs | 277 + Mimante/Services/FreeBidsPageParser.cs | 267 + Mimante/Services/FreeBidsRequestFactory.cs | 200 + Mimante/Services/FreeBidsResponseValidator.cs | 192 + Mimante/Services/HtmlCacheService.cs | 41 +- Mimante/Services/IFreeBidsClaimer.cs | 34 + Mimante/Services/IPromoLinkRedeemer.cs | 25 + Mimante/Services/ProductWatchService.cs | 240 + Mimante/Services/PromoLinkParser.cs | 191 + Mimante/Services/SessionManager.cs | 185 +- Mimante/Services/Settings.cs | 11 + Mimante/Services/StatsService.cs | 109 - Mimante/Services/WinnerBidsBackfill.cs | 131 + Mimante/Tests/AuctionDossierTests.cs | 232 + Mimante/Tests/AutoBidder.Tests.csproj | 24 + Mimante/Tests/BacktestTests.cs | 284 + Mimante/Tests/BidBudgetTests.cs | 312 + Mimante/Tests/BidLeadStatsTests.cs | 223 + Mimante/Tests/BidooResponseParserTests.cs | 319 + Mimante/Tests/CatalogPageParserTests.cs | 224 + Mimante/Tests/ExportAndLogTests.cs | 311 + Mimante/Tests/FreeBidsTests.cs | 796 + Mimante/Tests/PriceAdvisorTests.cs | 277 + Mimante/Tests/ProductKeyHelperTests.cs | 88 + Mimante/Tests/ProductRuleTests.cs | 332 + Mimante/Tests/RealDossierBacktest.cs | 109 + Mimante/Tests/ServerClockTests.cs | 200 + Mimante/Tests/TestEnvironment.cs | 29 + Mimante/Themes/Controls.xaml | 837 + Mimante/Themes/Tokens.Dark.xaml | 88 + Mimante/Themes/Tokens.Light.xaml | 87 + Mimante/Utilities/AppInfo.cs | 40 + Mimante/Utilities/AppPaths.cs | 197 + Mimante/Utilities/AuctionDetailStore.cs | 102 + Mimante/Utilities/AuctionDossier.cs | 493 + Mimante/Utilities/AuctionExporter.cs | 356 + Mimante/Utilities/AuctionIntegrity.cs | 67 + Mimante/Utilities/BidBudget.cs | 122 + Mimante/Utilities/BidLeadStats.cs | 247 + Mimante/Utilities/ClaimedPromoStore.cs | 158 + Mimante/Utilities/CompletedAuctionsStore.cs | 472 + Mimante/Utilities/CsvExporter.cs | 130 - Mimante/Utilities/ExportPreferences.cs | 41 - Mimante/Utilities/FileLogWriter.cs | 185 + Mimante/Utilities/FreeBidsConfigStore.cs | 189 + Mimante/Utilities/FreeBidsStats.cs | 179 + Mimante/Utilities/LogLevel.cs | 82 +- Mimante/Utilities/PersistenceManager.cs | 12 +- Mimante/Utilities/PriceAdvisor.cs | 334 + Mimante/Utilities/ProductKeyHelper.cs | 51 + Mimante/Utilities/ProductRuleResolver.cs | 75 + Mimante/Utilities/ProductStatsStore.cs | 334 + Mimante/Utilities/ProductValueCalculator.cs | 32 +- Mimante/Utilities/SettingsManager.cs | 784 +- Mimante/Utilities/StatsExporter.cs | 217 + Mimante/Utilities/StatsMaintenance.cs | 192 + Mimante/Utilities/TextLogService.cs | 103 + Mimante/Utilities/ThemeManager.cs | 124 + Mimante/Utilities/Wait.cs | 31 + Mimante/Utilities/WatchedProductsStore.cs | 252 + Mimante/Utilities/WindowsNotifier.cs | 80 + Mimante/ViewModels/AuctionViewModel.cs | 302 +- Mimante/ViewModels/ProductViewModel.cs | 469 + README.md | 1082 +- Template/Images/LockScreenLogo.scale-200.png | Bin 1430 -> 0 bytes Template/Images/SplashScreen.scale-200.png | Bin 7700 -> 0 bytes .../Images/Square150x150Logo.scale-200.png | Bin 2937 -> 0 bytes Template/Images/Square44x44Logo.scale-200.png | Bin 1647 -> 0 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 1255 -> 0 bytes Template/Images/StoreLogo.png | Bin 1451 -> 0 bytes Template/Images/Wide310x150Logo.scale-200.png | Bin 3204 -> 0 bytes Template/Package.appxmanifest | 52 - Template/Template.wapproj | 122 - 212 files changed, 112225 insertions(+), 26787 deletions(-) create mode 100644 Mimante/.vscode/launch.json create mode 100644 Mimante/.vscode/tasks.json create mode 100644 Mimante/Controls/ExportControl.xaml create mode 100644 Mimante/Controls/ExportControl.xaml.cs create mode 100644 Mimante/Controls/FreeBidsControl.xaml create mode 100644 Mimante/Controls/FreeBidsControl.xaml.cs create mode 100644 Mimante/Controls/HelpHeader.xaml create mode 100644 Mimante/Controls/HelpHeader.xaml.cs create mode 100644 Mimante/Controls/ProductsControl.xaml create mode 100644 Mimante/Controls/ProductsControl.xaml.cs delete mode 100644 Mimante/Controls/StatisticsControl.xaml delete mode 100644 Mimante/Controls/StatisticsControl.xaml.cs delete mode 100644 Mimante/Core/EventHandlers/MainWindow.EventHandlers.Export.cs create mode 100644 Mimante/Core/MainWindow.Catalog.cs create mode 100644 Mimante/Core/MainWindow.DataSettings.cs create mode 100644 Mimante/Core/MainWindow.Export.cs create mode 100644 Mimante/Core/MainWindow.FreeBids.cs create mode 100644 Mimante/Core/MainWindow.MonitorHeader.cs create mode 100644 Mimante/Core/MainWindow.ProductWatch.cs create mode 100644 Mimante/Core/MainWindow.Products.cs create mode 100644 Mimante/Core/MainWindow.Shutdown.cs create mode 100644 Mimante/Core/MainWindow.Statistics.cs delete mode 100644 Mimante/Data/StatisticsContext.cs delete mode 100644 Mimante/Dialogs/ClosedAuctionsWindow.xaml delete mode 100644 Mimante/Dialogs/ClosedAuctionsWindow.xaml.cs create mode 100644 Mimante/Dialogs/StatsCleanupDialog.xaml create mode 100644 Mimante/Dialogs/StatsCleanupDialog.xaml.cs delete mode 100644 Mimante/Documentation/ARCHITECTURE_OVERVIEW.md delete mode 100644 Mimante/Documentation/CHANGELOG.md delete mode 100644 Mimante/Documentation/DEBUG_COOKIE_DETECTION_LOGGING.md delete mode 100644 Mimante/Documentation/DIAGNOSTICA_DATI_UTENTE.md delete mode 100644 Mimante/Documentation/FEATURE_AUCTION_BUTTONS_REORGANIZED.md delete mode 100644 Mimante/Documentation/FEATURE_AUCTION_NAVIGATION_REORDER.md delete mode 100644 Mimante/Documentation/FEATURE_AUTO_FOCUS_AFTER_DELETE.md delete mode 100644 Mimante/Documentation/FEATURE_BID_HISTORY_TAB.md delete mode 100644 Mimante/Documentation/FEATURE_CONFIGURABLE_LOG_LIMITS.md delete mode 100644 Mimante/Documentation/FEATURE_HTML_CACHE_SERVICE.md delete mode 100644 Mimante/Documentation/FEATURE_INITIAL_AUCTION_STATE.md delete mode 100644 Mimante/Documentation/FEATURE_LOG_MAX_LINES.md delete mode 100644 Mimante/Documentation/FEATURE_MINIMUM_BIDS_LIMIT.md delete mode 100644 Mimante/Documentation/FEATURE_NUMERIC_INPUT_VALIDATION.md delete mode 100644 Mimante/Documentation/FEATURE_PRODUCT_INFO_AND_VALUE_CALCULATOR.md delete mode 100644 Mimante/Documentation/FEATURE_PRODUCT_VALUE_CALCULATOR.md delete mode 100644 Mimante/Documentation/FEATURE_WEBVIEW_PRELOAD_AND_COOKIE_EXTRACTION.md delete mode 100644 Mimante/Documentation/FIX_AUCTION_LOG_COLOR.md delete mode 100644 Mimante/Documentation/FIX_BID_COUNT_FROM_SERVER.md delete mode 100644 Mimante/Documentation/FIX_BID_HISTORY_PERSISTENCE.md delete mode 100644 Mimante/Documentation/FIX_BROWSER_URL_READONLY.md delete mode 100644 Mimante/Documentation/FIX_CLIPBOARD_FALSE_ERROR.md delete mode 100644 Mimante/Documentation/FIX_COOKIE_LOADING_USER_DATA.md delete mode 100644 Mimante/Documentation/FIX_COOKIE_PERSISTENCE.md delete mode 100644 Mimante/Documentation/FIX_COOKIE_SESSION_WARMUP.md delete mode 100644 Mimante/Documentation/FIX_CORRECT_FIELD_INDICES.md delete mode 100644 Mimante/Documentation/FIX_DEFAULT_SETTINGS_PERSISTENCE.md delete mode 100644 Mimante/Documentation/FIX_DELETE_KEY.md delete mode 100644 Mimante/Documentation/FIX_EMOJI_NOT_DISPLAYED.md delete mode 100644 Mimante/Documentation/FIX_LOG_CLEANUP_UX.md delete mode 100644 Mimante/Documentation/FIX_PRODUCT_INFO_PARSING.md delete mode 100644 Mimante/Documentation/FIX_RUNTIME_COOKIE_EVENTS.md delete mode 100644 Mimante/Documentation/FIX_SETTINGS_SAVE_AND_LOGGING.md delete mode 100644 Mimante/Documentation/FIX_SIDEBAR_VISIBILITY_WEBVIEW_INIT.md delete mode 100644 Mimante/Documentation/FIX_SINGLE_AUCTION_START.md delete mode 100644 Mimante/Documentation/FIX_SKIP_BID_IF_WINNER.md delete mode 100644 Mimante/Documentation/FIX_TAB_SETTINGS_WEBVIEW_INIT.md delete mode 100644 Mimante/Documentation/FIX_UI_UPDATE_AFTER_BID.md delete mode 100644 Mimante/Documentation/FIX_WEBVIEW_ALREADY_INITIALIZED.md delete mode 100644 Mimante/Documentation/FIX_WEBVIEW_THREADING_ERROR.md delete mode 100644 Mimante/Documentation/FIX_WEBVIEW_TIMEOUT_USERDATAFOLDER.md delete mode 100644 Mimante/Documentation/FIX_WEBVIEW_VISIBILITY_REQUIREMENT.md delete mode 100644 Mimante/Documentation/LOG_CLEANUP_FINAL.md delete mode 100644 Mimante/Documentation/PROJECT_REORGANIZATION.md delete mode 100644 Mimante/Documentation/REFACTORING_BROWSER_ADDRESS_BAR.md delete mode 100644 Mimante/Documentation/REFACTORING_COOKIE_DETECTION_TIMING.md delete mode 100644 Mimante/Documentation/REFACTORING_EXECUTIVE_SUMMARY.md delete mode 100644 Mimante/Documentation/REFACTORING_SESSION_SERVICE_COMPLETE.md delete mode 100644 Mimante/Documentation/REFACTORING_SESSION_SERVICE_PROPOSAL.md delete mode 100644 Mimante/Documentation/REFACTORING_SETTINGS_PERSISTENCE.md delete mode 100644 Mimante/Documentation/REFACTORING_SUMMARY.md delete mode 100644 Mimante/Documentation/UI_PRODUCT_VALUE_IMPLEMENTATION.md delete mode 100644 Mimante/Documentation/XAML_REFACTORING_CHECKLIST.md delete mode 100644 Mimante/Documentation/XAML_REFACTORING_SUMMARY.md create mode 100644 Mimante/Engine/AuctionRunner.cs create mode 100644 Mimante/Engine/Backtest/BacktestReport.cs create mode 100644 Mimante/Engine/Backtest/BacktestRunner.cs create mode 100644 Mimante/Engine/Backtest/DossierReader.cs create mode 100644 Mimante/Engine/PrecisionWait.cs create mode 100644 Mimante/Engine/ServerClock.cs create mode 100644 Mimante/Examples/puntateaste.it.txt create mode 100644 Mimante/Models/AuctionDetailRecord.cs create mode 100644 Mimante/Models/AuctionLogEntry.cs delete mode 100644 Mimante/Models/AuctionStatistics.cs create mode 100644 Mimante/Models/CatalogModels.cs create mode 100644 Mimante/Models/CompletedAuctionRecord.cs create mode 100644 Mimante/Models/FreeBidsModels.cs create mode 100644 Mimante/Models/FreeBidsSiteConfig.cs delete mode 100644 Mimante/Models/ProductInsights.cs delete mode 100644 Mimante/Models/ProductStat.cs create mode 100644 Mimante/Models/PromoLinkModels.cs create mode 100644 Mimante/Models/WatchedProduct.cs create mode 100644 Mimante/Net/BidooHttpClient.cs create mode 100644 Mimante/Services/BidStrategyService.cs create mode 100644 Mimante/Services/BidooCatalogClient.cs create mode 100644 Mimante/Services/BidooFreeBidsClaimer.cs create mode 100644 Mimante/Services/BidooPromoRedeemer.cs create mode 100644 Mimante/Services/BidooResponseParser.cs create mode 100644 Mimante/Services/CatalogPageParser.cs delete mode 100644 Mimante/Services/ClosedAuctionsScraper.cs create mode 100644 Mimante/Services/FreeBidsAutoClaimService.cs create mode 100644 Mimante/Services/FreeBidsPageParser.cs create mode 100644 Mimante/Services/FreeBidsRequestFactory.cs create mode 100644 Mimante/Services/FreeBidsResponseValidator.cs create mode 100644 Mimante/Services/IFreeBidsClaimer.cs create mode 100644 Mimante/Services/IPromoLinkRedeemer.cs create mode 100644 Mimante/Services/ProductWatchService.cs create mode 100644 Mimante/Services/PromoLinkParser.cs create mode 100644 Mimante/Services/Settings.cs delete mode 100644 Mimante/Services/StatsService.cs create mode 100644 Mimante/Services/WinnerBidsBackfill.cs create mode 100644 Mimante/Tests/AuctionDossierTests.cs create mode 100644 Mimante/Tests/AutoBidder.Tests.csproj create mode 100644 Mimante/Tests/BacktestTests.cs create mode 100644 Mimante/Tests/BidBudgetTests.cs create mode 100644 Mimante/Tests/BidLeadStatsTests.cs create mode 100644 Mimante/Tests/BidooResponseParserTests.cs create mode 100644 Mimante/Tests/CatalogPageParserTests.cs create mode 100644 Mimante/Tests/ExportAndLogTests.cs create mode 100644 Mimante/Tests/FreeBidsTests.cs create mode 100644 Mimante/Tests/PriceAdvisorTests.cs create mode 100644 Mimante/Tests/ProductKeyHelperTests.cs create mode 100644 Mimante/Tests/ProductRuleTests.cs create mode 100644 Mimante/Tests/RealDossierBacktest.cs create mode 100644 Mimante/Tests/ServerClockTests.cs create mode 100644 Mimante/Tests/TestEnvironment.cs create mode 100644 Mimante/Themes/Controls.xaml create mode 100644 Mimante/Themes/Tokens.Dark.xaml create mode 100644 Mimante/Themes/Tokens.Light.xaml create mode 100644 Mimante/Utilities/AppInfo.cs create mode 100644 Mimante/Utilities/AppPaths.cs create mode 100644 Mimante/Utilities/AuctionDetailStore.cs create mode 100644 Mimante/Utilities/AuctionDossier.cs create mode 100644 Mimante/Utilities/AuctionExporter.cs create mode 100644 Mimante/Utilities/AuctionIntegrity.cs create mode 100644 Mimante/Utilities/BidBudget.cs create mode 100644 Mimante/Utilities/BidLeadStats.cs create mode 100644 Mimante/Utilities/ClaimedPromoStore.cs create mode 100644 Mimante/Utilities/CompletedAuctionsStore.cs delete mode 100644 Mimante/Utilities/CsvExporter.cs delete mode 100644 Mimante/Utilities/ExportPreferences.cs create mode 100644 Mimante/Utilities/FileLogWriter.cs create mode 100644 Mimante/Utilities/FreeBidsConfigStore.cs create mode 100644 Mimante/Utilities/FreeBidsStats.cs create mode 100644 Mimante/Utilities/PriceAdvisor.cs create mode 100644 Mimante/Utilities/ProductKeyHelper.cs create mode 100644 Mimante/Utilities/ProductRuleResolver.cs create mode 100644 Mimante/Utilities/ProductStatsStore.cs create mode 100644 Mimante/Utilities/StatsExporter.cs create mode 100644 Mimante/Utilities/StatsMaintenance.cs create mode 100644 Mimante/Utilities/TextLogService.cs create mode 100644 Mimante/Utilities/ThemeManager.cs create mode 100644 Mimante/Utilities/Wait.cs create mode 100644 Mimante/Utilities/WatchedProductsStore.cs create mode 100644 Mimante/Utilities/WindowsNotifier.cs create mode 100644 Mimante/ViewModels/ProductViewModel.cs delete mode 100644 Template/Images/LockScreenLogo.scale-200.png delete mode 100644 Template/Images/SplashScreen.scale-200.png delete mode 100644 Template/Images/Square150x150Logo.scale-200.png delete mode 100644 Template/Images/Square44x44Logo.scale-200.png delete mode 100644 Template/Images/Square44x44Logo.targetsize-24_altform-unplated.png delete mode 100644 Template/Images/StoreLogo.png delete mode 100644 Template/Images/Wide310x150Logo.scale-200.png delete mode 100644 Template/Package.appxmanifest delete mode 100644 Template/Template.wapproj diff --git a/Mimante/.vscode/launch.json b/Mimante/.vscode/launch.json new file mode 100644 index 0000000..7f6eb21 --- /dev/null +++ b/Mimante/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug AutoBidder (WPF)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/bin/Debug/net10.0-windows/AutoBidder.exe", + "args": [], + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "stopAtEntry": false, + "enableStepFiltering": true + }, + { + "name": "Attach to AutoBidder (processo già avviato)", + "type": "coreclr", + "request": "attach", + "processName": "AutoBidder.exe" + } + ] +} diff --git a/Mimante/.vscode/tasks.json b/Mimante/.vscode/tasks.json new file mode 100644 index 0000000..d36b62c --- /dev/null +++ b/Mimante/.vscode/tasks.json @@ -0,0 +1,134 @@ +{ + // Tutte le attività passano da build/Release.proj: la catena è un solo file + // MSBuild versionato col codice, e qui restano soltanto i nomi e le domande. + // MSBuild non può chiedere niente a nessuno — i prompt stanno in "inputs". + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "detail": "Compilazione di debug, per F5 e per il controllo rapido degli errori.", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/AutoBidder.csproj", + "-c", + "Debug", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "group": { "kind": "build", "isDefault": true }, + "problemMatcher": "$msCompile" + }, + { + "label": "verifica", + "detail": "Compila e lancia i test. Da eseguire dopo ogni modifica.", + "type": "process", + "command": "dotnet", + "args": [ + "msbuild", + "${workspaceFolder}/build/Release.proj", + "-t:Verifica", + "-nologo", + "-v:m" + ], + "group": { "kind": "test", "isDefault": true }, + "problemMatcher": "$msCompile" + }, + { + "label": "backtest", + "detail": "Rigioca i dossier delle aste concluse: quante puntate costerebbe ciascun anticipo, e quante ne fermano i controlli di budget.", + "type": "process", + "command": "dotnet", + "args": [ + "msbuild", + "${workspaceFolder}/build/Release.proj", + "-t:Backtest", + "-nologo", + "-v:m" + ], + "presentation": { "reveal": "always", "panel": "dedicated" }, + "problemMatcher": [] + }, + { + "label": "backtest (giro veloce, 400 aste)", + "detail": "Come sopra ma su un campione: utile mentre si tarano le strategie.", + "type": "process", + "command": "dotnet", + "args": [ + "msbuild", + "${workspaceFolder}/build/Release.proj", + "-t:Backtest", + "-p:MaxDossier=400", + "-nologo", + "-v:m" + ], + "presentation": { "reveal": "always", "panel": "dedicated" }, + "problemMatcher": [] + }, + { + "label": "crea installatore", + "detail": "Chiede la versione, verifica, pubblica ed esegue Inno Setup: bin/installer/AutoBidder--setup.exe. Non tocca Gitea.", + "type": "process", + "command": "dotnet", + "args": [ + "msbuild", + "${workspaceFolder}/build/Release.proj", + "-t:Pacchetto", + "-p:Versione=${input:versione}", + "-nologo", + "-v:m" + ], + "presentation": { "reveal": "always", "panel": "dedicated" }, + "problemMatcher": "$msCompile" + }, + { + "label": "crea installatore (senza rieseguire i test)", + "detail": "Solo pubblicazione e Inno Setup. Da usare quando i test sono appena passati.", + "type": "process", + "command": "dotnet", + "args": [ + "msbuild", + "${workspaceFolder}/build/Release.proj", + "-t:Pacchetto", + "-p:Versione=${input:versione}", + "-p:SaltaVerifica=true", + "-nologo", + "-v:m" + ], + "presentation": { "reveal": "always", "panel": "dedicated" }, + "problemMatcher": "$msCompile" + }, + { + "label": "rilascia su Gitea", + "detail": "Chiede versione e note, poi: verifica, pubblica, installatore, tag e release su Gitea con i file allegati. Richiede build/gitea.json.", + "type": "process", + "command": "dotnet", + "args": [ + "msbuild", + "${workspaceFolder}/build/Release.proj", + "-t:Rilascia", + "-p:Versione=${input:versione}", + "-p:Note=${input:note}", + "-nologo", + "-v:m" + ], + "presentation": { "reveal": "always", "panel": "dedicated" }, + "problemMatcher": "$msCompile" + } + ], + "inputs": [ + { + "id": "versione", + "type": "promptString", + "description": "Versione (vuoto = incrementa la minor, es. 4.11.0 → 4.12.0)", + "default": "" + }, + { + "id": "note", + "type": "promptString", + "description": "Note di rilascio (vuoto = solo il numero di versione)", + "default": "" + } + ] +} diff --git a/Mimante/App.xaml b/Mimante/App.xaml index 0fdcb30..fc46850 100644 --- a/Mimante/App.xaml +++ b/Mimante/App.xaml @@ -1,29 +1,16 @@ - - - + + + + + + diff --git a/Mimante/App.xaml.cs b/Mimante/App.xaml.cs index ec7348a..9fd6226 100644 --- a/Mimante/App.xaml.cs +++ b/Mimante/App.xaml.cs @@ -1,14 +1,78 @@ -using System.Configuration; -using System.Data; +using System; using System.Windows; +using AutoBidder.Utilities; -namespace Mimante +namespace AutoBidder { /// /// Interaction logic for App.xaml /// public partial class App : Application { - } + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + // Le descrizioni delle impostazioni vivono nei suggerimenti invece che stampate + // sotto ogni campo. Quello predefinito di Windows sparisce dopo cinque secondi: + // per un paragrafo di tre righe significa doverlo rincorrere. + System.Windows.Controls.ToolTipService.ShowDurationProperty.OverrideMetadata( + typeof(DependencyObject), new FrameworkPropertyMetadata(60000)); + + // I percorsi vanno decisi per primi: da qui in poi ogni archivio li usa. + // Le impostazioni stanno nella radice fissa, quindi si possono leggere anche + // prima di sapere dove finiranno gli altri dati. + var settings = SettingsManager.Load(); + AppPaths.Configure(settings.DataFolder, settings.StatsFolder, settings.LogFolder); + AppPaths.EnsureFolders(); + + // Le versioni precedenti scrivevano parte dei dati in Roaming e parte in Local, + // senza sottocartelle: senza questo passaggio storico e aste sparirebbero. + AppPaths.MigrateLegacyFiles(); + + // I registri su file partono subito: quello che succede durante l'avvio è + // proprio ciò che non si riesce a leggere a video. + TextLogService.SessionStarted(AppInfo.Version); + TextLogService.PurgeOldLogs(); + + // Le righe di registro delle aste confluiscono nei rispettivi dossier. + AuctionDossier.CaptureAuctionLogs(); + + // Lo storico e' cresciuto per mesi con versioni diverse dell'applicazione: + // alcuni record hanno campi che allora non esistevano. Quelli deducibili si + // ricalcolano qui, una volta, prima che qualcuno li legga. Quelli NON + // deducibili non si toccano: se ne occupa la pulizia, che chiede all'utente. + try + { + var repair = CompletedAuctionsStore.RepairOnStartup(); + if (repair.AnythingDone) + { + TextLogService.App("INFO", $"[STORICO] Riparati {repair.Total} campi mancanti " + + $"(chiavi prodotto {repair.ProductKeys}, esiti {repair.Outcomes}, " + + $"nomi {repair.Names})"); + } + } + catch (Exception ex) + { + TextLogService.App("WARN", $"[STORICO] Riparazione non riuscita: {ex.Message}"); + } + + // Applica il tema salvato (chiaro/scuro) prima di mostrare la finestra + ThemeManager.ApplyFromSettings(); + } + + protected override void OnExit(ExitEventArgs e) + { + // Quello che è ancora in coda va sul disco adesso: un registro che perde + // proprio le ultime righe è inutile esattamente quando serve di più. + TextLogService.SessionEnded(); + FileLogWriter.DisposeAll(); + + // L'icona di notifica sopravvive al processo se non la si rimuove: + // resterebbe nell'area di notifica come icona fantasma. + WindowsNotifier.Dispose(); + + base.OnExit(e); + } + } } diff --git a/Mimante/AutoBidder.csproj b/Mimante/AutoBidder.csproj index 67b8469..d12a269 100644 --- a/Mimante/AutoBidder.csproj +++ b/Mimante/AutoBidder.csproj @@ -1,37 +1,54 @@ - + WinExe - net8.0-windows + net10.0-windows enable enable true + + true AutoBidder AutoBidder + Icon\favicon.ico + + + + 4.12.0 + 4.12.0.0 + 4.12.0.0 + 4.12.0 + + + win-x64 + it;en - - - - - - - - + + + - + + + - - - + + diff --git a/Mimante/AutoBidder.sln b/Mimante/AutoBidder.sln index 32f1060..0a2f736 100644 --- a/Mimante/AutoBidder.sln +++ b/Mimante/AutoBidder.sln @@ -1,48 +1,55 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.0.11217.181 d18.0 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoBidder", "AutoBidder.csproj", "{9BBAEF93-DF66-432C-9349-459E272D6538}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoBidder.Tests", "Tests\AutoBidder.Tests.csproj", "{CAC096C0-E399-4268-B2FF-E205947CB733}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|ARM = Debug|ARM - Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|ARM = Release|ARM - Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|ARM.ActiveCfg = Debug|Any CPU - {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|ARM.Build.0 = Debug|Any CPU - {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|ARM64.Build.0 = Debug|Any CPU {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|x64.ActiveCfg = Debug|Any CPU {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|x64.Build.0 = Debug|Any CPU {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|x86.ActiveCfg = Debug|Any CPU {9BBAEF93-DF66-432C-9349-459E272D6538}.Debug|x86.Build.0 = Debug|Any CPU {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|Any CPU.ActiveCfg = Release|Any CPU {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|Any CPU.Build.0 = Release|Any CPU - {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|ARM.ActiveCfg = Release|Any CPU - {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|ARM.Build.0 = Release|Any CPU - {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|ARM64.ActiveCfg = Release|Any CPU - {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|ARM64.Build.0 = Release|Any CPU {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|x64.ActiveCfg = Release|Any CPU {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|x64.Build.0 = Release|Any CPU {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|x86.ActiveCfg = Release|Any CPU {9BBAEF93-DF66-432C-9349-459E272D6538}.Release|x86.Build.0 = Release|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Debug|x64.ActiveCfg = Debug|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Debug|x64.Build.0 = Debug|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Debug|x86.ActiveCfg = Debug|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Debug|x86.Build.0 = Debug|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Release|Any CPU.Build.0 = Release|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Release|x64.ActiveCfg = Release|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Release|x64.Build.0 = Release|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Release|x86.ActiveCfg = Release|Any CPU + {CAC096C0-E399-4268-B2FF-E205947CB733}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {CAC096C0-E399-4268-B2FF-E205947CB733} = {0AB3BF05-4346-4AA6-1389-037BE0695223} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1C55CA56-D270-4D9A-91DA-410BF131E905} EndGlobalSection diff --git a/Mimante/Controls/AuctionMonitorControl.xaml b/Mimante/Controls/AuctionMonitorControl.xaml index b52bc69..210e157 100644 --- a/Mimante/Controls/AuctionMonitorControl.xaml +++ b/Mimante/Controls/AuctionMonitorControl.xaml @@ -1,51 +1,28 @@  - + Background="{DynamicResource Brush.Bg}"> + - - - - - - - - - @@ -53,912 +30,735 @@ + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mimante/Controls/BrowserControl.xaml.cs b/Mimante/Controls/BrowserControl.xaml.cs index 58e5751..19b5211 100644 --- a/Mimante/Controls/BrowserControl.xaml.cs +++ b/Mimante/Controls/BrowserControl.xaml.cs @@ -1,4 +1,4 @@ -using System.Windows; +using System.Windows; using System.Windows.Controls; using System.Windows.Input; using Microsoft.Web.WebView2.Core; @@ -44,7 +44,7 @@ namespace AutoBidder.Controls } /// - /// ? NUOVO: Aggiorna address bar quando la navigazione completata + /// ? NUOVO: Aggiorna address bar quando la navigazione � completata /// private void WebView_NavigationCompleted(object? sender, CoreWebView2NavigationCompletedEventArgs e) { @@ -93,6 +93,133 @@ namespace AutoBidder.Controls e.Handled = true; } + // ===== MODALITA' ESPLORA ===== + + private void ModeCatalogRadio_Checked(object sender, RoutedEventArgs e) => ApplyMode(catalog: true); + + private void ModeBrowserRadio_Checked(object sender, RoutedEventArgs e) => ApplyMode(catalog: false); + + private void ApplyMode(bool catalog) + { + // Chiamato anche durante InitializeComponent, quando gli elementi dichiarati piu' + // in basso nel XAML non esistono ancora. Vanno verificati tutti: fidarsi + // dell'ordine di dichiarazione rende il controllo fragile a ogni riordino. + if (CatalogPanel == null || BrowserPanel == null || BrowserToolbar == null || + CatalogToolbarLeft == null || CatalogSearchHost == null || + CatalogCountText == null || BrowserAddAuctionButton == null) + { + return; + } + + CatalogPanel.Visibility = catalog ? Visibility.Visible : Visibility.Collapsed; + BrowserPanel.Visibility = catalog ? Visibility.Collapsed : Visibility.Visible; + + // I due gruppi di comandi occupano lo stesso spazio: ne vive uno solo per volta. + BrowserToolbar.Visibility = catalog ? Visibility.Collapsed : Visibility.Visible; + CatalogToolbarLeft.Visibility = catalog ? Visibility.Visible : Visibility.Collapsed; + CatalogSearchHost.Visibility = catalog ? Visibility.Visible : Visibility.Collapsed; + CatalogCountText.Visibility = catalog ? Visibility.Visible : Visibility.Collapsed; + BrowserAddAuctionButton.Visibility = catalog ? Visibility.Collapsed : Visibility.Visible; + } + + /// Porta in primo piano il browser integrato (usato dal login e dal catalogo). + public void ShowBrowser() => ModeBrowserRadio.IsChecked = true; + + /// Porta in primo piano il catalogo nativo (scheda "Cerca"). + public void ShowCatalog() => ModeCatalogRadio.IsChecked = true; + + /// True quando l'utente sta guardando il catalogo nativo. + public bool IsCatalogMode => ModeCatalogRadio.IsChecked == true; + + // ===== CATALOGO ===== + + /// + /// Impedisce che il riempimento iniziale dell'elenco categorie faccia partire + /// un caricamento per ogni voce aggiunta. + /// + private bool _catalogReady; + + private void CategoryButton_Checked(object sender, RoutedEventArgs e) + { + if (!_catalogReady) return; + RaiseEvent(new RoutedEventArgs(CatalogCategoryChangedEvent, this)); + } + + private void CatalogRefreshButton_Click(object sender, RoutedEventArgs e) + => RaiseEvent(new RoutedEventArgs(CatalogRefreshClickedEvent, this)); + + private void CatalogSearchBox_TextChanged(object sender, TextChangedEventArgs e) + { + if (CatalogSearchHint != null) + { + CatalogSearchHint.Visibility = string.IsNullOrEmpty(CatalogSearchBox.Text) + ? Visibility.Visible + : Visibility.Collapsed; + } + + RaiseEvent(new RoutedEventArgs(CatalogSearchChangedEvent, this)); + } + + private void CatalogFilterChanged(object sender, RoutedEventArgs e) + => RaiseEvent(new RoutedEventArgs(CatalogSearchChangedEvent, this)); + + private void CatalogAutoRefresh_Changed(object sender, RoutedEventArgs e) + => RaiseEvent(new RoutedEventArgs(CatalogAutoRefreshChangedEvent, this)); + + /// Riempie l'elenco categorie senza scatenare un caricamento per voce. + public void SetCategories(System.Collections.IEnumerable categories) + { + _catalogReady = false; + try { CatalogCategoryList.ItemsSource = categories; } + finally { _catalogReady = true; } + } + + /// Mostra le schede oppure, se non ce ne sono, un messaggio di stato. + public void SetCatalogItems(System.Collections.IList items, int totalCount) + { + CatalogItems.ItemsSource = items; + CatalogCountText.Text = items.Count == 1 ? "1 asta" : $"{items.Count} aste"; + + var empty = items.Count == 0; + CatalogEmptyState.Visibility = empty ? Visibility.Visible : Visibility.Collapsed; + CatalogScroller.Visibility = empty ? Visibility.Collapsed : Visibility.Visible; + + if (empty) + { + CatalogEmptyText.Text = totalCount > 0 + ? "Nessuna asta corrisponde ai filtri." + : "Nessuna asta in questa categoria."; + } + } + + /// Messaggio a tutto pannello (caricamento in corso, errore di rete...). + public void SetCatalogMessage(string message) + { + CatalogEmptyState.Visibility = Visibility.Visible; + CatalogScroller.Visibility = Visibility.Collapsed; + CatalogEmptyText.Text = message; + } + + public bool AutoRefreshEnabled => CatalogAutoRefresh.IsChecked == true; + + /// + /// Allinea l'interruttore all'impostazione salvata senza far partire un giro di + /// aggiornamento: chi chiama sta ancora costruendo la pagina. + /// + public void SetAutoRefresh(bool enabled) + { + CatalogAutoRefresh.Checked -= CatalogAutoRefresh_Changed; + CatalogAutoRefresh.Unchecked -= CatalogAutoRefresh_Changed; + try { CatalogAutoRefresh.IsChecked = enabled; } + finally + { + CatalogAutoRefresh.Checked += CatalogAutoRefresh_Changed; + CatalogAutoRefresh.Unchecked += CatalogAutoRefresh_Changed; + } + } + public bool HideManualAuctions => CatalogHideManual.IsChecked == true; + public string SearchText => CatalogSearchBox.Text?.Trim() ?? ""; + // Routed Events public static readonly RoutedEvent BrowserBackClickedEvent = EventManager.RegisterRoutedEvent( "BrowserBackClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl)); @@ -144,6 +271,44 @@ namespace AutoBidder.Controls add { AddHandler(BrowserAddAuctionClickedEvent, value); } remove { RemoveHandler(BrowserAddAuctionClickedEvent, value); } } + + // ===== Eventi del catalogo ===== + + public static readonly RoutedEvent CatalogCategoryChangedEvent = EventManager.RegisterRoutedEvent( + "CatalogCategoryChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl)); + + public static readonly RoutedEvent CatalogRefreshClickedEvent = EventManager.RegisterRoutedEvent( + "CatalogRefreshClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl)); + + public static readonly RoutedEvent CatalogAutoRefreshChangedEvent = EventManager.RegisterRoutedEvent( + "CatalogAutoRefreshChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl)); + + public static readonly RoutedEvent CatalogSearchChangedEvent = EventManager.RegisterRoutedEvent( + "CatalogSearchChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BrowserControl)); + + public event RoutedEventHandler CatalogCategoryChanged + { + add { AddHandler(CatalogCategoryChangedEvent, value); } + remove { RemoveHandler(CatalogCategoryChangedEvent, value); } + } + + public event RoutedEventHandler CatalogRefreshClicked + { + add { AddHandler(CatalogRefreshClickedEvent, value); } + remove { RemoveHandler(CatalogRefreshClickedEvent, value); } + } + + public event RoutedEventHandler CatalogAutoRefreshChanged + { + add { AddHandler(CatalogAutoRefreshChangedEvent, value); } + remove { RemoveHandler(CatalogAutoRefreshChangedEvent, value); } + } + + public event RoutedEventHandler CatalogSearchChanged + { + add { AddHandler(CatalogSearchChangedEvent, value); } + remove { RemoveHandler(CatalogSearchChangedEvent, value); } + } } public class BrowserNavigationEventArgs : RoutedEventArgs diff --git a/Mimante/Controls/ExportControl.xaml b/Mimante/Controls/ExportControl.xaml new file mode 100644 index 0000000..e1c67a1 --- /dev/null +++ b/Mimante/Controls/ExportControl.xaml @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mimante/Controls/ProductsControl.xaml.cs b/Mimante/Controls/ProductsControl.xaml.cs new file mode 100644 index 0000000..8df7d8f --- /dev/null +++ b/Mimante/Controls/ProductsControl.xaml.cs @@ -0,0 +1,227 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using AutoBidder.ViewModels; + +namespace AutoBidder.Controls +{ + /// + /// Scheda Prodotti: una riga per articolo, con la stellina, i limiti su misura e le + /// statistiche dello storico tutte sulla stessa riga. + /// + /// Il controllo non conosce né gli archivi né il monitor: espone eventi e lascia + /// decidere a MainWindow, com'è per gli altri pannelli. + /// + public partial class ProductsControl : UserControl + { + /// + /// Elenco completo, indipendente da quello mostrato: la ricerca filtra la vista, non + /// i dati. Senza, scrivere nella casella cancellerebbe le righe invece di nasconderle. + /// + private IList? _allProducts; + + public ProductsControl() + { + InitializeComponent(); + } + + /// Prodotto selezionato, o null se la griglia è vuota o senza selezione. + public ProductViewModel? SelectedProduct => ProductsGrid.SelectedItem as ProductViewModel; + + /// + /// Riempie l'elenco. Le righe sono già ViewModel: la griglia scrive direttamente + /// nelle schede prodotto, e la persistenza avviene alla conferma della cella. + /// + public void SetProducts(IList products) + { + _allProducts = products; + ApplySearch(); + } + + /// + /// Mostra solo i prodotti il cui nome contiene il testo cercato. + /// + /// Il filtro si applica a una copia: la griglia è modificabile, e gli oggetti + /// mostrati sono gli stessi dell'elenco completo — quindi i limiti scritti mentre + /// un filtro è attivo restano quando il filtro sparisce. + /// + private void ApplySearch() + { + if (_allProducts == null) return; + + var query = SearchBox.Text?.Trim() ?? ""; + SearchPlaceholder.Visibility = query.Length == 0 ? Visibility.Visible : Visibility.Collapsed; + + var all = _allProducts.OfType().ToList(); + + var shown = query.Length == 0 + ? all + : all.Where(p => (p.DisplayName ?? "") + .Contains(query, System.StringComparison.OrdinalIgnoreCase)) + .ToList(); + + ProductsGrid.ItemsSource = shown; + + ProductsCountText.Text = query.Length == 0 + ? (all.Count == 1 ? "1 prodotto" : $"{all.Count} prodotti") + : $"{shown.Count} di {all.Count}"; + + ProductsWatchedText.Text = $"{all.Count(p => p.IsWatched)} seguiti"; + + // Con la ricerca attiva e nessun risultato non si mostra il messaggio di elenco + // vuoto: direbbe "non segui ancora nessun prodotto", che sarebbe falso. + ProductsEmptyState.Visibility = all.Count == 0 ? Visibility.Visible : Visibility.Collapsed; + } + + private void SearchBox_TextChanged(object sender, TextChangedEventArgs e) => ApplySearch(); + + /// Riallinea i contatori dopo che una stellina è cambiata. + public void RefreshCounters() + { + // Si conta sull'elenco completo, non su quello mostrato: con una ricerca attiva + // il numero dei seguiti non deve cambiare solo perché sono nascosti. + var source = _allProducts ?? ProductsGrid.ItemsSource as IList; + if (source == null) return; + + ProductsWatchedText.Text = $"{source.OfType().Count(p => p.IsWatched)} seguiti"; + } + + /// Porta la selezione sul prodotto indicato (usato arrivando dalla scheda Cerca). + public void SelectProduct(string identity) + { + if (ProductsGrid.ItemsSource is not IEnumerable items) return; + + var match = items.OfType() + .FirstOrDefault(p => p.Identity == identity); + if (match == null) return; + + ProductsGrid.SelectedItem = match; + ProductsGrid.ScrollIntoView(match); + } + + // ── Eventi dell'interfaccia ────────────────────────────────────── + + private void ProductsGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) + => RaiseEvent(new RoutedEventArgs(ProductSelectionChangedEvent, this)); + + /// + /// La cella è stata confermata: il valore è già nel ViewModel, resta da renderlo + /// durevole. Si rimanda al termine dell'operazione perché durante l'evento la + /// modifica non è ancora stata trasferita al modello. + /// + private void ProductsGrid_CellEditEnding(object? sender, DataGridCellEditEndingEventArgs e) + { + if (e.EditAction != DataGridEditAction.Commit) return; + + Dispatcher.BeginInvoke(new System.Action(() => + RaiseEvent(new RoutedEventArgs(ProductLimitsEditedEvent, this))), + System.Windows.Threading.DispatcherPriority.Background); + } + + private void RefreshButton_Click(object sender, RoutedEventArgs e) + => RaiseEvent(new RoutedEventArgs(RefreshClickedEvent, this)); + + private void ScanNowButton_Click(object sender, RoutedEventArgs e) + => RaiseEvent(new RoutedEventArgs(ScanNowClickedEvent, this)); + + private void ReapplyButton_Click(object sender, RoutedEventArgs e) + => RaiseEvent(new RoutedEventArgs(ReapplyClickedEvent, this)); + + private void RemoveButton_Click(object sender, RoutedEventArgs e) + => RaiseEvent(new RoutedEventArgs(RemoveClickedEvent, this)); + + private void ClearButton_Click(object sender, RoutedEventArgs e) + => RaiseEvent(new RoutedEventArgs(ClearClickedEvent, this)); + + private void ApplySuggestedButton_Click(object sender, RoutedEventArgs e) + => RaiseEvent(new RoutedEventArgs(ApplySuggestedClickedEvent, this)); + + private void ApplyAllSuggestedButton_Click(object sender, RoutedEventArgs e) + => RaiseEvent(new RoutedEventArgs(ApplyAllSuggestedClickedEvent, this)); + + // ── Routed events ──────────────────────────────────────────────── + + public static readonly RoutedEvent RefreshClickedEvent = EventManager.RegisterRoutedEvent( + "RefreshClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ProductsControl)); + + public static readonly RoutedEvent ScanNowClickedEvent = EventManager.RegisterRoutedEvent( + "ScanNowClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ProductsControl)); + + public static readonly RoutedEvent ReapplyClickedEvent = EventManager.RegisterRoutedEvent( + "ReapplyClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ProductsControl)); + + public static readonly RoutedEvent RemoveClickedEvent = EventManager.RegisterRoutedEvent( + "RemoveClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ProductsControl)); + + public static readonly RoutedEvent ClearClickedEvent = EventManager.RegisterRoutedEvent( + "ClearClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ProductsControl)); + + public static readonly RoutedEvent ApplySuggestedClickedEvent = EventManager.RegisterRoutedEvent( + "ApplySuggestedClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ProductsControl)); + + public static readonly RoutedEvent ApplyAllSuggestedClickedEvent = EventManager.RegisterRoutedEvent( + "ApplyAllSuggestedClicked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ProductsControl)); + + public static readonly RoutedEvent ProductLimitsEditedEvent = EventManager.RegisterRoutedEvent( + "ProductLimitsEdited", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ProductsControl)); + + public static readonly RoutedEvent ProductSelectionChangedEvent = EventManager.RegisterRoutedEvent( + "ProductSelectionChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ProductsControl)); + + public event RoutedEventHandler RefreshClicked + { + add { AddHandler(RefreshClickedEvent, value); } + remove { RemoveHandler(RefreshClickedEvent, value); } + } + + public event RoutedEventHandler ScanNowClicked + { + add { AddHandler(ScanNowClickedEvent, value); } + remove { RemoveHandler(ScanNowClickedEvent, value); } + } + + public event RoutedEventHandler ReapplyClicked + { + add { AddHandler(ReapplyClickedEvent, value); } + remove { RemoveHandler(ReapplyClickedEvent, value); } + } + + public event RoutedEventHandler RemoveClicked + { + add { AddHandler(RemoveClickedEvent, value); } + remove { RemoveHandler(RemoveClickedEvent, value); } + } + + public event RoutedEventHandler ClearClicked + { + add { AddHandler(ClearClickedEvent, value); } + remove { RemoveHandler(ClearClickedEvent, value); } + } + + public event RoutedEventHandler ApplySuggestedClicked + { + add { AddHandler(ApplySuggestedClickedEvent, value); } + remove { RemoveHandler(ApplySuggestedClickedEvent, value); } + } + + public event RoutedEventHandler ApplyAllSuggestedClicked + { + add { AddHandler(ApplyAllSuggestedClickedEvent, value); } + remove { RemoveHandler(ApplyAllSuggestedClickedEvent, value); } + } + + public event RoutedEventHandler ProductLimitsEdited + { + add { AddHandler(ProductLimitsEditedEvent, value); } + remove { RemoveHandler(ProductLimitsEditedEvent, value); } + } + + public event RoutedEventHandler ProductSelectionChanged + { + add { AddHandler(ProductSelectionChangedEvent, value); } + remove { RemoveHandler(ProductSelectionChangedEvent, value); } + } + } +} diff --git a/Mimante/Controls/SettingsControl.xaml b/Mimante/Controls/SettingsControl.xaml index 92bd06b..ed5876f 100644 --- a/Mimante/Controls/SettingsControl.xaml +++ b/Mimante/Controls/SettingsControl.xaml @@ -3,81 +3,13 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:ctl="clr-namespace:AutoBidder.Controls" mc:Ignorable="d" d:DesignHeight="800" d:DesignWidth="1200" - Background="#1E1E1E"> + Background="{DynamicResource Brush.Bg}"> - - - + - - - - - - - - - - - - - - - - - - @@ -88,95 +20,89 @@ - - - + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -323,7 +650,7 @@ @@ -334,44 +661,23 @@ - - - - - • Se impostato > 0, il sistema non punterà se le puntate residue scenderebbero sotto questa soglia. - • Utile per mantenere sempre un "cuscinetto" di sicurezza sull'account. - • Nel banner principale apparirà un indicatore colorato: - - Verde: Puntate abbondanti (oltre +10 dal limite) - - Giallo: Vicino al limite (entro 10 puntate) - - Rosso: Al limite o sotto (puntate bloccate) - • Valore 0 = nessun limite (comportamento default). - - - - - + - - - + @@ -386,7 +692,7 @@ @@ -397,7 +703,7 @@ @@ -409,7 +715,7 @@ @@ -420,23 +726,64 @@ - - - - - • I log più vecchi verranno automaticamente rimossi quando si raggiunge il limite. - • Valori più bassi = meno memoria utilizzata, ma meno storico disponibile. - • Valori più alti = più storico disponibile, ma maggiore uso di memoria. - • Valori consigliati: 500-1000 per asta, 1000-2000 per log globale. - - - + + + + + + + + + + + + + + + + + + + + + + @@ -444,20 +791,20 @@