接线1.10实名认证提醒弹窗到真实消息首页(第1章遗留项,此前一直是孤立demo路由)
auth-onboarding-view.jsx 的 RealnameReminder 组件此前是完全孤立的demo:自己画一套 假会话列表背景,只能靠手动改hash访问,没有任何真实入口会导航过去。核对参考图 "实名认证(安卓).png"确认过的弹窗文案本身是对的,只是从来没有接上真实触发条件。 抽出弹窗内容(不带假背景)做成 RealnameReminderModal,挂到真实 MessageHome 挂载时 按 bootstrap.security.realname 的真实状态判断——这个字段本来就在 MessageHome 已经 在用的同一个 bootstrap 请求里,不需要额外接口调用。 参考图本身画的是不可跳过的单按钮强制态,但真实核对 im_be 源码发现这只是 required:true 那个分支的样子;这个环境的真实配置(也是目前后端唯一会返回的默认值) 是 required:false / allow_skip:true。如果照抄参考图做成完全关不掉的弹层,等于把 "实名认证目前是可选的"这个真实产品配置,实现成挡住所有人使用消息功能的强制墙—— 按这两个字段分叉:允许跳过时加一个参考图没画、但后端字段明确允许的关闭按钮;且 遵守后端给的 remind_after(当前值24h),关闭后在这个时长内不重复弹,不是每次进 消息首页都弹一次。 联调中发现关闭按钮初版样式是个跟全局 button 选一样宽的圆角方块(漏了覆盖 min-width/box-shadow/margin 这几个全局 button 规则),已修正为纯图标按钮。 真实验证(账号 asn494180,真实 status:"unverified"):弹窗内容与参考图逐句一致; 点击关闭按钮弹窗消失且 localStorage 正确记录时间戳;刷新页面重新连接后,24小时 窗口内弹窗不会重复出现;未清除记录时保持不弹,符合"提醒而非骚扰"的预期。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d21d046443
commit
542aca3dc2
@@ -6408,6 +6408,24 @@ div.toast.show {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.auth-modal {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.auth-modal-close {
|
||||
position: absolute;
|
||||
top: 0.6rem;
|
||||
right: 0.6rem;
|
||||
min-width: 0;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 0.25rem;
|
||||
color: #9aa1ab;
|
||||
background: none;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.auth-modal h2 {
|
||||
margin: 0 0 1rem;
|
||||
font-size: 1.45rem;
|
||||
|
||||
@@ -204,6 +204,30 @@ function QuickMenu({onClose, onAction, permissions}) {
|
||||
</div>;
|
||||
}
|
||||
|
||||
// 1.10 实名认证提醒:docs/images/实名认证(安卓).png 显示的是不可跳过的强制态(单按钮"去实名
|
||||
// 认证",没有关闭/稍后处理)——但那张图对应的是 bootstrap.security.realname.required===true 这个
|
||||
// 分支,这个环境(以及目前后端唯一会返回的默认配置)实际是 required:false / allow_skip:true。
|
||||
// 照抄参考图做成完全关不掉的弹层,等于把"实名认证目前是可选的"这个真实配置,实现成挡住所有人的
|
||||
// 强制墙——所以按这两个字段分叉:允许跳过时加一个参考图没画但后端明确允许的关闭按钮;且遵守
|
||||
// remind_after,跳过后在这个时长内不重复弹(不是每次进消息首页都弹一次)。
|
||||
function parseRemindAfter(text) {
|
||||
const m = /^(\d+)\s*([hdm])$/i.exec(String(text || '').trim());
|
||||
if (!m) return 24 * 60 * 60 * 1000;
|
||||
const n = Number(m[1]);
|
||||
return m[2].toLowerCase() === 'd' ? n * 86400000 : m[2].toLowerCase() === 'h' ? n * 3600000 : n * 60000;
|
||||
}
|
||||
|
||||
function RealnameReminderModal({allowSkip, onDismiss}) {
|
||||
return <div className="auth-modal-backdrop" onClick={allowSkip ? onDismiss : undefined}>
|
||||
<div className="auth-modal" onClick={e => e.stopPropagation()}>
|
||||
{allowSkip ? <button type="button" className="auth-modal-close" aria-label="关闭" onClick={onDismiss}><Icon>close</Icon></button> : null}
|
||||
<h2>实名认证提醒</h2>
|
||||
<p>根据国家法律法规要求,您需先进行实名认证才能使用该项 APP</p>
|
||||
<button className="auth-primary" onClick={() => navigate('realname')}>去实名认证</button>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function MessageHome({view, connected, chatList, myUserId, onTopicSelected, onOpenScanner}) {
|
||||
const [query, setQuery] = React.useState('');
|
||||
const [menuOpen, setMenuOpen] = React.useState(view === 'messages-menu');
|
||||
@@ -217,6 +241,19 @@ function MessageHome({view, connected, chatList, myUserId, onTopicSelected, onOp
|
||||
const filtered = conversations.filter(item => `${item.title}${item.summary}`.toLowerCase().includes(query.trim().toLowerCase()));
|
||||
const frequent = frequentContacts(chatList);
|
||||
|
||||
const realname = bootstrap && bootstrap.security && bootstrap.security.realname;
|
||||
const [reminderDismissed, setReminderDismissed] = React.useState(false);
|
||||
const dismissKey = myUserId ? 'realname-reminder-dismissed:' + myUserId : null;
|
||||
let lastDismissedAt = 0;
|
||||
try { lastDismissedAt = dismissKey ? Number(localStorage.getItem(dismissKey) || 0) : 0; } catch (_) { /* ignore */ }
|
||||
const withinSnooze = lastDismissedAt > 0 && (Date.now() - lastDismissedAt) < parseRemindAfter(realname && realname.remind_after);
|
||||
const showRealnameReminder = !!realname && !reminderDismissed && !withinSnooze &&
|
||||
(realname.status === 'unverified' || realname.status === 'rejected');
|
||||
const dismissRealnameReminder = () => {
|
||||
if (dismissKey) { try { localStorage.setItem(dismissKey, String(Date.now())); } catch (_) { /* ignore */ } }
|
||||
setReminderDismissed(true);
|
||||
};
|
||||
|
||||
const selectConversation = item => {
|
||||
if (item.system) {
|
||||
navigate('system-messages');
|
||||
@@ -269,6 +306,7 @@ function MessageHome({view, connected, chatList, myUserId, onTopicSelected, onOp
|
||||
<BottomNavigation />
|
||||
{menuOpen ? <QuickMenu onClose={() => setMenuOpen(false)} onAction={runQuickAction} permissions={bootstrap && bootstrap.permissions} /> : null}
|
||||
<Toast message={toast} onDone={() => setToast('')} />
|
||||
{showRealnameReminder ? <RealnameReminderModal allowSkip={!!realname.allow_skip} onDismiss={dismissRealnameReminder} /> : null}
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
||||
+57
-1
@@ -13036,6 +13036,42 @@ function QuickMenu({
|
||||
}, void 0, false)
|
||||
}, void 0, false);
|
||||
}
|
||||
function parseRemindAfter(text) {
|
||||
const m = /^(\d+)\s*([hdm])$/i.exec(String(text || '').trim());
|
||||
if (!m) return 24 * 60 * 60 * 1000;
|
||||
const n = Number(m[1]);
|
||||
return m[2].toLowerCase() === 'd' ? n * 86400000 : m[2].toLowerCase() === 'h' ? n * 3600000 : n * 60000;
|
||||
}
|
||||
function RealnameReminderModal({
|
||||
allowSkip,
|
||||
onDismiss
|
||||
}) {
|
||||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
className: "auth-modal-backdrop",
|
||||
onClick: allowSkip ? onDismiss : undefined,
|
||||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
className: "auth-modal",
|
||||
onClick: e => e.stopPropagation(),
|
||||
children: [allowSkip ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("button", {
|
||||
type: "button",
|
||||
className: "auth-modal-close",
|
||||
"aria-label": "关闭",
|
||||
onClick: onDismiss,
|
||||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)(Icon, {
|
||||
children: "close"
|
||||
}, void 0, false)
|
||||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("h2", {
|
||||
children: "实名认证提醒"
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("p", {
|
||||
children: "根据国家法律法规要求,您需先进行实名认证才能使用该项 APP"
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("button", {
|
||||
className: "auth-primary",
|
||||
onClick: () => navigate('realname'),
|
||||
children: "去实名认证"
|
||||
}, void 0, false)]
|
||||
}, void 0, true)
|
||||
}, void 0, false);
|
||||
}
|
||||
function MessageHome({
|
||||
view,
|
||||
connected,
|
||||
@@ -13052,6 +13088,23 @@ function MessageHome({
|
||||
const conversations = entryConversations(bootstrap).concat(normalizeConversations(chatList, myUserId));
|
||||
const filtered = conversations.filter(item => `${item.title}${item.summary}`.toLowerCase().includes(query.trim().toLowerCase()));
|
||||
const frequent = frequentContacts(chatList);
|
||||
const realname = bootstrap && bootstrap.security && bootstrap.security.realname;
|
||||
const [reminderDismissed, setReminderDismissed] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(false);
|
||||
const dismissKey = myUserId ? 'realname-reminder-dismissed:' + myUserId : null;
|
||||
let lastDismissedAt = 0;
|
||||
try {
|
||||
lastDismissedAt = dismissKey ? Number(localStorage.getItem(dismissKey) || 0) : 0;
|
||||
} catch (_) {}
|
||||
const withinSnooze = lastDismissedAt > 0 && Date.now() - lastDismissedAt < parseRemindAfter(realname && realname.remind_after);
|
||||
const showRealnameReminder = !!realname && !reminderDismissed && !withinSnooze && (realname.status === 'unverified' || realname.status === 'rejected');
|
||||
const dismissRealnameReminder = () => {
|
||||
if (dismissKey) {
|
||||
try {
|
||||
localStorage.setItem(dismissKey, String(Date.now()));
|
||||
} catch (_) {}
|
||||
}
|
||||
setReminderDismissed(true);
|
||||
};
|
||||
const selectConversation = item => {
|
||||
if (item.system) {
|
||||
navigate('system-messages');
|
||||
@@ -13162,7 +13215,10 @@ function MessageHome({
|
||||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)(Toast, {
|
||||
message: toast,
|
||||
onDone: () => setToast('')
|
||||
}, void 0, false)]
|
||||
}, void 0, false), showRealnameReminder ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)(RealnameReminderModal, {
|
||||
allowSkip: !!realname.allow_skip,
|
||||
onDismiss: dismissRealnameReminder
|
||||
}, void 0, false) : null]
|
||||
}, void 0, true);
|
||||
}
|
||||
function MessageActions() {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user