user profile

stable
Juanfran 2015-05-26 12:36:45 +02:00
parent f597db104e
commit 4f0cac687d
19 changed files with 172 additions and 123 deletions

View File

@ -31,6 +31,7 @@ urls = {
# User # User
"users": "/users" "users": "/users"
"by_username": "/users/by_username"
"users-password-recovery": "/users/password_recovery" "users-password-recovery": "/users/password_recovery"
"users-change-password-from-recovery": "/users/change_password_from_recovery" "users-change-password-from-recovery": "/users/change_password_from_recovery"
"users-change-password": "/users/change_password" "users-change-password": "/users/change_password"
@ -145,10 +146,6 @@ urls = {
# locales # locales
"locales": "/locales" "locales": "/locales"
# users
"contacts": "/users/%s/contacts"
"stats": "/users/%s/stats"
} }
# Initialize api urls service # Initialize api urls service

View File

@ -1,8 +1,9 @@
aside.profile-sidebar aside.profile-sidebar
h4(translate="USER.PROFILE_SIDEBAR.TITLE") div(ng-if="::vm.isCurrentUser")
p(translate="USER.PROFILE_SIDEBAR.DESCRIPTION") h4(translate="USER.PROFILE_SIDEBAR.TITLE")
a.trans-button p(translate="USER.PROFILE_SIDEBAR.DESCRIPTION")
span(translate="USER.PROFILE_SIDEBAR.ADD_INFO") a.trans-button
span(translate="USER.PROFILE_SIDEBAR.ADD_INFO")
h4 h4
span.icon.icon-help span.icon.icon-help

View File

@ -1,16 +1,13 @@
class ProfileBarController class ProfileBarController
@.$inject = [ @.$inject = [
"$tgAuth",
"tgUserService" "tgUserService"
] ]
constructor: (@auth, @userService) -> constructor: (@userService) ->
@.user = @auth.getUser()
@.loadStats() @.loadStats()
loadStats: () -> loadStats: () ->
return @userService.getStats(@.user.id).then (stats) => return @userService.getStats(@.user.get("id")).then (stats) =>
@.stats = stats @.stats = stats
angular.module("taigaProfile").controller("ProfileBar", ProfileBarController) angular.module("taigaProfile").controller("ProfileBar", ProfileBarController)

View File

@ -1,37 +1,26 @@
describe "ProfileBar", -> describe "ProfileBar", ->
$controller = null $controller = null
$q = null
provide = null provide = null
$rootScope = null $rootScope = null
mocks = {} mocks = {}
_mockUserService = () -> _mockUserService = () ->
mocks.userService = { mocks.userService = {
getStats: sinon.stub() getStats: sinon.stub().promise()
} }
provide.value "tgUserService", mocks.userService provide.value "tgUserService", mocks.userService
_mockAuthService = () ->
stub = sinon.stub()
stub.returns({id: 2})
provide.value "$tgAuth", {
getUser: stub
}
_mocks = () -> _mocks = () ->
module ($provide) -> module ($provide) ->
provide = $provide provide = $provide
_mockUserService() _mockUserService()
_mockAuthService()
return null return null
_inject = (callback) -> _inject = (callback) ->
inject (_$controller_, _$q_, _$rootScope_) -> inject (_$controller_, _$rootScope_) ->
$q = _$q_
$rootScope = _$rootScope_ $rootScope = _$rootScope_
$controller = _$controller_ $controller = _$controller_
@ -40,7 +29,7 @@ describe "ProfileBar", ->
_mocks() _mocks()
_inject() _inject()
it "user stats filled", () -> it "user stats filled", (done) ->
userId = 2 userId = 2
stats = Immutable.fromJS([ stats = Immutable.fromJS([
{id: 1}, {id: 1},
@ -48,14 +37,15 @@ describe "ProfileBar", ->
{id: 3} {id: 3}
]) ])
mocks.userService.getStats = (userId) -> mocks.userService.getStats.withArgs(userId).resolve(stats)
expect(userId).to.be.equal(userId)
return $q (resolve, reject) -> $scope = $rootScope.$new
resolve(stats)
ctrl = $controller("ProfileBar") ctrl = $controller("ProfileBar", $scope, {
user: Immutable.fromJS(id: userId)
})
$rootScope.$apply() setTimeout ( ->
expect(ctrl.stats.toJS()).to.be.eql(stats.toJS())
expect(ctrl.stats.toJS()).to.be.eql(stats.toJS()) done()
)

