-
-
Notifications
You must be signed in to change notification settings - Fork 44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#2391 - Multiple images in the viewer #2419
Changes from 11 commits
707c683
915871e
b12cdd2
437e439
d36f3dd
58ab046
72f17ba
f5c85c6
41906b3
0deab9c
d973d38
8717b1d
1d121f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,7 +22,7 @@ | |
v-if='objectURLList[entryIndex]' | ||
:src='objectURLList[entryIndex]' | ||
:alt='entry.name' | ||
@click='openImageViewer(objectURLList[entryIndex], entry)' | ||
@click='openImageViewer(objectURLList[entryIndex])' | ||
) | ||
.loading-box(v-else :style='loadingBoxStyles[entryIndex]') | ||
|
||
|
@@ -60,7 +60,7 @@ | |
v-for='(entry, entryIndex) in attachmentList' | ||
:key='entryIndex' | ||
:class='"is-" + fileType(entry)' | ||
@click='openImageViewer(entry.url, entry)' | ||
@click='openImageViewer(entry.url)' | ||
) | ||
img.c-preview-img( | ||
v-if='fileType(entry) === "image" && entry.url' | ||
|
@@ -94,6 +94,7 @@ import { MESSAGE_VARIANTS } from '@model/contracts/shared/constants.js' | |
import { getFileExtension, getFileType } from '@view-utils/filters.js' | ||
import { Secret } from '~/shared/domains/chelonia/Secret.js' | ||
import { OPEN_MODAL } from '@utils/events.js' | ||
import { randomHexString } from '@model/contracts/shared/giLodash.js' | ||
|
||
export default { | ||
name: 'ChatAttachmentPreview', | ||
|
@@ -212,21 +213,29 @@ export default { | |
height: `${heightInPixel}px` | ||
} | ||
}, | ||
openImageViewer (objectURL, data) { | ||
if (objectURL) { | ||
sbp( | ||
'okTurtles.events/emit', OPEN_MODAL, 'ImageViewerModal', | ||
null, | ||
{ | ||
metaData: { | ||
name: data.name, | ||
ownerID: this.ownerID, | ||
imgUrl: objectURL, | ||
createdAt: this.createdAt || new Date() | ||
} | ||
openImageViewer (objectURL) { | ||
if (!objectURL) { return } | ||
|
||
const allImageAttachments = this.attachmentList.filter(entry => this.fileType(entry) === 'image') | ||
.map((entry, index) => { | ||
return { | ||
name: entry.name, | ||
ownerID: this.ownerID, | ||
imgUrl: entry.url || this.objectURLList[index] || '', | ||
createdAt: this.createdAt || new Date(), | ||
id: randomHexString(12) | ||
} | ||
) | ||
} | ||
}) | ||
const initialIndex = allImageAttachments.findIndex(attachment => attachment.imgUrl === objectURL) || 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. Fixed. |
||
|
||
sbp( | ||
'okTurtles.events/emit', OPEN_MODAL, 'ImageViewerModal', | ||
null, | ||
{ | ||
images: allImageAttachments, | ||
initialIndex | ||
} | ||
) | ||
} | ||
}, | ||
watch: { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,24 +2,43 @@ | |
.c-image-viewer-modal(@wheel.prevent.stop='') | ||
.c-image-viewer-content | ||
.c-image-blurry-background(:style='blurryBgStyles') | ||
preview-image-area(:img-src='metaData.imgUrl' :name='metaData.name') | ||
preview-image-area( | ||
v-if='currentImage' | ||
:key='currentImage.id' | ||
:img-src='currentImage.imgUrl' | ||
:name='currentImage.name' | ||
) | ||
|
||
header.c-modal-header | ||
avatar-user.c-avatar( | ||
v-if='metaData.ownerID' | ||
:contractID='metaData.ownerID' | ||
v-if='currentImage.ownerID' | ||
:contractID='currentImage.ownerID' | ||
size='sm' | ||
) | ||
|
||
.c-img-data | ||
.c-name.has-ellipsis {{ displayName }} | ||
.c-filename.has-ellipsis {{ metaData.name }} | ||
.c-filename.has-ellipsis {{ currentImage.name }} | ||
|
||
button.is-icon-small.c-close-btn( | ||
type='button' | ||
@click.stop='close' | ||
) | ||
i.icon-times | ||
|
||
button.is-icon.c-image-nav-btn.is-prev( | ||
v-if='showPrevButton' | ||
type='button' | ||
@click='selectPrevImage' | ||
) | ||
i.icon-chevron-left | ||
|
||
button.is-icon.c-image-nav-btn.is-next( | ||
v-if='showNextButton' | ||
type='button' | ||
@click='selectNextImage' | ||
) | ||
i.icon-chevron-right | ||
</template> | ||
|
||
<script> | ||
|
@@ -39,30 +58,18 @@ export default { | |
PreviewImageArea | ||
}, | ||
props: { | ||
metaData: Object | ||
images: Array, | ||
initialIndex: { | ||
type: Number, | ||
required: false, | ||
default: 0 | ||
} | ||
}, | ||
data () { | ||
return { | ||
testImgSrc: '/assets/images/home-bg.jpeg', | ||
ephemeral: { | ||
previewImgAttrs: { | ||
width: undefined, | ||
height: undefined | ||
}, | ||
currentZoom: 0, | ||
showSliderOutput: false | ||
}, | ||
config: { | ||
imgData: { | ||
minWidth: null, | ||
minHeight: null, | ||
naturalWidth: null, | ||
naturalHeight: null, | ||
aspectRatio: 1 // intrinsic ratio value of width / height | ||
}, | ||
zoomMin: 0, | ||
zoomMax: 400, // for now. | ||
sliderUnit: '%' | ||
currentIndex: 0, | ||
isTouch: false | ||
} | ||
} | ||
}, | ||
|
@@ -73,36 +80,81 @@ export default { | |
]), | ||
blurryBgStyles () { | ||
return { | ||
backgroundImage: `url(${this.metaData.imgUrl})` | ||
backgroundImage: `url(${this.currentImage.imgUrl})` | ||
} | ||
}, | ||
displayName () { | ||
const contractID = this.metaData.ownerID | ||
const contractID = this.currentImage.ownerID | ||
return this.globalProfile(contractID)?.displayName || | ||
this.usernameFromID(contractID) | ||
}, | ||
hasMultipleImages () { | ||
return Array.isArray(this.images) && this.images.length > 1 | ||
}, | ||
showPrevButton () { | ||
const len = this.images.length | ||
return len > 1 && this.ephemeral.currentIndex > 0 | ||
}, | ||
showNextButton () { | ||
const len = this.images.length | ||
return len > 1 && this.ephemeral.currentIndex < len - 1 | ||
}, | ||
currentImage () { | ||
return this.images[this.ephemeral.currentIndex] | ||
} | ||
}, | ||
created () { | ||
if (!this.metaData) { | ||
if (!Array.isArray(this.images)) { | ||
this.$nextTick(() => this.close()) | ||
} else { | ||
this.ephemeral.currentIndex = this.initialIndex | ||
} | ||
|
||
this.touchMatchMedia = window.matchMedia('(hover: none) and (pointer: coarse)') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is the same issue as on the other PR with not having this in |
||
this.ephemeral.isTouch = this.touchMatchMedia.matches | ||
this.touchMatchMedia.onchange = (e) => { | ||
this.ephemeral.isTouch = e.matches | ||
} | ||
}, | ||
mounted () { | ||
document.addEventListener('keydown', this.trapFocus) | ||
document.addEventListener('keydown', this.keydownHandler) | ||
}, | ||
beforeDestroy () { | ||
document.removeEventListener('keydown', this.trapFocus) | ||
document.removeEventListener('keydown', this.keydownHandler) | ||
this.touchMatchMedia.onchange = null | ||
}, | ||
methods: { | ||
close () { | ||
sbp('okTurtles.events/emit', CLOSE_MODAL, 'ImageViewerModal') | ||
}, | ||
selectNextImage () { | ||
if (this.ephemeral.currentIndex < this.images.length - 1) { | ||
this.ephemeral.currentIndex += 1 | ||
} | ||
}, | ||
selectPrevImage () { | ||
if (this.ephemeral.currentIndex > 0) { | ||
this.ephemeral.currentIndex -= 1 | ||
} | ||
}, | ||
keydownHandler (e) { | ||
this.trapFocus(e) | ||
|
||
switch (e.key) { | ||
case 'ArrowLeft': | ||
this.selectPrevImage() | ||
break | ||
case 'ArrowRight': | ||
this.selectNextImage() | ||
} | ||
} | ||
} | ||
} | ||
</script> | ||
|
||
<style lang="scss" scoped> | ||
@import "@assets/style/_variables.scss"; | ||
$cta-zindex: 3; | ||
|
||
.c-image-viewer-modal { | ||
position: fixed; | ||
|
@@ -140,6 +192,9 @@ export default { | |
width: 100%; | ||
height: 100%; | ||
overflow: hidden; | ||
display: flex; | ||
flex-direction: row; | ||
align-items: stretch; | ||
|
||
@include from($tablet) { | ||
border-radius: 0.375rem; | ||
|
@@ -154,7 +209,7 @@ export default { | |
top: 0; | ||
left: 0; | ||
height: auto; | ||
z-index: 3; | ||
z-index: $cta-zindex; | ||
padding: 1rem; | ||
padding-right: 3rem; | ||
display: flex; | ||
|
@@ -230,4 +285,37 @@ export default { | |
color: var(--image-viewer-btn-text-color_active); | ||
} | ||
} | ||
|
||
.c-image-nav-btn { | ||
position: absolute; | ||
z-index: $cta-zindex; | ||
top: 50%; | ||
transform: translateY(-50%); | ||
background-color: var(--image-viewer-text-color); | ||
color: var(--image-viewer-btn-color); | ||
width: 2.5rem; | ||
height: 2.5rem; | ||
|
||
&.is-prev { | ||
left: 1rem; | ||
} | ||
|
||
&.is-next { | ||
right: 1rem; | ||
} | ||
|
||
@include phone { | ||
width: 2rem; | ||
height: 2rem; | ||
font-size: 0.75rem; | ||
|
||
&.is-prev { | ||
left: 0.75rem; | ||
} | ||
|
||
&.is-next { | ||
right: 0.75rem; | ||
} | ||
} | ||
} | ||
</style> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of a random ID, wouldn't it be better to use a deterministic ID in this case? For example, I think the URLs would be both deterministic and unique (or it could be combined with the message ID).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Valid suggestion. But I think it's safe to use the random string here because the
id
here is merely used for differentiating multiple images rendered in the DOM.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think it's safe also as used here, but I'm partial to deterministic IDs because they're always safe and can also be used, e.g., in testing.