summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Orben <florian.orben@gmail.com>2015-12-04 02:07:47 +0100
committerFlorian Orben <florian.orben@gmail.com>2015-12-04 02:07:47 +0100
commit14d1ec5191867174837e15f616ad3fc1dc8e0dae (patch)
treecc74c752199a42b3e273570586eb5abd3bc41581
parentd5d66214db6c108eb94cf2e43c0c4538cdb5d7ae (diff)
downloadchat-14d1ec5191867174837e15f616ad3fc1dc8e0dae.tar.gz
chat-14d1ec5191867174837e15f616ad3fc1dc8e0dae.tar.bz2
chat-14d1ec5191867174837e15f616ad3fc1dc8e0dae.zip
PLT-1326: Enable channel posts of type join or leave not trigger unread notifications
-rw-r--r--api/channel.go2
-rw-r--r--model/post.go11
-rw-r--r--model/post_test.go15
-rw-r--r--store/sql_post_store.go3
-rw-r--r--web/react/components/post.jsx9
-rw-r--r--web/react/components/post_header.jsx12
-rw-r--r--web/react/components/posts_view.jsx8
-rw-r--r--web/react/components/rhs_root_post.jsx20
-rw-r--r--web/react/components/user_profile.jsx9
-rw-r--r--web/react/stores/socket_store.jsx2
-rw-r--r--web/react/utils/constants.jsx4
-rw-r--r--web/react/utils/utils.jsx4
-rw-r--r--web/sass-files/sass/partials/_post.scss7
-rw-r--r--web/static/images/logo_compact.pngbin0 -> 6258 bytes
14 files changed, 93 insertions, 13 deletions
diff --git a/api/channel.go b/api/channel.go
index 99640e71a..6fa6ec295 100644
--- a/api/channel.go
+++ b/api/channel.go
@@ -421,7 +421,7 @@ func JoinChannel(c *Context, channelId string, role string) {
c.Err = err
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 {
c.Err = model.NewAppError("join", "You do not have the appropriate permissions", "")
c.Err.StatusCode = http.StatusForbidden
diff --git a/model/post.go b/model/post.go
index e66009dd6..2c90d8b7b 100644
--- a/model/post.go
+++ b/model/post.go
@@ -10,9 +10,10 @@ import (
)
const (
- POST_DEFAULT = ""
- POST_SLACK_ATTACHMENT = "slack_attachment"
- POST_JOIN_LEAVE = "join_leave"
+ POST_SYSTEM_MESSAGE_PREFIX = "system_"
+ POST_DEFAULT = ""
+ POST_SLACK_ATTACHMENT = "slack_attachment"
+ POST_JOIN_LEAVE = "system_join_leave"
)
type Post struct {
@@ -159,3 +160,7 @@ func (o *Post) AddProp(key string, value interface{}) {
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
+}
diff --git a/model/post_test.go b/model/post_test.go
index f498c83e6..cbd323fab 100644
--- a/model/post_test.go
+++ b/model/post_test.go
@@ -98,3 +98,18 @@ func TestPostPreSave(t *testing.T) {
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")
+ }
+}
diff --git a/store/sql_post_store.go b/store/sql_post_store.go
index 9db5806c5..035309e21 100644
--- a/store/sql_post_store.go
+++ b/store/sql_post_store.go
@@ -38,7 +38,8 @@ func NewSqlPostStore(sqlStore *SqlStore) PostStore {
}
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() {
diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx
index b32656bfc..786a4a325 100644
--- a/web/react/components/post.jsx
+++ b/web/react/components/post.jsx
@@ -173,6 +173,11 @@ export default class Post extends React.Component {
shouldHighlightClass = 'post--highlight';
}
+ let systemMessageClass = '';
+ if (utils.isSystemMessage(post)) {
+ systemMessageClass = 'post--system';
+ }
+
let profilePic = null;
if (!this.props.hideProfilePic) {
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) {
src = post.props.override_icon_url;
}
+ } else if (utils.isSystemMessage(post)) {
+ src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
}
profilePic = (
@@ -195,7 +202,7 @@ export default class Post extends React.Component {
<div>
<div
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__img'>{profilePic}</div>
diff --git a/web/react/components/post_header.jsx b/web/react/components/post_header.jsx
index ffc32f82c..76b3a64be 100644
--- a/web/react/components/post_header.jsx
+++ b/web/react/components/post_header.jsx
@@ -3,6 +3,9 @@
import UserProfile from './user_profile.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 {
constructor(props) {
@@ -27,6 +30,15 @@ export default class PostHeader extends React.Component {
}
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 (
diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx
index d0eee5a23..740ce04f6 100644
--- a/web/react/components/posts_view.jsx
+++ b/web/react/components/posts_view.jsx
@@ -87,6 +87,7 @@ export default class PostsView extends React.Component {
const post = posts[order[i]];
const parentPost = posts[post.parent_id];
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 (parentPost && parentPost.state === Constants.POST_DELETED) {
@@ -102,6 +103,7 @@ export default class PostsView extends React.Component {
const prevPostIsComment = Utils.isComment(prevPost);
const postFromWebhook = Boolean(post.props && post.props.from_webhook);
const prevPostFromWebhook = Boolean(prevPost.props && prevPost.props.from_webhook);
+ const prevPostUserId = Utils.isSystemMessage(prevPost) ? '' : prevPostUserId;
let prevWebhookName = '';
if (prevPost.props && 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 and current post are both from webhooks or both not,
// 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 &&
postFromWebhook === prevPostFromWebhook &&
prevWebhookName === curWebhookName) {
@@ -144,7 +146,7 @@ export default class PostsView extends React.Component {
// 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 have the same webhook usernames
- if (prevPost.user_id === post.user_id &&
+ if (prevPostUserId === postUserId &&
!prevPostIsComment &&
!postIsComment &&
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 &&
post.create_at > this.props.messageSeparatorTime &&
!renderedLastViewed) {
diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx
index 0dd969ad0..0a37a6803 100644
--- a/web/react/components/rhs_root_post.jsx
+++ b/web/react/components/rhs_root_post.jsx
@@ -12,6 +12,8 @@ import twemoji from 'twemoji';
import PostBodyAdditionalContent from './post_body_additional_content.jsx';
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
+import Constants from '../utils/constants.jsx';
+
export default class RhsRootPost extends React.Component {
constructor(props) {
super(props);
@@ -58,6 +60,11 @@ export default class RhsRootPost extends React.Component {
currentUserCss = 'current--user';
}
+ var systemMessageClass = '';
+ if (utils.isSystemMessage(post)) {
+ systemMessageClass = 'post--system';
+ }
+
var channelName;
if (channel) {
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>;
+ } 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();
@@ -163,6 +179,8 @@ export default class RhsRootPost extends React.Component {
if (post.props.override_icon_url) {
src = post.props.override_icon_url;
}
+ } else if (utils.isSystemMessage(post)) {
+ src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE;
}
const profilePic = (
@@ -175,7 +193,7 @@ export default class RhsRootPost extends React.Component {
);
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__content'>
<div className='post__img'>
diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx
index ea104fedb..385cd0f52 100644
--- a/web/react/components/user_profile.jsx
+++ b/web/react/components/user_profile.jsx
@@ -65,11 +65,16 @@ export default class UserProfile extends React.Component {
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 = [];
dataContent.push(
<img
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'
width='128'
key='user-popover-image'
@@ -130,10 +135,12 @@ export default class UserProfile extends React.Component {
UserProfile.defaultProps = {
userId: '',
overwriteName: '',
+ overwriteImage: '',
disablePopover: false
};
UserProfile.propTypes = {
userId: React.PropTypes.string,
overwriteName: React.PropTypes.string,
+ overwriteImage: React.PropTypes.string,
disablePopover: React.PropTypes.bool
};
diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx
index 2e0769cc4..29aa32a08 100644
--- a/web/react/stores/socket_store.jsx
+++ b/web/react/stores/socket_store.jsx
@@ -163,7 +163,7 @@ function handleNewPostEvent(msg) {
}
// 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;
let mentions = [];
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 8164095b9..25a494d07 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -118,7 +118,9 @@ export default {
POST_LOADING: 'loading',
POST_FAILED: 'failed',
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: [
'www',
'web',
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 0a52f5b37..a1d800bca 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -1243,3 +1243,7 @@ export function getPostTerm(post) {
export function isFeatureEnabled(feature) {
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);
+} \ No newline at end of file
diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss
index ed1632681..4d639ee6f 100644
--- a/web/sass-files/sass/partials/_post.scss
+++ b/web/sass-files/sass/partials/_post.scss
@@ -371,6 +371,10 @@ body.ios {
background-color: beige;
}
+ &.post--system .post__header .col__name {
+ display: none;
+ }
+
ul {
margin: 0;
padding: 0;
@@ -625,6 +629,9 @@ body.ios {
.post__time {
font-size: 13px;
+ }
+
+ .post__time, &.post--system .post__body {
@include opacity(0.6);
}
diff --git a/web/static/images/logo_compact.png b/web/static/images/logo_compact.png
new file mode 100644
index 000000000..b861b7c6d
--- /dev/null
+++ b/web/static/images/logo_compact.png
Binary files differ