View File

@ -3,7 +3,11 @@ ProfileBarDirective = () ->
templateUrl: "profile/profile-bar/profile-bar.html", templateUrl: "profile/profile-bar/profile-bar.html",
controller: "ProfileBar", controller: "ProfileBar",
controllerAs: "vm", controllerAs: "vm",
scope: {} scope: {
user: "=user",
isCurrentUser: "=iscurrentuser"
},
bindToController: true
} }

View File

@ -1,11 +1,11 @@
section.profile-bar section.profile-bar
div.profile-image-wrapper div.profile-image-wrapper(ng-class="::{'is-current-user': vm.isCurrentUser}")
img.profile-img(ng-src="{{::vm.user.big_photo}}", alt="{{::vm.user.full_name}}") img.profile-img(ng-src="{{::vm.user.get('big_photo')}}", alt="{{::vm.user.get('full_name')}}")
a.profile-edition(title="{{ 'USER.PROFILE.EDIT' | translate }}", tg-nav="user-settings-user-profile", translate="USER.PROFILE.EDIT") a.profile-edition(title="{{ 'USER.PROFILE.EDIT' | translate }}", tg-nav="user-settings-user-profile", translate="USER.PROFILE.EDIT")
// a.button-green // a.button-green
// span(translate="USER.PROFILE.FOLLOW") // span(translate="USER.PROFILE.FOLLOW")
div.profile-data div.profile-data
h1 {{::vm.user.full_name}} h1 {{::vm.user.get("full_name")}}
h2 {{::vm.stats.get('roles').join(", ")}} h2 {{::vm.stats.get('roles').join(", ")}}
// div.location // div.location
// include ../../../svg/location.svg // include ../../../svg/location.svg
@ -33,5 +33,5 @@ section.profile-bar
// div.organization // div.organization
// div.organization // div.organization
div.profile-quote(ng-if="::vm.user.bio") div.profile-quote(ng-if="::vm.user.get('bio')")
span {{::vm.user.bio | limitTo:210 }}{{vm.user.bio.length < 210 ? '' : '...'}} span {{::vm.user.get("bio") | limitTo:210 }}{{vm.user.get("bio").length < 210 ? '' : '...'}}

View File

@ -1,15 +1,12 @@
class ProfileContactsController class ProfileContactsController
@.$inject = [ @.$inject = [
"tgUserService", "tgUserService"
"$tgAuth"
] ]
constructor: (@userService, @auth) -> constructor: (@userService) ->
loadContacts: () -> loadContacts: () ->
userId = @auth.getUser().id @userService.getContacts(@.userId)
@userService.getContacts(userId)
.then (contacts) => .then (contacts) =>
@.contacts = contacts @.contacts = contacts

View File

@ -1,37 +1,25 @@
describe "ProfileContacts", -> describe "ProfileContacts", ->
$controller = null $controller = null
$q = null
provide = null provide = null
$rootScope = null $rootScope = null
mocks = {} mocks = {}
_mockUserService = () -> _mockUserService = () ->
mocks.userServices = { mocks.userServices = {
getContacts: sinon.stub() getContacts: sinon.stub().promise()
} }
provide.value "tgUserService", mocks.userServices provide.value "tgUserService", mocks.userServices
_mockAuthService = () ->
stub = sinon.stub()
stub.returns({id: 2})
provide.value "$tgAuth", {
getUser: stub
}
_mocks = () -> _mocks = () ->
module ($provide) -> module ($provide) ->
provide = $provide provide = $provide
_mockUserService() _mockUserService()
_mockAuthService()
return null return null
_inject = (callback) -> _inject = (callback) ->
inject (_$controller_, _$q_, _$rootScope_) -> inject (_$controller_, _$rootScope_) ->
$q = _$q_
$rootScope = _$rootScope_ $rootScope = _$rootScope_
$controller = _$controller_ $controller = _$controller_
@ -48,16 +36,14 @@ describe "ProfileContacts", ->
{id: 3} {id: 3}
] ]
mocks.userServices.getContacts = (userId) -> mocks.userServices.getContacts.withArgs(userId).resolve(contacts)
expect(userId).to.be.equal(userId)
return $q (resolve, reject) -> $scope = $rootScope.$new()
resolve(contacts)
ctrl = $controller("ProfileContacts") ctrl = $controller("ProfileContacts", $scope, {
userId: userId
})
ctrl.loadContacts().then () -> ctrl.loadContacts().then () ->
expect(ctrl.contacts).to.be.equal(contacts) expect(ctrl.contacts).to.be.equal(contacts)
done() done()
$rootScope.$apply()

