feat: add 2m, 10m presets and custom duration for slideshow interval
Presets are now [1, 2, 5, 10, 15, 30, 60] minutes. A 'Custom' button reveals a number input clamped to 1–60 minutes. Backend also clamps the received value with constrain(v, 1, 60) for safety. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
20
data/app.js
20
data/app.js
@@ -86,14 +86,20 @@ async function renderAlbums(el) {
|
||||
|
||||
async function renderSlideshow(el) {
|
||||
const settings = await api('/api/settings');
|
||||
const intervals = [1, 5, 15, 30, 60];
|
||||
const presets = [1, 2, 5, 10, 15, 30, 60];
|
||||
const isCustom = !presets.includes(settings.interval_min);
|
||||
const modes = ['Random', 'Chronological', 'Reverse Chrono', 'Favorites Weighted',
|
||||
'Weighted Recent', 'Weighted Oldest'];
|
||||
el.innerHTML = `
|
||||
<div class="card">
|
||||
<h2>Interval</h2>
|
||||
<div class="preset-btns">
|
||||
${intervals.map(i => `<button class="${settings.interval_min===i?'active':''}" data-interval="${i}">${i}m</button>`).join('')}
|
||||
${presets.map(i => `<button class="${!isCustom && settings.interval_min===i?'active':''}" data-interval="${i}">${i}m</button>`).join('')}
|
||||
<button class="${isCustom?'active':''}" data-interval="custom">Custom</button>
|
||||
</div>
|
||||
<div class="field" id="customIntervalRow" style="margin-top:8px;display:${isCustom?'flex':'none'}">
|
||||
<label>Minutes (1–60):</label>
|
||||
<input type="number" id="customInterval" min="1" max="60" value="${isCustom?settings.interval_min:5}" style="width:60px;margin-left:8px">
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
@@ -109,10 +115,18 @@ async function renderSlideshow(el) {
|
||||
btn.onclick = () => {
|
||||
el.querySelectorAll('[data-interval]').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
document.getElementById('customIntervalRow').style.display =
|
||||
btn.dataset.interval === 'custom' ? 'flex' : 'none';
|
||||
};
|
||||
});
|
||||
document.getElementById('saveSlideshow').onclick = async () => {
|
||||
const interval = parseInt(el.querySelector('[data-interval].active')?.dataset.interval || '5');
|
||||
const activeBtn = el.querySelector('[data-interval].active');
|
||||
let interval;
|
||||
if (activeBtn && activeBtn.dataset.interval === 'custom') {
|
||||
interval = Math.min(60, Math.max(1, parseInt(document.getElementById('customInterval').value) || 5));
|
||||
} else {
|
||||
interval = parseInt(activeBtn?.dataset.interval || '5');
|
||||
}
|
||||
const mode = parseInt(document.getElementById('cycleMode').value);
|
||||
await api('/api/settings', { method: 'POST', body: JSON.stringify({ interval_min: interval, cycle_mode: mode }) });
|
||||
toast('Slideshow settings saved');
|
||||
|
||||
@@ -310,7 +310,10 @@ void AppWebServer::handlePostSettings(AsyncWebServerRequest* request,
|
||||
|
||||
Settings s = _settings->get();
|
||||
|
||||
if (!doc["interval_min"].isNull()) s.interval_min = doc["interval_min"];
|
||||
if (!doc["interval_min"].isNull()) {
|
||||
uint8_t v = doc["interval_min"];
|
||||
s.interval_min = constrain(v, 1, 60);
|
||||
}
|
||||
if (!doc["cycle_mode"].isNull()) s.cycle_mode = static_cast<CycleMode>((uint8_t)doc["cycle_mode"]);
|
||||
if (!doc["img_quality"].isNull()) s.img_quality = static_cast<ImageQuality>((uint8_t)doc["img_quality"]);
|
||||
if (!doc["meta_flags"].isNull()) s.meta_flags = doc["meta_flags"];
|
||||
|
||||
Reference in New Issue
Block a user