fix rating and fav
This commit is contained in:
@@ -186,6 +186,9 @@
|
|||||||
postid: postid
|
postid: postid
|
||||||
});
|
});
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
|
if (window.invalidateItemCache) {
|
||||||
|
window.invalidateItemCache(postid);
|
||||||
|
}
|
||||||
// New state is the logical opposite of what it was before the API call
|
// New state is the logical opposite of what it was before the API call
|
||||||
const isNowFav = !wasAlreadyFav;
|
const isNowFav = !wasAlreadyFav;
|
||||||
|
|
||||||
|
|||||||
@@ -4145,6 +4145,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
|
if (window.invalidateItemCache) window.invalidateItemCache(id);
|
||||||
const newOc = data.is_oc;
|
const newOc = data.is_oc;
|
||||||
ocBtn.dataset.isOc = newOc;
|
ocBtn.dataset.isOc = newOc;
|
||||||
ocBtn.setAttribute('title', newOc ? 'Remove OC status' : 'Mark as OC');
|
ocBtn.setAttribute('title', newOc ? 'Remove OC status' : 'Mark as OC');
|
||||||
@@ -4486,7 +4487,13 @@ window.cancelAnimFrame = (function () {
|
|||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(res => {
|
.then(res => {
|
||||||
toggleBtn._activeRequestsCount = Math.max(0, (toggleBtn._activeRequestsCount || 1) - 1);
|
toggleBtn._activeRequestsCount = Math.max(0, (toggleBtn._activeRequestsCount || 1) - 1);
|
||||||
|
|
||||||
|
// Evict cached item HTML immediately on success so navigating back fetches fresh content,
|
||||||
|
// even if the user already navigated to a different item while this request was in flight.
|
||||||
|
if (res && res.success && window.invalidateItemCache) {
|
||||||
|
window.invalidateItemCache(postid);
|
||||||
|
}
|
||||||
|
|
||||||
// Verify we are still on the same post (prevent dynamic/PJAX page leaks)
|
// Verify we are still on the same post (prevent dynamic/PJAX page leaks)
|
||||||
const currentIdStr = window.getCurrentItemId();
|
const currentIdStr = window.getCurrentItemId();
|
||||||
const currentPostId = currentIdStr ? parseInt(currentIdStr, 10) : null;
|
const currentPostId = currentIdStr ? parseInt(currentIdStr, 10) : null;
|
||||||
@@ -4502,10 +4509,6 @@ window.cancelAnimFrame = (function () {
|
|||||||
if (window.renderTags) {
|
if (window.renderTags) {
|
||||||
window.renderTags(res.tags);
|
window.renderTags(res.tags);
|
||||||
}
|
}
|
||||||
// Evict the cached item HTML so navigating back fetches fresh content
|
|
||||||
if (window.invalidateItemCache) {
|
|
||||||
window.invalidateItemCache(postid);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
revert();
|
revert();
|
||||||
window.flashMessage('Error: ' + (res.msg || 'Failed to update rating'), 3000, 'error');
|
window.flashMessage('Error: ' + (res.msg || 'Failed to update rating'), 3000, 'error');
|
||||||
@@ -7148,14 +7151,21 @@ window.cancelAnimFrame = (function () {
|
|||||||
window.loadPageAjax = loadPageAjax;
|
window.loadPageAjax = loadPageAjax;
|
||||||
window.loadItemAjax = loadItemAjax;
|
window.loadItemAjax = loadItemAjax;
|
||||||
|
|
||||||
// Invalidate all itemCacheMap entries that contain a given item ID.
|
// Invalidate all itemCacheMap entries that match a given item ID or slug.
|
||||||
// Call this after mutating an item (e.g. adding a tag) so the next
|
// Call this after mutating an item (e.g. rating, favorite, tags, OC) so the next
|
||||||
// AJAX navigation fetches fresh HTML instead of serving stale cache.
|
// AJAX navigation fetches fresh HTML instead of serving stale cache.
|
||||||
window.invalidateItemCache = (itemId) => {
|
window.invalidateItemCache = (itemId) => {
|
||||||
if (!itemId) return;
|
if (itemId === undefined || itemId === null || itemId === '') return;
|
||||||
const needle = `/${itemId}`;
|
const strId = String(itemId).trim();
|
||||||
for (const key of itemCacheMap.keys()) {
|
for (const [key, val] of itemCacheMap.entries()) {
|
||||||
if (key.includes(needle)) {
|
const urlPath = key.split('?')[0];
|
||||||
|
const segments = urlPath.split('/').filter(Boolean);
|
||||||
|
const lastSegment = segments[segments.length - 1];
|
||||||
|
if (lastSegment === strId) {
|
||||||
|
itemCacheMap.delete(key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (val && typeof val.html === 'string' && val.html.includes(`data-item-id="${strId}"`)) {
|
||||||
itemCacheMap.delete(key);
|
itemCacheMap.delete(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8697,6 +8707,10 @@ class NotificationSystem {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (window.invalidateItemCache) {
|
||||||
|
window.invalidateItemCache(data.item_id);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we are currently viewing this item
|
// Check if we are currently viewing this item
|
||||||
const currentIdStr = window.getCurrentItemId();
|
const currentIdStr = window.getCurrentItemId();
|
||||||
const currentId = currentIdStr ? parseInt(currentIdStr, 10) : null;
|
const currentId = currentIdStr ? parseInt(currentIdStr, 10) : null;
|
||||||
@@ -8780,6 +8794,10 @@ class NotificationSystem {
|
|||||||
handleFavoritesUpdate(data) {
|
handleFavoritesUpdate(data) {
|
||||||
if (!data || !data.item_id) return;
|
if (!data || !data.item_id) return;
|
||||||
|
|
||||||
|
if (window.invalidateItemCache) {
|
||||||
|
window.invalidateItemCache(data.item_id);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we are currently viewing this item
|
// Check if we are currently viewing this item
|
||||||
const currentIdStr = window.getCurrentItemId();
|
const currentIdStr = window.getCurrentItemId();
|
||||||
if (!currentIdStr || parseInt(currentIdStr, 10) !== parseInt(data.item_id, 10)) return;
|
if (!currentIdStr || parseInt(currentIdStr, 10) !== parseInt(data.item_id, 10)) return;
|
||||||
|
|||||||
@@ -175,6 +175,9 @@
|
|||||||
postid: postid
|
postid: postid
|
||||||
});
|
});
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
|
if (window.invalidateItemCache) {
|
||||||
|
window.invalidateItemCache(postid);
|
||||||
|
}
|
||||||
// New state is the logical opposite of what it was before the API call
|
// New state is the logical opposite of what it was before the API call
|
||||||
const isNowFav = !wasAlreadyFav;
|
const isNowFav = !wasAlreadyFav;
|
||||||
|
|
||||||
|
|||||||
@@ -200,27 +200,27 @@ export default new class {
|
|||||||
const tagged = +(await db`
|
const tagged = +(await db`
|
||||||
select count(*) as total
|
select count(*) as total
|
||||||
from "items"
|
from "items"
|
||||||
where id in (select item_id from tags_assign group by item_id) and active = true
|
where id in (select item_id from tags_assign group by item_id) and active = true and is_deleted = false
|
||||||
`)[0].total;
|
`)[0].total;
|
||||||
const untagged = +(await db`
|
const untagged = +(await db`
|
||||||
select count(*) as total
|
select count(*) as total
|
||||||
from "items"
|
from "items"
|
||||||
where not exists (select 1 from tags_assign where item_id = items.id) and active = true
|
where not exists (select 1 from tags_assign where item_id = items.id) and active = true and is_deleted = false
|
||||||
`)[0].total;
|
`)[0].total;
|
||||||
const sfw = +(await db`
|
const sfw = +(await db`
|
||||||
select count(*) as total
|
select count(*) as total
|
||||||
from "items"
|
from "items"
|
||||||
where id in (select item_id from tags_assign where tag_id = 1 group by item_id) and active = true
|
where id in (select item_id from tags_assign where tag_id = 1 group by item_id) and active = true and is_deleted = false
|
||||||
`)[0].total;
|
`)[0].total;
|
||||||
const nsfw = +(await db`
|
const nsfw = +(await db`
|
||||||
select count(*) as total
|
select count(*) as total
|
||||||
from "items"
|
from "items"
|
||||||
where id in (select item_id from tags_assign where tag_id = 2 group by item_id) and active = true
|
where id in (select item_id from tags_assign where tag_id = 2 group by item_id) and active = true and is_deleted = false
|
||||||
`)[0].total;
|
`)[0].total;
|
||||||
const nsfl = cfg.enable_nsfl ? +(await db`
|
const nsfl = cfg.enable_nsfl ? +(await db`
|
||||||
select count(*) as total
|
select count(*) as total
|
||||||
from "items"
|
from "items"
|
||||||
where id in (select item_id from tags_assign where tag_id = ${cfg.nsfl_tag_id || 3} group by item_id) and active = true
|
where id in (select item_id from tags_assign where tag_id = ${cfg.nsfl_tag_id || 3} group by item_id) and active = true and is_deleted = false
|
||||||
`)[0].total : 0;
|
`)[0].total : 0;
|
||||||
const deleted = +(await db`
|
const deleted = +(await db`
|
||||||
select count(*) as total
|
select count(*) as total
|
||||||
|
|||||||
@@ -845,12 +845,16 @@ export default (router, tpl) => {
|
|||||||
await fs.unlink(path.join(cfg.paths.ca, `${item.id}.webp`)).catch(() => { });
|
await fs.unlink(path.join(cfg.paths.ca, `${item.id}.webp`)).catch(() => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await db`DELETE FROM tags_assign WHERE item_id = ${item.id}`;
|
||||||
await db`UPDATE items SET is_deleted = true, is_purged = true, active = false WHERE id = ${item.id}`;
|
await db`UPDATE items SET is_deleted = true, is_purged = true, active = false WHERE id = ${item.id}`;
|
||||||
count++;
|
count++;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`[CLEANUP] Failed to delete item ${item.id}:`, e.message);
|
console.error(`[CLEANUP] Failed to delete item ${item.id}:`, e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up any remaining orphan tag assignments for deleted or inactive items
|
||||||
|
await db`DELETE FROM tags_assign WHERE item_id IN (SELECT id FROM items WHERE is_deleted = true OR active = false)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log it in audit
|
// Log it in audit
|
||||||
|
|||||||
@@ -115,10 +115,13 @@ export default (router, tpl) => {
|
|||||||
try {
|
try {
|
||||||
const slug = '%' + lib.slugify(q) + '%';
|
const slug = '%' + lib.slugify(q) + '%';
|
||||||
const rows = await db`
|
const rows = await db`
|
||||||
SELECT t.tag, t.normalized, COUNT(ta.item_id) as uses
|
SELECT t.tag, t.normalized, COUNT(DISTINCT items.id) as uses
|
||||||
FROM tags t
|
FROM tags t
|
||||||
JOIN tags_assign ta ON ta.tag_id = t.id
|
JOIN tags_assign ta ON ta.tag_id = t.id
|
||||||
|
JOIN items ON items.id = ta.item_id
|
||||||
WHERE t.id > 2
|
WHERE t.id > 2
|
||||||
|
AND items.active = true
|
||||||
|
AND items.is_deleted = false
|
||||||
AND lower(t.normalized) ILIKE ${slug}
|
AND lower(t.normalized) ILIKE ${slug}
|
||||||
GROUP BY t.tag, t.normalized
|
GROUP BY t.tag, t.normalized
|
||||||
ORDER BY uses DESC
|
ORDER BY uses DESC
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export async function regenerateTagImage(tag, mode) {
|
|||||||
${modeFilter}
|
${modeFilter}
|
||||||
WHERE (t.tag = ${tag} OR t.normalized = ${tag})
|
WHERE (t.tag = ${tag} OR t.normalized = ${tag})
|
||||||
AND i.active = true
|
AND i.active = true
|
||||||
|
AND i.is_deleted = false
|
||||||
AND COALESCE(i.visibility, 0) = 0
|
AND COALESCE(i.visibility, 0) = 0
|
||||||
ORDER BY RANDOM()
|
ORDER BY RANDOM()
|
||||||
LIMIT 3
|
LIMIT 3
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export default (router, tpl) => {
|
|||||||
JOIN tags_assign ta ON t.id = ta.tag_id
|
JOIN tags_assign ta ON t.id = ta.tag_id
|
||||||
JOIN items ON items.id = ta.item_id
|
JOIN items ON items.id = ta.item_id
|
||||||
WHERE items.active = true
|
WHERE items.active = true
|
||||||
|
AND items.is_deleted = false
|
||||||
AND COALESCE(items.visibility, 0) = 0
|
AND COALESCE(items.visibility, 0) = 0
|
||||||
AND t.id NOT IN (1, 2)
|
AND t.id NOT IN (1, 2)
|
||||||
AND ${db.unsafe(modequery)}
|
AND ${db.unsafe(modequery)}
|
||||||
@@ -75,6 +76,7 @@ export default (router, tpl) => {
|
|||||||
JOIN items ON items.id = ta.item_id
|
JOIN items ON items.id = ta.item_id
|
||||||
WHERE t.normalized LIKE '%' || ${tag.normalized} || '%'
|
WHERE t.normalized LIKE '%' || ${tag.normalized} || '%'
|
||||||
AND items.active = true
|
AND items.active = true
|
||||||
|
AND items.is_deleted = false
|
||||||
AND COALESCE(items.visibility, 0) = 0
|
AND COALESCE(items.visibility, 0) = 0
|
||||||
AND ${db.unsafe(modequery)}
|
AND ${db.unsafe(modequery)}
|
||||||
${restrictedFilter}
|
${restrictedFilter}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export default (router, tpl) => {
|
|||||||
JOIN tags_assign ta ON t.id = ta.tag_id
|
JOIN tags_assign ta ON t.id = ta.tag_id
|
||||||
JOIN items ON items.id = ta.item_id
|
JOIN items ON items.id = ta.item_id
|
||||||
WHERE items.active = true
|
WHERE items.active = true
|
||||||
|
AND items.is_deleted = false
|
||||||
AND COALESCE(items.visibility, 0) = 0
|
AND COALESCE(items.visibility, 0) = 0
|
||||||
AND t.id NOT IN (1, 2)
|
AND t.id NOT IN (1, 2)
|
||||||
AND ta.user_id = ${userId}
|
AND ta.user_id = ${userId}
|
||||||
@@ -76,6 +77,7 @@ export default (router, tpl) => {
|
|||||||
JOIN items ON items.id = ta.item_id
|
JOIN items ON items.id = ta.item_id
|
||||||
WHERE t.normalized LIKE '%' || ${tag.normalized} || '%'
|
WHERE t.normalized LIKE '%' || ${tag.normalized} || '%'
|
||||||
AND items.active = true
|
AND items.active = true
|
||||||
|
AND items.is_deleted = false
|
||||||
AND COALESCE(items.visibility, 0) = 0
|
AND COALESCE(items.visibility, 0) = 0
|
||||||
AND ta.user_id = ${userId}
|
AND ta.user_id = ${userId}
|
||||||
AND ${db.unsafe(modequery)}
|
AND ${db.unsafe(modequery)}
|
||||||
|
|||||||
Reference in New Issue
Block a user