Merge pull request #1613 from florianorben/PLT-1326
PLT-1326: Enable channel posts of type join or leave not trigger unread notifications
Этот коммит содержится в:
@@ -421,7 +421,7 @@ func JoinChannel(c *Context, channelId string, role string) {
|
|||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(`User %v has joined this channel.`, user.Username))
|
PostUserAddRemoveMessageAndForget(c, channel.Id, fmt.Sprintf(`%v has joined the channel.`, user.Username))
|
||||||
} else {
|
} else {
|
||||||
c.Err = model.NewAppError("join", "You do not have the appropriate permissions", "")
|
c.Err = model.NewAppError("join", "You do not have the appropriate permissions", "")
|
||||||
c.Err.StatusCode = http.StatusForbidden
|
c.Err.StatusCode = http.StatusForbidden
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
POST_DEFAULT = ""
|
POST_SYSTEM_MESSAGE_PREFIX = "system_"
|
||||||
POST_SLACK_ATTACHMENT = "slack_attachment"
|
POST_DEFAULT = ""
|
||||||
POST_JOIN_LEAVE = "join_leave"
|
POST_SLACK_ATTACHMENT = "slack_attachment"
|
||||||
|
POST_JOIN_LEAVE = "system_join_leave"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Post struct {
|
type Post struct {
|
||||||
@@ -159,3 +160,7 @@ func (o *Post) AddProp(key string, value interface{}) {
|
|||||||
|
|
||||||
func (o *Post) PreExport() {
|
func (o *Post) PreExport() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Post) IsSystemMessage() bool {
|
||||||
|
return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX
|
||||||
|
}
|
||||||
|
|||||||
@@ -98,3 +98,18 @@ func TestPostPreSave(t *testing.T) {
|
|||||||
|
|
||||||
o.Etag()
|
o.Etag()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPostIsSystemMessage(t *testing.T) {
|
||||||
|
post1 := Post{Message: "test_1"}
|
||||||
|
post1.PreSave()
|
||||||
|
|
||||||
|
if post1.IsSystemMessage() {
|
||||||
|
t.Fatalf("TestPostIsSystemMessage failed, expected post1.IsSystemMessage() to be false")
|
||||||
|
}
|
||||||
|
|
||||||
|
post2 := Post{Message: "test_2", Type: POST_JOIN_LEAVE}
|
||||||
|
post2.PreSave()
|
||||||
|
if !post2.IsSystemMessage() {
|
||||||
|
t.Fatalf("TestPostIsSystemMessage failed, expected post2.IsSystemMessage() to be true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ func NewSqlPostStore(sqlStore *SqlStore) PostStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlPostStore) UpgradeSchemaIfNeeded() {
|
func (s SqlPostStore) UpgradeSchemaIfNeeded() {
|
||||||
s.RemoveColumnIfExists("Posts", "ImgCount") // remove after 1.3 release
|
s.RemoveColumnIfExists("Posts", "ImgCount") // remove after 1.3 release
|
||||||
|
s.GetMaster().Exec(`UPDATE Preferences SET Type = :NewType WHERE Type = :CurrentType`, map[string]string{"NewType": model.POST_JOIN_LEAVE, "CurrentType": "join_leave"}) // remove after 1.3 release
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlPostStore) CreateIndexesIfNotExists() {
|
func (s SqlPostStore) CreateIndexesIfNotExists() {
|
||||||
|
|||||||
@@ -173,6 +173,11 @@ export default class Post extends React.Component {
|
|||||||
shouldHighlightClass = 'post--highlight';
|
shouldHighlightClass = 'post--highlight';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let systemMessageClass = '';
|
||||||
|
if (utils.isSystemMessage(post)) {
|
||||||
|
systemMessageClass = 'post--system';
|
||||||
|
}
|
||||||
|
|
||||||
let profilePic = null;
|
let profilePic = null;
|
||||||
if (!this.props.hideProfilePic) {
|
if (!this.props.hideProfilePic) {
|
||||||
let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex();
|
let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex();
|
||||||
@@ -180,6 +185,8 @@ export default class Post extends React.Component {
|
|||||||
if (post.props.override_icon_url) {
|
if (post.props.override_icon_url) {
|
||||||
src = post.props.override_icon_url;
|
src = post.props.override_icon_url;
|
||||||
}
|
}
|
||||||
|
} else if (utils.isSystemMessage(post)) {
|
||||||
|
src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
profilePic = (
|
profilePic = (
|
||||||
@@ -195,7 +202,7 @@ export default class Post extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
id={'post_' + post.id}
|
id={'post_' + post.id}
|
||||||
className={'post ' + sameUserClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss + ' ' + shouldHighlightClass}
|
className={'post ' + sameUserClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss + ' ' + shouldHighlightClass + ' ' + systemMessageClass}
|
||||||
>
|
>
|
||||||
<div className='post__content'>
|
<div className='post__content'>
|
||||||
<div className='post__img'>{profilePic}</div>
|
<div className='post__img'>{profilePic}</div>
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
import UserProfile from './user_profile.jsx';
|
import UserProfile from './user_profile.jsx';
|
||||||
import PostInfo from './post_info.jsx';
|
import PostInfo from './post_info.jsx';
|
||||||
|
import * as Utils from '../utils/utils.jsx';
|
||||||
|
|
||||||
|
import Constants from '../utils/constants.jsx';
|
||||||
|
|
||||||
export default class PostHeader extends React.Component {
|
export default class PostHeader extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -27,6 +30,15 @@ export default class PostHeader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>;
|
botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>;
|
||||||
|
} else if (Utils.isSystemMessage(post)) {
|
||||||
|
userProfile = (
|
||||||
|
<UserProfile
|
||||||
|
userId={''}
|
||||||
|
overwriteName={''}
|
||||||
|
overwriteImage={Constants.SYSTEM_MESSAGE_PROFILE_IMAGE}
|
||||||
|
disablePopover={true}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export default class PostsView extends React.Component {
|
|||||||
const post = posts[order[i]];
|
const post = posts[order[i]];
|
||||||
const parentPost = posts[post.parent_id];
|
const parentPost = posts[post.parent_id];
|
||||||
const prevPost = posts[order[i + 1]];
|
const prevPost = posts[order[i + 1]];
|
||||||
|
const postUserId = Utils.isSystemMessage(post) ? '' : post.user_id;
|
||||||
|
|
||||||
// If the post is a comment whose parent has been deleted, don't add it to the list.
|
// If the post is a comment whose parent has been deleted, don't add it to the list.
|
||||||
if (parentPost && parentPost.state === Constants.POST_DELETED) {
|
if (parentPost && parentPost.state === Constants.POST_DELETED) {
|
||||||
@@ -102,6 +103,7 @@ export default class PostsView extends React.Component {
|
|||||||
const prevPostIsComment = Utils.isComment(prevPost);
|
const prevPostIsComment = Utils.isComment(prevPost);
|
||||||
const postFromWebhook = Boolean(post.props && post.props.from_webhook);
|
const postFromWebhook = Boolean(post.props && post.props.from_webhook);
|
||||||
const prevPostFromWebhook = Boolean(prevPost.props && prevPost.props.from_webhook);
|
const prevPostFromWebhook = Boolean(prevPost.props && prevPost.props.from_webhook);
|
||||||
|
const prevPostUserId = Utils.isSystemMessage(prevPost) ? '' : prevPostUserId;
|
||||||
let prevWebhookName = '';
|
let prevWebhookName = '';
|
||||||
if (prevPost.props && prevPost.props.override_username) {
|
if (prevPost.props && prevPost.props.override_username) {
|
||||||
prevWebhookName = prevPost.props.override_username;
|
prevWebhookName = prevPost.props.override_username;
|
||||||
@@ -116,7 +118,7 @@ export default class PostsView extends React.Component {
|
|||||||
// the previous post was made within 5 minutes of the current post,
|
// the previous post was made within 5 minutes of the current post,
|
||||||
// the previous post and current post are both from webhooks or both not,
|
// the previous post and current post are both from webhooks or both not,
|
||||||
// the previous post and current post have the same webhook usernames
|
// the previous post and current post have the same webhook usernames
|
||||||
if (prevPost.user_id === post.user_id &&
|
if (prevPostUserId === postUserId &&
|
||||||
post.create_at - prevPost.create_at <= 1000 * 60 * 5 &&
|
post.create_at - prevPost.create_at <= 1000 * 60 * 5 &&
|
||||||
postFromWebhook === prevPostFromWebhook &&
|
postFromWebhook === prevPostFromWebhook &&
|
||||||
prevWebhookName === curWebhookName) {
|
prevWebhookName === curWebhookName) {
|
||||||
@@ -144,7 +146,7 @@ export default class PostsView extends React.Component {
|
|||||||
// the current post is not a comment,
|
// the current post is not a comment,
|
||||||
// the previous post and current post are both from webhooks or both not,
|
// the previous post and current post are both from webhooks or both not,
|
||||||
// the previous post and current post have the same webhook usernames
|
// the previous post and current post have the same webhook usernames
|
||||||
if (prevPost.user_id === post.user_id &&
|
if (prevPostUserId === postUserId &&
|
||||||
!prevPostIsComment &&
|
!prevPostIsComment &&
|
||||||
!postIsComment &&
|
!postIsComment &&
|
||||||
postFromWebhook === prevPostFromWebhook &&
|
postFromWebhook === prevPostFromWebhook &&
|
||||||
@@ -191,7 +193,7 @@ export default class PostsView extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (post.user_id !== userId &&
|
if (postUserId !== userId &&
|
||||||
this.props.messageSeparatorTime !== 0 &&
|
this.props.messageSeparatorTime !== 0 &&
|
||||||
post.create_at > this.props.messageSeparatorTime &&
|
post.create_at > this.props.messageSeparatorTime &&
|
||||||
!renderedLastViewed) {
|
!renderedLastViewed) {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import twemoji from 'twemoji';
|
|||||||
import PostBodyAdditionalContent from './post_body_additional_content.jsx';
|
import PostBodyAdditionalContent from './post_body_additional_content.jsx';
|
||||||
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
||||||
|
|
||||||
|
import Constants from '../utils/constants.jsx';
|
||||||
|
|
||||||
export default class RhsRootPost extends React.Component {
|
export default class RhsRootPost extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -58,6 +60,11 @@ export default class RhsRootPost extends React.Component {
|
|||||||
currentUserCss = 'current--user';
|
currentUserCss = 'current--user';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var systemMessageClass = '';
|
||||||
|
if (utils.isSystemMessage(post)) {
|
||||||
|
systemMessageClass = 'post--system';
|
||||||
|
}
|
||||||
|
|
||||||
var channelName;
|
var channelName;
|
||||||
if (channel) {
|
if (channel) {
|
||||||
if (channel.type === 'D') {
|
if (channel.type === 'D') {
|
||||||
@@ -156,6 +163,15 @@ export default class RhsRootPost extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>;
|
botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>;
|
||||||
|
} else if (utils.isSystemMessage(post)) {
|
||||||
|
userProfile = (
|
||||||
|
<UserProfile
|
||||||
|
userId={''}
|
||||||
|
overwriteName={''}
|
||||||
|
overwriteImage={Constants.SYSTEM_MESSAGE_PROFILE_IMAGE}
|
||||||
|
disablePopover={true}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex();
|
let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex();
|
||||||
@@ -163,6 +179,8 @@ export default class RhsRootPost extends React.Component {
|
|||||||
if (post.props.override_icon_url) {
|
if (post.props.override_icon_url) {
|
||||||
src = post.props.override_icon_url;
|
src = post.props.override_icon_url;
|
||||||
}
|
}
|
||||||
|
} else if (utils.isSystemMessage(post)) {
|
||||||
|
src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const profilePic = (
|
const profilePic = (
|
||||||
@@ -175,7 +193,7 @@ export default class RhsRootPost extends React.Component {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'post post--root ' + currentUserCss}>
|
<div className={'post post--root ' + currentUserCss + ' ' + systemMessageClass}>
|
||||||
<div className='post-right-channel__name'>{channelName}</div>
|
<div className='post-right-channel__name'>{channelName}</div>
|
||||||
<div className='post__content'>
|
<div className='post__content'>
|
||||||
<div className='post__img'>
|
<div className='post__img'>
|
||||||
|
|||||||
@@ -65,11 +65,16 @@ export default class UserProfile extends React.Component {
|
|||||||
return <div>{name}</div>;
|
return <div>{name}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var profileImg = '/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at + '&' + Utils.getSessionIndex();
|
||||||
|
if (this.props.overwriteImage) {
|
||||||
|
profileImg = this.props.overwriteImage;
|
||||||
|
}
|
||||||
|
|
||||||
var dataContent = [];
|
var dataContent = [];
|
||||||
dataContent.push(
|
dataContent.push(
|
||||||
<img
|
<img
|
||||||
className='user-popover__image'
|
className='user-popover__image'
|
||||||
src={'/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at + '&' + Utils.getSessionIndex()}
|
src={profileImg}
|
||||||
height='128'
|
height='128'
|
||||||
width='128'
|
width='128'
|
||||||
key='user-popover-image'
|
key='user-popover-image'
|
||||||
@@ -130,10 +135,12 @@ export default class UserProfile extends React.Component {
|
|||||||
UserProfile.defaultProps = {
|
UserProfile.defaultProps = {
|
||||||
userId: '',
|
userId: '',
|
||||||
overwriteName: '',
|
overwriteName: '',
|
||||||
|
overwriteImage: '',
|
||||||
disablePopover: false
|
disablePopover: false
|
||||||
};
|
};
|
||||||
UserProfile.propTypes = {
|
UserProfile.propTypes = {
|
||||||
userId: React.PropTypes.string,
|
userId: React.PropTypes.string,
|
||||||
overwriteName: React.PropTypes.string,
|
overwriteName: React.PropTypes.string,
|
||||||
|
overwriteImage: React.PropTypes.string,
|
||||||
disablePopover: React.PropTypes.bool
|
disablePopover: React.PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ function handleNewPostEvent(msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send desktop notification
|
// Send desktop notification
|
||||||
if (UserStore.getCurrentId() !== msg.user_id || post.props.from_webhook === 'true') {
|
if ((UserStore.getCurrentId() !== msg.user_id || post.props.from_webhook === 'true') && !Utils.isSystemMessage(post)) {
|
||||||
const msgProps = msg.props;
|
const msgProps = msg.props;
|
||||||
|
|
||||||
let mentions = [];
|
let mentions = [];
|
||||||
|
|||||||
@@ -119,7 +119,9 @@ export default {
|
|||||||
POST_LOADING: 'loading',
|
POST_LOADING: 'loading',
|
||||||
POST_FAILED: 'failed',
|
POST_FAILED: 'failed',
|
||||||
POST_DELETED: 'deleted',
|
POST_DELETED: 'deleted',
|
||||||
POST_TYPE_JOIN_LEAVE: 'join_leave',
|
POST_TYPE_JOIN_LEAVE: 'system_join_leave',
|
||||||
|
SYSTEM_MESSAGE_PREFIX: 'system_',
|
||||||
|
SYSTEM_MESSAGE_PROFILE_IMAGE: '/static/images/logo_compact.png',
|
||||||
RESERVED_TEAM_NAMES: [
|
RESERVED_TEAM_NAMES: [
|
||||||
'www',
|
'www',
|
||||||
'web',
|
'web',
|
||||||
|
|||||||
@@ -1247,3 +1247,7 @@ export function getPostTerm(post) {
|
|||||||
export function isFeatureEnabled(feature) {
|
export function isFeatureEnabled(feature) {
|
||||||
return PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, Constants.FeatureTogglePrefix + feature.label, {value: 'false'}).value === 'true';
|
return PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, Constants.FeatureTogglePrefix + feature.label, {value: 'false'}).value === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isSystemMessage(post) {
|
||||||
|
return post.type && (post.type.lastIndexOf(Constants.SYSTEM_MESSAGE_PREFIX) === 0);
|
||||||
|
}
|
||||||
@@ -371,6 +371,10 @@ body.ios {
|
|||||||
background-color: beige;
|
background-color: beige;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.post--system .post__header .col__name {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -629,6 +633,9 @@ body.ios {
|
|||||||
|
|
||||||
.post__time {
|
.post__time {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post__time, &.post--system .post__body {
|
||||||
@include opacity(0.6);
|
@include opacity(0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Двоичные данные
web/static/images/logo_compact.png
Обычный файл
Двоичные данные
web/static/images/logo_compact.png
Обычный файл
Двоичный файл не отображается.
|
После Ширина: | Высота: | Размер: 6.1 KiB |
Ссылка в новой задаче
Block a user