summaryrefslogtreecommitdiffstats
path: root/client/components
diff options
context:
space:
mode:
authorfloatinghotpot <rjfun.mobile@gmail.com>2015-12-07 11:15:57 +0800
committerfloatinghotpot <rjfun.mobile@gmail.com>2015-12-07 11:15:57 +0800
commit011f53ad0828c0979d15e232abf501180c741288 (patch)
tree41330fe4e47c443dd9fefd0493f30a186e4c4999 /client/components
parentd4c5310d65cbdfbd002288d33eba429ace33bc3c (diff)
downloadwekan-011f53ad0828c0979d15e232abf501180c741288.tar.gz
wekan-011f53ad0828c0979d15e232abf501180c741288.tar.bz2
wekan-011f53ad0828c0979d15e232abf501180c741288.zip
add: invite user via email, invited user can accept or decline, allow member to quit
Diffstat (limited to 'client/components')
-rw-r--r--client/components/boards/boardsList.jade25
-rw-r--r--client/components/boards/boardsList.js18
-rw-r--r--client/components/boards/boardsList.styl9
-rw-r--r--client/components/sidebar/sidebar.jade51
-rw-r--r--client/components/sidebar/sidebar.js109
-rw-r--r--client/components/users/userAvatar.js5
-rw-r--r--client/components/users/userAvatar.styl4
7 files changed, 179 insertions, 42 deletions
diff --git a/client/components/boards/boardsList.jade b/client/components/boards/boardsList.jade
index 11333eee..464f9b97 100644
--- a/client/components/boards/boardsList.jade
+++ b/client/components/boards/boardsList.jade
@@ -3,11 +3,22 @@ template(name="boardList")
ul.board-list.clearfix
each boards
li(class="{{#if isStarred}}starred{{/if}}" class=colorClass)
- a.js-open-board(href="{{pathFor 'board' id=_id slug=slug}}")
- span.details
- span.board-list-item-name= title
- i.fa.js-star-board(
- class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}"
- title="{{_ 'star-board-title'}}")
+ if isInvited
+ .board-list-item
+ span.details
+ span.board-list-item-name= title
+ i.fa.js-star-board(
+ class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}"
+ title="{{_ 'star-board-title'}}")
+ p.board-list-item-desc {{_ 'just-invited'}}
+ button.js-accept-invite.primary {{_ 'accept'}}
+ button.js-decline-invite {{_ 'decline'}}
+ else
+ a.js-open-board.board-list-item(href="{{pathFor 'board' id=_id slug=slug}}")
+ span.details
+ span.board-list-item-name= title
+ i.fa.js-star-board(
+ class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}"
+ title="{{_ 'star-board-title'}}")
li.js-add-board
- a.label {{_ 'add-board'}}
+ a.board-list-item.label {{_ 'add-board'}}
diff --git a/client/components/boards/boardsList.js b/client/components/boards/boardsList.js
index 1a2d3c9a..131adf9d 100644
--- a/client/components/boards/boardsList.js
+++ b/client/components/boards/boardsList.js
@@ -17,6 +17,11 @@ BlazeComponent.extendComponent({
return user && user.hasStarred(this.currentData()._id);
},
+ isInvited() {
+ const user = Meteor.user();
+ return user && user.isInvitedTo(this.currentData()._id);
+ },
+
events() {
return [{
'click .js-add-board': Popup.open('createBoard'),
@@ -25,6 +30,19 @@ BlazeComponent.extendComponent({
Meteor.user().toggleBoardStar(boardId);
evt.preventDefault();
},
+ 'click .js-accept-invite'() {
+ const boardId = this.currentData()._id;
+ Meteor.user().removeInvite(boardId);
+ },
+ 'click .js-decline-invite'() {
+ const boardId = this.currentData()._id;
+ Meteor.call('quitBoard', boardId, (err, ret) => {
+ if (!err && ret) {
+ Meteor.user().removeInvite(boardId);
+ FlowRouter.go('home');
+ }
+ });
+ },
}];
},
}).register('boardList');
diff --git a/client/components/boards/boardsList.styl b/client/components/boards/boardsList.styl
index 9978fab8..e24940a0 100644
--- a/client/components/boards/boardsList.styl
+++ b/client/components/boards/boardsList.styl
@@ -14,7 +14,7 @@ $spaceBetweenTiles = 16px
.fa-star-o
opacity: 1
- a
+ .board-list-item
background-color: #999
color: #f6f6f6
height: 90px
@@ -40,6 +40,13 @@ $spaceBetweenTiles = 16px
font-weight: 400
line-height: 22px
+ .board-list-item-desc
+ color: rgba(255, 255, 255, .5)
+ display: block
+ font-size: 10px
+ font-weight: 400
+ line-height: 18px
+
.js-add-board
text-align:center
diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade
index f98ea4ee..3a5c7fdb 100644
--- a/client/components/sidebar/sidebar.jade
+++ b/client/components/sidebar/sidebar.jade
@@ -33,6 +33,13 @@ template(name="membersWidget")
a.member.add-member.js-manage-board-members
i.fa.fa-plus
.clearfix
+ if isInvited
+ hr
+ p
+ i.fa.fa-exclamation-circle
+ | {{_ 'just-invited'}}
+ button.js-member-invite-accept.primary {{_ 'accept'}}
+ button.js-member-invite-decline {{_ 'decline'}}
template(name="labelsWidget")
.board-widget.board-widget-labels
@@ -56,6 +63,10 @@ template(name="memberPopup")
h3
.js-profile= user.profile.fullname
p.quiet @#{user.username}
+ if isInvited
+ p
+ i.fa.fa-exclamation-circle
+ | {{_ 'not-accepted-yet'}}
ul.pop-over-list
li
@@ -68,9 +79,7 @@ template(name="memberPopup")
span.quiet (#{memberType})
li
if $eq currentUser._id userId
- //-
- XXX Not implemented!
- // a.js-leave-member {{_ 'leave-board'}}
+ a.js-leave-member {{_ 'leave-board'}}
else
a.js-remove-member {{_ 'remove-from-board'}}
@@ -83,23 +92,29 @@ template(name="addMemberPopup")
.js-search-member
+esInput(index="users")
- ul.pop-over-list
- +esEach(index="users")
- li.item.js-member-item(class="{{#if isBoardMember}}disabled{{/if}}")
- a.name.js-select-member(title="{{profile.name}} ({{username}})")
- +userAvatar(userId=_id esSearch=true)
- span.full-name
- = profile.fullname
- | (<span class="username">{{username}}</span>)
- if isBoardMember
- .quiet ({{_ 'joined'}})
+ if loading.get
+ +spinner
+ else if error.get
+ .warning {{_ error.get}}
+ else
+ ul.pop-over-list
+ +esEach(index="users")
+ li.item.js-member-item(class="{{#if isBoardMember}}disabled{{/if}}")
+ a.name.js-select-member(title="{{profile.name}} ({{username}})")
+ +userAvatar(userId=_id esSearch=true)
+ span.full-name
+ = profile.fullname
+ | (<span class="username">{{username}}</span>)
+ if isBoardMember
+ .quiet ({{_ 'joined'}})
- +ifEsIsSearching(index='users')
- +spinner
+ +ifEsIsSearching(index='users')
+ +spinner
- +ifEsHasNoResults(index="users")
- .manage-member-section
- p.quiet {{_ 'no-results'}}
+ +ifEsHasNoResults(index="users")
+ .manage-member-section
+ p.quiet {{_ 'no-results'}}
+ button.js-email-invite.primary.full {{_ 'email-invite'}}
template(name="changePermissionsPopup")
ul.pop-over-list
diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js
index ef071fe0..5b58dbd9 100644
--- a/client/components/sidebar/sidebar.js
+++ b/client/components/sidebar/sidebar.js
@@ -117,6 +117,9 @@ Template.memberPopup.helpers({
const type = Users.findOne(this.userId).isBoardAdmin() ? 'admin' : 'normal';
return TAPi18n.__(type).toLowerCase();
},
+ isInvited() {
+ return Users.findOne(this.userId).isInvitedTo(Session.get('currentBoard'));
+ },
});
Template.memberPopup.events({
@@ -132,8 +135,13 @@ Template.memberPopup.events({
Popup.close();
}),
'click .js-leave-member'() {
- // XXX Not implemented
- Popup.close();
+ const currentBoard = Boards.findOne(Session.get('currentBoard'));
+ Meteor.call('quitBoard', currentBoard, (err, ret) => {
+ if (!ret && ret) {
+ Popup.close();
+ FlowRouter.go('home');
+ }
+ });
},
});
@@ -146,9 +154,29 @@ Template.removeMemberPopup.helpers({
},
});
+Template.membersWidget.helpers({
+ isInvited() {
+ const user = Meteor.user();
+ return user && user.isInvitedTo(Session.get('currentBoard'));
+ },
+});
+
Template.membersWidget.events({
'click .js-member': Popup.open('member'),
'click .js-manage-board-members': Popup.open('addMember'),
+ 'click .js-member-invite-accept'() {
+ const boardId = Session.get('currentBoard');
+ Meteor.user().removeInvite(boardId);
+ },
+ 'click .js-member-invite-decline'() {
+ const boardId = Session.get('currentBoard');
+ Meteor.call('quitBoard', boardId, (err, ret) => {
+ if (!err && ret) {
+ Meteor.user().removeInvite(boardId);
+ FlowRouter.go('home');
+ }
+ });
+ },
});
Template.labelsWidget.events({
@@ -194,25 +222,76 @@ function draggableMembersLabelsWidgets() {
Template.membersWidget.onRendered(draggableMembersLabelsWidgets);
Template.labelsWidget.onRendered(draggableMembersLabelsWidgets);
-Template.addMemberPopup.helpers({
+BlazeComponent.extendComponent({
+ template() {
+ return 'addMemberPopup';
+ },
+
+ onCreated() {
+ this.error = new ReactiveVar('');
+ this.loading = new ReactiveVar(false);
+ },
+
+ onRendered() {
+ this.find('.js-search-member input').focus();
+ this.setLoading(false);
+ },
+
isBoardMember() {
- const user = Users.findOne(this._id);
+ const userId = this.currentData()._id;
+ const user = Users.findOne(userId);
return user && user.isBoardMember();
},
-});
-Template.addMemberPopup.events({
- 'click .js-select-member'() {
- const userId = this._id;
- const currentBoard = Boards.findOne(Session.get('currentBoard'));
- currentBoard.addMember(userId);
- Popup.close();
+ isValidEmail(email) {
+ return SimpleSchema.RegEx.Email.test(email);
},
-});
-Template.addMemberPopup.onRendered(function() {
- this.find('.js-search-member input').focus();
-});
+ setError(error) {
+ this.error.set(error);
+ },
+
+ setLoading(w) {
+ this.loading.set(w);
+ },
+
+ isLoading() {
+ return this.loading.get();
+ },
+
+ inviteUser(idNameEmail) {
+ const boardId = Session.get('currentBoard');
+ this.setLoading(true);
+ const self = this;
+ Meteor.call('inviteUserToBoard', idNameEmail, boardId, (err, ret) => {
+ self.setLoading(false);
+ if (err) self.setError(err.error);
+ else if (ret.email) self.setError('email-sent');
+ else Popup.close();
+ });
+ },
+
+ events() {
+ return [{
+ 'keyup input'() {
+ this.setError('');
+ },
+ 'click .js-select-member'() {
+ const userId = this.currentData()._id;
+ const currentBoard = Boards.findOne(Session.get('currentBoard'));
+ if (currentBoard.memberIndex(userId)<0) {
+ this.inviteUser(userId);
+ }
+ },
+ 'click .js-email-invite'() {
+ const idNameEmail = $('.js-search-member input').val();
+ if (idNameEmail.indexOf('@')<0 || this.isValidEmail(idNameEmail)) {
+ this.inviteUser(idNameEmail);
+ } else this.setError('email-invalid');
+ },
+ }];
+ },
+}).register('addMemberPopup');
Template.changePermissionsPopup.events({
'click .js-set-admin, click .js-set-normal'(event) {
diff --git a/client/components/users/userAvatar.js b/client/components/users/userAvatar.js
index 1f1da251..1e531882 100644
--- a/client/components/users/userAvatar.js
+++ b/client/components/users/userAvatar.js
@@ -22,8 +22,11 @@ Template.userAvatar.helpers({
},
presenceStatusClassName() {
+ const user = Users.findOne(this.userId);
const userPresence = presences.findOne({ userId: this.userId });
- if (!userPresence)
+ if (user && user.isInvitedTo(Session.get('currentBoard')))
+ return 'pending';
+ else if (!userPresence)
return 'disconnected';
else if (Session.equals('currentBoard', userPresence.state.currentBoardId))
return 'active';
diff --git a/client/components/users/userAvatar.styl b/client/components/users/userAvatar.styl
index 83257792..b962b01c 100644
--- a/client/components/users/userAvatar.styl
+++ b/client/components/users/userAvatar.styl
@@ -56,6 +56,10 @@ avatar-radius = 50%
background: #bdbdbd
border-color: #ededed
+ &.pending
+ background: #e44242
+ border-color: #f1dada
+
.edit-avatar
position: absolute
top: 0