diff options
author | Romulus Urakagi Tsai <urakagi@gmail.com> | 2020-02-13 09:02:26 +0000 |
---|---|---|
committer | Romulus Urakagi Tsai <urakagi@gmail.com> | 2020-02-13 09:02:26 +0000 |
commit | 4b196d537896f39fb76090020cb5851a699546eb (patch) | |
tree | 28e2e025ce90645ed360bb8c26ab39e6f40214e4 /client | |
parent | b34ed58289a3dae5838d3b621260938a3ecf52d5 (diff) | |
parent | 3fcde252f705f9527f7190517082a047714a4eec (diff) | |
download | wekan-4b196d537896f39fb76090020cb5851a699546eb.tar.gz wekan-4b196d537896f39fb76090020cb5851a699546eb.tar.bz2 wekan-4b196d537896f39fb76090020cb5851a699546eb.zip |
Merge branch 'master' of https://github.com/wekan/wekan into lib-change
Diffstat (limited to 'client')
47 files changed, 1979 insertions, 561 deletions
diff --git a/client/components/activities/activities.jade b/client/components/activities/activities.jade index deb73072..8ecbdee8 100644 --- a/client/components/activities/activities.jade +++ b/client/components/activities/activities.jade @@ -201,20 +201,20 @@ template(name="cardActivities") .activity-checklist(href="{{ card.absoluteUrl }}") +viewer = checklistItem.title - + if(currentData.timeKey) | {{{_ activityType }}} = ' ' 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}}}. diff --git a/client/components/activities/activities.styl b/client/components/activities/activities.styl index 380e7b40..f3b1acdd 100644 --- a/client/components/activities/activities.styl +++ b/client/components/activities/activities.styl @@ -9,7 +9,7 @@ clear: both .activity - margin: 10px 0 + margin: 0.5px 0 display: flex .member diff --git a/client/components/activities/comments.js b/client/components/activities/comments.js index 95084646..50ca019b 100644 --- a/client/components/activities/comments.js +++ b/client/components/activities/comments.js @@ -16,9 +16,6 @@ BlazeComponent.extendComponent({ events() { return [ { - 'click .js-new-comment:not(.focus)'() { - commentFormIsOpen.set(true); - }, 'submit .js-new-comment-form'(evt) { const input = this.getInput(); const text = input.val().trim(); diff --git a/client/components/activities/comments.styl b/client/components/activities/comments.styl index 22f9c482..ccf24b72 100644 --- a/client/components/activities/comments.styl +++ b/client/components/activities/comments.styl @@ -46,3 +46,23 @@ &:is-open cursor: auto + +.comment-item + background-color: #fff + border: 0 + box-shadow: 0 1px 2px rgba(0, 0, 0, .23) + color: #8c8c8c + height: 36px + margin: 4px 4px 6px 0 + width: 92% + + &:hover + background: darken(white, 12%) + + &.add-comment + display: flex + margin: 5px + + a + display: block + margin: auto diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index 47042ae7..e70a9f67 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -1,3 +1,5 @@ +import { Cookies } from 'meteor/ostrio:cookies'; +const cookies = new Cookies(); const subManager = new SubsManager(); const { calculateIndex, enableClickOnTouch } = Utils; const swimlaneWhileSortingHeight = 150; @@ -89,7 +91,6 @@ BlazeComponent.extendComponent({ helper.append(list.clone()); return helper; }, - handle: '.js-swimlane-header-handle', items: '.swimlane:not(.placeholder)', placeholder: 'swimlane placeholder', distance: 7, @@ -193,6 +194,32 @@ BlazeComponent.extendComponent({ // ugly touch event hotfix enableClickOnTouch('.js-swimlane:not(.placeholder)'); + this.autorun(() => { + let showDesktopDragHandles = false; + currentUser = Meteor.user(); + if (currentUser) { + showDesktopDragHandles = (currentUser.profile || {}) + .showDesktopDragHandles; + } else if (cookies.has('showDesktopDragHandles')) { + showDesktopDragHandles = true; + } else { + showDesktopDragHandles = false; + } + if (!Utils.isMiniScreen() && showDesktopDragHandles) { + $swimlanesDom.sortable({ + handle: '.js-swimlane-header-handle', + }); + } else if (!Utils.isMiniScreen() && !showDesktopDragHandles) { + $swimlanesDom.sortable({ + handle: '.swimlane-header', + }); + } + + // Disable drag-dropping if the current user is not a board member or is miniscreen + $swimlanesDom.sortable('option', 'disabled', !userIsMember()); + $swimlanesDom.sortable('option', 'disabled', Utils.isMiniScreen()); + }); + function userIsMember() { return ( Meteor.user() && @@ -210,21 +237,30 @@ BlazeComponent.extendComponent({ }, isViewSwimlanes() { - const currentUser = Meteor.user(); - if (!currentUser) return false; - return (currentUser.profile || {}).boardView === 'board-view-swimlanes'; + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).boardView === 'board-view-swimlanes'; + } else { + return cookies.get('boardView') === 'board-view-swimlanes'; + } }, isViewLists() { - const currentUser = Meteor.user(); - if (!currentUser) return true; - return (currentUser.profile || {}).boardView === 'board-view-lists'; + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).boardView === 'board-view-lists'; + } else { + return cookies.get('boardView') === 'board-view-lists'; + } }, isViewCalendar() { - const currentUser = Meteor.user(); - if (!currentUser) return false; - return (currentUser.profile || {}).boardView === 'board-view-cal'; + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).boardView === 'board-view-cal'; + } else { + return cookies.get('boardView') === 'board-view-cal'; + } }, openNewListForm() { @@ -381,8 +417,11 @@ BlazeComponent.extendComponent({ }; }, isViewCalendar() { - const currentUser = Meteor.user(); - if (!currentUser) return false; - return (currentUser.profile || {}).boardView === 'board-view-cal'; + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).boardView === 'board-view-cal'; + } else { + return cookies.get('boardView') === 'board-view-cal'; + } }, }).register('calendarView'); diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade index 175cc2c2..53a74f76 100644 --- a/client/components/boards/boardHeader.jade +++ b/client/components/boards/boardHeader.jade @@ -77,10 +77,11 @@ template(name="boardHeaderBar") i.fa.fa-archive span {{_ 'archives'}} - if showSort - a.board-header-btn.js-open-sort-view(title="{{_ 'sort-desc'}}") - i.fa(class="{{directionClass}}") - span {{_ 'sort'}}{{_ listSortShortDesc}} + //if showSort + // a.board-header-btn.js-open-sort-view(title="{{_ 'sort-desc'}}") + // i.fa(class="{{directionClass}}") + // span {{_ 'sort'}}{{_ listSortShortDesc}} + a.board-header-btn.js-open-filter-view( title="{{#if Filter.isActive}}{{_ 'filter-on-desc'}}{{else}}{{_ 'filter'}}{{/if}}" class="{{#if Filter.isActive}}emphasis{{/if}}") @@ -89,15 +90,6 @@ template(name="boardHeaderBar") if Filter.isActive a.board-header-btn-close.js-filter-reset(title="{{_ 'filter-clear'}}") i.fa.fa-times-thin - - if currentUser.isAdmin - a.board-header-btn.js-open-rules-view(title="{{_ 'rules'}}") - i.fa.fa-magic - span {{_ 'rules'}} - else if currentUser.isBoardAdmin - a.board-header-btn.js-open-rules-view(title="{{_ 'rules'}}") - i.fa.fa-magic - span {{_ 'rules'}} a.board-header-btn.js-open-search-view(title="{{_ 'search'}}") i.fa.fa-search @@ -106,8 +98,14 @@ template(name="boardHeaderBar") unless currentBoard.isTemplatesBoard a.board-header-btn.js-toggle-board-view( title="{{_ 'board-view'}}") - i.fa.fa-th-large - span {{#if currentUser.profile.boardView}}{{_ currentUser.profile.boardView}}{{else}}{{_ 'board-view-lists'}}{{/if}} + i.fa.fa-caret-down + if $eq boardView 'board-view-lists' + i.fa.fa-trello + if $eq boardView 'board-view-swimlanes' + i.fa.fa-th-large + if $eq boardView 'board-view-cal' + i.fa.fa-calendar + span {{#if boardView}}{{_ boardView}}{{else}}{{_ 'board-view-lists'}}{{/if}} if canModifyBoard a.board-header-btn.js-multiselection-activate( @@ -172,6 +170,44 @@ template(name="boardChangeWatchPopup") i.fa.fa-check span.sub-name {{_ 'muted-info'}} +template(name="boardChangeViewPopup") + ul.pop-over-list + li + with "board-view-lists" + a.js-open-lists-view + i.fa.fa-trello.colorful + | {{_ 'board-view-lists'}} + if $eq Utils.boardView "board-view-lists" + i.fa.fa-check + li + with "board-view-swimlanes" + a.js-open-swimlanes-view + i.fa.fa-th-large.colorful + | {{_ 'board-view-swimlanes'}} + if $eq Utils.boardView "board-view-swimlanes" + i.fa.fa-check + li + with "board-view-cal" + a.js-open-cal-view + i.fa.fa-calendar.colorful + | {{_ '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 label @@ -198,19 +234,19 @@ template(name="createBoard") | / a.js-board-template {{_ 'template'}} -template(name="listsortPopup") - h2 - | {{_ 'list-sort-by'}} - hr - ul.pop-over-list - each value in allowedSortValues - li - a.js-sort-by(name="{{value.name}}") - if $eq sortby value.name - i(class="fa {{Direction}}") - | {{_ value.label }}{{_ value.shortLabel}} - if $eq sortby value.name - i(class="fa fa-check") +//template(name="listsortPopup") +// h2 +// | {{_ 'list-sort-by'}} +// hr +// ul.pop-over-list +// each value in allowedSortValues +// li +// a.js-sort-by(name="{{value.name}}") +// if $eq sortby value.name +// i(class="fa {{Direction}}") +// | {{_ value.label }}{{_ value.shortLabel}} +// if $eq sortby value.name +// i(class="fa fa-check") template(name="boardChangeTitlePopup") form diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js index e14b1444..9040ed83 100644 --- a/client/components/boards/boardHeader.js +++ b/client/components/boards/boardHeader.js @@ -1,5 +1,7 @@ +/* const DOWNCLS = 'fa-sort-down'; const UPCLS = 'fa-sort-up'; +*/ Template.boardMenuPopup.events({ 'click .js-rename-board': Popup.open('boardChangeTitle'), 'click .js-custom-fields'() { @@ -28,6 +30,7 @@ Template.boardMenuPopup.events({ 'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'), 'click .js-import-board': Popup.open('chooseBoardSource'), 'click .js-subtask-settings': Popup.open('boardSubtaskSettings'), + 'click .js-card-settings': Popup.open('boardCardSettings'), }); Template.boardMenuPopup.helpers({ @@ -82,6 +85,7 @@ BlazeComponent.extendComponent({ const currentBoard = Boards.findOne(Session.get('currentBoard')); return currentBoard && currentBoard.stars >= 2; }, + /* showSort() { return Meteor.user().hasSortBy(); }, @@ -101,6 +105,7 @@ BlazeComponent.extendComponent({ listSortShortDesc() { return `list-label-short-${this.currentListSortBy()}`; }, + */ events() { return [ { @@ -114,30 +119,14 @@ BlazeComponent.extendComponent({ 'click .js-open-archived-board'() { Modal.open('archivedBoards'); }, - 'click .js-toggle-board-view'() { - const currentUser = Meteor.user(); - if ( - (currentUser.profile || {}).boardView === 'board-view-swimlanes' - ) { - currentUser.setBoardView('board-view-cal'); - } else if ( - (currentUser.profile || {}).boardView === 'board-view-lists' - ) { - currentUser.setBoardView('board-view-swimlanes'); - } else if ( - (currentUser.profile || {}).boardView === 'board-view-cal' - ) { - currentUser.setBoardView('board-view-lists'); - } else { - currentUser.setBoardView('board-view-swimlanes'); - } - }, + 'click .js-toggle-board-view': Popup.open('boardChangeView'), 'click .js-toggle-sidebar'() { Sidebar.toggle(); }, 'click .js-open-filter-view'() { Sidebar.setView('filter'); }, + /* 'click .js-open-sort-view'(evt) { const target = evt.target; if (target.tagName === 'I') { @@ -148,6 +137,7 @@ BlazeComponent.extendComponent({ Popup.open('listsort')(evt); } }, + */ 'click .js-filter-reset'(event) { event.stopPropagation(); Sidebar.setView(); @@ -156,9 +146,6 @@ BlazeComponent.extendComponent({ 'click .js-open-search-view'() { Sidebar.setView('search'); }, - 'click .js-open-rules-view'() { - Modal.openWide('rulesMain'); - }, 'click .js-multiselection-activate'() { const currentCard = Session.get('currentCard'); MultiSelection.activate(); @@ -186,6 +173,28 @@ Template.boardHeaderBar.helpers({ !Meteor.user().isCommentOnly() ); }, + boardView() { + return Utils.boardView(); + }, +}); + +Template.boardChangeViewPopup.events({ + 'click .js-open-lists-view'() { + Utils.setBoardView('board-view-lists'); + Popup.close(); + }, + 'click .js-open-swimlanes-view'() { + Utils.setBoardView('board-view-swimlanes'); + Popup.close(); + }, + 'click .js-open-cal-view'() { + Utils.setBoardView('board-view-cal'); + Popup.close(); + }, + 'click .js-open-rules-view'() { + Modal.openWide('rulesMain'); + Popup.close(); + }, }); const CreateBoard = BlazeComponent.extendComponent({ @@ -308,6 +317,7 @@ BlazeComponent.extendComponent({ }, }).register('boardChangeWatchPopup'); +/* BlazeComponent.extendComponent({ onCreated() { //this.sortBy = new ReactiveVar(); @@ -377,3 +387,4 @@ BlazeComponent.extendComponent({ ]; }, }).register('listsortPopup'); +*/ diff --git a/client/components/cards/attachments.jade b/client/components/cards/attachments.jade index a5a5c00b..e6e50d7a 100644 --- a/client/components/cards/attachments.jade +++ b/client/components/cards/attachments.jade @@ -45,19 +45,22 @@ template(name="attachmentsGalery") | {{_ 'download'}} if currentUser.isBoardMember unless currentUser.isCommentOnly - if isImage - a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}") - i.fa.fa-thumb-tack - if($eq ../coverId _id) - | {{_ 'remove-cover'}} - else - | {{_ 'add-cover'}} - a.js-confirm-delete - i.fa.fa-close - | {{_ 'delete'}} + unless currentUser.isWorker + if isImage + a(class="{{#if $eq ../coverId _id}}js-remove-cover{{else}}js-add-cover{{/if}}") + i.fa.fa-thumb-tack + if($eq ../coverId _id) + | {{_ 'remove-cover'}} + else + | {{_ 'add-cover'}} + a.js-confirm-delete + i.fa.fa-close + | {{_ 'delete'}} if currentUser.isBoardMember unless currentUser.isCommentOnly - li.attachment-item.add-attachment - a.js-add-attachment {{_ 'add-attachment' }} - + unless currentUser.isWorker + //li.attachment-item.add-attachment + a.js-add-attachment + i.fa.fa-paperclip + | {{_ 'add-attachment' }} diff --git a/client/components/cards/cardDate.js b/client/components/cards/cardDate.js index cb54b033..c4b5c6d8 100644 --- a/client/components/cards/cardDate.js +++ b/client/components/cards/cardDate.js @@ -97,7 +97,8 @@ Template.dateBadge.helpers({ return ( Meteor.user() && Meteor.user().isBoardMember() && - !Meteor.user().isCommentOnly() + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() ); }, }); diff --git a/client/components/cards/cardDetails.jade b/client/components/cards/cardDetails.jade index 53a264ec..615ae1d5 100644 --- a/client/components/cards/cardDetails.jade +++ b/client/components/cards/cardDetails.jade @@ -4,15 +4,27 @@ template(name="cardDetails") +inlinedForm(classNames="js-card-details-title") +editCardTitleForm else - a.fa.fa-times-thin.close-card-details.js-close-card-details - if currentUser.isBoardMember - a.fa.fa-navicon.card-details-menu.js-open-card-details-menu + unless isMiniScreen + a.fa.fa-times-thin.close-card-details.js-close-card-details + if currentUser.isBoardMember + a.fa.fa-navicon.card-details-menu.js-open-card-details-menu + input.inline-input(type="text" id="cardURL_copy" value="{{ absoluteUrl }}") + a.fa.fa-link.card-copy-button.js-copy-link( + class="fa-link" + title="{{_ 'copy-card-link-to-clipboard'}}" + value="{{ absoluteUrl }}" + ) + if isMiniScreen + a.fa.fa-times-thin.close-card-details-mobile-web.js-close-card-details + if currentUser.isBoardMember + a.fa.fa-navicon.card-details-menu-mobile-web.js-open-card-details-menu + a.fa.fa-link.card-copy-mobile-button h2.card-details-title.js-card-title( class="{{#if canModifyCard}}js-open-inlined-form is-editable{{/if}}") +viewer = getTitle - if isWatching - i.fa.fa-eye.card-details-watch + if isWatching + i.card-details-watch.fa.fa-eye .card-details-path each parentList | > @@ -31,70 +43,105 @@ template(name="cardDetails") p.warning {{_ 'card-archived'}} .card-details-items - .card-details-item.card-details-item-received - h3.card-details-item-title {{_ 'card-received'}} - if getReceived - +cardReceivedDate - else - if canModifyCard - a.js-received-date {{_ 'add'}} - - .card-details-item.card-details-item-start - h3.card-details-item-title {{_ 'card-start'}} - if getStart - +cardStartDate - else - if canModifyCard - a.js-start-date {{_ 'add'}} - - .card-details-item.card-details-item-due - h3.card-details-item-title {{_ 'card-due'}} - if getDue - +cardDueDate - else + if currentBoard.allowsReceivedDate + .card-details-item.card-details-item-received + h3 + i.fa.fa-sign-out + card-details-item-title {{_ 'card-received'}} + if getReceived + +cardReceivedDate + else + if canModifyCard + unless currentUser.isWorker + a.card-label.add-label.js-received-date + i.fa.fa-plus + + if currentBoard.allowsStartDate + .card-details-item.card-details-item-start + h3 + i.fa.fa-hourglass-start + card-details-item-title {{_ 'card-start'}} + if getStart + +cardStartDate + else + if canModifyCard + unless currentUser.isWorker + a.card-label.add-label.js-start-date + i.fa.fa-plus + + if currentBoard.allowsDueDate + .card-details-item.card-details-item-due + h3 + i.fa.fa-sign-in + card-details-item-title {{_ 'card-due'}} + if getDue + +cardDueDate + else + if canModifyCard + unless currentUser.isWorker + a.card-label.add-label.js-due-date + i.fa.fa-plus + + if currentBoard.allowsEndDate + .card-details-item.card-details-item-end + h3 + i.fa.fa-hourglass-end + card-details-item-title {{_ 'card-end'}} + if getEnd + +cardEndDate + else + if canModifyCard + unless currentUser.isWorker + a.card-label.add-label.js-end-date + i.fa.fa-plus + + //.card-details-items + if currentBoard.allowsMembers + .card-details-item.card-details-item-members + h3 + i.fa.fa-users + card-details-item-title {{_ 'members'}} + each getMembers + +userAvatar(userId=this cardId=../_id) + | {{! XXX Hack to hide syntaxic coloration /// }} if canModifyCard - a.js-due-date {{_ 'add'}} - - .card-details-item.card-details-item-end - h3.card-details-item-title {{_ 'card-end'}} - if getEnd - +cardEndDate - else + unless currentUser.isWorker + a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}") + i.fa.fa-plus + + //if assigneeSelected + if currentBoard.allowsAssignee + .card-details-item.card-details-item-assignees + h3 + i.fa.fa-user + card-details-item-title {{_ 'assignee'}} + each getAssignees + +userAvatarAssignee(userId=this cardId=../_id) + | {{! XXX Hack to hide syntaxic coloration /// }} if canModifyCard - a.js-end-date {{_ 'add'}} - - .card-details-items - .card-details-item.card-details-item-members - h3.card-details-item-title {{_ 'members'}} - each getMembers - +userAvatar(userId=this cardId=../_id) - | {{! XXX Hack to hide syntaxic coloration /// }} - if canModifyCard - a.member.add-member.card-details-item-add-button.js-add-members(title="{{_ 'card-members-title'}}") - i.fa.fa-plus - - .card-details-item.card-details-item-assignees - h3.card-details-item-title {{_ 'assignee'}} - each getAssignees - +userAvatarAssignee(userId=this cardId=../_id) - | {{! XXX Hack to hide syntaxic coloration /// }} - if canModifyCard - unless assigneeSelected a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}") i.fa.fa-plus + if currentUser.isWorker + unless assigneeSelected + a.assignee.add-assignee.card-details-item-add-button.js-add-assignees(title="{{_ 'assignee'}}") + i.fa.fa-plus + + if currentBoard.allowsLabels + .card-details-item.card-details-item-labels + h3 + i.fa.fa-tags + card-details-item-title {{_ 'labels'}} + a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}") + each labels + span.card-label(class="card-label-{{color}}" title=name) + +viewer + = name + if canModifyCard + unless currentUser.isWorker + a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}") + i.fa.fa-plus - .card-details-item.card-details-item-labels - h3.card-details-item-title {{_ 'labels'}} - a(class="{{#if canModifyCard}}js-add-labels{{else}}is-disabled{{/if}}" title="{{_ 'card-labels-title'}}") - each labels - span.card-label(class="card-label-{{color}}" title=name) - +viewer - = name - if canModifyCard - a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}") - i.fa.fa-plus - - .card-details-items + //.card-details-items each customFieldsWD .card-details-item.card-details-item-customfield h3.card-details-item-title @@ -102,7 +149,7 @@ template(name="cardDetails") = definition.name +cardCustomField - .card-details-items + //.card-details-items if getSpentTime .card-details-item.card-details-item-spent if getIsOvertime @@ -111,84 +158,103 @@ template(name="cardDetails") h3.card-details-item-title {{_ 'spent-time-hours'}} +cardSpentTime - //- XXX We should use "editable" to avoid repetiting ourselves - if canModifyCard - h3.card-details-item-title {{_ 'description'}} - +inlinedCardDescription(classNames="card-description js-card-description") - +editor(autofocus=true) - | {{getUnsavedValue 'cardDescription' _id getDescription}} - .edit-controls.clearfix - button.primary(type="submit") {{_ 'save'}} - a.fa.fa-times-thin.js-close-inlined-form - else - a.js-open-inlined-form - if getDescription + //.card-details-items + if currentBoard.allowsRequestedBy + .card-details-item.card-details-item-name + h3 + i.fa.fa-shopping-cart + card-details-item-title {{_ 'requested-by'}} + if canModifyCard + unless currentUser.isWorker + +inlinedForm(classNames="js-card-details-requester") + +editCardRequesterForm + else + a.js-open-inlined-form + if getRequestedBy + +viewer + = getRequestedBy + else + | {{_ 'add'}} + else if getRequestedBy +viewer - = getDescription - else - | {{_ 'edit'}} - if (hasUnsavedValue 'cardDescription' _id) - p.quiet - | {{_ 'unsaved-description'}} - a.js-open-inlined-form {{_ 'view-it'}} - = ' - ' - a.js-close-inlined-form {{_ 'discard'}} - else if getDescription - h3.card-details-item-title {{_ 'description'}} - +viewer - = getDescription + = getRequestedBy - .card-details-items - .card-details-item.card-details-item-name - h3.card-details-item-title {{_ 'requested-by'}} - if canModifyCard - +inlinedForm(classNames="js-card-details-requester") - +editCardRequesterForm - else - a.js-open-inlined-form - if getRequestedBy - +viewer - = getRequestedBy - else - | {{_ 'add'}} - else if getRequestedBy - +viewer - = getRequestedBy - - .card-details-item.card-details-item-name - h3.card-details-item-title {{_ 'assigned-by'}} - if canModifyCard - +inlinedForm(classNames="js-card-details-assigner") - +editCardAssignerForm - else - a.js-open-inlined-form - if getAssignedBy - +viewer - = getAssignedBy + if currentBoard.allowsAssignedBy + .card-details-item.card-details-item-name + h3 + i.fa.fa-user-plus + card-details-item-title {{_ 'assigned-by'}} + if canModifyCard + unless currentUser.isWorker + +inlinedForm(classNames="js-card-details-assigner") + +editCardAssignerForm else - | {{_ 'add'}} - else if getRequestedBy - +viewer - = getAssignedBy - - hr - +checklists(cardId = _id) - - if currentBoard.allowsSubtasks - hr - +subtasks(cardId = _id) - - hr - h3 - i.fa.fa-paperclip - | {{_ 'attachments'}} + a.js-open-inlined-form + if getAssignedBy + +viewer + = getAssignedBy + else + | {{_ 'add'}} + else if getRequestedBy + +viewer + = getAssignedBy - +attachmentsGalery + //- XXX We should use "editable" to avoid repetiting ourselves + if canModifyCard + unless currentUser.isWorker + if currentBoard.allowsDescriptionTitle + h3 + i.fa.fa-align-left + card-details-item-title {{_ 'description'}} + if currentBoard.allowsDescriptionText + +inlinedCardDescription(classNames="card-description js-card-description") + +editor(autofocus=true) + | {{getUnsavedValue 'cardDescription' _id getDescription}} + .edit-controls.clearfix + button.primary(type="submit") {{_ 'save'}} + a.fa.fa-times-thin.js-close-inlined-form + else + if currentBoard.allowsDescriptionText + a.js-open-inlined-form + if getDescription + +viewer + = getDescription + else + | {{_ 'edit'}} + if (hasUnsavedValue 'cardDescription' _id) + p.quiet + | {{_ 'unsaved-description'}} + a.js-open-inlined-form {{_ 'view-it'}} + = ' - ' + a.js-close-inlined-form {{_ 'discard'}} + else if getDescription + if currentBoard.allowsDescriptionTitle + h3.card-details-item-title {{_ 'description'}} + if currentBoard.allowsDescriptionText + +viewer + = getDescription + + .card-checklist-attachmentGalerys + .card-checklist-attachmentGalery.card-checklists + if currentBoard.allowsChecklists + +checklists(cardId = _id) + if currentBoard.allowsSubtasks + hr + +subtasks(cardId = _id) + if currentBoard.allowsAttachments + //- hr + //- h3 + //- i.fa.fa-paperclip + //- | {{_ 'attachments'}} + .card-checklist-attachmentGalery.card-attachmentGalery + +attachmentsGalery hr unless currentUser.isNoComments .activity-title - h3 {{ _ 'activity'}} + h3 + i.fa.fa-history + | {{ _ 'activity'}} if currentUser.isBoardMember .material-toggle-switch span.toggle-switch-title {{_ 'hide-system-messages'}} @@ -197,9 +263,10 @@ template(name="cardDetails") else input.toggle-switch(type="checkbox" id="toggleButton") label.toggle-label(for="toggleButton") - if currentUser.isBoardMember - unless currentUser.isNoComments - +commentForm + if currentBoard.allowsComments + if currentUser.isBoardMember + unless currentUser.isNoComments + +commentForm unless currentUser.isNoComments if isLoaded.get if isLinkedCard @@ -230,32 +297,79 @@ template(name="editCardAssignerForm") template(name="cardDetailsActionsPopup") ul.pop-over-list - li: a.js-toggle-watch-card {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}} + li + a.js-toggle-watch-card + if isWatching + i.fa.fa-eye + | {{_ 'unwatch'}} + else + i.fa.fa-eye-slash + | {{_ 'watch'}} if canModifyCard - hr - ul.pop-over-list - //li: a.js-members {{_ 'card-edit-members'}} - //li: a.js-labels {{_ 'card-edit-labels'}} - //li: a.js-attachments {{_ 'card-edit-attachments'}} - li: a.js-custom-fields {{_ 'card-edit-custom-fields'}} - //li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}} - //li: a.js-start-date {{_ 'editCardStartDatePopup-title'}} - //li: a.js-due-date {{_ 'editCardDueDatePopup-title'}} - //li: a.js-end-date {{_ 'editCardEndDatePopup-title'}} - li: a.js-spent-time {{_ 'editCardSpentTimePopup-title'}} - li: a.js-set-card-color {{_ 'setCardColorPopup-title'}} - hr - ul.pop-over-list - li: a.js-move-card-to-top {{_ 'moveCardToTop-title'}} - li: a.js-move-card-to-bottom {{_ 'moveCardToBottom-title'}} - hr + unless currentUser.isWorker + hr + ul.pop-over-list + //li: a.js-members {{_ 'card-edit-members'}} + //li: a.js-labels {{_ 'card-edit-labels'}} + //li: a.js-attachments {{_ 'card-edit-attachments'}} + li + a.js-custom-fields + i.fa.fa-list-alt + | {{_ 'card-edit-custom-fields'}} + //li: a.js-received-date {{_ 'editCardReceivedDatePopup-title'}} + //li: a.js-start-date {{_ 'editCardStartDatePopup-title'}} + //li: a.js-due-date {{_ 'editCardDueDatePopup-title'}} + //li: a.js-end-date {{_ 'editCardEndDatePopup-title'}} + li + a.js-spent-time + i.fa.fa-clock-o + | {{_ 'editCardSpentTimePopup-title'}} + li + a.js-set-card-color + i.fa.fa-paint-brush + | {{_ 'setCardColorPopup-title'}} + hr ul.pop-over-list - li: a.js-move-card {{_ 'moveCardPopup-title'}} - li: a.js-copy-card {{_ 'copyCardPopup-title'}} - li: a.js-copy-checklist-cards {{_ 'copyChecklistToManyCardsPopup-title'}} + li + a.js-move-card-to-top + i.fa.fa-arrow-up + | {{_ 'moveCardToTop-title'}} + li + a.js-move-card-to-bottom + i.fa.fa-arrow-down + | {{_ 'moveCardToBottom-title'}} + unless currentUser.isWorker + hr + ul.pop-over-list + li + a.js-move-card + i.fa.fa-arrow-right + | {{_ 'moveCardPopup-title'}} + li + a.js-copy-card + i.fa.fa-copy + | {{_ 'copyCardPopup-title'}} + hr + ul.pop-over-list + li + a.js-copy-checklist-cards + i.fa.fa-list + i.fa.fa-copy + | {{_ 'copyChecklistToManyCardsPopup-title'}} unless archived - li: a.js-archive {{_ 'archive-card'}} - li: a.js-more {{_ 'cardMorePopup-title'}} + hr + ul.pop-over-list + li + a.js-archive + i.fa.fa-arrow-right + i.fa.fa-archive + | {{_ 'archive-card'}} + hr + ul.pop-over-list + li + a.js-more + i.fa.fa-link + | {{_ 'cardMorePopup-title'}} template(name="moveCardPopup") +boardsAndLists @@ -307,16 +421,27 @@ template(name="cardMembersPopup") i.fa.fa-check template(name="cardAssigneesPopup") - ul.pop-over-list.js-card-assignee-list - each board.activeMembers - li.item(class="{{#if isCardAssignee}}active{{/if}}") - a.name.js-select-assignee(href="#") - +userAvatar(userId=user._id) - span.full-name - = user.profile.fullname - | (<span class="username">{{ user.username }}</span>) - if isCardAssignee - i.fa.fa-check + unless currentUser.isWorker + ul.pop-over-list.js-card-assignee-list + each board.activeMembers + li.item(class="{{#if isCardAssignee}}active{{/if}}") + a.name.js-select-assignee(href="#") + +userAvatar(userId=user._id) + span.full-name + = user.profile.fullname + | (<span class="username">{{ user.username }}</span>) + if isCardAssignee + i.fa.fa-check + if currentUser.isWorker + ul.pop-over-list.js-card-assignee-list + li.item(class="{{#if currentUser.isCardAssignee}}active{{/if}}") + a.name.js-select-assignee(href="#") + +userAvatar(userId=currentUser._id) + span.full-name + = currentUser.profile.fullname + | (<span class="username">{{ currentUser.username }}</span>) + if currentUser.isCardAssignee + i.fa.fa-check template(name="userAvatarAssignee") a.assignee.js-assignee(title="{{userData.profile.fullname}} ({{userData.username}})") @@ -344,11 +469,13 @@ template(name="cardAssigneePopup") p.quiet @{{ user.username }} ul.pop-over-list if currentUser.isNotCommentOnly + unless currentUser.isWorker li: a.js-remove-assignee {{_ 'remove-member-from-card'}} - if $eq currentUser._id user._id - with currentUser - li: a.js-edit-profile {{_ 'edit-profile'}} + unless currentUser.isWorker + if $eq currentUser._id user._id + with currentUser + li: a.js-edit-profile {{_ 'edit-profile'}} template(name="userAvatarAssigneeInitials") svg.avatar.avatar-assignee-initials(viewBox="0 0 {{viewPortWidth}} 15") diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index 7bb54223..5fdc5579 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -51,7 +51,8 @@ BlazeComponent.extendComponent({ return ( Meteor.user() && Meteor.user().isBoardMember() && - !Meteor.user().isCommentOnly() + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() ); }, @@ -252,6 +253,12 @@ BlazeComponent.extendComponent({ if ($subtasksDom.data('sortable')) { $subtasksDom.sortable('option', 'disabled', !userIsMember()); } + if ($checklistsDom.data('sortable')) { + $checklistsDom.sortable('option', 'disabled', Utils.isMiniScreen()); + } + if ($subtasksDom.data('sortable')) { + $subtasksDom.sortable('option', 'disabled', Utils.isMiniScreen()); + } }); }, @@ -278,6 +285,29 @@ BlazeComponent.extendComponent({ 'click .js-close-card-details'() { Utils.goBoardId(this.data().boardId); }, + 'click .js-copy-link'() { + StringToCopyElement = document.getElementById('cardURL_copy'); + StringToCopyElement.select(); + if (document.execCommand('copy')) { + StringToCopyElement.blur(); + } else { + document.getElementById('cardURL_copy').selectionStart = 0; + document.getElementById('cardURL_copy').selectionEnd = 999; + document.execCommand('copy'); + if (window.getSelection) { + if (window.getSelection().empty) { + // Chrome + window.getSelection().empty(); + } else if (window.getSelection().removeAllRanges) { + // Firefox + window.getSelection().removeAllRanges(); + } + } else if (document.selection) { + // IE? + document.selection.empty(); + } + } + }, 'click .js-open-card-details-menu': Popup.open('cardDetailsActions'), 'submit .js-card-description'(event) { event.preventDefault(); @@ -291,6 +321,8 @@ BlazeComponent.extendComponent({ .trim(); if (title) { this.data().setTitle(title); + } else { + this.data().setTitle(''); } }, 'submit .js-card-details-assigner'(event) { @@ -300,6 +332,8 @@ BlazeComponent.extendComponent({ .trim(); if (assigner) { this.data().setAssignedBy(assigner); + } else { + this.data().setAssignedBy(''); } }, 'submit .js-card-details-requester'(event) { @@ -309,6 +343,8 @@ BlazeComponent.extendComponent({ .trim(); if (requester) { this.data().setRequestedBy(requester); + } else { + this.data().setRequestedBy(''); } }, 'click .js-member': Popup.open('cardMember'), @@ -364,6 +400,54 @@ Template.cardDetails.helpers({ }); }, + receivedSelected() { + if (this.getReceived().length === 0) { + return false; + } else { + return true; + } + }, + + startSelected() { + if (this.getStart().length === 0) { + return false; + } else { + return true; + } + }, + + endSelected() { + if (this.getEnd().length === 0) { + return false; + } else { + return true; + } + }, + + dueSelected() { + if (this.getDue().length === 0) { + return false; + } else { + return true; + } + }, + + memberSelected() { + if (this.getMembers().length === 0) { + return false; + } else { + return true; + } + }, + + labelSelected() { + if (this.getLabels().length === 0) { + return false; + } else { + return true; + } + }, + assigneeSelected() { if (this.getAssignees().length === 0) { return false; @@ -372,6 +456,22 @@ Template.cardDetails.helpers({ } }, + requestBySelected() { + if (this.getRequestBy().length === 0) { + return false; + } else { + return true; + } + }, + + assigneeBySelected() { + if (this.getAssigneeBy().length === 0) { + return false; + } else { + return true; + } + }, + memberType() { const user = Users.findOne(this.userId); return user && user.isBoardAdmin() ? 'admin' : 'normal'; diff --git a/client/components/cards/cardDetails.styl b/client/components/cards/cardDetails.styl index e4549e44..80fa87c0 100644 --- a/client/components/cards/cardDetails.styl +++ b/client/components/cards/cardDetails.styl @@ -4,6 +4,12 @@ avatar-radius = 50% +#cardURL_copy + // Have clipboard text not visible by moving it to far left + position: absolute + left: -2000px + top: 0px + .assignee border-radius: 3px display: block @@ -107,7 +113,11 @@ avatar-radius = 50% border-bottom: 1px solid darken(white, 14%) .close-card-details, - .card-details-menu + .card-details-menu, + .card-copy-button, + .card-copy-mobile-button, + .close-card-details-mobile-web, + .card-details-menu-mobile-web float: right .close-card-details @@ -115,10 +125,30 @@ avatar-radius = 50% padding: 5px margin-right: -8px + .close-card-details-mobile-web + font-size: 24px + padding: 5px + margin-right: 40px + + .card-copy-button + font-size: 17px + padding: 10px + margin-right: 10px + + .card-copy-mobile-button + font-size: 17px + padding: 10px + margin-right: 10px + .card-details-menu font-size: 17px padding: 10px + .card-details-menu-mobile-web + font-size: 17px + padding: 10px + margin-right: 30px + .card-details-watch font-size: 17px padding-left: 7px diff --git a/client/components/cards/checklists.jade b/client/components/cards/checklists.jade index 279d3671..391769e9 100644 --- a/client/components/cards/checklists.jade +++ b/client/components/cards/checklists.jade @@ -1,5 +1,7 @@ template(name="checklists") - h3 {{_ 'checklists'}} + h3 + i.fa.fa-check + | {{_ 'checklists'}} if toggleDeleteDialog.get .board-overlay#card-details-overlay +checklistDeleteDialog(checklist = checklistToDelete) diff --git a/client/components/cards/checklists.js b/client/components/cards/checklists.js index 57939eb8..c88fdd82 100644 --- a/client/components/cards/checklists.js +++ b/client/components/cards/checklists.js @@ -60,6 +60,9 @@ BlazeComponent.extendComponent({ if ($itemsDom.data('sortable')) { $(self.itemsDom).sortable('option', 'disabled', !userIsMember()); } + if ($itemsDom.data('sortable')) { + $(self.itemsDom).sortable('option', 'disabled', Utils.isMiniScreen()); + } }); }, @@ -67,7 +70,8 @@ BlazeComponent.extendComponent({ return ( Meteor.user() && Meteor.user().isBoardMember() && - !Meteor.user().isCommentOnly() + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() ); }, }).register('checklistDetail'); @@ -120,7 +124,8 @@ BlazeComponent.extendComponent({ return ( Meteor.user() && Meteor.user().isBoardMember() && - !Meteor.user().isCommentOnly() + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() ); }, @@ -228,7 +233,8 @@ Template.checklistItemDetail.helpers({ return ( Meteor.user() && Meteor.user().isBoardMember() && - !Meteor.user().isCommentOnly() + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() ); }, }); diff --git a/client/components/cards/minicard.jade b/client/components/cards/minicard.jade index 0a35bd3a..6a073424 100644 --- a/client/components/cards/minicard.jade +++ b/client/components/cards/minicard.jade @@ -4,8 +4,8 @@ template(name="minicard") class="{{#if isLinkedBoard}}linked-board{{/if}}" class="minicard-{{colorClass}}") if isMiniScreen - .handle - .fa.fa-arrows + //.handle + // .fa.fa-arrows unless isMiniScreen if showDesktopDragHandles .handle @@ -67,14 +67,15 @@ template(name="minicard") .minicard-custom-fields each customFieldsWD if definition.showOnCard - .minicard-custom-field - if definition.showLabelOnMiniCard + if trueValue + .minicard-custom-field + if definition.showLabelOnMiniCard + .minicard-custom-field-item + +viewer + = definition.name .minicard-custom-field-item +viewer - = definition.name - .minicard-custom-field-item - +viewer - = trueValue + = trueValue if getAssignees .minicard-assignees.js-minicard-assignees diff --git a/client/components/cards/minicard.js b/client/components/cards/minicard.js index 430042f4..200e019d 100644 --- a/client/components/cards/minicard.js +++ b/client/components/cards/minicard.js @@ -1,3 +1,5 @@ +import { Cookies } from 'meteor/ostrio:cookies'; +const cookies = new Cookies(); // Template.cards.events({ // 'click .member': Popup.open('cardMember') // }); @@ -18,7 +20,11 @@ BlazeComponent.extendComponent({ }, { 'click .js-toggle-minicard-label-text'() { - Meteor.call('toggleMinicardLabelText'); + if (cookies.has('hiddenMinicardLabelText')) { + cookies.remove('hiddenMinicardLabelText'); //true + } else { + cookies.set('hiddenMinicardLabelText', 'true'); //true + } }, }, ]; @@ -27,10 +33,24 @@ BlazeComponent.extendComponent({ Template.minicard.helpers({ showDesktopDragHandles() { - return Meteor.user().hasShowDesktopDragHandles(); + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).showDesktopDragHandles; + } else if (cookies.has('showDesktopDragHandles')) { + return true; + } else { + return false; + } }, hiddenMinicardLabelText() { - return Meteor.user().hasHiddenMinicardLabelText(); + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).hiddenMinicardLabelText; + } else if (cookies.has('hiddenMinicardLabelText')) { + return true; + } else { + return false; + } }, coverUrl() { return Attachments.findOne(this.coverId).link('original', '/'); diff --git a/client/components/cards/subtasks.jade b/client/components/cards/subtasks.jade index 7e64e23f..df35bed3 100644 --- a/client/components/cards/subtasks.jade +++ b/client/components/cards/subtasks.jade @@ -1,5 +1,7 @@ template(name="subtasks") - h3 {{_ 'subtasks'}} + h3 + i.fa.fa-sitemap + | {{_ 'subtasks'}} if toggleDeleteDialog.get .board-overlay#card-details-overlay +subtaskDeleteDialog(subtask = subtaskToDelete) diff --git a/client/components/cards/subtasks.js b/client/components/cards/subtasks.js index fab860bb..34348fe1 100644 --- a/client/components/cards/subtasks.js +++ b/client/components/cards/subtasks.js @@ -3,7 +3,8 @@ BlazeComponent.extendComponent({ return ( Meteor.user() && Meteor.user().isBoardMember() && - !Meteor.user().isCommentOnly() + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() ); }, }).register('subtaskDetail'); @@ -55,7 +56,8 @@ BlazeComponent.extendComponent({ return ( Meteor.user() && Meteor.user().isBoardMember() && - !Meteor.user().isCommentOnly() + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() ); }, @@ -154,7 +156,8 @@ Template.subtaskItemDetail.helpers({ return ( Meteor.user() && Meteor.user().isBoardMember() && - !Meteor.user().isCommentOnly() + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() ); }, }); diff --git a/client/components/lists/list.js b/client/components/lists/list.js index b7b8b2e0..8574caf7 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -1,3 +1,5 @@ +import { Cookies } from 'meteor/ostrio:cookies'; +const cookies = new Cookies(); const { calculateIndex, enableClickOnTouch } = Utils; BlazeComponent.extendComponent({ @@ -31,18 +33,6 @@ BlazeComponent.extendComponent({ const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)'; const $cards = this.$('.js-minicards'); - if (Utils.isMiniScreen) { - $('.js-minicards').sortable({ - handle: '.handle', - }); - } - - if (!Utils.isMiniScreen && showDesktopDragHandles) { - $('.js-minicards').sortable({ - handle: '.handle', - }); - } - $cards.sortable({ connectWith: '.js-minicards:not(.js-list-full)', tolerance: 'pointer', @@ -85,16 +75,15 @@ BlazeComponent.extendComponent({ const listId = Blaze.getData(ui.item.parents('.list').get(0))._id; const currentBoard = Boards.findOne(Session.get('currentBoard')); let swimlaneId = ''; - const boardView = (Meteor.user().profile || {}).boardView; if ( - boardView === 'board-view-swimlanes' || + Utils.boardView() === 'board-view-swimlanes' || currentBoard.isTemplatesBoard() ) swimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0))._id; else if ( - boardView === 'board-view-lists' || - boardView === 'board-view-cal' || - !boardView + Utils.boardView() === 'board-view-lists' || + Utils.boardView() === 'board-view-cal' || + !Utils.boardView ) swimlaneId = currentBoard.getDefaultSwimline()._id; @@ -128,9 +117,49 @@ BlazeComponent.extendComponent({ // ugly touch event hotfix enableClickOnTouch(itemsSelector); - // Disable drag-dropping if the current user is not a board member or is comment only this.autorun(() => { - $cards.sortable('option', 'disabled', !userIsMember()); + let showDesktopDragHandles = false; + currentUser = Meteor.user(); + if (currentUser) { + showDesktopDragHandles = (currentUser.profile || {}) + .showDesktopDragHandles; + } else if (cookies.has('showDesktopDragHandles')) { + showDesktopDragHandles = true; + } else { + showDesktopDragHandles = false; + } + + if (!Utils.isMiniScreen() && showDesktopDragHandles) { + $cards.sortable({ + handle: '.handle', + }); + } else if (!Utils.isMiniScreen() && !showDesktopDragHandles) { + $cards.sortable({ + handle: '.minicard', + }); + } + + if ($cards.data('sortable')) { + $cards.sortable( + 'option', + 'disabled', + // Disable drag-dropping when user is not member/is miniscreen + !userIsMember(), + // Not disable drag-dropping while in multi-selection mode + // MultiSelection.isActive() || !userIsMember(), + ); + } + + if ($cards.data('sortable')) { + $cards.sortable( + 'option', + 'disabled', + // Disable drag-dropping when user is not member/is miniscreen + Utils.isMiniScreen(), + // Not disable drag-dropping while in multi-selection mode + // MultiSelection.isActive() || !userIsMember(), + ); + } }); // We want to re-run this function any time a card is added. @@ -163,7 +192,14 @@ BlazeComponent.extendComponent({ Template.list.helpers({ showDesktopDragHandles() { - return Meteor.user().hasShowDesktopDragHandles(); + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).showDesktopDragHandles; + } else if (cookies.has('showDesktopDragHandles')) { + return true; + } else { + return false; + } }, }); diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index c8e41a0b..89c27ec7 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -48,7 +48,6 @@ BlazeComponent.extendComponent({ const board = this.data().board(); let linkedId = ''; let swimlaneId = ''; - const boardView = (Meteor.user().profile || {}).boardView; let cardType = 'cardType-card'; if (title) { if (board.isTemplatesBoard()) { @@ -71,14 +70,14 @@ BlazeComponent.extendComponent({ }); cardType = 'cardType-linkedBoard'; } - } else if (boardView === 'board-view-swimlanes') + } else if (Utils.boardView() === 'board-view-swimlanes') swimlaneId = this.parentComponent() .parentComponent() .data()._id; else if ( - boardView === 'board-view-lists' || - boardView === 'board-view-cal' || - !boardView + Utils.boardView() === 'board-view-lists' || + Utils.boardView() === 'board-view-cal' || + !Utils.boardView ) swimlaneId = board.getDefaultSwimline()._id; @@ -157,9 +156,8 @@ BlazeComponent.extendComponent({ }, idOrNull(swimlaneId) { - const currentUser = Meteor.user(); if ( - (currentUser.profile || {}).boardView === 'board-view-swimlanes' || + Utils.boardView() === 'board-view-swimlanes' || this.data() .board() .isTemplatesBoard() @@ -191,7 +189,8 @@ BlazeComponent.extendComponent({ !this.reachedWipLimit() && Meteor.user() && Meteor.user().isBoardMember() && - !Meteor.user().isCommentOnly() + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() ); }, @@ -397,10 +396,9 @@ BlazeComponent.extendComponent({ '.js-swimlane', ); this.swimlaneId = ''; - const boardView = (Meteor.user().profile || {}).boardView; - if (boardView === 'board-view-swimlanes') + if (Utils.boardView() === 'board-view-swimlanes') this.swimlaneId = Blaze.getData(swimlane[0])._id; - else if (boardView === 'board-view-lists' || !boardView) + else if (Utils.boardView() === 'board-view-lists' || !Utils.boardView) this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id; }, @@ -580,7 +578,7 @@ BlazeComponent.extendComponent({ const swimlane = $(Popup._getTopStack().openerElement).parents( '.js-swimlane', ); - if ((Meteor.user().profile || {}).boardView === 'board-view-swimlanes') + if (Utils.boardView() === 'board-view-swimlanes') this.swimlaneId = Blaze.getData(swimlane[0])._id; else this.swimlaneId = Swimlanes.findOne({ boardId: this.boardId })._id; // List where to insert card @@ -709,22 +707,18 @@ BlazeComponent.extendComponent({ if (isSandstorm) { const user = Meteor.user(); if (user) { - const boardView = (Meteor.user().profile || {}).boardView; - if (boardView === 'board-view-swimlanes') { + if (Utils.boardView() === 'board-view-swimlanes') { this.swimlaneId = this.parentComponent() .parentComponent() .parentComponent() .data()._id; } } - } else { - const boardView = (Meteor.user().profile || {}).boardView; - if (boardView === 'board-view-swimlanes') { - this.swimlaneId = this.parentComponent() - .parentComponent() - .parentComponent() - .data()._id; - } + } else if (Utils.boardView() === 'board-view-swimlanes') { + this.swimlaneId = this.parentComponent() + .parentComponent() + .parentComponent() + .data()._id; } }, diff --git a/client/components/lists/listHeader.jade b/client/components/lists/listHeader.jade index 064303ee..182fee9e 100644 --- a/client/components/lists/listHeader.jade +++ b/client/components/lists/listHeader.jade @@ -10,7 +10,7 @@ template(name="listHeader") a.list-header-left-icon.fa.fa-angle-left.js-unselect-list h2.list-header-name( title="{{ moment modifiedAt 'LLL' }}" - class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}js-open-inlined-form is-editable{{/unless}}{{/if}}") + class="{{#if currentUser.isBoardMember}}{{#unless currentUser.isCommentOnly}}{{#unless currentUser.isWorker}}js-open-inlined-form is-editable{{/unless}}{{/unless}}{{/if}}") +viewer = title if wipLimit.enabled @@ -30,17 +30,17 @@ template(name="listHeader") if canSeeAddCard a.js-add-card.fa.fa-plus.list-header-plus-icon a.fa.fa-navicon.js-open-list-menu - a.list-header-handle.handle.fa.fa-arrows.js-list-handle + //a.list-header-handle.handle.fa.fa-arrows.js-list-handle else a.list-header-menu-icon.fa.fa-angle-right.js-select-list - a.list-header-handle.handle.fa.fa-arrows.js-list-handle + //a.list-header-handle.handle.fa.fa-arrows.js-list-handle else if currentUser.isBoardMember if isWatching i.list-header-watch-icon.fa.fa-eye div.list-header-menu unless currentUser.isCommentOnly - if isBoardAdmin - a.fa.js-list-star.list-header-plus-icon(class="fa-star{{#unless starred}}-o{{/unless}}") + //if isBoardAdmin + // a.fa.js-list-star.list-header-plus-icon(class="fa-star{{#unless starred}}-o{{/unless}}") if canSeeAddCard a.js-add-card.fa.fa-plus.list-header-plus-icon a.fa.fa-navicon.js-open-list-menu @@ -56,25 +56,47 @@ template(name="editListTitleForm") template(name="listActionPopup") ul.pop-over-list - li: a.js-toggle-watch-list {{#if isWatching}}{{_ 'unwatch'}}{{else}}{{_ 'watch'}}{{/if}} + li + a.js-toggle-watch-list + if isWatching + i.fa.fa-eye + | {{_ 'unwatch'}} + else + i.fa.fa-eye-slash + | {{_ 'watch'}} unless currentUser.isCommentOnly - hr - ul.pop-over-list - li: a.js-set-color-list {{_ 'set-color-list'}} - hr + unless currentUser.isWorker + ul.pop-over-list + li + a.js-set-color-list + i.fa.fa-paint-brush + | {{_ 'set-color-list'}} ul.pop-over-list if cards.count - li: a.js-select-cards {{_ 'list-select-cards'}} - hr + li + a.js-select-cards + i.fa.fa-check-square + | {{_ 'list-select-cards'}} if currentUser.isBoardAdmin ul.pop-over-list - li: a.js-set-wip-limit {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}} + li + a.js-set-wip-limit + i.fa.fa-ban + | {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}} + unless currentUser.isWorker hr - ul.pop-over-list - li: a.js-close-list {{_ 'archive-list'}} + ul.pop-over-list + li + a.js-close-list + i.fa.fa-arrow-right + i.fa.fa-archive + | {{_ 'archive-list'}} hr ul.pop-over-list - li: a.js-more {{_ 'listMorePopup-title'}} + li + a.js-more + i.fa.fa-link + | {{_ 'listMorePopup-title'}} template(name="boardLists") ul.pop-over-list @@ -94,7 +116,8 @@ template(name="listMorePopup") input.inline-input(type="text" readonly value="{{ rootUrl }}") | {{_ 'added'}} span.date(title=list.createdAt) {{ moment createdAt 'LLL' }} - a.js-delete {{_ 'delete'}} + unless currentUser.isWorker + a.js-delete {{_ 'delete'}} template(name="listDeletePopup") p {{_ "list-delete-pop"}} diff --git a/client/components/lists/listHeader.js b/client/components/lists/listHeader.js index b524d4e0..46dbd748 100644 --- a/client/components/lists/listHeader.js +++ b/client/components/lists/listHeader.js @@ -1,3 +1,5 @@ +import { Cookies } from 'meteor/ostrio:cookies'; +const cookies = new Cookies(); let listsColors; Meteor.startup(() => { listsColors = Lists.simpleSchema()._schema.color.allowedValues; @@ -7,9 +9,10 @@ BlazeComponent.extendComponent({ canSeeAddCard() { const list = Template.currentData(); return ( - !list.getWipLimit('enabled') || - list.getWipLimit('soft') || - !this.reachedWipLimit() + (!list.getWipLimit('enabled') || + list.getWipLimit('soft') || + !this.reachedWipLimit()) && + !Meteor.user().isWorker() ); }, @@ -44,14 +47,18 @@ BlazeComponent.extendComponent({ }, limitToShowCardsCount() { - return Meteor.user().getLimitToShowCardsCount(); + const currentUser = Meteor.user(); + if (currentUser) { + return Meteor.user().getLimitToShowCardsCount(); + } else { + return false; + } }, cardsCount() { const list = Template.currentData(); let swimlaneId = ''; - const boardView = (Meteor.user().profile || {}).boardView; - if (boardView === 'board-view-swimlanes') + if (Utils.boardView() === 'board-view-swimlanes') swimlaneId = this.parentComponent() .parentComponent() .data()._id; @@ -100,7 +107,14 @@ BlazeComponent.extendComponent({ Template.listHeader.helpers({ showDesktopDragHandles() { - return Meteor.user().hasShowDesktopDragHandles(); + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).showDesktopDragHandles; + } else if (cookies.has('showDesktopDragHandles')) { + return true; + } else { + return false; + } }, }); diff --git a/client/components/main/header.styl b/client/components/main/header.styl index e3c7618d..f77c2aca 100644 --- a/client/components/main/header.styl +++ b/client/components/main/header.styl @@ -218,6 +218,9 @@ padding: 10px margin: -10px 0 -10px -10px +.announcement .viewer + display: inline-block + .announcement, .offline-warning width: 100% diff --git a/client/components/rules/actions/boardActions.jade b/client/components/rules/actions/boardActions.jade index 6034184c..fda15062 100644 --- a/client/components/rules/actions/boardActions.jade +++ b/client/components/rules/actions/boardActions.jade @@ -1,29 +1,42 @@ template(name="boardActions") div.trigger-item div.trigger-content - div.trigger-text + div.trigger-text | {{_'r-move-card-to'}} div.trigger-dropdown select(id="move-gen-action") option(value="top") {{_'r-top-of'}} option(value="bottom") {{_'r-bottom-of'}} - div.trigger-text + div.trigger-text | {{_'r-its-list'}} div.trigger-button.js-add-gen-move-action.js-goto-rules i.fa.fa-plus div.trigger-item div.trigger-content - div.trigger-text + div.trigger-text | {{_'r-move-card-to'}} div.trigger-dropdown select(id="move-spec-action") option(value="top") {{_'r-top-of'}} option(value="bottom") {{_'r-bottom-of'}} - div.trigger-text - | {{_'r-list'}} + div.trigger-text + | {{_'r-the-board'}} + div.trigger-dropdown + select(id="board-id") + each boards + if $eq _id currentBoard._id + option(value="{{_id}}" selected) {{_ 'current'}} + else + option(value="{{_id}}") {{title}} + div.trigger-text + | {{_'r-in-list'}} div.trigger-dropdown input(id="listName",type=text,placeholder="{{_'r-name'}}") + div.trigger-text + | {{_'r-in-swimlane'}} + div.trigger-dropdown + input(id="swimlaneName",type=text,placeholder="{{_'r-name'}}") div.trigger-button.js-add-spec-move-action.js-goto-rules i.fa.fa-plus @@ -33,14 +46,14 @@ template(name="boardActions") select(id="arch-action") option(value="archive") {{_'r-archive'}} option(value="unarchive") {{_'r-unarchive'}} - div.trigger-text + div.trigger-text | {{_'r-card'}} div.trigger-button.js-add-arch-action.js-goto-rules i.fa.fa-plus div.trigger-item div.trigger-content - div.trigger-text + div.trigger-text | {{_'r-add-swimlane'}} div.trigger-dropdown input(id="swimlane-name",type=text,placeholder="{{_'r-name'}}") @@ -49,15 +62,15 @@ template(name="boardActions") div.trigger-item div.trigger-content - div.trigger-text + div.trigger-text | {{_'r-create-card'}} div.trigger-dropdown input(id="card-name",type=text,placeholder="{{_'r-name'}}") - div.trigger-text + div.trigger-text | {{_'r-in-list'}} div.trigger-dropdown input(id="list-name",type=text,placeholder="{{_'r-name'}}") - div.trigger-text + div.trigger-text | {{_'r-in-swimlane'}} div.trigger-dropdown input(id="swimlane-name2",type=text,placeholder="{{_'r-name'}}") @@ -65,8 +78,8 @@ template(name="boardActions") i.fa.fa-plus - - + + diff --git a/client/components/rules/actions/boardActions.js b/client/components/rules/actions/boardActions.js index 8568d2bf..c2f2375a 100644 --- a/client/components/rules/actions/boardActions.js +++ b/client/components/rules/actions/boardActions.js @@ -1,6 +1,22 @@ BlazeComponent.extendComponent({ onCreated() {}, + boards() { + const boards = Boards.find( + { + archived: false, + 'members.userId': Meteor.userId(), + _id: { + $ne: Meteor.user().getTemplatesBoardId(), + }, + }, + { + sort: ['title'], + }, + ); + return boards; + }, + events() { return [ { @@ -52,15 +68,18 @@ BlazeComponent.extendComponent({ const ruleName = this.data().ruleName.get(); const trigger = this.data().triggerVar.get(); const actionSelected = this.find('#move-spec-action').value; - const listTitle = this.find('#listName').value; + const swimlaneName = this.find('#swimlaneName').value; + const listName = this.find('#listName').value; const boardId = Session.get('currentBoard'); + const destBoardId = this.find('#board-id').value; const desc = Utils.getTriggerActionDesc(event, this); if (actionSelected === 'top') { const triggerId = Triggers.insert(trigger); const actionId = Actions.insert({ actionType: 'moveCardToTop', - listTitle, - boardId, + listName, + swimlaneName, + boardId: destBoardId, desc, }); Rules.insert({ @@ -74,8 +93,9 @@ BlazeComponent.extendComponent({ const triggerId = Triggers.insert(trigger); const actionId = Actions.insert({ actionType: 'moveCardToBottom', - listTitle, - boardId, + listName, + swimlaneName, + boardId: destBoardId, desc, }); Rules.insert({ diff --git a/client/components/settings/informationBody.jade b/client/components/settings/informationBody.jade index 2c615ffd..0f85dd9c 100644 --- a/client/components/settings/informationBody.jade +++ b/client/components/settings/informationBody.jade @@ -4,12 +4,16 @@ template(name='information') | {{_ 'error-notAuthorized'}} else .content-title - span {{_ 'info'}} + span + i.fa.fa-info-circle + | {{_ 'info'}} .content-body .side-menu ul li.active - a.js-setting-menu(data-id="information-display") {{_ 'info'}} + a.js-setting-menu(data-id="information-display") + i.fa.fa-info-circle + | {{_ 'info'}} .main-body +statistics diff --git a/client/components/settings/peopleBody.jade b/client/components/settings/peopleBody.jade index ff343e37..b8a94337 100644 --- a/client/components/settings/peopleBody.jade +++ b/client/components/settings/peopleBody.jade @@ -5,16 +5,22 @@ template(name="people") else .content-title.ext-box .ext-box-left - span {{_ 'people'}} + span + i.fa.fa-users + | {{_ 'people'}} input#searchInput(placeholder="{{_ 'search'}}") - button#searchButton {{_ 'search'}} + button#searchButton + i.fa.fa-search + | {{_ 'search'}} .ext-box-right span {{_ 'people-number'}} #{peopleNumber} .content-body .side-menu ul li.active - a.js-setting-menu(data-id="people-setting") {{_ 'people'}} + a.js-setting-menu(data-id="people-setting") + i.fa.fa-users + | {{_ 'people'}} .main-body if loading.get +spinner @@ -39,28 +45,58 @@ template(name="peopleGeneral") template(name="peopleRow") tr - td.username {{ userData.username }} - td {{ userData.profile.fullname }} - td - if userData.isAdmin - | {{_ 'yes'}} - else - | {{_ 'no'}} - td {{ userData.emails.[0].address }} - td - if userData.emails.[0].verified - | {{_ 'yes'}} - else - | {{_ 'no'}} - td {{ moment userData.createdAt 'LLL' }} + if userData.loginDisabled + td.username <s>{{ userData.username }}</s> + else + td.username {{ userData.username }} + if userData.loginDisabled + td <s>{{ userData.profile.fullname }}</s> + else + td {{ userData.profile.fullname }} + if userData.loginDisabled + td + if userData.isAdmin + | <s>{{_ 'yes'}}</s> + else + | <s>{{_ 'no'}}</s> + else + td + if userData.isAdmin + | {{_ 'yes'}} + else + | {{_ 'no'}} + if userData.loginDisabled + td <s>{{ userData.emails.[0].address }}</s> + else + td {{ userData.emails.[0].address }} + if userData.loginDisabled + td + if userData.emails.[0].verified + | <s>{{_ 'yes'}}</s> + else + | <s>{{_ 'no'}}</s> + else + td + if userData.emails.[0].verified + | {{_ 'yes'}} + else + | {{_ 'no'}} + if userData.loginDisabled + td <s>{{ moment userData.createdAt 'LLL' }}</s> + else + td {{ moment userData.createdAt 'LLL' }} td if userData.loginDisabled | {{_ 'no'}} else | {{_ 'yes'}} - td {{_ userData.authenticationMethod }} + if userData.loginDisabled + td <s>{{_ userData.authenticationMethod }}</s> + else + td {{_ userData.authenticationMethod }} td a.edit-user + i.fa.fa-edit | {{_ 'edit'}} template(name="editUserPopup") diff --git a/client/components/settings/peopleBody.styl b/client/components/settings/peopleBody.styl index 80387611..c223e181 100644 --- a/client/components/settings/peopleBody.styl +++ b/client/components/settings/peopleBody.styl @@ -33,7 +33,7 @@ table padding: 0; button - min-width: 60px; + min-width: 90px; .content-wrapper margin-top: 10px diff --git a/client/components/settings/settingBody.jade b/client/components/settings/settingBody.jade index 04b635e8..835a3b81 100644 --- a/client/components/settings/settingBody.jade +++ b/client/components/settings/settingBody.jade @@ -4,22 +4,35 @@ template(name="setting") | {{_ 'error-notAuthorized'}} else .content-title + i.fa.fa-cog span {{_ 'settings'}} .content-body .side-menu ul li.active - a.js-setting-menu(data-id="registration-setting") {{_ 'registration'}} + a.js-setting-menu(data-id="registration-setting") + i.fa.fa-sign-in + | {{_ 'registration'}} li - a.js-setting-menu(data-id="email-setting") {{_ 'email'}} + a.js-setting-menu(data-id="email-setting") + i.fa.fa-envelope + | {{_ 'email'}} li - a.js-setting-menu(data-id="account-setting") {{_ 'accounts'}} + a.js-setting-menu(data-id="account-setting") + i.fa.fa-users + | {{_ 'accounts'}} li - a.js-setting-menu(data-id="announcement-setting") {{_ 'admin-announcement'}} + a.js-setting-menu(data-id="announcement-setting") + i.fa.fa-bullhorn + | {{_ 'admin-announcement'}} li - a.js-setting-menu(data-id="layout-setting") {{_ 'layout'}} + a.js-setting-menu(data-id="layout-setting") + i.fa.fa-object-group + | {{_ 'layout'}} li - a.js-setting-menu(data-id="webhook-setting") {{_ 'global-webhook'}} + a.js-setting-menu(data-id="webhook-setting") + i.fa.fa-globe + | {{_ 'global-webhook'}} .main-body if loading.get +spinner @@ -171,12 +184,6 @@ template(name='layoutSettings') .title {{_ 'custom-product-name'}} .form-group input.wekan-form-control#product-name(type="text", placeholder="" value="{{currentSetting.productName}}") - li.layout-form - .title {{_ 'add-custom-html-after-body-start'}} - textarea#customHTMLafterBodyStart.wekan-form-control= currentSetting.customHTMLafterBodyStart - li.layout-form - .title {{_ 'add-custom-html-before-body-end'}} - textarea#customHTMLbeforeBodyEnd.wekan-form-control= currentSetting.customHTMLbeforeBodyEnd li button.js-save-layout.primary {{_ 'save'}} diff --git a/client/components/settings/settingBody.js b/client/components/settings/settingBody.js index 4ff5aedd..319c066b 100644 --- a/client/components/settings/settingBody.js +++ b/client/components/settings/settingBody.js @@ -171,20 +171,12 @@ BlazeComponent.extendComponent({ const displayAuthenticationMethod = $('input[name=displayAuthenticationMethod]:checked').val() === 'true'; const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val(); - const customHTMLafterBodyStart = $('#customHTMLafterBodyStart') - .val() - .trim(); - const customHTMLbeforeBodyEnd = $('#customHTMLbeforeBodyEnd') - .val() - .trim(); try { Settings.update(Settings.findOne()._id, { $set: { productName, hideLogo: hideLogoChange, - customHTMLafterBodyStart, - customHTMLbeforeBodyEnd, displayAuthenticationMethod, defaultAuthenticationMethod, }, diff --git a/client/components/settings/settingBody.styl b/client/components/settings/settingBody.styl index bcbd2ea1..d6ac32b2 100644 --- a/client/components/settings/settingBody.styl +++ b/client/components/settings/settingBody.styl @@ -41,15 +41,18 @@ &:hover background #fff box-shadow 0 1px 2px rgba(0,0,0,0.15); + a @extends .flex padding: 1rem 0 1rem 1rem width: 100% - 5rem - span font-size: 13px + i + margin-right: 20px + .main-body padding: 0.1em 1em -webkit-user-select: text // Safari 3.1+ diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index ccfadc0c..ebcd8486 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -37,11 +37,12 @@ template(name='homeSidebar') template(name="membersWidget") .board-widget.board-widget-members h3 - i.fa.fa-user + i.fa.fa-users | {{_ 'members'}} unless currentUser.isCommentOnly - a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right - i.board-header-btn-icon.fa.fa-cog + unless currentUser.isWorker + a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}").right + i.board-header-btn-icon.fa.fa-cog .board-widget-content each currentBoard.activeMembers @@ -71,6 +72,108 @@ template(name="boardChangeColorPopup") if isSelected i.fa.fa-check +template(name="boardCardSettingsPopup") + form.board-card-settings + h3 {{_ 'show-on-card'}} + div.check-div + a.flex.js-field-has-receiveddate(class="{{#if allowsReceivedDate}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsReceivedDate}}is-checked{{/if}}") + span + i.fa.fa-sign-out + | {{_ 'card-received'}} + div.check-div + a.flex.js-field-has-startdate(class="{{#if allowsStartDate}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsStartDate}}is-checked{{/if}}") + span + i.fa.fa-hourglass-start + | {{_ 'card-start'}} + div.check-div + a.flex.js-field-has-duedate(class="{{#if allowsDueDate}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsDueDate}}is-checked{{/if}}") + span + i.fa.fa-sign-in + | {{_ 'card-due'}} + div.check-div + a.flex.js-field-has-enddate(class="{{#if allowsEndDate}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsEndDate}}is-checked{{/if}}") + span + i.fa.fa-hourglass-end + | {{_ 'card-end'}} + div.check-div + a.flex.js-field-has-members(class="{{#if allowsMembers}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsMembers}}is-checked{{/if}}") + span + i.fa.fa-users + | {{_ 'members'}} + div.check-div + a.flex.js-field-has-assignee(class="{{#if allowsAssignee}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsAssignee}}is-checked{{/if}}") + span + i.fa.fa-user + | {{_ 'assignee'}} + div.check-div + a.flex.js-field-has-assigned-by(class="{{#if allowsAssignedBy}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsAssignedBy}}is-checked{{/if}}") + span + i.fa.fa-shopping-cart + | {{_ 'assigned-by'}} + div.check-div + a.flex.js-field-has-requested-by(class="{{#if allowsRequestedBy}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsRequestedBy}}is-checked{{/if}}") + span + i.fa.fa-user-plus + | {{_ 'requested-by'}} + div.check-div + a.flex.js-field-has-labels(class="{{#if allowsLabels}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsLabels}}is-checked{{/if}}") + span + i.fa.fa-tags + | {{_ 'labels'}} + div.check-div + a.flex.js-field-has-description-title(class="{{#if allowsDescriptionTitle}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsDescriptionTitle}}is-checked{{/if}}") + span + i.fa.fa-align-left + | {{_ 'description'}} + | {{_ 'title'}} + div.check-div + a.flex.js-field-has-description-text(class="{{#if allowsDescriptionText}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsDescriptionText}}is-checked{{/if}}") + span + i.fa.fa-align-left + | {{_ 'description'}} + | {{_ 'custom-field-text'}} + div.check-div + a.flex.js-field-has-checklists(class="{{#if allowsChecklists}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsChecklists}}is-checked{{/if}}") + span + i.fa.fa-check + | {{_ 'checklists'}} + div.check-div + a.flex.js-field-has-subtasks(class="{{#if allowsSubtasks}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsSubtasks}}is-checked{{/if}}") + span + i.fa.fa-sitemap + | {{_ 'subtasks'}} + div.check-div + a.flex.js-field-has-attachments(class="{{#if allowsAttachments}}is-checked{{/if}}") + .materialCheckBox(class="{{#if allowsAttachments}}is-checked{{/if}}") + span + i.fa.fa-paperclip + | {{_ 'attachments'}} + //div.check-div + // a.flex.js-field-has-comments(class="{{#if allowsComments}}is-checked{{/if}}") + // .materialCheckBox(class="{{#if allowsComments}}is-checked{{/if}}") + // span + // i.fa.fa-comment-o + // | {{_ 'comment'}} + //div.check-div + // a.flex.js-field-has-activities(class="{{#if allowsActivities}}is-checked{{/if}}") + // .materialCheckBox(class="{{#if allowsActivities}}is-checked{{/if}}") + // span + // i.fa.fa-history + // | {{_ 'activities'}} + template(name="boardSubtaskSettingsPopup") form.board-subtask-settings h3 {{_ 'show-parent-in-minicard'}} @@ -130,7 +233,9 @@ template(name="chooseBoardSource") template(name="archiveBoardPopup") p {{_ 'close-board-pop'}} - button.js-confirm.negate.full(type="submit") {{_ 'archive'}} + button.js-confirm.negate.full(type="submit") + i.fa.fa-archive + | {{_ 'archive'}} template(name="outgoingWebhooksPopup") each integrations @@ -163,37 +268,88 @@ template(name="outgoingWebhooksPopup") template(name="boardMenuPopup") ul.pop-over-list li: a.js-custom-fields {{_ 'custom-fields'}} - li: a.js-open-archives {{_ 'archived-items'}} + li + a.js-open-archives + i.fa.fa-archive + | {{_ 'archived-items'}} if currentUser.isBoardAdmin - li: a.js-change-board-color {{_ 'board-change-color'}} + li + a.js-change-board-color + i.fa.fa-paint-brush + | {{_ 'board-change-color'}} + //- XXX Language should be handled by sandstorm, but for now display a language selection link in the board menu. This link is normally present in the header bar that is not displayed on sandstorm. if isSandstorm - li: a.js-change-language {{_ 'language'}} + li + a.js-change-language + i.fa.fa-flag + | {{_ 'language'}} unless isSandstorm if currentUser.isBoardAdmin hr ul.pop-over-list - li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}} - unless currentBoard.isTemplatesBoard - li: a.js-archive-board {{_ 'archive-board'}} - li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}} - hr - ul.pop-over-list - li: a.js-subtask-settings {{_ 'subtask-settings'}} + li + a(href="{{exportUrl}}", download="{{exportFilename}}") + i.fa.fa-share-alt + | {{_ 'export-board'}} + li + a.js-outgoing-webhooks + i.fa.fa-globe + | {{_ 'outgoing-webhooks'}} + li + a.js-card-settings + i.fa.fa-id-card-o + | {{_ 'card-settings'}} + li + a.js-subtask-settings + i.fa.fa-sitemap + | {{_ 'subtask-settings'}} + unless currentBoard.isTemplatesBoard + hr + ul.pop-over-list + li + a.js-archive-board + i.fa.fa-arrow-right + i.fa.fa-archive + | {{_ 'archive-board'}} if isSandstorm hr ul.pop-over-list - li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}} - li: a.js-import-board {{_ 'import-board-c'}} - li: a.js-archive-board {{_ 'archive-board'}} - li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}} + li + a(href="{{exportUrl}}", download="{{exportFilename}}") + i.fa.fa-share-alt + i.fa.fa-sign-out + | {{_ 'export-board'}} + li + a.js-import-board + i.fa.fa-share-alt + i.fa.fa-sign-in + | {{_ 'import-board-c'}} + li + a.js-archive-board + i.fa.fa-arrow-right + i.fa.fa-archive + | {{_ 'archive-board'}} + li + a.js-outgoing-webhooks + i.fa.fa-globe + | {{_ 'outgoing-webhooks'}} + hr + ul.pop-over-list + li + a.js-card-settings + i.fa.fa-id-card-o + | {{_ 'card-settings'}} hr ul.pop-over-list - li: a.js-subtask-settings {{_ 'subtask-settings'}} + li + a.js-subtask-settings + i.fa.fa-sitemap + | {{_ 'subtask-settings'}} template(name="labelsWidget") .board-widget.board-widget-labels @@ -203,7 +359,7 @@ template(name="labelsWidget") .board-widget-content each currentBoard.labels a.card-label(class="card-label-{{color}}" - class="{{#if currentUser.isNotCommentOnly}}js-label{{/if}}") + class="{{#if currentUser.isNotCommentOnly}}{{#if currentUser.isNotWorker}}js-label{{/if}}{{/if}}") span.card-label-name +viewer = name @@ -232,12 +388,12 @@ template(name="memberPopup") a.js-change-role | {{_ 'change-permissions'}} span.quiet (#{memberType}) - li - if $eq currentUser._id userId - a.js-leave-member {{_ 'leave-board'}} - else if currentUser.isBoardAdmin - a.js-remove-member {{_ 'remove-from-board'}} - + unless currentUser.isWorker + li + if $eq currentUser._id userId + a.js-leave-member {{_ 'leave-board'}} + else if currentUser.isBoardAdmin + a.js-remove-member {{_ 'remove-from-board'}} template(name="removeMemberPopup") p {{_ 'remove-member-pop' name=user.profile.fullname username=user.username boardTitle=board.title}} @@ -301,6 +457,12 @@ template(name="changePermissionsPopup") if isCommentOnly i.fa.fa-check span.sub-name {{_ 'comment-only-desc'}} + li + a(class="{{#if isLastAdmin}}disabled{{else}}js-set-worker{{/if}}") + | {{_ 'worker'}} + if isWorker + i.fa.fa-check + span.sub-name {{_ 'worker-desc'}} if isLastAdmin hr p.quiet.bottom {{_ 'last-admin-desc'}} diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js index f1ccfb1e..8e640564 100644 --- a/client/components/sidebar/sidebar.js +++ b/client/components/sidebar/sidebar.js @@ -1,3 +1,5 @@ +import { Cookies } from 'meteor/ostrio:cookies'; +const cookies = new Cookies(); Sidebar = null; const defaultView = 'home'; @@ -107,7 +109,14 @@ BlazeComponent.extendComponent({ 'click .js-toggle-sidebar': this.toggle, 'click .js-back-home': this.setView, 'click .js-toggle-minicard-label-text'() { - Meteor.call('toggleMinicardLabelText'); + currentUser = Meteor.user(); + if (currentUser) { + Meteor.call('toggleMinicardLabelText'); + } else if (cookies.has('hiddenMinicardLabelText')) { + cookies.remove('hiddenMinicardLabelText'); + } else { + cookies.set('hiddenMinicardLabelText', 'true'); + } }, 'click .js-shortcuts'() { FlowRouter.go('shortcuts'); @@ -121,7 +130,14 @@ Blaze.registerHelper('Sidebar', () => Sidebar); Template.homeSidebar.helpers({ hiddenMinicardLabelText() { - return Meteor.user().hasHiddenMinicardLabelText(); + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).hiddenMinicardLabelText; + } else if (cookies.has('hiddenMinicardLabelText')) { + return true; + } else { + return false; + } }, }); @@ -145,10 +161,13 @@ Template.memberPopup.helpers({ const currentBoard = Boards.findOne(Session.get('currentBoard')); const commentOnly = currentBoard.hasCommentOnly(this.userId); const noComments = currentBoard.hasNoComments(this.userId); + const worker = currentBoard.hasWorker(this.userId); if (commentOnly) { return TAPi18n.__('comment-only').toLowerCase(); } else if (noComments) { return TAPi18n.__('no-comments').toLowerCase(); + } else if (worker) { + return TAPi18n.__('worker').toLowerCase(); } else { return TAPi18n.__(type).toLowerCase(); } @@ -189,6 +208,7 @@ Template.boardMenuPopup.events({ 'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'), 'click .js-import-board': Popup.open('chooseBoardSource'), 'click .js-subtask-settings': Popup.open('boardSubtaskSettings'), + 'click .js-card-settings': Popup.open('boardCardSettings'), }); Template.boardMenuPopup.helpers({ @@ -251,6 +271,14 @@ Template.membersWidget.helpers({ const user = Meteor.user(); return user && user.isInvitedTo(Session.get('currentBoard')); }, + isWorker() { + const user = Meteor.user(); + if (user) { + return Meteor.call(Boards.hasWorker(user.memberId)); + } else { + return false; + } + }, }); Template.membersWidget.events({ @@ -445,6 +473,10 @@ BlazeComponent.extendComponent({ return this.currentBoard.allowsSubtasks; }, + allowsReceivedDate() { + return this.currentBoard.allowsReceivedDate; + }, + isBoardSelected() { return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id; }, @@ -560,6 +592,359 @@ BlazeComponent.extendComponent({ BlazeComponent.extendComponent({ onCreated() { + this.currentBoard = Boards.findOne(Session.get('currentBoard')); + }, + + allowsReceivedDate() { + return this.currentBoard.allowsReceivedDate; + }, + + allowsStartDate() { + return this.currentBoard.allowsStartDate; + }, + + allowsDueDate() { + return this.currentBoard.allowsDueDate; + }, + + allowsEndDate() { + return this.currentBoard.allowsEndDate; + }, + + allowsSubtasks() { + return this.currentBoard.allowsSubtasks; + }, + + allowsMembers() { + return this.currentBoard.allowsMembers; + }, + + allowsAssignee() { + return this.currentBoard.allowsAssignee; + }, + + allowsAssignedBy() { + return this.currentBoard.allowsAssignedBy; + }, + + allowsRequestedBy() { + return this.currentBoard.allowsRequestedBy; + }, + + allowsLabels() { + return this.currentBoard.allowsLabels; + }, + + allowsChecklists() { + return this.currentBoard.allowsChecklists; + }, + + allowsAttachments() { + return this.currentBoard.allowsAttachments; + }, + + allowsComments() { + return this.currentBoard.allowsComments; + }, + + allowsDescriptionTitle() { + return this.currentBoard.allowsDescriptionTitle; + }, + + allowsDescriptionText() { + return this.currentBoard.allowsDescriptionText; + }, + + isBoardSelected() { + return this.currentBoard.dateSettingsDefaultBoardID; + }, + + isNullBoardSelected() { + return ( + this.currentBoard.dateSettingsDefaultBoardId === null || + this.currentBoard.dateSettingsDefaultBoardId === undefined + ); + }, + + boards() { + return Boards.find( + { + archived: false, + 'members.userId': Meteor.userId(), + }, + { + sort: ['title'], + }, + ); + }, + + lists() { + return Lists.find( + { + boardId: this.currentBoard._id, + archived: false, + }, + { + sort: ['title'], + }, + ); + }, + + hasLists() { + return this.lists().count() > 0; + }, + + isListSelected() { + return ( + this.currentBoard.dateSettingsDefaultBoardId === this.currentData()._id + ); + }, + + events() { + return [ + { + 'click .js-field-has-receiveddate'(evt) { + evt.preventDefault(); + this.currentBoard.allowsReceivedDate = !this.currentBoard + .allowsReceivedDate; + this.currentBoard.setAllowsReceivedDate( + this.currentBoard.allowsReceivedDate, + ); + $(`.js-field-has-receiveddate ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsReceivedDate, + ); + $('.js-field-has-receiveddate').toggleClass( + CKCLS, + this.currentBoard.allowsReceivedDate, + ); + }, + 'click .js-field-has-startdate'(evt) { + evt.preventDefault(); + this.currentBoard.allowsStartDate = !this.currentBoard + .allowsStartDate; + this.currentBoard.setAllowsStartDate( + this.currentBoard.allowsStartDate, + ); + $(`.js-field-has-startdate ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsStartDate, + ); + $('.js-field-has-startdate').toggleClass( + CKCLS, + this.currentBoard.allowsStartDate, + ); + }, + 'click .js-field-has-enddate'(evt) { + evt.preventDefault(); + this.currentBoard.allowsEndDate = !this.currentBoard.allowsEndDate; + this.currentBoard.setAllowsEndDate(this.currentBoard.allowsEndDate); + $(`.js-field-has-enddate ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsEndDate, + ); + $('.js-field-has-enddate').toggleClass( + CKCLS, + this.currentBoard.allowsEndDate, + ); + }, + 'click .js-field-has-duedate'(evt) { + evt.preventDefault(); + this.currentBoard.allowsDueDate = !this.currentBoard.allowsDueDate; + this.currentBoard.setAllowsDueDate(this.currentBoard.allowsDueDate); + $(`.js-field-has-duedate ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsDueDate, + ); + $('.js-field-has-duedate').toggleClass( + CKCLS, + this.currentBoard.allowsDueDate, + ); + }, + 'click .js-field-has-subtasks'(evt) { + evt.preventDefault(); + this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks; + this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks); + $(`.js-field-has-subtasks ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsSubtasks, + ); + $('.js-field-has-subtasks').toggleClass( + CKCLS, + this.currentBoard.allowsSubtasks, + ); + }, + 'click .js-field-has-members'(evt) { + evt.preventDefault(); + this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers; + this.currentBoard.setAllowsMembers(this.currentBoard.allowsMembers); + $(`.js-field-has-members ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsMembers, + ); + $('.js-field-has-members').toggleClass( + CKCLS, + this.currentBoard.allowsMembers, + ); + }, + 'click .js-field-has-assignee'(evt) { + evt.preventDefault(); + this.currentBoard.allowsAssignee = !this.currentBoard.allowsAssignee; + this.currentBoard.setAllowsAssignee(this.currentBoard.allowsAssignee); + $(`.js-field-has-assignee ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsAssignee, + ); + $('.js-field-has-assignee').toggleClass( + CKCLS, + this.currentBoard.allowsAssignee, + ); + }, + 'click .js-field-has-assigned-by'(evt) { + evt.preventDefault(); + this.currentBoard.allowsAssignedBy = !this.currentBoard + .allowsAssignedBy; + this.currentBoard.setAllowsAssignedBy( + this.currentBoard.allowsAssignedBy, + ); + $(`.js-field-has-assigned-by ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsAssignedBy, + ); + $('.js-field-has-assigned-by').toggleClass( + CKCLS, + this.currentBoard.allowsAssignedBy, + ); + }, + 'click .js-field-has-requested-by'(evt) { + evt.preventDefault(); + this.currentBoard.allowsRequestedBy = !this.currentBoard + .allowsRequestedBy; + this.currentBoard.setAllowsRequestedBy( + this.currentBoard.allowsRequestedBy, + ); + $(`.js-field-has-requested-by ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsRequestedBy, + ); + $('.js-field-has-requested-by').toggleClass( + CKCLS, + this.currentBoard.allowsRequestedBy, + ); + }, + 'click .js-field-has-labels'(evt) { + evt.preventDefault(); + this.currentBoard.allowsLabels = !this.currentBoard.allowsLabels; + this.currentBoard.setAllowsLabels(this.currentBoard.allowsLabels); + $(`.js-field-has-labels ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsAssignee, + ); + $('.js-field-has-labels').toggleClass( + CKCLS, + this.currentBoard.allowsLabels, + ); + }, + 'click .js-field-has-description-title'(evt) { + evt.preventDefault(); + this.currentBoard.allowsDescriptionTitle = !this.currentBoard + .allowsDescriptionTitle; + this.currentBoard.setAllowsDescriptionTitle( + this.currentBoard.allowsDescriptionTitle, + ); + $(`.js-field-has-description-title ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsDescriptionTitle, + ); + $('.js-field-has-description-title').toggleClass( + CKCLS, + this.currentBoard.allowsDescriptionTitle, + ); + }, + 'click .js-field-has-description-text'(evt) { + evt.preventDefault(); + this.currentBoard.allowsDescriptionText = !this.currentBoard + .allowsDescriptionText; + this.currentBoard.setAllowsDescriptionText( + this.currentBoard.allowsDescriptionText, + ); + $(`.js-field-has-description-text ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsDescriptionText, + ); + $('.js-field-has-description-text').toggleClass( + CKCLS, + this.currentBoard.allowsDescriptionText, + ); + }, + 'click .js-field-has-checklists'(evt) { + evt.preventDefault(); + this.currentBoard.allowsChecklists = !this.currentBoard + .allowsChecklists; + this.currentBoard.setAllowsChecklists( + this.currentBoard.allowsChecklists, + ); + $(`.js-field-has-checklists ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsChecklists, + ); + $('.js-field-has-checklists').toggleClass( + CKCLS, + this.currentBoard.allowsChecklists, + ); + }, + 'click .js-field-has-attachments'(evt) { + evt.preventDefault(); + this.currentBoard.allowsAttachments = !this.currentBoard + .allowsAttachments; + this.currentBoard.setAllowsAttachments( + this.currentBoard.allowsAttachments, + ); + $(`.js-field-has-attachments ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsAttachments, + ); + $('.js-field-has-attachments').toggleClass( + CKCLS, + this.currentBoard.allowsAttachments, + ); + }, + 'click .js-field-has-comments'(evt) { + evt.preventDefault(); + this.currentBoard.allowsComments = !this.currentBoard.allowsComments; + this.currentBoard.setAllowsComments(this.currentBoard.allowsComments); + $(`.js-field-has-comments ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsComments, + ); + $('.js-field-has-comments').toggleClass( + CKCLS, + this.currentBoard.allowsComments, + ); + }, + 'click .js-field-has-activities'(evt) { + evt.preventDefault(); + this.currentBoard.allowsActivities = !this.currentBoard + .allowsActivities; + this.currentBoard.setAllowsActivities( + this.currentBoard.allowsActivities, + ); + $(`.js-field-has-activities ${MCB}`).toggleClass( + CKCLS, + this.currentBoard.allowsActivities, + ); + $('.js-field-has-activities').toggleClass( + CKCLS, + this.currentBoard.allowsActivities, + ); + }, + }, + ]; + }, +}).register('boardCardSettingsPopup'); + +BlazeComponent.extendComponent({ + onCreated() { this.error = new ReactiveVar(''); this.loading = new ReactiveVar(false); }, @@ -628,7 +1013,7 @@ BlazeComponent.extendComponent({ }).register('addMemberPopup'); Template.changePermissionsPopup.events({ - 'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only'( + 'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only, click .js-set-worker'( event, ) { const currentBoard = Boards.findOne(Session.get('currentBoard')); @@ -638,11 +1023,13 @@ Template.changePermissionsPopup.events({ 'js-set-comment-only', ); const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments'); + const isWorker = $(event.currentTarget).hasClass('js-set-worker'); currentBoard.setMemberPermission( memberId, isAdmin, isNoComments, isCommentOnly, + isWorker, ); Popup.back(1); }, @@ -659,7 +1046,8 @@ Template.changePermissionsPopup.helpers({ return ( !currentBoard.hasAdmin(this.userId) && !currentBoard.hasNoComments(this.userId) && - !currentBoard.hasCommentOnly(this.userId) + !currentBoard.hasCommentOnly(this.userId) && + !currentBoard.hasWorker(this.userId) ); }, @@ -679,6 +1067,13 @@ Template.changePermissionsPopup.helpers({ ); }, + isWorker() { + const currentBoard = Boards.findOne(Session.get('currentBoard')); + return ( + !currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId) + ); + }, + isLastAdmin() { const currentBoard = Boards.findOne(Session.get('currentBoard')); return ( diff --git a/client/components/sidebar/sidebar.styl b/client/components/sidebar/sidebar.styl index 740186b5..c1047277 100644 --- a/client/components/sidebar/sidebar.styl +++ b/client/components/sidebar/sidebar.styl @@ -109,7 +109,7 @@ color: darken(white, 40%) .board-sidebar - width: 248px + width: 548px right: -@width transition: top .1s, right .1s, width .1s diff --git a/client/components/sidebar/sidebarArchives.jade b/client/components/sidebar/sidebarArchives.jade index 466d2cb0..56423ad7 100644 --- a/client/components/sidebar/sidebarArchives.jade +++ b/client/components/sidebar/sidebarArchives.jade @@ -2,54 +2,60 @@ template(name="archivesSidebar") if isArchiveReady.get +basicTabs(tabs=tabs) +tabContent(slug="cards") - p.quiet - a.js-restore-all-cards {{_ 'restore-all'}} - | - - a.js-delete-all-cards {{_ 'delete-all'}} + unless isWorker + p.quiet + a.js-restore-all-cards {{_ 'restore-all'}} + | - + a.js-delete-all-cards {{_ 'delete-all'}} each archivedCards .minicard-wrapper.js-minicard +minicard(this) if currentUser.isBoardMember - p.quiet - a.js-restore-card {{_ 'restore'}} - | - - a.js-delete-card {{_ 'delete'}} + unless isWorker + p.quiet + a.js-restore-card {{_ 'restore'}} + | - + a.js-delete-card {{_ 'delete'}} if cardIsInArchivedList p.quiet.small ({{_ 'warn-list-archived'}}) else p.no-items-message {{_ 'no-archived-cards'}} +tabContent(slug="lists") - p.quiet - a.js-restore-all-lists {{_ 'restore-all'}} - | - - a.js-delete-all-lists {{_ 'delete-all'}} + unless isWorker + p.quiet + a.js-restore-all-lists {{_ 'restore-all'}} + | - + a.js-delete-all-lists {{_ 'delete-all'}} ul.archived-lists each archivedLists li.archived-lists-item = title if currentUser.isBoardMember - p.quiet - a.js-restore-list {{_ 'restore'}} - | - - a.js-delete-list {{_ 'delete'}} + unless isWorker + p.quiet + a.js-restore-list {{_ 'restore'}} + | - + a.js-delete-list {{_ 'delete'}} else li.no-items-message {{_ 'no-archived-lists'}} +tabContent(slug="swimlanes") - p.quiet - a.js-restore-all-swimlanes {{_ 'restore-all'}} - | - - a.js-delete-all-swimlanes {{_ 'delete-all'}} + unless isWorker + p.quiet + a.js-restore-all-swimlanes {{_ 'restore-all'}} + | - + a.js-delete-all-swimlanes {{_ 'delete-all'}} ul.archived-lists each archivedSwimlanes li.archived-lists-item = title if currentUser.isBoardMember - p.quiet - a.js-restore-swimlane {{_ 'restore'}} - | - - a.js-delete-swimlane {{_ 'delete'}} + unless isWorker + p.quiet + a.js-restore-swimlane {{_ 'restore'}} + | - + a.js-delete-swimlane {{_ 'delete'}} else li.no-items-message {{_ 'no-archived-swimlanes'}} else diff --git a/client/components/sidebar/sidebarArchives.js b/client/components/sidebar/sidebarArchives.js index a4846561..75b694e9 100644 --- a/client/components/sidebar/sidebarArchives.js +++ b/client/components/sidebar/sidebarArchives.js @@ -139,3 +139,12 @@ BlazeComponent.extendComponent({ ]; }, }).register('archivesSidebar'); + +Template.archivesSidebar.helpers({ + isWorker() { + const currentBoard = Boards.findOne(Session.get('currentBoard')); + return ( + !currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId) + ); + }, +}); diff --git a/client/components/sidebar/sidebarFilters.jade b/client/components/sidebar/sidebarFilters.jade index 5f929cb9..7f31dada 100644 --- a/client/components/sidebar/sidebarFilters.jade +++ b/client/components/sidebar/sidebarFilters.jade @@ -117,13 +117,14 @@ template(name="multiselectionSidebar") i.fa.fa-check else if someSelectedElementHave 'member' _id i.fa.fa-ellipsis-h - hr - a.sidebar-btn.js-move-selection - i.fa.fa-share - span {{_ 'move-selection'}} - a.sidebar-btn.js-archive-selection - i.fa.fa-archive - span {{_ 'archive-selection'}} + unless currentUser.isWorker + hr + a.sidebar-btn.js-move-selection + i.fa.fa-share + span {{_ 'move-selection'}} + a.sidebar-btn.js-archive-selection + i.fa.fa-archive + span {{_ 'archive-selection'}} template(name="disambiguateMultiLabelPopup") p {{_ 'what-to-do'}} diff --git a/client/components/swimlanes/swimlaneHeader.js b/client/components/swimlanes/swimlaneHeader.js index 6f8029fd..3032966d 100644 --- a/client/components/swimlanes/swimlaneHeader.js +++ b/client/components/swimlanes/swimlaneHeader.js @@ -1,3 +1,5 @@ +import { Cookies } from 'meteor/ostrio:cookies'; +const cookies = new Cookies(); const { calculateIndexData } = Utils; let swimlaneColors; @@ -30,7 +32,14 @@ BlazeComponent.extendComponent({ Template.swimlaneHeader.helpers({ showDesktopDragHandles() { - return Meteor.user().hasShowDesktopDragHandles(); + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).showDesktopDragHandles; + } else if (cookies.has('showDesktopDragHandles')) { + return true; + } else { + return false; + } }, }); diff --git a/client/components/swimlanes/swimlanes.jade b/client/components/swimlanes/swimlanes.jade index 9eab6054..9b00d9e8 100644 --- a/client/components/swimlanes/swimlanes.jade +++ b/client/components/swimlanes/swimlanes.jade @@ -1,24 +1,25 @@ template(name="swimlane") .swimlane +swimlaneHeader - .swimlane.js-lists.js-swimlane - if isMiniScreen - if currentListIsInThisSwimlane _id - +list(currentList) - unless currentList + unless collapseSwimlane + .swimlane.js-lists.js-swimlane + if isMiniScreen + if currentListIsInThisSwimlane _id + +list(currentList) + unless currentList + each lists + +miniList(this) + if currentUser.isBoardMember + unless currentUser.isCommentOnly + +addListForm + else each lists - +miniList(this) + +list(this) + if currentCardIsInThisList _id ../_id + +cardDetails(currentCard) if currentUser.isBoardMember unless currentUser.isCommentOnly +addListForm - else - each lists - +list(this) - if currentCardIsInThisList _id ../_id - +cardDetails(currentCard) - if currentUser.isBoardMember - unless currentUser.isCommentOnly - +addListForm template(name="listsGroup") .swimlane.list-group.js-lists @@ -42,19 +43,20 @@ template(name="listsGroup") +addListForm template(name="addListForm") - .list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}") - .list-header-add - +inlinedForm(autoclose=false) - input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}" - autocomplete="off" autofocus) - .edit-controls.clearfix - button.primary.confirm(type="submit") {{_ 'save'}} - unless currentBoard.isTemplatesBoard - unless currentBoard.isTemplateBoard - span.quiet - | {{_ 'or'}} - a.js-list-template {{_ 'template'}} - else - a.open-list-composer.js-open-inlined-form - i.fa.fa-plus - | {{_ 'add-list'}} + unless currentUser.isWorker + .list.list-composer.js-list-composer(class="{{#if isMiniScreen}}mini-list{{/if}}") + .list-header-add + +inlinedForm(autoclose=false) + input.list-name-input.full-line(type="text" placeholder="{{_ 'add-list'}}" + autocomplete="off" autofocus) + .edit-controls.clearfix + button.primary.confirm(type="submit") {{_ 'save'}} + unless currentBoard.isTemplatesBoard + unless currentBoard.isTemplateBoard + span.quiet + | {{_ 'or'}} + a.js-list-template {{_ 'template'}} + else + a.open-list-composer.js-open-inlined-form + i.fa.fa-plus + | {{_ 'add-list'}} diff --git a/client/components/swimlanes/swimlanes.js b/client/components/swimlanes/swimlanes.js index 8faad870..b7a55ce6 100644 --- a/client/components/swimlanes/swimlanes.js +++ b/client/components/swimlanes/swimlanes.js @@ -1,3 +1,5 @@ +import { Cookies } from 'meteor/ostrio:cookies'; +const cookies = new Cookies(); const { calculateIndex, enableClickOnTouch } = Utils; function currentListIsInThisSwimlane(swimlaneId) { @@ -14,7 +16,7 @@ function currentCardIsInThisList(listId, swimlaneId) { if ( currentUser && currentUser.profile && - currentUser.profile.boardView === 'board-view-swimlanes' + Utils.boardView() === 'board-view-swimlanes' ) return ( currentCard && @@ -53,18 +55,6 @@ function initSortable(boardComponent, $listsDom) { }, }; - if (Utils.isMiniScreen) { - $listsDom.sortable({ - handle: '.js-list-handle', - }); - } - - if (!Utils.isMiniScreen && showDesktopDragHandles) { - $listsDom.sortable({ - handle: '.js-list-header', - }); - } - $listsDom.sortable({ tolerance: 'pointer', helper: 'clone', @@ -104,19 +94,64 @@ function initSortable(boardComponent, $listsDom) { return ( Meteor.user() && Meteor.user().isBoardMember() && - !Meteor.user().isCommentOnly() + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() ); } - // Disable drag-dropping while in multi-selection mode, or if the current user - // is not a board member boardComponent.autorun(() => { + let showDesktopDragHandles = false; + currentUser = Meteor.user(); + if (currentUser) { + showDesktopDragHandles = (currentUser.profile || {}) + .showDesktopDragHandles; + } else if (cookies.has('showDesktopDragHandles')) { + showDesktopDragHandles = true; + } else { + showDesktopDragHandles = false; + } + + if (!Utils.isMiniScreen() && showDesktopDragHandles) { + $listsDom.sortable({ + handle: '.js-list-handle', + }); + } else if (!Utils.isMiniScreen() && !showDesktopDragHandles) { + $listsDom.sortable({ + handle: '.js-list-header', + }); + } + const $listDom = $listsDom; if ($listDom.data('sortable')) { $listsDom.sortable( 'option', 'disabled', - MultiSelection.isActive() || !userIsMember(), + // Disable drag-dropping when user is not member/is worker/is miniscreen + !userIsMember(), + // Not disable drag-dropping while in multi-selection mode + // MultiSelection.isActive() || !userIsMember(), + ); + } + + if ($listDom.data('sortable')) { + $listsDom.sortable( + 'option', + 'disabled', + // Disable drag-dropping when user is not member/is worker/is miniscreen + Meteor.user().isWorker(), + // Not disable drag-dropping while in multi-selection mode + // MultiSelection.isActive() || !userIsMember(), + ); + } + + if ($listDom.data('sortable')) { + $listsDom.sortable( + 'option', + 'disabled', + // Disable drag-dropping when user is not member/is worker/is miniscreen + Utils.isMiniScreen(), + // Not disable drag-dropping while in multi-selection mode + // MultiSelection.isActive() || !userIsMember(), ); } }); @@ -163,8 +198,20 @@ BlazeComponent.extendComponent({ // the user will legitimately expect to be able to select some text with // his mouse. + let showDesktopDragHandles = false; + currentUser = Meteor.user(); + if (currentUser) { + showDesktopDragHandles = (currentUser.profile || {}) + .showDesktopDragHandles; + } else if (cookies.has('showDesktopDragHandles')) { + showDesktopDragHandles = true; + } else { + showDesktopDragHandles = false; + } + const noDragInside = ['a', 'input', 'textarea', 'p'].concat( - Util.isMiniScreen || (!Util.isMiniScreen && showDesktopDragHandles) + Utils.isMiniScreen() || + (!Utils.isMiniScreen() && showDesktopDragHandles) ? ['.js-list-handle', '.js-swimlane-header-handle'] : ['.js-list-header'], ); @@ -245,13 +292,21 @@ BlazeComponent.extendComponent({ Template.swimlane.helpers({ showDesktopDragHandles() { - return Meteor.user().hasShowDesktopDragHandles(); + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).showDesktopDragHandles; + } else if (cookies.has('showDesktopDragHandles')) { + return true; + } else { + return false; + } }, canSeeAddList() { return ( Meteor.user() && Meteor.user().isBoardMember() && - !Meteor.user().isCommentOnly() + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() ); }, }); diff --git a/client/components/swimlanes/swimlanes.styl b/client/components/swimlanes/swimlanes.styl index 164c66d5..ca5611cc 100644 --- a/client/components/swimlanes/swimlanes.styl +++ b/client/components/swimlanes/swimlanes.styl @@ -1,5 +1,41 @@ @import 'nib' +/* +// Minimize swimlanes start https://www.w3schools.com/howto/howto_js_accordion.asp + +.accordion + cursor: pointer + width: 30px + height: 20px + border: none + outline: none + font-size: 18px + transition: 0.4s + padding-top: 0px + margin-top: 0px + +.accordion:after + // Unicode triagle right: + content: '\25B6' + color: #777 + font-weight: bold + float: left + +.active:after + // Unicode triangle down: + content: '\25BC' + +.panel + width: 100% + max-height: 0 + overflow: hidden + transition: max-height 0.2s ease-out + margin: 0px + padding: 0px + +// Minimize swimlanes end https://www.w3schools.com/howto/howto_js_accordion.asp +*/ + .swimlane // Even if this background color is the same as the body we can't leave it // transparent, because that won't work during a swimlane drag. @@ -25,22 +61,22 @@ cursor: grabbing .swimlane-header-wrap - display: flex; - flex-direction: row; - flex: 1 0 100%; - background-color: #ccc; + display: flex + flex-direction: row + flex: 1 0 100% + background-color: #ccc .swimlane-header - font-size: 14px; + font-size: 14px padding: 5px 5px - font-weight: bold; - min-height: 9px; - width: 100%; - overflow: hidden; - -o-text-overflow: ellipsis; - text-overflow: ellipsis; - word-wrap: break-word; - text-align: center; + font-weight: bold + min-height: 9px + width: 100% + overflow: hidden + -o-text-overflow: ellipsis + text-overflow: ellipsis + word-wrap: break-word + text-align: center .swimlane-header-menu position: absolute diff --git a/client/components/users/userAvatar.jade b/client/components/users/userAvatar.jade index ebfa48ba..7f2067ce 100644 --- a/client/components/users/userAvatar.jade +++ b/client/components/users/userAvatar.jade @@ -73,6 +73,7 @@ template(name="cardMemberPopup") p.quiet @{{ user.username }} ul.pop-over-list if currentUser.isNotCommentOnly + if currentUser.isNotWorker li: a.js-remove-member {{_ 'remove-member-from-card'}} if $eq currentUser._id user._id diff --git a/client/components/users/userHeader.jade b/client/components/users/userHeader.jade index 50a80396..9306d21d 100644 --- a/client/components/users/userHeader.jade +++ b/client/components/users/userHeader.jade @@ -13,21 +13,46 @@ template(name="headerUserBar") template(name="memberMenuPopup") ul.pop-over-list with currentUser - li: a.js-edit-profile {{_ 'edit-profile'}} - li: a.js-change-settings {{_ 'change-settings'}} - li: a.js-change-avatar {{_ 'edit-avatar'}} + li + a.js-edit-profile + i.fa.fa-user + | {{_ 'edit-profile'}} + li + a.js-change-settings + i.fa.fa-cog + | {{_ 'change-settings'}} + li + a.js-change-avatar + i.fa.fa-picture-o + | {{_ 'edit-avatar'}} unless isSandstorm - li: a.js-change-password {{_ 'changePasswordPopup-title'}} - li: a.js-change-language {{_ 'changeLanguagePopup-title'}} + li + a.js-change-password + i.fa.fa-key + | {{_ 'changePasswordPopup-title'}} + li + a.js-change-language + i.fa.fa-flag + | {{_ 'changeLanguagePopup-title'}} if currentUser.isAdmin - li: a.js-go-setting(href="{{pathFor 'setting'}}") {{_ 'admin-panel'}} - hr - ul.pop-over-list - li: a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}") {{_ 'templates'}} + li + a.js-go-setting(href="{{pathFor 'setting'}}") + i.fa.fa-lock + | {{_ 'admin-panel'}} + unless currentUser.isWorker + hr + ul.pop-over-list + li + a(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}") + i.fa.fa-clone + | {{_ 'templates'}} unless isSandstorm hr ul.pop-over-list - li: a.js-logout {{_ 'log-out'}} + li + a.js-logout + i.fa.fa-sign-out + | {{_ 'log-out'}} template(name="editProfilePopup") form @@ -75,21 +100,25 @@ template(name="changeSettingsPopup") ul.pop-over-list li a.js-toggle-system-messages + i.fa.fa-comments-o | {{_ 'hide-system-messages'}} if hiddenSystemMessages i.fa.fa-check li a.js-toggle-desktop-drag-handles + i.fa.fa-arrows | {{_ 'show-desktop-drag-handles'}} if showDesktopDragHandles i.fa.fa-check - li - label.bold - | {{_ 'show-cards-minimum-count'}} - input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false") - input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}") - + unless currentUser.isWorker + li + label.bold + i.fa.fa-sort-numeric-asc + | {{_ 'show-cards-minimum-count'}} + input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="0" max="99" onkeydown="return false") + input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}") template(name="userDeletePopup") - p {{_ 'delete-user-confirm-popup'}} - button.js-confirm.negate.full(type="submit") {{_ 'delete'}} + unless currentUser.isWorker + p {{_ 'delete-user-confirm-popup'}} + button.js-confirm.negate.full(type="submit") {{_ 'delete'}} diff --git a/client/components/users/userHeader.js b/client/components/users/userHeader.js index 194f990f..847d30fb 100644 --- a/client/components/users/userHeader.js +++ b/client/components/users/userHeader.js @@ -1,3 +1,6 @@ +import { Cookies } from 'meteor/ostrio:cookies'; +const cookies = new Cookies(); + Template.headerUserBar.events({ 'click .js-open-header-member-menu': Popup.open('memberMenu'), 'click .js-change-avatar': Popup.open('changeAvatar'), @@ -5,10 +8,22 @@ Template.headerUserBar.events({ Template.memberMenuPopup.helpers({ templatesBoardId() { - return Meteor.user().getTemplatesBoardId(); + currentUser = Meteor.user(); + if (currentUser) { + return Meteor.user().getTemplatesBoardId(); + } else { + // No need to getTemplatesBoardId on public board + return false; + } }, templatesBoardSlug() { - return Meteor.user().getTemplatesBoardSlug(); + currentUser = Meteor.user(); + if (currentUser) { + return Meteor.user().getTemplatesBoardSlug(); + } else { + // No need to getTemplatesBoardSlug() on public board + return false; + } }, }); @@ -30,13 +45,31 @@ Template.memberMenuPopup.events({ Template.editProfilePopup.helpers({ allowEmailChange() { - return AccountSettings.findOne('accounts-allowEmailChange').booleanValue; + Meteor.call('AccountSettings.allowEmailChange', (_, result) => { + if (result) { + return true; + } else { + return false; + } + }); }, allowUserNameChange() { - return AccountSettings.findOne('accounts-allowUserNameChange').booleanValue; + Meteor.call('AccountSettings.allowUserNameChange', (_, result) => { + if (result) { + return true; + } else { + return false; + } + }); }, allowUserDelete() { - return AccountSettings.findOne('accounts-allowUserDelete').booleanValue; + Meteor.call('AccountSettings.allowUserDelete', (_, result) => { + if (result) { + return true; + } else { + return false; + } + }); }, }); @@ -162,22 +195,55 @@ Template.changeLanguagePopup.events({ Template.changeSettingsPopup.helpers({ showDesktopDragHandles() { - return Meteor.user().hasShowDesktopDragHandles(); + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).showDesktopDragHandles; + } else if (cookies.has('showDesktopDragHandles')) { + return true; + } else { + return false; + } }, hiddenSystemMessages() { - return Meteor.user().hasHiddenSystemMessages(); + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).hasHiddenSystemMessages; + } else if (cookies.has('hasHiddenSystemMessages')) { + return true; + } else { + return false; + } }, showCardsCountAt() { - return Meteor.user().getLimitToShowCardsCount(); + currentUser = Meteor.user(); + if (currentUser) { + return Meteor.user().getLimitToShowCardsCount(); + } else { + return cookies.get('limitToShowCardsCount'); + } }, }); Template.changeSettingsPopup.events({ 'click .js-toggle-desktop-drag-handles'() { - Meteor.call('toggleDesktopDragHandles'); + currentUser = Meteor.user(); + if (currentUser) { + Meteor.call('toggleDesktopDragHandles'); + } else if (cookies.has('showDesktopDragHandles')) { + cookies.remove('showDesktopDragHandles'); + } else { + cookies.set('showDesktopDragHandles', 'true'); + } }, 'click .js-toggle-system-messages'() { - Meteor.call('toggleSystemMessages'); + currentUser = Meteor.user(); + if (currentUser) { + Meteor.call('toggleSystemMessages'); + } else if (cookies.has('hasHiddenSystemMessages')) { + cookies.remove('hasHiddenSystemMessages'); + } else { + cookies.set('hasHiddenSystemMessages', 'true'); + } }, 'click .js-apply-show-cards-at'(event, templateInstance) { event.preventDefault(); @@ -186,7 +252,12 @@ Template.changeSettingsPopup.events({ 10, ); if (!isNaN(minLimit)) { - Meteor.call('changeLimitToShowCardsCount', minLimit); + currentUser = Meteor.user(); + if (currentUser) { + Meteor.call('changeLimitToShowCardsCount', minLimit); + } else { + cookies.set('limitToShowCardsCount', minLimit); + } Popup.back(); } }, diff --git a/client/lib/keyboard.js b/client/lib/keyboard.js index d3f974be..da33f806 100755 --- a/client/lib/keyboard.js +++ b/client/lib/keyboard.js @@ -70,6 +70,30 @@ Mousetrap.bind('space', evt => { } }); +// XXX This shortcut should also work when hovering over a card in board view +Mousetrap.bind('c', evt => { + if (!Session.get('currentCard')) { + return; + } + + const currentUserId = Meteor.userId(); + if (currentUserId === null) { + return; + } + + if ( + Meteor.user().isBoardMember() && + !Meteor.user().isCommentOnly() && + !Meteor.user().isWorker() + ) { + const card = Cards.findOne(Session.get('currentCard')); + card.archive(); + // We should prevent scrolling in card when spacebar is clicked + // This should do it according to Mousetrap docs, but it doesn't + evt.preventDefault(); + } +}); + Template.keyboardShortcuts.helpers({ mapping: [ { @@ -104,5 +128,9 @@ Template.keyboardShortcuts.helpers({ keys: ['SPACE'], action: 'shortcut-assign-self', }, + { + keys: ['C'], + action: 'archive-card', + }, ], }); diff --git a/client/lib/textComplete.js b/client/lib/textComplete.js index 8b6dc1f7..e97d3853 100644 --- a/client/lib/textComplete.js +++ b/client/lib/textComplete.js @@ -48,6 +48,11 @@ $.fn.escapeableTextComplete = function(strategies, options, ...otherArgs) { return this; }; -EscapeActions.register('textcomplete', () => {}, () => dropdownMenuIsOpened, { - noClickEscapeOn: '.textcomplete-dropdown', -}); +EscapeActions.register( + 'textcomplete', + () => {}, + () => dropdownMenuIsOpened, + { + noClickEscapeOn: '.textcomplete-dropdown', + }, +); diff --git a/client/lib/utils.js b/client/lib/utils.js index 213124f1..d712cc73 100644 --- a/client/lib/utils.js +++ b/client/lib/utils.js @@ -1,4 +1,40 @@ +import { Cookies } from 'meteor/ostrio:cookies'; +const cookies = new Cookies(); + Utils = { + setBoardView(view) { + currentUser = Meteor.user(); + if (currentUser) { + Meteor.user().setBoardView(view); + } else if (view === 'board-view-lists') { + cookies.set('boardView', 'board-view-lists'); //true + } else if (view === 'board-view-swimlanes') { + cookies.set('boardView', 'board-view-swimlanes'); //true + } else if (view === 'board-view-cal') { + cookies.set('boardView', 'board-view-cal'); //true + } + }, + + unsetBoardView() { + cookies.remove('boardView'); + cookies.remove('collapseSwimlane'); + }, + + boardView() { + currentUser = Meteor.user(); + if (currentUser) { + return (currentUser.profile || {}).boardView; + } else if (cookies.get('boardView') === 'board-view-lists') { + return 'board-view-lists'; + } else if (cookies.get('boardView') === 'board-view-swimlanes') { + return 'board-view-swimlanes'; + } else if (cookies.get('boardView') === 'board-view-cal') { + return 'board-view-cal'; + } else { + return false; + } + }, + // XXX We should remove these two methods goBoardId(_id) { const board = Boards.findOne(_id); @@ -117,8 +153,38 @@ Utils = { // in a small window (even on desktop), Wekan run in compact mode. // we can easily debug with a small window of desktop browser. :-) isMiniScreen() { + // OLD WINDOW WIDTH DETECTION: this.windowResizeDep.depend(); return $(window).width() <= 800; + + // NEW TOUCH DEVICE DETECTION: + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent + + /* + var hasTouchScreen = false; + if ("maxTouchPoints" in navigator) { + hasTouchScreen = navigator.maxTouchPoints > 0; + } else if ("msMaxTouchPoints" in navigator) { + hasTouchScreen = navigator.msMaxTouchPoints > 0; + } else { + var mQ = window.matchMedia && matchMedia("(pointer:coarse)"); + if (mQ && mQ.media === "(pointer:coarse)") { + hasTouchScreen = !!mQ.matches; + } else if ('orientation' in window) { + hasTouchScreen = true; // deprecated, but good fallback + } else { + // Only as a last resort, fall back to user agent sniffing + var UA = navigator.userAgent; + hasTouchScreen = ( + /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) || + /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA) + ); + } + } + */ + //if (hasTouchScreen) + // document.getElementById("exampleButton").style.padding="1em"; + //return false; }, calculateIndexData(prevData, nextData, nItems = 1) { |