[MM-54493] Allow a user to disable the webapp prefetch (#24389)

* allow a user to disable the webapp prefetch if they are experiencing degraded performance on initial load
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Ben Cooke
2023-10-02 12:06:14 -04:00
коммит произвёл GitHub
родитель 460fa05eb9
Коммит 9bbdca7240
9 изменённых файлов: 131 добавлений и 5 удалений

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

@@ -68,6 +68,8 @@ describe('/components/data_prefetch', () => {
last_post_at: 1235, last_post_at: 1235,
last_root_post_at: 1235, last_root_post_at: 1235,
})], })],
disableWebappPrefetchAllowed: false,
dataPrefetchEnabled: true,
}; };
beforeEach(() => { beforeEach(() => {

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

@@ -21,6 +21,9 @@ type Props = {
sidebarLoaded: boolean; sidebarLoaded: boolean;
unreadChannels: Channel[]; unreadChannels: Channel[];
disableWebappPrefetchAllowed: boolean;
dataPrefetchEnabled: boolean;
actions: { actions: {
prefetchChannelPosts: (channelId: string, delay?: number) => Promise<any>; prefetchChannelPosts: (channelId: string, delay?: number) => Promise<any>;
trackPreloadedChannels: (prefetchQueueObj: Record<string, string[]>) => void; trackPreloadedChannels: (prefetchQueueObj: Record<string, string[]>) => void;
@@ -52,15 +55,20 @@ export default class DataPrefetch extends React.PureComponent<Props> {
private prefetchTimeout?: number; private prefetchTimeout?: number;
async componentDidUpdate(prevProps: Props) { async componentDidUpdate(prevProps: Props) {
const {currentChannelId, prefetchQueueObj, sidebarLoaded} = this.props; const {currentChannelId, prefetchQueueObj, sidebarLoaded, disableWebappPrefetchAllowed, dataPrefetchEnabled} = this.props;
const enablePrefetch = (!disableWebappPrefetchAllowed) || (disableWebappPrefetchAllowed && dataPrefetchEnabled);
if (currentChannelId && sidebarLoaded && (!prevProps.currentChannelId || !prevProps.sidebarLoaded)) { if (currentChannelId && sidebarLoaded && (!prevProps.currentChannelId || !prevProps.sidebarLoaded)) {
queue.add(async () => this.prefetchPosts(currentChannelId)); queue.add(async () => this.prefetchPosts(currentChannelId));
await loadProfilesForSidebar(); await loadProfilesForSidebar();
this.prefetchData(); if (enablePrefetch) {
this.prefetchData();
}
} else if (prevProps.prefetchQueueObj !== prefetchQueueObj) { } else if (prevProps.prefetchQueueObj !== prefetchQueueObj) {
clearTimeout(this.prefetchTimeout); clearTimeout(this.prefetchTimeout);
await queue.clear(); await queue.clear();
this.prefetchData(); if (enablePrefetch) {
this.prefetchData();
}
} }
if (currentChannelId && sidebarLoaded && (!prevProps.currentChannelId || !prevProps.sidebarLoaded)) { if (currentChannelId && sidebarLoaded && (!prevProps.currentChannelId || !prevProps.sidebarLoaded)) {

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

@@ -9,9 +9,11 @@ import type {Channel, ChannelMembership} from '@mattermost/types/channels';
import type {PostList} from '@mattermost/types/posts'; import type {PostList} from '@mattermost/types/posts';
import type {RelationOneToOne} from '@mattermost/types/utilities'; import type {RelationOneToOne} from '@mattermost/types/utilities';
import {Preferences} from 'mattermost-redux/constants';
import {getCurrentChannelId, getUnreadChannels} from 'mattermost-redux/selectors/entities/channels'; import {getCurrentChannelId, getUnreadChannels} from 'mattermost-redux/selectors/entities/channels';
import {getMyChannelMemberships} from 'mattermost-redux/selectors/entities/common'; import {getMyChannelMemberships} from 'mattermost-redux/selectors/entities/common';
import {isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences'; import {isPerformanceDebuggingEnabled} from 'mattermost-redux/selectors/entities/general';
import {getBool, isCollapsedThreadsEnabled} from 'mattermost-redux/selectors/entities/preferences';
import {isChannelMuted} from 'mattermost-redux/utils/channel_utils'; import {isChannelMuted} from 'mattermost-redux/utils/channel_utils';
import {memoizeResult} from 'mattermost-redux/utils/helpers'; import {memoizeResult} from 'mattermost-redux/utils/helpers';
@@ -82,6 +84,7 @@ function mapStateToProps(state: GlobalState) {
const unreadChannels = getUnreadChannels(state, lastUnreadChannel); const unreadChannels = getUnreadChannels(state, lastUnreadChannel);
const prefetchQueueObj = prefetchQueue(unreadChannels, memberships, isCollapsedThreadsEnabled(state)); const prefetchQueueObj = prefetchQueue(unreadChannels, memberships, isCollapsedThreadsEnabled(state));
const prefetchRequestStatus = state.views.channel.channelPrefetchStatus; const prefetchRequestStatus = state.views.channel.channelPrefetchStatus;
const disableWebappPrefetchAllowed = isPerformanceDebuggingEnabled(state);
return { return {
currentChannelId: getCurrentChannelId(state), currentChannelId: getCurrentChannelId(state),
@@ -89,6 +92,8 @@ function mapStateToProps(state: GlobalState) {
prefetchRequestStatus, prefetchRequestStatus,
sidebarLoaded: isSidebarLoaded(state), sidebarLoaded: isSidebarLoaded(state),
unreadChannels, unreadChannels,
disableWebappPrefetchAllowed,
dataPrefetchEnabled: getBool(state, Preferences.CATEGORY_ADVANCED_SETTINGS, Preferences.ADVANCED_DATA_PREFETCH, true),
}; };
} }

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

@@ -7,7 +7,7 @@ import type {ActionCreatorsMapObject, Dispatch} from 'redux';
import {savePreferences} from 'mattermost-redux/actions/preferences'; import {savePreferences} from 'mattermost-redux/actions/preferences';
import {updateUserActive, revokeAllSessionsForUser} from 'mattermost-redux/actions/users'; import {updateUserActive, revokeAllSessionsForUser} from 'mattermost-redux/actions/users';
import {getConfig} from 'mattermost-redux/selectors/entities/general'; import {getConfig, isPerformanceDebuggingEnabled} from 'mattermost-redux/selectors/entities/general';
import {get, getUnreadScrollPositionPreference, makeGetCategory, syncedDraftsAreAllowed} from 'mattermost-redux/selectors/entities/preferences'; import {get, getUnreadScrollPositionPreference, makeGetCategory, syncedDraftsAreAllowed} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
import type {ActionFunc} from 'mattermost-redux/types/actions'; import type {ActionFunc} from 'mattermost-redux/types/actions';
@@ -27,6 +27,7 @@ function makeMapStateToProps() {
const enablePreviewFeatures = config.EnablePreviewFeatures === 'true'; const enablePreviewFeatures = config.EnablePreviewFeatures === 'true';
const enableUserDeactivation = config.EnableUserDeactivation === 'true'; const enableUserDeactivation = config.EnableUserDeactivation === 'true';
const disableWebappPrefetchAllowed = isPerformanceDebuggingEnabled(state);
const enableJoinLeaveMessage = config.EnableJoinLeaveMessageByDefault === 'true'; const enableJoinLeaveMessage = config.EnableJoinLeaveMessageByDefault === 'true';
return { return {
@@ -41,6 +42,8 @@ function makeMapStateToProps() {
enablePreviewFeatures, enablePreviewFeatures,
enableUserDeactivation, enableUserDeactivation,
syncedDraftsAreAllowed: syncedDraftsAreAllowed(state), syncedDraftsAreAllowed: syncedDraftsAreAllowed(state),
disableWebappPrefetchAllowed,
dataPrefetchEnabled: get(state, Preferences.CATEGORY_ADVANCED_SETTINGS, 'data_prefetch', 'true'),
}; };
}; };
} }

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

@@ -47,6 +47,8 @@ describe('components/user_settings/display/UserSettingsDisplay', () => {
enablePreviewFeatures: false, enablePreviewFeatures: false,
enableUserDeactivation: false, enableUserDeactivation: false,
syncedDraftsAreAllowed: true, syncedDraftsAreAllowed: true,
disableWebappPrefetchAllowed: false,
dataPrefetchEnabled: 'true',
}; };
test('should have called handleSubmit', async () => { test('should have called handleSubmit', async () => {

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

@@ -36,6 +36,7 @@ type Settings = {
formatting: Props['formatting']; formatting: Props['formatting'];
join_leave: Props['joinLeave']; join_leave: Props['joinLeave'];
sync_drafts: Props['syncDrafts']; sync_drafts: Props['syncDrafts'];
data_prefetch: Props['dataPrefetchEnabled'];
}; };
export type Props = { export type Props = {
@@ -54,6 +55,8 @@ export type Props = {
enablePreviewFeatures: boolean; enablePreviewFeatures: boolean;
enableUserDeactivation: boolean; enableUserDeactivation: boolean;
syncedDraftsAreAllowed: boolean; syncedDraftsAreAllowed: boolean;
disableWebappPrefetchAllowed: boolean;
dataPrefetchEnabled: string;
actions: { actions: {
savePreferences: (userId: string, preferences: PreferenceType[]) => Promise<ActionResult>; savePreferences: (userId: string, preferences: PreferenceType[]) => Promise<ActionResult>;
updateUserActive: (userId: string, active: boolean) => Promise<ActionResult>; updateUserActive: (userId: string, active: boolean) => Promise<ActionResult>;
@@ -87,6 +90,7 @@ export default class AdvancedSettingsDisplay extends React.PureComponent<Props,
formatting: this.props.formatting, formatting: this.props.formatting,
join_leave: this.props.joinLeave, join_leave: this.props.joinLeave,
sync_drafts: this.props.syncDrafts, sync_drafts: this.props.syncDrafts,
data_prefetch: this.props.dataPrefetchEnabled,
[Preferences.UNREAD_SCROLL_POSITION]: this.props.unreadScrollPosition, [Preferences.UNREAD_SCROLL_POSITION]: this.props.unreadScrollPosition,
}; };
@@ -575,6 +579,93 @@ export default class AdvancedSettingsDisplay extends React.PureComponent<Props,
); );
}; };
renderDataPrefetchSection = () => {
const active = this.props.activeSection === AdvancedSections.DATA_PREFETCH;
let max = null;
if (active) {
max = (
<SettingItemMax
title={
<FormattedMessage
id='user.settings.advance.dataPrefetch.Title'
defaultMessage='Allow Mattermost to prefetch channel posts'
/>
}
inputs={[
<fieldset key='syncDraftsSetting'>
<legend className='form-legend hidden-label'>
<FormattedMessage
id='user.settings.advance.dataPrefetch.Title'
defaultMessage='Allow Mattermost to prefetch channel posts'
/>
</legend>
<div className='radio'>
<label>
<input
id='dataPrefetchOn'
type='radio'
name='dataPrefetch'
checked={this.state.settings.data_prefetch !== 'false'}
onChange={this.updateSetting.bind(this, 'data_prefetch', 'true')}
/>
<FormattedMessage
id='user.settings.advance.on'
defaultMessage='On'
/>
</label>
<br/>
</div>
<div className='radio'>
<label>
<input
id='dataPrefetchOff'
type='radio'
name='dataPrefetch'
checked={this.state.settings.data_prefetch === 'false'}
onChange={this.updateSetting.bind(this, 'data_prefetch', 'false')}
/>
<FormattedMessage
id='user.settings.advance.off'
defaultMessage='Off'
/>
</label>
<br/>
</div>
<div className='mt-5'>
<FormattedMessage
id='user.settings.advance.dataPrefetch.Desc'
defaultMessage='When disabled, messages and user information will be fetched on each channel load instead of being pre-fetched on startup. Disabling prefetch is recommended for users with a high unread channel count in order to improve application performance.'
/>
</div>
</fieldset>,
]}
setting={AdvancedSections.DATA_PREFETCH}
submit={this.handleSubmit.bind(this, ['data_prefetch'])}
saving={this.state.isSaving}
serverError={this.state.serverError}
updateSection={this.handleUpdateSection}
/>
);
}
return (
<SettingItem
active={active}
areAllSectionsInactive={this.props.activeSection === ''}
title={
<FormattedMessage
id='user.settings.advance.dataPrefetch.Title'
defaultMessage='Allow Mattermost to prefetch channel posts'
/>
}
describe={this.renderOnOffLabel(this.state.settings.data_prefetch)}
section={AdvancedSections.DATA_PREFETCH}
updateSection={this.handleUpdateSection}
max={max}
/>
);
};
renderFeatureLabel(feature: string): ReactNode { renderFeatureLabel(feature: string): ReactNode {
switch (feature) { switch (feature) {
case 'MARKDOWN_PREVIEW': case 'MARKDOWN_PREVIEW':
@@ -895,6 +986,15 @@ export default class AdvancedSettingsDisplay extends React.PureComponent<Props,
} }
} }
let dataPrefetchSection = null;
let dataPrefetchSectionDivider = null;
if (this.props.disableWebappPrefetchAllowed) {
dataPrefetchSection = this.renderDataPrefetchSection();
if (syncDraftsSection) {
dataPrefetchSectionDivider = <div className='divider-light'/>;
}
}
return ( return (
<div> <div>
<div className='modal-header'> <div className='modal-header'>
@@ -953,6 +1053,8 @@ export default class AdvancedSettingsDisplay extends React.PureComponent<Props,
{unreadScrollPositionSection} {unreadScrollPositionSection}
{syncDraftsSectionDivider} {syncDraftsSectionDivider}
{syncDraftsSection} {syncDraftsSection}
{dataPrefetchSectionDivider}
{dataPrefetchSection}
<div className='divider-dark'/> <div className='divider-dark'/>
{makeConfirmationModal} {makeConfirmationModal}
</div> </div>

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

@@ -5208,6 +5208,8 @@
"user_profile.send.dm.yourself": "Send yourself a message", "user_profile.send.dm.yourself": "Send yourself a message",
"user.settings.advance.confirmDeactivateAccountTitle": "Confirm Deactivation", "user.settings.advance.confirmDeactivateAccountTitle": "Confirm Deactivation",
"user.settings.advance.confirmDeactivateDesc": "Are you sure you want to deactivate your account? This can only be reversed by your System Administrator.", "user.settings.advance.confirmDeactivateDesc": "Are you sure you want to deactivate your account? This can only be reversed by your System Administrator.",
"user.settings.advance.dataPrefetch.Desc": "When disabled, messages and user information will be fetched on each channel load instead of being pre-fetched on startup. Disabling prefetch is recommended for users with a high unread channel count in order to improve application performance.",
"user.settings.advance.dataPrefetch.Title": "Allow Mattermost to prefetch channel posts",
"user.settings.advance.deactivate_member_modal.deactivateButton": "Yes, deactivate my account", "user.settings.advance.deactivate_member_modal.deactivateButton": "Yes, deactivate my account",
"user.settings.advance.deactivateAccountTitle": "Deactivate Account", "user.settings.advance.deactivateAccountTitle": "Deactivate Account",
"user.settings.advance.deactivateDesc": "Deactivating your account removes your ability to log in to this server and disables all email and mobile notifications. To reactivate your account, contact your System Administrator.", "user.settings.advance.deactivateDesc": "Deactivating your account removes your ability to log in to this server and disables all email and mobile notifications. To reactivate your account, contact your System Administrator.",

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

@@ -55,6 +55,7 @@ const Preferences = {
ADVANCED_CODE_BLOCK_ON_CTRL_ENTER: 'code_block_ctrl_enter', ADVANCED_CODE_BLOCK_ON_CTRL_ENTER: 'code_block_ctrl_enter',
ADVANCED_SEND_ON_CTRL_ENTER: 'send_on_ctrl_enter', ADVANCED_SEND_ON_CTRL_ENTER: 'send_on_ctrl_enter',
ADVANCED_SYNC_DRAFTS: 'sync_drafts', ADVANCED_SYNC_DRAFTS: 'sync_drafts',
ADVANCED_DATA_PREFETCH: 'data_prefetch',
CATEGORY_WHATS_NEW_MODAL: 'whats_new_modal', CATEGORY_WHATS_NEW_MODAL: 'whats_new_modal',
HAS_SEEN_SIDEBAR_WHATS_NEW_MODAL: 'has_seen_sidebar_whats_new_modal', HAS_SEEN_SIDEBAR_WHATS_NEW_MODAL: 'has_seen_sidebar_whats_new_modal',

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

@@ -1014,6 +1014,7 @@ export const AdvancedSections = {
PREVIEW_FEATURES: 'advancedPreviewFeatures', PREVIEW_FEATURES: 'advancedPreviewFeatures',
PERFORMANCE_DEBUGGING: 'performanceDebugging', PERFORMANCE_DEBUGGING: 'performanceDebugging',
SYNC_DRAFTS: 'syncDrafts', SYNC_DRAFTS: 'syncDrafts',
DATA_PREFETCH: 'dataPrefetch',
}; };
export const RHSStates = { export const RHSStates = {