refactored the way status suspensibility works
This commit is contained in:
parent
97fdee5c9d
commit
31f4ad343a
16 changed files with 182 additions and 283 deletions
|
|
@ -52,6 +52,7 @@ const Attachment = {
|
|||
'shiftDn',
|
||||
'edit',
|
||||
],
|
||||
emits: ['play', 'pause'],
|
||||
data() {
|
||||
return {
|
||||
localDescription: this.description || this.attachment.description,
|
||||
|
|
|
|||
|
|
@ -55,16 +55,6 @@ const sortAndFilterConversation = (conversation, statusoid) => {
|
|||
}
|
||||
|
||||
const conversation = {
|
||||
data() {
|
||||
return {
|
||||
highlight: null,
|
||||
expanded: false,
|
||||
threadDisplayStatusObject: {}, // id => 'showing' | 'hidden'
|
||||
statusContentPropertiesObject: {},
|
||||
inlineDivePosition: null,
|
||||
loadStatusError: null,
|
||||
}
|
||||
},
|
||||
props: [
|
||||
'statusId',
|
||||
'collapsable',
|
||||
|
|
@ -74,6 +64,16 @@ const conversation = {
|
|||
'profileUserId',
|
||||
'virtualHidden',
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
highlight: null,
|
||||
expanded: false,
|
||||
threadDisplayStatusObject: {}, // id => 'showing' | 'hidden'
|
||||
inlineDivePosition: null,
|
||||
loadStatusError: null,
|
||||
unsuspendibleIds: new Set(),
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.isPage) {
|
||||
this.fetchConversation()
|
||||
|
|
@ -118,16 +118,7 @@ const conversation = {
|
|||
return this.otherRepliesButtonPosition === 'inside'
|
||||
},
|
||||
suspendable() {
|
||||
if (this.isTreeView) {
|
||||
return Object.entries(this.statusContentProperties).every(
|
||||
([, prop]) => !prop.replying && prop.mediaPlaying.length === 0,
|
||||
)
|
||||
}
|
||||
if (this.$refs.statusComponent && this.$refs.statusComponent[0]) {
|
||||
return this.$refs.statusComponent.every((s) => s.suspendable)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
return this.unsuspendibleIds.size > 0
|
||||
},
|
||||
hideStatus() {
|
||||
return this.virtualHidden && this.suspendable
|
||||
|
|
@ -364,31 +355,6 @@ const conversation = {
|
|||
return a
|
||||
}, {})
|
||||
},
|
||||
statusContentProperties() {
|
||||
return this.conversation.reduce((a, k) => {
|
||||
const id = k.id
|
||||
const props = (() => {
|
||||
const def = {
|
||||
showingTall: false,
|
||||
expandingSubject: false,
|
||||
showingLongSubject: false,
|
||||
isReplying: false,
|
||||
mediaPlaying: [],
|
||||
}
|
||||
|
||||
if (this.statusContentPropertiesObject[id]) {
|
||||
return {
|
||||
...def,
|
||||
...this.statusContentPropertiesObject[id],
|
||||
}
|
||||
}
|
||||
return def
|
||||
})()
|
||||
|
||||
a[id] = props
|
||||
return a
|
||||
}, {})
|
||||
},
|
||||
canDive() {
|
||||
return this.isTreeView && this.isExpanded
|
||||
},
|
||||
|
|
@ -514,22 +480,6 @@ const conversation = {
|
|||
showThreadRecursively(id) {
|
||||
this.setThreadDisplayRecursively(id, 'showing')
|
||||
},
|
||||
setStatusContentProperty(id, name, value) {
|
||||
this.statusContentPropertiesObject = {
|
||||
...this.statusContentPropertiesObject,
|
||||
[id]: {
|
||||
...this.statusContentPropertiesObject[id],
|
||||
[name]: value,
|
||||
},
|
||||
}
|
||||
},
|
||||
toggleStatusContentProperty(id, name) {
|
||||
this.setStatusContentProperty(
|
||||
id,
|
||||
name,
|
||||
!this.statusContentProperties[id][name],
|
||||
)
|
||||
},
|
||||
leastVisibleAncestor(id) {
|
||||
let cur = id
|
||||
let parent = this.parentOf(cur)
|
||||
|
|
@ -629,6 +579,13 @@ const conversation = {
|
|||
this.undive()
|
||||
this.threadDisplayStatusObject = {}
|
||||
},
|
||||
onStatusSuspendStateChange({ id, suspend }) {
|
||||
if (!suspend) {
|
||||
this.unsuspendibleIds.add(id)
|
||||
} else {
|
||||
this.unsuspendibleIds.delete(id)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,19 +107,8 @@
|
|||
:show-other-replies-as-button="showOtherRepliesButtonInsideStatus"
|
||||
:dive="() => diveIntoStatus(status.id)"
|
||||
|
||||
:controlled-showing-tall="statusContentProperties[status.id].showingTall"
|
||||
:controlled-toggle-showing-tall="() => toggleStatusContentProperty(status.id, 'showingTall')"
|
||||
:controlled-expanding-subject="statusContentProperties[status.id].expandingSubject"
|
||||
:controlled-toggle-expanding-subject="() => toggleStatusContentProperty(status.id, 'expandingSubject')"
|
||||
:controlled-showing-long-subject="statusContentProperties[status.id].showingLongSubject"
|
||||
:controlled-toggle-showing-long-subject="() => toggleStatusContentProperty(status.id, 'showingLongSubject')"
|
||||
:controlled-replying="statusContentProperties[status.id].replying"
|
||||
:controlled-toggle-replying="() => toggleStatusContentProperty(status.id, 'replying')"
|
||||
:controlled-media-playing="statusContentProperties[status.id].mediaPlaying"
|
||||
:controlled-set-media-playing="(newVal) => toggleStatusContentProperty(status.id, 'mediaPlaying', newVal)"
|
||||
|
||||
@goto="setHighlight"
|
||||
@toggle-expanded="toggleExpanded"
|
||||
@suspendable-state-change="onStatusSuspendStateChange"
|
||||
/>
|
||||
<div
|
||||
v-if="showOtherRepliesButtonBelowStatus && getReplies(status.id).length > 1"
|
||||
|
|
@ -150,7 +139,7 @@
|
|||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<thread-tree
|
||||
<ThreadTree
|
||||
v-for="status in showingTopLevel"
|
||||
:key="status.id"
|
||||
ref="statusComponent"
|
||||
|
|
@ -176,10 +165,8 @@
|
|||
:show-thread-recursively="showThreadRecursively"
|
||||
:total-reply-count="totalReplyCount"
|
||||
:total-reply-depth="totalReplyDepth"
|
||||
:status-content-properties="statusContentProperties"
|
||||
:set-status-content-property="setStatusContentProperty"
|
||||
:toggle-status-content-property="toggleStatusContentProperty"
|
||||
:dive="canDive ? diveIntoStatus : undefined"
|
||||
@suspendable-state-change="onStatusSuspendStateChange"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -187,7 +174,7 @@
|
|||
class="thread-body"
|
||||
>
|
||||
<article>
|
||||
<status
|
||||
<Status
|
||||
v-for="status in conversation"
|
||||
:key="status.id"
|
||||
ref="statusComponent"
|
||||
|
|
@ -206,6 +193,7 @@
|
|||
|
||||
@goto="setHighlight"
|
||||
@toggle-expanded="toggleExpanded"
|
||||
@suspendable-state-change="onStatusSuspendStateChange"
|
||||
/>
|
||||
</article>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@ const Gallery = {
|
|||
return {
|
||||
sizes: {},
|
||||
hidingLong: true,
|
||||
playingMedia: new Set(),
|
||||
}
|
||||
},
|
||||
emits: ['play', 'pause'],
|
||||
components: { Attachment },
|
||||
computed: {
|
||||
rows() {
|
||||
|
|
@ -115,11 +117,21 @@ const Gallery = {
|
|||
return this.attachmentsDimensionalScore > 1
|
||||
}
|
||||
},
|
||||
hasPlayingMedia() {
|
||||
return this.playingMedia.size > 0
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onNaturalSizeLoad({ id, width, height }) {
|
||||
set(this.sizes, id, { width, height })
|
||||
},
|
||||
onMediaStateChange(playing, id) {
|
||||
if (playing) {
|
||||
this.playingMedia.add(id)
|
||||
} else {
|
||||
this.playingMedia.delete(id)
|
||||
}
|
||||
},
|
||||
rowStyle(row) {
|
||||
if (row.audio) {
|
||||
return { 'padding-bottom': '25%' } // fixed reduced height for audio
|
||||
|
|
@ -146,6 +158,15 @@ const Gallery = {
|
|||
useMediaViewerStore().setMedia(this.attachments)
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
hasPlayingMedia(newValue) {
|
||||
if (newValue) {
|
||||
this.$emit('play')
|
||||
} else {
|
||||
this.$emit('pause')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Gallery
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
:style="itemStyle(attachment.id, row.items)"
|
||||
@set-media="onMedia"
|
||||
@natural-size-load="onNaturalSizeLoad"
|
||||
@play="() => onMediaStateChange(true, attachment.id)"
|
||||
@pause="() => onMediaStateChange(false, attachment.id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ const PostStatusForm = {
|
|||
'resize',
|
||||
'mediaplay',
|
||||
'mediapause',
|
||||
'can-close',
|
||||
'close-accepted',
|
||||
'update',
|
||||
],
|
||||
components: {
|
||||
|
|
@ -963,19 +963,19 @@ const PostStatusForm = {
|
|||
},
|
||||
requestClose() {
|
||||
if (!this.saveable) {
|
||||
this.$emit('can-close')
|
||||
this.$emit('close-accepted')
|
||||
} else {
|
||||
this.$refs.draftCloser.requestClose()
|
||||
}
|
||||
},
|
||||
saveAndCloseDraft() {
|
||||
this.saveDraft().then(() => {
|
||||
this.$emit('can-close')
|
||||
this.$emit('close-accepted')
|
||||
})
|
||||
},
|
||||
discardAndCloseDraft() {
|
||||
this.abandonDraft().then(() => {
|
||||
this.$emit('can-close')
|
||||
this.$emit('close-accepted')
|
||||
})
|
||||
},
|
||||
addBeforeUnloadListener() {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@
|
|||
:compact="false"
|
||||
class="search-result"
|
||||
:statusoid="status"
|
||||
:no-heading="false"
|
||||
/>
|
||||
<button
|
||||
v-if="!loading && loaded && lastStatusFetchCount > 0"
|
||||
|
|
|
|||
|
|
@ -155,31 +155,19 @@ const Status = {
|
|||
|
||||
controlledThreadDisplayStatus: String,
|
||||
controlledToggleThreadDisplay: Function,
|
||||
controlledShowingTall: Boolean,
|
||||
controlledToggleShowingTall: Function,
|
||||
controlledExpandingSubject: Boolean,
|
||||
controlledToggleExpandingSubject: Function,
|
||||
controlledShowingLongSubject: Boolean,
|
||||
controlledToggleShowingLongSubject: Function,
|
||||
controlledReplying: Boolean,
|
||||
controlledToggleReplying: Function,
|
||||
controlledMediaPlaying: Boolean,
|
||||
controlledSetMediaPlaying: Function,
|
||||
},
|
||||
emits: ['goto', 'toggleExpanded'],
|
||||
emits: ['goto', 'toggleExpanded', 'suspendableStateChange'],
|
||||
data() {
|
||||
return {
|
||||
uncontrolledReplying: false,
|
||||
replying: false,
|
||||
unmuted: false,
|
||||
userExpanded: false,
|
||||
uncontrolledMediaPlaying: [],
|
||||
suspendable: true,
|
||||
mediaPlaying: new Set(),
|
||||
error: null,
|
||||
headTailLinks: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...controlledOrUncontrolledGetters(['replying', 'mediaPlaying']),
|
||||
showReasonMutedThread() {
|
||||
return (
|
||||
(this.status.thread_muted ||
|
||||
|
|
@ -489,7 +477,7 @@ const Status = {
|
|||
return useMergedConfigStore().mergedConfig
|
||||
},
|
||||
isSuspendable() {
|
||||
return !this.replying && this.mediaPlaying.length === 0
|
||||
return !this.replying && this.mediaPlaying.size === 0
|
||||
},
|
||||
inThreadForest() {
|
||||
return !!this.controlledThreadDisplayStatus
|
||||
|
|
@ -566,15 +554,17 @@ const Status = {
|
|||
clearError() {
|
||||
this.error = undefined
|
||||
},
|
||||
toggleReplying() {
|
||||
toggleReplyForm() {
|
||||
if (this.replying) {
|
||||
// This emits 'close-accepted' if successful
|
||||
// which in turn callse closeReply()
|
||||
this.$refs.postStatusForm.requestClose()
|
||||
} else {
|
||||
this.doToggleReplying()
|
||||
this.replying = true
|
||||
}
|
||||
},
|
||||
doToggleReplying() {
|
||||
controlledOrUncontrolledToggle(this, 'replying')
|
||||
closeReplyForm() {
|
||||
this.replying = false
|
||||
},
|
||||
gotoOriginal(id) {
|
||||
if (this.inConversation) {
|
||||
|
|
@ -598,18 +588,10 @@ const Status = {
|
|||
)
|
||||
},
|
||||
addMediaPlaying(id) {
|
||||
controlledOrUncontrolledSet(
|
||||
this,
|
||||
'mediaPlaying',
|
||||
this.mediaPlaying.concat(id),
|
||||
)
|
||||
this.mediaPlaying.add(id)
|
||||
},
|
||||
removeMediaPlaying(id) {
|
||||
controlledOrUncontrolledSet(
|
||||
this,
|
||||
'mediaPlaying',
|
||||
this.mediaPlaying.filter((mediaId) => mediaId !== id),
|
||||
)
|
||||
this.mediaPlaying.delete(id)
|
||||
},
|
||||
setHeadTailLinks(headTailLinks) {
|
||||
this.headTailLinks = headTailLinks
|
||||
|
|
@ -659,8 +641,8 @@ const Status = {
|
|||
this.$store.dispatch('fetchFavs', this.status.id)
|
||||
}
|
||||
},
|
||||
isSuspendable: function (val) {
|
||||
this.suspendable = val
|
||||
isSuspendable: function (suspend) {
|
||||
this.$emit('suspendableStateChange', { id: this.statusoid.id, suspend })
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -409,15 +409,9 @@
|
|||
<StatusContent
|
||||
ref="content"
|
||||
:status="status"
|
||||
:no-heading="noHeading"
|
||||
:highlight="highlight"
|
||||
:focused="isFocused"
|
||||
:controlled-showing-tall="controlledShowingTall"
|
||||
:controlled-expanding-subject="controlledExpandingSubject"
|
||||
:controlled-showing-long-subject="controlledShowingLongSubject"
|
||||
:controlled-toggle-showing-tall="controlledToggleShowingTall"
|
||||
:controlled-toggle-expanding-subject="controlledToggleExpandingSubject"
|
||||
:controlled-toggle-showing-long-subject="controlledToggleShowingLongSubject"
|
||||
:in-conversation="inConversation"
|
||||
@mediaplay="addMediaPlaying($event)"
|
||||
@mediapause="removeMediaPlaying($event)"
|
||||
@parse-ready="setHeadTailLinks"
|
||||
|
|
@ -521,7 +515,7 @@
|
|||
v-if="!noHeading && !isPreview"
|
||||
:status="status"
|
||||
:replying="replying"
|
||||
@toggle-replying="toggleReplying"
|
||||
@toggle-replying="toggleReplyForm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -555,9 +549,9 @@
|
|||
:replied-user="status.user"
|
||||
:copy-message-scope="status.visibility"
|
||||
:subject="replySubject"
|
||||
@posted="doToggleReplying"
|
||||
@draft-done="doToggleReplying"
|
||||
@can-close="doToggleReplying"
|
||||
@posted="closeReplyForm"
|
||||
@draft-done="closeReplyForm"
|
||||
@close-accepted="closeReplyForm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -15,32 +15,41 @@ library.add(faFile, faMusic, faImage, faLink, faPollH)
|
|||
|
||||
const StatusBody = {
|
||||
name: 'StatusBody',
|
||||
props: [
|
||||
'compact',
|
||||
'collapse', // replaces newlines with spaces
|
||||
'status',
|
||||
'focused',
|
||||
'noHeading',
|
||||
'fullContent',
|
||||
'singleLine',
|
||||
'showingTall',
|
||||
'expandingSubject',
|
||||
'showingLongSubject',
|
||||
'toggleShowingTall',
|
||||
'toggleExpandingSubject',
|
||||
'toggleShowingLongSubject',
|
||||
],
|
||||
props: {
|
||||
status: {
|
||||
// Main thing
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
compact: {
|
||||
// Resizes emoji and minimizes vertical space used
|
||||
// Primarily used for showing status in react notifications
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
collapse: {
|
||||
// replaces newlines with spaces
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
singleLine: {
|
||||
// Show entire thing (subject and content) in a single line
|
||||
// Primarily used in chats
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
postLength: this.status.text.length,
|
||||
parseReadyDone: false,
|
||||
showingTall: false,
|
||||
showingLongSubject: false,
|
||||
expandingSubject: null,
|
||||
}
|
||||
},
|
||||
emits: ['parseReady'],
|
||||
computed: {
|
||||
localCollapseSubjectDefault() {
|
||||
return this.mergedConfig.collapseMessageWithSubject
|
||||
},
|
||||
allowNonSquareEmoji() {
|
||||
return this.mergedConfig.nonSquareEmoji
|
||||
},
|
||||
|
|
@ -51,32 +60,31 @@ const StatusBody = {
|
|||
//
|
||||
// Using max-height + overflow: auto for status components resulted in false positives
|
||||
// very often with japanese characters, and it was very annoying.
|
||||
tallStatus() {
|
||||
hasLongSubject() {
|
||||
return this.status.summary.length > 240
|
||||
},
|
||||
hasSubject() {
|
||||
return !!this.status.summary
|
||||
},
|
||||
// When a status has a subject and is also tall, we should only have one show more/less
|
||||
// button. If the default is to collapse statuses with subjects, we just treat it like
|
||||
// a status with a subject; otherwise, we just treat it like a tall status.
|
||||
mightHideBecauseSubject() {
|
||||
return this.hasSubject && this.mergedConfig.collapseMessageWithSubject
|
||||
},
|
||||
mightHideBecauseTall() {
|
||||
if (this.singleLine || this.compact) return false
|
||||
const lengthScore =
|
||||
this.status.raw_html.split(/<p|<br/).length + this.postLength / 80
|
||||
return lengthScore > 20
|
||||
},
|
||||
longSubject() {
|
||||
return this.status.summary.length > 240
|
||||
},
|
||||
// When a status has a subject and is also tall, we should only have one show more/less button. If the default is to collapse statuses with subjects, we just treat it like a status with a subject; otherwise, we just treat it like a tall status.
|
||||
mightHideBecauseSubject() {
|
||||
return !!this.status.summary && this.localCollapseSubjectDefault
|
||||
},
|
||||
mightHideBecauseTall() {
|
||||
return (
|
||||
this.tallStatus &&
|
||||
!(this.status.summary && this.localCollapseSubjectDefault)
|
||||
)
|
||||
},
|
||||
hideSubjectStatus() {
|
||||
return this.mightHideBecauseSubject && !this.expandingSubject
|
||||
},
|
||||
hideTallStatus() {
|
||||
return this.mightHideBecauseTall && !this.showingTall
|
||||
},
|
||||
shouldShowToggle() {
|
||||
shouldShowExpandToggle() {
|
||||
return this.mightHideBecauseSubject || this.mightHideBecauseTall
|
||||
},
|
||||
toggleButtonClasses() {
|
||||
|
|
@ -97,6 +105,11 @@ const StatusBody = {
|
|||
: this.$t('general.show_more')
|
||||
}
|
||||
},
|
||||
shouldHide() {
|
||||
return (
|
||||
!this.showingMore && this.mightHideBecauseSubject && this.hasSubject
|
||||
)
|
||||
},
|
||||
showingMore() {
|
||||
return (
|
||||
(this.mightHideBecauseTall && this.showingTall) ||
|
||||
|
|
@ -147,9 +160,9 @@ const StatusBody = {
|
|||
},
|
||||
toggleShowMore() {
|
||||
if (this.mightHideBecauseTall) {
|
||||
this.toggleShowingTall()
|
||||
this.showingTall = !this.showingTall
|
||||
} else if (this.mightHideBecauseSubject) {
|
||||
this.toggleExpandingSubject()
|
||||
this.expandingSubject = !this.expandingSubject
|
||||
}
|
||||
},
|
||||
generateTagLink(tag) {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
}
|
||||
|
||||
.text-wrapper {
|
||||
position: relative;
|
||||
text-overflow: ellipsis;
|
||||
overflow-wrap: break-word;
|
||||
overflow: hidden;
|
||||
|
|
@ -60,10 +61,12 @@
|
|||
flex-flow: column nowrap;
|
||||
|
||||
&.-tall-status {
|
||||
position: relative;
|
||||
height: 16em;
|
||||
z-index: 1;
|
||||
|
||||
&:not(.-hidden) {
|
||||
height: 16em;
|
||||
}
|
||||
|
||||
.media-body {
|
||||
min-height: 0;
|
||||
mask:
|
||||
|
|
@ -98,6 +101,7 @@
|
|||
text-wrap: pretty;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin: 0.1em 0;
|
||||
}
|
||||
|
||||
.status-unhider {
|
||||
|
|
@ -107,17 +111,17 @@
|
|||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
.tall-status-hider {
|
||||
position: absolute;
|
||||
height: 5em;
|
||||
margin-top: 10em;
|
||||
line-height: 8em;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.tall-subject-hider {
|
||||
// position: absolute;
|
||||
padding-bottom: 0.5em;
|
||||
|
||||
&:not(.cw-status-hider) {
|
||||
position: absolute;
|
||||
margin-top: 10em;
|
||||
height: 5em;
|
||||
line-height: 8em;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
& .status-unhider,
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
>
|
||||
<div class="body">
|
||||
<div
|
||||
v-if="status.summary_raw_html"
|
||||
v-if="hasSubject"
|
||||
class="summary-wrapper"
|
||||
:class="{ '-tall': (longSubject && !showingLongSubject) }"
|
||||
:class="{ '-tall': (hasLongSubject && !showingLongSubject) }"
|
||||
>
|
||||
<RichContent
|
||||
class="media-body summary"
|
||||
|
|
@ -18,14 +18,14 @@
|
|||
:allow-non-square-emoji="allowNonSquareEmoji"
|
||||
/>
|
||||
<button
|
||||
v-show="longSubject && showingLongSubject"
|
||||
v-show="hasLongSubject && showingLongSubject"
|
||||
class="button-unstyled -link tall-subject-hider"
|
||||
@click.prevent="toggleShowingLongSubject"
|
||||
>
|
||||
{{ $t("status.hide_full_subject") }}
|
||||
</button>
|
||||
<button
|
||||
v-show="longSubject && !showingLongSubject"
|
||||
v-show="hasLongSubject && !showingLongSubject"
|
||||
class="button-unstyled -link tall-subject-hider"
|
||||
@click.prevent="toggleShowingLongSubject"
|
||||
>
|
||||
|
|
@ -34,10 +34,10 @@
|
|||
</div>
|
||||
<div
|
||||
class="text-wrapper"
|
||||
:class="{'-tall-status': hideTallStatus, '-expanded': showingMore}"
|
||||
:class="{'-tall-status': hideTallStatus, '-hidden': shouldHide, '-expanded': showingMore}"
|
||||
>
|
||||
<RichContent
|
||||
v-if="!hideSubjectStatus && !(singleLine && status.summary_raw_html)"
|
||||
v-if="!(singleLine && hasSubject) && !shouldHide"
|
||||
:class="{ '-single-line': singleLine }"
|
||||
class="text media-body"
|
||||
:html="status.raw_html"
|
||||
|
|
@ -52,12 +52,11 @@
|
|||
@parse-ready="onParseReady"
|
||||
/>
|
||||
<div
|
||||
v-show="shouldShowToggle"
|
||||
v-show="shouldShowExpandToggle"
|
||||
:class="toggleButtonClasses"
|
||||
>
|
||||
<button
|
||||
class="btn button-default toggle-button"
|
||||
:class="{ '-focused': focused }"
|
||||
:aria-expanded="showingMore"
|
||||
@click.prevent="toggleShowMore"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -22,69 +22,40 @@ import {
|
|||
|
||||
library.add(faCircleNotch, faFile, faMusic, faImage, faLink, faPollH)
|
||||
|
||||
const camelCase = (name) => name.charAt(0).toUpperCase() + name.slice(1)
|
||||
|
||||
const controlledOrUncontrolledGetters = (list) =>
|
||||
list.reduce((res, name) => {
|
||||
const camelized = camelCase(name)
|
||||
const toggle = `controlledToggle${camelized}`
|
||||
const controlledName = `controlled${camelized}`
|
||||
const uncontrolledName = `uncontrolled${camelized}`
|
||||
res[name] = function () {
|
||||
return (this.$data[toggle] !== undefined ||
|
||||
this.$props[toggle] !== undefined) &&
|
||||
this[toggle]
|
||||
? this[controlledName]
|
||||
: this[uncontrolledName]
|
||||
}
|
||||
return res
|
||||
}, {})
|
||||
|
||||
const controlledOrUncontrolledToggle = (obj, name) => {
|
||||
const camelized = camelCase(name)
|
||||
const toggle = `controlledToggle${camelized}`
|
||||
const uncontrolledName = `uncontrolled${camelized}`
|
||||
if (obj[toggle]) {
|
||||
obj[toggle]()
|
||||
} else {
|
||||
obj[uncontrolledName] = !obj[uncontrolledName]
|
||||
}
|
||||
}
|
||||
|
||||
const StatusContent = {
|
||||
name: 'StatusContent',
|
||||
props: [
|
||||
'status',
|
||||
'compact',
|
||||
'collapse',
|
||||
'focused',
|
||||
'noHeading',
|
||||
'fullContent',
|
||||
'singleLine',
|
||||
'controlledShowingTall',
|
||||
'controlledExpandingSubject',
|
||||
'controlledToggleShowingTall',
|
||||
'controlledToggleExpandingSubject',
|
||||
'controlledShowingLongSubject',
|
||||
'controlledToggleShowingLongSubject',
|
||||
],
|
||||
emits: ['parseReady', 'mediaplay', 'mediapause'],
|
||||
data() {
|
||||
return {
|
||||
uncontrolledShowingTall:
|
||||
this.fullContent || (this.inConversation && this.focused),
|
||||
uncontrolledShowingLongSubject: false,
|
||||
// not as computed because it sets the initial state which will be changed later
|
||||
uncontrolledExpandingSubject:
|
||||
!useMergedConfigStore().mergedConfig.collapseMessageWithSubject,
|
||||
}
|
||||
props: {
|
||||
status: {
|
||||
// Main thing
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
compact: {
|
||||
// Resizes emoji and minimizes vertical space used
|
||||
// Primarily used for showing status in react notifications
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
collapse: {
|
||||
// replaces newlines with spaces
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
singleLine: {
|
||||
// Show entire thing (subject and content) in a single line
|
||||
// Primarily used in chats
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
inConversation: {
|
||||
// Whether status content is being shown in an (open) conversation
|
||||
// Used to control whether to display attachments or not
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['parseReady', 'mediaplay', 'mediapause'],
|
||||
computed: {
|
||||
...controlledOrUncontrolledGetters([
|
||||
'showingTall',
|
||||
'expandingSubject',
|
||||
'showingLongSubject',
|
||||
]),
|
||||
statusCard() {
|
||||
if (!this.status.card) return null
|
||||
return this.status.card.url === this.status.quote_url
|
||||
|
|
@ -93,22 +64,20 @@ const StatusContent = {
|
|||
},
|
||||
hideAttachments() {
|
||||
return (
|
||||
(this.mergedConfig.hideAttachments && !this.inConversation) ||
|
||||
(this.mergedConfig.hideAttachmentsInConv && this.inConversation)
|
||||
!this.fullContent &&
|
||||
((this.mergedConfig.hideAttachments && !this.inConversation) ||
|
||||
(this.mergedConfig.hideAttachmentsInConv && this.inConversation))
|
||||
)
|
||||
},
|
||||
nsfwClickthrough() {
|
||||
if (!this.status.nsfw) {
|
||||
return false
|
||||
}
|
||||
if (this.status.summary && this.localCollapseSubjectDefault) {
|
||||
if (this.status.summary && this.mergedConfig.collapseMessageWithSubject) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
localCollapseSubjectDefault() {
|
||||
return this.mergedConfig.collapseMessageWithSubject
|
||||
},
|
||||
attachmentSize() {
|
||||
if (this.compact) {
|
||||
return 'small'
|
||||
|
|
@ -137,15 +106,6 @@ const StatusContent = {
|
|||
StatusBody,
|
||||
},
|
||||
methods: {
|
||||
toggleShowingTall() {
|
||||
controlledOrUncontrolledToggle(this, 'showingTall')
|
||||
},
|
||||
toggleExpandingSubject() {
|
||||
controlledOrUncontrolledToggle(this, 'expandingSubject')
|
||||
},
|
||||
toggleShowingLongSubject() {
|
||||
controlledOrUncontrolledToggle(this, 'showingLongSubject')
|
||||
},
|
||||
setMedia() {
|
||||
const attachments =
|
||||
this.attachmentSize === 'hide'
|
||||
|
|
|
|||
|
|
@ -8,12 +8,6 @@
|
|||
:status="status"
|
||||
:compact="compact"
|
||||
:single-line="singleLine"
|
||||
:showing-tall="showingTall"
|
||||
:expanding-subject="expandingSubject"
|
||||
:showing-long-subject="showingLongSubject"
|
||||
:toggle-showing-tall="toggleShowingTall"
|
||||
:toggle-expanding-subject="toggleExpandingSubject"
|
||||
:toggle-showing-long-subject="toggleShowingLongSubject"
|
||||
:collapse="collapse"
|
||||
@parse-ready="$emit('parseReady', $event)"
|
||||
>
|
||||
|
|
@ -42,12 +36,12 @@
|
|||
:attachments="status.attachments"
|
||||
:limit="compact ? 1 : 0"
|
||||
:size="attachmentSize"
|
||||
@play="$emit('mediaplay', attachment.id)"
|
||||
@pause="$emit('mediapause', attachment.id)"
|
||||
@play="$emit('mediaplay')"
|
||||
@pause="$emit('mediapause')"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="statusCard && !noHeading && !compact"
|
||||
v-if="statusCard && !compact"
|
||||
class="link-preview media-body"
|
||||
>
|
||||
<link-preview
|
||||
|
|
|
|||
|
|
@ -32,11 +32,9 @@ const ThreadTree = {
|
|||
showThreadRecursively: Function,
|
||||
totalReplyCount: Object,
|
||||
totalReplyDepth: Object,
|
||||
statusContentProperties: Object,
|
||||
setStatusContentProperty: Function,
|
||||
toggleStatusContentProperty: Function,
|
||||
dive: Function,
|
||||
},
|
||||
emits: ['suspendableStateChange'],
|
||||
computed: {
|
||||
suspendable() {
|
||||
const selfSuspendable = this.$refs.statusComponent
|
||||
|
|
@ -69,9 +67,6 @@ const ThreadTree = {
|
|||
threadShowing() {
|
||||
return this.threadDisplayStatus[this.status.id] === 'showing'
|
||||
},
|
||||
currentProp() {
|
||||
return this.statusContentProperties[this.status.id]
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
statusById(id) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<article class="thread-tree">
|
||||
<status
|
||||
<Status
|
||||
:key="status.id"
|
||||
ref="statusComponent"
|
||||
:inline-expanded="collapsable && isExpanded"
|
||||
|
|
@ -19,26 +19,17 @@
|
|||
:controlled-thread-display-status="threadDisplayStatus[status.id]"
|
||||
:controlled-toggle-thread-display="() => toggleThreadDisplay(status.id)"
|
||||
|
||||
:controlled-showing-tall="currentProp.showingTall"
|
||||
:controlled-expanding-subject="currentProp.expandingSubject"
|
||||
:controlled-showing-long-subject="currentProp.showingLongSubject"
|
||||
:controlled-replying="currentProp.replying"
|
||||
:controlled-media-playing="currentProp.mediaPlaying"
|
||||
:controlled-toggle-showing-tall="() => toggleCurrentProp('showingTall')"
|
||||
:controlled-toggle-expanding-subject="() => toggleCurrentProp('expandingSubject')"
|
||||
:controlled-toggle-showing-long-subject="() => toggleCurrentProp('showingLongSubject')"
|
||||
:controlled-toggle-replying="() => toggleCurrentProp('replying')"
|
||||
:controlled-set-media-playing="(newVal) => setCurrentProp('mediaPlaying', newVal)"
|
||||
:dive="dive ? () => dive(status.id) : undefined"
|
||||
|
||||
@goto="setHighlight"
|
||||
@toggle-expanded="toggleExpanded"
|
||||
@suspendable-state-change="e => $emit('suspendableStateChange', e)"
|
||||
/>
|
||||
<div
|
||||
v-if="currentReplies.length && threadShowing"
|
||||
class="thread-tree-replies"
|
||||
>
|
||||
<thread-tree
|
||||
<ThreadTree
|
||||
v-for="replyStatus in currentReplies"
|
||||
:key="replyStatus.id"
|
||||
ref="childComponent"
|
||||
|
|
@ -64,10 +55,9 @@
|
|||
:show-thread-recursively="showThreadRecursively"
|
||||
:total-reply-count="totalReplyCount"
|
||||
:total-reply-depth="totalReplyDepth"
|
||||
:status-content-properties="statusContentProperties"
|
||||
:set-status-content-property="setStatusContentProperty"
|
||||
:toggle-status-content-property="toggleStatusContentProperty"
|
||||
:dive="dive"
|
||||
|
||||
@suspendable-state-change="e => $emit('suspendableStateChange', e)"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue