tag depths
This commit is contained in:
@@ -2603,6 +2603,10 @@ window.cancelAnimFrame = (function () {
|
|||||||
if (isFav) ajaxUrl += `&fav=true`;
|
if (isFav) ajaxUrl += `&fav=true`;
|
||||||
if (mime) ajaxUrl += `&mime=${encodeURIComponent(mime)}`;
|
if (mime) ajaxUrl += `&mime=${encodeURIComponent(mime)}`;
|
||||||
|
|
||||||
|
// Preserve tagger filter from URL query string
|
||||||
|
const taggerMatch = urlObj.searchParams.get('tagger');
|
||||||
|
if (taggerMatch) ajaxUrl += `&tagger=${encodeURIComponent(taggerMatch)}`;
|
||||||
|
|
||||||
const isRandom = document.cookie.includes('random_mode=1') || url.includes('random=1') || window.location.search.includes('random=1');
|
const isRandom = document.cookie.includes('random_mode=1') || url.includes('random=1') || window.location.search.includes('random=1');
|
||||||
if (isRandom) ajaxUrl += `&random=1`;
|
if (isRandom) ajaxUrl += `&random=1`;
|
||||||
const isStrict = window.f0ckSession?.strict_mode || (localStorage.getItem('search_strict') === 'true');
|
const isStrict = window.f0ckSession?.strict_mode || (localStorage.getItem('search_strict') === 'true');
|
||||||
@@ -3327,10 +3331,15 @@ window.cancelAnimFrame = (function () {
|
|||||||
const itemid = numericSegments.pop();
|
const itemid = numericSegments.pop();
|
||||||
|
|
||||||
// Extract context from Target URL first
|
// Extract context from Target URL first
|
||||||
let tag = null, user = null, isFavs = false, mime = null, hall = null, userHall = null, userHallOwner = null;
|
let tag = null, user = null, isFavs = false, mime = null, hall = null, userHall = null, userHallOwner = null, tagger = null;
|
||||||
const tagMatch = url.match(/\/tag\/([^/?]+)/);
|
const tagMatch = url.match(/\/tag\/([^/?]+)/);
|
||||||
if (tagMatch) tag = decodeURIComponent(tagMatch[1]);
|
if (tagMatch) tag = decodeURIComponent(tagMatch[1]);
|
||||||
|
|
||||||
|
// Tagger filter from query string
|
||||||
|
const taggerUrlObj = new URL(url, window.location.origin);
|
||||||
|
const taggerParam = taggerUrlObj.searchParams.get('tagger');
|
||||||
|
if (taggerParam) tagger = decodeURIComponent(taggerParam);
|
||||||
|
|
||||||
// User hall: /user/:owner/hall/:slug/:id
|
// User hall: /user/:owner/hall/:slug/:id
|
||||||
const userHallMatch = url.match(/\/user\/([^/]+)\/hall\/([^/]+)/);
|
const userHallMatch = url.match(/\/user\/([^/]+)\/hall\/([^/]+)/);
|
||||||
if (userHallMatch) {
|
if (userHallMatch) {
|
||||||
@@ -3368,6 +3377,10 @@ window.cancelAnimFrame = (function () {
|
|||||||
const wTagMatch = window.location.href.match(/\/tag\/([^/?]+)/);
|
const wTagMatch = window.location.href.match(/\/tag\/([^/?]+)/);
|
||||||
if (wTagMatch) tag = decodeURIComponent(wTagMatch[1]);
|
if (wTagMatch) tag = decodeURIComponent(wTagMatch[1]);
|
||||||
}
|
}
|
||||||
|
if (!tagger) {
|
||||||
|
const wTaggerParam = new URL(window.location.href).searchParams.get('tagger');
|
||||||
|
if (wTaggerParam) tagger = decodeURIComponent(wTaggerParam);
|
||||||
|
}
|
||||||
if (!user && !userHall) {
|
if (!user && !userHall) {
|
||||||
const wUserMatch = window.location.href.match(/\/user\/([^/?]+)/);
|
const wUserMatch = window.location.href.match(/\/user\/([^/?]+)/);
|
||||||
if (wUserMatch && !window.location.href.match(/\/user\/[^/]+\/hall\//) ) {
|
if (wUserMatch && !window.location.href.match(/\/user\/[^/]+\/hall\//) ) {
|
||||||
@@ -3403,6 +3416,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
} else if (user) {
|
} else if (user) {
|
||||||
params.append('user', user);
|
params.append('user', user);
|
||||||
}
|
}
|
||||||
|
if (tagger) params.append('tagger', tagger);
|
||||||
if (isFavs) params.append('fav', 'true');
|
if (isFavs) params.append('fav', 'true');
|
||||||
|
|
||||||
const isStrict = window.f0ckSession?.strict_mode || (localStorage.getItem('search_strict') === 'true');
|
const isStrict = window.f0ckSession?.strict_mode || (localStorage.getItem('search_strict') === 'true');
|
||||||
@@ -3502,6 +3516,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
else if (user) { _pushUrl = `/user/${encodeURIComponent(user)}/${itemid}`; if (isFavs) _pushUrl = `/user/${encodeURIComponent(user)}/favs/${itemid}`; }
|
else if (user) { _pushUrl = `/user/${encodeURIComponent(user)}/${itemid}`; if (isFavs) _pushUrl = `/user/${encodeURIComponent(user)}/favs/${itemid}`; }
|
||||||
else if (tag) _pushUrl = `/tag/${encodeURIComponent(tag).replace(/%2C/g,',').replace(/%20/g,' ')}/${itemid}`;
|
else if (tag) _pushUrl = `/tag/${encodeURIComponent(tag).replace(/%2C/g,',').replace(/%20/g,' ')}/${itemid}`;
|
||||||
else if (hall) _pushUrl = `/h/${encodeURIComponent(hall).replace(/%20/g,' ')}/${itemid}`;
|
else if (hall) _pushUrl = `/h/${encodeURIComponent(hall).replace(/%20/g,' ')}/${itemid}`;
|
||||||
|
if (tagger && tag) _pushUrl += `?tagger=${encodeURIComponent(tagger)}`;
|
||||||
if (mime) _pushUrl = _pushUrl.replace(new RegExp(`/${itemid}$`), `/${mime}/${itemid}`);
|
if (mime) _pushUrl = _pushUrl.replace(new RegExp(`/${itemid}$`), `/${mime}/${itemid}`);
|
||||||
if (_hash) _pushUrl += _hash;
|
if (_hash) _pushUrl += _hash;
|
||||||
|
|
||||||
@@ -3704,6 +3719,8 @@ window.cancelAnimFrame = (function () {
|
|||||||
}
|
}
|
||||||
else if (tag) pushUrl = `/tag/${encodeURIComponent(tag).replace(/%2C/g, ',').replace(/%20/g, ' ')}/${itemid}`;
|
else if (tag) pushUrl = `/tag/${encodeURIComponent(tag).replace(/%2C/g, ',').replace(/%20/g, ' ')}/${itemid}`;
|
||||||
else if (hall) pushUrl = `/h/${encodeURIComponent(hall).replace(/%20/g, ' ')}/${itemid}`;
|
else if (hall) pushUrl = `/h/${encodeURIComponent(hall).replace(/%20/g, ' ')}/${itemid}`;
|
||||||
|
// Append tagger filter so item nav stays in tagger context
|
||||||
|
if (tagger && tag) pushUrl += `?tagger=${encodeURIComponent(tagger)}`;
|
||||||
|
|
||||||
if (mime) {
|
if (mime) {
|
||||||
// If it already has itemid at the end, insert mime before it
|
// If it already has itemid at the end, insert mime before it
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ export default new class {
|
|||||||
|
|
||||||
// Build suffix with query params
|
// Build suffix with query params
|
||||||
let suffix = env.strict ? '?strict=1' : '';
|
let suffix = env.strict ? '?strict=1' : '';
|
||||||
|
if (env.tagger) suffix += (suffix ? '&' : '?') + `tagger=${encodeURIComponent(env.tagger)}`;
|
||||||
|
|
||||||
// mainDisplay: decoded for human-readable display (e.g. div.location)
|
// mainDisplay: decoded for human-readable display (e.g. div.location)
|
||||||
// main: keeps percent-encoding for use in href attributes
|
// main: keeps percent-encoding for use in href attributes
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const flashMimes = Object.entries(cfg.mimes || {}).filter(([, ext]) => ext === '
|
|||||||
const COUNT_CACHE_TTL_MS = 90_000;
|
const COUNT_CACHE_TTL_MS = 90_000;
|
||||||
const countCache = new Map(); // key → { total, expiresAt }
|
const countCache = new Map(); // key → { total, expiresAt }
|
||||||
|
|
||||||
function buildCountCacheKey({ modequery, tag, user, hall, mime, fav, session, excludedTags, newerThan, minXd, userHallObj }) {
|
function buildCountCacheKey({ modequery, tag, user, hall, mime, fav, session, excludedTags, newerThan, minXd, userHallObj, tagger }) {
|
||||||
return JSON.stringify([
|
return JSON.stringify([
|
||||||
modequery,
|
modequery,
|
||||||
tag ?? '',
|
tag ?? '',
|
||||||
@@ -30,7 +30,8 @@ function buildCountCacheKey({ modequery, tag, user, hall, mime, fav, session, ex
|
|||||||
excludedTags.slice().sort().join(','),
|
excludedTags.slice().sort().join(','),
|
||||||
newerThan ?? '',
|
newerThan ?? '',
|
||||||
minXd,
|
minXd,
|
||||||
userHallObj?.id ?? ''
|
userHallObj?.id ?? '',
|
||||||
|
tagger ?? ''
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +137,7 @@ const xdScoreMeta = (score) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getf0cks: async ({ user: rawUser, tag: rawTag, hall: rawHall, mime: rawMime, page, mode, ratings, fav, session, limit, strict, newer, exclude, user_id, random, userHall: rawUserHall, userHallOwner: rawUserHallOwner, minXdScore } = {}) => {
|
getf0cks: async ({ user: rawUser, tag: rawTag, hall: rawHall, mime: rawMime, page, mode, ratings, fav, session, limit, strict, newer, exclude, user_id, random, userHall: rawUserHall, userHallOwner: rawUserHallOwner, minXdScore, tagger: rawTagger } = {}) => {
|
||||||
const user = rawUser ? lib.escapeLike(decodeURI(rawUser)) : null;
|
const user = rawUser ? lib.escapeLike(decodeURI(rawUser)) : null;
|
||||||
|
|
||||||
// --- title: prefix — search items.title instead of the tags table ---
|
// --- title: prefix — search items.title instead of the tags table ---
|
||||||
@@ -189,7 +190,8 @@ export default {
|
|||||||
const strictParams = ((strict || (tag && tag.includes(','))) && tag) ? tag.split(',').map(t => lib.slugify(t)).filter(t => t) : [];
|
const strictParams = ((strict || (tag && tag.includes(','))) && tag) ? tag.split(',').map(t => lib.slugify(t)).filter(t => t) : [];
|
||||||
const isStrict = strictParams.length > 0;
|
const isStrict = strictParams.length > 0;
|
||||||
|
|
||||||
const tmp = { user, tag: isTitleSearch ? _decodedTag : tag, hall: hallObj || hall, mime, page: actPage, mode: mode, view_mode: fav ? 'favs' : 'uploads', strict: strict, userHall: userHallObj || userHallSlug, userHallOwner };
|
const tagger = rawTagger ? lib.escapeLike(rawTagger) : null;
|
||||||
|
const tmp = { user, tag: isTitleSearch ? _decodedTag : tag, hall: hallObj || hall, mime, page: actPage, mode: mode, view_mode: fav ? 'favs' : 'uploads', strict: strict, userHall: userHallObj || userHallSlug, userHallOwner, tagger };
|
||||||
// Multi-rating support: if `ratings` array provided, build an OR-based SQL fragment
|
// Multi-rating support: if `ratings` array provided, build an OR-based SQL fragment
|
||||||
const multiRatingSQL = (Array.isArray(ratings) && ratings.length > 0) ? lib.getMultiRatingMode(ratings) : null;
|
const multiRatingSQL = (Array.isArray(ratings) && ratings.length > 0) ? lib.getMultiRatingMode(ratings) : null;
|
||||||
const baseMode = multiRatingSQL ?? lib.getMode(mode ?? 0);
|
const baseMode = multiRatingSQL ?? lib.getMode(mode ?? 0);
|
||||||
@@ -200,6 +202,21 @@ export default {
|
|||||||
if (isTitleSearch && titleQuery) {
|
if (isTitleSearch && titleQuery) {
|
||||||
// Title search: match items.title ILIKE '%query%'
|
// Title search: match items.title ILIKE '%query%'
|
||||||
titleFilter = db`and items.title ILIKE ${'%' + titleQuery + '%'} and items.title IS NOT NULL`;
|
titleFilter = db`and items.title ILIKE ${'%' + titleQuery + '%'} and items.title IS NOT NULL`;
|
||||||
|
} else if (tagger && tag) {
|
||||||
|
// Tagger+tag filter: items where the specific user applied this specific tag
|
||||||
|
const terms = tag.split(',').map(t => t.trim()).filter(Boolean);
|
||||||
|
if (terms.length > 0) {
|
||||||
|
const conditions = terms.map(term => {
|
||||||
|
return db`and items.id in (
|
||||||
|
select ta.item_id from tags_assign ta
|
||||||
|
join tags t on t.id = ta.tag_id
|
||||||
|
join "user" u on u.id = ta.user_id
|
||||||
|
where t.normalized like '%' || slugify(${term}) || '%'
|
||||||
|
and u.user ilike ${tagger}
|
||||||
|
)`;
|
||||||
|
});
|
||||||
|
tagFilter = db`${conditions}`;
|
||||||
|
}
|
||||||
} else if (tag) {
|
} else if (tag) {
|
||||||
const terms = tag.split(',').map(t => t.trim()).filter(Boolean);
|
const terms = tag.split(',').map(t => t.trim()).filter(Boolean);
|
||||||
if (terms.length > 0) {
|
if (terms.length > 0) {
|
||||||
@@ -233,7 +250,7 @@ export default {
|
|||||||
userHallFilter = db`and items.id in (select uha.item_id from user_halls_assign uha where uha.hall_id = ${userHallObj.id})`;
|
userHallFilter = db`and items.id in (select uha.item_id from user_halls_assign uha where uha.hall_id = ${userHallObj.id})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheKey = buildCountCacheKey({ modequery, tag, user, hall, mime, fav, session, excludedTags, newerThan, minXd, userHallObj });
|
const cacheKey = buildCountCacheKey({ modequery, tag, user, hall, mime, fav, session, excludedTags, newerThan, minXd, userHallObj, tagger });
|
||||||
let total = getCachedCount(cacheKey);
|
let total = getCachedCount(cacheKey);
|
||||||
|
|
||||||
if (total === null) {
|
if (total === null) {
|
||||||
@@ -372,7 +389,7 @@ export default {
|
|||||||
for (let i = Math.max(1, act_page - range); i <= Math.min(act_page + range, pages); i++)
|
for (let i = Math.max(1, act_page - range); i <= Math.min(act_page + range, pages); i++)
|
||||||
cheat.push(i);
|
cheat.push(i);
|
||||||
|
|
||||||
const link = lib.genLink({ user, tag, hall: hallObj ? hallObj.slug : hall, mime, type: fav ? 'favs' : 'uploads', path: 'p/', strict: strict });
|
const link = lib.genLink({ user, tag, hall: hallObj ? hallObj.slug : hall, mime, type: fav ? 'favs' : 'uploads', path: 'p/', strict: strict, tagger });
|
||||||
|
|
||||||
// Override link for title searches — pagination must use the /tag/title:... prefix
|
// Override link for title searches — pagination must use the /tag/title:... prefix
|
||||||
if (isTitleSearch && titleQuery) {
|
if (isTitleSearch && titleQuery) {
|
||||||
|
|||||||
@@ -226,7 +226,8 @@ export default (router, tpl) => {
|
|||||||
strict: query.strict === '1' || query.strict === 'true' || req.session?.strict_mode,
|
strict: query.strict === '1' || query.strict === 'true' || req.session?.strict_mode,
|
||||||
explicitStrict: query.strict === '1' || query.strict === 'true',
|
explicitStrict: query.strict === '1' || query.strict === 'true',
|
||||||
newer: query.newer || null,
|
newer: query.newer || null,
|
||||||
minXdScore: req.session?.min_xd_score || 0
|
minXdScore: req.session?.min_xd_score || 0,
|
||||||
|
tagger: query.tagger || null
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
|
|||||||
@@ -244,7 +244,8 @@ export default (router, tpl) => {
|
|||||||
explicitStrict: !!(req.query?.strict || req.url.qs?.strict),
|
explicitStrict: !!(req.query?.strict || req.url.qs?.strict),
|
||||||
random: req.cookies.random_mode === '1',
|
random: req.cookies.random_mode === '1',
|
||||||
minXdScore: req.params.itemid ? 0 : (req.url.qs?.min_xd !== undefined ? +req.url.qs.min_xd : (req.session?.min_xd_score || 0)),
|
minXdScore: req.params.itemid ? 0 : (req.url.qs?.min_xd !== undefined ? +req.url.qs.min_xd : (req.session?.min_xd_score || 0)),
|
||||||
lang: req.lang
|
lang: req.lang,
|
||||||
|
tagger: req.url.qs?.tagger || null
|
||||||
});
|
});
|
||||||
console.log(`[DEBUG] Checking strict mode: query=${req.query?.strict}, session=${req.session?.strict_mode}, effective=${!!(req.query?.strict || req.url.qs?.strict || req.session?.strict_mode)}`);
|
console.log(`[DEBUG] Checking strict mode: query=${req.query?.strict}, session=${req.session?.strict_mode}, effective=${!!(req.query?.strict || req.url.qs?.strict || req.session?.strict_mode)}`);
|
||||||
console.log(`[${new Date().toISOString()}] [ROUTE] Data fetch complete in ${Date.now() - tRouteStart}ms`);
|
console.log(`[${new Date().toISOString()}] [ROUTE] Data fetch complete in ${Date.now() - tRouteStart}ms`);
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ export default (router, tpl) => {
|
|||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
success: true,
|
success: true,
|
||||||
html: tpl.render('tag-cards', { toptags: tags, session: (req.session && req.session.user) ? { ...req.session } : false }, req),
|
html: tpl.render('tag-cards', { toptags: tags, user: { user: userName }, session: (req.session && req.session.user) ? { ...req.session } : false }, req),
|
||||||
currentPage: page,
|
currentPage: page,
|
||||||
pagination: paginationHtml,
|
pagination: paginationHtml,
|
||||||
hasMore: tags.length === TAGS_PER_PAGE
|
hasMore: tags.length === TAGS_PER_PAGE
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
@each(toptags as toptag)
|
@each(toptags as toptag)
|
||||||
<a href="/tag/{{ toptag.safe_tag }}" class="tag-card">
|
<a href="/tag/{{ toptag.safe_tag }}@if(user && user.user)?tagger={!! encodeURIComponent(user.user) !!}@endif" class="tag-card">
|
||||||
<div class="tag-card-image">
|
<div class="tag-card-image">
|
||||||
<img src="/tag_image/{{ toptag.encoded_tag }}?m={{ session.mode }}" loading="lazy" alt="{!! toptag.tag !!}">
|
<img src="/tag_image/{{ toptag.encoded_tag }}?m={{ session.mode }}" loading="lazy" alt="{!! toptag.tag !!}">
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user