Show likes, votes and watches list in user profile

stable
David Barragán Merino 2015-10-23 16:50:57 +02:00
parent f52935f8c9
commit 331b14f13f
24 changed files with 1494 additions and 117 deletions

View File

@ -28,7 +28,7 @@ resourceProvider = ($http, $urls) ->
service = {} service = {}
service.contacts = (userId, options={}) -> service.contacts = (userId, options={}) ->
url = $urls.resolve("contacts", userId) url = $urls.resolve("user-contacts", userId)
httpOptions = {headers: {}} httpOptions = {headers: {}}
if not options.enablePagination if not options.enablePagination

View File

@ -1,6 +0,0 @@
section.profile-favorites
nav.profile-favorites-filters
a.active(href="", title="No Filter") all
a(href="", title="Only show your team") projects
a(href="", title="Only show people you follow") US
a(href="", title="Only show people follow you") tasks

View File

@ -11,32 +11,30 @@ section.profile-contacts
div(ng-if="vm.isCurrentUser") div(ng-if="vm.isCurrentUser")
p(translate="USER.PROFILE.CURRENT_USER_CONTACTS_EMPTY") p(translate="USER.PROFILE.CURRENT_USER_CONTACTS_EMPTY")
p(translate="USER.PROFILE.CURRENT_USER_CONTACTS_EMPTY_EXPLAIN") p(translate="USER.PROFILE.CURRENT_USER_CONTACTS_EMPTY_EXPLAIN")
//-
nav.profile-contact-filters
a.active(href="", title="No Filter") all
a(href="", title="Only show your team") team
a(href="", title="Only show people you follow") following
a(href="", title="Only show people follow you") followers
// nav.profile-contact-filters div.list-itemtype-user(tg-repeat="contact in ::vm.contacts")
// a.active(href="", title="No Filter") all a.list-itemtype-avatar(tg-nav="user-profile:username=contact.get('username')", title="{{::contact.get('name')}}")
// a(href="", title="Only show your team") team img(ng-src="{{::contact.get('photo')}}", alt="{{::contact.get('full_name')}}")
// a(href="", title="Only show people you follow") following
// a(href="", title="Only show people follow you") followers
div.profile-contact-single(tg-repeat="contact in ::vm.contacts") div.list-itemtype-user-data
div.profile-contact-picture h2
a(tg-nav="user-profile:username=contact.get('username')", title="{{::contact.get('name') }}") a(tg-nav="user-profile:username=contact.get('username')", title="{{::contact.get('full_name_display') }}") {{::contact.get('full_name_display')}}
img(ng-src="{{::contact.get('photo')}}", alt="{{::contact.get('full_name')}}")
p {{::contact.get('roles').join(", ")}}
p.extra-info(ng-if="contact.get('bio')") {{::contact.get('bio')}}
div.profile-contact-data //-
h1 span.location todo
a(tg-nav="user-profile:username=contact.get('username')", title="{{::contact.get('full_name_display') }}") div.profile-project-stats
| {{::contact.get('full_name_display')}} div.stat-projects(title="2 projects")
span.icon.icon-project
p(ng-if="contact.bio") {{::contact.get('bio')}} span.stat-num 2
div.stat-viewer(title="2 followers")
div.extra-info span.icon.icon-open-eye
span.position {{::contact.get('roles').join(", ")}} span.stat-num 4
// span.location todo
// div.profile-project-stats
// div.stat-projects(title="2 projects")
// span.icon.icon-project
// span.stat-num 2
// div.stat-viewer(title="2 followers")
// span.icon.icon-open-eye
// span.stat-num 4

View File

@ -0,0 +1,20 @@
FavItemDirective = ->
link = (scope, el, attrs, ctrl) ->
scope.vm = {item: scope.item}
templateUrl = (el, attrs) ->
if attrs.itemType == "project"
return "profile/profile-favs/items/project.html"
else # if attr.itemType in ["userstory", "task", "issue"]
return "profile/profile-favs/items/ticket.html"
return {
scope: {
"item": "=tgFavItem"
}
link: link
templateUrl: templateUrl
}
angular.module("taigaProfile").directive("tgFavItem", FavItemDirective)

View File

@ -0,0 +1,34 @@
.list-itemtype-project
.list-itemtype-project-data
h2
a(
href="#"
tg-nav="project:project=vm.item.get('slug')"
title="{{ ::vm.item.get('name') }}"
) {{ ::vm.item.get('name') }}
span.private(ng-if="::project.get('is_private')", title="{{'PROJECT.PRIVATE' | translate}}")
p {{ ::vm.item.get('description') }}
.list-itemtype-project-tags.tags-container(ng-if="::vm.item.get('tags_colors').size")
span.tag(
tg-repeat="tag in ::vm.item.get('tags_colors')"
style='border-left: 5px solid {{ ::tag.get("color") }};'
)
span.tag-name {{ ::tag.get('name') }}
.list-itemtype-track
span.list-itemtype-track-likers(
ng-class="{'active': vm.item.get('is_fan')}"
title="{{ 'PROJECT.LIKE_BUTTON.COUNTER_TITLE'|translate:{total:vm.item.get(\"total_fans\")||0}:'messageformat' }}"
)
span.icon
include ../../../../svg/like.svg
span {{ ::vm.item.get('total_fans') }}
span.list-itemtype-track-watchers(
ng-class="{'active': vm.item.get('is_watcher')}"
title="{{ 'PROJECT.WATCH_BUTTON.COUNTER_TITLE'|translate:{total:vm.item.get(\"total_watchers\")||0}:'messageformat' }}"
)
span.icon
include ../../../../svg/watch.svg
span {{ ::vm.item.get('total_watchers') }}

View File

