Signed-off-by: Vinicius Reis <vinicius.reis@nextcloud.com>
This commit is contained in:
Vinicius Reis 2022-06-01 13:40:21 -03:00 ΠΊΠΎΠΌΠΌΠΈΡ‚ ΠΏΡ€ΠΎΠΈΠ·Π²Ρ‘Π» Julius HΓ€rtl
Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒ 1db2493238
ΠšΠΎΠΌΠΌΠΈΡ‚ f7451b5050
НС Π½Π°ΠΉΠ΄Π΅Π½ ΠΊΠ»ΡŽΡ‡, ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΉ Π΄Π°Π½Π½ΠΎΠΉ подписи
Π˜Π΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΠΊΠ»ΡŽΡ‡Π° GPG: 4C614C6ED2CDE6DF
2 ΠΈΠ·ΠΌΠ΅Π½Ρ‘Π½Π½Ρ‹Ρ… Ρ„Π°ΠΉΠ»ΠΎΠ²: 75 Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΉ ΠΈ 23 ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠΉ

ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ„Π°ΠΉΠ»

@ -21,7 +21,6 @@
*
*/
import MDI_Loading from 'vue-material-design-icons/Loading.vue'
import MDI_Check from 'vue-material-design-icons/Check.vue'
import MDI_CodeTags from 'vue-material-design-icons/CodeTags.vue'
import MDI_Danger from 'vue-material-design-icons/AlertDecagram.vue'
@ -45,9 +44,11 @@ import MDI_FormatQuote from 'vue-material-design-icons/FormatQuoteClose.vue'
import MDI_FormatStrikethrough from 'vue-material-design-icons/FormatStrikethrough.vue'
import MDI_FormatUnderline from 'vue-material-design-icons/FormatUnderline.vue'
import MDI_Help from 'vue-material-design-icons/HelpCircle.vue'
import MDI_Image from 'vue-material-design-icons/ImageOutline.vue'
import MDI_Images from 'vue-material-design-icons/ImageMultipleOutline.vue'
import MDI_Info from 'vue-material-design-icons/Information.vue'
import MDI_Link from 'vue-material-design-icons/Link.vue'
import MDI_Loading from 'vue-material-design-icons/Loading.vue'
import MDI_Lock from 'vue-material-design-icons/Lock.vue'
import MDI_Positive from 'vue-material-design-icons/CheckboxMarkedCircle.vue'
import MDI_Redo from 'vue-material-design-icons/ArrowURightTop.vue'
@ -57,6 +58,7 @@ import MDI_TableAddColumnBefore from 'vue-material-design-icons/TableColumnPlusB
import MDI_TableAddRowAfter from 'vue-material-design-icons/TableRowPlusAfter.vue'
import MDI_TableAddRowBefore from 'vue-material-design-icons/TableRowPlusBefore.vue'
import MDI_TableSettings from 'vue-material-design-icons/TableCog.vue'
import MDI_TrashCan from 'vue-material-design-icons/TrashCan.vue'
import MDI_Undo from 'vue-material-design-icons/ArrowULeftTop.vue'
import MDI_Upload from 'vue-material-design-icons/Upload.vue'
import MDI_Warn from 'vue-material-design-icons/Alert.vue'
@ -109,6 +111,7 @@ export const FormatQuote = makeIcon(MDI_FormatQuote)
export const FormatStrikethrough = makeIcon(MDI_FormatStrikethrough)
export const FormatUnderline = makeIcon(MDI_FormatUnderline)
export const Help = makeIcon(MDI_Help)
export const Image = makeIcon(MDI_Image)
export const Images = makeIcon(MDI_Images)
export const Info = makeIcon(MDI_Info)
export const Link = makeIcon(MDI_Link)
@ -121,6 +124,7 @@ export const TableAddColumnBefore = makeIcon(MDI_TableAddColumnBefore)
export const TableAddRowAfter = makeIcon(MDI_TableAddRowAfter)
export const TableAddRowBefore = makeIcon(MDI_TableAddRowBefore)
export const TableSettings = makeIcon(MDI_TableSettings)
export const TrashCan = makeIcon(MDI_TrashCan)
export const Undo = makeIcon(MDI_Undo)
export const Upload = makeIcon(MDI_Upload)
export const Warn = makeIcon(MDI_Warn)

ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ„Π°ΠΉΠ»

