Browse Source

Merge pull request #4643 from mfilser/move_copy_card_dialog_remember_last_selected_board

Move and copy card dialog remember last selected board
Lauri Ojansivu 2 years ago
parent
commit
0553e36bfb
3 changed files with 189 additions and 135 deletions
  1. 8 8
      client/components/cards/cardDetails.jade
  2. 179 127
      client/components/cards/cardDetails.js
  3. 2 0
      models/cards.js

+ 8 - 8
client/components/cards/cardDetails.jade

@@ -712,21 +712,21 @@ template(name="exportCardPopup")
         | {{_ 'export-card-pdf'}}
         | {{_ 'export-card-pdf'}}
 
 
 template(name="moveCardPopup")
 template(name="moveCardPopup")
-  +boardsAndLists
+  +copyAndMoveCard
 
 
 template(name="copyCardPopup")
 template(name="copyCardPopup")
   label(for='copy-card-title') {{_ 'title'}}:
   label(for='copy-card-title') {{_ 'title'}}:
   textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
   textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
     = getTitle
     = getTitle
-  +boardsAndLists
+  +copyAndMoveCard
 
 
 template(name="copyChecklistToManyCardsPopup")
 template(name="copyChecklistToManyCardsPopup")
   label(for='copy-checklist-cards-title') {{_ 'copyChecklistToManyCardsPopup-instructions'}}:
   label(for='copy-checklist-cards-title') {{_ 'copyChecklistToManyCardsPopup-instructions'}}:
   textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
   textarea#copy-card-title.minicard-composer-textarea.js-card-title(autofocus)
     | {{_ 'copyChecklistToManyCardsPopup-format'}}
     | {{_ 'copyChecklistToManyCardsPopup-format'}}
-  +boardsAndLists
+  +copyAndMoveCard
 
 
-template(name="boardsAndLists")
+template(name="copyAndMoveCard")
   unless currentUser.isWorker
   unless currentUser.isWorker
     label {{_ 'boards'}}:
     label {{_ 'boards'}}:
     select.js-select-boards(autofocus)
     select.js-select-boards(autofocus)
@@ -734,17 +734,17 @@ template(name="boardsAndLists")
         if $eq _id currentBoard._id
         if $eq _id currentBoard._id
           option(value="{{_id}}" selected) {{_ 'current'}}
           option(value="{{_id}}" selected) {{_ 'current'}}
         else
         else
-          option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionBoardId _id}}selected{{/if}}") {{title}}
+          option(value="{{_id}}" selected="{{#if isCardDialogOptionBoardId _id}}selected{{/if}}") {{title}}
 
 
   label {{_ 'swimlanes'}}:
   label {{_ 'swimlanes'}}:
   select.js-select-swimlanes
   select.js-select-swimlanes
     each swimlanes
     each swimlanes
-      option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
+      option(value="{{_id}}" selected="{{#if isCardDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
 
 
   label {{_ 'lists'}}:
   label {{_ 'lists'}}:
   select.js-select-lists
   select.js-select-lists
-    each aBoardLists
-      option(value="{{_id}}" selected="{{#if isMoveAndCopyDialogOptionListId _id}}selected{{/if}}") {{title}}
+    each lists
+      option(value="{{_id}}" selected="{{#if isCardDialogOptionListId _id}}selected{{/if}}") {{title}}
 
 
   .edit-controls.clearfix
   .edit-controls.clearfix
     button.primary.confirm.js-done {{_ 'done'}}
     button.primary.confirm.js-done {{_ 'done'}}

+ 179 - 127
client/components/cards/cardDetails.js

@@ -826,110 +826,152 @@ Template.editCardAssignerForm.events({
   },
   },
 });
 });
 
 
