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 SettingsSelect } from './SettingsSelect.svelte';
|
||||||
export { default as ToggleHeader } from './ToggleHeader.svelte';
|
export { default as ToggleHeader } from './ToggleHeader.svelte';
|
||||||
export { default as ColorSchemeSwitcher } from './ColorSchemeSwitcher.svelte';
|
export { default as ColorSchemeSwitcher } from './ColorSchemeSwitcher.svelte';
|
||||||
|
export { default as Placeholder } from './Placeholder.svelte';
|
||||||
|
|
|
@ -107,7 +107,8 @@
|
||||||
"latestVersion": "Letzte Version",
|
"latestVersion": "Letzte Version",
|
||||||
"releaseDate": "Veröffentlichungsdatum",
|
"releaseDate": "Veröffentlichungsdatum",
|
||||||
"viewRelease": "Veröffentlichung anzeigen",
|
"viewRelease": "Veröffentlichung anzeigen",
|
||||||
"autoUpdate": "Update installieren (experimentell)"
|
"autoUpdate": "Update installieren (experimentell)",
|
||||||
|
"autoUpdateInProgress": "Automatische Aktualisierung läuft, bitte warten..."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"colors": {
|
"colors": {
|
||||||
|
|
|
@ -126,7 +126,8 @@
|
||||||
"latestVersion": "Latest Version",
|
"latestVersion": "Latest Version",
|
||||||
"releaseDate": "Release Date",
|
"releaseDate": "Release Date",
|
||||||
"viewRelease": "View Release",
|
"viewRelease": "View Release",
|
||||||
"autoUpdate": "Install update (experimental)"
|
"autoUpdate": "Install update (experimental)",
|
||||||
|
"autoUpdateInProgress": "Auto-update in progress, please wait..."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"colors": {
|
"colors": {
|
||||||
|
|
|
@ -106,7 +106,8 @@
|
||||||
"latestVersion": "Ultima versión",
|
"latestVersion": "Ultima versión",
|
||||||
"releaseDate": "Fecha de lanzamiento",
|
"releaseDate": "Fecha de lanzamiento",
|
||||||
"viewRelease": "Ver lanzamiento",
|
"viewRelease": "Ver lanzamiento",
|
||||||
"autoUpdate": "Instalar actualización (experimental)"
|
"autoUpdate": "Instalar actualización (experimental)",
|
||||||
|
"autoUpdateInProgress": "Actualización automática en progreso, espere..."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
|
|
|
@ -97,7 +97,8 @@
|
||||||
"latestVersion": "Laatste versie",
|
"latestVersion": "Laatste versie",
|
||||||
"releaseDate": "Datum van publicatie",
|
"releaseDate": "Datum van publicatie",
|
||||||
"viewRelease": "Bekijk publicatie",
|
"viewRelease": "Bekijk publicatie",
|
||||||
"autoUpdate": "Update installeren (experimenteel)"
|
"autoUpdate": "Update installeren (experimenteel)",
|
||||||
|
"autoUpdateInProgress": "Automatische update wordt uitgevoerd. Even geduld a.u.b...."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"colors": {
|
"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/tooltip';
|
||||||
@import '../node_modules/bootstrap/scss/toasts';
|
@import '../node_modules/bootstrap/scss/toasts';
|
||||||
@import '../node_modules/bootstrap/scss/alert';
|
@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/helpers';
|
||||||
@import '../node_modules/bootstrap/scss/utilities/api';
|
@import '../node_modules/bootstrap/scss/utilities/api';
|
||||||
|
@ -317,3 +319,34 @@ input[type='number'] {
|
||||||
.lightMode .bitaxelogo {
|
.lightMode .bitaxelogo {
|
||||||
filter: brightness(0) saturate(100%);
|
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({
|
let settings = writable({
|
||||||
fgColor: '0',
|
fgColor: '0',
|
||||||
bgColor: '0'
|
bgColor: '0',
|
||||||
|
isLoaded: false
|
||||||
});
|
});
|
||||||
|
|
||||||
let status = writable({
|
let status = writable({
|
||||||
|
@ -25,34 +26,45 @@
|
||||||
price: false,
|
price: false,
|
||||||
blocks: false
|
blocks: false
|
||||||
},
|
},
|
||||||
leds: []
|
leds: [],
|
||||||
|
isUpdating: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchStatusData = () => {
|
const fetchStatusData = async () => {
|
||||||
fetch(`${PUBLIC_BASE_URL}/api/status`, { credentials: 'same-origin' })
|
const res = await fetch(`${PUBLIC_BASE_URL}/api/status`, { credentials: 'same-origin' });
|
||||||
.then((res) => res.json())
|
|
||||||
.then((data) => {
|
if (!res.ok) {
|
||||||
status.set(data);
|
console.error('Error fetching status data:', res.statusText);
|
||||||
});
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
status.set(data);
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchSettingsData = () => {
|
const fetchSettingsData = async () => {
|
||||||
fetch(PUBLIC_BASE_URL + `/api/settings`, { credentials: 'same-origin' })
|
const res = await 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;
|
|
||||||
|
|
||||||
if (data.fgColor > 65535) {
|
if (!res.ok) {
|
||||||
data.fgColor = '65535';
|
console.error('Error fetching settings data:', res.statusText);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (data.bgColor > 65535) {
|
const data = await res.json();
|
||||||
data.bgColor = '65535';
|
|
||||||
}
|
data.fgColor = String(data.fgColor);
|
||||||
settings.set(data);
|
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)[];
|
let sections: (HTMLElement | null)[];
|
||||||
|
@ -89,18 +101,45 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(() => {
|
onMount(async () => {
|
||||||
setupObserver();
|
setupObserver();
|
||||||
|
|
||||||
fetchSettingsData();
|
const connectEventSource = () => {
|
||||||
fetchStatusData();
|
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) => {
|
evtSource.addEventListener('message', (e) => {
|
||||||
let dataObj = JSON.parse(e.data);
|
if (e.data == 'closing') {
|
||||||
status.set(dataObj);
|
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() {
|
function handleResize() {
|
||||||
if (observer) {
|
if (observer) {
|
||||||
|
@ -159,7 +198,7 @@
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Container fluid>
|
<Container fluid>
|
||||||
<Row>
|
<Row class="placeholder-glow">
|
||||||
<Control bind:settings on:showToast={showToast} bind:status lg="3" xxl="4"></Control>
|
<Control bind:settings on:showToast={showToast} bind:status lg="3" xxl="4"></Control>
|
||||||
|
|
||||||
<Status bind:settings bind:status lg="6" xxl="4"></Status>
|
<Status bind:settings bind:status lg="6" xxl="4"></Status>
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
} from '@sveltestrap/sveltestrap';
|
} from '@sveltestrap/sveltestrap';
|
||||||
import FirmwareUpdater from './FirmwareUpdater.svelte';
|
import FirmwareUpdater from './FirmwareUpdater.svelte';
|
||||||
import { uiSettings } from '$lib/uiSettings';
|
import { uiSettings } from '$lib/uiSettings';
|
||||||
|
import { Placeholder } from '$lib/components';
|
||||||
|
|
||||||
export let settings = {};
|
export let settings = {};
|
||||||
|
|
||||||
|
@ -214,15 +215,16 @@
|
||||||
</li>
|
</li>
|
||||||
{/if}
|
{/if}
|
||||||
<li>
|
<li>
|
||||||
{$_('section.control.buildTime')}: {new Date(
|
{$_('section.control.buildTime')}: <Placeholder
|
||||||
$settings.lastBuildTime * 1000
|
value={new Date($settings.lastBuildTime * 1000).toLocaleString()}
|
||||||
).toLocaleString()}
|
checkValue={$settings.lastBuildTime}
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li>IP: {$settings.ip}</li>
|
<li>IP: <Placeholder value={$settings.ip} /></li>
|
||||||
<li>HW revision: {$settings.hwRev}</li>
|
<li>HW revision: <Placeholder value={$settings.hwRev} /></li>
|
||||||
<li>{$_('section.control.fwCommit')}: {$settings.gitRev}</li>
|
<li>{$_('section.control.fwCommit')}: <Placeholder value={$settings.gitRev} /></li>
|
||||||
<li>WebUI commit: {$settings.fsRev}</li>
|
<li>WebUI commit: <Placeholder value={$settings.fsRev} /></li>
|
||||||
<li>{$_('section.control.hostname')}: {$settings.hostname}</li>
|
<li>{$_('section.control.hostname')}: <Placeholder value={$settings.hostname} /></li>
|
||||||
</ul>
|
</ul>
|
||||||
<Row>
|
<Row>
|
||||||
<Col class="d-flex justify-content-end">
|
<Col class="d-flex justify-content-end">
|
||||||
|
@ -239,7 +241,7 @@
|
||||||
{#if $settings.otaEnabled}
|
{#if $settings.otaEnabled}
|
||||||
<hr />
|
<hr />
|
||||||
<h3>{$_('section.control.firmwareUpdate')}</h3>
|
<h3>{$_('section.control.firmwareUpdate')}</h3>
|
||||||
<FirmwareUpdater on:showToast bind:settings />
|
<FirmwareUpdater on:showToast bind:settings bind:status />
|
||||||
{/if}
|
{/if}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import { Progress, Alert, Button } from '@sveltestrap/sveltestrap';
|
import { Progress, Alert, Button } from '@sveltestrap/sveltestrap';
|
||||||
|
import HourglassSplitIcon from 'svelte-bootstrap-icons/lib/HourglassSplit.svelte';
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
export let settings = { hwRev: '' };
|
export let settings = { hwRev: '' };
|
||||||
|
export let status = writable({ isOTAUpdating: false });
|
||||||
let currentVersion: string = $settings.gitTag; // Replace with your current version
|
let currentVersion: string = $settings.gitTag; // Replace with your current version
|
||||||
|
|
||||||
let latestVersion: string = '';
|
let latestVersion: string = '';
|
||||||
|
@ -207,8 +208,12 @@
|
||||||
)}: {releaseDate} -
|
)}: {releaseDate} -
|
||||||
<a href={releaseUrl} target="_blank">{$_('section.firmwareUpdater.viewRelease')}</a><br />
|
<a href={releaseUrl} target="_blank">{$_('section.firmwareUpdater.viewRelease')}</a><br />
|
||||||
{#if isNewerVersionAvailable}
|
{#if isNewerVersionAvailable}
|
||||||
{$_('section.firmwareUpdater.swUpdateAvailable')} -
|
{#if !$status.isOTAUpdating}
|
||||||
<a href="/" on:click={onAutoUpdate}>{$_('section.firmwareUpdater.autoUpdate')}</a>.
|
{$_('section.firmwareUpdater.swUpdateAvailable')} -
|
||||||
|
<a href="/" on:click={onAutoUpdate}>{$_('section.firmwareUpdater.autoUpdate')}</a>.
|
||||||
|
{:else}
|
||||||
|
<HourglassSplitIcon /> {$_('section.firmwareUpdater.autoUpdateInProgress')}
|
||||||
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
{$_('section.firmwareUpdater.swUpToDate')}
|
{$_('section.firmwareUpdater.swUpToDate')}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -218,57 +223,59 @@
|
||||||
{:else}
|
{:else}
|
||||||
<p>Loading...</p>
|
<p>Loading...</p>
|
||||||
{/if}
|
{/if}
|
||||||
<section class="row row-cols-lg-auto align-items-end">
|
{#if !$status.isOTAUpdating}
|
||||||
<div class="col flex-fill">
|
<section class="row row-cols-lg-auto align-items-end">
|
||||||
<label for="firmwareFile" class="form-label">Firmware file ({getFirmwareBinaryName()})</label>
|
<div class="col flex-fill">
|
||||||
<input
|
<label for="firmwareFile" class="form-label">Firmware file ({getFirmwareBinaryName()})</label>
|
||||||
type="file"
|
<input
|
||||||
id="firmwareFile"
|
type="file"
|
||||||
on:change={(e) => handleFileChange(e, (file) => (firmwareUploadFile = file))}
|
id="firmwareFile"
|
||||||
name="update"
|
on:change={(e) => handleFileChange(e, (file) => (firmwareUploadFile = file))}
|
||||||
class="form-control"
|
name="update"
|
||||||
accept=".bin"
|
class="form-control"
|
||||||
/>
|
accept=".bin"
|
||||||
</div>
|
/>
|
||||||
<div class="flex-fill">
|
</div>
|
||||||
<Button block on:click={uploadFirmwareFile} color="primary" disabled={!firmwareUploadFile}
|
<div class="flex-fill">
|
||||||
>Update firmware</Button
|
<Button block on:click={uploadFirmwareFile} color="primary" disabled={!firmwareUploadFile}
|
||||||
|
>Update firmware</Button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="col flex-fill">
|
||||||
|
<label for="webuiFile" class="form-label">WebUI file ({getWebUiBinaryName()})</label>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="webuiFile"
|
||||||
|
name="update"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="littlefs.bin"
|
||||||
|
on:change={(e) => handleFileChange(e, (file) => (firmwareWebUiFile = file))}
|
||||||
|
accept=".bin"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex-fill">
|
||||||
|
<Button block on:click={uploadWebUiFile} color="secondary" disabled={!firmwareWebUiFile}
|
||||||
|
>Update WebUI</Button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{#if firmwareUploadProgress > 0}
|
||||||
|
<Progress striped value={firmwareUploadProgress} class="progress" id="firmwareUploadProgress"
|
||||||
|
>{$_('section.firmwareUpdater.uploading')}... {firmwareUploadProgress}%</Progress
|
||||||
>
|
>
|
||||||
</div>
|
{/if}
|
||||||
<div class="col flex-fill">
|
{#if firmwareUploadSuccess}
|
||||||
<label for="webuiFile" class="form-label">WebUI file ({getWebUiBinaryName()})</label>
|
<Alert color="success" class="firmwareUploadStatusAlert"
|
||||||
<input
|
>{$_('section.firmwareUpdater.fileUploadSuccess', { values: { countdown: $countdown } })}
|
||||||
type="file"
|
</Alert>
|
||||||
id="webuiFile"
|
{/if}
|
||||||
name="update"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="littlefs.bin"
|
|
||||||
on:change={(e) => handleFileChange(e, (file) => (firmwareWebUiFile = file))}
|
|
||||||
accept=".bin"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex-fill">
|
|
||||||
<Button block on:click={uploadWebUiFile} color="secondary" disabled={!firmwareWebUiFile}
|
|
||||||
>Update WebUI</Button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{#if firmwareUploadProgress > 0}
|
|
||||||
<Progress striped value={firmwareUploadProgress} class="progress" id="firmwareUploadProgress"
|
|
||||||
>{$_('section.firmwareUpdater.uploading')}... {firmwareUploadProgress}%</Progress
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
{#if firmwareUploadSuccess}
|
|
||||||
<Alert color="success" class="firmwareUploadStatusAlert"
|
|
||||||
>{$_('section.firmwareUpdater.fileUploadSuccess', { values: { countdown: $countdown } })}
|
|
||||||
</Alert>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if firmwareUploadError}
|
{#if firmwareUploadError}
|
||||||
<Alert color="danger" class="firmwareUploadStatusAlert"
|
<Alert color="danger" class="firmwareUploadStatusAlert"
|
||||||
>{$_('section.firmwareUpdater.fileUploadFailed')}</Alert
|
>{$_('section.firmwareUpdater.fileUploadFailed')}</Alert
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
<small
|
||||||
|
>⚠️ <strong>{$_('warning')}</strong>: {$_('section.firmwareUpdater.firmwareUpdateText')}</small
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
<small
|
|
||||||
>⚠️ <strong>{$_('warning')}</strong>: {$_('section.firmwareUpdater.firmwareUpdateText')}</small
|
|
||||||
>
|
|
||||||
|
|
|
@ -122,29 +122,36 @@
|
||||||
<CardTitle>{$_('section.settings.title')}</CardTitle>
|
<CardTitle>{$_('section.settings.title')}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<Form on:submit={onSave}>
|
{#if $settings.isLoaded === false}
|
||||||
<ScreenSpecificSettings {settings} bind:isOpen={screenSettingsIsOpen} />
|
<div class="d-flex align-items-center">
|
||||||
<DisplaySettings {settings} bind:isOpen={displaySettingsIsOpen} />
|
<strong role="status">Loading...</strong>
|
||||||
<DataSourceSettings {settings} bind:isOpen={dataSourceIsOpen} on:showToast />
|
<div class="spinner-border ms-auto" aria-hidden="true"></div>
|
||||||
<ExtraFeaturesSettings
|
</div>
|
||||||
{settings}
|
{:else}
|
||||||
bind:isOpen={extraFeaturesIsOpen}
|
<Form on:submit={onSave}>
|
||||||
{miningPoolMap}
|
<ScreenSpecificSettings {settings} bind:isOpen={screenSettingsIsOpen} />
|
||||||
on:showToast
|
<DisplaySettings {settings} bind:isOpen={displaySettingsIsOpen} />
|
||||||
/>
|
<DataSourceSettings {settings} bind:isOpen={dataSourceIsOpen} on:showToast />
|
||||||
<SystemSettings {settings} bind:isOpen={systemIsOpen} />
|
<ExtraFeaturesSettings
|
||||||
|
{settings}
|
||||||
|
bind:isOpen={extraFeaturesIsOpen}
|
||||||
|
{miningPoolMap}
|
||||||
|
on:showToast
|
||||||
|
/>
|
||||||
|
<SystemSettings {settings} bind:isOpen={systemIsOpen} />
|
||||||
|
|
||||||
<Row class="mt-4">
|
<Row class="mt-4">
|
||||||
<Col>
|
<Col>
|
||||||
<Button type="submit" color="primary" class="me-2">
|
<Button type="submit" color="primary" class="me-2">
|
||||||
{$_('button.save')}
|
{$_('button.save')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="button" color="secondary" on:click={handleReset}>
|
<Button type="button" color="secondary" on:click={handleReset}>
|
||||||
{$_('button.reset')}
|
{$_('button.reset')}
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Form>
|
</Form>
|
||||||
|
{/if}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
@ -111,11 +111,29 @@
|
||||||
<CardTitle>{$_('section.status.title', { default: 'Status' })}</CardTitle>
|
<CardTitle>{$_('section.status.title', { default: 'Status' })}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
{#if $settings.screens}
|
{#if $settings.isLoaded === false}
|
||||||
<div class=" d-block d-sm-none mx-auto text-center">
|
<div class="d-flex align-items-center">
|
||||||
{#each buttonChunks as chunk}
|
<strong role="status">Loading...</strong>
|
||||||
<ButtonGroup size="sm" class="mx-auto mb-1">
|
<div class="spinner-border ms-auto" aria-hidden="true"></div>
|
||||||
{#each chunk as s}
|
</div>
|
||||||
|
{:else}
|
||||||
|
{#if $settings.screens}
|
||||||
|
<div class=" d-block d-sm-none mx-auto text-center">
|
||||||
|
{#each buttonChunks as chunk}
|
||||||
|
<ButtonGroup size="sm" class="mx-auto mb-1">
|
||||||
|
{#each chunk as s}
|
||||||
|
<Button
|
||||||
|
color="outline-primary"
|
||||||
|
active={$status.currentScreen == s.id}
|
||||||
|
on:click={setScreen(s.id)}>{s.name}</Button
|
||||||
|
>
|
||||||
|
{/each}
|
||||||
|
</ButtonGroup>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-center d-none d-sm-flex">
|
||||||
|
<ButtonGroup size="sm">
|
||||||
|
{#each $settings.screens as s}
|
||||||
<Button
|
<Button
|
||||||
color="outline-primary"
|
color="outline-primary"
|
||||||
active={$status.currentScreen == s.id}
|
active={$status.currentScreen == s.id}
|
||||||
|
@ -123,148 +141,149 @@
|
||||||
>
|
>
|
||||||
{/each}
|
{/each}
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
<div class="d-flex justify-content-center d-none d-sm-flex">
|
|
||||||
<ButtonGroup size="sm">
|
|
||||||
{#each $settings.screens as s}
|
|
||||||
<Button
|
|
||||||
color="outline-primary"
|
|
||||||
active={$status.currentScreen == s.id}
|
|
||||||
on:click={setScreen(s.id)}>{s.name}</Button
|
|
||||||
>
|
|
||||||
{/each}
|
|
||||||
</ButtonGroup>
|
|
||||||
</div>
|
|
||||||
{#if $settings.actCurrencies && $settings.ownDataSource}
|
|
||||||
<div class="d-flex justify-content-center d-sm-flex mt-2">
|
|
||||||
<ButtonGroup size="sm">
|
|
||||||
{#each $settings.actCurrencies as c}
|
|
||||||
<Button
|
|
||||||
color="outline-success"
|
|
||||||
active={$status.currency == c}
|
|
||||||
on:click={setCurrency(c)}>{c}</Button
|
|
||||||
>
|
|
||||||
{/each}
|
|
||||||
</ButtonGroup>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{#if $settings.actCurrencies && $settings.ownDataSource}
|
||||||
<hr />
|
<div class="d-flex justify-content-center d-sm-flex mt-2">
|
||||||
{#if $status.data}
|
<ButtonGroup size="sm">
|
||||||
<section class={lightMode ? 'lightMode' : 'darkMode'}>
|
{#each $settings.actCurrencies as c}
|
||||||
<Rendered
|
<Button
|
||||||
status={$status}
|
color="outline-success"
|
||||||
className="btclock-wrapper"
|
active={$status.currency == c}
|
||||||
verticalDesc={$settings.verticalDesc}
|
on:click={setCurrency(c)}>{c}</Button
|
||||||
></Rendered>
|
>
|
||||||
</section>
|
{/each}
|
||||||
{$_('section.status.screenCycle')}:
|
</ButtonGroup>
|
||||||
<a
|
</div>
|
||||||
id="timerStatusText"
|
|
||||||
href={'#'}
|
|
||||||
style="cursor: pointer"
|
|
||||||
tabindex="0"
|
|
||||||
role="button"
|
|
||||||
aria-pressed="false"
|
|
||||||
on:click={toggleTimer($status.timerRunning)}
|
|
||||||
>{#if $status.timerRunning}⏵ {$_('timer.running')}{:else}⏸ {$_(
|
|
||||||
'timer.stopped'
|
|
||||||
)}{/if}</a
|
|
||||||
>
|
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
<hr />
|
|
||||||
{#if !$settings.disableLeds}
|
|
||||||
<Row class="justify-content-evenly">
|
|
||||||
{#if $status.leds}
|
|
||||||
{#each $status.leds as led}
|
|
||||||
<Col>
|
|
||||||
<Input
|
|
||||||
type="color"
|
|
||||||
id="ledColorPicker"
|
|
||||||
bind:value={led.hex}
|
|
||||||
class="mx-auto"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
{/if}
|
||||||
</Row>
|
<hr />
|
||||||
|
{#if $status.data}
|
||||||
|
<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"
|
||||||
|
verticalDesc={$settings.verticalDesc}
|
||||||
|
></Rendered>
|
||||||
|
</section>
|
||||||
|
{$_('section.status.screenCycle')}:
|
||||||
|
<a
|
||||||
|
id="timerStatusText"
|
||||||
|
href={'#'}
|
||||||
|
style="cursor: pointer"
|
||||||
|
tabindex="0"
|
||||||
|
role="button"
|
||||||
|
aria-pressed="false"
|
||||||
|
on:click={toggleTimer($status.timerRunning)}
|
||||||
|
>{#if $status.timerRunning}⏵ {$_('timer.running')}{:else}⏸ {$_(
|
||||||
|
'timer.stopped'
|
||||||
|
)}{/if}</a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
<hr />
|
<hr />
|
||||||
{/if}
|
{#if !$settings.disableLeds}
|
||||||
<Progress striped value={memoryFreePercent}>{memoryFreePercent}%</Progress>
|
<Row class="justify-content-evenly">
|
||||||
<div class="d-flex justify-content-between">
|
{#if $status.leds}
|
||||||
<div>{$_('section.status.memoryFree')}</div>
|
{#each $status.leds as led}
|
||||||
<div>
|
<Col>
|
||||||
{Math.round($status.espFreeHeap / 1024)} / {Math.round($status.espHeapSize / 1024)} KiB
|
<Input
|
||||||
</div>
|
type="color"
|
||||||
</div>
|
id="ledColorPicker"
|
||||||
<hr />
|
bind:value={led.hex}
|
||||||
{#if $settings.hasLightLevel}
|
class="mx-auto"
|
||||||
{$_('section.status.lightSensor')}: {Number(Math.round($status.lightLevel))} lux
|
disabled
|
||||||
<hr />
|
/>
|
||||||
{/if}
|
</Col>
|
||||||
<Progress striped id="rssiBar" color={wifiStrengthColor} value={rssiPercent}
|
{/each}
|
||||||
>{rssiPercent}%</Progress
|
|
||||||
>
|
|
||||||
<Tooltip target="rssiBar" placement="bottom">{$_('rssiBar.tooltip')}</Tooltip>
|
|
||||||
|
|
||||||
<div class="d-flex justify-content-between">
|
|
||||||
<div>{$_('section.status.wifiSignalStrength')}</div>
|
|
||||||
<div>
|
|
||||||
{$status.rssi} dBm
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
{$_('section.status.uptime')}: {toUptimestring($status.espUptime)}
|
|
||||||
<br />
|
|
||||||
<p>
|
|
||||||
{#if $settings.dataSource == DataSourceType.NOSTR_SOURCE || $settings.nostrZapNotify}
|
|
||||||
{$_('section.status.nostrConnection')}:
|
|
||||||
<span>
|
|
||||||
{#if $status.connectionStatus && $status.connectionStatus.nostr}
|
|
||||||
✅
|
|
||||||
{:else}
|
|
||||||
❌
|
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</Row>
|
||||||
|
<hr />
|
||||||
{/if}
|
{/if}
|
||||||
{#if $settings.dataSource != DataSourceType.NOSTR_SOURCE}
|
<Progress striped value={memoryFreePercent}>{memoryFreePercent}%</Progress>
|
||||||
{#if $settings.dataSource == DataSourceType.THIRD_PARTY_SOURCE}
|
<div class="d-flex justify-content-between">
|
||||||
{$_('section.status.wsPriceConnection')}:
|
<div>{$_('section.status.memoryFree')}</div>
|
||||||
|
<div>
|
||||||
|
{Math.round($status.espFreeHeap / 1024)} / {Math.round($status.espHeapSize / 1024)} KiB
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
{#if $settings.hasLightLevel}
|
||||||
|
{$_('section.status.lightSensor')}: {Number(Math.round($status.lightLevel))} lux
|
||||||
|
<hr />
|
||||||
|
{/if}
|
||||||
|
<Progress striped id="rssiBar" color={wifiStrengthColor} value={rssiPercent}
|
||||||
|
>{rssiPercent}%</Progress
|
||||||
|
>
|
||||||
|
<Tooltip target="rssiBar" placement="bottom">{$_('rssiBar.tooltip')}</Tooltip>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div>{$_('section.status.wifiSignalStrength')}</div>
|
||||||
|
<div>
|
||||||
|
{$status.rssi} dBm
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
{$_('section.status.uptime')}: {toUptimestring($status.espUptime)}
|
||||||
|
<br />
|
||||||
|
<p>
|
||||||
|
{#if $settings.dataSource == DataSourceType.NOSTR_SOURCE || $settings.nostrZapNotify}
|
||||||
|
{$_('section.status.nostrConnection')}:
|
||||||
<span>
|
<span>
|
||||||
{#if $status.connectionStatus && $status.connectionStatus.price}
|
{#if $status.connectionStatus && $status.connectionStatus.nostr}
|
||||||
✅
|
|
||||||
{:else}
|
|
||||||
❌
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
-
|
|
||||||
{$_('section.status.wsMempoolConnection', {
|
|
||||||
values: { instance: $settings.mempoolInstance }
|
|
||||||
})}:
|
|
||||||
<span>
|
|
||||||
{#if $status.connectionStatus && $status.connectionStatus.blocks}
|
|
||||||
✅
|
|
||||||
{:else}
|
|
||||||
❌
|
|
||||||
{/if}
|
|
||||||
</span><br />
|
|
||||||
{:else}
|
|
||||||
{$_('section.status.wsDataConnection')}:
|
|
||||||
<span>
|
|
||||||
{#if $status.connectionStatus && $status.connectionStatus.V2}
|
|
||||||
✅
|
✅
|
||||||
{:else}
|
{:else}
|
||||||
❌
|
❌
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{#if $settings.dataSource != DataSourceType.NOSTR_SOURCE}
|
||||||
{#if $settings.fetchEurPrice}
|
{#if $settings.dataSource == DataSourceType.THIRD_PARTY_SOURCE}
|
||||||
<small>{$_('section.status.fetchEuroNote')}</small>
|
{$_('section.status.wsPriceConnection')}:
|
||||||
{/if}
|
<span>
|
||||||
</p>
|
{#if $status.connectionStatus && $status.connectionStatus.price}
|
||||||
|
✅
|
||||||
|
{:else}
|
||||||
|
❌
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
-
|
||||||
|
{$_('section.status.wsMempoolConnection', {
|
||||||
|
values: { instance: $settings.mempoolInstance }
|
||||||
|
})}:
|
||||||
|
<span>
|
||||||
|
{#if $status.connectionStatus && $status.connectionStatus.blocks}
|
||||||
|
✅
|
||||||
|
{:else}
|
||||||
|
❌
|
||||||
|
{/if}
|
||||||
|
</span><br />
|
||||||
|
{:else}
|
||||||
|
{$_('section.status.wsDataConnection')}:
|
||||||
|
<span>
|
||||||
|
{#if $status.connectionStatus && $status.connectionStatus.V2}
|
||||||
|
✅
|
||||||
|
{:else}
|
||||||
|
❌
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
{#if $settings.fetchEurPrice}
|
||||||
|
<small>{$_('section.status.fetchEuroNote')}</small>
|
||||||
|
{/if}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue