Commit f851a3a2 authored by Nick Stenning's avatar Nick Stenning Committed by Randall Leeds

Move most of the logic out of the thread template

This moves almost all of the logic determining what bits of the thread
to render when out from the template and into the controller where it
belongs.
parent 22d2bb4c
......@@ -42,33 +42,163 @@ describe 'h:directives.thread', ->
controller.toggleCollapsed(false)
assert.isFalse(controller.collapsed)
describe '#shouldShowReply', ->
describe '#shouldShowAsReply', ->
controller = null
count = null
beforeEach ->
controller = createController()
count = sinon.stub().returns(0)
controller.counter = {count: count}
it 'is true by default', ->
assert.isTrue(controller.shouldShowAsReply())
it 'is false when the parent thread is collapsed', ->
controller.parent = {collapsed: true}
assert.isFalse(controller.shouldShowAsReply())
describe 'when the thread contains edits', ->
beforeEach ->
count.withArgs('edit').returns(1)
it 'is true when the thread has no parent', ->
assert.isTrue(controller.shouldShowAsReply())
it 'is true when the parent thread is not collapsed', ->
controller.parent = {collapsed: false}
assert.isTrue(controller.shouldShowAsReply())
it 'is true when the parent thread is collapsed', ->
controller.parent = {collapsed: true}
assert.isTrue(controller.shouldShowAsReply())
describe 'when the thread filter is active', ->
beforeEach ->
controller.filter = {active: -> true}
it 'is false when there are no matches in the thread', ->
assert.isFalse(controller.shouldShowAsReply())
it 'is true when there are matches in the thread', ->
count.withArgs('match').returns(1)
assert.isTrue(controller.shouldShowAsReply())
describe '#shouldShowNumReplies', ->
count = null
controller = null
filterActive = false
beforeEach ->
controller = createController()
count = sinon.stub()
controller.counter = {count: count}
controller.filter = {active: -> filterActive}
describe 'when not filtered', ->
it 'shows the reply if the thread has children', ->
count.withArgs('message').returns(1)
assert.isTrue(controller.shouldShowReply(count, false))
assert.isTrue(controller.shouldShowNumReplies())
it 'does not show the reply if the thread has no children', ->
count.withArgs('message').returns(0)
assert.isFalse(controller.shouldShowReply(count, false))
assert.isFalse(controller.shouldShowNumReplies())
describe 'when filtered with children', ->
beforeEach ->
filterActive = true
it 'shows the reply', ->
count.withArgs('match').returns(1)
count.withArgs('message').returns(1)
assert.isTrue(controller.shouldShowReply(count, true))
assert.isTrue(controller.shouldShowNumReplies())
it 'does not show the reply if the message count does not match the match count', ->
count.withArgs('match').returns(0)
count.withArgs('message').returns(1)
assert.isFalse(controller.shouldShowReply(count, true))
assert.isFalse(controller.shouldShowNumReplies())
describe '#numReplies', ->
controller = null
beforeEach ->
controller = createController()
it 'returns zero when there is no counter', ->
assert.equal(controller.numReplies(), 0)
it 'returns one less than the number of messages in the thread when there is a counter', ->
count = sinon.stub()
count.withArgs('message').returns(5)
controller.counter = {count: count}
assert.equal(controller.numReplies(), 4)
describe '#shouldShowLoadMore', ->
controller = null
beforeEach ->
controller = createController()
describe 'when the thread filter is not active', ->
it 'is false with an empty container', ->
assert.isFalse(controller.shouldShowLoadMore())
it 'is false when the container contains an annotation', ->
controller.container = {message: {id: 123}}
assert.isFalse(controller.shouldShowLoadMore())
describe 'when the thread filter is active', ->
beforeEach ->
controller.filter = {active: -> true}
it 'is false with an empty container', ->
assert.isFalse(controller.shouldShowLoadMore())
it 'is true when the container contains an annotation', ->
controller.container = {message: {id: 123}}
assert.isTrue(controller.shouldShowLoadMore())
describe '#loadMore', ->
controller = null
beforeEach ->
controller = createController()
it 'uncollapses the thread', ->
sinon.spy(controller, 'toggleCollapsed')
controller.loadMore()
assert.calledWith(controller.toggleCollapsed, false)
it 'uncollapses all the ancestors of the thread', ->
grandmother = {toggleCollapsed: sinon.stub()}
mother = {toggleCollapsed: sinon.stub()}
controller.parent = mother
controller.parent.parent = grandmother
controller.loadMore()
assert.calledWith(mother.toggleCollapsed, false)
assert.calledWith(grandmother.toggleCollapsed, false)
it 'deactivates the thread filter when present', ->
controller.filter = {active: sinon.stub()}
controller.loadMore()
assert.calledWith(controller.filter.active, false)
describe '#matchesFilter', ->
controller = null
beforeEach ->
controller = createController()
it 'is true by default', ->
assert.isTrue(controller.matchesFilter())
it 'checks with the thread filter to see if the root annotation matches', ->
check = sinon.stub().returns(false)
controller.filter = {check: check}
controller.container = {}
assert.isFalse(controller.matchesFilter())
assert.calledWith(check, controller.container)
describe '.thread', ->
......
......@@ -14,12 +14,17 @@ ThreadController = [
->
@container = null
@collapsed = true
@parent = null
@counter = null
@filter = null
###*
# @ngdoc method
# @name thread.ThreadController#toggleCollapsed
# @description
# Toggle the collapsed property.
# Toggle whether or not the replies to this thread are hidden by default.
# Note that the visibility of replies is also dependent on the state of the
# thread filter, if present.
###
this.toggleCollapsed = (value) ->
@collapsed = if value?
......@@ -29,17 +34,114 @@ ThreadController = [
###*
# @ngdoc method
# @name thread.ThreadController#shouldShowReply
# @name thread.ThreadController#shouldShowAsReply
# @description
# Determines if the reply counter should be rendered. Requires the
# `count` directive to be passed and a boolean that indicates whether
# the thread is currently filtered.
# Return a boolean indicating whether this thread should be shown if it is
# being rendered as a reply to another annotation.
###
this.shouldShowReply = (count, isFilterActive) ->
hasChildren = count('message') > 0
hasFilterMatch = !isFilterActive || count('message') == count('match')
this.shouldShowAsReply = ->
shouldShowUnfiltered = not @parent?.collapsed
shouldShowFiltered = this._count('match') > 0
# We always show replies that contain an editor
if this._count('edit') > 0
return true
if this._isFilterActive()
return shouldShowFiltered
else
return shouldShowUnfiltered
###*
# @ngdoc method
# @name thread.ThreadController#shouldShowNumReplies
# @description
# Returns a boolean indicating whether the reply count should be rendered
# for the annotation at the root of this thread.
###
this.shouldShowNumReplies = ->
hasChildren = this._count('message') > 0
allRepliesShown = this._count('message') == this._count('match')
hasFilterMatch = !this._isFilterActive() || allRepliesShown
hasChildren && hasFilterMatch
###*
# @ngdoc method
# @name thread.ThreadController#numReplies
# @description
# Returns the cumulative number of replies to the annotation at the root of
# this thread.
###
this.numReplies = ->
if @counter
this._count('message') - 1
else
0
###*
# @ngdoc method
# @name thread.ThreadController#shouldShowLoadMore
# @description
# Return a boolean indicating whether the "load more" link should be shown
# for the annotation at the root of this thread. The "load more" link can be
# shown when the thread filter is active (although it may not be visible if
# no replies are hidden in this thread).
###
this.shouldShowLoadMore = ->
this.container?.message?.id? and this._isFilterActive()
###*
# @ngdoc method
# @name thread.ThreadController#numLoadMore
# @description
# Returns the number of replies in this thread which are currently hidden as
# a result of the thread filter.
###
this.numLoadMore = ->
this._count('message') - this._count('match')
###*
# @ngdoc method
# @name thread.ThreadController#loadMore
# @description
# Makes visible any replies in this thread which have been hidden by the
# thread filter.
###
this.loadMore = ->
# If we want to show the rest of the replies in the thread, we need to
# uncollapse all parent threads.
ctrl = this
while ctrl
ctrl.toggleCollapsed(false)
ctrl = ctrl.parent
# Deactivate the thread filter for this thread.
@filter?.active(false)
###*
# @ngdoc method
# @name thread.ThreadController#matchesFilter
# @description
# Returns a boolean indicating whether the annotation at the root of this
# thread is marked as a match by the current thread filter. If there is no
# thread filter attached to this thread, it will return true.
###
this.matchesFilter = ->
if not @filter
return true
return @filter.check(@container)
this._isFilterActive = ->
if @filter
@filter.active()
else
false
this._count = (name) ->
if @counter
@counter.count(name)
else
0
this
]
......@@ -85,7 +187,13 @@ isHiddenThread = (elem) ->
thread = [
'$parse', '$window', 'pulse', 'render',
($parse, $window, pulse, render) ->
linkFn = (scope, elem, attrs, [ctrl, counter]) ->
linkFn = (scope, elem, attrs, [ctrl, counter, filter]) ->
# We would ideally use require for this, but searching parents only for a
# controller is a feature of Angular 1.3 and above only.
ctrl.parent = elem.parent().controller('thread')
ctrl.counter = counter
ctrl.filter = filter
# Toggle collapse on click.
elem.on 'click', (event) ->
......@@ -116,8 +224,6 @@ thread = [
if counter?
counter.count 'message', 1
scope.$on '$destroy', -> counter.count 'message', -1
else
scope.count = -> 1
# Flash the thread when any child annotations are updated.
scope.$on 'annotationUpdate', (event) ->
......@@ -144,7 +250,7 @@ thread = [
controller: 'ThreadController'
controllerAs: 'vm'
link: linkFn
require: ['thread', '?^deepCount']
require: ['thread', '?^deepCount', '?^threadFilter']
scope: true
]
......
......@@ -13,22 +13,25 @@
annotation="vm.container.message"
annotation-embedded="{{isEmbedded}}"
ng-if="vm.container.message"
ng-show="threadFilter.check(vm.container)">
ng-show="vm.matchesFilter()">
</article>
<!-- Reply count -->
<div class="thread-reply" ng-show="vm.shouldShowReply(count, threadFilter.active())">
<div class="thread-reply"
ng-show="vm.shouldShowNumReplies()">
<a class="reply-count small"
href=""
ng-pluralize count="count('message') - 1"
ng-pluralize
count="vm.numReplies()"
when="{'0': '', one: '1 reply', other: '{} replies'}"></a>
</div>
<div class="thread-load-more" ng-show="vm.container.message.id && threadFilter.active()">
<div class="thread-load-more" ng-show="vm.shouldShowLoadMore()">
<a class="load-more small"
href=""
ng-click="threadFilter.active(false)"
ng-pluralize count="count('message') - count('match')"
ng-click="vm.loadMore()"
ng-pluralize
count="vm.numLoadMore()"
when="{'0': '',
one: 'View one more in conversation',
other: 'View {} more in conversation'}"
......@@ -36,14 +39,13 @@
</div>
<!-- Replies -->
<ul class="thread-replies" ng-hide="vm.collapsed">
<ul class="thread-replies">
<li class="thread"
deep-count="count"
thread="child" thread-filter
ng-include="'thread.html'"
ng-init="child.message.id || threadFilter.active(false)"
ng-repeat="child in vm.container.children
| orderBy : 'message.updated' : true"
ng-show="count('edit') || count('match') || !threadFilter.active()">
ng-show="vm.shouldShowAsReply()">
</li>
</ul>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment