Merge branch 'virtual-scrolling-2.0' into shigusegubu-themes3
This commit is contained in:
commit
24ac79a41d
23 changed files with 760 additions and 891 deletions
|
|
@ -67,8 +67,6 @@
|
||||||
"@vue/devtools-api": "8.2.0",
|
"@vue/devtools-api": "8.2.0",
|
||||||
"@vue/test-utils": "2.5.0",
|
"@vue/test-utils": "2.5.0",
|
||||||
"autoprefixer": "10.5.4",
|
"autoprefixer": "10.5.4",
|
||||||
"chalk": "6.0.0",
|
|
||||||
"cross-spawn": "7.0.6",
|
|
||||||
"iso-639-1": "3.1.6",
|
"iso-639-1": "3.1.6",
|
||||||
"playwright": "1.61.0",
|
"playwright": "1.61.0",
|
||||||
"postcss": "8.5.28",
|
"postcss": "8.5.28",
|
||||||
|
|
|
||||||
|
|
@ -83,8 +83,9 @@ export default () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'conversation',
|
name: 'conversation',
|
||||||
path: '/notice/:id',
|
path: '/notice/:statusId',
|
||||||
component: ConversationPage,
|
component: ConversationPage,
|
||||||
|
props: true,
|
||||||
meta: { dontScroll: true },
|
meta: { dontScroll: true },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -6,6 +6,15 @@
|
||||||
backdrop-filter: var(--__panel-backdrop-filter);
|
backdrop-filter: var(--__panel-backdrop-filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.conversation-status:not(.-last) {
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation-status:not(.-last)
|
||||||
|
.conversation-status:not(.-first) {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.conversation-dive-to-top-level-box {
|
.conversation-dive-to-top-level-box {
|
||||||
padding: var(--status-margin);
|
padding: var(--status-margin);
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
|
|
@ -49,26 +58,12 @@
|
||||||
padding: var(--status-margin);
|
padding: var(--status-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
.conversation-status {
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thread-ancestor-has-other-replies .conversation-status,
|
|
||||||
&:last-child:not(.-expanded) .conversation-status,
|
|
||||||
&.-expanded .conversation-status:last-child,
|
|
||||||
.thread-ancestor:last-child .conversation-status,
|
|
||||||
.thread-ancestor:last-child .thread-ancestor-dive-box,
|
|
||||||
&.-expanded .thread-tree .conversation-status {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thread-ancestors + .thread-tree > .conversation-status {
|
.thread-ancestors + .thread-tree > .conversation-status {
|
||||||
border-top: 1px solid var(--border);
|
border-top: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* expanded conversation in timeline */
|
/* expanded conversation in timeline */
|
||||||
&.status-fadein.-expanded .thread-body {
|
&.-expanded .thread-body {
|
||||||
border-left: 4px solid var(--cRed);
|
border-left: 4px solid var(--cRed);
|
||||||
border-radius: var(--roundness);
|
border-radius: var(--roundness);
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
|
|
@ -76,7 +71,7 @@
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.-expanded.status-fadein {
|
&.-expanded:not(.-page) {
|
||||||
--___margin: calc(var(--status-margin) / 2);
|
--___margin: calc(var(--status-margin) / 2);
|
||||||
|
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="!hide"
|
ref="root"
|
||||||
class="Conversation"
|
class="Conversation"
|
||||||
:class="{ '-expanded' : isExpanded, 'panel' : isExpanded }"
|
:class="{ '-expanded' : isExpanded, '-page': isPage, 'panel' : isExpanded }"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="isExpanded"
|
v-if="isExpanded"
|
||||||
|
|
@ -19,19 +19,19 @@
|
||||||
</template>
|
</template>
|
||||||
</h1>
|
</h1>
|
||||||
<button
|
<button
|
||||||
v-if="collapsable"
|
v-if="!isPage"
|
||||||
class="button-unstyled -link"
|
class="button-unstyled -link"
|
||||||
@click.prevent="toggleExpanded"
|
@click.prevent="toggleExpanded"
|
||||||
>
|
>
|
||||||
{{ $t('timeline.collapse') }}
|
{{ $t('timeline.collapse') }}
|
||||||
</button>
|
</button>
|
||||||
<QuickFilterSettings
|
<QuickFilterSettings
|
||||||
v-if="!collapsable && mobileLayout"
|
v-if="isPage && mobileLayout"
|
||||||
:conversation="true"
|
:conversation="true"
|
||||||
class="rightside-button"
|
class="rightside-button"
|
||||||
/>
|
/>
|
||||||
<QuickViewSettings
|
<QuickViewSettings
|
||||||
v-if="!collapsable"
|
v-if="isPage"
|
||||||
:conversation="true"
|
:conversation="true"
|
||||||
class="rightside-button"
|
class="rightside-button"
|
||||||
/>
|
/>
|
||||||
|
|
@ -81,7 +81,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template #text>
|
<template #text>
|
||||||
<span>
|
<span>
|
||||||
{{ $t('status.show_all_conversation', { numStatus: otherTopLevelCount }, otherTopLevelCount) }}
|
{{ $t('status.show_all_conversation', { numStatus: topLevel.length - 1 }, topLevel.length - 1) }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
|
|
@ -91,36 +91,27 @@
|
||||||
class="thread-ancestors"
|
class="thread-ancestors"
|
||||||
>
|
>
|
||||||
<article
|
<article
|
||||||
v-for="status in ancestorsOf(diveRoot)"
|
v-for="status in currentAncestors"
|
||||||
:key="status.id"
|
|
||||||
class="thread-ancestor"
|
class="thread-ancestor"
|
||||||
:class="{'thread-ancestor-has-other-replies': getReplies(status.id).length > 1, '-faded': shouldFadeAncestors}"
|
:class="{'thread-ancestor-has-other-replies': statusReplies.size > 1, '-faded': shouldFadeAncestors}"
|
||||||
>
|
>
|
||||||
<Status
|
<Status
|
||||||
ref="statusComponent"
|
class="conversation-status panel-body"
|
||||||
class="conversation-status status-fadein panel-body"
|
:class="getStatusClasses(status)"
|
||||||
|
|
||||||
:status-id="status.id"
|
:status-id="status.id"
|
||||||
:replies="getReplies(status.id)"
|
:replies="statusReplies"
|
||||||
|
|
||||||
:expandable="!isExpanded"
|
:focused="focused === status.id"
|
||||||
:focused="maybeFocused === status.id"
|
|
||||||
:inline-expanded="collapsable && isExpanded"
|
|
||||||
:show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
|
|
||||||
:in-profile="inProfile"
|
|
||||||
:in-conversation="isExpanded"
|
|
||||||
:profile-user-id="profileUserId"
|
|
||||||
:simple-tree="treeViewIsSimple"
|
|
||||||
:show-other-replies-as-button="showOtherRepliesButtonInsideStatus"
|
|
||||||
can-dive
|
can-dive
|
||||||
|
|
||||||
@goto="setFocused"
|
@goto="setFocused"
|
||||||
@dive="() => diveIntoStatus(status.id)"
|
@dive="diveIntoStatus(status.id)"
|
||||||
@suspendable-state-change="onStatusSuspendStateChange"
|
@suspendable-state-change="onStatusSuspendStateChange"
|
||||||
@height-change="updateVirtualHeight"
|
@height-change="updateVirtualHeight"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="showOtherRepliesButtonBelowStatus && getReplies(status.id).length > 1"
|
v-if="shouldShowOtherRepliesButton && statusReplies.size > 1"
|
||||||
class="thread-ancestor-dive-box"
|
class="thread-ancestor-dive-box"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
@ -140,7 +131,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template #text>
|
<template #text>
|
||||||
<span>
|
<span>
|
||||||
{{ $t('status.ancestor_follow', { numReplies: getReplies(status.id, getReplies(status.id).length - 1).length - 1 }) }}
|
{{ $t('status.ancestor_follow', { numReplies: statusReplies.size - 1 }) }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
|
|
@ -149,32 +140,13 @@
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
<ThreadTree
|
<ThreadTree
|
||||||
v-for="status in showingTopLevel"
|
|
||||||
:key="status.id"
|
|
||||||
ref="statusComponent"
|
|
||||||
:depth="0"
|
|
||||||
|
|
||||||
:status-id="status.id"
|
:status-id="status.id"
|
||||||
:in-profile="inProfile"
|
:depth="0"
|
||||||
:conversation="conversation"
|
|
||||||
:collapsable="collapsable"
|
|
||||||
:is-expanded="isExpanded"
|
|
||||||
:pinned-status-ids-object="pinnedStatusIdsObject"
|
|
||||||
:profile-user-id="profileUserId"
|
|
||||||
|
|
||||||
:get-replies="getReplies"
|
|
||||||
:focused="maybeFocused"
|
|
||||||
:toggle-expanded="toggleExpanded"
|
|
||||||
|
|
||||||
:simple="treeViewIsSimple"
|
|
||||||
:thread-display-status="threadDisplayStatus"
|
|
||||||
:show-thread-recursively="showThreadRecursively"
|
|
||||||
:total-reply-count="totalReplyCount"
|
|
||||||
:total-reply-depth="totalReplyDepth"
|
|
||||||
:can-dive="canDive"
|
|
||||||
|
|
||||||
@goto="setFocused"
|
@goto="setFocused"
|
||||||
@dive="diveIntoStatus"
|
@dive="diveIntoStatus"
|
||||||
|
@toggle-expanded="toggleExpanded"
|
||||||
|
@show-thread-recursively="showThreadRecursively"
|
||||||
@suspendable-state-change="onStatusSuspendStateChange"
|
@suspendable-state-change="onStatusSuspendStateChange"
|
||||||
@height-change="updateVirtualHeight"
|
@height-change="updateVirtualHeight"
|
||||||
/>
|
/>
|
||||||
|
|
@ -183,22 +155,24 @@
|
||||||
v-else-if="isLinearView"
|
v-else-if="isLinearView"
|
||||||
class="thread-body"
|
class="thread-body"
|
||||||
>
|
>
|
||||||
<article>
|
<article
|
||||||
|
v-for="element in heightChartLinear"
|
||||||
|
class="panel-body"
|
||||||
|
:key="element.id ?? element.ids"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="element.type === 'spacer'"
|
||||||
|
class="virtual-spacer"
|
||||||
|
:style="{ height: element.height + 'px' }"
|
||||||
|
/>
|
||||||
<Status
|
<Status
|
||||||
v-for="status in conversation"
|
v-if="element.type === 'status'"
|
||||||
:key="status.id"
|
class="conversation-status"
|
||||||
ref="statusComponent"
|
:class="getStatusClasses(status)"
|
||||||
class="conversation-status status-fadein panel-body"
|
:status-id="element.status.id"
|
||||||
:status-id="status.id"
|
|
||||||
:replies="getReplies(status.id)"
|
:replies="getReplies(status.id)"
|
||||||
|
|
||||||
:expandable="!isExpanded"
|
:focused="focused === element.id || focused === element.status.retweeted_status?.id"
|
||||||
:focused="maybeFocused === status.id || maybeFocused === status.retweeted_status?.id"
|
|
||||||
:inline-expanded="collapsable && isExpanded"
|
|
||||||
:show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
|
|
||||||
:in-profile="inProfile"
|
|
||||||
:in-conversation="isExpanded"
|
|
||||||
:profile-user-id="profileUserId"
|
|
||||||
|
|
||||||
@goto="setFocused"
|
@goto="setFocused"
|
||||||
@toggle-expanded="toggleExpanded"
|
@toggle-expanded="toggleExpanded"
|
||||||
|
|
@ -209,11 +183,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="Conversation -hidden"
|
|
||||||
:style="hiddenStyle"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./conversation.js"></script>
|
<script src="./conversation.js"></script>
|
||||||
|
|
|
||||||
21
src/components/conversation/useScrollPosition.js
Normal file
21
src/components/conversation/useScrollPosition.js
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
|
export function useScrollPosition() {
|
||||||
|
const x = ref(0)
|
||||||
|
const y = ref(0)
|
||||||
|
|
||||||
|
const update = (e) => {
|
||||||
|
x.value = window.scrollX
|
||||||
|
y.value = window.scrollY
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('scroll', update)
|
||||||
|
update()
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('scroll', update)
|
||||||
|
})
|
||||||
|
|
||||||
|
return { x, y }
|
||||||
|
}
|
||||||
18
src/components/conversation/useWindowSize.js
Normal file
18
src/components/conversation/useWindowSize.js
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
|
||||||
|
export function useWindowSize() {
|
||||||
|
const height = ref(0)
|
||||||
|
const width = ref(0)
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
height.value = window.innerHeight
|
||||||
|
width.value = window.innerWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => window.addEventListener('resize', update))
|
||||||
|
onUnmounted(() => window.removeEventListener('resize', update))
|
||||||
|
|
||||||
|
update()
|
||||||
|
|
||||||
|
return { height, width }
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<template #statusLink>
|
<template #statusLink>
|
||||||
<router-link
|
<router-link
|
||||||
class="faint-link"
|
class="faint-link"
|
||||||
:to="{ name: 'conversation', params: { id: draft.refId } }"
|
:to="{ name: 'conversation', params: { statusId: draft.refId } }"
|
||||||
>
|
>
|
||||||
{{ refStatus ? refStatus.external_url : $t('drafts.unavailable') }}
|
{{ refStatus ? refStatus.external_url : $t('drafts.unavailable') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
v-if="notification.status"
|
v-if="notification.status"
|
||||||
:to="{ name: 'conversation', params: { id: notification.status.id } }"
|
:to="{ name: 'conversation', params: { statusId: notification.status.id } }"
|
||||||
class="timeago-link faint"
|
class="timeago-link faint"
|
||||||
>
|
>
|
||||||
<Timeago
|
<Timeago
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
<Status
|
<Status
|
||||||
v-if="shouldDisplayQuote"
|
v-if="shouldDisplayQuote"
|
||||||
:statusoid="quotedStatus"
|
:statusoid="quotedStatus"
|
||||||
:in-quote="true"
|
in-quote
|
||||||
/>
|
/>
|
||||||
</article>
|
</article>
|
||||||
<p
|
<p
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
<router-link
|
<router-link
|
||||||
v-for="status in report.statuses"
|
v-for="status in report.statuses"
|
||||||
:key="status.id"
|
:key="status.id"
|
||||||
:to="{ name: 'conversation', params: { id: status.id } }"
|
:to="{ name: 'conversation', params: { statusId: status.id } }"
|
||||||
class="reported-status"
|
class="reported-status"
|
||||||
>
|
>
|
||||||
<div class="reported-status-heading">
|
<div class="reported-status-heading">
|
||||||
|
|
|
||||||
|
|
@ -96,25 +96,17 @@ const Status = {
|
||||||
props: {
|
props: {
|
||||||
statusId: String,
|
statusId: String,
|
||||||
statusoid: Object,
|
statusoid: Object,
|
||||||
replies: Array,
|
replies: Set,
|
||||||
|
|
||||||
expandable: Boolean,
|
|
||||||
focused: Boolean,
|
focused: Boolean,
|
||||||
compact: Boolean,
|
compact: Boolean,
|
||||||
isPreview: Boolean,
|
isPreview: Boolean,
|
||||||
noHeading: Boolean,
|
noHeading: Boolean,
|
||||||
inlineExpanded: Boolean,
|
|
||||||
inProfile: Boolean,
|
|
||||||
inConversation: Boolean,
|
|
||||||
inQuote: Boolean,
|
inQuote: Boolean,
|
||||||
|
|
||||||
profileUserId: String,
|
|
||||||
simpleTree: Boolean,
|
|
||||||
showOtherRepliesAsButton: Boolean,
|
|
||||||
canDive: Boolean,
|
|
||||||
ignoreMute: Boolean,
|
ignoreMute: Boolean,
|
||||||
|
|
||||||
threadDisplayStatus: String,
|
threadDisplayState: String,
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
'goto',
|
'goto',
|
||||||
|
|
@ -123,8 +115,14 @@ const Status = {
|
||||||
'suspendableStateChange',
|
'suspendableStateChange',
|
||||||
'heightChange',
|
'heightChange',
|
||||||
],
|
],
|
||||||
|
inject: {
|
||||||
|
profileUserId: { default: null },
|
||||||
|
isPage: { default: false },
|
||||||
|
isExpanded: { default: false },
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
resizeObserver: new ResizeObserver(this.updateVirtualHeight),
|
||||||
replying: false,
|
replying: false,
|
||||||
unmuted: false,
|
unmuted: false,
|
||||||
mediaPlaying: new Set(),
|
mediaPlaying: new Set(),
|
||||||
|
|
@ -140,6 +138,12 @@ const Status = {
|
||||||
status() {
|
status() {
|
||||||
return this.statusoid ?? useStatusesStore().allStatuses.get(this.statusId)
|
return this.statusoid ?? useStatusesStore().allStatuses.get(this.statusId)
|
||||||
},
|
},
|
||||||
|
inConversation() {
|
||||||
|
return this.isExpanded
|
||||||
|
},
|
||||||
|
inProfile() {
|
||||||
|
return this.profileUserId != null
|
||||||
|
},
|
||||||
// Status repeated
|
// Status repeated
|
||||||
repeatedStatus() {
|
repeatedStatus() {
|
||||||
if (this.status.retweeted_status === undefined) return undefined
|
if (this.status.retweeted_status === undefined) return undefined
|
||||||
|
|
@ -166,6 +170,12 @@ const Status = {
|
||||||
user() {
|
user() {
|
||||||
return useUsersStore().findUser(this.mainStatus.user.id)
|
return useUsersStore().findUser(this.mainStatus.user.id)
|
||||||
},
|
},
|
||||||
|
simpleTree() {
|
||||||
|
return !this.mergedConfig.conversationTreeAdvanced
|
||||||
|
},
|
||||||
|
showOtherRepliesAsButton() {
|
||||||
|
return this.mergedConfig.conversationOtherRepliesButton === 'inside'
|
||||||
|
},
|
||||||
showReasonMutedThread() {
|
showReasonMutedThread() {
|
||||||
return (
|
return (
|
||||||
(this.mainStatus.thread_muted || this.repeatStatus?.thread_muted) &&
|
(this.mainStatus.thread_muted || this.repeatStatus?.thread_muted) &&
|
||||||
|
|
@ -436,10 +446,10 @@ const Status = {
|
||||||
return !this.replying && this.mediaPlaying.size === 0
|
return !this.replying && this.mediaPlaying.size === 0
|
||||||
},
|
},
|
||||||
inThreadForest() {
|
inThreadForest() {
|
||||||
return !!this.threadDisplayStatus
|
return !!this.threadDisplayState
|
||||||
},
|
},
|
||||||
threadShowing() {
|
threadShowing() {
|
||||||
return this.threadDisplayStatus === 'showing'
|
return this.threadDisplayState === 'showing'
|
||||||
},
|
},
|
||||||
visibilityLocalized() {
|
visibilityLocalized() {
|
||||||
return this.$i18n.t('general.scope_in_timeline.' + this.status.visibility)
|
return this.$i18n.t('general.scope_in_timeline.' + this.status.visibility)
|
||||||
|
|
@ -553,47 +563,22 @@ const Status = {
|
||||||
// FIXME
|
// FIXME
|
||||||
this.controlledToggleThreadDisplay()
|
this.controlledToggleThreadDisplay()
|
||||||
},
|
},
|
||||||
scrollIfFocused(focused) {
|
updateVirtualHeight(e) {
|
||||||
if (this.$el.getBoundingClientRect == null) return
|
const [entry] = e
|
||||||
if (focused) {
|
this.$emit('heightChange', {
|
||||||
const rect = this.$el.getBoundingClientRect()
|
id: this.status.id,
|
||||||
if (rect.top < 100) {
|
height: entry.contentRect.height,
|
||||||
// Post is above screen, match its top to screen top
|
element: this.$el,
|
||||||
window.scrollBy(0, rect.top - 100)
|
|
||||||
} else if (rect.height >= window.innerHeight - 50) {
|
|
||||||
// Post we want to see is taller than screen so match its top to screen top
|
|
||||||
window.scrollBy(0, rect.top - 100)
|
|
||||||
} else if (rect.bottom > window.innerHeight - 50) {
|
|
||||||
// Post is below screen, match its bottom to screen bottom
|
|
||||||
window.scrollBy(0, rect.bottom - window.innerHeight + 50)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onTransitionEnd() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$emit('heightChange')
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
this.resizeObserver.observe(this.$el)
|
||||||
|
},
|
||||||
|
unmounted() {
|
||||||
|
this.resizeObserver.disconnect()
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
status: {
|
|
||||||
deep: true,
|
|
||||||
handler() {
|
|
||||||
this.$emit('heightChange')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
unmuted() {
|
|
||||||
this.$emit('heightChange')
|
|
||||||
},
|
|
||||||
error() {
|
|
||||||
this.$emit('heightChange')
|
|
||||||
},
|
|
||||||
replying() {
|
|
||||||
this.$emit('heightChange')
|
|
||||||
},
|
|
||||||
focused: function (id) {
|
|
||||||
this.scrollIfFocused(id)
|
|
||||||
},
|
|
||||||
'mainStatus.repeat_num': function (num) {
|
'mainStatus.repeat_num': function (num) {
|
||||||
// refetch repeats when repeat_num is changed in any way
|
// refetch repeats when repeat_num is changed in any way
|
||||||
if (this.focused && this.repeatedBy.size !== num) {
|
if (this.focused && this.repeatedBy.size !== num) {
|
||||||
|
|
|
||||||
|
|
@ -271,21 +271,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-fadein {
|
|
||||||
animation-duration: 0.4s;
|
|
||||||
animation-name: fadein;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadein {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-actions {
|
.status-actions {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
v-if="!hideStatus"
|
v-if="!hideStatus"
|
||||||
ref="root"
|
ref="root"
|
||||||
class="Status"
|
class="Status"
|
||||||
:class="[{ '-focused': focused }, { '-conversation': inlineExpanded }]"
|
:class="[{ '-focused': focused }, { '-conversation': !isPage && isExpanded }]"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="error"
|
v-if="error"
|
||||||
|
|
@ -180,7 +180,7 @@
|
||||||
</span>
|
</span>
|
||||||
<router-link
|
<router-link
|
||||||
class="timeago faint"
|
class="timeago faint"
|
||||||
:to="{ name: 'conversation', params: { id: status.id } }"
|
:to="{ name: 'conversation', params: { statusId: status.id } }"
|
||||||
>
|
>
|
||||||
<Timeago
|
<Timeago
|
||||||
:time="mainStatus.created_at"
|
:time="mainStatus.created_at"
|
||||||
|
|
@ -199,7 +199,7 @@
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
v-if="expandable && !isPreview"
|
v-if="!isExpanded && !isPreview"
|
||||||
class="button-unstyled"
|
class="button-unstyled"
|
||||||
:title="$t('status.expand')"
|
:title="$t('status.expand')"
|
||||||
@click.prevent="toggleExpanded"
|
@click.prevent="toggleExpanded"
|
||||||
|
|
@ -222,7 +222,7 @@
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="inThreadForest && replies?.length && !simpleTree"
|
v-if="inThreadForest && replies?.size && !simpleTree"
|
||||||
class="button-unstyled"
|
class="button-unstyled"
|
||||||
:title="threadShowing ? $t('status.thread_hide') : $t('status.thread_show')"
|
:title="threadShowing ? $t('status.thread_hide') : $t('status.thread_show')"
|
||||||
:aria-expanded="threadShowing ? 'true' : 'false'"
|
:aria-expanded="threadShowing ? 'true' : 'false'"
|
||||||
|
|
@ -235,7 +235,7 @@
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="canDive && !simpleTree"
|
v-if="isExpanded && !simpleTree"
|
||||||
class="button-unstyled"
|
class="button-unstyled"
|
||||||
:title="$t('status.show_only_conversation_under_this')"
|
:title="$t('status.show_only_conversation_under_this')"
|
||||||
@click.prevent="$emit('dive')"
|
@click.prevent="$emit('dive')"
|
||||||
|
|
@ -423,16 +423,16 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="inConversation && !isPreview && replies?.length"
|
v-if="inConversation && !isPreview && replies?.size"
|
||||||
class="replies"
|
class="replies"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
v-if="showOtherRepliesAsButton && replies.length > 1"
|
v-if="showOtherRepliesAsButton && replies.size > 1"
|
||||||
class="button-unstyled -link"
|
class="button-unstyled -link"
|
||||||
:title="$t('status.ancestor_follow', { numReplies: replies.length - 1 }, replies.length - 1)"
|
:title="$t('status.ancestor_follow', { numReplies: replies.size - 1 }, replies.size - 1)"
|
||||||
@click.prevent="$emit('dive')"
|
@click.prevent="$emit('dive')"
|
||||||
>
|
>
|
||||||
{{ $t('status.replies_list_with_others', { numReplies: replies.length - 1 }, replies.length - 1) }}
|
{{ $t('status.replies_list_with_others', { numReplies: replies.size - 1 }, replies.size - 1) }}
|
||||||
</button>
|
</button>
|
||||||
<span
|
<span
|
||||||
v-else
|
v-else
|
||||||
|
|
@ -441,7 +441,7 @@
|
||||||
{{ $t('status.replies_list') }}
|
{{ $t('status.replies_list') }}
|
||||||
</span>
|
</span>
|
||||||
<StatusPopover
|
<StatusPopover
|
||||||
v-for="reply in replies"
|
v-for="reply in replies.values()"
|
||||||
:key="reply.id"
|
:key="reply.id"
|
||||||
:status-id="reply.id"
|
:status-id="reply.id"
|
||||||
>
|
>
|
||||||
|
|
@ -454,10 +454,7 @@
|
||||||
</StatusPopover>
|
</StatusPopover>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Transition
|
<Transition name="fade">
|
||||||
@after-leave="onTransitionEnd"
|
|
||||||
name="fade"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
v-if="shouldDisplayFavsAndRepeats"
|
v-if="shouldDisplayFavsAndRepeats"
|
||||||
class="favs-repeated-users"
|
class="favs-repeated-users"
|
||||||
|
|
@ -549,7 +546,6 @@
|
||||||
@posted="closeReplyForm"
|
@posted="closeReplyForm"
|
||||||
@draft-done="closeReplyForm"
|
@draft-done="closeReplyForm"
|
||||||
@close-accepted="closeReplyForm"
|
@close-accepted="closeReplyForm"
|
||||||
@resize="$emit('heightChange')"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ export const BUTTONS = [
|
||||||
return chatView
|
return chatView
|
||||||
},
|
},
|
||||||
action({ router, status }) {
|
action({ router, status }) {
|
||||||
router.push({ name: 'conversation', params: { id: status.id } })
|
router.push({ name: 'conversation', params: { statusId: status.id } })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -293,8 +293,10 @@ export const BUTTONS = [
|
||||||
navigator.clipboard.writeText(
|
navigator.clipboard.writeText(
|
||||||
[
|
[
|
||||||
useInstanceStore().server,
|
useInstanceStore().server,
|
||||||
router.resolve({ name: 'conversation', params: { id: status.id } })
|
router.resolve({
|
||||||
.href,
|
name: 'conversation',
|
||||||
|
params: { statusId: status.id },
|
||||||
|
}).href,
|
||||||
].join(''),
|
].join(''),
|
||||||
)
|
)
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
:key="status.id"
|
:key="status.id"
|
||||||
:statusoid="status"
|
:statusoid="status"
|
||||||
:is-preview="true"
|
:is-preview="true"
|
||||||
class="conversation-status status-fadein panel-body"
|
class="conversation-status panel-body"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faAngleDoubleDown,
|
faAngleDoubleDown,
|
||||||
|
|
@ -10,33 +12,85 @@ const ThreadTree = {
|
||||||
components: {},
|
components: {},
|
||||||
name: 'ThreadTree',
|
name: 'ThreadTree',
|
||||||
props: {
|
props: {
|
||||||
depth: Number,
|
|
||||||
statusId: String,
|
statusId: String,
|
||||||
inProfile: Boolean,
|
depth: Number,
|
||||||
conversation: Array,
|
|
||||||
collapsable: Boolean,
|
|
||||||
isExpanded: Boolean,
|
|
||||||
pinnedStatusIdsObject: Object,
|
|
||||||
profileUserId: String,
|
|
||||||
|
|
||||||
focused: String,
|
|
||||||
getReplies: Function,
|
|
||||||
toggleExpanded: Function,
|
|
||||||
|
|
||||||
simple: Boolean,
|
|
||||||
canDive: Boolean,
|
|
||||||
threadDisplayStatus: Object,
|
|
||||||
showThreadRecursively: Function,
|
|
||||||
totalReplyCount: Object,
|
|
||||||
totalReplyDepth: Object,
|
|
||||||
},
|
},
|
||||||
emits: ['suspendableStateChange', 'goto', 'dive', 'heightChange'],
|
emits: [
|
||||||
|
'suspendableStateChange',
|
||||||
|
'goto',
|
||||||
|
'dive',
|
||||||
|
'toggleExpanded',
|
||||||
|
'showThreadRecursively',
|
||||||
|
],
|
||||||
|
inject: [
|
||||||
|
'conversation',
|
||||||
|
'focused',
|
||||||
|
'replies',
|
||||||
|
'threadDisplay',
|
||||||
|
'threadDisplayDefault',
|
||||||
|
'isExpanded',
|
||||||
|
'isPage',
|
||||||
|
],
|
||||||
computed: {
|
computed: {
|
||||||
currentReplies() {
|
currentReplies() {
|
||||||
return this.getReplies(this.statusId).map(({ id }) => id)
|
return [...this.getReplies(this.statusId)].map(({ id }) => id)
|
||||||
|
},
|
||||||
|
simple() {
|
||||||
|
return !useMergedConfigStore().mergedConfig.conversationTreeAdvanced
|
||||||
},
|
},
|
||||||
threadShowing() {
|
threadShowing() {
|
||||||
return this.threadDisplayStatus[this.statusId] === 'showing'
|
const result =
|
||||||
|
this.threadDisplay.get(this.statusId) ??
|
||||||
|
this.threadDisplayDefault.get(this.statusId)
|
||||||
|
return result === 'showing'
|
||||||
|
},
|
||||||
|
canDive() {
|
||||||
|
return this.isExpanded
|
||||||
|
},
|
||||||
|
totalReplyCount() {
|
||||||
|
const sizes = {}
|
||||||
|
const subTreeSizeFor = (id) => {
|
||||||
|
if (sizes[id]) {
|
||||||
|
return sizes[id]
|
||||||
|
}
|
||||||
|
sizes[id] =
|
||||||
|
1 +
|
||||||
|
[...this.getReplies(id)]
|
||||||
|
.map(({ id }) => id)
|
||||||
|
.map((cid) => subTreeSizeFor(cid))
|
||||||
|
.reduce((a, b) => a + b, 0)
|
||||||
|
return sizes[id]
|
||||||
|
}
|
||||||
|
this.conversation.map((k) => k.id).forEach(subTreeSizeFor)
|
||||||
|
return Object.keys(sizes).reduce((res, id) => {
|
||||||
|
res[id] = sizes[id] - 1 // exclude itself
|
||||||
|
return res
|
||||||
|
}, {})
|
||||||
|
},
|
||||||
|
totalReplyDepth() {
|
||||||
|
const depths = {}
|
||||||
|
const subTreeDepthFor = (id) => {
|
||||||
|
if (depths[id]) {
|
||||||
|
return depths[id]
|
||||||
|
}
|
||||||
|
depths[id] =
|
||||||
|
1 +
|
||||||
|
[...this.getReplies(id)]
|
||||||
|
.map(({ id }) => id)
|
||||||
|
.map((cid) => subTreeDepthFor(cid))
|
||||||
|
.reduce((a, b) => (a > b ? a : b), 0)
|
||||||
|
return depths[id]
|
||||||
|
}
|
||||||
|
this.conversation.map((k) => k.id).forEach(subTreeDepthFor)
|
||||||
|
return Object.keys(depths).reduce((res, id) => {
|
||||||
|
res[id] = depths[id] - 1 // exclude itself
|
||||||
|
return res
|
||||||
|
}, {})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getReplies(id) {
|
||||||
|
return this.replies.get(id) ?? new Set()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,27 +2,17 @@
|
||||||
<article class="thread-tree">
|
<article class="thread-tree">
|
||||||
<Status
|
<Status
|
||||||
:key="statusId"
|
:key="statusId"
|
||||||
ref="statusComponent"
|
class="conversation-status conversation-status-treeview panel-body"
|
||||||
:status-id="statusId"
|
:status-id="statusId"
|
||||||
:replies="getReplies(statusId)"
|
:replies="getReplies(statusId)"
|
||||||
:inline-expanded="collapsable && isExpanded"
|
|
||||||
:expandable="!isExpanded"
|
|
||||||
:show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
|
|
||||||
:in-conversation="isExpanded"
|
|
||||||
:focused="focused === statusId"
|
:focused="focused === statusId"
|
||||||
:in-profile="inProfile"
|
|
||||||
:profile-user-id="profileUserId"
|
|
||||||
class="conversation-status conversation-status-treeview status-fadein panel-body"
|
|
||||||
|
|
||||||
:simple-tree="simple"
|
:thread-display-state="threadDisplay.get(statusId)"
|
||||||
:thread-display-status="threadDisplayStatus[statusId]"
|
|
||||||
:can-dive="canDive"
|
|
||||||
|
|
||||||
@dive="$emit('dive', statusId)"
|
@dive="$emit('dive', statusId)"
|
||||||
@goto="$emit('goto', statusId)"
|
@goto="$emit('goto', statusId)"
|
||||||
@toggle-expanded="toggleExpanded"
|
@toggle-expanded="$emit('toggleExpanded', statusId)"
|
||||||
@suspendable-state-change="e => $emit('suspendableStateChange', e)"
|
@suspendable-state-change="$emit('suspendableStateChange', e)"
|
||||||
@height-change="e => $emit('heightChange', e)"
|
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="currentReplies.length > 0 && threadShowing"
|
v-if="currentReplies.length > 0 && threadShowing"
|
||||||
|
|
@ -31,32 +21,14 @@
|
||||||
<ThreadTree
|
<ThreadTree
|
||||||
v-for="replyStatusId in currentReplies"
|
v-for="replyStatusId in currentReplies"
|
||||||
:key="replyStatusId"
|
:key="replyStatusId"
|
||||||
ref="childComponent"
|
|
||||||
:depth="depth + 1"
|
:depth="depth + 1"
|
||||||
:status-id="replyStatusId"
|
:status-id="replyStatusId"
|
||||||
|
|
||||||
:in-profile="inProfile"
|
@show-thread-recursively="(e) => $emit('showThreadRecursively', e)"
|
||||||
:conversation="conversation"
|
|
||||||
:collapsable="collapsable"
|
|
||||||
:is-expanded="isExpanded"
|
|
||||||
:pinned-status-ids-object="pinnedStatusIdsObject"
|
|
||||||
:profile-user-id="profileUserId"
|
|
||||||
|
|
||||||
:get-replies="getReplies"
|
|
||||||
:focused="focused"
|
|
||||||
:toggle-expanded="toggleExpanded"
|
|
||||||
|
|
||||||
:simple="simple"
|
|
||||||
:thread-display-status="threadDisplayStatus"
|
|
||||||
:show-thread-recursively="showThreadRecursively"
|
|
||||||
:total-reply-count="totalReplyCount"
|
|
||||||
:total-reply-depth="totalReplyDepth"
|
|
||||||
|
|
||||||
:can-dive="canDive"
|
|
||||||
@goto="(e) => $emit('goto', e)"
|
@goto="(e) => $emit('goto', e)"
|
||||||
@dive="(e) => $emit('dive', e)"
|
@dive="(e) => $emit('dive', e)"
|
||||||
@suspendable-state-change="e => $emit('suspendableStateChange', e)"
|
@suspendable-state-change="e => $emit('suspendableStateChange', e)"
|
||||||
@height-change="e => $emit('heightChange', e)"
|
@toggle-expanded="(e) => $emit('toggleExpanded', e)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
@ -88,7 +60,7 @@
|
||||||
tag="button"
|
tag="button"
|
||||||
keypath="status.thread_show_full_with_icon"
|
keypath="status.thread_show_full_with_icon"
|
||||||
class="button-unstyled -link thread-tree-show-replies-button"
|
class="button-unstyled -link thread-tree-show-replies-button"
|
||||||
@click.prevent="showThreadRecursively(statusId)"
|
@click.prevent="$emit('showThreadRecursively', statusId)"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<FAIcon
|
<FAIcon
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,14 @@ const Timeline = {
|
||||||
showScrollTop: false,
|
showScrollTop: false,
|
||||||
paused: false,
|
paused: false,
|
||||||
unfocused: false,
|
unfocused: false,
|
||||||
virtualScrollIndex: 0,
|
|
||||||
blockingClicks: false,
|
blockingClicks: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
profileUserId: this.inProfile && this.timelineRef.argument,
|
||||||
|
}
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
ScrollTopButton,
|
ScrollTopButton,
|
||||||
Conversation,
|
Conversation,
|
||||||
|
|
@ -102,20 +106,7 @@ const Timeline = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
statusesToDisplay() {
|
statusesToDisplay() {
|
||||||
if (!this.virtualScrollingEnabled) {
|
return new Set(this.filteredVisibleStatuses.map(({ id }) => id))
|
||||||
return new Set(this.filteredVisibleStatuses.map(({ id }) => id))
|
|
||||||
}
|
|
||||||
|
|
||||||
const amount = this.timeline.visibleStatusIds.size
|
|
||||||
const statusesPerSide = Math.ceil(Math.max(3, window.innerHeight / 80))
|
|
||||||
const min = Math.max(0, this.virtualScrollIndex - statusesPerSide)
|
|
||||||
const max = Math.min(amount, this.virtualScrollIndex + statusesPerSide)
|
|
||||||
return new Set(
|
|
||||||
this.filteredVisibleStatuses.slice(min, max).map(({ id }) => id),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
virtualScrollingEnabled() {
|
|
||||||
return useMergedConfigStore().mergedConfig.virtualScrolling
|
|
||||||
},
|
},
|
||||||
...mapState(useInterfaceStore, {
|
...mapState(useInterfaceStore, {
|
||||||
mobileLayout: (store) => store.layoutType === 'mobile',
|
mobileLayout: (store) => store.layoutType === 'mobile',
|
||||||
|
|
@ -135,7 +126,6 @@ const Timeline = {
|
||||||
}
|
}
|
||||||
window.addEventListener('keydown', this.handleShortKey)
|
window.addEventListener('keydown', this.handleShortKey)
|
||||||
window.addEventListener('scroll', this.handleScroll)
|
window.addEventListener('scroll', this.handleScroll)
|
||||||
setTimeout(this.determineVisibleStatuses, 250)
|
|
||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
this.timelineChange(null, this.timelineRef)
|
this.timelineChange(null, this.timelineRef)
|
||||||
|
|
@ -193,54 +183,6 @@ const Timeline = {
|
||||||
1000,
|
1000,
|
||||||
this,
|
this,
|
||||||
),
|
),
|
||||||
determineVisibleStatuses() {
|
|
||||||
if (!this.$refs.timeline) return
|
|
||||||
if (!this.virtualScrollingEnabled) return
|
|
||||||
|
|
||||||
const statuses = this.$refs.timeline.children
|
|
||||||
if (statuses.length === 0) return
|
|
||||||
const cappedScrollIndex = Math.max(
|
|
||||||
0,
|
|
||||||
Math.min(this.virtualScrollIndex, statuses.length - 1),
|
|
||||||
)
|
|
||||||
|
|
||||||
const height = Math.max(document.body.offsetHeight, window.pageYOffset)
|
|
||||||
|
|
||||||
const centerOfScreen = window.pageYOffset + window.innerHeight * 0.5
|
|
||||||
|
|
||||||
// Start from approximating the index of some visible status by using the
|
|
||||||
// the center of the screen on the timeline.
|
|
||||||
let approxIndex = Math.min(
|
|
||||||
Math.floor(statuses.length * (centerOfScreen / height)),
|
|
||||||
statuses.length - 1,
|
|
||||||
)
|
|
||||||
let err = statuses[approxIndex].getBoundingClientRect().y
|
|
||||||
|
|
||||||
// if we have a previous scroll index that can be used, test if it's
|
|
||||||
// closer than the previous approximation, use it if so
|
|
||||||
|
|
||||||
const virtualScrollIndexY =
|
|
||||||
statuses[cappedScrollIndex].getBoundingClientRect().y
|
|
||||||
if (Math.abs(err) > virtualScrollIndexY) {
|
|
||||||
approxIndex = cappedScrollIndex
|
|
||||||
err = virtualScrollIndexY
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the status is too far from viewport, check the next/previous ones if
|
|
||||||
// they happen to be better
|
|
||||||
while (err < -20 && approxIndex < statuses.length - 1) {
|
|
||||||
err += statuses[approxIndex].offsetHeight
|
|
||||||
approxIndex++
|
|
||||||
}
|
|
||||||
while (err > window.innerHeight + 100 && approxIndex > 0) {
|
|
||||||
approxIndex--
|
|
||||||
err -= statuses[approxIndex].offsetHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
// this status is now the center point for virtual scrolling and visible
|
|
||||||
// statuses will be nearby statuses before and after it
|
|
||||||
this.virtualScrollIndex = approxIndex
|
|
||||||
},
|
|
||||||
scrollLoad() {
|
scrollLoad() {
|
||||||
// TODO simplify this logic
|
// TODO simplify this logic
|
||||||
const bodyBRect = document.body.getBoundingClientRect()
|
const bodyBRect = document.body.getBoundingClientRect()
|
||||||
|
|
@ -253,7 +195,6 @@ const Timeline = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleScroll: throttle(function (e) {
|
handleScroll: throttle(function (e) {
|
||||||
this.determineVisibleStatuses()
|
|
||||||
this.scrollLoad(e)
|
this.scrollLoad(e)
|
||||||
}, 200),
|
}, 200),
|
||||||
handleVisibilityChange() {
|
handleVisibilityChange() {
|
||||||
|
|
@ -264,9 +205,6 @@ const Timeline = {
|
||||||
timelineRef(newTimeline, oldTimeline) {
|
timelineRef(newTimeline, oldTimeline) {
|
||||||
this.timelineChange(newTimeline, oldTimeline)
|
this.timelineChange(newTimeline, oldTimeline)
|
||||||
},
|
},
|
||||||
filteredVisibleStatuses() {
|
|
||||||
this.determineVisibleStatuses()
|
|
||||||
},
|
|
||||||
newStatusCount(count) {
|
newStatusCount(count) {
|
||||||
if (!useMergedConfigStore().mergedConfig.streaming) {
|
if (!useMergedConfigStore().mergedConfig.streaming) {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@
|
||||||
backdrop-filter: none;
|
backdrop-filter: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vue-recycle-scroller.timeline {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
.timeline-placeholder {
|
.timeline-placeholder {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -78,20 +78,15 @@
|
||||||
</div>
|
</div>
|
||||||
<div :class="classes.body">
|
<div :class="classes.body">
|
||||||
<div
|
<div
|
||||||
ref="timeline"
|
|
||||||
class="timeline"
|
class="timeline"
|
||||||
|
ref="timeline"
|
||||||
role="feed"
|
role="feed"
|
||||||
>
|
>
|
||||||
<Conversation
|
<Conversation
|
||||||
v-for="status in filteredVisibleStatuses"
|
v-for="status in filteredVisibleStatuses"
|
||||||
:key="status.id"
|
:key="status.id"
|
||||||
role="listitem"
|
|
||||||
class="status-fadein"
|
|
||||||
:status-id="status.id"
|
:status-id="status.id"
|
||||||
:in-profile="inProfile"
|
role="listitem"
|
||||||
:profile-user-id="timelineRef.argument"
|
|
||||||
:virtual-hidden="virtualScrollingEnabled && !statusesToDisplay.has(status.id)"
|
|
||||||
collapsable
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="!hideEmpty && count === 0">
|
<template v-if="!hideEmpty && count === 0">
|
||||||
|
|
|
||||||
|
|
@ -457,10 +457,6 @@ export const INSTANCE_DEFAULT_CONFIG_DEFINITIONS = {
|
||||||
description: 'Hide user stats (followers etc)',
|
description: 'Hide user stats (followers etc)',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
virtualScrolling: {
|
|
||||||
description: 'Timeline virtual scrolling',
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
sensitiveByDefault: {
|
sensitiveByDefault: {
|
||||||
description: 'Assume attachments are NSFW by default',
|
description: 'Assume attachments are NSFW by default',
|
||||||
default: false,
|
default: false,
|
||||||
|
|
|
||||||
38
yarn.lock
38
yarn.lock
|
|
@ -1499,11 +1499,6 @@ chai@^6.2.2:
|
||||||
resolved "https://registry.yarnpkg.com/chai/-/chai-6.2.2.tgz#ae41b52c9aca87734505362717f3255facda360e"
|
resolved "https://registry.yarnpkg.com/chai/-/chai-6.2.2.tgz#ae41b52c9aca87734505362717f3255facda360e"
|
||||||
integrity sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==
|
integrity sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==
|
||||||
|
|
||||||
chalk@6.0.0:
|
|
||||||
version "6.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-6.0.0.tgz#a3bae843bc8454f41ef66ef3fd584dcf34dd805d"
|
|
||||||
integrity sha512-2uNTXIuTTxk7ciZgAU1BQcgnchcG0xXnrs6jzkQfj9SsRa9M2s5zE8WT96hS6KmG4MzWHSrvH43DF1m4XRkrFg==
|
|
||||||
|
|
||||||
chokidar@^5.0.0:
|
chokidar@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-5.0.0.tgz#949c126a9238a80792be9a0265934f098af369a5"
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-5.0.0.tgz#949c126a9238a80792be9a0265934f098af369a5"
|
||||||
|
|
@ -1608,15 +1603,6 @@ cropperjs@2.2.0:
|
||||||
"@cropper/elements" "^2.2.0"
|
"@cropper/elements" "^2.2.0"
|
||||||
"@cropper/utils" "^2.2.0"
|
"@cropper/utils" "^2.2.0"
|
||||||
|
|
||||||
cross-spawn@7.0.6:
|
|
||||||
version "7.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
|
||||||
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
|
||||||
dependencies:
|
|
||||||
path-key "^3.1.0"
|
|
||||||
shebang-command "^2.0.0"
|
|
||||||
which "^2.0.1"
|
|
||||||
|
|
||||||
crossws@^0.4.12:
|
crossws@^0.4.12:
|
||||||
version "0.4.12"
|
version "0.4.12"
|
||||||
resolved "https://registry.yarnpkg.com/crossws/-/crossws-0.4.12.tgz#3be6300b1e5a06db08e890e72eb1c7f6c809c9af"
|
resolved "https://registry.yarnpkg.com/crossws/-/crossws-0.4.12.tgz#3be6300b1e5a06db08e890e72eb1c7f6c809c9af"
|
||||||
|
|
@ -2617,11 +2603,6 @@ path-exists@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||||
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
|
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
|
||||||
|
|
||||||
path-key@^3.1.0:
|
|
||||||
version "3.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
|
||||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
|
||||||
|
|
||||||
path-scurry@^2.0.2:
|
path-scurry@^2.0.2:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.2.tgz#6be0d0ee02a10d9e0de7a98bae65e182c9061f85"
|
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.2.tgz#6be0d0ee02a10d9e0de7a98bae65e182c9061f85"
|
||||||
|
|
@ -3083,18 +3064,6 @@ setprototypeof@~1.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||||
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
||||||
|
|
||||||
shebang-command@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
|
||||||
integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
|
|
||||||
dependencies:
|
|
||||||
shebang-regex "^3.0.0"
|
|
||||||
|
|
||||||
shebang-regex@^3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
|
|
||||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
|
||||||
|
|
||||||
siginfo@^2.0.0:
|
siginfo@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
|
resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
|
||||||
|
|
@ -3603,13 +3572,6 @@ which@^1.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe "^2.0.0"
|
isexe "^2.0.0"
|
||||||
|
|
||||||
which@^2.0.1:
|
|
||||||
version "2.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
|
||||||
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
|
||||||
dependencies:
|
|
||||||
isexe "^2.0.0"
|
|
||||||
|
|
||||||
why-is-node-running@^2.3.0:
|
why-is-node-running@^2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04"
|
resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue