(function (root, factory) {
// 家族圈动态模块同时支持浏览器页面和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.FeedPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.FeedPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
var PAGE_SIZE = 20;
var currentFeedPage = 1;
var currentCommentPages = {};
var currentReplyPages = {};
var pendingActions = {};
var viewerContext = {
userId: '',
canEditContent: false,
canManage: false
};
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function shouldRedirectToLogin(api, error) {
var status = error && (error.status || error.code);
return !api || !api.getToken || !api.getToken() || Number(status) === 401;
}
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
if (!root.location) return true;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return true;
}
root.location.href = 'login.html';
return true;
}
function query(selector, rootNode) {
return documentRef ? (rootNode || documentRef).querySelector(selector) : null;
}
function queryAll(selector, rootNode) {
return documentRef ? Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)) : [];
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
if (root.alert) root.alert(message);
}
function getQueryParam(search, name) {
var params = new URLSearchParams(String(search || '').replace(/^\?/, ''));
return params.get(name) || '';
}
function getCurrentGenealogyId(search) {
var page = query('[data-feed-page], [data-feed-edit-page], [data-feed-detail-page]');
var source = search === undefined && root.location ? root.location.search : search;
var profileGenealogyId;
if (search === undefined && root.ProfileUI && root.ProfileUI.getGenealogyId) {
profileGenealogyId = root.ProfileUI.getGenealogyId();
if (profileGenealogyId) return profileGenealogyId;
}
return getQueryParam(source, 'genealogyId') || (page && page.getAttribute('data-genealogy-id')) || '';
}
function getCurrentFeedId(search) {
var source = search === undefined && root.location ? root.location.search : search;
return getQueryParam(source, 'feedId');
}
function trimOrUndefined(value) {
var text = String(value === undefined || value === null ? '' : value).trim();
return text || undefined;
}
function toIntegerOrUndefined(value) {
var text = trimOrUndefined(value);
var number;
if (!text) return undefined;
number = Number(text);
return Number.isSafeInteger(number) && number >= 0 ? number : undefined;
}
function toId(value) {
var text;
if (value === undefined || value === null || value === '') return '';
text = String(value);
return /^\d+$/.test(text) ? text : '';
}
function setDefined(target, source, fields) {
fields.forEach(function (field) {
if (source[field] !== undefined && source[field] !== null) target[field] = source[field];
});
return target;
}
function buildPageQuery(pageNum) {
var number = Number(pageNum);
if (!Number.isSafeInteger(number) || number < 1) number = 1;
return {
pageNum: number,
pageSize: PAGE_SIZE
};
}
function buildFeedUrl(kind, genealogyId, feedId) {
var params = new URLSearchParams();
var page = 'profile-feed.html';
params.set('genealogyId', toId(genealogyId));
if (kind === 'detail' || kind === 'edit') {
params.set('feedId', toId(feedId));
page = kind === 'detail' ? 'profile-feed-detail.html' : 'profile-feed-edit.html';
}
return page + '?' + params.toString();
}
function buildPostSaveUrl(genealogyId, feedId, status) {
return String(status || '0') === '1'
? buildFeedUrl('list', genealogyId)
: buildFeedUrl('detail', genealogyId, feedId);
}
function withActionLock(key, task) {
var promise;
if (pendingActions[key]) return pendingActions[key];
try {
promise = Promise.resolve(task());
} catch (error) {
promise = Promise.reject(error);
}
pendingActions[key] = promise.finally(function () {
delete pendingActions[key];
});
return pendingActions[key];
}
function buildFeedBody(values) {
// FamilyFeedBody 只提交最新接口文档定义的字段。
var source = values || {};
var mediaOssIds = trimOrUndefined(source.mediaOssIds);
var status = trimOrUndefined(source.status);
var body = {
feedType: trimOrUndefined(source.feedType) || 'text',
feedContent: String(source.feedContent || '').trim()
};
var sortOrder = toIntegerOrUndefined(source.sortOrder);
if (status !== undefined && status !== '0' && status !== '1') {
throw new Error('动态状态只能是 0 或 1');
}
if (trimOrUndefined(source.sortOrder) !== undefined && sortOrder === undefined) {
throw new Error('排序值必须是非负整数');
}
if (mediaOssIds !== undefined) body.mediaOssIds = mediaOssIds;
if (status !== undefined) body.status = status;
if (sortOrder !== undefined) body.sortOrder = sortOrder;
return body;
}
function buildCommentBody(values) {
// FamilyFeedCommentBody 仅接受 commentContent 和可选的 parentCommentId。
var source = values || {};
var body = {
commentContent: String(source.commentContent || '').trim()
};
var parentCommentId = trimOrUndefined(source.parentCommentId);
if (parentCommentId !== undefined) body.parentCommentId = parentCommentId;
return body;
}
function getFeedId(item) {
return toId(item && item.feedId);
}
function getCommentId(item) {
return toId(item && item.commentId);
}
function normalizeFeed(item) {
var feedId = getFeedId(item);
var feedContent = item && item.feedContent;
var feed;
if (!feedId || feedContent === undefined || feedContent === null) return null;
feed = setDefined({
feedId: feedId,
feedContent: String(feedContent)
}, item, [
'genealogyNo',
'genealogyName',
'publisherNickName',
'publisherStatus',
'feedType',
'mediaOssIds',
'likedByMe',
'likeCount',
'commentCount',
'pinned',
'pinnedTime',
'sortOrder',
'status',
'remark',
'createTime',
'updateTime'
]);
if (toId(item.genealogyId)) feed.genealogyId = toId(item.genealogyId);
if (toId(item.publisherUserId)) feed.publisherUserId = toId(item.publisherUserId);
return feed;
}
function normalizeComment(item) {
var commentId = getCommentId(item);
var commentContent = item && item.commentContent;
var userDeleted;
if (!commentId || commentContent === undefined) return null;
userDeleted = String(item && item.userDeleted || '') === '1';
var comment = {
commentId: commentId,
commentContent: commentContent === null ? '该评论已删除' : String(commentContent),
appUserNickName: trimOrUndefined(item && item.appUserNickName),
replyCount: item && item.replyCount,
userDeleted: userDeleted || commentContent === null
};
setDefined(comment, item, [
'appUserAvatar',
'parentAppUserNickName',
'commentLevel',
'status',
'createTime'
]);
['genealogyId', 'feedId', 'parentCommentId', 'appUserId', 'parentAppUserId'].forEach(function (field) {
if (toId(item && item[field])) comment[field] = toId(item[field]);
});
return comment;
}
function normalizeList(data) {
// AxiosRequestUtil 已解包 ListResult 与 PageResult,这里只接收对应的 data 或 rows。
if (Array.isArray(data)) return data;
if (data && Array.isArray(data.rows)) return data.rows;
return [];
}
function escapeHtml(value) {
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function getListUrl(genealogyId, feedId) {
return buildFeedUrl(feedId ? 'edit' : 'list', genealogyId, feedId);
}
function renderFeedRow(feed, genealogyId) {
var canDelete = viewerContext.userId && viewerContext.userId === feed.publisherUserId;
var mediaCount = feed.mediaOssIds ? String(feed.mediaOssIds).split(',').filter(Boolean).length : 0;
var title = feed.publisherNickName || feed.genealogyName || '家族动态';
var meta = [];
var actions = '';
if (feed.pinned === true || String(feed.pinned) === '1') meta.push('置顶');
if (feed.createTime) meta.push(feed.createTime);
if (mediaCount) meta.push('附件 ' + mediaCount);
meta.push('点赞 ' + Number(feed.likeCount || 0));
meta.push('评论 ' + Number(feed.commentCount || 0));
actions += feed.likedByMe
? '取消点赞 '
: '点赞 ';
actions += '查看详情 ';
actions += '查看评论 ';
actions += '发表评论 ';
if (viewerContext.canEditContent) {
actions += '编辑 ';
}
if (canDelete) {
actions += '删除 ';
}
return '' +
'' + escapeHtml(title) + ' ' + escapeHtml(feed.feedContent) + '
' +
'
' + escapeHtml(meta.join(' · ')) + '
' +
'' + actions + '
';
}
function renderFeeds(data, genealogyId) {
var container = query('[data-feed-list]');
var items = normalizeList(data).map(normalizeFeed);
var invalidCount = items.filter(function (item) { return !item; }).length;
var feeds = items.filter(Boolean);
if (!container) return;
if (invalidCount) {
container.innerHTML = '
动态响应缺少 feedId 或 feedContent,请联系后端补充 DTO。
';
return;
}
if (!feeds.length) {
container.innerHTML = '暂无家族动态
';
return;
}
container.innerHTML = feeds.map(function (feed) {
return renderFeedRow(feed, genealogyId);
}).join('');
}
function getPageTotal(data) {
var total = Number(data && data.total);
return Number.isFinite(total) && total >= 0 ? total : 0;
}
function updateListPager(data) {
var total = getPageTotal(data);
var totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
var previous = query('[data-feed-page-prev]');
var next = query('[data-feed-page-next]');
var label = query('[data-feed-page-label]');
if (previous) previous.disabled = currentFeedPage <= 1;
if (next) next.disabled = currentFeedPage >= totalPages;
if (label) label.textContent = '第 ' + currentFeedPage + ' / ' + totalPages + ' 页,共 ' + total + ' 条';
}
function hasReplies(comment) {
var count = comment && comment.replyCount;
return count !== undefined && count !== null && String(count) !== '' && String(count) !== '0';
}
function renderCommentRow(feedId, comment) {
var author = comment.appUserNickName ? '' + escapeHtml(comment.appUserNickName) + ' ' : '';
var actions = '';
var canDelete = viewerContext.userId && viewerContext.userId === comment.appUserId;
if (!comment.userDeleted) {
actions += '回复 ';
if (canDelete) {
actions += '删除评论 ';
}
}
if (hasReplies(comment)) {
actions += '查看回复 ';
}
return '' + author + escapeHtml(comment.commentContent) + '
' +
(comment.createTime ? '
' + escapeHtml(comment.createTime) + '
' : '') + actions + '
';
}
function normalizeComments(data) {
var comments = normalizeList(data).map(normalizeComment);
return {
invalidCount: comments.filter(function (item) { return !item; }).length,
comments: comments.filter(Boolean)
};
}
function renderComments(feedId, data) {
var detailContainer = query('[data-feed-detail-comments]');
var row = query('[data-feed-id="' + feedId + '"]');
var result = normalizeComments(data);
var list = result.comments;
var previous = row && row.nextElementSibling;
var content;
if (!row && !detailContainer) return;
if (previous && previous.getAttribute('data-feed-comment-list') === feedId) previous.remove();
if (result.invalidCount) {
content = '评论响应缺少 commentId 或 commentContent,请联系后端补充 DTO。
';
if (detailContainer) detailContainer.innerHTML = content;
else row.insertAdjacentHTML('afterend', '' + content + '
');
return;
}
if (!list.length) {
content = '暂无评论
';
if (detailContainer) detailContainer.innerHTML = content;
else row.insertAdjacentHTML('afterend', '' + content + '
');
return;
}
content = '' + list.map(function (comment) {
return renderCommentRow(feedId, comment);
}).join('') + '
' + renderPageControls('comments', feedId, '', currentCommentPages[feedId] || 1, getPageTotal(data));
if (detailContainer) detailContainer.innerHTML = content;
else row.insertAdjacentHTML('afterend', '' + content + '
');
}
function renderCommentReplies(feedId, parentCommentId, data) {
var row = query('[data-feed-comment-node="' + parentCommentId + '"]');
var result = normalizeComments(data);
var previous = row && row.nextElementSibling;
if (!row) return;
if (previous && previous.getAttribute('data-feed-reply-list') === parentCommentId) previous.remove();
if (result.invalidCount) {
row.insertAdjacentHTML('afterend', '回复响应缺少 commentId 或 commentContent,请联系后端补充 DTO。
');
return;
}
if (!result.comments.length) {
row.insertAdjacentHTML('afterend', '暂无直接回复
');
return;
}
row.insertAdjacentHTML('afterend', '' + result.comments.map(function (comment) {
return renderCommentRow(feedId, comment);
}).join('') + renderPageControls('replies', feedId, parentCommentId, currentReplyPages[parentCommentId] || 1, getPageTotal(data)) + '
');
}
function renderPageControls(kind, feedId, commentId, pageNum, total) {
var totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
var prefix = kind === 'comments' ? 'data-feed-comments-' : 'data-feed-replies-';
var ids = ' data-feed-id="' + escapeHtml(feedId) + '"' +
(commentId ? ' data-comment-id="' + escapeHtml(commentId) + '"' : '');
return '' +
'上一页 ' +
'第 ' + pageNum + ' / ' + totalPages + ' 页 ' +
'= totalPages ? ' disabled' : '') + '>下一页 ' +
'
';
}
function getFormValues(form) {
var values = {};
queryAll('[name]', form).forEach(function (field) {
if (field.disabled) return;
values[field.name] = field.value;
});
return values;
}
function fillFeedForm(data) {
var feed = normalizeFeed(data);
if (!feed) throw new Error('动态响应缺少 feedId 或 feedContent,请联系后端补充 DTO。');
queryAll('[data-feed-form] [name]').forEach(function (field) {
if (feed[field.name] !== undefined && feed[field.name] !== null) field.value = feed[field.name];
});
}
function normalizeViewerContext(genealogy, profile) {
return {
userId: toId(profile && profile.userId),
canEditContent: genealogy && genealogy.canEditContent === true,
canManage: genealogy && genealogy.canManage === true
};
}
async function loadViewerContext(api, genealogyId) {
var genealogy;
var profile;
try {
genealogy = await api.genealogyDetail(genealogyId);
} catch (error) {
genealogy = null;
}
try {
profile = await api.currentProfile();
} catch (error) {
profile = null;
}
viewerContext = normalizeViewerContext(genealogy, profile);
return viewerContext;
}
function renderFeedDetail(feed, genealogyId) {
var container = query('[data-feed-detail]');
var canDelete = viewerContext.userId && viewerContext.userId === feed.publisherUserId;
var actions = '';
if (!container) return;
actions += feed.likedByMe
? '取消点赞 '
: '点赞 ';
actions += '发表评论 ';
actions += '复制分享链接 ';
if (viewerContext.canEditContent) {
actions += '编辑 ';
}
if (canDelete) {
actions += '删除 ';
}
container.setAttribute('data-feed-id', feed.feedId);
container.innerHTML = '' + escapeHtml(feed.publisherNickName || feed.genealogyName || '家族动态') + ' ' +
'' + escapeHtml(feed.feedContent) + '
' +
'' +
escapeHtml([
feed.createTime || '',
'点赞 ' + Number(feed.likeCount || 0),
'评论 ' + Number(feed.commentCount || 0),
String(feed.status) === '1' ? '已停用' : '正常'
].filter(Boolean).join(' · ')) +
'
' + actions + '
';
}
function syncGenealogyLinks(genealogyId) {
if (root.ProfileUI && root.ProfileUI.syncGenealogyContextLinks) {
root.ProfileUI.syncGenealogyContextLinks();
}
queryAll('[data-feed-create-link], [data-feed-list-link]').forEach(function (link) {
link.href = link.hasAttribute('data-feed-create-link')
? buildFeedUrl('edit', genealogyId)
: buildFeedUrl('list', genealogyId);
});
}
function requireGenealogyId() {
var genealogyId = getCurrentGenealogyId();
var container;
if (!genealogyId) {
container = query('[data-feed-list]');
if (container) container.innerHTML = '请从具体家谱进入家族圈动态
';
showMessage('请从具体家谱进入家族圈动态');
}
return genealogyId;
}
async function loadFeeds(pageNum) {
var api = getApi();
var genealogyId;
var data;
if (redirectUnauthorized(api)) return;
genealogyId = requireGenealogyId();
if (!genealogyId) return;
syncGenealogyLinks(genealogyId);
currentFeedPage = buildPageQuery(pageNum || getQueryParam(root.location && root.location.search, 'pageNum')).pageNum;
try {
await loadViewerContext(api, genealogyId);
data = await api.feedsPage(genealogyId, buildPageQuery(currentFeedPage));
renderFeeds(data, genealogyId);
updateListPager(data);
} catch (error) {
if (redirectUnauthorized(api, error)) return;
if (query('[data-feed-list]')) query('[data-feed-list]').innerHTML = '家族动态加载失败,请稍后重试。
';
showMessage(error.message || '家族动态加载失败');
}
}
async function loadFeedForEdit() {
var api = getApi();
var genealogyId;
var feedId = getCurrentFeedId();
if (redirectUnauthorized(api)) return;
genealogyId = requireGenealogyId();
if (!genealogyId) return;
syncGenealogyLinks(genealogyId);
try {
await loadViewerContext(api, genealogyId);
if (query('[data-feed-advanced]')) {
query('[data-feed-advanced]').hidden = !viewerContext.canManage;
queryAll('[name]', query('[data-feed-advanced]')).forEach(function (field) {
field.disabled = !viewerContext.canManage;
});
}
if (!feedId) return;
fillFeedForm(await api.feedDetail(genealogyId, feedId));
} catch (error) {
if (redirectUnauthorized(api, error)) return;
showMessage(error.message || '动态详情加载失败');
}
}
async function loadFeedDetail() {
var api = getApi();
var genealogyId;
var feedId = getCurrentFeedId();
var feed;
if (redirectUnauthorized(api)) return;
genealogyId = requireGenealogyId();
if (!genealogyId || !feedId) {
if (query('[data-feed-detail]')) query('[data-feed-detail]').innerHTML = '动态链接缺少有效编号
';
return;
}
syncGenealogyLinks(genealogyId);
try {
await loadViewerContext(api, genealogyId);
feed = normalizeFeed(await api.feedDetail(genealogyId, feedId));
if (!feed) throw new Error('动态响应缺少 feedId 或 feedContent,请联系后端补充 DTO。');
renderFeedDetail(feed, genealogyId);
await showComments(feedId, currentCommentPages[feedId] || 1);
} catch (error) {
if (redirectUnauthorized(api, error)) return;
if (query('[data-feed-detail]')) query('[data-feed-detail]').innerHTML = '动态详情加载失败,请返回列表重试。
';
showMessage(error.message || '动态详情加载失败');
}
}
async function submitFeed(form) {
var api = getApi();
var genealogyId;
var feedId = getCurrentFeedId();
var body;
if (redirectUnauthorized(api)) return;
genealogyId = requireGenealogyId();
if (!genealogyId) return;
// 富文本编辑器在提交前同步回 textarea,保证发送的是用户当前输入。
if (root.AppRichEditor && root.AppRichEditor.syncAll) root.AppRichEditor.syncAll();
try {
body = buildFeedBody(getFormValues(form));
} catch (error) {
showMessage(error.message || '动态表单校验失败');
return;
}
if (!body.feedContent) {
showMessage('请填写动态内容');
return;
}
return withActionLock('save:' + (feedId || 'new'), async function () {
var saved;
var savedFeedId;
var buttons = queryAll('[type="submit"]', form);
buttons.forEach(function (button) { button.disabled = true; });
try {
if (feedId) {
saved = await api.updateFeed(genealogyId, feedId, body);
} else {
saved = await api.createFeed(genealogyId, body);
}
savedFeedId = getFeedId(saved) || feedId;
if (!savedFeedId) throw new Error('动态保存响应缺少 feedId');
if (String(body.status || '0') === '0') {
await api.feedDetail(genealogyId, savedFeedId);
}
showMessage('动态已保存');
root.location.href = buildPostSaveUrl(genealogyId, savedFeedId, body.status);
} catch (error) {
if (redirectUnauthorized(api, error)) return;
showMessage(error.message || '动态保存失败');
} finally {
buttons.forEach(function (button) { button.disabled = false; });
}
});
}
async function likeFeed(feedId, shouldLike) {
var api = getApi();
var genealogyId;
if (redirectUnauthorized(api)) return;
genealogyId = requireGenealogyId();
if (!genealogyId || !feedId) return;
return withActionLock((shouldLike ? 'like:' : 'unlike:') + feedId, async function () {
try {
if (shouldLike) {
await api.likeFeed(genealogyId, feedId);
} else {
await api.unlikeFeed(genealogyId, feedId);
}
if (query('[data-feed-detail-page]')) await loadFeedDetail();
else await loadFeeds(currentFeedPage);
} catch (error) {
if (redirectUnauthorized(api, error)) return;
showMessage(error.message || (shouldLike ? '点赞失败' : '取消点赞失败'));
}
});
}
async function deleteFeed(feedId) {
var api = getApi();
var genealogyId;
if (redirectUnauthorized(api)) return;
genealogyId = requireGenealogyId();
if (!genealogyId || !feedId) return;
if (root.confirm && !root.confirm('确认删除这条动态吗?')) return;
return withActionLock('delete:' + feedId, async function () {
try {
await api.deleteFeed(genealogyId, feedId);
if (query('[data-feed-detail-page]')) root.location.href = buildFeedUrl('list', genealogyId);
else await loadFeeds(currentFeedPage);
} catch (error) {
if (redirectUnauthorized(api, error)) return;
showMessage(error.message || '删除动态失败');
}
});
}
async function showComments(feedId, pageNum) {
var api = getApi();
var genealogyId;
if (redirectUnauthorized(api)) return;
genealogyId = requireGenealogyId();
if (!genealogyId || !feedId) return;
currentCommentPages[feedId] = buildPageQuery(pageNum).pageNum;
try {
renderComments(
feedId,
await api.feedCommentsPage(genealogyId, feedId, buildPageQuery(currentCommentPages[feedId]))
);
} catch (error) {
if (redirectUnauthorized(api, error)) return;
if (query('[data-feed-detail-comments]')) {
query('[data-feed-detail-comments]').innerHTML = '评论加载失败,请稍后重试。
';
}
showMessage(error.message || '评论加载失败');
}
}
async function showCommentReplies(feedId, commentId, pageNum) {
var api = getApi();
var genealogyId;
if (redirectUnauthorized(api)) return;
genealogyId = requireGenealogyId();
if (!genealogyId || !feedId || !commentId) return;
currentReplyPages[commentId] = buildPageQuery(pageNum).pageNum;
try {
renderCommentReplies(
feedId,
commentId,
await api.feedCommentRepliesPage(
genealogyId,
feedId,
commentId,
buildPageQuery(currentReplyPages[commentId])
)
);
} catch (error) {
if (redirectUnauthorized(api, error)) return;
if (query('[data-feed-comment-node="' + commentId + '"]')) {
query('[data-feed-comment-node="' + commentId + '"]').insertAdjacentHTML(
'afterend',
'回复加载失败,请稍后重试。
'
);
}
showMessage(error.message || '回复加载失败');
}
}
async function createComment(feedId, parentCommentId) {
var api = getApi();
var genealogyId;
var content;
var body;
if (redirectUnauthorized(api)) return;
genealogyId = requireGenealogyId();
if (!genealogyId || !feedId) return;
content = root.prompt ? root.prompt(parentCommentId ? '请输入回复内容' : '请输入评论内容', '') : '';
body = buildCommentBody({ commentContent: content, parentCommentId: parentCommentId });
if (!body.commentContent) return;
if (body.commentContent.length > 1000) {
showMessage('评论不能超过 1000 字');
return;
}
return withActionLock('comment:' + feedId + ':' + (parentCommentId || 'root'), async function () {
try {
await api.createFeedComment(genealogyId, feedId, body);
await showComments(feedId, currentCommentPages[feedId] || 1);
if (parentCommentId) await showCommentReplies(feedId, parentCommentId, currentReplyPages[parentCommentId] || 1);
if (query('[data-feed-detail-page]')) {
var feed = normalizeFeed(await api.feedDetail(genealogyId, feedId));
if (feed) renderFeedDetail(feed, genealogyId);
} else {
await loadFeeds(currentFeedPage);
}
} catch (error) {
if (redirectUnauthorized(api, error)) return;
showMessage(error.message || '评论发布失败');
}
});
}
async function deleteComment(feedId, commentId) {
var api = getApi();
var genealogyId;
if (redirectUnauthorized(api)) return;
genealogyId = requireGenealogyId();
if (!genealogyId || !feedId || !commentId) return;
if (root.confirm && !root.confirm('确认删除这条评论吗?')) return;
return withActionLock('delete-comment:' + commentId, async function () {
try {
await api.deleteFeedComment(genealogyId, feedId, commentId);
await showComments(feedId, currentCommentPages[feedId] || 1);
if (query('[data-feed-detail-page]')) {
var feed = normalizeFeed(await api.feedDetail(genealogyId, feedId));
if (feed) renderFeedDetail(feed, genealogyId);
} else {
await loadFeeds(currentFeedPage);
}
} catch (error) {
if (redirectUnauthorized(api, error)) return;
showMessage(error.message || '删除评论失败');
}
});
}
function bindActions() {
if (!documentRef) return;
documentRef.addEventListener('submit', function (event) {
var form = event.target.closest('[data-feed-form]');
if (!form) return;
event.preventDefault();
submitFeed(form);
});
documentRef.addEventListener('click', function (event) {
var target = event.target;
var feedId;
var commentId;
var pageNum;
if (target.closest('[data-feed-like]')) {
event.preventDefault();
likeFeed(target.closest('[data-feed-like]').getAttribute('data-feed-like'), true);
}
if (target.closest('[data-feed-unlike]')) {
event.preventDefault();
likeFeed(target.closest('[data-feed-unlike]').getAttribute('data-feed-unlike'), false);
}
if (target.closest('[data-feed-comments]')) {
event.preventDefault();
feedId = target.closest('[data-feed-comments]').getAttribute('data-feed-comments');
showComments(feedId, 1);
}
if (target.closest('[data-feed-comment]')) {
event.preventDefault();
feedId = target.closest('[data-feed-comment]').getAttribute('data-feed-comment');
createComment(feedId);
}
if (target.closest('[data-feed-comment-reply]')) {
event.preventDefault();
commentId = target.closest('[data-feed-comment-reply]').getAttribute('data-feed-comment-reply');
feedId = target.closest('[data-feed-comment-reply]').getAttribute('data-feed-id');
createComment(feedId, commentId);
}
if (target.closest('[data-feed-comment-replies]')) {
event.preventDefault();
commentId = target.closest('[data-feed-comment-replies]').getAttribute('data-feed-comment-replies');
feedId = target.closest('[data-feed-comment-replies]').getAttribute('data-feed-id');
showCommentReplies(feedId, commentId, 1);
}
if (target.closest('[data-feed-delete]')) {
event.preventDefault();
deleteFeed(target.closest('[data-feed-delete]').getAttribute('data-feed-delete'));
}
if (target.closest('[data-feed-comment-delete]')) {
event.preventDefault();
commentId = target.closest('[data-feed-comment-delete]').getAttribute('data-feed-comment-delete');
feedId = target.closest('[data-feed-comment-delete]').getAttribute('data-feed-id');
deleteComment(feedId, commentId);
}
if (target.closest('[data-feed-page-prev]')) {
event.preventDefault();
loadFeeds(Math.max(1, currentFeedPage - 1));
}
if (target.closest('[data-feed-page-next]')) {
event.preventDefault();
loadFeeds(currentFeedPage + 1);
}
if (target.closest('[data-feed-comments-prev], [data-feed-comments-next]')) {
event.preventDefault();
feedId = target.closest('[data-feed-id]').getAttribute('data-feed-id');
pageNum = currentCommentPages[feedId] || 1;
showComments(feedId, target.closest('[data-feed-comments-prev]') ? Math.max(1, pageNum - 1) : pageNum + 1);
}
if (target.closest('[data-feed-replies-prev], [data-feed-replies-next]')) {
event.preventDefault();
feedId = target.closest('[data-feed-id]').getAttribute('data-feed-id');
commentId = target.closest('[data-comment-id]').getAttribute('data-comment-id');
pageNum = currentReplyPages[commentId] || 1;
showCommentReplies(feedId, commentId, target.closest('[data-feed-replies-prev]') ? Math.max(1, pageNum - 1) : pageNum + 1);
}
if (target.closest('[data-feed-share]')) {
event.preventDefault();
if (root.navigator && root.navigator.clipboard && root.location) {
root.navigator.clipboard.writeText(root.location.href).then(function () {
showMessage('分享链接已复制');
}).catch(function () {
showMessage('复制失败,请从浏览器地址栏复制');
});
} else {
showMessage('请从浏览器地址栏复制分享链接');
}
}
});
}
function init() {
if (!documentRef) return;
bindActions();
if (query('[data-feed-page]')) loadFeeds();
if (query('[data-feed-edit-page]')) loadFeedForEdit();
if (query('[data-feed-detail-page]')) loadFeedDetail();
}
return {
getCurrentGenealogyId: getCurrentGenealogyId,
getCurrentFeedId: getCurrentFeedId,
buildFeedBody: buildFeedBody,
buildCommentBody: buildCommentBody,
buildPageQuery: buildPageQuery,
buildFeedUrl: buildFeedUrl,
buildPostSaveUrl: buildPostSaveUrl,
withActionLock: withActionLock,
normalizeFeed: normalizeFeed,
normalizeComment: normalizeComment,
normalizeViewerContext: normalizeViewerContext,
normalizeComments: normalizeComments,
normalizeList: normalizeList,
shouldRedirectToLogin: shouldRedirectToLogin,
init: init
};
});