diff --git a/.jshintrc b/.jshintrc
index bf6b5ffe5..a03f740f3 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -55,6 +55,8 @@
"Mousetrap": false,
"Avatar": true,
"Ps": true,
+ "Presence": true,
+ "Presences": true,
// Our collections
"Boards": true,
diff --git a/.meteor/packages b/.meteor/packages
index c04186c6e..48d8665ed 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -2,6 +2,9 @@
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
+#
+# XXX Should we replace tmeasday:presence by 3stack:presence? Or maybe the
+# packages will merge in the future?
meteor-platform
diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade
index 9dd47b0db..40e0dae31 100644
--- a/client/components/sidebar/sidebar.jade
+++ b/client/components/sidebar/sidebar.jade
@@ -31,10 +31,11 @@ template(name="membersWidget")
userId=this.userId
draggable=true
size="small"
- showBadges=true)
+ showStatus=true)
unless isSandstorm
if currentUser.isBoardAdmin
a.js-open-manage-board-members
+ .clearfix
template(name="labelsWidget")
.board-widget.board-widget-labels
diff --git a/client/components/users/avatar.jade b/client/components/users/avatar.jade
deleted file mode 100644
index 70ef69e01..000000000
--- a/client/components/users/avatar.jade
+++ /dev/null
@@ -1,7 +0,0 @@
-template(name="userAvatar")
- .member(class="{{class}} {{# if draggable }}js-member{{else}}js-member-on-card-menu{{/if}}"
- title="{{userData.profile.name}} ({{userData.username}})")
- +avatar(user=userData size=size)
- if showBadges
- span.member-status(class="{{# if userData.profile.status}}active{{/if}}")
- span.member-type(class=memberType)
diff --git a/client/components/users/headerButtons.js b/client/components/users/headerButtons.js
deleted file mode 100644
index 70594fb58..000000000
--- a/client/components/users/headerButtons.js
+++ /dev/null
@@ -1,5 +0,0 @@
-Template.headerUserBar.events({
- 'click .js-sign-in': Popup.open('signup'),
- 'click .js-log-in': Popup.open('login'),
- 'click .js-open-header-member-menu': Popup.open('memberMenu')
-});
diff --git a/client/components/users/router.js b/client/components/users/router.js
index d59e174d2..6c4ab3b66 100644
--- a/client/components/users/router.js
+++ b/client/components/users/router.js
@@ -1,4 +1,3 @@
-
_.each(['signIn', 'signUp', 'resetPwd',
'forgotPwd', 'enrollAccount', 'changePwd'], function(routeName) {
AccountsTemplates.configureRoute(routeName, {
diff --git a/client/components/users/userAvatar.jade b/client/components/users/userAvatar.jade
new file mode 100644
index 000000000..a76c46179
--- /dev/null
+++ b/client/components/users/userAvatar.jade
@@ -0,0 +1,23 @@
+template(name="userAvatar")
+ .member(class="{{class}} {{# if draggable }}js-member{{else}}js-member-on-card-menu{{/if}}"
+ title="{{userData.profile.name}} ({{userData.username}})")
+ +avatar(user=userData size=size)
+ if showStatus
+ span.member-presence-status(class=presenceStatusClassName)
+ span.member-type(class=memberType)
+
+
+template(name="userPopup")
+ .board-member-menu
+ .mini-profile-info
+ +userAvatar(user=user)
+ .info
+ h3.bottom
+ a.js-profile(href="{{ pathFor route='Profile' username=user.username }}")= user.profile.name
+ p.quiet.bottom @{{ user.username }}
+
+template(name="memberName")
+ a.inline-object.js-show-mem-menu(href="{{ pathFor route='Profile' username=user.username }}")
+ = user.profile.name
+ if username
+ | ({{ user.username }})
diff --git a/client/components/users/helpers.js b/client/components/users/userAvatar.js
similarity index 53%
rename from client/components/users/helpers.js
rename to client/components/users/userAvatar.js
index 33867298c..d7d221db1 100644
--- a/client/components/users/helpers.js
+++ b/client/components/users/userAvatar.js
@@ -9,19 +9,14 @@ Template.userAvatar.helpers({
var userId = this.userId || this.user._id;
var user = Users.findOne(userId);
return user && user.isBoardAdmin() ? 'admin' : 'normal';
- }
-});
-
-Template.setLanguagePopup.helpers({
- languages: function() {
- return _.map(TAPi18n.getLanguages(), function(lang, tag) {
- return {
- tag: tag,
- name: lang.name
- };
- });
},
- isCurrentLanguage: function() {
- return this.tag === TAPi18n.getLanguage();
+ presenceStatusClassName: function() {
+ var userPresence = Presences.findOne({ userId: this.user._id });
+ if (! userPresence)
+ return 'disconnected';
+ else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
+ return 'active';
+ else
+ return 'idle';
}
});
diff --git a/client/components/users/member.styl b/client/components/users/userAvatar.styl
similarity index 95%
rename from client/components/users/member.styl
rename to client/components/users/userAvatar.styl
index 3dfdaa371..dfe591430 100644
--- a/client/components/users/member.styl
+++ b/client/components/users/userAvatar.styl
@@ -38,16 +38,17 @@ avatar-radius = 50%
max-width: 100%
max-height: 100%
- .member-status
+ .member-presence-status
background-color: #b3b3b3
border: 1px solid #fff
border-radius: 50%
- height: 8px
+ height: 7px
width: @height
position: absolute
- right: 0px
- bottom: 0px
+ right: -1px
+ bottom: -1px
border: 1px solid white
+ z-index: 15
&.active
background: #64c464
diff --git a/client/components/users/form.styl b/client/components/users/userForm.styl
similarity index 99%
rename from client/components/users/form.styl
rename to client/components/users/userForm.styl
index 845c810dd..5ecef7aaf 100644
--- a/client/components/users/form.styl
+++ b/client/components/users/userForm.styl
@@ -7,7 +7,6 @@
img
width: 275px
-
.at-form
margin: auto
width: 275px
diff --git a/client/components/users/headerButtons.jade b/client/components/users/userHeader.jade
similarity index 71%
rename from client/components/users/headerButtons.jade
rename to client/components/users/userHeader.jade
index 0a2f64cf7..eb3d265ca 100644
--- a/client/components/users/headerButtons.jade
+++ b/client/components/users/userHeader.jade
@@ -8,11 +8,6 @@ template(name="headerUserBar")
= currentUser.username
+userAvatar(user=currentUser)
-template(name="memberHeader")
- a.header-member.js-open-header-member-menu
- span= currentUser.profile.name
- +userAvatar(user=currentUser size="small")
-
template(name="memberMenuPopup")
ul.pop-over-list
li: a(href="{{pathFor route='Profile' username=currentUser.username}}") {{_ 'profile'}}
@@ -21,3 +16,12 @@ template(name="memberMenuPopup")
hr
ul.pop-over-list
li: a.js-logout {{_ 'log-out'}}
+
+template(name="setLanguagePopup")
+ ul.pop-over-list
+ each languages
+ li(class="{{# if isCurrentLanguage}}active{{/if}}")
+ a.js-set-language
+ = name
+ if isCurrentLanguage
+ i.fa.fa-check
diff --git a/client/components/users/userHeader.js b/client/components/users/userHeader.js
new file mode 100644
index 000000000..3bb9e6234
--- /dev/null
+++ b/client/components/users/userHeader.js
@@ -0,0 +1,39 @@
+Template.headerUserBar.events({
+ 'click .js-open-header-member-menu': Popup.open('memberMenu')
+});
+
+Template.setLanguagePopup.helpers({
+ languages: function() {
+ return _.map(TAPi18n.getLanguages(), function(lang, tag) {
+ return {
+ tag: tag,
+ name: lang.name
+ };
+ });
+ },
+ isCurrentLanguage: function() {
+ return this.tag === TAPi18n.getLanguage();
+ }
+});
+
+Template.memberMenuPopup.events({
+ 'click .js-language': Popup.open('setLanguage'),
+ 'click .js-logout': function(evt) {
+ evt.preventDefault();
+
+ Meteor.logout(function() {
+ Router.go('Home');
+ });
+ }
+});
+
+Template.setLanguagePopup.events({
+ 'click .js-set-language': function(evt) {
+ Users.update(Meteor.userId(), {
+ $set: {
+ 'profile.language': this.tag
+ }
+ });
+ evt.preventDefault();
+ }
+});
diff --git a/client/components/users/templates.html b/client/components/users/userProfile.html
similarity index 75%
rename from client/components/users/templates.html
rename to client/components/users/userProfile.html
index 5783eebf2..3d1f8c9b8 100644
--- a/client/components/users/templates.html
+++ b/client/components/users/userProfile.html
@@ -1,18 +1,3 @@
-
-
-
-
{{ # if profile }}