From 2f1cbd31432d87e9e0328d6fccb28f2cc3e2160c Mon Sep 17 00:00:00 2001 From: DJObleezy Date: Sun, 27 Apr 2025 11:18:42 -0700 Subject: [PATCH] Enhance hashrate detection and UI updates Updated `NotificationService` to distinguish between low and normal hashrate modes, utilizing 3-hour and 10-minute averages for detection. Improved `updateChartWithNormalizedData` in `main.js` to support localStorage persistence for hashrate state and refined mode-switching logic. Introduced `showHashrateNormalizeNotice` for user notifications regarding hashrate normalization. Updated HTML files for UI consistency, including version number and structured display of pool fees and unpaid earnings. Ensured proper chart updates and annotations for 24-hour averages. --- notification_service.py | 91 +++++--- static/js/main.js | 452 ++++++++++++++++++++++++++++++--------- templates/base.html | 2 +- templates/boot.html | 2 +- templates/dashboard.html | 40 ++-- 5 files changed, 432 insertions(+), 155 deletions(-) diff --git a/notification_service.py b/notification_service.py index a492dfa..fe73a7e 100644 --- a/notification_service.py +++ b/notification_service.py @@ -447,38 +447,71 @@ class NotificationService: return 0.0 def _check_hashrate_change(self, current: Dict[str, Any], previous: Dict[str, Any]) -> Optional[Dict[str, Any]]: - """Check for significant hashrate changes using 10-minute average.""" + """Check for significant hashrate changes using appropriate time window based on mode.""" try: - # Get 10min hashrate values - current_10min = current.get("hashrate_10min", 0) - previous_10min = previous.get("hashrate_10min", 0) - - # Log what we're comparing - logging.debug(f"[NotificationService] Comparing 10min hashrates - current: {current_10min}, previous: {previous_10min}") - - # Skip if values are missing - if not current_10min or not previous_10min: - logging.debug("[NotificationService] Skipping hashrate check - missing values") - return None - - # Parse values consistently - current_value = self._parse_numeric_value(current_10min) - previous_value = self._parse_numeric_value(previous_10min) + # Check if we're in low hashrate mode + # A simple threshold approach: if hashrate_3hr is below 1 TH/s, consider it low hashrate mode + is_low_hashrate_mode = False + + if "hashrate_3hr" in current: + current_3hr = self._parse_numeric_value(current.get("hashrate_3hr", 0)) + current_3hr_unit = current.get("hashrate_3hr_unit", "TH/s").lower() - logging.debug(f"[NotificationService] Converted 10min hashrates - current: {current_value}, previous: {previous_value}") - + # Normalize to TH/s for comparison + if "ph/s" in current_3hr_unit: + current_3hr *= 1000 + elif "gh/s" in current_3hr_unit: + current_3hr /= 1000 + elif "mh/s" in current_3hr_unit: + current_3hr /= 1000000 + + # If hashrate is less than 3 TH/s, consider it low hashrate mode + is_low_hashrate_mode = current_3hr < 3.0 + + logging.debug(f"[NotificationService] Low hashrate mode: {is_low_hashrate_mode}") + + # Choose the appropriate hashrate metric based on mode + if is_low_hashrate_mode: + # In low hashrate mode, use 3hr averages for more stability + current_hashrate_key = "hashrate_3hr" + previous_hashrate_key = "hashrate_3hr" + timeframe = "3hr" + else: + # In normal mode, use 10min averages for faster response + current_hashrate_key = "hashrate_10min" + previous_hashrate_key = "hashrate_10min" + timeframe = "10min" + + # Get hashrate values + current_hashrate = current.get(current_hashrate_key, 0) + previous_hashrate = previous.get(previous_hashrate_key, 0) + + # Log what we're comparing + logging.debug(f"[NotificationService] Comparing {timeframe} hashrates - current: {current_hashrate}, previous: {previous_hashrate}") + + # Skip if values are missing + if not current_hashrate or not previous_hashrate: + logging.debug(f"[NotificationService] Skipping hashrate check - missing {timeframe} values") + return None + + # Parse values consistently + current_value = self._parse_numeric_value(current_hashrate) + previous_value = self._parse_numeric_value(previous_hashrate) + + logging.debug(f"[NotificationService] Converted {timeframe} hashrates - current: {current_value}, previous: {previous_value}") + # Skip if previous was zero (prevents division by zero) if previous_value == 0: - logging.debug("[NotificationService] Skipping hashrate check - previous was zero") + logging.debug(f"[NotificationService] Skipping hashrate check - previous {timeframe} was zero") return None - + # Calculate percentage change percent_change = ((current_value - previous_value) / previous_value) * 100 - logging.debug(f"[NotificationService] 10min hashrate change: {percent_change:.1f}%") - + logging.debug(f"[NotificationService] {timeframe} hashrate change: {percent_change:.1f}%") + # Significant decrease if percent_change <= -SIGNIFICANT_HASHRATE_CHANGE_PERCENT: - message = f"Significant 10min hashrate drop detected: {abs(percent_change):.1f}% decrease" + message = f"Significant {timeframe} hashrate drop detected: {abs(percent_change):.1f}% decrease" logging.info(f"[NotificationService] Generating hashrate notification: {message}") return self.add_notification( message, @@ -488,13 +521,14 @@ class NotificationService: "previous": previous_value, "current": current_value, "change": percent_change, - "timeframe": "10min" + "timeframe": timeframe, + "is_low_hashrate_mode": is_low_hashrate_mode } ) - + # Significant increase elif percent_change >= SIGNIFICANT_HASHRATE_CHANGE_PERCENT: - message = f"10min hashrate increase detected: {percent_change:.1f}% increase" + message = f"{timeframe} hashrate increase detected: {percent_change:.1f}% increase" logging.info(f"[NotificationService] Generating hashrate notification: {message}") return self.add_notification( message, @@ -504,10 +538,11 @@ class NotificationService: "previous": previous_value, "current": current_value, "change": percent_change, - "timeframe": "10min" + "timeframe": timeframe, + "is_low_hashrate_mode": is_low_hashrate_mode } ) - + return None except Exception as e: logging.error(f"[NotificationService] Error checking hashrate change: {e}") diff --git a/static/js/main.js b/static/js/main.js index 7cd4803..6eae195 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -1130,6 +1130,7 @@ function showCongrats(message) { // Enhanced Chart Update Function to handle temporary hashrate spikes // Modified the updateChartWithNormalizedData function to ensure the 24hr avg line is visible in low hashrate mode +// Enhanced Chart Update Function with localStorage persistence function updateChartWithNormalizedData(chart, data) { if (!chart || !data) { console.warn("Cannot update chart - chart or data is null"); @@ -1137,143 +1138,171 @@ function updateChartWithNormalizedData(chart, data) { } try { - // Process and validate 24hr average line data - let normalizedAvg = 0; - try { - const avg24hr = parseFloat(data.hashrate_24hr || 0); - const avg24hrUnit = data.hashrate_24hr_unit ? data.hashrate_24hr_unit.toLowerCase() : 'th/s'; - normalizedAvg = normalizeHashrate(avg24hr, avg24hrUnit); + // Try to load lowHashrate state from localStorage first + const storedLowHashrateState = localStorage.getItem('lowHashrateState'); - // Sanity check - if the value seems unreasonable, log a warning - if (normalizedAvg > 100000) { // Extremely large value (100,000+ TH/s) - console.warn(`WARNING: 24hr avg hashrate seems unreasonably high: ${normalizedAvg} TH/s`); - console.warn(`Original value: ${avg24hr} ${avg24hrUnit}`); - } - } catch (err) { - console.error("Error processing 24hr avg hashrate:", err); - normalizedAvg = 0; - } - - // Update the 24HR AVG line annotation if available - if (!isNaN(normalizedAvg) && - chart.options.plugins.annotation && - chart.options.plugins.annotation.annotations && - chart.options.plugins.annotation.annotations.averageLine) { - const annotation = chart.options.plugins.annotation.annotations.averageLine; - annotation.yMin = normalizedAvg; - annotation.yMax = normalizedAvg; - - // Use the formatting function already available to ensure consistent units - const formattedAvg = formatHashrateForDisplay(normalizedAvg); - annotation.label.content = '24HR AVG: ' + formattedAvg.toUpperCase(); - } - - // Process and validate current hashrates with better error handling - let normalizedHashrate60sec = 0; - let normalizedHashrate3hr = 0; - - try { - const hashrate60sec = parseFloat(data.hashrate_60sec || 0); - const hashrate60secUnit = data.hashrate_60sec_unit ? data.hashrate_60sec_unit.toLowerCase() : 'th/s'; - normalizedHashrate60sec = normalizeHashrate(hashrate60sec, hashrate60secUnit); - - const hashrate3hr = parseFloat(data.hashrate_3hr || 0); - const hashrate3hrUnit = data.hashrate_3hr_unit ? data.hashrate_3hr_unit.toLowerCase() : 'th/s'; - normalizedHashrate3hr = normalizeHashrate(hashrate3hr, hashrate3hrUnit); - - // Check for inconsistency between 60sec and 3hr values (could indicate unit issues) - const ratioThreshold = 100; // Maximum reasonable difference - if (normalizedHashrate60sec > 0 && normalizedHashrate3hr > 0) { - const ratio = Math.max( - normalizedHashrate60sec / normalizedHashrate3hr, - normalizedHashrate3hr / normalizedHashrate60sec - ); - - if (ratio > ratioThreshold) { - console.warn(`WARNING: Large discrepancy between 60sec and 3hr hashrates. Possible unit error!`); - console.warn(`60sec: ${hashrate60sec} ${hashrate60secUnit} → ${normalizedHashrate60sec} TH/s`); - console.warn(`3hr: ${hashrate3hr} ${hashrate3hrUnit} → ${normalizedHashrate3hr} TH/s`); - console.warn(`Ratio: ${ratio.toFixed(2)}x difference`); - } - } - } catch (err) { - console.error("Error processing current hashrates:", err); - } - - // Add persistence for mode switching with debounce - // Initialize mode state if not already present + // Initialize mode state by combining stored state with defaults if (!chart.lowHashrateState) { - chart.lowHashrateState = { + const defaultState = { isLowHashrateMode: false, highHashrateSpikeTime: 0, + spikeCount: 0, lowHashrateConfirmTime: 0, - modeSwitchTimeoutId: null + modeSwitchTimeoutId: null, + lastModeChange: 0, + stableModePeriod: 600000 }; + + // If we have stored state, use it + if (storedLowHashrateState) { + try { + const parsedState = JSON.parse(storedLowHashrateState); + chart.lowHashrateState = { + ...defaultState, + ...parsedState, + // Reset any volatile state that shouldn't persist + highHashrateSpikeTime: parsedState.highHashrateSpikeTime || 0, + modeSwitchTimeoutId: null + }; + console.log("Restored low hashrate mode from localStorage:", chart.lowHashrateState.isLowHashrateMode); + } catch (e) { + console.error("Error parsing stored low hashrate state:", e); + chart.lowHashrateState = defaultState; + } + } else { + chart.lowHashrateState = defaultState; + } } - // Choose which hashrate average to display based on device characteristics, - // with hysteresis to prevent rapid mode switching + // Get values with enhanced stability let useHashrate3hr = false; const currentTime = Date.now(); const LOW_HASHRATE_THRESHOLD = 0.01; // TH/s - const HIGH_HASHRATE_THRESHOLD = 2.0; // TH/s - const MODE_SWITCH_DELAY = 60000; // 60 seconds delay before switching back to normal mode after spike + const HIGH_HASHRATE_THRESHOLD = 20.0; // TH/s + const MODE_SWITCH_DELAY = 120000; // Increase to 2 minutes for more stability + const CONSECUTIVE_SPIKES_THRESHOLD = 3; // Increase to require more consistent high readings + const MIN_MODE_STABILITY_TIME = 120000; // 2 minutes minimum between mode switches + + // Check if we changed modes recently - enforce a minimum stability period + const timeSinceLastModeChange = currentTime - chart.lowHashrateState.lastModeChange; + const enforceStabilityPeriod = timeSinceLastModeChange < MIN_MODE_STABILITY_TIME; + + // IMPORTANT: Calculate normalized hashrate values + const normalizedHashrate60sec = normalizeHashrate(data.hashrate_60sec || 0, data.hashrate_60sec_unit || 'th/s'); + const normalizedHashrate3hr = normalizeHashrate(data.hashrate_3hr || 0, data.hashrate_3hr_unit || 'th/s'); + const normalizedAvg = normalizeHashrate(data.hashrate_24hr || 0, data.hashrate_24hr_unit || 'th/s'); + + // First check if we should use 3hr data based on the stored state + useHashrate3hr = chart.lowHashrateState.isLowHashrateMode; // Case 1: Currently in low hashrate mode if (chart.lowHashrateState.isLowHashrateMode) { - // If 60sec hashrate suddenly spikes above threshold - if (normalizedHashrate60sec >= HIGH_HASHRATE_THRESHOLD) { - // Record the spike time if not already recorded + // Default to staying in low hashrate mode + useHashrate3hr = true; + + // If we're enforcing stability, don't even check for mode change + if (!enforceStabilityPeriod && normalizedHashrate60sec >= HIGH_HASHRATE_THRESHOLD) { + // Only track spikes if we aren't in stability enforcement period if (!chart.lowHashrateState.highHashrateSpikeTime) { chart.lowHashrateState.highHashrateSpikeTime = currentTime; console.log("High hashrate spike detected in low hashrate mode"); } - // Don't switch modes immediately - check if spike has persisted + // Increment spike counter + chart.lowHashrateState.spikeCount++; + console.log(`Spike count: ${chart.lowHashrateState.spikeCount}/${CONSECUTIVE_SPIKES_THRESHOLD}`); + + // Check if spikes have persisted long enough const spikeElapsedTime = currentTime - chart.lowHashrateState.highHashrateSpikeTime; - // If the spike has persisted for longer than our delay, switch to normal mode - if (spikeElapsedTime > MODE_SWITCH_DELAY) { + if (chart.lowHashrateState.spikeCount >= CONSECUTIVE_SPIKES_THRESHOLD && + spikeElapsedTime > MODE_SWITCH_DELAY) { useHashrate3hr = false; chart.lowHashrateState.isLowHashrateMode = false; chart.lowHashrateState.highHashrateSpikeTime = 0; + chart.lowHashrateState.spikeCount = 0; + chart.lowHashrateState.lastModeChange = currentTime; console.log("Exiting low hashrate mode after sustained high hashrate"); + + // Save state changes to localStorage + saveLowHashrateState(chart.lowHashrateState); } else { - // Spike not persistent enough yet, stay in low hashrate mode - useHashrate3hr = true; - console.log(`Remaining in low hashrate mode despite spike (waiting: ${Math.round(spikeElapsedTime / 1000)}/${MODE_SWITCH_DELAY / 1000}s)`); + console.log(`Remaining in low hashrate mode despite spike (waiting: ${Math.round(spikeElapsedTime / 1000)}/${MODE_SWITCH_DELAY / 1000}s, count: ${chart.lowHashrateState.spikeCount}/${CONSECUTIVE_SPIKES_THRESHOLD})`); } } else { - // Reset spike timer if hashrate dropped back down - if (chart.lowHashrateState.highHashrateSpikeTime) { - console.log("Hashrate spike ended, resetting timer"); - chart.lowHashrateState.highHashrateSpikeTime = 0; - } + // Don't reset counters immediately on every drop - make the counter more persistent + if (chart.lowHashrateState.spikeCount > 0 && normalizedHashrate60sec < HIGH_HASHRATE_THRESHOLD) { + // Don't reset immediately, use a gradual decay approach + if (Math.random() < 0.2) { // 20% chance to decrement counter each update + chart.lowHashrateState.spikeCount--; + console.log("Spike counter decayed to:", chart.lowHashrateState.spikeCount); - // Continue using 3hr average in low hashrate mode - useHashrate3hr = true; + // Save state changes to localStorage + saveLowHashrateState(chart.lowHashrateState); + } + } } } // Case 2: Currently in normal mode else { - // Switch to low hashrate mode if: - // 1. 60sec average is near zero (appears offline) AND - // 2. 3hr average shows actual mining activity - if (normalizedHashrate60sec < LOW_HASHRATE_THRESHOLD && normalizedHashrate3hr > LOW_HASHRATE_THRESHOLD) { + // Default to staying in normal mode + useHashrate3hr = false; + + // Don't switch to low hashrate mode immediately if we recently switched modes + if (!enforceStabilityPeriod && normalizedHashrate60sec < LOW_HASHRATE_THRESHOLD && normalizedHashrate3hr > LOW_HASHRATE_THRESHOLD) { // Record when low hashrate condition was first observed if (!chart.lowHashrateState.lowHashrateConfirmTime) { chart.lowHashrateState.lowHashrateConfirmTime = currentTime; console.log("Low hashrate condition detected"); + + // Save state changes to localStorage + saveLowHashrateState(chart.lowHashrateState); } - // Immediately switch to low hashrate mode - useHashrate3hr = true; - chart.lowHashrateState.isLowHashrateMode = true; - console.log("Entering low hashrate mode"); + // Require at least 60 seconds of low hashrate before switching modes + const lowHashrateTime = currentTime - chart.lowHashrateState.lowHashrateConfirmTime; + if (lowHashrateTime > 60000) { // 1 minute + useHashrate3hr = true; + chart.lowHashrateState.isLowHashrateMode = true; + chart.lowHashrateState.lastModeChange = currentTime; + console.log("Entering low hashrate mode after persistent low hashrate condition"); + + // Save state changes to localStorage + saveLowHashrateState(chart.lowHashrateState); + } else { + console.log(`Low hashrate detected but waiting for persistence: ${Math.round(lowHashrateTime / 1000)}/60s`); + } } else { - // Reset low hashrate confirmation timer - chart.lowHashrateState.lowHashrateConfirmTime = 0; - useHashrate3hr = false; + // Only reset the confirmation timer if we've been above threshold consistently + if (chart.lowHashrateState.lowHashrateConfirmTime && + currentTime - chart.lowHashrateState.lowHashrateConfirmTime > 30000) { // 30 seconds above threshold + chart.lowHashrateState.lowHashrateConfirmTime = 0; + console.log("Low hashrate condition cleared after consistent normal hashrate"); + + // Save state changes to localStorage + saveLowHashrateState(chart.lowHashrateState); + } else if (chart.lowHashrateState.lowHashrateConfirmTime) { + console.log("Brief hashrate spike, maintaining low hashrate detection timer"); + } + } + } + + // Helper function to save lowHashrateState to localStorage + function saveLowHashrateState(state) { + try { + // Create a clean copy without circular references or functions + const stateToSave = { + isLowHashrateMode: state.isLowHashrateMode, + highHashrateSpikeTime: state.highHashrateSpikeTime, + spikeCount: state.spikeCount, + lowHashrateConfirmTime: state.lowHashrateConfirmTime, + lastModeChange: state.lastModeChange, + stableModePeriod: state.stableModePeriod + }; + localStorage.setItem('lowHashrateState', JSON.stringify(stateToSave)); + console.log("Saved low hashrate state:", state.isLowHashrateMode); + } catch (e) { + console.error("Error saving low hashrate state to localStorage:", e); } } @@ -1281,12 +1310,22 @@ function updateChartWithNormalizedData(chart, data) { if (data.arrow_history && data.arrow_history.hashrate_60sec) { // Validate history data try { - console.log("History data received:", data.arrow_history.hashrate_60sec); + // Log 60sec data + console.log("60sec history data received:", data.arrow_history.hashrate_60sec); + + // Also log 3hr data if available + if (data.arrow_history.hashrate_3hr) { + console.log("3hr history data received:", data.arrow_history.hashrate_3hr); + } else { + console.log("3hr history data not available in API response"); + } // If we're using 3hr average, try to use that history if available const historyData = useHashrate3hr && data.arrow_history.hashrate_3hr ? data.arrow_history.hashrate_3hr : data.arrow_history.hashrate_60sec; + console.log("Using history data:", useHashrate3hr ? "3hr data" : "60sec data"); + if (historyData && historyData.length > 0) { // Format time labels chart.data.labels = historyData.map(item => { @@ -1395,15 +1434,52 @@ function updateChartWithNormalizedData(chart, data) { // Set appropriate step size based on range const range = chart.options.scales.y.max - chart.options.scales.y.min; - if (range > 1000) { - chart.options.scales.y.ticks.stepSize = 500; - } else if (range > 100) { - chart.options.scales.y.ticks.stepSize = 50; - } else if (range > 10) { - chart.options.scales.y.ticks.stepSize = 5; + + // Calculate an appropriate stepSize that won't exceed Chart.js tick limits + // Aim for approximately 5-10 ticks (Chart.js recommends ~5-6 ticks for readability) + let stepSize; + const targetTicks = 6; // Target number of ticks we want to display + + if (range <= 0.1) { + // For very small ranges (< 0.1 TH/s) + stepSize = 0.01; + } else if (range <= 1) { + // For small ranges (0.1 - 1 TH/s) + stepSize = 0.1; + } else if (range <= 10) { + // For medium ranges (1 - 10 TH/s) + stepSize = 1; + } else if (range <= 50) { + stepSize = 5; + } else if (range <= 100) { + stepSize = 10; + } else if (range <= 500) { + stepSize = 50; + } else if (range <= 1000) { + stepSize = 100; + } else if (range <= 5000) { + stepSize = 500; } else { - chart.options.scales.y.ticks.stepSize = 1; + // For very large ranges, calculate stepSize that will produce ~targetTicks ticks + stepSize = Math.ceil(range / targetTicks); + + // Round to a nice number (nearest power of 10 multiple) + const magnitude = Math.pow(10, Math.floor(Math.log10(stepSize))); + stepSize = Math.ceil(stepSize / magnitude) * magnitude; + + // Safety check for extremely large ranges + if (range / stepSize > 1000) { + console.warn(`Y-axis range (${range.toFixed(2)}) requires extremely large stepSize. + Adjusting to limit ticks to 1000.`); + stepSize = Math.ceil(range / 1000); + } } + + // Set the calculated stepSize + chart.options.scales.y.ticks.stepSize = stepSize; + + // Log the chosen stepSize for debugging + console.log(`Y-axis range: ${range.toFixed(2)}, using stepSize: ${stepSize}`); } } else { console.warn("No valid history data items available"); @@ -1508,6 +1584,31 @@ function updateChartWithNormalizedData(chart, data) { chart.lowHashrateIndicator.style.display = 'none'; } + // UPDATE THE 24HR AVERAGE LINE ANNOTATION - THIS WAS MISSING + if (chart.options && chart.options.plugins && chart.options.plugins.annotation && + chart.options.plugins.annotation.annotations && chart.options.plugins.annotation.annotations.averageLine) { + + // Get current theme for styling + const theme = getCurrentTheme(); + + // Update the position of the average line to match the 24hr hashrate + chart.options.plugins.annotation.annotations.averageLine.yMin = normalizedAvg; + chart.options.plugins.annotation.annotations.averageLine.yMax = normalizedAvg; + + // Update the annotation label + const formattedAvg = formatHashrateForDisplay(data.hashrate_24hr, data.hashrate_24hr_unit); + chart.options.plugins.annotation.annotations.averageLine.label.content = + `24HR AVG: ${formattedAvg}`; + + // Set the color based on current theme + chart.options.plugins.annotation.annotations.averageLine.borderColor = theme.CHART.ANNOTATION; + chart.options.plugins.annotation.annotations.averageLine.label.color = theme.CHART.ANNOTATION; + + console.log(`Updated 24hr average line: ${normalizedAvg.toFixed(2)} TH/s`); + } else { + console.warn("Chart annotation plugin not properly configured"); + } + // Finally update the chart with a safe non-animating update chart.update('none'); } catch (chartError) { @@ -2304,6 +2405,111 @@ function resetWalletAddressOnly() { }); } +// Function to show a helpful notification to the user about hashrate normalization +function showHashrateNormalizeNotice() { + // Only show if the notification doesn't already exist on the page + if ($("#hashrateNormalizeNotice").length === 0) { + const theme = getCurrentTheme(); + + // Create notification element with theme-appropriate styling + const notice = $(` +
+
+
+ +
+
+
Hashrate Chart Notice
+
Please wait 2-3 minutes for the chart to collect data and normalize for your hashrate pattern.
+
+
+
+ + +
+
+ `); + + // Add to body and handle close button + $("body").append(notice); + + // Handler for the close button + $("#hashrateNoticeClose").on("click", function () { + // Check if "Don't show again" is checked + if ($("#dontShowAgain").is(":checked")) { + // Remember permanently in localStorage + localStorage.setItem('hideHashrateNotice', 'true'); + console.log("User chose to permanently hide hashrate notice"); + } else { + // Only remember for this session + sessionStorage.setItem('hideHashrateNoticeSession', 'true'); + console.log("User dismissed hashrate notice for this session"); + } + + // Hide and remove the notice + $("#hashrateNormalizeNotice").fadeOut(300, function () { + $(this).remove(); + }); + }); + + // Auto-hide after 60 seconds + setTimeout(function () { + if ($("#hashrateNormalizeNotice").length) { + $("#hashrateNormalizeNotice").fadeOut(500, function () { + $(this).remove(); + }); + } + }, 60000); // Changed to 60 seconds for better visibility + } +} + +// Helper function to check if we should show the notice (call this during page initialization) +function checkAndShowHashrateNotice() { + // Check if user has permanently hidden the notice + const permanentlyHidden = localStorage.getItem('hideHashrateNotice') === 'true'; + + // Check if user has hidden the notice for this session + const sessionHidden = sessionStorage.getItem('hideHashrateNoticeSession') === 'true'; + + // Also check low hashrate mode state (to potentially show a different message) + const inLowHashrateMode = localStorage.getItem('lowHashrateState') ? + JSON.parse(localStorage.getItem('lowHashrateState')).isLowHashrateMode : false; + + if (!permanentlyHidden && !sessionHidden) { + // Show the notice with a short delay to ensure the page is fully loaded + setTimeout(function () { + showHashrateNormalizeNotice(); + }, 2000); + } else { + console.log("Hashrate notice will not be shown: permanently hidden = " + + permanentlyHidden + ", session hidden = " + sessionHidden); + } +} + $(document).ready(function () { // Apply theme based on stored preference - moved to beginning for better initialization try { @@ -2539,6 +2745,13 @@ $(document).ready(function () { } }; + // No need to create a copy of lowHashrateState here, + // as we'll load it from localStorage after chart recreation + + // Save the low hashrate indicator element if it exists + const wasInLowHashrateMode = trendChart.lowHashrateState && + trendChart.lowHashrateState.isLowHashrateMode; + // Check if we're on mobile (viewport width < 768px) const isMobile = window.innerWidth < 768; @@ -2551,6 +2764,8 @@ $(document).ready(function () { trendChart.destroy(); trendChart = initializeChart(); + // The state will be automatically loaded from localStorage in updateChartWithNormalizedData + // Ensure font sizes are explicitly set to original values // This is especially important for mobile if (isMobile) { @@ -2673,6 +2888,33 @@ $(document).ready(function () { // Call this function fixLastBlockLine(); + // Check if we should show the hashrate normalization notice + checkAndShowHashrateNotice(); + + // Also show notice when entering low hashrate mode for the first time in a session + // Track when we enter low hashrate mode to show specialized notification + const originalUpdateChartWithNormalizedData = updateChartWithNormalizedData; + window.updateChartWithNormalizedData = function (chart, data) { + const wasInLowHashrateMode = chart && chart.lowHashrateState && + chart.lowHashrateState.isLowHashrateMode; + + // Call original function + originalUpdateChartWithNormalizedData(chart, data); + + // Check if we just entered low hashrate mode + if (chart && chart.lowHashrateState && + chart.lowHashrateState.isLowHashrateMode && !wasInLowHashrateMode) { + + console.log("Entered low hashrate mode - showing notification"); + + // Show the notice if it hasn't been permanently hidden + if (localStorage.getItem('hideHashrateNotice') !== 'true' && + !$("#hashrateNormalizeNotice").length) { + showHashrateNormalizeNotice(); + } + } + }; + // Load timezone setting early (function loadTimezoneEarly() { // First try to get from localStorage for instant access diff --git a/templates/base.html b/templates/base.html index 88140cd..a98fc07 100644 --- a/templates/base.html +++ b/templates/base.html @@ -135,7 +135,7 @@ diff --git a/templates/boot.html b/templates/boot.html index eb63472..22d2471 100644 --- a/templates/boot.html +++ b/templates/boot.html @@ -131,7 +131,7 @@ v.21