-Template.moveCardPopup.events({
-  'click .js-done'() {
-    const card = Cards.findOne(this._id);
-    const bSelect = $('.js-select-boards')[0];
-    let boardId;
-    // if we are a worker, we won't have a board select so we just use the
-    // current boardId of the card.
-    if (bSelect) {
-      boardId = bSelect.options[bSelect.selectedIndex].value;
-    } else {
-      boardId = card.boardId;
-    }
-    const lSelect = $('.js-select-lists')[0];
-    const listId = lSelect.options[lSelect.selectedIndex].value;
-    const slSelect = $('.js-select-swimlanes')[0];
-    const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
-
-    const minOrder = card.getMinSort(listId, swimlaneId);
-    card.move(boardId, swimlaneId, listId, minOrder - 1);
+class DialogWithBoardSwimlaneList extends BlazeComponent {
+  /** returns the card dialog options
+   * @return Object with properties { boardId, swimlaneId, listId }
+   */
+  getCardDialogOptions() {
+  }
 
 
-    // set new id's to card object in case the card is moved to top by the comment "moveCard" after this command (.js-move-card)
-    this.boardId = boardId;
-    this.swimlaneId = swimlaneId;
-    this.listId = listId;
+  /** list is done
+   * @param listId the selected list id
+   * @param options the selected options (Object with properties { boardId, swimlaneId, listId })
+   */
+  setDone(listId, options) {
+  }
 
 
-    Popup.back(2);
-  },
-});
-BlazeComponent.extendComponent({
   onCreated() {
   onCreated() {
     this.currentBoardId = Utils.getCurrentBoardId();
     this.currentBoardId = Utils.getCurrentBoardId();
     this.selectedBoardId = new ReactiveVar(this.currentBoardId);
     this.selectedBoardId = new ReactiveVar(this.currentBoardId);
-    this.setMoveAndCopyDialogOption(this.currentBoardId);
-  },
+    this.selectedSwimlaneId = new ReactiveVar('');
+    this.selectedListId = new ReactiveVar('');
+    this.setCardDialogOption(this.currentBoardId);
+  }
 
 
   /** set the last confirmed dialog field values
   /** set the last confirmed dialog field values
    * @param boardId the current board id
    * @param boardId the current board id
    */
    */
-  setMoveAndCopyDialogOption(boardId) {
-    this.moveAndCopyDialogOption = {
-      'boardId': "",
-      'swimlaneId': "",
-      'listId': "",
+  setCardDialogOption(boardId) {
+    this.cardDialogOption = {
+      'boardId' : "",
+      'swimlaneId' : "",
+      'listId' : "",
     }
     }
 
 
-    let currentOptions = Meteor.user().getMoveAndCopyDialogOptions();
+    let currentOptions = this.getCardDialogOptions();
     if (currentOptions && boardId && currentOptions[boardId]) {
     if (currentOptions && boardId && currentOptions[boardId]) {
-      this.moveAndCopyDialogOption = currentOptions[boardId];
-      if (this.moveAndCopyDialogOption.boardId) {
-        this.selectedBoardId.set(this.moveAndCopyDialogOption.boardId)
+      this.cardDialogOption = currentOptions[boardId];
+      if (this.cardDialogOption.boardId &&
+          this.cardDialogOption.swimlaneId &&
+          this.cardDialogOption.listId
+      )
+      {
+        this.selectedBoardId.set(this.cardDialogOption.boardId)
+        this.selectedSwimlaneId.set(this.cardDialogOption.swimlaneId);
+        this.selectedListId.set(this.cardDialogOption.listId);
       }
       }
     }
     }
-    subManager.subscribe('board', this.selectedBoardId.get(), false);
-  },
+    this.getBoardData(this.selectedBoardId.get());
+    if (!this.selectedSwimlaneId.get() || !Swimlanes.findOne({_id: this.selectedSwimlaneId.get(), boardId: this.selectedBoardId.get()})) {
+      this.setFirstSwimlaneId();
+    }
+    if (!this.selectedListId.get() || !Lists.findOne({_id: this.selectedListId.get(), boardId: this.selectedBoardId.get()})) {
+      this.setFirstListId();
+    }
+  }
+  /** sets the first swimlane id */
+  setFirstSwimlaneId() {
+    try {
+      const board = Boards.findOne(this.selectedBoardId.get());
+      const swimlaneId = board.swimlanes().fetch()[0]._id;
+      this.selectedSwimlaneId.set(swimlaneId);
+    } catch (e) {}
+  }
+  /** sets the first list id */
+  setFirstListId() {
+    try {
+      const board = Boards.findOne(this.selectedBoardId.get());
+      const listId = board.lists().fetch()[0]._id;
+      this.selectedListId.set(listId);
+    } catch (e) {}
+  }
 
 
   /** returns if the board id was the last confirmed one
   /** returns if the board id was the last confirmed one
    * @param boardId check this board id
    * @param boardId check this board id
    * @return if the board id was the last confirmed one
    * @return if the board id was the last confirmed one
    */
    */
-  isMoveAndCopyDialogOptionBoardId(boardId) {
-    let ret = this.moveAndCopyDialogOption.boardId == boardId;
+  isCardDialogOptionBoardId(boardId) {
+    let ret = this.cardDialogOption.boardId == boardId;
     return ret;
     return ret;
-  },
+  }
 
 
   /** returns if the swimlane id was the last confirmed one
   /** returns if the swimlane id was the last confirmed one
    * @param swimlaneId check this swimlane id
    * @param swimlaneId check this swimlane id
    * @return if the swimlane id was the last confirmed one
    * @return if the swimlane id was the last confirmed one
    */
    */
-  isMoveAndCopyDialogOptionSwimlaneId(swimlaneId) {
-    let ret = this.moveAndCopyDialogOption.swimlaneId == swimlaneId;
+  isCardDialogOptionSwimlaneId(swimlaneId) {
+    let ret = this.cardDialogOption.swimlaneId == swimlaneId;
     return ret;
     return ret;
-  },
+  }
 
 
   /** returns if the list id was the last confirmed one
   /** returns if the list id was the last confirmed one
    * @param listId check this list id
    * @param listId check this list id
    * @return if the list id was the last confirmed one
    * @return if the list id was the last confirmed one
    */
    */
-  isMoveAndCopyDialogOptionListId(listId) {
-    let ret = this.moveAndCopyDialogOption.listId == listId;
+  isCardDialogOptionListId(listId) {
+    let ret = this.cardDialogOption.listId == listId;
     return ret;
     return ret;
-  },
+  }
 
 
+  /** returns all available board */
   boards() {
   boards() {
-    return Boards.find(
+    const ret = Boards.find(
       {
       {
         archived: false,
         archived: false,
         'members.userId': Meteor.userId(),
         'members.userId': Meteor.userId(),
         _id: { $ne: Meteor.user().getTemplatesBoardId() },
         _id: { $ne: Meteor.user().getTemplatesBoardId() },
       },
       },
       {
       {
-        sort: { sort: 1 /* boards default sorting */ },
+        sort: { sort: 1 },
       },
       },
     );
     );
-  },
+    return ret;
+  }
 
 
+  /** returns all available swimlanes of the current board */
   swimlanes() {
   swimlanes() {
     const board = Boards.findOne(this.selectedBoardId.get());
     const board = Boards.findOne(this.selectedBoardId.get());
-    return board.swimlanes();
-  },
+    const ret = board.swimlanes();
+    return ret;
+  }
 
 
-  aBoardLists() {
+  /** returns all available lists of the current board */
+  lists() {
     const board = Boards.findOne(this.selectedBoardId.get());
     const board = Boards.findOne(this.selectedBoardId.get());
-    return board.lists();
-  },
+    const ret = board.lists();
+    return ret;
+  }
+
+  /** get the board data from the server
+   * @param boardId get the board data of this board id
+   */
+  getBoardData(boardId) {
+    const self = this;
+    Meteor.subscribe('board', boardId, false, {
+      onReady() {
+        const sameBoardId = self.selectedBoardId.get() == boardId;
+        self.selectedBoardId.set(boardId);
+
+        if (!sameBoardId) {
+          // reset swimlane id (for selection in cards())
+          self.setFirstSwimlaneId();
+
+          // reset list id (for selection in cards())
+          self.setFirstListId();
+        }
+      },
+    });
+  }
 
 
   events() {
   events() {
     return [
     return [
@@ -945,52 +987,75 @@ BlazeComponent.extendComponent({
           const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;
           const swimlaneId = swimlaneSelect.options[swimlaneSelect.selectedIndex].value;
 
 
           const options = {
           const options = {
-            'boardId': boardId,
-            'swimlaneId': swimlaneId,
-            'listId': listId,
+            'boardId' : boardId,
+            'swimlaneId' : swimlaneId,
+            'listId' : listId,
           }
           }
-          Meteor.user().setMoveAndCopyDialogOption(this.currentBoardId, options);
+          this.setDone(boardId, swimlaneId, listId, options);
+          Popup.back(2);
         },
         },
         'change .js-select-boards'(event) {
         'change .js-select-boards'(event) {
           const boardId = $(event.currentTarget).val();
           const boardId = $(event.currentTarget).val();
-          this.selectedBoardId.set(boardId);
-          subManager.subscribe('board', boardId, false);
+          this.getBoardData(boardId);
+        },
+        'change .js-select-swimlanes'(event) {
+          this.selectedSwimlaneId.set($(event.currentTarget).val());
         },
         },
       },
       },
     ];
     ];
-  },
-}).register('boardsAndLists');
-
-Template.copyCardPopup.events({
-  'click .js-done'() {
-    const card = Utils.getCurrentCard();
-    const lSelect = $('.js-select-lists')[0];
-    const listId = lSelect.options[lSelect.selectedIndex].value;
-    const slSelect = $('.js-select-swimlanes')[0];
-    const swimlaneId = slSelect.options[slSelect.selectedIndex].value;
-    const bSelect = $('.js-select-boards')[0];
-    const boardId = bSelect.options[bSelect.selectedIndex].value;
-    const textarea = $('#copy-card-title');
-    const title = textarea.val().trim();
+  }
+}
 
 
-    // insert new card to the top of new list
+/** Move Card Dialog */
+(class extends DialogWithBoardSwimlaneList {
+  getCardDialogOptions() {
+    const ret = Meteor.user().getMoveAndCopyDialogOptions();
+    return ret;
+  }
+  setDone(boardId, swimlaneId, listId, options) {
+    Meteor.user().setMoveAndCopyDialogOption(this.currentBoardId, options);
+    const card = this.data();
     const minOrder = card.getMinSort(listId, swimlaneId);
     const minOrder = card.getMinSort(listId, swimlaneId);
-    card.sort = minOrder - 1;
+    card.move(boardId, swimlaneId, listId, minOrder - 1);
+  }
+}).register('moveCardPopup');
+
+/** Copy Card Dialog */
+(class extends DialogWithBoardSwimlaneList {
+  getCardDialogOptions() {
+    const ret = Meteor.user().getMoveAndCopyDialogOptions();
+    return ret;
+  }
+  setDone(boardId, swimlaneId, listId, options) {
+    Meteor.user().setMoveAndCopyDialogOption(this.currentBoardId, options);
+    const card = this.data();
+
+    // const textarea = $('#copy-card-title');
+    const textarea = this.$('#copy-card-title');
+    const title = textarea.val().trim();
 
 
     if (title) {
     if (title) {
+      const oldTitle = card.title;
       card.title = title;
       card.title = title;
       card.coverId = '';
       card.coverId = '';
-      const _id = card.copy(boardId, swimlaneId, listId);
+
+      // insert new card to the top of new list
+      const minOrder = card.getMinSort(listId, swimlaneId);
+      card.sort = minOrder - 1;
+
+      const newCardId = card.copy(boardId, swimlaneId, listId);
+
+      // restore the old card title, otherwise the card title would change in the current view (only temporary)
+      card.title = oldTitle;
+
       // In case the filter is active we need to add the newly inserted card in
       // 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
       // the list of exceptions -- cards that are not filtered. Otherwise the
       // card will disappear instantly.
       // card will disappear instantly.
       // See https://github.com/wekan/wekan/issues/80
       // See https://github.com/wekan/wekan/issues/80
-      Filter.addException(_id);
-
-      Popup.back(2);
+      Filter.addException(newCardId);
     }
     }
-  },
-});
+  }
+}).register('copyCardPopup');
 
 
 Template.convertChecklistItemToCardPopup.events({
 Template.convertChecklistItemToCardPopup.events({
   'click .js-done'() {
   'click .js-done'() {
@@ -1020,60 +1085,47 @@ Template.convertChecklistItemToCardPopup.events({
   },
   },
 });
 });
 
 
-Template.copyChecklistToManyCardsPopup.events({
-  'click .js-done'() {
-    const card = Utils.getCurrentCard();
-    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();
+/** Copy many cards dialog */
+(class extends DialogWithBoardSwimlaneList {
+  getCardDialogOptions() {
+    const ret = Meteor.user().getMoveAndCopyDialogOptions();
+    return ret;
+  }
+  setDone(boardId, swimlaneId, listId, options) {
+    Meteor.user().setMoveAndCopyDialogOption(this.currentBoardId, options);
+    const card = this.data();
+
+    const textarea = this.$('#copy-card-title');
+    const title = textarea.val().trim();
 
 
-    if (titleEntry) {
-      const titleList = JSON.parse(titleEntry);
+    if (title) {
+      const oldTitle = card.title;
+
+      const titleList = JSON.parse(title);
       for (let i = 0; i < titleList.length; i++) {
       for (let i = 0; i < titleList.length; i++) {
         const obj = titleList[i];
         const obj = titleList[i];
         card.title = obj.title;
         card.title = obj.title;
         card.description = obj.description;
         card.description = obj.description;
         card.coverId = '';
         card.coverId = '';
-        const _id = Cards.insert(card);
+
+        // insert new card to the top of new list
+        const maxOrder = card.getMaxSort(listId, swimlaneId);
+        card.sort = maxOrder + 1;
+
+        const newCardId = card.copy(boardId, swimlaneId, listId);
+
         // In case the filter is active we need to add the newly inserted card in
         // 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
         // the list of exceptions -- cards that are not filtered. Otherwise the
         // card will disappear instantly.
         // card will disappear instantly.
         // See https://github.com/wekan/wekan/issues/80
         // See https://github.com/wekan/wekan/issues/80
-        Filter.addException(_id);
-
-        // copy checklists
-        Checklists.find({ cardId: oldId }).forEach((ch) => {
-          ch.copy(_id);
-        });
-
-        // copy subtasks
-        const cursor = Cards.find({ parentId: oldId });
-        cursor.forEach(function () {
-          'use strict';
-          const subtask = arguments[0];
-          subtask.parentId = _id;
-          subtask._id = null;
-          /* const newSubtaskId = */ Cards.insert(subtask);
-        });
-
-        // copy card comments
-        CardComments.find({ cardId: oldId }).forEach((cmt) => {
-          cmt.copy(_id);
-        });
+        Filter.addException(newCardId);
       }
       }
-      Popup.back();
+
+      // restore the old card title, otherwise the card title would change in the current view (only temporary)
+      card.title = oldTitle;
     }
     }
-  },
-});
+  }
+}).register('copyChecklistToManyCardsPopup');
 
 
 BlazeComponent.extendComponent({
 BlazeComponent.extendComponent({
   onCreated() {
   onCreated() {

+ 2 - 0
models/cards.js

@@ -609,6 +609,8 @@ Cards.helpers({
     CardComments.find({ cardId: oldId }).forEach(cmt => {
     CardComments.find({ cardId: oldId }).forEach(cmt => {
       cmt.copy(_id);
       cmt.copy(_id);
     });
     });
+    // restore the id, otherwise new copies will fail
+    this._id = oldId;
 
 
     return _id;
     return _id;
   },
   },