mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-10-31 05:43:41 +00:00 
			
		
		
		
	feat: add autorefresh button to the main page (#149)
* also add configuration options and change behaviour accordingly
This commit is contained in:
		| @ -28,6 +28,9 @@ allow_read_only = yes | ||||
| ; External address of the web service. Will be used for some features like OAuth. If none set will be generated as | ||||
| ;     address = http://${web:host}:${web:port} | ||||
| ;address = http://${web:host}:${web:port} | ||||
| ; Enable page auto refresh. Intervals are given in seconds. Default interval is always the first element of the list. | ||||
| ; If no intervals set, auto refresh will be disabled. | ||||
| autorefresh_intervals = 5 1 10 30 60 | ||||
| ; Enable file upload endpoint used by some triggers. | ||||
| ;enable_archive_upload = no | ||||
| ; Address to bind the server. | ||||
|  | ||||
| @ -80,6 +80,21 @@ | ||||
|                 <button type="button" class="btn btn-secondary" onclick="reload()"> | ||||
|                     <i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span> | ||||
|                 </button> | ||||
|  | ||||
|                 {% if autorefresh_intervals %} | ||||
|                     <div class="btn-group"> | ||||
|                         <input id="table-autoreload-button" type="checkbox" class="btn-check" autocomplete="off" onclick="toggleTableAutoReload()" checked> | ||||
|                         <label for="table-autoreload-button" class="btn btn-outline-secondary" title="toggle auto reload"><i class="bi bi-clock"></i></label> | ||||
|                         <button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"> | ||||
|                             <span class="visually-hidden">select interval</span> | ||||
|                         </button> | ||||
|                         <ul id="table-autoreload-input" class="dropdown-menu"> | ||||
|                             {% for interval in autorefresh_intervals %} | ||||
|                                 <li><a class="dropdown-item {{ "active" if interval.is_active }}" onclick="toggleTableAutoReload({{ interval.interval }})" data-interval="{{ interval.interval }}">{{ interval.text }}</a></li> | ||||
|                             {% endfor %} | ||||
|                         </ul> | ||||
|                     </div> | ||||
|                 {% endif %} | ||||
|             </div> | ||||
|  | ||||
|             <table id="packages" | ||||
|  | ||||
| @ -101,9 +101,21 @@ | ||||
|                     <button id="package-info-update-button" type="submit" class="btn btn-success" onclick="packageInfoUpdate()" data-bs-dismiss="modal"><i class="bi bi-play"></i><span class="d-none d-sm-inline"> update</span></button> | ||||
|                     <button id="package-info-remove-button" type="submit" class="btn btn-danger" onclick="packageInfoRemove()" data-bs-dismiss="modal"><i class="bi bi-trash"></i><span class="d-none d-sm-inline"> remove</span></button> | ||||
|                 {% endif %} | ||||
|                 <input id="package-info-autoreload-button" type="checkbox" class="btn-check" autocomplete="off" onclick="togglePackageInfoAutoReload()" checked> | ||||
|                 <label for="package-info-autoreload-button" class="btn btn-outline-secondary" title="toggle auto reload"><i class="bi bi-clock"></i></label> | ||||
|                 <button type="button" class="btn btn-secondary" onclick="showPackageInfo()"><i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span></button> | ||||
|                 {% if autorefresh_intervals %} | ||||
|                     <button type="button" class="btn btn-secondary" onclick="showPackageInfo()"><i class="bi bi-arrow-clockwise"></i><span class="d-none d-sm-inline"> reload</span></button> | ||||
|                     <div class="btn-group dropup"> | ||||
|                         <input id="package-info-autoreload-button" type="checkbox" class="btn-check" autocomplete="off" onclick="togglePackageInfoAutoReload()" checked> | ||||
|                         <label for="package-info-autoreload-button" class="btn btn-outline-secondary" title="toggle auto reload"><i class="bi bi-clock"></i></label> | ||||
|                         <button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false"> | ||||
|                             <span class="visually-hidden">select interval</span> | ||||
|                         </button> | ||||
|                         <ul id="package-info-autoreload-input" class="dropdown-menu"> | ||||
|                             {% for interval in autorefresh_intervals %} | ||||
|                                 <li><a class="dropdown-item {{ "active" if interval.is_active }}" onclick="togglePackageInfoAutoReload({{ interval.interval }})" data-interval="{{ interval.interval }}">{{ interval.text }}</a></li> | ||||
|                             {% endfor %} | ||||
|                         </ul> | ||||
|                     </div> | ||||
|                 {% endif %} | ||||
|                 <button type="button" class="btn btn-primary" data-bs-dismiss="modal"><i class="bi bi-x"></i><span class="d-none d-sm-inline"> close</span></button> | ||||
|             </div> | ||||
|         </div> | ||||
| @ -143,6 +155,7 @@ | ||||
|     const packageInfoRefreshInput = document.getElementById("package-info-refresh-input"); | ||||
|  | ||||
|     const packageInfoAutoReloadButton = document.getElementById("package-info-autoreload-button"); | ||||
|     const packageInfoAutoReloadInput = document.getElementById("package-info-autoreload-input"); | ||||
|     let packageInfoAutoReloadTask = null; | ||||
|  | ||||
|     function clearChart() { | ||||
| @ -477,22 +490,22 @@ | ||||
|  | ||||
|         if (isPackageBaseSet) { | ||||
|             bootstrap.Modal.getOrCreateInstance(packageInfoModal).show(); | ||||
|             togglePackageInfoAutoReload(); | ||||
|             {% if autorefresh_intervals %} | ||||
|                 togglePackageInfoAutoReload(); | ||||
|             {% endif %} | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function togglePackageInfoAutoReload() { | ||||
|     function togglePackageInfoAutoReload(interval) { | ||||
|         clearInterval(packageInfoAutoReloadTask); | ||||
|         if (packageInfoAutoReloadButton.checked) { | ||||
|             packageInfoAutoReloadTask = setInterval(_ => { | ||||
|                 if (!hasActiveSelection()) { | ||||
|                     const packageBase = packageInfoModal.dataset.package; | ||||
|                     // we only poll status and logs here | ||||
|                     loadPackage(packageBase); | ||||
|                     reloadActiveLogs(packageBase); | ||||
|                 } | ||||
|             }, 5000); | ||||
|         } | ||||
|         packageInfoAutoReloadTask = toggleAutoReload(packageInfoAutoReloadButton, interval, packageInfoAutoReloadInput, _ => { | ||||
|             if (!hasActiveSelection()) { | ||||
|                 const packageBase = packageInfoModal.dataset.package; | ||||
|                 // we only poll status and logs here | ||||
|                 loadPackage(packageBase); | ||||
|                 reloadActiveLogs(packageBase); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     ready(_ => { | ||||
|  | ||||
| @ -10,6 +10,10 @@ | ||||
|     const dashboardButton = document.getElementById("dashboard-button"); | ||||
|     const versionBadge = document.getElementById("badge-version"); | ||||
|  | ||||
|     const tableAutoReloadButton = document.getElementById("table-autoreload-button"); | ||||
|     const tableAutoReloadInput = document.getElementById("table-autoreload-input"); | ||||
|     let tableAutoReloadTask = null; | ||||
|  | ||||
|     function doPackageAction(uri, packages, repository, successText, failureText, data) { | ||||
|         makeRequest( | ||||
|             uri, | ||||
| @ -86,8 +90,10 @@ | ||||
|         doPackageAction("/api/v1/service/update", [], repository, onSuccess, onFailure, parameters); | ||||
|     } | ||||
|  | ||||
|     function reload() { | ||||
|         table.bootstrapTable("showLoading"); | ||||
|     function reload(silent) { | ||||
|         if (!silent) { | ||||
|             table.bootstrapTable("showLoading"); | ||||
|         } | ||||
|  | ||||
|         const badgeClass = status => { | ||||
|             if (status === "pending") return "btn-outline-warning"; | ||||
| @ -128,16 +134,18 @@ | ||||
|                 table.bootstrapTable("hideLoading"); | ||||
|             }, | ||||
|             error => { | ||||
|                 if ((error.status === 401) || (error.status === 403)) { | ||||
|                     // authorization error | ||||
|                     const text = "In order to see statuses you must login first."; | ||||
|                     table.find("tr.unauthorized").remove(); | ||||
|                     table.find("tbody").append(`<tr class="unauthorized"><td colspan="100%">${safe(text)}</td></tr>`); | ||||
|                     table.bootstrapTable("hideLoading"); | ||||
|                 } else { | ||||
|                     // other errors | ||||
|                     const message = details => `Could not load list of packages: ${details}`; | ||||
|                     showFailure("Load failure", message, error); | ||||
|                 if (!silent) { | ||||
|                     if ((error.status === 401) || (error.status === 403)) { | ||||
|                         // authorization error | ||||
|                         const text = "In order to see statuses you must login first."; | ||||
|                         table.find("tr.unauthorized").remove(); | ||||
|                         table.find("tbody").append(`<tr class="unauthorized"><td colspan="100%">${safe(text)}</td></tr>`); | ||||
|                         table.bootstrapTable("hideLoading"); | ||||
|                     } else { | ||||
|                         // other errors | ||||
|                         const message = details => `Could not load list of packages: ${details}`; | ||||
|                         showFailure("Load failure", message, error); | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|         ); | ||||
| @ -230,6 +238,15 @@ | ||||
|         return {classes: cellClass(value)}; | ||||
|     } | ||||
|  | ||||
|     function toggleTableAutoReload(interval) { | ||||
|         clearInterval(tableAutoReloadTask); | ||||
|         tableAutoReloadTask = toggleAutoReload(tableAutoReloadButton, interval, tableAutoReloadInput, _ => { | ||||
|             if (getSelection().length === 0) { | ||||
|                 reload(true); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     ready(_ => { | ||||
|         document.querySelectorAll("#repositories a").forEach(element => { | ||||
|             element.onclick = _ => { | ||||
| @ -289,5 +306,8 @@ | ||||
|         }); | ||||
|  | ||||
|         selectRepository(); | ||||
|         {% if autorefresh_intervals %} | ||||
|             toggleTableAutoReload(); | ||||
|         {% endif %} | ||||
|     }); | ||||
| </script> | ||||
|  | ||||
| @ -137,6 +137,28 @@ | ||||
|         return element; | ||||
|     } | ||||
|  | ||||
|     function toggleAutoReload(toggle, interval, intervalSelector, callback) { | ||||
|         if (interval) { | ||||
|             toggle.checked = true; // toggle reload | ||||
|         } else { | ||||
|             interval = intervalSelector.querySelector(".active")?.dataset?.interval; // find active element | ||||
|         } | ||||
|  | ||||
|         if (interval) { | ||||
|             if (toggle.checked) { | ||||
|                 // refresh UI | ||||
|                 Array.from(intervalSelector.children).forEach(il => { | ||||
|                     Array.from(il.children).forEach(el => el.classList.remove("active")); | ||||
|                 }); | ||||
|                 intervalSelector.querySelector(`a[data-interval="${interval}"]`)?.classList?.add("active"); | ||||
|                 // finally create timer task | ||||
|                 return setInterval(callback, interval); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return null; // return null to assign to keep method sane | ||||
|     } | ||||
|  | ||||
|     Date.prototype.toISOStringShort = function() { | ||||
|         const pad = number => String(number).padStart(2, "0"); | ||||
|         return `${this.getFullYear()}-${pad(this.getMonth() + 1)}-${pad(this.getDate())} ${pad(this.getHours())}:${pad(this.getMinutes())}:${pad(this.getSeconds())}`; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user