diff --git a/app/assets/javascripts/discourse/controllers/list_controller.js b/app/assets/javascripts/discourse/controllers/list_controller.js index 3a052f72c..761696d5d 100644 --- a/app/assets/javascripts/discourse/controllers/list_controller.js +++ b/app/assets/javascripts/discourse/controllers/list_controller.js @@ -36,6 +36,8 @@ Discourse.ListController = Discourse.Controller.extend({ var listController = this; this.set('loading', true); + var trackingState = Discourse.get('currentUser.userTrackingState'); + if (filterMode === 'categories') { return Discourse.CategoryList.list(filterMode).then(function(items) { listController.setProperties({ @@ -46,6 +48,10 @@ Discourse.ListController = Discourse.Controller.extend({ draft_key: items.draft_key, draft_sequence: items.draft_sequence }); + if(trackingState) { + trackingState.sync(items, filterMode); + trackingState.trackIncoming(filterMode); + } return items; }); } @@ -63,7 +69,11 @@ Discourse.ListController = Discourse.Controller.extend({ draft: items.draft, draft_key: items.draft_key, draft_sequence: items.draft_sequence - }) + }); + if(trackingState) { + trackingState.sync(items, filterMode); + trackingState.trackIncoming(filterMode); + } return items; }); }, diff --git a/app/assets/javascripts/discourse/controllers/list_topics_controller.js b/app/assets/javascripts/discourse/controllers/list_topics_controller.js index 36ec590c0..3cf53a7de 100644 --- a/app/assets/javascripts/discourse/controllers/list_topics_controller.js +++ b/app/assets/javascripts/discourse/controllers/list_topics_controller.js @@ -8,7 +8,6 @@ **/ Discourse.ListTopicsController = Discourse.ObjectController.extend({ needs: ['list', 'composer', 'modal'], - rankDetailsVisible: false, // If we're changing our channel @@ -16,25 +15,6 @@ Discourse.ListTopicsController = Discourse.ObjectController.extend({ latest: Ember.computed.equal('filter', 'latest'), - filterModeChanged: function() { - // Unsubscribe from a previous channel if necessary - var previousChannel = this.get('previousChannel'); - if (previousChannel) { - Discourse.MessageBus.unsubscribe("/" + previousChannel); - this.set('previousChannel', null); - } - - var filterMode = this.get('controllers.list.filterMode'); - if (!filterMode) return; - - var listTopicsController = this; - Discourse.MessageBus.subscribe("/" + filterMode, function(data) { - return listTopicsController.get('content').insert(data); - }); - this.set('previousChannel', filterMode); - - }.observes('controllers.list.filterMode'), - draftLoaded: function() { var draft = this.get('content.draft'); if (draft) { @@ -75,11 +55,11 @@ Discourse.ListTopicsController = Discourse.ObjectController.extend({ // Show newly inserted topics showInserted: function(e) { - // Move inserted into topics - this.get('topics').unshiftObjects(this.get('inserted')); + var tracker = Discourse.get('currentUser.userTrackingState'); - // Clear inserted - this.set('inserted', Em.A()); + // Move inserted into topics + this.get('content').loadBefore(tracker.get('newIncoming')); + tracker.resetTracking(); return false; }, diff --git a/app/assets/javascripts/discourse/models/topic_list.js b/app/assets/javascripts/discourse/models/topic_list.js index c9901fd7c..222db1c82 100644 --- a/app/assets/javascripts/discourse/models/topic_list.js +++ b/app/assets/javascripts/discourse/models/topic_list.js @@ -9,31 +9,36 @@ Discourse.TopicList = Discourse.Model.extend({ + forEachNew: function(topics, callback) { + var topicIds = []; + this.get('topics').each(function(t) { + topicIds[t.get('id')] = true; + }); + + topics.each(function(t) { + if(!topicIds[t.id]) { + callback(t); + } + }); + }, + loadMoreTopics: function() { var moreUrl, _this = this; if (moreUrl = this.get('more_topics_url')) { Discourse.URL.replaceState(Discourse.getURL("/") + (this.get('filter')) + "/more"); return Discourse.ajax({url: moreUrl}).then(function (result) { - var newTopics, topicIds, topics, topicsAdded = 0; + var newTopics, topics, topicsAdded = 0; if (result) { // the new topics loaded from the server newTopics = Discourse.TopicList.topicsFrom(result); - // the current topics - topics = _this.get('topics'); - // keeps track of the ids of the current topics - topicIds = []; - topics.each(function(t) { - topicIds[t.get('id')] = true; - }); - // add new topics to the list of current topics if not already present - newTopics.each(function(t) { - if (!topicIds[t.get('id')]) { - // highlight the first of the new topics so we can get a visual feedback - t.set('highlight', topicsAdded++ === 0); - return topics.pushObject(t); - } + topics = _this.get("topics"); + + _this.forEachNew(newTopics, function(t) { + t.set('highlight', topicsAdded++ === 0); + topics.pushObject(t); }); + _this.set('more_topics_url', result.topic_list.more_topics_url); Discourse.set('transient.topicsList', _this); } @@ -47,15 +52,35 @@ Discourse.TopicList = Discourse.Model.extend({ } }, - insert: function(json) { - var newTopic = Discourse.TopicList.decodeTopic(json); - newTopic.setProperties({ - unseen: true, - highlight: true - }); - this.get('inserted').unshiftObject(newTopic); - } + // loads topics with these ids "before" the current topics + loadBefore: function(topic_ids){ + // filter out any existing topics + + var _this = this; + var url = Discourse.getURL("/") + (this.get('filter')) + "?topic_ids=" + topic_ids.join(","); + return Discourse.ajax({url: url}).then(function (result) { + if (result) { + // the new topics loaded from the server + var newTopics = Discourse.TopicList.topicsFrom(result); + + var mapped = topic_ids.map(function(id){ + return newTopics.find(function(t){ return t.id === id; }); + }); + + var topicsAdded = 0; + var topics = _this.get("topics"); + + // add new topics to the list of current topics if not already present + _this.forEachNew(mapped, function(t) { + // highlight the first of the new topics so we can get a visual feedback + t.set('highlight', topicsAdded++ === 0); + topics.insertAt(0,t); + }); + Discourse.set('transient.topicsList', _this); + } + }); + } }); Discourse.TopicList.reopenClass({ diff --git a/app/assets/javascripts/discourse/models/user_tracking_state.js b/app/assets/javascripts/discourse/models/user_tracking_state.js index db6cbd413..a0297ad20 100644 --- a/app/assets/javascripts/discourse/models/user_tracking_state.js +++ b/app/assets/javascripts/discourse/models/user_tracking_state.js @@ -1,22 +1,110 @@ Discourse.UserTrackingState = Discourse.Model.extend({ + messageCount: 0, + init: function(){ this._super(); - this.states = {}; + this.unreadSequence = []; + this.newSequence = []; - var _this = this; - setTimeout(function(){ - console.log("YYYYYYYYYYY"); - _this.loadStates([{ - topic_id: 100, - last_read_post_number: null - }]); - _this.set('messageCount', 100); - }, 2000); + this.states = {}; }, establishChannels: function() { + var tracker = this; + var process = function(data){ + if (data.message_type === "delete") { + tracker.removeTopic(data.topic_id); + } + if (data.message_type === "new_topic") { + tracker.states["t" + data.topic_id] = data.payload; + tracker.notify(data); + } + + tracker.incrementMessageCount(); + }; + + Discourse.MessageBus.subscribe("/new", process); + Discourse.MessageBus.subscribe("/unread/" + Discourse.currentUser.id, process); + }, + + notify: function(data){ + if (!this.newIncoming) { return; } + + if ((this.filter === "latest" || this.filter === "new") && data.message_type === "new_topic" ) { + this.newIncoming.push(data.topic_id); + } + this.set("incomingCount", this.newIncoming.length); + }, + + resetTracking: function(){ + this.newIncoming = []; + this.set("incomingCount", 0); + }, + + // track how many new topics came for this filter + trackIncoming: function(filter) { + this.newIncoming = []; + this.filter = filter; + this.set("incomingCount", 0); + }, + + hasIncoming: function(){ + var count = this.get('incomingCount'); + return count && count > 0; + }.property('incomingCount'), + + removeTopic: function(topic_id) { + delete this.states["t" + topic_id]; + }, + + sync: function(list, filter){ + var tracker = this; + + if(filter === "new" && !list.more_topics_url){ + // scrub all new rows and reload from list + $.each(this.states, function(){ + if(this.last_read_post_number === null) { + tracker.removeTopic(this.topic_id); + } + }); + } + + if(filter === "unread" && !list.more_topics_url){ + // scrub all new rows and reload from list + $.each(this.states, function(){ + if(this.last_read_post_number !== null) { + tracker.removeTopic(this.topic_id); + } + }); + } + + $.each(list.topics, function(){ + var row = {}; + var topic = this; + + row.topic_id = topic.id; + if(topic.unseen) { + row.last_read_post_number = null; + } else { + row.last_read_post_number = topic.last_read_post_number; + } + row.highest_post_number = topic.highest_post_number; + if (topic.category) { + row.category_name = topic.category.name; + } + + if (row.last_read_post_number === null || row.highest_post_number > row.last_read_post_number) { + tracker.states["t" + topic.id] = row; + } + }); + + this.incrementMessageCount(); + }, + + incrementMessageCount: function() { + this.set("messageCount", this.get("messageCount") + 1); }, countNew: function(){ diff --git a/app/assets/javascripts/discourse/templates/list/topics.js.handlebars b/app/assets/javascripts/discourse/templates/list/topics.js.handlebars index 8d0bd897f..7ad5eaa80 100644 --- a/app/assets/javascripts/discourse/templates/list/topics.js.handlebars +++ b/app/assets/javascripts/discourse/templates/list/topics.js.handlebars @@ -1,7 +1,7 @@ {{#unless loading}} {{#if loaded}}
- {{#if topics.length}} + {{#if view.showTable}} {{#if canViewRankDetails}}