diff --git a/app/assets/javascripts/discourse/controllers/topic_controller.js b/app/assets/javascripts/discourse/controllers/topic_controller.js index d4dd5ac9ef7..7481e49f2d7 100644 --- a/app/assets/javascripts/discourse/controllers/topic_controller.js +++ b/app/assets/javascripts/discourse/controllers/topic_controller.js @@ -200,13 +200,13 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected }, replyAsNewTopic: function(post) { - var composerController = this.get('controllers.composer'); - var promise = composerController.open({ - action: Discourse.Composer.CREATE_TOPIC, - draftKey: Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY - }); - var postUrl = "" + location.protocol + "//" + location.host + (post.get('url')); - var postLink = "[" + (this.get('title')) + "](" + postUrl + ")"; + var composerController = this.get('controllers.composer'), + promise = composerController.open({ + action: Discourse.Composer.CREATE_TOPIC, + draftKey: Discourse.Composer.REPLY_AS_NEW_TOPIC_KEY + }), + postUrl = "" + location.protocol + "//" + location.host + (post.get('url')), + postLink = "[" + (this.get('title')) + "](" + postUrl + ")"; promise.then(function() { Discourse.Post.loadQuote(post.get('id')).then(function(q) { @@ -459,6 +459,65 @@ Discourse.TopicController = Discourse.ObjectController.extend(Discourse.Selected } return true; } + }, + + // If our current post is changed, notify the router + _currentPostChanged: function() { + var currentPost = this.get('currentPost'); + if (currentPost) { + this.send('postChangedRoute', currentPost); + } + }.observes('currentPost'), + + sawObjects: function(posts) { + if (posts) { + var self = this, + lastReadPostNumber = this.get('last_read_post_number'); + + posts.forEach(function(post) { + var postNumber = post.get('post_number'); + if (postNumber > lastReadPostNumber) { + lastReadPostNumber = postNumber; + } + post.set('read', true); + }); + self.set('last_read_post_number', lastReadPostNumber); + + } + }, + + topVisibleChanged: function(post) { + var postStream = this.get('postStream'), + firstLoadedPost = postStream.get('firstLoadedPost'); + + this.set('currentPost', post.get('post_number')); + + if (firstLoadedPost && firstLoadedPost === post) { + // Note: jQuery shouldn't be done in a controller, but how else can we + // trigger a scroll after a promise resolves in a controller? We need + // to do this to preserve upwards infinte scrolling. + var $body = $('body'), + $elem = $('#post-cloak-' + post.get('post_number')), + distToElement = $body.scrollTop() - $elem.position().top; + + postStream.prependMore().then(function() { + Em.run.next(function () { + $elem = $('#post-cloak-' + post.get('post_number')); + $('html, body').scrollTop($elem.position().top + distToElement); + }); + }); + } + }, + + bottomVisibleChanged: function(post) { + this.set('progressPosition', post.get('post_number')); + + var postStream = this.get('postStream'), + lastLoadedPost = postStream.get('lastLoadedPost'); + + if (lastLoadedPost && lastLoadedPost === post) { + postStream.appendMore(); + } } diff --git a/app/assets/javascripts/discourse/lib/eyeline.js b/app/assets/javascripts/discourse/lib/eyeline.js index 1dc3defb382..74ffd125c37 100644 --- a/app/assets/javascripts/discourse/lib/eyeline.js +++ b/app/assets/javascripts/discourse/lib/eyeline.js @@ -1,12 +1,6 @@ /** Track visible elemnts on the screen. - You can register for triggers on: - - `focusChanged` the top element we're focusing on - - `seenElement` if we've seen the element - @class Eyeline @namespace Discourse @module Discourse @@ -16,107 +10,23 @@ Discourse.Eyeline = function Eyeline(selector) { this.selector = selector; }; - -/** - Call this to analyze the positions of all the nodes in a set - - returns: a hash with top, bottom and onScreen items - {top: , bottom:, onScreen:} - **/ -Discourse.Eyeline.analyze = function(rows) { - var current, goingUp, i, increment, offset, - winHeight, winOffset, detected, onScreen, - bottom, top, outerHeight; - - if (rows.length === 0) return; - - i = parseInt(rows.length / 2, 10); - increment = parseInt(rows.length / 4, 10); - goingUp = undefined; - winOffset = window.pageYOffset || $('html').scrollTop(); - winHeight = window.innerHeight || $(window).height(); - - while (true) { - if (i === 0 || (i >= rows.length - 1)) { - break; - } - current = $(rows[i]); - offset = current.offset(); - - if (offset.top - winHeight < winOffset) { - if (offset.top + current.outerHeight() - window.innerHeight > winOffset) { - break; - } else { - i = i + increment; - if (goingUp !== undefined && increment === 1 && !goingUp) { - break; - } - goingUp = true; - } - } else { - i = i - increment; - if (goingUp !== undefined && increment === 1 && goingUp) { - break; - } - goingUp = false; - } - if (increment > 1) { - increment = parseInt(increment / 2, 10); - goingUp = undefined; - } - if (increment === 0) { - increment = 1; - goingUp = undefined; - } - } - - onScreen = []; - bottom = i; - // quick analysis of whats on screen - while(true) { - if(i < 0) { break;} - - current = $(rows[i]); - offset = current.offset(); - outerHeight = current.outerHeight(); - - // on screen - if(offset.top > winOffset && offset.top + outerHeight < winOffset + winHeight) { - onScreen.unshift(i); - } else { - - if(offset.top < winOffset) { - top = i; - break; - } else { - // bottom - } - } - i -=1; - } - - return({top: top, bottom: bottom, onScreen: onScreen}); - -}; - - /** Call this whenever you want to consider what is being seen by the browser @method update **/ Discourse.Eyeline.prototype.update = function() { - var $elements, atBottom, bottomOffset, docViewBottom, docViewTop, documentHeight, foundElement, windowHeight, - _this = this; + var docViewTop = $(window).scrollTop(), + windowHeight = $(window).height(), + docViewBottom = docViewTop + windowHeight, + documentHeight = $(document).height(), + $elements = $(this.selector), + atBottom = false, + foundElement = false, + bottomOffset = $elements.last().offset(), + self = this; - docViewTop = $(window).scrollTop(); - windowHeight = $(window).height(); - docViewBottom = docViewTop + windowHeight; - documentHeight = $(document).height(); - $elements = $(this.selector); - atBottom = false; - - if (bottomOffset = $elements.last().offset()) { + if (bottomOffset) { atBottom = (bottomOffset.top <= docViewBottom) && (bottomOffset.top >= docViewTop); } @@ -124,14 +34,12 @@ Discourse.Eyeline.prototype.update = function() { foundElement = false; return $elements.each(function(i, elem) { - var $elem, elemBottom, elemTop, markSeen; + var $elem = $(elem), + elemTop = $elem.offset().top, + elemBottom = elemTop + $elem.height(), + markSeen = false; - $elem = $(elem); - elemTop = $elem.offset().top; - elemBottom = elemTop + $elem.height(); - markSeen = false; // It's seen if... - // ...the element is vertically within the top and botom if ((elemTop <= docViewBottom) && (elemTop >= docViewTop)) markSeen = true; @@ -145,19 +53,17 @@ Discourse.Eyeline.prototype.update = function() { // If you hit the bottom we mark all the elements as seen. Otherwise, just the first one if (!atBottom) { - _this.trigger('saw', { - detail: $elem - }); + self.trigger('saw', { detail: $elem }); if (i === 0) { - _this.trigger('sawTop', { detail: $elem }); + self.trigger('sawTop', { detail: $elem }); } return false; } if (i === 0) { - _this.trigger('sawTop', { detail: $elem }); + self.trigger('sawTop', { detail: $elem }); } if (i === ($elements.length - 1)) { - return _this.trigger('sawBottom', { detail: $elem }); + return self.trigger('sawBottom', { detail: $elem }); } }); }; @@ -169,10 +75,9 @@ Discourse.Eyeline.prototype.update = function() { @method flushRest **/ Discourse.Eyeline.prototype.flushRest = function() { - var eyeline = this; - return $(this.selector).each(function(i, elem) { - var $elem = $(elem); - return eyeline.trigger('saw', { detail: $elem }); + var self = this; + $(this.selector).each(function(i, elem) { + return self.trigger('saw', { detail: $(elem) }); }); }; diff --git a/app/assets/javascripts/discourse/lib/screen_track.js b/app/assets/javascripts/discourse/lib/screen_track.js index c5f7c7947b0..1627092e3db 100644 --- a/app/assets/javascripts/discourse/lib/screen_track.js +++ b/app/assets/javascripts/discourse/lib/screen_track.js @@ -6,10 +6,13 @@ @namespace Discourse @module Discourse **/ + +var PAUSE_UNLESS_SCROLLED = 1000 * 60 * 3, + MAX_TRACKING_TIME = 1000 * 60 * 6; + Discourse.ScreenTrack = Ember.Object.extend({ init: function() { - var screenTrack = this; this.reset(); }, @@ -24,9 +27,9 @@ Discourse.ScreenTrack = Ember.Object.extend({ // Create an interval timer if we don't have one. if (!this.get('interval')) { - var screenTrack = this; + var self = this; this.set('interval', setInterval(function () { - screenTrack.tick(); + self.tick(); }, 1000)); } @@ -57,13 +60,15 @@ Discourse.ScreenTrack = Ember.Object.extend({ // Reset our timers reset: function() { - this.set('lastTick', new Date().getTime()); - this.set('lastScrolled', new Date().getTime()); - this.set('lastFlush', 0); - this.set('cancelled', false); - this.set('timings', {}); - this.set('totalTimings', {}); - this.set('topicTime', 0); + this.setProperties({ + lastTick: new Date().getTime(), + lastScrolled: new Date().getTime(), + lastFlush: 0, + cancelled: false, + timings: {}, + totalTimings: {}, + topicTime: 0 + }); }, scrolled: function() { @@ -76,24 +81,23 @@ Discourse.ScreenTrack = Ember.Object.extend({ // We don't log anything unless we're logged in if (!Discourse.User.current()) return; - var newTimings = {}; - - // Update our total timings - var totalTimings = this.get('totalTimings'); + var newTimings = {}, + totalTimings = this.get('totalTimings'); _.each(this.get('timings'), function(timing,key) { if (!totalTimings[timing.postNumber]) totalTimings[timing.postNumber] = 0; - if (timing.time > 0 && totalTimings[timing.postNumber] < Discourse.ScreenTrack.MAX_TRACKING_TIME) { + if (timing.time > 0 && totalTimings[timing.postNumber] < MAX_TRACKING_TIME) { totalTimings[timing.postNumber] += timing.time; newTimings[timing.postNumber] = timing.time; } timing.time = 0; }); - var topicId = parseInt(this.get('topicId'), 10); - var highestSeen = 0; + var topicId = parseInt(this.get('topicId'), 10), + highestSeen = 0; + _.each(newTimings, function(time,postNumber) { highestSeen = Math.max(highestSeen, parseInt(postNumber, 10)); }); @@ -103,6 +107,7 @@ Discourse.ScreenTrack = Ember.Object.extend({ highestSeenByTopic[topicId] = highestSeen; Discourse.TopicTrackingState.current().updateSeen(topicId, highestSeen); } + if (!$.isEmptyObject(newTimings)) { Discourse.ajax('/topics/timings', { data: { @@ -126,7 +131,7 @@ Discourse.ScreenTrack = Ember.Object.extend({ // If the user hasn't scrolled the browser in a long time, stop tracking time read var sinceScrolled = new Date().getTime() - this.get('lastScrolled'); - if (sinceScrolled > Discourse.ScreenTrack.PAUSE_UNLESS_SCROLLED) { + if (sinceScrolled > PAUSE_UNLESS_SCROLLED) { this.reset(); return; } @@ -142,18 +147,16 @@ Discourse.ScreenTrack = Ember.Object.extend({ if (!Discourse.get("hasFocus")) return; this.set('topicTime', this.get('topicTime') + diff); - var docViewTop = $(window).scrollTop() + $('header').height(); - var docViewBottom = docViewTop + $(window).height(); + var docViewTop = $(window).scrollTop() + $('header').height(), + docViewBottom = docViewTop + $(window).height(); // TODO: Eyeline has a smarter more accurate function here. It's bad to do jQuery // in a model like component, so we should refactor this out later. - var screenTrack = this; _.each(this.get('timings'),function(timing,id) { - var $element, elemBottom, elemTop; - $element = $(id); + var $element = $(id); if ($element.length === 1) { - elemTop = $element.offset().top; - elemBottom = elemTop + $element.height(); + var elemTop = $element.offset().top, + elemBottom = elemTop + $element.height(); // If part of the element is on the screen, increase the counter if (((docViewTop <= elemTop && elemTop <= docViewBottom)) || ((docViewTop <= elemBottom && elemBottom <= docViewBottom))) { @@ -165,13 +168,5 @@ Discourse.ScreenTrack = Ember.Object.extend({ }); -Discourse.ScreenTrack.reopenClass(Discourse.Singleton, { - - // Don't send events if we haven't scrolled in a long time - PAUSE_UNLESS_SCROLLED: 1000 * 60 * 3, - - // After 6 minutes stop tracking read position on post - MAX_TRACKING_TIME: 1000 * 60 * 6 - -}); +Discourse.ScreenTrack.reopenClass(Discourse.Singleton); diff --git a/app/assets/javascripts/discourse/lib/url.js b/app/assets/javascripts/discourse/lib/url.js index c5532686a54..6071aace125 100644 --- a/app/assets/javascripts/discourse/lib/url.js +++ b/app/assets/javascripts/discourse/lib/url.js @@ -32,7 +32,7 @@ Discourse.URL = Em.Object.createWithMixins({ // which triggers a replaceState even though the topic hasn't fully loaded yet! Em.run.next(function() { var location = Discourse.URL.get('router.location'); - if (location.replaceURL) { location.replaceURL(path); } + if (location && location.replaceURL) { location.replaceURL(path); } }); } }, @@ -133,15 +133,21 @@ Discourse.URL = Em.Object.createWithMixins({ Discourse.URL.replaceState(path); var topicController = Discourse.__container__.lookup('controller:topic'), - opts = {}; + opts = {}, + postStream = topicController.get('postStream'); if (newMatches[3]) opts.nearPost = newMatches[3]; - var postStream = topicController.get('postStream'); + var closest = opts.nearPost || 1; + postStream.refresh(opts).then(function() { topicController.setProperties({ - currentPost: opts.nearPost || 1, - progressPosition: opts.nearPost || 1 + currentPost: closest, + progressPosition: closest, + highlightOnInsert: closest, + enteredAt: new Date().getTime().toString() }); + }).then(function() { + Discourse.TopicView.jumpToPost(closest); }); // Abort routing, we have replaced our state. diff --git a/app/assets/javascripts/discourse/mixins/scrolling.js b/app/assets/javascripts/discourse/mixins/scrolling.js index b106836ea12..def3220df78 100644 --- a/app/assets/javascripts/discourse/mixins/scrolling.js +++ b/app/assets/javascripts/discourse/mixins/scrolling.js @@ -7,6 +7,7 @@ @namespace Discourse @module Discourse **/ + Discourse.Scrolling = Em.Mixin.create({ /** @@ -18,20 +19,16 @@ Discourse.Scrolling = Em.Mixin.create({ bindScrolling: function(opts) { opts = opts || {debounce: 100}; - var scrollingMixin = this; - var onScrollMethod; + var self = this, + onScrollMethod = function(e) { + return Em.run.scheduleOnce('afterRender', self, 'scrolled'); + }; if (opts.debounce) { - onScrollMethod = Discourse.debounce(function() { - return scrollingMixin.scrolled(); - }, opts.debounce); - } else { - onScrollMethod = function() { - return scrollingMixin.scrolled(); - }; + onScrollMethod = Discourse.debounce(onScrollMethod, opts.debounce); } - Discourse.ScrollingDOMMethods.bindOnScroll(onScrollMethod); + Discourse.ScrollingDOMMethods.bindOnScroll(onScrollMethod, opts.name); }, /** @@ -39,8 +36,8 @@ Discourse.Scrolling = Em.Mixin.create({ @method unbindScrolling */ - unbindScrolling: function() { - Discourse.ScrollingDOMMethods.unbindOnScroll(); + unbindScrolling: function(name) { + Discourse.ScrollingDOMMethods.unbindOnScroll(name); } }); @@ -56,14 +53,16 @@ Discourse.Scrolling = Em.Mixin.create({ **/ Discourse.ScrollingDOMMethods = { - bindOnScroll: function(onScrollMethod) { - $(document).bind('touchmove.discourse', onScrollMethod); - $(window).bind('scroll.discourse', onScrollMethod); + bindOnScroll: function(onScrollMethod, name) { + name = name || 'default'; + $(document).bind('touchmove.discourse-' + name, onScrollMethod); + $(window).bind('scroll.discourse-' + name, onScrollMethod); }, - unbindOnScroll: function() { - $(window).unbind('scroll.discourse'); - $(document).unbind('touchmove.discourse'); + unbindOnScroll: function(name) { + name = name || 'default'; + $(window).unbind('scroll.discourse-' + name); + $(document).unbind('touchmove.discourse-' + name); } }; \ No newline at end of file diff --git a/app/assets/javascripts/discourse/models/post_stream.js b/app/assets/javascripts/discourse/models/post_stream.js index 53a03b4750c..5209a33c3e3 100644 --- a/app/assets/javascripts/discourse/models/post_stream.js +++ b/app/assets/javascripts/discourse/models/post_stream.js @@ -50,14 +50,32 @@ Discourse.PostStream = Em.Object.extend({ /** Have we loaded the first post in the stream? - @property firstPostLoaded + @property firstPostPresent **/ - firstPostLoaded: function() { + firstPostPresent: function() { if (!this.get('hasLoadedData')) { return false; } return !!this.get('posts').findProperty('id', this.get('firstPostId')); }.property('hasLoadedData', 'posts.[]', 'firstPostId'), - firstPostNotLoaded: Em.computed.not('firstPostLoaded'), + firstPostNotLoaded: Em.computed.not('firstPostPresent'), + + /** + The first post that we have loaded. Useful for checking to see if we should scroll upwards + + @property firstLoadedPost + **/ + firstLoadedPost: function() { + return _.first(this.get('posts')); + }.property('posts.@each'), + + /** + The last post we have loaded. Useful for checking to see if we should load more + + @property lastLoadedPost + **/ + lastLoadedPost: function() { + return _.last(this.get('posts')); + }.property('posts.@each'), /** Returns the id of the first post in the set @@ -80,14 +98,14 @@ Discourse.PostStream = Em.Object.extend({ /** Have we loaded the last post in the stream? - @property lastPostLoaded + @property loadedAllPosts **/ - lastPostLoaded: function() { + loadedAllPosts: function() { if (!this.get('hasLoadedData')) { return false; } return !!this.get('posts').findProperty('id', this.get('lastPostId')); }.property('hasLoadedData', 'posts.@each.id', 'lastPostId'), - lastPostNotLoaded: Em.computed.not('lastPostLoaded'), + lastPostNotLoaded: Em.computed.not('loadedAllPosts'), /** Returns a JS Object of current stream filter options. It should match the query @@ -163,18 +181,18 @@ Discourse.PostStream = Em.Object.extend({ **/ nextWindow: function() { // If we can't find the last post loaded, bail - var lastPost = _.last(this.get('posts')); - if (!lastPost) { return []; } + var lastLoadedPost = this.get('lastLoadedPost'); + if (!lastLoadedPost) { return []; } // Find the index of the last post loaded, if not found, bail var stream = this.get('stream'); - var lastIndex = this.indexOf(lastPost); + var lastIndex = this.indexOf(lastLoadedPost); if (lastIndex === -1) { return []; } if ((lastIndex + 1) >= this.get('filteredPostsCount')) { return []; } // find our window of posts return stream.slice(lastIndex+1, lastIndex+Discourse.SiteSettings.posts_per_page+1); - }.property('posts.@each', 'stream.@each'), + }.property('lastLoadedPost', 'stream.@each'), /** @@ -197,7 +215,7 @@ Discourse.PostStream = Em.Object.extend({ **/ toggleSummary: function() { this.toggleProperty('summary'); - this.refresh(); + return this.refresh(); }, /** @@ -227,39 +245,31 @@ Discourse.PostStream = Em.Object.extend({ @returns {Ember.Deferred} a promise that is resolved when the posts have been inserted into the stream. **/ refresh: function(opts) { + opts = opts || {}; opts.nearPost = parseInt(opts.nearPost, 10); - var topic = this.get('topic'); - var postStream = this; + var topic = this.get('topic'), + self = this; // Do we already have the post in our list of posts? Jump there. var postWeWant = this.get('posts').findProperty('post_number', opts.nearPost); - if (postWeWant) { - Discourse.TopicView.jumpToPost(topic.get('id'), opts.nearPost); - return Ember.RSVP.reject(); - } + if (postWeWant) { return Ember.RSVP.resolve(); } // TODO: if we have all the posts in the filter, don't go to the server for them. - postStream.set('loadingFilter', true); + self.set('loadingFilter', true); - opts = _.merge(opts, postStream.get('streamFilters')); + opts = _.merge(opts, self.get('streamFilters')); // Request a topicView return Discourse.PostStream.loadTopicView(topic.get('id'), opts).then(function (json) { topic.updateFromJson(json); - postStream.updateFromJson(json.post_stream); - postStream.setProperties({ loadingFilter: false, loaded: true }); + self.updateFromJson(json.post_stream); + self.setProperties({ loadingFilter: false, loaded: true }); - if (opts.nearPost) { - Discourse.TopicView.jumpToPost(topic.get('id'), opts.nearPost); - } else { - Discourse.TopicView.jumpToPost(topic.get('id'), 1); - } - - Discourse.URL.set('queryParams', postStream.get('streamFilters')); - }, function(result) { - postStream.errorLoading(result); + Discourse.URL.set('queryParams', self.get('streamFilters')); + }).fail(function(result) { + self.errorLoading(result); }); }, hasLoadedData: Em.computed.and('hasPosts', 'hasStream'), @@ -271,23 +281,23 @@ Discourse.PostStream = Em.Object.extend({ @returns {Ember.Deferred} a promise that's resolved when the posts have been added. **/ appendMore: function() { - var postStream = this; + var self = this; // Make sure we can append more posts - if (!postStream.get('canAppendMore')) { return Ember.RSVP.reject(); } + if (!self.get('canAppendMore')) { return Ember.RSVP.reject(); } - var postIds = postStream.get('nextWindow'); + var postIds = self.get('nextWindow'); if (Ember.isEmpty(postIds)) { return Ember.RSVP.reject(); } - postStream.set('loadingBelow', true); + self.set('loadingBelow', true); var stopLoading = function() { - postStream.set('loadingBelow', false); + self.set('loadingBelow', false); }; - return postStream.findPostsByIds(postIds).then(function(posts) { + return self.findPostsByIds(postIds).then(function(posts) { posts.forEach(function(p) { - postStream.appendPost(p); + self.appendPost(p); }); stopLoading(); }, stopLoading); @@ -349,7 +359,7 @@ Discourse.PostStream = Em.Object.extend({ }); // If we're at the end of the stream, add the post - if (this.get('lastPostLoaded')) { + if (this.get('loadedAllPosts')) { this.appendPost(post); } @@ -452,11 +462,11 @@ Discourse.PostStream = Em.Object.extend({ // We only trigger if there are no filters active if (!this.get('hasNoFilters')) { return; } - var lastPostLoaded = this.get('lastPostLoaded'); + var loadedAllPosts = this.get('loadedAllPosts'); if (this.get('stream').indexOf(postId) === -1) { this.get('stream').addObject(postId); - if (lastPostLoaded) { this.appendMore(); } + if (loadedAllPosts) { this.appendMore(); } } }, @@ -702,8 +712,8 @@ Discourse.PostStream.reopenClass({ }, loadTopicView: function(topicId, args) { - var opts = _.merge({}, args); - var url = Discourse.getURL("/t/") + topicId; + var opts = _.merge({}, args), + url = Discourse.getURL("/t/") + topicId; if (opts.nearPost) { url += "/" + opts.nearPost; } diff --git a/app/assets/javascripts/discourse/routes/topic_from_params_route.js b/app/assets/javascripts/discourse/routes/topic_from_params_route.js index cbded81cb9e..b940df2603b 100644 --- a/app/assets/javascripts/discourse/routes/topic_from_params_route.js +++ b/app/assets/javascripts/discourse/routes/topic_from_params_route.js @@ -7,16 +7,16 @@ @module Discourse **/ Discourse.TopicFromParamsRoute = Discourse.Route.extend({ + abc: 'asdfasdf', setupController: function(controller, params) { - params = params || {}; params.track_visit = true; - var topic = this.modelFor('topic'); - var postStream = topic.get('postStream'); + var topic = this.modelFor('topic'), + postStream = topic.get('postStream'), + queryParams = Discourse.URL.get('queryParams'); - var queryParams = Discourse.URL.get('queryParams'); if (queryParams) { // Set summary on the postStream if present postStream.set('summary', Em.get(queryParams, 'filter') === 'summary'); @@ -41,9 +41,12 @@ Discourse.TopicFromParamsRoute = Discourse.Route.extend({ topicController.setProperties({ currentPost: closest, progressPosition: closest, - enteredAt: new Date().getTime() + enteredAt: new Date().getTime().toString(), + highlightOnInsert: closest }); + Discourse.TopicView.jumpToPost(closest); + if (topic.present('draft')) { composerController.open({ draft: Discourse.Draft.getLocal(topic.get('draft_key'), topic.get('draft')), diff --git a/app/assets/javascripts/discourse/routes/topic_route.js b/app/assets/javascripts/discourse/routes/topic_route.js index c4031d9d63f..b0804416332 100644 --- a/app/assets/javascripts/discourse/routes/topic_route.js +++ b/app/assets/javascripts/discourse/routes/topic_route.js @@ -7,12 +7,12 @@ @module Discourse **/ Discourse.TopicRoute = Discourse.Route.extend({ + abc: 'def', redirect: function() { Discourse.redirectIfLoginRequired(this); }, actions: { // Modals that can pop up within a topic - showPosterExpansion: function(post) { this.controllerFor('posterExpansion').show(post); }, @@ -61,7 +61,17 @@ Discourse.TopicRoute = Discourse.Route.extend({ splitTopic: function() { Discourse.Route.showModal(this, 'splitTopic', this.modelFor('topic')); - } + }, + + // Use replaceState to update the URL once it changes + postChangedRoute: Discourse.debounce(function(currentPost) { + var topic = this.modelFor('topic'); + if (topic && currentPost) { + var postUrl = topic.get('url'); + if (currentPost > 1) { postUrl += "/" + currentPost; } + Discourse.URL.replaceState(postUrl); + } + }, 1000) }, diff --git a/app/assets/javascripts/discourse/templates/topic.js.handlebars b/app/assets/javascripts/discourse/templates/topic.js.handlebars index e9fd76a1a31..45464c27c95 100644 --- a/app/assets/javascripts/discourse/templates/topic.js.handlebars +++ b/app/assets/javascripts/discourse/templates/topic.js.handlebars @@ -2,7 +2,7 @@ {{#if postStream.loaded}} - {{#if postStream.firstPostLoaded}} + {{#if postStream.firstPostPresent}}