Merge branch 'master' into lib-change

This commit is contained in:
Romulus Tsai 蔡仲明 2020-05-08 10:13:11 +08:00
commit c3458855bd
425 changed files with 15176 additions and 28903 deletions

View file

@ -62,5 +62,5 @@ template(name="attachmentsGalery")
unless currentUser.isWorker
//li.attachment-item.add-attachment
a.js-add-attachment
i.fa.fa-paperclip
i.fa.fa-plus
| {{_ 'add-attachment' }}

View file

@ -32,7 +32,7 @@ template(name="cardDetails")
// else
{{_ 'top-level-card'}}
if isLinkedCard
h3.linked-card-location
a.linked-card-location.js-go-to-linked-card
+viewer
| {{getBoardTitle}} > {{getTitle}}
@ -199,10 +199,29 @@ template(name="cardDetails")
+viewer
= getAssignedBy
if getVoteQuestion
hr
.vote-title
h3
i.fa.fa-thumbs-up
card-details-item-title {{_ 'vote-question'}}
.vote-result
if votePublic
a.card-label.card-label-green.js-show-positive-votes {{ voteCountPositive }}
a.card-label.card-label-red.js-show-negative-votes {{ voteCountNegative }}
else
.card-label.card-label-green {{ voteCountPositive }}
.card-label.card-label-red {{ voteCountNegative }}
+viewer
= getVoteQuestion
button.card-details-green.js-vote.js-vote-positive(class="{{#if voteState}}voted{{/if}}") {{_ 'vote-for-it'}}
button.card-details-red.js-vote.js-vote-negative(class="{{#if $eq voteState false}}voted{{/if}}") {{_ 'vote-against'}}
//- XXX We should use "editable" to avoid repetiting ourselves
if canModifyCard
unless currentUser.isWorker
if currentBoard.allowsDescriptionTitle
hr
h3
i.fa.fa-align-left
card-details-item-title {{_ 'description'}}
@ -229,6 +248,7 @@ template(name="cardDetails")
a.js-close-inlined-form {{_ 'discard'}}
else if getDescription
if currentBoard.allowsDescriptionTitle
hr
h3.card-details-item-title {{_ 'description'}}
if currentBoard.allowsDescriptionText
+viewer
@ -237,15 +257,16 @@ template(name="cardDetails")
.card-checklist-attachmentGalerys
.card-checklist-attachmentGalery.card-checklists
if currentBoard.allowsChecklists
hr
+checklists(cardId = _id)
if currentBoard.allowsSubtasks
hr
+subtasks(cardId = _id)
if currentBoard.allowsAttachments
//- hr
//- h3
//- i.fa.fa-paperclip
//- | {{_ 'attachments'}}
hr
h3
i.fa.fa-paperclip
| {{_ 'attachments'}}
.card-checklist-attachmentGalery.card-attachmentGalery
+attachmentsGalery
@ -312,6 +333,16 @@ template(name="cardDetailsActionsPopup")
//li: a.js-members {{_ 'card-edit-members'}}
//li: a.js-labels {{_ 'card-edit-labels'}}
//li: a.js-attachments {{_ 'card-edit-attachments'}}
if getVoteQuestion
li
a.js-cancel-voting
i.fa.fa-thumbs-up
| {{_ 'card-cancel-voting'}}
else
li
a.js-start-voting
i.fa.fa-thumbs-up
| {{_ 'card-start-voting'}}
li
a.js-custom-fields
i.fa.fa-list-alt
@ -535,3 +566,35 @@ template(name="cardDeletePopup")
unless archived
p {{_ "card-delete-suggest-archive"}}
button.js-confirm.negate.full(type="submit") {{_ 'delete'}}
template(name="cardStartVotingPopup")
form.edit-vote-question
.fields
label(for="vote") {{_ 'vote-question'}}
input.js-vote-field#vote(type="text" name="vote" value="{{card.getVoteQuestion}}" autofocus)
label(for="vote-public") {{_ 'vote-public'}}
a.js-toggle-vote-public
.materialCheckBox#vote-public(name="vote-public")
button.primary.confirm.js-submit {{_ 'save'}}
//- button.js-remove-color.negate.wide.right {{_ 'delete'}}
template(name="positiveVoteMembersPopup")
ul.pop-over-list.js-card-member-list
each m in voteMemberPositive
li.item
a.name
+userAvatar(userId=m._id)
span.full-name
= m.profile.fullname
| (<span class="username">{{ m.username }}</span>)
template(name="negativeVoteMembersPopup")
ul.pop-over-list.js-card-member-list
each m in voteMemberNegative
li.item
a.name
+userAvatar(userId=m._id)
span.full-name
= m.profile.fullname
| (<span class="username">{{ m.username }}</span>)

