diff options
author | Debanshu Kundu <debanshu.kundu@joshtechnologygroup.com> | 2017-01-12 21:17:29 +0530 |
---|---|---|
committer | Harrison Healey <harrisonmhealey@gmail.com> | 2017-01-12 10:47:29 -0500 |
commit | 739f32272ff4079b5638d6293a28ed8ffbbb926e (patch) | |
tree | 41003e0c12c7ca1bd517bac0d29dd6cd9a3d59bb | |
parent | 99493bc5fa11d8e50d46c19e2678ddb2c99c0bbb (diff) | |
download | chat-739f32272ff4079b5638d6293a28ed8ffbbb926e.tar.gz chat-739f32272ff4079b5638d6293a28ed8ffbbb926e.tar.bz2 chat-739f32272ff4079b5638d6293a28ed8ffbbb926e.zip |
PLT-135 Showing "(Edited)" indicator if a message has been edited. (#4923)
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | api/post.go | 1 | ||||
-rw-r--r-- | api/post_test.go | 11 | ||||
-rw-r--r-- | model/post.go | 1 | ||||
-rw-r--r-- | store/sql_upgrade.go | 10 | ||||
-rw-r--r-- | webapp/components/post_view/components/post_message_container.jsx | 2 | ||||
-rw-r--r-- | webapp/components/post_view/components/post_message_view.jsx | 38 | ||||
-rw-r--r-- | webapp/components/search_results_item.jsx | 2 | ||||
-rw-r--r-- | webapp/i18n/en.json | 1 | ||||
-rw-r--r-- | webapp/sass/layout/_post.scss | 39 | ||||
-rw-r--r-- | webapp/sass/responsive/_mobile.scss | 3 | ||||
-rw-r--r-- | webapp/utils/post_utils.jsx | 6 |
12 files changed, 102 insertions, 16 deletions
@@ -218,7 +218,7 @@ ifeq ($(BUILD_ENTERPRISE_READY),true) tail -n +2 csaml.out >> ecover.out tail -n +2 ccluster.out >> ecover.out tail -n +2 cmetrics.out >> ecover.out - tail -n +2 caccount_migration.out >> ecover.out + tail -n +2 caccount_migration.out >> ecover.out rm -f cldap.out ccompliance.out cmfa.out cemoji.out csaml.out ccluster.out cmetrics.out caccount_migration.out rm -r ldap.test rm -r compliance.test @@ -394,7 +394,7 @@ else echo stopping mattermost $$PID; \ kill $$PID; \ done -endif +endif stop-client: @echo Stopping mattermost client diff --git a/api/post.go b/api/post.go index 354fe35db..e8aef2f86 100644 --- a/api/post.go +++ b/api/post.go @@ -1217,6 +1217,7 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { *newPost = *oldPost newPost.Message = post.Message + newPost.EditAt = model.GetMillis() newPost.Hashtags, _ = model.ParseHashtags(post.Message) if result := <-Srv.Store.Post().Update(newPost, oldPost); result.Err != nil { diff --git a/api/post_test.go b/api/post_test.go index e48debcae..6101d2c8d 100644 --- a/api/post_test.go +++ b/api/post_test.go @@ -47,6 +47,10 @@ func TestCreatePost(t *testing.T) { t.Fatal("shouldn't have files") } + if rpost1.Data.(*model.Post).EditAt != 0 { + t.Fatal("Newly craeted post shouldn't have EditAt set") + } + post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id} rpost2, err := Client.CreatePost(post2) if err != nil { @@ -326,6 +330,10 @@ func TestUpdatePost(t *testing.T) { t.Fatal(err) } + if rpost2.Data.(*model.Post).EditAt != 0 { + t.Fatal("Newly craeted post shouldn't have EditAt set") + } + msg2 := "a" + model.NewId() + " update post 1" rpost2.Data.(*model.Post).Message = msg2 if rupost2, err := Client.UpdatePost(rpost2.Data.(*model.Post)); err != nil { @@ -334,6 +342,9 @@ func TestUpdatePost(t *testing.T) { if rupost2.Data.(*model.Post).Message != msg2 { t.Fatal("failed to updates") } + if rupost2.Data.(*model.Post).EditAt == 0 { + t.Fatal("EditAt not updated for post") + } } msg1 := "#hashtag a" + model.NewId() + " update post 2" diff --git a/model/post.go b/model/post.go index 7097e031d..766324b4c 100644 --- a/model/post.go +++ b/model/post.go @@ -31,6 +31,7 @@ type Post struct { Id string `json:"id"` CreateAt int64 `json:"create_at"` UpdateAt int64 `json:"update_at"` + EditAt int64 `json:"edit_at"` DeleteAt int64 `json:"delete_at"` UserId string `json:"user_id"` ChannelId string `json:"channel_id"` diff --git a/store/sql_upgrade.go b/store/sql_upgrade.go index d69404baa..9ee8f08e2 100644 --- a/store/sql_upgrade.go +++ b/store/sql_upgrade.go @@ -15,6 +15,7 @@ import ( ) const ( + VERIONS_3_7_0 = "3.7.0" VERSION_3_6_0 = "3.6.0" VERSION_3_5_0 = "3.5.0" VERSION_3_4_0 = "3.4.0" @@ -39,6 +40,7 @@ func UpgradeDatabase(sqlStore *SqlStore) { UpgradeDatabaseToVersion34(sqlStore) UpgradeDatabaseToVersion35(sqlStore) UpgradeDatabaseToVersion36(sqlStore) + UpgradeDatabaseToVersion37(sqlStore) // If the SchemaVersion is empty this this is the first time it has ran // so lets set it to the current version. @@ -229,3 +231,11 @@ func UpgradeDatabaseToVersion36(sqlStore *SqlStore) { saveSchemaVersion(sqlStore, VERSION_3_6_0) } } + +func UpgradeDatabaseToVersion37(sqlStore *SqlStore) { + // TODO: Uncomment following condition when version 3.7.0 is released + // if shouldPerformUpgrade(sqlStore, VERSION_3_6_0, VERSION_3_7_0) { + // Add EditAt column to Posts + sqlStore.CreateColumnIfNotExists("Posts", "EditAt", " bigint", " bigint", "0") + // } +} diff --git a/webapp/components/post_view/components/post_message_container.jsx b/webapp/components/post_view/components/post_message_container.jsx index 2d17e74c4..4e27cd29a 100644 --- a/webapp/components/post_view/components/post_message_container.jsx +++ b/webapp/components/post_view/components/post_message_container.jsx @@ -89,7 +89,7 @@ export default class PostMessageContainer extends React.Component { return ( <PostMessageView options={this.props.options} - message={this.props.post.message} + post={this.props.post} emojis={this.state.emojis} enableFormatting={this.state.enableFormatting} mentionKeys={this.state.mentionKeys} diff --git a/webapp/components/post_view/components/post_message_view.jsx b/webapp/components/post_view/components/post_message_view.jsx index 24f96a8d9..eff791aec 100644 --- a/webapp/components/post_view/components/post_message_view.jsx +++ b/webapp/components/post_view/components/post_message_view.jsx @@ -2,14 +2,16 @@ // See License.txt for license information. import React from 'react'; +import {FormattedMessage} from 'react-intl'; import * as TextFormatting from 'utils/text_formatting.jsx'; import * as Utils from 'utils/utils.jsx'; +import * as PostUtils from 'utils/post_utils.jsx'; export default class PostMessageView extends React.Component { static propTypes = { options: React.PropTypes.object.isRequired, - message: React.PropTypes.string.isRequired, + post: React.PropTypes.object.isRequired, emojis: React.PropTypes.object.isRequired, enableFormatting: React.PropTypes.bool.isRequired, mentionKeys: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, @@ -23,7 +25,7 @@ export default class PostMessageView extends React.Component { return true; } - if (nextProps.message !== this.props.message) { + if (nextProps.post.message !== this.props.post.message) { return true; } @@ -47,9 +49,28 @@ export default class PostMessageView extends React.Component { return false; } + editedIndicator() { + return ( + PostUtils.isEdited(this.props.post) ? + <span className='edited'> + <FormattedMessage + id='post_message_view.edited' + defaultMessage='(edited)' + /> + </span> : + '' + ); + } + render() { if (!this.props.enableFormatting) { - return <span>{this.props.message}</span>; + return ( + <span> + {this.props.post.message} + + {this.editedIndicator()} + </span> + ); } const options = Object.assign({}, this.props.options, { @@ -62,10 +83,13 @@ export default class PostMessageView extends React.Component { }); return ( - <span - onClick={Utils.handleFormattedTextClick} - dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.message, options)}} - /> + <div> + <span + onClick={Utils.handleFormattedTextClick} + dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, options)}} + /> + {this.editedIndicator()} + </div> ); } } diff --git a/webapp/components/search_results_item.jsx b/webapp/components/search_results_item.jsx index 50e440b04..be62653c0 100644 --- a/webapp/components/search_results_item.jsx +++ b/webapp/components/search_results_item.jsx @@ -285,7 +285,7 @@ export default class SearchResultsItem extends React.Component { </li> {rhsControls} </ul> - <div className='search-item-snippet'> + <div className='search-item-snippet post__body'> {message} </div> </div> diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index aae21a8e6..d18ce059a 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -1667,6 +1667,7 @@ "post_info.mobile.unflag": "Unflag", "post_info.permalink": "Permalink", "post_info.reply": "Reply", + "post_message_view.edited": "(edited)", "posts_view.loadMore": "Load more messages", "posts_view.newMsg": "New Messages", "posts_view.newMsgBelow": "New {count, plural, one {message} other {messages}} below", diff --git a/webapp/sass/layout/_post.scss b/webapp/sass/layout/_post.scss index 1e7b45fba..ff2dce422 100644 --- a/webapp/sass/layout/_post.scss +++ b/webapp/sass/layout/_post.scss @@ -560,9 +560,9 @@ } blockquote { - display: inline-block; font-size: 1em; margin-left: 0; + margin-top: 1.3em; padding: 3px 0 0 25px; vertical-align: top; @@ -572,6 +572,11 @@ top: 2px; } } + .search-item-snippet { + blockquote { + margin-top: 0; + } + } .markdown__heading { clear: both; @@ -598,7 +603,15 @@ } p + p { - margin-top: 1em; + margin: 1em 0; + &:last-of-type { + margin-bottom: 0; + } + } + span { + > p:first-child { + margin-bottom: 1em; + } } ol, @@ -978,12 +991,24 @@ width: 100%; word-wrap: break-word; - p { + div { margin: 0 0 .4em; } p + p { - margin-top: 1.4em; + margin: 1.4em 0; + &:last-of-type { + margin-bottom: 0; + } + } + + span { + > p:last-child { + display: inline; + } + > p:first-child { + margin-bottom: 1.4em; + } } li { @@ -1063,6 +1088,12 @@ color: $white; } } + + span.edited { + color: #A3A3A3; + font-size: 0.87em; + opacity: 0.6; + } } .post__link { diff --git a/webapp/sass/responsive/_mobile.scss b/webapp/sass/responsive/_mobile.scss index 63c890007..67f56b8a2 100644 --- a/webapp/sass/responsive/_mobile.scss +++ b/webapp/sass/responsive/_mobile.scss @@ -213,6 +213,9 @@ } } } + blockquote { + margin-top: 0; + } } &.same--root { diff --git a/webapp/utils/post_utils.jsx b/webapp/utils/post_utils.jsx index 4bba784cb..88021c2a5 100644 --- a/webapp/utils/post_utils.jsx +++ b/webapp/utils/post_utils.jsx @@ -15,6 +15,10 @@ export function isComment(post) { return false; } +export function isEdited(post) { + return post.edit_at > 0; +} + export function getProfilePicSrcForPost(post, timestamp) { let src = Client.getUsersRoute() + '/' + post.user_id + '/image?time=' + timestamp; if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') { @@ -28,4 +32,4 @@ export function getProfilePicSrcForPost(post, timestamp) { } return src; -}
\ No newline at end of file +} |