@ -0,0 +1,80 @@
div.list-itemtype-ticket
a.list-itemtype-avatar(
href=""
ng-if="::vm.item.get('assigned_to')"
tg-nav="user-profile:username=vm.item.get('assigned_to_username')"
title="{{ ::vm.item.get('assigned_to_full_name') }}"
)
img(
ng-src="{{ ::vm.item.get('assigned_to_photo') }}",
alt="{{ ::vm.item.get('assigned_to_full_name') }}"
)
a.list-itemtype-avatar(
href=""
ng-if="::!vm.item.get('assigned_to')",
title="{{ 'COMMON.ASSIGNED_TO.NOT_ASSIGNED'|translate }}"
)
img(
src="/images/unnamed.png",
alt="{{ 'COMMON.ASSIGNED_TO.NOT_ASSIGNED'|translate }}"
)
div.list-itemtype-ticket-data
p
span.ticket-project
| {{:: vm.item.get('project_name') }}
span.ticket-type(
ng-if="::vm.item.get('type') === 'userstory'"
translate="COMMON.USER_STORY"
)
span.ticket-type(
ng-if="::vm.item.get('type') === 'task'"
translate="COMMON.TASK"
)
span.ticket-type(
ng-if="::vm.item.get('type') === 'issue'"
translate="COMMON.ISSUE"
)
span.ticket-status(ng-style="::{'color': vm.item.get('status_color')}")
| {{:: vm.item.get('status') }}
h2
span.ticket-id(tg-bo-ref="vm.item.get('ref')")
a.ticket-title(
href="#"
ng-if="::vm.item.get('type') === 'userstory'"
tg-nav="project-userstories-detail:project=vm.item.get('project_slug'),ref=vm.item.get('ref')"
title="#{{ ::vm.item.get('ref') }} {{ ::vm.item.get('subject') }}"
)
| {{ ::vm.item.get('subject') }}
a.ticket-title(
href="#"
ng-if="::vm.item.get('type') === 'task'"
tg-nav="project-tasks-detail:project=vm.item.get('project_slug'),ref=vm.item.get('ref')"
title="#{{ ::vm.item.get('ref') }} {{ ::vm.item.get('subject') }}"
)
| {{ ::vm.item.get('subject') }}
a.ticket-title(
href="#"
ng-if="::vm.item.get('type') === 'issue'"
tg-nav="project-issues-detail:project=vm.item.get('project_slug'),ref=vm.item.get('ref')"
title="#{{ ::vm.item.get('ref') }} {{ ::vm.item.get('subject') }}"
)
| {{ ::vm.item.get('subject') }}
div.list-itemtype-track
span.list-itemtype-track-likers(
ng-class="{'active': vm.item.get('is_voter')}",
title="{{ 'COMMON.VOTE_BUTTON.COUNTER_TITLE'|translate:{total:vm.item.get(\"total_voters\")||0}:'messageformat' }}"
)
span.icon
include ../../../../svg/upvote.svg
span {{ ::vm.item.get('total_voters') }}
span.list-itemtype-track-watchers(
ng-class="{'active': vm.item.get('is_watcher')}"
title="{{ 'COMMON.WATCH_BUTTON.COUNTER_TITLE'|translate:{total:vm.item.get(\"total_watchers\")||0}:'messageformat' }}"
)
span.icon
include ../../../../svg/watch.svg
span {{ ::vm.item.get('total_watchers') }}

View File

@ -0,0 +1,168 @@
debounceLeading = @.taiga.debounceLeading
class FavsBaseController
constructor: ->
@._init()
#@._getItems = null # Define in inheritance classes
#
_init: ->
@.enableFilterByAll = true
@.enableFilterByProjects = true
@.enableFilterByUserStories = true
@.enableFilterByTasks = true
@.enableFilterByIssues = true
@.enableFilterByTextQuery = true
@._resetList()
@.q = null
@.type = null
_resetList: ->
@.items = Immutable.List()
@.scrollDisabled = false
@._page = 1
_enableLoadingSpinner: ->
@.isLoading = true
_disableLoadingSpinner: ->
@.isLoading = false
_enableScroll : ->
@.scrollDisabled = false
_disableScroll : ->
@.scrollDisabled = true
_checkIfHasMorePages: (hasNext) ->
if hasNext
@._page += 1
@._enableScroll()
else
@._disableScroll()
_checkIfHasNoResults: ->
@.hasNoResults = @.items.size == 0
loadItems: ->
@._enableLoadingSpinner()
@._disableScroll()
@._getItems(@.user.get("id"), @._page, @.type, @.q)
.then (response) =>
@.items = @.items.concat(response.get("data"))
@._checkIfHasMorePages(response.get("next"))
@._checkIfHasNoResults()
@._disableLoadingSpinner()
return @.items
.catch =>
@._disableLoadingSpinner()
return @.items
################################################
## Filtre actions
################################################
filterByTextQuery: debounceLeading 500, ->
@._resetList()
@.loadItems()
showAll: ->
if @.type isnt null
@.type = null
@._resetList()
@.loadItems()
showProjectsOnly: ->
if @.type isnt "project"
@.type = "project"
@._resetList()
@.loadItems()
showUserStoriesOnly: ->
if @.type isnt "userstory"
@.type = "userstory"
@._resetList()
@.loadItems()
showTasksOnly: ->
if @.type isnt "task"
@.type = "task"
@._resetList()
@.loadItems()
showIssuesOnly: ->
if @.type isnt "issue"
@.type = "issue"
@._resetList()
@.loadItems()
####################################################
## Liked
####################################################
class ProfileLikedController extends FavsBaseController
@.$inject = [
"tgUserService",
]
constructor: (@userService) ->
super()
@.enableFilterByAll = false
@.enableFilterByProjects = false
@.enableFilterByUserStories = false
@.enableFilterByTasks = false
@.enableFilterByIssues = false
@.enableFilterByTextQuery = true
@._getItems = @userService.getLiked
angular.module("taigaProfile")
.controller("ProfileLiked", ProfileLikedController)
####################################################
## Voted
####################################################
class ProfileVotedController extends FavsBaseController
@.$inject = [
"tgUserService",
]
constructor: (@userService) ->
super()
@.enableFilterByAll = true
@.enableFilterByProjects = false
@.enableFilterByUserStories = true
@.enableFilterByTasks = true
@.enableFilterByIssues = true
@.enableFilterByTextQuery = true
@._getItems = @userService.getVoted
angular.module("taigaProfile")
.controller("ProfileVoted", ProfileVotedController)
####################################################
## Watched
####################################################
class ProfileWatchedController extends FavsBaseController
@.$inject = [
"tgUserService",
]
constructor: (@userService) ->
super()
@._getItems = @userService.getWatched
angular.module("taigaProfile")
.controller("ProfileWatched", ProfileWatchedController)

