Convert TopicView to component

This commit is contained in:
Robin Ward 2016-11-21 15:10:36 -05:00
parent 28061316ad
commit 2c585783ff
2 changed files with 244 additions and 251 deletions

View File

@ -2,13 +2,24 @@ import AddArchetypeClass from 'discourse/mixins/add-archetype-class';
import ClickTrack from 'discourse/lib/click-track'; import ClickTrack from 'discourse/lib/click-track';
import Scrolling from 'discourse/mixins/scrolling'; import Scrolling from 'discourse/mixins/scrolling';
import { selectedText } from 'discourse/lib/utilities'; import { selectedText } from 'discourse/lib/utilities';
import { observes } from 'ember-addons/ember-computed-decorators';
const TopicView = Ember.View.extend(AddArchetypeClass, Scrolling, { function highlight(postNumber) {
templateName: 'topic', const $contents = $(`#post_${postNumber} .topic-body`),
topic: Ember.computed.alias('controller.model'), origColor = $contents.data('orig-color') || $contents.css('backgroundColor');
$contents.data("orig-color", origColor)
.addClass('highlighted')
.stop()
.animate({ backgroundColor: origColor }, 2500, 'swing', function() {
$contents.removeClass('highlighted');
$contents.css({'background-color': ''});
});
}
export default Ember.Component.extend(AddArchetypeClass, Scrolling, {
userFilters: Ember.computed.alias('topic.userFilters'), userFilters: Ember.computed.alias('topic.userFilters'),
classNameBindings: ['controller.multiSelect:multi-select', classNameBindings: ['multiSelect',
'topic.archetype', 'topic.archetype',
'topic.is_warning', 'topic.is_warning',
'topic.category.read_restricted:read_restricted', 'topic.category.read_restricted:read_restricted',
@ -17,30 +28,26 @@ const TopicView = Ember.View.extend(AddArchetypeClass, Scrolling, {
menuVisible: true, menuVisible: true,
SHORT_POST: 1200, SHORT_POST: 1200,
postStream: Em.computed.alias('topic.postStream'), postStream: Ember.computed.alias('topic.postStream'),
archetype: Em.computed.alias('topic.archetype'), archetype: Ember.computed.alias('topic.archetype'),
_lastShowTopic: null, _lastShowTopic: null,
_composeChanged: function() { @observes('enteredAt')
const composerController = Discourse.get('router.composerController'); _enteredTopic() {
composerController.clearState();
composerController.set('topic', this.get('topic'));
}.observes('composer'),
_enteredTopic: function() {
// Ember is supposed to only call observers when values change but something // Ember is supposed to only call observers when values change but something
// in our view set up is firing this observer with the same value. This check // in our view set up is firing this observer with the same value. This check
// prevents scrolled from being called twice. // prevents scrolled from being called twice.
const enteredAt = this.get('controller.enteredAt'); const enteredAt = this.get('enteredAt');
if (enteredAt && (this.get('lastEnteredAt') !== enteredAt)) { if (enteredAt && (this.get('lastEnteredAt') !== enteredAt)) {
this._lastShowTopic = null; this._lastShowTopic = null;
this.scrolled(); this.scrolled();
this.set('lastEnteredAt', enteredAt); this.set('lastEnteredAt', enteredAt);
} }
}.observes('controller.enteredAt'), },
_inserted: function() { didInsertElement() {
this._super();
this.bindScrolling({name: 'topic-view'}); this.bindScrolling({name: 'topic-view'});
$(window).on('resize.discourse-on-scroll', () => this.scrolled()); $(window).on('resize.discourse-on-scroll', () => this.scrolled());
@ -63,10 +70,10 @@ const TopicView = Ember.View.extend(AddArchetypeClass, Scrolling, {
this.appEvents.on('post:highlight', postNumber => { this.appEvents.on('post:highlight', postNumber => {
Ember.run.scheduleOnce('afterRender', null, highlight, postNumber); Ember.run.scheduleOnce('afterRender', null, highlight, postNumber);
}); });
}.on('didInsertElement'), },
// This view is being removed. Shut down operations willDestroyElement() {
_destroyed: function() { this._super();
this.unbindScrolling('topic-view'); this.unbindScrolling('topic-view');
$(window).unbind('resize.discourse-on-scroll'); $(window).unbind('resize.discourse-on-scroll');
@ -79,15 +86,16 @@ const TopicView = Ember.View.extend(AddArchetypeClass, Scrolling, {
this.appEvents.trigger('header:hide-topic'); this.appEvents.trigger('header:hide-topic');
this.appEvents.off('post:highlight'); this.appEvents.off('post:highlight');
}.on('willDestroyElement'), },
gotFocus: function() { @observes('Discourse.hasFocus')
gotFocus() {
if (Discourse.get('hasFocus')){ if (Discourse.get('hasFocus')){
this.scrolled(); this.scrolled();
} }
}.observes("Discourse.hasFocus"), },
resetExamineDockCache: function() { resetExamineDockCache() {
this.set('docAt', false); this.set('docAt', false);
}, },
@ -113,7 +121,7 @@ const TopicView = Ember.View.extend(AddArchetypeClass, Scrolling, {
} }
} }
this.set('controller.hasScrolled', offset > 0); this.set('hasScrolled', offset > 0);
const topic = this.get('topic'); const topic = this.get('topic');
const showTopic = this.showTopicInHeader(topic, offset); const showTopic = this.showTopicInHeader(topic, offset);
@ -131,18 +139,3 @@ const TopicView = Ember.View.extend(AddArchetypeClass, Scrolling, {
this.appEvents.trigger('topic:scrolled', offset); this.appEvents.trigger('topic:scrolled', offset);
} }
}); });
function highlight(postNumber) {
const $contents = $(`#post_${postNumber} .topic-body`),
origColor = $contents.data('orig-color') || $contents.css('backgroundColor');
$contents.data("orig-color", origColor)
.addClass('highlighted')
.stop()
.animate({ backgroundColor: origColor }, 2500, 'swing', function() {
$contents.removeClass('highlighted');
$contents.css({'background-color': ''});
});
}
export default TopicView;

View File

@ -1,233 +1,233 @@
{{#discourse-topic multiSelect=multiSelect enteredAt=enteredAt topic=model hasScrolled=hasScrolled}}
{{#if model}} {{#if model}}
{{add-category-class category=model.category}} {{add-category-class category=model.category}}
<div class="container"> <div class="container">
{{discourse-banner user=currentUser banner=site.banner overlay=hasScrolled hide=model.errorLoading}} {{discourse-banner user=currentUser banner=site.banner overlay=hasScrolled hide=model.errorLoading}}
</div>
{{/if}}
{{plugin-outlet "topic-above-post-stream"}}
{{#if model.postStream.loaded}}
{{#if model.postStream.firstPostPresent}}
<div id="topic-title">
<div class="container">
<div class="title-wrapper">
{{#if editingTopic}}
{{#if model.isPrivateMessage}}
<span class="private-message-glyph">{{fa-icon "envelope"}}</span>
{{/if}}
{{text-field id="edit-title" value=buffered.title maxlength=siteSettings.max_topic_title_length autofocus="true"}}
{{#if showCategoryChooser}}
<br>
{{category-chooser valueAttribute="id" value=buffered.category_id}}
{{/if}}
{{#if canEditTags}}
<br>
{{tag-chooser tags=buffered.tags categoryId=buffered.category_id}}
{{/if}}
{{plugin-outlet "edit-topic"}}
{{d-button action="finishedEditingTopic" class="btn-primary btn-small submit-edit" icon="check"}}
{{d-button action="cancelEditingTopic" class="btn-small cancel-edit" icon="times"}}
{{else}}
<h1>
{{#unless model.is_warning}}
<a href={{pmPath}}>
<span class="private-message-glyph">{{fa-icon "envelope"}}</span>
</a>
{{/unless}}
{{#if model.details.loaded}}
{{topic-status topic=model}}
<a href="{{unbound model.url}}" {{action "jumpTop"}} class="fancy-title">
{{{model.fancyTitle}}}
</a>
{{/if}}
{{#if model.details.can_edit}}
<a href {{action "editTopic"}} class="edit-topic" title="{{i18n "edit"}}">{{fa-icon "pencil"}}</a>
{{/if}}
</h1>
{{#unless model.isPrivateMessage}}
{{topic-category topic=model class="topic-category"}}
{{/unless}}
{{/if}}
</div>
{{plugin-outlet "topic-title"}}
</div>
</div> </div>
{{/if}} {{/if}}
<div class="container posts"> {{plugin-outlet "topic-above-post-stream"}}
<div class='selected-posts {{unless multiSelect 'hidden'}}'>
{{partial "selected-posts"}}
</div>
{{#topic-navigation jumpToIndex="jumpToIndex" as |info|}} {{#if model.postStream.loaded}}
{{#if model.postStream.firstPostPresent}}
<div id="topic-title">
<div class="container">
{{#if info.renderAdminMenuButton}} <div class="title-wrapper">
{{topic-admin-menu-button topic=model fixed="true" delegated=topicDelegated}} {{#if editingTopic}}
{{/if}} {{#if model.isPrivateMessage}}
<span class="private-message-glyph">{{fa-icon "envelope"}}</span>
{{#if info.renderTimeline}}
{{topic-timeline topic=model
prevEvent=info.prevEvent
fullscreen=info.topicProgressExpanded
enteredIndex=enteredIndex
loading=model.postStream.loading
delegated=topicDelegated}}
{{else}}
{{topic-progress prevEvent=info.prevEvent topic=model delegated=topicDelegated expanded=info.topicProgressExpanded}}
{{/if}}
{{/topic-navigation}}
<div class="row">
<section class="topic-area" id="topic" data-topic-id="{{unbound model.id}}">
<div class="posts-wrapper">
{{conditional-loading-spinner condition=model.postStream.loadingAbove}}
{{plugin-outlet "topic-above-posts"}}
{{#unless model.postStream.loadingFilter}}
{{scrolling-post-stream
posts=postsToRender
canCreatePost=model.details.can_create_post
multiSelect=multiSelect
selectedPostsCount=selectedPostsCount
selectedQuery=selectedQuery
gaps=model.postStream.gaps
showFlags="showFlags"
editPost="editPost"
showHistory="showHistory"
showLogin="showLogin"
showRawEmail="showRawEmail"
deletePost="deletePost"
recoverPost="recoverPost"
expandHidden="expandHidden"
newTopicAction="replyAsNewTopic"
expandFirstPost="expandFirstPost"
toggleBookmark="toggleBookmark"
togglePostType="togglePostType"
rebakePost="rebakePost"
changePostOwner="changePostOwner"
unhidePost="unhidePost"
replyToPost="replyToPost"
toggleWiki="toggleWiki"
toggleSummary="toggleSummary"
removeAllowedUser="removeAllowedUser"
removeAllowedGroup="removeAllowedGroup"
showInvite="showInvite"
topVisibleChanged="topVisibleChanged"
currentPostChanged="currentPostChanged"
currentPostScrolled="currentPostScrolled"
bottomVisibleChanged="bottomVisibleChanged"
selectPost="toggledSelectedPost"
selectReplies="toggledSelectedPostReplies"
fillGapBefore="fillGapBefore"
fillGapAfter="fillGapAfter"}}
{{/unless}}
{{conditional-loading-spinner condition=model.postStream.loadingBelow}}
</div>
<div id="topic-bottom"></div>
{{#conditional-loading-spinner condition=model.postStream.loadingFilter}}
{{#if loadedAllPosts}}
{{topic-closing topic=model}}
{{#if session.showSignupCta}}
{{! replace "Log In to Reply" with the infobox }}
{{signup-cta}}
{{else}}
{{#if currentUser}}
{{topic-footer-buttons topic=model topicDelegated=topicDelegated}}
{{else}}
{{d-button icon="reply" class="btn-primary" action="showLogin" label="topic.reply.title"}}
{{/if}} {{/if}}
{{/if}}
{{#if model.pending_posts_count}} {{text-field id="edit-title" value=buffered.title maxlength=siteSettings.max_topic_title_length autofocus="true"}}
<div class="has-pending-posts"> {{#if showCategoryChooser}}
{{{i18n "queue.has_pending_posts" count=model.pending_posts_count}}} <br>
{{category-chooser valueAttribute="id" value=buffered.category_id}}
{{/if}}
{{#if currentUser.show_queued_posts}} {{#if canEditTags}}
{{#link-to "queued-posts"}} <br>
{{fa-icon "check"}} {{tag-chooser tags=buffered.tags categoryId=buffered.category_id}}
{{i18n "queue.view_pending"}} {{/if}}
{{/link-to}}
{{plugin-outlet "edit-topic"}}
{{d-button action="finishedEditingTopic" class="btn-primary btn-small submit-edit" icon="check"}}
{{d-button action="cancelEditingTopic" class="btn-small cancel-edit" icon="times"}}
{{else}}
<h1>
{{#unless model.is_warning}}
<a href={{pmPath}}>
<span class="private-message-glyph">{{fa-icon "envelope"}}</span>
</a>
{{/unless}}
{{#if model.details.loaded}}
{{topic-status topic=model}}
<a href="{{unbound model.url}}" {{action "jumpTop"}} class="fancy-title">
{{{model.fancyTitle}}}
</a>
{{/if}} {{/if}}
</div>
{{#if model.details.can_edit}}
<a href {{action "editTopic"}} class="edit-topic" title="{{i18n "edit"}}">{{fa-icon "pencil"}}</a>
{{/if}}
</h1>
{{#unless model.isPrivateMessage}}
{{topic-category topic=model class="topic-category"}}
{{/unless}}
{{/if}} {{/if}}
</div>
{{plugin-outlet "topic-title"}}
</div>
</div>
{{/if}}
{{#if showSelectedPostsAtBottom}} <div class="container posts">
<div class='selected-posts {{unless multiSelect 'hidden'}}'> <div class='selected-posts {{unless multiSelect 'hidden'}}'>
{{partial "selected-posts"}} {{partial "selected-posts"}}
</div> </div>
{{/if}}
{{plugin-outlet "topic-above-suggested"}} {{#topic-navigation jumpToIndex="jumpToIndex" as |info|}}
{{#if model.details.suggested_topics.length}} {{#if info.renderAdminMenuButton}}
<div id="suggested-topics"> {{topic-admin-menu-button topic=model fixed="true" delegated=topicDelegated}}
<h3>{{{suggestedTitle}}}</h3> {{/if}}
<div class="topics">
{{#if model.isPrivateMessage}} {{#if info.renderTimeline}}
{{basic-topic-list hideCategory="true" {{topic-timeline topic=model
showPosters="true" prevEvent=info.prevEvent
topics=model.details.suggested_topics}} fullscreen=info.topicProgressExpanded
{{else}} enteredIndex=enteredIndex
{{basic-topic-list topics=model.details.suggested_topics}} loading=model.postStream.loading
delegated=topicDelegated}}
{{else}}
{{topic-progress prevEvent=info.prevEvent topic=model delegated=topicDelegated expanded=info.topicProgressExpanded}}
{{/if}}
{{/topic-navigation}}
<div class="row">
<section class="topic-area" id="topic" data-topic-id="{{unbound model.id}}">
<div class="posts-wrapper">
{{conditional-loading-spinner condition=model.postStream.loadingAbove}}
{{plugin-outlet "topic-above-posts"}}
{{#unless model.postStream.loadingFilter}}
{{scrolling-post-stream
posts=postsToRender
canCreatePost=model.details.can_create_post
multiSelect=multiSelect
selectedPostsCount=selectedPostsCount
selectedQuery=selectedQuery
gaps=model.postStream.gaps
showFlags="showFlags"
editPost="editPost"
showHistory="showHistory"
showLogin="showLogin"
showRawEmail="showRawEmail"
deletePost="deletePost"
recoverPost="recoverPost"
expandHidden="expandHidden"
newTopicAction="replyAsNewTopic"
expandFirstPost="expandFirstPost"
toggleBookmark="toggleBookmark"
togglePostType="togglePostType"
rebakePost="rebakePost"
changePostOwner="changePostOwner"
unhidePost="unhidePost"
replyToPost="replyToPost"
toggleWiki="toggleWiki"
toggleSummary="toggleSummary"
removeAllowedUser="removeAllowedUser"
removeAllowedGroup="removeAllowedGroup"
showInvite="showInvite"
topVisibleChanged="topVisibleChanged"
currentPostChanged="currentPostChanged"
currentPostScrolled="currentPostScrolled"
bottomVisibleChanged="bottomVisibleChanged"
selectPost="toggledSelectedPost"
selectReplies="toggledSelectedPostReplies"
fillGapBefore="fillGapBefore"
fillGapAfter="fillGapAfter"}}
{{/unless}}
{{conditional-loading-spinner condition=model.postStream.loadingBelow}}
</div>
<div id="topic-bottom"></div>
{{#conditional-loading-spinner condition=model.postStream.loadingFilter}}
{{#if loadedAllPosts}}
{{topic-closing topic=model}}
{{#if session.showSignupCta}}
{{! replace "Log In to Reply" with the infobox }}
{{signup-cta}}
{{else}}
{{#if currentUser}}
{{topic-footer-buttons topic=model topicDelegated=topicDelegated}}
{{else}}
{{d-button icon="reply" class="btn-primary" action="showLogin" label="topic.reply.title"}}
{{/if}}
{{/if}}
{{#if model.pending_posts_count}}
<div class="has-pending-posts">
{{{i18n "queue.has_pending_posts" count=model.pending_posts_count}}}
{{#if currentUser.show_queued_posts}}
{{#link-to "queued-posts"}}
{{fa-icon "check"}}
{{i18n "queue.view_pending"}}
{{/link-to}}
{{/if}} {{/if}}
</div> </div>
<h3>{{{browseMoreMessage}}}</h3> {{/if}}
</div>
{{#if showSelectedPostsAtBottom}}
<div class='selected-posts {{unless multiSelect 'hidden'}}'>
{{partial "selected-posts"}}
</div>
{{/if}}
{{plugin-outlet "topic-above-suggested"}}
{{#if model.details.suggested_topics.length}}
<div id="suggested-topics">
<h3>{{{suggestedTitle}}}</h3>
<div class="topics">
{{#if model.isPrivateMessage}}
{{basic-topic-list hideCategory="true"
showPosters="true"
topics=model.details.suggested_topics}}
{{else}}
{{basic-topic-list topics=model.details.suggested_topics}}
{{/if}}
</div>
<h3>{{{browseMoreMessage}}}</h3>
</div>
{{/if}}
{{/if}} {{/if}}
{{/conditional-loading-spinner}}
{{/if}} </section>
{{/conditional-loading-spinner}} </div>
</section>
</div> </div>
{{else}}
<div class="container">
{{#conditional-loading-spinner condition=noErrorYet}}
{{#if model.notFoundHtml}}
<div class="not-found">{{{model.notFoundHtml}}}</div>
{{else}}
<div class="topic-error">
<div>{{model.message}}</div>
{{#if model.noRetry}}
{{#unless currentUser}}
{{d-button action="showLogin" class="btn-primary topic-retry" icon="user" label="log_in"}}
{{/unless}}
{{else}}
{{d-button action="retryLoading" class="btn-primary topic-retry" icon="refresh" label="errors.buttons.again"}}
{{/if}}
</div>
{{conditional-loading-spinner condition=retrying}}
{{/if}}
{{/conditional-loading-spinner}}
</div>
{{/if}}
</div> {{share-popup topic=model replyAsNewTopic="replyAsNewTopic"}}
{{else}}
<div class="container">
{{#conditional-loading-spinner condition=noErrorYet}}
{{#if model.notFoundHtml}}
<div class="not-found">{{{model.notFoundHtml}}}</div>
{{else}}
<div class="topic-error">
<div>{{model.message}}</div>
{{#if model.noRetry}}
{{#unless currentUser}}
{{d-button action="showLogin" class="btn-primary topic-retry" icon="user" label="log_in"}}
{{/unless}}
{{else}}
{{d-button action="retryLoading" class="btn-primary topic-retry" icon="refresh" label="errors.buttons.again"}}
{{/if}}
</div>
{{conditional-loading-spinner condition=retrying}}
{{/if}}
{{/conditional-loading-spinner}}
</div>
{{/if}}
{{share-popup topic=model replyAsNewTopic="replyAsNewTopic"}}
{{#if currentUser.enable_quoting}}
{{quote-button topic=model
quoteState=quoteState
selectText="selectText"
deselectText="deselectText"}}
{{/if}}
{{#if currentUser.enable_quoting}}
{{quote-button topic=model
quoteState=quoteState
selectText="selectText"
deselectText="deselectText"}}
{{/if}}
{{/discourse-topic}}