[MM-59299] Investigate further breaking down chunks of components/node_modules for initial load (#27845)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
e104575505
Коммит
7232feecf7
@@ -13,6 +13,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
|
|||||||
<img
|
<img
|
||||||
alt="Files"
|
alt="Files"
|
||||||
className="overlay__files"
|
className="overlay__files"
|
||||||
|
loading="lazy"
|
||||||
src=""
|
src=""
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
@@ -28,6 +29,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
|
|||||||
<img
|
<img
|
||||||
alt="Logo"
|
alt="Logo"
|
||||||
className="overlay__logo"
|
className="overlay__logo"
|
||||||
|
loading="lazy"
|
||||||
src=""
|
src=""
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,6 +50,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
|
|||||||
<img
|
<img
|
||||||
alt="Files"
|
alt="Files"
|
||||||
className="overlay__files"
|
className="overlay__files"
|
||||||
|
loading="lazy"
|
||||||
src=""
|
src=""
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
@@ -63,6 +66,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
|
|||||||
<img
|
<img
|
||||||
alt="Logo"
|
alt="Logo"
|
||||||
className="overlay__logo"
|
className="overlay__logo"
|
||||||
|
loading="lazy"
|
||||||
src=""
|
src=""
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,6 +87,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
|
|||||||
<img
|
<img
|
||||||
alt="Files"
|
alt="Files"
|
||||||
className="overlay__files"
|
className="overlay__files"
|
||||||
|
loading="lazy"
|
||||||
src=""
|
src=""
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
@@ -98,6 +103,7 @@ exports[`components/FileUploadOverlay should match snapshot when file upload is
|
|||||||
<img
|
<img
|
||||||
alt="Logo"
|
alt="Logo"
|
||||||
className="overlay__logo"
|
className="overlay__logo"
|
||||||
|
loading="lazy"
|
||||||
src=""
|
src=""
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import classNames from 'classnames';
|
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 {FormattedMessage, useIntl} from 'react-intl';
|
||||||
import {useDispatch, useSelector} from 'react-redux';
|
import {useDispatch, useSelector} from 'react-redux';
|
||||||
|
|
||||||
@@ -24,9 +24,9 @@ import {makeGetDraft} from 'selectors/rhs';
|
|||||||
import {connectionErrorCount} from 'selectors/views/system';
|
import {connectionErrorCount} from 'selectors/views/system';
|
||||||
import LocalStorageStore from 'stores/local_storage_store';
|
import LocalStorageStore from 'stores/local_storage_store';
|
||||||
|
|
||||||
|
import {makeAsyncComponent} from 'components/async_load';
|
||||||
import AutoHeightSwitcher from 'components/common/auto_height_switcher';
|
import AutoHeightSwitcher from 'components/common/auto_height_switcher';
|
||||||
import useDidUpdate from 'components/common/hooks/useDidUpdate';
|
import useDidUpdate from 'components/common/hooks/useDidUpdate';
|
||||||
import FileLimitStickyBanner from 'components/file_limit_sticky_banner';
|
|
||||||
import MessageSubmitError from 'components/message_submit_error';
|
import MessageSubmitError from 'components/message_submit_error';
|
||||||
import MsgTyping from 'components/msg_typing';
|
import MsgTyping from 'components/msg_typing';
|
||||||
import RhsSuggestionList from 'components/suggestion/rhs_suggestion_list';
|
import RhsSuggestionList from 'components/suggestion/rhs_suggestion_list';
|
||||||
@@ -66,6 +66,8 @@ import useUploadFiles from './use_upload_files';
|
|||||||
|
|
||||||
import './advanced_text_editor.scss';
|
import './advanced_text_editor.scss';
|
||||||
|
|
||||||
|
const FileLimitStickyBanner = makeAsyncComponent('FileLimitStickyBanner', lazy(() => import('components/file_limit_sticky_banner')));
|
||||||
|
|
||||||
function isDraftEmpty(draft: PostDraft) {
|
function isDraftEmpty(draft: PostDraft) {
|
||||||
return draft.message === '' && draft.fileInfos.length === 0 && draft.uploadsInProgress.length === 0;
|
return draft.message === '' && draft.fileInfos.length === 0 && draft.uploadsInProgress.length === 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react';
|
import React, {lazy} from 'react';
|
||||||
import {hot} from 'react-hot-loader/root';
|
import {hot} from 'react-hot-loader/root';
|
||||||
import {Provider} from 'react-redux';
|
import {Provider} from 'react-redux';
|
||||||
import {Router} from 'react-router-dom';
|
import {Router} from 'react-router-dom';
|
||||||
@@ -11,7 +11,7 @@ import store from 'stores/redux_store';
|
|||||||
import {makeAsyncComponent} from 'components/async_load';
|
import {makeAsyncComponent} from 'components/async_load';
|
||||||
|
|
||||||
import {getHistory} from 'utils/browser_history';
|
import {getHistory} from 'utils/browser_history';
|
||||||
const LazyRoot = React.lazy(() => import('components/root'));
|
const LazyRoot = lazy(() => import('components/root'));
|
||||||
|
|
||||||
const Root = makeAsyncComponent('Root', LazyRoot);
|
const Root = makeAsyncComponent('Root', LazyRoot);
|
||||||
|
|
||||||
|
|||||||
@@ -2,43 +2,34 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React, {lazy} from 'react';
|
||||||
import {Route, Switch, Redirect} from 'react-router-dom';
|
import {Route, Switch, Redirect} from 'react-router-dom';
|
||||||
|
|
||||||
import {makeAsyncComponent} from 'components/async_load';
|
import {makeAsyncComponent} from 'components/async_load';
|
||||||
import ChannelIdentifierRouter from 'components/channel_layout/channel_identifier_router';
|
import ChannelIdentifierRouter from 'components/channel_layout/channel_identifier_router';
|
||||||
import PlaybookRunner from 'components/channel_layout/playbook_runner';
|
|
||||||
import LoadingScreen from 'components/loading_screen';
|
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 {IDENTIFIER_PATH_PATTERN, ID_PATH_PATTERN, TEAM_NAME_PATH_PATTERN} from 'utils/path';
|
||||||
|
|
||||||
import type {OwnProps, PropsFromRedux} from './index';
|
import type {OwnProps, PropsFromRedux} from './index';
|
||||||
|
|
||||||
const LazyChannelHeaderMobile = makeAsyncComponent(
|
const ChannelHeaderMobile = makeAsyncComponent('ChannelHeaderMobile', lazy(() => import('components/channel_header_mobile')));
|
||||||
'LazyChannelHeaderMobile',
|
const GlobalThreads = makeAsyncComponent('GlobalThreads', lazy(() => import('components/threading/global_threads')),
|
||||||
React.lazy(() => import('components/channel_header_mobile')),
|
|
||||||
);
|
|
||||||
|
|
||||||
const LazyGlobalThreads = makeAsyncComponent(
|
|
||||||
'LazyGlobalThreads',
|
|
||||||
React.lazy(() => import('components/threading/global_threads')),
|
|
||||||
(
|
(
|
||||||
<div className='app__content'>
|
<div className='app__content'>
|
||||||
<LoadingScreen/>
|
<LoadingScreen/>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
const Drafts = makeAsyncComponent('Drafts', lazy(() => import('components/drafts')),
|
||||||
const LazyDrafts = makeAsyncComponent(
|
|
||||||
'LazyDrafts',
|
|
||||||
React.lazy(() => import('components/drafts')),
|
|
||||||
(
|
(
|
||||||
<div className='app__content'>
|
<div className='app__content'>
|
||||||
<LoadingScreen/>
|
<LoadingScreen/>
|
||||||
</div>
|
</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;
|
type Props = PropsFromRedux & OwnProps;
|
||||||
|
|
||||||
@@ -85,11 +76,13 @@ export default class CenterChannel extends React.PureComponent<Props, State> {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{isMobileView && (
|
{isMobileView && (
|
||||||
|
<>
|
||||||
<div className='row header'>
|
<div className='row header'>
|
||||||
<div id='navbar_wrapper'>
|
<div id='navbar_wrapper'>
|
||||||
<LazyChannelHeaderMobile/>
|
<ChannelHeaderMobile/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<div className='row main'>
|
<div className='row main'>
|
||||||
<Switch>
|
<Switch>
|
||||||
@@ -114,12 +107,12 @@ export default class CenterChannel extends React.PureComponent<Props, State> {
|
|||||||
{isCollapsedThreadsEnabled ? (
|
{isCollapsedThreadsEnabled ? (
|
||||||
<Route
|
<Route
|
||||||
path={`/:team(${TEAM_NAME_PATH_PATTERN})/threads/:threadIdentifier(${ID_PATH_PATTERN})?`}
|
path={`/:team(${TEAM_NAME_PATH_PATTERN})/threads/:threadIdentifier(${ID_PATH_PATTERN})?`}
|
||||||
component={LazyGlobalThreads}
|
component={GlobalThreads}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<Route
|
<Route
|
||||||
path={`/:team(${TEAM_NAME_PATH_PATTERN})/drafts`}
|
path={`/:team(${TEAM_NAME_PATH_PATTERN})/drafts`}
|
||||||
component={LazyDrafts}
|
component={Drafts}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Redirect to={lastChannelPath}/>
|
<Redirect to={lastChannelPath}/>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, {useEffect} from 'react';
|
import React, {lazy, useEffect} from 'react';
|
||||||
import {useDispatch, useSelector} from 'react-redux';
|
import {useDispatch, useSelector} from 'react-redux';
|
||||||
|
|
||||||
import {cleanUpStatusAndProfileFetchingPoll} from 'mattermost-redux/actions/status_profile_polling';
|
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 {addVisibleUsersInCurrentChannelToStatusPoll} from 'actions/status_actions';
|
||||||
|
|
||||||
|
import {makeAsyncComponent} from 'components/async_load';
|
||||||
import CenterChannel from 'components/channel_layout/center_channel';
|
import CenterChannel from 'components/channel_layout/center_channel';
|
||||||
import LoadingScreen from 'components/loading_screen';
|
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 Sidebar from 'components/sidebar';
|
||||||
import CRTPostsChannelResetWatcher from 'components/threading/channel_threads/posts_channel_reset_watcher';
|
import CRTPostsChannelResetWatcher from 'components/threading/channel_threads/posts_channel_reset_watcher';
|
||||||
import UnreadsStatusHandler from 'components/unreads_status_handler';
|
import UnreadsStatusHandler from 'components/unreads_status_handler';
|
||||||
@@ -22,6 +21,9 @@ import Pluggable from 'plugins/pluggable';
|
|||||||
import {Constants} from 'utils/constants';
|
import {Constants} from 'utils/constants';
|
||||||
import {isInternetExplorer, isEdge} from 'utils/user_agent';
|
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'];
|
const BODY_CLASS_FOR_CHANNEL = ['app__body', 'channel-view'];
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ exports[`components/channel_view Should match snapshot if channel is archived 1`
|
|||||||
<FileUploadOverlay
|
<FileUploadOverlay
|
||||||
overlayType="center"
|
overlayType="center"
|
||||||
/>
|
/>
|
||||||
<withRouter(Connect(injectIntl(ChannelHeader)))
|
<ChannelHeader
|
||||||
channelId="channelId"
|
channelId="channelId"
|
||||||
channelIsArchived={true}
|
channelIsArchived={true}
|
||||||
deactivatedChannel={false}
|
deactivatedChannel={false}
|
||||||
@@ -66,7 +66,7 @@ exports[`components/channel_view Should match snapshot if channel is deactivated
|
|||||||
<FileUploadOverlay
|
<FileUploadOverlay
|
||||||
overlayType="center"
|
overlayType="center"
|
||||||
/>
|
/>
|
||||||
<withRouter(Connect(injectIntl(ChannelHeader)))
|
<ChannelHeader
|
||||||
channelId="channelId"
|
channelId="channelId"
|
||||||
channelIsArchived={false}
|
channelIsArchived={false}
|
||||||
deactivatedChannel={true}
|
deactivatedChannel={true}
|
||||||
@@ -123,7 +123,7 @@ exports[`components/channel_view Should match snapshot with base props 1`] = `
|
|||||||
<FileUploadOverlay
|
<FileUploadOverlay
|
||||||
overlayType="center"
|
overlayType="center"
|
||||||
/>
|
/>
|
||||||
<withRouter(Connect(injectIntl(ChannelHeader)))
|
<ChannelHeader
|
||||||
channelId="channelId"
|
channelId="channelId"
|
||||||
channelIsArchived={false}
|
channelIsArchived={false}
|
||||||
deactivatedChannel={false}
|
deactivatedChannel={false}
|
||||||
@@ -152,7 +152,7 @@ exports[`components/channel_view Should match snapshot with base props 1`] = `
|
|||||||
data-testid="post-create"
|
data-testid="post-create"
|
||||||
id="post-create"
|
id="post-create"
|
||||||
>
|
>
|
||||||
<Memo(AdvancedCreatePost) />
|
<AdvancedCreatePost />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React from 'react';
|
import React, {lazy} from 'react';
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
import type {RouteComponentProps} from 'react-router-dom';
|
import type {RouteComponentProps} from 'react-router-dom';
|
||||||
|
|
||||||
import AdvancedCreatePost from 'components/advanced_create_post';
|
import {makeAsyncComponent} from 'components/async_load';
|
||||||
import ChannelBookmarks from 'components/channel_bookmarks';
|
|
||||||
import ChannelHeader from 'components/channel_header';
|
|
||||||
import deferComponentRender from 'components/deferComponentRender';
|
import deferComponentRender from 'components/deferComponentRender';
|
||||||
import FileUploadOverlay from 'components/file_upload_overlay';
|
|
||||||
import FormattedMarkdownMessage from 'components/formatted_markdown_message';
|
import FormattedMarkdownMessage from 'components/formatted_markdown_message';
|
||||||
import PostView from 'components/post_view';
|
import PostView from 'components/post_view';
|
||||||
|
|
||||||
@@ -17,6 +14,11 @@ import WebSocketClient from 'client/web_websocket_client';
|
|||||||
|
|
||||||
import type {PropsFromRedux} from './index';
|
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<{
|
export type Props = PropsFromRedux & RouteComponentProps<{
|
||||||
postid?: string;
|
postid?: string;
|
||||||
}>;
|
}>;
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const FileUploadOverlay = (props: Props) => {
|
|||||||
className='overlay__files'
|
className='overlay__files'
|
||||||
src={fileOverlayImage}
|
src={fileOverlayImage}
|
||||||
alt='Files'
|
alt='Files'
|
||||||
|
loading='lazy'
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
<i
|
<i
|
||||||
@@ -44,6 +45,7 @@ const FileUploadOverlay = (props: Props) => {
|
|||||||
className='overlay__logo'
|
className='overlay__logo'
|
||||||
src={overlayLogoImage}
|
src={overlayLogoImage}
|
||||||
alt='Logo'
|
alt='Logo'
|
||||||
|
loading='lazy'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
40
webapp/channels/src/components/logged_in_route/index.tsx
Обычный файл
40
webapp/channels/src/components/logged_in_route/index.tsx
Обычный файл
@@ -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 classNames from 'classnames';
|
||||||
import deepEqual from 'fast-deep-equal';
|
import deepEqual from 'fast-deep-equal';
|
||||||
import React from 'react';
|
import React, {lazy} from 'react';
|
||||||
import {Route, Switch, Redirect} from 'react-router-dom';
|
import {Route, Switch, Redirect} from 'react-router-dom';
|
||||||
import type {RouteComponentProps} 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 {setUrl} from 'mattermost-redux/actions/general';
|
||||||
import {Client4} from 'mattermost-redux/client';
|
import {Client4} from 'mattermost-redux/client';
|
||||||
import {rudderAnalytics, RudderTelemetryHandler} from 'mattermost-redux/client/rudder';
|
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 {measurePageLoadTelemetry, temporarilySetPageLoadContext, trackEvent, trackSelectorMetrics} from 'actions/telemetry_actions.jsx';
|
||||||
import BrowserStore from 'stores/browser_store';
|
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 {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 OpenPluginInstallPost from 'components/custom_open_plugin_install_post_renderer';
|
||||||
import GlobalHeader from 'components/global_header/global_header';
|
import GlobalHeader from 'components/global_header/global_header';
|
||||||
import {HFRoute} from 'components/header_footer_route/header_footer_route';
|
import {HFRoute} from 'components/header_footer_route/header_footer_route';
|
||||||
import {HFTRoute, LoggedInHFTRoute} from 'components/header_footer_template_route';
|
import {HFTRoute, LoggedInHFTRoute} from 'components/header_footer_template_route';
|
||||||
import InitialLoadingScreen from 'components/initial_loading_screen';
|
import InitialLoadingScreen from 'components/initial_loading_screen';
|
||||||
import MobileViewWatcher from 'components/mobile_view_watcher';
|
import LoggedIn from 'components/logged_in';
|
||||||
import ModalController from 'components/modal_controller';
|
import LoggedInRoute from 'components/logged_in_route';
|
||||||
import LaunchingWorkspace, {LAUNCHING_WORKSPACE_FULLSCREEN_Z_INDEX} from 'components/preparing_workspace/launching_workspace';
|
import {LAUNCHING_WORKSPACE_FULLSCREEN_Z_INDEX} from 'components/preparing_workspace/launching_workspace';
|
||||||
import {Animations} from 'components/preparing_workspace/steps';
|
import {Animations} from 'components/preparing_workspace/steps';
|
||||||
import SidebarRight from 'components/sidebar_right';
|
import SidebarMobileRightMenu from 'components/sidebar_mobile_right_menu';
|
||||||
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 webSocketClient from 'client/web_websocket_client';
|
import webSocketClient from 'client/web_websocket_client';
|
||||||
import {initializePlugins} from 'plugins';
|
import {initializePlugins} from 'plugins';
|
||||||
import Pluggable from 'plugins/pluggable';
|
|
||||||
import A11yController from 'utils/a11y_controller';
|
import A11yController from 'utils/a11y_controller';
|
||||||
import {PageLoadContext} from 'utils/constants';
|
import {PageLoadContext} from 'utils/constants';
|
||||||
import {EmojiIndicesByAlias} from 'utils/emoji';
|
import {EmojiIndicesByAlias} from 'utils/emoji';
|
||||||
import {TEAM_NAME_PATH_PATTERN} from 'utils/path';
|
import {TEAM_NAME_PATH_PATTERN} from 'utils/path';
|
||||||
import {getSiteURL} from 'utils/url';
|
import {getSiteURL} from 'utils/url';
|
||||||
import * as UserAgent from 'utils/user_agent';
|
import {isAndroidWeb, isChromebook, isDesktopApp, isIosWeb} from 'utils/user_agent';
|
||||||
import * as Utils from 'utils/utils';
|
import {applyTheme, isTextDroppableEvent} from 'utils/utils';
|
||||||
|
|
||||||
import LuxonController from './luxon_controller';
|
import LuxonController from './luxon_controller';
|
||||||
import PerformanceReporterController from './performance_reporter_controller';
|
import PerformanceReporterController from './performance_reporter_controller';
|
||||||
@@ -59,68 +48,36 @@ import type {PropsFromRedux} from './index';
|
|||||||
|
|
||||||
import 'plugins/export.js';
|
import 'plugins/export.js';
|
||||||
|
|
||||||
const LazyErrorPage = React.lazy(() => import('components/error_page'));
|
const MobileViewWatcher = makeAsyncComponent('MobileViewWatcher', lazy(() => import('components/mobile_view_watcher')));
|
||||||
const LazyLogin = React.lazy(() => import('components/login/login'));
|
const WindowSizeObserver = makeAsyncComponent('WindowSizeObserver', lazy(() => import('components/window_size_observer/WindowSizeObserver')));
|
||||||
const LazyAdminConsole = React.lazy(() => import('components/admin_console'));
|
const ErrorPage = makeAsyncComponent('ErrorPage', lazy(() => import('components/error_page')));
|
||||||
const LazyLoggedIn = React.lazy(() => import('components/logged_in'));
|
const Login = makeAsyncComponent('LoginController', lazy(() => import('components/login/login')));
|
||||||
const LazyPasswordResetSendLink = React.lazy(() => import('components/password_reset_send_link'));
|
const AccessProblem = makeAsyncComponent('AccessProblem', lazy(() => import('components/access_problem')));
|
||||||
const LazyPasswordResetForm = React.lazy(() => import('components/password_reset_form'));
|
const PasswordResetSendLink = makeAsyncComponent('PasswordResedSendLink', lazy(() => import('components/password_reset_send_link')));
|
||||||
const LazySignup = React.lazy(() => import('components/signup/signup'));
|
const PasswordResetForm = makeAsyncComponent('PasswordResetForm', lazy(() => import('components/password_reset_form')));
|
||||||
const LazyTermsOfService = React.lazy(() => import('components/terms_of_service'));
|
const Signup = makeAsyncComponent('SignupController', lazy(() => import('components/signup/signup')));
|
||||||
const LazyShouldVerifyEmail = React.lazy(() => import('components/should_verify_email/should_verify_email'));
|
const ShouldVerifyEmail = makeAsyncComponent('ShouldVerifyEmail', lazy(() => import('components/should_verify_email/should_verify_email')));
|
||||||
const LazyDoVerifyEmail = React.lazy(() => import('components/do_verify_email/do_verify_email'));
|
const DoVerifyEmail = makeAsyncComponent('DoVerifyEmail', lazy(() => import('components/do_verify_email/do_verify_email')));
|
||||||
const LazyClaimController = React.lazy(() => import('components/claim'));
|
const ClaimController = makeAsyncComponent('ClaimController', lazy(() => import('components/claim')));
|
||||||
const LazyLinkingLandingPage = React.lazy(() => import('components/linking_landing_page'));
|
const TermsOfService = makeAsyncComponent('TermsOfService', lazy(() => import('components/terms_of_service')));
|
||||||
const LazySelectTeam = React.lazy(() => import('components/select_team'));
|
const LinkingLandingPage = makeAsyncComponent('LinkingLandingPage', lazy(() => import('components/linking_landing_page')));
|
||||||
const LazyAuthorize = React.lazy(() => import('components/authorize'));
|
const AdminConsole = makeAsyncComponent('AdminConsole', lazy(() => import('components/admin_console')));
|
||||||
const LazyCreateTeam = React.lazy(() => import('components/create_team'));
|
const SelectTeam = makeAsyncComponent('SelectTeam', lazy(() => import('components/select_team')));
|
||||||
const LazyMfa = React.lazy(() => import('components/mfa/mfa_controller'));
|
const Authorize = makeAsyncComponent('Authorize', lazy(() => import('components/authorize')));
|
||||||
const LazyPreparingWorkspace = React.lazy(() => import('components/preparing_workspace'));
|
const CreateTeam = makeAsyncComponent('CreateTeam', lazy(() => import('components/create_team')));
|
||||||
const LazyTeamController = React.lazy(() => import('components/team_controller'));
|
const Mfa = makeAsyncComponent('Mfa', lazy(() => import('components/mfa/mfa_controller')));
|
||||||
const LazyOnBoardingTaskList = React.lazy(() => import('components/onboarding_tasklist'));
|
const PreparingWorkspace = makeAsyncComponent('PreparingWorkspace', lazy(() => import('components/preparing_workspace')));
|
||||||
|
const Pluggable = makeAsyncComponent('Pluggable', lazy(() => import('plugins/pluggable')));
|
||||||
const CreateTeam = makeAsyncComponent('CreateTeam', LazyCreateTeam);
|
const LaunchingWorkspace = makeAsyncComponent('LaunchingWorkspace', lazy(() => import('components/preparing_workspace/launching_workspace')));
|
||||||
const ErrorPage = makeAsyncComponent('ErrorPage', LazyErrorPage);
|
const CompassThemeProvider = makeAsyncComponent('CompassThemeProvider', lazy(() => import('components/compass_theme_provider/compass_theme_provider')));
|
||||||
const TermsOfService = makeAsyncComponent('TermsOfService', LazyTermsOfService);
|
const TeamController = makeAsyncComponent('TeamController', lazy(() => import('components/team_controller')));
|
||||||
const Login = makeAsyncComponent('LoginController', LazyLogin);
|
const AnnouncementBarController = makeAsyncComponent('AnnouncementBarController', lazy(() => import('components/announcement_bar')));
|
||||||
const AdminConsole = makeAsyncComponent('AdminConsole', LazyAdminConsole);
|
const SystemNotice = makeAsyncComponent('SystemNotice', lazy(() => import('components/system_notice')));
|
||||||
const LoggedIn = makeAsyncComponent('LoggedIn', LazyLoggedIn);
|
const CloudEffects = makeAsyncComponent('CloudEffects', lazy(() => import('components/cloud_effects')));
|
||||||
const PasswordResetSendLink = makeAsyncComponent('PasswordResedSendLink', LazyPasswordResetSendLink);
|
const TeamSidebar = makeAsyncComponent('TeamSidebar', lazy(() => import('components/team_sidebar')));
|
||||||
const PasswordResetForm = makeAsyncComponent('PasswordResetForm', LazyPasswordResetForm);
|
const SidebarRight = makeAsyncComponent('SidebarRight', lazy(() => import('components/sidebar_right')));
|
||||||
const Signup = makeAsyncComponent('SignupController', LazySignup);
|
const ModalController = makeAsyncComponent('ModalController', lazy(() => import('components/modal_controller')));
|
||||||
const ShouldVerifyEmail = makeAsyncComponent('ShouldVerifyEmail', LazyShouldVerifyEmail);
|
const AppBar = makeAsyncComponent('AppBar', lazy(() => import('components/app_bar/app_bar')));
|
||||||
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 noop = () => {};
|
const noop = () => {};
|
||||||
|
|
||||||
@@ -138,7 +95,6 @@ export default class Root extends React.PureComponent<Props, State> {
|
|||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
// Redux
|
|
||||||
setUrl(getSiteURL());
|
setUrl(getSiteURL());
|
||||||
|
|
||||||
// Disable auth header to enable CSRF check
|
// 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()));
|
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 = {
|
this.state = {
|
||||||
shouldMountAppRoutes: false,
|
shouldMountAppRoutes: false,
|
||||||
};
|
};
|
||||||
@@ -251,23 +189,23 @@ export default class Root extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
this.showLandingPageIfNecessary();
|
this.showLandingPageIfNecessary();
|
||||||
|
|
||||||
Utils.applyTheme(this.props.theme);
|
applyTheme(this.props.theme);
|
||||||
};
|
};
|
||||||
|
|
||||||
private showLandingPageIfNecessary = () => {
|
private showLandingPageIfNecessary = () => {
|
||||||
// We have nothing to redirect to if we're already on Desktop App
|
// We have nothing to redirect to if we're already on Desktop App
|
||||||
// Chromebook has no Desktop App to switch to
|
// Chromebook has no Desktop App to switch to
|
||||||
if (UserAgent.isDesktopApp() || UserAgent.isChromebook()) {
|
if (isDesktopApp() || isChromebook()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing to link to if we've removed the Android App download link
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing to link to if we've removed the iOS App download link
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +250,7 @@ export default class Root extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
componentDidUpdate(prevProps: Props, prevState: State) {
|
componentDidUpdate(prevProps: Props, prevState: State) {
|
||||||
if (!deepEqual(prevProps.theme, this.props.theme)) {
|
if (!deepEqual(prevProps.theme, this.props.theme)) {
|
||||||
Utils.applyTheme(this.props.theme);
|
applyTheme(this.props.theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.location.pathname === '/') {
|
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() {
|
componentDidMount() {
|
||||||
temporarilySetPageLoadContext(PageLoadContext.PAGE_LOAD);
|
temporarilySetPageLoadContext(PageLoadContext.PAGE_LOAD);
|
||||||
|
|
||||||
@@ -394,10 +346,20 @@ export default class Root extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
measurePageLoadTelemetry();
|
measurePageLoadTelemetry();
|
||||||
trackSelectorMetrics();
|
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() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('storage', this.handleLogoutLoginSignal);
|
window.removeEventListener('storage', this.handleLogoutLoginSignal);
|
||||||
|
document.removeEventListener('drop', this.handleDropEvent);
|
||||||
|
document.removeEventListener('dragover', this.handleDragOverEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLogoutLoginSignal = (e: StorageEvent) => {
|
handleLogoutLoginSignal = (e: StorageEvent) => {
|
||||||
@@ -600,7 +562,7 @@ export default class Root extends React.PureComponent<Props, State> {
|
|||||||
</div>
|
</div>
|
||||||
<Pluggable pluggableName='Global'/>
|
<Pluggable pluggableName='Global'/>
|
||||||
<AppBar/>
|
<AppBar/>
|
||||||
<SidebarRightMenu/>
|
<SidebarMobileRightMenu/>
|
||||||
</CompassThemeProvider>
|
</CompassThemeProvider>
|
||||||
</Switch>
|
</Switch>
|
||||||
</RootProvider>
|
</RootProvider>
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ exports[`components/sidebar should match snapshot when direct channels modal is
|
|||||||
onDragStart={[Function]}
|
onDragStart={[Function]}
|
||||||
/>
|
/>
|
||||||
<Connect(DataPrefetch) />
|
<Connect(DataPrefetch) />
|
||||||
<Connect(MoreDirectChannels)
|
<MoreDirectChannels
|
||||||
isExistingChannel={false}
|
isExistingChannel={false}
|
||||||
onModalDismissed={[Function]}
|
onModalDismissed={[Function]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type {DeepPartial} from '@mattermost/types/utilities';
|
|||||||
import {Preferences} from 'mattermost-redux/constants';
|
import {Preferences} from 'mattermost-redux/constants';
|
||||||
|
|
||||||
import mergeObjects from 'packages/mattermost-redux/test/merge_objects';
|
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 Constants, {ModalIdentifiers} from 'utils/constants';
|
||||||
import {TestHelper} from 'utils/test_helper';
|
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 = {
|
const testState = {
|
||||||
entities: {
|
entities: {
|
||||||
channels: {
|
channels: {
|
||||||
@@ -185,10 +185,12 @@ describe('components/sidebar', () => {
|
|||||||
mergeObjects(baseState, testState),
|
mergeObjects(baseState, testState),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.queryByText('UNREADS')).not.toBeInTheDocument();
|
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> = {
|
const testState: DeepPartial<GlobalState> = {
|
||||||
entities: {
|
entities: {
|
||||||
channels: {
|
channels: {
|
||||||
@@ -209,10 +211,12 @@ describe('components/sidebar', () => {
|
|||||||
mergeObjects(baseState, testState),
|
mergeObjects(baseState, testState),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.queryByText('UNREADS')).toBeInTheDocument();
|
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> = {
|
const testState: DeepPartial<GlobalState> = {
|
||||||
entities: {
|
entities: {
|
||||||
preferences: {
|
preferences: {
|
||||||
@@ -228,10 +232,12 @@ describe('components/sidebar', () => {
|
|||||||
mergeObjects(baseState, testState),
|
mergeObjects(baseState, testState),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.queryByText('UNREADS')).not.toBeInTheDocument();
|
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> = {
|
const testState: DeepPartial<GlobalState> = {
|
||||||
entities: {
|
entities: {
|
||||||
preferences: {
|
preferences: {
|
||||||
@@ -252,7 +258,9 @@ describe('components/sidebar', () => {
|
|||||||
mergeObjects(baseState, testState),
|
mergeObjects(baseState, testState),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(screen.queryByText('UNREADS')).toBeInTheDocument();
|
expect(screen.queryByText('UNREADS')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,34 +2,36 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React, {lazy} from 'react';
|
||||||
|
|
||||||
import {trackEvent} from 'actions/telemetry_actions';
|
import {trackEvent} from 'actions/telemetry_actions';
|
||||||
|
|
||||||
import BrowseChannels from 'components/browse_channels';
|
import {makeAsyncComponent} from 'components/async_load';
|
||||||
import CreateUserGroupsModal from 'components/create_user_groups_modal';
|
|
||||||
import DataPrefetch from 'components/data_prefetch';
|
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 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 Pluggable from 'plugins/pluggable';
|
||||||
import Constants, {ModalIdentifiers, RHSStates} from 'utils/constants';
|
import Constants, {ModalIdentifiers, RHSStates} from 'utils/constants';
|
||||||
import * as Keyboard from 'utils/keyboard';
|
import {isKeyPressed, cmdOrCtrlPressed} from 'utils/keyboard';
|
||||||
import * as Utils from 'utils/utils';
|
import {localizeMessage} from 'utils/utils';
|
||||||
|
|
||||||
import type {ModalData} from 'types/actions';
|
import type {ModalData} from 'types/actions';
|
||||||
import type {RhsState} from 'types/store/rhs';
|
import type {RhsState} from 'types/store/rhs';
|
||||||
|
|
||||||
import ChannelNavigator from './channel_navigator';
|
import ChannelNavigator from './channel_navigator';
|
||||||
import MobileSidebarHeader from './mobile_sidebar_header';
|
|
||||||
import SidebarHeader from './sidebar_header';
|
|
||||||
import SidebarList from './sidebar_list';
|
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 = {
|
type Props = {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
canCreatePublicChannel: boolean;
|
canCreatePublicChannel: boolean;
|
||||||
@@ -95,15 +97,15 @@ export default class Sidebar extends React.PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleKeyDownEvent = (event: KeyboardEvent) => {
|
handleKeyDownEvent = (event: KeyboardEvent) => {
|
||||||
if (Keyboard.isKeyPressed(event, Constants.KeyCodes.ESCAPE)) {
|
if (isKeyPressed(event, Constants.KeyCodes.ESCAPE)) {
|
||||||
this.props.actions.clearChannelSelection();
|
this.props.actions.clearChannelSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ctrlOrMetaKeyPressed = Keyboard.cmdOrCtrlPressed(event, true);
|
const ctrlOrMetaKeyPressed = cmdOrCtrlPressed(event, true);
|
||||||
|
|
||||||
if (ctrlOrMetaKeyPressed) {
|
if (ctrlOrMetaKeyPressed) {
|
||||||
if (Keyboard.isKeyPressed(event, Constants.KeyCodes.FORWARD_SLASH)) {
|
if (isKeyPressed(event, Constants.KeyCodes.FORWARD_SLASH)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (this.props.isKeyBoardShortcutModalOpen) {
|
if (this.props.isKeyBoardShortcutModalOpen) {
|
||||||
this.props.actions.closeModal(ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL);
|
this.props.actions.closeModal(ModalIdentifiers.KEYBOARD_SHORTCUTS_MODAL);
|
||||||
@@ -113,7 +115,7 @@ export default class Sidebar extends React.PureComponent<Props, State> {
|
|||||||
dialogType: KeyboardShortcutsModal,
|
dialogType: KeyboardShortcutsModal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (Keyboard.isKeyPressed(event, Constants.KeyCodes.A) && event.shiftKey) {
|
} else if (isKeyPressed(event, Constants.KeyCodes.A) && event.shiftKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
this.props.actions.openModal({
|
this.props.actions.openModal({
|
||||||
@@ -225,7 +227,7 @@ export default class Sidebar extends React.PureComponent<Props, State> {
|
|||||||
return (<div/>);
|
return (<div/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ariaLabel = Utils.localizeMessage('accessibility.sections.lhsNavigator', 'channel navigator region');
|
const ariaLabel = localizeMessage('accessibility.sections.lhsNavigator', 'channel navigator region');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizableLhs
|
<ResizableLhs
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
exports[`SidebarList should match snapshot 1`] = `
|
exports[`SidebarList should match snapshot 1`] = `
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<GlobalThreadsLink />
|
<GlobalThreadsLink />
|
||||||
<Memo(DraftsLink) />
|
<DraftsLink />
|
||||||
<div
|
<div
|
||||||
aria-label="channel sidebar region"
|
aria-label="channel sidebar region"
|
||||||
className="SidebarNavContainer a11y__region"
|
className="SidebarNavContainer a11y__region"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import React from 'react';
|
import React, {lazy} from 'react';
|
||||||
import type {CSSProperties} from 'react';
|
import type {CSSProperties} from 'react';
|
||||||
import {DragDropContext, Droppable} from 'react-beautiful-dnd';
|
import {DragDropContext, Droppable} from 'react-beautiful-dnd';
|
||||||
import type {DropResult, DragStart, BeforeCapture} 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 {trackEvent} from 'actions/telemetry_actions';
|
||||||
|
|
||||||
import DraftsLink from 'components/drafts/drafts_link/drafts_link';
|
import {makeAsyncComponent} from 'components/async_load';
|
||||||
import GlobalThreadsLink from 'components/threading/global_threads_link';
|
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 {Constants, DraggingStates, DraggingStateTypes} from 'utils/constants';
|
||||||
import * as Keyboard from 'utils/keyboard';
|
import {isKeyPressed, cmdOrCtrlPressed} from 'utils/keyboard';
|
||||||
import * as Utils from 'utils/utils';
|
import {localizeMessage, mod} from 'utils/utils';
|
||||||
|
|
||||||
import type {DraggingState} from 'types/store';
|
import type {DraggingState} from 'types/store';
|
||||||
import type {StaticPage} from 'types/store/lhs';
|
import type {StaticPage} from 'types/store/lhs';
|
||||||
|
|
||||||
import SidebarCategory from '../sidebar_category';
|
const DraftsLink = makeAsyncComponent('DraftsLink', lazy(() => import('components/drafts/drafts_link/drafts_link')));
|
||||||
import UnreadChannelIndicator from '../unread_channel_indicator';
|
const GlobalThreadsLink = makeAsyncComponent('GlobalThreadsLink', lazy(() => import('components/threading/global_threads_link')));
|
||||||
import UnreadChannels from '../unread_channels';
|
const UnreadChannelIndicator = makeAsyncComponent('UnreadChannelIndicator', lazy(() => import('../unread_channel_indicator')));
|
||||||
|
const UnreadChannels = makeAsyncComponent('UnreadChannels', lazy(() => import('../unread_channels')));
|
||||||
|
|
||||||
export function renderView(props: React.HTMLProps<HTMLDivElement>) {
|
export function renderView(props: React.HTMLProps<HTMLDivElement>) {
|
||||||
return (
|
return (
|
||||||
@@ -318,7 +319,7 @@ export default class SidebarList extends React.PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
navigateChannelShortcut = (e: KeyboardEvent) => {
|
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();
|
e.preventDefault();
|
||||||
|
|
||||||
const staticPageIds = this.getDisplayedStaticPageIds();
|
const staticPageIds = this.getDisplayedStaticPageIds();
|
||||||
@@ -328,24 +329,24 @@ export default class SidebarList extends React.PureComponent<Props, State> {
|
|||||||
const curIndex = allIds.indexOf(curSelectedId);
|
const curIndex = allIds.indexOf(curSelectedId);
|
||||||
|
|
||||||
let nextIndex;
|
let nextIndex;
|
||||||
if (Keyboard.isKeyPressed(e, Constants.KeyCodes.DOWN)) {
|
if (isKeyPressed(e, Constants.KeyCodes.DOWN)) {
|
||||||
nextIndex = curIndex + 1;
|
nextIndex = curIndex + 1;
|
||||||
} else {
|
} else {
|
||||||
nextIndex = curIndex - 1;
|
nextIndex = curIndex - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextId = allIds[Utils.mod(nextIndex, allIds.length)];
|
const nextId = allIds[mod(nextIndex, allIds.length)];
|
||||||
this.navigateById(nextId);
|
this.navigateById(nextId);
|
||||||
if (nextIndex >= staticPageIds.length) {
|
if (nextIndex >= staticPageIds.length) {
|
||||||
this.scrollToChannel(nextId);
|
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);
|
this.props.handleOpenMoreDirectChannelsModal(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
navigateUnreadChannelShortcut = (e: KeyboardEvent) => {
|
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();
|
e.preventDefault();
|
||||||
|
|
||||||
const allChannelIds = this.getDisplayedChannelIds();
|
const allChannelIds = this.getDisplayedChannelIds();
|
||||||
@@ -360,13 +361,13 @@ export default class SidebarList extends React.PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let direction = 0;
|
let direction = 0;
|
||||||
if (Keyboard.isKeyPressed(e, Constants.KeyCodes.UP)) {
|
if (isKeyPressed(e, Constants.KeyCodes.UP)) {
|
||||||
direction = -1;
|
direction = -1;
|
||||||
} else {
|
} else {
|
||||||
direction = 1;
|
direction = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextIndex = ChannelUtils.findNextUnreadChannelId(
|
const nextIndex = findNextUnreadChannelId(
|
||||||
this.props.currentChannelId,
|
this.props.currentChannelId,
|
||||||
allChannelIds,
|
allChannelIds,
|
||||||
unreadChannelIds,
|
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 (
|
return (
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {getIsMobileView} from 'selectors/views/browser';
|
|||||||
|
|
||||||
import type {GlobalState} from 'types/store';
|
import type {GlobalState} from 'types/store';
|
||||||
|
|
||||||
import SidebarRightMenu from './sidebar_right_menu';
|
import SidebarMobileRightMenu from './sidebar_mobile_right_menu';
|
||||||
|
|
||||||
function mapStateToProps(state: GlobalState) {
|
function mapStateToProps(state: GlobalState) {
|
||||||
const config = getConfig(state);
|
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', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
expect(identifyElementRegion(screen.getAllByText(channel.display_name)[0])).toEqual('channel_sidebar');
|
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.display_name)[1])).toEqual('channel_header');
|
||||||
expect(identifyElementRegion(screen.getAllByText(channel.header)[0])).toEqual('channel_header');
|
expect(identifyElementRegion(screen.getAllByText(channel.header)[0])).toEqual('channel_header');
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user