View File

@ -0,0 +1,679 @@
describe "ProfileLiked", ->
$controller = null
provide = null
$rootScope = null
mocks = {}
user = Immutable.fromJS({id: 2})
_mockUserService = () ->
mocks.userServices = {
getLiked: sinon.stub()
}
provide.value "tgUserService", mocks.userServices
_mocks = () ->
module ($provide) ->
provide = $provide
_mockUserService()
return null
_inject = (callback) ->
inject (_$controller_, _$rootScope_) ->
$rootScope = _$rootScope_
$controller = _$controller_
beforeEach ->
module "taigaProfile"
_mocks()
_inject()
it "load paginated items", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileLiked", $scope, {user: user})
items1 = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
items2 = Immutable.fromJS({
data: [
{id: 4},
{id: 5},
],
next: false
})
mocks.userServices.getLiked.withArgs(user.get("id"), 1, null, null).promise().resolve(items1)
mocks.userServices.getLiked.withArgs(user.get("id"), 2, null, null).promise().resolve(items2)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.loadItems().then () =>
expectItems = items1.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.loadItems().then () =>
expectItems = expectItems.concat(items2.get("data"))
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.true
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
done()
it "filter items by text query", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileLiked", $scope, {user: user})
textQuery = "_test_"
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mocks.userServices.getLiked.withArgs(user.get("id"), 1, null, textQuery).promise().resolve(items)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.q = textQuery
ctrl.loadItems().then () =>
expectItems = items.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.equal(textQuery)
done()
it "shou loading spinner during the call to the api", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileLiked", $scope, {user: user})
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mockPromise = mocks.userServices.getLiked.withArgs(user.get("id"), 1, null, null).promise()
expect(ctrl.isLoading).to.be.undefined
promise = ctrl.loadItems()
expect(ctrl.isLoading).to.be.true
mockPromise.resolve(items)
promise.then () =>
expect(ctrl.isLoading).to.be.false
done()
it "shou no results placeholder", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileLiked", $scope, {user: user})
items = Immutable.fromJS({
data: [],
next: false
})
mocks.userServices.getLiked.withArgs(user.get("id"), 1, null, null).promise().resolve(items)
expect(ctrl.hasNoResults).to.be.undefined
ctrl.loadItems().then () =>
expect(ctrl.hasNoResults).to.be.true
done()
describe "ProfileVoted", ->
$controller = null
provide = null
$rootScope = null
mocks = {}
user = Immutable.fromJS({id: 2})
_mockUserService = () ->
mocks.userServices = {
getVoted: sinon.stub()
}
provide.value "tgUserService", mocks.userServices
_mocks = () ->
module ($provide) ->
provide = $provide
_mockUserService()
return null
_inject = (callback) ->
inject (_$controller_, _$rootScope_) ->
$rootScope = _$rootScope_
$controller = _$controller_
beforeEach ->
module "taigaProfile"
_mocks()
_inject()
it "load paginated items", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileVoted", $scope, {user: user})
items1 = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
items2 = Immutable.fromJS({
data: [
{id: 4},
{id: 5},
],
next: false
})
mocks.userServices.getVoted.withArgs(user.get("id"), 1, null, null).promise().resolve(items1)
mocks.userServices.getVoted.withArgs(user.get("id"), 2, null, null).promise().resolve(items2)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.loadItems().then () =>
expectItems = items1.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.loadItems().then () =>
expectItems = expectItems.concat(items2.get("data"))
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.true
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
done()
it "filter items by text query", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileVoted", $scope, {user: user})
textQuery = "_test_"
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mocks.userServices.getVoted.withArgs(user.get("id"), 1, null, textQuery).promise().resolve(items)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.q = textQuery
ctrl.loadItems().then () =>
expectItems = items.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.equal(textQuery)
done()
it "show only items of user stories", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileVoted", $scope, {user: user})
type = "userstory"
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mocks.userServices.getVoted.withArgs(user.get("id"), 1, type, null).promise().resolve(items)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.showUserStoriesOnly().then () =>
expectItems = items.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.type
expect(ctrl.q).to.be.null
done()
it "show only items of tasks", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileVoted", $scope, {user: user})
type = "task"
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mocks.userServices.getVoted.withArgs(user.get("id"), 1, type, null).promise().resolve(items)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.showTasksOnly().then () =>
expectItems = items.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.type
expect(ctrl.q).to.be.null
done()
it "show only items of issues", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileVoted", $scope, {user: user})
type = "issue"
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mocks.userServices.getVoted.withArgs(user.get("id"), 1, type, null).promise().resolve(items)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.showIssuesOnly().then () =>
expectItems = items.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.type
expect(ctrl.q).to.be.null
done()
it "shou loading spinner during the call to the api", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileVoted", $scope, {user: user})
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mockPromise = mocks.userServices.getVoted.withArgs(user.get("id"), 1, null, null).promise()
expect(ctrl.isLoading).to.be.undefined
promise = ctrl.loadItems()
expect(ctrl.isLoading).to.be.true
mockPromise.resolve(items)
promise.then () =>
expect(ctrl.isLoading).to.be.false
done()
it "shou no results placeholder", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileVoted", $scope, {user: user})
items = Immutable.fromJS({
data: [],
next: false
})
mocks.userServices.getVoted.withArgs(user.get("id"), 1, null, null).promise().resolve(items)
expect(ctrl.hasNoResults).to.be.undefined
ctrl.loadItems().then () =>
expect(ctrl.hasNoResults).to.be.true
done()
describe "ProfileWatched", ->
$controller = null
provide = null
$rootScope = null
mocks = {}
user = Immutable.fromJS({id: 2})
_mockUserService = () ->
mocks.userServices = {
getWatched: sinon.stub()
}
provide.value "tgUserService", mocks.userServices
_mocks = () ->
module ($provide) ->
provide = $provide
_mockUserService()
return null
_inject = (callback) ->
inject (_$controller_, _$rootScope_) ->
$rootScope = _$rootScope_
$controller = _$controller_
beforeEach ->
module "taigaProfile"
_mocks()
_inject()
it "load paginated items", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileWatched", $scope, {user: user})
items1 = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
items2 = Immutable.fromJS({
data: [
{id: 4},
{id: 5},
],
next: false
})
mocks.userServices.getWatched.withArgs(user.get("id"), 1, null, null).promise().resolve(items1)
mocks.userServices.getWatched.withArgs(user.get("id"), 2, null, null).promise().resolve(items2)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.loadItems().then () =>
expectItems = items1.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.loadItems().then () =>
expectItems = expectItems.concat(items2.get("data"))
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.true
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
done()
it "filter items by text query", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileWatched", $scope, {user: user})
textQuery = "_test_"
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mocks.userServices.getWatched.withArgs(user.get("id"), 1, null, textQuery).promise().resolve(items)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.q = textQuery
ctrl.loadItems().then () =>
expectItems = items.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.equal(textQuery)
done()
it "show only items of projects", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileWatched", $scope, {user: user})
type = "project"
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mocks.userServices.getWatched.withArgs(user.get("id"), 1, type, null).promise().resolve(items)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.showProjectsOnly().then () =>
expectItems = items.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.type
expect(ctrl.q).to.be.null
done()
it "show only items of user stories", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileWatched", $scope, {user: user})
type = "userstory"
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mocks.userServices.getWatched.withArgs(user.get("id"), 1, type, null).promise().resolve(items)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.showUserStoriesOnly().then () =>
expectItems = items.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.type
expect(ctrl.q).to.be.null
done()
it "show only items of tasks", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileWatched", $scope, {user: user})
type = "task"
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mocks.userServices.getWatched.withArgs(user.get("id"), 1, type, null).promise().resolve(items)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.showTasksOnly().then () =>
expectItems = items.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.type
expect(ctrl.q).to.be.null
done()
it "show only items of issues", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileWatched", $scope, {user: user})
type = "issue"
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mocks.userServices.getWatched.withArgs(user.get("id"), 1, type, null).promise().resolve(items)
expect(ctrl.items.size).to.be.equal(0)
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.null
expect(ctrl.q).to.be.null
ctrl.showIssuesOnly().then () =>
expectItems = items.get("data")
expect(ctrl.items.equals(expectItems)).to.be.true
expect(ctrl.scrollDisabled).to.be.false
expect(ctrl.type).to.be.type
expect(ctrl.q).to.be.null
done()
it "shou loading spinner during the call to the api", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileWatched", $scope, {user: user})
items = Immutable.fromJS({
data: [
{id: 1},
{id: 2},
{id: 3}
],
next: true
})
mockPromise = mocks.userServices.getWatched.withArgs(user.get("id"), 1, null, null).promise()
expect(ctrl.isLoading).to.be.undefined
promise = ctrl.loadItems()
expect(ctrl.isLoading).to.be.true
mockPromise.resolve(items)
promise.then () =>
expect(ctrl.isLoading).to.be.false
done()
it "shou no results placeholder", (done) ->
$scope = $rootScope.$new()
ctrl = $controller("ProfileWatched", $scope, {user: user})
items = Immutable.fromJS({
data: [],
next: false
})
mocks.userServices.getWatched.withArgs(user.get("id"), 1, null, null).promise().resolve(items)
expect(ctrl.hasNoResults).to.be.undefined
ctrl.loadItems().then () =>
expect(ctrl.hasNoResults).to.be.true
done()

