feat(comments): AJAX fragment list/submit + load-more parity
Add REST comment/list + comment/submit HTML fragments, shared query renderer for order/author/hottest, fix load-more policy and DOM targets, and stop full-page scrapes for order/author/reload.
This commit is contained in:
@@ -28,7 +28,7 @@
|
||||
if (!window.aksrtData) return;
|
||||
|
||||
var COMMENT_SELECTOR = '.aksrt-comment, .comment';
|
||||
var CONTENT_SELECTOR = '.aksrt-comment__text';
|
||||
var CONTENT_SELECTOR = '.aksrt-comment__text, .comment-content';
|
||||
|
||||
var REST_BASE = (aksrtData.siteUrl || '').replace(/\/$/, '') + '/wp-json/aksrt/v1';
|
||||
var REST_NONCE = aksrtData.restNonce || '';
|
||||
@@ -174,7 +174,17 @@
|
||||
commentEl.style.maxHeight = '0';
|
||||
commentEl.style.overflow = 'hidden';
|
||||
});
|
||||
setTimeout(function () { commentEl.remove(); }, 300);
|
||||
setTimeout(function () {
|
||||
commentEl.remove();
|
||||
var countEl = document.querySelector('.comments-count');
|
||||
if (countEl) {
|
||||
var m = (countEl.textContent || '').match(/(\d+)/);
|
||||
if (m) {
|
||||
var n = Math.max(0, parseInt(m[1], 10) - 1);
|
||||
countEl.textContent = (countEl.textContent || '').replace(m[1], String(n));
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
} else {
|
||||
notify((result.data && result.data.message) || result.message || '删除失败', 'error');
|
||||
}
|
||||
@@ -206,7 +216,7 @@
|
||||
}
|
||||
|
||||
function syncPinnedBadge(commentEl, pinned) {
|
||||
var header = commentEl.querySelector('.aksrt-comment__header');
|
||||
var header = commentEl.querySelector('.aksrt-comment__header, .comment-header');
|
||||
if (!header) return;
|
||||
var badge = header.querySelector('.aksrt-comment__badge--pinned');
|
||||
if (pinned && !badge) {
|
||||
|
||||
+160
-184
@@ -205,7 +205,8 @@
|
||||
setLoading(true);
|
||||
|
||||
var formData = collectFormData();
|
||||
var restBase = '/wp-json/aksrt/v1/comment/submit';
|
||||
var restRoot = (window.aksrtData && aksrtData.siteUrl) ? String(aksrtData.siteUrl).replace(/\/$/, '') : '';
|
||||
var restBase = (restRoot || '') + '/wp-json/aksrt/v1/comment/submit';
|
||||
|
||||
// Try REST endpoint for local-insert HTML response
|
||||
var restBody = {};
|
||||
@@ -407,57 +408,150 @@
|
||||
form.addEventListener('submit', handleSubmit, true);
|
||||
}
|
||||
|
||||
|
||||
function getCommentsRestBase() {
|
||||
var root = (window.aksrtData && aksrtData.siteUrl) ? String(aksrtData.siteUrl).replace(/\/$/, "") : "";
|
||||
return root + "/wp-json/aksrt/v1";
|
||||
}
|
||||
|
||||
function getCurrentCommentPostId() {
|
||||
var input = document.querySelector('input[name="comment_post_ID"]');
|
||||
if (input && input.value) return parseInt(input.value, 10) || 0;
|
||||
var comments = document.getElementById("comments");
|
||||
if (comments && comments.dataset.postId) return parseInt(comments.dataset.postId, 10) || 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getCurrentCommentOrder() {
|
||||
var active = document.querySelector("#comments .comment-order-box li.active .comment-orderby");
|
||||
if (active && active.dataset && active.dataset.order) return active.dataset.order;
|
||||
var url = new URL(window.location.href);
|
||||
return url.searchParams.get("aksrt_comment_order") || "newest";
|
||||
}
|
||||
|
||||
function getCurrentAuthorOnly() {
|
||||
var url = new URL(window.location.href);
|
||||
if (url.searchParams.get("aksrt_author_only") === "1") return true;
|
||||
var btn = document.querySelector("#comments .comment-author-filter");
|
||||
return !!(btn && btn.classList.contains("active") && btn.dataset.authorOnly === "0");
|
||||
}
|
||||
|
||||
function applyFragmentPayload(commentsArea, data, urlState) {
|
||||
if (!commentsArea || !data) return;
|
||||
|
||||
if (data.filter_html) {
|
||||
var currentFilter = commentsArea.querySelector(".comment-filter");
|
||||
var temp = document.createElement("div");
|
||||
temp.innerHTML = data.filter_html;
|
||||
var newFilter = temp.firstElementChild;
|
||||
if (newFilter) {
|
||||
if (currentFilter) currentFilter.replaceWith(newFilter);
|
||||
else {
|
||||
var listAnchor = commentsArea.querySelector("#postcomments, #comment-list");
|
||||
if (listAnchor && listAnchor.parentNode) {
|
||||
listAnchor.parentNode.insertBefore(newFilter, listAnchor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var currentListWrap = commentsArea.querySelector("#postcomments");
|
||||
var currentList = commentsArea.querySelector("#comment-list");
|
||||
var currentEmpty = commentsArea.querySelector(".comments-empty");
|
||||
var tempList = document.createElement("div");
|
||||
tempList.innerHTML = data.html || "";
|
||||
var newListWrap = tempList.querySelector("#postcomments");
|
||||
var newEmpty = tempList.querySelector(".comments-empty");
|
||||
|
||||
if (newListWrap) {
|
||||
if (currentListWrap) currentListWrap.replaceWith(newListWrap);
|
||||
else if (currentList) currentList.replaceWith(newListWrap);
|
||||
else commentsArea.appendChild(newListWrap);
|
||||
if (currentEmpty) currentEmpty.remove();
|
||||
} else if (newEmpty) {
|
||||
if (currentListWrap) currentListWrap.remove();
|
||||
if (currentList) currentList.remove();
|
||||
if (currentEmpty) currentEmpty.replaceWith(newEmpty);
|
||||
else commentsArea.appendChild(newEmpty);
|
||||
}
|
||||
|
||||
var currentPagination = commentsArea.querySelector(".aksrt-comment-pagination, .comment-navigation, .pagenav.ajax-pag");
|
||||
if (data.pagination_html) {
|
||||
var ptemp = document.createElement("div");
|
||||
ptemp.innerHTML = data.pagination_html;
|
||||
var newPag = ptemp.firstElementChild;
|
||||
if (newPag) {
|
||||
if (currentPagination) currentPagination.replaceWith(newPag);
|
||||
else {
|
||||
var list = commentsArea.querySelector("#postcomments, #comment-list");
|
||||
if (list && list.parentNode) list.parentNode.insertBefore(newPag, list.nextSibling);
|
||||
}
|
||||
}
|
||||
} else if (currentPagination) {
|
||||
currentPagination.remove();
|
||||
}
|
||||
|
||||
var currentCount = commentsArea.querySelector(".comments-count");
|
||||
if (currentCount && data.count_text) currentCount.textContent = data.count_text;
|
||||
|
||||
if (urlState && window.history && typeof window.history.replaceState === "function") {
|
||||
window.history.replaceState(null, "", urlState);
|
||||
}
|
||||
if (window.AksrtFrontend) AksrtFrontend.rehydrate(commentsArea, "ajax");
|
||||
}
|
||||
|
||||
function fetchCommentFragment(opts) {
|
||||
opts = opts || {};
|
||||
var postId = opts.postId || getCurrentCommentPostId();
|
||||
if (!postId) return Promise.reject(new Error("missing post id"));
|
||||
var order = opts.order || getCurrentCommentOrder() || "newest";
|
||||
var authorOnly = !!opts.authorOnly;
|
||||
var page = opts.page || 1;
|
||||
var qs = new URLSearchParams();
|
||||
qs.set("post_id", String(postId));
|
||||
qs.set("order", order);
|
||||
qs.set("page", String(page));
|
||||
if (authorOnly) qs.set("author_only", "1");
|
||||
var url = getCommentsRestBase() + "/comment/list?" + qs.toString();
|
||||
return fetch(url, {
|
||||
method: "GET",
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
"X-WP-Nonce": (window.aksrtData && aksrtData.restNonce) || ""
|
||||
}
|
||||
}).then(function (r) { return r.json(); });
|
||||
}
|
||||
|
||||
/* ====== 评论列表动态刷新 ====== */
|
||||
function reloadCommentList() {
|
||||
var commentsArea = document.getElementById('comments');
|
||||
var listContainer = document.getElementById('comment-list');
|
||||
if (!listContainer) {
|
||||
// 降级:整页刷新
|
||||
if (!commentsArea && !listContainer) {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
if (listContainer) {
|
||||
listContainer.innerHTML = '<li class="comment-loading-skeleton">' +
|
||||
'<div class="aksrt-comment"><div class="aksrt-comment__body">' +
|
||||
'<div style="height:60px;background:#f0f0f0;border-radius:8px;margin-bottom:10px;animation:pulse 1.5s ease-in-out infinite;"></div>' +
|
||||
'<div style="height:20px;width:70%;background:#f0f0f0;border-radius:4px;animation:pulse 1.5s ease-in-out infinite;"></div>' +
|
||||
'</div></div></li>';
|
||||
}
|
||||
|
||||
var postIdInput = document.querySelector('input[name="comment_post_ID"]');
|
||||
if (!postIdInput) return;
|
||||
|
||||
// 显示骨架屏加载效果
|
||||
listContainer.innerHTML = '<li class="comment-loading-skeleton">' +
|
||||
'<div class="aksrt-comment"><div class="aksrt-comment__body">' +
|
||||
'<div style="height:60px;background:#f0f0f0;border-radius:8px;margin-bottom:10px;animation:pulse 1.5s ease-in-out infinite;"></div>' +
|
||||
'<div style="height:20px;width:70%;background:#f0f0f0;border-radius:4px;animation:pulse 1.5s ease-in-out infinite;"></div>' +
|
||||
'</div></div></li>';
|
||||
// 获取当前页面 URL(带时间戳避免缓存)
|
||||
var currentUrl = window.location.href.split('#')[0];
|
||||
var bustUrl = currentUrl + (currentUrl.indexOf('?') === -1 ? '?' : '&') + '_cache_bust=' + Date.now();
|
||||
|
||||
fetch(bustUrl, {
|
||||
method: 'GET',
|
||||
credentials: 'same-origin'
|
||||
})
|
||||
.then(function (response) { return response.text(); })
|
||||
.then(function (html) {
|
||||
// 从新页面提取评论列表
|
||||
var parser = new DOMParser();
|
||||
var doc = parser.parseFromString(html, 'text/html');
|
||||
var newList = doc.getElementById('comment-list');
|
||||
|
||||
if (newList && newList.innerHTML.trim()) {
|
||||
listContainer.innerHTML = newList.innerHTML;
|
||||
|
||||
// 更新评论计数
|
||||
var countEl = document.querySelector('.comments-count');
|
||||
var newCountEl = doc.querySelector('.comments-count');
|
||||
if (countEl && newCountEl) {
|
||||
countEl.textContent = newCountEl.textContent;
|
||||
}
|
||||
} else {
|
||||
listContainer.innerHTML = '';
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error('[AKSRT Comment] 刷新评论列表失败:', error);
|
||||
// 失败时降级为整页刷新
|
||||
window.location.reload();
|
||||
});
|
||||
fetchCommentFragment({
|
||||
order: getCurrentCommentOrder(),
|
||||
authorOnly: getCurrentAuthorOnly(),
|
||||
page: 1
|
||||
}).then(function (json) {
|
||||
if (json && json.success && json.data) {
|
||||
applyFragmentPayload(commentsArea || document, json.data, null);
|
||||
return;
|
||||
}
|
||||
throw new Error('fragment failed');
|
||||
}).catch(function (error) {
|
||||
console.error('[AKSRT Comment] 刷新评论列表失败:', error);
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
/* ====== 评论排序(最新 / 最早) ====== */
|
||||
@@ -486,63 +580,21 @@
|
||||
if (order !== "newest" && order !== "oldest" && order !== "hottest") {
|
||||
order = "newest";
|
||||
}
|
||||
|
||||
renderLoadingState();
|
||||
|
||||
var authorOnly = getCurrentAuthorOnly();
|
||||
var url = new URL(window.location.href);
|
||||
url.searchParams.set("aksrt_comment_order", order);
|
||||
url.searchParams.delete("cpage");
|
||||
var fetchUrl = new URL(url.toString());
|
||||
fetchUrl.searchParams.set("_cache_bust", Date.now().toString());
|
||||
if (authorOnly) url.searchParams.set("aksrt_author_only", "1");
|
||||
else url.searchParams.delete("aksrt_author_only");
|
||||
|
||||
fetch(fetchUrl.toString(), {
|
||||
method: "GET",
|
||||
credentials: "same-origin"
|
||||
})
|
||||
.then(function (response) { return response.text(); })
|
||||
.then(function (html) {
|
||||
var parser = new DOMParser();
|
||||
var doc = parser.parseFromString(html, "text/html");
|
||||
var newComments = doc.getElementById("comments");
|
||||
if (!newComments) {
|
||||
throw new Error("new comments area not found");
|
||||
fetchCommentFragment({ order: order, authorOnly: authorOnly, page: 1 })
|
||||
.then(function (json) {
|
||||
if (json && json.success && json.data) {
|
||||
applyFragmentPayload(commentsArea, json.data, url.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
var currentFilter = commentsArea.querySelector(".comment-filter");
|
||||
var newFilter = newComments.querySelector(".comment-filter");
|
||||
if (currentFilter && newFilter) {
|
||||
currentFilter.replaceWith(newFilter.cloneNode(true));
|
||||
}
|
||||
|
||||
var currentList = commentsArea.querySelector("#comment-list");
|
||||
var newList = newComments.querySelector("#comment-list");
|
||||
if (currentList && newList) {
|
||||
currentList.replaceWith(newList.cloneNode(true));
|
||||
}
|
||||
|
||||
var currentPagination = getPaginationEl(commentsArea);
|
||||
var newPagination = getPaginationEl(newComments);
|
||||
if (currentPagination && newPagination) {
|
||||
currentPagination.replaceWith(newPagination.cloneNode(true));
|
||||
} else if (currentPagination && !newPagination) {
|
||||
currentPagination.remove();
|
||||
} else if (!currentPagination && newPagination) {
|
||||
var list = commentsArea.querySelector("#comment-list");
|
||||
if (list && list.parentNode) {
|
||||
list.parentNode.insertBefore(newPagination.cloneNode(true), list.nextSibling);
|
||||
}
|
||||
}
|
||||
var currentCount = commentsArea.querySelector(".comments-count");
|
||||
var newCount = newComments.querySelector(".comments-count");
|
||||
if (currentCount && newCount) {
|
||||
currentCount.textContent = newCount.textContent;
|
||||
}
|
||||
|
||||
if (window.history && typeof window.history.replaceState === "function") {
|
||||
window.history.replaceState(null, "", url.toString());
|
||||
}
|
||||
|
||||
if (window.AksrtFrontend) AksrtFrontend.rehydrate(commentsArea, 'ajax');
|
||||
throw new Error("order fragment failed");
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error("[AKSRT Comment] 排序刷新失败:", error);
|
||||
@@ -550,99 +602,23 @@
|
||||
});
|
||||
}
|
||||
|
||||
commentsArea.addEventListener("click", function (e) {
|
||||
var link = e.target.closest(".comment-orderby");
|
||||
if (link && commentsArea.contains(link)) {
|
||||
e.preventDefault();
|
||||
var order = (link.dataset && link.dataset.order) ? link.dataset.order : "newest";
|
||||
applyCommentOrder(order);
|
||||
return;
|
||||
}
|
||||
|
||||
var authorLink = e.target.closest(".comment-author-filter");
|
||||
if (authorLink && commentsArea.contains(authorLink)) {
|
||||
e.preventDefault();
|
||||
var authorOnly = (authorLink.dataset && authorLink.dataset.authorOnly) ? authorLink.dataset.authorOnly : "1";
|
||||
applyAuthorFilter(authorOnly);
|
||||
}
|
||||
});
|
||||
|
||||
function applyAuthorFilter(authorOnly) {
|
||||
function applyAuthorFilter(authorOnlyFlag) {
|
||||
renderLoadingState();
|
||||
|
||||
var authorOnly = authorOnlyFlag === "1" || authorOnlyFlag === 1 || authorOnlyFlag === true;
|
||||
var order = getCurrentCommentOrder();
|
||||
var url = new URL(window.location.href);
|
||||
if (authorOnly === "1") {
|
||||
url.searchParams.set("aksrt_author_only", "1");
|
||||
} else {
|
||||
url.searchParams.delete("aksrt_author_only");
|
||||
}
|
||||
if (authorOnly) url.searchParams.set("aksrt_author_only", "1");
|
||||
else url.searchParams.delete("aksrt_author_only");
|
||||
url.searchParams.delete("cpage");
|
||||
var fetchUrl = new URL(url.toString());
|
||||
fetchUrl.searchParams.set("_cache_bust", Date.now().toString());
|
||||
fetch(fetchUrl.toString(), {
|
||||
method: "GET",
|
||||
credentials: "same-origin"
|
||||
})
|
||||
.then(function (response) { return response.text(); })
|
||||
.then(function (html) {
|
||||
var parser = new DOMParser();
|
||||
var doc = parser.parseFromString(html, "text/html");
|
||||
var newComments = doc.getElementById("comments");
|
||||
if (!newComments) {
|
||||
throw new Error("new comments area not found");
|
||||
}
|
||||
if (order) url.searchParams.set("aksrt_comment_order", order);
|
||||
|
||||
var currentFilter = commentsArea.querySelector(".comment-filter");
|
||||
var newFilter = newComments.querySelector(".comment-filter");
|
||||
if (currentFilter && newFilter) {
|
||||
currentFilter.replaceWith(newFilter.cloneNode(true));
|
||||
} else if (!currentFilter && newFilter) {
|
||||
var listAnchor = commentsArea.querySelector("#postcomments, #comment-list");
|
||||
if (listAnchor && listAnchor.parentNode) {
|
||||
listAnchor.parentNode.insertBefore(newFilter.cloneNode(true), listAnchor);
|
||||
}
|
||||
fetchCommentFragment({ order: order, authorOnly: authorOnly, page: 1 })
|
||||
.then(function (json) {
|
||||
if (json && json.success && json.data) {
|
||||
applyFragmentPayload(commentsArea, json.data, url.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
var currentListWrap = commentsArea.querySelector("#postcomments");
|
||||
var newListWrap = newComments.querySelector("#postcomments");
|
||||
var currentList = commentsArea.querySelector("#comment-list");
|
||||
var newList = newComments.querySelector("#comment-list");
|
||||
if (currentListWrap && newListWrap) {
|
||||
currentListWrap.replaceWith(newListWrap.cloneNode(true));
|
||||
} else if (currentList && newList) {
|
||||
currentList.replaceWith(newList.cloneNode(true));
|
||||
}
|
||||
|
||||
var currentEmpty = commentsArea.querySelector(".comments-empty");
|
||||
var newEmpty = newComments.querySelector(".comments-empty");
|
||||
if (currentEmpty && newEmpty) {
|
||||
currentEmpty.replaceWith(newEmpty.cloneNode(true));
|
||||
} else if (currentEmpty && !newEmpty) {
|
||||
currentEmpty.remove();
|
||||
} else if (!currentEmpty && newEmpty) {
|
||||
var insertAt = commentsArea.querySelector("#postcomments") || commentsArea;
|
||||
insertAt.appendChild(newEmpty.cloneNode(true));
|
||||
}
|
||||
|
||||
var currentPagination = getPaginationEl(commentsArea);
|
||||
var newPagination = getPaginationEl(newComments);
|
||||
if (currentPagination && newPagination) {
|
||||
currentPagination.replaceWith(newPagination.cloneNode(true));
|
||||
} else if (currentPagination && !newPagination) {
|
||||
currentPagination.remove();
|
||||
}
|
||||
|
||||
var currentCount = commentsArea.querySelector(".comments-count");
|
||||
var newCount = newComments.querySelector(".comments-count");
|
||||
if (currentCount && newCount) {
|
||||
currentCount.textContent = newCount.textContent;
|
||||
}
|
||||
|
||||
if (window.history && typeof window.history.replaceState === "function") {
|
||||
window.history.replaceState(null, "", url.toString());
|
||||
}
|
||||
|
||||
if (window.AksrtFrontend) AksrtFrontend.rehydrate(commentsArea, 'ajax');
|
||||
throw new Error("author fragment failed");
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error("[AKSRT Comment] Author filter failed:", error);
|
||||
|
||||
@@ -29,8 +29,13 @@
|
||||
if (target && target.classList && target.classList.contains("content-stream__footer")) {
|
||||
target = target.previousElementSibling;
|
||||
}
|
||||
if (isComment) {
|
||||
var commentList = document.getElementById("comment-list") ||
|
||||
(container.parentElement && container.parentElement.querySelector("#comment-list, .commentlist, ol.commentlist"));
|
||||
if (commentList) target = commentList;
|
||||
}
|
||||
if (!target && container.parentElement) {
|
||||
target = container.parentElement.querySelector(".post-grid, .comment-list, .content-stream__list");
|
||||
target = container.parentElement.querySelector(".post-grid, .comment-list, .commentlist, .content-stream__list");
|
||||
}
|
||||
|
||||
function showSkeleton() {
|
||||
@@ -79,6 +84,8 @@
|
||||
if (isComment && postId) {
|
||||
body.set("post_id", postId);
|
||||
body.set("comment_order", commentOrder);
|
||||
var authorOnly = container.dataset.authorOnly || "0";
|
||||
if (authorOnly === "1") body.set("author_only", "1");
|
||||
}
|
||||
|
||||
fetch(aksrtData.ajaxUrl, {
|
||||
@@ -98,6 +105,9 @@
|
||||
while (newItems.length > 0) {
|
||||
target.appendChild(newItems[0]);
|
||||
}
|
||||
if (isComment && window.AksrtFrontend) {
|
||||
AksrtFrontend.rehydrate(target, "ajax");
|
||||
}
|
||||
nextPage++;
|
||||
container.dataset.nextPage = String(nextPage);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user