Merge branch 'proper-attachments' into shigusegubu
* proper-attachments: ability to move attachments around when making a new post inline description display add media description into media modal add key attribute to make image refresh in media modal to give feedback when images are still loaded fix "+X more" sticking fix video attachments in notifications not having pointer cursor replace poll with an icon in notifications fix long posts double-fading in notifications fix links sticking to mentionsline fix console errors
This commit is contained in:
commit
42dd1d8611
15 changed files with 185 additions and 81 deletions
|
@ -15,7 +15,8 @@ import {
|
|||
faStop,
|
||||
faSearchPlus,
|
||||
faTrashAlt,
|
||||
faPencilAlt
|
||||
faPencilAlt,
|
||||
faAlignRight
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
|
@ -28,7 +29,8 @@ library.add(
|
|||
faStop,
|
||||
faSearchPlus,
|
||||
faTrashAlt,
|
||||
faPencilAlt
|
||||
faPencilAlt,
|
||||
faAlignRight
|
||||
)
|
||||
|
||||
const Attachment = {
|
||||
|
@ -40,6 +42,8 @@ const Attachment = {
|
|||
'size',
|
||||
'setMedia',
|
||||
'remove',
|
||||
'shiftUp',
|
||||
'shiftDn',
|
||||
'edit'
|
||||
],
|
||||
data () {
|
||||
|
@ -52,7 +56,8 @@ const Attachment = {
|
|||
img: fileTypeService.fileType(this.attachment.mimetype) === 'image' && document.createElement('img'),
|
||||
modalOpen: false,
|
||||
showHidden: false,
|
||||
flashLoaded: false
|
||||
flashLoaded: false,
|
||||
showDescription: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
@ -118,6 +123,9 @@ const Attachment = {
|
|||
}
|
||||
return modalTypes.includes(this.type)
|
||||
},
|
||||
videoTag () {
|
||||
return this.useModal ? 'button' : 'span'
|
||||
},
|
||||
...mapGetters(['mergedConfig'])
|
||||
},
|
||||
watch: {
|
||||
|
@ -148,12 +156,21 @@ const Attachment = {
|
|||
onRemove () {
|
||||
this.remove && this.remove(this.attachment)
|
||||
},
|
||||
onShiftUp () {
|
||||
this.shiftUp && this.shiftUp(this.attachment)
|
||||
},
|
||||
onShiftDn () {
|
||||
this.shiftDn && this.shiftDn(this.attachment)
|
||||
},
|
||||
stopFlash () {
|
||||
this.$refs.flash.closePlayer()
|
||||
},
|
||||
setFlashLoaded (event) {
|
||||
this.flashLoaded = event
|
||||
},
|
||||
toggleDescription () {
|
||||
this.showDescription = !this.showDescription
|
||||
},
|
||||
toggleHidden (event) {
|
||||
if (
|
||||
(this.mergedConfig.useOneClickNsfw && !this.showHidden) &&
|
||||
|
|
|
@ -42,19 +42,10 @@
|
|||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
padding-top: 0;
|
||||
background: var(--popover);
|
||||
box-shadow: var(--popupShadow);
|
||||
opacity: 0;
|
||||
transition: 0.35s all;
|
||||
transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.description-container.-static {
|
||||
opacity: 1;
|
||||
transform: translateY(-3em);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="size !== 'hide' && !hideDescription && (edit || localDescription)"
|
||||
v-if="size !== 'hide' && !hideDescription && (edit || localDescription || showDescription)"
|
||||
class="description-container"
|
||||
:class="{ '-static': !edit }"
|
||||
>
|
||||
|
@ -83,13 +83,23 @@
|
|||
v-if="type === 'flash' && flashLoaded"
|
||||
class="button-unstyled attachment-button"
|
||||
@click.prevent="stopFlash"
|
||||
:title="$t('status.attachment_stop_flash')"
|
||||
>
|
||||
<FAIcon icon="stop" />
|
||||
</button>
|
||||
<button
|
||||
v-if="attachment.description && size !== 'small' && !edit"
|
||||
class="button-unstyled attachment-button"
|
||||
@click.prevent="toggleDescription"
|
||||
:title="$t('status.show_attachment_description')"
|
||||
>
|
||||
<FAIcon icon="align-right" />
|
||||
</button>
|
||||
<button
|
||||
v-if="!useModal"
|
||||
class="button-unstyled attachment-button"
|
||||
@click.prevent="openModalForce"
|
||||
:title="$t('status.show_attachment_in_modal')"
|
||||
>
|
||||
<FAIcon icon="search-plus" />
|
||||
</button>
|
||||
|
@ -97,13 +107,31 @@
|
|||
v-if="nsfw && hideNsfwLocal"
|
||||
class="button-unstyled attachment-button"
|
||||
@click.prevent="toggleHidden"
|
||||
:title="$t('status.hide_attachment')"
|
||||
>
|
||||
<FAIcon icon="times" />
|
||||
</button>
|
||||
<button
|
||||
v-if="shiftUp"
|
||||
class="button-unstyled attachment-button"
|
||||
@click.prevent="onShiftUp"
|
||||
:title="$t('status.move_up')"
|
||||
>
|
||||
<FAIcon icon="chevron-left" />
|
||||
</button>
|
||||
<button
|
||||
v-if="shiftDn"
|
||||
class="button-unstyled attachment-button"
|
||||
@click.prevent="onShiftDn"
|
||||
:title="$t('status.move_down')"
|
||||
>
|
||||
<FAIcon icon="chevron-right" />
|
||||
</button>
|
||||
<button
|
||||
v-if="remove"
|
||||
class="button-unstyled attachment-button"
|
||||
@click.prevent="onRemove"
|
||||
:title="$t('status.remove_attachment')"
|
||||
>
|
||||
<FAIcon icon="trash-alt" />
|
||||
</button>
|
||||
|
@ -127,9 +155,11 @@
|
|||
/>
|
||||
</a>
|
||||
|
||||
<span
|
||||
<component
|
||||
:is="videoTag"
|
||||
v-if="type === 'video' && !hidden"
|
||||
class="video-container"
|
||||
:class="{ 'button-unstyled': 'isModal' }"
|
||||
:href="attachment.url"
|
||||
@click.stop.prevent="openModal"
|
||||
>
|
||||
|
@ -145,7 +175,7 @@
|
|||
class="play-icon"
|
||||
icon="play-circle"
|
||||
/>
|
||||
</span>
|
||||
</component>
|
||||
|
||||
<span
|
||||
v-if="type === 'audio' && !hidden"
|
||||
|
@ -199,7 +229,7 @@
|
|||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="size !== 'hide' && !hideDescription && (edit || localDescription)"
|
||||
v-if="size !== 'hide' && !hideDescription && (edit || (localDescription && showDescription))"
|
||||
class="description-container"
|
||||
:class="{ '-static': !edit }"
|
||||
>
|
||||
|
|
|
@ -12,6 +12,8 @@ const Gallery = {
|
|||
'size',
|
||||
'editable',
|
||||
'removeAttachment',
|
||||
'shiftUpAttachment',
|
||||
'shiftDnAttachment',
|
||||
'editAttachment',
|
||||
'grid'
|
||||
],
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
>
|
||||
<div class="gallery-rows">
|
||||
<div
|
||||
v-for="(row, index) in rows"
|
||||
:key="index"
|
||||
v-for="(row, rowIndex) in rows"
|
||||
:key="rowIndex"
|
||||
class="gallery-row"
|
||||
:style="rowStyle(row)"
|
||||
:class="{ '-audio': row.audio, '-minimal': row.minimal, '-grid': grid }"
|
||||
|
@ -16,8 +16,8 @@
|
|||
class="gallery-row-inner"
|
||||
:class="{ '-grid': grid }"
|
||||
>
|
||||
<attachment
|
||||
v-for="attachment in row.items"
|
||||
<Attachment
|
||||
v-for="(attachment, attachmentIndex) in row.items"
|
||||
:key="attachment.id"
|
||||
class="gallery-item"
|
||||
:nsfw="nsfw"
|
||||
|
@ -26,6 +26,8 @@
|
|||
:size="size"
|
||||
:editable="editable"
|
||||
:remove="removeAttachment"
|
||||
:shiftUp="!(attachmentIndex === 0 && rowIndex === 0) && shiftUpAttachment"
|
||||
:shiftDn="!(attachmentIndex === row.items.length - 1 && rowIndex === rows.length - 1) && shiftDnAttachment"
|
||||
:edit="editAttachment"
|
||||
:description="descriptions && descriptions[attachment.id]"
|
||||
:hide-description="size === 'small' || tooManyAttachments && hidingLong"
|
||||
|
|
|
@ -29,6 +29,9 @@ const MediaModal = {
|
|||
media () {
|
||||
return this.$store.state.mediaViewer.media
|
||||
},
|
||||
description () {
|
||||
return this.currentMedia.description
|
||||
},
|
||||
currentIndex () {
|
||||
return this.$store.state.mediaViewer.currentIndex
|
||||
},
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
v-if="type === 'image'"
|
||||
class="modal-image"
|
||||
:src="currentMedia.url"
|
||||
:key="currentMedia.url"
|
||||
:alt="currentMedia.description"
|
||||
:title="currentMedia.description"
|
||||
@touchstart.stop="mediaTouchStart"
|
||||
|
@ -19,11 +20,13 @@
|
|||
class="modal-image"
|
||||
:attachment="currentMedia"
|
||||
:controls="true"
|
||||
:key="currentMedia.url"
|
||||
/>
|
||||
<audio
|
||||
v-if="type === 'audio'"
|
||||
class="modal-image"
|
||||
:src="currentMedia.url"
|
||||
:key="currentMedia.url"
|
||||
:alt="currentMedia.description"
|
||||
:title="currentMedia.description"
|
||||
controls
|
||||
|
@ -31,6 +34,7 @@
|
|||
<Flash
|
||||
v-if="type === 'flash'"
|
||||
class="modal-image"
|
||||
:key="currentMedia.url"
|
||||
:src="currentMedia.url"
|
||||
:alt="currentMedia.description"
|
||||
:title="currentMedia.description"
|
||||
|
@ -57,6 +61,12 @@
|
|||
icon="chevron-right"
|
||||
/>
|
||||
</button>
|
||||
<span
|
||||
v-if="description"
|
||||
class="description"
|
||||
>
|
||||
{{ description }}
|
||||
</span>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
|
@ -65,6 +75,7 @@
|
|||
<style lang="scss">
|
||||
.modal-view.media-modal-view {
|
||||
z-index: 1001;
|
||||
flex-direction: column;
|
||||
|
||||
.modal-view-button-arrow {
|
||||
opacity: 0.75;
|
||||
|
@ -80,63 +91,76 @@
|
|||
}
|
||||
}
|
||||
|
||||
@keyframes media-fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-image {
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
box-shadow: 0px 5px 15px 0 rgba(0, 0, 0, 0.5);
|
||||
image-orientation: from-image; // NOTE: only FF supports this
|
||||
animation: 0.1s cubic-bezier(0.7, 0, 1, 0.6) media-fadein;
|
||||
}
|
||||
|
||||
.modal-view-button-arrow {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 50%;
|
||||
margin-top: -50px;
|
||||
width: 70px;
|
||||
height: 100px;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
box-shadow: none;
|
||||
background: none;
|
||||
appearance: none;
|
||||
overflow: visible;
|
||||
cursor: pointer;
|
||||
transition: opacity 333ms cubic-bezier(.4,0,.22,1);
|
||||
|
||||
.arrow-icon {
|
||||
position: absolute;
|
||||
top: 35px;
|
||||
height: 30px;
|
||||
width: 32px;
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
color: #FFF;
|
||||
text-align: center;
|
||||
background-color: rgba(0,0,0,.3);
|
||||
}
|
||||
|
||||
&--prev {
|
||||
left: 0;
|
||||
.arrow-icon {
|
||||
left: 6px;
|
||||
.media-modal-view {
|
||||
@keyframes media-fadein {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&--next {
|
||||
right: 0;
|
||||
/* Hardcoded since background is also hardcoded */
|
||||
.description {
|
||||
color: white;
|
||||
margin-top: 1em;
|
||||
text-shadow: 0 0 10px black, 0 0 10px black;
|
||||
max-width: 500px;
|
||||
max-height: 9.5em;
|
||||
overflow-y: auto;
|
||||
padding: 0 2em;
|
||||
}
|
||||
|
||||
.modal-image {
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
box-shadow: 0px 5px 15px 0 rgba(0, 0, 0, 0.5);
|
||||
image-orientation: from-image; // NOTE: only FF supports this
|
||||
animation: 0.1s cubic-bezier(0.7, 0, 1, 0.6) media-fadein;
|
||||
}
|
||||
|
||||
.modal-view-button-arrow {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 50%;
|
||||
margin-top: -50px;
|
||||
width: 70px;
|
||||
height: 100px;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
opacity: 0;
|
||||
box-shadow: none;
|
||||
background: none;
|
||||
appearance: none;
|
||||
overflow: visible;
|
||||
cursor: pointer;
|
||||
transition: opacity 333ms cubic-bezier(.4,0,.22,1);
|
||||
|
||||
.arrow-icon {
|
||||
right: 6px;
|
||||
position: absolute;
|
||||
top: 35px;
|
||||
height: 30px;
|
||||
width: 32px;
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
color: #FFF;
|
||||
text-align: center;
|
||||
background-color: rgba(0,0,0,.3);
|
||||
}
|
||||
|
||||
&--prev {
|
||||
left: 0;
|
||||
.arrow-icon {
|
||||
left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&--next {
|
||||
right: 0;
|
||||
.arrow-icon {
|
||||
right: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
.showMoreLess {
|
||||
white-space: normal;
|
||||
color: var(--link);
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
.mention-link:not(:last-child) {
|
||||
.mention-link {
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -391,9 +391,20 @@ const PostStatusForm = {
|
|||
this.$emit('resize')
|
||||
},
|
||||
editAttachment (fileInfo, newText) {
|
||||
console.log(fileInfo, newText)
|
||||
this.newStatus.mediaDescriptions[fileInfo.id] = newText
|
||||
},
|
||||
shiftUpMediaFile (fileInfo) {
|
||||
const { files } = this.newStatus
|
||||
const index = this.newStatus.files.indexOf(fileInfo)
|
||||
files.splice(index, 1)
|
||||
files.splice(index - 1, 0, fileInfo)
|
||||
},
|
||||
shiftDnMediaFile (fileInfo) {
|
||||
const { files } = this.newStatus
|
||||
const index = this.newStatus.files.indexOf(fileInfo)
|
||||
files.splice(index, 1)
|
||||
files.splice(index + 1, 0, fileInfo)
|
||||
},
|
||||
uploadFailed (errString, templateArgs) {
|
||||
templateArgs = templateArgs || {}
|
||||
this.error = this.$t('upload.error.base') + ' ' + this.$t('upload.error.' + errString, templateArgs)
|
||||
|
|
|
@ -298,6 +298,8 @@
|
|||
:editable="true"
|
||||
:edit-attachment="editAttachment"
|
||||
:remove-attachment="removeMediaFile"
|
||||
:shift-up-attachment="newStatus.files.length > 1 && shiftUpMediaFile"
|
||||
:shift-dn-attachment="newStatus.files.length > 1 && shiftDnMediaFile"
|
||||
@play="$emit('mediaplay', attachment.id)"
|
||||
@pause="$emit('mediapause', attachment.id)"
|
||||
/>
|
||||
|
|
|
@ -121,6 +121,13 @@ export default Vue.component('RichContent', {
|
|||
// in MentionsLine
|
||||
return currentMentions !== null ? item.trim() : item
|
||||
}
|
||||
// We add space with mentionsLine, otherwise non-text elements will
|
||||
// stick to them.
|
||||
if (currentMentions !== null) {
|
||||
// single whitespace trim
|
||||
item = item[0].match(/\s/) ? item.slice(1) : item
|
||||
}
|
||||
|
||||
currentMentions = null
|
||||
if (item.includes(':')) {
|
||||
item = ['', processTextForEmoji(
|
||||
|
|
|
@ -50,6 +50,7 @@ const StatusContent = {
|
|||
// Using max-height + overflow: auto for status components resulted in false positives
|
||||
// very often with japanese characters, and it was very annoying.
|
||||
tallStatus () {
|
||||
if (this.singleLine || this.compact) return false
|
||||
const lengthScore = this.status.raw_html.split(/<p|<br/).length + this.postLength / 80
|
||||
return lengthScore > 20
|
||||
},
|
||||
|
|
|
@ -10,13 +10,20 @@
|
|||
:single-line="singleLine"
|
||||
@parseReady="$emit('parseReady', $event)"
|
||||
>
|
||||
<div v-if="status.poll && status.poll.options">
|
||||
<div v-if="status.poll && status.poll.options && !compact">
|
||||
<Poll
|
||||
:base-poll="status.poll"
|
||||
:emoji="status.emojis"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-else-if="status.poll && status.poll.options && compact">
|
||||
<FAIcon
|
||||
icon="poll-h"
|
||||
size="2x"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<gallery
|
||||
v-if="status.attachments.length !== 0"
|
||||
class="attachments media-body"
|
||||
|
@ -24,7 +31,6 @@
|
|||
:attachments="status.attachments"
|
||||
:limit="compact ? 1 : 0"
|
||||
:size="attachmentSize"
|
||||
@setMedia="onMedia"
|
||||
@play="$emit('mediaplay', attachment.id)"
|
||||
@pause="$emit('mediapause', attachment.id)"
|
||||
/>
|
||||
|
|
|
@ -727,6 +727,13 @@
|
|||
"many_attachments": "Post has {number} attachment(s)",
|
||||
"collapse_attachments": "Collapse attachments",
|
||||
"show_all_attachments": "Show all attachments",
|
||||
"show_attachment_in_modal": "Show in media modal",
|
||||
"show_attachment_description": "Preview description (open attachment for full description)",
|
||||
"hide_attachment": "Hide attachment",
|
||||
"remove_attachment": "Remove attachment",
|
||||
"attachment_stop_flash": "Stop Flash player",
|
||||
"move_up": "Shift attachment left",
|
||||
"move_down": "Shift attachment right",
|
||||
"open_gallery": "Open gallery"
|
||||
},
|
||||
"user_card": {
|
||||
|
|
|
@ -59,7 +59,7 @@ describe('RichContent', () => {
|
|||
it('replaces mention with mentionsline', () => {
|
||||
const html = p(
|
||||
makeMention('John'),
|
||||
' how are you doing today?'
|
||||
' how are you doing today?' // also testing single-trimming function
|
||||
)
|
||||
const wrapper = shallowMount(RichContent, {
|
||||
localVue,
|
||||
|
@ -74,7 +74,7 @@ describe('RichContent', () => {
|
|||
|
||||
expect(wrapper.html()).to.eql(compwrap(p(
|
||||
mentionsLine(1),
|
||||
' how are you doing today?'
|
||||
' how are you doing today?' // space removed to compensate for <ML> padding
|
||||
)))
|
||||
})
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue