feat(comments): 匿名评论 + 表情最近使用;fix: 自动填充聚焦提示 & 邮件模板回退原版 & 返回顶部改 CSS 动画
- 匿名评论:comments.is_anonymous 列;对外昵称/头像/网址/徽标/等级/IP归属全部脱敏, 真实身份留 users 表(回复通知照发、autofill 不受污染);邮件昵称也用「匿名」防泄露 - 表情包最近使用:面板顶部「最近」栏,localStorage 记录 8 个,零侵入复用 Artalk 插入逻辑 - 匿名开关插在 textarea 与底栏之间独立一行,不挤移动端底栏 - 自动填充:提示改 focusin 触发(原 input+sessionStorage 一次性门导致永不触发); 已填昵称/网址时不覆盖并弹提示 - 邮件模板 UI 回退原版深色气泡设计,保留表情内联尺寸/主题文案等逻辑修复 - 返回顶部:rAF 自绘改 CSS scroll-behavior(滚动期临时挂类),修移动端掉帧
This commit is contained in:
1 parent
090262d278
commit
215929a67f
11 files changed
+587
-207
No files matched your search
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* comment-anonymous —— 「匿名评论」开关
|
||||
*
|
||||
* 场景:有人想说话但不想暴露身份(昵称/头像/网址都能对上号)。
|
||||
* 勾选后提交的评论对外显示为「匿名」:头像换通用图、网址/徽标/等级/IP归属
|
||||
* 全部隐藏。真实昵称和邮箱仍存在后端 users 表里 —— 有人回复时通知照发、
|
||||
* 下次来「填邮箱自动带出昵称」不受污染。博主后台能看到真实身份(审核需要)。
|
||||
*
|
||||
* 实现要点:
|
||||
* - 开关插在 textarea 和底栏之间**独立一行**(右对齐小开关),不挤底栏 ——
|
||||
* 移动端底栏本来就塞了表情/图片/发送按钮,再塞一个就挤爆了。
|
||||
* - Artalk 是 editorTravel 单实例游走,全站只有一个活的 .atk-main-editor,
|
||||
* fetch 拦截读它的开关状态即可。
|
||||
* - 借已有的 fetch 拦截链给 POST /api/v2/comments 的 body 注入 anonymous:true。
|
||||
*/
|
||||
(function () {
|
||||
if (window.__atkAnonReady) return; // PJAX 重复执行防抖
|
||||
window.__atkAnonReady = true;
|
||||
|
||||
var TIP = '对外显示为「匿名」,头像和网址会隐藏;邮箱只用于接收回复通知,对外不可见';
|
||||
|
||||
// 取「当前活着的」编辑器里的开关:优先可见的(editorTravel 游走的那个)
|
||||
function currentToggle() {
|
||||
var eds = document.querySelectorAll('.atk-main-editor');
|
||||
var fallback = null;
|
||||
for (var i = 0; i < eds.length; i++) {
|
||||
var cb = eds[i].querySelector('.atk-anon-toggle input');
|
||||
if (!cb) continue;
|
||||
if (eds[i].offsetParent !== null) return cb; // 可见编辑器优先
|
||||
if (!fallback) fallback = cb;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function inject(editor) {
|
||||
if (!editor || !editor.querySelector) return;
|
||||
if (editor.querySelector('.atk-anon-toggle')) return; // 已注入
|
||||
var bottom = editor.querySelector('.atk-bottom');
|
||||
if (!bottom) return; // 结构没就绪,等下轮扫描
|
||||
|
||||
var label = document.createElement('label');
|
||||
label.className = 'atk-anon-toggle';
|
||||
label.title = TIP;
|
||||
var cb = document.createElement('input');
|
||||
cb.type = 'checkbox';
|
||||
var txt = document.createElement('span');
|
||||
txt.textContent = '匿名评论';
|
||||
label.appendChild(cb);
|
||||
label.appendChild(txt);
|
||||
bottom.parentNode.insertBefore(label, bottom);
|
||||
}
|
||||
|
||||
var scanTimer = null;
|
||||
function scheduleScan() {
|
||||
if (scanTimer) return;
|
||||
scanTimer = setTimeout(function () {
|
||||
scanTimer = null;
|
||||
try {
|
||||
var eds = document.querySelectorAll('.atk-main-editor');
|
||||
for (var i = 0; i < eds.length; i++) inject(eds[i]);
|
||||
} catch (e) {}
|
||||
}, 120);
|
||||
}
|
||||
|
||||
try {
|
||||
// 编辑器由 Artalk 动态创建(PJAX / 点回复时游走重建位置),观察兜底
|
||||
new MutationObserver(scheduleScan).observe(document.body, { childList: true, subtree: true });
|
||||
} catch (e) {}
|
||||
// MutationObserver 不监听「隐藏节点变可见」,用点击/聚焦兜底扫一遍
|
||||
document.addEventListener('click', scheduleScan, true);
|
||||
document.addEventListener('focusin', scheduleScan, true);
|
||||
scheduleScan();
|
||||
|
||||
// ── fetch 拦截:给新建评论请求注入 anonymous 标志 ──────────────────────
|
||||
// Artalk 的提交走 fetch + JSON body;链上已有别的拦截器(提交反馈/失败
|
||||
// 翻译),这里只改 init.body,然后原样放行,互不干扰。
|
||||
var origFetch = window.fetch;
|
||||
if (typeof origFetch === 'function') {
|
||||
window.fetch = function (input, init) {
|
||||
try {
|
||||
var url = String(typeof input === 'string' ? input : (input && input.url) || '');
|
||||
var method = String((init && init.method) || (input && input.method) || 'GET').toUpperCase();
|
||||
if (method === 'POST' && /\/api\/v2\/comments(\?|$)/.test(url) &&
|
||||
init && typeof init.body === 'string') {
|
||||
var cb = currentToggle();
|
||||
if (cb && cb.checked) {
|
||||
var data = JSON.parse(init.body);
|
||||
if (data && typeof data === 'object' && !data.anonymous) {
|
||||
data.anonymous = true;
|
||||
init = Object.assign({}, init, { body: JSON.stringify(data) });
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {} // 解析失败就按原样发,绝不能因为匿名开关挂掉评论
|
||||
return origFetch.apply(this, [input, init]);
|
||||
};
|
||||
}
|
||||
})();
|
||||
Reference in new issue
Block a user