修复置顶/免打扰开关未接后端、好友二维码死按钮(第5章复测发现)

置顶聊天/消息免打扰:原来是纯本地 useState,onChange 只切一下布尔值,从不调用任何接口——
GET/PATCH /conversations/{topic}/settings 早就封装好了,只是零调用点。打开设置页永远显示
"关",点了也只是那次会话内生效、离开页面就丢,和会话列表真正读取的置顶/免打扰状态是两套
毫不相关的东西。

实测确认真实字段:{pinned, mute_notifications, version},PATCH 需要带 version 做乐观锁。
接入 getConversationSettings/updateConversationSettings,失败时回滚开关视觉状态。
已用真实账号验证:点击后发出真实 PATCH 请求,带正确的 version。

好友个人主页"二维码"图标:参考图里是独立一行(不是贴在头像卡片里的小图标),点击生成这个
好友的二维码名片,对应接口 createUserQRCode() 早就封装好了,原来这个图标没有任何 onClick,
是个看着能点、点了没反应的死装饰。新增 friend-qr 路由和 FriendQr 组件(复用 QrPage 的结构),
CSS 复用了 .people-settings 那套现成的行样式,不是新造一套。
已用真实账号验证:点击后正确跳转、生成出真实二维码。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-04 13:43:22 +08:00
co-authored by Claude Sonnet 5
parent d3d7e11b08
commit 0dcef54fed
5 changed files with 149 additions and 15 deletions
+2 -1
View File
@@ -9350,7 +9350,8 @@ div.toast.show {
.group-governance section > button,
.group-governance section > div,
.group-profile > button,
.group-profile > label {
.group-profile > label,
.people-profile-qr-row {
display: flex;
align-items: center;
width: 100%;
+51 -5
View File
@@ -6,7 +6,8 @@ import HashNavigation from '../lib/navigation.js';
import {
getFriends, getFriendRequests, createFriendRequest, acceptFriendRequest, rejectFriendRequest,
deleteFriend, updateFriendRemark, searchUsers, getUserProfile, createMyQRCode, getBlocks,
blockUser, unblockUser, getFileUrl, getBootstrap
blockUser, unblockUser, getFileUrl, getBootstrap, getConversationSettings, updateConversationSettings,
createUserQRCode
} from '../lib/im-api.js';
import { readCachedData, writeCachedData, useCachedData } from '../lib/use-cached-data.js';
@@ -19,7 +20,7 @@ const navigate = (view, uid) => {
};
export const CONTACT_MANAGEMENT_VIEWS = [
'address-book', 'friend-requests', 'add-friend', 'my-friend-qr', 'friend-search-result',
'address-book', 'friend-requests', 'add-friend', 'my-friend-qr', 'friend-qr', 'friend-search-result',
'friend-verification', 'friend-profile', 'group-member-profile', 'friend-settings',
'friend-settings-blocked', 'friend-delete-confirm', 'block-list', 'block-confirm',
'clear-chat-confirm'
@@ -313,6 +314,27 @@ function QrPage({onToast}) {
</main></div>;
}
function FriendQr({uid, onToast}) {
const [payload, setPayload] = React.useState(null);
const [profile, setProfile] = React.useState(null);
React.useEffect(() => {
if (!uid) return;
createUserQRCode(uid).then(data => { setPayload(data.qr_payload); setProfile(data.display_profile); }).catch(() => onToast('二维码生成失败'));
}, [uid]);
const save = () => {
const canvas = document.querySelector('.people-qrcode canvas');
if (!canvas) { onToast('二维码尚未生成'); return; }
const a = document.createElement('a');
a.href = canvas.toDataURL('image/png');
a.download = '好友二维码.png';
a.click();
onToast('二维码已保存到相册');
};
return <div className="people-page people-qr-page"><Header title="二维码" back="friend-profile" backUid={uid} /><main><QrCard payload={payload} profile={profile} loading={!payload} />
<div className="people-qr-actions"><button type="button" onClick={save}><Icon>download</Icon><span>保存到相册</span></button></div>
</main></div>;
}
function Verification({uid, onToast}) {
// Same seed-then-best-effort-refresh as Profile -- reached right after it (via its "添加好友"
// button), so the cache this reads is the one Profile itself just kept warm.
@@ -391,7 +413,13 @@ function Profile({uid, friend, onTopicSelected, onToast}) {
<main className="people-profile">
<section><Avatar person={profile} /><div><h2>{profile.display_name}</h2>
{profile.public_account ? <p>用户名:{profile.public_account}</p> : null}
{profile.region ? <p>地区:{profile.region}</p> : null}</div><Icon>qr_code</Icon></section>
{profile.region ? <p>地区:{profile.region}</p> : null}</div></section>
{/* 参考图 好友个人主页.png 里"二维码"是独立一行(不是贴在头像卡片里的小图标),点击生成
这个好友的二维码名片。对应接口 createUserQRCode() 早就封装好了,原来这个图标没有任何
onClick,是个看着能点、点了没反应的死装饰。 */}
<button type="button" className="people-profile-qr-row" onClick={() => navigate('friend-qr', uid)}>
<span>二维码</span><Icon>qr_code</Icon>
</button>
{/* 后端存的是 male/female/other 枚举(personal-settings-view.jsx:203 的 GENDER_OPTIONS 写进去的),
原来直接原样输出,资料页上显示的是英文 "male"。 */}
<dl>{profile.gender ? <div><dt>性别</dt><dd>{GENDER_LABELS[profile.gender] || profile.gender}</dd></div> : null}
@@ -415,6 +443,12 @@ function Settings({uid, initialBlocked, pinBlocked, onToast}) {
// blank instead of just stuck like Profile, but no less wrong.
const [profile, setProfile] = React.useState(() => (uid ? readCachedData('profile-seed:' + uid) : null));
const [blocked, setBlocked] = React.useState(initialBlocked);
// 置顶/免打扰原来是纯本地 useState,onChange 只是切一下布尔值,从不调用任何接口——
// GET/PATCH /conversations/{topic}/settings 早就封装好了(getConversationSettings/
// updateConversationSettings),只是零调用点。实测接口返回真实的 {pinned, mute_notifications,
// version},PATCH 需要带 version 做乐观锁。打开设置页永远显示"关",点了也只是那次会话内
// 生效、离开页面就丢,和会话列表真正读取的置顶/免打扰状态是两套毫不相关的东西。
const [settingsVersion, setSettingsVersion] = React.useState(null);
const [pinned, setPinned] = React.useState(false);
const [muted, setMuted] = React.useState(false);
const [editingRemark, setEditingRemark] = React.useState(false);
@@ -422,6 +456,9 @@ function Settings({uid, initialBlocked, pinBlocked, onToast}) {
const [remarkDraft, setRemarkDraft] = React.useState('');
React.useEffect(() => {
if (!uid) return;
getConversationSettings(uid).then(s => {
setPinned(!!s.pinned); setMuted(!!s.mute_notifications); setSettingsVersion(s.version);
}).catch(() => {});
// pinBlocked:确认弹窗把这个页面当背景渲染,并用 initialBlocked 摆出"操作之后"的样子。
// 这时不能让这次拉取回来的真实关系(还是 friend)把它改回去——原来会导致弹窗一弹出来,
// 背后的拉黑开关自己跳回"未拉黑",同时"删除好友"红条又冒出来,跟参考图不符也跟弹窗
@@ -450,8 +487,16 @@ function Settings({uid, initialBlocked, pinBlocked, onToast}) {
<span>查看聊天记录</span><Icon>chevron_right</Icon>
</button>
<div><span>消息传输方式</span><small>已启动加密传输</small></div></section>
<section><div><span>置顶聊天</span><Toggle checked={pinned} onChange={setPinned} /></div>
<div><span>消息免打扰</span><Toggle checked={muted} onChange={setMuted} /></div>
<section><div><span>置顶聊天</span><Toggle checked={pinned} onChange={value => {
const prev = pinned; setPinned(value);
updateConversationSettings(uid, {pinned: value, version: settingsVersion}).then(s => setSettingsVersion(s.version))
.catch(err => { setPinned(prev); onToast(err.message || '设置失败'); });
}} /></div>
<div><span>消息免打扰</span><Toggle checked={muted} onChange={value => {
const prev = muted; setMuted(value);
updateConversationSettings(uid, {mute_notifications: value, version: settingsVersion}).then(s => setSettingsVersion(s.version))
.catch(err => { setMuted(prev); onToast(err.message || '设置失败'); });
}} /></div>
<button type="button" onClick={() => navigate('clear-chat-confirm', uid)}><span>清空聊天记录</span><Icon>chevron_right</Icon></button>
{editingRemark ?
<div className="people-remark-edit"><input value={remarkDraft} maxLength="20" onChange={e => setRemarkDraft(e.target.value)} autoFocus /><button type="button" onClick={saveRemark}>保存</button></div> :
@@ -532,6 +577,7 @@ export default function ContactManagementView({view, hashParams, myUserId, tinod
else if (view === 'friend-requests') content = <FriendRequests onToast={setToast} />;
else if (view === 'add-friend' || view === 'friend-search-result') content = <AddFriend result={view === 'friend-search-result'} onToast={setToast} onOpenScanner={onOpenScanner} />;
else if (view === 'my-friend-qr') content = <QrPage onToast={setToast} />;
else if (view === 'friend-qr') content = <FriendQr uid={uid} onToast={setToast} />;
else if (view === 'friend-verification') content = <Verification uid={uid} onToast={setToast} />;
else if (view === 'friend-profile' || view === 'group-member-profile') content = <Profile uid={uid} friend={view === 'friend-profile'} onTopicSelected={onTopicSelected} onToast={setToast} />;
else if (view === 'friend-settings' || view === 'friend-settings-blocked') content = <Settings uid={uid} initialBlocked={view === 'friend-settings-blocked'} onToast={setToast} />;
+2 -2
View File
@@ -118,7 +118,7 @@ const SIDE_PANEL_VIEWS = ['acc_general','archive','blocked','contacts','cred','c
'media-uploading','media-gallery','media-retry','upload-file','file-message',
'message-multiselect','favorite-success','forward-select','forward-multiple',
'forward-individual','forward-bundle',
'address-book','friend-requests','add-friend','my-friend-qr','friend-search-result',
'address-book','friend-requests','add-friend','my-friend-qr','friend-qr','friend-search-result',
'friend-verification','friend-profile','group-member-profile','friend-settings',
'friend-settings-blocked','friend-delete-confirm','block-list','block-confirm','clear-chat-confirm',
'group-list','group-empty','group-invite','group-events','group-join-requests',
@@ -2522,7 +2522,7 @@ class TinodeWeb extends React.Component {
'media-uploading', 'media-gallery', 'media-retry', 'upload-file', 'file-message',
'message-multiselect', 'favorite-success', 'forward-select', 'forward-multiple',
'forward-individual', 'forward-bundle'].includes(activeProductView);
const chapterFiveActive = ['address-book', 'friend-requests', 'add-friend', 'my-friend-qr',
const chapterFiveActive = ['address-book', 'friend-requests', 'add-friend', 'my-friend-qr', 'friend-qr',
'friend-search-result', 'friend-verification', 'friend-profile', 'group-member-profile',
'friend-settings', 'friend-settings-blocked', 'friend-delete-confirm', 'block-list',
'block-confirm', 'clear-chat-confirm'].includes(activeProductView);
+93 -6
View File
@@ -7380,7 +7380,7 @@ const navigate = (view, uid) => {
hash = uid ? _lib_navigation_js__WEBPACK_IMPORTED_MODULE_3__["default"].addUrlParam(hash, 'uid', uid) : _lib_navigation_js__WEBPACK_IMPORTED_MODULE_3__["default"].removeUrlParam(hash, 'uid');
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_3__["default"].navigateTo(hash);
};
const CONTACT_MANAGEMENT_VIEWS = ['address-book', 'friend-requests', 'add-friend', 'my-friend-qr', 'friend-search-result', 'friend-verification', 'friend-profile', 'group-member-profile', 'friend-settings', 'friend-settings-blocked', 'friend-delete-confirm', 'block-list', 'block-confirm', 'clear-chat-confirm'];
const CONTACT_MANAGEMENT_VIEWS = ['address-book', 'friend-requests', 'add-friend', 'my-friend-qr', 'friend-qr', 'friend-search-result', 'friend-verification', 'friend-profile', 'group-member-profile', 'friend-settings', 'friend-settings-blocked', 'friend-delete-confirm', 'block-list', 'block-confirm', 'clear-chat-confirm'];
function alphaInitial(name) {
const ch = (name || '').trim()[0] || '#';
if (/[A-Za-z]/.test(ch)) {
@@ -7978,6 +7978,57 @@ function QrPage({
}, void 0, true)]
}, void 0, true);
}
function FriendQr({
uid,
onToast
}) {
const [payload, setPayload] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(null);
const [profile, setProfile] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(null);
react__WEBPACK_IMPORTED_MODULE_0___default().useEffect(() => {
if (!uid) return;
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_4__.createUserQRCode)(uid).then(data => {
setPayload(data.qr_payload);
setProfile(data.display_profile);
}).catch(() => onToast('二维码生成失败'));
}, [uid]);
const save = () => {
const canvas = document.querySelector('.people-qrcode canvas');
if (!canvas) {
onToast('二维码尚未生成');
return;
}
const a = document.createElement('a');
a.href = canvas.toDataURL('image/png');
a.download = '好友二维码.png';
a.click();
onToast('二维码已保存到相册');
};
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
className: "people-page people-qr-page",
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Header, {
title: "二维码",
back: "friend-profile",
backUid: uid
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("main", {
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(QrCard, {
payload: payload,
profile: profile,
loading: !payload
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
className: "people-qr-actions",
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", {
type: "button",
onClick: save,
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Icon, {
children: "download"
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("span", {
children: "保存到相册"
}, void 0, false)]
}, void 0, true)
}, void 0, false)]
}, void 0, true)]
}, void 0, true);
}
function Verification({
uid,
onToast
@@ -8113,7 +8164,14 @@ function Profile({
}, void 0, true) : null, profile.region ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("p", {
children: ["地区:", profile.region]
}, void 0, true) : null]
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Icon, {
}, void 0, true)]
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", {
type: "button",
className: "people-profile-qr-row",
onClick: () => navigate('friend-qr', uid),
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("span", {
children: "二维码"
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Icon, {
children: "qr_code"
}, void 0, false)]
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("dl", {
@@ -8182,6 +8240,7 @@ function Settings({
}) {
const [profile, setProfile] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(() => uid ? (0,_lib_use_cached_data_js__WEBPACK_IMPORTED_MODULE_5__.readCachedData)('profile-seed:' + uid) : null);
const [blocked, setBlocked] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(initialBlocked);
const [settingsVersion, setSettingsVersion] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(null);
const [pinned, setPinned] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(false);
const [muted, setMuted] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(false);
const [editingRemark, setEditingRemark] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(false);
@@ -8189,6 +8248,11 @@ function Settings({
const [remarkDraft, setRemarkDraft] = react__WEBPACK_IMPORTED_MODULE_0___default().useState('');
react__WEBPACK_IMPORTED_MODULE_0___default().useEffect(() => {
if (!uid) return;
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_4__.getConversationSettings)(uid).then(s => {
setPinned(!!s.pinned);
setMuted(!!s.mute_notifications);
setSettingsVersion(s.version);
}).catch(() => {});
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_4__.getUserProfile)(uid).then(p => {
(0,_lib_use_cached_data_js__WEBPACK_IMPORTED_MODULE_5__.writeCachedData)('profile-seed:' + uid, p);
setProfile(p);
@@ -8244,14 +8308,34 @@ function Settings({
children: "置顶聊天"
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Toggle, {
checked: pinned,
onChange: setPinned
onChange: value => {
const prev = pinned;
setPinned(value);
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_4__.updateConversationSettings)(uid, {
pinned: value,
version: settingsVersion
}).then(s => setSettingsVersion(s.version)).catch(err => {
setPinned(prev);
onToast(err.message || '设置失败');
});
}
}, void 0, false)]
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("span", {
children: "消息免打扰"
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Toggle, {
checked: muted,
onChange: setMuted
onChange: value => {
const prev = muted;
setMuted(value);
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_4__.updateConversationSettings)(uid, {
mute_notifications: value,
version: settingsVersion
}).then(s => setSettingsVersion(s.version)).catch(err => {
setMuted(prev);
onToast(err.message || '设置失败');
});
}
}, void 0, false)]
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", {
type: "button",
@@ -8474,6 +8558,9 @@ function ContactManagementView({
onOpenScanner: onOpenScanner
}, void 0, false);else if (view === 'my-friend-qr') content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(QrPage, {
onToast: setToast
}, void 0, false);else if (view === 'friend-qr') content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(FriendQr, {
uid: uid,
onToast: setToast
}, void 0, false);else if (view === 'friend-verification') content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Verification, {
uid: uid,
onToast: setToast
@@ -17232,7 +17319,7 @@ const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
description: 'Placeholder for the country selector'
}
});
const SIDE_PANEL_VIEWS = ['acc_general', 'archive', 'blocked', 'contacts', 'cred', 'crop', 'edit', 'general', 'newtpk', 'notif', 'register', 'reset', 'security', 'settings', 'support', 'wallpapers', 'tenant', 'login', 'enterprise-register', 'enterprise-feedback', 'identity-check', 'realname', 'realname-verified', 'realname-reminder', 'independent-password', 'messages-home', 'messages-offline', 'messages-menu', 'message-actions', 'system-messages', 'chat-inbound-request', 'chat-inbound-message', 'chat-outbound-request', 'chat-status', 'chat-friend', 'chat-media', 'chat-blocked', 'chat-deleted', 'chat-standard', 'chat-attachments', 'chat-mixed', 'chat-contact-card', 'compose-keyboard', 'mentions-select', 'mentions-complete', 'mentions-message', 'emoji-panel', 'emoji-message', 'voice-ready', 'voice-hold', 'voice-recording', 'media-uploading', 'media-gallery', 'media-retry', 'upload-file', 'file-message', 'message-multiselect', 'favorite-success', 'forward-select', 'forward-multiple', 'forward-individual', 'forward-bundle', 'address-book', 'friend-requests', 'add-friend', 'my-friend-qr', 'friend-search-result', 'friend-verification', 'friend-profile', 'group-member-profile', 'friend-settings', 'friend-settings-blocked', 'friend-delete-confirm', 'block-list', 'block-confirm', 'clear-chat-confirm', 'group-list', 'group-empty', 'group-invite', 'group-events', 'group-join-requests', 'group-new-request', 'group-announcement', 'group-summary', 'group-announcement-detail', 'group-settings', 'group-profile', 'group-avatar', 'group-nickname', 'group-description', 'group-my-nickname', 'group-role-message', 'group-qr', 'group-governance', 'group-admins', 'group-admin-add', 'group-admin-success', 'group-admin-partial', 'group-admin-removed', 'group-mute-add', 'group-muted', 'group-owner-select', 'group-owner-confirm', 'group-files', 'group-file-upload', 'favorites', 'favorites-mixed', 'favorites-files', 'favorites-text', 'favorites-voice', 'favorites-links', 'favorites-select', 'favorite-share', 'file-download', 'file-downloading', 'file-downloaded', 'history-search', 'history-files', 'history-links', 'history-images', 'history-videos', 'call-history', 'call-incoming', 'audio-call-ringing', 'audio-call-active', 'audio-call-muted', 'audio-call-speaker', 'video-call-ringing', 'video-call-camera-off', 'video-call-active', 'video-call-peer-camera-off', 'call-minimized', 'workbench', 'organization-directory', 'meeting-organizer', 'meeting-attendee', 'meeting-create', 'meeting-detail', 'daily-report-editor', 'daily-report-mine', 'daily-report-team', 'daily-report-detail', 'monthly-report-editor', 'monthly-report-list', 'monthly-report-detail', 'executive-mailbox', 'executive-mailbox-notice', 'personal-center', 'personal-profile', 'profile-nickname', 'profile-birth-date', 'profile-signature', 'profile-gender', 'profile-region', 'personal-qrcode', 'android-settings', 'cache-clear-confirm', 'clear-history-confirm', 'change-password', 'delete-account-confirm', 'app-version', 'privacy-settings', 'friend-discovery-settings', 'youth-mode-off', 'youth-mode-on', 'youth-mode-verify', 'notification-permission', 'permission-settings-reminder', 'camera-permission-info', 'microphone-permission-info', 'fraud-risk-warning', 'fraud-risk-confirmation', 'abuse-report-reasons', 'abuse-report-details', 'user-feedback', ''];
const SIDE_PANEL_VIEWS = ['acc_general', 'archive', 'blocked', 'contacts', 'cred', 'crop', 'edit', 'general', 'newtpk', 'notif', 'register', 'reset', 'security', 'settings', 'support', 'wallpapers', 'tenant', 'login', 'enterprise-register', 'enterprise-feedback', 'identity-check', 'realname', 'realname-verified', 'realname-reminder', 'independent-password', 'messages-home', 'messages-offline', 'messages-menu', 'message-actions', 'system-messages', 'chat-inbound-request', 'chat-inbound-message', 'chat-outbound-request', 'chat-status', 'chat-friend', 'chat-media', 'chat-blocked', 'chat-deleted', 'chat-standard', 'chat-attachments', 'chat-mixed', 'chat-contact-card', 'compose-keyboard', 'mentions-select', 'mentions-complete', 'mentions-message', 'emoji-panel', 'emoji-message', 'voice-ready', 'voice-hold', 'voice-recording', 'media-uploading', 'media-gallery', 'media-retry', 'upload-file', 'file-message', 'message-multiselect', 'favorite-success', 'forward-select', 'forward-multiple', 'forward-individual', 'forward-bundle', 'address-book', 'friend-requests', 'add-friend', 'my-friend-qr', 'friend-qr', 'friend-search-result', 'friend-verification', 'friend-profile', 'group-member-profile', 'friend-settings', 'friend-settings-blocked', 'friend-delete-confirm', 'block-list', 'block-confirm', 'clear-chat-confirm', 'group-list', 'group-empty', 'group-invite', 'group-events', 'group-join-requests', 'group-new-request', 'group-announcement', 'group-summary', 'group-announcement-detail', 'group-settings', 'group-profile', 'group-avatar', 'group-nickname', 'group-description', 'group-my-nickname', 'group-role-message', 'group-qr', 'group-governance', 'group-admins', 'group-admin-add', 'group-admin-success', 'group-admin-partial', 'group-admin-removed', 'group-mute-add', 'group-muted', 'group-owner-select', 'group-owner-confirm', 'group-files', 'group-file-upload', 'favorites', 'favorites-mixed', 'favorites-files', 'favorites-text', 'favorites-voice', 'favorites-links', 'favorites-select', 'favorite-share', 'file-download', 'file-downloading', 'file-downloaded', 'history-search', 'history-files', 'history-links', 'history-images', 'history-videos', 'call-history', 'call-incoming', 'audio-call-ringing', 'audio-call-active', 'audio-call-muted', 'audio-call-speaker', 'video-call-ringing', 'video-call-camera-off', 'video-call-active', 'video-call-peer-camera-off', 'call-minimized', 'workbench', 'organization-directory', 'meeting-organizer', 'meeting-attendee', 'meeting-create', 'meeting-detail', 'daily-report-editor', 'daily-report-mine', 'daily-report-team', 'daily-report-detail', 'monthly-report-editor', 'monthly-report-list', 'monthly-report-detail', 'executive-mailbox', 'executive-mailbox-notice', 'personal-center', 'personal-profile', 'profile-nickname', 'profile-birth-date', 'profile-signature', 'profile-gender', 'profile-region', 'personal-qrcode', 'android-settings', 'cache-clear-confirm', 'clear-history-confirm', 'change-password', 'delete-account-confirm', 'app-version', 'privacy-settings', 'friend-discovery-settings', 'youth-mode-off', 'youth-mode-on', 'youth-mode-verify', 'notification-permission', 'permission-settings-reminder', 'camera-permission-info', 'microphone-permission-info', 'fraud-risk-warning', 'fraud-risk-confirmation', 'abuse-report-reasons', 'abuse-report-details', 'user-feedback', ''];
const AUTH_FLOW_VIEWS = ['tenant', 'login', 'register', 'reset', 'cred', 'enterprise-register', 'enterprise-feedback'];
const LOGIN_ERROR_TEXT_CN = {
'authentication failed': '账号或密码错误',
@@ -19280,7 +19367,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
const chapterTwoFullPage = ['messages-offline', 'messages-menu', 'message-actions', 'system-messages'].includes(activeProductView) || !this.state.myUserId && activeProductView === 'messages-home';
const chapterThreeActive = ['chat-inbound-request', 'chat-inbound-message', 'chat-outbound-request', 'chat-status', 'chat-friend', 'chat-media', 'chat-blocked', 'chat-deleted', 'chat-standard', 'chat-attachments', 'chat-mixed', 'chat-contact-card'].includes(activeProductView);
const chapterFourActive = ['compose-keyboard', 'mentions-select', 'mentions-complete', 'mentions-message', 'emoji-panel', 'emoji-message', 'voice-ready', 'voice-hold', 'voice-recording', 'media-uploading', 'media-gallery', 'media-retry', 'upload-file', 'file-message', 'message-multiselect', 'favorite-success', 'forward-select', 'forward-multiple', 'forward-individual', 'forward-bundle'].includes(activeProductView);
const chapterFiveActive = ['address-book', 'friend-requests', 'add-friend', 'my-friend-qr', 'friend-search-result', 'friend-verification', 'friend-profile', 'group-member-profile', 'friend-settings', 'friend-settings-blocked', 'friend-delete-confirm', 'block-list', 'block-confirm', 'clear-chat-confirm'].includes(activeProductView);
const chapterFiveActive = ['address-book', 'friend-requests', 'add-friend', 'my-friend-qr', 'friend-qr', 'friend-search-result', 'friend-verification', 'friend-profile', 'group-member-profile', 'friend-settings', 'friend-settings-blocked', 'friend-delete-confirm', 'block-list', 'block-confirm', 'clear-chat-confirm'].includes(activeProductView);
const chapterSixActive = ['group-list', 'group-empty', 'group-invite', 'group-events', 'group-join-requests', 'group-new-request', 'group-announcement', 'group-summary', 'group-announcement-detail', 'group-settings', 'group-profile', 'group-avatar', 'group-nickname', 'group-description', 'group-my-nickname', 'group-role-message', 'group-qr', 'group-governance', 'group-admins', 'group-admin-add', 'group-admin-success', 'group-admin-partial', 'group-admin-removed', 'group-mute-add', 'group-muted', 'group-owner-select', 'group-owner-confirm', 'group-files', 'group-file-upload'].includes(activeProductView);
const chapterSevenActive = ['favorites', 'favorites-mixed', 'favorites-files', 'favorites-text', 'favorites-voice', 'favorites-links', 'favorites-select', 'favorite-share', 'file-download', 'file-downloading', 'file-downloaded', 'history-search', 'history-files', 'history-links', 'history-images', 'history-videos'].includes(activeProductView);
const callExperienceActive = ['call-history', 'call-incoming', 'audio-call-ringing', 'audio-call-active', 'audio-call-muted', 'audio-call-speaker', 'video-call-ringing', 'video-call-camera-off', 'video-call-active', 'video-call-peer-camera-off', 'call-minimized'].includes(activeProductView);
+1 -1
View File
File diff suppressed because one or more lines are too long