View File

@ -4,10 +4,13 @@ ProfileContactsDirective = () ->
return { return {
templateUrl: "profile/profile-contacts/profile-contacts.html", templateUrl: "profile/profile-contacts/profile-contacts.html",
scope: {}, scope: {
userId: "=userid"
},
controllerAs: "vm", controllerAs: "vm",
controller: "ProfileContacts", controller: "ProfileContacts",
link: link link: link,
bindToController: true
} }
angular.module("taigaProfile").directive("tgProfileContacts", ProfileContactsDirective) angular.module("taigaProfile").directive("tgProfileContacts", ProfileContactsDirective)

View File

@ -1,18 +1,15 @@
class ProfileProjectsController class ProfileProjectsController
@.$inject = [ @.$inject = [
"tgProjectsService", "tgProjectsService",
"tgUserService", "tgUserService"
"$tgAuth"
] ]
constructor: (@projectsService, @userService, @auth) -> constructor: (@projectsService, @userService) ->
loadProjects: () -> loadProjects: () ->
userId = @auth.getUser().id @projectsService.getProjectsByUserId(@.userId)
@projectsService.getProjectsByUserId(userId)
.then (projects) => .then (projects) =>
return @userService.attachUserContactsToProjects(userId, projects) return @userService.attachUserContactsToProjects(@.userId, projects)
.then (projects) => .then (projects) =>
@.projects = projects @.projects = projects

View File

@ -1,6 +1,5 @@
describe "ProfileProjects", -> describe "ProfileProjects", ->
$controller = null $controller = null
$q = null
provide = null provide = null
$rootScope = null $rootScope = null
mocks = {} mocks = {}
@ -14,7 +13,7 @@ describe "ProfileProjects", ->
_mockProjectsService = () -> _mockProjectsService = () ->
mocks.projectsService = { mocks.projectsService = {
getProjectsByUserId: sinon.stub() getProjectsByUserId: sinon.stub().promise()
} }
provide.value "tgProjectsService", mocks.projectsService provide.value "tgProjectsService", mocks.projectsService
@ -38,8 +37,7 @@ describe "ProfileProjects", ->
return null return null
_inject = (callback) -> _inject = (callback) ->
inject (_$controller_, _$q_, _$rootScope_) -> inject (_$controller_, _$rootScope_) ->
$q = _$q_
$rootScope = _$rootScope_ $rootScope = _$rootScope_
$controller = _$controller_ $controller = _$controller_
@ -62,18 +60,15 @@ describe "ProfileProjects", ->
{id: 3, contacts: "fake"} {id: 3, contacts: "fake"}
] ]
mocks.projectsService.getProjectsByUserId = (userId) -> mocks.projectsService.getProjectsByUserId.withArgs(userId).resolve(projects)
expect(userId).to.be.equal(userId)
return $q (resolve, reject) ->
resolve(projects)
mocks.userService.attachUserContactsToProjects.withArgs(userId, projects).returns(projectsWithContacts) mocks.userService.attachUserContactsToProjects.withArgs(userId, projects).returns(projectsWithContacts)
ctrl = $controller("ProfileProjects") $scope = $rootScope.$new()
ctrl = $controller("ProfileProjects", $scope, {
userId: userId
})
ctrl.loadProjects().then () -> ctrl.loadProjects().then () ->
expect(ctrl.projects).to.be.equal(projectsWithContacts) expect(ctrl.projects).to.be.equal(projectsWithContacts)
done() done()
$rootScope.$apply()

View File

@ -4,7 +4,9 @@ ProfileProjectsDirective = () ->
return { return {
templateUrl: "profile/profile-projects/profile-projects.html", templateUrl: "profile/profile-projects/profile-projects.html",
scope: {}, scope: {
userId: "=userid"
},
link: link link: link
bindToController: true, bindToController: true,
controllerAs: "vm", controllerAs: "vm",

View File

@ -1,16 +1,22 @@
class ProfilePageController extends taiga.Controller class ProfilePageController extends taiga.Controller
@.$inject = [ @.$inject = [
"$appTitle", "$appTitle",
"$tgAuth", "tgCurrentUserService",
"$routeParams" "$routeParams",
"tgUserService"
] ]
constructor: (@appTitle, @auth, @routeParams) -> constructor: (@appTitle, @currentUserService, @routeParams, @userService) ->
if @routeParams.slug if @routeParams.slug
@.user = @auth.userData @userService
.getUserByUserName(@routeParams.slug)
.then (user) =>
@.user = user
@.isCurrentUser = false
@appTitle.set(@.user.get('username'))
else else
@.user = @auth.userData @.user = @currentUserService.getUser()
@.isCurrentUser = true
@appTitle.set(@.user.get('username')) @appTitle.set(@.user.get('username'))
angular.module("taigaProfile").controller("Profile", ProfilePageController) angular.module("taigaProfile").controller("Profile", ProfilePageController)

View File

@ -1,7 +1,7 @@
describe "ProfileController", -> describe "ProfileController", ->
pageCtrl = null
provide = null provide = null
controller = null $controller = null
$rootScope = null
mocks = {} mocks = {}
projects = Immutable.fromJS([ projects = Immutable.fromJS([
@ -14,44 +14,89 @@ describe "ProfileController", ->
stub = sinon.stub() stub = sinon.stub()
mocks.appTitle = { mocks.appTitle = {
set: sinon.stub() set: sinon.spy()
} }
provide.value "$appTitle", mocks.appTitle provide.value "$appTitle", mocks.appTitle
_mockAuth = () -> _mockCurrentUser = () ->
stub = sinon.stub() stub = sinon.stub()
mocks.auth = { mocks.currentUser = {
userData: Immutable.fromJS({username: "UserName"}) getUser: sinon.stub()
} }
provide.value "$tgAuth", mocks.auth provide.value "tgCurrentUserService", mocks.currentUser
_mockUserService = () ->
stub = sinon.stub()
mocks.userService = {
getUserByUserName: sinon.stub().promise()
}
provide.value "tgUserService", mocks.userService
_mockRouteParams = () ->
stub = sinon.stub()
mocks.routeParams = {}
provide.value "$routeParams", mocks.routeParams
_mocks = () -> _mocks = () ->
module ($provide) -> module ($provide) ->
provide = $provide provide = $provide
_mockAppTitle() _mockAppTitle()
_mockAuth() _mockCurrentUser()
_mockRouteParams()
_mockUserService()
return null return null
_inject = (callback) ->
inject (_$controller_, _$rootScope_) ->
$rootScope = _$rootScope_
$controller = _$controller_
beforeEach -> beforeEach ->
module "taigaProfile" module "taigaProfile"
_mocks() _mocks()
_inject()
inject ($controller) -> it "define external user", (done) ->
controller = $controller $scope = $rootScope.$new()
it "define user", () -> mocks.routeParams.slug = "user-slug"
ctrl = controller "Profile",
$scope: {}
expect(ctrl.user).to.be.equal(mocks.auth.userData) ctrl = $controller("Profile")
it "define projects", () -> user = Immutable.fromJS({
ctrl = controller "Profile", username: "user-name"
$scope: {} })
expect(mocks.appTitle.set.withArgs("UserName")).to.be.calledOnce mocks.userService.getUserByUserName.withArgs(mocks.routeParams.slug).resolve(user)
setTimeout ( ->
expect(ctrl.user).to.be.equal(user)
expect(ctrl.isCurrentUser).to.be.false
expect(mocks.appTitle.set.calledWithExactly("user-name")).to.be.true
done()
)
it "define current user", () ->
$scope = $rootScope.$new()
user = Immutable.fromJS({
username: "user-name"
})
mocks.currentUser.getUser.returns(user)
ctrl = $controller("Profile")
expect(ctrl.user).to.be.equal(user)
expect(ctrl.isCurrentUser).to.be.true
expect(mocks.appTitle.set.calledWithExactly("user-name")).to.be.true

View File

@ -1,16 +1,16 @@
include ../../partials/includes/components/beta include ../../partials/includes/components/beta
div.profile.centered div.profile.centered(ng-if="vm.user")
div(tg-profile-bar) 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(tg-profile-tab="activity", tab-title="{{'USER.PROFILE.ACTIVITY_TAB' | translate}}", tab-icon="icon-timeline", tab-active)
div(tg-user-timeline, userId="vm.user.get('id')") div(tg-user-timeline, userId="vm.user.get('id')")
div(tg-profile-tab="projects", tab-title="{{'USER.PROFILE.PROJECTS_TAB' | translate}}", tab-icon="icon-project") div(tg-profile-tab="projects", tab-title="{{'USER.PROFILE.PROJECTS_TAB' | translate}}", tab-icon="icon-project")
div(tg-profile-projects) div(tg-profile-projects, userId="vm.user.get('id')")
div(tg-profile-tab="contacts", tab-title="{{'USER.PROFILE.CONTACTS_TAB' | translate}}", tab-icon="icon-team") div(tg-profile-tab="contacts", tab-title="{{'USER.PROFILE.CONTACTS_TAB' | translate}}", tab-icon="icon-team")
div(tg-profile-contacts) div(tg-profile-contacts, userId="vm.user.get('id')")
// div(tg-profile-tab="favorites", tab-title="{{'USER.PROFILE.FAVORITES_TAB' | translate}}", tab-icon="icon-star-fill") // div(tg-profile-tab="favorites", tab-title="{{'USER.PROFILE.FAVORITES_TAB' | translate}}", tab-icon="icon-star-fill")
// include includes/profile-favorites // include includes/profile-favorites

View File

@ -4,7 +4,7 @@
margin-bottom: 1rem; margin-bottom: 1rem;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
&:hover { &.is-current-user:hover {
img { img {
filter: brightness(40%) saturate(150%) hue-rotate(60deg); filter: brightness(40%) saturate(150%) hue-rotate(60deg);
transition: all .2s cubic-bezier(.01, .7, 1, 1); transition: all .2s cubic-bezier(.01, .7, 1, 1);

View File

@ -1,7 +1,22 @@
Resource = (urlsService, http) -> Resource = (urlsService, http) ->
service = {} service = {}
service.getUserBySlug = (userSlug) -> service.getUserByUsername = (username) ->
url = urlsService.resolve("by_username")
httpOptions = {
headers: {
"x-disable-pagination": "1"
}
}
params = {
username: username
}
return http.get(url, params, httpOptions)
.then (result) ->
return Immutable.fromJS(result.data)
service.getStats = (userId) -> service.getStats = (userId) ->
url = urlsService.resolve("stats", userId) url = urlsService.resolve("stats", userId)
@ -16,7 +31,6 @@ Resource = (urlsService, http) ->
.then (result) -> .then (result) ->
return Immutable.fromJS(result.data) return Immutable.fromJS(result.data)
service.getContacts = (userId) -> service.getContacts = (userId) ->
url = urlsService.resolve("contacts", userId) url = urlsService.resolve("contacts", userId)

View File

@ -5,6 +5,9 @@ class UserService extends taiga.Service
constructor: (@rs) -> constructor: (@rs) ->
getUserByUserName: (username) ->
return @rs.users.getUserByUsername(username)
getContacts: (userId) -> getContacts: (userId) ->
return @rs.users.getContacts(userId) return @rs.users.getContacts(userId)

View File

@ -87,3 +87,15 @@ describe "UserService", ->
done() done()
$rootScope.$apply() $rootScope.$apply()
it "get user by username", (done) ->
username = "username-1"
user = {id: 1}
mocks.resources.users.getUserByUsername = sinon.stub().promise()
mocks.resources.users.getUserByUsername.withArgs(username).resolve(user)
userService.getUserByUserName(username).then (_user_) ->
expect(_user_).to.be.eql(user)
done()