修复全页面走查发现的问题(第四批:精准查找、隐私锁定、搜索防抖)

精准查找搜不到手机号(第5章 5.5)
- 一直写死 type=exact_account,而该分支只比对 profile.public_account / u.username / u.id
  (tenant_adapter.go:9170-9172),**不匹配 u.phone**。参考图 5.5 里输的恰恰是手机号,
  这个场景必然搜不到。改为按输入形态分流:11 位 1 开头走 type=phone。
- phone 分支服务端不做 E.164 归一化(只有模糊的 account 分支才调 normalizeUserPhone),
  库里存的是 +86 开头,所以裸号码由前端补区号——注册流程本来也是这么拼的。
- 已用真实后端验证请求正确路由为 q=%2B8613800001188&type=phone。

隐私设置忽略 editable(第10章 10.12/10.13)
- 后端 userPrivacyPayload 返回 editable{add_me_methods, profile_visibility,
  last_seen_visibility, read_receipts} 表达"企业强制策略锁死了这一项",前端完全没读。
  被锁的开关照样能点,点了 400 再回滚,用户只看到开关自己弹回去。现在禁用并给出说明文案,
  同时补 .personal-switch:disabled 的视觉反馈(否则看不出为什么点不动)。

搜索防抖
- 新增共享的 useDebouncedValue,用在三处真正发服务端请求的搜索:收藏、会话内检索、
  群列表、群文件。原来 query 直接进 effect 依赖,每敲一个字符发一次请求;群列表更糟,
  reload 对三个分类各发一次,一个字符就是 3 个请求。而且返回顺序没有保证,后发的短前缀
  请求可能后到、把正确结果覆盖掉。
- 实测:群列表输入 4 个字,请求数从 12 降到 3。
- FavoriteShare 那处搜索是本地过滤 chatList、不发请求,没有动。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-03 18:56:41 +08:00
co-authored by Claude Opus 5
parent c9b8125013
commit 35d3e1cf6b
8 changed files with 144 additions and 39 deletions
+14 -8
View File
@@ -475,19 +475,23 @@ function PrivacySettings({onToast}) {
if (on.length === 4) return '全部允许';
return on.length ? on.join('、') : '全部关闭';
};
// 后端 userPrivacyPayload 会返回 editable{add_me_methods, profile_visibility, last_seen_visibility,
// read_receipts},用来表达"企业强制策略锁死了这一项"。原来完全没读这个字段,被锁的开关照样能点,
// 点了服务端 400 再回滚,用户只看到开关自己弹回去、不知道为什么。
const locked = key => !!(privacy && privacy.editable && privacy.editable[key] === false);
return <div className="personal-page"><Header title="隐私设置" back="android-settings" /><main className="privacy-list">
<section><MenuRow title="添加我的方式" value={methodSummary()} route="friend-discovery-settings" /></section>
{privacy ? <section>
<div><span><strong>显示消息已读状态</strong><small>关闭后你也无法查看对方的已读状态</small></span>
<Toggle checked={!!privacy.read_receipts} onChange={v => update('read_receipts', v)} /></div>
<div><span><strong>展示最后在线时间</strong><small>仅向联系人显示</small></span>
<div><span><strong>显示消息已读状态</strong><small>{locked('read_receipts') ? '该项由企业统一设置,不可修改' : '关闭后你也无法查看对方的已读状态'}</small></span>
<Toggle checked={!!privacy.read_receipts} disabled={locked('read_receipts')} onChange={v => update('read_receipts', v)} /></div>
<div><span><strong>展示最后在线时间</strong><small>{locked('last_seen_visibility') ? '该项由企业统一设置,不可修改' : '仅向联系人显示'}</small></span>
{/* Backend's validVisibility only accepts everyone/friends/nobody -- 'contacts'/'hidden' were
rejected by the PATCH validator outright (every toggle attempt 400'd and silently reverted),
and even a lenient backend would enforce the opposite of what turning this "on" should do
(imCanViewLastSeen's default case treats any unrecognized value as hidden). */}
<Toggle checked={privacy.last_seen_visibility !== 'nobody'} onChange={v => update('last_seen_visibility', v ? 'friends' : 'nobody')} /></div>
<div><span><strong>允许好友查看我的资料</strong><small>关闭后仅你自己可见</small></span>
<Toggle checked={privacy.profile_visibility !== 'nobody'} onChange={v => update('profile_visibility', v ? 'friends' : 'nobody')} /></div>
<Toggle checked={privacy.last_seen_visibility !== 'nobody'} disabled={locked('last_seen_visibility')} onChange={v => update('last_seen_visibility', v ? 'friends' : 'nobody')} /></div>
<div><span><strong>允许好友查看我的资料</strong><small>{locked('profile_visibility') ? '该项由企业统一设置,不可修改' : '关闭后仅你自己可见'}</small></span>
<Toggle checked={privacy.profile_visibility !== 'nobody'} disabled={locked('profile_visibility')} onChange={v => update('profile_visibility', v ? 'friends' : 'nobody')} /></div>
</section> : null}
</main></div>;
}
@@ -505,11 +509,13 @@ function FriendDiscoverySettings({onToast}) {
.catch(err => { onToast(err.message || '保存失败'); setPrivacy(prev); writeCachedData('me-privacy', prev); });
};
const rows = [{key: 'account', title: '账号'}, {key: 'phone', title: '手机号'}, {key: 'qr', title: '二维码'}, {key: 'group', title: '群聊'}];
// 同 PrivacySettings:企业强制策略锁死时不让点,避免"能点但必然 400 再弹回"。
const methodsLocked = !!(privacy && privacy.editable && privacy.editable.add_me_methods === false);
return <div className="personal-page"><Header title="添加我的方式" back="privacy-settings" /><main className="privacy-list">
<p className="privacy-note">关闭某种方式后,其他人将无法通过该渠道搜索或添加你。</p>
<p className="privacy-note">{methodsLocked ? '添加方式由企业统一设置,不可修改。' : '关闭某种方式后,其他人将无法通过该渠道搜索或添加你。'}</p>
{privacy ? <section>{rows.map(row => <div key={row.key}>
<span><strong>可通过{row.title}添加我</strong></span>
<Toggle checked={!!(privacy.add_me_methods || {})[row.key]} onChange={v => update(row.key, v)} />
<Toggle checked={!!(privacy.add_me_methods || {})[row.key]} disabled={methodsLocked} onChange={v => update(row.key, v)} />
</div>)}</section> : null}
</main></div>;
}