mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
* improve performance on sendNotifications * Fix SQL queries * Remove get direct profiles, not needed anymore * Add raw data to error details if AppError fails to decode * men * Fix decode (#4052) * Fixing json decode * Adding unit test * Initial work for client scaling (#4051) * Begin adding paging to profiles API * Added more paging functionality * Finish hooking up admin console user lists * Add API for searching users and add searching to all user lists * Add lazy loading of profiles * Revert config.json * Fix unit tests and some style issues * Add GetProfilesFromList to Go driver and fix web unit test * Update etag for GetProfiles * Updating ui for filters and pagination (#4044) * Updating UI for pagination * Adjusting margins for filter row * Adjusting margin for specific modals * Adding relative padding to system console * Adjusting responsive view * Update client user tests * Minor fixes for direct messages modal (#4056) * Remove some unneeded initial load calls (#4057) * UX updates to user lists, added smart counts and bug fixes (#4059) * Improved getExplicitMentions and unit tests (#4064) * Refactor getting posts to lazy load profiles correctly (#4062) * Comment out SetActiveChannel test (#4066) * Profiler cpu, block, and memory profiler. (#4081) * Fix TestSetActiveChannel unit test (#4071) * Fixing build failure caused by dependancies updating (#4076) * Adding profiler * Fix admin_team_member_dropdown eslint errors * Bumping session cache size (#4077) * Bumping session cache size * Bumping status cache * Refactor how the client handles channel members to be large team friendly (#4106) * Refactor how the client handles channel members to be large team friendly * Change Id to ChannelId in ChannelStats model * Updated getChannelMember and getProfilesByIds routes to match proposal * Performance improvements (#4100) * Performance improvements * Fixing re-connect issue * Fixing error message * Some other minor perf tweaks * Some other minor perf tweaks * Fixing config file * Fixing buffer size * Fixing web socket send message * adding some error logging * fix getMe to be user required * Fix websocket event for new user * Fixing shutting down * Reverting web socket changes * Fixing logging lvl * Adding caching to GetMember * Adding some logging * Fixing caching * Fixing caching invalidate * Fixing direct message caching * Fixing caching * Fixing caching * Remove GetDirectProfiles from initial load * Adding logging and fixing websocket client * Adding back caching from bad merge. * Explicitly close go driver requests (#4162) * Refactored how the client handles team members to be more large team friendly (#4159) * Refactor getProfilesForDirectMessageList API into getAllProfiles API * Refactored how the client handles team members to be more large team friendly * Fix js error when receiving a notification * Fix JS error caused by current user being overwritten with sanitized version (#4165) * Adding error message to status failure (#4167) * Fix a few bugs caused by client scaling refactoring (#4170) * When there is no read replica, don't open a second set of connections to the master database (#4173) * Adding connection tacking to stats (#4174) * Reduce DB writes for statuses and other status related changes (#4175) * Fix bug preventing opening of DM channels from more modal (#4181) * Fixing socket timing error (#4183) * Fixing ping/pong handler * Fixing socket timing error * Commenting out status broadcasting * Removing user status changes * Removing user status changes * Removing user status changes * Removing user status changes * Adding DoPreComputeJson() * Performance improvements (#4194) * * Fix System Console Analytics queries * Add db.SetConnMaxLifetime to 15 minutes * Add "net/http/pprof" for profiling * Add FreeOSMemory() to manually release memory on reload config * Add flag to enable http profiler * Fix memory leak (#4197) * Fix memory leak * removed unneeded nil assignment * Fixing go routine leak (#4208) * Merge fixes * Merge fix * Refactored statuses to be queried by the client rather than broadcast by the server (#4212) * Refactored server code to reduce status broadcasts and to allow getting statuses by IDs * Refactor client code to periodically fetch statuses * Add store unit test for getting statuses by ids * Fix status unit test * Add getStatusesByIds REST API and move the client over to use that instead of the WebSocket * Adding multiple threads to websocket hub (#4230) * Adding multiple threads to websocket hub * Fixing unit tests * Fixing so websocket connections from the same user end up in the same… (#4240) * Fixing so websocket connections from the same user end up in the same list * Removing old comment * Refactor user autocomplete to query the server (#4239) * Add API for autocompleting users * Converted at mention autocomplete to query server * Converted user search autocomplete to query server * Switch autocomplete API naming to use term instead of username * Split autocomplete API into two, one for channels and for teams * Fix copy/paste error * Some final client scaling fixes (#4246) * Add lazy loading of profiles to integration pages * Add lazy loading of profiles to emoji page * Fix JS error when receiving post in select team menu and also clean up channel store
260 lines
8.4 KiB
JavaScript
260 lines
8.4 KiB
JavaScript
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
|
// See License.txt for license information.
|
|
|
|
import $ from 'jquery';
|
|
import AtMentionProvider from './suggestion/at_mention_provider.jsx';
|
|
import ChannelMentionProvider from './suggestion/channel_mention_provider.jsx';
|
|
import CommandProvider from './suggestion/command_provider.jsx';
|
|
import EmoticonProvider from './suggestion/emoticon_provider.jsx';
|
|
import SuggestionList from './suggestion/suggestion_list.jsx';
|
|
import SuggestionBox from './suggestion/suggestion_box.jsx';
|
|
import ErrorStore from 'stores/error_store.jsx';
|
|
|
|
import * as TextFormatting from 'utils/text_formatting.jsx';
|
|
import * as Utils from 'utils/utils.jsx';
|
|
import Constants from 'utils/constants.jsx';
|
|
|
|
import {FormattedMessage} from 'react-intl';
|
|
|
|
const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
|
|
|
|
import React from 'react';
|
|
|
|
export default class Textbox extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.focus = this.focus.bind(this);
|
|
this.getStateFromStores = this.getStateFromStores.bind(this);
|
|
this.onRecievedError = this.onRecievedError.bind(this);
|
|
this.handleKeyPress = this.handleKeyPress.bind(this);
|
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
|
this.handleHeightChange = this.handleHeightChange.bind(this);
|
|
this.showPreview = this.showPreview.bind(this);
|
|
|
|
this.state = {
|
|
connection: ''
|
|
};
|
|
|
|
this.suggestionProviders = [
|
|
new AtMentionProvider(this.props.channelId),
|
|
new ChannelMentionProvider(),
|
|
new EmoticonProvider()
|
|
];
|
|
if (props.supportsCommands) {
|
|
this.suggestionProviders.push(new CommandProvider());
|
|
}
|
|
}
|
|
|
|
getStateFromStores() {
|
|
const error = ErrorStore.getLastError();
|
|
|
|
if (error) {
|
|
return {message: error.message};
|
|
}
|
|
|
|
return {message: null};
|
|
}
|
|
|
|
componentDidMount() {
|
|
ErrorStore.addChangeListener(this.onRecievedError);
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
ErrorStore.removeChangeListener(this.onRecievedError);
|
|
}
|
|
|
|
onRecievedError() {
|
|
const errorCount = ErrorStore.getConnectionErrorCount();
|
|
|
|
if (errorCount > 1) {
|
|
this.setState({connection: 'bad-connection'});
|
|
} else {
|
|
this.setState({connection: ''});
|
|
}
|
|
}
|
|
|
|
handleKeyPress(e) {
|
|
this.props.onKeyPress(e);
|
|
}
|
|
|
|
handleKeyDown(e) {
|
|
if (this.props.onKeyDown) {
|
|
this.props.onKeyDown(e);
|
|
}
|
|
}
|
|
|
|
handleHeightChange(height) {
|
|
const textbox = $(this.refs.message.getTextbox());
|
|
const wrapper = $(this.refs.wrapper);
|
|
|
|
const maxHeight = parseInt(textbox.css('max-height'), 10);
|
|
|
|
// move over attachment icon to compensate for the scrollbar
|
|
if (height > maxHeight) {
|
|
wrapper.closest('.post-body__cell').addClass('scroll');
|
|
} else {
|
|
wrapper.closest('.post-body__cell').removeClass('scroll');
|
|
}
|
|
}
|
|
|
|
focus() {
|
|
this.refs.message.getTextbox().focus();
|
|
}
|
|
|
|
showPreview(e) {
|
|
e.preventDefault();
|
|
e.target.blur();
|
|
this.setState({preview: !this.state.preview});
|
|
}
|
|
|
|
componentWillReceiveProps(nextProps) {
|
|
if (nextProps.channelId !== this.props.channelId) {
|
|
// Update channel id for AtMentionProvider.
|
|
const providers = this.suggestionProviders;
|
|
for (let i = 0; i < providers.length; i++) {
|
|
if (providers[i] instanceof AtMentionProvider) {
|
|
providers[i] = new AtMentionProvider(nextProps.channelId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
render() {
|
|
const hasText = this.props.messageText.length > 0;
|
|
|
|
let previewLink = null;
|
|
if (Utils.isFeatureEnabled(PreReleaseFeatures.MARKDOWN_PREVIEW)) {
|
|
previewLink = (
|
|
<a
|
|
onClick={this.showPreview}
|
|
className='textbox-preview-link'
|
|
>
|
|
{this.state.preview ? (
|
|
<FormattedMessage
|
|
id='textbox.edit'
|
|
defaultMessage='Edit message'
|
|
/>
|
|
) : (
|
|
<FormattedMessage
|
|
id='textbox.preview'
|
|
defaultMessage='Preview'
|
|
/>
|
|
)}
|
|
</a>
|
|
);
|
|
}
|
|
|
|
const helpText = (
|
|
<div
|
|
style={{visibility: hasText ? 'visible' : 'hidden', opacity: hasText ? '0.45' : '0'}}
|
|
className='help__format-text'
|
|
>
|
|
<b>
|
|
<FormattedMessage
|
|
id='textbox.bold'
|
|
defaultMessage='**bold**'
|
|
/>
|
|
</b>
|
|
<i>
|
|
<FormattedMessage
|
|
id='textbox.italic'
|
|
defaultMessage='_italic_'
|
|
/>
|
|
</i>
|
|
<span>
|
|
{'~~'}
|
|
<strike>
|
|
<FormattedMessage
|
|
id='textbox.strike'
|
|
defaultMessage='strike'
|
|
/>
|
|
</strike>
|
|
{'~~ '}
|
|
</span>
|
|
<span>
|
|
<FormattedMessage
|
|
id='textbox.inlinecode'
|
|
defaultMessage='`inline code`'
|
|
/>
|
|
</span>
|
|
<span>
|
|
<FormattedMessage
|
|
id='textbox.preformatted'
|
|
defaultMessage='```preformatted```'
|
|
/>
|
|
</span>
|
|
<span>
|
|
<FormattedMessage
|
|
id='textbox.quote'
|
|
defaultMessage='>quote'
|
|
/>
|
|
</span>
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div
|
|
ref='wrapper'
|
|
className='textarea-wrapper'
|
|
>
|
|
<SuggestionBox
|
|
id={this.props.id}
|
|
ref='message'
|
|
className={`form-control custom-textarea ${this.state.connection}`}
|
|
type='textarea'
|
|
spellCheck='true'
|
|
maxLength={Constants.MAX_POST_LEN}
|
|
placeholder={this.props.createMessage}
|
|
onInput={this.props.onInput}
|
|
onKeyPress={this.handleKeyPress}
|
|
onKeyDown={this.handleKeyDown}
|
|
onHeightChange={this.handleHeightChange}
|
|
style={{visibility: this.state.preview ? 'hidden' : 'visible'}}
|
|
listComponent={SuggestionList}
|
|
providers={this.suggestionProviders}
|
|
channelId={this.props.channelId}
|
|
value={this.props.messageText}
|
|
renderDividers={true}
|
|
/>
|
|
<div
|
|
ref='preview'
|
|
className='form-control custom-textarea textbox-preview-area'
|
|
style={{display: this.state.preview ? 'block' : 'none'}}
|
|
dangerouslySetInnerHTML={{__html: this.state.preview ? TextFormatting.formatText(this.props.messageText) : ''}}
|
|
/>
|
|
<div className='help__text'>
|
|
{helpText}
|
|
{previewLink}
|
|
<a
|
|
target='_blank'
|
|
rel='noopener noreferrer'
|
|
href='/help/messaging'
|
|
className='textbox-help-link'
|
|
>
|
|
<FormattedMessage
|
|
id='textbox.help'
|
|
defaultMessage='Help'
|
|
/>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
Textbox.defaultProps = {
|
|
supportsCommands: true
|
|
};
|
|
|
|
Textbox.propTypes = {
|
|
id: React.PropTypes.string.isRequired,
|
|
channelId: React.PropTypes.string,
|
|
messageText: React.PropTypes.string.isRequired,
|
|
onInput: React.PropTypes.func.isRequired,
|
|
onKeyPress: React.PropTypes.func.isRequired,
|
|
createMessage: React.PropTypes.string.isRequired,
|
|
onKeyDown: React.PropTypes.func,
|
|
supportsCommands: React.PropTypes.bool.isRequired
|
|
};
|