Better feedback when auto-updating and improve handling of disconnects and restarts
This commit is contained in:
parent
4057e18755
commit
468e105adf
12 changed files with 378 additions and 255 deletions
11
src/lib/components/Placeholder.svelte
Normal file
11
src/lib/components/Placeholder.svelte
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">
|
||||
export let value: unknown;
|
||||
export let checkValue: unknown = null;
|
||||
export let width: number = 25;
|
||||
|
||||
$: valueToCheck = checkValue === null ? value : checkValue;
|
||||
</script>
|
||||
|
||||
<span class:placeholder={!valueToCheck} class={!valueToCheck ? `w-${width}` : ''}>
|
||||
{valueToCheck ? value : ''}
|
||||
</span>
|
|
@ -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';
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
|
||||
let settings = writable({
|
||||
fgColor: '0',
|
||||
bgColor: '0'
|
||||
bgColor: '0',
|
||||
isLoaded: false
|
||||
});
|
||||
|
||||
let status = writable({
|
||||
|
@ -25,21 +26,33 @@
|
|||
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) => {
|
||||
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) => {
|
||||
const fetchSettingsData = async () => {
|
||||
const res = await fetch(PUBLIC_BASE_URL + `/api/settings`, { credentials: 'same-origin' });
|
||||
|
||||
if (!res.ok) {
|
||||
console.error('Error fetching settings data:', res.statusText);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
data.fgColor = String(data.fgColor);
|
||||
data.bgColor = String(data.bgColor);
|
||||
data.timePerScreen = data.timerSeconds / 60;
|
||||
|
@ -52,7 +65,6 @@
|
|||
data.bgColor = '65535';
|
||||
}
|
||||
settings.set(data);
|
||||
});
|
||||
};
|
||||
|
||||
let sections: (HTMLElement | null)[];
|
||||
|
@ -89,19 +101,46 @@
|
|||
}
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
onMount(async () => {
|
||||
setupObserver();
|
||||
|
||||
fetchSettingsData();
|
||||
fetchStatusData();
|
||||
|
||||
const connectEventSource = () => {
|
||||
console.log('Connecting to EventSource');
|
||||
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('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) {
|
||||
observer.disconnect();
|
||||
|
@ -159,7 +198,7 @@
|
|||
</svelte:head>
|
||||
|
||||
<Container fluid>
|
||||
<Row>
|
||||
<Row class="placeholder-glow">
|
||||
<Control bind:settings on:showToast={showToast} bind:status lg="3" xxl="4"></Control>
|
||||
|
||||
<Status bind:settings bind:status lg="6" xxl="4"></Status>
|
||||
|
|
|
@ -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 @@
|
|||
</li>
|
||||
{/if}
|
||||
<li>
|
||||
{$_('section.control.buildTime')}: {new Date(
|
||||
$settings.lastBuildTime * 1000
|
||||
).toLocaleString()}
|
||||
{$_('section.control.buildTime')}: <Placeholder
|
||||
value={new Date($settings.lastBuildTime * 1000).toLocaleString()}
|
||||
checkValue={$settings.lastBuildTime}
|
||||
/>
|
||||
</li>
|
||||
<li>IP: {$settings.ip}</li>
|
||||
<li>HW revision: {$settings.hwRev}</li>
|
||||
<li>{$_('section.control.fwCommit')}: {$settings.gitRev}</li>
|
||||
<li>WebUI commit: {$settings.fsRev}</li>
|
||||
<li>{$_('section.control.hostname')}: {$settings.hostname}</li>
|
||||
<li>IP: <Placeholder value={$settings.ip} /></li>
|
||||
<li>HW revision: <Placeholder value={$settings.hwRev} /></li>
|
||||
<li>{$_('section.control.fwCommit')}: <Placeholder value={$settings.gitRev} /></li>
|
||||
<li>WebUI commit: <Placeholder value={$settings.fsRev} /></li>
|
||||
<li>{$_('section.control.hostname')}: <Placeholder value={$settings.hostname} /></li>
|
||||
</ul>
|
||||
<Row>
|
||||
<Col class="d-flex justify-content-end">
|
||||
|
@ -239,7 +241,7 @@
|
|||
{#if $settings.otaEnabled}
|
||||
<hr />
|
||||
<h3>{$_('section.control.firmwareUpdate')}</h3>
|
||||
<FirmwareUpdater on:showToast bind:settings />
|
||||
<FirmwareUpdater on:showToast bind:settings bind:status />
|
||||
{/if}
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
|
|
@ -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} -
|
||||
<a href={releaseUrl} target="_blank">{$_('section.firmwareUpdater.viewRelease')}</a><br />
|
||||
{#if isNewerVersionAvailable}
|
||||
{#if !$status.isOTAUpdating}
|
||||
{$_('section.firmwareUpdater.swUpdateAvailable')} -
|
||||
<a href="/" on:click={onAutoUpdate}>{$_('section.firmwareUpdater.autoUpdate')}</a>.
|
||||
{:else}
|
||||
<HourglassSplitIcon /> {$_('section.firmwareUpdater.autoUpdateInProgress')}
|
||||
{/if}
|
||||
{:else}
|
||||
{$_('section.firmwareUpdater.swUpToDate')}
|
||||
{/if}
|
||||
|
@ -218,7 +223,8 @@
|
|||
{:else}
|
||||
<p>Loading...</p>
|
||||
{/if}
|
||||
<section class="row row-cols-lg-auto align-items-end">
|
||||
{#if !$status.isOTAUpdating}
|
||||
<section class="row row-cols-lg-auto align-items-end">
|
||||
<div class="col flex-fill">
|
||||
<label for="firmwareFile" class="form-label">Firmware file ({getFirmwareBinaryName()})</label>
|
||||
<input
|
||||
|
@ -252,23 +258,24 @@
|
|||
>Update WebUI</Button
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
{#if firmwareUploadProgress > 0}
|
||||
</section>
|
||||
{#if firmwareUploadProgress > 0}
|
||||
<Progress striped value={firmwareUploadProgress} class="progress" id="firmwareUploadProgress"
|
||||
>{$_('section.firmwareUpdater.uploading')}... {firmwareUploadProgress}%</Progress
|
||||
>
|
||||
{/if}
|
||||
{#if firmwareUploadSuccess}
|
||||
{/if}
|
||||
{#if firmwareUploadSuccess}
|
||||
<Alert color="success" class="firmwareUploadStatusAlert"
|
||||
>{$_('section.firmwareUpdater.fileUploadSuccess', { values: { countdown: $countdown } })}
|
||||
</Alert>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if firmwareUploadError}
|
||||
{#if firmwareUploadError}
|
||||
<Alert color="danger" class="firmwareUploadStatusAlert"
|
||||
>{$_('section.firmwareUpdater.fileUploadFailed')}</Alert
|
||||
>
|
||||
{/if}
|
||||
<small
|
||||
{/if}
|
||||
<small
|
||||
>⚠️ <strong>{$_('warning')}</strong>: {$_('section.firmwareUpdater.firmwareUpdateText')}</small
|
||||
>
|
||||
>
|
||||
{/if}
|
||||
|
|
|
@ -122,6 +122,12 @@
|
|||
<CardTitle>{$_('section.settings.title')}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
{#if $settings.isLoaded === false}
|
||||
<div class="d-flex align-items-center">
|
||||
<strong role="status">Loading...</strong>
|
||||
<div class="spinner-border ms-auto" aria-hidden="true"></div>
|
||||
</div>
|
||||
{:else}
|
||||
<Form on:submit={onSave}>
|
||||
<ScreenSpecificSettings {settings} bind:isOpen={screenSettingsIsOpen} />
|
||||
<DisplaySettings {settings} bind:isOpen={displaySettingsIsOpen} />
|
||||
|
@ -145,6 +151,7 @@
|
|||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
{/if}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
|
|
|
@ -111,6 +111,12 @@
|
|||
<CardTitle>{$_('section.status.title', { default: 'Status' })}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
{#if $settings.isLoaded === false}
|
||||
<div class="d-flex align-items-center">
|
||||
<strong role="status">Loading...</strong>
|
||||
<div class="spinner-border ms-auto" aria-hidden="true"></div>
|
||||
</div>
|
||||
{:else}
|
||||
{#if $settings.screens}
|
||||
<div class=" d-block d-sm-none mx-auto text-center">
|
||||
{#each buttonChunks as chunk}
|
||||
|
@ -151,7 +157,16 @@
|
|||
{/if}
|
||||
<hr />
|
||||
{#if $status.data}
|
||||
<section class={lightMode ? 'lightMode' : 'darkMode'}>
|
||||
<section class={lightMode ? 'lightMode' : 'darkMode'} style="position: relative;">
|
||||
{#if $status.isUpdating === false}
|
||||
<div class="connection-lost-overlay">
|
||||
<div class="overlay-content">
|
||||
<i class="bi bi-wifi-off"></i>
|
||||
<h4>Lost connection</h4>
|
||||
<p>Trying to reconnect...</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<Rendered
|
||||
status={$status}
|
||||
className="btclock-wrapper"
|
||||
|
@ -265,6 +280,10 @@
|
|||
<small>{$_('section.status.fetchEuroNote')}</small>
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
|
|
Loading…
Reference in a new issue