Major refactor of admin view

This commit is contained in:
2026-01-09 00:36:11 +01:00
parent e8e2f9be15
commit d835dedb66
4 changed files with 2673 additions and 2631 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,34 @@
export function createCustomAssetModal({ broadcaster, showToast = globalThis.showToast, onAssetSaved }) {
const assetModal = document.getElementById("custom-asset-modal");
const userNameInput = document.getElementById("custom-asset-name");
const userSourceTextArea = document.getElementById("custom-asset-code");
const formErrorWrapper = document.getElementById("custom-asset-error");
const jsErrorTitle = document.getElementById("js-error-title");
const jsErrorDetails = document.getElementById("js-error-details");
const form = document.getElementById("custom-asset-form");
const cancelButton = document.getElementById("custom-asset-cancel");
function toggleCustomAssetModal(event) {
if (event !== undefined && event.target !== event.currentTarget) {
return;
const resetErrors = () => {
if (formErrorWrapper) {
formErrorWrapper.classList.add("hidden");
}
if (assetModal.classList.contains("hidden")) {
assetModal.classList.remove("hidden");
if (jsErrorTitle) {
jsErrorTitle.textContent = "";
}
if (jsErrorDetails) {
jsErrorDetails.textContent = "";
}
};
const openModal = () => {
assetModal?.classList.remove("hidden");
};
const closeModal = () => {
assetModal?.classList.add("hidden");
};
const openNew = () => {
if (userNameInput) {
userNameInput.value = "";
}
@@ -21,38 +39,76 @@ function toggleCustomAssetModal(event) {
userSourceTextArea.placeholder =
"function init({ surface, assets, channel }) {\n\n}\n\nfunction tick() {\n\n}";
}
if (formErrorWrapper) {
formErrorWrapper.classList.add("hidden");
}
if (jsErrorTitle) {
jsErrorTitle.textContent = "";
}
if (jsErrorDetails) {
jsErrorDetails.textContent = "";
}
} else {
assetModal.classList.add("hidden");
}
}
resetErrors();
openModal();
};
function submitCodeAsset(formEvent) {
const openEditor = (asset) => {
if (!asset) {
return;
}
resetErrors();
if (userNameInput) {
userNameInput.value = asset.name || "";
}
if (userSourceTextArea) {
userSourceTextArea.value = "";
userSourceTextArea.placeholder = "Loading script...";
userSourceTextArea.disabled = true;
userSourceTextArea.dataset.assetId = asset.id;
}
openModal();
fetch(asset.url)
.then((response) => {
if (!response.ok) {
throw new Error("Failed to load script");
}
return response.text();
})
.then((text) => {
if (userSourceTextArea) {
userSourceTextArea.disabled = false;
userSourceTextArea.value = text;
}
})
.catch(() => {
if (userSourceTextArea) {
userSourceTextArea.disabled = false;
userSourceTextArea.value = "";
}
showToast?.("Unable to load script content.", "error");
});
};
const handleFormSubmit = (formEvent) => {
formEvent.preventDefault();
const src = userSourceTextArea.value;
const src = userSourceTextArea?.value;
const error = getUserJavaScriptSourceError(src);
if (error) {
if (jsErrorTitle) {
jsErrorTitle.textContent = error.title;
}
if (jsErrorDetails) {
jsErrorDetails.textContent = error.details;
}
if (formErrorWrapper) {
formErrorWrapper.classList.remove("hidden");
}
return false;
}
formErrorWrapper.classList.add("hidden");
jsErrorTitle.textContent = "";
jsErrorDetails.textContent = "";
resetErrors();
const name = userNameInput?.value?.trim();
if (!name) {
if (jsErrorTitle) {
jsErrorTitle.textContent = "Missing name";
}
if (jsErrorDetails) {
jsErrorDetails.textContent = "Please provide a name for your custom asset.";
}
if (formErrorWrapper) {
formErrorWrapper.classList.remove("hidden");
}
return false;
}
const assetId = userSourceTextArea?.dataset?.assetId;
@@ -63,32 +119,14 @@ function submitCodeAsset(formEvent) {
}
saveCodeAsset({ name, src, assetId })
.then((asset) => {
if (asset && typeof storeAsset === "function") {
storeAsset(asset);
if (typeof updateRenderState === "function") {
updateRenderState(asset);
}
if (typeof selectedAssetId !== "undefined") {
selectedAssetId = asset.id;
}
if (typeof updateSelectedAssetControls === "function") {
updateSelectedAssetControls(asset);
}
if (typeof drawAndList === "function") {
drawAndList();
}
}
if (assetModal) {
assetModal.classList.add("hidden");
}
if (typeof showToast === "function") {
showToast(assetId ? "Custom asset updated." : "Custom asset created.", "success");
if (asset) {
onAssetSaved?.(asset);
}
closeModal();
showToast?.(assetId ? "Custom asset updated." : "Custom asset created.", "success");
})
.catch((e) => {
if (typeof showToast === "function") {
showToast("Unable to save custom asset. Please try again.", "error");
}
showToast?.("Unable to save custom asset. Please try again.", "error");
console.error(e);
})
.finally(() => {
@@ -98,7 +136,23 @@ function submitCodeAsset(formEvent) {
}
});
return false;
};
if (assetModal) {
assetModal.addEventListener("click", (event) => {
if (event.target === assetModal) {
closeModal();
}
});
}
if (form) {
form.addEventListener("submit", handleFormSubmit);
}
if (cancelButton) {
cancelButton.addEventListener("click", () => closeModal());
}
return { openNew, openEditor };
function saveCodeAsset({ name, src, assetId }) {
const payload = { name, source: src };
@@ -121,8 +175,13 @@ function saveCodeAsset({ name, src, assetId }) {
function getUserJavaScriptSourceError(src) {
let ast;
const parser = globalThis.acorn;
if (!parser) {
return { title: "Parser unavailable", details: "JavaScript parser is not available yet." };
}
try {
ast = acorn.parse(src, {
ast = parser.parse(src, {
ecmaVersion: "latest",
sourceType: "script",
});
@@ -170,3 +229,4 @@ function getUserJavaScriptSourceError(src) {
return undefined;
}
}

View File

@@ -52,7 +52,6 @@
class="file-input-field"
type="file"
accept="image/*,video/*,audio/*,application/javascript,text/javascript,.js,.mjs"
onchange="handleFileSelection(this)"
/>
<label for="asset-file" class="file-input-trigger">
<span class="file-input-icon"><i class="fa-solid fa-cloud-arrow-up"></i></span>
@@ -373,10 +372,10 @@
</section>
</div>
</div>
<div id="custom-asset-modal" class="modal hidden" onclick="toggleCustomAssetModal(event)">
<div id="custom-asset-modal" class="modal hidden">
<section class="modal-inner">
<h1>Create Custom Asset</h1>
<form id="custom-asset-form" onsubmit="submitCodeAsset(event)">
<form id="custom-asset-form">
<div class="form-group">
<label for="custom-asset-name">Asset name</label>
<input
@@ -402,7 +401,7 @@
<pre id="js-error-details"></pre>
</div>
<div class="form-actions">
<button type="button" class="secondary" onclick="toggleCustomAssetModal(event)">Cancel</button>
<button type="button" class="secondary" id="custom-asset-cancel">Cancel</button>
<button type="submit" class="primary">Test and save</button>
</div>
</form>
@@ -416,7 +415,6 @@
</script>
<script src="/js/cookie-consent.js"></script>
<script src="/js/toast.js"></script>
<script src="/js/customAssets.js"></script>
<script type="module" src="/js/admin.js"></script>
</body>
</html>