@ -22,38 +22,47 @@
<template>
<NodeViewWrapper>
<div class="image"
<div class="image image-view"
data-component="image-view"
:class="{'icon-loading': !loaded}"
:class="{'icon-loading': !loaded, 'image-view--failed': failed}"
:data-src="src">
<div v-if="imageLoaded && isSupportedImage"
<small v-if="errorMessage" class="image__error-message">
{{ errorMessage }}
</small>
<div v-if="canDisplayImage"
v-click-outside="() => showIcons = false"
class="image__view"
@click="showIcons = true"
@mouseover="showIcons = true"
@mouseleave="showIcons = false">
<transition name="fade">
<img v-show="loaded"
:src="imageUrl"
class="image__main"
@load="onLoaded">
<template v-if="!failed">
<img v-show="loaded"
:src="imageUrl"
class="image__main"
@load="onLoaded">
</template>
<template v-else>
<ImageBroken class="image__main image__main--broken-icon" :size="100" />
</template>
</transition>
<transition name="fade">
<div v-show="loaded" class="image__caption">
<input ref="altInput"
type="text"
class="image__caption__input"
:value="alt"
@keyup.enter="updateAlt()">
<div v-if="editor.isEditable && showIcons"
class="trash-icon"
class="image__caption__delete"
title="Delete this image"
@click="deleteNode">
<TrashCanIcon />
<TrashCan />
</div>
</div>
</transition>
</div>
<div v-else>
<div v-else class="image-view__cant_display">
<transition name="fade">
<div v-show="loaded">
<a :href="internalLinkOrImage" target="_blank">
@ -78,7 +87,8 @@
import { generateUrl } from '@nextcloud/router'
import { NodeViewWrapper } from '@tiptap/vue-2'
import ClickOutside from 'vue-click-outside'
import TrashCanIcon from 'vue-material-design-icons/TrashCan.vue'
// import TrashCanIcon from 'vue-material-design-icons/TrashCan.vue'
import { ImageBroken, TrashCan } from '../components/icons.js'
import store from './../mixins/store.js'
import { useImageResolver } from './../components/EditorWrapper.provider.js'
@ -111,10 +121,21 @@ const getQueryVariable = (src, variable) => {
}
}
class ErrorLoadImage extends Error {
constructor(reason, imageUrl) {
super(reason?.message || t('text', 'Fail to load image'))
this.reason = reason
this.imageUrl = imageUrl
}
}
export default {
name: 'ImageView',
components: {
TrashCanIcon,
ImageBroken,
TrashCan,
NodeViewWrapper,
},
directives: {
@ -132,9 +153,21 @@ export default {
failed: false,
showIcons: false,
imageUrl: null,
errorMessage: null,
}
},
computed: {
canDisplayImage() {
if (!this.isSupportedImage) {
return false
}
if (this.failed && this.loaded) {
return true
}
return this.loaded && this.imageLoaded
},
imageFileId() {
return getQueryVariable(this.src, 'fileId')
},
@ -183,20 +216,22 @@ export default {
this.failed = true
this.imageLoaded = false
this.loaded = true
this.errorMessage = t('text', 'Unsuported image')
return
}
this.init().catch((e) => {
this.onImageLoadFailure()
})
this.init()
.catch(this.onImageLoadFailure)
},
methods: {
async init() {
const [url, fallback] = this.$imageResolver.resolve(this.src)
this.loadImage(url).catch((e) => {
return this.loadImage(url).catch((e) => {
if (fallback) {
this.loadImage(fallback)
return this.loadImage(fallback)
// TODO if fallback works, rewrite the url with correct document ID
}
return Promise.reject(e)
})
},
@ -206,18 +241,20 @@ export default {
img.onload = () => {
this.imageUrl = imageUrl
this.imageLoaded = true
resolve()
this.loaded = true
resolve(imageUrl)
}
img.onerror = (e) => {
reject(e)
reject(new ErrorLoadImage(e, imageUrl))
}
img.src = imageUrl
})
},
onImageLoadFailure() {
onImageLoadFailure(err) {
this.failed = true
this.imageLoaded = false
this.loaded = true
this.errorMessage = err.message
},
updateAlt() {
this.alt = this.$refs.altInput.value
@ -256,6 +293,10 @@ export default {
height: 100px;
}
.image__main--broken-icon, .image__error-message {
color: var(--color-error);
}
.image__view {
text-align: center;
position: relative;
@ -269,6 +310,11 @@ export default {
max-height: calc(100vh - 50px - 50px);
}
.image__error-message {
display: block;
text-align: center;
}
.fade-enter-active {
transition: opacity .3s ease-in-out;
}
@ -281,13 +327,15 @@ export default {
opacity: 0;
}
.trash-icon {
.image__caption__delete {
position: absolute;
right: 0;
display: flex;
justify-content: flex-end;
align-items: center;
svg {
width: 20px;
height: 20px;
&, svg {
cursor: pointer;
}
}