fixed issues with the random button and hotkeys #2

Merged
eins merged 3 commits from eins/f0bm:f0bm into f0bm 2026-01-23 14:53:20 +00:00
6 changed files with 105 additions and 90 deletions

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ deleted/b
deleted/ca deleted/ca
deleted/t deleted/t
tmp/* tmp/*
tools

View File

@@ -130,12 +130,12 @@
return false; return false;
} }
renderTags(res.tags); renderTags(res.tags);
span.parentElement.removeChild(span); if (span.parentElement) span.parentElement.removeChild(span);
testList.innerText = ""; testList.innerText = "";
addtagClick(); addtagClick();
} }
else if (e.key === "Escape") { else if (e.key === "Escape") {
span.parentElement.removeChild(span); if (span.parentElement) span.parentElement.removeChild(span);
testList.innerText = ""; testList.innerText = "";
} }
else { else {

View File

@@ -140,7 +140,7 @@ window.requestAnimFrame = (function () {
html = rawText; html = rawText;
} }
let container = document.querySelector('.container'); let container = document.querySelector('#main .container');
if (!container && document.querySelector('.index-container')) { if (!container && document.querySelector('.index-container')) {
// Transition from Index to Item View // Transition from Index to Item View
@@ -148,13 +148,19 @@ window.requestAnimFrame = (function () {
main.innerHTML = '<div class="container"></div>'; main.innerHTML = '<div class="container"></div>';
container = main.querySelector('.container'); container = main.querySelector('.container');
} else if (container) { } else if (container) {
// Already in Item View, clear usage // Check if we are on Tags Overview logic (which reuses .container)
const oldContent = container.querySelector('.content'); const tagsOverview = container.querySelector('.tags');
const oldMetadata = container.querySelector('.metadata'); if (tagsOverview) {
const oldHeader = container.querySelector('._204863'); container.innerHTML = '';
if (oldHeader) oldHeader.remove(); } else {
if (oldContent) oldContent.remove(); // Already in Item View, clear usage
if (oldMetadata) oldMetadata.remove(); const oldContent = container.querySelector('.content');
const oldMetadata = container.querySelector('.metadata');
const oldHeader = container.querySelector('._204863');
if (oldHeader) oldHeader.remove();
if (oldContent) oldContent.remove();
if (oldMetadata) oldMetadata.remove();
}
} }
container.insertAdjacentHTML('beforeend', html); container.insertAdjacentHTML('beforeend', html);
@@ -187,13 +193,12 @@ window.requestAnimFrame = (function () {
}; };
const changePage = (e, pbwork = true) => { const changePage = (e, pbwork = true) => {
if (e.tagName === 'A') { if (pbwork) {
e.preventDefault(); const nav = document.querySelector("nav.navbar");
loadItemAjax(e.href); if (nav) nav.classList.add("pbwork");
} else {
pbwork && document.querySelector("nav.navbar").classList.add("pbwork");
!tt && (tt = setTimeout(() => e.click(), stimeout));
} }
// Trigger native click for navigation
e.click();
}; };
// Intercept clicks // Intercept clicks
@@ -208,10 +213,10 @@ window.requestAnimFrame = (function () {
return; return;
} }
const link = e.target.closest('#next, #prev, #random, .id-link, .nav-next, .nav-prev'); const link = e.target.closest('#next, #prev, #random, #nav-random, .id-link, .nav-next, .nav-prev');
if (link && link.href && link.hostname === window.location.hostname && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey) { if (link && link.href && link.hostname === window.location.hostname && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey) {
// Special check for random // Special check for random
if (link.id === 'random') { if (link.id === 'random' || link.id === 'nav-random') {
e.preventDefault(); e.preventDefault();
const nav = document.querySelector("nav.navbar"); const nav = document.querySelector("nav.navbar");
if (nav) nav.classList.add("pbwork"); if (nav) nav.classList.add("pbwork");
@@ -240,7 +245,10 @@ window.requestAnimFrame = (function () {
window.location.href = link.href; window.location.href = link.href;
} }
}) })
.catch(() => window.location.href = link.href); .catch((err) => {
console.error("Random fetch failed:", err);
window.location.href = link.href;
});
return; return;
} }
@@ -261,7 +269,7 @@ window.requestAnimFrame = (function () {
"a": clickOnElementBinding("#next"), "a": clickOnElementBinding("#next"),
"ArrowRight": clickOnElementBinding("#prev"), "ArrowRight": clickOnElementBinding("#prev"),
"d": clickOnElementBinding("#prev"), "d": clickOnElementBinding("#prev"),
"r": clickOnElementBinding("#random"), "r": clickOnElementBinding("#random, #nav-random"),
" ": clickOnElementBinding("#f0ck-image") " ": clickOnElementBinding("#f0ck-image")
}; };
document.addEventListener("keydown", e => { document.addEventListener("keydown", e => {
@@ -541,10 +549,13 @@ function init() {
window.addEventListener('load', init); window.addEventListener('load', init);
document.getElementById('sbtForm').addEventListener('submit', (e) => { const sbtForm = document.getElementById('sbtForm');
e.preventDefault(); if (sbtForm) {
const input = document.getElementById('sbtInput').value.trim(); sbtForm.addEventListener('submit', (e) => {
if (input) { e.preventDefault();
window.location.href = `/tag/${encodeURIComponent(input)}`; const input = document.getElementById('sbtInput').value.trim();
} if (input) {
}); window.location.href = `/tag/${encodeURIComponent(input)}`;
}
});
}

View File

@@ -1,10 +1,10 @@
const Cookie = { const Cookie = {
get: name => { get: name => {
const c = document.cookie.match(`(?:(?:^|.*; *)${name} *= *([^;]*).*$)|^.*$`)[1]; const c = document.cookie.match(`(?:(?:^|.*; *)${name} *= *([^;]*).*$)|^.*$`)[1];
if(c) return decodeURIComponent(c); if (c) return decodeURIComponent(c);
}, },
set: (name, value, opts = {}) => { set: (name, value, opts = {}) => {
if(opts.days) { if (opts.days) {
opts['max-age'] = opts.days * 60 * 60 * 24; opts['max-age'] = opts.days * 60 * 60 * 24;
delete opts.days; delete opts.days;
} }
@@ -17,8 +17,11 @@ const Cookie = {
(() => { (() => {
const acttheme = Cookie.get('theme') ?? "w0bm"; const acttheme = Cookie.get('theme') ?? "w0bm";
const themecontainer = document.querySelector("li#themes > ul.dropdown-menu"); const themecontainer = document.querySelector("li#themes > ul.dropdown-menu");
if (!themecontainer) return; // Theme menu not present on this page
const themes = [...themecontainer.querySelectorAll("li > a")].map(t => t.innerText.toLowerCase()); const themes = [...themecontainer.querySelectorAll("li > a")].map(t => t.innerText.toLowerCase());
if(acttheme !== document.documentElement.getAttribute("theme") && themes.includes(acttheme)) if (acttheme !== document.documentElement.getAttribute("theme") && themes.includes(acttheme))
document.documentElement.setAttribute("theme", acttheme); document.documentElement.setAttribute("theme", acttheme);
[...themecontainer.querySelectorAll("li > a")].forEach(t => t.addEventListener("click", e => { [...themecontainer.querySelectorAll("li > a")].forEach(t => t.addEventListener("click", e => {
e.preventDefault(); e.preventDefault();
@@ -30,15 +33,15 @@ const Cookie = {
})); }));
document.addEventListener("keydown", e => { document.addEventListener("keydown", e => {
if(e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA")
return; return;
const acttheme = Cookie.get('theme') ?? "w0bm"; const acttheme = Cookie.get('theme') ?? "w0bm";
const themes = [...themecontainer.querySelectorAll("li > a")].map(t => t.innerText.toLowerCase()); const themes = [...themecontainer.querySelectorAll("li > a")].map(t => t.innerText.toLowerCase());
const k = e.key; const k = e.key;
if(k === "t") { if (k === "t") {
e.preventDefault(); e.preventDefault();
let i = themes.indexOf(acttheme); let i = themes.indexOf(acttheme);
if(++i >= themes.length) if (++i >= themes.length)
i = 0; i = 0;
document.documentElement.setAttribute("theme", themes[i]); document.documentElement.setAttribute("theme", themes[i]);
document.querySelector("#themes > a").setAttribute("content", themes[i]); document.querySelector("#themes > a").setAttribute("content", themes[i]);
@@ -46,10 +49,10 @@ const Cookie = {
} }
}); });
if(tbuttonfull = document.querySelector('svg#a_tfull')) { if (tbuttonfull = document.querySelector('svg#a_tfull')) {
tbuttonfull.addEventListener('click', e => { tbuttonfull.addEventListener('click', e => {
let f = Cookie.get('fullscreen'); let f = Cookie.get('fullscreen');
if(f == 1) { if (f == 1) {
Cookie.set('fullscreen', 0); Cookie.set('fullscreen', 0);
document.querySelector('html').setAttribute('res', ''); document.querySelector('html').setAttribute('res', '');
tbuttonfull.innerHTML = `<use href="/s/img/iconset.svg#window-maximize"></use>`; tbuttonfull.innerHTML = `<use href="/s/img/iconset.svg#window-maximize"></use>`;

View File

@@ -3,7 +3,7 @@ import db from '../../sql.mjs';
import lib from '../../lib.mjs'; import lib from '../../lib.mjs';
import search from '../../routeinc/search.mjs'; import search from '../../routeinc/search.mjs';
const allowedMimes = [ "audio", "image", "video", "%" ]; const allowedMimes = ["audio", "image", "video", "%"];
export default router => { export default router => {
router.group(/^\/api\/v2/, group => { router.group(/^\/api\/v2/, group => {
group.get(/$/, (req, res) => { group.get(/$/, (req, res) => {
@@ -13,24 +13,28 @@ export default router => {
group.get(/\/random(\/user\/.+|\/image|\/video|\/audio)?$/, async (req, res) => { group.get(/\/random(\/user\/.+|\/image|\/video|\/audio)?$/, async (req, res) => {
const user = req.url.split[3] === "user" ? req.url.split[4] : "%"; const user = req.url.split[3] === "user" ? req.url.split[4] : "%";
const mime = (allowedMimes.filter(n => req.url.split[3]?.startsWith(n))[0] ? req.url.split[3] : "") + "%"; const mime = (allowedMimes.filter(n => req.url.split[3]?.startsWith(n))[0] ? req.url.split[3] : "") + "%";
const tag = req.url.qs.tag || null;
const rows = await db` const rows = await db`
select * select "items".*
from "items" from "items"
left join tags_assign on tags_assign.item_id = items.id
left join tags on tags.id = tags_assign.tag_id
where where
mime ilike ${mime} and mime ilike ${mime} and
username ilike ${user} and username ilike ${user} and
active = 'true' active = 'true'
${tag ? db`and tags.normalized ilike ${'%' + tag + '%'}` : db``}
order by random() order by random()
limit 1 limit 1
`; `;
return res.json({ return res.json({
success: rows.length > 0, success: rows.length > 0,
items: rows.length > 0 ? rows[0] : [] items: rows.length > 0 ? rows[0] : []
}); });
}); });
group.get(/\/items\/get/, async (req, res) => { group.get(/\/items\/get/, async (req, res) => {
let eps = 150; let eps = 150;
@@ -51,17 +55,15 @@ export default router => {
where where
${db.unsafe(modequery)} and ${db.unsafe(modequery)} and
active = 'true' active = 'true'
${ ${opt.older
opt.older ? db`and id <= ${opt.older}`
? db`and id <= ${opt.older}` : opt.newer
: opt.newer ? db`and id >= ${opt.newer}`
? db`and id >= ${opt.newer}` : db``
: db`` }
} order by id ${opt.newer
order by id ${ ? db`asc`
opt.newer : db`desc`
? db`asc`
: db`desc`
} }
limit ${eps} limit ${eps}
`).sort((a, b) => b.id - a.id); `).sort((a, b) => b.id - a.id);
@@ -73,10 +75,10 @@ export default router => {
items: rows items: rows
}, 200); }, 200);
}); });
group.get(/\/item\/[0-9]+$/, async (req, res) => { group.get(/\/item\/[0-9]+$/, async (req, res) => {
const id = +req.url.split[3]; const id = +req.url.split[3];
const item = await db` const item = await db`
select * select *
from "items" from "items"
@@ -97,14 +99,14 @@ export default router => {
order by id desc order by id desc
limit 1 limit 1
`; `;
if(item.length === 0) { if (item.length === 0) {
return res.json({ return res.json({
success: false, success: false,
msg: 'no items found' msg: 'no items found'
}); });
} }
const rows = { const rows = {
...item[0], ...item[0],
...{ ...{
@@ -118,11 +120,11 @@ export default router => {
rows rows
}); });
}); });
group.get(/\/user\/.*(\/\d+)?$/, async (req, res) => { group.get(/\/user\/.*(\/\d+)?$/, async (req, res) => {
const user = req.url.split[3]; const user = req.url.split[3];
const eps = +req.url.split[4] || 50; const eps = +req.url.split[4] || 50;
const rows = db` const rows = db`
select id, mime, size, src, stamp, userchannel, username, usernetwork select id, mime, size, src, stamp, userchannel, username, usernetwork
from "items" from "items"
@@ -130,7 +132,7 @@ export default router => {
order by stamp desc order by stamp desc
limit ${+eps} limit ${+eps}
`; `;
return res.json({ return res.json({
success: rows.length > 0, success: rows.length > 0,
items: rows.length > 0 ? rows : [] items: rows.length > 0 ? rows : []
@@ -140,7 +142,7 @@ export default router => {
// tags lol // tags lol
group.put(/\/admin\/tags\/(?<tagname>.*)/, lib.loggedin, async (req, res) => { group.put(/\/admin\/tags\/(?<tagname>.*)/, lib.loggedin, async (req, res) => {
if(!req.params.tagname || !req.post.newtag) { if (!req.params.tagname || !req.post.newtag) {
return res.json({ return res.json({
success: false, success: false,
msg: 'missing tagname or newtag', msg: 'missing tagname or newtag',
@@ -154,7 +156,7 @@ export default router => {
const tagname = decodeURIComponent(req.params.tagname); const tagname = decodeURIComponent(req.params.tagname);
const newtag = req.post.newtag; const newtag = req.post.newtag;
if(['sfw', 'nsfw'].includes(tagname) || ['sfw', 'nsfw'].includes(newtag)) { if (['sfw', 'nsfw'].includes(tagname) || ['sfw', 'nsfw'].includes(newtag)) {
return res.json({ return res.json({
msg: 'f0ck you' msg: 'f0ck you'
}, 405); // method not allowed }, 405); // method not allowed
@@ -166,8 +168,8 @@ export default router => {
where tag = ${tagname} where tag = ${tagname}
limit 1 limit 1
`)[0]; `)[0];
if(!tmptag) { if (!tmptag) {
return res.json({ return res.json({
success: false, success: false,
msg: 'no tag found' msg: 'no tag found'
@@ -175,10 +177,9 @@ export default router => {
} }
const q = (await db` const q = (await db`
update "tags" set ${ update "tags" set ${db({
db({ tag: newtag
tag: newtag }, 'tag')
}, 'tag')
} }
where tag = ${tagname} where tag = ${tagname}
returning * returning *
@@ -195,7 +196,7 @@ export default router => {
const searchString = req.url.qs.q; const searchString = req.url.qs.q;
if(searchString?.length <= 1) { if (searchString?.length <= 1) {
reply.error = 'too short lol'; reply.error = 'too short lol';
return res.json(reply); return res.json(reply);
} }
@@ -212,7 +213,7 @@ export default router => {
`; `;
reply.success = true; reply.success = true;
reply.suggestions = search(q, searchString); reply.suggestions = search(q, searchString);
} catch(err) { } catch (err) {
reply.error = err.msg; reply.error = err.msg;
} }
@@ -220,7 +221,7 @@ export default router => {
}); });
group.post(/\/admin\/deletepost$/, lib.auth, async (req, res) => { group.post(/\/admin\/deletepost$/, lib.auth, async (req, res) => {
if(!req.post.postid) { if (!req.post.postid) {
return res.json({ return res.json({
success: false, success: false,
msg: 'no postid' msg: 'no postid'
@@ -228,7 +229,7 @@ export default router => {
} }
const id = +req.post.postid; const id = +req.post.postid;
if(id <= 1) { if (id <= 1) {
return res.json({ return res.json({
success: false success: false
}); });
@@ -243,7 +244,7 @@ export default router => {
limit 1 limit 1
`; `;
if(f0ck.length === 0) { if (f0ck.length === 0) {
return res.json({ return res.json({
success: false, success: false,
msg: `f0ck ${id}: f0ck not found` msg: `f0ck ${id}: f0ck not found`
@@ -251,15 +252,15 @@ export default router => {
} }
await db`update "items" set active = 'false' where id = ${id}`; await db`update "items" set active = 'false' where id = ${id}`;
await fs.copyFile(`./public/b/${f0ck[0].dest}`, `./deleted/b/${f0ck[0].dest}`).catch(_=>{});
await fs.copyFile(`./public/t/${id}.webp`, `./deleted/t/${id}.webp`).catch(_=>{});
await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(_=>{});
await fs.unlink(`./public/t/${id}.webp`).catch(_=>{});
if(f0ck[0].mime.startsWith('audio')) { await fs.copyFile(`./public/b/${f0ck[0].dest}`, `./deleted/b/${f0ck[0].dest}`).catch(_ => { });
await fs.copyFile(`./public/ca/${id}.webp`, `./deleted/ca/${id}.webp`).catch(_=>{}); await fs.copyFile(`./public/t/${id}.webp`, `./deleted/t/${id}.webp`).catch(_ => { });
await fs.unlink(`./public/ca/${id}.webp`).catch(_=>{}); await fs.unlink(`./public/b/${f0ck[0].dest}`).catch(_ => { });
await fs.unlink(`./public/t/${id}.webp`).catch(_ => { });
if (f0ck[0].mime.startsWith('audio')) {
await fs.copyFile(`./public/ca/${id}.webp`, `./deleted/ca/${id}.webp`).catch(_ => { });
await fs.unlink(`./public/ca/${id}.webp`).catch(_ => { });
} }
res.json({ res.json({
@@ -269,14 +270,14 @@ export default router => {
group.post(/\/admin\/togglefav$/, lib.loggedin, async (req, res) => { group.post(/\/admin\/togglefav$/, lib.loggedin, async (req, res) => {
const postid = +req.post.postid; const postid = +req.post.postid;
let favs = await db` let favs = await db`
select user_id select user_id
from "favorites" from "favorites"
where item_id = ${+postid} where item_id = ${+postid}
`; `;
if(Object.values(favs).filter(u => u.user_id === req.session.id)[0]) { if (Object.values(favs).filter(u => u.user_id === req.session.id)[0]) {
// del fav // del fav
await db` await db`
delete from "favorites" delete from "favorites"
@@ -287,11 +288,10 @@ export default router => {
else { else {
// add fav // add fav
await db` await db`
insert into "favorites" ${ insert into "favorites" ${db({
db({ item_id: +postid,
item_id: +postid, user_id: +req.session.id
user_id: +req.session.id }, 'item_id', 'user_id')
}, 'item_id', 'user_id')
} }
`; `;
} }
@@ -310,7 +310,7 @@ export default router => {
favs favs
}); });
}); });
}); });
return router; return router;

View File

@@ -8,7 +8,7 @@
<a href="/tags">tags</a> <a href="/tags">tags</a>
<a href="/about">about</a> <a href="/about">about</a>
@if(!/^\/\d$/.test(url.pathname)) @if(!/^\/\d$/.test(url.pathname))
<a href="/random">rand</a> <a href="/random" id="nav-random">rand</a>
@endif @endif
</ol> </ol>
</div> </div>
@@ -33,7 +33,7 @@
<a href="/tags">tags</a> <a href="/tags">tags</a>
<a href="/about">about</a> <a href="/about">about</a>
@if(!/^\/\d$/.test(url.pathname)) @if(!/^\/\d$/.test(url.pathname))
<a href="/random">rand</a> <a href="/random" id="nav-random">rand</a>
@endif @endif
</ol> </ol>
</div> </div>