View File

@ -0,0 +1,50 @@
base = {
scope: {},
bindToController: {
user: "="
type: "@"
q: "@"
scrollDisabled: "@"
isLoading: "@"
hasNoResults: "@"
}
controller: null, # Define in directives
controllerAs: "vm",
templateUrl: "profile/profile-favs/profile-favs.html",
}
####################################################
## Liked
####################################################
ProfileLikedDirective = () ->
return _.extend({}, base, {
controller: "ProfileLiked"
})
angular.module("taigaProfile").directive("tgProfileLiked", ProfileLikedDirective)
####################################################
## Voted
####################################################
ProfileVotedDirective = () ->
return _.extend({}, base, {
controller: "ProfileVoted"
})
angular.module("taigaProfile").directive("tgProfileVoted", ProfileVotedDirective)
####################################################
## Watched
####################################################
ProfileWatchedDirective = () ->
return _.extend({}, base, {
controller: "ProfileWatched"
})
angular.module("taigaProfile").directive("tgProfileWatched", ProfileWatchedDirective)

View File

@ -0,0 +1,80 @@
section.profile-favs
div.profile-filter
div.searchbox(ng-if="::vm.enableFilterByTextQuery")
span.icon-search
input(
type="text"
ng-model="vm.q"
ng-change="vm.filterByTextQuery()"
placeholder="{{ 'USER.PROFILE_FAVS.FILTER_INPUT_PLACEHOLDER'|translate }}"
)
div.filters
a(
href=""
ng-if="::vm.enableFilterByAll"
ng-click="vm.showAll()"
ng-class="{active: vm.type === null}"
title="{{ 'USER.PROFILE_FAVS.FILTER_TYPE_ALL_TITLE'|translate }}"
translate="{{ 'USER.PROFILE_FAVS.FILTER_TYPE_ALL'|translate }}"
)
a(
href=""
ng-if="::vm.enableFilterByProjects"
ng-click="vm.showProjectsOnly()"
ng-class="{active: vm.type === 'project'}"
title="{{ 'USER.PROFILE_FAVS.FILTER_TYPE_PROJECTS_TITLE'|translate }}"
translate="{{ 'USER.PROFILE_FAVS.FILTER_TYPE_PROJECTS'|translate }}"
)
a(
href=""
ng-if="::vm.enableFilterByUserStories"
ng-click="vm.showUserStoriesOnly()"
ng-class="{active: vm.type === 'userstory'}",
title="{{ 'USER.PROFILE_FAVS.FILTER_TYPE_USER_STORIES_TITLE'|translate }}"
translate="{{ 'USER.PROFILE_FAVS.FILTER_TYPE_USER_STORIES'|translate }}"
)
a(
href=""
ng-if="::vm.enableFilterByTasks"
ng-click="vm.showTasksOnly()"
ng-class="{active: vm.type === 'task'}"
title="{{ 'USER.PROFILE_FAVS.FILTER_TYPE_TASKS_TITLE'|translate }}"
translate="{{ 'USER.PROFILE_FAVS.FILTER_TYPE_TASKS'|translate }}"
)
a(
href=""
ng-if="::vm.enableFilterByIssues"
ng-click="vm.showIssuesOnly()"
ng-class="{active: vm.type === 'issue'}"
title="{{ 'USER.PROFILE_FAVS.FILTER_TYPE_ISSUES_TITLE'|translate }}"
translate="{{ 'USER.PROFILE_FAVS.FILTER_TYPE_ISSUES'|translate }}"
)
div(
infinite-scroll="vm.loadItems()"
infinite-scroll-distance="2"
infinite-scroll-disabled="vm.scrollDisabled"
)
div(
tg-repeat="item in vm.items track by $index"
ng-switch="item.get('type')"
)
div(ng-switch-when="project", tg-fav-item="item", item-type="project")
div(ng-switch-when="userstory", tg-fav-item="item", item-type="userstory")
div(ng-switch-when="task", tg-fav-item="item", item-type="task")
div(ng-switch-when="issue", tg-fav-item="item", item-type="issue")
div(ng-if="vm.isLoading")
div.spin
img(
src="/svg/spinner-circle.svg"
alt="{{ 'COMMON.LOADING'|translate }}"
)
.empty-search-results(ng-if="vm.hasNoResults && !vm.isLoading")
img(
src="../../images/search-empty.png"
alt="{{ 'USER.PROFILE_FAVS.EMPTY_TITLE' | translate }}"
)
p.title {{ 'USER.PROFILE_FAVS.EMPTY_TITLE' | translate }}

View File

@ -0,0 +1,43 @@
.profile-favs {
border-top: 1px solid $whitish;
}
.profile-filter {
align-items: center;
background: $whitish;
display: flex;
justify-content: space-between;
margin: 1rem 0;
padding: .5rem 1rem;
.searchbox {
align-items: center;
display: flex;
flex: 1;
.icon-search {
color: grayer;
margin-right: .5rem;
}
input {
border: 0;
border-bottom: 1px solid transparent;
flex: 1;
margin-right: 1rem;
&:focus {
border-bottom: 1px solid $gray-light;
outline: none;
transition: border-bottom .3s ease-in;
}
}
}
.filters {
a {
color: $gray-light;
display: inline-block;
padding: 0 .5rem;
&:hover,
&.active {
color: $blackish;
}
}
}
}

View File

@ -3,37 +3,56 @@ section.profile-projects
div.spin div.spin
img(src="/svg/spinner-circle.svg", alt="Loading...") img(src="/svg/spinner-circle.svg", alt="Loading...")
div.empty-tab(ng-if="vm.projects && !vm.projects.size") .empty-tab(ng-if="vm.projects && !vm.projects.size")
include ../../../svg/hide.svg include ../../../svg/hide.svg
p(translate="USER.PROFILE.PROJECTS_EMPTY", translate-values="{username: vm.user.get('full_name_display')}") p(
translate="USER.PROFILE.PROJECTS_EMPTY"
translate-values="{username: vm.user.get('full_name_display')}"
)
div.project-list-single(tg-repeat="project in vm.projects") .list-itemtype-project(tg-repeat="project in vm.projects")
div.project-list-single-left .list-itemtype-project-left
div.project-list-single-title .project-list-single-title
h1 h2
a(href="#", tg-nav="project:project=project.get('slug')", title="{{ ::project.get('name') }}") {{::project.get('name')}} a(
href="#"
tg-nav="project:project=project.get('slug')"
title="{{ ::project.get('name') }}"
) {{::project.get('name')}}
p {{ ::project.get('description') | limitTo:300 }} p {{ ::project.get('description') | limitTo:300 }}
div.project-list-single-tags.tags-container(ng-if="::project.get('tags').size") .list-itemtype-project-tags.tags-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(
style='border-left: 5px solid {{::tag.get("color")}};'
tg-repeat="tag in ::project.get('colorized_tags')"
)
span.tag-name {{::tag.get('name')}} span.tag-name {{::tag.get('name')}}
div.project-list-single-right .list-itemtype-project-right
div.project-list-single-members .list-itemtype-track
a(tg-repeat="contact in ::project.get('contacts')", tg-nav="user-profile:username=contact.get('username')", title="{{::contact.get('full_name')}}") span.list-itemtype-track-likers(
ng-class="{'active': project.get('is_fan')}"
title="{{ 'PROJECT.LIKE_BUTTON.COUNTER_TITLE'|translate:{total:project.get(\"total_fans\")||0}:'messageformat' }}"
)
span.icon
include ../../../svg/like.svg
span {{ ::project.get('total_fans') }}
span.list-itemtype-track-watchers(
ng-class="{'active': project.get('is_watcher')}"
title="{{ 'PROJECT.WATCH_BUTTON.COUNTER_TITLE'|translate:{total:project.get(\"total_watchers\")||0}:'messageformat' }}"
)
span.icon
include ../../../svg/watch.svg
span {{ ::project.get('total_watchers') }}
.list-itemtype-project-members
a(
tg-repeat="contact in ::project.get('contacts')"
tg-nav="user-profile:username=contact.get('username')"
title="{{::contact.get('full_name')}}"
)
img(ng-src="{{::contact.get('photo')}}") img(ng-src="{{::contact.get('photo')}}")
// div.project-list-single-right
// div.project-list-single-stats
// div.stat-comments(title="2 comments")
// span.icon.icon-comment
// span.stat-num 2
// div.stat-favorite.active(title="2 favorites")
// span.icon.icon-star-fill
// span.stat-num 4
// div.stat-viewer(title="2 followers")
// span.icon.icon-open-eye
// span.stat-num 4

