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>
|
<title>Konfiguration</title>
|
||||||
<link rel="stylesheet" href="/style.css" />
|
<link rel="stylesheet" href="/style.css" />
|
||||||
<script type="module" src="/input-visibility.js" defer></script>
|
<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="/load-data.js" defer></script>
|
||||||
<script type="module" src="/submit.js" defer></script>
|
<script type="module" src="/submit.js" defer></script>
|
||||||
<script type="module" src="/reset.js" defer></script>
|
<script type="module" src="/reset.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<h1>Konfiguration</h1>
|
<section class="loading-screen">
|
||||||
<form>
|
<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>
|
<fieldset>
|
||||||
<legend>Verbindung</legend>
|
<legend>Verbindung</legend>
|
||||||
<label for="ip-method"
|
<label for="ip-method"
|
||||||
|
@ -63,8 +79,8 @@
|
||||||
id="input-connection"
|
id="input-connection"
|
||||||
title="Verbindung"
|
title="Verbindung"
|
||||||
>
|
>
|
||||||
<option value="0">WiFi-Station</option>
|
<option value="0">WiFi-AccessPoint</option>
|
||||||
<option value="1">WiFi-AccessPoint</option>
|
<option value="1">WiFi-Station</option>
|
||||||
<option value="2">Ethernet</option>
|
<option value="2">Ethernet</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -1,25 +1,27 @@
|
||||||
|
import {
|
||||||
|
showLoadingScreen,
|
||||||
|
showError,
|
||||||
|
hideLoadingScreen,
|
||||||
|
} from "./loading-screen.js";
|
||||||
|
|
||||||
const form = document.querySelector("form");
|
const form = document.querySelector("form");
|
||||||
|
|
||||||
async function loadData() {
|
export async function loadData(timeout = null) {
|
||||||
try {
|
const req = await fetch("/config", {
|
||||||
const req = await fetch("/config", {
|
method: "GET",
|
||||||
method: "GET",
|
signal: timeout !== null ? AbortSignal.timeout(timeout) : undefined,
|
||||||
});
|
});
|
||||||
if (!req.ok) {
|
if (!req.ok) {
|
||||||
throw new Error(`Response status: ${req.status}`);
|
throw new Error(`Response status: ${req.status}`);
|
||||||
}
|
|
||||||
|
|
||||||
const json = await req.json();
|
|
||||||
console.log(json);
|
|
||||||
return json;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const json = await req.json();
|
||||||
|
console.log(json);
|
||||||
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function writeDataToInput(data) {
|
export function writeDataToInput(data) {
|
||||||
console.log("write data", typeof data);
|
console.log("write data");
|
||||||
for (const [key, value] of Object.entries(data)) {
|
for (const [key, value] of Object.entries(data)) {
|
||||||
const element = document.querySelector(`[name=${key}]`);
|
const element = document.querySelector(`[name=${key}]`);
|
||||||
console.log(key, element);
|
console.log(key, element);
|
||||||
|
@ -34,7 +36,12 @@ export function writeDataToInput(data) {
|
||||||
form.dispatchEvent(new Event("change", { bubbles: true }));
|
form.dispatchEvent(new Event("change", { bubbles: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await loadData();
|
showLoadingScreen("Konfiguration wird geladen...");
|
||||||
if (data !== null) {
|
try {
|
||||||
|
const data = await loadData();
|
||||||
|
hideLoadingScreen();
|
||||||
writeDataToInput(data);
|
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");
|
const form = document.querySelector("form");
|
||||||
|
|
||||||
|
@ -9,22 +9,8 @@ form.addEventListener("reset", async (event) => {
|
||||||
"Sicher, dass du alle Einstellungen zurücksetzen möchtest?"
|
"Sicher, dass du alle Einstellungen zurücksetzen möchtest?"
|
||||||
);
|
);
|
||||||
if (ok) {
|
if (ok) {
|
||||||
reset();
|
updateConfig({
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function reset() {
|
|
||||||
try {
|
|
||||||
const res = await fetch("/config", {
|
|
||||||
method: "DELETE",
|
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 {
|
:root {
|
||||||
--color-primary: #087e8b;
|
--color-primary: #087e8b;
|
||||||
--color-on-primary: white;
|
--color-on-primary: white;
|
||||||
|
--color-background: #222;
|
||||||
--color-danger: #fa2b58;
|
--color-danger: #fa2b58;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
background-color: #222;
|
background-color: var(--color-background);
|
||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
padding: 8px max(5%, 8px);
|
padding: 8px max(5%, 8px);
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
@ -45,7 +46,7 @@ label {
|
||||||
input,
|
input,
|
||||||
select {
|
select {
|
||||||
width: clamp(200px, 100%, 400px);
|
width: clamp(200px, 100%, 400px);
|
||||||
background-color: #222;
|
background-color: var(--color-background);
|
||||||
color: white;
|
color: white;
|
||||||
border: 1px solid white;
|
border: 1px solid white;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
@ -78,7 +79,7 @@ button[type="reset"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
label.switch {
|
label.switch {
|
||||||
|
@ -136,3 +137,61 @@ label.switch input:checked + .slider::before {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 8px;
|
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");
|
const form = document.querySelector("form");
|
||||||
|
|
||||||
function parseValue(input) {
|
function parseValue(input) {
|
||||||
|
@ -31,25 +38,35 @@ form.addEventListener("submit", (event) => {
|
||||||
}, {});
|
}, {});
|
||||||
console.log(data);
|
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 {
|
try {
|
||||||
const res = await fetch("/config", {
|
const res = await fetch("/config", fetchOptions);
|
||||||
method: "PUT",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
});
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error(`Response status: ${res.status}`);
|
throw new Error(`Response status: ${res.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const json = await res.json();
|
// wait for the esp to restart
|
||||||
console.log(json);
|
const delay = new Promise((resolve) =>
|
||||||
|
setTimeout(() => resolve(), 500)
|
||||||
|
);
|
||||||
|
await delay;
|
||||||
|
|
||||||
|
const data = await loadData(30 * 1000);
|
||||||
|
writeDataToInput(data);
|
||||||
|
hideLoadingScreen();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error.message);
|
console.error(error.message);
|
||||||
|
showError(error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue