diff options
Diffstat (limited to 'client/components')
24 files changed, 582 insertions, 314 deletions
diff --git a/client/components/activities/activities.jade b/client/components/activities/activities.jade index 8ecbdee8..c86936a0 100644 --- a/client/components/activities/activities.jade +++ b/client/components/activities/activities.jade @@ -8,234 +8,201 @@ template(name="activities") +cardActivities template(name="boardActivities") - each currentBoard.activities - .activity - +userAvatar(userId=user._id) - p.activity-desc - +memberName(user=user) + each activityData in currentBoard.activities + +activity(activity=activityData card=card mode=mode) - if($eq activityType 'deleteAttachment') - | {{{_ 'activity-delete-attach' cardLink}}}. +template(name="cardActivities") + each activityData in currentCard.activities + +activity(activity=activityData card=card mode=mode) + +template(name="activity") + .activity + +userAvatar(userId=activity.user._id) + p.activity-desc + +memberName(user=activity.user) + + //- attachment activity ------------------------------------------------- + if($eq activity.activityType 'deleteAttachment') + | {{{_ 'activity-delete-attach' cardLink}}}. + + if($eq activity.activityType 'addAttachment') + | {{{_ 'activity-attached' attachmentLink cardLink}}}. + if($neq mode 'board') + if activity.attachment.isImage + img.attachment-image-preview(src=activity.attachment.url) + + //- board activity ------------------------------------------------------ + if($eq mode 'board') + if($eq activity.activityType 'createBoard') + | {{_ 'activity-created' boardLabel}}. - if($eq activityType 'addAttachment') - | {{{_ 'activity-attached' attachmentLink cardLink}}}. + if($eq activity.activityType 'importBoard') + | {{{_ 'activity-imported-board' boardLabel sourceLink}}}. - if($eq activityType 'addBoardMember') + if($eq activity.activityType 'addBoardMember') | {{{_ 'activity-added' memberLink boardLabel}}}. - if($eq activityType 'addComment') - | {{{_ 'activity-on' cardLink}}} - a.activity-comment(href="{{ card.absoluteUrl }}") - +viewer - = comment.text - - if($eq activityType 'addChecklist') - | {{{_ 'activity-checklist-added' cardLink}}}. - .activity-checklist(href="{{ card.absoluteUrl }}") - +viewer - = checklist.title - if($eq activityType 'removeChecklist') - | {{{_ 'activity-checklist-removed' cardLink}}}. - - if($eq activityType 'checkedItem') - | {{{_ 'activity-checked-item' checkItem checklist.title cardLink}}}. - - if($eq activityType 'uncheckedItem') - | {{{_ 'activity-unchecked-item' checkItem checklist.title cardLink}}}. - - if($eq activityType 'checklistCompleted') - | {{{_ 'activity-checklist-completed' checklist.title cardLink}}}. + if($eq activity.activityType 'removeBoardMember') + | {{{_ 'activity-excluded' memberLink boardLabel}}}. - if($eq activityType 'checklistUncompleted') - | {{{_ 'activity-checklist-uncompleted' checklist.title cardLink}}}. + //- card activity ------------------------------------------------------- + if($eq activity.activityType 'createCard') + if($eq mode 'card') + | {{{_ 'activity-added' cardLabel activity.listName}}}. + else + | {{{_ 'activity-added' cardLabel boardLabel}}}. - if($eq activityType 'addChecklistItem') - | {{{_ 'activity-checklist-item-added' checklist.title cardLink}}}. - .activity-checklist(href="{{ card.absoluteUrl }}") - +viewer - = checklistItem.title - if($eq activityType 'removedChecklistItem') - | {{{_ 'activity-checklist-item-removed' checklist.title cardLink}}}. + if($eq activity.activityType 'importCard') + | {{{_ 'activity-imported' cardLink boardLabel sourceLink}}}. - if($eq activityType 'archivedCard') - | {{{_ 'activity-archived' cardLink}}}. + if($eq activity.activityType 'moveCard') + | {{{_ 'activity-moved' cardLabel activity.oldList.title activity.list.title}}}. - if($eq activityType 'archivedList') - | {{_ 'activity-archived' list.title}}. + if($eq activity.activityType 'moveCardBoard') + | {{{_ 'activity-moved' cardLink activity.oldBoardName activity.boardName}}}. - if($eq activityType 'archivedSwimlane') - | {{_ 'activity-archived' swimlane.title}}. + if($eq activity.activityType 'archivedCard') + | {{{_ 'activity-archived' cardLink}}}. - if($eq activityType 'createBoard') - | {{_ 'activity-created' boardLabel}}. + if($eq activity.activityType 'restoredCard') + | {{{_ 'activity-sent' cardLink boardLabel}}}. - if($eq activityType 'createCard') - | {{{_ 'activity-added' cardLink boardLabel}}}. + //- checklist activity -------------------------------------------------- + if($eq activity.activityType 'addChecklist') + | {{{_ 'activity-checklist-added' cardLink}}}. + if($eq mode 'card') + .activity-checklist + +viewer + = activity.checklist.title + else + a.activity-checklist(href="{{ activity.card.absoluteUrl }}") + +viewer + = activity.checklist.title - if($eq activityType 'createCustomField') - | {{_ 'activity-customfield-created' customField}}. + if($eq activity.activityType 'removedChecklist') + | {{{_ 'activity-checklist-removed' cardLink}}}. - if($eq activityType 'createList') - | {{_ 'activity-added' list.title boardLabel}}. + if($eq activity.activityType 'completeChecklist') + | {{{_ 'activity-checklist-completed' activity.checklist.title cardLink}}}. - if($eq activityType 'createSwimlane') - | {{_ 'activity-added' swimlane.title boardLabel}}. + if($eq activity.activityType 'uncompleteChecklist') + | {{{_ 'activity-checklist-uncompleted' activity.checklist.title cardLink}}}. - if($eq activityType 'removeList') - | {{_ 'activity-removed' title boardLabel}}. + if($eq activity.activityType 'checkedItem') + | {{{_ 'activity-checked-item' checkItem activity.checklist.title cardLink}}}. - if($eq activityType 'importBoard') - | {{{_ 'activity-imported-board' boardLabel sourceLink}}}. + if($eq activity.activityType 'uncheckedItem') + | {{{_ 'activity-unchecked-item' checkItem activity.checklist.title cardLink}}}. - if($eq activityType 'importCard') - | {{{_ 'activity-imported' cardLink boardLabel sourceLink}}}. + if($eq activity.activityType 'addChecklistItem') + | {{{_ 'activity-checklist-item-added' activity.checklist.title cardLink}}}. + .activity-checklist(href="{{ activity.card.absoluteUrl }}") + +viewer + = activity.checklistItem.title - if($eq activityType 'importList') - | {{{_ 'activity-imported' listLabel boardLabel sourceLink}}}. + if($eq activity.activityType 'removedChecklistItem') + | {{{_ 'activity-checklist-item-removed' activity.checklist.title cardLink}}}. - if($eq activityType 'joinMember') - if($eq user._id member._id) - | {{{_ 'activity-joined' cardLink}}}. + //- comment activity ---------------------------------------------------- + if($eq mode 'card') + //- if we are in card mode we display the comment in a way that it + //- can be edited by the owner + if($eq activity.activityType 'addComment') + +inlinedForm(classNames='js-edit-comment') + +editor(autofocus=true) + = activity.comment.text + .edit-controls + button.primary(type="submit") {{_ 'edit'}} else - | {{{_ 'activity-added' memberLink cardLink}}}. - - if($eq activityType 'moveCardBoard') - | {{{_ 'activity-moved' cardLink oldBoardName boardName}}}. - - if($eq activityType 'moveCard') - | {{{_ 'activity-moved' cardLink oldList.title list.title}}}. - - if($eq activityType 'removeBoardMember') - | {{{_ 'activity-excluded' memberLink boardLabel}}}. + .activity-comment + +viewer + = activity.comment.text + span(title=activity.createdAt).activity-meta {{ moment activity.createdAt }} + if ($eq currentUser._id activity.comment.userId) + = ' - ' + a.js-open-inlined-form {{_ "edit"}} + = ' - ' + a.js-delete-comment {{_ "delete"}} - if($eq activityType 'restoredCard') - | {{{_ 'activity-sent' cardLink boardLabel}}}. + if($eq activity.activityType 'deleteComment') + | {{{_ 'activity-deleteComment' currentData.commentId}}}. - if($eq activityType 'addedLabel') - | {{{_ 'activity-added-label' lastLabel cardLink}}}. + if($eq activity.activityType 'editComment') + | {{{_ 'activity-editComment' currentData.commentId}}}. + else + //- if we are not in card mode we only display a summary of the comment + if($eq activity.activityType 'addComment') + | {{{_ 'activity-on' cardLink}}} + a.activity-comment(href="{{ activity.card.absoluteUrl }}") + +viewer + = activity.comment.text - if($eq activityType 'removedLabel') - | {{{_ 'activity-removed-label' lastLabel cardLink}}}. + //- customField activity ------------------------------------------------ + if($eq mode 'board') + if($eq activity.activityType 'createCustomField') + | {{_ 'activity-customfield-created' customField}}. - if($eq activityType 'setCustomField') + if($eq activity.activityType 'setCustomField') | {{{_ 'activity-set-customfield' lastCustomField lastCustomFieldValue cardLink}}}. - if($eq activityType 'unsetCustomField') + if($eq activity.activityType 'unsetCustomField') | {{{_ 'activity-unset-customfield' lastCustomField cardLink}}}. - if($eq activityType 'unjoinMember') - if($eq user._id member._id) - | {{{_ 'activity-unjoined' cardLink}}}. - else - | {{{_ 'activity-removed' memberLink cardLink}}}. + //- label activity ------------------------------------------------------ + if($eq activity.activityType 'addedLabel') + | {{{_ 'activity-added-label' lastLabel cardLink}}}. - span(title=createdAt).activity-meta {{ moment createdAt }} + if($eq activity.activityType 'removedLabel') + | {{{_ 'activity-removed-label' lastLabel cardLink}}}. -template(name="cardActivities") - each currentCard.activities - .activity - +userAvatar(userId=user._id) - p.activity-desc - +memberName(user=user) - if($eq activityType 'createCard') - | {{_ 'activity-added' cardLabel listName}}. - if($eq activityType 'importCard') - | {{{_ 'activity-imported' cardLabel list.title sourceLink}}}. - if($eq activityType 'joinMember') - if($eq user._id member._id) - | {{_ 'activity-joined' cardLabel}}. - else - | {{{_ 'activity-added' memberLink cardLabel}}}. - if($eq activityType 'unjoinMember') - if($eq user._id member._id) - | {{_ 'activity-unjoined' cardLabel}}. - else - | {{{_ 'activity-removed' cardLabel memberLink}}}. - if($eq activityType 'archivedCard') - | {{_ 'activity-archived' cardLabel}}. - - if($eq activityType 'addedLabel') - | {{{_ 'activity-added-label-card' lastLabel }}}. + //- list activity ------------------------------------------------------- + if($neq mode 'card') + if($eq activity.activityType 'createList') + | {{{_ 'activity-added' listLabel boardLabel}}}. - if($eq activityType 'removedLabel') - | {{{_ 'activity-removed-label-card' lastLabel }}}. + if($eq activity.activityType 'importList') + | {{{_ 'activity-imported' listLabel boardLabel sourceLink}}}. - if($eq activityType 'removeChecklist') - | {{{_ 'activity-checklist-removed' cardLabel}}}. + if($eq activity.activityType 'removeList') + | {{{_ 'activity-removed' activity.title boardLabel}}}. - if($eq activityType 'checkedItem') - | {{{_ 'activity-checked-item-card' checkItem checklist.title }}}. + if($eq activity.activityType 'archivedList') + | {{_ 'activity-archived' listLabel}}. - if($eq activityType 'uncheckedItem') - | {{{_ 'activity-unchecked-item-card' checkItem checklist.title }}}. + //- member activity ---------------------------------------------------- + if($eq activity.activityType 'joinMember') + if($eq user._id activity.member._id) + | {{{_ 'activity-joined' cardLink}}}. + else + | {{{_ 'activity-added' memberLink cardLink}}}. - if($eq activityType 'checklistCompleted') - | {{{_ 'activity-checklist-completed-card' checklist.title }}}. + if($eq activity.activityType 'unjoinMember') + if($eq user._id activity.member._id) + | {{{_ 'activity-unjoined' cardLink}}}. + else + | {{{_ 'activity-removed' memberLink cardLink}}}. - if($eq activityType 'checklistUncompleted') - | {{{_ 'activity-checklist-uncompleted-card' checklist.title }}}. + //- swimlane activity -------------------------------------------------- + if($neq mode 'card') + if($eq activity.activityType 'createSwimlane') + | {{{_ 'activity-added' activity.swimlane.title boardLabel}}}. - if($eq activityType 'restoredCard') - | {{_ 'activity-sent' cardLabel boardLabel}}. - if($eq activityType 'moveCard') - | {{_ 'activity-moved' cardLabel oldList.title list.title}}. + if($eq activity.activityType 'archivedSwimlane') + | {{_ 'activity-archived' activity.swimlane.title}}. - if($eq activityType 'moveCardBoard') - | {{{_ 'activity-moved' cardLink oldBoardName boardName}}}. - if($eq activityType 'addAttachment') - | {{{_ 'activity-attached' attachmentLink cardLabel}}}. - if attachment.isImage - img.attachment-image-preview(src=attachment.url) - if($eq activityType 'deleteAttachment') - | {{{_ 'activity-delete-attach' cardLabel}}}. - if($eq activityType 'removedChecklist') - | {{{_ 'activity-checklist-removed' cardLabel}}}. - if($eq activityType 'addChecklist') - | {{{_ 'activity-checklist-added' cardLabel}}}. - .activity-checklist - +viewer - = checklist.title - if($eq activityType 'addChecklistItem') - | {{{_ 'activity-checklist-item-added' checklist.title cardLink}}}. - .activity-checklist(href="{{ card.absoluteUrl }}") - +viewer - = checklistItem.title - - if(currentData.timeKey) - | {{{_ activityType }}} + //- I don't understand this part ---------------------------------------- + if(currentData.timeKey) + | {{{_ activity.activityType }}} + = ' ' + i(title=currentData.timeValue).activity-meta {{ moment currentData.timeValue 'LLL' }} + if (currentData.timeOldValue) = ' ' - i(title=currentData.timeValue).activity-meta {{ moment currentData.timeValue 'LLL' }} - if (currentData.timeOldValue) - = ' ' - | {{{_ "previous_as" }}} - = ' ' - i(title=currentData.timeOldValue).activity-meta {{ moment currentData.timeOldValue 'LLL' }} - = ' @' - else if(currentData.timeValue) - | {{{_ activityType currentData.timeValue}}} - - - if($eq activityType 'deleteComment') - | {{{_ 'activity-deleteComment' currentData.commentId}}}. - if($eq activityType 'editComment') - | {{{_ 'activity-editComment' currentData.commentId}}}. - if($eq activityType 'addComment') - +inlinedForm(classNames='js-edit-comment') - +editor(autofocus=true) - = comment.text - .edit-controls - button.primary(type="submit") {{_ 'edit'}} - else - .activity-comment - +viewer - = comment.text - span(title=createdAt).activity-meta {{ moment createdAt }} - if ($eq currentUser._id comment.userId) - = ' - ' - a.js-open-inlined-form {{_ "edit"}} - = ' - ' - a.js-delete-comment {{_ "delete"}} - - else - span(title=createdAt).activity-meta {{ moment createdAt }} + | {{{_ "previous_as" }}} + = ' ' + i(title=currentData.timeOldValue).activity-meta {{ moment currentData.timeOldValue 'LLL' }} + = ' @' + else if(currentData.timeValue) + | {{{_ activity.activityType currentData.timeValue}}} + + span(title=activity.createdAt).activity-meta {{ moment activity.createdAt }} diff --git a/client/components/activities/activities.js b/client/components/activities/activities.js index b082273a..36214e19 100644 --- a/client/components/activities/activities.js +++ b/client/components/activities/activities.js @@ -41,7 +41,9 @@ BlazeComponent.extendComponent({ }); }); }, +}).register('activities'); +BlazeComponent.extendComponent({ loadNextPage() { if (this.loadNextPageLocked === false) { this.page.set(this.page.get() + 1); @@ -50,41 +52,37 @@ BlazeComponent.extendComponent({ }, checkItem() { - const checkItemId = this.currentData().checklistItemId; + const checkItemId = this.currentData().activity.checklistItemId; const checkItem = ChecklistItems.findOne({ _id: checkItemId }); - return checkItem.title; + return checkItem && checkItem.title; }, boardLabel() { + const data = this.currentData(); + if (data.mode !== 'board') { + return createBoardLink(data.activity.board(), data.activity.listName); + } return TAPi18n.__('this-board'); }, cardLabel() { + const data = this.currentData(); + if (data.mode !== 'card') { + return createCardLink(this.currentData().activity.card()); + } return TAPi18n.__('this-card'); }, cardLink() { - const card = this.currentData().card(); - return ( - card && - Blaze.toHTML( - HTML.A( - { - href: card.absoluteUrl(), - class: 'action-card', - }, - card.title, - ), - ) - ); + return createCardLink(this.currentData().activity.card()); }, lastLabel() { - const lastLabelId = this.currentData().labelId; + const lastLabelId = this.currentData().activity.labelId; if (!lastLabelId) return null; - const lastLabel = Boards.findOne(Session.get('currentBoard')).getLabelById( - lastLabelId, - ); + const lastLabel = Boards.findOne( + this.currentData().activity.boardId, + ).getLabelById(lastLabelId); if (lastLabel && (lastLabel.name === undefined || lastLabel.name === '')) { return lastLabel.color; } else { @@ -94,7 +92,7 @@ BlazeComponent.extendComponent({ lastCustomField() { const lastCustomField = CustomFields.findOne( - this.currentData().customFieldId, + this.currentData().activity.customFieldId, ); if (!lastCustomField) return null; return lastCustomField.name; @@ -102,10 +100,10 @@ BlazeComponent.extendComponent({ lastCustomFieldValue() { const lastCustomField = CustomFields.findOne( - this.currentData().customFieldId, + this.currentData().activity.customFieldId, ); if (!lastCustomField) return null; - const value = this.currentData().value; + const value = this.currentData().activity.value; if ( lastCustomField.settings.dropdownItems && lastCustomField.settings.dropdownItems.length > 0 @@ -122,11 +120,13 @@ BlazeComponent.extendComponent({ }, listLabel() { - return this.currentData().list().title; + const activity = this.currentData().activity; + const list = activity.list(); + return (list && list.title) || activity.title; }, sourceLink() { - const source = this.currentData().source; + const source = this.currentData().activity.source; if (source) { if (source.url) { return Blaze.toHTML( @@ -146,30 +146,31 @@ BlazeComponent.extendComponent({ memberLink() { return Blaze.toHTMLWithData(Template.memberName, { - user: this.currentData().member(), + user: this.currentData().activity.member(), }); }, attachmentLink() { - const attachment = this.currentData().attachment(); + const attachment = this.currentData().activity.attachment(); // trying to display url before file is stored generates js errors return ( - attachment && - attachment.url({ download: true }) && - Blaze.toHTML( - HTML.A( - { - href: attachment.url({ download: true }), - target: '_blank', - }, - attachment.name(), - ), - ) + (attachment && + attachment.url({ download: true }) && + Blaze.toHTML( + HTML.A( + { + href: attachment.url({ download: true }), + target: '_blank', + }, + attachment.name(), + ), + )) || + this.currentData().activity.attachmentName ); }, customField() { - const customField = this.currentData().customField(); + const customField = this.currentData().activity.customField(); if (!customField) return null; return customField.name; }, @@ -199,4 +200,36 @@ BlazeComponent.extendComponent({ }, ]; }, -}).register('activities'); +}).register('activity'); + +function createCardLink(card) { + return ( + card && + Blaze.toHTML( + HTML.A( + { + href: card.absoluteUrl(), + class: 'action-card', + }, + card.title, + ), + ) + ); +} + +function createBoardLink(board, list) { + let text = board.title; + if (list) text += `: ${list}`; + return ( + board && + Blaze.toHTML( + HTML.A( + { + href: board.absoluteUrl(), + class: 'action-board', + }, + text, + ), + ) + ); +} diff --git a/client/components/activities/comments.js b/client/components/activities/comments.js index e885459e..50ca019b 100644 --- a/client/components/activities/comments.js +++ b/client/components/activities/comments.js @@ -33,13 +33,6 @@ BlazeComponent.extendComponent({ cardId, }); resetCommentInput(input); - // With Richer editor is in use, and comment is submitted, - // clear comment form with JQuery. Id #summernote is defined - // at client/components/main/editor.jade where it previously was - // id=id, now it is id="summernote". - if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR === 'true') { - $('#summernote').summernote('code', ''); - } Tracker.flush(); autosize.update(input); input.trigger('submitted'); diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade index 53a74f76..4c0edac4 100644 --- a/client/components/boards/boardHeader.jade +++ b/client/components/boards/boardHeader.jade @@ -193,20 +193,6 @@ template(name="boardChangeViewPopup") | {{_ 'board-view-cal'}} if $eq Utils.boardView "board-view-cal" i.fa.fa-check - if currentUser.isAdmin - hr - li - with "board-view-rules" - a.js-open-rules-view(title="{{_ 'rules'}}") - i.fa.fa-magic - | {{_ 'rules'}} - else if currentUser.isBoardAdmin - hr - li - with "board-view-rules" - a.js-open-rules-view(title="{{_ 'rules'}}") - i.fa.fa-magic - | {{_ 'rules'}} template(name="createBoard") form diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js index 9040ed83..dc553134 100644 --- a/client/components/boards/boardHeader.js +++ b/client/components/boards/boardHeader.js @@ -191,10 +191,6 @@ Template.boardChangeViewPopup.events({ Utils.setBoardView('board-view-cal'); Popup.close(); }, - 'click .js-open-rules-view'() { - Modal.openWide('rulesMain'); - Popup.close(); - }, }); const CreateBoard = BlazeComponent.extendComponent({ diff --git a/client/components/cards/attachments.jade b/client/components/cards/attachments.jade index b695ea41..61454fa7 100644 --- a/client/components/cards/attachments.jade +++ b/client/components/cards/attachments.jade @@ -55,5 +55,5 @@ template(name="attachmentsGalery") unless currentUser.isWorker //li.attachment-item.add-attachment a.js-add-attachment - i.fa.fa-paperclip + i.fa.fa-plus | {{_ 'add-attachment' }} diff --git a/client/components/cards/cardDetails.jade b/client/components/cards/cardDetails.jade index 615ae1d5..257ca0a8 100644 --- a/client/components/cards/cardDetails.jade +++ b/client/components/cards/cardDetails.jade @@ -203,6 +203,7 @@ template(name="cardDetails") if canModifyCard unless currentUser.isWorker if currentBoard.allowsDescriptionTitle + hr h3 i.fa.fa-align-left card-details-item-title {{_ 'description'}} @@ -229,6 +230,7 @@ template(name="cardDetails") a.js-close-inlined-form {{_ 'discard'}} else if getDescription if currentBoard.allowsDescriptionTitle + hr h3.card-details-item-title {{_ 'description'}} if currentBoard.allowsDescriptionText +viewer @@ -237,15 +239,16 @@ template(name="cardDetails") .card-checklist-attachmentGalerys .card-checklist-attachmentGalery.card-checklists if currentBoard.allowsChecklists + hr +checklists(cardId = _id) if currentBoard.allowsSubtasks hr +subtasks(cardId = _id) if currentBoard.allowsAttachments - //- hr - //- h3 - //- i.fa.fa-paperclip - //- | {{_ 'attachments'}} + hr + h3 + i.fa.fa-paperclip + | {{_ 'attachments'}} .card-checklist-attachmentGalery.card-attachmentGalery +attachmentsGalery diff --git a/client/components/cards/minicard.styl b/client/components/cards/minicard.styl index 8607e118..7d72a588 100644 --- a/client/components/cards/minicard.styl +++ b/client/components/cards/minicard.styl @@ -79,7 +79,7 @@ border-radius: top 2px .minicard-labels - float: right + float: none display: flex flex-wrap: wrap diff --git a/client/components/main/editor.jade b/client/components/main/editor.jade index 5c5454ee..dbd61715 100644 --- a/client/components/main/editor.jade +++ b/client/components/main/editor.jade @@ -1,13 +1,8 @@ template(name="editor") - // With Richer editor is in use, and comment is submitted, - // clear comment form with JQuery Comment at - // client/components/activities/comments.js . Id #summernote is defined - // here at client/components/main/editor.jade where it previously was - // id=id, now it is id="summernote". textarea.editor( dir="auto" class="{{class}}" - id="summernote" + id=id autofocus=autofocus placeholder="{{_ 'comment-placeholder'}}") +Template.contentBlock diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 3f09d284..081c6521 100755 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -30,7 +30,7 @@ Template.editor.onRendered(() => { autosize($textarea); $textarea.escapeableTextComplete(mentions); }; - if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR === 'true') { + if (Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR !== false) { const isSmall = Utils.isMiniScreen(); const toolbar = isSmall ? [ @@ -108,45 +108,17 @@ Template.editor.onRendered(() => { } return undefined; }; - // Prevent @member mentions on Add Comment input field - // from closing card, part 1. - let popupShown = false; inputs.each(function(idx, input) { mSummernotes[idx] = $(input).summernote({ placeholder, - // Prevent @member mentions on Add Comment input field - // from closing card, part 2. - onKeydown(e) { - if (popupShown) { - e.preventDefault(); - } - }, - onKeyup(e) { - if (popupShown) { - e.preventDefault(); - } - }, callbacks: { - // Prevent @member mentions on Add Comment input field - // from closing card, part 3. - onKeydown(e) { - if (popupShown) { - e.preventDefault(); - } - }, - onKeyup(e) { - if (popupShown) { - e.preventDefault(); - } - }, onInit(object) { const originalInput = this; - $(originalInput).on('input', function() { + $(originalInput).on('submitted', function() { // when comment is submitted, the original textarea will be set to '', so shall we if (!this.value) { const sn = getSummernote(this); - sn && sn.summernote('reset'); - object && object.editingArea.find('.note-placeholder').show(); + sn && sn.summernote('code', ''); } }); const jEditor = object && object.editable; @@ -163,6 +135,7 @@ Template.editor.onRendered(() => { }); } }, + onImageUpload(files) { const $summernote = getSummernote(this); if (files && files.length > 0) { @@ -249,7 +222,7 @@ Template.editor.onRendered(() => { // == Fix End == const original = someNote.summernote('code'); const cleaned = cleanPastedHTML(original); //this is where to call whatever clean function you want. I have mine in a different file, called CleanPastedHTML. - someNote.summernote('reset'); //clear original + someNote.summernote('code', ''); //clear original someNote.summernote('pasteHTML', cleaned); //this sets the displayed content editor to the cleaned pasted code. }; setTimeout(function() { @@ -316,15 +289,15 @@ Blaze.Template.registerHelper( let currentMention; while ((currentMention = mentionRegex.exec(content)) !== null) { - const [fullMention, username] = currentMention; + const [fullMention, quoteduser, simple] = currentMention; + const username = quoteduser || simple; const knowedUser = _.findWhere(knowedUsers, { username }); if (!knowedUser) { continue; } const linkValue = [' ', at, knowedUser.username]; - //let linkClass = 'atMention js-open-member'; - let linkClass = 'atMention'; + let linkClass = 'atMention js-open-member'; if (knowedUser.userId === Meteor.userId()) { linkClass += ' me'; } @@ -357,25 +330,24 @@ Template.viewer.events({ // the corresponding text). Clicking a link shouldn't fire these actions, stop // we stop these event at the viewer component level. 'click a'(event, templateInstance) { - event.stopPropagation(); - - // XXX We hijack the build-in browser action because we currently don't have - // `_blank` attributes in viewer links, and the transformer function is - // handled by a third party package that we can't configure easily. Fix that - // by using directly `_blank` attribute in the rendered HTML. - event.preventDefault(); - + let prevent = true; const userId = event.currentTarget.dataset.userid; if (userId) { - // Prevent @member mentions on Add Comment input field - // from closing card, part 4. - PopupNoClose.open('member').call({ userId }, event, templateInstance); - event.preventDefault(); + Popup.open('member').call({ userId }, event, templateInstance); } else { const href = event.currentTarget.href; if (href) { window.open(href, '_blank'); } } + if (prevent) { + event.stopPropagation(); + + // XXX We hijack the build-in browser action because we currently don't have + // `_blank` attributes in viewer links, and the transformer function is + // handled by a third party package that we can't configure easily. Fix that + // by using directly `_blank` attribute in the rendered HTML. + event.preventDefault(); + } }, }); diff --git a/client/components/main/header.jade b/client/components/main/header.jade index 75e84c0c..9a5a6b9b 100644 --- a/client/components/main/header.jade +++ b/client/components/main/header.jade @@ -35,6 +35,8 @@ template(name="header") a#header-new-board-icon.js-create-board i.fa.fa-plus(title="Create a new board") + +notifications + +headerUserBar #header(class=currentBoard.colorClass) diff --git a/client/components/main/header.styl b/client/components/main/header.styl index f77c2aca..632d1535 100644 --- a/client/components/main/header.styl +++ b/client/components/main/header.styl @@ -99,7 +99,7 @@ height: 28px font-size: 12px display: flex - z-index: 17 + z-index: 21 #header-user-bar, #header-new-board-icon, diff --git a/client/components/notifications/notification.jade b/client/components/notifications/notification.jade new file mode 100644 index 00000000..c98bbdba --- /dev/null +++ b/client/components/notifications/notification.jade @@ -0,0 +1,10 @@ +template(name='notification') + li.notification(class="{{#if read}}read{{/if}}") + .read-status + .materialCheckBox(class="{{#if read}}is-checked{{/if}}") + +notificationIcon(activityData) + .details + +activity(activity=activityData mode='none') + if read + .remove + a.fa.fa-trash diff --git a/client/components/notifications/notification.js b/client/components/notifications/notification.js new file mode 100644 index 00000000..89277520 --- /dev/null +++ b/client/components/notifications/notification.js @@ -0,0 +1,28 @@ +Template.notification.events({ + 'click .read-status .materialCheckBox'() { + const update = {}; + update[`profile.notifications.${this.index}.read`] = this.read + ? null + : Date.now(); + Users.update(Meteor.userId(), { $set: update }); + }, + 'click .remove a'() { + Meteor.user().removeNotification(this.activityData._id); + }, +}); + +Template.notification.helpers({ + mode: 'board', + isOfActivityType(activityId, type) { + const activity = Activities.findOne(activityId); + return activity && activity.activityType === type; + }, + activityType(activityId) { + const activity = Activities.findOne(activityId); + return activity ? activity.activityType : ''; + }, + activityUser(activityId) { + const activity = Activities.findOne(activityId); + return activity && activity.userId; + }, +}); diff --git a/client/components/notifications/notification.styl b/client/components/notifications/notification.styl new file mode 100644 index 00000000..0cf0cfd5 --- /dev/null +++ b/client/components/notifications/notification.styl @@ -0,0 +1,57 @@ +#notifications-drawer + &.show-read .notification.read + display: flex + + .notification + display: flex + float: none + padding: 12px 8px 8px + color: black + border-bottom: 1px solid #dbdbdb + + &.read + display: none + + .read-status + width: 30px + + input + width: 24px + height: 24px + + .activity-type + margin: 16px 0 0 + width: 17px + height: 17px + font-size: 17px + display: block + color: #bbb + + .details + width: calc(100% - 30px) + + .activity + display: flex + + .activity-desc + width: 100%; + + .activity-comment + display: block + width: 100% + border-radius: 3px + background: #fff + text-decoration: none + box-shadow: 0 1px 2px rgba(0,0,0,0.2) + margin-top: 5px + padding: 5px + + .activity-meta + display: block + font-size: 0.8em + color: #999 + font-style: italic + + .remove + a:hover + color #eb4646 !important diff --git a/client/components/notifications/notificationIcon.jade b/client/components/notifications/notificationIcon.jade new file mode 100644 index 00000000..04377606 --- /dev/null +++ b/client/components/notifications/notificationIcon.jade @@ -0,0 +1,53 @@ +template(name='notificationIcon') + if($in activityType 'deleteAttachment' 'addAttachment') + i.fa.fa-paperclip.activity-type(title="attachment") + else if($in activityType 'createBoard' 'importBoard') + i.fa.fa-chalkboard.activity-type(title="board") + + else if($in activityType 'createCard' 'importCard' 'moveCard') + +cardNotificationIcon + else if($in activityType 'moveCardBoard' 'archivedCard' 'restoredCard') + +cardNotificationIcon + //- $in can only handle up to 3 cases so we have to break this case over 2 cases... use a simple template to keep it + //- DRY and consistant + + else if($in activityType 'addChecklist' 'removedChecklist' 'completeChecklist') + +checklistNotificationIcon + else if($in activityType 'uncompleteChecklist') + +checklistNotificationIcon + //- $in can only handle up to 3 cases so we have to break this case over 2 cases... use a simple template to keep it + //- DRY and consistant + + else if($in activityType 'checkedItem' 'uncheckedItem' 'addChecklistItem' 'removedChecklistItem') + i.fa.fa-check-square.activity-type(title="checklist item") + else if($in activityType 'addComment') + i.fa.fa-comment-o.activity-type(title="comment") + else if($in activityType 'createCustomField' 'setCustomField' 'unsetCustomField') + i.fa.fa-code.activity-type(title="custom field") + else if($in activityType 'addedLabel' 'removedLabel') + i.fa.fa-tag.activity-type(title="label") + + else if($in activityType 'createList' 'removeList' 'archivedList') + +listNotificationIcon + else if($in activityType 'importList') + +listNotificationIcon + //- $in can only handle up to 3 cases so we have to break this case over 2 cases... use a simple template to keep it + //- DRY and consistant + + //- elswhere in the app we use fa-trello to indicate lists... + //- i personally like fa-columns a bit better + else if($in activityType 'unjoinMember' 'addBoardMember' 'joinMember' 'removeBoardMember') + i.fa.fa-user.activity-type(title="member") + else if($in activityType 'createSwimlane' 'archivedSwimlane') + i.fa.fa-th-large.activity-type(title="swimlane") + else + i.fa.fa-bug.activity-type(title="can't find icon for #{activityType}") + +template(name='cardNotificationIcon') + i.fa.fa-clone.activity-type(title="card") + +template(name='checklistNotificationIcon') + i.fa.fa-list.activity-type(title="checklist") + +template(name='listNotificationIcon') + i.fa.fa-columns.activity-type(title="list") diff --git a/client/components/notifications/notifications.jade b/client/components/notifications/notifications.jade new file mode 100644 index 00000000..bf8acbbf --- /dev/null +++ b/client/components/notifications/notifications.jade @@ -0,0 +1,5 @@ +template(name='notifications') + #notifications.board-header-btns.right + a.notifications-drawer-toggle.fa.fa-bell(class="{{#if $gt unreadNotifications 0}}alert{{/if}}") + if $.Session.get 'showNotificationsDrawer' + +notificationsDrawer(unreadNotifications=unreadNotifications) diff --git a/client/components/notifications/notifications.js b/client/components/notifications/notifications.js new file mode 100644 index 00000000..c0aa6cb5 --- /dev/null +++ b/client/components/notifications/notifications.js @@ -0,0 +1,32 @@ +// this hides the notifications drawer if anyone clicks off of the panel +Template.body.events({ + click(event) { + if ( + !$(event.target).is('#notifications *') && + Session.get('showNotificationsDrawer') + ) { + toggleNotificationsDrawer(); + } + }, +}); + +Template.notifications.helpers({ + unreadNotifications() { + const notifications = Users.findOne(Meteor.userId()).notifications(); + const unreadNotifications = _.filter(notifications, v => !v.read); + return unreadNotifications.length; + }, +}); + +Template.notifications.events({ + 'click .notifications-drawer-toggle'() { + toggleNotificationsDrawer(); + }, +}); + +export function toggleNotificationsDrawer() { + Session.set( + 'showNotificationsDrawer', + !Session.get('showNotificationsDrawer'), + ); +} diff --git a/client/components/notifications/notifications.styl b/client/components/notifications/notifications.styl new file mode 100644 index 00000000..710cd3f9 --- /dev/null +++ b/client/components/notifications/notifications.styl @@ -0,0 +1,17 @@ +#notifications + position: relative + + .notifications-drawer-toggle + display: block + line-height: 28px + color: #f2f2f2 + margin: 0 10px + width: 28px + height: 28px + text-align: center + border: 0 + padding: 0 + + &.alert + background-color: #eb4646; + diff --git a/client/components/notifications/notificationsDrawer.jade b/client/components/notifications/notificationsDrawer.jade new file mode 100644 index 00000000..01117009 --- /dev/null +++ b/client/components/notifications/notificationsDrawer.jade @@ -0,0 +1,16 @@ +template(name='notificationsDrawer') + section#notifications-drawer(class="{{#if $.Session.get 'showReadNotifications'}}show-read{{/if}}") + .header + if $.Session.get 'showReadNotifications' + a.toggle-read {{_ 'filter-by-unread'}} + else + a.toggle-read {{_ 'view-all'}} + h5 {{_ 'notifications'}} + if($gt unreadNotifications 0) + |(#{unreadNotifications}) + a.fa.fa-times-thin.close + ul.notifications + each transformedProfile.notifications + +notification(activityData=activity index=dbIndex read=read) + if($gt unreadNotifications 0) + a.all-read {{_ 'mark-all-as-read'}} diff --git a/client/components/notifications/notificationsDrawer.js b/client/components/notifications/notificationsDrawer.js new file mode 100644 index 00000000..98d4750d --- /dev/null +++ b/client/components/notifications/notificationsDrawer.js @@ -0,0 +1,38 @@ +import { toggleNotificationsDrawer } from './notifications.js'; + +Template.notificationsDrawer.onCreated(function() { + Meteor.subscribe('notificationActivities'); + Meteor.subscribe('notificationCards'); + Meteor.subscribe('notificationUsers'); + Meteor.subscribe('notificationsAttachments'); + Meteor.subscribe('notificationChecklistItems'); + Meteor.subscribe('notificationChecklists'); + Meteor.subscribe('notificationComments'); + Meteor.subscribe('notificationLists'); + Meteor.subscribe('notificationSwimlanes'); +}); + +Template.notificationsDrawer.helpers({ + transformedProfile() { + return Users.findOne(Meteor.userId()); + }, +}); + +Template.notificationsDrawer.events({ + 'click .all-read'() { + const notifications = Meteor.user().profile.notifications; + for (const index in notifications) { + if (notifications.hasOwnProperty(index) && !notifications[index].read) { + const update = {}; + update[`profile.notifications.${index}.read`] = Date.now(); + Users.update(Meteor.userId(), { $set: update }); + } + } + }, + 'click .close'() { + toggleNotificationsDrawer(); + }, + 'click .toggle-read'() { + Session.set('showReadNotifications', !Session.get('showReadNotifications')); + }, +}); diff --git a/client/components/notifications/notificationsDrawer.styl b/client/components/notifications/notificationsDrawer.styl new file mode 100644 index 00000000..a26b5e4a --- /dev/null +++ b/client/components/notifications/notificationsDrawer.styl @@ -0,0 +1,57 @@ +belize = #2980b9 + +section#notifications-drawer + position: fixed + top: 28px + right: 0 + width: 400px + background-color: #fafafa + box-shadow: 0 1px 2px rgba(0,0,0,0.15) + border-radius: 2px + max-height: calc(100vh - 28px - 36px) + color: black + padding-top 36px; + overflow: scroll + + a:hover + color: belize !important + + .header + position: fixed + top 28px + right 0 + width calc(400px - 32px) + padding: 8px 16px + background: #ededed + border-bottom: 1px solid #dbdbdb + z-index 2 + + .toggle-read + position absolute + left 16px + top calc(50% - 8px) + color belize + + h5 + text-align: center + margin: 0 + + .close + position: absolute + top: calc(50% - 12px) + right: 12px + font-size: 24px + height: 24px + line-height: 24px + opacity 1 + + .all-read + color belize + background-color: #fafafa + margin 8px 16px 12px + display inline-block + + ul.notifications + display: block + padding: 0px 16px + margin: 0 diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index f0b0e4be..901fe99f 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -268,6 +268,10 @@ template(name="outgoingWebhooksPopup") template(name="boardMenuPopup") ul.pop-over-list li + a.js-open-rules-view(title="{{_ 'rules'}}") + i.fa.fa-magic + | {{_ 'rules'}} + li a.js-custom-fields i.fa.fa-list-alt | {{_ 'custom-fields'}} diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js index 8e640564..baf57114 100644 --- a/client/components/sidebar/sidebar.js +++ b/client/components/sidebar/sidebar.js @@ -182,6 +182,10 @@ Template.memberPopup.helpers({ Template.boardMenuPopup.events({ 'click .js-rename-board': Popup.open('boardChangeTitle'), + 'click .js-open-rules-view'() { + Modal.openWide('rulesMain'); + Popup.close(); + }, 'click .js-custom-fields'() { Sidebar.setView('customFields'); Popup.close(); |