View file

@ -1,5 +1,5 @@
const subManager = new SubsManager();
const { calculateIndexData, enableClickOnTouch } = Utils;
const { calculateIndexData } = Utils;
let cardColors;
Meteor.startup(() => {
@ -38,6 +38,37 @@ BlazeComponent.extendComponent({
Meteor.subscribe('unsaved-edits');
},
voteState() {
const card = this.currentData();
const userId = Meteor.userId();
let state;
if (card.vote) {
if (card.vote.positive) {
state = _.contains(card.vote.positive, userId);
if (state === true) return true;
}
if (card.vote.negative) {
state = _.contains(card.vote.negative, userId);
if (state === true) return false;
}
}
return null;
},
votePublic() {
const card = this.currentData();
if (card.vote) return card.vote.public;
return null;
},
voteCountPositive() {
const card = this.currentData();
if (card.vote && card.vote.positive) return card.vote.positive.length;
return null;
},
voteCountNegative() {
const card = this.currentData();
if (card.vote && card.vote.negative) return card.vote.negative.length;
return null;
},
isWatching() {
const card = this.currentData();
return card.findWatcher(Meteor.userId());
@ -200,9 +231,6 @@ BlazeComponent.extendComponent({
},
});
// ugly touch event hotfix
enableClickOnTouch('.card-checklist-items .js-checklist');
const $subtasksDom = this.$('.card-subtasks-items');
$subtasksDom.sortable({
@ -238,26 +266,21 @@ BlazeComponent.extendComponent({
},
});
// ugly touch event hotfix
enableClickOnTouch('.card-subtasks-items .js-subtasks');
function userIsMember() {
return Meteor.user() && Meteor.user().isBoardMember();
}
// Disable sorting if the current user is not a board member
this.autorun(() => {
if ($checklistsDom.data('sortable')) {
$checklistsDom.sortable('option', 'disabled', !userIsMember());
const disabled = !userIsMember() || Utils.isMiniScreen();
if (
$checklistsDom.data('uiSortable') ||
$checklistsDom.data('sortable')
) {
$checklistsDom.sortable('option', 'disabled', disabled);
}
if ($subtasksDom.data('sortable')) {
$subtasksDom.sortable('option', 'disabled', !userIsMember());
}
if ($checklistsDom.data('sortable')) {
$checklistsDom.sortable('option', 'disabled', Utils.isMiniScreen());
}
if ($subtasksDom.data('sortable')) {
$subtasksDom.sortable('option', 'disabled', Utils.isMiniScreen());
if ($subtasksDom.data('uiSortable') || $subtasksDom.data('sortable')) {
$subtasksDom.sortable('option', 'disabled', disabled);
}
});
},
@ -347,6 +370,9 @@ BlazeComponent.extendComponent({
this.data().setRequestedBy('');
}
},
'click .js-go-to-linked-card'() {
Utils.goCardId(this.data().linkedId);
},
'click .js-member': Popup.open('cardMember'),
'click .js-add-members': Popup.open('cardMembers'),
'click .js-assignee': Popup.open('cardAssignee'),
@ -356,6 +382,8 @@ BlazeComponent.extendComponent({
'click .js-start-date': Popup.open('editCardStartDate'),
'click .js-due-date': Popup.open('editCardDueDate'),
'click .js-end-date': Popup.open('editCardEndDate'),
'click .js-show-positive-votes': Popup.open('positiveVoteMembers'),
'click .js-show-negative-votes': Popup.open('negativeVoteMembers'),
'mouseenter .js-card-details'() {
const parentComponent = this.parentComponent().parentComponent();
//on mobile view parent is Board, not BoardBody.
@ -379,6 +407,18 @@ BlazeComponent.extendComponent({
'click #toggleButton'() {
Meteor.call('toggleSystemMessages');
},
'click .js-vote'(e) {
const forIt = $(e.target).hasClass('js-vote-positive');
let newState = null;
if (
this.voteState() === null ||
(this.voteState() === false && forIt) ||
(this.voteState() === true && !forIt)
) {
newState = forIt;
}
this.data().setVote(Meteor.userId(), newState);
},
},
];
},
@ -560,6 +600,7 @@ Template.cardDetailsActionsPopup.events({
'click .js-assignees': Popup.open('cardAssignees'),
'click .js-labels': Popup.open('cardLabels'),
'click .js-attachments': Popup.open('cardAttachments'),
'click .js-start-voting': Popup.open('cardStartVoting'),
'click .js-custom-fields': Popup.open('cardCustomFields'),
'click .js-received-date': Popup.open('editCardReceivedDate'),
'click .js-start-date': Popup.open('editCardStartDate'),
@ -570,6 +611,11 @@ Template.cardDetailsActionsPopup.events({
'click .js-copy-card': Popup.open('copyCard'),
'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'),
'click .js-set-card-color': Popup.open('setCardColor'),
'click .js-cancel-voting'(event) {
event.preventDefault();
this.unsetVote();
Popup.close();
},
'click .js-move-card-to-top'(event) {
event.preventDefault();
const minOrder = _.min(
@ -672,7 +718,7 @@ BlazeComponent.extendComponent({
_id: { $ne: Meteor.user().getTemplatesBoardId() },
},
{
sort: ['title'],
sort: { sort: 1 /* boards default sorting */ },
},
);
return boards;
@ -848,7 +894,7 @@ BlazeComponent.extendComponent({
},
},
{
sort: ['title'],
sort: { sort: 1 /* boards default sorting */ },
},
);
return boards;
@ -945,6 +991,31 @@ BlazeComponent.extendComponent({
},
}).register('cardMorePopup');
BlazeComponent.extendComponent({
onCreated() {
this.currentCard = this.currentData();
this.voteQuestion = new ReactiveVar(this.currentCard.voteQuestion);
},
events() {
return [
{
'submit .edit-vote-question'(evt) {
evt.preventDefault();
const voteQuestion = evt.target.vote.value;
const publicVote = $('#vote-public').hasClass('is-checked');
this.currentCard.setVoteQuestion(voteQuestion, publicVote);
Popup.close();
},
'click a.js-toggle-vote-public'(event) {
event.preventDefault();
$('#vote-public').toggleClass('is-checked');
},
},
];
},
}).register('cardStartVotingPopup');
// Close the card details pane by pressing escape
EscapeActions.register(
'detailsPane',

View file

@ -94,17 +94,18 @@ avatar-radius = 50%
animation: flexGrowIn 0.1s
box-shadow: 0 0 7px 0 darken(white, 30%)
transition: flex-basis 0.1s
box-sizing: border-box
.mCustomScrollBox
padding-left: 0
.ps-scrollbar-y-rail
pointer-event: all
position: absolute;
position: absolute
.card-details-canvas
width: 470px
padding-left: 20px;
padding-left: 20px
.card-details-header
margin: 0 -20px 5px
@ -241,7 +242,7 @@ input[type="submit"].attachment-add-link-submit
.card-details-canvas
width: 100%
padding-left: 0px;
padding-left: 0px
.card-details-header
.close-card-details
@ -330,3 +331,13 @@ card-details-color(background, color...)
.card-details-indigo
card-details-color(#4b0082, #ffffff) //White text for better visibility
.voted
opacity: .7
.vote-title
display: flex
justify-content: space-between
.vote-result
display: flex
.js-show-positive-votes
cursor: pointer

View file

@ -88,7 +88,8 @@ template(name="checklistItems")
template(name='checklistItemDetail')
.js-checklist-item.checklist-item
if canModifyCard
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
.check-box-container
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
.item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}")
+viewer
= item.title

View file

@ -1,4 +1,4 @@
const { calculateIndexData, enableClickOnTouch } = Utils;
const { calculateIndexData, capitalize } = Utils;
function initSorting(items) {
items.sortable({
@ -36,9 +36,6 @@ function initSorting(items) {
checklistItem.move(checklistId, sortIndex.base);
},
});
// ugly touch event hotfix
enableClickOnTouch('.js-checklist-item:not(.placeholder)');
}
BlazeComponent.extendComponent({
@ -54,14 +51,15 @@ BlazeComponent.extendComponent({
return Meteor.user() && Meteor.user().isBoardMember();
}
// Disable sorting if the current user is not a board member
// Disable sorting if the current user is not a board member or is a miniscreen
self.autorun(() => {
const $itemsDom = $(self.itemsDom);
if ($itemsDom.data('sortable')) {
$(self.itemsDom).sortable('option', 'disabled', !userIsMember());
}
if ($itemsDom.data('sortable')) {
$(self.itemsDom).sortable('option', 'disabled', Utils.isMiniScreen());
if ($itemsDom.data('uiSortable') || $itemsDom.data('sortable')) {
$(self.itemsDom).sortable(
'option',
'disabled',
!userIsMember() || Utils.isMiniScreen(),
);
}
});
},
@ -177,6 +175,16 @@ BlazeComponent.extendComponent({
}
},
focusChecklistItem(event) {
// If a new checklist is created, pre-fill the title and select it.
const checklist = this.currentData().checklist;
if (!checklist) {
const textarea = event.target;
textarea.value = capitalize(TAPi18n.__('r-checklist'));
textarea.select();
}
},
events() {
const events = {
'click .toggle-delete-checklist-dialog'(event) {
@ -196,6 +204,7 @@ BlazeComponent.extendComponent({
'submit .js-edit-checklist-item': this.editChecklistItem,
'click .js-delete-checklist-item': this.deleteItem,
'click .confirm-checklist-delete': this.deleteChecklist,
'focus .js-add-checklist-item': this.focusChecklistItem,
keydown: this.pressKey,
},
];
@ -250,7 +259,7 @@ BlazeComponent.extendComponent({
events() {
return [
{
'click .js-checklist-item .check-box': this.toggleItem,
'click .js-checklist-item .check-box-container': this.toggleItem,
},
];
},

View file

@ -113,6 +113,9 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
&:hover
background-color: darken(white, 8%)
.check-box-container
padding-right: 1px;
.check-box
margin: 0.1em 0 0 0;
&.is-checked
@ -121,7 +124,7 @@ textarea.js-add-checklist-item, textarea.js-edit-checklist-item
.item-title
flex: 1
padding-left: 10px;
margin-left: 10px;
&.is-checked
color: #8c8c8c
font-style: italic

View file

@ -158,6 +158,8 @@
.edit-labels-pop-over
margin-bottom: 8px
.card-label .viewer p
margin: 0
.edit-labels-pop-over .shortcut
display: inline-block

View file

@ -4,8 +4,8 @@ template(name="minicard")
class="{{#if isLinkedBoard}}linked-board{{/if}}"
class="minicard-{{colorClass}}")
if isMiniScreen
//.handle
// .fa.fa-arrows
.handle
.fa.fa-arrows
unless isMiniScreen
if showDesktopDragHandles
.handle
@ -100,6 +100,10 @@ template(name="minicard")
if getDescription
.badge.badge-state-image-only(title=getDescription)
span.badge-icon.fa.fa-align-left
if getVoteQuestion
.badge.badge-state-image-only(title=getVoteQuestion)
span.badge-icon.fa.fa-thumbs-up
span.badge-icon.fa.fa-thumbs-down
if attachments.count
.badge
span.badge-icon.fa.fa-paperclip

View file

@ -79,7 +79,7 @@
border-radius: top 2px
.minicard-labels
float: right
float: none
display: flex
flex-wrap: wrap

View file

@ -20,7 +20,22 @@ BlazeComponent.extendComponent({
const crtBoard = Boards.findOne(card.boardId);
const targetBoard = crtBoard.getDefaultSubtasksBoard();
const listId = targetBoard.getDefaultSubtasksListId();
const swimlaneId = targetBoard.getDefaultSwimline()._id;
//Get the full swimlane data for the parent task.
const parentSwimlane = Swimlanes.findOne({
boardId: crtBoard._id,
_id: card.swimlaneId,
});
//find the swimlane of the same name in the target board.
const targetSwimlane = Swimlanes.findOne({
boardId: targetBoard._id,
title: parentSwimlane.title,
});
//If no swimlane with a matching title exists in the target board, fall back to the default swimlane.
const swimlaneId =
targetSwimlane === undefined
? targetBoard.getDefaultSwimline()._id
: targetSwimlane._id;
if (title) {
const _id = Cards.insert({