diff --git a/src/App.vue b/src/App.vue index 31b5b7ca9..fbfa0eb0c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -192,4 +192,10 @@ export default { width: 100%; } + .modal__card { + height: 100vh; + max-height: calc(100vh - 120px); + overflow: auto; + } + diff --git a/src/components/ActivityEntry.vue b/src/components/ActivityEntry.vue index 211ffb208..3c6ee6902 100644 --- a/src/components/ActivityEntry.vue +++ b/src/components/ActivityEntry.vue @@ -25,7 +25,7 @@
-
+
{{ relativeDate(activity.datetime) }}
@@ -39,6 +39,7 @@ import { NcRichText, NcUserBubble } from '@nextcloud/vue' import moment from '@nextcloud/moment' import DOMPurify from 'dompurify' import relativeDate from '../mixins/relativeDate.js' +import formatReadableDate from '../mixins/readableDate.js' const InternalLink = { name: 'InternalLink', @@ -62,7 +63,7 @@ export default { components: { NcRichText, }, - mixins: [relativeDate], + mixins: [relativeDate, formatReadableDate], props: { activity: { type: Object, diff --git a/src/components/ActivityList.vue b/src/components/ActivityList.vue index fa1eb7df2..e16519fe1 100644 --- a/src/components/ActivityList.vue +++ b/src/components/ActivityList.vue @@ -84,7 +84,20 @@ export default { params.append('object_id', '' + this.objectId) params.append('limit', ACTIVITY_FETCH_LIMIT) - const response = await axios.get(generateOcsUrl(`apps/activity/api/v2/activity/${this.filter}`) + '?' + params) + const response = await axios.get( + generateOcsUrl(`apps/activity/api/v2/activity/${this.filter}`) + '?' + params, + { + validateStatus: (status) => { + return (status >= 200 && status < 300) || status === 304 + }, + }, + ) + + if (response.status === 304) { + this.endReached = true + return [] + } + let activities = response.data.ocs.data if (this.filter === 'deck') { // We need to manually filter activities here, since currently we use two different types and there is no way @@ -95,7 +108,7 @@ export default { }) } this.activities.push(...activities) - if (response.data.ocs.meta.statuscode === 304 || activities.length === 0) { + if (activities.length === 0) { this.endReached = true return [] } diff --git a/src/components/board/SharingTabSidebar.vue b/src/components/board/SharingTabSidebar.vue index de2f720a9..e5dea65aa 100644 --- a/src/components/board/SharingTabSidebar.vue +++ b/src/components/board/SharingTabSidebar.vue @@ -247,7 +247,7 @@ export default { } } }, - true + true, ) }, }, diff --git a/src/components/board/Stack.vue b/src/components/board/Stack.vue index 342174c25..63c44d45a 100644 --- a/src/components/board/Stack.vue +++ b/src/components/board/Stack.vue @@ -216,7 +216,7 @@ export default { }) }, dragHandleSelector() { - return this.canEdit ? null : '.no-drag' + return this.canEdit && !this.showArchived ? null : '.no-drag' }, cardDetailsInModal: { get() { diff --git a/src/components/board/TagsTabSidebar.vue b/src/components/board/TagsTabSidebar.vue index 0a05a6b85..149e64ebf 100644 --- a/src/components/board/TagsTabSidebar.vue +++ b/src/components/board/TagsTabSidebar.vue @@ -124,7 +124,7 @@ export default { return true }, labelsSorted() { - return [...this.labels].sort((a, b) => (a.title < b.title) ? -1 : 1) + return [...this.labels].sort((a, b) => a.title.localeCompare(b.title)) }, }, diff --git a/src/components/card/CardSidebar.vue b/src/components/card/CardSidebar.vue index 603cc4af8..4a4c48c52 100644 --- a/src/components/card/CardSidebar.vue +++ b/src/components/card/CardSidebar.vue @@ -239,7 +239,7 @@ section.app-sidebar__tab--active { right: 0; max-width: calc(100% - #{$modal-padding * 2}); padding: 0 14px; - max-height: 100%; + height: auto; overflow: initial; user-select: text; -webkit-user-select: text; diff --git a/src/components/card/CardSidebarTabDetails.vue b/src/components/card/CardSidebarTabDetails.vue index 5fb648149..fef730d40 100644 --- a/src/components/card/CardSidebarTabDetails.vue +++ b/src/components/card/CardSidebarTabDetails.vue @@ -119,8 +119,10 @@ export default { this.initialize() }, methods: { - descriptionChanged(newDesc) { - this.$store.dispatch('updateCardDesc', { ...this.card, description: newDesc }) + async descriptionChanged(newDesc) { + if (newDesc === this.copiedCard.description) { + return + } this.copiedCard.description = newDesc }, async initialize() { diff --git a/src/components/card/CommentItem.vue b/src/components/card/CommentItem.vue index 66b20d8e6..cf87c306c 100644 --- a/src/components/card/CommentItem.vue +++ b/src/components/card/CommentItem.vue @@ -79,7 +79,7 @@ const AtMention = { return createElement( 'span', { attrs: { 'data-at-embedded': true, contenteditable: false } }, - [createElement(NcUserBubble, { props: { user, displayName }, attrs: { 'data-mention-id': user } })] + [createElement(NcUserBubble, { props: { user, displayName }, attrs: { 'data-mention-id': user } })], ) }, } diff --git a/src/components/card/Description.vue b/src/components/card/Description.vue index ba06a0fff..a3be5dc5a 100644 --- a/src/components/card/Description.vue +++ b/src/components/card/Description.vue @@ -186,17 +186,26 @@ export default { mounted() { this.setupEditor() }, - beforeDestroy() { - this?.editor?.destroy() + async beforeDestroy() { + await this.destroyEditor() }, methods: { async setupEditor() { - this?.editor?.destroy() + await this.destroyEditor() + this.descriptionLastEdit = 0 + this.description = this.card.description this.editor = await window.OCA.Text.createEditor({ el: this.$refs.editor, content: this.card.description, readOnly: !this.canEdit, + onLoaded: () => { + this.descriptionLastEdit = 0 + }, onUpdate: ({ markdown }) => { + if (this.description === markdown) { + this.descriptionLastEdit = 0 + return + } this.description = markdown this.updateDescription() }, @@ -206,6 +215,10 @@ export default { }) }, + async destroyEditor() { + await this.saveDescription() + this?.editor?.destroy() + }, addKeyListeners() { this.$refs.markdownEditor.easymde.codemirror.on('keydown', (a, b) => { if (this.keyExitState === 0 && (b.key === 'Meta' || b.key === 'Alt')) { @@ -247,7 +260,7 @@ export default { this.editor.insertAtCursor( asImage ? `${attachment.data}` - : `${attachment.data}` + : `${attachment.data}`, ) return } else { @@ -286,6 +299,7 @@ export default { return } this.descriptionSaving = true + await this.$store.dispatch('updateCardDesc', { ...this.card, description: this.description }) this.$emit('change', this.description) this.descriptionLastEdit = 0 this.descriptionSaving = false diff --git a/src/components/card/DueDateSelector.vue b/src/components/card/DueDateSelector.vue index a068d8bde..29bf8ae98 100644 --- a/src/components/card/DueDateSelector.vue +++ b/src/components/card/DueDateSelector.vue @@ -29,6 +29,7 @@ import { defineComponent } from 'vue' import { NcActionButton, NcActions, NcDatetimePicker } from '@nextcloud/vue' import { getDayNamesMin, getFirstDay, getMonthNamesShort } from '@nextcloud/l10n' import Calendar from 'vue-material-design-icons/Calendar.vue' +import readableDate from '../../mixins/readableDate.js' export default defineComponent({ name: 'DueDateSelector', @@ -38,6 +39,9 @@ export default defineComponent({ NcActionButton, NcDatetimePicker, }, + mixins: [ + readableDate, + ], props: { card: { type: Object, diff --git a/src/components/cards/CardItem.vue b/src/components/cards/CardItem.vue index 57d85947e..bf62c356b 100644 --- a/src/components/cards/CardItem.vue +++ b/src/components/cards/CardItem.vue @@ -42,9 +42,8 @@ tabindex="0" class="editable" :aria-label="t('deck', 'Edit card title')" - @click.stop="startEditing(card)" @keydown.enter.stop.prevent="startEditing(card)"> - {{ card.title }} + {{ card.title }}
{{ t('deck', 'Move card') }} + + {{ action.label }} + {{ t('deck', 'Card details') }} @@ -119,6 +126,7 @@ export default { ...mapGetters([ 'isArchived', 'boards', + 'cardActions', ]), ...mapState({ showArchived: state => state.showArchived, diff --git a/src/components/navigation/AppNavigationBoard.vue b/src/components/navigation/AppNavigationBoard.vue index c1132025f..e2f498e77 100644 --- a/src/components/navigation/AppNavigationBoard.vue +++ b/src/components/navigation/AppNavigationBoard.vue @@ -289,7 +289,7 @@ export default { }) } }, - true + true, ) }, actionDetails() { diff --git a/src/mixins/attachmentUpload.js b/src/mixins/attachmentUpload.js index c65b23ec2..2d1edf400 100644 --- a/src/mixins/attachmentUpload.js +++ b/src/mixins/attachmentUpload.js @@ -37,7 +37,7 @@ export default { if (this.maxUploadSize > 0 && file.size > this.maxUploadSize) { showError( t('deck', 'Failed to upload {name}', { name: file.name }) + ' - ' - + t('deck', 'Maximum file size of {size} exceeded', { size: formatFileSize(this.maxUploadSize) }) + + t('deck', 'Maximum file size of {size} exceeded', { size: formatFileSize(this.maxUploadSize) }), ) event.target.value = '' return diff --git a/src/mixins/readableDate.js b/src/mixins/readableDate.js new file mode 100644 index 000000000..3205431f5 --- /dev/null +++ b/src/mixins/readableDate.js @@ -0,0 +1,33 @@ +/* + * @copyright Copyright (c) 2020 Julius Härtl + * + * @author Julius Härtl + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +import moment from '@nextcloud/moment' + +export default { + computed: { + formatReadableDate() { + return (timestamp) => { + return moment(timestamp).format('lll') + } + }, + }, +} diff --git a/src/services/BoardApi.js b/src/services/BoardApi.js index 46c383421..73529d7ee 100644 --- a/src/services/BoardApi.js +++ b/src/services/BoardApi.js @@ -48,7 +48,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -73,7 +73,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -88,7 +88,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -103,7 +103,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -118,7 +118,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -133,7 +133,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -207,7 +207,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -223,7 +223,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -238,7 +238,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -253,7 +253,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -270,7 +270,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -285,7 +285,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -300,7 +300,7 @@ export class BoardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) diff --git a/src/services/CardApi.js b/src/services/CardApi.js index f1ec7aec0..3bd28114b 100644 --- a/src/services/CardApi.js +++ b/src/services/CardApi.js @@ -38,7 +38,7 @@ export class CardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -53,7 +53,7 @@ export class CardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -68,7 +68,7 @@ export class CardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -83,7 +83,7 @@ export class CardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -98,7 +98,7 @@ export class CardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -113,7 +113,7 @@ export class CardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -128,7 +128,7 @@ export class CardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -143,7 +143,7 @@ export class CardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -158,7 +158,7 @@ export class CardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -173,7 +173,7 @@ export class CardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -188,7 +188,7 @@ export class CardApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) diff --git a/src/services/OverviewApi.js b/src/services/OverviewApi.js index c9981fb56..8b1b55bd2 100644 --- a/src/services/OverviewApi.js +++ b/src/services/OverviewApi.js @@ -35,9 +35,9 @@ export class OverviewApi { }) .then( (response) => Promise.resolve(response.data.ocs.data), - (err) => Promise.reject(err) + (err) => Promise.reject(err), ) - .catch((err) => Promise.reject(err) + .catch((err) => Promise.reject(err), ) } diff --git a/src/services/StackApi.js b/src/services/StackApi.js index 26d3e7e04..4e50e67b0 100644 --- a/src/services/StackApi.js +++ b/src/services/StackApi.js @@ -39,7 +39,7 @@ export class StackApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -54,7 +54,7 @@ export class StackApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -69,7 +69,7 @@ export class StackApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -88,7 +88,7 @@ export class StackApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -103,7 +103,7 @@ export class StackApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -118,7 +118,7 @@ export class StackApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err) @@ -133,7 +133,7 @@ export class StackApi { }, (err) => { return Promise.reject(err) - } + }, ) .catch((err) => { return Promise.reject(err)