summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxime Quandalle <maxime@quandalle.com>2015-05-30 15:50:48 +0200
committerMaxime Quandalle <maxime@quandalle.com>2015-05-30 15:50:48 +0200
commitf4c80d1315c181300bd37ef5de98c365325bc130 (patch)
tree811dd0a9bba9bbe49d7e4c360719e72899574783
parent6db01bb3c7bbcf3e5451dd828d3d0932eb5efec3 (diff)
downloadwekan-f4c80d1315c181300bd37ef5de98c365325bc130.tar.gz
wekan-f4c80d1315c181300bd37ef5de98c365325bc130.tar.bz2
wekan-f4c80d1315c181300bd37ef5de98c365325bc130.zip
Implement presence indicators
-rw-r--r--.jshintrc2
-rw-r--r--.meteor/packages3
-rw-r--r--client/components/sidebar/sidebar.jade3
-rw-r--r--client/components/users/avatar.jade7
-rw-r--r--client/components/users/headerButtons.js5
-rw-r--r--client/components/users/router.js1
-rw-r--r--client/components/users/userAvatar.jade23
-rw-r--r--client/components/users/userAvatar.js (renamed from client/components/users/helpers.js)21
-rw-r--r--client/components/users/userAvatar.styl (renamed from client/components/users/member.styl)9
-rw-r--r--client/components/users/userForm.styl (renamed from client/components/users/form.styl)1
-rw-r--r--client/components/users/userHeader.jade (renamed from client/components/users/headerButtons.jade)14
-rw-r--r--client/components/users/userHeader.js39
-rw-r--r--client/components/users/userProfile.html (renamed from client/components/users/templates.html)39
-rw-r--r--client/components/users/userProfile.js (renamed from client/components/users/events.js)22
-rw-r--r--client/config/router.js21
-rw-r--r--client/styles/temp.styl110
-rw-r--r--collections/users.js12
-rw-r--r--server/publications/boards.js12
18 files changed, 116 insertions, 228 deletions
diff --git a/.jshintrc b/.jshintrc
index bf6b5ffe..a03f740f 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 c04186c6..48d8665e 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 9dd47b0d..40e0dae3 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 70ef69e0..00000000
--- 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 70594fb5..00000000
--- 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 d59e174d..6c4ab3b6 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 00000000..a76c4617
--- /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
index 33867298..d7d221db 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
index 3dfdaa37..dfe59143 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
index 845c810d..5ecef7aa 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
index 0a2f64cf..eb3d265c 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 00000000..3bb9e623
--- /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
index 5783eebf..3d1f8c9b 100644
--- a/client/components/users/templates.html
+++ b/client/components/users/userProfile.html
@@ -1,18 +1,3 @@
-<template name="setLanguagePopup">
-<ul class="pop-over-list">
- {{#each languages}}
- <li class="{{# if isCurrentLanguage}}active{{/if}}">
- <a class="js-set-language">
- {{name}}
- {{# if isCurrentLanguage}}
- <span class="icon-sm fa fa-check"></span>
- {{/if}}
- </a>
- </li>
- {{/each}}
-</ul>
-</template>
-
<template name='profile'>
{{ # if profile }}
<div class="tabbed-pane-header">
@@ -92,27 +77,3 @@
{{ /if }}
{{ /if }}
</template>
-
-<template name="userPopup">
- <div class="board-member-menu">
- <div class="mini-profile-info">
- {{> userAvatar user=user}}
- <div class="info">
- <h3 class="bottom" style="margin-right: 40px;">
- <a class="js-profile" href="{{ pathFor route='Profile' username=user.username }}">{{ user.profile.name }}</a>
- </h3>
- <p class="quiet bottom">@{{ user.username }}</p>
- </div>
- </div>
- </div>
-</template>
-
-
-<template name="memberName">
- <a class="inline-object js-show-mem-menu" href="{{ pathFor route='Profile' username=user.username }}">
- {{ user.profile.name }}
- {{# if username }}
- ({{ user.username }})
- {{ /if }}
- </a>
-</template>
diff --git a/client/components/users/events.js b/client/components/users/userProfile.js
index b741532f..040abed0 100644
--- a/client/components/users/events.js
+++ b/client/components/users/userProfile.js
@@ -1,25 +1,3 @@
-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();
- }
-});
-
Template.profileEditForm.events({
'click .js-edit-profile': function() {
Session.set('ProfileEditForm', true);
diff --git a/client/config/router.js b/client/config/router.js
index ed9a069d..8fa74bee 100644
--- a/client/config/router.js
+++ b/client/config/router.js
@@ -24,6 +24,13 @@ Router.configure({
return this.redirect('atSignIn');
}
+ // We want to execute our EscapeActions.executeLowerThan method any time the
+ // route is changed, but not if the stays the same but only the parameters
+ // change (eg when a user is navigation from a card A to a card B). Iron-
+ // Router onBeforeAction is a reactive context (which is a bad desig choice
+ // as explained in
+ // https://github.com/meteorhacks/flow-router#routercurrent-is-evil) so we
+ // need to use Tracker.nonreactive
Tracker.nonreactive(function() {
if (! options.noEscapeActions &&
! (previousRoute && previousRoute.options.noEscapeActions))
@@ -35,17 +42,3 @@ Router.configure({
this.next();
}
});
-
-// We want to execute our EscapeActions.executeLowerThan method any time the
-// route is changed, but not if the stays the same but only the parameters
-// change (eg when a user is navigation from a card A to a card B). This is why
-// we can’t put this function in the above `onBeforeAction` that is being run
-// too many times, instead we register a dependency only on the route name and
-// use Tracker.autorun. The following paragraph explains the problem quite well:
-// https://github.com/meteorhacks/flow-router#routercurrent-is-evil
-// Tracker.autorun(function(computation) {
-// routeName.get();
-// if (! computation.firstRun) {
-// EscapeActions.executeLowerThan('inlinedForm');
-// }
-// });
diff --git a/client/styles/temp.styl b/client/styles/temp.styl
deleted file mode 100644
index 9dab7802..00000000
--- a/client/styles/temp.styl
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * We should merge these declarations in the appropriate stylus files.
- */
-
-.dn {
- display:none;
-}
-
-.header-btn-btn {
- padding-left:23px!important;
-}
-
-.bgnone {
- background:none!important;
-}
-
-.tac {
- text-align:center;
-
- h1 {
- font-size: 2em;
- }
-}
-
-.tdn {
- text-decoration:none;
-}
-
-.header-member {
- min-width:105px!important;
- text-align:center;
-}
-
-.primarys {
- font-size:20px;
- line-height: 1.44em;
- padding: .6em 1.3em!important;
- border-radius: 3px!important;
- box-shadow: 0 2px 0 #4d4d4d!important;
-}
-
-.layout-twothirds-center {
- display: block;
- max-width: 585px;
- margin: 0 auto;
- position: relative;
- font-size:20px;
- line-height: 100px;
-}
-
-#WindowTitleEdit .single-line, .single-line2 {
- overflow: hidden;
- word-wrap: break-word;
- resize: none;
- height: 60px;
-}
-
-.single-line2 {
- overflow: hidden;
- word-wrap: break-word;
- resize: none;
- height: 108px;
-}
-
-#header-search {
- float: left;
- margin: 1px 8px 0 0;
- position: relative;
- z-index: 1;
-
- label {
- display:none;
- }
- input[type="text"] {
- background:rgba(255,255,255,0.5);
- border-top-left-radius:3px;
- border-top-right-radius:0;
- border-bottom-right-radius:0;
- border-bottom-left-radius:3px;
- border:none;
- float:left;
- font-size:13px;
- height:29px;
- min-height:29px;
- line-height:19px;
- width:160px;
- margin:0;
-
- &:hover{
- background:rgba(255,255,255,0.7);
- }
-
- &:focus{
- background:#e8ebee;
- -webkit-box-shadow:none;
- box-shadow:none
- }
- }
-
- .header-btn{
- border-top-left-radius:0;
- border-top-right-radius:3px;
- border-bottom-right-radius:3px;
- border-bottom-left-radius:0
- }
-
- input[type="submit"]{
- display:none
- }
-}
diff --git a/collections/users.js b/collections/users.js
index 6e2c6bdc..2f44e19e 100644
--- a/collections/users.js
+++ b/collections/users.js
@@ -43,9 +43,6 @@ Users.helpers({
Users.before.insert(function(userId, doc) {
doc.profile = doc.profile || {};
-
- // connect profile.status default
- doc.profile.status = 'offline';
});
if (Meteor.isServer) {
@@ -110,3 +107,12 @@ if (Meteor.isServer) {
});
});
}
+
+// Presence indicator
+if (Meteor.isClient) {
+ Presence.state = function() {
+ return {
+ currentBoardId: Session.get('currentBoard')
+ };
+ };
+}
diff --git a/server/publications/boards.js b/server/publications/boards.js
index 6a4e476d..103e9080 100644
--- a/server/publications/boards.js
+++ b/server/publications/boards.js
@@ -108,14 +108,20 @@ Meteor.publishComposite('board', function(boardId, slug) {
},
// Board members. This publication also includes former board members that
- // are no more members of the board but may have some activities attached
- // to them.
+ // aren't members anymore but may have some activities attached to them in
+ // the history.
{
find: function(board) {
return Users.find({
_id: { $in: _.pluck(board.members, 'userId') }
});
- }
+ },
+ // Presence indicators
+ children: [{
+ find: function(user) {
+ return Presences.find({userId: user._id});
+ }
+ }]
}
]
};