gallery in post status form!

This commit is contained in:
Henry Jameson 2021-06-18 02:04:01 +03:00
parent 90345f158f
commit f15599e6e5
6 changed files with 231 additions and 196 deletions

View File

@ -13,7 +13,9 @@ import {
faPlayCircle, faPlayCircle,
faTimes, faTimes,
faStop, faStop,
faSearchPlus faSearchPlus,
faTrashAlt,
faPencilAlt
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
library.add( library.add(
@ -24,19 +26,25 @@ library.add(
faPlayCircle, faPlayCircle,
faTimes, faTimes,
faStop, faStop,
faSearchPlus faSearchPlus,
faTrashAlt,
faPencilAlt
) )
const Attachment = { const Attachment = {
props: [ props: [
'attachment', 'attachment',
'description',
'hideDescription',
'nsfw', 'nsfw',
'size', 'size',
'setMedia', 'setMedia',
'naturalSizeLoad' 'remove',
'edit'
], ],
data () { data () {
return { return {
localDescription: this.description || this.attachment.description,
nsfwImage: this.$store.state.instance.nsfwCensorImage || nsfwImage, nsfwImage: this.$store.state.instance.nsfwCensorImage || nsfwImage,
hideNsfwLocal: this.$store.getters.mergedConfig.hideNsfw, hideNsfwLocal: this.$store.getters.mergedConfig.hideNsfw,
preloadImage: this.$store.getters.mergedConfig.preloadImage, preloadImage: this.$store.getters.mergedConfig.preloadImage,
@ -93,9 +101,6 @@ const Attachment = {
isEmpty () { isEmpty () {
return (this.type === 'html' && !this.attachment.oembed) || this.type === 'unknown' return (this.type === 'html' && !this.attachment.oembed) || this.type === 'unknown'
}, },
isSmall () {
return this.size === 'small'
},
useModal () { useModal () {
const modalTypes = this.size === 'hide' ? ['image', 'video', 'audio'] const modalTypes = this.size === 'hide' ? ['image', 'video', 'audio']
: this.mergedConfig.playVideosInModal : this.mergedConfig.playVideosInModal
@ -105,6 +110,11 @@ const Attachment = {
}, },
...mapGetters(['mergedConfig']) ...mapGetters(['mergedConfig'])
}, },
watch: {
localDescription (newVal) {
this.onEdit(newVal)
}
},
methods: { methods: {
linkClicked ({ target }) { linkClicked ({ target }) {
if (target.tagName === 'A') { if (target.tagName === 'A') {
@ -121,6 +131,12 @@ const Attachment = {
this.$emit('setMedia') this.$emit('setMedia')
this.$store.dispatch('setCurrentMedia', this.attachment) this.$store.dispatch('setCurrentMedia', this.attachment)
}, },
onEdit (event) {
console.log('ONEDIT', event)
this.edit && this.edit(this.attachment, event)
},
onRemove () {
this.remove && this.remove(this.attachment)
}, },
stopFlash () { stopFlash () {
this.$refs.flash.closePlayer() this.$refs.flash.closePlayer()
@ -154,7 +170,7 @@ const Attachment = {
onImageLoad (image) { onImageLoad (image) {
const width = image.naturalWidth const width = image.naturalWidth
const height = image.naturalHeight const height = image.naturalHeight
this.naturalSizeLoad && this.naturalSizeLoad({ width, height }) this.$emit('naturalSizeLoad', { id: this.attachment.id, width, height })
} }
} }
} }

View File

@ -12,151 +12,180 @@
:href="attachment.url" :href="attachment.url"
:alt="attachment.description" :alt="attachment.description"
:title="attachment.description" :title="attachment.description"
@click.prevent=""
> >
<FAIcon :icon="placeholderIconClass" /> <FAIcon :icon="placeholderIconClass" />
<b>{{ nsfw ? "NSFW / " : "" }}</b>{{ placeholderName }} <b>{{ nsfw ? "NSFW / " : "" }}</b>{{ placeholderName }}
</a> </a>
</button> </button>
<div <div
v-else
v-show="!isEmpty"
class="Attachment" class="Attachment"
:class="classNames" :class="classNames"
v-else
> >
<a
v-if="hidden"
class="image-container"
:href="attachment.url"
:alt="attachment.description"
:title="attachment.description"
@click.prevent.stop="toggleHidden"
>
<img
:key="nsfwImage"
class="nsfw"
:src="nsfwImage"
>
<FAIcon
v-if="type === 'video'"
class="play-icon"
icon="play-circle"
/>
</a>
<div <div
class="attachment-buttons" class="attachment-wrapper"
v-if="!hidden" v-show="!isEmpty"
>
<button
v-if="type === 'flash' && flashLoaded"
class="button-unstyled attachment-button"
@click.prevent="stopFlash"
>
<FAIcon icon="stop" />
</button>
<button
v-if="!useModal"
class="button-unstyled attachment-button"
@click.prevent="openModalForce"
>
<FAIcon icon="search-plus" />
</button>
<button
v-if="nsfw && hideNsfwLocal"
class="button-unstyled attachment-button"
@click.prevent="toggleHidden"
>
<FAIcon icon="times" />
</button>
</div>
<a
v-if="type === 'image' && (!hidden || preloadImage)"
class="image-container"
:class="{'-hidden': hidden && preloadImage }"
:href="attachment.url"
target="_blank"
@click.stop.prevent="openModal"
> >
<StillImage <a
class="image" v-if="hidden"
:referrerpolicy="referrerpolicy" class="image-container"
:mimetype="attachment.mimetype" :href="attachment.url"
:src="attachment.large_thumb_url || attachment.url"
:image-load-handler="onImageLoad"
:alt="attachment.description"
/>
</a>
<a
v-if="type === 'video' && !hidden"
class="video-container"
:href="attachment.url"
@click.stop.prevent="openModal"
>
<VideoAttachment
class="video"
:attachment="attachment"
:controls="!useModal"
@play="$emit('play')"
@pause="$emit('pause')"
/>
<FAIcon
v-if="useModal"
class="play-icon"
icon="play-circle"
/>
</a>
<a
v-if="type === 'audio' && !hidden"
class="audio-container"
:href="attachment.url"
@click.stop.prevent="openModal"
>
<audio
v-if="type === 'audio'"
:src="attachment.url"
:alt="attachment.description" :alt="attachment.description"
:title="attachment.description" :title="attachment.description"
controls @click.prevent.stop="toggleHidden"
@play="$emit('play')"
@pause="$emit('pause')"
/>
</a>
<div
v-if="type === 'html' && attachment.oembed"
class="oembed-container"
@click.prevent="linkClicked"
>
<div
v-if="attachment.thumb_url"
class="image"
> >
<img :src="attachment.thumb_url"> <img
:key="nsfwImage"
class="nsfw"
:src="nsfwImage"
>
<FAIcon
v-if="type === 'video'"
class="play-icon"
icon="play-circle"
/>
</a>
<div
class="attachment-buttons"
v-if="!hidden"
>
<button
v-if="type === 'flash' && flashLoaded"
class="button-unstyled attachment-button"
@click.prevent="stopFlash"
>
<FAIcon icon="stop" />
</button>
<button
v-if="!useModal"
class="button-unstyled attachment-button"
@click.prevent="openModalForce"
>
<FAIcon icon="search-plus" />
</button>
<button
v-if="nsfw && hideNsfwLocal"
class="button-unstyled attachment-button"
@click.prevent="toggleHidden"
>
<FAIcon icon="times" />
</button>
<button
v-if="remove"
class="button-unstyled attachment-button"
@click.prevent="onRemove"
>
<FAIcon icon="trash-alt" />
</button>
</div> </div>
<div class="text">
<!-- eslint-disable vue/no-v-html -->
<h1><a :href="attachment.url">{{ attachment.oembed.title }}</a></h1>
<div v-html="attachment.oembed.oembedHTML" />
<!-- eslint-enable vue/no-v-html -->
</div>
</div>
<a <a
v-if="type === 'flash' && !hidden" v-if="type === 'image' && (!hidden || preloadImage)"
class="flash-container" class="image-container"
:href="attachment.url" :class="{'-hidden': hidden && preloadImage }"
@click.stop.prevent="openModal" :href="attachment.url"
target="_blank"
@click.stop.prevent="openModal"
>
<StillImage
class="image"
:referrerpolicy="referrerpolicy"
:mimetype="attachment.mimetype"
:src="attachment.large_thumb_url || attachment.url"
:image-load-handler="onImageLoad"
:alt="attachment.description"
/>
</a>
<a
v-if="type === 'video' && !hidden"
class="video-container"
:href="attachment.url"
@click.stop.prevent="openModal"
>
<VideoAttachment
class="video"
:attachment="attachment"
:controls="!useModal"
@play="$emit('play')"
@pause="$emit('pause')"
/>
<FAIcon
v-if="useModal"
class="play-icon"
icon="play-circle"
/>
</a>
<a
v-if="type === 'audio' && !hidden"
class="audio-container"
:href="attachment.url"
@click.stop.prevent="openModal"
>
<audio
v-if="type === 'audio'"
:src="attachment.url"
:alt="attachment.description"
:title="attachment.description"
controls
@play="$emit('play')"
@pause="$emit('pause')"
/>
</a>
<div
v-if="type === 'html' && attachment.oembed"
class="oembed-container"
@click.prevent="linkClicked"
>
<div
v-if="attachment.thumb_url"
class="image"
>
<img :src="attachment.thumb_url">
</div>
<div class="text">
<!-- eslint-disable vue/no-v-html -->
<h1><a :href="attachment.url">{{ attachment.oembed.title }}</a></h1>
<div v-html="attachment.oembed.oembedHTML" />
<!-- eslint-enable vue/no-v-html -->
</div>
</div>
<a
v-if="type === 'flash' && !hidden"
class="flash-container"
:href="attachment.url"
@click.stop.prevent="openModal"
>
<Flash
class="flash"
ref="flash"
:src="attachment.large_thumb_url || attachment.url"
@playerOpened="setFlashLoaded(true)"
@playerClosed="setFlashLoaded(false)"
/>
</a>
</div>
<div
v-if="size !== 'hide' && !hideDescription && (edit || localDescription)"
class="description-container"
:class="{ '-static': !edit }"
> >
<Flash <input
class="flash" v-if="edit"
ref="flash" v-model="localDescription"
:src="attachment.large_thumb_url || attachment.url" type="text"
@playerOpened="setFlashLoaded(true)" class="description-field"
@playerClosed="setFlashLoaded(false)" :placeholder="$t('post_status.media_description')"
/> @keydown.enter.prevent=""
</a> >
<p v-else>
{{ localDescription }}
</p>
</div>
</div> </div>
</template> </template>

View File

@ -4,9 +4,15 @@ import { sumBy } from 'lodash'
const Gallery = { const Gallery = {
props: [ props: [
'attachments', 'attachments',
'limitRows',
'descriptions',
'nsfw', 'nsfw',
'setMedia', 'setMedia',
'size' 'size',
'editable',
'removeAttachment',
'editAttachment',
'maxPerRow'
], ],
data () { data () {
return { return {
@ -20,24 +26,19 @@ const Gallery = {
if (!this.attachments) { if (!this.attachments) {
return [] return []
} }
console.log(this.size)
if (this.size === 'hide') { if (this.size === 'hide') {
return this.attachments.map(item => ({ minimal: true, items: [item] })) return this.attachments.map(item => ({ minimal: true, items: [item] }))
} }
const rows = this.attachments.reduce((acc, attachment, i) => { const rows = this.attachments.reduce((acc, attachment, i) => {
if (this.size === 'small' && acc.length === 2) return acc
if (attachment.mimetype.includes('audio')) { if (attachment.mimetype.includes('audio')) {
return [...acc, { audio: true, items: [attachment] }, { items: [] }] return [...acc, { audio: true, items: [attachment] }, { items: [] }]
} }
const maxPerRow = 3 const maxPerRow = this.maxPerRow || 3
const attachmentsRemaining = this.attachments.length - i - 1 const attachmentsRemaining = this.attachments.length - i + 1
const currentRow = acc[acc.length - 1].items const currentRow = acc[acc.length - 1].items
if ( currentRow.push(attachment)
currentRow.length <= maxPerRow || if (currentRow.length >= maxPerRow && attachmentsRemaining > maxPerRow) {
attachmentsRemaining === 1
) {
currentRow.push(attachment)
}
if (currentRow.length === maxPerRow && attachmentsRemaining > 1) {
return [...acc, { items: [] }] return [...acc, { items: [] }]
} else { } else {
return acc return acc
@ -51,7 +52,9 @@ const Gallery = {
}, 0) }, 0)
}, },
tooManyAttachments () { tooManyAttachments () {
if (this.size === 'hide') { if (this.editable || this.size === 'small') {
return false
} else if (this.size === 'hide') {
return this.attachments.length > 8 return this.attachments.length > 8
} else { } else {
return this.attachmentsDimensionalScore > 1 return this.attachmentsDimensionalScore > 1
@ -59,8 +62,8 @@ const Gallery = {
} }
}, },
methods: { methods: {
onNaturalSizeLoad (id, size) { onNaturalSizeLoad ({ id, width, height }) {
this.$set(this.sizes, id, size) this.$set(this.sizes, id, { width, height })
}, },
rowStyle (row) { rowStyle (row) {
if (row.audio) { if (row.audio) {
@ -81,8 +84,11 @@ const Gallery = {
this.hidingLong = event this.hidingLong = event
}, },
openGallery () { openGallery () {
this.setMedia() this.$store.dispatch('setMedia', this.attachments)
this.$store.dispatch('setCurrent', this.attachments[0]) this.$store.dispatch('setCurrentMedia', this.attachments[0])
},
onMedia () {
this.$store.dispatch('setMedia', this.attachments)
} }
} }
} }

View File

@ -17,13 +17,18 @@
v-for="attachment in row.items" v-for="attachment in row.items"
class="gallery-item" class="gallery-item"
:key="attachment.id" :key="attachment.id"
:set-media="setMedia"
:nsfw="nsfw" :nsfw="nsfw"
:attachment="attachment" :attachment="attachment"
:allow-play="false" :allow-play="false"
:size="size" :size="size"
:natural-size-load="onNaturalSizeLoad.bind(null, attachment.id)" :editable="editable"
:remove="removeAttachment"
:edit="editAttachment"
:description="descriptions && descriptions[attachment.id]"
:hideDescription="tooManyAttachments && hidingLong"
:style="itemStyle(attachment.id, row.items)" :style="itemStyle(attachment.id, row.items)"
@setMedia="onMedia"
@naturalSizeLoad="onNaturalSizeLoad"
/> />
</div> </div>
</div> </div>

View File

@ -4,6 +4,7 @@ import ScopeSelector from '../scope_selector/scope_selector.vue'
import EmojiInput from '../emoji_input/emoji_input.vue' import EmojiInput from '../emoji_input/emoji_input.vue'
import PollForm from '../poll/poll_form.vue' import PollForm from '../poll/poll_form.vue'
import Attachment from '../attachment/attachment.vue' import Attachment from '../attachment/attachment.vue'
import Gallery from 'src/components/gallery/gallery.vue'
import StatusContent from '../status_content/status_content.vue' import StatusContent from '../status_content/status_content.vue'
import fileTypeService from '../../services/file_type/file_type.service.js' import fileTypeService from '../../services/file_type/file_type.service.js'
import { findOffset } from '../../services/offset_finder/offset_finder.service.js' import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
@ -85,7 +86,8 @@ const PostStatusForm = {
Checkbox, Checkbox,
Select, Select,
Attachment, Attachment,
StatusContent StatusContent,
Gallery
}, },
mounted () { mounted () {
this.updateIdempotencyKey() this.updateIdempotencyKey()
@ -388,6 +390,10 @@ const PostStatusForm = {
this.newStatus.files.splice(index, 1) this.newStatus.files.splice(index, 1)
this.$emit('resize') this.$emit('resize')
}, },
editAttachment (fileInfo, newText) {
console.log(fileInfo, newText)
this.newStatus.mediaDescriptions[fileInfo.id] = newText
},
uploadFailed (errString, templateArgs) { uploadFailed (errString, templateArgs) {
templateArgs = templateArgs || {} templateArgs = templateArgs || {}
this.error = this.$t('upload.error.base') + ' ' + this.$t('upload.error.' + errString, templateArgs) this.error = this.$t('upload.error.base') + ' ' + this.$t('upload.error.' + errString, templateArgs)

View File

@ -287,32 +287,21 @@
@click="clearError" @click="clearError"
/> />
</div> </div>
<div class="attachments"> <gallery
<div v-if="newStatus.files && newStatus.files.length > 0"
v-for="file in newStatus.files" class="attachments"
:key="file.url" :maxPerRow="1"
class="media-upload-wrapper" :nsfw="false"
> :attachments="newStatus.files"
<button :descriptions="newStatus.mediaDescriptions"
class="button-unstyled hider" :set-media="() => $store.dispatch('setMedia', newStatus.files)"
@click="removeMediaFile(file)" :editable="true"
> :editAttachment="editAttachment"
<FAIcon icon="times" /> :removeAttachment="removeMediaFile"
</button> size="small"
<attachment @play="$emit('mediaplay', attachment.id)"
:attachment="file" @pause="$emit('mediapause', attachment.id)"
:set-media="() => $store.dispatch('setMedia', newStatus.files)" />
size="small"
allow-play="false"
/>
<input
v-model="newStatus.mediaDescriptions[file.id]"
type="text"
:placeholder="$t('post_status.media_description')"
@keydown.enter.prevent=""
>
</div>
</div>
<div <div
v-if="newStatus.files.length > 0 && !disableSensitivityCheckbox" v-if="newStatus.files.length > 0 && !disableSensitivityCheckbox"
class="upload_settings" class="upload_settings"
@ -507,15 +496,6 @@
flex-direction: column; flex-direction: column;
} }
.attachments .media-upload-wrapper {
position: relative;
.attachment {
margin: 0;
padding: 0;
}
}
.btn { .btn {
cursor: pointer; cursor: pointer;
} }
@ -616,11 +596,4 @@
border: 2px dashed var(--text, $fallback--text); border: 2px dashed var(--text, $fallback--text);
} }
} }
// todo: unify with attachment.vue (otherwise the uploaded images are not minified unless a status with an attachment was displayed before)
img.media-upload, .media-upload-container > video {
line-height: 0;
max-height: 200px;
max-width: 100%;
}
</style> </style>