[MM-59299] Investigate further breaking down chunks of components/node_modules for initial load (#27845)

Этот коммит содержится в:
M-ZubairAhmed
2024-09-04 05:55:47 +00:00
коммит произвёл GitHub
родитель e104575505
Коммит 7232feecf7
19 изменённых файлов: 213 добавлений и 191 удалений

Просмотреть файл

@@ -13,6 +13,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img
alt="Files"
className="overlay__files"
loading="lazy"
src=""
/>
<span>
@@ -28,6 +29,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img
alt="Logo"
className="overlay__logo"
loading="lazy"
src=""
/>
</div>
@@ -48,6 +50,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img
alt="Files"
className="overlay__files"
loading="lazy"
src=""
/>
<span>
@@ -63,6 +66,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img
alt="Logo"
className="overlay__logo"
loading="lazy"
src=""
/>
</div>
@@ -83,6 +87,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img
alt="Files"
className="overlay__files"
loading="lazy"
src=""
/>
<span>
@@ -98,6 +103,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
<img
alt="Logo"
className="overlay__logo"
loading="lazy"
src=""
/>
</div>

Просмотреть файл

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import classNames from 'classnames';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import React, {lazy, useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import {useDispatch, useSelector} from 'react-redux';
@@ -24,9 +24,9 @@ import {makeGetDraft} from 'selectors/rhs';
import {connectionErrorCount} from 'selectors/views/system';
import LocalStorageStore from 'stores/local_storage_store';
import {makeAsyncComponent} from 'components/async_load';
import AutoHeightSwitcher from 'components/common/auto_height_switcher';
import useDidUpdate from 'components/common/hooks/useDidUpdate';
import FileLimitStickyBanner from 'components/file_limit_sticky_banner';
import MessageSubmitError from 'components/message_submit_error';
import MsgTyping from 'components/msg_typing';
import RhsSuggestionList from 'components/suggestion/rhs_suggestion_list';
@@ -66,6 +66,8 @@ import useUploadFiles from './use_upload_files';
import './advanced_text_editor.scss';
const FileLimitStickyBanner = makeAsyncComponent('FileLimitStickyBanner', lazy(() => import('components/file_limit_sticky_banner')));
function isDraftEmpty(draft: PostDraft) {
return draft.message === '' && draft.fileInfos.length === 0 && draft.uploadsInProgress.length === 0;
}

Просмотреть файл

@@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import React, {lazy} from 'react';
import {hot} from 'react-hot-loader/root';
import {Provider} from 'react-redux';
import {Router} from 'react-router-dom';
@@ -11,7 +11,7 @@ import store from 'stores/redux_store';
import {makeAsyncComponent} from 'components/async_load';
import {getHistory} from 'utils/browser_history';
const LazyRoot = React.lazy(() => import('components/root'));
const LazyRoot = lazy(() => import('components/root'));
const Root = makeAsyncComponent('Root', LazyRoot);

Просмотреть файл

@@ -2,43 +2,34 @@
// See LICENSE.txt for license information.
import classNames from 'classnames';
import React from 'react';
import React, {lazy} from 'react';
import {Route, Switch, Redirect} from 'react-router-dom';
import {makeAsyncComponent} from 'components/async_load';
import ChannelIdentifierRouter from 'components/channel_layout/channel_identifier_router';
import PlaybookRunner from 'components/channel_layout/playbook_runner';
import LoadingScreen from 'components/loading_screen';
import PermalinkView from 'components/permalink_view';
import {IDENTIFIER_PATH_PATTERN, ID_PATH_PATTERN, TEAM_NAME_PATH_PATTERN} from 'utils/path';
import type {OwnProps, PropsFromRedux} from './index';
const LazyChannelHeaderMobile = makeAsyncComponent(
'LazyChannelHeaderMobile',
React.lazy(() => import('components/channel_header_mobile')),
);
const LazyGlobalThreads = makeAsyncComponent(
'LazyGlobalThreads',
React.lazy(() => import('components/threading/global_threads')),
const ChannelHeaderMobile = makeAsyncComponent('ChannelHeaderMobile', lazy(() => import('components/channel_header_mobile')));
const GlobalThreads = makeAsyncComponent('GlobalThreads', lazy(() => import('components/threading/global_threads')),
(
<div className='app__content'>
<LoadingScreen/>
</div>
),
);
const LazyDrafts = makeAsyncComponent(
'LazyDrafts',
React.lazy(() => import('components/drafts')),
const Drafts = makeAsyncComponent('Drafts', lazy(() => import('components/drafts')),
(
<div className='app__content'>
<LoadingScreen/>
</div>
),
);
const PermalinkView = makeAsyncComponent('PermalinkView', lazy(() => import('components/permalink_view')));
const PlaybookRunner = makeAsyncComponent('PlaybookRunner', lazy(() => import('components/channel_layout/playbook_runner')));
type Props = PropsFromRedux & OwnProps;
@@ -85,11 +76,13 @@ export default class CenterChannel extends React.PureComponent<Props, State> {
})}
>
{isMobileView && (
<div className='row header'>
<div id='navbar_wrapper'>
<LazyChannelHeaderMobile/>
<>
<div className='row header'>
<div id='navbar_wrapper'>
<ChannelHeaderMobile/>
</div>
</div>
</div>
</>
)}
<div className='row main'>
<Switch>
@@ -114,12 +107,12 @@ export default class CenterChannel extends React.PureComponent<Props, State> {
{isCollapsedThreadsEnabled ? (
<Route
path={`/:team(${TEAM_NAME_PATH_PATTERN})/threads/:threadIdentifier(${ID_PATH_PATTERN})?`}
component={LazyGlobalThreads}
component={GlobalThreads}
/>
) : null}
<Route
path={`/:team(${TEAM_NAME_PATH_PATTERN})/drafts`}
component={LazyDrafts}
component={Drafts}
/>
<Redirect to={lastChannelPath}/>

Просмотреть файл

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import classNames from 'classnames';
import React, {useEffect} from 'react';
import React, {lazy, useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {cleanUpStatusAndProfileFetchingPoll} from 'mattermost-redux/actions/status_profile_polling';
@@ -10,10 +10,9 @@ import {getIsUserStatusesConfigEnabled} from 'mattermost-redux/selectors/entitie
import {addVisibleUsersInCurrentChannelToStatusPoll} from 'actions/status_actions';
import {makeAsyncComponent} from 'components/async_load';
import CenterChannel from 'components/channel_layout/center_channel';
import LoadingScreen from 'components/loading_screen';
import ProductNoticesModal from 'components/product_notices_modal';
import ResetStatusModal from 'components/reset_status_modal';
import Sidebar from 'components/sidebar';
import CRTPostsChannelResetWatcher from 'components/threading/channel_threads/posts_channel_reset_watcher';
import UnreadsStatusHandler from 'components/unreads_status_handler';
@@ -22,6 +21,9 @@ import Pluggable from 'plugins/pluggable';
import {Constants} from 'utils/constants';
import {isInternetExplorer, isEdge} from 'utils/user_agent';
const ProductNoticesModal = makeAsyncComponent('ProductNoticesModal', lazy(() => import('components/product_notices_modal')));
const ResetStatusModal = makeAsyncComponent('ResetStatusModal', lazy(() => import('components/reset_status_modal')));
const BODY_CLASS_FOR_CHANNEL = ['app__body', 'channel-view'];
type Props = {

Просмотреть файл

@@ -8,7 +8,7 @@ exports[`components/channel_view Should match snapshot if channel is archived 1`
<FileUploadOverlay
overlayType="center"
/>
<withRouter(Connect(injectIntl(ChannelHeader)))
<ChannelHeader
channelId="channelId"
channelIsArchived={true}
deactivatedChannel={false}
@@ -66,7 +66,7 @@ exports[`components/channel_view Should match snapshot if channel is deactivated
<FileUploadOverlay
overlayType="center"
/>
<withRouter(Connect(injectIntl(ChannelHeader)))
<ChannelHeader
channelId="channelId"
channelIsArchived={false}
deactivatedChannel={true}
@@ -123,7 +123,7 @@ exports[`components/channel_view Should match snapshot with base props 1`] = `
<FileUploadOverlay
overlayType="center"
/>
<withRouter(Connect(injectIntl(ChannelHeader)))
<ChannelHeader
channelId="channelId"
channelIsArchived={false}
deactivatedChannel={false}
@@ -152,7 +152,7 @@ exports[`components/channel_view Should match snapshot with base props 1`] = `
data-testid="post-create"
id="post-create"
>
<Memo(AdvancedCreatePost) />
<AdvancedCreatePost />
</div>
</div>
`;

Просмотреть файл

@@ -1,15 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import React, {lazy} from 'react';
import {FormattedMessage} from 'react-intl';
import type {RouteComponentProps} from 'react-router-dom';
import AdvancedCreatePost from 'components/advanced_create_post';
import ChannelBookmarks from 'components/channel_bookmarks';
import ChannelHeader from 'components/channel_header';
import {makeAsyncComponent} from 'components/async_load';
import deferComponentRender from 'components/deferComponentRender';
import FileUploadOverlay from 'components/file_upload_overlay';
import FormattedMarkdownMessage from 'components/formatted_markdown_message';
import PostView from 'components/post_view';
@@ -17,6 +14,11 @@ import WebSocketClient from 'client/web_websocket_client';
import type {PropsFromRedux} from './index';
const ChannelHeader = makeAsyncComponent('ChannelHeader', lazy(() => import('components/channel_header')));
const FileUploadOverlay = makeAsyncComponent('FileUploadOverlay', lazy(() => import('components/file_upload_overlay')));
const ChannelBookmarks = makeAsyncComponent('ChannelBookmarks', lazy(() => import('components/channel_bookmarks')));
const AdvancedCreatePost = makeAsyncComponent('AdvancedCreatePost', lazy(() => import('components/advanced_create_post')));
export type Props = PropsFromRedux & RouteComponentProps<{
postid?: string;
}>;

Просмотреть файл

@@ -29,6 +29,7 @@ const FileUploadOverlay = (props: Props) => {
className='overlay__files'
src={fileOverlayImage}
alt='Files'
loading='lazy'
/>
<span>
<i
@@ -44,6 +45,7 @@ const FileUploadOverlay = (props: Props) => {
className='overlay__logo'
src={overlayLogoImage}
alt='Logo'
loading='lazy'
/>
</div>
</div>

Просмотреть файл

@@ -0,0 +1,40 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {lazy} from 'react';
import type {RouteComponentProps} from 'react-router-dom';
import {Route} from 'react-router-dom';
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
import {makeAsyncComponent} from 'components/async_load';
import CompassThemeProvider from 'components/compass_theme_provider/compass_theme_provider';
import LoggedIn from 'components/logged_in';
const OnBoardingTaskList = makeAsyncComponent('OnboardingTaskList', lazy(() => import('components/onboarding_tasklist')));
type Props = {
component: React.ComponentType<RouteComponentProps<any>>;
path: string | string[];
theme?: Theme; // the routes that send the theme are the ones that will actually need to show the onboarding tasklist
};
export default function LoggedInRoute(props: Props) {
const {component: Component, theme, ...rest} = props;
return (
<Route
{...rest}
render={(routeProps) => (
<LoggedIn {...routeProps}>
{theme && (
<CompassThemeProvider theme={theme}>
<OnBoardingTaskList/>
</CompassThemeProvider>
)}
<Component {...(routeProps)}/>
</LoggedIn>
)}
/>
);
}

Просмотреть файл

@@ -3,7 +3,7 @@
import classNames from 'classnames';
import deepEqual from 'fast-deep-equal';
import React from 'react';
import React, {lazy} from 'react';
import {Route, Switch, Redirect} from 'react-router-dom';
import type {RouteComponentProps} from 'react-router-dom';
@@ -13,42 +13,31 @@ import {setSystemEmojis} from 'mattermost-redux/actions/emojis';
import {setUrl} from 'mattermost-redux/actions/general';
import {Client4} from 'mattermost-redux/client';
import {rudderAnalytics, RudderTelemetryHandler} from 'mattermost-redux/client/rudder';
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
import {measurePageLoadTelemetry, temporarilySetPageLoadContext, trackEvent, trackSelectorMetrics} from 'actions/telemetry_actions.jsx';
import BrowserStore from 'stores/browser_store';
import AccessProblem from 'components/access_problem';
import AnnouncementBarController from 'components/announcement_bar';
import AppBar from 'components/app_bar/app_bar';
import {makeAsyncComponent} from 'components/async_load';
import CloudEffects from 'components/cloud_effects';
import CompassThemeProvider from 'components/compass_theme_provider/compass_theme_provider';
import OpenPluginInstallPost from 'components/custom_open_plugin_install_post_renderer';
import GlobalHeader from 'components/global_header/global_header';
import {HFRoute} from 'components/header_footer_route/header_footer_route';
import {HFTRoute, LoggedInHFTRoute} from 'components/header_footer_template_route';
import InitialLoadingScreen from 'components/initial_loading_screen';
import MobileViewWatcher from 'components/mobile_view_watcher';
import ModalController from 'components/modal_controller';
import LaunchingWorkspace, {LAUNCHING_WORKSPACE_FULLSCREEN_Z_INDEX} from 'components/preparing_workspace/launching_workspace';
import LoggedIn from 'components/logged_in';
import LoggedInRoute from 'components/logged_in_route';
import {LAUNCHING_WORKSPACE_FULLSCREEN_Z_INDEX} from 'components/preparing_workspace/launching_workspace';
import {Animations} from 'components/preparing_workspace/steps';
import SidebarRight from 'components/sidebar_right';
import SidebarRightMenu from 'components/sidebar_right_menu';
import SystemNotice from 'components/system_notice';
import TeamSidebar from 'components/team_sidebar';
import WindowSizeObserver from 'components/window_size_observer/WindowSizeObserver';
import SidebarMobileRightMenu from 'components/sidebar_mobile_right_menu';
import webSocketClient from 'client/web_websocket_client';
import {initializePlugins} from 'plugins';
import Pluggable from 'plugins/pluggable';
import A11yController from 'utils/a11y_controller';
import {PageLoadContext} from 'utils/constants';
import {EmojiIndicesByAlias} from 'utils/emoji';
import {TEAM_NAME_PATH_PATTERN} from 'utils/path';
import {getSiteURL} from 'utils/url';
import * as UserAgent from 'utils/user_agent';
import * as Utils from 'utils/utils';
import {isAndroidWeb, isChromebook, isDesktopApp, isIosWeb} from 'utils/user_agent';
import {applyTheme, isTextDroppableEvent} from 'utils/utils';
import LuxonController from './luxon_controller';
import PerformanceReporterController from './performance_reporter_controller';
@@ -59,68 +48,36 @@ import type {PropsFromRedux} from './index';
import 'plugins/export.js';
const LazyErrorPage = React.lazy(() => import('components/error_page'));
const LazyLogin = React.lazy(() => import('components/login/login'));
const LazyAdminConsole = React.lazy(() => import('components/admin_console'));
const LazyLoggedIn = React.lazy(() => import('components/logged_in'));
const LazyPasswordResetSendLink = React.lazy(() => import('components/password_reset_send_link'));
const LazyPasswordResetForm = React.lazy(() => import('components/password_reset_form'));
const LazySignup = React.lazy(() => import('components/signup/signup'));
const LazyTermsOfService = React.lazy(() => import('components/terms_of_service'));
const LazyShouldVerifyEmail = React.lazy(() => import('components/should_verify_email/should_verify_email'));
const LazyDoVerifyEmail = React.lazy(() => import('components/do_verify_email/do_verify_email'));
const LazyClaimController = React.lazy(() => import('components/claim'));
const LazyLinkingLandingPage = React.lazy(() => import('components/linking_landing_page'));
const LazySelectTeam = React.lazy(() => import('components/select_team'));
const LazyAuthorize = React.lazy(() => import('components/authorize'));
const LazyCreateTeam = React.lazy(() => import('components/create_team'));
const LazyMfa = React.lazy(() => import('components/mfa/mfa_controller'));
const LazyPreparingWorkspace = React.lazy(() => import('components/preparing_workspace'));
const LazyTeamController = React.lazy(() => import('components/team_controller'));
const LazyOnBoardingTaskList = React.lazy(() => import('components/onboarding_tasklist'));
const CreateTeam = makeAsyncComponent('CreateTeam', LazyCreateTeam);
const ErrorPage = makeAsyncComponent('ErrorPage', LazyErrorPage);
const TermsOfService = makeAsyncComponent('TermsOfService', LazyTermsOfService);
const Login = makeAsyncComponent('LoginController', LazyLogin);
const AdminConsole = makeAsyncComponent('AdminConsole', LazyAdminConsole);
const LoggedIn = makeAsyncComponent('LoggedIn', LazyLoggedIn);
const PasswordResetSendLink = makeAsyncComponent('PasswordResedSendLink', LazyPasswordResetSendLink);
const PasswordResetForm = makeAsyncComponent('PasswordResetForm', LazyPasswordResetForm);
const Signup = makeAsyncComponent('SignupController', LazySignup);
const ShouldVerifyEmail = makeAsyncComponent('ShouldVerifyEmail', LazyShouldVerifyEmail);
const DoVerifyEmail = makeAsyncComponent('DoVerifyEmail', LazyDoVerifyEmail);
const ClaimController = makeAsyncComponent('ClaimController', LazyClaimController);
const LinkingLandingPage = makeAsyncComponent('LinkingLandingPage', LazyLinkingLandingPage);
const SelectTeam = makeAsyncComponent('SelectTeam', LazySelectTeam);
const Authorize = makeAsyncComponent('Authorize', LazyAuthorize);
const Mfa = makeAsyncComponent('Mfa', LazyMfa);
const PreparingWorkspace = makeAsyncComponent('PreparingWorkspace', LazyPreparingWorkspace);
const TeamController = makeAsyncComponent('TeamController', LazyTeamController);
const OnBoardingTaskList = makeAsyncComponent('OnboardingTaskList', LazyOnBoardingTaskList);
type LoggedInRouteProps = {
component: React.ComponentType<RouteComponentProps<any>>;
path: string | string[];
theme?: Theme; // the routes that send the theme are the ones that will actually need to show the onboarding tasklist
};
function LoggedInRoute(props: LoggedInRouteProps) {
const {component: Component, theme, ...rest} = props;
return (
<Route
{...rest}
render={(routeProps) => (
<LoggedIn {...routeProps}>
{theme && <CompassThemeProvider theme={theme}>
<OnBoardingTaskList/>
</CompassThemeProvider>}
<Component {...(routeProps)}/>
</LoggedIn>
)}
/>
);
}
const MobileViewWatcher = makeAsyncComponent('MobileViewWatcher', lazy(() => import('components/mobile_view_watcher')));
const WindowSizeObserver = makeAsyncComponent('WindowSizeObserver', lazy(() => import('components/window_size_observer/WindowSizeObserver')));
const ErrorPage = makeAsyncComponent('ErrorPage', lazy(() => import('components/error_page')));
const Login = makeAsyncComponent('LoginController', lazy(() => import('components/login/login')));
const AccessProblem = makeAsyncComponent('AccessProblem', lazy(() => import('components/access_problem')));
const PasswordResetSendLink = makeAsyncComponent('PasswordResedSendLink', lazy(() => import('components/password_reset_send_link')));
const PasswordResetForm = makeAsyncComponent('PasswordResetForm', lazy(() => import('components/password_reset_form')));
const Signup = makeAsyncComponent('SignupController', lazy(() => import('components/signup/signup')));
const ShouldVerifyEmail = makeAsyncComponent('ShouldVerifyEmail', lazy(() => import('components/should_verify_email/should_verify_email')));
const DoVerifyEmail = makeAsyncComponent('DoVerifyEmail', lazy(() => import('components/do_verify_email/do_verify_email')));
const ClaimController = makeAsyncComponent('ClaimController', lazy(() => import('components/claim')));
const TermsOfService = makeAsyncComponent('TermsOfService', lazy(() => import('components/terms_of_service')));
const LinkingLandingPage = makeAsyncComponent('LinkingLandingPage', lazy(() => import('components/linking_landing_page')));
const AdminConsole = makeAsyncComponent('AdminConsole', lazy(() => import('components/admin_console')));
const SelectTeam = makeAsyncComponent('SelectTeam', lazy(() => import('components/select_team')));
const Authorize = makeAsyncComponent('Authorize', lazy(() => import('components/authorize')));
const CreateTeam = makeAsyncComponent('CreateTeam', lazy(() => import('components/create_team')));
const Mfa = makeAsyncComponent('Mfa', lazy(() => import('components/mfa/mfa_controller')));
const PreparingWorkspace = makeAsyncComponent('PreparingWorkspace', lazy(() => import('components/preparing_workspace')));
const Pluggable = makeAsyncComponent('Pluggable', lazy(() => import('plugins/pluggable')));
const LaunchingWorkspace = makeAsyncComponent('LaunchingWorkspace', lazy(() => import('components/preparing_workspace/launching_workspace')));
const CompassThemeProvider = makeAsyncComponent('CompassThemeProvider', lazy(() => import('components/compass_theme_provider/compass_theme_provider')));
const TeamController = makeAsyncComponent('TeamController', lazy(() => import('components/team_controller')));
const AnnouncementBarController = makeAsyncComponent('AnnouncementBarController', lazy(() => import('components/announcement_bar')));
const SystemNotice = makeAsyncComponent('SystemNotice', lazy(() => import('components/system_notice')));
const CloudEffects = makeAsyncComponent('CloudEffects', lazy(() => import('components/cloud_effects')));
const TeamSidebar = makeAsyncComponent('TeamSidebar', lazy(() => import('components/team_sidebar')));
const SidebarRight = makeAsyncComponent('SidebarRight', lazy(() => import('components/sidebar_right')));
const ModalController = makeAsyncComponent('ModalController', lazy(() => import('components/modal_controller')));
const AppBar = makeAsyncComponent('AppBar', lazy(() => import('components/app_bar/app_bar')));
const noop = () => {};
@@ -138,7 +95,6 @@ export default class Root extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
// Redux
setUrl(getSiteURL());
// Disable auth header to enable CSRF check
@@ -146,24 +102,6 @@ export default class Root extends React.PureComponent<Props, State> {
setSystemEmojis(new Set(EmojiIndicesByAlias.keys()));
// Force logout of all tabs if one tab is logged out
window.addEventListener('storage', this.handleLogoutLoginSignal);
// Prevent drag and drop files from navigating away from the app
document.addEventListener('drop', (e) => {
if (e.dataTransfer && e.dataTransfer.items.length > 0 && e.dataTransfer.items[0].kind === 'file') {
e.preventDefault();
e.stopPropagation();
}
});
document.addEventListener('dragover', (e) => {
if (!Utils.isTextDroppableEvent(e) && !document.body.classList.contains('focalboard-body')) {
e.preventDefault();
e.stopPropagation();
}
});
this.state = {
shouldMountAppRoutes: false,
};
@@ -251,23 +189,23 @@ export default class Root extends React.PureComponent<Props, State> {
this.showLandingPageIfNecessary();
Utils.applyTheme(this.props.theme);
applyTheme(this.props.theme);
};
private showLandingPageIfNecessary = () => {
// We have nothing to redirect to if we're already on Desktop App
// Chromebook has no Desktop App to switch to
if (UserAgent.isDesktopApp() || UserAgent.isChromebook()) {
if (isDesktopApp() || isChromebook()) {
return;
}
// Nothing to link to if we've removed the Android App download link
if (UserAgent.isAndroidWeb() && !this.props.androidDownloadLink) {
if (isAndroidWeb() && !this.props.androidDownloadLink) {
return;
}
// Nothing to link to if we've removed the iOS App download link
if (UserAgent.isIosWeb() && !this.props.iosDownloadLink) {
if (isIosWeb() && !this.props.iosDownloadLink) {
return;
}
@@ -312,7 +250,7 @@ export default class Root extends React.PureComponent<Props, State> {
componentDidUpdate(prevProps: Props, prevState: State) {
if (!deepEqual(prevProps.theme, this.props.theme)) {
Utils.applyTheme(this.props.theme);
applyTheme(this.props.theme);
}
if (this.props.location.pathname === '/') {
@@ -384,6 +322,20 @@ export default class Root extends React.PureComponent<Props, State> {
}
};
handleDropEvent = (e: DragEvent) => {
if (e.dataTransfer && e.dataTransfer.items.length > 0 && e.dataTransfer.items[0].kind === 'file') {
e.preventDefault();
e.stopPropagation();
}
};
handleDragOverEvent = (e: DragEvent) => {
if (!isTextDroppableEvent(e) && !document.body.classList.contains('focalboard-body')) {
e.preventDefault();
e.stopPropagation();
}
};
componentDidMount() {
temporarilySetPageLoadContext(PageLoadContext.PAGE_LOAD);
@@ -394,10 +346,20 @@ export default class Root extends React.PureComponent<Props, State> {
measurePageLoadTelemetry();
trackSelectorMetrics();
// Force logout of all tabs if one tab is logged out
window.addEventListener('storage', this.handleLogoutLoginSignal);
// Prevent drag and drop files from navigating away from the app
document.addEventListener('drop', this.handleDropEvent);
document.addEventListener('dragover', this.handleDragOverEvent);
}
componentWillUnmount() {
window.removeEventListener('storage', this.handleLogoutLoginSignal);
document.removeEventListener('drop', this.handleDropEvent);
document.removeEventListener('dragover', this.handleDragOverEvent);
}
handleLogoutLoginSignal = (e: StorageEvent) => {
@@ -600,7 +562,7 @@ export default class Root extends React.PureComponent<Props, State> {
</div>
<Pluggable pluggableName='Global'/>
<AppBar/>
<SidebarRightMenu/>
<SidebarMobileRightMenu/>
</CompassThemeProvider>
</Switch>
</RootProvider>

Просмотреть файл

@@ -85,7 +85,7 @@ exports[`components/sidebar should match snapshot when direct channels modal is
onDragStart={[Function]}
/>
<Connect(DataPrefetch) />
<Connect(MoreDirectChannels)
<MoreDirectChannels
isExistingChannel={false}
onModalDismissed={[Function]}
/>

Просмотреть файл

@@ -9,7 +9,7 @@ import type {DeepPartial} from '@mattermost/types/utilities';
import {Preferences} from 'mattermost-redux/constants';
import mergeObjects from 'packages/mattermost-redux/test/merge_objects';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import {renderWithContext, screen, waitFor} from 'tests/react_testing_utils';
import Constants, {ModalIdentifiers} from 'utils/constants';
import {TestHelper} from 'utils/test_helper';
@@ -164,7 +164,7 @@ describe('components/sidebar', () => {
},
};
test('should not render unreads category when disabled by user preference', () => {
test('should not render unreads category when disabled by user preference', async () => {
const testState = {
entities: {
channels: {
@@ -185,10 +185,12 @@ describe('components/sidebar', () => {
mergeObjects(baseState, testState),
);
expect(screen.queryByText('UNREADS')).not.toBeInTheDocument();
await waitFor(() => {
expect(screen.queryByText('UNREADS')).not.toBeInTheDocument();
});
});
test('should render unreads category when there are unread channels', () => {
test('should render unreads category when there are unread channels', async () => {
const testState: DeepPartial<GlobalState> = {
entities: {
channels: {
@@ -209,10 +211,12 @@ describe('components/sidebar', () => {
mergeObjects(baseState, testState),
);
expect(screen.queryByText('UNREADS')).toBeInTheDocument();
await waitFor(() => {
expect(screen.queryByText('UNREADS')).toBeInTheDocument();
});
});
test('should not render unreads category when there are no unread channels', () => {
test('should not render unreads category when there are no unread channels', async () => {
const testState: DeepPartial<GlobalState> = {
entities: {
preferences: {
@@ -228,10 +232,12 @@ describe('components/sidebar', () => {
mergeObjects(baseState, testState),
);
expect(screen.queryByText('UNREADS')).not.toBeInTheDocument();
await waitFor(() => {
expect(screen.queryByText('UNREADS')).not.toBeInTheDocument();
});
});
test('should render unreads category when there are no unread channels but the current channel was previously unread', () => {
test('should render unreads category when there are no unread channels but the current channel was previously unread', async () => {
const testState: DeepPartial<GlobalState> = {
entities: {
preferences: {
@@ -252,7 +258,9 @@ describe('components/sidebar', () => {
mergeObjects(baseState, testState),
);
expect(screen.queryByText('UNREADS')).toBeInTheDocument();
await waitFor(() => {
expect(screen.queryByText('UNREADS')).toBeInTheDocument();
});
});
});
});

Просмотреть файл

@@ -2,34 +2,36 @@
// See LICENSE.txt for license information.
import classNames from 'classnames';
import React from 'react';
import React, {lazy} from 'react';
import {trackEvent} from 'actions/telemetry_actions';
import BrowseChannels from 'components/browse_channels';
import CreateUserGroupsModal from 'components/create_user_groups_modal';
import {makeAsyncComponent} from 'components/async_load';
import DataPrefetch from 'components/data_prefetch';
import EditCategoryModal from 'components/edit_category_modal';
import InvitationModal from 'components/invitation_modal';
import KeyboardShortcutsModal from 'components/keyboard_shortcuts/keyboard_shortcuts_modal/keyboard_shortcuts_modal';
import MoreDirectChannels from 'components/more_direct_channels';
import NewChannelModal from 'components/new_channel_modal/new_channel_modal';
import ResizableLhs from 'components/resizable_sidebar/resizable_lhs';
import UserSettingsModal from 'components/user_settings/modal';
import SidebarHeader from 'components/sidebar/sidebar_header';
import Pluggable from 'plugins/pluggable';
import Constants, {ModalIdentifiers, RHSStates} from 'utils/constants';
import * as Keyboard from 'utils/keyboard';
import * as Utils from 'utils/utils';
import {isKeyPressed, cmdOrCtrlPressed} from 'utils/keyboard';
import {localizeMessage} from 'utils/utils';
import type {ModalData} from 'types/actions';
import type {RhsState} from 'types/store/rhs';
import ChannelNavigator from './channel_navigator';
import MobileSidebarHeader from './mobile_sidebar_header';
import SidebarHeader from './sidebar_header';
import SidebarList from './sidebar_list';
const MobileSidebarHeader = makeAsyncComponent('MobileSidebarHeader', lazy(() => import('./mobile_sidebar_header')));
const MoreDirectChannels = makeAsyncComponent('MoreDirectChannels', lazy(() => import('components/more_direct_channels')));
const BrowseChannels = makeAsyncComponent('BrowseChannels', lazy(() => import('components/browse_channels')));
const EditCategoryModal = makeAsyncComponent('EditCategoryModal', lazy(() => import('components/edit_category_modal')));
const CreateUserGroupsModal = makeAsyncComponent('CreateUserGroupsModal', lazy(() => import('components/create_user_groups_modal')));
const InvitationModal = makeAsyncComponent('InvitationModal', lazy(() => import('components/invitation_modal')));
const KeyboardShortcutsModal = makeAsyncComponent('KeyboardShortcutsModal', lazy(() => import('components/keyboard_shortcuts/keyboard_shortcuts_modal/keyboard_shortcuts_modal')));
const NewChannelModal = makeAsyncComponent('NewChannelModal', lazy(() => import('components/new_channel_modal/new_channel_modal')));
const UserSettingsModal = makeAsyncComponent('UserSettingsModal', lazy(() => import('components/user_settings/modal')));
type Props = {
teamId: string;
canCreatePublicChannel: boolean;
@@ -95,15 +97,15 @@ export default class Sidebar extends React.PureComponent<Props, State> {
};
handleKeyDownEvent = (event: KeyboardEvent) => {
if (Keyboard.isKeyPressed(event, Constants.KeyCodes.ESCAPE)) {
if (isKeyPressed(event, Constants.KeyCodes.ESCAPE)) {
this.props.actions.clearChannelSelection();
return;
}
const ctrlOrMetaKeyPressed = Keyboard.cmdOrCtrlPressed(event, true);
const ctrlOrMetaKeyPressed = cmdOrCtrlPressed(event, true);
if (ctrlOrMetaKeyPressed) {
if (Keyboard.isKeyPressed(event, Constants.KeyCodes.FORWARD_SLASH)) {
if (isKeyPressed(event, Constants.KeyCodes.FORWARD_SLASH)) {
event.preventDefault();
if (this.props.isKeyBoardShortcutModalOpen) {
this.props.actions.closeModal(ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL);
@@ -113,7 +115,7 @@ export default class Sidebar extends React.PureComponent<Props, State> {
dialogType: KeyboardShortcutsModal,
});
}
} else if (Keyboard.isKeyPressed(event, Constants.KeyCodes.A) && event.shiftKey) {
} else if (isKeyPressed(event, Constants.KeyCodes.A) && event.shiftKey) {
event.preventDefault();
this.props.actions.openModal({
@@ -225,7 +227,7 @@ export default class Sidebar extends React.PureComponent<Props, State> {
return (<div/>);
}
const ariaLabel = Utils.localizeMessage('accessibility.sections.lhsNavigator', 'channel navigator region');
const ariaLabel = localizeMessage('accessibility.sections.lhsNavigator', 'channel navigator region');
return (
<ResizableLhs

Просмотреть файл

@@ -3,7 +3,7 @@
exports[`SidebarList should match snapshot 1`] = `
<Fragment>
<GlobalThreadsLink />
<Memo(DraftsLink) />
<DraftsLink />
<div
aria-label="channel sidebar region"
className="SidebarNavContainer a11y__region"

Просмотреть файл

@@ -3,7 +3,7 @@
import classNames from 'classnames';
import debounce from 'lodash/debounce';
import React from 'react';
import React, {lazy} from 'react';
import type {CSSProperties} from 'react';
import {DragDropContext, Droppable} from 'react-beautiful-dnd';
import type {DropResult, DragStart, BeforeCapture} from 'react-beautiful-dnd';
@@ -20,20 +20,21 @@ import {General} from 'mattermost-redux/constants';
import {trackEvent} from 'actions/telemetry_actions';
import DraftsLink from 'components/drafts/drafts_link/drafts_link';
import GlobalThreadsLink from 'components/threading/global_threads_link';
import {makeAsyncComponent} from 'components/async_load';
import SidebarCategory from 'components/sidebar/sidebar_category';
import * as ChannelUtils from 'utils/channel_utils';
import {findNextUnreadChannelId} from 'utils/channel_utils';
import {Constants, DraggingStates, DraggingStateTypes} from 'utils/constants';
import * as Keyboard from 'utils/keyboard';
import * as Utils from 'utils/utils';
import {isKeyPressed, cmdOrCtrlPressed} from 'utils/keyboard';
import {localizeMessage, mod} from 'utils/utils';
import type {DraggingState} from 'types/store';
import type {StaticPage} from 'types/store/lhs';
import SidebarCategory from '../sidebar_category';
import UnreadChannelIndicator from '../unread_channel_indicator';
import UnreadChannels from '../unread_channels';
const DraftsLink = makeAsyncComponent('DraftsLink', lazy(() => import('components/drafts/drafts_link/drafts_link')));
const GlobalThreadsLink = makeAsyncComponent('GlobalThreadsLink', lazy(() => import('components/threading/global_threads_link')));
const UnreadChannelIndicator = makeAsyncComponent('UnreadChannelIndicator', lazy(() => import('../unread_channel_indicator')));
const UnreadChannels = makeAsyncComponent('UnreadChannels', lazy(() => import('../unread_channels')));
export function renderView(props: React.HTMLProps<HTMLDivElement>) {
return (
@@ -318,7 +319,7 @@ export default class SidebarList extends React.PureComponent<Props, State> {
};
navigateChannelShortcut = (e: KeyboardEvent) => {
if (e.altKey && !e.shiftKey && !e.ctrlKey && !e.metaKey && (Keyboard.isKeyPressed(e, Constants.KeyCodes.UP) || Keyboard.isKeyPressed(e, Constants.KeyCodes.DOWN))) {
if (e.altKey && !e.shiftKey && !e.ctrlKey && !e.metaKey && (isKeyPressed(e, Constants.KeyCodes.UP) || isKeyPressed(e, Constants.KeyCodes.DOWN))) {
e.preventDefault();
const staticPageIds = this.getDisplayedStaticPageIds();
@@ -328,24 +329,24 @@ export default class SidebarList extends React.PureComponent<Props, State> {
const curIndex = allIds.indexOf(curSelectedId);
let nextIndex;
if (Keyboard.isKeyPressed(e, Constants.KeyCodes.DOWN)) {
if (isKeyPressed(e, Constants.KeyCodes.DOWN)) {
nextIndex = curIndex + 1;
} else {
nextIndex = curIndex - 1;
}
const nextId = allIds[Utils.mod(nextIndex, allIds.length)];
const nextId = allIds[mod(nextIndex, allIds.length)];
this.navigateById(nextId);
if (nextIndex >= staticPageIds.length) {
this.scrollToChannel(nextId);
}
} else if (Keyboard.cmdOrCtrlPressed(e) && e.shiftKey && Keyboard.isKeyPressed(e, Constants.KeyCodes.K)) {
} else if (cmdOrCtrlPressed(e) && e.shiftKey && isKeyPressed(e, Constants.KeyCodes.K)) {
this.props.handleOpenMoreDirectChannelsModal(e);
}
};
navigateUnreadChannelShortcut = (e: KeyboardEvent) => {
if (e.altKey && e.shiftKey && !e.ctrlKey && !e.metaKey && (Keyboard.isKeyPressed(e, Constants.KeyCodes.UP) || Keyboard.isKeyPressed(e, Constants.KeyCodes.DOWN))) {
if (e.altKey && e.shiftKey && !e.ctrlKey && !e.metaKey && (isKeyPressed(e, Constants.KeyCodes.UP) || isKeyPressed(e, Constants.KeyCodes.DOWN))) {
e.preventDefault();
const allChannelIds = this.getDisplayedChannelIds();
@@ -360,13 +361,13 @@ export default class SidebarList extends React.PureComponent<Props, State> {
}
let direction = 0;
if (Keyboard.isKeyPressed(e, Constants.KeyCodes.UP)) {
if (isKeyPressed(e, Constants.KeyCodes.UP)) {
direction = -1;
} else {
direction = 1;
}
const nextIndex = ChannelUtils.findNextUnreadChannelId(
const nextIndex = findNextUnreadChannelId(
this.props.currentChannelId,
allChannelIds,
unreadChannelIds,
@@ -546,7 +547,7 @@ export default class SidebarList extends React.PureComponent<Props, State> {
/>
);
const ariaLabel = Utils.localizeMessage('accessibility.sections.lhsList', 'channel sidebar region');
const ariaLabel = localizeMessage('accessibility.sections.lhsList', 'channel sidebar region');
return (

Просмотреть файл

@@ -11,7 +11,7 @@ import {getIsMobileView} from 'selectors/views/browser';
import type {GlobalState} from 'types/store';
import SidebarRightMenu from './sidebar_right_menu';
import SidebarMobileRightMenu from './sidebar_mobile_right_menu';
function mapStateToProps(state: GlobalState) {
const config = getConfig(state);
@@ -27,4 +27,4 @@ function mapStateToProps(state: GlobalState) {
};
}
export default connect(mapStateToProps)(SidebarRightMenu);
export default connect(mapStateToProps)(SidebarMobileRightMenu);

Просмотреть файл

@@ -131,7 +131,9 @@ describe('identifyElementRegion', () => {
},
);
expect(identifyElementRegion(screen.getAllByText(channel.display_name)[0])).toEqual('channel_sidebar');
await waitFor(() => {
expect(identifyElementRegion(screen.getAllByText(channel.display_name)[0])).toEqual('channel_sidebar');
});
expect(identifyElementRegion(screen.getAllByText(channel.display_name)[1])).toEqual('channel_header');
expect(identifyElementRegion(screen.getAllByText(channel.header)[0])).toEqual('channel_header');