FEATURE: Show time gap between posts if more than a few days

This commit is contained in:
Robin Ward 2015-06-18 17:06:25 -04:00
parent 4e898c604e
commit 42bd9b6199
10 changed files with 125 additions and 5 deletions

View file

@ -0,0 +1,20 @@
export default Ember.Component.extend({
classNameBindings: [':time-gap'],
render(buffer) {
const gapDays = this.get('gapDays');
let timeGapWords;
if (gapDays < 30) {
timeGapWords = I18n.t('dates.later.x_days', {count: gapDays});
} else if (gapDays < 365) {
const gapMonths = Math.floor(gapDays / 30);
timeGapWords = I18n.t('dates.later.x_months', {count: gapMonths});
} else {
const gapYears = Math.floor(gapDays / 365);
timeGapWords = I18n.t('dates.later.x_years', {count: gapYears});
}
buffer.push("<div class='time-gap-words'>" + timeGapWords + "</div>");
}
});

View file

@ -1,5 +1,21 @@
import RestModel from 'discourse/models/rest'; import RestModel from 'discourse/models/rest';
function calcDayDiff(p1, p2) {
if (!p1) { return; }
const date = p1.get('created_at');
if (date) {
if (p2) {
const lastDate = p2.get('created_at');
if (lastDate) {
const delta = new Date(date).getTime() - new Date(lastDate).getTime();
const days = Math.round(delta / (1000 * 60 * 60 * 24));
p1.set('daysSincePrevious', days);
}
}
}
}
const PostStream = RestModel.extend({ const PostStream = RestModel.extend({
loading: Em.computed.or('loadingAbove', 'loadingBelow', 'loadingFilter', 'stagingPost'), loading: Em.computed.or('loadingAbove', 'loadingBelow', 'loadingFilter', 'stagingPost'),
notLoading: Em.computed.not('loading'), notLoading: Em.computed.not('loading'),
@ -367,14 +383,22 @@ const PostStream = RestModel.extend({
}, },
prependPost(post) { prependPost(post) {
this.get('posts').unshiftObject(this.storePost(post)); const stored = this.storePost(post);
if (stored) {
const posts = this.get('posts');
calcDayDiff(posts.get('firstObject'), stored);
posts.unshiftObject(stored);
}
return post; return post;
}, },
appendPost(post) { appendPost(post) {
const stored = this.storePost(post); const stored = this.storePost(post);
if (stored) { if (stored) {
this.get('posts').addObject(stored); const posts = this.get('posts');
calcDayDiff(stored, posts.get('lastObject'));
posts.addObject(stored);
} }
return post; return post;
}, },

View file

@ -27,6 +27,10 @@ const Post = RestModel.extend({
notDeleted: Em.computed.not('deleted'), notDeleted: Em.computed.not('deleted'),
userDeleted: Em.computed.empty('user_id'), userDeleted: Em.computed.empty('user_id'),
hasTimeGap: function() {
return (this.get('daysSincePrevious') || 0) > Discourse.SiteSettings.show_time_gap_days;
}.property('daysSincePrevious'),
showName: function() { showName: function() {
const name = this.get('name'); const name = this.get('name');
return name && (name !== this.get('username')) && Discourse.SiteSettings.display_name_on_posts; return name && (name !== this.get('username')) && Discourse.SiteSettings.display_name_on_posts;

View file

@ -0,0 +1,5 @@
<div class='time-gap'>
<div class='time-gap-days'>
{{gapInWords}}
</div>
</div>

View file

@ -1,5 +1,9 @@
{{post-gap post=this postStream=controller.model.postStream before="true"}} {{post-gap post=this postStream=controller.model.postStream before="true"}}
{{#if hasTimeGap}}
{{time-gap gapDays=daysSincePrevious}}
{{/if}}
<div class='row'> <div class='row'>
{{view 'reply-history' content=replyHistory}} {{view 'reply-history' content=replyHistory}}
</div> </div>

View file

@ -717,6 +717,21 @@ $topic-avatar-width: 45px;
width: calc(#{$topic-avatar-width} + #{$topic-body-width} + 2 * #{$topic-body-width-padding}); width: calc(#{$topic-avatar-width} + #{$topic-body-width} + 2 * #{$topic-body-width-padding});
} }
.time-gap {
width: 755px;
border-top: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
}
.time-gap-words {
display: inline-block;
padding: 0.5em 1em;
margin: 0.5em 0 0.5em 56px;
text-transform: uppercase;
font-weight: bold;
font-size: 0.8em;
background-color: dark-light-diff($primary, $secondary, 90%, -60%);
color: lighten($primary, 30%);
}
.posts-wrapper { .posts-wrapper {
position: relative; position: relative;
-webkit-font-smoothing: subpixel-antialiased; -webkit-font-smoothing: subpixel-antialiased;

View file

@ -92,6 +92,16 @@ en:
x_days: x_days:
one: "1 day ago" one: "1 day ago"
other: "%{count} days ago" other: "%{count} days ago"
later:
x_days:
one: "1 day layer"
other: "%{count} days later"
x_months:
one: "1 month layer"
other: "%{count} months later"
x_years:
one: "1 year layer"
other: "%{count} years later"
share: share:
topic: 'share a link to this topic' topic: 'share a link to this topic'
post: 'post #%{postNumber}' post: 'post #%{postNumber}'

View file

@ -1120,6 +1120,7 @@ en:
full_name_required: "Full name is a required field of a user's profile." full_name_required: "Full name is a required field of a user's profile."
enable_names: "Show the user's full name on their profile, user card, and emails. Disable to hide full name everywhere." enable_names: "Show the user's full name on their profile, user card, and emails. Disable to hide full name everywhere."
display_name_on_posts: "Show a user's full name on their posts in addition to their @username." display_name_on_posts: "Show a user's full name on their posts in addition to their @username."
show_time_gap_days: "If two posts are made this many days apart, display the time gap in the topic."
invites_per_page: "Default invites shown on the user page." invites_per_page: "Default invites shown on the user page."
short_progress_text_threshold: "After the number of posts in a topic goes above this number, the progress bar will only show the current post number. If you change the progress bar's width, you may need to change this value." short_progress_text_threshold: "After the number of posts in a topic goes above this number, the progress bar will only show the current post number. If you change the progress bar's width, you may need to change this value."
default_code_lang: "Default programming language syntax highlighting applied to GitHub code blocks (lang-auto, ruby, python etc.)" default_code_lang: "Default programming language syntax highlighting applied to GitHub code blocks (lang-auto, ruby, python etc.)"

View file

@ -415,6 +415,9 @@ posting:
display_name_on_posts: display_name_on_posts:
client: true client: true
default: false default: false
show_time_gap_days:
default: 4
client: true
short_progress_text_threshold: short_progress_text_threshold:
client: true client: true
default: 10000 default: 10000

View file

@ -26,6 +26,40 @@ test('defaults', function() {
present(postStream.get('topic')); present(postStream.get('topic'));
}); });
test('daysSincePrevious when appending', function(assert) {
const postStream = buildStream(10000001, [1,2,3]);
const store = postStream.store;
const p1 = store.createRecord('post', {id: 1, post_number: 1, created_at: "2015-05-29T18:17:35.868Z"}),
p2 = store.createRecord('post', {id: 2, post_number: 2, created_at: "2015-06-01T01:07:25.761Z"}),
p3 = store.createRecord('post', {id: 3, post_number: 3, created_at: "2015-06-02T01:07:25.761Z"});
postStream.appendPost(p1);
postStream.appendPost(p2);
postStream.appendPost(p3);
assert.ok(!p1.get('daysSincePrevious'));
assert.equal(p2.get('daysSincePrevious'), 2);
assert.equal(p3.get('daysSincePrevious'), 1);
});
test('daysSincePrevious when prepending', function(assert) {
const postStream = buildStream(10000001, [1,2,3]);
const store = postStream.store;
const p1 = store.createRecord('post', {id: 1, post_number: 1, created_at: "2015-05-29T18:17:35.868Z"}),
p2 = store.createRecord('post', {id: 2, post_number: 2, created_at: "2015-06-01T01:07:25.761Z"}),
p3 = store.createRecord('post', {id: 3, post_number: 3, created_at: "2015-06-02T01:07:25.761Z"});
postStream.prependPost(p3);
postStream.prependPost(p2);
postStream.prependPost(p1);
assert.ok(!p1.get('daysSincePrevious'));
assert.equal(p2.get('daysSincePrevious'), 2);
assert.equal(p3.get('daysSincePrevious'), 1);
});
test('appending posts', function() { test('appending posts', function() {
const postStream = buildStream(4567, [1, 3, 4]); const postStream = buildStream(4567, [1, 3, 4]);
const store = postStream.store; const store = postStream.store;