This commit is contained in:
2026-09-11 23:07:03 +02:00
parent 802a59d090
commit 4b615428de
14 changed files with 243 additions and 174 deletions
+103 -138
View File
@@ -6526,7 +6526,7 @@ window.cancelAnimFrame = (function () {
}
};
const initExcludedTagsModal = () => {
if (window.f0ckSession && !window.f0ckSession.logged_in) return;
const isLoggedIn = !!(window.f0ckSession && window.f0ckSession.logged_in);
const overlay = document.getElementById('excluded-tags-overlay');
if (!overlay) return;
@@ -6544,9 +6544,9 @@ window.cancelAnimFrame = (function () {
overlay.offsetHeight;
overlay.classList.add('visible');
document.body.style.overflow = 'hidden';
renderTags();
if (isLoggedIn) renderTags();
if (window.syncRatingButtonUI) window.syncRatingButtonUI();
if (window.innerWidth > 768) input.focus();
if (isLoggedIn && window.innerWidth > 768) input.focus();
} else {
overlay.classList.remove('visible');
document.body.style.overflow = '';
@@ -6611,149 +6611,105 @@ window.cancelAnimFrame = (function () {
if (e.target === overlay) toggleModal(false);
});
const addTag = async () => {
const tagname = input.value.trim();
if (!tagname) return;
suggestions.style.display = 'none';
try {
const res = await fetch('/api/v2/settings/excluded_tags', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
tagname
})
});
const data = await res.json();
if (data.success) {
renderTags();
input.value = '';
} else {
window.flashMessage(data.msg || 'Error adding tag', 3000, 'error');
}
} catch (e) {
console.error(e);
} finally {
if (window.innerWidth > 768) input.focus();
}
};
input.addEventListener('keydown', e => {
if (e.key === 'Enter') {
e.preventDefault();
// If a suggestion is highlighted, use it
if (highlightIdx >= 0) {
const items = suggestions.querySelectorAll('.tag-suggestion-item');
if (items[highlightIdx]) {
input.value = items[highlightIdx].querySelector('.tag-suggestion-name').textContent;
}
}
if (isLoggedIn) {
const addTag = async () => {
const tagname = input.value.trim();
if (!tagname) return;
suggestions.style.display = 'none';
addTag();
}
});
list.addEventListener('click', async e => {
if (e.target.classList.contains('remove-excluded-tag')) {
e.preventDefault();
const tag = e.target.getAttribute('data-tag');
try {
const res = await fetch(`/api/v2/settings/excluded_tags/${encodeURIComponent(tag)}`, {
method: 'DELETE'
const res = await fetch('/api/v2/settings/excluded_tags', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tagname })
});
const data = await res.json();
if (data.success) renderTags();
if (data.success) { renderTags(); input.value = ''; }
else window.flashMessage(data.msg || 'Error adding tag', 3000, 'error');
} catch (e) {
console.error(e);
} finally {
if (window.innerWidth > 768) input.focus();
}
}
});
// Autocomplete with dropdown suggestions
let debounceTimer = null;
let highlightIdx = -1;
const renderSuggestions = (items) => {
suggestions.innerHTML = '';
highlightIdx = -1;
if (!items.length) {
suggestions.style.display = 'none';
return;
}
items.forEach(s => {
const div = document.createElement('div');
div.className = 'tag-suggestion-item';
const name = document.createElement('span');
name.className = 'tag-suggestion-name';
name.textContent = s.tag;
const meta = document.createElement('span');
meta.className = 'tag-suggestion-meta';
meta.textContent = `${s.tagged}× · ${s.score.toFixed(2)}`;
div.appendChild(name);
div.appendChild(meta);
div.addEventListener('mousedown', (e) => {
};
input.addEventListener('keydown', e => {
if (e.key === 'Enter') {
e.preventDefault();
input.value = s.tag;
if (highlightIdx >= 0) {
const items = suggestions.querySelectorAll('.tag-suggestion-item');
if (items[highlightIdx]) input.value = items[highlightIdx].querySelector('.tag-suggestion-name').textContent;
}
suggestions.style.display = 'none';
addTag();
});
suggestions.appendChild(div);
});
suggestions.style.display = 'block';
};
input.addEventListener('input', () => {
const q = input.value.trim();
highlightIdx = -1;
if (q.length < 1) {
suggestions.style.display = 'none';
return;
}
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
try {
const res = await fetch(`/api/v2/tags/suggest?q=${encodeURIComponent(q)}`);
const json = await res.json();
if (json.success && json.suggestions) {
renderSuggestions(json.suggestions.slice(0, 8));
} else {
suggestions.style.display = 'none';
}
} catch (e) {
suggestions.style.display = 'none';
}
}, 300);
});
});
input.addEventListener('keydown', e => {
const items = suggestions.querySelectorAll('.tag-suggestion-item');
if (!items.length || suggestions.style.display === 'none') return;
list.addEventListener('click', async e => {
if (e.target.classList.contains('remove-excluded-tag')) {
e.preventDefault();
const tag = e.target.getAttribute('data-tag');
try {
const res = await fetch(`/api/v2/settings/excluded_tags/${encodeURIComponent(tag)}`, { method: 'DELETE' });
const data = await res.json();
if (data.success) renderTags();
} catch (e) { console.error(e); }
}
});
if (e.key === 'ArrowDown') {
e.preventDefault();
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
highlightIdx = highlightIdx < items.length - 1 ? highlightIdx + 1 : 0;
items[highlightIdx].classList.add('active');
items[highlightIdx].scrollIntoView({ block: 'nearest' });
} else if (e.key === 'ArrowUp') {
e.preventDefault();
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
highlightIdx = highlightIdx > 0 ? highlightIdx - 1 : items.length - 1;
items[highlightIdx].classList.add('active');
items[highlightIdx].scrollIntoView({ block: 'nearest' });
} else if (e.key === 'Escape') {
suggestions.style.display = 'none';
// Autocomplete with dropdown suggestions
let debounceTimer = null;
let highlightIdx = -1;
const renderSuggestions = (items) => {
suggestions.innerHTML = '';
highlightIdx = -1;
}
});
if (!items.length) { suggestions.style.display = 'none'; return; }
items.forEach(s => {
const div = document.createElement('div');
div.className = 'tag-suggestion-item';
const name = document.createElement('span'); name.className = 'tag-suggestion-name'; name.textContent = s.tag;
const meta = document.createElement('span'); meta.className = 'tag-suggestion-meta'; meta.textContent = `${s.tagged}× · ${s.score.toFixed(2)}`;
div.appendChild(name); div.appendChild(meta);
div.addEventListener('mousedown', (e) => { e.preventDefault(); input.value = s.tag; suggestions.style.display = 'none'; addTag(); });
suggestions.appendChild(div);
});
suggestions.style.display = 'block';
};
input.addEventListener('blur', () => {
setTimeout(() => { suggestions.style.display = 'none'; }, 150);
});
input.addEventListener('input', () => {
const q = input.value.trim();
highlightIdx = -1;
if (q.length < 1) { suggestions.style.display = 'none'; return; }
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
try {
const res = await fetch(`/api/v2/tags/suggest?q=${encodeURIComponent(q)}`);
const json = await res.json();
if (json.success && json.suggestions) renderSuggestions(json.suggestions.slice(0, 8));
else suggestions.style.display = 'none';
} catch (e) { suggestions.style.display = 'none'; }
}, 300);
});
input.addEventListener('keydown', e => {
const items = suggestions.querySelectorAll('.tag-suggestion-item');
if (!items.length || suggestions.style.display === 'none') return;
if (e.key === 'ArrowDown') {
e.preventDefault();
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
highlightIdx = highlightIdx < items.length - 1 ? highlightIdx + 1 : 0;
items[highlightIdx].classList.add('active'); items[highlightIdx].scrollIntoView({ block: 'nearest' });
} else if (e.key === 'ArrowUp') {
e.preventDefault();
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
highlightIdx = highlightIdx > 0 ? highlightIdx - 1 : items.length - 1;
items[highlightIdx].classList.add('active'); items[highlightIdx].scrollIntoView({ block: 'nearest' });
} else if (e.key === 'Escape') {
suggestions.style.display = 'none'; highlightIdx = -1;
}
});
input.addEventListener('blur', () => { setTimeout(() => { suggestions.style.display = 'none'; }, 150); });
} // end isLoggedIn tag block
// ESC to close, "e" to open
document.addEventListener('keydown', (e) => {
@@ -6922,30 +6878,39 @@ window.cancelAnimFrame = (function () {
const THUMB_SIZES = ['s', 'm', 'l', 'xl'];
const gridSizeBtns = overlay.querySelectorAll('.nav-grid-size-btn');
const applyNavThumbSize = (size) => {
if (!THUMB_SIZES.includes(size)) size = 'm';
const applyNavThumbSize = (size, persist = true) => {
if (!THUMB_SIZES.includes(size)) size = window.f0ckDefaultThumbSize || 'm';
document.documentElement.dataset.thumbSize = size;
localStorage.setItem(THUMB_SIZE_KEY, size);
if (persist) localStorage.setItem(THUMB_SIZE_KEY, size);
gridSizeBtns.forEach(b => b.classList.toggle('active', b.dataset.thumbSize === size));
};
// Sync active state each time the modal opens
if (filterBtn) {
filterBtn.addEventListener('click', () => {
const cur = localStorage.getItem(THUMB_SIZE_KEY) || 'm';
const cur = localStorage.getItem(THUMB_SIZE_KEY) || window.f0ckDefaultThumbSize || 'm';
gridSizeBtns.forEach(b => b.classList.toggle('active', b.dataset.thumbSize === cur));
});
}
gridSizeBtns.forEach(btn => {
btn.addEventListener('click', () => applyNavThumbSize(btn.dataset.thumbSize));
btn.addEventListener('click', () => applyNavThumbSize(btn.dataset.thumbSize, true));
});
// Apply persisted size immediately on init (every page load)
applyNavThumbSize(localStorage.getItem(THUMB_SIZE_KEY) || 'm');
// Apply on init — do NOT persist so config default stays effective until user chooses
applyNavThumbSize(localStorage.getItem(THUMB_SIZE_KEY) || window.f0ckDefaultThumbSize || 'm', false);
};
// ── Thumb size init (runs on every page, independently of the filter modal) ──
const initThumbSize = () => {
const THUMB_SIZE_KEY = 'scroller_thumb_size';
const THUMB_SIZES = ['s', 'm', 'l', 'xl'];
const size = localStorage.getItem(THUMB_SIZE_KEY) || window.f0ckDefaultThumbSize || 'm';
document.documentElement.dataset.thumbSize = THUMB_SIZES.includes(size) ? size : (window.f0ckDefaultThumbSize || 'm');
};
document.addEventListener('DOMContentLoaded', () => {
initThumbSize(); // always runs — applies data-thumb-size to <html>
initSearch();
initExcludedTagsModal();
if (window.updateFilterBadge) window.updateFilterBadge();