gsdf
This commit is contained in:
+347
-28
@@ -57,6 +57,129 @@ window.cancelAnimFrame = (function () {
|
||||
return null;
|
||||
};
|
||||
|
||||
// <guest-favs> - disabled for clean guest mode
|
||||
const f0ckGuestFavs = {
|
||||
get: () => [],
|
||||
has: () => false,
|
||||
toggle: () => false,
|
||||
clear: () => {
|
||||
try {
|
||||
localStorage.removeItem('f0ck_guest_favs');
|
||||
localStorage.removeItem('guest_favs');
|
||||
} catch (e) {}
|
||||
},
|
||||
count: () => 0
|
||||
};
|
||||
window.f0ckGuestFavs = f0ckGuestFavs;
|
||||
const syncGuestFavoIcon = () => {};
|
||||
window.syncGuestFavoIcon = syncGuestFavoIcon;
|
||||
|
||||
document.addEventListener('click', e => {
|
||||
if (window.f0ckSession && window.f0ckSession.user) return;
|
||||
const target = e.target.nodeType === 3 ? e.target.parentElement : e.target;
|
||||
const favoBtn = target.closest('#a_favo');
|
||||
if (!favoBtn) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (typeof window.flashMessage === 'function') {
|
||||
window.flashMessage('Login to favorite posts');
|
||||
}
|
||||
});
|
||||
|
||||
const checkGuestFavsImportBanner = () => {
|
||||
if (!window.f0ckSession || !window.f0ckSession.user) return;
|
||||
const isUserFavs = window.location.pathname.match(/\/user\/([^/]+)\/favs/);
|
||||
if (!isUserFavs) return;
|
||||
const currentUser = window.f0ckSession.user.toLowerCase();
|
||||
if (decodeURIComponent(isUserFavs[1]).toLowerCase() !== currentUser) return;
|
||||
|
||||
const count = window.f0ckGuestFavs ? window.f0ckGuestFavs.count() : 0;
|
||||
if (count <= 0) {
|
||||
const existing = document.getElementById('guest-favs-import-banner');
|
||||
if (existing) existing.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.getElementById('guest-favs-import-banner')) return;
|
||||
|
||||
const postsContainer = document.querySelector('.posts');
|
||||
if (!postsContainer || !postsContainer.parentElement) return;
|
||||
|
||||
const banner = document.createElement('div');
|
||||
banner.id = 'guest-favs-import-banner';
|
||||
banner.className = 'guest-favs-banner';
|
||||
banner.style.cssText = 'background: rgba(255, 107, 157, 0.12); border: 1px solid rgba(255, 107, 157, 0.35); border-radius: 8px; padding: 12px 18px; margin: 15px auto; max-width: 900px; display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 0.95em; color: var(--text-color, #fff);';
|
||||
|
||||
const textSpan = document.createElement('span');
|
||||
const msg = (window.f0ckI18n && window.f0ckI18n.guest_favs_saved) || 'You have {count} guest favorites saved on this device.';
|
||||
textSpan.innerHTML = `<i class="fa-solid fa-heart" style="color: #ff6b9d; margin-right: 8px;"></i> ${msg.replace('{count}', `<strong>${count}</strong>`)}`;
|
||||
|
||||
const actionsDiv = document.createElement('div');
|
||||
actionsDiv.style.cssText = 'display: flex; gap: 8px; align-items: center; flex-shrink: 0;';
|
||||
|
||||
const importBtn = document.createElement('button');
|
||||
importBtn.id = 'btn-import-guest-favs';
|
||||
importBtn.className = 'btn btn-sm';
|
||||
importBtn.style.cssText = 'background: #ff6b9d; border: none; border-radius: 4px; padding: 6px 14px; color: white; cursor: pointer; font-weight: 600; font-size: 0.85em; transition: background 0.15s;';
|
||||
importBtn.textContent = (window.f0ckI18n && window.f0ckI18n.sync_guest_favs) || 'Import to Account';
|
||||
|
||||
const dismissBtn = document.createElement('button');
|
||||
dismissBtn.id = 'btn-dismiss-guest-favs';
|
||||
dismissBtn.className = 'btn btn-sm';
|
||||
dismissBtn.style.cssText = 'background: transparent; border: 1px solid rgba(255,255,255,0.2); border-radius: 4px; padding: 6px 10px; color: #ccc; cursor: pointer; font-size: 0.85em;';
|
||||
dismissBtn.textContent = (window.f0ckI18n && window.f0ckI18n.dismiss) || 'Dismiss';
|
||||
|
||||
importBtn.onclick = async () => {
|
||||
importBtn.disabled = true;
|
||||
importBtn.textContent = '...';
|
||||
try {
|
||||
const ids = window.f0ckGuestFavs.get();
|
||||
const res = await fetch('/api/v2/favorites/import', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-Token': window.f0ckSession?.csrf_token || ''
|
||||
},
|
||||
body: JSON.stringify({ ids })
|
||||
}).then(r => r.json());
|
||||
|
||||
if (res.success) {
|
||||
window.f0ckGuestFavs.clear();
|
||||
banner.remove();
|
||||
if (window.flashMessage) {
|
||||
const succMsg = (window.f0ckI18n && window.f0ckI18n.guest_favs_imported) || 'Imported favorites to your account!';
|
||||
window.flashMessage(succMsg);
|
||||
}
|
||||
if (typeof window.loadPageAjax === 'function') {
|
||||
window.loadPageAjax(window.location.pathname, true, { bypassCache: true });
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
} else {
|
||||
importBtn.disabled = false;
|
||||
importBtn.textContent = (window.f0ckI18n && window.f0ckI18n.sync_guest_favs) || 'Import to Account';
|
||||
}
|
||||
} catch (err) {
|
||||
importBtn.disabled = false;
|
||||
importBtn.textContent = (window.f0ckI18n && window.f0ckI18n.sync_guest_favs) || 'Import to Account';
|
||||
}
|
||||
};
|
||||
|
||||
dismissBtn.onclick = () => {
|
||||
window.f0ckGuestFavs.clear();
|
||||
banner.remove();
|
||||
};
|
||||
|
||||
actionsDiv.appendChild(importBtn);
|
||||
actionsDiv.appendChild(dismissBtn);
|
||||
banner.appendChild(textSpan);
|
||||
banner.appendChild(actionsDiv);
|
||||
|
||||
postsContainer.parentElement.insertBefore(banner, postsContainer);
|
||||
};
|
||||
window.checkGuestFavsImportBanner = checkGuestFavsImportBanner;
|
||||
// </guest-favs>
|
||||
|
||||
// OS and Browser detection for CSS targeting
|
||||
const ua = navigator.userAgent;
|
||||
const htmlEl = document.documentElement;
|
||||
@@ -893,8 +1016,8 @@ window.cancelAnimFrame = (function () {
|
||||
if (userToggle && userMenu) {
|
||||
userToggle.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
if (e.target.closest('.nav-avatar-img')) {
|
||||
const username = window.f0ckSession?.user;
|
||||
if (e.target.closest('.nav-avatar-img, .nav-avatar-icon')) {
|
||||
const username = window.f0ckSession?.is_anon ? (window.f0ckSession?.login || window.f0ckSession?.user) : window.f0ckSession?.user;
|
||||
if (username) {
|
||||
const url = `/user/${username.toLowerCase()}`;
|
||||
if (typeof window.loadPageAjax === 'function') {
|
||||
@@ -3052,6 +3175,7 @@ window.cancelAnimFrame = (function () {
|
||||
const isProfile = !isUserHall && !isUserHalls && pathname.match(/\/user\/([^/?]+)(?:$|\?|$)/) && !pathname.match(/\/user\/[^/]+\/(f0cks|favs|comments|hall|halls)/);
|
||||
const isUserF0cks = pathname.match(/\/user\/([^/?]+)\/f0cks/);
|
||||
const isUserFavs = pathname.match(/\/user\/([^/?]+)\/favs/);
|
||||
const isGuestFavs = pathname.match(/^\/favs(?:\/|$|\?)/);
|
||||
const isTags = pathname.match(/\/tags\/?(?:$|\?)/);
|
||||
const isComments = pathname.match(/\/user\/([^/?]+)\/comments\/?(?:$|\?)/);
|
||||
const isNotifs = pathname.match(/\/notifications\/?(?:$|\?)/);
|
||||
@@ -3065,7 +3189,8 @@ window.cancelAnimFrame = (function () {
|
||||
const parts = pathname.split('/').filter(Boolean);
|
||||
const isItem = !pathname.match(/\/p\//) && (
|
||||
pathname.match(/^\/\d+/) || pathname.match(/^\/[a-zA-Z0-9_-]{11}(?:[?#]|$)/) ||
|
||||
(parts.length >= 3 && (parts[0] === 'tag' || parts[0] === 'user' || parts[0] === 'h') && (/^\d+$/.test(parts[parts.length - 1]) || /^[a-zA-Z0-9_-]{11}$/.test(parts[parts.length - 1])))
|
||||
(parts.length >= 3 && (parts[0] === 'tag' || parts[0] === 'user' || parts[0] === 'h') && (/^\d+$/.test(parts[parts.length - 1]) || /^[a-zA-Z0-9_-]{11}$/.test(parts[parts.length - 1]))) ||
|
||||
(parts.length >= 2 && parts[0] === 'favs' && (/^\d+$/.test(parts[parts.length - 1]) || /^[a-zA-Z0-9_-]{11}$/.test(parts[parts.length - 1])))
|
||||
);
|
||||
const isMessages = !!pathname.match(/^\/messages(\/|$)/);
|
||||
const isAbyss = !!pathname.match(/^\/abyss(\/|$|\?|#)/) || pathname === '/abyss';
|
||||
@@ -3266,12 +3391,17 @@ window.cancelAnimFrame = (function () {
|
||||
}
|
||||
|
||||
const favMatch = url.match(/\/user\/([^/]+)\/favs/);
|
||||
const guestFavMatch = url.match(/^\/favs(?:\/|$|\?)/);
|
||||
const f0cksMatch = url.match(/\/user\/([^/]+)\/f0cks/);
|
||||
|
||||
let isFav = false;
|
||||
let isGuestFavMode = false;
|
||||
if (favMatch) {
|
||||
user = decodeURIComponent(favMatch[1]);
|
||||
isFav = true;
|
||||
} else if (guestFavMatch && (!window.f0ckSession || !window.f0ckSession.user)) {
|
||||
isFav = true;
|
||||
isGuestFavMode = true;
|
||||
} else if (f0cksMatch) {
|
||||
user = decodeURIComponent(f0cksMatch[1]);
|
||||
}
|
||||
@@ -3282,8 +3412,27 @@ window.cancelAnimFrame = (function () {
|
||||
let ajaxUrl = `/ajax/items/?page=${page}&mode=${window.activeMode}`;
|
||||
if (tag) ajaxUrl += `&tag=${encodeURIComponent(tag)}`;
|
||||
if (hall) ajaxUrl += `&hall=${encodeURIComponent(hall)}`;
|
||||
if (user) ajaxUrl += `&user=${encodeURIComponent(user)}`;
|
||||
if (isFav) ajaxUrl += `&fav=true`;
|
||||
if (isGuestFavMode) {
|
||||
const guestFavIds = f0ckGuestFavs.get();
|
||||
if (guestFavIds.length === 0) {
|
||||
if (replace && posts) {
|
||||
posts.innerHTML = `<div class="private-favs-msg" style="padding: 50px 20px; text-align: center; color: var(--text-muted); font-size: 1.1em;"><i class="fa-regular fa-heart" style="font-size: 2.5em; margin-bottom: 15px; display: block; opacity: 0.5;"></i>${(window.f0ckI18n && window.f0ckI18n.no_favs) || 'No favorites yet'}</div>`;
|
||||
posts.classList.add('show');
|
||||
}
|
||||
const existingPagContainer = document.querySelector('.pagination-container-fluid');
|
||||
if (existingPagContainer) existingPagContainer.style.display = 'none';
|
||||
if (navbar) navbar.classList.remove('pbwork');
|
||||
isNavigating = false;
|
||||
return;
|
||||
}
|
||||
const eps = 24;
|
||||
const pageNum = parseInt(page, 10) || 1;
|
||||
const sliceIds = guestFavIds.slice((pageNum - 1) * eps, pageNum * eps);
|
||||
ajaxUrl = `/ajax/items/?fav=true&ids=${encodeURIComponent(sliceIds.join(','))}&page=${pageNum}&total=${guestFavIds.length}&mode=${window.activeMode}`;
|
||||
} else {
|
||||
if (user) ajaxUrl += `&user=${encodeURIComponent(user)}`;
|
||||
if (isFav) ajaxUrl += `&fav=true`;
|
||||
}
|
||||
if (mime) ajaxUrl += `&mime=${encodeURIComponent(mime)}`;
|
||||
|
||||
// Preserve tagger filter from URL query string
|
||||
@@ -3835,6 +3984,7 @@ window.cancelAnimFrame = (function () {
|
||||
// Sync has-notif highlights after grid loads — handles PWA where visibilitychange doesn't fire
|
||||
window.NotificationSystemInstance?.pollDebounced?.();
|
||||
window._onaraCurrentGridUrl = urlObj.pathname + urlObj.search;
|
||||
checkGuestFavsImportBanner();
|
||||
|
||||
// Instant jump to hash (e.g. #c123)
|
||||
if (hash) {
|
||||
@@ -4098,6 +4248,8 @@ window.cancelAnimFrame = (function () {
|
||||
if (userMatch && !userHall) {
|
||||
user = decodeURIComponent(userMatch[1]);
|
||||
if (url.match(/\/user\/[^/]+\/favs(\/|$|\?)/)) isFavs = true;
|
||||
} else if (url.match(/^\/favs(\/|$|\?)/)) {
|
||||
isFavs = true;
|
||||
}
|
||||
|
||||
const hallMatch = url.match(/\/h\/([^/?]+)/);
|
||||
@@ -4133,6 +4285,8 @@ window.cancelAnimFrame = (function () {
|
||||
if (wUserMatch && !window.location.href.match(/\/user\/[^/]+\/hall\//) ) {
|
||||
user = decodeURIComponent(wUserMatch[1]);
|
||||
if (window.location.href.match(/\/user\/[^/]+\/favs(\/|$|\?)/)) isFavs = true;
|
||||
} else if (window.location.href.match(/\/favs(\/|$|\?)/)) {
|
||||
isFavs = true;
|
||||
}
|
||||
}
|
||||
if (!userHall) {
|
||||
@@ -4164,7 +4318,15 @@ window.cancelAnimFrame = (function () {
|
||||
params.append('user', user);
|
||||
}
|
||||
if (tagger) params.append('tagger', tagger);
|
||||
if (isFavs) params.append('fav', 'true');
|
||||
if (isFavs) {
|
||||
params.append('fav', 'true');
|
||||
if (!user && window.f0ckGuestFavs) {
|
||||
const guestIds = window.f0ckGuestFavs.get();
|
||||
if (guestIds.length > 0) {
|
||||
params.append('ids', guestIds.slice(0, 100).join(','));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isStrict = window.f0ckSession?.strict_mode || (localStorage.getItem('search_strict') === 'true');
|
||||
|
||||
@@ -4312,6 +4474,7 @@ window.cancelAnimFrame = (function () {
|
||||
let _pushUrl = `/${itemKey}`;
|
||||
if (userHall && userHallOwner) _pushUrl = `/user/${encodeURIComponent(userHallOwner)}/hall/${encodeURIComponent(userHall)}/${itemKey}`;
|
||||
else if (user) { _pushUrl = `/user/${encodeURIComponent(user)}/${itemKey}`; if (isFavs) _pushUrl = `/user/${encodeURIComponent(user)}/favs/${itemKey}`; }
|
||||
else if (isFavs) _pushUrl = `/favs/${itemKey}`;
|
||||
else if (tag) _pushUrl = `/tag/${encodeURIComponent(tag).replace(/%2C/g,',').replace(/%20/g,' ')}/${itemKey}`;
|
||||
else if (hall) _pushUrl = `/h/${encodeURIComponent(hall).replace(/%20/g,' ')}/${itemKey}`;
|
||||
if (tagger && tag) _pushUrl += `?tagger=${encodeURIComponent(tagger)}`;
|
||||
@@ -4564,6 +4727,8 @@ window.cancelAnimFrame = (function () {
|
||||
} else if (user) {
|
||||
pushUrl = `/user/${encodeURIComponent(user)}/${itemKey}`;
|
||||
if (isFavs) pushUrl = `/user/${encodeURIComponent(user)}/favs/${itemKey}`;
|
||||
} else if (isFavs) {
|
||||
pushUrl = `/favs/${itemKey}`;
|
||||
}
|
||||
else if (tag) pushUrl = `/tag/${encodeURIComponent(tag).replace(/%2C/g, ',').replace(/%20/g, ' ')}/${itemKey}`;
|
||||
else if (hall) pushUrl = `/h/${encodeURIComponent(hall).replace(/%20/g, ' ')}/${itemKey}`;
|
||||
@@ -4831,6 +4996,15 @@ window.cancelAnimFrame = (function () {
|
||||
const wUserM = window.location.href.match(/\/user\/([^/]+)/);
|
||||
if (wUserM && window.location.href.match(/\/favs(\/|$|\?)/)) {
|
||||
wFavsUser = decodeURIComponent(wUserM[1]);
|
||||
} else if (window.location.href.match(/\/favs(\/|$|\?)/)) {
|
||||
const guestIds = window.f0ckGuestFavs ? window.f0ckGuestFavs.get() : [];
|
||||
if (guestIds.length > 0) {
|
||||
const currentId = window.getCurrentItemId();
|
||||
const candidates = guestIds.filter(id => id !== parseInt(currentId, 10));
|
||||
const chosen = candidates.length > 0 ? candidates[Math.floor(Math.random() * candidates.length)] : guestIds[0];
|
||||
loadItemAjax(`/favs/${chosen}`, true, { transition: 'fade-zoom' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5762,6 +5936,10 @@ window.cancelAnimFrame = (function () {
|
||||
if (window.location.pathname.includes('/f0cks')) ctx.f0cks = true;
|
||||
if (window.location.pathname.includes('/tags')) ctx.userTags = true;
|
||||
}
|
||||
if (window.location.pathname.startsWith('/favs')) {
|
||||
ctx.fav = true;
|
||||
if (!window.f0ckSession || !window.f0ckSession.user) ctx.guestFavs = true;
|
||||
}
|
||||
|
||||
const mimeMatch = window.location.pathname.match(/\/(image|audio|video)(?:\/|$)/);
|
||||
if (mimeMatch) ctx.mime = mimeMatch[1];
|
||||
@@ -5958,6 +6136,17 @@ window.cancelAnimFrame = (function () {
|
||||
if (ctx.hall) params.append('hall', ctx.hall);
|
||||
if (ctx.user) params.append('user', ctx.user);
|
||||
if (ctx.fav) params.append('fav', 'true');
|
||||
if (ctx.guestFavs && window.f0ckGuestFavs) {
|
||||
const guestFavIds = window.f0ckGuestFavs.get();
|
||||
const eps = 24;
|
||||
const sliceIds = guestFavIds.slice((nextPage - 1) * eps, nextPage * eps);
|
||||
if (sliceIds.length === 0) {
|
||||
infiniteState.hasMore = false;
|
||||
return;
|
||||
}
|
||||
params.append('ids', sliceIds.join(','));
|
||||
params.append('total', guestFavIds.length);
|
||||
}
|
||||
if (ctx.mime) params.append('mime', ctx.mime);
|
||||
|
||||
const isStrict = window.f0ckSession?.strict_mode || (localStorage.getItem('search_strict') === 'true');
|
||||
@@ -6986,6 +7175,12 @@ window.cancelAnimFrame = (function () {
|
||||
initSearch();
|
||||
initExcludedTagsModal();
|
||||
if (window.updateFilterBadge) window.updateFilterBadge();
|
||||
if (window.location.pathname.startsWith('/favs') && (!window.f0ckSession || !window.f0ckSession.user)) {
|
||||
const postsEl = document.querySelector('.posts');
|
||||
if (postsEl && !postsEl.children.length) {
|
||||
loadPageAjax(window.location.pathname + window.location.search, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
// </search-overlay>
|
||||
|
||||
@@ -12260,15 +12455,103 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
})();
|
||||
|
||||
// ── Steuerung haptic feedback ─────────────────────────────────────────────────
|
||||
// Short vibration when tapping .steuerung nav links on mobile.
|
||||
if (navigator.vibrate) {
|
||||
document.addEventListener('touchstart', (e) => {
|
||||
if (e.target.closest('.steuerung a')) {
|
||||
navigator.vibrate(30);
|
||||
// ── Steuerung haptic & sound feedback ─────────────────────────────────────────
|
||||
(function() {
|
||||
let _audioCtx = null;
|
||||
|
||||
function playSteuerungClickSound() {
|
||||
try {
|
||||
const AudioCtx = window.AudioContext || window.webkitAudioContext;
|
||||
if (!AudioCtx) return;
|
||||
if (!_audioCtx) {
|
||||
_audioCtx = new AudioCtx();
|
||||
}
|
||||
if (_audioCtx.state === 'suspended') {
|
||||
_audioCtx.resume();
|
||||
}
|
||||
const t = _audioCtx.currentTime;
|
||||
|
||||
// Master gain for smooth, gentle volume
|
||||
const master = _audioCtx.createGain();
|
||||
master.gain.setValueAtTime(0.15, t);
|
||||
master.connect(_audioCtx.destination);
|
||||
|
||||
// Warm body oscillator: smooth sine wave sweeping down (soft mechanical tactile feel)
|
||||
const osc = _audioCtx.createOscillator();
|
||||
const oscGain = _audioCtx.createGain();
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(420, t);
|
||||
osc.frequency.exponentialRampToValueAtTime(110, t + 0.024);
|
||||
|
||||
oscGain.gain.setValueAtTime(0.0001, t);
|
||||
oscGain.gain.linearRampToValueAtTime(1.0, t + 0.0015);
|
||||
oscGain.gain.exponentialRampToValueAtTime(0.0001, t + 0.024);
|
||||
|
||||
osc.connect(oscGain);
|
||||
oscGain.connect(master);
|
||||
|
||||
// Subtle transient click layer: triangle wave with gentle decay
|
||||
const snap = _audioCtx.createOscillator();
|
||||
const snapGain = _audioCtx.createGain();
|
||||
snap.type = 'triangle';
|
||||
snap.frequency.setValueAtTime(1200, t);
|
||||
snap.frequency.exponentialRampToValueAtTime(320, t + 0.008);
|
||||
|
||||
snapGain.gain.setValueAtTime(0.0001, t);
|
||||
snapGain.gain.linearRampToValueAtTime(0.3, t + 0.001);
|
||||
snapGain.gain.exponentialRampToValueAtTime(0.0001, t + 0.009);
|
||||
|
||||
snap.connect(snapGain);
|
||||
snapGain.connect(master);
|
||||
|
||||
osc.start(t);
|
||||
snap.start(t);
|
||||
osc.stop(t + 0.03);
|
||||
snap.stop(t + 0.012);
|
||||
} catch (e) {
|
||||
// AudioContext blocked or unsupported
|
||||
}
|
||||
}
|
||||
|
||||
window.playSteuerungClickSound = playSteuerungClickSound;
|
||||
|
||||
let lastTriggerTime = 0;
|
||||
function triggerSteuerungFeedback(target) {
|
||||
if (!target) return;
|
||||
if (target.style.visibility === 'hidden' || target.getAttribute('href') === '#') return;
|
||||
|
||||
const now = Date.now();
|
||||
if (now - lastTriggerTime < 80) return;
|
||||
lastTriggerTime = now;
|
||||
|
||||
if (navigator.vibrate) {
|
||||
try { navigator.vibrate(25); } catch (e) {}
|
||||
}
|
||||
|
||||
target.classList.add('is-clicked');
|
||||
setTimeout(() => {
|
||||
target.classList.remove('is-clicked');
|
||||
}, 120);
|
||||
|
||||
playSteuerungClickSound();
|
||||
}
|
||||
|
||||
// Pointerdown gives instant tactile audio without waiting for pointerup/click
|
||||
document.addEventListener('pointerdown', (e) => {
|
||||
const target = e.target.closest('.steuerung a, .steuerung button, .steuerung [role="button"]');
|
||||
if (target) {
|
||||
triggerSteuerungFeedback(target);
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// Keyboard navigation / programmatic .click() fallback
|
||||
document.addEventListener('click', (e) => {
|
||||
const target = e.target.closest('.steuerung a, .steuerung button, .steuerung [role="button"]');
|
||||
if (target) {
|
||||
triggerSteuerungFeedback(target);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
// ── Steuerung icon style: #scrolltobottom smooth scroll ───────────────────────
|
||||
// The alternative icon nav replaces the Zufall link with a down-chevron that
|
||||
@@ -12700,6 +12983,39 @@ document.addEventListener('click', (e) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Title Status Notification Management
|
||||
let titleStatusTimeout = null;
|
||||
function setTitleStatus(statusEl, text, isError = false) {
|
||||
if (!statusEl) return;
|
||||
if (titleStatusTimeout) {
|
||||
clearTimeout(titleStatusTimeout);
|
||||
titleStatusTimeout = null;
|
||||
}
|
||||
if (!text) {
|
||||
statusEl.style.display = 'none';
|
||||
statusEl.textContent = '';
|
||||
return;
|
||||
}
|
||||
statusEl.textContent = text;
|
||||
statusEl.style.color = isError ? '#e84040' : '';
|
||||
statusEl.style.display = 'inline';
|
||||
titleStatusTimeout = setTimeout(() => {
|
||||
statusEl.style.display = 'none';
|
||||
statusEl.textContent = '';
|
||||
titleStatusTimeout = null;
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
function clearTitleStatus(statusEl) {
|
||||
if (!statusEl) return;
|
||||
if (titleStatusTimeout) {
|
||||
clearTimeout(titleStatusTimeout);
|
||||
titleStatusTimeout = null;
|
||||
}
|
||||
statusEl.style.display = 'none';
|
||||
statusEl.textContent = '';
|
||||
}
|
||||
|
||||
// Post & File Info Modal Logic
|
||||
document.addEventListener('click', (e) => {
|
||||
const infoBtn = e.target.closest('#a_info');
|
||||
@@ -12823,7 +13139,6 @@ document.addEventListener('click', (e) => {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Title text click to edit
|
||||
const titleText = e.target.closest('.item_title_text');
|
||||
if (titleText && !document.body.classList.contains('preview-as-user')) {
|
||||
@@ -12831,6 +13146,8 @@ document.addEventListener('click', (e) => {
|
||||
if (container) {
|
||||
const editWrap = container.querySelector('.info-title-edit-wrap');
|
||||
const input = container.querySelector('#info-title-input');
|
||||
const status = container.querySelector('#info-title-status') || document.getElementById('info-title-status');
|
||||
clearTitleStatus(status);
|
||||
if (editWrap && input) {
|
||||
e.preventDefault();
|
||||
titleText.style.display = 'none';
|
||||
@@ -12851,6 +13168,8 @@ document.addEventListener('click', (e) => {
|
||||
const container = openTitleWrap.closest('.item_title');
|
||||
const textEl = container?.querySelector('.item_title_text');
|
||||
const input = container?.querySelector('#info-title-input');
|
||||
const status = container?.querySelector('#info-title-status') || document.getElementById('info-title-status');
|
||||
clearTitleStatus(status);
|
||||
openTitleWrap.style.display = 'none';
|
||||
if (textEl) textEl.style.display = '';
|
||||
if (input && input.dataset.origVal !== undefined) {
|
||||
@@ -12915,20 +13234,13 @@ document.addEventListener('click', (e) => {
|
||||
window.invalidateItemCache(itemId);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
status.style.display = 'none';
|
||||
status.textContent = '';
|
||||
}
|
||||
clearTitleStatus(status);
|
||||
|
||||
if (window.flashMessage) {
|
||||
window.flashMessage('Title saved', 2000, 'success');
|
||||
}
|
||||
} else {
|
||||
if (status) {
|
||||
status.textContent = data.msg || 'Error saving title';
|
||||
status.style.color = '#e84040';
|
||||
status.style.display = 'inline';
|
||||
}
|
||||
setTitleStatus(status, data.msg || 'Error saving title', true);
|
||||
if (window.flashMessage) {
|
||||
window.flashMessage('Error while saving Title', 3000, 'error');
|
||||
}
|
||||
@@ -12937,11 +13249,7 @@ document.addEventListener('click', (e) => {
|
||||
.catch(() => {
|
||||
saveBtn.disabled = false;
|
||||
saveBtn.innerHTML = origIcon;
|
||||
if (status) {
|
||||
status.textContent = 'Network error';
|
||||
status.style.color = '#e84040';
|
||||
status.style.display = 'inline';
|
||||
}
|
||||
setTitleStatus(status, 'Network error', true);
|
||||
if (window.flashMessage) {
|
||||
window.flashMessage('Error while saving Title', 3000, 'error');
|
||||
}
|
||||
@@ -13093,6 +13401,8 @@ document.addEventListener('click', (e) => {
|
||||
const container = e.target.closest('.item_title') || document;
|
||||
const editWrap = container.querySelector('.info-title-edit-wrap');
|
||||
const textEl = container.querySelector('.item_title_text');
|
||||
const status = container.querySelector('#info-title-status') || document.getElementById('info-title-status');
|
||||
clearTitleStatus(status);
|
||||
const input = e.target;
|
||||
if (editWrap) editWrap.style.display = 'none';
|
||||
if (textEl) textEl.style.display = '';
|
||||
@@ -13103,6 +13413,15 @@ document.addEventListener('click', (e) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Clear title error/status as soon as user types into title input
|
||||
document.addEventListener('input', (e) => {
|
||||
if (e.target && e.target.id === 'info-title-input') {
|
||||
const container = e.target.closest('.item_title') || document;
|
||||
const status = container.querySelector('#info-title-status') || document.getElementById('info-title-status');
|
||||
clearTitleStatus(status);
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure any navigation event restores the scroll state
|
||||
window.addEventListener('pjax:start', () => {
|
||||
if (window.resetGlobalScrollState) window.resetGlobalScrollState();
|
||||
|
||||
Reference in New Issue
Block a user