View File

@ -2,10 +2,12 @@ ProfileTabDirective = () ->
link = (scope, element, attrs, ctrl, transclude) -> link = (scope, element, attrs, ctrl, transclude) ->
scope.tab = {} scope.tab = {}
attrs.$observe "tgProfileTab", (name) ->
scope.tab.name = name
attrs.$observe "tabTitle", (title) -> attrs.$observe "tabTitle", (title) ->
scope.tab.title = title scope.tab.title = title
scope.tab.name = attrs.tgProfileTab
scope.tab.icon = attrs.tabIcon scope.tab.icon = attrs.tabIcon
scope.tab.active = !!attrs.tabActive scope.tab.active = !!attrs.tabActive

View File

@ -1,7 +1,13 @@
div div
nav.profile-content-tabs nav.profile-content-tabs
a.tab(ng-repeat="tab in ::vm.tabs", href="", title="{{tab.title}}", ng-class="{active: tab.active}" ng-click="vm.toggleTab(tab)") a.tab(
href=""
ng-repeat="tab in ::vm.tabs"
title="{{tab.title}}"
ng-click="vm.toggleTab(tab)"
ng-class="{active: tab.active}"
)
span.icon(ng-class="::tab.icon") span.icon(ng-class="::tab.icon")
span {{::tab.name}} span {{::tab.name}}
ng-transclude ng-transclude

View File

@ -2,16 +2,48 @@ div.profile.centered(ng-if="vm.user")
div(tg-profile-bar, user="vm.user", isCurrentUser="vm.isCurrentUser") div(tg-profile-bar, user="vm.user", isCurrentUser="vm.isCurrentUser")
div.main div.main
div.timeline-wrapper(tg-profile-tabs) div.timeline-wrapper(tg-profile-tabs)
div(tg-profile-tab="activity", tab-title="{{'USER.PROFILE.ACTIVITY_TAB' | translate}}", tab-icon="icon-timeline", tab-active) div(
div(tg-user-timeline, user="vm.user", current-user="vm.isCurrentUser") tg-profile-tab="{{'USER.PROFILE.TABS.ACTIVITY_TAB' | translate}}"
tab-title="{{'USER.PROFILE.TABS.ACTIVITY_TAB_TITLE' | translate}}"
tab-icon="icon-timeline"
tab-active
)
div(tg-user-timeline, user="vm.user", current-user="vm.isCurrentUser")
div(tab-disabled="{{vm.isCurrentUser}}", tg-profile-tab="projects", tab-title="{{'USER.PROFILE.PROJECTS_TAB' | translate}}", tab-icon="icon-project") div(
div(tg-profile-projects, user="vm.user") tg-profile-tab="{{'USER.PROFILE.TABS.PROJECTS_TAB' | translate}}"
tab-title="{{'USER.PROFILE.TABS.PROJECTS_TAB_TITLE' | translate}}"
tab-icon="icon-project"
tab-disabled="{{vm.isCurrentUser}}"
)
div(tg-profile-projects, user="vm.user")
div(tg-profile-tab="contacts", tab-title="{{'USER.PROFILE.CONTACTS_TAB' | translate}}", tab-icon="icon-team") div(
div(tg-profile-contacts, user="vm.user") tg-profile-tab="{{'USER.PROFILE.TABS.LIKES_TAB' | translate}}"
tab-title="{{'USER.PROFILE.TABS.LIKES_TAB_TITLE' | translate}}"
tab-icon="icon-heart"
)
div(tg-profile-liked, user="vm.user")
// div(tg-profile-tab="favorites", tab-title="{{'USER.PROFILE.FAVORITES_TAB' | translate}}", tab-icon="icon-star-fill") div(
// include includes/profile-favorites tg-profile-tab="{{'USER.PROFILE.TABS.VOTES_TAB' | translate}}"
tab-title="{{'USER.PROFILE.TABS.VOTES_TAB_TITLE' | translate}}"
tab-icon="icon-caret-up"
)
div(tg-profile-voted, user="vm.user")
div(
tg-profile-tab="{{'USER.PROFILE.TABS.WATCHED_TAB' | translate}}"
tab-title="{{'USER.PROFILE.TABS.WATCHED_TAB_TITLE' | translate}}"
tab-icon="icon-eye"
)
div(tg-profile-watched, user="vm.user")
div(
tg-profile-tab="{{'USER.PROFILE.TABS.CONTACTS_TAB' | translate}}"
tab-title="{{'USER.PROFILE.TABS.CONTACTS_TAB_TITLE' | translate}}"
tab-icon="icon-team"
)
div(tg-profile-contacts, user="vm.user")
include includes/profile-sidebar include includes/profile-sidebar

View File

@ -1,21 +0,0 @@
.profile-favorites {
border-top: 1px solid $whitish;
display: flex;
flex-direction: column;
.profile-favorites-filters {
align-self: flex-start;
display: flex;
a {
border-bottom: 2px solid $white;
color: $gray-light;
display: inline-block;
padding: 1rem 1.5rem;
transition: all .2s linear;
&:hover,
&.active {
border-bottom: 2px solid $gray-light;
color: $primary;
}
}
}
}

View File

