PLT-135 Showing "(Edited)" indicator if a message has been edited. (#4923)
Этот коммит содержится в:
коммит произвёл
Harrison Healey
родитель
99493bc5fa
Коммит
739f32272f
4
Makefile
4
Makefile
@@ -218,7 +218,7 @@ ifeq ($(BUILD_ENTERPRISE_READY),true)
|
|||||||
tail -n +2 csaml.out >> ecover.out
|
tail -n +2 csaml.out >> ecover.out
|
||||||
tail -n +2 ccluster.out >> ecover.out
|
tail -n +2 ccluster.out >> ecover.out
|
||||||
tail -n +2 cmetrics.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 -f cldap.out ccompliance.out cmfa.out cemoji.out csaml.out ccluster.out cmetrics.out caccount_migration.out
|
||||||
rm -r ldap.test
|
rm -r ldap.test
|
||||||
rm -r compliance.test
|
rm -r compliance.test
|
||||||
@@ -394,7 +394,7 @@ else
|
|||||||
echo stopping mattermost $$PID; \
|
echo stopping mattermost $$PID; \
|
||||||
kill $$PID; \
|
kill $$PID; \
|
||||||
done
|
done
|
||||||
endif
|
endif
|
||||||
|
|
||||||
stop-client:
|
stop-client:
|
||||||
@echo Stopping mattermost client
|
@echo Stopping mattermost client
|
||||||
|
|||||||
@@ -1217,6 +1217,7 @@ func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
*newPost = *oldPost
|
*newPost = *oldPost
|
||||||
|
|
||||||
newPost.Message = post.Message
|
newPost.Message = post.Message
|
||||||
|
newPost.EditAt = model.GetMillis()
|
||||||
newPost.Hashtags, _ = model.ParseHashtags(post.Message)
|
newPost.Hashtags, _ = model.ParseHashtags(post.Message)
|
||||||
|
|
||||||
if result := <-Srv.Store.Post().Update(newPost, oldPost); result.Err != nil {
|
if result := <-Srv.Store.Post().Update(newPost, oldPost); result.Err != nil {
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ func TestCreatePost(t *testing.T) {
|
|||||||
t.Fatal("shouldn't have files")
|
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}
|
post2 := &model.Post{ChannelId: channel1.Id, Message: "a" + model.NewId() + "a", RootId: rpost1.Data.(*model.Post).Id}
|
||||||
rpost2, err := Client.CreatePost(post2)
|
rpost2, err := Client.CreatePost(post2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -326,6 +330,10 @@ func TestUpdatePost(t *testing.T) {
|
|||||||
t.Fatal(err)
|
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"
|
msg2 := "a" + model.NewId() + " update post 1"
|
||||||
rpost2.Data.(*model.Post).Message = msg2
|
rpost2.Data.(*model.Post).Message = msg2
|
||||||
if rupost2, err := Client.UpdatePost(rpost2.Data.(*model.Post)); err != nil {
|
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 {
|
if rupost2.Data.(*model.Post).Message != msg2 {
|
||||||
t.Fatal("failed to updates")
|
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"
|
msg1 := "#hashtag a" + model.NewId() + " update post 2"
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type Post struct {
|
|||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
CreateAt int64 `json:"create_at"`
|
CreateAt int64 `json:"create_at"`
|
||||||
UpdateAt int64 `json:"update_at"`
|
UpdateAt int64 `json:"update_at"`
|
||||||
|
EditAt int64 `json:"edit_at"`
|
||||||
DeleteAt int64 `json:"delete_at"`
|
DeleteAt int64 `json:"delete_at"`
|
||||||
UserId string `json:"user_id"`
|
UserId string `json:"user_id"`
|
||||||
ChannelId string `json:"channel_id"`
|
ChannelId string `json:"channel_id"`
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
VERIONS_3_7_0 = "3.7.0"
|
||||||
VERSION_3_6_0 = "3.6.0"
|
VERSION_3_6_0 = "3.6.0"
|
||||||
VERSION_3_5_0 = "3.5.0"
|
VERSION_3_5_0 = "3.5.0"
|
||||||
VERSION_3_4_0 = "3.4.0"
|
VERSION_3_4_0 = "3.4.0"
|
||||||
@@ -39,6 +40,7 @@ func UpgradeDatabase(sqlStore *SqlStore) {
|
|||||||
UpgradeDatabaseToVersion34(sqlStore)
|
UpgradeDatabaseToVersion34(sqlStore)
|
||||||
UpgradeDatabaseToVersion35(sqlStore)
|
UpgradeDatabaseToVersion35(sqlStore)
|
||||||
UpgradeDatabaseToVersion36(sqlStore)
|
UpgradeDatabaseToVersion36(sqlStore)
|
||||||
|
UpgradeDatabaseToVersion37(sqlStore)
|
||||||
|
|
||||||
// If the SchemaVersion is empty this this is the first time it has ran
|
// If the SchemaVersion is empty this this is the first time it has ran
|
||||||
// so lets set it to the current version.
|
// so lets set it to the current version.
|
||||||
@@ -229,3 +231,11 @@ func UpgradeDatabaseToVersion36(sqlStore *SqlStore) {
|
|||||||
saveSchemaVersion(sqlStore, VERSION_3_6_0)
|
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")
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export default class PostMessageContainer extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<PostMessageView
|
<PostMessageView
|
||||||
options={this.props.options}
|
options={this.props.options}
|
||||||
message={this.props.post.message}
|
post={this.props.post}
|
||||||
emojis={this.state.emojis}
|
emojis={this.state.emojis}
|
||||||
enableFormatting={this.state.enableFormatting}
|
enableFormatting={this.state.enableFormatting}
|
||||||
mentionKeys={this.state.mentionKeys}
|
mentionKeys={this.state.mentionKeys}
|
||||||
|
|||||||
@@ -2,14 +2,16 @@
|
|||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
import * as TextFormatting from 'utils/text_formatting.jsx';
|
import * as TextFormatting from 'utils/text_formatting.jsx';
|
||||||
import * as Utils from 'utils/utils.jsx';
|
import * as Utils from 'utils/utils.jsx';
|
||||||
|
import * as PostUtils from 'utils/post_utils.jsx';
|
||||||
|
|
||||||
export default class PostMessageView extends React.Component {
|
export default class PostMessageView extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
options: React.PropTypes.object.isRequired,
|
options: React.PropTypes.object.isRequired,
|
||||||
message: React.PropTypes.string.isRequired,
|
post: React.PropTypes.object.isRequired,
|
||||||
emojis: React.PropTypes.object.isRequired,
|
emojis: React.PropTypes.object.isRequired,
|
||||||
enableFormatting: React.PropTypes.bool.isRequired,
|
enableFormatting: React.PropTypes.bool.isRequired,
|
||||||
mentionKeys: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
|
mentionKeys: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
|
||||||
@@ -23,7 +25,7 @@ export default class PostMessageView extends React.Component {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextProps.message !== this.props.message) {
|
if (nextProps.post.message !== this.props.post.message) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,9 +49,28 @@ export default class PostMessageView extends React.Component {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editedIndicator() {
|
||||||
|
return (
|
||||||
|
PostUtils.isEdited(this.props.post) ?
|
||||||
|
<span className='edited'>
|
||||||
|
<FormattedMessage
|
||||||
|
id='post_message_view.edited'
|
||||||
|
defaultMessage='(edited)'
|
||||||
|
/>
|
||||||
|
</span> :
|
||||||
|
''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.enableFormatting) {
|
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, {
|
const options = Object.assign({}, this.props.options, {
|
||||||
@@ -62,10 +83,13 @@ export default class PostMessageView extends React.Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<div>
|
||||||
onClick={Utils.handleFormattedTextClick}
|
<span
|
||||||
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.message, options)}}
|
onClick={Utils.handleFormattedTextClick}
|
||||||
/>
|
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, options)}}
|
||||||
|
/>
|
||||||
|
{this.editedIndicator()}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ export default class SearchResultsItem extends React.Component {
|
|||||||
</li>
|
</li>
|
||||||
{rhsControls}
|
{rhsControls}
|
||||||
</ul>
|
</ul>
|
||||||
<div className='search-item-snippet'>
|
<div className='search-item-snippet post__body'>
|
||||||
{message}
|
{message}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1667,6 +1667,7 @@
|
|||||||
"post_info.mobile.unflag": "Unflag",
|
"post_info.mobile.unflag": "Unflag",
|
||||||
"post_info.permalink": "Permalink",
|
"post_info.permalink": "Permalink",
|
||||||
"post_info.reply": "Reply",
|
"post_info.reply": "Reply",
|
||||||
|
"post_message_view.edited": "(edited)",
|
||||||
"posts_view.loadMore": "Load more messages",
|
"posts_view.loadMore": "Load more messages",
|
||||||
"posts_view.newMsg": "New Messages",
|
"posts_view.newMsg": "New Messages",
|
||||||
"posts_view.newMsgBelow": "New {count, plural, one {message} other {messages}} below",
|
"posts_view.newMsgBelow": "New {count, plural, one {message} other {messages}} below",
|
||||||
|
|||||||
@@ -560,9 +560,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
display: inline-block;
|
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
margin-top: 1.3em;
|
||||||
padding: 3px 0 0 25px;
|
padding: 3px 0 0 25px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
|
||||||
@@ -572,6 +572,11 @@
|
|||||||
top: 2px;
|
top: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.search-item-snippet {
|
||||||
|
blockquote {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.markdown__heading {
|
.markdown__heading {
|
||||||
clear: both;
|
clear: both;
|
||||||
@@ -598,7 +603,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
p + p {
|
p + p {
|
||||||
margin-top: 1em;
|
margin: 1em 0;
|
||||||
|
&:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
> p:first-child {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ol,
|
ol,
|
||||||
@@ -978,12 +991,24 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
||||||
p {
|
div {
|
||||||
margin: 0 0 .4em;
|
margin: 0 0 .4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
p + p {
|
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 {
|
li {
|
||||||
@@ -1063,6 +1088,12 @@
|
|||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span.edited {
|
||||||
|
color: #A3A3A3;
|
||||||
|
font-size: 0.87em;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.post__link {
|
.post__link {
|
||||||
|
|||||||
@@ -213,6 +213,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
blockquote {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.same--root {
|
&.same--root {
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ export function isComment(post) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isEdited(post) {
|
||||||
|
return post.edit_at > 0;
|
||||||
|
}
|
||||||
|
|
||||||
export function getProfilePicSrcForPost(post, timestamp) {
|
export function getProfilePicSrcForPost(post, timestamp) {
|
||||||
let src = Client.getUsersRoute() + '/' + post.user_id + '/image?time=' + timestamp;
|
let src = Client.getUsersRoute() + '/' + post.user_id + '/image?time=' + timestamp;
|
||||||
if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
|
if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
|
||||||
@@ -28,4 +32,4 @@ export function getProfilePicSrcForPost(post, timestamp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user