abyss internal link shortening, removal of # for ids and external preview
This commit is contained in:
@@ -1668,7 +1668,14 @@ class CommentSystem {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Build regex for allowed media hosters (video/audio)
|
// Abyss label replacement
|
||||||
|
md = md.replace(
|
||||||
|
/<a\s[^>]*href="(?:https?:\/\/[^\/]+)?\/abyss(?:#|\/)(\d+)"[^>]*>([\s\S]*?)<\/a>/gi,
|
||||||
|
(match, abyssId) => {
|
||||||
|
return `<a href="/abyss/${abyssId}" class="abyss-link" data-abyss-id="${abyssId}"><i class="fa-solid fa-dice-d6"></i> /abyss/${abyssId}</a>`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const mediaHosts = [escapedSiteHost];
|
const mediaHosts = [escapedSiteHost];
|
||||||
if (window.f0ckAllowedImages && Array.isArray(window.f0ckAllowedImages)) {
|
if (window.f0ckAllowedImages && Array.isArray(window.f0ckAllowedImages)) {
|
||||||
window.f0ckAllowedImages.forEach(h => {
|
window.f0ckAllowedImages.forEach(h => {
|
||||||
|
|||||||
@@ -1678,7 +1678,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
(parts.length >= 3 && (parts[0] === 'tag' || parts[0] === 'user' || parts[0] === 'h') && /^\d+$/.test(parts[parts.length - 1]))
|
(parts.length >= 3 && (parts[0] === 'tag' || parts[0] === 'user' || parts[0] === 'h') && /^\d+$/.test(parts[parts.length - 1]))
|
||||||
);
|
);
|
||||||
const isMessages = !!pathname.match(/^\/messages(\/|$)/);
|
const isMessages = !!pathname.match(/^\/messages(\/|$)/);
|
||||||
const isAbyss = !!pathname.match(/^\/abyss(\/?$|\?|#)/) || pathname === '/abyss';
|
const isAbyss = !!pathname.match(/^\/abyss(\/|$|\?|#)/) || pathname === '/abyss';
|
||||||
const isGrid = !isProfile && !isUserHall && !isUserHalls && !isHall && !isHalls && !isTags && !isComments && !isNotifs && !isItem && !isAdmin && !isMod && !isSettings && !isStatic && !isUpload && !isMessages && !isAbyss;
|
const isGrid = !isProfile && !isUserHall && !isUserHalls && !isHall && !isHalls && !isTags && !isComments && !isNotifs && !isItem && !isAdmin && !isMod && !isSettings && !isStatic && !isUpload && !isMessages && !isAbyss;
|
||||||
|
|
||||||
if (isItem) {
|
if (isItem) {
|
||||||
|
|||||||
@@ -229,28 +229,32 @@
|
|||||||
return ago(fmt(y === 1 ? i.ta_year : i.ta_years, y, 'year'));
|
return ago(fmt(y === 1 ? i.ta_year : i.ta_years, y, 'year'));
|
||||||
}
|
}
|
||||||
function hashId() {
|
function hashId() {
|
||||||
// Strip the leading '#' and allow numeric IDs or board/postid format
|
// Check path first /abyss/1234
|
||||||
|
const pathMatch = location.pathname.match(/\/abyss\/(\d+)$/);
|
||||||
|
if (pathMatch) return pathMatch[1];
|
||||||
|
|
||||||
|
// Fallback to hash
|
||||||
const raw = location.hash.replace(/^#/, '').trim();
|
const raw = location.hash.replace(/^#/, '').trim();
|
||||||
if (/^\d+$/.test(raw)) return raw;
|
if (/^\d+$/.test(raw)) return raw;
|
||||||
if (/^[a-z0-9]+\/\d+$/.test(raw)) return raw;
|
if (/^[a-z0-9]+\/\d+$/.test(raw)) return raw;
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
let lastPushedHash = location.hash;
|
let lastPushedUrl = location.pathname + location.hash;
|
||||||
function pushHash(id) {
|
function pushHash(id) {
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
const newHash = '#' + id;
|
const newUrl = '/abyss/' + id;
|
||||||
if (newHash === lastPushedHash) return;
|
if (newUrl === lastPushedUrl) return;
|
||||||
lastPushedHash = newHash;
|
lastPushedUrl = newUrl;
|
||||||
history.pushState({ scrollerId: id }, '', '/abyss' + newHash);
|
history.pushState({ scrollerId: id }, '', newUrl);
|
||||||
updateCacheActiveId(id);
|
updateCacheActiveId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle back/forward within abyss — scroll to the target slide
|
// Handle back/forward within abyss — scroll to the target slide
|
||||||
window.addEventListener('popstate', (e) => {
|
window.addEventListener('popstate', (e) => {
|
||||||
if (!document.body.classList.contains('scroller-active')) return;
|
if (!document.body.classList.contains('scroller-active')) return;
|
||||||
const id = e.state?.scrollerId || location.hash.replace('#', '');
|
const id = e.state?.scrollerId || hashId();
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
lastPushedHash = '#' + id;
|
lastPushedUrl = '/abyss/' + id;
|
||||||
const slide = feed.querySelector(`.scroll-slide[data-id="${id}"], .scroll-slide[data-local-id="${id}"]`);
|
const slide = feed.querySelector(`.scroll-slide[data-id="${id}"], .scroll-slide[data-local-id="${id}"]`);
|
||||||
if (slide) {
|
if (slide) {
|
||||||
slide.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
slide.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
const ytOembedCache = new Map(); // videoId -> meta object
|
const ytOembedCache = new Map(); // videoId -> meta object
|
||||||
const ytOembedPending = new Map(); // videoId -> Promise
|
const ytOembedPending = new Map(); // videoId -> Promise
|
||||||
|
|
||||||
|
|
||||||
const fetchSidebarYoutubeTitles = async (container) => {
|
const fetchSidebarYoutubeTitles = async (container) => {
|
||||||
const links = container.querySelectorAll('.sidebar-video-link[data-yt-id]');
|
const links = container.querySelectorAll('.sidebar-video-link[data-yt-id]');
|
||||||
if (links.length === 0) return;
|
if (links.length === 0) return;
|
||||||
@@ -290,6 +291,14 @@
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Abyss label replacement
|
||||||
|
md = md.replace(
|
||||||
|
/<a\s[^>]*href="(?:https?:\/\/[^\/]+)?\/abyss(?:#|\/)(\d+)"[^>]*>([\s\S]*?)<\/a>/gi,
|
||||||
|
(match, abyssId) => {
|
||||||
|
return `<a href="/abyss/${abyssId}" class="sidebar-abyss-link" data-abyss-id="${abyssId}"><i class="fa-solid fa-dice-d6"></i> /abyss/${abyssId}</a>`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Build regex for allowed media hosters (video/audio)
|
// Build regex for allowed media hosters (video/audio)
|
||||||
const escapedSiteHost = window.location.host.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
const escapedSiteHost = window.location.host.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
const mediaHosts = [escapedSiteHost];
|
const mediaHosts = [escapedSiteHost];
|
||||||
|
|||||||
@@ -4,22 +4,56 @@ import lib from "../lib.mjs";
|
|||||||
|
|
||||||
export default (router, tpl) => {
|
export default (router, tpl) => {
|
||||||
// Serve the scroller page
|
// Serve the scroller page
|
||||||
router.get(/^\/abyss\/?$/, async (req, res) => {
|
router.get(/^\/abyss(?:\/(?<id>[0-9]+))?\/?$/, async (req, res) => {
|
||||||
if (cfg.websrv.abyss_enabled === false) return res.reply({ code: 404, body: tpl.render('error', { message: 'Not found', tmp: null }, req) });
|
if (cfg.websrv.abyss_enabled === false) return res.reply({ code: 404, body: tpl.render('error', { message: 'Not found', tmp: null }, req) });
|
||||||
if (cfg.websrv.private_society && !req.session) {
|
if (cfg.websrv.private_society && !req.session) {
|
||||||
return res.reply({ code: 502, body: '<html><body>502 Bad Gateway</body></html>' });
|
return res.reply({ code: 502, body: '<html><body>502 Bad Gateway</body></html>' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const id = req.params?.id;
|
||||||
|
let page_meta = {
|
||||||
|
title: 'doomscroll',
|
||||||
|
description: 'Scroll through content endlessly',
|
||||||
|
url: `https://${cfg.main.url.domain}/abyss`
|
||||||
|
};
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
try {
|
||||||
|
const items = await db`
|
||||||
|
select i.*, uo.display_name
|
||||||
|
from "items" i
|
||||||
|
left join users u on u.id = i.author
|
||||||
|
left join user_options uo on uo.user_id = u.id
|
||||||
|
where i.id = ${+id} and i.active = true
|
||||||
|
limit 1
|
||||||
|
`;
|
||||||
|
if (items.length > 0) {
|
||||||
|
const item = items[0];
|
||||||
|
|
||||||
|
// Fetch tags to check for NSFW/NSFL
|
||||||
|
const tags = await db`
|
||||||
|
select tag_id from tags_assign where item_id = ${+id}
|
||||||
|
`;
|
||||||
|
const tagIds = tags.map(t => t.tag_id);
|
||||||
|
const isBlurred = tagIds.includes(2) || tagIds.includes(cfg.nsfl_tag_id || 3);
|
||||||
|
|
||||||
|
page_meta.title = `${id}`;
|
||||||
|
page_meta.description = cfg.websrv.description || "The webs dumpster";
|
||||||
|
page_meta.url = `https://${cfg.main.url.domain}/abyss/${id}`;
|
||||||
|
page_meta.image = `https://${cfg.main.url.domain}/t/${id}${isBlurred ? '_blur' : ''}.webp`;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[SCROLLER] Failed to fetch meta for ID:', id, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res.reply({
|
return res.reply({
|
||||||
body: tpl.render('scroller', {
|
body: tpl.render('scroller', {
|
||||||
tmp: null,
|
tmp: null,
|
||||||
session: req.session ? { ...req.session } : false,
|
session: req.session ? { ...req.session } : false,
|
||||||
enable_nsfl: !!cfg.enable_nsfl,
|
enable_nsfl: !!cfg.enable_nsfl,
|
||||||
enable_swf: !!cfg.websrv.enable_swf,
|
enable_swf: !!cfg.websrv.enable_swf,
|
||||||
page_meta: {
|
page_meta
|
||||||
title: 'doomscroll',
|
|
||||||
description: 'Scroll through content endlessly',
|
|
||||||
url: `https://${cfg.main.url.domain}/abyss`
|
|
||||||
}
|
|
||||||
}, req)
|
}, req)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user