summaryrefslogtreecommitdiffstats
path: root/client/components
diff options
context:
space:
mode:
authorMaxime Quandalle <maxime@quandalle.com>2015-08-31 15:09:53 +0200
committerMaxime Quandalle <maxime@quandalle.com>2015-08-31 15:52:16 +0200
commitd644cba38ff06369cc43c1ebd08d344fd1d248ea (patch)
treebad4bcef1f549132792301cf134b3c8234ad1d13 /client/components
parentcc88e78483d2f39048d3558193f0c82b711c12c8 (diff)
downloadwekan-d644cba38ff06369cc43c1ebd08d344fd1d248ea.tar.gz
wekan-d644cba38ff06369cc43c1ebd08d344fd1d248ea.tar.bz2
wekan-d644cba38ff06369cc43c1ebd08d344fd1d248ea.zip
Replace the component bounded `cachedValue` by a global `UnsavedEdits`
This new draft saving system is currently only implemented for the card description and comment. We need better a component inheritance/composition model to support this for all editable fields. Fixes #186
Diffstat (limited to 'client/components')
-rw-r--r--client/components/activities/comments.jade1
-rw-r--r--client/components/activities/comments.js104
-rw-r--r--client/components/cards/cardDetails.jade12
-rw-r--r--client/components/cards/cardDetails.js35
-rw-r--r--client/components/forms/cachedValue.js22
-rw-r--r--client/components/forms/inlinedform.js90
-rw-r--r--client/components/lists/listHeader.jade2
7 files changed, 112 insertions, 154 deletions
diff --git a/client/components/activities/comments.jade b/client/components/activities/comments.jade
index 3b47cbf6..405778de 100644
--- a/client/components/activities/comments.jade
+++ b/client/components/activities/comments.jade
@@ -4,5 +4,6 @@ template(name="commentForm")
+userAvatar(userId=currentUser._id)
form.js-new-comment-form
+editor(class="js-new-comment-input")
+ | {{getUnsavedValue 'cardComment' currentCard._id}}
.add-controls
button.primary.confirm.clear.js-add-comment(type="submit") {{_ 'comment'}}
diff --git a/client/components/activities/comments.js b/client/components/activities/comments.js
index d41b86dd..a3af7ba6 100644
--- a/client/components/activities/comments.js
+++ b/client/components/activities/comments.js
@@ -1,47 +1,83 @@
-var commentFormIsOpen = new ReactiveVar(false);
+let commentFormIsOpen = new ReactiveVar(false);
-Template.commentForm.helpers({
- commentFormIsOpen: function() {
- return commentFormIsOpen.get();
- }
-});
+BlazeComponent.extendComponent({
+ template() {
+ return 'commentForm';
+ },
-Template.commentForm.events({
- 'click .js-new-comment:not(.focus)': function() {
- commentFormIsOpen.set(true);
+ onDestroyed() {
+ commentFormIsOpen.set(false);
},
- 'submit .js-new-comment-form': function(evt, tpl) {
- var input = tpl.$('.js-new-comment-input');
- if ($.trim(input.val())) {
- CardComments.insert({
- boardId: this.boardId,
- cardId: this._id,
- text: input.val()
- });
- input.val('');
- input.blur();
- commentFormIsOpen.set(false);
- Tracker.flush();
- autosize.update(input);
- }
- evt.preventDefault();
+
+ commentFormIsOpen() {
+ return commentFormIsOpen.get();
},
- // Pressing Ctrl+Enter should submit the form
- 'keydown form textarea': function(evt, tpl) {
- if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
- tpl.find('button[type=submit]').click();
- }
+
+ getInput() {
+ return this.$('.js-new-comment-input');
+ },
+
+ events() {
+ return [{
+ 'click .js-new-comment:not(.focus)': function() {
+ commentFormIsOpen.set(true);
+ },
+ 'submit .js-new-comment-form': function(evt) {
+ let input = this.getInput();
+ if ($.trim(input.val())) {
+ CardComments.insert({
+ boardId: this.boardId,
+ cardId: this._id,
+ text: input.val()
+ });
+ resetCommentInput(input);
+ Tracker.flush();
+ autosize.update(input);
+ }
+ evt.preventDefault();
+ },
+ // Pressing Ctrl+Enter should submit the form
+ 'keydown form textarea': function(evt) {
+ if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
+ this.find('button[type=submit]').click();
+ }
+ }
+ }];
}
-});
+}).register('commentForm');
-Template.commentForm.onDestroyed(function() {
+// XXX This should be a static method of the `commentForm` component
+function resetCommentInput(input) {
+ input.val('');
+ input.blur();
commentFormIsOpen.set(false);
-});
+}
+
+// XXX This should handled a `onUpdated` callback of the `commentForm` component
+// but since this callback doesn't exists, and `onRendered` is not called if the
+// data is not destroyed and recreated, we simulate the desired callback using
+// Tracker.autorun to register the component dependencies, and re-run when these
+// dependencies are invalidated. A better component API would remove this hack.
+Tracker.autorun(() => {
+ Session.get('currentCard');
+ Tracker.afterFlush(() => {
+ autosize.update($('.js-new-comment-input'));
+ });
+})
EscapeActions.register('inlinedForm',
function() {
- commentFormIsOpen.set(false);
- $('.js-new-comment-input').blur();
+ const draftKey = {
+ fieldName: 'cardComment',
+ docId: Session.get('currentCard')
+ };
+ let commentInput = $('.js-new-comment-input');
+ if ($.trim(commentInput.val())) {
+ UnsavedEdits.set(draftKey, commentInput.val());
+ } else {
+ UnsavedEdits.reset(draftKey);
+ }
+ resetCommentInput(commentInput);
},
function() { return commentFormIsOpen.get(); }, {
noClickEscapeOn: '.js-new-comment'
diff --git a/client/components/cards/cardDetails.jade b/client/components/cards/cardDetails.jade
index 01b56894..a5dcb47b 100644
--- a/client/components/cards/cardDetails.jade
+++ b/client/components/cards/cardDetails.jade
@@ -34,11 +34,11 @@ template(name="cardDetails")
//- XXX We should use "editable" to avoid repetiting ourselves
if currentUser.isBoardMember
h3.card-details-item-title Description
- +inlinedForm(classNames="card-description js-card-description")
+ +inlinedCardDescription(classNames="card-description js-card-description")
+editor(autofocus=true)
- = description
+ | {{getUnsavedValue 'cardDescription' _id description}}
.edit-controls.clearfix
- button.primary(type="submit") {{_ 'edit'}}
+ button.primary(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form
else
a.js-open-inlined-form
@@ -47,6 +47,12 @@ template(name="cardDetails")
= description
else
| {{_ 'edit'}}
+ if (hasUnsavedValue 'cardDescription' _id)
+ p.quiet
+ | You have an unsaved description.
+ a.js-open-inlined-form View it
+ = ' - '
+ a.js-close-inlined-form Discard
else if description
h3.card-details-item-title Description
+viewer
diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js
index c0ea6a05..caa5993f 100644
--- a/client/components/cards/cardDetails.js
+++ b/client/components/cards/cardDetails.js
@@ -1,7 +1,3 @@
-// XXX Obviously this shouldn't be a global, but this is currently the only way
-// to share a variable between two files.
-
-
BlazeComponent.extendComponent({
template: function() {
return 'cardDetails';
@@ -80,6 +76,37 @@ BlazeComponent.extendComponent({
}
}).register('cardDetails');
+// We extends the normal InlinedForm component to support UnsavedEdits draft
+// feature.
+(class extends InlinedForm {
+ _getUnsavedEditKey() {
+ return {
+ fieldName: 'cardDescription',
+ docId: Session.get('currentCard'),
+ }
+ }
+
+ close(isReset = false) {
+ if (this.isOpen.get() && ! isReset) {
+ UnsavedEdits.set(this._getUnsavedEditKey(), this.getValue());
+ }
+ super();
+ }
+
+ reset() {
+ UnsavedEdits.reset(this._getUnsavedEditKey());
+ this.close(true);
+ }
+
+ events() {
+ const parentEvents = InlinedForm.prototype.events()[0];
+ return [{
+ ...parentEvents,
+ 'click .js-close-inlined-form': this.reset,
+ }];
+ }
+}).register('inlinedCardDescription');
+
Template.cardDetailsActionsPopup.events({
'click .js-members': Popup.open('cardMembers'),
'click .js-labels': Popup.open('cardLabels'),
diff --git a/client/components/forms/cachedValue.js b/client/components/forms/cachedValue.js
deleted file mode 100644
index a2898d85..00000000
--- a/client/components/forms/cachedValue.js
+++ /dev/null
@@ -1,22 +0,0 @@
-var emptyValue = '';
-
-Mixins.CachedValue = BlazeComponent.extendComponent({
- onCreated: function() {
- this._cachedValue = emptyValue;
- },
-
- setCache: function(value) {
- this._cachedValue = value;
- },
-
- getCache: function(defaultValue) {
- if (this._cachedValue === emptyValue)
- return defaultValue || '';
- else
- return this._cachedValue;
- },
-
- resetCache: function() {
- this.setCache('');
- }
-});
diff --git a/client/components/forms/inlinedform.js b/client/components/forms/inlinedform.js
deleted file mode 100644
index 09c75ce1..00000000
--- a/client/components/forms/inlinedform.js
+++ /dev/null
@@ -1,90 +0,0 @@
-// A inlined form is used to provide a quick edition of single field for a given
-// document. Clicking on a edit button should display the form to edit the field
-// value. The form can then be submited, or just closed.
-//
-// When the form is closed we save non-submitted values in memory to avoid any
-// data loss.
-//
-// Usage:
-//
-// +inlineForm
-// // the content when the form is open
-// else
-// // the content when the form is close (optional)
-
-// We can only have one inlined form element opened at a time
-// XXX Could we avoid using a global here ? This is used in Mousetrap
-// keyboard.js
-var currentlyOpenedForm = new ReactiveVar(null);
-
-BlazeComponent.extendComponent({
- template: function() {
- return 'inlinedForm';
- },
-
- mixins: function() {
- return [Mixins.CachedValue];
- },
-
- onCreated: function() {
- this.isOpen = new ReactiveVar(false);
- },
-
- onDestroyed: function() {
- currentlyOpenedForm.set(null);
- },
-
- open: function() {
- // Close currently opened form, if any
- EscapeActions.executeUpTo('inlinedForm');
- this.isOpen.set(true);
- currentlyOpenedForm.set(this);
- },
-
- close: function() {
- this.saveValue();
- this.isOpen.set(false);
- currentlyOpenedForm.set(null);
- },
-
- getValue: function() {
- var input = this.find('textarea,input[type=text]');
- return this.isOpen.get() && input && input.value;
- },
-
- saveValue: function() {
- this.callFirstWith(this, 'setCache', this.getValue());
- },
-
- events: function() {
- return [{
- 'click .js-close-inlined-form': this.close,
- 'click .js-open-inlined-form': this.open,
-
- // Pressing Ctrl+Enter should submit the form
- 'keydown form textarea': function(evt) {
- if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
- this.find('button[type=submit]').click();
- }
- },
-
- // Close the inlined form when after its submission
- submit: function() {
- if (this.currentData().autoclose !== false) {
- Tracker.afterFlush(() => {
- this.close();
- this.callFirstWith(this, 'resetCache');
- });
- }
- }
- }];
- }
-}).register('inlinedForm');
-
-// Press escape to close the currently opened inlinedForm
-EscapeActions.register('inlinedForm',
- function() { currentlyOpenedForm.get().close(); },
- function() { return currentlyOpenedForm.get() !== null; }, {
- noClickEscapeOn: '.js-inlined-form'
- }
-);
diff --git a/client/components/lists/listHeader.jade b/client/components/lists/listHeader.jade
index 0528514e..d87078cd 100644
--- a/client/components/lists/listHeader.jade
+++ b/client/components/lists/listHeader.jade
@@ -11,7 +11,7 @@ template(name="listHeader")
template(name="editListTitleForm")
.list-composer
- input.full-line(type="text" value=title autofocus)
+ input.full-line(type="text" value="{{../trySomething}}" autofocus)
.edit-controls.clearfix
button.primary.confirm(type="submit") {{_ 'save'}}
a.fa.fa-times-thin.js-close-inlined-form