From bfcfd2ebda283bed1caa973f27e1af6b81fe621b Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Sat, 3 Oct 2015 15:58:36 -0600 Subject: [PATCH 01/27] Initial support for @user and #label use in new cards. When creating a new [mini]card, typing `@` or `#` brings up an auto-complete box for board members and labels which will get applied to the card upon creation. These textual tags are removed from the card title before saving to maintain sanity. If a label doesn't have a name, it's colour is used (i.e. `red`, `purple`, etc). This was developed to ease the creation of new cards and allow users to rapidly create cards without having to click numerous times just to apply labels & members. --- client/components/lists/listBody.js | 98 ++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 2e00cb4fd..9d6aab88c 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -26,7 +26,7 @@ BlazeComponent.extendComponent({ const firstCardDom = this.find('.js-minicard:first'); const lastCardDom = this.find('.js-minicard:last'); const textarea = $(evt.currentTarget).find('textarea'); - const title = textarea.val(); + let title = textarea.val(); const position = Blaze.getData(evt.currentTarget).position; let sortIndex; if (position === 'top') { @@ -36,10 +36,41 @@ BlazeComponent.extendComponent({ } if ($.trim(title)) { + // Parse for @user and #label mentions, stripping them from the title + // and applying the appropriate users and labels to the card instead. + const currentBoard = Boards.findOne(Session.get('currentBoard')); + + // Find all @-mentioned usernames, collect a list of their IDs + // and strip their mention out of the title. + let foundUserIds = []; + currentBoard.members.forEach(member => { + const username = Users.findOne(member.userId).username; + let nameNdx = title.indexOf('@' + username); + if(nameNdx !== -1) { + foundUserIds.push(member.userId); + title = title.substr(0, nameNdx) + title.substr(nameNdx + username.length + 1); + } + }); + + // Find all #-mentioned labels (based on their colour or name), + // collect a list of their IDs, and strip their mention out of + // the title. + let foundLabelIds = []; + currentBoard.labels.forEach(label => { + const labelName = (!label.name || label.name === "") ? label.color : label.name; + let labelNdx = title.indexOf('#' + labelName); + if(labelNdx !== -1) { + foundLabelIds.push(label._id); + title = title.substr(0, labelNdx) + title.substr(labelNdx + labelName.length + 1); + } + }); + const _id = Cards.insert({ title, listId: this.data()._id, boardId: this.data().board()._id, + labelIds: foundLabelIds, + members: foundUserIds, sort: sortIndex, }); // In case the filter is active we need to add the newly inserted card in @@ -100,12 +131,18 @@ BlazeComponent.extendComponent({ }, }).register('listBody'); +let dropdownMenuIsOpened = false; BlazeComponent.extendComponent({ template() { return 'addCardForm'; }, pressKey(evt) { + // don't do anything if the drop down is showing + if(dropDownIsOpened) { + return; + } + // Pressing Enter should submit the card if (evt.keyCode === 13) { evt.preventDefault(); @@ -140,4 +177,63 @@ BlazeComponent.extendComponent({ keydown: this.pressKey, }]; }, + + onCreated() { + dropDownIsOpened = false; + }, + + onRendered() { + const $textarea = this.$('textarea'); + $textarea.textcomplete([ + // User mentions + { + match: /\B@(\w*)$/, + search(term, callback) { + const currentBoard = Boards.findOne(Session.get('currentBoard')); + callback($.map(currentBoard.members, (member) => { + const username = Users.findOne(member.userId).username; + return username.indexOf(term) === 0 ? username : null; + })); + }, + template(value) { + return value; + }, + replace(username) { + return `@${username} `; + }, + index: 1, + }, + + // Labels + { + match: /\B#(\w*)$/, + search(term, callback) { + const currentBoard = Boards.findOne(Session.get('currentBoard')); + callback($.map(currentBoard.labels, (label) => { + const labelName = (!label.name || label.name === "") ? label.color : label.name; + return labelName.indexOf(term) === 0 ? labelName : null; + })); + }, + template(value) { + return value; + }, + replace(label) { + return `#${label} `; + }, + index: 1, + }, + ]); + + // customize hooks for dealing with the dropdowns + $textarea.on({ + 'textComplete:show'() { + dropDownIsOpened = true; + }, + 'textComplete:hide'() { + Tracker.afterFlush(() => { + dropDownIsOpened = false; + }); + }, + }); + }, }).register('addCardForm'); From d105da5bc776446045a04bad83a0bb9d4a3fe50c Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Sat, 3 Oct 2015 15:59:13 -0600 Subject: [PATCH 02/27] Conformed to the 80-character line length limit. --- client/components/lists/listBody.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 9d6aab88c..8bc828fbe 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -40,28 +40,30 @@ BlazeComponent.extendComponent({ // and applying the appropriate users and labels to the card instead. const currentBoard = Boards.findOne(Session.get('currentBoard')); - // Find all @-mentioned usernames, collect a list of their IDs - // and strip their mention out of the title. + // Find all @-mentioned usernames, collect a list of their IDs and strip + // their mention out of the title. let foundUserIds = []; currentBoard.members.forEach(member => { const username = Users.findOne(member.userId).username; let nameNdx = title.indexOf('@' + username); if(nameNdx !== -1) { foundUserIds.push(member.userId); - title = title.substr(0, nameNdx) + title.substr(nameNdx + username.length + 1); + title = title.substr(0, nameNdx) + + title.substr(nameNdx + username.length + 1); } }); - // Find all #-mentioned labels (based on their colour or name), - // collect a list of their IDs, and strip their mention out of - // the title. + // Find all #-mentioned labels (based on their colour or name), collect a + // list of their IDs, and strip their mention out of the title. let foundLabelIds = []; currentBoard.labels.forEach(label => { - const labelName = (!label.name || label.name === "") ? label.color : label.name; + const labelName = (!label.name || label.name === "") + ? label.color : label.name; let labelNdx = title.indexOf('#' + labelName); if(labelNdx !== -1) { foundLabelIds.push(label._id); - title = title.substr(0, labelNdx) + title.substr(labelNdx + labelName.length + 1); + title = title.substr(0, labelNdx) + + title.substr(labelNdx + labelName.length + 1); } }); @@ -138,7 +140,7 @@ BlazeComponent.extendComponent({ }, pressKey(evt) { - // don't do anything if the drop down is showing + // Don't do anything if the drop down is showing if(dropDownIsOpened) { return; } From 8010ed8d6d15d8735aeec7a16b6e942c4bedd9dd Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Sat, 3 Oct 2015 16:38:43 -0600 Subject: [PATCH 03/27] Added package.json for meteor deployment --- package.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 000000000..8c9c7ff74 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "Wekan", + "description": "The open-source Trello-like kanban", + "repository": "https://github.com/FuzzyWuzzie/wekan", + "logo": "https://raw.githubusercontent.com/wekan/wekan/master/meta/icons/wekan-150.png", + "keywords": ["productivity", "tool", "team", "kanban"], + "website": "http://wekan.io", + "engines": { + "node": "0.10.40" + } +} \ No newline at end of file From 429686ef480a56039e0d1a6da0a8668755b2bfa5 Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Sat, 3 Oct 2015 16:53:45 -0600 Subject: [PATCH 04/27] Made eslinter happy. --- client/components/lists/listBody.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 8bc828fbe..a96e964ce 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -42,10 +42,10 @@ BlazeComponent.extendComponent({ // Find all @-mentioned usernames, collect a list of their IDs and strip // their mention out of the title. - let foundUserIds = []; - currentBoard.members.forEach(member => { + let foundUserIds = []; // eslint-disable-line prefer-const + currentBoard.members.forEach((member) => { const username = Users.findOne(member.userId).username; - let nameNdx = title.indexOf('@' + username); + const nameNdx = title.indexOf(`@${username}!`); if(nameNdx !== -1) { foundUserIds.push(member.userId); title = title.substr(0, nameNdx) @@ -55,11 +55,11 @@ BlazeComponent.extendComponent({ // Find all #-mentioned labels (based on their colour or name), collect a // list of their IDs, and strip their mention out of the title. - let foundLabelIds = []; - currentBoard.labels.forEach(label => { - const labelName = (!label.name || label.name === "") + let foundLabelIds = []; // eslint-disable-line prefer-const + currentBoard.labels.forEach((label) => { + const labelName = (!label.name || label.name === '') ? label.color : label.name; - let labelNdx = title.indexOf('#' + labelName); + const labelNdx = title.indexOf(`#${labelName}`); if(labelNdx !== -1) { foundLabelIds.push(label._id); title = title.substr(0, labelNdx) @@ -141,7 +141,7 @@ BlazeComponent.extendComponent({ pressKey(evt) { // Don't do anything if the drop down is showing - if(dropDownIsOpened) { + if(dropdownMenuIsOpened) { return; } @@ -181,7 +181,7 @@ BlazeComponent.extendComponent({ }, onCreated() { - dropDownIsOpened = false; + dropdownMenuIsOpened = false; }, onRendered() { @@ -212,7 +212,7 @@ BlazeComponent.extendComponent({ search(term, callback) { const currentBoard = Boards.findOne(Session.get('currentBoard')); callback($.map(currentBoard.labels, (label) => { - const labelName = (!label.name || label.name === "") ? label.color : label.name; + const labelName = (!label.name || label.name === '') ? label.color : label.name; return labelName.indexOf(term) === 0 ? labelName : null; })); }, @@ -229,11 +229,11 @@ BlazeComponent.extendComponent({ // customize hooks for dealing with the dropdowns $textarea.on({ 'textComplete:show'() { - dropDownIsOpened = true; + dropdownMenuIsOpened = true; }, 'textComplete:hide'() { Tracker.afterFlush(() => { - dropDownIsOpened = false; + dropdownMenuIsOpened = false; }); }, }); From e4c5d2cbe64f42f1fc2e49aea6a9a25bc0d686cb Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Sat, 3 Oct 2015 16:56:27 -0600 Subject: [PATCH 05/27] Fixed typo in template for quick-adding a user. --- client/components/lists/listBody.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index a96e964ce..61e269758 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -45,7 +45,7 @@ BlazeComponent.extendComponent({ let foundUserIds = []; // eslint-disable-line prefer-const currentBoard.members.forEach((member) => { const username = Users.findOne(member.userId).username; - const nameNdx = title.indexOf(`@${username}!`); + const nameNdx = title.indexOf(`@${username}`); if(nameNdx !== -1) { foundUserIds.push(member.userId); title = title.substr(0, nameNdx) From 77ca52d8c20211170a0f6d28e751768a4f9c3b8c Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Thu, 8 Oct 2015 12:22:03 -0600 Subject: [PATCH 06/27] Fixed issue with possible race condition, suggested by @mquandalle --- client/components/lists/listBody.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 61e269758..7c524b932 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -45,11 +45,9 @@ BlazeComponent.extendComponent({ let foundUserIds = []; // eslint-disable-line prefer-const currentBoard.members.forEach((member) => { const username = Users.findOne(member.userId).username; - const nameNdx = title.indexOf(`@${username}`); - if(nameNdx !== -1) { + if(title.indexOf(`@${username}`) !== -1) { foundUserIds.push(member.userId); - title = title.substr(0, nameNdx) - + title.substr(nameNdx + username.length + 1); + title = title.replace(`@${username}`, ''); } }); @@ -59,11 +57,9 @@ BlazeComponent.extendComponent({ currentBoard.labels.forEach((label) => { const labelName = (!label.name || label.name === '') ? label.color : label.name; - const labelNdx = title.indexOf(`#${labelName}`); - if(labelNdx !== -1) { + if(title.indexOf(`#${labelName}`) !== -1) { foundLabelIds.push(label._id); - title = title.substr(0, labelNdx) - + title.substr(labelNdx + labelName.length + 1); + title = title.replace(`#${labelName}`, ''); } }); From a212b1310cf5c722a397e2c50af9a7b289e77e5a Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Thu, 8 Oct 2015 12:22:55 -0600 Subject: [PATCH 07/27] Added space after if to conform to formatting --- client/components/lists/listBody.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 7c524b932..ce095ed6d 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -45,7 +45,7 @@ BlazeComponent.extendComponent({ let foundUserIds = []; // eslint-disable-line prefer-const currentBoard.members.forEach((member) => { const username = Users.findOne(member.userId).username; - if(title.indexOf(`@${username}`) !== -1) { + if (title.indexOf(`@${username}`) !== -1) { foundUserIds.push(member.userId); title = title.replace(`@${username}`, ''); } @@ -57,7 +57,7 @@ BlazeComponent.extendComponent({ currentBoard.labels.forEach((label) => { const labelName = (!label.name || label.name === '') ? label.color : label.name; - if(title.indexOf(`#${labelName}`) !== -1) { + if (title.indexOf(`#${labelName}`) !== -1) { foundLabelIds.push(label._id); title = title.replace(`#${labelName}`, ''); } @@ -137,7 +137,7 @@ BlazeComponent.extendComponent({ pressKey(evt) { // Don't do anything if the drop down is showing - if(dropdownMenuIsOpened) { + if (dropdownMenuIsOpened) { return; } From c2cb17c5dff153fb5b11572036f8acc3155ea7e3 Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Thu, 8 Oct 2015 12:25:58 -0600 Subject: [PATCH 08/27] Now cards with *only* metadata aren't created empty --- client/components/lists/listBody.js | 54 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index ce095ed6d..6c191a719 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -34,35 +34,35 @@ BlazeComponent.extendComponent({ } else if (position === 'bottom') { sortIndex = Utils.calculateIndex(lastCardDom, null).base; } + + // Parse for @user and #label mentions, stripping them from the title + // and applying the appropriate users and labels to the card instead. + const currentBoard = Boards.findOne(Session.get('currentBoard')); + + // Find all @-mentioned usernames, collect a list of their IDs and strip + // their mention out of the title. + let foundUserIds = []; // eslint-disable-line prefer-const + currentBoard.members.forEach((member) => { + const username = Users.findOne(member.userId).username; + if (title.indexOf(`@${username}`) !== -1) { + foundUserIds.push(member.userId); + title = title.replace(`@${username}`, ''); + } + }); + + // Find all #-mentioned labels (based on their colour or name), collect a + // list of their IDs, and strip their mention out of the title. + let foundLabelIds = []; // eslint-disable-line prefer-const + currentBoard.labels.forEach((label) => { + const labelName = (!label.name || label.name === '') + ? label.color : label.name; + if (title.indexOf(`#${labelName}`) !== -1) { + foundLabelIds.push(label._id); + title = title.replace(`#${labelName}`, ''); + } + }); if ($.trim(title)) { - // Parse for @user and #label mentions, stripping them from the title - // and applying the appropriate users and labels to the card instead. - const currentBoard = Boards.findOne(Session.get('currentBoard')); - - // Find all @-mentioned usernames, collect a list of their IDs and strip - // their mention out of the title. - let foundUserIds = []; // eslint-disable-line prefer-const - currentBoard.members.forEach((member) => { - const username = Users.findOne(member.userId).username; - if (title.indexOf(`@${username}`) !== -1) { - foundUserIds.push(member.userId); - title = title.replace(`@${username}`, ''); - } - }); - - // Find all #-mentioned labels (based on their colour or name), collect a - // list of their IDs, and strip their mention out of the title. - let foundLabelIds = []; // eslint-disable-line prefer-const - currentBoard.labels.forEach((label) => { - const labelName = (!label.name || label.name === '') - ? label.color : label.name; - if (title.indexOf(`#${labelName}`) !== -1) { - foundLabelIds.push(label._id); - title = title.replace(`#${labelName}`, ''); - } - }); - const _id = Cards.insert({ title, listId: this.data()._id, From f5be121cf368415652ec3bb14aeaccecab6f663e Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Thu, 8 Oct 2015 12:32:31 -0600 Subject: [PATCH 09/27] Pressing escape while autocomplete is open no longer closes the minicard --- client/components/lists/listBody.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 6c191a719..e311ac76a 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -233,5 +233,10 @@ BlazeComponent.extendComponent({ }); }, }); + + EscapeActions.register('textcomplete', + () => {}, + () => dropdownMenuIsOpened + ); }, }).register('addCardForm'); From fde2a39ee3dbbdfd5a6b648dbe17541343ac3b2a Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Thu, 8 Oct 2015 13:10:46 -0600 Subject: [PATCH 10/27] Added coloured label badges in autocomplete list --- client/components/forms/forms.styl | 7 +++++++ client/components/lists/listBody.js | 19 ++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/client/components/forms/forms.styl b/client/components/forms/forms.styl index 83d253701..2d92aca95 100644 --- a/client/components/forms/forms.styl +++ b/client/components/forms/forms.styl @@ -617,6 +617,13 @@ button margin-right: 5px vertical-align: middle + .minicard-label + width: 11px + height: @width + border-radius: 2px + margin-right: 3px + display: inline-block + &.active background: #005377 diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index e311ac76a..e248f2030 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -34,7 +34,7 @@ BlazeComponent.extendComponent({ } else if (position === 'bottom') { sortIndex = Utils.calculateIndex(lastCardDom, null).base; } - + // Parse for @user and #label mentions, stripping them from the title // and applying the appropriate users and labels to the card instead. const currentBoard = Boards.findOne(Session.get('currentBoard')); @@ -182,12 +182,12 @@ BlazeComponent.extendComponent({ onRendered() { const $textarea = this.$('textarea'); + const currentBoard = Boards.findOne(Session.get('currentBoard')); $textarea.textcomplete([ // User mentions { match: /\B@(\w*)$/, search(term, callback) { - const currentBoard = Boards.findOne(Session.get('currentBoard')); callback($.map(currentBoard.members, (member) => { const username = Users.findOne(member.userId).username; return username.indexOf(term) === 0 ? username : null; @@ -206,14 +206,23 @@ BlazeComponent.extendComponent({ { match: /\B#(\w*)$/, search(term, callback) { - const currentBoard = Boards.findOne(Session.get('currentBoard')); callback($.map(currentBoard.labels, (label) => { - const labelName = (!label.name || label.name === '') ? label.color : label.name; + const labelName = (!label.name || label.name === '') + ? label.color + : label.name; return labelName.indexOf(term) === 0 ? labelName : null; })); }, template(value) { - return value; + // add a "colour badge" in front of the label name + // but first, get the colour's name from its value + const colorName = currentBoard.labels.find((label) => { + return value === label.name || value === label.color; + }).color; + return (colorName && colorName !== '') + ? `
${value}` + : value; }, replace(label) { return `#${label} `; From 3507c6565bb16b5f45c6f269f7376902f8b1ff37 Mon Sep 17 00:00:00 2001 From: Kenton Hamaluik Date: Sat, 10 Oct 2015 23:08:50 -0600 Subject: [PATCH 11/27] Made colours light grey in the labels dropdown --- client/components/lists/listBody.js | 7 ++++++- package.json | 11 ----------- 2 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 package.json diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index e248f2030..a60ffe257 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -214,14 +214,19 @@ BlazeComponent.extendComponent({ })); }, template(value) { + // XXX the following is duplicated from editor.js and should be + // abstracted to keep things DRY // add a "colour badge" in front of the label name // but first, get the colour's name from its value const colorName = currentBoard.labels.find((label) => { return value === label.name || value === label.color; }).color; + const valueSpan = (colorName === value) + ? `${value}` + : value; return (colorName && colorName !== '') ? `
${value}` + title="${value}"> ${valueSpan}` : value; }, replace(label) { diff --git a/package.json b/package.json deleted file mode 100644 index 8c9c7ff74..000000000 --- a/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "Wekan", - "description": "The open-source Trello-like kanban", - "repository": "https://github.com/FuzzyWuzzie/wekan", - "logo": "https://raw.githubusercontent.com/wekan/wekan/master/meta/icons/wekan-150.png", - "keywords": ["productivity", "tool", "team", "kanban"], - "website": "http://wekan.io", - "engines": { - "node": "0.10.40" - } -} \ No newline at end of file From 31b60d82fcae64a844805a2a76a0af25fb9c16c2 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Fri, 23 Oct 2015 16:56:55 +0200 Subject: [PATCH 12/27] Upgrade Meteor to 1.2.1-rc4 This version includes a more complete selection of ES2015 polyfills that I started used across the code base, for instance by replacing `$.trim(str)` by `str.trim()`. --- .meteor/release | 2 +- .meteor/versions | 36 +++++++++++----------- client/components/activities/activities.js | 4 +-- client/components/activities/comments.js | 12 +++++--- client/components/boards/boardBody.js | 6 ++-- client/components/cards/cardDetails.js | 6 ++-- client/components/lists/listBody.js | 6 ++-- client/components/lists/listHeader.js | 6 ++-- client/components/main/editor.js | 8 ++--- client/components/users/userHeader.js | 6 ++-- models/boards.js | 4 +-- sandstorm.js | 2 +- 12 files changed, 50 insertions(+), 48 deletions(-) diff --git a/.meteor/release b/.meteor/release index 5684262a8..71252f554 100644 --- a/.meteor/release +++ b/.meteor/release @@ -1 +1 @@ -METEOR@1.2.0.2 +METEOR@1.2.1-rc.4 diff --git a/.meteor/versions b/.meteor/versions index 840f09f94..b661658fa 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -1,12 +1,12 @@ 3stack:presence@1.0.3 -accounts-base@1.2.1 -accounts-password@1.1.3 +accounts-base@1.2.2-rc.0 +accounts-password@1.1.4-rc.0 aldeed:collection2@2.5.0 aldeed:simple-schema@1.3.3 alethes:pages@1.8.4 arillo:flow-router-helpers@0.4.5 audit-argument-checks@1.0.4 -autoupdate@1.2.3 +autoupdate@1.2.4-rc.0 babel-compiler@5.8.24_1 babel-runtime@0.1.4 base64@1.0.4 @@ -34,22 +34,22 @@ cfs:storage-adapter@0.2.3 cfs:tempstore@0.1.5 cfs:upload-http@0.0.20 cfs:worker@0.1.4 -check@1.0.6 -coffeescript@1.0.10 +check@1.1.0-rc.0 +coffeescript@1.0.11-rc.0 cosmos:browserify@0.5.1 dburles:collection-helpers@1.0.3 ddp@1.2.2 ddp-client@1.2.1 -ddp-common@1.2.1 +ddp-common@1.2.2-rc.0 ddp-rate-limiter@1.0.0 -ddp-server@1.2.1 +ddp-server@1.2.2-rc.0 deps@1.0.9 diff-sequence@1.0.1 -ecmascript@0.1.5 -ecmascript-collections@0.1.6 +ecmascript@0.1.6-rc.0 +ecmascript-runtime@0.2.6-rc.0 ejson@1.0.7 -email@1.0.7 -es5-shim@4.1.13 +email@1.0.8-rc.0 +es5-shim@4.1.14-rc.0 fastclick@1.0.7 fortawesome:fontawesome@4.4.0 geojson-utils@1.0.4 @@ -71,7 +71,7 @@ localstorage@1.0.5 logging@1.0.8 matb33:collection-hooks@0.8.1 matteodem:easy-search@1.6.4 -meteor@1.1.9 +meteor@1.1.10-rc.0 meteor-base@1.0.1 meteor-platform@1.2.3 meteorhacks:aggregate@1.3.0 @@ -82,7 +82,7 @@ meteorspark:util@0.2.0 minifiers@1.1.7 minimongo@1.0.10 mobile-status-bar@1.0.6 -mongo@1.1.2 +mongo@1.1.3-rc.0 mongo-id@1.0.1 mongo-livedata@1.0.9 mousetrap:mousetrap@1.4.6_1 @@ -107,10 +107,10 @@ peerlibrary:blaze-components@0.14.0 peerlibrary:computed-field@0.3.0 peerlibrary:reactive-field@0.1.0 perak:markdown@1.0.5 -promise@0.5.0 +promise@0.5.1-rc.0 raix:eventemitter@0.1.3 raix:handlebar-helpers@0.2.5 -random@1.0.4 +random@1.0.5-rc.0 rate-limit@1.0.0 reactive-dict@1.1.2 reactive-var@1.0.6 @@ -126,10 +126,10 @@ softwarerero:accounts-t9n@1.1.4 spacebars@1.0.7 spacebars-compiler@1.0.7 srp@1.0.4 -standard-minifiers@1.0.1 +standard-minifiers@1.0.2-rc.0 tap:i18n@1.7.0 templates:tabs@2.2.0 -templating@1.1.4 +templating@1.1.5-rc.0 templating-tools@1.0.0 tracker@1.0.9 ui@1.0.8 @@ -139,6 +139,6 @@ useraccounts:core@1.12.4 useraccounts:flow-routing@1.12.4 useraccounts:unstyled@1.12.4 verron:autosize@3.0.8 -webapp@1.2.2 +webapp@1.2.3-rc.0 webapp-hashing@1.0.5 zimme:active-route@2.3.2 diff --git a/client/components/activities/activities.js b/client/components/activities/activities.js index a491c2e85..64e9865d7 100644 --- a/client/components/activities/activities.js +++ b/client/components/activities/activities.js @@ -101,9 +101,9 @@ BlazeComponent.extendComponent({ }, 'submit .js-edit-comment'(evt) { evt.preventDefault(); - const commentText = this.currentComponent().getValue(); + const commentText = this.currentComponent().getValue().trim(); const commentId = Template.parentData().commentId; - if ($.trim(commentText)) { + if (commentText) { CardComments.update(commentId, { $set: { text: commentText, diff --git a/client/components/activities/comments.js b/client/components/activities/comments.js index 08401caae..f9bbcff88 100644 --- a/client/components/activities/comments.js +++ b/client/components/activities/comments.js @@ -23,12 +23,13 @@ BlazeComponent.extendComponent({ commentFormIsOpen.set(true); }, 'submit .js-new-comment-form'(evt) { - const input = this.getInput(); - if ($.trim(input.val())) { + const input = this.getInput() + const text = input.val().trim(); + if (text) { CardComments.insert({ + text, boardId: this.currentData().boardId, cardId: this.currentData()._id, - text: input.val(), }); resetCommentInput(input); Tracker.flush(); @@ -72,8 +73,9 @@ EscapeActions.register('inlinedForm', docId: Session.get('currentCard'), }; const commentInput = $('.js-new-comment-input'); - if ($.trim(commentInput.val())) { - UnsavedEdits.set(draftKey, commentInput.val()); + const draft = commentInput.val().trim(); + if (draft) { + UnsavedEdits.set(draftKey, draft); } else { UnsavedEdits.reset(draftKey); } diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index 517e53bab..e7cfc791f 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -186,10 +186,10 @@ BlazeComponent.extendComponent({ return [{ submit(evt) { evt.preventDefault(); - const title = this.find('.list-name-input'); - if ($.trim(title.value)) { + const title = this.find('.list-name-input').value.trim(); + if (title) { Lists.insert({ - title: title.value, + title, boardId: Session.get('currentBoard'), sort: $('.list').length, }); diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index 2d2679ec2..fa818c5aa 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -75,8 +75,8 @@ BlazeComponent.extendComponent({ }, 'submit .js-card-details-title'(evt) { evt.preventDefault(); - const title = this.currentComponent().getValue(); - if ($.trim(title)) { + const title = this.currentComponent().getValue().trim(); + if (title) { this.data().setTitle(title); } }, @@ -106,7 +106,7 @@ BlazeComponent.extendComponent({ close(isReset = false) { if (this.isOpen.get() && !isReset) { - const draft = $.trim(this.getValue()); + const draft = this.getValue().trim(); if (draft !== Cards.findOne(Session.get('currentCard')).description) { UnsavedEdits.set(this._getUnsavedEditKey(), this.getValue()); } diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 88b317887..25aeffcc0 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -12,7 +12,7 @@ BlazeComponent.extendComponent({ options.position = options.position || 'top'; const forms = this.childrenComponents('inlinedForm'); - let form = _.find(forms, (component) => { + let form = forms.find((component) => { return component.data().position === options.position; }); if (!form && forms.length > 0) { @@ -26,7 +26,7 @@ BlazeComponent.extendComponent({ const firstCardDom = this.find('.js-minicard:first'); const lastCardDom = this.find('.js-minicard:last'); const textarea = $(evt.currentTarget).find('textarea'); - const title = textarea.val(); + const title = textarea.val().trim(); const position = this.currentData().position; let sortIndex; if (position === 'top') { @@ -35,7 +35,7 @@ BlazeComponent.extendComponent({ sortIndex = Utils.calculateIndex(lastCardDom, null).base; } - if ($.trim(title)) { + if (title) { const _id = Cards.insert({ title, listId: this.data()._id, diff --git a/client/components/lists/listHeader.js b/client/components/lists/listHeader.js index b5df2c81f..ab547f024 100644 --- a/client/components/lists/listHeader.js +++ b/client/components/lists/listHeader.js @@ -6,9 +6,9 @@ BlazeComponent.extendComponent({ editTitle(evt) { evt.preventDefault(); const newTitle = this.childrenComponents('inlinedForm')[0].getValue(); - const list = this.currentData(); - if ($.trim(newTitle)) { - list.rename(newTitle); + const list = this.currentData().trim(); + if (newTitle) { + list.rename(newTitle.trim()); } }, diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 168c85d0a..82fce641c 100644 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -8,8 +8,8 @@ Template.editor.onRendered(() => { { match: /\B:([\-+\w]*)$/, search(term, callback) { - callback($.map(Emoji.values, (emoji) => { - return emoji.indexOf(term) === 0 ? emoji : null; + callback(Emoji.values.map((emoji) => { + return emoji.includes(term) ? emoji : null; })); }, template(value) { @@ -28,9 +28,9 @@ Template.editor.onRendered(() => { match: /\B@(\w*)$/, search(term, callback) { const currentBoard = Boards.findOne(Session.get('currentBoard')); - callback($.map(currentBoard.members, (member) => { + callback(currentBoard.members.map((member) => { const username = Users.findOne(member.userId).username; - return username.indexOf(term) === 0 ? username : null; + return username.includes(term) ? username : null; })); }, template(value) { diff --git a/client/components/users/userHeader.js b/client/components/users/userHeader.js index a73827697..7d35c1b9c 100644 --- a/client/components/users/userHeader.js +++ b/client/components/users/userHeader.js @@ -18,9 +18,9 @@ Template.memberMenuPopup.events({ Template.editProfilePopup.events({ submit(evt, tpl) { evt.preventDefault(); - const fullname = $.trim(tpl.find('.js-profile-fullname').value); - const username = $.trim(tpl.find('.js-profile-username').value); - const initials = $.trim(tpl.find('.js-profile-initials').value); + const fullname = tpl.find('.js-profile-fullname').value.trim(); + const username = tpl.find('.js-profile-username').value.trim(); + const initials = tpl.find('.js-profile-initials').value.trim(); Users.update(Meteor.userId(), {$set: { 'profile.fullname': fullname, 'profile.initials': initials, diff --git a/models/boards.js b/models/boards.js index 1d365a953..98d6ec779 100644 --- a/models/boards.js +++ b/models/boards.js @@ -97,11 +97,11 @@ Boards.helpers({ }, labelIndex(labelId) { - return _.indexOf(_.pluck(this.labels, '_id'), labelId); + return _.pluck(this.labels, '_id').indexOf(labelId); }, memberIndex(memberId) { - return _.indexOf(_.pluck(this.members, 'userId'), memberId); + return _.pluck(this.members, 'userId').indexOf(memberId); }, absoluteUrl() { diff --git a/sandstorm.js b/sandstorm.js index b30064544..51f8ba34e 100644 --- a/sandstorm.js +++ b/sandstorm.js @@ -27,7 +27,7 @@ if (isSandstorm && Meteor.isServer) { const permissionDoc = { userId, isActive, isAdmin }; const boardMembers = Boards.findOne(sandstormBoard._id).members; - const memberIndex = _.indexOf(_.pluck(boardMembers, 'userId'), userId); + const memberIndex = _.pluck(boardMembers, 'userId').indexOf(userId); let modifier; if (memberIndex > -1) From 3ad672a20b8e216684bbcb932b792d67548c2484 Mon Sep 17 00:00:00 2001 From: Dominik Ferber Date: Tue, 27 Oct 2015 17:38:04 +0100 Subject: [PATCH 13/27] Add missing semicolon --- client/components/activities/comments.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/components/activities/comments.js b/client/components/activities/comments.js index f9bbcff88..18bf9ef06 100644 --- a/client/components/activities/comments.js +++ b/client/components/activities/comments.js @@ -23,7 +23,7 @@ BlazeComponent.extendComponent({ commentFormIsOpen.set(true); }, 'submit .js-new-comment-form'(evt) { - const input = this.getInput() + const input = this.getInput(); const text = input.val().trim(); if (text) { CardComments.insert({ From 3956a6ec205b753c55a2d11f8f780acb996bb461 Mon Sep 17 00:00:00 2001 From: Dominik Ferber Date: Tue, 27 Oct 2015 17:40:38 +0100 Subject: [PATCH 14/27] Add eslint-plugin-meteor Add rules for eslint-plugin-meteor. Use local version of eslint and eslint-plugin-meteor, instead of relying on global versions. Ensures consistent versions of eslint and eslint-plugin-meteor for all developers. --- .eslintrc | 73 ++++++++++++++++++++----------------------- .gitignore | 1 + models/attachments.js | 2 +- models/users.js | 32 +++++++++++-------- package.json | 24 ++++++++++++++ sandstorm.js | 2 +- 6 files changed, 80 insertions(+), 54 deletions(-) create mode 100644 package.json diff --git a/.eslintrc b/.eslintrc index c952e5e08..14a9e6673 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,7 +1,14 @@ ecmaFeatures: experimentalObjectRestSpread: true +plugins: + - meteor + +parser: babel-eslint + rules: + strict: 0 + no-undef: 2 accessor-pairs: 2 comma-dangle: [2, 'always-multiline'] consistent-return: 2 @@ -43,36 +50,35 @@ rules: prefer-spread: 2 prefer-template: 2 -globals: - # Meteor globals - Meteor: false - DDP: false - Mongo: false - Session: false - Accounts: false - Template: false - Blaze: false - UI: false - Match: false - check: false - Tracker: false - Deps: false - ReactiveVar: false - EJSON: false - HTTP: false - Email: false - Assets: false - Handlebars: false - Package: false - App: false - Npm: false - Tinytest: false - Random: false - HTML: false + # eslint-plugin-meteor + meteor/globals: 2 + meteor/no-zero-timeout: 2 + meteor/no-session: 0 + meteor/pubsub: 2 + meteor/core: 2 + meteor/methods: 2 + meteor/check: 2 + meteor/connections: 2 + meteor/collections: 2 + meteor/session: [2, 'no-equal'] +settings: + meteor: + + # Our collections + collections: + - AccountsTemplates + - Activities + - Attachments + - Boards + - CardComments + - Cards + - Lists + - UnsavedEditCollection + - Users + +globals: # Exported by packages we use - '$': false - _: false autosize: false Avatar: true Avatars: true @@ -97,17 +103,6 @@ globals: T9n: false TAPi18n: false - # Our collections - AccountsTemplates: true - Activities: true - Attachments: true - Boards: true - CardComments: true - Cards: true - Lists: true - UnsavedEditCollection: true - Users: true - # Our objects CSSEvents: true EscapeActions: true diff --git a/.gitignore b/.gitignore index 986a8b467..7c40fcd4e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ .tx/ *.sublime-workspace tmp/ +node_modules/ diff --git a/models/attachments.js b/models/attachments.js index 8ef0fef02..01e467ff2 100644 --- a/models/attachments.js +++ b/models/attachments.js @@ -1,4 +1,4 @@ -Attachments = new FS.Collection('attachments', { +Attachments = new FS.Collection('attachments', { // eslint-disable-line meteor/collections stores: [ // XXX Add a new store for cover thumbnails so we don't load big images in diff --git a/models/users.js b/models/users.js index b35104ecf..1e69564de 100644 --- a/models/users.js +++ b/models/users.js @@ -1,4 +1,4 @@ -Users = Meteor.users; +Users = Meteor.users; // eslint-disable-line meteor/collections // Search a user in the complete server database by its name or username. This // is used for instance to add a new user to a board. @@ -8,6 +8,24 @@ Users.initEasySearch(searchInFields, { returnFields: [...searchInFields, 'profile.avatarUrl'], }); +if (Meteor.isClient) { + Users.helpers({ + isBoardMember() { + const board = Boards.findOne(Session.get('currentBoard')); + return board && + _.contains(_.pluck(board.members, 'userId'), this._id) && + _.where(board.members, {userId: this._id})[0].isActive; + }, + + isBoardAdmin() { + const board = Boards.findOne(Session.get('currentBoard')); + return board && + this.isBoardMember(board) && + _.where(board.members, {userId: this._id})[0].isAdmin; + }, + }); +} + Users.helpers({ boards() { return Boards.find({ userId: this._id }); @@ -23,18 +41,6 @@ Users.helpers({ return _.contains(starredBoards, boardId); }, - isBoardMember() { - const board = Boards.findOne(Session.get('currentBoard')); - return board && _.contains(_.pluck(board.members, 'userId'), this._id) && - _.where(board.members, {userId: this._id})[0].isActive; - }, - - isBoardAdmin() { - const board = Boards.findOne(Session.get('currentBoard')); - return board && this.isBoardMember(board) && - _.where(board.members, {userId: this._id})[0].isAdmin; - }, - getAvatarUrl() { // Although we put the avatar picture URL in the `profile` object, we need // to support Sandstorm which put in the `picture` attribute by default. diff --git a/package.json b/package.json new file mode 100644 index 000000000..821f46196 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "wekan", + "version": "1.0.0", + "description": "The open-source Trello-like kanban", + "private": true, + "scripts": { + "lint": "eslint .", + "test": "npm run lint" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wekan/wekan.git" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/wekan/wekan/issues" + }, + "homepage": "https://github.com/wekan/wekan#readme", + "devDependencies": { + "babel-eslint": "4.1.3", + "eslint": "1.7.3", + "eslint-plugin-meteor": "1.5.0" + } +} diff --git a/sandstorm.js b/sandstorm.js index 51f8ba34e..65f248666 100644 --- a/sandstorm.js +++ b/sandstorm.js @@ -109,7 +109,7 @@ if (isSandstorm && Meteor.isClient) { // sandstorm client to return relative paths instead of absolutes. const _absoluteUrl = Meteor.absoluteUrl; const _defaultOptions = Meteor.absoluteUrl.defaultOptions; - Meteor.absoluteUrl = (path, options) => { + Meteor.absoluteUrl = (path, options) => { // eslint-disable-line meteor/core const url = _absoluteUrl(path, options); return url.replace(/^https?:\/\/127\.0\.0\.1:[0-9]{2,5}/, ''); }; From 6e87d50941821dec7c8be5d77211fa5c5836733d Mon Sep 17 00:00:00 2001 From: Dominik Ferber Date: Tue, 27 Oct 2015 18:01:44 +0100 Subject: [PATCH 15/27] Adapt CI --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 55f8ed48d..a87246311 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: node_js node_js: - "0.10.40" install: - - "npm install -g eslint" - - "npm install -g eslint-plugin-meteor" + - "npm install" script: - - "eslint ./" + - "npm test" From c9130324f6c337d0393bf37fe72b67b47ece8322 Mon Sep 17 00:00:00 2001 From: Dominik Ferber Date: Tue, 27 Oct 2015 18:34:09 +0100 Subject: [PATCH 16/27] Enable more ESLint-plugin-Meteor rules --- .eslintrc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.eslintrc b/.eslintrc index 14a9e6673..a8017e664 100644 --- a/.eslintrc +++ b/.eslintrc @@ -51,17 +51,21 @@ rules: prefer-template: 2 # eslint-plugin-meteor + ## Meteor API meteor/globals: 2 - meteor/no-zero-timeout: 2 - meteor/no-session: 0 - meteor/pubsub: 2 meteor/core: 2 + meteor/pubsub: 2 meteor/methods: 2 meteor/check: 2 meteor/connections: 2 meteor/collections: 2 meteor/session: [2, 'no-equal'] + ## Best practices + meteor/no-session: 0 + meteor/no-zero-timeout: 2 + meteor/no-blaze-lifecycle-assignment: 2 + settings: meteor: From dd101543920148a48b74541ebbbaca97f6bc6d11 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Thu, 29 Oct 2015 01:17:23 +0100 Subject: [PATCH 17/27] Clean the linting script This also has the side effect of correcting the `npm test` Unix exit status number -- which was previously always 0. We also edit the website URL. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 821f46196..cf998e9ce 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "private": true, "scripts": { "lint": "eslint .", - "test": "npm run lint" + "test": "npm run --silent lint" }, "repository": { "type": "git", @@ -15,7 +15,7 @@ "bugs": { "url": "https://github.com/wekan/wekan/issues" }, - "homepage": "https://github.com/wekan/wekan#readme", + "homepage": "http://wekan.io", "devDependencies": { "babel-eslint": "4.1.3", "eslint": "1.7.3", From 68521fc1c3d52b6b7b6d39931618c90338b5b3aa Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Thu, 29 Oct 2015 15:31:33 +0100 Subject: [PATCH 18/27] Update packages and update Meteor to 1.2.1 Blaze-components had yet another methods rename. --- .meteor/packages | 3 -- .meteor/release | 2 +- .meteor/versions | 57 +++++++++++++-------------- client/components/boards/boardBody.js | 4 +- 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/.meteor/packages b/.meteor/packages index 39242f484..5b666ed10 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -2,9 +2,6 @@ # # 'meteor add' and 'meteor remove' will edit this file for you, # but you can also edit it by hand. -# -# XXX Should we replace tmeasday:presence by 3stack:presence? Or maybe the -# packages will merge in the future? meteor-base diff --git a/.meteor/release b/.meteor/release index 71252f554..3a05e0a2f 100644 --- a/.meteor/release +++ b/.meteor/release @@ -1 +1 @@ -METEOR@1.2.1-rc.4 +METEOR@1.2.1 diff --git a/.meteor/versions b/.meteor/versions index b661658fa..bd2390c46 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -1,12 +1,12 @@ 3stack:presence@1.0.3 -accounts-base@1.2.2-rc.0 -accounts-password@1.1.4-rc.0 +accounts-base@1.2.2 +accounts-password@1.1.4 aldeed:collection2@2.5.0 aldeed:simple-schema@1.3.3 alethes:pages@1.8.4 arillo:flow-router-helpers@0.4.5 audit-argument-checks@1.0.4 -autoupdate@1.2.4-rc.0 +autoupdate@1.2.4 babel-compiler@5.8.24_1 babel-runtime@0.1.4 base64@1.0.4 @@ -34,22 +34,22 @@ cfs:storage-adapter@0.2.3 cfs:tempstore@0.1.5 cfs:upload-http@0.0.20 cfs:worker@0.1.4 -check@1.1.0-rc.0 -coffeescript@1.0.11-rc.0 -cosmos:browserify@0.5.1 -dburles:collection-helpers@1.0.3 +check@1.1.0 +coffeescript@1.0.11 +cosmos:browserify@0.8.1 +dburles:collection-helpers@1.0.4 ddp@1.2.2 ddp-client@1.2.1 -ddp-common@1.2.2-rc.0 +ddp-common@1.2.2 ddp-rate-limiter@1.0.0 -ddp-server@1.2.2-rc.0 +ddp-server@1.2.2 deps@1.0.9 diff-sequence@1.0.1 -ecmascript@0.1.6-rc.0 -ecmascript-runtime@0.2.6-rc.0 +ecmascript@0.1.6 +ecmascript-runtime@0.2.6 ejson@1.0.7 -email@1.0.8-rc.0 -es5-shim@4.1.14-rc.0 +email@1.0.8 +es5-shim@4.1.14 fastclick@1.0.7 fortawesome:fontawesome@4.4.0 geojson-utils@1.0.4 @@ -58,20 +58,19 @@ html-tools@1.0.5 htmljs@1.0.5 http@1.1.1 id-map@1.0.4 -idmontie:migrations@1.0.0 +idmontie:migrations@1.0.1 jquery@1.11.4 -jsx@0.1.6 kadira:blaze-layout@2.2.0 -kadira:dochead@1.1.0 +kadira:dochead@1.3.2 kadira:flow-router@2.7.0 -kenton:accounts-sandstorm@0.1.6 +kenton:accounts-sandstorm@0.1.7 launch-screen@1.0.4 livedata@1.0.15 localstorage@1.0.5 logging@1.0.8 matb33:collection-hooks@0.8.1 matteodem:easy-search@1.6.4 -meteor@1.1.10-rc.0 +meteor@1.1.10 meteor-base@1.0.1 meteor-platform@1.2.3 meteorhacks:aggregate@1.3.0 @@ -82,14 +81,14 @@ meteorspark:util@0.2.0 minifiers@1.1.7 minimongo@1.0.10 mobile-status-bar@1.0.6 -mongo@1.1.3-rc.0 +mongo@1.1.3 mongo-id@1.0.1 mongo-livedata@1.0.9 mousetrap:mousetrap@1.4.6_1 mquandalle:autofocus@1.0.0 mquandalle:collection-mutations@0.1.0 -mquandalle:jade@0.4.4 -mquandalle:jade-compiler@0.4.4 +mquandalle:jade@0.4.5 +mquandalle:jade-compiler@0.4.5 mquandalle:jquery-textcomplete@0.8.0_1 mquandalle:jquery-ui-drag-drop-sort@0.1.0 mquandalle:moment@1.0.0 @@ -102,17 +101,17 @@ observe-sequence@1.0.7 ongoworks:speakingurl@1.1.0 ordered-dict@1.0.4 peerlibrary:assert@0.2.5 -peerlibrary:base-component@0.13.0 -peerlibrary:blaze-components@0.14.0 +peerlibrary:base-component@0.14.0 +peerlibrary:blaze-components@0.15.1 peerlibrary:computed-field@0.3.0 peerlibrary:reactive-field@0.1.0 perak:markdown@1.0.5 -promise@0.5.1-rc.0 +promise@0.5.1 raix:eventemitter@0.1.3 raix:handlebar-helpers@0.2.5 -random@1.0.5-rc.0 +random@1.0.5 rate-limit@1.0.0 -reactive-dict@1.1.2 +reactive-dict@1.1.3 reactive-var@1.0.6 reload@1.1.4 retry@1.0.4 @@ -126,10 +125,10 @@ softwarerero:accounts-t9n@1.1.4 spacebars@1.0.7 spacebars-compiler@1.0.7 srp@1.0.4 -standard-minifiers@1.0.2-rc.0 +standard-minifiers@1.0.2 tap:i18n@1.7.0 templates:tabs@2.2.0 -templating@1.1.5-rc.0 +templating@1.1.5 templating-tools@1.0.0 tracker@1.0.9 ui@1.0.8 @@ -139,6 +138,6 @@ useraccounts:core@1.12.4 useraccounts:flow-routing@1.12.4 useraccounts:unstyled@1.12.4 verron:autosize@3.0.8 -webapp@1.2.3-rc.0 +webapp@1.2.3 webapp-hashing@1.0.5 zimme:active-route@2.3.2 diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index e7cfc791f..28257d001 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -34,7 +34,7 @@ BlazeComponent.extendComponent({ }, openNewListForm() { - this.childrenComponents('addListForm')[0].open(); + this.childComponents('addListForm')[0].open(); }, // XXX Flow components allow us to avoid creating these two setter methods by @@ -179,7 +179,7 @@ BlazeComponent.extendComponent({ // Proxy open() { - this.childrenComponents('inlinedForm')[0].open(); + this.childComponents('inlinedForm')[0].open(); }, events() { From e92f67f1910a0a3bc40b85436a89e7d55a04a615 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Thu, 29 Oct 2015 18:47:39 +0100 Subject: [PATCH 19/27] Update eslint-plugin-meteor to 1.7.0 This fixes https://github.com/dferber90/eslint-plugin-meteor/issues/49 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cf998e9ce..77a9fb743 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,6 @@ "devDependencies": { "babel-eslint": "4.1.3", "eslint": "1.7.3", - "eslint-plugin-meteor": "1.5.0" + "eslint-plugin-meteor": "1.7.0" } } From dd3cdf3945b26c70b00b5c9c1dd13c74eaed2f8b Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Thu, 29 Oct 2015 23:08:27 +0100 Subject: [PATCH 20/27] Fix some bugs introduced in aa974aa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Yes Wekan need some tests. Yes I need to stop refactoring my code when I’m halp-sleeping in my bed at 4am. --- client/components/boards/boardBody.js | 6 ++++-- client/components/lists/listHeader.js | 4 ++-- client/components/users/userHeader.js | 2 +- client/lib/modal.js | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index 28257d001..5c1c974f0 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -186,7 +186,8 @@ BlazeComponent.extendComponent({ return [{ submit(evt) { evt.preventDefault(); - const title = this.find('.list-name-input').value.trim(); + const titleInput = this.find('.list-name-input'); + const title = titleInput.value.trim(); if (title) { Lists.insert({ title, @@ -194,7 +195,8 @@ BlazeComponent.extendComponent({ sort: $('.list').length, }); - title.value = ''; + titleInput.value = ''; + titleInput.focus(); } }, }]; diff --git a/client/components/lists/listHeader.js b/client/components/lists/listHeader.js index ab547f024..dbf9fced0 100644 --- a/client/components/lists/listHeader.js +++ b/client/components/lists/listHeader.js @@ -5,8 +5,8 @@ BlazeComponent.extendComponent({ editTitle(evt) { evt.preventDefault(); - const newTitle = this.childrenComponents('inlinedForm')[0].getValue(); - const list = this.currentData().trim(); + const newTitle = this.childrenComponents('inlinedForm')[0].getValue().trim(); + const list = this.currentData(); if (newTitle) { list.rename(newTitle.trim()); } diff --git a/client/components/users/userHeader.js b/client/components/users/userHeader.js index 7d35c1b9c..a478da0c2 100644 --- a/client/components/users/userHeader.js +++ b/client/components/users/userHeader.js @@ -41,7 +41,7 @@ Template.changePasswordPopup.onRendered(function() { Template.changeLanguagePopup.helpers({ languages() { - return TAPi18n.getLanguages().map((lang, tag) => { + return _.map(TAPi18n.getLanguages(), (lang, tag) => { const name = lang.name; return { tag, name }; }); diff --git a/client/lib/modal.js b/client/lib/modal.js index 7b7516e03..e6301cb5b 100644 --- a/client/lib/modal.js +++ b/client/lib/modal.js @@ -21,7 +21,7 @@ window.Modal = new class { } } - open(modalName, { onCloseGoTo = ''}) { + open(modalName, { onCloseGoTo = ''} = {}) { this._currentModal.set(modalName); this._onCloseGoTo = onCloseGoTo; } From 41b23f88aea0f421226f92b081cdb1b61c64bde4 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Fri, 30 Oct 2015 03:05:43 +0100 Subject: [PATCH 21/27] Implement fast-render This required updating 3stack:presence because of the following bug: https://github.com/3stack-software/meteor-presence/pull/3 --- .eslintrc | 1 + .meteor/packages | 1 + .meteor/versions | 5 ++++- History.md | 2 ++ server/publications/fast-render.js | 7 +++++++ 5 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 server/publications/fast-render.js diff --git a/.eslintrc b/.eslintrc index a8017e664..f9321bfb9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -90,6 +90,7 @@ globals: BlazeLayout: false DocHead: false ESSearchResults: false + FastRender: false FlowRouter: false FS: false getSlug: false diff --git a/.meteor/packages b/.meteor/packages index 5b666ed10..bdb0b60e3 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -49,6 +49,7 @@ audit-argument-checks kadira:blaze-layout kadira:dochead kadira:flow-router +meteorhacks:fast-render meteorhacks:picker meteorhacks:subs-manager mquandalle:autofocus diff --git a/.meteor/versions b/.meteor/versions index bd2390c46..db5d11e72 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -1,4 +1,4 @@ -3stack:presence@1.0.3 +3stack:presence@1.0.4 accounts-base@1.2.2 accounts-password@1.1.4 aldeed:collection2@2.5.0 @@ -35,6 +35,7 @@ cfs:tempstore@0.1.5 cfs:upload-http@0.0.20 cfs:worker@0.1.4 check@1.1.0 +chuangbo:cookie@1.1.0 coffeescript@1.0.11 cosmos:browserify@0.8.1 dburles:collection-helpers@1.0.4 @@ -75,6 +76,8 @@ meteor-base@1.0.1 meteor-platform@1.2.3 meteorhacks:aggregate@1.3.0 meteorhacks:collection-utils@1.2.0 +meteorhacks:fast-render@2.10.0 +meteorhacks:inject-data@1.4.1 meteorhacks:picker@1.0.3 meteorhacks:subs-manager@1.6.2 meteorspark:util@0.2.0 diff --git a/History.md b/History.md index 462e37a7c..bc8ff5cac 100644 --- a/History.md +++ b/History.md @@ -3,6 +3,8 @@ This release features: * Card import from Trello +* Accelerate the initial page rendering by sending the data on the intial HTTP + response instead of waiting for the DDP connection to open. Thanks to GitHub users AlexanderS, fisle, ndarilek, and xavierpriour for their contributions. diff --git a/server/publications/fast-render.js b/server/publications/fast-render.js new file mode 100644 index 000000000..e28b6f2e1 --- /dev/null +++ b/server/publications/fast-render.js @@ -0,0 +1,7 @@ +FastRender.onAllRoutes(function() { + this.subscribe('boards'); +}); + +FastRender.route('/b/:id/:slug', function({ id }) { + this.subscribe('board', id); +}); From f9cbc1da4c1729d62b8c7dbbb7586dc95537b3c4 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Sat, 31 Oct 2015 13:21:12 -0700 Subject: [PATCH 22/27] Fix an exception introduced in 41b23f8 --- client/components/boards/boardBody.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index 5c1c974f0..5a74e61bf 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -45,7 +45,8 @@ BlazeComponent.extendComponent({ }, scrollLeft(position = 0) { - this.$('.js-lists').animate({ + const lists = this.$('.js-lists'); + lists && lists.animate({ scrollLeft: position, }); }, From e551d067b1ff85b9095952059ab8d6cb6dc3a4f1 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Mon, 2 Nov 2015 18:47:06 -0800 Subject: [PATCH 23/27] Share the useraccounts configuration with the server The previous users accounts templates configuration only happened on the client, which was wrong and caused some bugs, for instance an invalid URL was generated in the reset password e-mail. Fixes #297 --- {client/config => config}/accounts.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {client/config => config}/accounts.js (100%) diff --git a/client/config/accounts.js b/config/accounts.js similarity index 100% rename from client/config/accounts.js rename to config/accounts.js From a6bd49da9fdbd523853a2b041a90178ff1f0531c Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Sun, 8 Nov 2015 21:15:31 -0800 Subject: [PATCH 24/27] Update Meteor packages --- .meteor/versions | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.meteor/versions b/.meteor/versions index db5d11e72..b2d2cff66 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -37,7 +37,7 @@ cfs:worker@0.1.4 check@1.1.0 chuangbo:cookie@1.1.0 coffeescript@1.0.11 -cosmos:browserify@0.8.1 +cosmos:browserify@0.8.3 dburles:collection-helpers@1.0.4 ddp@1.2.2 ddp-client@1.2.1 @@ -63,7 +63,7 @@ idmontie:migrations@1.0.1 jquery@1.11.4 kadira:blaze-layout@2.2.0 kadira:dochead@1.3.2 -kadira:flow-router@2.7.0 +kadira:flow-router@2.8.0 kenton:accounts-sandstorm@0.1.7 launch-screen@1.0.4 livedata@1.0.15 @@ -106,7 +106,7 @@ ordered-dict@1.0.4 peerlibrary:assert@0.2.5 peerlibrary:base-component@0.14.0 peerlibrary:blaze-components@0.15.1 -peerlibrary:computed-field@0.3.0 +peerlibrary:computed-field@0.3.1 peerlibrary:reactive-field@0.1.0 perak:markdown@1.0.5 promise@0.5.1 From 5d77ad4f6ba70038486d734e97844c547e664e88 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Sat, 31 Oct 2015 09:26:55 -0700 Subject: [PATCH 25/27] Finish the minicard editor auto-completion feature This commit stands on the initial support implemented in #342. We now avoid error-prone parsing step by adding the member or the label directly to the card object. We also added support for `Tab` to completion on our textComplete component. Closes #342 --- client/components/cards/cardDetails.js | 2 +- client/components/forms/forms.styl | 4 +- client/components/lists/list.js | 2 +- client/components/lists/listBody.jade | 15 ++- client/components/lists/listBody.js | 156 +++++++++++-------------- client/components/lists/listHeader.js | 2 +- client/components/sidebar/sidebar.js | 2 +- client/lib/textComplete.js | 32 ++++- 8 files changed, 118 insertions(+), 97 deletions(-) diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index fa818c5aa..b4fdca52d 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -13,7 +13,7 @@ BlazeComponent.extendComponent({ }, reachNextPeak() { - const activitiesComponent = this.childrenComponents('activities')[0]; + const activitiesComponent = this.childComponents('activities')[0]; activitiesComponent.loadNextPage(); }, diff --git a/client/components/forms/forms.styl b/client/components/forms/forms.styl index 2d92aca95..9ae951403 100644 --- a/client/components/forms/forms.styl +++ b/client/components/forms/forms.styl @@ -621,11 +621,11 @@ button width: 11px height: @width border-radius: 2px - margin-right: 3px + margin: 2px 7px -2px -2px display: inline-block &.active background: #005377 - a + a, .quiet color: white diff --git a/client/components/lists/list.js b/client/components/lists/list.js index 75e816b59..f5410ed0f 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -7,7 +7,7 @@ BlazeComponent.extendComponent({ // Proxy openForm(options) { - this.childrenComponents('listBody')[0].openForm(options); + this.childComponents('listBody')[0].openForm(options); }, onCreated() { diff --git a/client/components/lists/listBody.jade b/client/components/lists/listBody.jade index b0a374ea7..e659b1798 100644 --- a/client/components/lists/listBody.jade +++ b/client/components/lists/listBody.jade @@ -22,9 +22,20 @@ template(name="listBody") template(name="addCardForm") .minicard.minicard-composer.js-composer - .minicard-detailss.clearfix - textarea.minicard-composer-textarea.js-card-title(autofocus) + if getLabels + .minicard-labels + each getLabels + .minicard-label(class="card-label-{{color}}" title="{{name}}") + textarea.minicard-composer-textarea.js-card-title(autofocus) + if members.get .minicard-members.js-minicard-composer-members + each members.get + +userAvatar(userId=this) + .add-controls.clearfix button.primary.confirm(type="submit") {{_ 'add'}} a.fa.fa-times-thin.js-close-inlined-form + +template(name="autocompleteLabelLine") + .minicard-label(class="card-label-{{colorName}}" title=labelName) + span(class="{{#if hasNoName}}quiet{{/if}}")= labelName diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 2ed5d38a4..36b60d06c 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -11,7 +11,7 @@ BlazeComponent.extendComponent({ options = options || {}; options.position = options.position || 'top'; - const forms = this.childrenComponents('inlinedForm'); + const forms = this.childComponents('inlinedForm'); let form = forms.find((component) => { return component.data().position === options.position; }); @@ -27,7 +27,9 @@ BlazeComponent.extendComponent({ const lastCardDom = this.find('.js-minicard:last'); const textarea = $(evt.currentTarget).find('textarea'); const position = this.currentData().position; - let title = textarea.val().trim(); + const title = textarea.val().trim(); + + const formComponent = this.childComponents('addCardForm')[0]; let sortIndex; if (position === 'top') { sortIndex = Utils.calculateIndex(null, firstCardDom).base; @@ -35,40 +37,16 @@ BlazeComponent.extendComponent({ sortIndex = Utils.calculateIndex(lastCardDom, null).base; } - // Parse for @user and #label mentions, stripping them from the title - // and applying the appropriate users and labels to the card instead. - const currentBoard = Boards.findOne(Session.get('currentBoard')); - - // Find all @-mentioned usernames, collect a list of their IDs and strip - // their mention out of the title. - let foundUserIds = []; // eslint-disable-line prefer-const - currentBoard.members.forEach((member) => { - const username = Users.findOne(member.userId).username; - if (title.indexOf(`@${username}`) !== -1) { - foundUserIds.push(member.userId); - title = title.replace(`@${username}`, ''); - } - }); - - // Find all #-mentioned labels (based on their colour or name), collect a - // list of their IDs, and strip their mention out of the title. - let foundLabelIds = []; // eslint-disable-line prefer-const - currentBoard.labels.forEach((label) => { - const labelName = (!label.name || label.name === '') - ? label.color : label.name; - if (title.indexOf(`#${labelName}`) !== -1) { - foundLabelIds.push(label._id); - title = title.replace(`#${labelName}`, ''); - } - }); + const members = formComponent.members.get(); + const labelIds = formComponent.labels.get(); if (title) { const _id = Cards.insert({ title, + members, + labelIds, listId: this.data()._id, boardId: this.data().board()._id, - labelIds: foundLabelIds, - members: foundUserIds, sort: sortIndex, }); // In case the filter is active we need to add the newly inserted card in @@ -82,6 +60,8 @@ BlazeComponent.extendComponent({ if (position === 'bottom') { this.scrollToBottom(); } + + formComponent.reset(); } }, @@ -129,18 +109,40 @@ BlazeComponent.extendComponent({ }, }).register('listBody'); -let dropdownMenuIsOpened = false; +function toggleValueInReactiveArray(reactiveValue, value) { + const array = reactiveValue.get(); + const valueIndex = array.indexOf(value); + if (valueIndex === -1) { + array.push(value); + } else { + array.splice(valueIndex, 1); + } + reactiveValue.set(array); +} + BlazeComponent.extendComponent({ template() { return 'addCardForm'; }, - pressKey(evt) { - // Don't do anything if the drop down is showing - if (dropdownMenuIsOpened) { - return; - } + onCreated() { + this.labels = new ReactiveVar([]); + this.members = new ReactiveVar([]); + }, + reset() { + this.labels.set([]); + this.members.set([]); + }, + + getLabels() { + const currentBoardId = Session.get('currentBoard'); + return Boards.findOne(currentBoardId).labels.filter((label) => { + return this.labels.get().indexOf(label._id) > -1; + }); + }, + + pressKey(evt) { // Pressing Enter should submit the card if (evt.keyCode === 13) { evt.preventDefault(); @@ -176,28 +178,25 @@ BlazeComponent.extendComponent({ }]; }, - onCreated() { - dropdownMenuIsOpened = false; - }, - onRendered() { - const $textarea = this.$('textarea'); - const currentBoard = Boards.findOne(Session.get('currentBoard')); - $textarea.textcomplete([ + const editor = this; + this.$('textarea').escapeableTextComplete([ // User mentions { match: /\B@(\w*)$/, search(term, callback) { + const currentBoard = Boards.findOne(Session.get('currentBoard')); callback($.map(currentBoard.members, (member) => { - const username = Users.findOne(member.userId).username; - return username.indexOf(term) === 0 ? username : null; + const user = Users.findOne(member.userId); + return user.username.indexOf(term) === 0 ? user : null; })); }, - template(value) { - return value; + template(user) { + return user.username; }, - replace(username) { - return `@${username} `; + replace(user) { + toggleValueInReactiveArray(editor.members, user._id); + return ''; }, index: 1, }, @@ -206,51 +205,38 @@ BlazeComponent.extendComponent({ { match: /\B#(\w*)$/, search(term, callback) { + const currentBoard = Boards.findOne(Session.get('currentBoard')); callback($.map(currentBoard.labels, (label) => { - const labelName = (!label.name || label.name === '') - ? label.color - : label.name; - return labelName.indexOf(term) === 0 ? labelName : null; + if (label.name.indexOf(term) > -1 || + label.color.indexOf(term) > -1) { + return label; + } })); }, - template(value) { - // XXX the following is duplicated from editor.js and should be - // abstracted to keep things DRY - // add a "colour badge" in front of the label name - // but first, get the colour's name from its value - const colorName = currentBoard.labels.find((label) => { - return value === label.name || value === label.color; - }).color; - const valueSpan = (colorName === value) - ? `${value}` - : value; - return (colorName && colorName !== '') - ? `
${valueSpan}` - : value; + template(label) { + return Blaze.toHTMLWithData(Template.autocompleteLabelLine, { + hasNoName: !Boolean(label.name), + colorName: label.color, + labelName: label.name || label.color, + }); }, replace(label) { - return `#${label} `; + toggleValueInReactiveArray(editor.labels, label._id); + return ''; }, index: 1, }, - ]); - - // customize hooks for dealing with the dropdowns - $textarea.on({ - 'textComplete:show'() { - dropdownMenuIsOpened = true; - }, - 'textComplete:hide'() { - Tracker.afterFlush(() => { - dropdownMenuIsOpened = false; - }); + ], { + // When the autocomplete menu is shown we want both a press of both `Tab` + // or `Enter` to validation the auto-completion. We also need to stop the + // event propagation to prevent the card from submitting (on `Enter`) or + // going on the next column (on `Tab`). + onKeydown(evt, commands) { + if (evt.keyCode === 9 || evt.keyCode === 13) { + evt.stopPropagation(); + return commands.KEY_ENTER; + } }, }); - - EscapeActions.register('textcomplete', - () => {}, - () => dropdownMenuIsOpened - ); }, }).register('addCardForm'); diff --git a/client/components/lists/listHeader.js b/client/components/lists/listHeader.js index dbf9fced0..d660508a7 100644 --- a/client/components/lists/listHeader.js +++ b/client/components/lists/listHeader.js @@ -5,7 +5,7 @@ BlazeComponent.extendComponent({ editTitle(evt) { evt.preventDefault(); - const newTitle = this.childrenComponents('inlinedForm')[0].getValue().trim(); + const newTitle = this.childComponents('inlinedForm')[0].getValue().trim(); const list = this.currentData(); if (newTitle) { list.rename(newTitle.trim()); diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js index ccb9f2f5d..ef071fe0c 100644 --- a/client/components/sidebar/sidebar.js +++ b/client/components/sidebar/sidebar.js @@ -54,7 +54,7 @@ BlazeComponent.extendComponent({ }, reachNextPeak() { - const activitiesComponent = this.childrenComponents('activities')[0]; + const activitiesComponent = this.childComponents('activities')[0]; activitiesComponent.loadNextPage(); }, diff --git a/client/lib/textComplete.js b/client/lib/textComplete.js index e50d7cbc7..3e69d07f5 100644 --- a/client/lib/textComplete.js +++ b/client/lib/textComplete.js @@ -3,8 +3,23 @@ // of the vanilla `textcomplete`. let dropdownMenuIsOpened = false; -$.fn.escapeableTextComplete = function(...args) { - this.textcomplete(...args); +$.fn.escapeableTextComplete = function(strategies, options, ...otherArgs) { + // When the autocomplete menu is shown we want both a press of both `Tab` + // or `Enter` to validation the auto-completion. We also need to stop the + // event propagation to prevent EscapeActions side effect, for instance the + // minicard submission (on `Enter`) or going on the next column (on `Tab`). + options = { + onKeydown(evt, commands) { + if (evt.keyCode === 9 || evt.keyCode === 13) { + evt.stopPropagation(); + return commands.KEY_ENTER; + } + }, + ...options, + }; + + // Proxy to the vanilla jQuery component + this.textcomplete(strategies, options, ...otherArgs); // Since commit d474017 jquery-textComplete automatically closes a potential // opened dropdown menu when the user press Escape. This behavior conflicts @@ -18,7 +33,14 @@ $.fn.escapeableTextComplete = function(...args) { }, 'textComplete:hide'() { Tracker.afterFlush(() => { - dropdownMenuIsOpened = false; + // XXX Hack. We unfortunately need to set a setTimeout here to make the + // `noClickEscapeOn` work bellow, otherwise clicking on a autocomplete + // item will close both the autocomplete menu (as expected) but also the + // next item in the stack (for example the minicard editor) which we + // don't want. + setTimeout(() => { + dropdownMenuIsOpened = false; + }, 100); }); }, }); @@ -26,5 +48,7 @@ $.fn.escapeableTextComplete = function(...args) { EscapeActions.register('textcomplete', () => {}, - () => dropdownMenuIsOpened + () => dropdownMenuIsOpened, { + noClickEscapeOn: '.textcomplete-dropdown', + } ); From fc82f7227ac4a38df4d5f1145f0fb5012a1545c1 Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Sun, 8 Nov 2015 23:31:08 -0800 Subject: [PATCH 26/27] Complete the release notes for #342 --- History.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/History.md b/History.md index bc8ff5cac..505be8810 100644 --- a/History.md +++ b/History.md @@ -3,11 +3,13 @@ This release features: * Card import from Trello +* Autocompletion in the minicard editor. Start with @ to start the + a board member autocompletion, or # for a label. * Accelerate the initial page rendering by sending the data on the intial HTTP response instead of waiting for the DDP connection to open. -Thanks to GitHub users AlexanderS, fisle, ndarilek, and xavierpriour for their -contributions. +Thanks to GitHub users AlexanderS, fisle, FuzzyWuzzie, ndarilek, and +xavierpriour for their contributions. # v0.9 From cb3bd86396e4d19e1f05fcb94e3527f81e70412e Mon Sep 17 00:00:00 2001 From: Maxime Quandalle Date: Wed, 11 Nov 2015 14:15:37 -0800 Subject: [PATCH 27/27] Improve Sandstorm usernames management We now use the `preferredHandle` exposed by Sandstorm as source for the username and append a number if the username is already taken since we need to ensure username uniqueness (eg 'max', 'max1', 'max2') Fixes #352 --- .meteor/versions | 2 +- client/components/sidebar/sidebar.jade | 2 +- sandstorm.js | 33 ++++++++++++++++++++++---- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/.meteor/versions b/.meteor/versions index b2d2cff66..5c4c189b7 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -64,7 +64,7 @@ jquery@1.11.4 kadira:blaze-layout@2.2.0 kadira:dochead@1.3.2 kadira:flow-router@2.8.0 -kenton:accounts-sandstorm@0.1.7 +kenton:accounts-sandstorm@0.1.8 launch-screen@1.0.4 livedata@1.0.15 localstorage@1.0.5 diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index 910470563..f98ea4ee7 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -89,7 +89,7 @@ template(name="addMemberPopup") a.name.js-select-member(title="{{profile.name}} ({{username}})") +userAvatar(userId=_id esSearch=true) span.full-name - = profile.name + = profile.fullname | ({{username}}) if isBoardMember .quiet ({{_ 'joined'}}) diff --git a/sandstorm.js b/sandstorm.js index 65f248666..997aed468 100644 --- a/sandstorm.js +++ b/sandstorm.js @@ -22,8 +22,8 @@ if (isSandstorm && Meteor.isServer) { }; function updateUserPermissions(userId, permissions) { - const isActive = permissions.includes('participate'); - const isAdmin = permissions.includes('configure'); + const isActive = permissions.indexOf('participate') > -1; + const isAdmin = permissions.indexOf('configure') > -1; const permissionDoc = { userId, isActive, isAdmin }; const boardMembers = Boards.findOne(sandstormBoard._id).members; @@ -78,17 +78,40 @@ if (isSandstorm && Meteor.isServer) { // unique board document. Note that when the `Users.after.insert` hook is // called, the user is inserted into the database but not connected. So // despite the appearances `userId` is null in this block. - // - // XXX We should support the `preferredHandle` exposed by Sandstorm Users.after.insert((userId, doc) => { if (!Boards.findOne(sandstormBoard._id)) { - Boards.insert(sandstormBoard, {validate: false}); + Boards.insert(sandstormBoard, { validate: false }); Activities.update( { activityTypeId: sandstormBoard._id }, { $set: { userId: doc._id }} ); } + // We rely on username uniqueness for the user mention feature, but + // Sandstorm doesn't enforce this property -- see #352. Our strategy to + // generate unique usernames from the Sandstorm `preferredHandle` is to + // append a number that we increment until we generate a username that no + // one already uses (eg, 'max', 'max1', 'max2'). + function generateUniqueUsername(username, appendNumber) { + return username + String(appendNumber === 0 ? '' : appendNumber); + } + + const username = doc.services.sandstorm.preferredHandle; + let appendNumber = 0; + while (Users.findOne({ + _id: { $ne: doc._id }, + username: generateUniqueUsername(username, appendNumber), + })) { + appendNumber += 1; + } + + Users.update(doc._id, { + $set: { + username: generateUniqueUsername(username, appendNumber), + 'profile.fullname': doc.services.sandstorm.name, + }, + }); + updateUserPermissions(doc._id, doc.services.sandstorm.permissions); });