mirror of
https://github.com/HendrikRauh/dmx-interface.git
synced 2025-05-19 10:32:56 +00:00
implemented loading animation during requests
This commit is contained in:
parent
b8b87db0f2
commit
f0204c2477
6 changed files with 178 additions and 53 deletions
|
@ -6,14 +6,30 @@
|
|||
<title>Konfiguration</title>
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<script type="module" src="/input-visibility.js" defer></script>
|
||||
<script type="module" src="/loading-screen.js" defer></script>
|
||||
<script type="module" src="/load-data.js" defer></script>
|
||||
<script type="module" src="/submit.js" defer></script>
|
||||
<script type="module" src="/reset.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Konfiguration</h1>
|
||||
<form>
|
||||
<section class="loading-screen">
|
||||
<div class="spinner-container">
|
||||
<!-- h2 is filled dynamically -->
|
||||
<h2></h2>
|
||||
<div class="spinner"></div>
|
||||
<button
|
||||
class="reload"
|
||||
type="button"
|
||||
onclick="window.location.reload()"
|
||||
>
|
||||
Seite neu laden
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<form class="hidden">
|
||||
<h1>Konfiguration</h1>
|
||||
<fieldset>
|
||||
<legend>Verbindung</legend>
|
||||
<label for="ip-method"
|
||||
|
@ -63,8 +79,8 @@
|
|||
id="input-connection"
|
||||
title="Verbindung"
|
||||
>
|
||||
<option value="0">WiFi-Station</option>
|
||||
<option value="1">WiFi-AccessPoint</option>
|
||||
<option value="0">WiFi-AccessPoint</option>
|
||||
<option value="1">WiFi-Station</option>
|
||||
<option value="2">Ethernet</option>
|
||||
</select>
|
||||
</label>
|
||||
|
|
|
@ -1,25 +1,27 @@
|
|||
import {
|
||||
showLoadingScreen,
|
||||
showError,
|
||||
hideLoadingScreen,
|
||||
} from "./loading-screen.js";
|
||||
|
||||
const form = document.querySelector("form");
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
const req = await fetch("/config", {
|
||||
method: "GET",
|
||||
});
|
||||
if (!req.ok) {
|
||||
throw new Error(`Response status: ${req.status}`);
|
||||
}
|
||||
|
||||
const json = await req.json();
|
||||
console.log(json);
|
||||
return json;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return null;
|
||||
export async function loadData(timeout = null) {
|
||||
const req = await fetch("/config", {
|
||||
method: "GET",
|
||||
signal: timeout !== null ? AbortSignal.timeout(timeout) : undefined,
|
||||
});
|
||||
if (!req.ok) {
|
||||
throw new Error(`Response status: ${req.status}`);
|
||||
}
|
||||
|
||||
const json = await req.json();
|
||||
console.log(json);
|
||||
return json;
|
||||
}
|
||||
|
||||
export function writeDataToInput(data) {
|
||||
console.log("write data", typeof data);
|
||||
console.log("write data");
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
const element = document.querySelector(`[name=${key}]`);
|
||||
console.log(key, element);
|
||||
|
@ -34,7 +36,12 @@ export function writeDataToInput(data) {
|
|||
form.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
}
|
||||
|
||||
const data = await loadData();
|
||||
if (data !== null) {
|
||||
showLoadingScreen("Konfiguration wird geladen...");
|
||||
try {
|
||||
const data = await loadData();
|
||||
hideLoadingScreen();
|
||||
writeDataToInput(data);
|
||||
} catch (error) {
|
||||
console.log(error.message);
|
||||
showError("Die Konfiguration konnte nicht geladen werden.");
|
||||
}
|
||||
|
|
40
data/loading-screen.js
Normal file
40
data/loading-screen.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
const form = document.querySelector("form");
|
||||
const loadingScreen = document.querySelector(".loading-screen");
|
||||
const loadingMsg = loadingScreen.querySelector("h2");
|
||||
const spinner = loadingScreen.querySelector(".spinner");
|
||||
const reloadBtn = loadingScreen.querySelector(".reload");
|
||||
|
||||
export function showLoadingScreen(msg) {
|
||||
hide(form, reloadBtn);
|
||||
show(loadingScreen, spinner);
|
||||
loadingMsg.classList.remove("error");
|
||||
loadingMsg.textContent = msg;
|
||||
}
|
||||
|
||||
export function showError(msg) {
|
||||
showLoadingScreen(msg);
|
||||
loadingMsg.innerHTML +=
|
||||
"<br/>Stelle sicher, dass du mit dem DMX-Interface verbunden bist und die IP-Adresse stimmt.";
|
||||
show(reloadBtn);
|
||||
hide(spinner);
|
||||
loadingMsg.classList.add("error");
|
||||
}
|
||||
|
||||
export function hideLoadingScreen() {
|
||||
hide(loadingScreen, reloadBtn);
|
||||
show(form);
|
||||
loadingMsg.classList.remove("error");
|
||||
loadingMsg.textContent = "";
|
||||
}
|
||||
|
||||
function show(...elements) {
|
||||
for (const element of elements) {
|
||||
element.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function hide(...elements) {
|
||||
for (const element of elements) {
|
||||
element.classList.add("hidden");
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { writeDataToInput } from "/load-data.js";
|
||||
import { updateConfig } from "/submit.js";
|
||||
|
||||
const form = document.querySelector("form");
|
||||
|
||||
|
@ -9,22 +9,8 @@ form.addEventListener("reset", async (event) => {
|
|||
"Sicher, dass du alle Einstellungen zurücksetzen möchtest?"
|
||||
);
|
||||
if (ok) {
|
||||
reset();
|
||||
}
|
||||
});
|
||||
|
||||
async function reset() {
|
||||
try {
|
||||
const res = await fetch("/config", {
|
||||
updateConfig({
|
||||
method: "DELETE",
|
||||
});
|
||||
if (!res.ok) {
|
||||
throw new Error(`Response status: ${res.status}`);
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
writeDataToInput(json);
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
:root {
|
||||
--color-primary: #087e8b;
|
||||
--color-on-primary: white;
|
||||
--color-background: #222;
|
||||
--color-danger: #fa2b58;
|
||||
}
|
||||
|
||||
|
@ -14,7 +15,7 @@ body {
|
|||
}
|
||||
|
||||
main {
|
||||
background-color: #222;
|
||||
background-color: var(--color-background);
|
||||
max-width: 700px;
|
||||
padding: 8px max(5%, 8px);
|
||||
margin: 0 auto;
|
||||
|
@ -45,7 +46,7 @@ label {
|
|||
input,
|
||||
select {
|
||||
width: clamp(200px, 100%, 400px);
|
||||
background-color: #222;
|
||||
background-color: var(--color-background);
|
||||
color: white;
|
||||
border: 1px solid white;
|
||||
border-radius: 8px;
|
||||
|
@ -78,7 +79,7 @@ button[type="reset"] {
|
|||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
label.switch {
|
||||
|
@ -136,3 +137,61 @@ label.switch input:checked + .slider::before {
|
|||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.loading-screen {
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h2.error {
|
||||
color: var(--color-danger);
|
||||
}
|
||||
|
||||
button.reload {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.spinner-container {
|
||||
width: min(max-content, 100%);
|
||||
}
|
||||
|
||||
.spinner {
|
||||
position: relative;
|
||||
margin: 10px auto;
|
||||
background: conic-gradient(transparent 150deg, var(--color-primary));
|
||||
--outer-diameter: 50px;
|
||||
width: var(--outer-diameter);
|
||||
height: var(--outer-diameter);
|
||||
border-radius: 50%;
|
||||
|
||||
animation-name: spin;
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.spinner::after {
|
||||
position: absolute;
|
||||
content: "";
|
||||
display: block;
|
||||
--spinner-border: 5px;
|
||||
top: var(--spinner-border);
|
||||
left: var(--spinner-border);
|
||||
|
||||
--inner-diameter: calc(var(--outer-diameter) - 2 * var(--spinner-border));
|
||||
width: var(--inner-diameter);
|
||||
height: var(--inner-diameter);
|
||||
|
||||
background-color: var(--color-background);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
import { loadData, writeDataToInput } from "./load-data.js";
|
||||
import {
|
||||
showLoadingScreen,
|
||||
hideLoadingScreen,
|
||||
showError,
|
||||
} from "./loading-screen.js";
|
||||
|
||||
const form = document.querySelector("form");
|
||||
|
||||
function parseValue(input) {
|
||||
|
@ -31,25 +38,35 @@ form.addEventListener("submit", (event) => {
|
|||
}, {});
|
||||
console.log(data);
|
||||
|
||||
putData(data);
|
||||
updateConfig({
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
});
|
||||
|
||||
async function putData(data) {
|
||||
export async function updateConfig(fetchOptions) {
|
||||
showLoadingScreen("Konfiguration anwenden und ESP neustarten...");
|
||||
|
||||
try {
|
||||
const res = await fetch("/config", {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
const res = await fetch("/config", fetchOptions);
|
||||
if (!res.ok) {
|
||||
throw new Error(`Response status: ${res.status}`);
|
||||
}
|
||||
|
||||
const json = await res.json();
|
||||
console.log(json);
|
||||
// wait for the esp to restart
|
||||
const delay = new Promise((resolve) =>
|
||||
setTimeout(() => resolve(), 500)
|
||||
);
|
||||
await delay;
|
||||
|
||||
const data = await loadData(30 * 1000);
|
||||
writeDataToInput(data);
|
||||
hideLoadingScreen();
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
showError(error.message);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue