Files
im_webapp/src/views/sidepanel-view.jsx
T
Your Name c2e07a89b8 清理死代码:sidepanel-view.jsx里被chapterOneViews短路掉的旧版登录/注册/找回密码分支
不是功能修复,纯清理。sidepanel-view.jsx的render()方法开头(chapterOneViews数组
含'login'/'register'/'reset'等)已经对这些view值提前return真实对接后端的
AuthOnboardingView。但同一个render()方法下半部分的三元表达式链里,还留着一份基于
view==='login'/'register'/'reset'的旧分支,分别渲染LoginView/CreateAccountView/
PasswordResetView——由于上面的提前return,这三个view值永远不可能落到这里,是无法
触达的死代码。

确认方式:全仓库搜索确认SidepanelView只有tinode-web.jsx一处渲染入口,其view prop
(state={this.state.sidePanelSelected})只能来自handleHashRoute对SIDE_PANEL_VIEWS
白名单的校验结果——'login'/'register'/'reset'确实在这个白名单里(否则连
sidePanelSelected都设置不了),但chapterOneViews这个子集在render()内部的检查顺序
必然先于旧分支执行,所以旧分支不可能被触达。

删除范围:三个旧分支的JSX(对应import的LoginView/CreateAccountView懒加载/
PasswordResetView懒加载)+ 三个组件源文件本身(login-view.jsx/create-account-view.jsx/
password-reset-view.jsx,删除引用后确认全仓库不含任何其它referrer)+ 两个文件对应的
已生成umd构建产物(webpack不会自动清理不再产出的旧chunk文件,这两个是被删除源文件
遗留下来的孤儿产物)。'cred'分支(ValidationView,不在chapterOneViews里,仍在真实使用)
未改动。defineMessages里对应的login/register/reset翻译key本次未删——它们现在也
无法被messages[view]这条动态查找命中,但涉及i18n提取产物的联动改动超出这次清理范围,
如实记录,未处理。

验证:npm run build:dev干净通过(三个旧chunk不再产出);真实浏览器完整走了一遍
登录(已有账号)/注册(全新手机号)/找回密码(重置刚注册账号的密码,重置后用新密码
重新登录成功)三条流程,均无回归。
2026-09-06 17:31:20 +08:00

403 lines
16 KiB
React