@ -51,6 +51,25 @@ Resource = (urlsService, http, paginateResponseService) ->
result = Immutable.fromJS(result) result = Immutable.fromJS(result)
return paginateResponseService(result) return paginateResponseService(result)
service.likeProject = (projectId) ->
url = urlsService.resolve("project-like", projectId)
return http.post(url)
service.unlikeProject = (projectId) ->
url = urlsService.resolve("project-unlike", projectId)
return http.post(url)
service.watchProject = (projectId, notifyPolicy) ->
data = {
notify_policy: notifyPolicy
}
url = urlsService.resolve("project-watch", projectId)
return http.post(url, data)
service.unwatchProject = (projectId) ->
url = urlsService.resolve("project-unwatch", projectId)
return http.post(url)
return () -> return () ->
return {"projects": service} return {"projects": service}

View File

@ -19,7 +19,7 @@ Resource = (urlsService, http, paginateResponseService) ->
return Immutable.fromJS(result.data) return Immutable.fromJS(result.data)
service.getStats = (userId) -> service.getStats = (userId) ->
url = urlsService.resolve("stats", userId) url = urlsService.resolve("user-stats", userId)
httpOptions = { httpOptions = {
headers: { headers: {
@ -32,7 +32,7 @@ Resource = (urlsService, http, paginateResponseService) ->
return Immutable.fromJS(result.data) return Immutable.fromJS(result.data)
service.getContacts = (userId) -> service.getContacts = (userId) ->
url = urlsService.resolve("contacts", userId) url = urlsService.resolve("user-contacts", userId)
httpOptions = { httpOptions = {
headers: { headers: {
@ -44,6 +44,45 @@ Resource = (urlsService, http, paginateResponseService) ->
.then (result) -> .then (result) ->
return Immutable.fromJS(result.data) return Immutable.fromJS(result.data)
service.getLiked = (userId, page, type, q) ->
url = urlsService.resolve("user-liked", userId)
params = {}
params.page = page if page?
params.type = type if type?
params.q = q if q?
return http.get(url, params)
.then (result) ->
result = Immutable.fromJS(result)
return paginateResponseService(result)
service.getVoted = (userId, page, type, q) ->
url = urlsService.resolve("user-voted", userId)
params = {}
params.page = page if page?
params.type = type if type?
params.q = q if q?
return http.get(url, params)
.then (result) ->
result = Immutable.fromJS(result)
return paginateResponseService(result)
service.getWatched = (userId, page, type, q) ->
url = urlsService.resolve("user-watched", userId)
params = {}
params.page = page if page?
params.type = type if type?
params.q = q if q?
return http.get(url, params)
.then (result) ->
result = Immutable.fromJS(result)
return paginateResponseService(result)
service.getProfileTimeline = (userId, page) -> service.getProfileTimeline = (userId, page) ->
params = { params = {
page: page page: page

View File

@ -3,7 +3,13 @@ taiga = @.taiga
truncate = taiga.truncate truncate = taiga.truncate
class AppMetaService extends taiga.Service = -> class AppMetaService
@.$inject = [
"$rootScope"
]
constructor: (@rootScope) ->
_set: (key, value) -> _set: (key, value) ->
return if not key return if not key
@ -60,12 +66,19 @@ class AppMetaService extends taiga.Service = ->
@.setOpenGraphMetas(title, description) @.setOpenGraphMetas(title, description)
addMobileViewport: () -> addMobileViewport: () ->
$('head').append( $("head").append(
'<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">' "<meta name=\"viewport\"
content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\">"
) )
removeMobileViewport: () -> removeMobileViewport: () ->
$('meta[name="viewport"]').remove() $("meta[name=\"viewport\"]").remove()
setfn: (fn) ->
@._listener() if @.listener
@._listener = @rootScope.$watchCollection fn, (metas) =>
@.setAll(metas.title, metas.description)
angular.module("taigaCommon").service("tgAppMetaService", AppMetaService) angular.module("taigaCommon").service("tgAppMetaService", AppMetaService)

View File

@ -1,13 +1,15 @@
describe "AppMetaService", -> describe "AppMetaService", ->
appMetaService = null appMetaService = null
$rootScope = null
data = { data = {
title: "--title--", title: "--title--",
description: "--description--" description: "--description--"
} }
_inject = () -> _inject = () ->
inject (_tgAppMetaService_) -> inject (_tgAppMetaService_, _$rootScope_) ->
appMetaService = _tgAppMetaService_ appMetaService = _tgAppMetaService_
$rootScope = _$rootScope_
beforeEach -> beforeEach ->
module "taigaCommon" module "taigaCommon"
@ -53,3 +55,18 @@ describe "AppMetaService", ->
expect($("meta[property='og:description']")).to.have.attr("content", data.description) expect($("meta[property='og:description']")).to.have.attr("content", data.description)
expect($("meta[property='og:image']")).to.have.attr("content", "#{window.location.origin}/images/logo-color.png") expect($("meta[property='og:image']")).to.have.attr("content", "#{window.location.origin}/images/logo-color.png")
expect($("meta[property='og:url']")).to.have.attr("content", window.location.href) expect($("meta[property='og:url']")).to.have.attr("content", window.location.href)
it "set function to set the metas", () ->
fn = () ->
return {
title: 'test',
description: 'test2'
}
appMetaService.setAll = sinon.stub()
appMetaService.setfn(fn)
$rootScope.$digest()
expect(appMetaService.setAll).to.have.been.calledWith('test', 'test2')

View File

@ -49,13 +49,7 @@ class CurrentUserService
loadProjects: () -> loadProjects: () ->
return @projectsService.getProjectsByUserId(@._user.get("id")) return @projectsService.getProjectsByUserId(@._user.get("id"))
.then (projects) => .then (projects) => @.setProjects(projects)
@._projects = @._projects.set("all", projects)
@._projects = @._projects.set("recents", projects.slice(0, 10))
@._projectsById = Immutable.fromJS(groupBy(projects.toJS(), (p) -> p.id))
return @.projects
disableJoyRide: (section) -> disableJoyRide: (section) ->
if section if section
@ -96,4 +90,12 @@ class CurrentUserService
@.loadProjects() @.loadProjects()
]) ])
setProjects: (projects) ->
@._projects = @._projects.set("all", projects)
@._projects = @._projects.set("recents", projects.slice(0, 10))
@._projectsById = Immutable.fromJS(groupBy(projects.toJS(), (p) -> p.id))
return @.projects
angular.module("taigaCommon").service("tgCurrentUserService", CurrentUserService) angular.module("taigaCommon").service("tgCurrentUserService", CurrentUserService)

View File

@ -92,7 +92,7 @@ describe "tgCurrentUserService", ->
it "bulkUpdateProjectsOrder and reload projects", (done) -> it "bulkUpdateProjectsOrder and reload projects", (done) ->
fakeData = [{id: 1, id: 2}] fakeData = [{id: 1, id: 2}]
currentUserService.loadProjects = sinon.spy() currentUserService.loadProjects = sinon.stub()
mocks.projectsService.bulkUpdateProjectsOrder.withArgs(fakeData).promise().resolve() mocks.projectsService.bulkUpdateProjectsOrder.withArgs(fakeData).promise().resolve()
@ -101,6 +101,41 @@ describe "tgCurrentUserService", ->
done() done()
it "loadProject and set it", (done) ->
user = Immutable.fromJS({id: 1, name: "fake1"})
project = Immutable.fromJS({id: 2, name: "fake2"})
currentUserService._user = user
currentUserService.setProjects = sinon.stub()
mocks.projectsService.getProjectsByUserId.withArgs(1).promise().resolve(project)
currentUserService.loadProjects().then () ->
expect(currentUserService.setProjects).to.have.been.calledWith(project)
done()
it "setProject", () ->
projectsRaw = [
{id: 1, name: "fake1"},
{id: 2, name: "fake2"},
{id: 3, name: "fake3"},
{id: 4, name: "fake4"}
]
projectsRawById = {
1: {id: 1, name: "fake1"},
2: {id: 2, name: "fake2"},
3: {id: 3, name: "fake3"},
4: {id: 4, name: "fake4"}
}
projects = Immutable.fromJS(projectsRaw)
currentUserService.setProjects(projects)
expect(currentUserService.projects.get('all').toJS()).to.be.eql(projectsRaw)
expect(currentUserService.projects.get('recents').toJS()).to.be.eql(projectsRaw)
expect(currentUserService.projectsById.toJS()).to.be.eql(projectsRawById)
it "is authenticated", () -> it "is authenticated", () ->
currentUserService.getUser = sinon.stub() currentUserService.getUser = sinon.stub()
currentUserService.getUser.returns({}) currentUserService.getUser.returns({})

View File

@ -1,9 +1,12 @@
taiga = @.taiga taiga = @.taiga
bindMethods = taiga.bindMethods
class UserService extends taiga.Service class UserService extends taiga.Service
@.$inject = ["tgResources"] @.$inject = ["tgResources"]
constructor: (@rs) -> constructor: (@rs) ->
bindMethods(@)
getUserByUserName: (username) -> getUserByUserName: (username) ->
return @rs.users.getUserByUsername(username) return @rs.users.getUserByUsername(username)
@ -11,6 +14,15 @@ class UserService extends taiga.Service
getContacts: (userId) -> getContacts: (userId) ->
return @rs.users.getContacts(userId) return @rs.users.getContacts(userId)
getLiked: (userId, pageNumber, objectType, textQuery) ->
return @rs.users.getLiked(userId, pageNumber, objectType, textQuery)
getVoted: (userId, pageNumber, objectType, textQuery) ->
return @rs.users.getVoted(userId, pageNumber, objectType, textQuery)
getWatched: (userId, pageNumber, objectType, textQuery) ->
return @rs.users.getWatched(userId, pageNumber, objectType, textQuery)
getStats: (userId) -> getStats: (userId) ->
return @rs.users.getStats(userId) return @rs.users.getStats(userId)

View File

@ -32,19 +32,6 @@ describe "UserService", ->
_mocks() _mocks()
_inject() _inject()
it "get user contacts", () ->
userId = 2
contacts = [
{id: 1},
{id: 2},
{id: 3}
]
mocks.resources.users.getContacts.withArgs(userId).returns(true)
expect(userService.getContacts(userId)).to.be.true
it "attach user contacts to projects", (done) -> it "attach user contacts to projects", (done) ->
userId = 2 userId = 2
@ -88,6 +75,75 @@ describe "UserService", ->
$rootScope.$apply() $rootScope.$apply()
it "get user liked", (done) ->
userId = 2
pageNumber = 1
objectType = null
textQuery = null
liked = [
{id: 1},
{id: 2},
{id: 3}
]
mocks.resources.users.getLiked = sinon.stub()
mocks.resources.users.getLiked.withArgs(userId, pageNumber, objectType, textQuery)
.promise()
.resolve(liked)
userService.getLiked(userId, pageNumber, objectType, textQuery).then (_liked_) ->
expect(_liked_).to.be.eql(liked)
done()
$rootScope.$apply()
it "get user voted", (done) ->
userId = 2
pageNumber = 1
objectType = null
textQuery = null
voted = [
{id: 1},
{id: 2},
{id: 3}
]
mocks.resources.users.getVoted = sinon.stub()
mocks.resources.users.getVoted.withArgs(userId, pageNumber, objectType, textQuery)
.promise()
.resolve(voted)
userService.getVoted(userId, pageNumber, objectType, textQuery).then (_voted_) ->
expect(_voted_).to.be.eql(voted)
done()
$rootScope.$apply()
it "get user watched", (done) ->
userId = 2
pageNumber = 1
objectType = null
textQuery = null
watched = [
{id: 1},
{id: 2},
{id: 3}
]
mocks.resources.users.getWatched = sinon.stub()
mocks.resources.users.getWatched.withArgs(userId, pageNumber, objectType, textQuery)
.promise()
.resolve(watched)
userService.getWatched(userId, pageNumber, objectType, textQuery).then (_watched_) ->
expect(_watched_).to.be.eql(watched)
done()
$rootScope.$apply()
it "get user by username", (done) -> it "get user by username", (done) ->
username = "username-1" username = "username-1"