diff --git a/src/lib/components/Placeholder.svelte b/src/lib/components/Placeholder.svelte new file mode 100644 index 0000000..3eecfdc --- /dev/null +++ b/src/lib/components/Placeholder.svelte @@ -0,0 +1,11 @@ + + + + {valueToCheck ? value : ''} + diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts index 53338a1..b026c1c 100644 --- a/src/lib/components/index.ts +++ b/src/lib/components/index.ts @@ -3,3 +3,4 @@ export { default as SettingsInput } from './SettingsInput.svelte'; export { default as SettingsSelect } from './SettingsSelect.svelte'; export { default as ToggleHeader } from './ToggleHeader.svelte'; export { default as ColorSchemeSwitcher } from './ColorSchemeSwitcher.svelte'; +export { default as Placeholder } from './Placeholder.svelte'; diff --git a/src/lib/locales/de.json b/src/lib/locales/de.json index 852e68b..796b166 100644 --- a/src/lib/locales/de.json +++ b/src/lib/locales/de.json @@ -107,7 +107,8 @@ "latestVersion": "Letzte Version", "releaseDate": "Veröffentlichungsdatum", "viewRelease": "Veröffentlichung anzeigen", - "autoUpdate": "Update installieren (experimentell)" + "autoUpdate": "Update installieren (experimentell)", + "autoUpdateInProgress": "Automatische Aktualisierung läuft, bitte warten..." } }, "colors": { diff --git a/src/lib/locales/en.json b/src/lib/locales/en.json index 4cd430c..8336ac1 100644 --- a/src/lib/locales/en.json +++ b/src/lib/locales/en.json @@ -126,7 +126,8 @@ "latestVersion": "Latest Version", "releaseDate": "Release Date", "viewRelease": "View Release", - "autoUpdate": "Install update (experimental)" + "autoUpdate": "Install update (experimental)", + "autoUpdateInProgress": "Auto-update in progress, please wait..." } }, "colors": { diff --git a/src/lib/locales/es.json b/src/lib/locales/es.json index 96824e3..1b318a9 100644 --- a/src/lib/locales/es.json +++ b/src/lib/locales/es.json @@ -106,7 +106,8 @@ "latestVersion": "Ultima versión", "releaseDate": "Fecha de lanzamiento", "viewRelease": "Ver lanzamiento", - "autoUpdate": "Instalar actualización (experimental)" + "autoUpdate": "Instalar actualización (experimental)", + "autoUpdateInProgress": "Actualización automática en progreso, espere..." } }, "button": { diff --git a/src/lib/locales/nl.json b/src/lib/locales/nl.json index eb5b5af..96cfc6c 100644 --- a/src/lib/locales/nl.json +++ b/src/lib/locales/nl.json @@ -97,7 +97,8 @@ "latestVersion": "Laatste versie", "releaseDate": "Datum van publicatie", "viewRelease": "Bekijk publicatie", - "autoUpdate": "Update installeren (experimenteel)" + "autoUpdate": "Update installeren (experimenteel)", + "autoUpdateInProgress": "Automatische update wordt uitgevoerd. Even geduld a.u.b...." } }, "colors": { diff --git a/src/lib/style/app.scss b/src/lib/style/app.scss index c09af73..dd3d50a 100644 --- a/src/lib/style/app.scss +++ b/src/lib/style/app.scss @@ -53,6 +53,8 @@ $input-font-size-sm: $font-size-base * 0.875; @import '../node_modules/bootstrap/scss/tooltip'; @import '../node_modules/bootstrap/scss/toasts'; @import '../node_modules/bootstrap/scss/alert'; +@import '../node_modules/bootstrap/scss/placeholders'; +@import '../node_modules/bootstrap/scss/spinners'; @import '../node_modules/bootstrap/scss/helpers'; @import '../node_modules/bootstrap/scss/utilities/api'; @@ -317,3 +319,34 @@ input[type='number'] { .lightMode .bitaxelogo { filter: brightness(0) saturate(100%); } + +.connection-lost-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.75); + z-index: 1050; + display: flex; + justify-content: center; + align-items: center; + + .overlay-content { + background-color: rgba(255, 255, 255, 0.75); + + padding: 0.5rem; + border-radius: 0.5rem; + text-align: center; + + i { + font-size: 1rem; + color: $danger; + margin-bottom: 1rem; + } + + h4 { + margin-bottom: 0.5rem; + } + } +} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index c2e4e3b..a6f0bfb 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -14,7 +14,8 @@ let settings = writable({ fgColor: '0', - bgColor: '0' + bgColor: '0', + isLoaded: false }); let status = writable({ @@ -25,34 +26,45 @@ price: false, blocks: false }, - leds: [] + leds: [], + isUpdating: false }); - const fetchStatusData = () => { - fetch(`${PUBLIC_BASE_URL}/api/status`, { credentials: 'same-origin' }) - .then((res) => res.json()) - .then((data) => { - status.set(data); - }); + const fetchStatusData = async () => { + const res = await fetch(`${PUBLIC_BASE_URL}/api/status`, { credentials: 'same-origin' }); + + if (!res.ok) { + console.error('Error fetching status data:', res.statusText); + return false; + } + + const data = await res.json(); + status.set(data); + return true; }; - const fetchSettingsData = () => { - fetch(PUBLIC_BASE_URL + `/api/settings`, { credentials: 'same-origin' }) - .then((res) => res.json()) - .then((data) => { - data.fgColor = String(data.fgColor); - data.bgColor = String(data.bgColor); - data.timePerScreen = data.timerSeconds / 60; + const fetchSettingsData = async () => { + const res = await fetch(PUBLIC_BASE_URL + `/api/settings`, { credentials: 'same-origin' }); - if (data.fgColor > 65535) { - data.fgColor = '65535'; - } + if (!res.ok) { + console.error('Error fetching settings data:', res.statusText); + return; + } - if (data.bgColor > 65535) { - data.bgColor = '65535'; - } - settings.set(data); - }); + const data = await res.json(); + + data.fgColor = String(data.fgColor); + data.bgColor = String(data.bgColor); + data.timePerScreen = data.timerSeconds / 60; + + if (data.fgColor > 65535) { + data.fgColor = '65535'; + } + + if (data.bgColor > 65535) { + data.bgColor = '65535'; + } + settings.set(data); }; let sections: (HTMLElement | null)[]; @@ -89,18 +101,45 @@ } }; - onMount(() => { + onMount(async () => { setupObserver(); - fetchSettingsData(); - fetchStatusData(); + const connectEventSource = () => { + console.log('Connecting to EventSource'); + const evtSource = new EventSource(`${PUBLIC_BASE_URL}/events`); - const evtSource = new EventSource(`${PUBLIC_BASE_URL}/events`); + evtSource.addEventListener('status', (e) => { + let dataObj = JSON.parse(e.data); + status.update((s) => ({ ...s, isUpdating: true })); + status.set(dataObj); + }); - evtSource.addEventListener('status', (e) => { - let dataObj = JSON.parse(e.data); - status.set(dataObj); - }); + evtSource.addEventListener('message', (e) => { + if (e.data == 'closing') { + console.log('EventSource closing'); + status.update((s) => ({ ...s, isUpdating: false })); + evtSource.close(); // Close the current connection + setTimeout(connectEventSource, 5000); + } + }); + + evtSource.addEventListener('error', (e) => { + console.error('EventSource failed:', e); + status.update((s) => ({ ...s, isUpdating: false })); + evtSource.close(); // Close the current connection + setTimeout(connectEventSource, 1000); + }); + }; + + try { + await fetchSettingsData(); + if (await fetchStatusData()) { + settings.update((s) => ({ ...s, isLoaded: true })); + connectEventSource(); + } + } catch (error) { + console.log('Error fetching data:', error); + } function handleResize() { if (observer) { @@ -159,7 +198,7 @@ - + diff --git a/src/routes/Control.svelte b/src/routes/Control.svelte index 25f5a44..fc3d76e 100644 --- a/src/routes/Control.svelte +++ b/src/routes/Control.svelte @@ -17,6 +17,7 @@ } from '@sveltestrap/sveltestrap'; import FirmwareUpdater from './FirmwareUpdater.svelte'; import { uiSettings } from '$lib/uiSettings'; + import { Placeholder } from '$lib/components'; export let settings = {}; @@ -214,15 +215,16 @@ {/if}
  • - {$_('section.control.buildTime')}: {new Date( - $settings.lastBuildTime * 1000 - ).toLocaleString()} + {$_('section.control.buildTime')}:
  • -
  • IP: {$settings.ip}
  • -
  • HW revision: {$settings.hwRev}
  • -
  • {$_('section.control.fwCommit')}: {$settings.gitRev}
  • -
  • WebUI commit: {$settings.fsRev}
  • -
  • {$_('section.control.hostname')}: {$settings.hostname}
  • +
  • IP:
  • +
  • HW revision:
  • +
  • {$_('section.control.fwCommit')}:
  • +
  • WebUI commit:
  • +
  • {$_('section.control.hostname')}:
  • @@ -239,7 +241,7 @@ {#if $settings.otaEnabled}

    {$_('section.control.firmwareUpdate')}

    - + {/if} diff --git a/src/routes/FirmwareUpdater.svelte b/src/routes/FirmwareUpdater.svelte index 27b1b63..06f4d0e 100644 --- a/src/routes/FirmwareUpdater.svelte +++ b/src/routes/FirmwareUpdater.svelte @@ -4,11 +4,12 @@ import { _ } from 'svelte-i18n'; import { writable } from 'svelte/store'; import { Progress, Alert, Button } from '@sveltestrap/sveltestrap'; + import HourglassSplitIcon from 'svelte-bootstrap-icons/lib/HourglassSplit.svelte'; const dispatch = createEventDispatcher(); export let settings = { hwRev: '' }; - + export let status = writable({ isOTAUpdating: false }); let currentVersion: string = $settings.gitTag; // Replace with your current version let latestVersion: string = ''; @@ -207,8 +208,12 @@ )}: {releaseDate} - {$_('section.firmwareUpdater.viewRelease')}
    {#if isNewerVersionAvailable} - {$_('section.firmwareUpdater.swUpdateAvailable')} - - {$_('section.firmwareUpdater.autoUpdate')}. + {#if !$status.isOTAUpdating} + {$_('section.firmwareUpdater.swUpdateAvailable')} - + {$_('section.firmwareUpdater.autoUpdate')}. + {:else} + {$_('section.firmwareUpdater.autoUpdateInProgress')} + {/if} {:else} {$_('section.firmwareUpdater.swUpToDate')} {/if} @@ -218,57 +223,59 @@ {:else}

    Loading...

    {/if} -
    -
    - - handleFileChange(e, (file) => (firmwareUploadFile = file))} - name="update" - class="form-control" - accept=".bin" - /> -
    -
    - +
    + + handleFileChange(e, (file) => (firmwareUploadFile = file))} + name="update" + class="form-control" + accept=".bin" + /> +
    +
    + +
    +
    + + handleFileChange(e, (file) => (firmwareWebUiFile = file))} + accept=".bin" + /> +
    +
    + +
    +
    + {#if firmwareUploadProgress > 0} + {$_('section.firmwareUpdater.uploading')}... {firmwareUploadProgress}% - -
    - - handleFileChange(e, (file) => (firmwareWebUiFile = file))} - accept=".bin" - /> -
    -
    - -
    - -{#if firmwareUploadProgress > 0} - {$_('section.firmwareUpdater.uploading')}... {firmwareUploadProgress}% -{/if} -{#if firmwareUploadSuccess} - {$_('section.firmwareUpdater.fileUploadSuccess', { values: { countdown: $countdown } })} - -{/if} + {/if} + {#if firmwareUploadSuccess} + {$_('section.firmwareUpdater.fileUploadSuccess', { values: { countdown: $countdown } })} + + {/if} -{#if firmwareUploadError} - {$_('section.firmwareUpdater.fileUploadFailed')}{$_('section.firmwareUpdater.fileUploadFailed')} + {/if} + ⚠️ {$_('warning')}: {$_('section.firmwareUpdater.firmwareUpdateText')} {/if} -⚠️ {$_('warning')}: {$_('section.firmwareUpdater.firmwareUpdateText')} diff --git a/src/routes/Settings.svelte b/src/routes/Settings.svelte index 1210ad6..17a5df1 100644 --- a/src/routes/Settings.svelte +++ b/src/routes/Settings.svelte @@ -122,29 +122,36 @@ {$_('section.settings.title')} -
    - - - - - + {#if $settings.isLoaded === false} +
    + Loading... + +
    + {:else} + + + + + + - - - - - - - + + + + + + + + {/if}
    diff --git a/src/routes/Status.svelte b/src/routes/Status.svelte index 678a23a..1061fc3 100644 --- a/src/routes/Status.svelte +++ b/src/routes/Status.svelte @@ -111,11 +111,29 @@ {$_('section.status.title', { default: 'Status' })} - {#if $settings.screens} -
    - {#each buttonChunks as chunk} - - {#each chunk as s} + {#if $settings.isLoaded === false} +
    + Loading... + +
    + {:else} + {#if $settings.screens} +
    + {#each buttonChunks as chunk} + + {#each chunk as s} + + {/each} + + {/each} +
    +
    + + {#each $settings.screens as s}
    -
    - - {#each $settings.screens as s} - - {/each} - -
    - {#if $settings.actCurrencies && $settings.ownDataSource} -
    - - {#each $settings.actCurrencies as c} - - {/each} -
    - {/if} -
    - {#if $status.data} -
    - -
    - {$_('section.status.screenCycle')}: - {#if $status.timerRunning}⏵ {$_('timer.running')}{:else}⏸ {$_( - 'timer.stopped' - )}{/if} - {/if} - {/if} -
    - {#if !$settings.disableLeds} - - {#if $status.leds} - {#each $status.leds as led} - - - - {/each} + {#if $settings.actCurrencies && $settings.ownDataSource} +
    + + {#each $settings.actCurrencies as c} + + {/each} + +
    {/if} -
    +
    + {#if $status.data} +
    + {#if $status.isUpdating === false} +
    +
    + +

    Lost connection

    +

    Trying to reconnect...

    +
    +
    + {/if} + +
    + {$_('section.status.screenCycle')}: + {#if $status.timerRunning}⏵ {$_('timer.running')}{:else}⏸ {$_( + 'timer.stopped' + )}{/if} + {/if} + {/if}
    - {/if} - {memoryFreePercent}% -
    -
    {$_('section.status.memoryFree')}
    -
    - {Math.round($status.espFreeHeap / 1024)} / {Math.round($status.espHeapSize / 1024)} KiB -
    -
    -
    - {#if $settings.hasLightLevel} - {$_('section.status.lightSensor')}: {Number(Math.round($status.lightLevel))} lux -
    - {/if} - {rssiPercent}% - {$_('rssiBar.tooltip')} - -
    -
    {$_('section.status.wifiSignalStrength')}
    -
    - {$status.rssi} dBm -
    -
    -
    - {$_('section.status.uptime')}: {toUptimestring($status.espUptime)} -
    -

    - {#if $settings.dataSource == DataSourceType.NOSTR_SOURCE || $settings.nostrZapNotify} - {$_('section.status.nostrConnection')}: - - {#if $status.connectionStatus && $status.connectionStatus.nostr} - ✅ - {:else} - ❌ + {#if !$settings.disableLeds} + + {#if $status.leds} + {#each $status.leds as led} + + + + {/each} {/if} - + +


    {/if} - {#if $settings.dataSource != DataSourceType.NOSTR_SOURCE} - {#if $settings.dataSource == DataSourceType.THIRD_PARTY_SOURCE} - {$_('section.status.wsPriceConnection')}: + {memoryFreePercent}% +
    +
    {$_('section.status.memoryFree')}
    +
    + {Math.round($status.espFreeHeap / 1024)} / {Math.round($status.espHeapSize / 1024)} KiB +
    +
    +
    + {#if $settings.hasLightLevel} + {$_('section.status.lightSensor')}: {Number(Math.round($status.lightLevel))} lux +
    + {/if} + {rssiPercent}% + {$_('rssiBar.tooltip')} + +
    +
    {$_('section.status.wifiSignalStrength')}
    +
    + {$status.rssi} dBm +
    +
    +
    + {$_('section.status.uptime')}: {toUptimestring($status.espUptime)} +
    +

    + {#if $settings.dataSource == DataSourceType.NOSTR_SOURCE || $settings.nostrZapNotify} + {$_('section.status.nostrConnection')}: - {#if $status.connectionStatus && $status.connectionStatus.price} - ✅ - {:else} - ❌ - {/if} - - - - {$_('section.status.wsMempoolConnection', { - values: { instance: $settings.mempoolInstance } - })}: - - {#if $status.connectionStatus && $status.connectionStatus.blocks} - ✅ - {:else} - ❌ - {/if} -
    - {:else} - {$_('section.status.wsDataConnection')}: - - {#if $status.connectionStatus && $status.connectionStatus.V2} + {#if $status.connectionStatus && $status.connectionStatus.nostr} ✅ {:else} ❌ {/if} {/if} - {/if} - {#if $settings.fetchEurPrice} - {$_('section.status.fetchEuroNote')} - {/if} -

    + {#if $settings.dataSource != DataSourceType.NOSTR_SOURCE} + {#if $settings.dataSource == DataSourceType.THIRD_PARTY_SOURCE} + {$_('section.status.wsPriceConnection')}: + + {#if $status.connectionStatus && $status.connectionStatus.price} + ✅ + {:else} + ❌ + {/if} + + - + {$_('section.status.wsMempoolConnection', { + values: { instance: $settings.mempoolInstance } + })}: + + {#if $status.connectionStatus && $status.connectionStatus.blocks} + ✅ + {:else} + ❌ + {/if} +
    + {:else} + {$_('section.status.wsDataConnection')}: + + {#if $status.connectionStatus && $status.connectionStatus.V2} + ✅ + {:else} + ❌ + {/if} + + {/if} + {/if} + {#if $settings.fetchEurPrice} + {$_('section.status.fetchEuroNote')} + {/if} +

    + {/if} + +