timeline fetcher improvements
This commit is contained in:
parent
e1e20b5ad2
commit
d7f19d75f1
8 changed files with 135 additions and 157 deletions
|
|
@ -322,15 +322,15 @@ const Status = {
|
|||
shouldNotMute() {
|
||||
if (this.ignoreMute) return true
|
||||
if (this.focused) return true
|
||||
const { reblog } = this.status
|
||||
const { reblog } = this.mainStatus
|
||||
return (
|
||||
((this.inProfile &&
|
||||
// Don't mute user's posts on user timeline (except reblogs)
|
||||
((!reblog && status.user.id === this.profileUserId) ||
|
||||
((!reblog && this.mainStatus.user.id === this.profileUserId) ||
|
||||
// Same as above but also allow self-reblogs
|
||||
reblog?.user.id === this.profileUserId)) ||
|
||||
// Don't mute statuses in muted conversation when said conversation is opened
|
||||
(this.inConversation && status.thread_muted)) &&
|
||||
(this.inConversation && this.mainStatus.thread_muted)) &&
|
||||
// No excuses if post has muted words
|
||||
!this.muteFilterHits.length > 0
|
||||
)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ const Timeline = {
|
|||
showScrollTop: false,
|
||||
paused: false,
|
||||
unfocused: false,
|
||||
bottomedOut: false,
|
||||
virtualScrollIndex: 0,
|
||||
blockingClicks: false,
|
||||
}
|
||||
|
|
@ -183,16 +182,7 @@ const Timeline = {
|
|||
},
|
||||
fetchOlderStatuses: throttle(
|
||||
function () {
|
||||
this.timeline.fetcher
|
||||
.fetchAndUpdate({
|
||||
older: true,
|
||||
showImmediately: true,
|
||||
})
|
||||
.then(({ statuses }) => {
|
||||
if (statuses?.length === 0) {
|
||||
this.bottomedOut = true
|
||||
}
|
||||
})
|
||||
this.timeline.fetcher.fetchOlder()
|
||||
},
|
||||
1000,
|
||||
this,
|
||||
|
|
@ -222,7 +212,7 @@ const Timeline = {
|
|||
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
|
||||
|
|
@ -250,7 +240,7 @@ const Timeline = {
|
|||
const bodyBRect = document.body.getBoundingClientRect()
|
||||
const height = Math.max(bodyBRect.height, -bodyBRect.y)
|
||||
if (
|
||||
this.timeline.loading === false &&
|
||||
!this.timeline.fetcher.loading.value &&
|
||||
this.$el.offsetHeight > 0 &&
|
||||
window.innerHeight + window.pageYOffset >= height - 750
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -90,19 +90,19 @@
|
|||
:disabled="!embedded || !footerSlipgate"
|
||||
>
|
||||
<div
|
||||
v-if="count===0"
|
||||
v-if="count === 0"
|
||||
class="new-status-notification text-center faint"
|
||||
>
|
||||
{{ $t('timeline.no_statuses') }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="bottomedOut"
|
||||
v-else-if="timeline.fetcher.bottomedOut"
|
||||
class="new-status-notification text-center faint"
|
||||
>
|
||||
{{ $t('timeline.no_more_statuses') }}
|
||||
</div>
|
||||
<button
|
||||
v-else-if="!timeline.loading"
|
||||
v-else-if="!timeline.fetcher.loading"
|
||||
class="button-unstyled -link"
|
||||
@click.prevent="fetchOlderStatuses()"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,132 +0,0 @@
|
|||
import { promiseInterval } from '../promise_interval/promise_interval.js'
|
||||
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { ARGUMENT_MAP, useTimelinesStore } from 'src/stores/timelines.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { fetchTimeline } from 'src/api/timelines.js'
|
||||
|
||||
const REPLY_VISIBILITY_TIMELINES = new Set([
|
||||
'friends',
|
||||
'public',
|
||||
'publicAndExternal',
|
||||
'bubble',
|
||||
])
|
||||
|
||||
const fetchAndUpdate = (
|
||||
{ timeline, argument, credentials },
|
||||
{ older = false, showImmediately = false },
|
||||
) => {
|
||||
timeline.loading = true
|
||||
const { hideMutedPosts, replyVisibility } =
|
||||
useMergedConfigStore().mergedConfig
|
||||
const loggedIn = useUsersStore().loggedIn
|
||||
|
||||
const args = { timeline: timeline.name, credentials }
|
||||
const mainArg = ARGUMENT_MAP[timeline.name]
|
||||
if (mainArg) args[mainArg] = argument
|
||||
|
||||
if (older) {
|
||||
// When minId = 0 we need to fetch without maxId param
|
||||
args.maxId = timeline.minId || null
|
||||
} else {
|
||||
args.sinceId = timeline.maxId || null
|
||||
}
|
||||
|
||||
args.withMuted = !hideMutedPosts
|
||||
if (loggedIn && REPLY_VISIBILITY_TIMELINES.has(timeline)) {
|
||||
args.replyVisibility = replyVisibility
|
||||
}
|
||||
|
||||
const numStatusesBeforeFetch = timeline.statusIds.size
|
||||
|
||||
return fetchTimeline(args)
|
||||
.then((response) => {
|
||||
const { data: statuses, pagination, timestamp } = response
|
||||
if (
|
||||
!older &&
|
||||
statuses.length >= 20 &&
|
||||
!timeline.loading &&
|
||||
numStatusesBeforeFetch > 0
|
||||
) {
|
||||
useTimelinesStore().queueFlush(timeline.name, timeline.maxId)
|
||||
}
|
||||
|
||||
const processed = useStatusesStore()
|
||||
.addNewStatuses({ statuses, timestamp })
|
||||
.filter(Boolean)
|
||||
.map(({ id }) => id)
|
||||
|
||||
useTimelinesStore().addStatusesToTimeline(timeline.name, argument, {
|
||||
statuses: processed,
|
||||
showImmediately,
|
||||
older,
|
||||
pagination,
|
||||
})
|
||||
return { statuses, pagination }
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.statusCode === 403 && timeline === 'favorites') {
|
||||
useInstanceCapabilitiesStore().pleromaPublicFavouritesAvailable = false
|
||||
return
|
||||
}
|
||||
console.error('Timeline Error', error)
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'error',
|
||||
messageKey: 'timeline.error',
|
||||
messageArgs: [error.message],
|
||||
timeout: 5000,
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
timeline.loading = false
|
||||
})
|
||||
}
|
||||
|
||||
const timelineFetcher = (timeline, argument, credentials) => {
|
||||
const state = {
|
||||
interval: null,
|
||||
}
|
||||
|
||||
const boundFetchAndUpdate = ({
|
||||
showImmediately,
|
||||
older,
|
||||
} = {}) =>
|
||||
fetchAndUpdate(
|
||||
{
|
||||
timeline,
|
||||
argument,
|
||||
credentials,
|
||||
},
|
||||
{
|
||||
older,
|
||||
showImmediately,
|
||||
},
|
||||
)
|
||||
|
||||
const startFetching = () => {
|
||||
if (state.interval) throw new Error('Interval already exists!')
|
||||
|
||||
boundFetchAndUpdate({
|
||||
showImmediately: timeline.visibleStatusIds.size === 0,
|
||||
})
|
||||
|
||||
state.interval = promiseInterval(boundFetchAndUpdate, 10000)
|
||||
}
|
||||
|
||||
const stopFetching = () => {
|
||||
state.interval.stop()
|
||||
state.interval = null
|
||||
}
|
||||
|
||||
return {
|
||||
startFetching,
|
||||
stopFetching,
|
||||
fetchAndUpdate: boundFetchAndUpdate,
|
||||
}
|
||||
}
|
||||
|
||||
export default timelineFetcher
|
||||
121
src/stores/fetchers/timeline_fetcher.js
Normal file
121
src/stores/fetchers/timeline_fetcher.js
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import { ref } from 'vue'
|
||||
import { promiseInterval } from 'src/services/promise_interval/promise_interval.js'
|
||||
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { ARGUMENT_MAP, useTimelinesStore } from 'src/stores/timelines.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { fetchTimeline } from 'src/api/timelines.js'
|
||||
|
||||
const REPLY_VISIBILITY_TIMELINES = new Set([
|
||||
'friends',
|
||||
'public',
|
||||
'publicAndExternal',
|
||||
'bubble',
|
||||
])
|
||||
|
||||
const timelineFetcher = (timeline, argument, credentials) => {
|
||||
const loading = ref(false)
|
||||
const bottomedOut = ref(false)
|
||||
const interval = ref(null)
|
||||
|
||||
const fetchAndUpdate = ({ older = false, showImmediately = false } = {}) => {
|
||||
loading.value = true
|
||||
|
||||
const { hideMutedPosts, replyVisibility } =
|
||||
useMergedConfigStore().mergedConfig
|
||||
const loggedIn = useUsersStore().loggedIn
|
||||
|
||||
const args = { timeline: timeline.name, credentials }
|
||||
const mainArg = ARGUMENT_MAP[timeline.name]
|
||||
if (mainArg) args[mainArg] = argument
|
||||
|
||||
if (older) {
|
||||
// When minId = 0 we need to fetch without maxId param
|
||||
args.maxId = timeline.minId || null
|
||||
} else {
|
||||
args.sinceId = timeline.maxId || null
|
||||
}
|
||||
|
||||
args.withMuted = !hideMutedPosts
|
||||
if (loggedIn && REPLY_VISIBILITY_TIMELINES.has(timeline)) {
|
||||
args.replyVisibility = replyVisibility
|
||||
}
|
||||
|
||||
const numStatusesBeforeFetch = timeline.statusIds.size
|
||||
|
||||
return fetchTimeline(args)
|
||||
.then(({ data: statuses, pagination, timestamp }) => {
|
||||
if (
|
||||
!older &&
|
||||
!loading.value &&
|
||||
statuses.length >= 20 &&
|
||||
numStatusesBeforeFetch > 0
|
||||
) {
|
||||
useTimelinesStore().queueFlush(timeline.name, timeline.maxId)
|
||||
}
|
||||
|
||||
if (older && statuses.length === 0) {
|
||||
bottomedOut.value = true
|
||||
}
|
||||
|
||||
const processed = useStatusesStore()
|
||||
.addNewStatuses({ statuses, timestamp })
|
||||
.map(({ id }) => id)
|
||||
|
||||
useTimelinesStore().addStatusesToTimeline(timeline.name, argument, {
|
||||
statuses: processed,
|
||||
showImmediately,
|
||||
older,
|
||||
pagination,
|
||||
})
|
||||
|
||||
return { statuses, pagination }
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.statusCode === 403 && timeline === 'favorites') {
|
||||
useInstanceCapabilitiesStore().pleromaPublicFavouritesAvailable = false
|
||||
return
|
||||
}
|
||||
console.error('Timeline Error', error)
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'error',
|
||||
messageKey: 'timeline.error',
|
||||
messageArgs: [error.message],
|
||||
timeout: 5000,
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const startFetching = () => {
|
||||
if (interval.value) throw new Error('Interval already exists!')
|
||||
|
||||
fetchAndUpdate({
|
||||
showImmediately: timeline.visibleStatusIds.size === 0,
|
||||
})
|
||||
|
||||
interval.value = promiseInterval(fetchAndUpdate, 10000)
|
||||
}
|
||||
|
||||
const stopFetching = () => {
|
||||
interval.value.stop()
|
||||
interval.value = null
|
||||
}
|
||||
|
||||
return {
|
||||
startFetching,
|
||||
stopFetching,
|
||||
fetchOlder: () => fetchAndUpdate({ showImmediately: true, older: true }),
|
||||
fetchNewer: () => fetchAndUpdate({ showImmediately: true, older: false }),
|
||||
loading,
|
||||
bottomedOut,
|
||||
}
|
||||
}
|
||||
|
||||
export default timelineFetcher
|
||||
|
|
@ -7,7 +7,7 @@ import { useStatusesStore } from 'src/stores/statuses.js'
|
|||
import { TIMELINE_STREAM_MAP, useStreamingStore } from 'src/stores/streaming.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import timelineFetcher from 'src/services/timeline_fetcher/timeline_fetcher.service.js'
|
||||
import timelineFetcher from 'src/stores/fetchers/timeline_fetcher.js'
|
||||
|
||||
const emptyTl = (name, argument = null) => {
|
||||
const result = {
|
||||
|
|
@ -18,7 +18,6 @@ const emptyTl = (name, argument = null) => {
|
|||
newStatusCount: 0,
|
||||
maxId: '',
|
||||
minId: '',
|
||||
loading: false,
|
||||
streaming: false,
|
||||
flushMarker: 0,
|
||||
fetcher: null,
|
||||
|
|
|
|||
|
|
@ -559,8 +559,8 @@ export const useUsersStore = defineStore('users', {
|
|||
return blockUser({ id, expiresIn }).then((result) => {
|
||||
this.updateUserRelationships(result)
|
||||
|
||||
useStatusesStore().wipeUserStatuses(id)
|
||||
useTimelinesStore().wipeUserStatuses(id)
|
||||
const ids = useStatusesStore().wipeUserStatuses(id)
|
||||
useTimelinesStore().wipeStatuses(ids)
|
||||
})
|
||||
},
|
||||
blockUsers(data = []) {
|
||||
|
|
|
|||
|
|
@ -1119,7 +1119,7 @@ describe('Users store', () => {
|
|||
},
|
||||
)
|
||||
|
||||
vi.spyOn(useTimelinesStore(), 'wipeUserStatuses').mockImplementation(
|
||||
vi.spyOn(useTimelinesStore(), 'wipeStatuses').mockImplementation(
|
||||
async () => {
|
||||
/* no-op */
|
||||
},
|
||||
|
|
@ -1150,7 +1150,7 @@ describe('Users store', () => {
|
|||
},
|
||||
)
|
||||
|
||||
vi.spyOn(useTimelinesStore(), 'wipeUserStatuses').mockImplementation(
|
||||
vi.spyOn(useTimelinesStore(), 'wipeStatuses').mockImplementation(
|
||||
async () => {
|
||||
/* no-op */
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue