diff --git a/src/components/card/CardSidebar.vue b/src/components/card/CardSidebar.vue index b3e18c9cd..e966cbff6 100644 --- a/src/components/card/CardSidebar.vue +++ b/src/components/card/CardSidebar.vue @@ -87,7 +87,7 @@ import HomeIcon from 'vue-material-design-icons/Home.vue' import CommentIcon from 'vue-material-design-icons/Comment.vue' import ActivityIcon from 'vue-material-design-icons/LightningBolt.vue' -import { showError } from '@nextcloud/dialogs' +import { showError, showWarning } from '@nextcloud/dialogs' import { getLocale } from '@nextcloud/l10n' import CardMenuEntries from '../cards/CardMenuEntries.vue' @@ -139,6 +139,7 @@ export default { ...mapState({ isFullApp: (state) => state.isFullApp, currentBoard: (state) => state.currentBoard, + hasCardSaveError: (state) => state.hasCardSaveError, }), ...mapGetters(['canEdit', 'assignables', 'cardActions', 'stackById']), currentCard() { @@ -198,6 +199,10 @@ export default { }, closeSidebar() { + if (this.hasCardSaveError) { + showWarning(t('deck', 'Cannot close unsaved card!')) + return + } this.$router?.push({ name: 'board' }) this.$emit('close') }, diff --git a/src/components/card/Description.vue b/src/components/card/Description.vue index 5969bcf25..50fd6f6e2 100644 --- a/src/components/card/Description.vue +++ b/src/components/card/Description.vue @@ -73,7 +73,9 @@ import AttachmentList from './AttachmentList.vue' import { NcActions, NcActionButton, NcModal } from '@nextcloud/vue' import { formatFileSize } from '@nextcloud/files' import { generateUrl } from '@nextcloud/router' +import { showWarning } from '@nextcloud/dialogs' import PaperclipIcon from 'vue-material-design-icons/Paperclip.vue' +import { mapState } from 'vuex' const markdownIt = new MarkdownIt({ linkify: true, @@ -135,6 +137,9 @@ export default { } }, computed: { + ...mapState({ + hasCardSaveError: (state) => state.hasCardSaveError, + }), mimetypeForAttachment() { return (mimetype) => { const url = OC.MimeType.getIconUrl(mimetype) @@ -285,15 +290,31 @@ export default { return } this.descriptionSaving = true - if (this.card.id !== undefined) { - await this.$store.dispatch('updateCardDesc', { ...this.card, description: this.description }) + try { + if (this.card.id !== undefined) { + await this.$store.dispatch('updateCardDesc', { ...this.card, description: this.description }) + } + this.$emit('change', this.description) + this.descriptionLastEdit = 0 + this.$store.commit('setHasCardSaveError', false) + } catch (e) { + this.$store.commit('setHasCardSaveError', true) + showWarning(t('deck', 'Could not save description'), { timeout: 2500 }) + console.error(e) + + // Retry of network error + if (['ERR_NETWORK', 'ETIMEDOUT'].includes(e.code)) { + this.setSaveTimeout() + } + } finally { + this.descriptionSaving = false } - this.$emit('change', this.description) - this.descriptionLastEdit = 0 - this.descriptionSaving = false }, updateDescription() { this.descriptionLastEdit = Date.now() + this.setSaveTimeout() + }, + setSaveTimeout() { clearTimeout(this.descriptionSaveTimeout) this.descriptionSaveTimeout = setTimeout(async () => { await this.saveDescription() diff --git a/src/store/main.js b/src/store/main.js index a61a85f2b..b7e3122c5 100644 --- a/src/store/main.js +++ b/src/store/main.js @@ -50,6 +50,7 @@ export default new Vuex.Store({ sidebarShown: false, currentBoard: null, currentCard: null, + hasCardSaveError: false, boards: loadState('deck', 'initialBoards', []), sharees: [], assignableUsers: [], @@ -131,6 +132,9 @@ export default new Vuex.Store({ setFullApp(state, isFullApp) { Vue.set(state, 'isFullApp', isFullApp) }, + setHasCardSaveError(state, hasCardSaveError) { + Vue.set(state, 'hasCardSaveError', hasCardSaveError) + }, SET_CONFIG(state, { key, value }) { const [scope, id, configKey] = key.split(':', 3) let indexExisting = -1