diff --git a/src/components/card/AttachmentList.vue b/src/components/card/AttachmentList.vue
index d602ae3bb..72915f25b 100644
--- a/src/components/card/AttachmentList.vue
+++ b/src/components/card/AttachmentList.vue
@@ -46,9 +46,9 @@
.{{ attachmentExtension(attachment) }}
- {{ formattedFileSize(attachment.extendedData.filesize) }}
+ {{ formattedFileSize(attachment.extendedData?.filesize) }}
{{ relativeDate(attachment.createdAt*1000) }}
- {{ attachment.extendedData.attachmentCreator.displayName }}
+ {{ attachment.extendedData?.attachmentCreator?.displayName }}
{{ t('deck', 'Pending share') }}
@@ -156,7 +156,7 @@ export default {
if (!attachment) {
return {}
}
- const url = attachment.extendedData.hasPreview ? this.attachmentPreview(attachment) : OC.MimeType.getIconUrl(attachment.extendedData.mimetype)
+ const url = attachment?.extendedData?.hasPreview ? this.attachmentPreview(attachment) : OC.MimeType.getIconUrl(attachment?.extendedData?.mimetype)
const styles = {
'background-image': `url("${url}")`,
}
@@ -164,16 +164,16 @@ export default {
}
},
attachmentPreview() {
- return (attachment) => (attachment.extendedData.fileid ? generateUrl(`/core/preview?fileId=${attachment.extendedData.fileid}&x=64&y=64`) : null)
+ return (attachment) => (attachment?.extendedData?.fileid ? generateUrl(`/core/preview?fileId=${attachment.extendedData.fileid}&x=64&y=64`) : null)
},
attachmentUrl() {
- return (attachment) => generateUrl(`/apps/deck/cards/${attachment.cardId}/attachment/${attachment.id}`)
+ return (attachment) => (attachment?.cardId && attachment?.id ? generateUrl(`/apps/deck/cards/${attachment.cardId}/attachment/${attachment.id}`) : '#')
},
internalLink() {
- return (attachment) => generateUrl('/f/' + attachment.extendedData.fileid)
+ return (attachment) => (attachment?.extendedData?.fileid ? generateUrl('/f/' + attachment.extendedData.fileid) : '#')
},
downloadLink() {
- return (attachment) => generateRemoteUrl(`dav/files/${getCurrentUser().uid}/${attachment.extendedData.path}`)
+ return (attachment) => (attachment?.extendedData?.path ? generateRemoteUrl(`dav/files/${getCurrentUser().uid}/${attachment.extendedData.path}`) : '#')
},
formattedFileSize() {
return (filesize) => formatFileSize(filesize)
@@ -194,11 +194,11 @@ export default {
},
attachmentBasename() {
return (attachment) => attachment?.extendedData?.info?.filename
- ?? (attachment?.name ?? attachment.data).replace(/\.[^/.]+$/, '')
+ ?? (attachment?.name ?? attachment?.data ?? '').replace(/\.[^/.]+$/, '')
},
attachmentExtension() {
return (attachment) => attachment?.extendedData?.info?.extension
- ?? (attachment?.name ?? attachment.data).split('.').pop()
+ ?? (attachment?.name ?? attachment?.data ?? '').split('.').pop()
},
cardDetailsInModal() {
return this.$store.getters.config('cardDetailsInModal')
diff --git a/src/components/card/CardSidebar.vue b/src/components/card/CardSidebar.vue
index 3cafc6ce8..f003b9ed6 100644
--- a/src/components/card/CardSidebar.vue
+++ b/src/components/card/CardSidebar.vue
@@ -209,7 +209,9 @@ export default {
watch: {
currentCard(newCard, oldCard) {
if (newCard?.id === oldCard?.id) return
- this.focusHeader()
+ if (newCard) {
+ this.focusHeader()
+ }
},
'currentCard.title': {
immediate: true,
diff --git a/src/components/card/Description.vue b/src/components/card/Description.vue
index 125635382..fb59145ae 100644
--- a/src/components/card/Description.vue
+++ b/src/components/card/Description.vue
@@ -183,8 +183,12 @@ export default {
this.descriptionOld = newCard.description
this.description = newCard.description
- if (this.editor) {
- this.editor.setContent(this.description)
+ if (this.editor && typeof this.editor.setContent === 'function') {
+ try {
+ this.editor.setContent(this.description)
+ } catch (e) {
+ console.debug('Failed to set editor content', e)
+ }
}
showWarning(t('deck', 'The description has been changed by another user.'), { timeout: 3000 })
}
@@ -205,29 +209,56 @@ export default {
this.descriptionLastEdit = 0
this.descriptionOld = this.card.description
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) {
- return
+ if (!window.OCA?.Text?.createEditor || !this.$refs.editor) {
+ return
+ }
+ try {
+ const 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) {
+ return
+ }
+ this.description = markdown
+ this.updateDescription()
+ },
+ onFileInsert: () => {
+ this.showAttachmentModal()
+ },
+ })
+ if (this._isBeingDestroyed || this._isDestroyed) {
+ if (editor && typeof editor.destroy === 'function') {
+ try {
+ await editor.destroy()
+ } catch (e) {
+ // Ignore teardown error on already destroyed component
+ }
}
- this.description = markdown
- this.updateDescription()
- },
- onFileInsert: () => {
- this.showAttachmentModal()
- },
- })
-
+ return
+ }
+ this.editor = editor
+ } catch (e) {
+ console.warn('Failed to initialize text editor', e)
+ }
},
async destroyEditor() {
await this.saveDescription()
- this?.editor?.destroy()
+ if (this.editor) {
+ const editor = this.editor
+ this.editor = null
+ try {
+ if (typeof editor.destroy === 'function' && !editor.isDestroyed) {
+ await editor.destroy()
+ }
+ } catch (e) {
+ console.debug('Caught editor teardown error', e)
+ }
+ }
},
addKeyListeners() {
this.$refs.markdownEditor.easymde.codemirror.on('keydown', (a, b) => {
@@ -268,7 +299,9 @@ export default {
const asImage = (attachment.type === 'file' && attachment.extendedData.hasPreview) || attachment.extendedData.mimetype.includes('image')
// We need to strip those as text does not support rtl yet, so we cannot insert them separately
const stripRTLO = (text) => text.replaceAll('\u202e', '')
- const fileName = stripRTLO(attachment.extendedData.info.filename) + '.' + stripRTLO(attachment.extendedData.info.extension)
+ const base = attachment?.extendedData?.info?.filename ?? attachment?.data ?? ''
+ const ext = attachment?.extendedData?.info?.extension
+ const fileName = stripRTLO(base) + (ext ? '.' + stripRTLO(ext) : '')
if (this.editor) {
this.editor.insertAtCursor(
asImage