diff --git a/common/static/common/js/discussion/views/discussion_thread_list_view.js b/common/static/common/js/discussion/views/discussion_thread_list_view.js index ef9cf793a2..e34025c5a3 100644 --- a/common/static/common/js/discussion/views/discussion_thread_list_view.js +++ b/common/static/common/js/discussion/views/discussion_thread_list_view.js @@ -93,6 +93,7 @@ this.courseSettings = options.courseSettings; this.hideRefineBar = options.hideRefineBar; this.supportsActiveThread = options.supportsActiveThread; + this.profilePage = options.profilePage || false; this.displayedCollection = new Discussion(this.collection.models, { pages: this.collection.pages }); @@ -335,7 +336,14 @@ DiscussionThreadListView.prototype.renderThread = function(thread) { var threadCommentCount = thread.get('comments_count'), threadUnreadCommentCount = thread.get('unread_comments_count'), - neverRead = !thread.get('read') && threadUnreadCommentCount === threadCommentCount, + // @TODO: On the profile page, thread read state for the viewing user is not accessible via the API. + // In this case, neverRead is set to false regardless of read state returned by the API. + // Fix this when the Discussions API can support this query. + neverRead = ( + !thread.get('read') && + threadUnreadCommentCount === threadCommentCount && + !this.profilePage + ), threadPreview = this.containsMarkup(thread.get('body')) ? '' : thread.get('body'), context = _.extend( { @@ -344,7 +352,8 @@ threadPreview: threadPreview, showThreadPreview: this.showThreadPreview }, - thread.toJSON() + thread.toJSON(), + this.profilePage ? {unread_comments_count: 0} : {} // See comment above about profile page ); return $(this.threadListItemTemplate(context).toString()); }; diff --git a/common/static/common/js/spec/discussion/view/discussion_thread_list_view_spec.js b/common/static/common/js/spec/discussion/view/discussion_thread_list_view_spec.js index 50f5048ab8..c2aa40328e 100644 --- a/common/static/common/js/spec/discussion/view/discussion_thread_list_view_spec.js +++ b/common/static/common/js/spec/discussion/view/discussion_thread_list_view_spec.js @@ -169,6 +169,7 @@ }); return this.view.render(); }); + setupAjax = function(callback) { return $.ajax.and.callFake(function(params) { if (callback) { @@ -185,19 +186,27 @@ }; }); }; + renderSingleThreadWithProps = function(props) { return makeView(new Discussion([new Thread(DiscussionViewSpecHelper.makeThreadWithProps(props))])).render(); }; - makeView = function(discussion) { - return new DiscussionThreadListView({ - el: $('#fixture-element'), - collection: discussion, - showThreadPreview: true, - courseSettings: new DiscussionCourseSettings({ - is_cohorted: true - }) - }); + + makeView = function(discussion, props) { + return new DiscussionThreadListView( + _.extend( + { + el: $('#fixture-element'), + collection: discussion, + showThreadPreview: true, + courseSettings: new DiscussionCourseSettings({ + is_cohorted: true + }) + }, + props + ) + ); }; + expectFilter = function(filterVal) { return $.ajax.and.callFake(function(params) { _.each(['unread', 'unanswered', 'flagged'], function(paramName) { @@ -681,5 +690,47 @@ expect(view.$el.find('.thread-preview-body').length).toEqual(0); }); }); + + describe('read/unread state', function() { + it('adds never-read class to unread threads', function() { + var unreads = this.threads.filter(function(thread) { + return !thread.read && thread.unread_comments_count === thread.comments_count; + }).length; + + this.view = makeView(new Discussion(this.threads)); + this.view.render(); + expect(this.view.$('.never-read').length).toEqual(unreads); + }); + + it('shows a "x new" message for threads that are read, but have unread comments', function() { + var unreadThread = this.threads.filter(function(thread) { + return thread.read && thread.unread_comments_count !== thread.comments_count; + })[0], + newCommentsOnUnreadThread = unreadThread.unread_comments_count; + + this.view = makeView(new Discussion(this.threads)); + this.view.render(); + expect( + this.view.$('.forum-nav-thread-unread-comments-count') + .first() + .text() + .trim() + ).toEqual(newCommentsOnUnreadThread + ' new'); + }); + + it('should display every thread as read if profilePage is passed to the constructor', function() { + // @TODO: This is temporary, see comment in DiscussionThreadListView.prototype.renderThread + this.view = makeView(new Discussion(this.threads), {profilePage: true}); + this.view.render(); + expect(this.view.$('.never-read').length).toEqual(0); + }); + + it('does not show the "x new" indicator for any thread if profilePage is passed', function() { + // @TODO: This is temporary, see comment in DiscussionThreadListView.prototype.renderThread + this.view = makeView(new Discussion(this.threads), {profilePage: true}); + this.view.render(); + expect(this.view.$('.forum-nav-thread-unread-comments-count').length).toEqual(0); + }); + }); }); }).call(this); diff --git a/lms/djangoapps/discussion/static/discussion/js/views/discussion_user_profile_view.js b/lms/djangoapps/discussion/static/discussion/js/views/discussion_user_profile_view.js index 7c3e199ecd..f9c7de0175 100644 --- a/lms/djangoapps/discussion/static/discussion/js/views/discussion_user_profile_view.js +++ b/lms/djangoapps/discussion/static/discussion/js/views/discussion_user_profile_view.js @@ -39,7 +39,10 @@ collection: this.discussion, el: this.$('.inline-threads'), courseSettings: this.courseSettings, - hideRefineBar: true // TODO: re-enable the search/filter bar when it works correctly + hideRefineBar: true, // TODO: re-enable the search/filter bar when it works correctly + // TODO: remove. Used temporarily to disable read state on profile page. See comment in + // discussion_thread_list_view.js / DiscussionThreadListView.prototype.renderThread + profilePage: true }).render(); this.discussionThreadListView.on('thread:selected', _.bind(this.navigateToThread, this));