Merge branch 'release-3.6'
Этот коммит содержится в:
@@ -432,10 +432,6 @@ export default class AdminTeamMembersDropdown extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (global.window.mm_config.EnableSignInWithEmail !== 'true') {
|
||||
passwordReset = null;
|
||||
}
|
||||
|
||||
let makeDemoteModal = null;
|
||||
if (this.props.user.id === me.id) {
|
||||
const title = (
|
||||
|
||||
@@ -146,7 +146,11 @@ export default class PolicySettings extends AdminSettings {
|
||||
defaultMessage='Set policy on who can delete public channels. Deleted channels can be recovered from the database using a {commandLineToolLink}.'
|
||||
values={{
|
||||
commandLineToolLink: (
|
||||
<a href='https://docs.mattermost.com/administration/command-line-tools.html'>
|
||||
<a
|
||||
href='https://docs.mattermost.com/administration/command-line-tools.html'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='admin.general.policy.restrictPublicChannelDeletionCommandLineToolLink'
|
||||
defaultMessage='command line tool'
|
||||
@@ -222,7 +226,11 @@ export default class PolicySettings extends AdminSettings {
|
||||
defaultMessage='Set policy on who can delete private groups. Deleted groups can be recovered from the database using a {commandLineToolLink}.'
|
||||
values={{
|
||||
commandLineToolLink: (
|
||||
<a href='https://docs.mattermost.com/administration/command-line-tools.html'>
|
||||
<a
|
||||
href='https://docs.mattermost.com/administration/command-line-tools.html'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='admin.general.policy.restrictPrivateChannelDeletionCommandLineToolLink'
|
||||
defaultMessage='command line tool'
|
||||
|
||||
@@ -296,9 +296,9 @@ export default class SamlSettings extends AdminSettings {
|
||||
<SettingsGroup>
|
||||
<div className='banner'>
|
||||
<div className='banner__content'>
|
||||
<FormattedMessage
|
||||
<FormattedHTMLMessage
|
||||
id='admin.saml.bannerDesc'
|
||||
defaultMessage='If a user attribute changes on the SAML server it will be updated the next time the user enters their credentials to log in to Mattermost. This includes if a user is made inactive or removed from a SAML Identity Provider. Remote logout with SAML servers is considered in a future release.'
|
||||
defaultMessage='User attributes in SAML server, including user deactivation or removal, are updated in Mattermost during user login. Learn more at: <a href=\"https://docs.mattermost.com/deployment/sso-saml.html\">https://docs.mattermost.com/deployment/sso-saml.html</a>'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -80,68 +80,140 @@ class SystemAnalytics extends React.Component {
|
||||
|
||||
render() {
|
||||
const stats = this.state.stats;
|
||||
const isLicensed = global.window.mm_license.IsLicensed === 'true';
|
||||
const skippedIntensiveQueries = stats[StatTypes.TOTAL_POSTS] === -1;
|
||||
const postCountsDay = formatPostsPerDayData(stats[StatTypes.POST_PER_DAY]);
|
||||
const userCountsWithPostsDay = formatUsersWithPostsPerDayData(stats[StatTypes.USERS_WITH_POSTS_PER_DAY]);
|
||||
|
||||
let banner;
|
||||
if (stats[StatTypes.TOTAL_POSTS] === -1) {
|
||||
let postCount;
|
||||
let postTotalGraph;
|
||||
let activeUserGraph;
|
||||
if (skippedIntensiveQueries) {
|
||||
banner = (
|
||||
<Banner
|
||||
description={
|
||||
<div className='banner'>
|
||||
<div className='banner__content'>
|
||||
<FormattedHTMLMessage
|
||||
id='analytics.system.skippedIntensiveQueries'
|
||||
defaultMessage="Some statistics have been omitted because they put too much load on the system to calculate. See <a href='https://docs.mattermost.com/administration/statistics.html' target='_blank'>https://docs.mattermost.com/administration/statistics.html</a> for more details."
|
||||
defaultMessage="To maximize performance, some statistics are disabled. You can re-enable them in config.json. See: <a href='https://docs.mattermost.com/administration/statistics.html' target='_blank'>https://docs.mattermost.com/administration/statistics.html</a>"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
postCount = (
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalPosts'
|
||||
defaultMessage='Total Posts'
|
||||
/>
|
||||
}
|
||||
icon='fa-comment'
|
||||
count={stats[StatTypes.TOTAL_POSTS]}
|
||||
/>
|
||||
);
|
||||
|
||||
postTotalGraph = (
|
||||
<div className='row'>
|
||||
<LineChart
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalPosts'
|
||||
defaultMessage='Total Posts'
|
||||
/>
|
||||
}
|
||||
data={postCountsDay}
|
||||
options={{
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}}
|
||||
width='740'
|
||||
height='225'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
activeUserGraph = (
|
||||
<div className='row'>
|
||||
<LineChart
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.activeUsers'
|
||||
defaultMessage='Active Users With Posts'
|
||||
/>
|
||||
}
|
||||
data={userCountsWithPostsDay}
|
||||
options={{
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}}
|
||||
width='740'
|
||||
height='225'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let advancedCounts;
|
||||
let advancedStats;
|
||||
let advancedGraphs;
|
||||
let sessionCount;
|
||||
let commandCount;
|
||||
let incomingCount;
|
||||
let outgoingCount;
|
||||
if (global.window.mm_license.IsLicensed === 'true') {
|
||||
advancedCounts = (
|
||||
<div className='row'>
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalSessions'
|
||||
defaultMessage='Total Sessions'
|
||||
/>
|
||||
}
|
||||
icon='fa-signal'
|
||||
count={stats[StatTypes.TOTAL_SESSIONS]}
|
||||
/>
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalCommands'
|
||||
defaultMessage='Total Commands'
|
||||
/>
|
||||
}
|
||||
icon='fa-terminal'
|
||||
count={stats[StatTypes.TOTAL_COMMANDS]}
|
||||
/>
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalIncomingWebhooks'
|
||||
defaultMessage='Incoming Webhooks'
|
||||
/>
|
||||
}
|
||||
icon='fa-arrow-down'
|
||||
count={stats[StatTypes.TOTAL_IHOOKS]}
|
||||
/>
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalOutgoingWebhooks'
|
||||
defaultMessage='Outgoing Webhooks'
|
||||
/>
|
||||
}
|
||||
icon='fa-arrow-up'
|
||||
count={stats[StatTypes.TOTAL_OHOOKS]}
|
||||
/>
|
||||
</div>
|
||||
sessionCount = (
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalSessions'
|
||||
defaultMessage='Total Sessions'
|
||||
/>
|
||||
}
|
||||
icon='fa-signal'
|
||||
count={stats[StatTypes.TOTAL_SESSIONS]}
|
||||
/>
|
||||
);
|
||||
|
||||
commandCount = (
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalCommands'
|
||||
defaultMessage='Total Commands'
|
||||
/>
|
||||
}
|
||||
icon='fa-terminal'
|
||||
count={stats[StatTypes.TOTAL_COMMANDS]}
|
||||
/>
|
||||
);
|
||||
|
||||
incomingCount = (
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalIncomingWebhooks'
|
||||
defaultMessage='Incoming Webhooks'
|
||||
/>
|
||||
}
|
||||
icon='fa-arrow-down'
|
||||
count={stats[StatTypes.TOTAL_IHOOKS]}
|
||||
/>
|
||||
);
|
||||
|
||||
outgoingCount = (
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalOutgoingWebhooks'
|
||||
defaultMessage='Outgoing Webhooks'
|
||||
/>
|
||||
}
|
||||
icon='fa-arrow-up'
|
||||
count={stats[StatTypes.TOTAL_OHOOKS]}
|
||||
/>
|
||||
);
|
||||
|
||||
advancedStats = (
|
||||
@@ -247,65 +319,89 @@ class SystemAnalytics extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const postCountsDay = formatPostsPerDayData(stats[StatTypes.POST_PER_DAY]);
|
||||
const userCountsWithPostsDay = formatUsersWithPostsPerDayData(stats[StatTypes.USERS_WITH_POSTS_PER_DAY]);
|
||||
|
||||
let totalPostsCount;
|
||||
let postTotalGraph;
|
||||
let activeUserGraph;
|
||||
if (stats[StatTypes.TOTAL_POSTS] !== -1) {
|
||||
totalPostsCount = (
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalPosts'
|
||||
defaultMessage='Total Posts'
|
||||
/>
|
||||
}
|
||||
icon='fa-comment'
|
||||
count={stats[StatTypes.TOTAL_POSTS]}
|
||||
/>
|
||||
);
|
||||
|
||||
postTotalGraph = (
|
||||
<div className='row'>
|
||||
<LineChart
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalPosts'
|
||||
defaultMessage='Total Posts'
|
||||
/>
|
||||
}
|
||||
data={postCountsDay}
|
||||
options={{
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}}
|
||||
width='740'
|
||||
height='225'
|
||||
const userCount = (
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalUsers'
|
||||
defaultMessage='Total Users'
|
||||
/>
|
||||
}
|
||||
icon='fa-user'
|
||||
count={stats[StatTypes.TOTAL_USERS]}
|
||||
/>
|
||||
);
|
||||
|
||||
const teamCount = (
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalTeams'
|
||||
defaultMessage='Total Teams'
|
||||
/>
|
||||
}
|
||||
icon='fa-users'
|
||||
count={stats[StatTypes.TOTAL_TEAMS]}
|
||||
/>
|
||||
);
|
||||
|
||||
const channelCount = (
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalChannels'
|
||||
defaultMessage='Total Channels'
|
||||
/>
|
||||
}
|
||||
icon='fa-globe'
|
||||
count={stats[StatTypes.TOTAL_PUBLIC_CHANNELS] + stats[StatTypes.TOTAL_PRIVATE_GROUPS]}
|
||||
/>
|
||||
);
|
||||
|
||||
let firstRow;
|
||||
let secondRow;
|
||||
if (isLicensed && skippedIntensiveQueries) {
|
||||
firstRow = (
|
||||
<div className='row'>
|
||||
{userCount}
|
||||
{teamCount}
|
||||
{channelCount}
|
||||
{sessionCount}
|
||||
</div>
|
||||
);
|
||||
|
||||
activeUserGraph = (
|
||||
secondRow = (
|
||||
<div className='row'>
|
||||
<LineChart
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.activeUsers'
|
||||
defaultMessage='Active Users With Posts'
|
||||
/>
|
||||
}
|
||||
data={userCountsWithPostsDay}
|
||||
options={{
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}}
|
||||
width='740'
|
||||
height='225'
|
||||
/>
|
||||
{commandCount}
|
||||
{incomingCount}
|
||||
{outgoingCount}
|
||||
</div>
|
||||
);
|
||||
} else if (isLicensed && !skippedIntensiveQueries) {
|
||||
firstRow = (
|
||||
<div className='row'>
|
||||
{userCount}
|
||||
{teamCount}
|
||||
{channelCount}
|
||||
{postCount}
|
||||
</div>
|
||||
);
|
||||
|
||||
secondRow = (
|
||||
<div className='row'>
|
||||
{sessionCount}
|
||||
{commandCount}
|
||||
{incomingCount}
|
||||
{outgoingCount}
|
||||
</div>
|
||||
);
|
||||
} else if (!isLicensed) {
|
||||
firstRow = (
|
||||
<div className='row'>
|
||||
{userCount}
|
||||
{teamCount}
|
||||
{channelCount}
|
||||
{postCount}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -319,40 +415,8 @@ class SystemAnalytics extends React.Component {
|
||||
/>
|
||||
</h3>
|
||||
{banner}
|
||||
<div className='row'>
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalUsers'
|
||||
defaultMessage='Total Users'
|
||||
/>
|
||||
}
|
||||
icon='fa-user'
|
||||
count={stats[StatTypes.TOTAL_USERS]}
|
||||
/>
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalTeams'
|
||||
defaultMessage='Total Teams'
|
||||
/>
|
||||
}
|
||||
icon='fa-users'
|
||||
count={stats[StatTypes.TOTAL_TEAMS]}
|
||||
/>
|
||||
{totalPostsCount}
|
||||
<StatisticCount
|
||||
title={
|
||||
<FormattedMessage
|
||||
id='analytics.system.totalChannels'
|
||||
defaultMessage='Total Channels'
|
||||
/>
|
||||
}
|
||||
icon='fa-globe'
|
||||
count={stats[StatTypes.TOTAL_PUBLIC_CHANNELS] + stats[StatTypes.TOTAL_PRIVATE_GROUPS]}
|
||||
/>
|
||||
</div>
|
||||
{advancedCounts}
|
||||
{firstRow}
|
||||
{secondRow}
|
||||
{advancedStats}
|
||||
{advancedGraphs}
|
||||
{postTotalGraph}
|
||||
|
||||
@@ -168,7 +168,7 @@ export default class ChannelMembersModal extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
dialogClassName='more-modal'
|
||||
dialogClassName='more-modal more-modal--action'
|
||||
show={this.state.show}
|
||||
onHide={this.onHide}
|
||||
onExited={this.props.onModalDismissed}
|
||||
@@ -210,4 +210,4 @@ ChannelMembersModal.propTypes = {
|
||||
onModalDismissed: React.PropTypes.func.isRequired,
|
||||
showInviteModal: React.PropTypes.func.isRequired,
|
||||
channel: React.PropTypes.object.isRequired
|
||||
};
|
||||
};
|
||||
@@ -67,9 +67,10 @@ export default class CreateComment extends React.Component {
|
||||
fileInfos: draft.fileInfos,
|
||||
submitting: false,
|
||||
ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter'),
|
||||
showPostDeletedModal: false,
|
||||
lastBlurAt: 0
|
||||
showPostDeletedModal: false
|
||||
};
|
||||
|
||||
this.lastBlurAt = 0;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -145,7 +146,7 @@ export default class CreateComment extends React.Component {
|
||||
});
|
||||
|
||||
const fasterThanHumanWillClick = 150;
|
||||
const forceFocus = (Date.now() - this.state.lastBlurAt < fasterThanHumanWillClick);
|
||||
const forceFocus = (Date.now() - this.lastBlurAt < fasterThanHumanWillClick);
|
||||
this.focusTextbox(forceFocus);
|
||||
}
|
||||
|
||||
@@ -405,7 +406,7 @@ export default class CreateComment extends React.Component {
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
this.setState({lastBlurAt: Date.now()});
|
||||
this.lastBlurAt = Date.now();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -76,9 +76,10 @@ export default class CreatePost extends React.Component {
|
||||
ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter'),
|
||||
fullWidthTextBox: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_FULL_SCREEN,
|
||||
showTutorialTip: false,
|
||||
showPostDeletedModal: false,
|
||||
lastBlurAt: 0
|
||||
showPostDeletedModal: false
|
||||
};
|
||||
|
||||
this.lastBlurAt = 0;
|
||||
}
|
||||
|
||||
handlePostError(postError) {
|
||||
@@ -154,7 +155,8 @@ export default class CreatePost extends React.Component {
|
||||
this.setState({message: '', submitting: false, postError: null, fileInfos: [], serverError: null});
|
||||
|
||||
const fasterThanHumanWillClick = 150;
|
||||
const forceFocus = (Date.now() - this.state.lastBlurAt < fasterThanHumanWillClick);
|
||||
const forceFocus = (Date.now() - this.lastBlurAt < fasterThanHumanWillClick);
|
||||
|
||||
this.focusTextbox(forceFocus);
|
||||
}
|
||||
|
||||
@@ -438,7 +440,7 @@ export default class CreatePost extends React.Component {
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
this.setState({lastBlurAt: Date.now()});
|
||||
this.lastBlurAt = Date.now();
|
||||
}
|
||||
|
||||
showPostDeletedModal() {
|
||||
|
||||
@@ -67,7 +67,7 @@ export default class Setup extends React.Component {
|
||||
}
|
||||
|
||||
let mfaRequired;
|
||||
if (global.window.mm_config.EnforceMultifactorAuthentication) {
|
||||
if (global.window.mm_config.EnforceMultifactorAuthentication === 'true') {
|
||||
mfaRequired = (
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
|
||||
@@ -9,7 +9,6 @@ import TeamStore from 'stores/team_store.jsx';
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
import * as AsyncClient from 'utils/async_client.jsx';
|
||||
import * as ChannelUtils from 'utils/channel_utils.jsx';
|
||||
import {joinChannel, searchMoreChannels} from 'actions/channel_actions.jsx';
|
||||
|
||||
import React from 'react';
|
||||
@@ -152,14 +151,19 @@ export default class MoreChannels extends React.Component {
|
||||
const isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser();
|
||||
const isSystemAdmin = UserStore.isSystemAdminForCurrentUser();
|
||||
|
||||
if (!ChannelUtils.showCreateOption(Constants.OPEN_CHANNEL, isAdmin, isSystemAdmin)) {
|
||||
createNewChannelButton = null;
|
||||
createChannelHelpText = null;
|
||||
if (global.window.mm_license.IsLicensed === 'true') {
|
||||
if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_SYSTEM_ADMIN && !isSystemAdmin) {
|
||||
createNewChannelButton = null;
|
||||
createChannelHelpText = null;
|
||||
} else if (global.window.mm_config.RestrictPublicChannelManagement === Constants.PERMISSIONS_TEAM_ADMIN && !isAdmin) {
|
||||
createNewChannelButton = null;
|
||||
createChannelHelpText = null;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
dialogClassName='more-modal more-public-channels'
|
||||
dialogClassName='more-modal more-modal--action'
|
||||
show={this.state.show}
|
||||
onHide={this.handleHide}
|
||||
onExited={this.handleExit}
|
||||
@@ -192,4 +196,4 @@ export default class MoreChannels extends React.Component {
|
||||
MoreChannels.propTypes = {
|
||||
onModalDismissed: React.PropTypes.func,
|
||||
handleNewChannel: React.PropTypes.func
|
||||
};
|
||||
};
|
||||
@@ -195,7 +195,9 @@ export default class MoreDirectChannels extends React.Component {
|
||||
|
||||
render() {
|
||||
let teamToggle;
|
||||
let memberClass = '';
|
||||
if (global.window.mm_config.RestrictDirectMessage === 'any') {
|
||||
memberClass = 'more-system-members';
|
||||
teamToggle = (
|
||||
<div className='member-select__container'>
|
||||
<select
|
||||
@@ -226,7 +228,7 @@ export default class MoreDirectChannels extends React.Component {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
dialogClassName='more-modal more-direct-channels'
|
||||
dialogClassName={'more-modal more-direct-channels ' + memberClass}
|
||||
show={this.state.show}
|
||||
onHide={this.handleHide}
|
||||
onExited={this.handleExit}
|
||||
@@ -258,4 +260,4 @@ export default class MoreDirectChannels extends React.Component {
|
||||
|
||||
MoreDirectChannels.propTypes = {
|
||||
onModalDismissed: React.PropTypes.func
|
||||
};
|
||||
};
|
||||
@@ -13,7 +13,7 @@ function getCountsStateFromStores() {
|
||||
|
||||
teamMembers.forEach((member) => {
|
||||
if (member.team_id !== TeamStore.getCurrentId()) {
|
||||
count += ((member.msg_count || 0) + (member.mention_count || 0));
|
||||
count += (member.mention_count || 0);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -27,8 +27,6 @@ function getCountsStateFromStores() {
|
||||
count += channel.total_msg_count - channelMember.msg_count;
|
||||
} else if (channelMember.mention_count > 0) {
|
||||
count += channelMember.mention_count;
|
||||
} else if (channelMember.notify_props.mark_unread !== 'mention' && channel.total_msg_count - channelMember.msg_count > 0) {
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ export default class PostBodyAdditionalContent extends React.Component {
|
||||
}
|
||||
|
||||
generateStaticEmbed() {
|
||||
if (this.props.post.type === Constants.POST_TYPE_ATTACHMENT) {
|
||||
if (this.props.post.props && this.props.post.props.attachments) {
|
||||
return this.getSlackAttachment();
|
||||
}
|
||||
|
||||
|
||||
@@ -111,9 +111,8 @@ export default class SearchBar extends React.Component {
|
||||
}
|
||||
|
||||
handleUserFocus() {
|
||||
$('.search-bar__container').addClass('focused');
|
||||
|
||||
this.setState({focused: true});
|
||||
$('.search-bar__container').addClass('focused');
|
||||
}
|
||||
|
||||
performSearch(terms, isMentionSearch) {
|
||||
@@ -193,19 +192,6 @@ export default class SearchBar extends React.Component {
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
const searchPopover = (
|
||||
<Popover
|
||||
id='searchbar-help-popup'
|
||||
placement='bottom'
|
||||
className={helpClass}
|
||||
>
|
||||
<FormattedHTMLMessage
|
||||
id='search_bar.usage'
|
||||
defaultMessage='<h4>Search Options</h4><ul><li><span>Use </span><b>"quotation marks"</b><span> to search for phrases</span></li><li><span>Use </span><b>from:</b><span> to find posts from specific users and </span><b>in:</b><span> to find posts in specific channels</span></li></ul>'
|
||||
/>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
const flaggedTooltip = (
|
||||
<Tooltip id='flaggedTooltip'>
|
||||
<FormattedMessage
|
||||
@@ -289,28 +275,29 @@ export default class SearchBar extends React.Component {
|
||||
autoComplete='off'
|
||||
>
|
||||
<span className='fa fa-search sidebar__search-icon'/>
|
||||
<OverlayTrigger
|
||||
delayShow={Constants.OVERLAY_TIME_DELAY_SMALL}
|
||||
<SuggestionBox
|
||||
ref='search'
|
||||
className='form-control search-bar'
|
||||
placeholder={Utils.localizeMessage('search_bar.search', 'Search')}
|
||||
value={this.state.searchTerm}
|
||||
onFocus={this.handleUserFocus}
|
||||
onBlur={this.handleUserBlur}
|
||||
onChange={this.handleChange}
|
||||
listComponent={SearchSuggestionList}
|
||||
providers={this.suggestionProviders}
|
||||
type='search'
|
||||
/>
|
||||
{isSearching}
|
||||
<Popover
|
||||
id='searchbar-help-popup'
|
||||
placement='bottom'
|
||||
overlay={searchPopover}
|
||||
trigger='focus'
|
||||
rootClose={true}
|
||||
className={helpClass}
|
||||
>
|
||||
<SuggestionBox
|
||||
ref='search'
|
||||
className='form-control search-bar'
|
||||
placeholder={Utils.localizeMessage('search_bar.search', 'Search')}
|
||||
value={this.state.searchTerm}
|
||||
onFocus={this.handleUserFocus}
|
||||
onBlur={this.handleUserBlur}
|
||||
onChange={this.handleChange}
|
||||
listComponent={SearchSuggestionList}
|
||||
providers={this.suggestionProviders}
|
||||
type='search'
|
||||
<FormattedHTMLMessage
|
||||
id='search_bar.usage'
|
||||
defaultMessage='<h4>Search Options</h4><ul><li><span>Use </span><b>"quotation marks"</b><span> to search for phrases</span></li><li><span>Use </span><b>from:</b><span> to find posts from specific users and </span><b>in:</b><span> to find posts in specific channels</span></li></ul>'
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
{isSearching}
|
||||
</Popover>
|
||||
</form>
|
||||
|
||||
{mentionBtn}
|
||||
|
||||
@@ -180,7 +180,9 @@ export default class SearchableChannelList extends React.Component {
|
||||
ref='channelList'
|
||||
className='more-modal__list'
|
||||
>
|
||||
{listContent}
|
||||
<div>
|
||||
{listContent}
|
||||
</div>
|
||||
</div>
|
||||
<div className='filter-controls'>
|
||||
{previousButton}
|
||||
@@ -202,4 +204,4 @@ SearchableChannelList.propTypes = {
|
||||
search: React.PropTypes.func.isRequired,
|
||||
handleJoin: React.PropTypes.func.isRequired,
|
||||
noResultsText: React.PropTypes.object
|
||||
};
|
||||
};
|
||||
@@ -64,6 +64,10 @@ export default class SuggestionBox extends React.Component {
|
||||
// Delay this slightly so that we don't clear the suggestions before we run click handlers on SuggestionList
|
||||
GlobalActions.emitClearSuggestions(this.suggestionId);
|
||||
}, 100);
|
||||
|
||||
if (this.props.onBlur) {
|
||||
this.props.onBlur();
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
@@ -272,6 +276,7 @@ SuggestionBox.propTypes = {
|
||||
renderDividers: React.PropTypes.bool,
|
||||
|
||||
// explicitly name any input event handlers we override and need to manually call
|
||||
onBlur: React.PropTypes.func,
|
||||
onChange: React.PropTypes.func,
|
||||
onKeyDown: React.PropTypes.func,
|
||||
onItemSelected: React.PropTypes.func
|
||||
|
||||
@@ -7,9 +7,6 @@ import React from 'react';
|
||||
import {Link} from 'react-router/es6';
|
||||
import {Tooltip, OverlayTrigger} from 'react-bootstrap';
|
||||
|
||||
import {isMobile} from 'utils/utils.jsx';
|
||||
import {isMobileApp} from 'utils/user_agent.jsx';
|
||||
|
||||
export default class TeamButton extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -50,14 +47,21 @@ export default class TeamButton extends React.Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (!isMobile() && !isMobileApp()) {
|
||||
if (this.props.isMobile) {
|
||||
btn = (
|
||||
<div className={'team-btn ' + btnClass}>
|
||||
{badge}
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
btn = (
|
||||
<OverlayTrigger
|
||||
delayShow={Constants.OVERLAY_TIME_DELAY}
|
||||
placement={this.props.placement}
|
||||
overlay={
|
||||
<Tooltip id={`tooltip-${this.props.url}`}>
|
||||
{this.props.displayName}
|
||||
{this.props.tip}
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
@@ -67,13 +71,6 @@ export default class TeamButton extends React.Component {
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
} else {
|
||||
btn = (
|
||||
<div className={'team-btn ' + btnClass}>
|
||||
{badge}
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -110,6 +107,7 @@ TeamButton.propTypes = {
|
||||
tip: React.PropTypes.node.isRequired,
|
||||
active: React.PropTypes.bool,
|
||||
disabled: React.PropTypes.bool,
|
||||
isMobile: React.PropTypes.bool,
|
||||
unread: React.PropTypes.bool,
|
||||
mentions: React.PropTypes.number,
|
||||
placement: React.PropTypes.oneOf(['left', 'right', 'top', 'bottom'])
|
||||
|
||||
@@ -34,7 +34,8 @@ export default class TeamSidebar extends React.Component {
|
||||
teamListings: TeamStore.getTeamListings(),
|
||||
teamMembers,
|
||||
currentTeamId,
|
||||
show: teamMembers && teamMembers.length > 1
|
||||
show: teamMembers && teamMembers.length > 1,
|
||||
isMobile: Utils.isMobile()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,12 +54,16 @@ export default class TeamSidebar extends React.Component {
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
$('.team-wrapper').perfectScrollbar();
|
||||
if (!this.state.isMobile) {
|
||||
$('.team-wrapper').perfectScrollbar();
|
||||
}
|
||||
|
||||
// reset the scrollbar upon switching teams
|
||||
if (this.state.currentTeam !== prevState.currentTeam) {
|
||||
this.refs.container.scrollTop = 0;
|
||||
$('.team-wrapper').perfectScrollbar('update');
|
||||
if (!this.state.isMobile) {
|
||||
$('.team-wrapper').perfectScrollbar('update');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +126,7 @@ export default class TeamSidebar extends React.Component {
|
||||
url={`/${team.name}`}
|
||||
tip={team.display_name}
|
||||
active={team.id === this.state.currentTeamId}
|
||||
isMobile={this.state.isMobile}
|
||||
displayName={team.display_name}
|
||||
unread={team.unread}
|
||||
mentions={team.mentions}
|
||||
@@ -134,6 +140,7 @@ export default class TeamSidebar extends React.Component {
|
||||
btnClass='team-btn__add'
|
||||
key='more_teams'
|
||||
url='/select_team'
|
||||
isMobile={this.state.isMobile}
|
||||
tip={
|
||||
<FormattedMessage
|
||||
id='team_sidebar.join'
|
||||
@@ -149,6 +156,7 @@ export default class TeamSidebar extends React.Component {
|
||||
btnClass='team-btn__add'
|
||||
key='more_teams'
|
||||
url='/create_team'
|
||||
isMobile={this.state.isMobile}
|
||||
tip={
|
||||
<FormattedMessage
|
||||
id='navbar_dropdown.create'
|
||||
|
||||
Ссылка в новой задаче
Block a user