From 14d1ec5191867174837e15f616ad3fc1dc8e0dae Mon Sep 17 00:00:00 2001 From: Florian Orben Date: Fri, 4 Dec 2015 02:07:47 +0100 Subject: [PATCH] PLT-1326: Enable channel posts of type join or leave not trigger unread notifications --- api/channel.go | 2 +- model/post.go | 11 ++++++++--- model/post_test.go | 15 +++++++++++++++ store/sql_post_store.go | 3 ++- web/react/components/post.jsx | 9 ++++++++- web/react/components/post_header.jsx | 12 ++++++++++++ web/react/components/posts_view.jsx | 8 +++++--- web/react/components/rhs_root_post.jsx | 20 +++++++++++++++++++- web/react/components/user_profile.jsx | 9 ++++++++- web/react/stores/socket_store.jsx | 2 +- web/react/utils/constants.jsx | 4 +++- web/react/utils/utils.jsx | 4 ++++ web/sass-files/sass/partials/_post.scss | 7 +++++++ web/static/images/logo_compact.png | Bin 0 -> 6258 bytes 14 files changed, 93 insertions(+), 13 deletions(-) create mode 100644 web/static/images/logo_compact.png diff --git a/api/channel.go b/api/channel.go index 99640e71a7..6fa6ec2954 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 e66009dd69..2c90d8b7b0 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 f498c83e6e..cbd323fab4 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 9db5806c54..035309e211 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 b32656bfca..786a4a3250 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 {
{profilePic}
diff --git a/web/react/components/post_header.jsx b/web/react/components/post_header.jsx index ffc32f82cd..76b3a64be3 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 =
  • {'BOT'}
  • ; + } else if (Utils.isSystemMessage(post)) { + userProfile = ( + + ); } return ( diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx index d0eee5a23b..740ce04f62 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 0dd969ad00..0a37a68030 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 =
  • {'BOT'}
  • ; + } else if (utils.isSystemMessage(post)) { + userProfile = ( + + ); } 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 ( -
    +
    {channelName}
    diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx index ea104fedbc..385cd0f525 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
    {name}
    ; } + 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( )*now*9eJ{XwkalSFhOdJ94# zN(hE%LG<#;``-6{_m8{oS?lcQIs5GL+ig8-ofBiAr$I}_Mg;%>Xtgv|jV@NgpPPd8 zg6dfBWnL`T@oMJyJ2)r2Kgt^eK%#LD7%nY$lrzQ%gF**-^*#BXJdAR-(!Mn=y{0+)n z*MLg}=Z)cli%EhUp-?E7l#Cb@E-em&i*msrP&gPO1BOCC5GVo)LBJqf|GIcCym_OY z5Jsx%|MGS5B+ui5$9p2cV1IvqF@JF}oVPO=DkCEUhQPou80bO*LO*}0F);fDh2_&yZ?#n zFKHjV5$6A4{8woo<3LXg*a+i;^YwPTsD~5pKgt)m``?cK2wtE;=zC)?ih^=e#X0)A zV?6L$s`5M+Z^Y18G(rLfk(7{T0#co0E5ebBpu;!6a*q8iAIV4W9Pr> ztHa@v;?j~bNQg2Nss@*aTu^0Is5%lV2}8fWxZv9u{_fKY&P5!a z|G+^w!ZDIChyx5HBP|UBL7^BpNLo_d35148$%tPhL}T}Lfb$6`%u;Sxed#?0K| zQBEpjd<;1sw;i;`iiM66ooB?MJ)ycF~bE0*jn5ftliYe05#=t?ntC&vXm#6(5k=MnY z$@o?oNStDjr7V97tqlr%6M6_;C~|JG zlmN@XgsHd;Uzd{4T^>~K2L_EVbS={F^oa;>Qytc~$VHw_MG8Sv#IlR3IUiUvoa~ja zLB5A>tbLJ{k*RNC#^QtCvCZ?xDF&WAguj>_0zPDOH8L{#G%9|8SxkoOKNU*5A$3U1 z!>!lbenwESAFHI79^|;0vobL&@iX;96%fKFS|%kU>7CWUYr7^V;Z0VG-x0EDvv?<+ zn34)nsAawR6T;F*0V~PSZXY;n5C>o@lhcu3)mSmer8cyx|C+sY;KAJeik5o2ZjHTQ zE4seUk-@3a56M!Ze9|uWI;5RhQ&7!F73!+ys2u;`%`;qlCL(%mIRTZwAfOS|vYQxh zmmA#$Ve2Bfh7Pw`CiP8sJZ{_4G78pE%W)LkvS;~*eA z`K5JdQ`-LCL8^H4J|+8&pAvWbl!i_1ybg)kpT5xy+h5}YqE9>rzffjZ*bfVSN99eO z)$|l6T2`OEII3Q(7z#w>bFP%#b-e}z62aMWKZy3^)ThI91V_8g=dtM^>%#}ty)U1os#Zqzx2}d3LBHxr4H#Tox@;y1$8qE)@LC zK)kQ^j75EtayYb%ifU`1F|?<6!o2zG)k`v72@9dKSKGe(xm)6ZNY`6R!X7b?DIymA zY_s4hljalPEAhcAW{$jFXIS8i#BK3%%b%i}CB$nt>BvF^$d8B4>uie*f8UM@YxZjE zI;|-6Vn>t+%8m(7?v0I^JHY0Q2|iS?qm1l`oy@9di4VaiCW{7^crJTJp!^~s{YWC zEt!TfMC!)dA4rdLd))Ai*|!nP=&5J!IgKr(Y4frx6RZLPlH zzK{@6V0XNiT4V2PWstb<>=JD^escn7wh5cTmjAh6jJ#ieMlB-Rac>Y15*1w^I z+ww+8*$cm5s&EejiVyLnDd$ag zUz;1tP*W|lQQ^&6GggxN`9!6$&aqOKq0{_V6VL2SMHa6ywSt9ry_c0$!}`1J4IS$i zM{u}Twsk}}TNH3)Fs$4Xcz67vb*9^|Z!%%qPO9x~({`<^zaSI<+Vr@ajnwc)oucV? z-h;_5sLdhxSaa#ZwX|I+7#t{jO1gGJW;KRV1Ksmu~GuK*!HG7x~_1W`C_|Z#}mARR9k(u-2 zJTs=dyfN*=*c-fUOIm^e$LxZ>+Vq;drh3_1tT}_gTARs&QMzImGU8l zImn93NWH_1=qTTZdmlP<;Yi+-VBy8d`_oO2cba|^j%6%{uk^AG=Jet5AIjGwEEn@m z8zeW6Uf#)52Ek9TELYm@$?wVG5KUc9Uc^Z)tOmIoYr!b?(Q^vBT2=Q`|IK9tEKYOfef+Bp3>pz8j~_P zKb@N~BZdm0>l^b1zivxfvY4hQ5DQ=XbkVi?AqRfLf5#0N>~pTKb4=fZ+&vq(s+QyS zMa-;Z#N>9N(c0Sj$fu>b_E@S-uln?aWU|$|Eo%eHn;*SiFoc0Q7&AUdvK^Le>M<+4 zLfSy1)n6K6U9gUoZpuM^?aSrX&tjq=6b9Os71pODLBB>F1Q@q~qFWV$AO*i8(becf zF95^Z&z@R;<=fl|I&?HN#FaaR$|gqE6cL1%57k!>#ltX?$P)H@ej|1U}+M*IQ*jmOj!J&-l0Bbv3 ze6|7zTtQq-QsM)+U1>8X?pSB)sxYuX#>YPk8?)(9*c-^*yEihvGWTQt`)5bN*J^@E z57nMh4EJ;_#%t%XuE{^X9hFH&DzfGz!3J;x#*=9=v%N=LeIPlgr9M-0ZU-HteDpD+Y$MJf@)7-C3E&@G>yPbm#7CzS8-U>?J_YM50^96Jeu1d>(p62xEsU`;<^_ATo zf(ZR-9B-MaNQy|%q4N4OeobJdj2|BpO^U1p_lVA>no@Pl?sL zi`tmj%CfhHv=Y8#oRJn!H?ZlKcf;$g&0uMd)$YVS$zWOgdH?;_5$^kM#6B39i6r-y z{$vk647V*JG3jllba8Q6TU`yeg|xg@=3j3O2jsLqWw@C{S-Ajl=Hr`(roxH4K~dGE zW~8-LQH?m#yXls`LD$3yjY6R z5|r0W@l?CeGd~`o%Swpj;~U5lmuCUF5AMCj^v$*t!*3&POm2#>4+G?+%>7gdDYEn% z-2!*9A0GnbracB$lQ;APe~6_D|DJht>kiYhJxZW>!Zaym_l%P*Zg6RGSC`OIA(cgk zOS3h#rL*UE6cBJb-Q~4%bztoaF1N& z)hpM>mpT178k^Cb7st~(?sf8)mR+ik5uWxf&&W;F$X3OCQFiyOn9?G{Lf;>pNdEdx z7HmI_aAcAyFJ;o@s@o4SGi9x{?dxozbPvnX39abT}nP<-IbN0@NH+D#JPeom2JQHozkqC+=I>~ z2AAFDV5K`(bAHZKOoZ=?`U>iBW3}XcJP%L4NJ{Wjk@I&0GmB~lG)hzC-h~XfRCX+e zu`8NS38;qMPT#r5V=GnuU|?zP`(`EU_HAScYqsWxcQ*kvvMFghvzaiFz(e-5&Jzt? zQsn9YO}%m(YFKB~O|CdcGDO=LjkfO=9BQos%rHrft}=6$yoDW~q^HE&NyP+AXLdb{ z$#3s!&32HTG}+lX{;m)%mR9-R-24uYYneN)_^G7g18=cQHa!Ex;}b!W=&7nxYRwo6 zzRS)X50%~l@%UcwxhGlWJBDIWft&=rnKn^pu$6xpoxgPU$2XOdqR+;DfjKS?G&DdfO!A!(%&1iv4UW?@GcvcAaIxjs)h*4z(Q%q8bz8;7 zWcTCek+)huwqg5=8Gx5Xf=60 zqFUceJfLJ`E2Nn@By1JCn9F`>c^g|^4&N*@>nVr`+2)n@J}F?cJnAm>Vd}xNG~UQf z>PvJQP;#4nqkEHXW9huYw{+_%j;^mEgt`a0F0Mg0=SlI5N$y*qzVKnh)zcS^vTxoP z?7Yw9TxVxrO<*uc_3!FrJmk)nVi^=?!$;7EB)?v`)t%xjM|-FIpyAE(gi;W%l9+24 zocjipL_+xRTg-=uc!GOQs48KNHIs8KRIla9N%=$Y8xKk60`GvZ=GVGy|!UuVTl$aF}X!AH$9+>(an}u5oI<9A0>YNSZP>!bHl2} zL57M|GVMoJa$kSk*DDm%wi&!`9+m35R_w2qn=>ii_WHT0+kTEGGHj*h4Egv%{q?77 zxiV$y>Z(4!A7&jh8F-vS^R_xb&SUB&twH&4RFM0v*Z$f;2^~Vv@e{k(mqJ3%hOY2_ z8Ec@~-+o#v@j_pzq1adbo`mIyD$o7O zdnj)on{k4V?7q))aC+=JTS>P7GmZ9!W`)IX{(N$T7pjpDtQ!J?2uc+j%$#4&?M8o0 zXR?)9z6Xq9t9X zmuTzX-SRWyTRmpyVDafpj4C|*lB?%#=wFePJk$3$F1rJrnlY-;x z>C~mKjqkAM%gDyU@6w&>Plf9|uk%=`t+rvKrcKbCn2*pfxf?C`k$XTVu6rNv4m|;- zdK?U-vxVSi2j9PfU49sD@=9dtCj2APP(jR>3XUGH; z85*1Dyg5_9zTBUo9b=Kl7xnJ&EbB3uorg_QCUF%VU!H}^4?j5ce|r6aI2+>(Ie$uR zb5qdo5}y=MCE3QR%kt0lmG$p8nCGOt$283ZG_s3QvaW2dXx-qr!FApiO|S&$189!) z3|$>nnr7^!{eRv4mA_3_Ras&&%)`w7nAk9Idp3;qY6=0O9FYomma2X-k@#%$>T`2Z z*83M9Q~BeR`A4tg%WJNX0NmF(i$$%lMWTGe^uw>HZZK08YG>(1lPlF*qX0-l**AyR zHJWD{@Qdkojy4ZFbmG(f1wPD9kp*x^DRH@-y5*++2Axn$+MoIQ7M^z7muHY}Mt0 zX>gFAXa44zeb`wCk{L-hY_nMVSwJ#2Yh-w9D#l$fY}NEC$?X(;W~%FPsJotW&% zBg~gnZF|D3ehKS1mC)xcd;QjPdaCzR*p#A9U8merCdyS9LfZZ`J$as=dJ$~#h~NxG z8IIO3%&I?#<47GQKMbs}+A-C|-oFK{5+oV=p*5ag;kT}-m#G~QC`9Ks@8Z1kD|*x9 z7xl2^tfhlutBzsM*Ud$=O=G}WbjI6DW$vdAWLfHQH0<)SULQLSB1atCkDQA)!KB$S zre$}FOZwj>`)y6`7bYhMHO-JNF>D1OvYJDGf9koy|ClDe)@Pn4e#uIfq(ywBuviS# zJV;o@b>EoG+}&-z<-{I74RZ3Aanm9-&L36PCS?&{y+bY=`;y-v z31g_Mtvl0FvAI*K`OVhE^cy#0A7)#xBvRRN#n;l;EX6u6Nr;)|^7Qi$d4?s@ax=S% zv7LD^nZYN1qc1xQ1(Q%8