feat: extended theme switching #87
This commit is contained in:
@@ -72,6 +72,11 @@
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
li.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
button {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
@@ -138,6 +143,9 @@
|
||||
};
|
||||
|
||||
const registerReceiveEvents = () => {
|
||||
ipcRenderer.on("button-disabled", (e, button) => {
|
||||
document.getElementById(button.id).parentNode.classList.toggle("disabled", button.disabled);
|
||||
});
|
||||
ipcRenderer.on("text-content", (e, text) => {
|
||||
document.getElementById(text.id).textContent = text.content;
|
||||
});
|
||||
|
||||
@@ -78,6 +78,16 @@
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button id="theme">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<title>compare</title>
|
||||
<path
|
||||
d="M19,3H14V5H19V18L14,12V21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M10,18H5L10,12M10,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H10V23H12V1H10V3Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button id="keyboard">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
|
||||
+42
-2
@@ -48,12 +48,15 @@ const init = async () => {
|
||||
configuration_url: APP.homepage,
|
||||
};
|
||||
|
||||
// Connection settings
|
||||
// Init options
|
||||
const masked = password === null ? null : "*".repeat(password.length);
|
||||
const options = user === null || password === null ? {} : { username: user, password: password };
|
||||
options.rejectUnauthorized = !("ignore_certificate_errors" in ARGS);
|
||||
|
||||
// Client connect
|
||||
console.log("MQTT Connecting:", `${user}:${masked}@${url.toString()}`);
|
||||
INTEGRATION.client = mqtt.connect(url.toString(), options);
|
||||
INTEGRATION.client.setMaxListeners(20);
|
||||
|
||||
// Client connected
|
||||
INTEGRATION.client
|
||||
@@ -67,6 +70,7 @@ const init = async () => {
|
||||
initReboot();
|
||||
initRefresh();
|
||||
initKiosk();
|
||||
initTheme();
|
||||
initDisplay();
|
||||
initVolume();
|
||||
initKeyboard();
|
||||
@@ -97,6 +101,8 @@ const init = async () => {
|
||||
});
|
||||
|
||||
// Update sensor states from events
|
||||
EVENTS.on("updateStatus", updateKiosk);
|
||||
EVENTS.on("updateTheme", updateTheme);
|
||||
EVENTS.on("updateDisplay", updateDisplay);
|
||||
EVENTS.on("updateVolume", updateVolume);
|
||||
EVENTS.on("updateKeyboard", updateKeyboard);
|
||||
@@ -130,7 +136,6 @@ const update = async () => {
|
||||
}
|
||||
|
||||
// Update client sensors
|
||||
updateKiosk();
|
||||
updatePageNumber();
|
||||
updatePageZoom();
|
||||
updatePageUrl();
|
||||
@@ -379,6 +384,41 @@ const updateKiosk = async () => {
|
||||
publishState("kiosk", kiosk);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the application theme and handles the execute logic.
|
||||
*/
|
||||
const initTheme = () => {
|
||||
const root = `${INTEGRATION.root}/theme`;
|
||||
const config = {
|
||||
name: "Theme",
|
||||
unique_id: `${INTEGRATION.node}_theme`,
|
||||
command_topic: `${root}/set`,
|
||||
state_topic: `${root}/state`,
|
||||
value_template: "{{ value }}",
|
||||
options: ["Light", "Dark"],
|
||||
icon: "mdi:compare",
|
||||
device: INTEGRATION.device,
|
||||
};
|
||||
publishConfig("select", config)
|
||||
.on("message", (topic, message) => {
|
||||
if (topic === config.command_topic) {
|
||||
const theme = message.toString();
|
||||
console.log("Set Application Theme:", theme);
|
||||
WEBVIEW.theme.set(theme);
|
||||
}
|
||||
})
|
||||
.subscribe(config.command_topic);
|
||||
updateTheme();
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the application theme via the mqtt connection.
|
||||
*/
|
||||
const updateTheme = async () => {
|
||||
const theme = WEBVIEW.theme.get();
|
||||
publishState("theme", theme.charAt(0).toUpperCase() + theme.slice(1));
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the display status, brightness and handles the execute logic.
|
||||
*/
|
||||
|
||||
+63
-19
@@ -54,15 +54,37 @@ const init = async () => {
|
||||
WEBVIEW.viewActive = 0;
|
||||
WEBVIEW.viewUrls = urls;
|
||||
WEBVIEW.viewZoom = zoom;
|
||||
WEBVIEW.viewTheme = theme;
|
||||
WEBVIEW.pagerEnabled = widget;
|
||||
WEBVIEW.widgetTheme = theme;
|
||||
WEBVIEW.widgetEnabled = widget;
|
||||
WEBVIEW.statusTheme = theme;
|
||||
WEBVIEW.statusEnabled = true;
|
||||
WEBVIEW.navigationTheme = theme;
|
||||
WEBVIEW.pagerEnabled = widget;
|
||||
WEBVIEW.widgetEnabled = widget;
|
||||
WEBVIEW.navigationEnabled = widget;
|
||||
nativeTheme.themeSource = WEBVIEW.viewTheme;
|
||||
|
||||
// Init global theme
|
||||
WEBVIEW.theme = {
|
||||
set: (name) => {
|
||||
nativeTheme.themeSource = name.toLowerCase();
|
||||
},
|
||||
get: () => {
|
||||
return nativeTheme.shouldUseDarkColors ? "dark" : "light";
|
||||
},
|
||||
toggle: () => {
|
||||
nativeTheme.themeSource = nativeTheme.shouldUseDarkColors ? "light" : "dark";
|
||||
},
|
||||
register: () => {
|
||||
nativeTheme.on("updated", () => {
|
||||
const theme = WEBVIEW.theme.get();
|
||||
WEBVIEW.statusTheme = theme;
|
||||
WEBVIEW.widgetTheme = theme;
|
||||
WEBVIEW.navigationTheme = theme;
|
||||
if (WEBVIEW.initialized) {
|
||||
resizeView();
|
||||
}
|
||||
EVENTS.emit("updateTheme");
|
||||
});
|
||||
},
|
||||
};
|
||||
WEBVIEW.theme.register();
|
||||
WEBVIEW.theme.set(theme);
|
||||
|
||||
// Init global root window
|
||||
WEBVIEW.display = screen.getPrimaryDisplay().workAreaSize;
|
||||
@@ -190,10 +212,7 @@ const updateView = () => {
|
||||
|
||||
// Update window title
|
||||
console.log(`Update View: ${title}`);
|
||||
WEBVIEW.status.webContents.send("text-content", {
|
||||
id: "title",
|
||||
content: title,
|
||||
});
|
||||
WEBVIEW.status.webContents.send("text-content", { id: "title", content: title });
|
||||
WEBVIEW.window.setTitle(title);
|
||||
|
||||
// Hide all other webviews and show only the active one
|
||||
@@ -628,7 +647,7 @@ const windowEvents = async () => {
|
||||
const delta = (now - then) / 1000;
|
||||
|
||||
// Auto-hide navigation
|
||||
if (delta > 60) {
|
||||
if (delta > 60 && WEBVIEW.tracker.status !== "Terminated") {
|
||||
toggleNavigation("OFF");
|
||||
}
|
||||
}, 1000);
|
||||
@@ -675,6 +694,9 @@ const widgetEvents = async () => {
|
||||
// Handle widget button click events
|
||||
ipcMain.on("button-click", (e, button) => {
|
||||
switch (button.id) {
|
||||
case "theme":
|
||||
WEBVIEW.theme.toggle();
|
||||
break;
|
||||
case "keyboard":
|
||||
const toggle = hardware.getKeyboardVisibility() === "ON" ? "OFF" : "ON";
|
||||
hardware.setKeyboardVisibility(toggle, () => {
|
||||
@@ -711,14 +733,23 @@ const statusEvents = async () => {
|
||||
WEBVIEW.window.setStatus("Minimized");
|
||||
break;
|
||||
case "terminate":
|
||||
WEBVIEW.status.webContents.send("button-disabled", { id: "fullscreen", disabled: true });
|
||||
WEBVIEW.status.webContents.send("button-disabled", { id: "minimize", disabled: true });
|
||||
WEBVIEW.status.webContents.send("button-disabled", { id: "terminate", disabled: true });
|
||||
const button = dialog.showMessageBoxSync(WEBVIEW.window, {
|
||||
type: "question",
|
||||
title: "Confirm",
|
||||
message: `\nExit ${APP.title}?`,
|
||||
buttons: ["No", "Yes"],
|
||||
});
|
||||
if (button === 1) {
|
||||
switch (button) {
|
||||
case 1:
|
||||
WEBVIEW.window.setStatus("Terminated");
|
||||
break;
|
||||
default:
|
||||
WEBVIEW.status.webContents.send("button-disabled", { id: "fullscreen", disabled: false });
|
||||
WEBVIEW.status.webContents.send("button-disabled", { id: "minimize", disabled: false });
|
||||
WEBVIEW.status.webContents.send("button-disabled", { id: "terminate", disabled: false });
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -863,7 +894,7 @@ const viewEvents = async () => {
|
||||
if (WEBVIEW.viewActive === 0 && ready.length === WEBVIEW.views.length) {
|
||||
nextView();
|
||||
}
|
||||
view.webContents.loadURL(errorHtml(code, text, url));
|
||||
view.webContents.loadURL(errorHtml(code, text, url, WEBVIEW.theme.get()));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1089,9 +1120,14 @@ const loaderHtml = (size, speed, theme) => {
|
||||
* @param {number} code - The error code of the response.
|
||||
* @param {string} text - The error text of the response.
|
||||
* @param {string} url - The url of the requested page.
|
||||
* @param {string} theme - The theme used for text colors.
|
||||
* @returns {string} A data string with the generated html.
|
||||
*/
|
||||
const errorHtml = (code, text, url) => {
|
||||
const errorHtml = (code, text, url, theme) => {
|
||||
const color = {
|
||||
dark: { icon: "#FFA500", text: "#E5E5E5", background: "#111111" },
|
||||
light: { icon: "#FFA500", text: "#1A1A1A", background: "#FAFAFA" },
|
||||
}[theme];
|
||||
const html = `
|
||||
<html>
|
||||
<head>
|
||||
@@ -1099,17 +1135,25 @@ const errorHtml = (code, text, url) => {
|
||||
body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
font-family: sans-serif;
|
||||
text-align: center;
|
||||
background-color: ${color.background};
|
||||
}
|
||||
.icon {
|
||||
margin: 0;
|
||||
font-size: 5rem;
|
||||
color: orange;
|
||||
color: ${color.icon};
|
||||
}
|
||||
.title {
|
||||
margin: 0;
|
||||
color: ${color.text};
|
||||
}
|
||||
.url {
|
||||
color: ${color.text};
|
||||
}
|
||||
.error {
|
||||
color: ${color.text};
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -1117,8 +1161,8 @@ const errorHtml = (code, text, url) => {
|
||||
<div>
|
||||
<p class="icon">⚠</p>
|
||||
<h1 class="title">Whoopsie!</h1>
|
||||
<p><strong>Loading:</strong> ${url}</p>
|
||||
<p><strong>Error:</strong> ${text} (${code})</p>
|
||||
<p class="url"><strong>Loading:</strong> ${url}</p>
|
||||
<p class="error"><strong>Error:</strong> ${text} (${code})</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
Reference in New Issue
Block a user