Create like and watch buttons in project detail pages
parent
d1349c4272
commit
f52935f8c9
|
@ -0,0 +1,40 @@
|
|||
class LikeProjectButtonController
|
||||
@.$inject = [
|
||||
"$tgConfirm"
|
||||
"tgLikeProjectButtonService"
|
||||
]
|
||||
|
||||
constructor: (@confirm, @likeButtonService)->
|
||||
@.isMouseOver = false
|
||||
@.loading = false
|
||||
|
||||
showTextWhenMouseIsOver: ->
|
||||
@.isMouseOver = true
|
||||
|
||||
showTextWhenMouseIsLeave: ->
|
||||
@.isMouseOver = false
|
||||
|
||||
toggleLike: ->
|
||||
@.loading = true
|
||||
|
||||
if not @.project.get("is_fan")
|
||||
promise = @._like()
|
||||
else
|
||||
promise = @._unlike()
|
||||
|
||||
promise.finally () => @.loading = false
|
||||
|
||||
return promise
|
||||
|
||||
_like: ->
|
||||
return @likeButtonService.like(@.project.get('id'))
|
||||
.then =>
|
||||
@.showTextWhenMouseIsLeave()
|
||||
.catch =>
|
||||
@confirm.notify("error")
|
||||
|
||||
_unlike: ->
|
||||
return @likeButtonService.unlike(@.project.get('id')).catch =>
|
||||
@confirm.notify("error")
|
||||
|
||||
angular.module("taigaProjects").controller("LikeProjectButton", LikeProjectButtonController)
|
|
@ -0,0 +1,115 @@
|
|||
describe "LikeProjectButton", ->
|
||||
$provide = null
|
||||
$controller = null
|
||||
mocks = {}
|
||||
|
||||
_mockTgConfirm = ->
|
||||
mocks.tgConfirm = {
|
||||
notify: sinon.stub()
|
||||
}
|
||||
|
||||
$provide.value("$tgConfirm", mocks.tgConfirm)
|
||||
|
||||
_mockTgLikeProjectButton = ->
|
||||
mocks.tgLikeProjectButton = {
|
||||
like: sinon.stub(),
|
||||
unlike: sinon.stub()
|
||||
}
|
||||
|
||||
$provide.value("tgLikeProjectButtonService", mocks.tgLikeProjectButton)
|
||||
|
||||
_mocks = ->
|
||||
module (_$provide_) ->
|
||||
$provide = _$provide_
|
||||
|
||||
_mockTgConfirm()
|
||||
_mockTgLikeProjectButton()
|
||||
|
||||
return null
|
||||
|
||||
_inject = ->
|
||||
inject (_$controller_) ->
|
||||
$controller = _$controller_
|
||||
|
||||
_setup = ->
|
||||
_mocks()
|
||||
_inject()
|
||||
|
||||
beforeEach ->
|
||||
module "taigaProjects"
|
||||
|
||||
_setup()
|
||||
|
||||
it "toggleLike false -> true", (done) ->
|
||||
project = Immutable.fromJS({
|
||||
id: 3,
|
||||
is_fan: false
|
||||
})
|
||||
|
||||
ctrl = $controller("LikeProjectButton")
|
||||
ctrl.project = project
|
||||
|
||||
mocks.tgLikeProjectButton.like = sinon.stub().promise()
|
||||
|
||||
promise = ctrl.toggleLike()
|
||||
|
||||
expect(ctrl.loading).to.be.true;
|
||||
|
||||
mocks.tgLikeProjectButton.like.withArgs(project.get('id')).resolve()
|
||||
|
||||
promise.finally () ->
|
||||
expect(mocks.tgLikeProjectButton.like).to.be.calledOnce
|
||||
expect(ctrl.loading).to.be.false;
|
||||
|
||||
done()
|
||||
|
||||
it "toggleLike false -> true, notify error", (done) ->
|
||||
project = Immutable.fromJS({
|
||||
id: 3,
|
||||
is_fan: false
|
||||
})
|
||||
|
||||
ctrl = $controller("LikeProjectButton")
|
||||
ctrl.project = project
|
||||
|
||||
mocks.tgLikeProjectButton.like.withArgs(project.get('id')).promise().reject()
|
||||
|
||||
ctrl.toggleLike().finally () ->
|
||||
expect(mocks.tgConfirm.notify.withArgs("error")).to.be.calledOnce
|
||||
done()
|
||||
|
||||
it "toggleLike true -> false", (done) ->
|
||||
project = Immutable.fromJS({
|
||||
is_fan: true
|
||||
})
|
||||
|
||||
ctrl = $controller("LikeProjectButton")
|
||||
ctrl.project = project
|
||||
|
||||
mocks.tgLikeProjectButton.unlike = sinon.stub().promise()
|
||||
|
||||
promise = ctrl.toggleLike()
|
||||
|
||||
expect(ctrl.loading).to.be.true;
|
||||
|
||||
mocks.tgLikeProjectButton.unlike.withArgs(project.get('id')).resolve()
|
||||
|
||||
promise.finally () ->
|
||||
expect(mocks.tgLikeProjectButton.unlike).to.be.calledOnce
|
||||
expect(ctrl.loading).to.be.false;
|
||||
|
||||
done()
|
||||
|
||||
it "toggleLike true -> false, notify error", (done) ->
|
||||
project = Immutable.fromJS({
|
||||
is_fan: true
|
||||
})
|
||||
|
||||
ctrl = $controller("LikeProjectButton")
|
||||
ctrl.project = project
|
||||
|
||||
mocks.tgLikeProjectButton.unlike.withArgs(project.get('id')).promise().reject()
|
||||
|
||||
ctrl.toggleLike().finally () ->
|
||||
expect(mocks.tgConfirm.notify.withArgs("error")).to.be.calledOnce
|
||||
done()
|
|
@ -0,0 +1,12 @@
|
|||
LikeProjectButtonDirective = ->
|
||||
return {
|
||||
scope: {}
|
||||
controller: "LikeProjectButton",
|
||||
bindToController: {
|
||||
project: '='
|
||||
}
|
||||
controllerAs: "vm",
|
||||
templateUrl: "projects/components/like-project-button/like-project-button.html",
|
||||
}
|
||||
|
||||
angular.module("taigaProjects").directive("tgLikeProjectButton", LikeProjectButtonDirective)
|
|
@ -0,0 +1,29 @@
|
|||
a.track-button.like-button.like-container(
|
||||
href="",
|
||||
title="{{ 'PROJECT.LIKE_BUTTON.BUTTON_TITLE' | translate }}"
|
||||
ng-click="vm.toggleLike()"
|
||||
ng-class="{'active':vm.project.get('is_fan'), 'is-hover':vm.project.get('is_fan') && vm.isMouseOver}"
|
||||
ng-mouseover="vm.showTextWhenMouseIsOver()"
|
||||
ng-mouseleave="vm.showTextWhenMouseIsLeave()"
|
||||
)
|
||||
span.track-inner
|
||||
span.track-icon
|
||||
include ../../../../svg/like.svg
|
||||
span(
|
||||
ng-if="!vm.project.get('is_fan')"
|
||||
translate="PROJECT.LIKE_BUTTON.LIKE"
|
||||
)
|
||||
span(
|
||||
ng-if="vm.project.get('is_fan') && !vm.isMouseOver"
|
||||
translate="PROJECT.LIKE_BUTTON.LIKED"
|
||||
)
|
||||
span(
|
||||
ng-if="vm.project.get('is_fan') && vm.isMouseOver"
|
||||
translate="PROJECT.LIKE_BUTTON.UNLIKE"
|
||||
)
|
||||
|
||||
span.track-button-counter(
|
||||
title="{{ 'PROJECT.LIKE_BUTTON.COUNTER_TITLE'|translate:{total:vm.project.get(\"total_fans\")||0}:'messageformat' }}",
|
||||
tg-loading="vm.loading"
|
||||
)
|
||||
| {{ vm.project.get('total_fans') }}
|
|
@ -0,0 +1,52 @@
|
|||
taiga = @.taiga
|
||||
|
||||
class LikeProjectButtonService extends taiga.Service
|
||||
@.$inject = ["tgResources", "tgCurrentUserService", "tgProjectService"]
|
||||
|
||||
constructor: (@rs, @currentUserService, @projectService) ->
|
||||
|
||||
_getProjectIndex: (projectId) ->
|
||||
return @currentUserService.projects
|
||||
.get('all')
|
||||
.findIndex (project) -> project.get('id') == projectId
|
||||
|
||||
_updateProjects: (projectId, isFan) ->
|
||||
projectIndex = @._getProjectIndex(projectId)
|
||||
projects = @currentUserService.projects
|
||||
.get('all')
|
||||
.update projectIndex, (project) ->
|
||||
|
||||
totalFans = project.get("total_fans")
|
||||
|
||||
if isFan then totalFans++ else totalFans--
|
||||
|
||||
return project.merge({
|
||||
is_fan: isFan,
|
||||
total_fans: totalFans
|
||||
})
|
||||
|
||||
@currentUserService.setProjects(projects)
|
||||
|
||||
_updateCurrentProject: (isFan) ->
|
||||
totalFans = @projectService.project.get("total_fans")
|
||||
|
||||
if isFan then totalFans++ else totalFans--
|
||||
|
||||
project = @projectService.project.merge({
|
||||
is_fan: isFan,
|
||||
total_fans: totalFans
|
||||
})
|
||||
|
||||
@projectService.setProject(project)
|
||||
|
||||
like: (projectId) ->
|
||||
return @rs.projects.likeProject(projectId).then =>
|
||||
@._updateProjects(projectId, true)
|
||||
@._updateCurrentProject(true)
|
||||
|
||||
unlike: (projectId) ->
|
||||
return @rs.projects.unlikeProject(projectId).then =>
|
||||
@._updateProjects(projectId, false)
|
||||
@._updateCurrentProject(false)
|
||||
|
||||
angular.module("taigaProjects").service("tgLikeProjectButtonService", LikeProjectButtonService)
|
|
@ -0,0 +1,132 @@
|
|||
describe "tgLikeProjectButtonService", ->
|
||||
likeButtonService = null
|
||||
provide = null
|
||||
mocks = {}
|
||||
|
||||
_mockTgResources = () ->
|
||||
mocks.tgResources = {
|
||||
projects: {
|
||||
likeProject: sinon.stub(),
|
||||
unlikeProject: sinon.stub()
|
||||
}
|
||||
}
|
||||
|
||||
provide.value "tgResources", mocks.tgResources
|
||||
|
||||
_mockTgCurrentUserService = () ->
|
||||
mocks.tgCurrentUserService = {
|
||||
setProjects: sinon.stub(),
|
||||
projects: Immutable.fromJS({
|
||||
all: [
|
||||
{
|
||||
id: 4,
|
||||
total_fans: 2,
|
||||
is_fan: false
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
total_fans: 7,
|
||||
is_fan: true
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
total_fans: 4,
|
||||
is_fan: true
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
provide.value "tgCurrentUserService", mocks.tgCurrentUserService
|
||||
|
||||
_mockTgProjectService = () ->
|
||||
mocks.tgProjectService = {
|
||||
setProject: sinon.stub()
|
||||
}
|
||||
|
||||
provide.value "tgProjectService", mocks.tgProjectService
|
||||
|
||||
_inject = (callback) ->
|
||||
inject (_tgLikeProjectButtonService_) ->
|
||||
likeButtonService = _tgLikeProjectButtonService_
|
||||
callback() if callback
|
||||
|
||||
_mocks = () ->
|
||||
module ($provide) ->
|
||||
provide = $provide
|
||||
_mockTgResources()
|
||||
_mockTgCurrentUserService()
|
||||
_mockTgProjectService()
|
||||
return null
|
||||
|
||||
_setup = ->
|
||||
_mocks()
|
||||
|
||||
beforeEach ->
|
||||
module "taigaProjects"
|
||||
_setup()
|
||||
_inject()
|
||||
|
||||
it "like", (done) ->
|
||||
projectId = 4
|
||||
|
||||
mocks.tgResources.projects.likeProject.withArgs(projectId).promise().resolve()
|
||||
|
||||
newProject = {
|
||||
id: 4,
|
||||
total_fans: 3,
|
||||
is_fan: true
|
||||
}
|
||||
|
||||
mocks.tgProjectService.project = mocks.tgCurrentUserService.projects.getIn(['all', 0])
|
||||
|
||||
userServiceCheckImmutable = sinon.match ((immutable) ->
|
||||
immutable = immutable.toJS()
|
||||
|
||||
return _.isEqual(immutable[0], newProject)
|
||||
), 'userServiceCheckImmutable'
|
||||
|
||||
projectServiceCheckImmutable = sinon.match ((immutable) ->
|
||||
immutable = immutable.toJS()
|
||||
|
||||
return _.isEqual(immutable, newProject)
|
||||
), 'projectServiceCheckImmutable'
|
||||
|
||||
|
||||
likeButtonService.like(projectId).finally () ->
|
||||
expect(mocks.tgCurrentUserService.setProjects).to.have.been.calledWith(userServiceCheckImmutable)
|
||||
expect(mocks.tgProjectService.setProject).to.have.been.calledWith(projectServiceCheckImmutable)
|
||||
|
||||
done()
|
||||
|
||||
it "unlike", (done) ->
|
||||
projectId = 5
|
||||
|
||||
mocks.tgResources.projects.unlikeProject.withArgs(projectId).promise().resolve()
|
||||
|
||||
newProject = {
|
||||
id: 5,
|
||||
total_fans: 6,
|
||||
is_fan: false
|
||||
}
|
||||
|
||||
mocks.tgProjectService.project = mocks.tgCurrentUserService.projects.getIn(['all', 1])
|
||||
|
||||
userServiceCheckImmutable = sinon.match ((immutable) ->
|
||||
immutable = immutable.toJS()
|
||||
|
||||
return _.isEqual(immutable[1], newProject)
|
||||
), 'userServiceCheckImmutable'
|
||||
|
||||
projectServiceCheckImmutable = sinon.match ((immutable) ->
|
||||
immutable = immutable.toJS()
|
||||
|
||||
return _.isEqual(immutable, newProject)
|
||||
), 'projectServiceCheckImmutable'
|
||||
|
||||
|
||||
likeButtonService.unlike(projectId).finally () ->
|
||||
expect(mocks.tgCurrentUserService.setProjects).to.have.been.calledWith(userServiceCheckImmutable)
|
||||
expect(mocks.tgProjectService.setProject).to.have.been.calledWith(projectServiceCheckImmutable)
|
||||
|
||||
done()
|
|
@ -0,0 +1,33 @@
|
|||
class WatchProjectButtonController
|
||||
@.$inject = [
|
||||
"$tgConfirm"
|
||||
"tgWatchProjectButtonService"
|
||||
]
|
||||
|
||||
constructor: (@confirm, @watchButtonService)->
|
||||
@.showWatchOptions = false
|
||||
@.loading = false
|
||||
|
||||
toggleWatcherOptions: () ->
|
||||
@.showWatchOptions = !@.showWatchOptions
|
||||
|
||||
closeWatcherOptions: () ->
|
||||
@.showWatchOptions = false
|
||||
|
||||
watch: (notifyLevel) ->
|
||||
@.loading = true
|
||||
@.closeWatcherOptions()
|
||||
|
||||
return @watchButtonService.watch(@.project.get('id'), notifyLevel)
|
||||
.catch () => @confirm.notify("error")
|
||||
.finally () => @.loading = false
|
||||
|
||||
unwatch: ->
|
||||
@.loading = true
|
||||
@.closeWatcherOptions()
|
||||
|
||||
return @watchButtonService.unwatch(@.project.get('id'))
|
||||
.catch () => @confirm.notify("error")
|
||||
.finally () => @.loading = false
|
||||
|
||||
angular.module("taigaProjects").controller("WatchProjectButton", WatchProjectButtonController)
|
|
@ -0,0 +1,136 @@
|
|||
describe "WatchProjectButton", ->
|
||||
$provide = null
|
||||
$controller = null
|
||||
mocks = {}
|
||||
|
||||
_mockTgConfirm = ->
|
||||
mocks.tgConfirm = {
|
||||
notify: sinon.stub()
|
||||
}
|
||||
|
||||
$provide.value("$tgConfirm", mocks.tgConfirm)
|
||||
|
||||
_mockTgWatchProjectButton = ->
|
||||
mocks.tgWatchProjectButton = {
|
||||
watch: sinon.stub(),
|
||||
unwatch: sinon.stub()
|
||||
}
|
||||
|
||||
$provide.value("tgWatchProjectButtonService", mocks.tgWatchProjectButton)
|
||||
|
||||
_mocks = ->
|
||||
module (_$provide_) ->
|
||||
$provide = _$provide_
|
||||
|
||||
_mockTgConfirm()
|
||||
_mockTgWatchProjectButton()
|
||||
|
||||
return null
|
||||
|
||||
_inject = ->
|
||||
inject (_$controller_) ->
|
||||
$controller = _$controller_
|
||||
|
||||
_setup = ->
|
||||
_mocks()
|
||||
_inject()
|
||||
|
||||
beforeEach ->
|
||||
module "taigaProjects"
|
||||
|
||||
_setup()
|
||||
|
||||
it "toggleWatcherOption", () ->
|
||||
ctrl = $controller("WatchProjectButton")
|
||||
|
||||
ctrl.toggleWatcherOptions()
|
||||
|
||||
expect(ctrl.showWatchOptions).to.be.true
|
||||
|
||||
ctrl.toggleWatcherOptions()
|
||||
|
||||
expect(ctrl.showWatchOptions).to.be.false
|
||||
|
||||
it "watch", (done) ->
|
||||
notifyLevel = 5
|
||||
project = Immutable.fromJS({
|
||||
id: 3
|
||||
})
|
||||
|
||||
ctrl = $controller("WatchProjectButton")
|
||||
ctrl.project = project
|
||||
ctrl.showWatchOptions = true
|
||||
|
||||
mocks.tgWatchProjectButton.watch = sinon.stub().promise()
|
||||
|
||||
promise = ctrl.watch(notifyLevel)
|
||||
|
||||
expect(ctrl.loading).to.be.true
|
||||
|
||||
mocks.tgWatchProjectButton.watch.withArgs(project.get('id'), notifyLevel).resolve()
|
||||
|
||||
promise.finally () ->
|
||||
expect(mocks.tgWatchProjectButton.watch).to.be.calledOnce
|
||||
expect(ctrl.showWatchOptions).to.be.false
|
||||
expect(ctrl.loading).to.be.false
|
||||
|
||||
done()
|
||||
|
||||
it "watch, notify error", (done) ->
|
||||
notifyLevel = 5
|
||||
project = Immutable.fromJS({
|
||||
id: 3
|
||||
})
|
||||
|
||||
ctrl = $controller("WatchProjectButton")
|
||||
ctrl.project = project
|
||||
ctrl.showWatchOptions = true
|
||||
|
||||
mocks.tgWatchProjectButton.watch.withArgs(project.get('id'), notifyLevel).promise().reject()
|
||||
|
||||
ctrl.watch(notifyLevel).finally () ->
|
||||
expect(mocks.tgConfirm.notify.withArgs("error")).to.be.calledOnce
|
||||
expect(ctrl.showWatchOptions).to.be.false
|
||||
expect(ctrl.loading).to.be.false
|
||||
|
||||
done()
|
||||
|
||||
it "unwatch", (done) ->
|
||||
project = Immutable.fromJS({
|
||||
id: 3
|
||||
})
|
||||
|
||||
ctrl = $controller("WatchProjectButton")
|
||||
ctrl.project = project
|
||||
ctrl.showWatchOptions = true
|
||||
|
||||
mocks.tgWatchProjectButton.unwatch = sinon.stub().promise()
|
||||
|
||||
promise = ctrl.unwatch()
|
||||
|
||||
expect(ctrl.loading).to.be.true
|
||||
|
||||
mocks.tgWatchProjectButton.unwatch.withArgs(project.get('id')).resolve()
|
||||
|
||||
promise.finally () ->
|
||||
expect(mocks.tgWatchProjectButton.unwatch).to.be.calledOnce
|
||||
expect(ctrl.showWatchOptions).to.be.false
|
||||
|
||||
done()
|
||||
|
||||
it "unwatch, notify error", (done) ->
|
||||
project = Immutable.fromJS({
|
||||
id: 3
|
||||
})
|
||||
|
||||
ctrl = $controller("WatchProjectButton")
|
||||
ctrl.project = project
|
||||
ctrl.showWatchOptions = true
|
||||
|
||||
mocks.tgWatchProjectButton.unwatch.withArgs(project.get('id')).promise().reject()
|
||||
|
||||
ctrl.unwatch().finally () ->
|
||||
expect(mocks.tgConfirm.notify.withArgs("error")).to.be.calledOnce
|
||||
expect(ctrl.showWatchOptions).to.be.false
|
||||
|
||||
done()
|
|
@ -0,0 +1,12 @@
|
|||
WatchProjectButtonDirective = ->
|
||||
return {
|
||||
scope: {}
|
||||
controller: "WatchProjectButton",
|
||||
bindToController: {
|
||||
project: "="
|
||||
}
|
||||
controllerAs: "vm",
|
||||
templateUrl: "projects/components/watch-project-button/watch-project-button.html",
|
||||
}
|
||||
|
||||
angular.module("taigaProjects").directive("tgWatchProjectButton", WatchProjectButtonDirective)
|
|
@ -0,0 +1,56 @@
|
|||
a.track-button.watch-button.watch-container(
|
||||
href="",
|
||||
title="{{ 'PROJECT.WATCH_BUTTON.BUTTON_TITLE' | translate }}"
|
||||
ng-click="vm.toggleWatcherOptions()"
|
||||
ng-class="{'active': vm.project.get('is_watcher')}"
|
||||
)
|
||||
span.track-inner
|
||||
span.track-icon
|
||||
include ../../../../svg/watch.svg
|
||||
span(ng-if="!vm.project.get('is_watcher')", translate="PROJECT.WATCH_BUTTON.WATCH")
|
||||
span(ng-if="vm.project.get('is_watcher')", translate="PROJECT.WATCH_BUTTON.WATCHING")
|
||||
span.icon.icon-arrow-up
|
||||
|
||||
span.track-button-counter(
|
||||
title="{{ 'PROJECT.WATCH_BUTTON.COUNTER_TITLE'|translate:{total:vm.project.get(\"total_watchers\")||0}:'messageformat' }}",
|
||||
tg-loading="vm.loading"
|
||||
)
|
||||
| {{ vm.project.get('total_watchers') }}
|
||||
|
||||
ul.watch-options(
|
||||
ng-class="{'hidden': !vm.showWatchOptions}"
|
||||
ng-mouseleave="vm.closeWatcherOptions()"
|
||||
)
|
||||
//- NOTIFY LEVEL CHOICES:
|
||||
//- 1 - Only involved
|
||||
//- 2 - Receive all
|
||||
//- 3 - No notifications
|
||||
|
||||
li
|
||||
a(
|
||||
href="",
|
||||
title="{{ 'PROJECT.WATCH_BUTTON.OPTIONS.NOTIFY_ALL_TITLE' | translate }}",
|
||||
ng-click="vm.watch(2)",
|
||||
ng-class="{'active': vm.project.get('is_watcher') && vm.project.get('notify_level') == 2}"
|
||||
)
|
||||
span(translate="PROJECT.WATCH_BUTTON.OPTIONS.NOTIFY_ALL")
|
||||
span.watch-check(ng-if="vm.project.get('is_watcher') && vm.project.get('notify_level') == 2")
|
||||
include ../../../../svg/check.svg
|
||||
li
|
||||
a(
|
||||
href="",
|
||||
title="{{ 'PROJECT.WATCH_BUTTON.OPTIONS.NOTIFY_INVOLVED_TITLE' | translate }}",
|
||||
ng-click="vm.watch(1)",
|
||||
ng-class="{'active': vm.project.get('is_watcher') && vm.project.get('notify_level') == 1}"
|
||||
)
|
||||
span(translate="PROJECT.WATCH_BUTTON.OPTIONS.NOTIFY_INVOLVED")
|
||||
span.watch-check(ng-if="vm.project.get('is_watcher') && vm.project.get('notify_level') == 1")
|
||||
include ../../../../svg/check.svg
|
||||
|
||||
li(ng-if="vm.project.get('is_watcher')")
|
||||
a(
|
||||
href="",
|
||||
title="{{ 'PROJECT.WATCH_BUTTON.OPTIONS.UNWATCH_TITLE' | translate }}",
|
||||
ng-click="vm.unwatch()"
|
||||
)
|
||||
span(translate="PROJECT.WATCH_BUTTON.OPTIONS.UNWATCH")
|
|
@ -0,0 +1,59 @@
|
|||
taiga = @.taiga
|
||||
|
||||
class WatchProjectButtonService extends taiga.Service
|
||||
@.$inject = [
|
||||
"tgResources",
|
||||
"tgCurrentUserService",
|
||||
"tgProjectService"
|
||||
]
|
||||
|
||||
constructor: (@rs, @currentUserService, @projectService) ->
|
||||
|
||||
_getProjectIndex: (projectId) ->
|
||||
return @currentUserService.projects
|
||||
.get('all')
|
||||
.findIndex (project) -> project.get('id') == projectId
|
||||
|
||||
|
||||
_updateProjects: (projectId, notifyLevel, isWatcher) ->
|
||||
projectIndex = @._getProjectIndex(projectId)
|
||||
|
||||
projects = @currentUserService.projects
|
||||
.get('all')
|
||||
.update projectIndex, (project) =>
|
||||
totalWatchers = project.get('total_watchers')
|
||||
|
||||
if isWatcher then totalWatchers++ else totalWatchers--
|
||||
|
||||
return project.merge({
|
||||
is_watcher: isWatcher,
|
||||
total_watchers: totalWatchers
|
||||
notify_level: notifyLevel
|
||||
})
|
||||
|
||||
@currentUserService.setProjects(projects)
|
||||
|
||||
_updateCurrentProject: (notifyLevel, isWatcher) ->
|
||||
totalWatchers = @projectService.project.get("total_watchers")
|
||||
|
||||
if isWatcher then totalWatchers++ else totalWatchers--
|
||||
|
||||
project = @projectService.project.merge({
|
||||
is_watcher: isWatcher,
|
||||
total_watchers: totalWatchers
|
||||
notify_level: notifyLevel
|
||||
})
|
||||
|
||||
@projectService.setProject(project)
|
||||
|
||||
watch: (projectId, notifyLevel) ->
|
||||
return @rs.projects.watchProject(projectId, notifyLevel).then =>
|
||||
@._updateProjects(projectId, notifyLevel, true)
|
||||
@._updateCurrentProject(notifyLevel, true)
|
||||
|
||||
unwatch: (projectId) ->
|
||||
return @rs.projects.unwatchProject(projectId).then =>
|
||||
@._updateProjects(projectId, null, false)
|
||||
@._updateCurrentProject(null, false)
|
||||
|
||||
angular.module("taigaProjects").service("tgWatchProjectButtonService", WatchProjectButtonService)
|
|
@ -0,0 +1,142 @@
|
|||
describe "tgWatchProjectButtonService", ->
|
||||
watchButtonService = null
|
||||
provide = null
|
||||
mocks = {}
|
||||
|
||||
_mockTgResources = () ->
|
||||
mocks.tgResources = {
|
||||
projects: {
|
||||
watchProject: sinon.stub(),
|
||||
unwatchProject: sinon.stub()
|
||||
}
|
||||
}
|
||||
|
||||
provide.value "tgResources", mocks.tgResources
|
||||
|
||||
_mockTgCurrentUserService = () ->
|
||||
mocks.tgCurrentUserService = {
|
||||
setProjects: sinon.stub(),
|
||||
getUser: () ->
|
||||
return Immutable.fromJS({
|
||||
id: 89
|
||||
})
|
||||
projects: Immutable.fromJS({
|
||||
all: [
|
||||
{
|
||||
id: 4,
|
||||
total_watchers: 0,
|
||||
is_watcher: false,
|
||||
notify_level: null
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
total_watchers: 1,
|
||||
is_watcher: true,
|
||||
notify_level: 3
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
total_watchers: 0,
|
||||
is_watcher: true,
|
||||
notify_level: null
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
provide.value "tgCurrentUserService", mocks.tgCurrentUserService
|
||||
|
||||
_mockTgProjectService = () ->
|
||||
mocks.tgProjectService = {
|
||||
setProject: sinon.stub()
|
||||
}
|
||||
|
||||
provide.value "tgProjectService", mocks.tgProjectService
|
||||
|
||||
_inject = (callback) ->
|
||||
inject (_tgWatchProjectButtonService_) ->
|
||||
watchButtonService = _tgWatchProjectButtonService_
|
||||
callback() if callback
|
||||
|
||||
_mocks = () ->
|
||||
module ($provide) ->
|
||||
provide = $provide
|
||||
_mockTgResources()
|
||||
_mockTgCurrentUserService()
|
||||
_mockTgProjectService()
|
||||
return null
|
||||
|
||||
_setup = ->
|
||||
_mocks()
|
||||
|
||||
beforeEach ->
|
||||
module "taigaProjects"
|
||||
_setup()
|
||||
_inject()
|
||||
|
||||
it "watch", (done) ->
|
||||
projectId = 4
|
||||
notifyLevel = 3
|
||||
|
||||
mocks.tgResources.projects.watchProject.withArgs(projectId, notifyLevel).promise().resolve()
|
||||
|
||||
newProject = {
|
||||
id: 4,
|
||||
total_watchers: 1,
|
||||
is_watcher: true,
|
||||
notify_level: notifyLevel
|
||||
}
|
||||
|
||||
mocks.tgProjectService.project = mocks.tgCurrentUserService.projects.getIn(['all', 0])
|
||||
|
||||
userServiceCheckImmutable = sinon.match ((immutable) ->
|
||||
immutable = immutable.toJS()
|
||||
|
||||
return _.isEqual(immutable[0], newProject)
|
||||
), 'userServiceCheckImmutable'
|
||||
|
||||
projectServiceCheckImmutable = sinon.match ((immutable) ->
|
||||
immutable = immutable.toJS()
|
||||
|
||||
return _.isEqual(immutable, newProject)
|
||||
), 'projectServiceCheckImmutable'
|
||||
|
||||
|
||||
watchButtonService.watch(projectId, notifyLevel).finally () ->
|
||||
expect(mocks.tgCurrentUserService.setProjects).to.have.been.calledWith(userServiceCheckImmutable)
|
||||
expect(mocks.tgProjectService.setProject).to.have.been.calledWith(projectServiceCheckImmutable)
|
||||
|
||||
done()
|
||||
|
||||
it "unwatch", (done) ->
|
||||
projectId = 5
|
||||
|
||||
mocks.tgResources.projects.unwatchProject.withArgs(projectId).promise().resolve()
|
||||
|
||||
newProject = {
|
||||
id: 5,
|
||||
total_watchers: 0,
|
||||
is_watcher: false,
|
||||
notify_level: null
|
||||
}
|
||||
|
||||
mocks.tgProjectService.project = mocks.tgCurrentUserService.projects.getIn(['all', 1])
|
||||
|
||||
userServiceCheckImmutable = sinon.match ((immutable) ->
|
||||
immutable = immutable.toJS()
|
||||
|
||||
return _.isEqual(immutable[1], newProject)
|
||||
), 'userServiceCheckImmutable'
|
||||
|
||||
projectServiceCheckImmutable = sinon.match ((immutable) ->
|
||||
immutable = immutable.toJS()
|
||||
|
||||
return _.isEqual(immutable, newProject)
|
||||
), 'projectServiceCheckImmutable'
|
||||
|
||||
|
||||
watchButtonService.unwatch(projectId).finally () ->
|
||||
expect(mocks.tgCurrentUserService.setProjects).to.have.been.calledWith(userServiceCheckImmutable)
|
||||
expect(mocks.tgProjectService.setProject).to.have.been.calledWith(projectServiceCheckImmutable)
|
||||
|
||||
done()
|
|
@ -11,17 +11,17 @@ div.project-list-wrapper.centered
|
|||
section.project-list-section
|
||||
div.project-list
|
||||
ul(tg-sort-projects="vm.projects")
|
||||
li.project-list-single(tg-bind-scope, tg-repeat="project in vm.projects track by project.get('id')")
|
||||
div.project-list-single-left
|
||||
div.project-title
|
||||
h1.project-name
|
||||
li.list-itemtype-project(tg-bind-scope, tg-repeat="project in vm.projects track by project.get('id')")
|
||||
div.list-itemtype-project-left
|
||||
div.list-itemtype-project-data
|
||||
h2
|
||||
a(href="#", tg-nav="project:project=project.get('slug')", title="{{ ::project.get('name') }}") {{project.get('name')}}
|
||||
span.private(ng-if="project.get('is_private')", title="{{'PROJECT.PRIVATE' | translate}}")
|
||||
include ../../../svg/lock.svg
|
||||
p {{ ::project.get('description') | limitTo:300 }}
|
||||
span(ng-if="::project.get('description').length > 300") ...
|
||||
|
||||
div.project-list-single-tags.tags-container(ng-if="::project.get('tags').size")
|
||||
span.private(ng-if="project.get('is_private')", title="{{'PROJECT.PRIVATE' | translate}}")
|
||||
include ../../../svg/lock.svg
|
||||
p {{ ::project.get('description') | limitTo:300 }}
|
||||
span(ng-if="::project.get('description').length > 300") ...
|
||||
|
||||
div.list-itemtype-project-tags.tag-container(ng-if="::project.get('tags').size")
|
||||
span.tag(style='border-left: 5px solid {{::tag.get("color")}};', tg-repeat="tag in ::project.get('colorized_tags')")
|
||||
span.tag-name {{::tag.get('name')}}
|
||||
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
.project-list-single {
|
||||
border-bottom: 1px solid $whitish;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
min-height: 9rem;
|
||||
padding: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.project-list-single-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-right: 1rem;
|
||||
h1 {
|
||||
@extend %text;
|
||||
@extend %larger;
|
||||
color: $gray;
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
text-transform: none;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
p {
|
||||
@extend %text;
|
||||
@extend %xsmall;
|
||||
color: $gray;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.project-list-single-tags {
|
||||
align-self: flex-end;
|
||||
display: flex;
|
||||
flex: 3;
|
||||
flex-wrap: wrap;
|
||||
margin-top: .5rem;
|
||||
}
|
||||
.tag {
|
||||
align-self: flex-end;
|
||||
margin-right: .5rem;
|
||||
padding: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.project-list-single-right {
|
||||
flex-shrink: 0;
|
||||
justify-content: space-between;
|
||||
width: 200px;
|
||||
.project-list-single-stats {
|
||||
align-self: flex-end;
|
||||
display: flex;
|
||||
div {
|
||||
color: $gray-light;
|
||||
margin-right: .5rem;
|
||||
.icon {
|
||||
margin-right: .2rem;
|
||||
vertical-align: center;
|
||||
}
|
||||
}
|
||||
.active {
|
||||
.icon {
|
||||
color: $primary-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
.project-list-single-members {
|
||||
align-self: flex-end;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
flex-grow: 0;
|
||||
flex-wrap: wrap-reverse;
|
||||
margin-top: 1rem;
|
||||
a {
|
||||
display: block; }
|
||||
img {
|
||||
border-radius: .1rem;
|
||||
margin-right: .3rem;
|
||||
width: 34px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
.profile-projects {
|
||||
border-top: 1px solid $whitish;
|
||||
.project-list-single {
|
||||
.list-itemtype-project {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
min-height: 10rem;
|
||||
.list-itemtype-project-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
padding: .9rem 1rem;
|
||||
h1 {
|
||||
@extend %larger;
|
||||
@extend %light;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
@ -41,13 +42,12 @@
|
|||
}
|
||||
.placeholder {
|
||||
background-color: lighten($whitish, 3%);
|
||||
height: 7rem;
|
||||
width: 100%;
|
||||
height: 5rem;
|
||||
}
|
||||
.project-list-single {
|
||||
background: $white;
|
||||
.list-itemtype-project {
|
||||
background: rgba($white, .6);
|
||||
&:hover {
|
||||
background: lighten($primary, 60%);
|
||||
background: lighten($primary, 63%);
|
||||
cursor: move;
|
||||
transition: background .3s;
|
||||
.drag {
|
||||
|
|
|
@ -1,36 +1,31 @@
|
|||
class ProjectController
|
||||
@.$inject = [
|
||||
"tgProjectsService",
|
||||
"$routeParams",
|
||||
"tgAppMetaService",
|
||||
"$tgAuth",
|
||||
"tgXhrErrorService",
|
||||
"$translate"
|
||||
"$translate",
|
||||
"tgProjectService"
|
||||
]
|
||||
|
||||
constructor: (@projectsService, @routeParams, @appMetaService, @auth, @xhrError, @translate) ->
|
||||
constructor: (@routeParams, @appMetaService, @auth, @translate, @projectService) ->
|
||||
projectSlug = @routeParams.pslug
|
||||
@.user = @auth.userData
|
||||
|
||||
@projectsService
|
||||
.getProjectBySlug(projectSlug)
|
||||
.then (project) =>
|
||||
@.project = project
|
||||
taiga.defineImmutableProperty @, "project", () => return @projectService.project
|
||||
taiga.defineImmutableProperty @, "members", () => return @projectService.activeMembers
|
||||
|
||||
members = @.project.get('members').filter (member) -> member.get('is_active')
|
||||
|
||||
@.project = @.project.set('members', members)
|
||||
|
||||
@._setMeta(@.project)
|
||||
|
||||
.catch (xhr) =>
|
||||
@xhrError.response(xhr)
|
||||
@appMetaService.setfn @._setMeta.bind(this)
|
||||
|
||||
_setMeta: (project)->
|
||||
ctx = {projectName: project.get("name")}
|
||||
metas = {}
|
||||
|
||||
title = @translate.instant("PROJECT.PAGE_TITLE", ctx)
|
||||
description = project.get("description")
|
||||
@appMetaService.setAll(title, description)
|
||||
return metas if !@.project
|
||||
|
||||
ctx = {projectName: @.project.get("name")}
|
||||
|
||||
metas.title = @translate.instant("PROJECT.PAGE_TITLE", ctx)
|
||||
metas.description = @.project.get("description")
|
||||
|
||||
return metas
|
||||
|
||||
angular.module("taigaProjects").controller("Project", ProjectController)
|
||||
|
|
|
@ -5,16 +5,14 @@ describe "ProjectController", ->
|
|||
$rootScope = null
|
||||
mocks = {}
|
||||
|
||||
_mockProjectsService = () ->
|
||||
mocks.projectService = {
|
||||
getProjectBySlug: sinon.stub()
|
||||
}
|
||||
_mockProjectService = () ->
|
||||
mocks.projectService = {}
|
||||
|
||||
provide.value "tgProjectsService", mocks.projectService
|
||||
provide.value "tgProjectService", mocks.projectService
|
||||
|
||||
_mockAppMetaService = () ->
|
||||
mocks.appMetaService = {
|
||||
setAll: sinon.stub()
|
||||
setfn: sinon.stub()
|
||||
}
|
||||
|
||||
provide.value "tgAppMetaService", mocks.appMetaService
|
||||
|
@ -31,13 +29,6 @@ describe "ProjectController", ->
|
|||
pslug: "project-slug"
|
||||
}
|
||||
|
||||
_mockXhrErrorService = () ->
|
||||
mocks.xhrErrorService = {
|
||||
response: sinon.spy()
|
||||
}
|
||||
|
||||
provide.value "tgXhrErrorService", mocks.xhrErrorService
|
||||
|
||||
_mockTranslate = () ->
|
||||
mocks.translate = {}
|
||||
mocks.translate.instant = sinon.stub()
|
||||
|
@ -47,11 +38,10 @@ describe "ProjectController", ->
|
|||
_mocks = () ->
|
||||
module ($provide) ->
|
||||
provide = $provide
|
||||
_mockProjectsService()
|
||||
_mockProjectService()
|
||||
_mockRouteParams()
|
||||
_mockAppMetaService()
|
||||
_mockAuth()
|
||||
_mockXhrErrorService()
|
||||
_mockTranslate()
|
||||
return null
|
||||
|
||||
|
@ -72,14 +62,12 @@ describe "ProjectController", ->
|
|||
members: []
|
||||
})
|
||||
|
||||
mocks.projectService.getProjectBySlug.withArgs("project-slug").promise().resolve(project)
|
||||
|
||||
ctrl = $controller "Project",
|
||||
$scope: {}
|
||||
|
||||
expect(ctrl.user).to.be.equal(mocks.auth.userData)
|
||||
|
||||
it "set page title", (done) ->
|
||||
it "set page title", () ->
|
||||
$scope = $rootScope.$new()
|
||||
project = Immutable.fromJS({
|
||||
name: "projectName"
|
||||
|
@ -93,44 +81,31 @@ describe "ProjectController", ->
|
|||
})
|
||||
.returns('projectTitle')
|
||||
|
||||
mocks.projectService.getProjectBySlug.withArgs("project-slug").promise().resolve(project)
|
||||
mocks.projectService.project = project
|
||||
|
||||
ctrl = $controller("Project")
|
||||
|
||||
setTimeout ( ->
|
||||
expect(mocks.appMetaService.setAll.calledWithExactly("projectTitle", "projectDescription")).to.be.true
|
||||
done()
|
||||
)
|
||||
metas = ctrl._setMeta(project)
|
||||
|
||||
it "set local project variable with active members", (done) ->
|
||||
expect(metas.title).to.be.equal('projectTitle')
|
||||
expect(metas.description).to.be.equal('projectDescription')
|
||||
expect(mocks.appMetaService.setfn).to.be.calledOnce
|
||||
|
||||
it "set local project variable and members", () ->
|
||||
project = Immutable.fromJS({
|
||||
name: "projectName",
|
||||
members: [
|
||||
{is_active: true},
|
||||
{is_active: true},
|
||||
{is_active: true},
|
||||
{is_active: false}
|
||||
]
|
||||
name: "projectName"
|
||||
})
|
||||
|
||||
mocks.projectService.getProjectBySlug.withArgs("project-slug").promise().resolve(project)
|
||||
members = Immutable.fromJS([
|
||||
{is_active: true},
|
||||
{is_active: true},
|
||||
{is_active: true}
|
||||
])
|
||||
|
||||
mocks.projectService.project = project
|
||||
mocks.projectService.activeMembers = members
|
||||
|
||||
ctrl = $controller("Project")
|
||||
|
||||
setTimeout (() ->
|
||||
expect(ctrl.project.get('members').size).to.be.equal(3)
|
||||
|
||||
done()
|
||||
)
|
||||
|
||||
it "handle project error", (done) ->
|
||||
xhr = {code: 403}
|
||||
|
||||
mocks.projectService.getProjectBySlug.withArgs("project-slug").promise().reject(xhr)
|
||||
|
||||
ctrl = $controller("Project")
|
||||
|
||||
setTimeout (() ->
|
||||
expect(mocks.xhrErrorService.response.withArgs(xhr)).to.be.calledOnce
|
||||
done()
|
||||
)
|
||||
expect(ctrl.project).to.be.equal(project)
|
||||
expect(ctrl.members).to.be.equal(members)
|
||||
|
|
|
@ -2,29 +2,64 @@ div.wrapper
|
|||
tg-project-menu
|
||||
div.centered.single-project
|
||||
section.single-project-intro
|
||||
h1
|
||||
span.green(class="project-name") {{::vm.project.get("name")}}
|
||||
span.private(ng-if="::vm.project.get('is_private')", title="{{'PROJECT.PRIVATE' | translate}}")
|
||||
include ../../../svg/lock.svg
|
||||
div.intro-options
|
||||
h1
|
||||
span.project-name {{::vm.project.get("name")}}
|
||||
span.private(
|
||||
ng-if="::vm.project.get('is_private')"
|
||||
title="{{'PROJECT.PRIVATE' | translate}}"
|
||||
)
|
||||
include ../../../svg/lock.svg
|
||||
|
||||
//- Like and wacht buttons for authenticated users
|
||||
div.track-buttons-container(ng-if="vm.user")
|
||||
tg-like-project-button(project="vm.project")
|
||||
tg-watch-project-button(project="vm.project")
|
||||
|
||||
//- Like and wacht buttons for anonymous users
|
||||
div.track-container(ng-if="!vm.user")
|
||||
.list-itemtype-track
|
||||
span.list-itemtype-track-likers(
|
||||
title="{{ 'PROJECT.LIKE_BUTTON.COUNTER_TITLE'|translate:{total:vm.project.get(\"total_fans\")||0}:'messageformat' }}"
|
||||
)
|
||||
span.icon
|
||||
include ../../../svg/like.svg
|
||||
span {{ ::vm.project.get('total_fans') }}
|
||||
|
||||
span.list-itemtype-track-watchers(
|
||||
title="{{ 'PROJECT.WATCH_BUTTON.COUNTER_TITLE'|translate:{total:vm.project.get(\"total_watchers\")||0}:'messageformat' }}"
|
||||
)
|
||||
span.icon
|
||||
include ../../../svg/watch.svg
|
||||
span {{ ::vm.project.get('total_watchers') }}
|
||||
|
||||
p.description {{vm.project.get('description')}}
|
||||
|
||||
div.single-project-tags.tags-container(ng-if="::vm.project.get('tags').size")
|
||||
span.tag(style='border-left: 5px solid {{::tag.get("color")}};',
|
||||
tg-repeat="tag in ::vm.project.get('colorized_tags')")
|
||||
span.tag(
|
||||
style='border-left: 5px solid {{::tag.get("color")}};',
|
||||
tg-repeat="tag in ::vm.project.get('colorized_tags')"
|
||||
)
|
||||
span.tag-name {{::tag.get('name')}}
|
||||
|
||||
div.project-data
|
||||
section.timeline(ng-if="vm.project")
|
||||
div(tg-user-timeline, projectId="vm.project.get('id')")
|
||||
|
||||
section.involved-data
|
||||
h2.title {{"PROJECT.SECTION.TEAM" | translate}}
|
||||
ul.involved-team
|
||||
li(tg-repeat="member in ::vm.project.get('members')")
|
||||
a(tg-nav="user-profile:username=member.get('username')",
|
||||
title="{{::member.get('full_name')}}")
|
||||
img(ng-src="{{::member.get('photo')}}", alt="{{::member.get('full_name')}}")
|
||||
|
||||
// h2.title Organizations
|
||||
// div.involved-organization
|
||||
// a(href="", title="User Name")
|
||||
// img(src="https://s3.amazonaws.com/uifaces/faces/twitter/dan_higham/48.jpg", alt="{{member.full_name}}")
|
||||
li(tg-repeat="member in vm.members")
|
||||
a(
|
||||
tg-nav="user-profile:username=member.get('username')",
|
||||
title="{{::member.get('full_name')}}"
|
||||
)
|
||||
img(ng-src="{{::member.get('photo')}}", alt="{{::member.get('full_name')}}")
|
||||
//-
|
||||
h2.title Organizations
|
||||
div.involved-organization
|
||||
a(href="", title="User Name")
|
||||
img(
|
||||
src="https://s3.amazonaws.com/uifaces/faces/twitter/dan_higham/48.jpg"
|
||||
alt="{{member.full_name}}"
|
||||
)
|
||||
|
|
|
@ -2,17 +2,20 @@ taiga = @.taiga
|
|||
|
||||
class ProjectService
|
||||
@.$inject = [
|
||||
"tgProjectsService"
|
||||
"tgProjectsService",
|
||||
"tgXhrErrorService"
|
||||
]
|
||||
|
||||
constructor: (@projectsService) ->
|
||||
constructor: (@projectsService, @xhrError) ->
|
||||
@._project = null
|
||||
@._section = null
|
||||
@._sectionsBreadcrumb = Immutable.List()
|
||||
@._activeMembers = Immutable.List()
|
||||
|
||||
taiga.defineImmutableProperty @, "project", () => return @._project
|
||||
taiga.defineImmutableProperty @, "section", () => return @._section
|
||||
taiga.defineImmutableProperty @, "sectionsBreadcrumb", () => return @._sectionsBreadcrumb
|
||||
taiga.defineImmutableProperty @, "activeMembers", () => return @._activeMembers
|
||||
|
||||
setSection: (section) ->
|
||||
@._section = section
|
||||
|
@ -22,20 +25,32 @@ class ProjectService
|
|||
else
|
||||
@._sectionsBreadcrumb = Immutable.List()
|
||||
|
||||
setProject: (pslug) ->
|
||||
if @._pslug != pslug
|
||||
@._pslug = pslug
|
||||
setProjectBySlug: (pslug) ->
|
||||
return new Promise (resolve, reject) =>
|
||||
if !@.project || @.project.get('slug') != pslug
|
||||
@projectsService
|
||||
.getProjectBySlug(pslug)
|
||||
.then (project) =>
|
||||
@.setProject(project)
|
||||
resolve()
|
||||
.catch (xhr) =>
|
||||
@xhrError.response(xhr)
|
||||
|
||||
@.fetchProject()
|
||||
else resolve()
|
||||
|
||||
setProject: (project) ->
|
||||
@._project = project
|
||||
@._activeMembers = @._project.get('members').filter (member) -> member.get('is_active')
|
||||
|
||||
cleanProject: () ->
|
||||
@._pslug = null
|
||||
@._project = null
|
||||
@._activeMembers = Immutable.List()
|
||||
@._section = null
|
||||
@._sectionsBreadcrumb = Immutable.List()
|
||||
|
||||
fetchProject: () ->
|
||||
return @projectsService.getProjectBySlug(@._pslug).then (project) =>
|
||||
@._project = project
|
||||
pslug = @.project.get('slug')
|
||||
|
||||
return @projectsService.getProjectBySlug(pslug).then (project) => @.setProject(project)
|
||||
|
||||
angular.module("taigaCommon").service("tgProjectService", ProjectService)
|
||||
|
|
|
@ -10,11 +10,19 @@ describe "tgProjectService", ->
|
|||
|
||||
$provide.value "tgProjectsService", mocks.projectsService
|
||||
|
||||
_mockXhrErrorService = () ->
|
||||
mocks.xhrErrorService = {
|
||||
response: sinon.stub()
|
||||
}
|
||||
|
||||
$provide.value "tgXhrErrorService", mocks.xhrErrorService
|
||||
|
||||
_mocks = () ->
|
||||
module (_$provide_) ->
|
||||
$provide = _$provide_
|
||||
|
||||
_mockProjectsService()
|
||||
_mockXhrErrorService()
|
||||
|
||||
return null
|
||||
|
||||
|
@ -46,31 +54,70 @@ describe "tgProjectService", ->
|
|||
|
||||
expect(projectService.sectionsBreadcrumb.toJS()).to.be.eql(breadcrumb)
|
||||
|
||||
it "set project if the project slug has changed", () ->
|
||||
projectService.fetchProject = sinon.spy()
|
||||
it "set project if the project slug has changed", (done) ->
|
||||
projectService.setProject = sinon.spy()
|
||||
|
||||
pslug = "slug-1"
|
||||
project = Immutable.Map({
|
||||
id: 1,
|
||||
slug: 'slug-1',
|
||||
members: []
|
||||
})
|
||||
|
||||
projectService.setProject(pslug)
|
||||
mocks.projectsService.getProjectBySlug.withArgs('slug-1').promise().resolve(project)
|
||||
mocks.projectsService.getProjectBySlug.withArgs('slug-2').promise().resolve(project)
|
||||
|
||||
expect(projectService.fetchProject).to.be.calledOnce
|
||||
projectService.setProjectBySlug('slug-1')
|
||||
.then () -> projectService.setProjectBySlug('slug-1')
|
||||
.then () -> projectService.setProjectBySlug('slug-2')
|
||||
.finally () ->
|
||||
expect(projectService.setProject).to.be.called.twice;
|
||||
done()
|
||||
|
||||
projectService.setProject(pslug)
|
||||
it "set project and set active members", () ->
|
||||
project = Immutable.fromJS({
|
||||
name: 'test project',
|
||||
members: [
|
||||
{is_active: true},
|
||||
{is_active: false},
|
||||
{is_active: true},
|
||||
{is_active: false},
|
||||
{is_active: false}
|
||||
]
|
||||
})
|
||||
|
||||
expect(projectService.fetchProject).to.be.calledOnce
|
||||
projectService.setProject(project)
|
||||
|
||||
projectService.setProject("slug-2")
|
||||
|
||||
expect(projectService.fetchProject).to.be.calledTwice
|
||||
expect(projectService.project).to.be.equal(project)
|
||||
expect(projectService.activeMembers.size).to.be.equal(2)
|
||||
|
||||
it "fetch project", (done) ->
|
||||
project = Immutable.Map({id: 1})
|
||||
pslug = "slug-1"
|
||||
project = Immutable.Map({
|
||||
id: 1,
|
||||
slug: 'slug',
|
||||
members: []
|
||||
})
|
||||
|
||||
projectService._pslug = pslug
|
||||
projectService._project = project
|
||||
|
||||
mocks.projectsService.getProjectBySlug.withArgs(pslug).promise().resolve(project)
|
||||
mocks.projectsService.getProjectBySlug.withArgs(project.get('slug')).promise().resolve(project)
|
||||
|
||||
projectService.fetchProject().then () ->
|
||||
expect(projectService.project).to.be.equal(project)
|
||||
done()
|
||||
|
||||
it "clean project", () ->
|
||||
projectService._section = "fakeSection"
|
||||
projectService._sectionsBreadcrumb = ["fakeSection"]
|
||||
projectService._activeMembers = ["fakeMember"]
|
||||
projectService._project = Immutable.Map({
|
||||
id: 1,
|
||||
slug: 'slug',
|
||||
members: []
|
||||
})
|
||||
|
||||
projectService.cleanProject()
|
||||
|
||||
expect(projectService.project).to.be.null;
|
||||
expect(projectService.activeMembers.size).to.be.equal(0);
|
||||
expect(projectService.section).to.be.null;
|
||||
expect(projectService.sectionsBreadcrumb.size).to.be.equal(0);
|
||||
|
|
Loading…
Reference in New Issue