Files
blog/themes/Ying/assets/js/modules/comment-autofill.js
T

145 lines
6.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* comment-autofill —— 老朋友「填邮箱自动带出昵称和网址」
*
* 场景:很多人隔几个月来评论一次,早忘了上次填的昵称/网址,每次都重新想。
* 这个模块做两件事:
* 1. 访客开始在昵称框打字时,弹一次 toast 提示「填邮箱可以自动带回来」;
* 2. 邮箱填完(失焦或格式完整)就问后端:这个邮箱上次评论用的什么昵称/网址,
* 拿到后回填到空着的输入框,让老朋友少打几个字。
*
* 隐私红线:后端 /comments/lookup **只回昵称和网址,永远不回邮箱**;
* 邮箱只作为查询参数发给评论后端(跟提交评论本身一样),不在本地落任何存储,
* 也不打日志。查不到就安静地什么都不做,绝不给访客「你在被查询」的感觉。
*
* 实现说明:用 document 级事件委托(capture),不绑定具体输入框 ——
* Artalk 的编辑器会反复重建(PJAX 切页、点回复、登录态变化),委托一劳永逸。
*/
(function () {
if (window.__atkAutofillReady) return; // PJAX 重复执行防抖
window.__atkAutofillReady = true;
var HINT_KEY = 'atk_friend_hint'; // sessionStorage:提示只弹一次
var asked = {}; // 查过的邮箱 → 结果,避免重复请求
var timer = null;
function toast(msg, type, duration) {
try {
if (window.Toast && typeof window.Toast[type || 'info'] === 'function') {
window.Toast[type || 'info'](msg, duration);
}
} catch (e) {}
}
function isEmail(s) { return /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(String(s || '').trim()); }
function editorOf(el) {
// 线上 Artalk 的编辑器容器是 .atk-main-editor(旧版/别处可能是 .atk-editor),
// 兜底再往上找整个 .artalk 根 —— 宁可范围大一点,也别让委托匹配不到
return (el && el.closest)
? el.closest('.atk-main-editor, .atk-editor, .artalk, .atk-layer-wrap')
: null;
}
function fieldIn(editor, name) {
return editor ? editor.querySelector('input[name="' + name + '"]') : null;
}
/** Artalk 对受控输入有自己的状态,改完必须补一个 input 事件它才认 */
function setVal(input, value) {
if (!input) return;
input.value = value;
try { input.dispatchEvent(new Event('input', { bubbles: true })); } catch (e) {}
}
function showHint(editor) {
try {
if (sessionStorage.getItem(HINT_KEY)) return;
sessionStorage.setItem(HINT_KEY, '1');
} catch (e) { return; } // 存储被禁用就别弹了,宁缺毋滥
var email = fieldIn(editor, 'email');
if (email && email.value.trim()) return; // 已经填了邮箱,不用提示
toast('老朋友?填一下邮箱,昵称和网址会自动帮你带回来 ✨', 'info', 4500);
}
function fillFrom(editor, data) {
if (!data || !data.found) return;
var nick = fieldIn(editor, 'nick');
var link = fieldIn(editor, 'link');
var name = String(data.name || '').trim();
var url = String(data.link || '').trim();
if (!name) return;
var filled = [];
// 昵称:空着,或者只打了历史昵称的开头(说明 TA 正在凭印象敲同一个名字)
// 才替 TA 补全 —— 已经敲了一个完全不同的名字就别抢,那是故意的。
if (nick) {
var cur = nick.value.trim();
var lower = cur.toLowerCase();
var lowerName = name.toLowerCase();
if (!cur || lowerName.indexOf(lower) === 0) {
if (cur !== name) { setVal(nick, name); filled.push('昵称'); }
}
}
if (link && !link.value.trim() && url) { setVal(link, url); filled.push('网址'); }
if (filled.length) {
toast('欢迎回来,' + name + '!已帮你填好' + filled.join('和') + ' 👋', 'success', 4000);
}
}
function lookup(editor, email) {
var key = email.toLowerCase();
if (asked[key]) { fillFrom(editor, asked[key]); return; }
var server = (window.artalkConfig && window.artalkConfig.server || '').replace(/\/+$/, '');
if (!server) return;
// 超时兜底:评论后端在国内访问偶尔抖,别让访客干等
var ctrl = typeof AbortController !== 'undefined' ? new AbortController() : null;
var killer = null;
if (ctrl) killer = setTimeout(function () { try { ctrl.abort(); } catch (e) {} }, 8000);
fetch(server + '/api/v2/comments/lookup?email=' + encodeURIComponent(email), {
method: 'GET',
credentials: 'omit',
signal: ctrl ? ctrl.signal : undefined
}).then(function (r) { return r.ok ? r.json() : null; }).then(function (d) {
asked[key] = d || { found: false };
fillFrom(editor, asked[key]);
}).catch(function () {
asked[key] = { found: false }; // 失败也记一下,别反复打同一个请求
}).finally(function () { if (killer) clearTimeout(killer); });
}
// ── 事件委托(capture:Artalk 内部可能 stopPropagation)──────────────
document.addEventListener('input', function (e) {
var el = e.target;
if (!el || el.tagName !== 'INPUT' || !el.name) return;
var editor = editorOf(el);
if (!editor) return; // 只管 Artalk 的评论框
if (el.name === 'nick') {
if (el.value.trim()) showHint(editor); // 打了第一个字就开始提示
return;
}
if (el.name === 'email') {
var v = el.value.trim();
if (isEmail(v)) {
if (timer) clearTimeout(timer);
timer = setTimeout(function () { lookup(editor, v); }, 700); // 停手再查
}
}
}, true);
document.addEventListener('focusout', function (e) {
var el = e.target;
if (!el || el.tagName !== 'INPUT' || el.name !== 'email') return;
var editor = editorOf(el);
if (!editor) return;
var v = el.value.trim();
if (!isEmail(v)) return;
if (timer) clearTimeout(timer);
lookup(editor, v); // 离开邮箱框就立刻查
}, true);
})();