From a13fad749e8a75025bb13de87f0170e1ea9e462d Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Tue, 8 Dec 2015 16:18:44 -0500 Subject: Change the board import layout from a popup to a full page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit also removes the “import a single Trello card” as we couldn’t figure out some reasonable use case. We also create a new publication on the server to provide the minimal user profile informations required to display an avatar. --- client/components/import/import.js | 205 +++++++++++++++---------------------- 1 file changed, 82 insertions(+), 123 deletions(-) (limited to 'client/components/import/import.js') diff --git a/client/components/import/import.js b/client/components/import/import.js index ec469a77..169f9dd0 100644 --- a/client/components/import/import.js +++ b/client/components/import/import.js @@ -1,68 +1,46 @@ -/// Abstract root for all import popup screens. -/// Descendants must define: -/// - getMethodName(): return the Meteor method to call for import, passing json -/// data decoded as object and additional data (see below); -/// - getAdditionalData(): return object containing additional data passed to -/// Meteor method (like list ID and position for a card import); -/// - getLabel(): i18n key for the text displayed in the popup, usually to -/// explain how to get the data out of the source system. -const ImportPopup = BlazeComponent.extendComponent({ - jsonText() { - return Session.get('import.text'); - }, - - membersMapping() { - return Session.get('import.membersToMap'); - }, - +BlazeComponent.extendComponent({ onCreated() { this.error = new ReactiveVar(''); - this.dataToImport = ''; + this.steps = ['importTextarea', 'importMapMembers']; + this._currentStepIndex = new ReactiveVar(0); + this.importedData = new ReactiveVar(); + this.membersToMap = new ReactiveVar([]); }, - onFinish() { - Popup.close(); + currentTemplate() { + return this.steps[this._currentStepIndex.get()]; }, - onShowMapping(evt) { - this._storeText(evt); - Popup.open('mapMembers')(evt); + nextStep() { + const nextStepIndex = this._currentStepIndex.get() + 1; + if (nextStepIndex >= this.steps.length) { + this.finishImport(); + } else { + this._currentStepIndex.set(nextStepIndex); + } }, - onSubmit(evt){ + importData(evt) { evt.preventDefault(); - const dataJson = this._storeText(evt); - let dataObject; + const dataJson = this.find('.js-import-json').value; try { - dataObject = JSON.parse(dataJson); + const dataObject = JSON.parse(dataJson); this.setError(''); + this.importedData.set(dataObject); + this._prepareAdditionalData(dataObject); + this.nextStep(); } catch (e) { this.setError('error-json-malformed'); - return; - } - if(this._hasAllNeededData(dataObject)) { - this._import(dataObject); - } else { - this._prepareAdditionalData(dataObject); - Popup.open(this._screenAdditionalData())(evt); - } }, - events() { - return [{ - submit: this.onSubmit, - 'click .show-mapping': this.onShowMapping, - }]; - }, - setError(error) { this.error.set(error); }, - _import(dataObject) { - const additionalData = this.getAdditionalData(); - const membersMapping = this.membersMapping(); + finishImport() { + const additionalData = {}; + const membersMapping = this.membersToMap.get(); if (membersMapping) { const mappingById = {}; membersMapping.forEach((member) => { @@ -72,99 +50,75 @@ const ImportPopup = BlazeComponent.extendComponent({ }); additionalData.membersMapping = mappingById; } - Session.set('import.membersToMap', null); - Session.set('import.text', null); - Meteor.call(this.getMethodName(), dataObject, additionalData, - (error, response) => { - if (error) { - this.setError(error.error); + this.membersToMap.set([]); + Meteor.call('importTrelloBoard', this.importedData.get(), additionalData, + (err, res) => { + if (err) { + this.setError(err.error); } else { - // ensure will display what we just imported - Filter.addException(response); - this.onFinish(response); + Utils.goBoardId(res); } } ); }, - _hasAllNeededData(dataObject) { - // import has no members or they are already mapped - return dataObject.members.length === 0 || this.membersMapping(); - }, - _prepareAdditionalData(dataObject) { - // we will work on the list itself (an ordered array of objects) - // when a mapping is done, we add a 'wekan' field to the object representing the imported member + // we will work on the list itself (an ordered array of objects) when a + // mapping is done, we add a 'wekan' field to the object representing the + // imported member const membersToMap = dataObject.members; // auto-map based on username membersToMap.forEach((importedMember) => { - const wekanUser = Users.findOne({username: importedMember.username}); - if(wekanUser) { + const wekanUser = Users.findOne({ username: importedMember.username }); + if (wekanUser) { importedMember.wekan = wekanUser; } }); // store members data and mapping in Session // (we go deep and 2-way, so storing in data context is not a viable option) - Session.set('import.membersToMap', membersToMap); + this.membersToMap.set(membersToMap); return membersToMap; }, _screenAdditionalData() { return 'mapMembers'; }, +}).register('import'); - _storeText() { - const dataJson = this.$('.js-import-json').val(); - Session.set('import.text', dataJson); - return dataJson; - }, -}); - -ImportPopup.extendComponent({ - getAdditionalData() { - const listId = this.currentData()._id; - const selector = `#js-list-${this.currentData()._id} .js-minicard:first`; - const firstCardDom = $(selector).get(0); - const sortIndex = Utils.calculateIndex(null, firstCardDom).base; - const result = {listId, sortIndex}; - return result; - }, - - getMethodName() { - return 'importTrelloCard'; +BlazeComponent.extendComponent({ + template() { + return 'importTextarea'; }, - getLabel() { - return 'import-card-trello-instruction'; - }, -}).register('listImportCardPopup'); - -ImportPopup.extendComponent({ - getAdditionalData() { - const result = {}; - return result; - }, - - getMethodName() { - return 'importTrelloBoard'; - }, - - getLabel() { - return 'import-board-trello-instruction'; + events() { + return [{ + submit(evt) { + return this.parentComponent().importData(evt); + }, + }]; }, +}).register('importTextarea'); - onFinish(response) { - Utils.goBoardId(response); +BlazeComponent.extendComponent({ + onCreated() { + this.autorun(() => { + this.parentComponent().membersToMap.get().forEach(({ wekan }) => { + if (wekan !== undefined) { + const userId = wekan._id; + this.subscribe('user-miniprofile', userId); + } + }); + }); }, -}).register('boardImportBoardPopup'); -const ImportMapMembers = BlazeComponent.extendComponent({ members() { - return Session.get('import.membersToMap'); + return this.parentComponent().membersToMap.get(); }, + _refreshMembers(listOfMembers) { - Session.set('import.membersToMap', listOfMembers); + return this.parentComponent().membersToMap.set(listOfMembers); }, + /** * Will look into the list of members to import for the specified memberId, * then set its property to the supplied value. @@ -202,15 +156,17 @@ const ImportMapMembers = BlazeComponent.extendComponent({ // Session.get gives us a copy, we have to set it back so it sticks this._refreshMembers(listOfMembers); }, + setSelectedMember(memberId) { return this._setPropertyForMember('selected', true, memberId, true); }, + /** * returns the member with specified id, * or the selected member if memberId is not specified */ getMember(memberId = null) { - const allMembers = Session.get('import.membersToMap'); + const allMembers = this.members(); let finder = null; if(memberId) { finder = (user) => user.id === memberId; @@ -219,15 +175,20 @@ const ImportMapMembers = BlazeComponent.extendComponent({ } return allMembers.find(finder); }, + mapSelectedMember(wekan) { return this._setPropertyForMember('wekan', wekan, null); }, + unmapMember(memberId){ return this._setPropertyForMember('wekan', null, memberId); }, -}); -ImportMapMembers.extendComponent({ + onSubmit(evt) { + evt.preventDefault(); + this.parentComponent().nextStep(); + }, + onMapMember(evt) { const memberToMap = this.currentData(); if(memberToMap.wekan) { @@ -235,33 +196,31 @@ ImportMapMembers.extendComponent({ this.unmapMember(memberToMap.id); } else { this.setSelectedMember(memberToMap.id); - Popup.open('mapMembersAdd')(evt); + Popup.open('importMapMembersAdd')(evt); } }, - onSubmit(evt) { - evt.preventDefault(); - Popup.back(); - }, + events() { return [{ 'submit': this.onSubmit, - 'click .mapping': this.onMapMember, + 'click .js-select-member': this.onMapMember, }]; }, -}).register('mapMembersPopup'); +}).register('importMapMembers'); + +BlazeComponent.extendComponent({ + onRendered() { + this.find('.js-map-member input').focus(); + }, -ImportMapMembers.extendComponent({ onSelectUser(){ - this.mapSelectedMember(this.currentData()); + Popup.getOpenerComponent().mapSelectedMember(this.currentData()); Popup.back(); }, + events() { return [{ 'click .js-select-import': this.onSelectUser, }]; }, - onRendered() { - // todo XXX why do I not get the focus?? - this.find('.js-map-member input').focus(); - }, -}).register('mapMembersAddPopup'); +}).register('importMapMembersAddPopup'); -- cgit v1.2.3-1-g7c22