Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
13 KB
Referenced Files
None
Subscribers
None
diff --git a/src/components/scroll_top_button/scroll_top_button.js b/src/components/scroll_top_button/scroll_top_button.js
index 41340dbad0..45e6923f9f 100644
--- a/src/components/scroll_top_button/scroll_top_button.js
+++ b/src/components/scroll_top_button/scroll_top_button.js
@@ -1,19 +1,19 @@
const ScrollTopButton = {
props: {
fast: {
type: Boolean,
required: false,
default: false,
},
},
inject: ['bodyScrollPosition'],
methods: {
scrollToTop() {
const speed = this.fast ? 'instant' : 'smooth'
- this.bodyScrollPosition.scrollTo({ top: 0, behavior: speed })
+ this.bodyScrollPosition.scrollToPriority({ top: 0, behavior: speed })
},
},
}
export default ScrollTopButton
diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js
index 0b3d16b4a3..2b5aec0b5f 100644
--- a/src/components/timeline/timeline.js
+++ b/src/components/timeline/timeline.js
@@ -1,269 +1,269 @@
import { debounce, throttle } from 'lodash-es'
import { storeToRefs } from 'pinia'
import {
computed,
inject,
nextTick,
onMounted,
onUnmounted,
ref,
toRefs,
useTemplateRef,
watch,
} from 'vue'
import { useI18n } from 'vue-i18n'
import Conversation from 'src/components/conversation/conversation.vue'
import QuickFilterSettings from 'src/components/quick_filter_settings/quick_filter_settings.vue'
import QuickViewSettings from 'src/components/quick_view_settings/quick_view_settings.vue'
import ScrollTopButton from 'src/components/scroll_top_button/scroll_top_button.vue'
import TimelineMenu from 'src/components/timeline_menu/timeline_menu.vue'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useStatusesStore } from 'src/stores/statuses.js'
import { useTimelinesStore } from 'src/stores/timelines.js'
import { useDocumentFocus } from 'src/composables/useDocumentFocus.js'
import { useInterfaceSizes } from 'src/composables/useInterfaceSizes.js'
import { useVirtualScrolling } from 'src/composables/useVirtualScrolling.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faArrowUp,
faCheck,
faCircleNotch,
faCirclePlus,
faCog,
faMinus,
} from '@fortawesome/free-solid-svg-icons'
library.add(faCircleNotch, faCog, faMinus, faArrowUp, faCirclePlus, faCheck)
const Timeline = {
props: {
timelineRef: Object,
footerSlipgate: Object, // reference to an element where we should put our footer
embedded: Boolean,
skipPinned: Boolean,
hideEmpty: Boolean,
},
components: {
ScrollTopButton,
Conversation,
TimelineMenu,
QuickFilterSettings,
QuickViewSettings,
},
setup(props, ctx) {
const { t } = useI18n()
const { focused } = useDocumentFocus()
const unfocused = computed(() => !focused.value)
// Timeline
const { timelineRef } = toRefs(props)
const timeline = computed(() => useTimelinesStore()[timelineRef.value.name])
const { skipPinned } = toRefs(props)
const filteredVisibleStatuses = computed(() => {
return timeline.value.order
.filter((id) => timeline.value.visibleStatusIds.has(id))
.map((id) => useStatusesStore().allStatuses.get(id))
.filter(({ pinned }) => (skipPinned.value ? !pinned : true))
})
// Scroll position
const scroller = inject('bodyScrollPosition')
const { hasReachedTop, shouldLoadBottom } = scroller
// Virtual scrolling
const { fontSize, navbarSize } = useInterfaceSizes()
// Placeholder heights.
const mutedStatusHeight = computed(() => fontSize.value * 1.5)
const normalStatusHeight = computed(() => fontSize.value * 10)
const getPlaceholderHeight = (id) =>
filteredVisibleStatuses.value.find((item) => item.id === id)?.muted
? mutedStatusHeight
: normalStatusHeight
const body = useTemplateRef('timeline')
const offset = computed(() => {
if (embedded.value) {
// The fontsize after navbar is the little gap between navbar and content
return navbarSize.value + fontSize.value
} else {
return 0
}
})
const { heightChart, changeSuspendState, updateVirtualHeight } =
useVirtualScrolling({
name: 'Timeline',
enabled: ref(true),
list: filteredVisibleStatuses,
body,
offset,
scrollPositionInstance: scroller,
scrollCompensation: false,
getPlaceholderHeight,
})
// Counter
const count = computed(() => timeline.value.order.length)
const newStatusCount = computed(() => timeline.value.newStatusCount)
const showLoadButton = computed(
() => timeline.value.newStatusCount > 0 || timeline.value.reloadNeeded,
)
// Showing new
const paused = ref(false)
const { mergedConfig } = storeToRefs(useMergedConfigStore())
const unfocusedPause = computed(
() => unfocused.value && mergedConfig.value.pauseOnUnfocused,
)
const showNewAutomatically = computed(
() => useMergedConfigStore().mergedConfig.streaming,
)
watch(newStatusCount, (count) => {
if (!showNewAutomatically.value) return
if (count <= 0) return
// only 'stream' them when you're scrolled to the top
if (hasReachedTop.value && !paused.value && !unfocusedPause.value) {
showNewStatuses()
} else {
paused.value = true
}
})
const showNewStatuses = async () => {
if (timeline.value.reloadNeeded) {
useTimelinesStore().clearTimeline(timelineRef.value.name)
fetchOlderStatuses()
} else {
blockClicksTemporarily()
useTimelinesStore().showNewStatuses(timelineRef.value.name)
}
paused.value = false
await nextTick()
- scroller.scrollTo({ top: 0 })
+ scroller.scrollToPriority({ top: 0, behavior: 'smooth' })
}
const fetchOlderStatuses = throttle(() => {
timeline.value.fetcher.fetchOlder()
}, 1000)
// Timeline change
const timelineChange = (newTimeline, oldTimeline) => {
const sameName = newTimeline?.name === oldTimeline?.name
const sameArgument = newTimeline?.argument === oldTimeline?.argument
if (sameName && sameArgument) return
if (oldTimeline) {
useTimelinesStore().deactivate(oldTimeline.name)
}
if (newTimeline) {
useTimelinesStore().activate(newTimeline.name, newTimeline.argument)
}
}
watch(timelineRef, timelineChange, { immediate: true })
onUnmounted(() => {
timelineChange(null, timelineRef.value)
})
// Misclick prevention
const blockingClicks = ref(false)
const stopBlockingClicks = debounce(() => {
blockingClicks.value = false
}, 1000)
const blockClicksTemporarily = () => {
if (!blockingClicks.value) {
blockingClicks.value = true
}
stopBlockingClicks()
}
// Shortcuts
const handleShortKey = (e) => {
// Ignore when input fields are focused
if (['textarea', 'input'].includes(e.target.tagName.toLowerCase())) return
if (e.key === '.') showNewStatuses()
}
onMounted(() => {
window.addEventListener('keydown', handleShortKey)
})
onUnmounted(() => {
window.removeEventListener('keydown', handleShortKey)
})
// Scroll
watch(shouldLoadBottom, (value) => {
if (!value) return
fetchOlderStatuses()
})
// Misc UI things
const classes = computed(() => {
let rootClasses = !embedded.value
? ['panel', 'panel-default']
: ['-embedded']
if (blockingClicks.value)
rootClasses = rootClasses.concat(['-blocked', '_misclick-prevention'])
return {
root: rootClasses,
header: ['timeline-heading'].concat(
!embedded.value ? ['panel-heading', '-sticky'] : ['panel-body'],
),
body: ['timeline-body'].concat(
!embedded.value ? ['panel-body'] : ['panel-body'],
),
footer: ['timeline-footer'].concat(
!embedded.value ? ['panel-footer'] : ['panel-body'],
),
}
})
const loadButtonString = computed(() => {
if (timeline.value.reloadNeeded) {
return t('timeline.reload')
} else {
return `${t('timeline.show_new')} (${newStatusCount.value})`
}
})
const mobileLoadButtonString = computed(() => {
if (timeline.value.reloadNeeded) {
return '+'
} else {
return newStatusCount.value > 99 ? '∞' : newStatusCount.value
}
})
const { layoutType } = storeToRefs(useInterfaceStore())
const mobileLayout = computed(() => layoutType.value === 'mobile')
const { footerSlipgate, embedded, hideEmpty } = toRefs(props)
return {
timelineRef,
timeline,
filteredVisibleStatuses,
heightChart,
updateVirtualHeight,
changeSuspendState,
count,
showLoadButton,
showNewStatuses,
fetchOlderStatuses,
classes,
loadButtonString,
mobileLoadButtonString,
mobileLayout,
footerSlipgate,
embedded,
hideEmpty,
}
},
}
export default Timeline
diff --git a/src/composables/useBodyScroller.js b/src/composables/useBodyScroller.js
index 867c1e5321..d3b4a5bd6a 100644
--- a/src/composables/useBodyScroller.js
+++ b/src/composables/useBodyScroller.js
@@ -1,12 +1,12 @@
import { computed } from 'vue'
import { useClientRectSize } from 'src/composables/useClientRectSize.js'
export function useBodyScroller(windowScroller, windowSize) {
const element = computed(() => window.document.body)
- const { x, y, scrollBy } = windowScroller
+ const { x, y, scrollBy, scrollTo } = windowScroller
const { width: vWidth, height: vHeight } = windowSize
const { width: cWidth, height: cHeight } = useClientRectSize(element)
- return { x, y, vWidth, vHeight, cWidth, cHeight, scrollBy }
+ return { x, y, vWidth, vHeight, cWidth, cHeight, scrollBy, scrollTo }
}
diff --git a/src/composables/useScrollPosition.js b/src/composables/useScrollPosition.js
index aec92301cf..59b95da178 100644
--- a/src/composables/useScrollPosition.js
+++ b/src/composables/useScrollPosition.js
@@ -1,66 +1,77 @@
-import { computed } from 'vue'
+import { computed, ref } from 'vue'
export function useScrollPosition(scroller) {
- const { x, y, cWidth, cHeight, vWidth, vHeight, scrollBy } = scroller
+ const { x, y, cWidth, cHeight, vWidth, vHeight, scrollBy: realScrollBy, scrollTo: realScrollTo } = scroller
+ const priorityInProgress = ref(false)
const top = computed(() => y.value)
const bottom = computed(() => y.value + vHeight.value)
const left = computed(() => x.value)
const right = computed(() => x.value + vWidth.value)
const distanceToTop = top
const distanceToLeft = left
const distanceToBottom = computed(() => cHeight.value - bottom.value)
const distanceToRight = computed(() => cWidth.value - right.value)
const reachMargin = 15
const hasReachedTop = computed(() => distanceToTop.value < reachMargin)
const hasReachedBottom = computed(() => distanceToBottom.value < reachMargin)
const hasReachedLeft = computed(() => distanceToLeft.value < reachMargin)
const hasReachedRight = computed(() => distanceToRight.value < reachMargin)
const loadMargin = 750
const shouldLoadTop = computed(() => distanceToTop.value < loadMargin)
const shouldLoadBottom = computed(() => distanceToBottom.value < loadMargin)
const shouldLoadLeft = computed(() => distanceToLeft.value < loadMargin)
const shouldLoadRight = computed(() => distanceToRight.value < loadMargin)
- const scrollTo = (position = {}) => {
- const amount = {
- left: 0,
- top: 0,
- }
- if (position.left != null) {
- amount.left = position.left - left.value
- }
- if (position.top != null) {
- amount.top = position.top - top.value
- }
- scrollBy(amount.left, amount.top)
+ const scrollTo = async (...args) => {
+ if (priorityInProgress.value) return true
+ return await realScrollTo(...args)
+ }
+ const scrollBy = async (...args) => {
+ if (priorityInProgress.value) return true
+ return await realScrollBy(...args)
+ }
+
+ const scrollToPriority = async (...args) => {
+ priorityInProgress.value = true
+ const result = await realScrollTo(...args)
+ priorityInProgress.value = false
+ return result
+ }
+ const scrollByPriority = async (...args) => {
+ priorityInProgress.value = true
+ const result = await realScrollBy(...args)
+ priorityInProgress.value = false
+ return result
}
return {
top,
left,
right,
bottom,
cWidth,
cHeight,
vWidth,
vHeight,
hasReachedLeft,
hasReachedRight,
hasReachedTop,
hasReachedBottom,
shouldLoadLeft,
shouldLoadRight,
shouldLoadTop,
shouldLoadBottom,
scrollBy,
scrollTo,
+ scrollByPriority,
+ scrollToPriority,
}
}
diff --git a/src/composables/useWindowScroll.js b/src/composables/useWindowScroll.js
index 9f484bab05..1f2b8bd9d0 100644
--- a/src/composables/useWindowScroll.js
+++ b/src/composables/useWindowScroll.js
@@ -1,28 +1,24 @@
import { onMounted, onUnmounted, ref } from 'vue'
export function useWindowScroll() {
const x = ref(0)
const y = ref(0)
- const inProgress = ref(false)
const update = (e) => {
x.value = window.scrollX
y.value = window.scrollY
}
onMounted(() => {
window.addEventListener('scroll', update)
update()
})
onUnmounted(() => {
window.removeEventListener('scroll', update)
})
- const scrollBy = async (...args) => {
- inProgress.value = true
- await window.scrollBy(...args)
- inProgress.value = false
- }
+ const scrollBy = window.scrollBy
+ const scrollTo = window.scrollTo
- return { x, y, scrollBy, inProgress }
+ return { x, y, scrollBy, scrollTo }
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 1, 5:38 AM (14 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1802580
Default Alt Text
(13 KB)

Event Timeline