import React, { Suspense } from 'react';
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import ErrorPanel from '../widgets/error-panel.jsx';
import LoadSpinner from '../widgets/load-spinner.jsx';
import SideNavbar from '../widgets/side-navbar.jsx';
import TopicCommonView from './topic-common-view.jsx';
import AuthOnboardingView from './auth-onboarding-view.jsx';
import MessageCenterView from './message-center-view.jsx';
import DirectMessageView from './direct-message-view.jsx';
import MessageComposerView, {MESSAGE_COMPOSER_VIEWS} from './message-composer-view.jsx';
import ContactManagementView, {CONTACT_MANAGEMENT_VIEWS} from './contact-management-view.jsx';
import GroupManagementView, {GROUP_MANAGEMENT_VIEWS} from './group-management-view.jsx';
import ContentLibraryView, {CONTENT_LIBRARY_VIEWS} from './content-library-view.jsx';
import CallExperienceView, {CALL_EXPERIENCE_VIEWS} from './call-experience-view.jsx';
import WorkplaceCollaborationView, {WORKPLACE_COLLABORATION_VIEWS} from './workplace-collaboration-view.jsx';
import PersonalSettingsView, {PERSONAL_SETTINGS_VIEWS} from './personal-settings-view.jsx';
import SecuritySupportView, {SECURITY_SUPPORT_VIEWS} from './security-support-view.jsx';
const AccountSettingsView = React.lazy(_ => import('./account-settings-view.jsx'));
import ContactsView from './contacts-view.jsx';
import AccGeneralView from './acc-general-view.jsx';
import AccNotificationsView from './acc-notifications-view.jsx';
const AccSecurityView = React.lazy(_ => import('./acc-security-view.jsx'));
import AccSupportView from './acc-support-view.jsx';
import NewTopicView from './new-topic-view.jsx';
import SettingsView from './settings-view.jsx';
import ValidationView from './validation-view.jsx';
const WallpapersView = React.lazy(_ => import('./wallpapers.jsx'));
// Panel titles for translation.
const messages = defineMessages({
login: {
id: 'sidepanel_title_login',
description: 'Sidepanel title for LoginView.',
defaultMessage: 'Sign In'
},
register: {
id: 'sidepanel_title_register',
description: 'Sidepanel title for CreateAccountView.',
defaultMessage: 'Create Account'
},
settings: {
id: 'sidepanel_title_settings',
description: 'Sidepanel title for SettingsView.',
defaultMessage: 'Settings'
},
edit: {
id: 'sidepanel_title_account_settings',
description: 'Sidepanel title for AccountSettingsView.',
defaultMessage: 'Account Settings'
},
general: {
id: 'panel_title_general',
description: 'Title for TopicCommon.',
defaultMessage: 'General'
},
security: {
id: 'panel_title_security',
description: 'Title for TopicSecirity and AccSecurity.',
defaultMessage: 'Security'
},
crop: {
id: 'panel_title_crop',
description: 'Title for AvatarCropView.',
defaultMessage: 'Drag to Adjust'
},
notif: {
id: 'sidepanel_title_acc_notifications',
description: 'Sidepanel title for AccNotificationsView.',
defaultMessage: 'Notifications'
},
acc_general: {
id: 'sidepanel_title_acc_general',
description: 'Sidepanel title for AccGeneralView.',
defaultMessage: 'General'
},
support: {
id: 'sidepanel_title_acc_support',
description: 'Sidepanel title for AccSupportView.',
defaultMessage: 'Support'
},
newtpk: {
id: 'sidepanel_title_newtpk',
description: 'Sidepanel title for NewTopicView.',
defaultMessage: 'Start New Chat'
},
cred: {
id: 'sidepanel_title_cred',
description: 'Sidepanel title for ValidationView.',
defaultMessage: 'Confirm Credentials'
},
reset: {
id: 'sidepanel_title_reset',
description: 'Sidepanel title for PasswordResetView.',
defaultMessage: 'Reset Password'
},
archive: {
id: 'sidepanel_title_archive',
description: 'Sidepanel title for ContactsView-Archive.',
defaultMessage: 'Archived Chats'
},
blocked: {
id: 'sidepanel_title_blocked',
description: 'Sidepanel title for ContactsView-Blocked.',
defaultMessage: 'Blocked Chats'
},
wallpapers: {
id: 'wallpapers',
description: 'Link or title for working with wallpapers.',
defaultMessage: 'Wallpapers'
}
});
class SidepanelView extends React.PureComponent {
constructor(props) {
super(props);
this.handleNewTopic = this.handleNewTopic.bind(this);
}
handleNewTopic() {
this.props.onNavigate('newtpk');
}
render() {
const {formatMessage} = this.props.intl;
const view = this.props.state || (this.props.myUserId ? 'messages-home' : 'tenant');
const chapterOneViews = ['tenant', 'login', 'enterprise-register', 'enterprise-feedback', 'register',
'reset', 'identity-check', 'realname', 'realname-verified', 'realname-reminder', 'independent-password'];
if (chapterOneViews.includes(view)) {
return <div id="sidepanel" className="auth-onboarding-panel">
<AuthOnboardingView
view={view}
hashParams={this.props.hashParams}
authenticated={!!this.props.myUserId}
login={this.props.login}
loginDisabled={this.props.loginDisabled}
persist={this.props.persist}
errorText={this.props.errorText}
errorLevel={this.props.errorLevel}
onLoginRequest={this.props.onLoginRequest}
onPersistenceChange={this.props.onPersistenceChange}
onCreateAccount={this.props.onCreateAccount}
onImRegisterRequest={this.props.onImRegisterRequest}
onTenantResolved={this.props.onTenantResolved}
onError={this.props.onError} />
</div>;
}
const chapterTwoViews = ['messages-home', 'messages-offline', 'messages-menu', 'message-actions', 'system-messages'];
if (chapterTwoViews.includes(view)) {
const fullPage = !this.props.myUserId || view !== 'messages-home';
return <div id="sidepanel" className={'message-center-panel' + (fullPage ? ' full-page' : '')}>
<MessageCenterView
view={view}
key={view}
connected={this.props.connected}
chatList={this.props.chatList}
myUserId={this.props.myUserId}
onTopicSelected={this.props.onTopicSelected}
onOpenScanner={this.props.onOpenScanner} />
{/* This branch never rendered ErrorPanel at all -- on desktop (non-mobile), opening a real
conversation lands here (view stays 'messages-home' behind the split-view topic pane),
and messages-view.jsx's own ErrorPanel is only rendered on displayMobile. Every
this.props.onError(...) call reachable from an open conversation on desktop (failed
uploads, failed forwards, URL risk blocks, etc.) had no visible outlet at all. */}
<ErrorPanel
level={this.props.errorLevel}
text={this.props.errorText}
action={this.props.errorAction}
actionText={this.props.errorActionText}
onClearError={this.props.onError} />
</div>;
}
const chapterThreeViews = ['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'];
if (chapterThreeViews.includes(view)) {
return <div id="sidepanel" className="direct-message-panel">
<DirectMessageView view={view} key={view} />
</div>;
}
if (MESSAGE_COMPOSER_VIEWS.includes(view)) {
return <div id="sidepanel" className="message-composer-panel">
<MessageComposerView view={view} key={view} />
</div>;
}
if (CONTACT_MANAGEMENT_VIEWS.includes(view)) {
return <div id="sidepanel" className="contact-management-panel">
<ContactManagementView view={view} hashParams={this.props.hashParams} myUserId={this.props.myUserId}
tinode={this.props.tinode} onTopicSelected={this.props.onTopicSelected} onCreateTopic={this.props.onCreateTopic}
onOpenScanner={this.props.onOpenScanner} key={view} />
</div>;
}
if (GROUP_MANAGEMENT_VIEWS.includes(view)) {
return <div id="sidepanel" className="group-management-panel">
<GroupManagementView view={view} hashParams={this.props.hashParams} myUserId={this.props.myUserId}
tinode={this.props.tinode} onTopicSelected={this.props.onTopicSelected} key={view} />
</div>;
}
if (CONTENT_LIBRARY_VIEWS.includes(view)) {
return <div id="sidepanel" className="content-library-panel">
<ContentLibraryView view={view} hashParams={this.props.hashParams} myUserId={this.props.myUserId}
tinode={this.props.tinode} onTopicSelected={this.props.onTopicSelected} chatList={this.props.chatList} key={view} />
</div>;
}
if (CALL_EXPERIENCE_VIEWS.includes(view)) {
return <div id="sidepanel" className="call-experience-panel">
<CallExperienceView view={view} key={view} />
</div>;
}
if (WORKPLACE_COLLABORATION_VIEWS.includes(view)) {
return <div id="sidepanel" className="workplace-collaboration-panel">
<WorkplaceCollaborationView view={view} hashParams={this.props.hashParams} myUserId={this.props.myUserId}
tinode={this.props.tinode} onTopicSelected={this.props.onTopicSelected} key={view} />
</div>;
}
if (PERSONAL_SETTINGS_VIEWS.includes(view)) {
return <div id="sidepanel" className="personal-settings-panel">
<PersonalSettingsView view={view} hashParams={this.props.hashParams} myUserId={this.props.myUserId}
tinode={this.props.tinode} chatList={this.props.chatList}
onUpdatePassword={this.props.onUpdatePassword} onDeleteAccount={this.props.onDeleteAccount}
onLogout={this.props.onLogout} key={view} />
</div>;
}
if (SECURITY_SUPPORT_VIEWS.includes(view)) {
return <div id="sidepanel" className="security-support-panel">
<SecuritySupportView view={view} hashParams={this.props.hashParams} tinode={this.props.tinode} key={view} />
</div>;
}
let title, avatar, badges;
if (view == 'contacts') {
title = this.props.title;
avatar = this.props.avatar ? this.props.avatar : true;
badges = this.props.trustedBadges;
} else {
title = formatMessage(messages[view]);
avatar = false;
badges = null;
}
let onCancel;
if (['login', 'contacts'].indexOf(view) == -1) {
onCancel = this.props.onCancel;
}
return (
<div id="sidepanel">
<SideNavbar
state={view}
title={title}
avatar={avatar}
tinode={this.props.tinode}
trustedBadges={badges}
myUserId={this.props.myUserId}
onSignUp={this.props.onSignUp}
onSettings={this.props.onSettings}
onNewTopic={this.handleNewTopic}
onCancel={onCancel} />
<ErrorPanel
level={this.props.errorLevel}
text={this.props.errorText}
action={this.props.errorAction}
actionText={this.props.errorActionText}
onClearError={this.props.onError} />
<LoadSpinner show={this.props.loadSpinnerVisible} />
{view === 'settings' ?
<SettingsView
transport={this.props.transport}
serverAddress={this.props.serverAddress}
secureConnection={this.props.secureConnection}
onCancel={this.props.onCancel}
onUpdate={this.props.onGlobalSettings} /> :
view === 'edit' ?
<Suspense fallback={<div className="panel-form-row"><FormattedMessage id="loading_note"
defaultMessage="Loading..." description="Message shown when component is loading"/></div>}>
<AccountSettingsView
tinode={this.props.tinode}
myUserId={this.props.myUserId}
trustedBadges={this.props.trustedBadges}
reqCredMethod={this.props.reqCredMethod}
onShowCountrySelector={this.props.onShowCountrySelector}
onNavigate={this.props.onNavigate}
onCredAdd={this.props.onCredAdd}
onCredDelete={this.props.onCredDelete}
onCredConfirm={this.props.onCredConfirm}
onError={this.props.onError} />
</Suspense> :
(view === 'general' || view === 'crop') ?
<TopicCommonView
topic="me"
tinode={this.props.tinode}
myUserId={this.props.myUserId}
reqCredMethod={this.props.reqCredMethod}
onUpdateTopicDesc={this.props.onUpdateAccountDesc}
onUpdateTagsRequest={this.props.onUpdateAccountTags}
onError={this.props.onError} /> :
view === 'acc_general' ?
<AccGeneralView
colorSchema={this.props.colorSchema}
onSelectWallpapers={this.props.onSelectWallpapers}
textSize={this.props.textSize}
sendOnEnter={this.props.sendOnEnter}
onChangeColorSchema={this.props.onChangeColorSchema}
onTextSizeChanged={this.props.onTextSizeChanged}
onSendOnEnterChanged={this.props.onSendOnEnterChanged} /> :
view === 'notif' ?
<AccNotificationsView
messageSounds={this.props.messageSounds}
desktopAlerts={this.props.desktopAlerts}
desktopAlertsEnabled={this.props.desktopAlertsEnabled}
onTogglePushNotifications={this.props.onTogglePushNotifications}
onToggleMessageSounds={this.props.onToggleMessageSounds} /> :
view === 'security' ?
<Suspense fallback={<div className="panel-form-row"><FormattedMessage id="loading_note"
defaultMessage="Loading..." description="Message shown when component is loading"/></div>}>
<AccSecurityView
tinode={this.props.tinode}
incognitoMode={this.props.incognitoMode}
onNavigate={this.props.onNavigate}
onUpdateAccountDesc={this.props.onUpdateAccountDesc}
onUpdatePassword={this.props.onUpdatePassword}
onLogout={this.props.onLogout}
onDeleteAccount={this.props.onDeleteAccount}
onShowAlert={this.props.onShowAlert}
onShowBlocked={this.props.onShowBlocked}
onToggleIncognitoMode={this.props.onToggleIncognitoMode} />
</Suspense> :
view === 'support' ?
<AccSupportView
serverAddress={this.props.serverAddress}
serverVersion={this.props.serverVersion} /> :
view === 'wallpapers' ?
<Suspense fallback={<div className="panel-form-row"><FormattedMessage id="loading_note"
defaultMessage="Loading..." description="Message shown when component is loading"/></div>}>
<WallpapersView
wallpaper={this.props.wallpaper}
wallpaperSize={this.props.wallpaperSize}
wallpaperBlur={this.props.wallpaperBlur}
colorSchema={this.props.colorSchema !== 'auto' ? this.props.colorSchema : this.props.systemColorSchema}
onWallpaperSelected={this.props.onWallpaperSelected}
/>
</Suspense> :
(view === 'contacts' || view == 'archive' || view == 'blocked') ?
<ContactsView
tinode={this.props.tinode}
myUserId={this.props.myUserId}
connected={this.props.connected}
topicSelected={this.props.topicSelected}
archive={view == 'archive'}
blocked={view == 'blocked'}
chatList={this.props.chatList}
showContextMenu={this.props.showContextMenu}
onTopicSelected={this.props.onTopicSelected}
onShowArchive={this.props.onShowArchive} /> :
view === 'newtpk' ?
<NewTopicView
tinode={this.props.tinode}
searchResults={this.props.searchResults}
onInitFind={this.props.onInitFind}
onSearchContacts={this.props.onSearchContacts}
onCreateTopic={this.props.onCreateTopic}
onError={this.props.onError} /> :
view === 'cred' ?
<ValidationView
credCode={this.props.credCode}
credMethod={this.props.credMethod}
credToken={this.props.credToken}
onSubmit={this.props.onValidateCredentials}
onCancel={this.props.onCancel} /> :
null}
</div>
);
}
};
export default injectIntl(SidepanelView);