fixing background visibility and states
This commit is contained in:
@@ -1585,14 +1585,10 @@ span.placeholder {
|
||||
}
|
||||
|
||||
@media (max-width: 1056px) {
|
||||
.navbar {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
grid-template-areas: 'f0ck f0ck f0ck';
|
||||
}
|
||||
|
||||
/* Navbar grid layout removed for modern-navbar compatibility */
|
||||
.navbar-brand {
|
||||
grid-area: f0ck;
|
||||
/* maintained for potential other uses or reset */
|
||||
}
|
||||
|
||||
.pagination-container-fluid {
|
||||
@@ -2947,7 +2943,7 @@ div.favs div.posts {
|
||||
filter: blur(100px);
|
||||
transform: translate3d(0, 0, 0);
|
||||
z-index: 0;
|
||||
transition: 2s ease;
|
||||
transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
@@ -3000,11 +2996,11 @@ button#togglebg {
|
||||
}
|
||||
|
||||
.fader-in {
|
||||
animation: fadeIn .8s steps(100) forwards;
|
||||
opacity: 0.4 !important;
|
||||
}
|
||||
|
||||
.fader-out {
|
||||
animation: fadeOut .8s steps(100) forwards
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
.settings {
|
||||
@@ -3052,12 +3048,13 @@ input#s_avatar {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.4;
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Modern Tags Layout */
|
||||
.tags-grid {
|
||||
display: grid;
|
||||
|
||||
@@ -8,6 +8,25 @@ window.requestAnimFrame = (function () {
|
||||
|
||||
(() => {
|
||||
let video;
|
||||
|
||||
// Initialize background preference
|
||||
if (localStorage.getItem('background') == undefined) {
|
||||
localStorage.setItem('background', 'true');
|
||||
}
|
||||
var background = localStorage.getItem('background') === 'true';
|
||||
// Apply initial visual state
|
||||
var initialCanvas = document.getElementById('bg');
|
||||
if (initialCanvas) {
|
||||
if (background) {
|
||||
initialCanvas.classList.add('fader-in');
|
||||
initialCanvas.classList.remove('fader-out');
|
||||
} else {
|
||||
initialCanvas.classList.add('fader-out');
|
||||
initialCanvas.classList.remove('fader-in');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (elem = document.querySelector("#my-video")) {
|
||||
video = new v0ck(elem);
|
||||
document.addEventListener("keydown", e => {
|
||||
@@ -17,13 +36,27 @@ window.requestAnimFrame = (function () {
|
||||
}
|
||||
});
|
||||
|
||||
const toggleBg = document.getElementById('togglebg');
|
||||
if (toggleBg) {
|
||||
toggleBg.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
background = !background;
|
||||
localStorage.setItem('background', background.toString());
|
||||
var canvas = document.getElementById('bg');
|
||||
|
||||
|
||||
if (elem !== null) {
|
||||
// ... existing code ...
|
||||
}
|
||||
}
|
||||
|
||||
// Export init function for dynamic calls
|
||||
window.initBackground = () => {
|
||||
// Re-fetch elements as they might have been replaced
|
||||
const elem = document.querySelector("#my-video");
|
||||
const canvas = document.getElementById('bg');
|
||||
|
||||
if (elem) {
|
||||
// Initialize video wrapper if needed or just get instance
|
||||
// Assuming v0ck handles re-init or we just use raw element for events
|
||||
// But video variable is local.
|
||||
// We need to re-bind 'play' event if it's a new element.
|
||||
|
||||
if (canvas) {
|
||||
// Restore visual state on re-init
|
||||
if (background) {
|
||||
canvas.classList.add('fader-in');
|
||||
canvas.classList.remove('fader-out');
|
||||
@@ -31,33 +64,102 @@ window.requestAnimFrame = (function () {
|
||||
canvas.classList.add('fader-out');
|
||||
canvas.classList.remove('fader-in');
|
||||
}
|
||||
animationLoop();
|
||||
});
|
||||
}
|
||||
|
||||
if (elem !== null) {
|
||||
if (localStorage.getItem('background') == undefined) {
|
||||
localStorage.setItem('background', 'true');
|
||||
}
|
||||
const context = canvas.getContext('2d');
|
||||
const cw = canvas.width = canvas.clientWidth | 0;
|
||||
const ch = canvas.height = canvas.clientHeight | 0;
|
||||
|
||||
var background = localStorage.getItem('background') === 'true';
|
||||
var canvas = document.getElementById('bg');
|
||||
if (canvas) {
|
||||
var context = canvas.getContext('2d');
|
||||
var cw = canvas.width = canvas.clientWidth | 0;
|
||||
var ch = canvas.height = canvas.clientHeight | 0;
|
||||
|
||||
function animationLoop() {
|
||||
if (video.paused || video.ended || !background)
|
||||
const animationLoop = () => {
|
||||
if (elem.paused || elem.ended || !background)
|
||||
return;
|
||||
context.drawImage(video, 0, 0, cw, ch);
|
||||
context.drawImage(elem, 0, 0, cw, ch);
|
||||
window.requestAnimFrame(animationLoop);
|
||||
}
|
||||
|
||||
elem.addEventListener('play', animationLoop);
|
||||
|
||||
if (!elem.paused) {
|
||||
animationLoop();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Initial call
|
||||
window.initBackground();
|
||||
|
||||
const loadPageAjax = async (url) => {
|
||||
// Show loading indicator
|
||||
const navbar = document.querySelector("nav.navbar");
|
||||
if (navbar) navbar.classList.add("pbwork");
|
||||
|
||||
try {
|
||||
// Extract page number, user, tag, etc.
|
||||
let page = 1;
|
||||
const pMatch = url.match(/\/p\/(\d+)/);
|
||||
if (pMatch) page = pMatch[1];
|
||||
|
||||
// Extract context
|
||||
let tag = null, user = null, mime = null;
|
||||
const tagMatch = url.match(/\/tag\/([^/]+)/);
|
||||
if (tagMatch) tag = decodeURIComponent(tagMatch[1]);
|
||||
|
||||
const userMatch = url.match(/\/user\/([^/]+)/);
|
||||
if (userMatch) user = decodeURIComponent(userMatch[1]);
|
||||
|
||||
const mimeMatch = url.match(/\/(image|audio|video)/);
|
||||
if (mimeMatch) mime = mimeMatch[1];
|
||||
|
||||
let ajaxUrl = `/ajax/items/?page=${page}`;
|
||||
if (tag) ajaxUrl += `&tag=${encodeURIComponent(tag)}`;
|
||||
if (user) ajaxUrl += `&user=${encodeURIComponent(user)}`;
|
||||
if (mime) ajaxUrl += `&mime=${encodeURIComponent(mime)}`;
|
||||
|
||||
console.log("Fetching Page:", ajaxUrl);
|
||||
const response = await fetch(ajaxUrl, { credentials: 'include' });
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// Replace grid content
|
||||
// If "infinite scroll" we might append, but pagination implies jumping properly?
|
||||
// User said "resembled in pagination", which implies staying in sync.
|
||||
// If I click Next Page, I expect to SEE page 2.
|
||||
// But infinite scroll usually appends.
|
||||
// Let's implement REPLACE for explicit page navigation to be safe/standard.
|
||||
// Wait, the "infinite scroll" feature usually implies APPEND.
|
||||
// If the user wants infinite scroll, they shouldn't click pagination?
|
||||
// But if they scroll, `changePage` is called which clicks `.next`.
|
||||
// So if I replace content, it breaks infinite scroll flow (items disappear).
|
||||
// So I should APPEND if it's "next page" and we are already on the page?
|
||||
// But `changePage` is triggered by scroll.
|
||||
// Let's APPEND.
|
||||
|
||||
const posts = document.querySelector('.posts');
|
||||
if (posts) {
|
||||
// Check if we are appending (next page) or jumping
|
||||
// For simple "infinite scroll", we append.
|
||||
posts.insertAdjacentHTML('beforeend', data.html);
|
||||
}
|
||||
|
||||
// Update pagination
|
||||
if (data.pagination) {
|
||||
document.querySelectorAll('.pagination-wrapper').forEach(el => el.innerHTML = data.pagination);
|
||||
}
|
||||
|
||||
// Update History
|
||||
history.pushState({}, '', url);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
window.location.href = url; // Fallback
|
||||
} finally {
|
||||
if (navbar) navbar.classList.remove("pbwork");
|
||||
// Restore pagination visibility for Grid View
|
||||
const navPag = document.querySelector('.pagination-container-fluid');
|
||||
if (navPag) navPag.style.display = '';
|
||||
}
|
||||
};
|
||||
|
||||
let tt = false;
|
||||
const stimeout = 500;
|
||||
@@ -89,6 +191,10 @@ window.requestAnimFrame = (function () {
|
||||
// Extract item ID from URL. Regex now handles query params, hashes, and trailing slashes.
|
||||
const match = url.match(/\/(\d+)(?:\/|#|\?|$)/);
|
||||
|
||||
// Hide navbar pagination for Item View (matches SSR)
|
||||
const navPag = document.querySelector('.pagination-container-fluid');
|
||||
if (navPag) navPag.style.display = 'none';
|
||||
|
||||
if (!match) {
|
||||
console.warn("loadItemAjax: No ID match found in URL", url);
|
||||
// fallback for weird/external links
|
||||
@@ -99,12 +205,15 @@ window.requestAnimFrame = (function () {
|
||||
|
||||
// <context-preservation>
|
||||
// Extract context from Target URL first
|
||||
let tag = null, user = null;
|
||||
let tag = null, user = null, isFavs = false;
|
||||
const tagMatch = url.match(/\/tag\/([^/]+)/);
|
||||
if (tagMatch) tag = decodeURIComponent(tagMatch[1]);
|
||||
|
||||
const userMatch = url.match(/\/user\/([^/]+)/);
|
||||
if (userMatch) user = decodeURIComponent(userMatch[1]); // Note: "user" variable shadowed? No, block scope or different name? let user defined above.
|
||||
if (userMatch) {
|
||||
user = decodeURIComponent(userMatch[1]);
|
||||
if (url.includes(`/user/${userMatch[1]}/favs`)) isFavs = true;
|
||||
}
|
||||
|
||||
// If missing and inheritContext is true, check Window Location
|
||||
if (inheritContext) {
|
||||
@@ -114,7 +223,10 @@ window.requestAnimFrame = (function () {
|
||||
}
|
||||
if (!user) {
|
||||
const wUserMatch = window.location.href.match(/\/user\/([^/]+)/);
|
||||
if (wUserMatch) user = decodeURIComponent(wUserMatch[1]);
|
||||
if (wUserMatch) {
|
||||
user = decodeURIComponent(wUserMatch[1]);
|
||||
if (window.location.href.includes(`/user/${wUserMatch[1]}/favs`)) isFavs = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// </context-preservation>
|
||||
@@ -126,13 +238,14 @@ window.requestAnimFrame = (function () {
|
||||
const params = new URLSearchParams();
|
||||
if (tag) params.append('tag', tag);
|
||||
if (user) params.append('user', user);
|
||||
if (isFavs) params.append('fav', 'true');
|
||||
|
||||
if ([...params].length > 0) {
|
||||
ajaxUrl += '?' + params.toString();
|
||||
}
|
||||
|
||||
console.log("Fetching:", ajaxUrl);
|
||||
const response = await fetch(ajaxUrl);
|
||||
const response = await fetch(ajaxUrl, { credentials: 'include' });
|
||||
if (!response.ok) throw new Error(`Network response was not ok: ${response.status}`);
|
||||
|
||||
const rawText = await response.text();
|
||||
@@ -187,13 +300,17 @@ window.requestAnimFrame = (function () {
|
||||
// If we inherited context, we should reflect it in the URL
|
||||
let pushUrl = `/${itemid}`;
|
||||
// Logic from ajax.mjs context reconstruction:
|
||||
if (user) pushUrl = `/user/${user}/${itemid}`; // User takes precedence usually? Or strictly mutually exclusive in UI
|
||||
if (user) {
|
||||
pushUrl = `/user/${user}/${itemid}`;
|
||||
if (isFavs) pushUrl = `/user/${user}/favs/${itemid}`;
|
||||
}
|
||||
else if (tag) pushUrl = `/tag/${tag}/${itemid}`;
|
||||
|
||||
// We overwrite proper URL even if the link clicked was "naked"
|
||||
history.pushState({}, '', pushUrl);
|
||||
|
||||
setupMedia();
|
||||
if (window.initBackground) window.initBackground();
|
||||
// Try to extract ID from response if possible or just use itemid
|
||||
document.title = `f0bm - ${itemid}`;
|
||||
if (navbar) navbar.classList.remove("pbwork");
|
||||
@@ -266,12 +383,50 @@ window.requestAnimFrame = (function () {
|
||||
|
||||
// Standard item links
|
||||
e.preventDefault();
|
||||
if (link.href.match(/\/p\/\d+/) || link.href.match(/[?&]page=\d+/)) {
|
||||
loadPageAjax(link.href);
|
||||
} else {
|
||||
loadItemAjax(link.href, true);
|
||||
}
|
||||
} else if (e.target.closest('#togglebg')) {
|
||||
e.preventDefault();
|
||||
background = !background;
|
||||
localStorage.setItem('background', background.toString());
|
||||
var canvas = document.getElementById('bg');
|
||||
if (canvas) {
|
||||
if (background) {
|
||||
canvas.classList.remove('fader-out');
|
||||
canvas.classList.add('fader-in');
|
||||
// Re-trigger loop if started completely fresh or paused
|
||||
if (video && !video.paused) {
|
||||
// We need to access animationLoop from closure?
|
||||
// Accessing it via window.initBackground might be cleaner or just restart it.
|
||||
// But initBackground defines it locally.
|
||||
// We can just rely on initBackground being called or canvas update.
|
||||
// Actually, if we just change opacity, the loop doesn't need to stop/start technically,
|
||||
// but for performance we stopped it if !background.
|
||||
// So we should restart it.
|
||||
window.initBackground();
|
||||
}
|
||||
} else {
|
||||
canvas.classList.remove('fader-in');
|
||||
canvas.classList.add('fader-out');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('popstate', (e) => {
|
||||
if (window.location.href.match(/\/p\/\d+/) || window.location.href.match(/[?&]page=\d+/) || window.location.pathname === '/') {
|
||||
// Ideally we should reload page or call loadPageAjax(currentUrl) if it supports it
|
||||
// But if we are going BACK to index from item, we expect grid.
|
||||
// loadItemAjax fails on index.
|
||||
// loadPageAjax handles /p/N logic.
|
||||
// If just slash, loadPageAjax might default to page 1.
|
||||
loadPageAjax(window.location.href);
|
||||
} else {
|
||||
loadItemAjax(window.location.href, true);
|
||||
}
|
||||
});
|
||||
|
||||
// <keybindings>
|
||||
@@ -593,6 +748,7 @@ window.requestAnimFrame = (function () {
|
||||
// </scroller>
|
||||
})();
|
||||
|
||||
|
||||
// disable default scroll event when mouse is on content div
|
||||
// this is useful for items that have a lot of tags for example: 12536
|
||||
const targetSelector = '.content';
|
||||
|
||||
@@ -23,7 +23,8 @@ export default (router, tpl) => {
|
||||
url: contextUrl,
|
||||
user: query.user,
|
||||
tag: query.tag,
|
||||
mime: query.mime
|
||||
mime: query.mime,
|
||||
fav: query.fav === 'true'
|
||||
});
|
||||
|
||||
if (!data.success) {
|
||||
@@ -38,10 +39,8 @@ export default (router, tpl) => {
|
||||
if (req.session) {
|
||||
data.session = { ...req.session };
|
||||
// data.user comes from f0cklib (uploader). req.session.user is logged-in user string.
|
||||
// If template engine confuses them, removing session.user from this context might help.
|
||||
// item-partial doesn't use session.user.
|
||||
// Note: If anything fails, it prints literal code, so we ensure no collision.
|
||||
if (data.session.user) delete data.session.user;
|
||||
// Templates use session.user for matching favorites. We must preserve it.
|
||||
// if (data.session.user) delete data.session.user; // REMOVED THIS
|
||||
} else {
|
||||
data.session = false;
|
||||
}
|
||||
@@ -103,6 +102,12 @@ export default (router, tpl) => {
|
||||
link: data.link
|
||||
});
|
||||
|
||||
// Render pagination
|
||||
const paginationHtml = tpl.render('snippets/pagination', {
|
||||
pagination: data.pagination,
|
||||
link: data.link
|
||||
});
|
||||
|
||||
const hasMore = data.pagination.next !== null;
|
||||
|
||||
return res.reply({
|
||||
@@ -110,6 +115,7 @@ export default (router, tpl) => {
|
||||
body: JSON.stringify({
|
||||
success: true,
|
||||
html: itemsHtml,
|
||||
pagination: paginationHtml,
|
||||
hasMore: hasMore,
|
||||
nextPage: data.pagination.next,
|
||||
currentPage: data.pagination.page
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@include(snippets/header)
|
||||
<canvas class="hidden-xs" id="bg"></canvas>
|
||||
|
||||
<div class="wrapper">
|
||||
<div id="main">
|
||||
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
<!doctype html>
|
||||
<html lang="en" theme="@if(typeof theme !== "undefined"){{ theme }}@endif" res="@if(typeof fullscreen !== "undefined"){{ fullscreen == 1 ? 'fullscreen' : '' }}@endif">
|
||||
<html lang="en" theme="@if(typeof theme !== 'undefined'){{ theme }}@endif"
|
||||
res="@if(typeof fullscreen !== 'undefined'){{ fullscreen == 1 ? 'fullscreen' : '' }}@endif">
|
||||
|
||||
<head>
|
||||
@if(typeof item !== "undefined")<title>f0bm - {{ item.id }}</title>@else<title>f0bm</title>@endif
|
||||
@if(typeof item !== 'undefined')<title>f0bm - {{ item.id }}</title>@else<title>f0bm</title>@endif
|
||||
<link rel="icon" type="image/gif" href="/s/img/favicon.png" />
|
||||
<link rel="stylesheet" href="/s/css/f0ck.css?v=@mtime(/public/s/css/f0ck.css)">
|
||||
<link rel="stylesheet" href="/s/css/w0bm.css?v=@mtime(/public/s/css/w0bm.css)">
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
@if(typeof item !== "undefined")<link rel="canonical" href="https://w0bm.com/{{ item.id }}" />@endif
|
||||
@if(typeof item !== 'undefined')
|
||||
<link rel="canonical" href="https://w0bm.com/{{ item.id }}" />@endif
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- hier splitting betreiben -->
|
||||
<canvas class="hidden-xs" id="bg"></canvas>
|
||||
@include(snippets/navbar)
|
||||
Reference in New Issue
Block a user