This repository has been archived on 2025-05-04. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
discourse/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6
2016-05-31 10:51:47 -04:00

239 lines
6.3 KiB
JavaScript

import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
import { smartShortDate, relativeAge } from 'discourse/lib/formatter';
import { iconNode } from 'discourse/helpers/fa-icon';
const SCROLLAREA_HEIGHT = 300;
const SCROLLER_HEIGHT = 50;
const SCROLLAREA_REMAINING = SCROLLAREA_HEIGHT - SCROLLER_HEIGHT;
createWidget('timeline-last-read', {
tagName: 'div.timeline-last-read',
buildAttributes(attrs) {
return { style: `height: 40px; top: ${attrs.top}px` };
},
html() {
return [
iconNode('circle', { class: 'progress' }),
this.attach('button', {
className: 'btn btn-primary btn-small',
icon: 'arrow-left',
label: 'go_back',
action: 'goBack'
})
];
},
goBack() {
this.sendWidgetAction('jumpToPost', this.attrs.lastRead);
}
});
createWidget('timeline-scroller', {
tagName: 'div.timeline-scroller',
buildAttributes() {
return { style: `height: ${SCROLLER_HEIGHT}px` };
},
html(attrs) {
const { current, total, date } = attrs;
const repliesKey = (total < 1000) ? 'replies' : 'replies_short';
const contents = [
h('div.timeline-replies', I18n.t(`topic.timeline.${repliesKey}`, { current, total }))
];
if (date) {
contents.push(h('div.timeline-ago', smartShortDate(date)));
}
return [ h('div.timeline-handle'), h('div.timeline-scroller-content', contents) ];
},
drag(e) {
this.sendWidgetAction('updatePercentage', e.pageY);
},
dragEnd() {
this.sendWidgetAction('commit');
}
});
createWidget('timeline-padding', {
tagName: 'div.timeline-padding',
buildAttributes(attrs) {
return { style: `height: ${attrs.height}px` };
},
click(e) {
this.sendWidgetAction('updatePercentage', e.pageY);
this.sendWidgetAction('commit');
}
});
createWidget('timeline-scrollarea', {
tagName: 'div.timeline-scrollarea',
buildKey: () => `timeline-scrollarea`,
buildAttributes() {
return { style: `height: ${SCROLLAREA_HEIGHT}px` };
},
defaultState(attrs) {
return { percentage: this._percentFor(attrs.topic, attrs.topic.currentPost), scrolledPost: 1 };
},
position() {
const { attrs } = this;
const percentage = this.state.percentage;
const postStream = attrs.topic.get('postStream');
const total = postStream.get('filteredPostsCount');
let current = Math.round(total * percentage);
if (current < 1) { current = 1; }
if (current > total) { current = total; }
const daysAgo = postStream.closestDaysAgoFor(current);
const date = new Date();
date.setDate(date.getDate() - daysAgo || 0);
const result = {
current,
total,
date,
lastRead: null,
lastReadPercentage: null
};
if (attrs.topicTrackingState) {
const lastRead = attrs.topicTrackingState.lastReadPostNumber(attrs.topic.id);
if (lastRead) {
result.lastRead = lastRead;
result.lastReadPercentage = lastRead === 1 ? 0.0 : parseFloat(lastRead) / total;
}
}
return result;
},
html(attrs, state) {
const position = this.position();
state.scrolledPost = position.current;
const percentage = state.percentage;
if (percentage === null) { return; }
const before = SCROLLAREA_REMAINING * percentage;
const after = (SCROLLAREA_HEIGHT - before) - SCROLLER_HEIGHT;
const result = [
this.attach('timeline-padding', { height: before }),
this.attach('timeline-scroller', position),
this.attach('timeline-padding', { height: after })
];
if (position.lastRead && position.lastRead < attrs.topic.posts_count) {
const lastReadTop = Math.round(position.lastReadPercentage * SCROLLAREA_HEIGHT);
if (lastReadTop > (before + SCROLLER_HEIGHT)) {
result.push(this.attach('timeline-last-read', { top: lastReadTop, lastRead: position.lastRead }));
}
}
return result;
},
updatePercentage(y) {
const $area = $('.timeline-scrollarea');
const areaTop = $area.offset().top;
let percentage = parseFloat(y - areaTop) / $area.height();
if (percentage > 1.0) { percentage = 1.0; };
if (percentage < 0.0) { percentage = 0.0; };
this.state.percentage = percentage;
},
commit() {
const position = this.position();
this.sendWidgetAction('jumpToIndex', position.current);
},
topicCurrentPostChanged(postNumber) {
// If the post number didn't change keep our scroll position
if (postNumber !== this.state.scrolledPost) {
this.state.percentage = this._percentFor(this.attrs.topic, postNumber);
}
},
_percentFor(topic, postNumber) {
const total = topic.get('postStream.filteredPostsCount');
return postNumber === 1 ? 0.0 : parseFloat(postNumber) / total;
}
});
createWidget('topic-timeline-container', {
tagName: 'div.timeline-container',
buildClasses(attrs) {
if (attrs.dockAt) { return 'timeline-docked'; }
},
buildAttributes(attrs) {
if (attrs.dockAt) {
return { style: `top: ${attrs.dockAt}px` };
};
return { style: 'top: 140px' };
},
html(attrs) {
return this.attach('topic-timeline', attrs);
}
});
export default createWidget('topic-timeline', {
tagName: 'div.topic-timeline',
html(attrs) {
const { topic } = attrs;
const createdAt = new Date(topic.created_at);
const controls = [];
if (attrs.topic.get('details.can_create_post')) {
controls.push(this.attach('button', {
className: 'btn btn-primary create',
icon: 'reply',
label: 'topic.reply.title',
action: 'replyToPost'
}));
}
const { currentUser } = this;
if (currentUser && currentUser.get('canManageTopic')) {
controls.push(this.attach('topic-admin-menu-button', { topic }));
}
const result = [ h('div.timeline-controls', controls) ];
const stream = attrs.topic.get('postStream.stream');
if (stream.length > 2) {
return result.concat([
this.attach('link', {
className: 'start-date',
rawLabel: moment(createdAt).format(I18n.t('dates.timeline_start')),
action: 'jumpTop'
}),
this.attach('timeline-scrollarea', attrs),
this.attach('link', {
className: 'now-date',
icon: 'dot-circle-o',
rawLabel: relativeAge(new Date(topic.last_posted_at)),
action: 'jumpBottom'
})
]);
}
return result;
}
});