Sponsor to downline messaging: lineage view, daily broadcast (email+inbox), login modal
- Downline resolver (3 levels); email visible for directs only, username+ID deeper - Broadcast composer (rich editor) to directs or full downline, 1/day cap, sends email + on-site inbox - Upline messages inbox in My line; unmissable sign-in modal for unread, ack marks read - messages.js dual-mode store; sponsor_messages table; sanitizeRich reused Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+25
-1
@@ -261,6 +261,30 @@ async function setUsername(email, username) {
|
||||
async function setMemberId(email, id) { return impl().setMemberId(normEmail(email), Number(id) || 0); }
|
||||
async function namesForMembers(ids) { return impl().namesForMembers([...new Set(ids)].filter(n => n > 0)); }
|
||||
async function listByReferrer(refs) { return impl().listByReferrer(refs || []); }
|
||||
// the tokens a member could have been joined under (code, username, or numeric id)
|
||||
function refTokens(a) {
|
||||
return [a.code, a.username, a.memberId ? String(a.memberId) : null].filter(Boolean).map(String);
|
||||
}
|
||||
// walk the downline breadth-first to `depth` levels. Returns
|
||||
// [{ level, members:[pub...] }]; emails are included on pub but the CALLER
|
||||
// decides who may see them (directs only, per product rule).
|
||||
async function downline(email, depth = 3) {
|
||||
const root = await byEmail(email);
|
||||
if (!root) return [];
|
||||
const seen = new Set([String(root.email)]);
|
||||
const levels = [];
|
||||
let frontier = [root];
|
||||
for (let lvl = 1; lvl <= depth; lvl++) {
|
||||
const toks = [...new Set(frontier.flatMap(refTokens))];
|
||||
if (!toks.length) break;
|
||||
const kids = (await listByReferrer(toks)).filter(k => !seen.has(String(k.email)));
|
||||
if (!kids.length) break;
|
||||
kids.forEach(k => seen.add(String(k.email)));
|
||||
levels.push({ level: lvl, members: kids });
|
||||
frontier = kids;
|
||||
}
|
||||
return levels;
|
||||
}
|
||||
async function linkWallet(email, address) {
|
||||
const a = normAddr(address);
|
||||
if (!/^0x[0-9a-f]{40}$/.test(a)) return { error: 'Bad wallet address.' };
|
||||
@@ -269,6 +293,6 @@ async function linkWallet(email, address) {
|
||||
async function count() { return impl().count(); }
|
||||
|
||||
module.exports = { init, signup, login, ensure, byEmail, byAddress, byCode, byUsername,
|
||||
setUsername, setMemberId, namesForMembers, listByReferrer, linkWallet, count,
|
||||
setUsername, setMemberId, namesForMembers, listByReferrer, downline, linkWallet, count,
|
||||
setLineBanner: (e, b, t) => impl().setLineBanner(String(e || '').toLowerCase(), b, t),
|
||||
byMemberId: id => impl().byMemberId(id) };
|
||||
|
||||
Reference in New Issue
Block a user