diff --git a/lib/I18n/translations/english.yaml b/lib/I18n/translations/english.yaml index e3f4a23c..3a6f780d 100644 --- a/lib/I18n/translations/english.yaml +++ b/lib/I18n/translations/english.yaml @@ -456,6 +456,7 @@ STR_UPTIME: "Uptime" STR_CHARGING: "Charging" STR_GATHERING_DATA: "Gathering data..." STR_READING: "Reading..." +STR_SD_UPDATE_PROMPT: "Press Update to load SD info" STR_UNSUPPORTED_IMAGE_FORMAT: "Unsupported image format" STR_COULD_NOT_RENDER_IMAGE: "Could not render image" STR_FAILED_TO_SET_SLEEP_SCREEN: "Failed to set sleep screen" diff --git a/lib/I18n/translations/french.yaml b/lib/I18n/translations/french.yaml index 74dee8e8..609bc425 100644 --- a/lib/I18n/translations/french.yaml +++ b/lib/I18n/translations/french.yaml @@ -375,6 +375,7 @@ STR_FLASH_USED: "Flash utilisé" STR_UPTIME: "Temps d'activité" STR_CHARGING: "En charge" STR_GATHERING_DATA: "Collecte des données…" +STR_SD_UPDATE_PROMPT: "Appuyez sur mise à jour" STR_WEATHER: "Météo" STR_WEATHER_LOCATION: "Lieu" STR_WEATHER_NO_LOCATION: "Aucun lieu défini pour la météo" diff --git a/lib/I18n/translations/german.yaml b/lib/I18n/translations/german.yaml index b3bb55dc..1de48bd6 100644 --- a/lib/I18n/translations/german.yaml +++ b/lib/I18n/translations/german.yaml @@ -334,6 +334,7 @@ STR_UPTIME: "Laufzeit" STR_CHARGING: "Lädt" STR_GATHERING_DATA: "Daten werden gesammelt..." STR_READING: "Lese..." +STR_SD_UPDATE_PROMPT: "Zum Anzeigen aktualisieren" STR_UNSUPPORTED_IMAGE_FORMAT: "Nicht unterstütztes Bildformat" STR_COULD_NOT_RENDER_IMAGE: "Bild konnte nicht dargestellt werden" STR_FAILED_TO_SET_SLEEP_SCREEN: "Standby-Bild konnte nicht gesetzt werden" diff --git a/lib/I18n/translations/portuguese_br.yaml b/lib/I18n/translations/portuguese_br.yaml index d9131dad..9a1e4013 100644 --- a/lib/I18n/translations/portuguese_br.yaml +++ b/lib/I18n/translations/portuguese_br.yaml @@ -372,6 +372,7 @@ STR_FLASH_USED: "Flash usado" STR_UPTIME: "Tempo ativo" STR_CHARGING: "Carregando" STR_GATHERING_DATA: "Coletando dados..." +STR_SD_UPDATE_PROMPT: "Pressione atualizar" STR_WEATHER: "Clima" STR_WEATHER_LOCATION: "Localização" STR_WEATHER_NO_LOCATION: "Nenhuma localização definida" diff --git a/lib/I18n/translations/russian.yaml b/lib/I18n/translations/russian.yaml index 2a74fa7e..76098cb4 100644 --- a/lib/I18n/translations/russian.yaml +++ b/lib/I18n/translations/russian.yaml @@ -372,6 +372,7 @@ STR_FLASH_USED: "Flash занято" STR_UPTIME: "Время работы" STR_CHARGING: "Зарядка" STR_GATHERING_DATA: "Сбор данных..." +STR_SD_UPDATE_PROMPT: "Нажмите обновить" STR_WEATHER: "Погода" STR_WEATHER_LOCATION: "Местоположение" STR_WEATHER_NO_LOCATION: "Местоположение не задано" diff --git a/lib/I18n/translations/spanish.yaml b/lib/I18n/translations/spanish.yaml index e3aa458f..cb03c08b 100644 --- a/lib/I18n/translations/spanish.yaml +++ b/lib/I18n/translations/spanish.yaml @@ -375,6 +375,7 @@ STR_FLASH_USED: "Flash usado" STR_UPTIME: "Tiempo activo" STR_CHARGING: "Cargando" STR_GATHERING_DATA: "Recopilando datos..." +STR_SD_UPDATE_PROMPT: "Presiona actualizar" STR_WEATHER: "Clima" STR_WEATHER_LOCATION: "Ubicación" STR_WEATHER_NO_LOCATION: "No se ha configurado ubicación" diff --git a/platformio.ini b/platformio.ini index 901b652a..7e0c3df9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,6 +21,7 @@ board_upload.offset_address = 0x10000 build_flags = -DARDUINO_USB_MODE=1 + -DMAINTAIN_FREE_CLUSTER_COUNT=1 ; let sdFat cache cluster count data to speed up free space calculations -DARDUINO_USB_CDC_ON_BOOT=1 -DEINK_DISPLAY_SINGLE_BUFFER_MODE=1 -DDISABLE_FS_H_WARNING=1 diff --git a/src/activities/settings/SystemInformationActivity.cpp b/src/activities/settings/SystemInformationActivity.cpp index 7a79e538..1483247a 100644 --- a/src/activities/settings/SystemInformationActivity.cpp +++ b/src/activities/settings/SystemInformationActivity.cpp @@ -26,6 +26,7 @@ void SystemInformationActivity::onEnter() { Activity::onEnter(); status_.reset(); sdStatusReady_ = false; + sdLoadRequested_ = false; requestUpdate(); } @@ -36,6 +37,7 @@ void SystemInformationActivity::loop() { finish(); return; } + // Collect fast fields first so this page appears immediately. if (!status_.has_value()) { status_ = SystemStatus::collectFast(); @@ -43,11 +45,18 @@ void SystemInformationActivity::loop() { return; } - // SD stats can be slower to compute on large cards. + // SD stats can be slower to compute on large cards. Load them only when the + // user explicitly confirms. if (!sdStatusReady_) { - SystemStatus::fillSdStatus(*status_); - sdStatusReady_ = true; - requestUpdate(); + if (sdLoadRequested_) { + SystemStatus::fillSdStatus(*status_); + sdStatusReady_ = true; + sdLoadRequested_ = false; + requestUpdate(); + } else if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { + sdLoadRequested_ = true; + requestUpdate(); + } } } @@ -109,14 +118,15 @@ void SystemInformationActivity::render(RenderLock&&) { drawRow(8, tr(STR_UPTIME), uptimeBuf); if (!sdStatusReady_) { - drawRow(9, tr(STR_SD_CARD), tr(STR_READING)); + const char* sdMessage = sdLoadRequested_ ? tr(STR_READING) : tr(STR_SD_UPDATE_PROMPT); + drawRow(9, tr(STR_SD_CARD), sdMessage); } else if (status.sdTotalBytes > 0) { drawRow(9, tr(STR_SD_CARD), formatBytes(status.sdUsedBytes) + " / " + formatBytes(status.sdTotalBytes)); } else { drawRow(9, tr(STR_SD_CARD), tr(STR_NOT_SET)); } - const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", "", ""); + const auto labels = mappedInput.mapLabels(tr(STR_BACK), sdStatusReady_ ? "" : tr(STR_UPDATE), "", ""); GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4); renderer.displayBuffer(); diff --git a/src/activities/settings/SystemInformationActivity.h b/src/activities/settings/SystemInformationActivity.h index cb917f48..001bcccd 100644 --- a/src/activities/settings/SystemInformationActivity.h +++ b/src/activities/settings/SystemInformationActivity.h @@ -18,4 +18,5 @@ class SystemInformationActivity final : public Activity { private: std::optional status_; bool sdStatusReady_ = false; + bool sdLoadRequested_ = false; }; diff --git a/src/network/html/HomePage.html b/src/network/html/HomePage.html index ef52ed49..8a3e3507 100644 --- a/src/network/html/HomePage.html +++ b/src/network/html/HomePage.html @@ -110,6 +110,26 @@ background-color: var(--accent-hover-color); color: white; } + + .action-row { + text-align: center; + margin-top: 16px; + } + + .action-row button { + padding: 10px 18px; + border: none; + border-radius: 8px; + background-color: var(--accent-color); + color: white; + font-size: 1rem; + cursor: pointer; + } + + .action-row button:disabled { + opacity: 0.6; + cursor: default; + } @@ -174,7 +194,10 @@
SD Card - Loading... + Not loaded +
+
+
@@ -192,6 +215,13 @@ return parseFloat(val.toFixed(2)).toLocaleString() + ' ' + units[i]; } + const SD_LABELS = { + NOT_LOADED: 'Not loaded', + LOAD_BUTTON: 'Load SD info', + LOADING: 'Loading...', + ERROR: 'Error loading' + }; + function formatUptime(seconds) { if (seconds == null) return 'N/A'; const h = Math.floor(seconds / 3600); @@ -231,6 +261,8 @@ document.getElementById('sd-space').textContent = data.sdTotal ? formatBytes(data.sdUsed) + ' / ' + formatBytes(data.sdTotal) : 'N/A'; + } else { + document.getElementById('sd-space').textContent = SD_LABELS.NOT_LOADED; } } @@ -242,21 +274,45 @@ } const fastData = await fastResponse.json(); applyStatus(fastData, false); - document.getElementById('sd-space').textContent = 'Reading...'; - - const response = await fetch('/api/status'); - if (!response.ok) { - throw new Error('Failed to fetch full status: ' + response.status + ' ' + response.statusText); - } - const data = await response.json(); - applyStatus(data, true); + setSdButtonState(true); } catch (error) { console.error('Error fetching status:', error); + document.getElementById('sd-space').textContent = SD_LABELS.ERROR; } } - // Fetch status on page load - window.onload = fetchStatus; + async function fetchSdStatus() { + const button = document.getElementById('load-sd-button'); + button.disabled = true; + button.textContent = SD_LABELS.LOADING; + document.getElementById('sd-space').textContent = SD_LABELS.LOADING; + + try { + const response = await fetch('/api/status'); + if (!response.ok) { + throw new Error('Failed to fetch SD status: ' + response.status + ' ' + response.statusText); + } + const data = await response.json(); + applyStatus(data, true); + button.style.display = 'none'; + } catch (error) { + console.error('Error fetching SD status:', error); + button.disabled = false; + button.textContent = SD_LABELS.LOAD_BUTTON; + document.getElementById('sd-space').textContent = SD_LABELS.ERROR; + } + } + + function setSdButtonState(enabled) { + const button = document.getElementById('load-sd-button'); + button.disabled = !enabled; + button.textContent = enabled ? SD_LABELS.LOAD_BUTTON : SD_LABELS.LOADING; + } + + document.addEventListener('DOMContentLoaded', () => { + document.getElementById('load-sd-button').addEventListener('click', fetchSdStatus); + fetchStatus(); + });