diff options
Diffstat (limited to 'client/components/cards/cardDetails.js')
-rw-r--r-- | client/components/cards/cardDetails.js | 221 |
1 files changed, 201 insertions, 20 deletions
diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index 8d5c478d..26549fda 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -1,3 +1,6 @@ +const subManager = new SubsManager(); +const { calculateIndexData } = Utils; + BlazeComponent.extendComponent({ mixins() { return [Mixins.InfiniteScrolling, Mixins.PerfectScrollbar]; @@ -18,9 +21,11 @@ BlazeComponent.extendComponent({ onCreated() { this.isLoaded = new ReactiveVar(false); - this.parentComponent().showOverlay.set(true); - this.parentComponent().mouseHasEnterCardDetails = false; + this.parentComponent().parentComponent().showOverlay.set(true); + this.parentComponent().parentComponent().mouseHasEnterCardDetails = false; this.calculateNextPeak(); + + Meteor.subscribe('unsaved-edits'); }, isWatching() { @@ -28,16 +33,20 @@ BlazeComponent.extendComponent({ return card.findWatcher(Meteor.userId()); }, + hiddenSystemMessages() { + return Meteor.user().hasHiddenSystemMessages(); + }, + canModifyCard() { return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); }, scrollParentContainer() { const cardPanelWidth = 510; - const bodyBoardComponent = this.parentComponent(); + const bodyBoardComponent = this.parentComponent().parentComponent(); - const $cardContainer = bodyBoardComponent.$('.js-lists'); const $cardView = this.$(this.firstNode()); + const $cardContainer = bodyBoardComponent.$('.js-swimlanes'); const cardContainerScroll = $cardContainer.scrollLeft(); const cardContainerWidth = $cardContainer.width(); @@ -58,10 +67,55 @@ BlazeComponent.extendComponent({ onRendered() { if (!Utils.isMiniScreen()) this.scrollParentContainer(); + const $checklistsDom = this.$('.card-checklist-items'); + + $checklistsDom.sortable({ + tolerance: 'pointer', + helper: 'clone', + handle: '.checklist-title', + items: '.js-checklist', + placeholder: 'checklist placeholder', + distance: 7, + start(evt, ui) { + ui.placeholder.height(ui.helper.height()); + EscapeActions.executeUpTo('popup-close'); + }, + stop(evt, ui) { + let prevChecklist = ui.item.prev('.js-checklist').get(0); + if (prevChecklist) { + prevChecklist = Blaze.getData(prevChecklist).checklist; + } + let nextChecklist = ui.item.next('.js-checklist').get(0); + if (nextChecklist) { + nextChecklist = Blaze.getData(nextChecklist).checklist; + } + const sortIndex = calculateIndexData(prevChecklist, nextChecklist, 1); + + $checklistsDom.sortable('cancel'); + const checklist = Blaze.getData(ui.item.get(0)).checklist; + + Checklists.update(checklist._id, { + $set: { + sort: sortIndex.base, + }, + }); + }, + }); + + 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()); + } + }); }, onDestroyed() { - this.parentComponent().showOverlay.set(false); + this.parentComponent().parentComponent().showOverlay.set(false); }, events() { @@ -95,9 +149,16 @@ BlazeComponent.extendComponent({ 'click .js-member': Popup.open('cardMember'), 'click .js-add-members': Popup.open('cardMembers'), 'click .js-add-labels': Popup.open('cardLabels'), + 'click .js-received-date': Popup.open('editCardReceivedDate'), + 'click .js-start-date': Popup.open('editCardStartDate'), + 'click .js-due-date': Popup.open('editCardDueDate'), + 'click .js-end-date': Popup.open('editCardEndDate'), 'mouseenter .js-card-details' () { - this.parentComponent().showOverlay.set(true); - this.parentComponent().mouseHasEnterCardDetails = true; + this.parentComponent().parentComponent().showOverlay.set(true); + this.parentComponent().parentComponent().mouseHasEnterCardDetails = true; + }, + 'click #toggleButton'() { + Meteor.call('toggleSystemMessages'); }, }]; }, @@ -154,20 +215,24 @@ Template.cardDetailsActionsPopup.events({ 'click .js-members': Popup.open('cardMembers'), 'click .js-labels': Popup.open('cardLabels'), 'click .js-attachments': Popup.open('cardAttachments'), + 'click .js-received-date': Popup.open('editCardReceivedDate'), 'click .js-custom-fields': Popup.open('cardCustomFields'), 'click .js-start-date': Popup.open('editCardStartDate'), 'click .js-due-date': Popup.open('editCardDueDate'), + 'click .js-end-date': Popup.open('editCardEndDate'), + 'click .js-spent-time': Popup.open('editCardSpentTime'), 'click .js-move-card': Popup.open('moveCard'), 'click .js-copy-card': Popup.open('copyCard'), + 'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'), 'click .js-move-card-to-top' (evt) { evt.preventDefault(); - const minOrder = _.min(this.list().cards().map((c) => c.sort)); - this.move(this.listId, minOrder - 1); + const minOrder = _.min(this.list().cards(this.swimlaneId).map((c) => c.sort)); + this.move(this.swimlaneId, this.listId, minOrder - 1); }, 'click .js-move-card-to-bottom' (evt) { evt.preventDefault(); - const maxOrder = _.max(this.list().cards().map((c) => c.sort)); - this.move(this.listId, maxOrder + 1); + const maxOrder = _.max(this.list().cards(this.swimlaneId).map((c) => c.sort)); + this.move(this.swimlaneId, this.listId, maxOrder + 1); }, 'click .js-archive' (evt) { evt.preventDefault(); @@ -191,36 +256,82 @@ Template.editCardTitleForm.onRendered(function () { Template.editCardTitleForm.events({ 'keydown .js-edit-card-title' (evt) { // If enter key was pressed, submit the data - if (evt.keyCode === 13) { + // Unless the shift key is also being pressed + if (evt.keyCode === 13 && !evt.shiftKey) { $('.js-submit-edit-card-title-form').click(); } }, }); Template.moveCardPopup.events({ - 'click .js-select-list' () { + 'click .js-done' () { // XXX We should *not* get the currentCard from the global state, but // instead from a “component” state. const card = Cards.findOne(Session.get('currentCard')); - const newListId = this._id; - card.move(newListId); + const lSelect = $('.js-select-lists')[0]; + const newListId = lSelect.options[lSelect.selectedIndex].value; + const slSelect = $('.js-select-swimlanes')[0]; + card.swimlaneId = slSelect.options[slSelect.selectedIndex].value; + card.move(card.swimlaneId, newListId, 0); Popup.close(); }, }); +BlazeComponent.extendComponent({ + onCreated() { + subManager.subscribe('board', Session.get('currentBoard')); + this.selectedBoardId = new ReactiveVar(Session.get('currentBoard')); + }, + + boards() { + const boards = Boards.find({ + archived: false, + 'members.userId': Meteor.userId(), + }, { + sort: ['title'], + }); + return boards; + }, + + swimlanes() { + const board = Boards.findOne(this.selectedBoardId.get()); + return board.swimlanes(); + }, + + aBoardLists() { + const board = Boards.findOne(this.selectedBoardId.get()); + return board.lists(); + }, + + events() { + return [{ + 'change .js-select-boards'(evt) { + this.selectedBoardId.set($(evt.currentTarget).val()); + subManager.subscribe('board', this.selectedBoardId.get()); + }, + }]; + }, +}).register('boardsAndLists'); + Template.copyCardPopup.events({ - 'click .js-select-list' (evt) { + 'click .js-done'() { const card = Cards.findOne(Session.get('currentCard')); const oldId = card._id; card._id = null; - card.listId = this._id; - const textarea = $(evt.currentTarget).parents('.content').find('textarea'); + const lSelect = $('.js-select-lists')[0]; + card.listId = lSelect.options[lSelect.selectedIndex].value; + const slSelect = $('.js-select-swimlanes')[0]; + card.swimlaneId = slSelect.options[slSelect.selectedIndex].value; + const bSelect = $('.js-select-boards')[0]; + card.boardId = bSelect.options[bSelect.selectedIndex].value; + const textarea = $('#copy-card-title'); const title = textarea.val().trim(); // insert new card to the bottom of new list - card.sort = Lists.findOne(this._id).cards().count(); + card.sort = Lists.findOne(card.listId).cards().count(); if (title) { card.title = title; + card.coverId = ''; const _id = Cards.insert(card); // In case the filter is active we need to add the newly inserted card in // the list of exceptions -- cards that are not filtered. Otherwise the @@ -233,9 +344,16 @@ Template.copyCardPopup.events({ cursor.forEach(function() { 'use strict'; const checklist = arguments[0]; + const checklistId = checklist._id; checklist.cardId = _id; checklist._id = null; - Checklists.insert(checklist); + const newChecklistId = Checklists.insert(checklist); + ChecklistItems.find({checklistId}).forEach(function(item) { + item._id = null; + item.checklistId = newChecklistId; + item.cardId = _id; + ChecklistItems.insert(item); + }); }); // copy card comments @@ -252,6 +370,69 @@ Template.copyCardPopup.events({ }, }); +Template.copyChecklistToManyCardsPopup.events({ + 'click .js-done' () { + const card = Cards.findOne(Session.get('currentCard')); + const oldId = card._id; + card._id = null; + const lSelect = $('.js-select-lists')[0]; + card.listId = lSelect.options[lSelect.selectedIndex].value; + const slSelect = $('.js-select-swimlanes')[0]; + card.swimlaneId = slSelect.options[slSelect.selectedIndex].value; + const bSelect = $('.js-select-boards')[0]; + card.boardId = bSelect.options[bSelect.selectedIndex].value; + const textarea = $('#copy-card-title'); + const titleEntry = textarea.val().trim(); + // insert new card to the bottom of new list + card.sort = Lists.findOne(card.listId).cards().count(); + + if (titleEntry) { + const titleList = JSON.parse(titleEntry); + for (let i = 0; i < titleList.length; i++){ + const obj = titleList[i]; + card.title = obj.title; + card.description = obj.description; + card.coverId = ''; + const _id = Cards.insert(card); + // In case the filter is active we need to add the newly inserted card in + // the list of exceptions -- cards that are not filtered. Otherwise the + // card will disappear instantly. + // See https://github.com/wekan/wekan/issues/80 + Filter.addException(_id); + + // copy checklists + let cursor = Checklists.find({cardId: oldId}); + cursor.forEach(function() { + 'use strict'; + const checklist = arguments[0]; + const checklistId = checklist._id; + checklist.cardId = _id; + checklist._id = null; + const newChecklistId = Checklists.insert(checklist); + ChecklistItems.find({checklistId}).forEach(function(item) { + item._id = null; + item.checklistId = newChecklistId; + item.cardId = _id; + ChecklistItems.insert(item); + }); + }); + + // copy card comments + cursor = CardComments.find({cardId: oldId}); + cursor.forEach(function () { + 'use strict'; + const comment = arguments[0]; + comment.cardId = _id; + comment._id = null; + CardComments.insert(comment); + }); + } + Popup.close(); + } + }, +}); + + Template.cardMorePopup.events({ 'click .js-copy-card-link-to-clipboard' () { // Clipboard code from: |