悄悄话第 2 步:星空页留言区 最热/最新/同孕周、真实留言服务端点赞、赞够自动挂星标记;「说一句」改发进留言区(先审后发)(v26110616)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
19
js/app.js
19
js/app.js
@@ -3,7 +3,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
||||
var VER = '26110614';
|
||||
var VER = '26110616';
|
||||
const $ = (s, c) => (c || document).querySelector(s);
|
||||
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
|
||||
|
||||
@@ -4775,7 +4775,9 @@
|
||||
paintSky(l);
|
||||
paintList(l);
|
||||
if (cur && list()[cur - 1]) $$('.wsk-st[data-go="' + cur + '"]', page).forEach(x => x.classList.add('cur'));
|
||||
$('#whpLeft').textContent = !data ? '' : !oid() || !data.canPost ? '填好预产期,就能说一句' : data.left ? '今天还能说 ' + data.left + ' 句' : '今天的 3 句说完啦,明天再来';
|
||||
$('#whpLeft').textContent = !data ? '' : !oid() || !data.canPost ? '填好预产期,就能说一句' :
|
||||
window.MBWhCm ? '说的话先审核再公开,攒够 ' + MBWhCm.starLikes() + ' 个赞就挂上星星' : /* 11-07 悄悄话第 2 步:说一句 = 发进下面的留言区 */
|
||||
data.left ? '今天还能说 ' + data.left + ' 句' : '今天的 3 句说完啦,明天再来';
|
||||
}
|
||||
function drawer(on) {
|
||||
$('#whDrawer').classList.toggle('show', on);
|
||||
@@ -4829,6 +4831,7 @@
|
||||
const r = j && j.code === 200 && j.result;
|
||||
if (!r || !r.show || !(r.list || []).length) { data = null; show(false); autoFail(); return; }
|
||||
data = r;
|
||||
window.MB_WH_WEEK = r.week; /* 留言区「同孕周」用(js/wh-cm.js) */
|
||||
const ns = r.list.map(x => x.id + ':' + x.hugs + (x.hugged ? 'h' : '')).join('|');
|
||||
if (ns !== sig) {
|
||||
sig = ns;
|
||||
@@ -4914,7 +4917,7 @@
|
||||
$('#whSay').addEventListener('click', () => {
|
||||
if (!data) return;
|
||||
if (!oid() || !data.canPost) { toast('填好预产期,就能和同孕周妈妈说悄悄话啦'); return; }
|
||||
if (!data.left) { toast('今天已经说了 3 句啦,明天再来'); return; }
|
||||
if (!data.left && !window.MBWhCm) { toast('今天已经说了 3 句啦,明天再来'); return; }
|
||||
ta.value = '';
|
||||
ta.maxLength = data.maxLen || 40;
|
||||
cnt();
|
||||
@@ -4924,7 +4927,8 @@
|
||||
$('#whMTopic').style.display = th ? '' : 'none';
|
||||
ta.placeholder = th && th.ph ? th.ph : '今天想对谁说点什么?';
|
||||
$('#whPresets').innerHTML = ((th && th.presets && th.presets.length) ? th.presets : (data.presets || [])).map(t => '<button class="wh-pre" type="button">' + esc(t) + '</button>').join('');
|
||||
$('#whLeft').textContent = '今天还能说 ' + data.left + ' 句';
|
||||
$('#whLeft').textContent = window.MBWhCm ? '' : '今天还能说 ' + data.left + ' 句';
|
||||
if (window.MBWhCm) { $('#whMLead').textContent = '发出后先审核,通过了会出现在星空下面的留言区;攒够 ' + MBWhCm.starLikes() + ' 个赞,就会挂到星空上,同孕期的妈妈都能点到。'; }
|
||||
openModal('modalWhisper');
|
||||
});
|
||||
$('#whPresets').addEventListener('click', e => {
|
||||
@@ -4942,6 +4946,13 @@
|
||||
const btn = $('#whSend');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '正在发出…';
|
||||
/* 11-07 悄悄话第 2 步(用户定):星空页「说一句」并入留言区 —— 发成置顶笔记下的留言(先审后发),赞够门槛自动挂星 */
|
||||
if (window.MBWhCm) {
|
||||
MBWhCm.post(t, data.week)
|
||||
.then(m => { if (m) { toast(m); return; } closeModal(); toast('发出去啦,审核通过后会出现在留言区'); })
|
||||
.then(() => { busy = false; btn.disabled = false; btn.textContent = '悄悄发出'; });
|
||||
return;
|
||||
}
|
||||
fetch(API + '?openid=' + encodeURIComponent(oid()), {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: t })
|
||||
})
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
var REC = ['taidong', 'yunqitizhong', 'yun', 'yunqixuetang', 'gongsuopinlv', 'taierguzhong', 'chanjianrili', 'bushui']
|
||||
.map(function (t) { return '#htGrid .tg[data-tool="' + t + '"]'; }).join(',');
|
||||
var SEL = {
|
||||
home: REC + ',#vitals .vt-it,.tab-fab,.fab,.fab-w,#htEdit,.tg-add,#homeExchMod,#meExchCard,#meExchGo,#meExchOrders,#bbBanner,[data-modal="modalProfile"],[data-modal="modalWhisper"],#bfMe,.preg-card,#whSay,[data-hug]',
|
||||
home: REC + ',#vitals .vt-it,.tab-fab,.fab,.fab-w,#htEdit,.tg-add,#homeExchMod,#meExchCard,#meExchGo,#meExchOrders,#bbBanner,[data-modal="modalProfile"],[data-modal="modalWhisper"],#bfMe,.preg-card,#whSay,[data-hug],[data-wcl]',
|
||||
plaza: '#blk,#bfv,#csend,#cpic,#cemo,#cpOk,[data-like],[data-cl],[data-rk],#cmgo,[data-write],[data-mywrite],#tabRec',
|
||||
topic: '.tc-lk,.tc-go,.tc-in .pic-b,.tc-in .emo-b'
|
||||
};
|
||||
|
||||
11
js/wh-cm.css
11
js/wh-cm.css
@@ -33,3 +33,14 @@
|
||||
.wcm-more[hidden]{display:none}
|
||||
/* 内容卡 / 落完提示往上让一让,别被留言面板顶住 */
|
||||
.wh-page .wsc{bottom:calc(172px + env(safe-area-inset-bottom))}
|
||||
/* 11-07 第 2 步:最热 / 最新 / 同孕周 + 挂星标记 */
|
||||
.wcm-tb{display:flex;align-items:center;gap:6px;margin:0 18px 4px}
|
||||
.wcm-tb button{height:26px;padding:0 12px;border-radius:99px;border:0;background:rgba(255,255,255,.07);color:rgba(232,222,255,.62);font:inherit;font-size:12.5px}
|
||||
.wcm-tb button.on{background:linear-gradient(135deg,rgba(255,214,170,.92),rgba(255,170,160,.92));color:#3A2312;font-weight:800}
|
||||
.wcm-rule{margin-left:auto;font-size:11px;color:rgba(232,222,255,.42)}
|
||||
.wcm-star{display:inline-flex;align-items:center;gap:4px;margin:6px 0 0;padding:2px 8px 2px 6px;border-radius:99px;font-size:11px;font-weight:700;color:#FFE2B8;
|
||||
background:linear-gradient(135deg,rgba(122,79,214,.55),rgba(90,59,170,.35));box-shadow:0 0 10px rgba(255,214,170,.25)}
|
||||
.wcm-star svg{width:10px;height:10px;fill:currentColor}
|
||||
.wcm-lk.me{opacity:.7}
|
||||
.wcm-lk.pop svg{animation:wcmPop .45s ease}
|
||||
@keyframes wcmPop{40%{transform:scale(1.45)}100%{transform:scale(1.12)}}
|
||||
|
||||
101
js/wh-cm.js
101
js/wh-cm.js
@@ -4,7 +4,9 @@
|
||||
把原来的 .whp-bar(说一句悄悄话)挪进面板顶上,往上滑留言就盖上来。
|
||||
· 数据:广场置顶笔记 wh-<话题 from> 的评论 = cm.json 模拟(AGENTS.md 12c,不得挂星)+ /plaza/comments 已审核通过的真实评论
|
||||
+ 我自己发的(含审核中)。进星空页才拉 cm.json(600K+,gzip 后小很多;版本号跟广场 index.html 引的一致,共用缓存)。
|
||||
· 赞只记本机 mmbox.whcm.v1(服务端计数是悄悄话第 2 步)。 */
|
||||
· 11-07 第 2 步:真实留言的赞走服务端(POST /plaza/comment/like,每人每条一次、不能赞自己),赞够 starLikes(后台可改,先 3)
|
||||
自动挂到星空,挂上的标「已挂上星星」;模拟留言的赞只记本机 mmbox.whcm.v1(模拟的永远不挂星)。
|
||||
最热 / 最新 / 同孕周 三种看法;星空页「说一句」发的话也进这里(window.MBWhCm.post,app.js 调),先审后发。 */
|
||||
(function () {
|
||||
var page = document.getElementById('whPage'), bar = page && page.querySelector('.whp-bar');
|
||||
if (!page || !bar || !window.MB_WH_THEME) return;
|
||||
@@ -15,6 +17,8 @@
|
||||
var box = document.createElement('div');
|
||||
box.className = 'wcm'; box.id = 'wcm';
|
||||
box.innerHTML = '<div class="wcm-sp" aria-hidden="true"></div><div class="wcm-in"><div class="wcm-hd"><b>妈妈们的留言</b><span id="wcmN"></span><i class="wcm-up">上滑看看</i></div>' +
|
||||
'<div class="wcm-tb" role="tablist"><button type="button" class="on" data-wso="hot">最热</button><button type="button" data-wso="new">最新</button><button type="button" data-wso="wk">同孕周</button>' +
|
||||
'<span class="wcm-rule" id="wcmRule"></span></div>' +
|
||||
'<div class="wcm-ls" id="wcmLs"><p class="wcm-e">留言加载中…</p></div><button type="button" class="wcm-more" id="wcmMore" hidden>再看看</button></div>';
|
||||
box.querySelector('.wcm-in').insertBefore(bar, box.querySelector('.wcm-hd'));
|
||||
page.appendChild(box);
|
||||
@@ -31,7 +35,7 @@
|
||||
var t = new Date(ts + 8 * 3600e3); return (t.getUTCMonth() + 1) + '-' + t.getUTCDate();
|
||||
};
|
||||
|
||||
var SIM = null, REAL = null, ALL = [], shown = 0, loading = false;
|
||||
var SIM = null, REAL = null, ALL = [], shown = 0, loading = false, SORT = 'hot', STAR_N = 3;
|
||||
function load() {
|
||||
if (loading || SIM) return; loading = true;
|
||||
fetch('guangchang/cm.json?v=26110612').then(function (r) { return r.json(); }).then(function (j) {
|
||||
@@ -45,42 +49,107 @@
|
||||
}).catch(function () { SIM = []; build(); });
|
||||
fetch('/api/mp/public/mama-box/plaza/comments?kind=post&target=' + encodeURIComponent(PID) + (OID ? '&openid=' + encodeURIComponent(OID) : ''))
|
||||
.then(function (r) { return r.json(); }).then(function (j) {
|
||||
var R = j && j.code === 200 && j.result || {}, top = function (c) { return !c.parent; };
|
||||
REAL = (R.list || []).filter(top).map(function (c) { return { key: 'r' + c.id, n: c.nickname || '孕妈', av: c.avatar || 'assets/img/mom/k01.jpg', s: c.stage, t: c.text, likes: 0, ts: c.at, r: [] }; })
|
||||
.concat((R.mine || []).filter(function (c) { return top(c) && c.status !== 'rejected'; }).map(function (c) {
|
||||
return { key: 'r' + c.id, n: '我', me: 1, av: c.avatar || 'assets/img/mom/k01.jpg', s: c.stage, t: c.text, likes: 0, ts: c.at, st: c.status, r: [] }; }))
|
||||
.sort(function (a, b) { return b.ts - a.ts; });
|
||||
var R = j && j.code === 200 && j.result || {};
|
||||
if (R.starLikes) STAR_N = R.starLikes;
|
||||
REAL = (R.list || []).filter(top).map(realOf).concat((R.mine || []).filter(function (c) { return top(c) && c.status !== 'rejected'; }).map(function (c) {
|
||||
var o = realOf(c); o.n = '我'; o.me = 1; o.st = c.status; return o; }));
|
||||
build();
|
||||
}).catch(function () { REAL = []; build(); });
|
||||
}
|
||||
var top = function (c) { return !c.parent; };
|
||||
function realOf(c) {
|
||||
return { key: 'r' + c.id, id: c.id, real: 1, n: c.nickname || '孕妈', av: c.avatar || 'assets/img/mom/k01.jpg', s: c.stage, t: c.text,
|
||||
likes: c.likes || 0, liked: !!c.liked, star: !!c.star, ts: c.at, r: [] };
|
||||
}
|
||||
var wkOf = function (c) { var m = /孕\s*(\d+)\s*周/.exec(c.s || ''); return m ? +m[1] : 0; };
|
||||
var likesOf = function (c) { return c.real ? c.likes : c.likes + (LIKE[c.key] ? 1 : 0); };
|
||||
var likedOf = function (c) { return c.real ? c.liked : !!LIKE[c.key]; };
|
||||
/* 最热:赞多的在前(挂了星的真实留言再往前提);最新:真实留言按时间,模拟的接在后面;同孕周:和我差 ±1 周的,按最热 */
|
||||
function sorted() {
|
||||
var mine = REAL.filter(function (c) { return c.me; }), rest = REAL.filter(function (c) { return !c.me; }).concat(SIM);
|
||||
var hot = function (a, b) { return (b.star - a.star) || (likesOf(b) - likesOf(a)); };
|
||||
if (SORT === 'new') rest = rest.filter(function (c) { return c.real; }).sort(function (a, b) { return b.ts - a.ts; }).concat(SIM);
|
||||
else if (SORT === 'wk') {
|
||||
var w = +window.MB_WH_WEEK || 0;
|
||||
rest = w ? rest.filter(function (c) { var k = wkOf(c); return k && Math.abs(k - w) <= 1; }) : [];
|
||||
rest.sort(hot);
|
||||
} else rest.sort(hot);
|
||||
return mine.sort(function (a, b) { return b.ts - a.ts; }).concat(rest);
|
||||
}
|
||||
function build() {
|
||||
if (!SIM || !REAL) return;
|
||||
ALL = REAL.concat(SIM); shown = 0; ls.innerHTML = '';
|
||||
document.getElementById('wcmN').textContent = ALL.length ? ALL.length + ' 条' : '';
|
||||
if (!ALL.length) { ls.innerHTML = '<p class="wcm-e">还没有留言</p>'; more.hidden = true; return; }
|
||||
ALL = sorted(); shown = 0; ls.innerHTML = '';
|
||||
document.getElementById('wcmN').textContent = (REAL.length + SIM.length) ? (REAL.length + SIM.length) + ' 条' : '';
|
||||
document.getElementById('wcmRule').textContent = '赞够 ' + STAR_N + ' 个挂上星星';
|
||||
if (!ALL.length) {
|
||||
ls.innerHTML = '<p class="wcm-e">' + (SORT === 'wk' ? (window.MB_WH_WEEK ? '同孕周的妈妈还没留言,说一句吧' : '填好预产期,就能看同孕周妈妈的留言') : '还没有留言') + '</p>';
|
||||
more.hidden = true; return;
|
||||
}
|
||||
add(20);
|
||||
}
|
||||
function item(c) {
|
||||
var on = !!LIKE[c.key];
|
||||
var on = likedOf(c);
|
||||
return '<div class="wcm-it"><img class="wcm-av" src="' + esc(c.av) + '" alt="" loading="lazy" onerror="this.style.visibility=\'hidden\'">' +
|
||||
'<div class="wcm-bd"><p class="wcm-nm"><b>' + esc(c.n) + '</b>' + (c.s ? '<span>' + esc(c.s) + '</span>' : '') + (c.ts ? '<span>' + ago(c.ts) + '</span>' : '') + '</p>' +
|
||||
'<p class="wcm-tx">' + esc(c.t) + '</p>' + (c.st === 'pending' ? '<p class="wcm-st">审核中 · 通过后大家才能看到</p>' : '') +
|
||||
(c.star ? '<p class="wcm-star"><svg viewBox="0 0 20 20"><path d="M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z"/></svg>已挂上星星</p>' : '') +
|
||||
(c.r.length ? '<div class="wcm-rp">' + c.r.map(function (x) { return '<p><b>' + esc(x.n) + '</b>:' + esc(x.t) + '</p>'; }).join('') + '</div>' : '') + '</div>' +
|
||||
'<button type="button" class="wcm-lk' + (on ? ' on' : '') + '" data-wcl="' + esc(c.key) + '" aria-label="赞"><svg viewBox="0 0 24 24"><path d="M12 20.5s-7.5-4.6-7.5-10.2A4.3 4.3 0 0112 7.6a4.3 4.3 0 017.5 2.7c0 5.6-7.5 10.2-7.5 10.2z"/></svg><span>' + fmt(c.likes + (on ? 1 : 0)) + '</span></button></div>';
|
||||
(c.me ? '<span class="wcm-lk me"><svg viewBox="0 0 24 24"><path d="M12 20.5s-7.5-4.6-7.5-10.2A4.3 4.3 0 0112 7.6a4.3 4.3 0 017.5 2.7c0 5.6-7.5 10.2-7.5 10.2z"/></svg><span>' + fmt(likesOf(c)) + '</span></span></div>' :
|
||||
'<button type="button" class="wcm-lk' + (on ? ' on' : '') + '" data-wcl="' + esc(c.key) + '" aria-label="赞"><svg viewBox="0 0 24 24"><path d="M12 20.5s-7.5-4.6-7.5-10.2A4.3 4.3 0 0112 7.6a4.3 4.3 0 017.5 2.7c0 5.6-7.5 10.2-7.5 10.2z"/></svg><span>' + fmt(likesOf(c)) + '</span></button></div>');
|
||||
}
|
||||
function add(k) {
|
||||
ls.insertAdjacentHTML('beforeend', ALL.slice(shown, shown + k).map(item).join(''));
|
||||
shown = Math.min(ALL.length, shown + k); more.hidden = shown >= ALL.length;
|
||||
}
|
||||
more.addEventListener('click', function () { add(30); });
|
||||
/* 用首页同一个 #toast(app.js 的 toast 在闭包里拿不到,这里照它的写法) */
|
||||
var tT;
|
||||
function toast(t) { var x = document.getElementById('toast'); if (!x) return; x.textContent = t; x.classList.add('show'); clearTimeout(tT); tT = setTimeout(function () { x.classList.remove('show'); }, 1800); }
|
||||
function paintLk(b, c) { var on = likedOf(c); b.classList.toggle('on', on); b.querySelector('span').textContent = fmt(likesOf(c)); }
|
||||
ls.addEventListener('click', function (e) {
|
||||
var b = e.target.closest('[data-wcl]'); if (!b) return;
|
||||
var k = b.dataset.wcl, c = ALL.filter(function (x) { return x.key === k; })[0]; if (!c) return;
|
||||
if (LIKE[k]) delete LIKE[k]; else LIKE[k] = 1;
|
||||
try { localStorage.setItem(LK, JSON.stringify(LIKE)); } catch (e2) {}
|
||||
var on = !!LIKE[k]; b.classList.toggle('on', on); b.querySelector('span').textContent = fmt(c.likes + (on ? 1 : 0));
|
||||
if (on && navigator.vibrate) navigator.vibrate(8);
|
||||
if (!c.real) {
|
||||
if (LIKE[k]) delete LIKE[k]; else LIKE[k] = 1;
|
||||
try { localStorage.setItem(LK, JSON.stringify(LIKE)); } catch (e2) {}
|
||||
paintLk(b, c); if (LIKE[k] && navigator.vibrate) navigator.vibrate(8);
|
||||
return;
|
||||
}
|
||||
/* 真实留言:服务端计数,赞了就不能取消(同星星的抱抱) */
|
||||
if (!OID) { toast('登录后就能点赞啦'); return; }
|
||||
if (c.liked) { toast('你已经赞过啦'); return; }
|
||||
c.liked = true; c.likes++; paintLk(b, c); b.classList.add('pop'); setTimeout(function () { b.classList.remove('pop'); }, 500);
|
||||
if (navigator.vibrate) navigator.vibrate(8);
|
||||
fetch('/api/mp/public/mama-box/plaza/comment/like?openid=' + encodeURIComponent(OID) + '&id=' + encodeURIComponent(c.id), { method: 'POST' })
|
||||
.then(function (r) { return r.json(); }).then(function (j) {
|
||||
if (!j || j.code !== 200) throw j;
|
||||
c.likes = j.result.likes; var was = c.star; c.star = !!j.result.star; paintLk(b, c);
|
||||
if (c.star && !was) { toast('这句被赞够 ' + STAR_N + ' 次,挂上星星啦'); var it = b.closest('.wcm-it'); if (it) it.outerHTML = item(c); }
|
||||
}).catch(function (j) { c.liked = false; c.likes--; paintLk(b, c); toast((j && j.message) || '网络不太好,再点一次试试'); });
|
||||
});
|
||||
box.querySelector('.wcm-tb').addEventListener('click', function (e) {
|
||||
var b = e.target.closest('[data-wso]'); if (!b || b.dataset.wso === SORT) return;
|
||||
SORT = b.dataset.wso;
|
||||
[].forEach.call(this.querySelectorAll('[data-wso]'), function (x) { x.classList.toggle('on', x === b); });
|
||||
build();
|
||||
});
|
||||
/* 星空页「说一句」→ 发成留言(app.js 的 #whSend 调)。返回 Promise:resolve(null) = 成功,resolve('提示') = 没发出去 */
|
||||
window.MBWhCm = {
|
||||
starLikes: function () { return STAR_N; },
|
||||
post: function (text, week) {
|
||||
if (!OID) return Promise.resolve('登录后就能说一句啦');
|
||||
return fetch('/api/mp/public/mama-box/plaza/comment?openid=' + encodeURIComponent(OID), {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ kind: 'post', target: PID, title: '孕妈悄悄话 · ' + TH.name, text: text, stage: week ? '孕 ' + week + ' 周' : '' })
|
||||
}).then(function (r) { return r.json(); }).then(function (j) {
|
||||
if (!j || j.code !== 200 || !j.result) return (j && j.message) || '没发出去,再试一次';
|
||||
var o = realOf(j.result); o.n = '我'; o.me = 1; o.st = j.result.status;
|
||||
REAL = [o].concat(REAL || []);
|
||||
if (SIM) { build(); box.scrollTo({ top: box.querySelector('.wcm-hd').offsetTop + box.querySelector('.wcm-sp').offsetHeight - 80, behavior: 'smooth' }); }
|
||||
return null;
|
||||
}).catch(function () { return '网络不太好,再试一次'; });
|
||||
}
|
||||
};
|
||||
/* 11-07 用户:页面滑不动 —— 原来垫高层 pointer-events:none,手机上按在星空上滑,滚动不归 #wcm。
|
||||
现在整层都接触摸(按哪都能滑),点在垫高层上就把点击转给底下的星星 / 顶栏按钮 / 内容卡 */
|
||||
var sp = box.querySelector('.wcm-sp');
|
||||
|
||||
Reference in New Issue
Block a user