PLT-914 Add mention notifications for replies on a comment thread (#3130)
* PLT-914 Add mention notifications for replies on a comment thread * remove useless store method fix highlighting comments posted before th user write something to thread * refactor out isCommentMention function after rebase * change comment bar highlighting to replay icon mention highlighting * settings and always visible highlight * fix unit tests for new settings * change highlight behaviour - if any message in comment thread generates mention - all thread is highlighted - remove always visible highlightion * fix bug about the textarea in the center channel not clearing * fix default settings value notify_props.comments * do not highlight own comments if there is no other user's messages in thread * refactor out ReactDOM.findDOMNode * refactor out using of UserStore from component
Этот коммит содержится в:
коммит произвёл
Christopher Speller
родитель
febe3a01cd
Коммит
f31e8e09f5
21
api/post.go
21
api/post.go
@@ -535,9 +535,8 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
|||||||
return model.SplitRunes[c]
|
return model.SplitRunes[c]
|
||||||
}
|
}
|
||||||
splitMessage := strings.Fields(post.Message)
|
splitMessage := strings.Fields(post.Message)
|
||||||
for _, word := range splitMessage {
|
|
||||||
var userIds []string
|
var userIds []string
|
||||||
|
for _, word := range splitMessage {
|
||||||
// Non-case-sensitive check for regular keys
|
// Non-case-sensitive check for regular keys
|
||||||
if ids, match := keywordMap[strings.ToLower(word)]; match {
|
if ids, match := keywordMap[strings.ToLower(word)]; match {
|
||||||
userIds = append(userIds, ids...)
|
userIds = append(userIds, ids...)
|
||||||
@@ -565,6 +564,23 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(post.RootId) > 0 {
|
||||||
|
if result := <-Srv.Store.Post().Get(post.RootId); result.Err != nil {
|
||||||
|
l4g.Error(utils.T("api.post.send_notifications_and_forget.comment_thread.error"), post.RootId, result.Err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
list := result.Data.(*model.PostList)
|
||||||
|
|
||||||
|
for _, threadPost := range list.Posts {
|
||||||
|
profile := profileMap[threadPost.UserId]
|
||||||
|
if profile.NotifyProps["comments"] == "any" || (profile.NotifyProps["comments"] == "root" && threadPost.Id == list.Order[0]) {
|
||||||
|
userIds = append(userIds, threadPost.UserId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, userId := range userIds {
|
for _, userId := range userIds {
|
||||||
if post.UserId == userId && post.Props["from_webhook"] != "true" {
|
if post.UserId == userId && post.Props["from_webhook"] != "true" {
|
||||||
@@ -573,7 +589,6 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
|||||||
|
|
||||||
mentionedUserIds[userId] = true
|
mentionedUserIds[userId] = true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for id := range mentionedUserIds {
|
for id := range mentionedUserIds {
|
||||||
go updateMentionCount(post.ChannelId, id)
|
go updateMentionCount(post.ChannelId, id)
|
||||||
|
|||||||
@@ -1930,6 +1930,12 @@ func updateUserNotify(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
comments := props["comments"]
|
||||||
|
if len(comments) == 0 {
|
||||||
|
c.SetInvalidParam("updateUserNotify", "comments")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var user *model.User
|
var user *model.User
|
||||||
if result := <-uchan; result.Err != nil {
|
if result := <-uchan; result.Err != nil {
|
||||||
c.Err = result.Err
|
c.Err = result.Err
|
||||||
|
|||||||
@@ -1247,6 +1247,7 @@ func TestUserUpdateNotify(t *testing.T) {
|
|||||||
data["email"] = "true"
|
data["email"] = "true"
|
||||||
data["desktop"] = "all"
|
data["desktop"] = "all"
|
||||||
data["desktop_sound"] = "false"
|
data["desktop_sound"] = "false"
|
||||||
|
data["comments"] = "any"
|
||||||
|
|
||||||
if _, err := Client.UpdateUserNotify(data); err == nil {
|
if _, err := Client.UpdateUserNotify(data); err == nil {
|
||||||
t.Fatal("Should have errored - not logged in")
|
t.Fatal("Should have errored - not logged in")
|
||||||
@@ -1267,6 +1268,9 @@ func TestUserUpdateNotify(t *testing.T) {
|
|||||||
if result.Data.(*model.User).NotifyProps["email"] != data["email"] {
|
if result.Data.(*model.User).NotifyProps["email"] != data["email"] {
|
||||||
t.Fatal("NotifyProps did not update properly - email")
|
t.Fatal("NotifyProps did not update properly - email")
|
||||||
}
|
}
|
||||||
|
if result.Data.(*model.User).NotifyProps["comments"] != data["comments"] {
|
||||||
|
t.Fatal("NotifyProps did not update properly - comments")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := Client.UpdateUserNotify(nil); err == nil {
|
if _, err := Client.UpdateUserNotify(nil); err == nil {
|
||||||
@@ -1300,6 +1304,12 @@ func TestUserUpdateNotify(t *testing.T) {
|
|||||||
if _, err := Client.UpdateUserNotify(data); err == nil {
|
if _, err := Client.UpdateUserNotify(data); err == nil {
|
||||||
t.Fatal("Should have errored - empty email")
|
t.Fatal("Should have errored - empty email")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data["email"] = "true"
|
||||||
|
data["comments"] = ""
|
||||||
|
if _, err := Client.UpdateUserNotify(data); err == nil {
|
||||||
|
t.Fatal("Should have errored - empty comments")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFuzzyUserCreate(t *testing.T) {
|
func TestFuzzyUserCreate(t *testing.T) {
|
||||||
|
|||||||
@@ -1023,6 +1023,10 @@
|
|||||||
"id": "api.post.make_direct_channel_visible.update_pref.error",
|
"id": "api.post.make_direct_channel_visible.update_pref.error",
|
||||||
"translation": "Failed to update direct channel preference user_id=%v other_user_id=%v err=%v"
|
"translation": "Failed to update direct channel preference user_id=%v other_user_id=%v err=%v"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "api.post.send_notifications_and_forget.comment_thread.error",
|
||||||
|
"translation": "Failed to retrieve comment thread posts in notifications root_post_id=%v, err=%v"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "api.post.send_notifications_and_forget.mention_body",
|
"id": "api.post.send_notifications_and_forget.mention_body",
|
||||||
"translation": "You have one new mention."
|
"translation": "You have one new mention."
|
||||||
|
|||||||
@@ -76,6 +76,10 @@ export default class Post extends React.Component {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nextProps.isCommentMention !== this.props.isCommentMention) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (nextProps.shouldHighlight !== this.props.shouldHighlight) {
|
if (nextProps.shouldHighlight !== this.props.shouldHighlight) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -231,6 +235,7 @@ export default class Post extends React.Component {
|
|||||||
post={post}
|
post={post}
|
||||||
sameRoot={this.props.sameRoot}
|
sameRoot={this.props.sameRoot}
|
||||||
commentCount={commentCount}
|
commentCount={commentCount}
|
||||||
|
isCommentMention={this.props.isCommentMention}
|
||||||
handleCommentClick={this.handleCommentClick}
|
handleCommentClick={this.handleCommentClick}
|
||||||
handleDropdownOpened={this.handleDropdownOpened}
|
handleDropdownOpened={this.handleDropdownOpened}
|
||||||
isLastComment={this.props.isLastComment}
|
isLastComment={this.props.isLastComment}
|
||||||
@@ -274,6 +279,7 @@ Post.propTypes = {
|
|||||||
compactDisplay: React.PropTypes.bool,
|
compactDisplay: React.PropTypes.bool,
|
||||||
previewCollapsed: React.PropTypes.string,
|
previewCollapsed: React.PropTypes.string,
|
||||||
commentCount: React.PropTypes.number,
|
commentCount: React.PropTypes.number,
|
||||||
|
isCommentMention: React.PropTypes.bool,
|
||||||
useMilitaryTime: React.PropTypes.bool.isRequired,
|
useMilitaryTime: React.PropTypes.bool.isRequired,
|
||||||
emojis: React.PropTypes.object.isRequired
|
emojis: React.PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ export default class PostHeader extends React.Component {
|
|||||||
<PostInfo
|
<PostInfo
|
||||||
post={post}
|
post={post}
|
||||||
commentCount={this.props.commentCount}
|
commentCount={this.props.commentCount}
|
||||||
|
isCommentMention={this.props.isCommentMention}
|
||||||
handleCommentClick={this.props.handleCommentClick}
|
handleCommentClick={this.props.handleCommentClick}
|
||||||
handleDropdownOpened={this.props.handleDropdownOpened}
|
handleDropdownOpened={this.props.handleDropdownOpened}
|
||||||
allowReply='true'
|
allowReply='true'
|
||||||
@@ -89,6 +90,7 @@ PostHeader.propTypes = {
|
|||||||
user: React.PropTypes.object,
|
user: React.PropTypes.object,
|
||||||
currentUser: React.PropTypes.object.isRequired,
|
currentUser: React.PropTypes.object.isRequired,
|
||||||
commentCount: React.PropTypes.number.isRequired,
|
commentCount: React.PropTypes.number.isRequired,
|
||||||
|
isCommentMention: React.PropTypes.bool.isRequired,
|
||||||
isLastComment: React.PropTypes.bool.isRequired,
|
isLastComment: React.PropTypes.bool.isRequired,
|
||||||
handleCommentClick: React.PropTypes.func.isRequired,
|
handleCommentClick: React.PropTypes.func.isRequired,
|
||||||
handleDropdownOpened: React.PropTypes.func.isRequired,
|
handleDropdownOpened: React.PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ export default class PostInfo extends React.Component {
|
|||||||
var post = this.props.post;
|
var post = this.props.post;
|
||||||
var comments = '';
|
var comments = '';
|
||||||
var showCommentClass = '';
|
var showCommentClass = '';
|
||||||
|
var highlightMentionClass = '';
|
||||||
var commentCountText = this.props.commentCount;
|
var commentCountText = this.props.commentCount;
|
||||||
|
|
||||||
if (this.props.commentCount >= 1) {
|
if (this.props.commentCount >= 1) {
|
||||||
@@ -182,11 +183,15 @@ export default class PostInfo extends React.Component {
|
|||||||
commentCountText = '';
|
commentCountText = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.isCommentMention) {
|
||||||
|
highlightMentionClass = ' mention--highlight';
|
||||||
|
}
|
||||||
|
|
||||||
if (post.state !== Constants.POST_FAILED && post.state !== Constants.POST_LOADING && !Utils.isPostEphemeral(post)) {
|
if (post.state !== Constants.POST_FAILED && post.state !== Constants.POST_LOADING && !Utils.isPostEphemeral(post)) {
|
||||||
comments = (
|
comments = (
|
||||||
<a
|
<a
|
||||||
href='#'
|
href='#'
|
||||||
className={'comment-icon__container' + showCommentClass}
|
className={'comment-icon__container' + showCommentClass + highlightMentionClass}
|
||||||
onClick={this.props.handleCommentClick}
|
onClick={this.props.handleCommentClick}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -234,6 +239,7 @@ PostInfo.defaultProps = {
|
|||||||
PostInfo.propTypes = {
|
PostInfo.propTypes = {
|
||||||
post: React.PropTypes.object.isRequired,
|
post: React.PropTypes.object.isRequired,
|
||||||
commentCount: React.PropTypes.number.isRequired,
|
commentCount: React.PropTypes.number.isRequired,
|
||||||
|
isCommentMention: React.PropTypes.bool.isRequired,
|
||||||
isLastComment: React.PropTypes.bool.isRequired,
|
isLastComment: React.PropTypes.bool.isRequired,
|
||||||
allowReply: React.PropTypes.string.isRequired,
|
allowReply: React.PropTypes.string.isRequired,
|
||||||
handleCommentClick: React.PropTypes.func.isRequired,
|
handleCommentClick: React.PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -251,15 +251,35 @@ export default class PostList extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let commentCount = 0;
|
let commentCount = 0;
|
||||||
|
let nonOwnCommentsExists = false;
|
||||||
|
let isCommentMention = false;
|
||||||
let commentRootId;
|
let commentRootId;
|
||||||
if (parentPost) {
|
if (parentPost) {
|
||||||
commentRootId = post.root_id;
|
commentRootId = post.root_id;
|
||||||
} else {
|
} else {
|
||||||
commentRootId = post.id;
|
commentRootId = post.id;
|
||||||
}
|
}
|
||||||
|
if (commentRootId) {
|
||||||
|
const commentsNotifyLevel = this.props.currentUser.notify_props.comments || 'never';
|
||||||
for (const postId in posts) {
|
for (const postId in posts) {
|
||||||
if (posts[postId].root_id === commentRootId) {
|
if (posts[postId].root_id === commentRootId) {
|
||||||
commentCount += 1;
|
commentCount += 1;
|
||||||
|
if (posts[postId].user_id !== this.props.currentUser.id) {
|
||||||
|
nonOwnCommentsExists = true;
|
||||||
|
}
|
||||||
|
if (posts[postId].user_id === this.props.currentUser.id && commentsNotifyLevel === 'any' && !isCommentMention) {
|
||||||
|
for (const nextPostId in posts) {
|
||||||
|
if (posts[nextPostId].root_id === commentRootId && posts[nextPostId].user_id !== this.props.currentUser.id &&
|
||||||
|
posts[postId].create_at < posts[nextPostId].create_at) {
|
||||||
|
isCommentMention = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nonOwnCommentsExists && posts[commentRootId].user_id === this.props.currentUser.id && commentsNotifyLevel !== 'never') {
|
||||||
|
isCommentMention = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +299,7 @@ export default class PostList extends React.Component {
|
|||||||
currentUser={this.props.currentUser}
|
currentUser={this.props.currentUser}
|
||||||
center={this.props.displayPostsInCenter}
|
center={this.props.displayPostsInCenter}
|
||||||
commentCount={commentCount}
|
commentCount={commentCount}
|
||||||
|
isCommentMention={isCommentMention}
|
||||||
compactDisplay={this.props.compactDisplay}
|
compactDisplay={this.props.compactDisplay}
|
||||||
previewCollapsed={this.props.previewsCollapsed}
|
previewCollapsed={this.props.previewsCollapsed}
|
||||||
useMilitaryTime={this.props.useMilitaryTime}
|
useMilitaryTime={this.props.useMilitaryTime}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import SettingItemMin from '../setting_item_min.jsx';
|
import SettingItemMin from '../setting_item_min.jsx';
|
||||||
import SettingItemMax from '../setting_item_max.jsx';
|
import SettingItemMax from '../setting_item_max.jsx';
|
||||||
|
|
||||||
@@ -26,6 +25,10 @@ function getNotificationsStateFromStores() {
|
|||||||
if (user.notify_props && user.notify_props.desktop) {
|
if (user.notify_props && user.notify_props.desktop) {
|
||||||
desktop = user.notify_props.desktop;
|
desktop = user.notify_props.desktop;
|
||||||
}
|
}
|
||||||
|
var comments = 'never';
|
||||||
|
if (user.notify_props && user.notify_props.comments) {
|
||||||
|
comments = user.notify_props.comments;
|
||||||
|
}
|
||||||
var email = 'true';
|
var email = 'true';
|
||||||
if (user.notify_props && user.notify_props.email) {
|
if (user.notify_props && user.notify_props.email) {
|
||||||
email = user.notify_props.email;
|
email = user.notify_props.email;
|
||||||
@@ -82,7 +85,8 @@ function getNotificationsStateFromStores() {
|
|||||||
customKeys,
|
customKeys,
|
||||||
customKeysChecked: customKeys.length > 0,
|
customKeysChecked: customKeys.length > 0,
|
||||||
firstNameKey,
|
firstNameKey,
|
||||||
channelKey
|
channelKey,
|
||||||
|
notifyCommentsLevel: comments
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +107,10 @@ const holders = defineMessages({
|
|||||||
id: 'user.settings.notifications.wordsTrigger',
|
id: 'user.settings.notifications.wordsTrigger',
|
||||||
defaultMessage: 'Words that trigger mentions'
|
defaultMessage: 'Words that trigger mentions'
|
||||||
},
|
},
|
||||||
|
comments: {
|
||||||
|
id: 'user.settings.notifications.comments',
|
||||||
|
defaultMessage: 'Comment threads notifications'
|
||||||
|
},
|
||||||
close: {
|
close: {
|
||||||
id: 'user.settings.notifications.close',
|
id: 'user.settings.notifications.close',
|
||||||
defaultMessage: 'Close'
|
defaultMessage: 'Close'
|
||||||
@@ -140,6 +148,7 @@ class NotificationsTab extends React.Component {
|
|||||||
data.desktop_sound = this.state.enableSound;
|
data.desktop_sound = this.state.enableSound;
|
||||||
data.desktop = this.state.notifyLevel;
|
data.desktop = this.state.notifyLevel;
|
||||||
data.push = this.state.notifyPushLevel;
|
data.push = this.state.notifyPushLevel;
|
||||||
|
data.comments = this.state.notifyCommentsLevel;
|
||||||
|
|
||||||
var mentionKeys = [];
|
var mentionKeys = [];
|
||||||
if (this.state.usernameKey) {
|
if (this.state.usernameKey) {
|
||||||
@@ -195,21 +204,26 @@ class NotificationsTab extends React.Component {
|
|||||||
}
|
}
|
||||||
handleNotifyRadio(notifyLevel) {
|
handleNotifyRadio(notifyLevel) {
|
||||||
this.setState({notifyLevel});
|
this.setState({notifyLevel});
|
||||||
ReactDOM.findDOMNode(this.refs.wrapper).focus();
|
this.refs.wrapper.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNotifyCommentsRadio(notifyCommentsLevel) {
|
||||||
|
this.setState({notifyCommentsLevel});
|
||||||
|
this.refs.wrapper.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePushRadio(notifyPushLevel) {
|
handlePushRadio(notifyPushLevel) {
|
||||||
this.setState({notifyPushLevel});
|
this.setState({notifyPushLevel});
|
||||||
ReactDOM.findDOMNode(this.refs.wrapper).focus();
|
this.refs.wrapper.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEmailRadio(enableEmail) {
|
handleEmailRadio(enableEmail) {
|
||||||
this.setState({enableEmail});
|
this.setState({enableEmail});
|
||||||
ReactDOM.findDOMNode(this.refs.wrapper).focus();
|
this.refs.wrapper.focus();
|
||||||
}
|
}
|
||||||
handleSoundRadio(enableSound) {
|
handleSoundRadio(enableSound) {
|
||||||
this.setState({enableSound});
|
this.setState({enableSound});
|
||||||
ReactDOM.findDOMNode(this.refs.wrapper).focus();
|
this.refs.wrapper.focus();
|
||||||
}
|
}
|
||||||
updateUsernameKey(val) {
|
updateUsernameKey(val) {
|
||||||
this.setState({usernameKey: val});
|
this.setState({usernameKey: val});
|
||||||
@@ -224,10 +238,10 @@ class NotificationsTab extends React.Component {
|
|||||||
this.setState({channelKey: val});
|
this.setState({channelKey: val});
|
||||||
}
|
}
|
||||||
updateCustomMentionKeys() {
|
updateCustomMentionKeys() {
|
||||||
var checked = ReactDOM.findDOMNode(this.refs.customcheck).checked;
|
var checked = this.refs.customcheck.checked;
|
||||||
|
|
||||||
if (checked) {
|
if (checked) {
|
||||||
var text = ReactDOM.findDOMNode(this.refs.custommentions).value;
|
var text = this.refs.custommentions.value;
|
||||||
|
|
||||||
// remove all spaces and split string into individual keys
|
// remove all spaces and split string into individual keys
|
||||||
this.setState({customKeys: text.replace(/ /g, ''), customKeysChecked: true});
|
this.setState({customKeys: text.replace(/ /g, ''), customKeysChecked: true});
|
||||||
@@ -236,7 +250,7 @@ class NotificationsTab extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onCustomChange() {
|
onCustomChange() {
|
||||||
ReactDOM.findDOMNode(this.refs.customcheck).checked = true;
|
this.refs.customcheck.checked = true;
|
||||||
this.updateCustomMentionKeys();
|
this.updateCustomMentionKeys();
|
||||||
}
|
}
|
||||||
createPushNotificationSection() {
|
createPushNotificationSection() {
|
||||||
@@ -902,6 +916,126 @@ class NotificationsTab extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var commentsSection;
|
||||||
|
var handleUpdateCommentsSection;
|
||||||
|
if (this.props.activeSection === 'comments') {
|
||||||
|
var commentsActive = [false, false, false];
|
||||||
|
if (this.state.notifyCommentsLevel === 'never') {
|
||||||
|
commentsActive[2] = true;
|
||||||
|
} else if (this.state.notifyCommentsLevel === 'root') {
|
||||||
|
commentsActive[1] = true;
|
||||||
|
} else {
|
||||||
|
commentsActive[0] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputs = [];
|
||||||
|
|
||||||
|
inputs.push(
|
||||||
|
<div key='userNotificationLevelOption'>
|
||||||
|
<div className='radio'>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='commentsNotificationLevel'
|
||||||
|
checked={commentsActive[0]}
|
||||||
|
onChange={this.handleNotifyCommentsRadio.bind(this, 'any')}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.notifications.commentsAny'
|
||||||
|
defaultMessage='Mention any comments in a thread you participated in (This will include both mentions to your root post and any comments after you commented on a post)'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<div className='radio'>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='commentsNotificationLevel'
|
||||||
|
checked={commentsActive[1]}
|
||||||
|
onChange={this.handleNotifyCommentsRadio.bind(this, 'root')}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.notifications.commentsRoot'
|
||||||
|
defaultMessage='Mention any comments on your post'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<div className='radio'>
|
||||||
|
<label>
|
||||||
|
<input
|
||||||
|
type='radio'
|
||||||
|
name='commentsNotificationLevel'
|
||||||
|
checked={commentsActive[2]}
|
||||||
|
onChange={this.handleNotifyCommentsRadio.bind(this, 'never')}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.notifications.commentsNever'
|
||||||
|
defaultMessage='No mentions for comments'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const extraInfo = (
|
||||||
|
<span>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.notifications.commentsInfo'
|
||||||
|
defaultMessage='Mode of triggering notifications on posts in comment threads you participated in.'
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
commentsSection = (
|
||||||
|
<SettingItemMax
|
||||||
|
title={formatMessage(holders.comments)}
|
||||||
|
extraInfo={extraInfo}
|
||||||
|
inputs={inputs}
|
||||||
|
submit={this.handleSubmit}
|
||||||
|
server_error={serverError}
|
||||||
|
updateSection={this.handleCancel}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let describe = '';
|
||||||
|
if (this.state.notifyCommentsLevel === 'never') {
|
||||||
|
describe = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.notifications.commentsNever'
|
||||||
|
defaultMessage='No mentions for comments'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (this.state.notifyCommentsLevel === 'root') {
|
||||||
|
describe = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.notifications.commentsRoot'
|
||||||
|
defaultMessage='Mention any comments on your post'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
describe = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.notifications.commentsAny'
|
||||||
|
defaultMessage='Mention any comments in a thread you participated in (This will include both mentions to your root post and any comments after you commented on a post)'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUpdateCommentsSection = function updateCommentsSection() {
|
||||||
|
this.props.updateSection('comments');
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
commentsSection = (
|
||||||
|
<SettingItemMin
|
||||||
|
title={formatMessage(holders.comments)}
|
||||||
|
describe={describe}
|
||||||
|
updateSection={handleUpdateCommentsSection}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const pushNotificationSection = this.createPushNotificationSection();
|
const pushNotificationSection = this.createPushNotificationSection();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -952,6 +1086,8 @@ class NotificationsTab extends React.Component {
|
|||||||
{pushNotificationSection}
|
{pushNotificationSection}
|
||||||
<div className='divider-light'/>
|
<div className='divider-light'/>
|
||||||
{keysSection}
|
{keysSection}
|
||||||
|
<div className='divider-light'/>
|
||||||
|
{commentsSection}
|
||||||
<div className='divider-dark'/>
|
<div className='divider-dark'/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1610,6 +1610,11 @@
|
|||||||
"user.settings.notification.soundConfig": "Please configure notification sounds in your browser settings",
|
"user.settings.notification.soundConfig": "Please configure notification sounds in your browser settings",
|
||||||
"user.settings.notifications.channelWide": "Channel-wide mentions \"@channel\", \"@all\"",
|
"user.settings.notifications.channelWide": "Channel-wide mentions \"@channel\", \"@all\"",
|
||||||
"user.settings.notifications.close": "Close",
|
"user.settings.notifications.close": "Close",
|
||||||
|
"user.settings.notifications.comments": "Comment threads notifications",
|
||||||
|
"user.settings.notifications.commentsAny": "Mention any comments in a thread you participated in (This will include both mentions to your root post and any comments after you commented on a post)",
|
||||||
|
"user.settings.notifications.commentsInfo": "Mode of triggering notifications on posts in comment threads you participated in.",
|
||||||
|
"user.settings.notifications.commentsNever": "No mentions for comments",
|
||||||
|
"user.settings.notifications.commentsRoot": "Mention any comments on your post",
|
||||||
"user.settings.notifications.desktop": "Send desktop notifications",
|
"user.settings.notifications.desktop": "Send desktop notifications",
|
||||||
"user.settings.notifications.desktopSounds": "Desktop notification sounds",
|
"user.settings.notifications.desktopSounds": "Desktop notification sounds",
|
||||||
"user.settings.notifications.emailInfo": "Email notifications are sent for mentions and direct messages after you’ve been offline for more than 60 seconds or away from {siteName} for more than 5 minutes.",
|
"user.settings.notifications.emailInfo": "Email notifications are sent for mentions and direct messages after you’ve been offline for more than 60 seconds or away from {siteName} for more than 5 minutes.",
|
||||||
|
|||||||
@@ -1070,7 +1070,6 @@ body.ios {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: inherit;
|
fill: inherit;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user