TIMELINES STORE YEAH BABY
This commit is contained in:
parent
c723977d90
commit
cb25b392cf
21 changed files with 426 additions and 734 deletions
|
|
@ -1,10 +1,7 @@
|
||||||
import AuthForm from 'src/components/auth_form/auth_form.js'
|
import AuthForm from 'src/components/auth_form/auth_form.js'
|
||||||
import BookmarkTimeline from 'src/components/bookmark_timeline/bookmark_timeline.vue'
|
|
||||||
import ConversationPage from 'src/components/conversation-page/conversation-page.vue'
|
import ConversationPage from 'src/components/conversation-page/conversation-page.vue'
|
||||||
import NavPanel from 'src/components/nav_panel/nav_panel.vue'
|
import NavPanel from 'src/components/nav_panel/nav_panel.vue'
|
||||||
import QuotesTimeline from 'src/components/quotes_timeline/quotes_timeline.vue'
|
|
||||||
import RemoteUserResolver from 'src/components/remote_user_resolver/remote_user_resolver.vue'
|
import RemoteUserResolver from 'src/components/remote_user_resolver/remote_user_resolver.vue'
|
||||||
import TagTimeline from 'src/components/tag_timeline/tag_timeline.vue'
|
|
||||||
import Timeline from 'src/components/timeline/timeline.vue'
|
import Timeline from 'src/components/timeline/timeline.vue'
|
||||||
|
|
||||||
import { useInstanceStore } from 'src/stores/instance.js'
|
import { useInstanceStore } from 'src/stores/instance.js'
|
||||||
|
|
@ -40,7 +37,7 @@ export default (store) => {
|
||||||
path: '/main/all',
|
path: '/main/all',
|
||||||
component: Timeline,
|
component: Timeline,
|
||||||
props: () => ({
|
props: () => ({
|
||||||
timelineName: 'publicAndExternal',
|
timelineRef: { name: 'publicAndExternal' },
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -48,7 +45,7 @@ export default (store) => {
|
||||||
path: '/main/public',
|
path: '/main/public',
|
||||||
component: Timeline,
|
component: Timeline,
|
||||||
props: () => ({
|
props: () => ({
|
||||||
timelineName: 'public',
|
timelineRef: { name: 'public' },
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -57,17 +54,31 @@ export default (store) => {
|
||||||
component: Timeline,
|
component: Timeline,
|
||||||
beforeEnter: validateAuthenticatedRoute,
|
beforeEnter: validateAuthenticatedRoute,
|
||||||
props: () => ({
|
props: () => ({
|
||||||
timelineName: 'friends',
|
timelineRef: { name: 'friends' },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tag-timeline',
|
||||||
|
path: '/tag/:id',
|
||||||
|
component: Timeline,
|
||||||
|
props: (route) => ({
|
||||||
|
timelineRef: { name: 'tag', argument: route.params.id },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'bookmarks',
|
||||||
|
path: '/bookmarks',
|
||||||
|
component: Timeline,
|
||||||
|
props: (route) => ({
|
||||||
|
timelineRef: { name: 'bookmarks', argument: null },
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{ name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline },
|
|
||||||
{ name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline },
|
|
||||||
{
|
{
|
||||||
name: 'bubble',
|
name: 'bubble',
|
||||||
path: '/bubble',
|
path: '/bubble',
|
||||||
component: Timeline,
|
component: Timeline,
|
||||||
props: () => ({
|
props: () => ({
|
||||||
timelineName: 'bubble',
|
timelineRef: { name: 'bubble' },
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -84,7 +95,14 @@ export default (store) => {
|
||||||
meta: { dontScroll: true },
|
meta: { dontScroll: true },
|
||||||
beforeEnter: validateAuthenticatedRoute,
|
beforeEnter: validateAuthenticatedRoute,
|
||||||
},
|
},
|
||||||
{ name: 'quotes', path: '/notice/:id/quotes', component: QuotesTimeline },
|
{
|
||||||
|
name: 'quotes',
|
||||||
|
path: '/notice/:id/quotes',
|
||||||
|
component: Timeline,
|
||||||
|
props: (route) => ({
|
||||||
|
timelineRef: { name: 'quotes', argument: route.params.id },
|
||||||
|
}),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'remote-user-profile-acct',
|
name: 'remote-user-profile-acct',
|
||||||
path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)',
|
path: '/remote-users/:_(@)?:username([^/@]+)@:hostname([^/@]+)',
|
||||||
|
|
@ -120,7 +138,7 @@ export default (store) => {
|
||||||
component: Timeline,
|
component: Timeline,
|
||||||
beforeEnter: validateAuthenticatedRoute,
|
beforeEnter: validateAuthenticatedRoute,
|
||||||
props: () => ({
|
props: () => ({
|
||||||
timelineName: 'dms',
|
timelineRef: { name: 'dms' },
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -218,8 +236,10 @@ export default (store) => {
|
||||||
{
|
{
|
||||||
name: 'lists-timeline',
|
name: 'lists-timeline',
|
||||||
path: '/lists/:id',
|
path: '/lists/:id',
|
||||||
component: () =>
|
component: Timeline,
|
||||||
import('src/components/lists_timeline/lists_timeline.vue'),
|
props: (route) => ({
|
||||||
|
timelineRef: { name: 'lists', argument: route.params.id },
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'lists-edit',
|
name: 'lists-edit',
|
||||||
|
|
@ -253,7 +273,10 @@ export default (store) => {
|
||||||
{
|
{
|
||||||
name: 'bookmark-folder',
|
name: 'bookmark-folder',
|
||||||
path: '/bookmarks/:id',
|
path: '/bookmarks/:id',
|
||||||
component: BookmarkTimeline,
|
component: Timeline,
|
||||||
|
props: (route) => ({
|
||||||
|
timelineRef: { name: 'bookmarks', argument: route.params.id },
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'bookmark-folder-edit',
|
name: 'bookmark-folder-edit',
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import Timeline from 'src/components/timeline/timeline.vue'
|
|
||||||
|
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
|
||||||
|
|
||||||
const Bookmarks = {
|
|
||||||
created() {
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'bookmarks' })
|
|
||||||
this.$store.dispatch('startFetchingTimeline', {
|
|
||||||
timeline: 'bookmarks',
|
|
||||||
bookmarkFolderId: this.folderId || null,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Timeline,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
folderId() {
|
|
||||||
return this.$route.params.id
|
|
||||||
},
|
|
||||||
timeline() {
|
|
||||||
return this.$store.state.statuses.timelines.bookmarks
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
folderId() {
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'bookmarks' })
|
|
||||||
this.$store.dispatch('stopFetchingTimeline', 'bookmarks')
|
|
||||||
this.$store.dispatch('startFetchingTimeline', {
|
|
||||||
timeline: 'bookmarks',
|
|
||||||
bookmarkFolderId: this.folderId || null,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
unmounted() {
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'bookmarks' })
|
|
||||||
this.$store.dispatch('stopFetchingTimeline', 'bookmarks')
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Bookmarks
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<template>
|
|
||||||
<Timeline
|
|
||||||
:title="$t('nav.bookmarks')"
|
|
||||||
:timeline="timeline"
|
|
||||||
:timeline-name="'bookmarks'"
|
|
||||||
:bookmark-folder-id="folderId"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script src="./bookmark_timeline.js"></script>
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import Timeline from 'src/components/timeline/timeline.vue'
|
|
||||||
|
|
||||||
const BubbleTimeline = {
|
|
||||||
components: {
|
|
||||||
Timeline,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
timeline() {
|
|
||||||
return this.$store.state.statuses.timelines.bubble
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.$store.dispatch('startFetchingTimeline', { timeline: 'bubble' })
|
|
||||||
},
|
|
||||||
unmounted() {
|
|
||||||
this.$store.dispatch('stopFetchingTimeline', 'bubble')
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BubbleTimeline
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
import Timeline from 'src/components/timeline/timeline.vue'
|
|
||||||
|
|
||||||
import { useListsStore } from 'src/stores/lists.js'
|
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
|
||||||
|
|
||||||
const ListsTimeline = {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
listId: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Timeline,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
timeline() {
|
|
||||||
return this.$store.state.statuses.timelines.list
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
$route: function (route) {
|
|
||||||
if (route.name === 'lists-timeline' && route.params.id !== this.listId) {
|
|
||||||
this.listId = route.params.id
|
|
||||||
this.$store.dispatch('stopFetchingTimeline', 'list')
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'list' })
|
|
||||||
useListsStore().fetchList({ listId: this.listId })
|
|
||||||
this.$store.dispatch('startFetchingTimeline', {
|
|
||||||
timeline: 'list',
|
|
||||||
listId: this.listId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.listId = this.$route.params.id
|
|
||||||
useListsStore().fetchList({ listId: this.listId })
|
|
||||||
this.$store.dispatch('startFetchingTimeline', {
|
|
||||||
timeline: 'list',
|
|
||||||
listId: this.listId,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
unmounted() {
|
|
||||||
this.$store.dispatch('stopFetchingTimeline', 'list')
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'list' })
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ListsTimeline
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<template>
|
|
||||||
<Timeline
|
|
||||||
title="list.name"
|
|
||||||
:timeline="timeline"
|
|
||||||
:list-id="listId"
|
|
||||||
timeline-name="list"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script src="./lists_timeline.js"></script>
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import Timeline from 'src/components/timeline/timeline.vue'
|
|
||||||
|
|
||||||
const Mentions = {
|
|
||||||
computed: {
|
|
||||||
timeline() {
|
|
||||||
return this.$store.state.statuses.timelines.mentions
|
|
||||||
},
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Timeline,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Mentions
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<template>
|
|
||||||
<Timeline
|
|
||||||
:title="$t('nav.interactions')"
|
|
||||||
:timeline="timeline"
|
|
||||||
:timeline-name="'mentions'"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script src="./mentions.js"></script>
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
import Timeline from 'src/components/timeline/timeline.vue'
|
|
||||||
|
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
|
||||||
|
|
||||||
const QuotesTimeline = {
|
|
||||||
created() {
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'tag' })
|
|
||||||
this.$store.dispatch('startFetchingTimeline', {
|
|
||||||
timeline: 'quotes',
|
|
||||||
statusId: this.statusId,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Timeline,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
statusId() {
|
|
||||||
return this.$route.params.id
|
|
||||||
},
|
|
||||||
timeline() {
|
|
||||||
return this.$store.state.statuses.timelines.quotes
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
statusId() {
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'tag' })
|
|
||||||
this.$store.dispatch('startFetchingTimeline', {
|
|
||||||
timeline: 'quotes',
|
|
||||||
statusId: this.statusId,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
unmounted() {
|
|
||||||
this.$store.dispatch('stopFetchingTimeline', 'quotes')
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default QuotesTimeline
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<template>
|
|
||||||
<Timeline
|
|
||||||
:title="$t('nav.quotes')"
|
|
||||||
:timeline="timeline"
|
|
||||||
:timeline-name="'quotes'"
|
|
||||||
:status-id="statusId"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script src='./quotes_timeline.js'></script>
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
import Timeline from 'src/components/timeline/timeline.vue'
|
|
||||||
|
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
|
||||||
|
|
||||||
const TagTimeline = {
|
|
||||||
created() {
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'tag' })
|
|
||||||
this.$store.dispatch('startFetchingTimeline', {
|
|
||||||
timeline: 'tag',
|
|
||||||
tag: this.tag,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Timeline,
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
tag() {
|
|
||||||
return this.$route.params.tag
|
|
||||||
},
|
|
||||||
timeline() {
|
|
||||||
return this.$store.state.statuses.timelines.tag
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
tag() {
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'tag' })
|
|
||||||
this.$store.dispatch('startFetchingTimeline', {
|
|
||||||
timeline: 'tag',
|
|
||||||
tag: this.tag,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
unmounted() {
|
|
||||||
this.$store.dispatch('stopFetchingTimeline', 'tag')
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TagTimeline
|
|
||||||
|
|
@ -10,10 +10,9 @@ import TimelineMenu from 'src/components/timeline_menu/timeline_menu.vue'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
|
import { useTimelinesStore } from 'src/stores/timelines.js'
|
||||||
import { useUsersStore } from 'src/stores/users.js'
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import timelineFetcher from 'src/services/timeline_fetcher/timeline_fetcher.service.js'
|
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
import {
|
import {
|
||||||
faArrowUp,
|
faArrowUp,
|
||||||
|
|
@ -28,12 +27,8 @@ library.add(faCircleNotch, faCog, faMinus, faArrowUp, faCirclePlus, faCheck)
|
||||||
|
|
||||||
const Timeline = {
|
const Timeline = {
|
||||||
props: {
|
props: {
|
||||||
timelineName: String,
|
timelineRef: Object,
|
||||||
userId: String,
|
argument: String,
|
||||||
listId: String,
|
|
||||||
statusId: String,
|
|
||||||
bookmarkFolderId: String,
|
|
||||||
tag: String,
|
|
||||||
embedded: Boolean,
|
embedded: Boolean,
|
||||||
count: Number,
|
count: Number,
|
||||||
pinnedStatusIds: Set,
|
pinnedStatusIds: Set,
|
||||||
|
|
@ -59,15 +54,16 @@ const Timeline = {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
timeline() {
|
timeline() {
|
||||||
return useStatusesStore().timelines[this.timelineName]
|
return useTimelinesStore()[this.timelineRef.name]
|
||||||
},
|
},
|
||||||
filteredVisibleStatuses() {
|
filteredVisibleStatuses() {
|
||||||
return [...this.timeline.visibleStatuses.values()].filter(
|
return [...this.timeline.visibleStatusesIds.keys()]
|
||||||
(status) =>
|
.filter(
|
||||||
this.timelineName !== 'user' ||
|
(id) =>
|
||||||
(status.id >= this.timeline.minId &&
|
this.timelineRef.name !== 'user' ||
|
||||||
status.id <= this.timeline.maxId),
|
(id >= this.timeline.minId && id <= this.timeline.maxId),
|
||||||
)
|
)
|
||||||
|
.map((id) => this.timeline.statuses.get(id))
|
||||||
},
|
},
|
||||||
filteredPinnedStatusIds() {
|
filteredPinnedStatusIds() {
|
||||||
return (this.pinnedStatusIds || []).filter(
|
return (this.pinnedStatusIds || []).filter(
|
||||||
|
|
@ -118,13 +114,15 @@ const Timeline = {
|
||||||
return keyBy(this.pinnedStatusIds)
|
return keyBy(this.pinnedStatusIds)
|
||||||
},
|
},
|
||||||
statusesToDisplay() {
|
statusesToDisplay() {
|
||||||
const amount = this.timeline.visibleStatuses.size
|
const amount = this.timeline.visibleStatusesIds.size
|
||||||
const statusesPerSide = Math.ceil(Math.max(3, window.innerHeight / 80))
|
const statusesPerSide = Math.ceil(Math.max(3, window.innerHeight / 80))
|
||||||
const nonPinnedIndex =
|
const nonPinnedIndex =
|
||||||
this.virtualScrollIndex - this.filteredPinnedStatusIds.length
|
this.virtualScrollIndex - this.filteredPinnedStatusIds.length
|
||||||
const min = Math.max(0, nonPinnedIndex - statusesPerSide)
|
const min = Math.max(0, nonPinnedIndex - statusesPerSide)
|
||||||
const max = Math.min(amount, nonPinnedIndex + statusesPerSide)
|
const max = Math.min(amount, nonPinnedIndex + statusesPerSide)
|
||||||
return new Set([...this.timeline.visibleStatuses.keys()].slice(min, max))
|
return new Set(
|
||||||
|
[...this.timeline.visibleStatusesIds.keys()].slice(min, max),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
virtualScrollingEnabled() {
|
virtualScrollingEnabled() {
|
||||||
return useMergedConfigStore().mergedConfig.virtualScrolling
|
return useMergedConfigStore().mergedConfig.virtualScrolling
|
||||||
|
|
@ -134,26 +132,7 @@ const Timeline = {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
const store = this.$store
|
this.timelineChange(this.timelineRef)
|
||||||
const credentials = useUsersStore().currentUser.credentials
|
|
||||||
const showImmediately = this.timeline.visibleStatuses.length === 0
|
|
||||||
|
|
||||||
window.addEventListener('scroll', this.handleScroll)
|
|
||||||
|
|
||||||
if (store.state.api.fetchers[this.timelineName]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
timelineFetcher.fetchAndUpdate({
|
|
||||||
credentials,
|
|
||||||
timeline: this.timelineName,
|
|
||||||
showImmediately,
|
|
||||||
userId: this.userId,
|
|
||||||
listId: this.listId,
|
|
||||||
statusId: this.statusId,
|
|
||||||
bookmarkFolderId: this.bookmarkFolderId,
|
|
||||||
tag: this.tag,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (document.hidden !== undefined) {
|
if (document.hidden !== undefined) {
|
||||||
|
|
@ -165,6 +144,7 @@ const Timeline = {
|
||||||
this.unfocused = document.hidden
|
this.unfocused = document.hidden
|
||||||
}
|
}
|
||||||
window.addEventListener('keydown', this.handleShortKey)
|
window.addEventListener('keydown', this.handleShortKey)
|
||||||
|
window.addEventListener('scroll', this.handleScroll)
|
||||||
setTimeout(this.determineVisibleStatuses, 250)
|
setTimeout(this.determineVisibleStatuses, 250)
|
||||||
},
|
},
|
||||||
unmounted() {
|
unmounted() {
|
||||||
|
|
@ -176,12 +156,14 @@ const Timeline = {
|
||||||
this.handleVisibilityChange,
|
this.handleVisibilityChange,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
useStatusesStore().setLoading({
|
|
||||||
timeline: this.timelineName,
|
|
||||||
value: false,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
timelineChange(newTimeline, oldTimeline) {
|
||||||
|
if (oldTimeline && oldTimeline.name !== 'friends') {
|
||||||
|
useTimelinesStore().clearTimeline(oldTimeline.name)
|
||||||
|
}
|
||||||
|
useTimelinesStore().startFetchingTimeline(newTimeline.name, newTimeline.argument)
|
||||||
|
},
|
||||||
stopBlockingClicks: debounce(function () {
|
stopBlockingClicks: debounce(function () {
|
||||||
this.blockingClicks = false
|
this.blockingClicks = false
|
||||||
}, 1000),
|
}, 1000),
|
||||||
|
|
@ -198,52 +180,25 @@ const Timeline = {
|
||||||
},
|
},
|
||||||
showNewStatuses() {
|
showNewStatuses() {
|
||||||
if (this.timeline.flushMarker !== 0) {
|
if (this.timeline.flushMarker !== 0) {
|
||||||
useStatusesStore().clearTimeline({
|
useTimelinesStore().clearTimeline(this.timelineRef.name)
|
||||||
timeline: this.timelineName,
|
useTimelinesStore().queueFlush(this.timelineRef.name, '')
|
||||||
excludeUserId: true,
|
|
||||||
})
|
|
||||||
useStatusesStore().queueFlush({ timeline: this.timelineName, id: 0 })
|
|
||||||
if (this.timelineName === 'user') {
|
|
||||||
this.$store.dispatch('fetchPinnedStatuses', this.userId)
|
|
||||||
}
|
|
||||||
this.fetchOlderStatuses()
|
this.fetchOlderStatuses()
|
||||||
} else {
|
} else {
|
||||||
this.blockClicksTemporarily()
|
this.blockClicksTemporarily()
|
||||||
useStatusesStore().showNewStatuses(this.timelineName)
|
useTimelinesStore().showNewStatuses(this.timelineRef.name)
|
||||||
this.paused = false
|
this.paused = false
|
||||||
}
|
}
|
||||||
window.scrollTo({ top: 0 })
|
window.scrollTo({ top: 0 })
|
||||||
},
|
},
|
||||||
fetchOlderStatuses: throttle(
|
fetchOlderStatuses: throttle(
|
||||||
function () {
|
function () {
|
||||||
const credentials = useUsersStore().currentUser.credentials
|
this.timeline.fetcher
|
||||||
useStatusesStore().setLoading({
|
.fetchAndUpdate()
|
||||||
timeline: this.timelineName,
|
|
||||||
value: true,
|
|
||||||
})
|
|
||||||
timelineFetcher
|
|
||||||
.fetchAndUpdate({
|
|
||||||
credentials,
|
|
||||||
timeline: this.timelineName,
|
|
||||||
older: true,
|
|
||||||
showImmediately: true,
|
|
||||||
userId: this.userId,
|
|
||||||
listId: this.listId,
|
|
||||||
statusId: this.statusId,
|
|
||||||
bookmarkFolderId: this.bookmarkFolderId,
|
|
||||||
tag: this.tag,
|
|
||||||
})
|
|
||||||
.then(({ statuses }) => {
|
.then(({ statuses }) => {
|
||||||
if (statuses?.length === 0) {
|
if (statuses?.length === 0) {
|
||||||
this.bottomedOut = true
|
this.bottomedOut = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() =>
|
|
||||||
useStatusesStore().setLoading({
|
|
||||||
timeline: this.timelineName,
|
|
||||||
value: false,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
1000,
|
1000,
|
||||||
this,
|
this,
|
||||||
|
|
@ -314,6 +269,9 @@ const Timeline = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
timelineRef(newTimeline, oldTimeline) {
|
||||||
|
this.timelineChange(newTimeline, oldTimeline)
|
||||||
|
},
|
||||||
filteredVisibleStatuses() {
|
filteredVisibleStatuses() {
|
||||||
this.determineVisibleStatuses()
|
this.determineVisibleStatuses()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import UserCard from 'src/components/user_card/user_card.vue'
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
import { useTimelinesStore } from 'src/stores/statuses.js'
|
||||||
import { useUsersStore } from 'src/stores/users.js'
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||||
|
|
@ -44,14 +44,11 @@ const UserProfile = {
|
||||||
this.$store.dispatch('clearFriends', this.userId)
|
this.$store.dispatch('clearFriends', this.userId)
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
timeline() {
|
|
||||||
return this.$store.state.statuses.timelines.user
|
|
||||||
},
|
|
||||||
favorites() {
|
favorites() {
|
||||||
return this.$store.state.statuses.timelines.favorites
|
return useTimelinesStore().favorites
|
||||||
},
|
},
|
||||||
media() {
|
media() {
|
||||||
return this.$store.state.statuses.timelines.media
|
return useTimelinesStore().media
|
||||||
},
|
},
|
||||||
isUs() {
|
isUs() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -99,32 +96,12 @@ const UserProfile = {
|
||||||
},
|
},
|
||||||
fetchUsers(group) {
|
fetchUsers(group) {
|
||||||
return () =>
|
return () =>
|
||||||
this.$store
|
useUsersStore()['fetch' + group](this.userId)
|
||||||
.dispatch('fetch' + group, this.userId)
|
|
||||||
.then((result) => ({ items: result }))
|
.then((result) => ({ items: result }))
|
||||||
},
|
},
|
||||||
load(userNameOrId) {
|
load(userNameOrId) {
|
||||||
const startFetchingTimeline = (timeline, userId) => {
|
|
||||||
// Clear timeline only if load another user's profile
|
|
||||||
if (userId !== this.$store.state.statuses.timelines[timeline].userId) {
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'user' })
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'userPinned' })
|
|
||||||
useStatusesStore().clearTimeline({ timeline: 'media' })
|
|
||||||
}
|
|
||||||
this.$store.dispatch('startFetchingTimeline', { timeline, userId })
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadById = (userId) => {
|
const loadById = (userId) => {
|
||||||
this.userId = userId
|
this.userId = userId
|
||||||
startFetchingTimeline('user', userId)
|
|
||||||
startFetchingTimeline('media', userId)
|
|
||||||
if (this.isUs) {
|
|
||||||
startFetchingTimeline('favorites')
|
|
||||||
} else if (!this.user.hide_favorites) {
|
|
||||||
startFetchingTimeline('favorites', userId)
|
|
||||||
}
|
|
||||||
// Fetch all pinned statuses immediately
|
|
||||||
this.$store.dispatch('fetchPinnedStatuses', userId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset view
|
// Reset view
|
||||||
|
|
@ -138,6 +115,7 @@ const UserProfile = {
|
||||||
const user = maybeId
|
const user = maybeId
|
||||||
? useUsersStore().findUser(maybeId)
|
? useUsersStore().findUser(maybeId)
|
||||||
: useUsersStore().findUserByName(maybeName)
|
: useUsersStore().findUserByName(maybeName)
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
loadById(user.id)
|
loadById(user.id)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -159,13 +137,7 @@ const UserProfile = {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
stopFetching() {
|
|
||||||
this.$store.dispatch('stopFetchingTimeline', 'user')
|
|
||||||
this.$store.dispatch('stopFetchingTimeline', 'favorites')
|
|
||||||
this.$store.dispatch('stopFetchingTimeline', 'media')
|
|
||||||
},
|
|
||||||
switchUser(userNameOrId) {
|
switchUser(userNameOrId) {
|
||||||
this.stopFetching()
|
|
||||||
this.load(userNameOrId)
|
this.load(userNameOrId)
|
||||||
},
|
},
|
||||||
onTabSwitch(tab) {
|
onTabSwitch(tab) {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
<UserCard
|
<UserCard
|
||||||
:user-id="userId"
|
:user-id="userId"
|
||||||
:switcher="true"
|
:switcher="true"
|
||||||
:selected="timeline.viewing"
|
|
||||||
:compact="compactProfiles"
|
:compact="compactProfiles"
|
||||||
avatar-action="zoom"
|
avatar-action="zoom"
|
||||||
:has-note-editor="true"
|
:has-note-editor="true"
|
||||||
|
|
@ -19,19 +18,21 @@
|
||||||
:render-only-focused="true"
|
:render-only-focused="true"
|
||||||
:on-switch="onTabSwitch"
|
:on-switch="onTabSwitch"
|
||||||
>
|
>
|
||||||
<Timeline
|
<div
|
||||||
key="statuses"
|
class="statuses"
|
||||||
:label="$t('user_card.statuses')"
|
:label="$t('user_card.statuses')"
|
||||||
:count="user.statuses_count"
|
:count="user.statuses_count"
|
||||||
:embedded="true"
|
|
||||||
:title="$t('user_profile.timeline_title')"
|
:title="$t('user_profile.timeline_title')"
|
||||||
:timeline="timeline"
|
>
|
||||||
timeline-name="user"
|
<Timeline
|
||||||
:user-id="userId"
|
key="statuses"
|
||||||
:pinned-status-ids="user.pinnedStatusIds"
|
:embedded="true"
|
||||||
:in-profile="true"
|
timeline-name="user"
|
||||||
:footer-slipgate="footerRef"
|
:argument="userId"
|
||||||
/>
|
:in-profile="true"
|
||||||
|
:footer-slipgate="footerRef"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="followsTabVisible"
|
v-if="followsTabVisible"
|
||||||
key="followees"
|
key="followees"
|
||||||
|
|
@ -74,8 +75,7 @@
|
||||||
:embedded="true"
|
:embedded="true"
|
||||||
:title="$t('user_card.media')"
|
:title="$t('user_card.media')"
|
||||||
timeline-name="media"
|
timeline-name="media"
|
||||||
:timeline="media"
|
:argument="userId"
|
||||||
:user-id="userId"
|
|
||||||
:in-profile="true"
|
:in-profile="true"
|
||||||
:footer-slipgate="footerRef"
|
:footer-slipgate="footerRef"
|
||||||
/>
|
/>
|
||||||
|
|
@ -87,8 +87,7 @@
|
||||||
:embedded="true"
|
:embedded="true"
|
||||||
:title="$t('user_card.favorites')"
|
:title="$t('user_card.favorites')"
|
||||||
timeline-name="favorites"
|
timeline-name="favorites"
|
||||||
:timeline="favorites"
|
:argument="isUs ? undefined : userId"
|
||||||
:user-id="isUs ? undefined : userId"
|
|
||||||
:in-profile="true"
|
:in-profile="true"
|
||||||
:footer-slipgate="footerRef"
|
:footer-slipgate="footerRef"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useShoutStore } from 'src/stores/shout.js'
|
import { useShoutStore } from 'src/stores/shout.js'
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
|
|
||||||
import { fetchTimeline } from 'src/api/timelines.js'
|
|
||||||
import {
|
import {
|
||||||
getMastodonSocketURI,
|
getMastodonSocketURI,
|
||||||
ProcessedWS,
|
ProcessedWS,
|
||||||
|
|
@ -18,7 +17,6 @@ import {
|
||||||
} from 'src/api/websocket.js'
|
} from 'src/api/websocket.js'
|
||||||
import followRequestFetcher from 'src/services/follow_request_fetcher/follow_request_fetcher.service'
|
import followRequestFetcher from 'src/services/follow_request_fetcher/follow_request_fetcher.service'
|
||||||
import notificationsFetcher from 'src/services/notifications_fetcher/notifications_fetcher.service.js'
|
import notificationsFetcher from 'src/services/notifications_fetcher/notifications_fetcher.service.js'
|
||||||
import timelineFetcher from 'src/services/timeline_fetcher/timeline_fetcher.service.js'
|
|
||||||
|
|
||||||
const retryTimeout = (multiplier) => 1000 * multiplier
|
const retryTimeout = (multiplier) => 1000 * multiplier
|
||||||
|
|
||||||
|
|
@ -240,53 +238,6 @@ const api = {
|
||||||
state.mastoUserSocket.close()
|
state.mastoUserSocket.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
// Timelines
|
|
||||||
startFetchingTimeline(
|
|
||||||
store,
|
|
||||||
{
|
|
||||||
timeline = 'friends',
|
|
||||||
tag = false,
|
|
||||||
userId = false,
|
|
||||||
listId = false,
|
|
||||||
statusId = false,
|
|
||||||
bookmarkFolderId = false,
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
timeline === 'favourites' &&
|
|
||||||
!useInstanceCapabilitiesStore().pleromaPublicFavouritesAvailable
|
|
||||||
)
|
|
||||||
return
|
|
||||||
if (store.state.fetchers[timeline]) return
|
|
||||||
|
|
||||||
const fetcher = timelineFetcher.startFetching({
|
|
||||||
timeline,
|
|
||||||
store,
|
|
||||||
userId,
|
|
||||||
listId,
|
|
||||||
statusId,
|
|
||||||
bookmarkFolderId,
|
|
||||||
tag,
|
|
||||||
credentials: useOAuthStore().token,
|
|
||||||
})
|
|
||||||
|
|
||||||
store.commit('addFetcher', { fetcherName: timeline, fetcher })
|
|
||||||
},
|
|
||||||
stopFetchingTimeline(store, timeline) {
|
|
||||||
const fetcher = store.state.fetchers[timeline]
|
|
||||||
if (!fetcher) return
|
|
||||||
store.commit('removeFetcher', { fetcherName: timeline, fetcher })
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchTimeline(store, { timeline, ...rest }) {
|
|
||||||
fetchTimeline({
|
|
||||||
store,
|
|
||||||
timeline,
|
|
||||||
...rest,
|
|
||||||
credentials: useOAuthStore().token,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
startFetchingNotifications(store) {
|
startFetchingNotifications(store) {
|
||||||
if (store.state.fetchers.notifications) return
|
if (store.state.fetchers.notifications) return
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js'
|
import { showDesktopNotification } from '../desktop_notification_utils/desktop_notification_utils.js'
|
||||||
import { muteFilterHits } from '../status_parser/status_parser.js'
|
import { muteFilterHits } from '../status_parser/status_parser.js'
|
||||||
import { prepareNotificationObject, isStatusNotification } from './notification_utils_sw.js'
|
import {
|
||||||
|
isStatusNotification,
|
||||||
|
prepareNotificationObject,
|
||||||
|
} from './notification_utils_sw.js'
|
||||||
|
|
||||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,94 +6,81 @@ import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.j
|
||||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useStatusesStore } from 'src/stores/statuses.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 { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
import { fetchTimeline } from 'src/api/timelines.js'
|
import { fetchTimeline } from 'src/api/timelines.js'
|
||||||
|
|
||||||
const update = ({
|
const REPLY_VISIBILITY_TIMELINES = new Set([
|
||||||
statuses,
|
'friends',
|
||||||
timeline,
|
'public',
|
||||||
showImmediately,
|
'publicAndExternal',
|
||||||
userId,
|
'bubble',
|
||||||
listId,
|
])
|
||||||
pagination,
|
|
||||||
}) => {
|
|
||||||
const ccTimeline = camelCase(timeline)
|
|
||||||
|
|
||||||
useStatusesStore().addNewStatuses({
|
|
||||||
timelineName: ccTimeline,
|
|
||||||
userId,
|
|
||||||
listId,
|
|
||||||
statuses,
|
|
||||||
showImmediately,
|
|
||||||
pagination,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchAndUpdate = ({
|
const fetchAndUpdate = ({
|
||||||
|
timeline,
|
||||||
|
argument,
|
||||||
credentials,
|
credentials,
|
||||||
timeline = 'friends',
|
}, {
|
||||||
older = false,
|
|
||||||
showImmediately = false,
|
|
||||||
userId,
|
|
||||||
listId,
|
|
||||||
statusId,
|
|
||||||
bookmarkFolderId,
|
|
||||||
tag,
|
|
||||||
maxId,
|
maxId,
|
||||||
sinceId,
|
sinceId,
|
||||||
|
older = false,
|
||||||
|
showImmediately = false,
|
||||||
}) => {
|
}) => {
|
||||||
const args = { timeline, credentials }
|
timeline.loading = true
|
||||||
const timelineData = useStatusesStore().timelines[camelCase(timeline)]
|
|
||||||
const { hideMutedPosts, replyVisibility } =
|
const { hideMutedPosts, replyVisibility } =
|
||||||
useMergedConfigStore().mergedConfig
|
useMergedConfigStore().mergedConfig
|
||||||
const loggedIn = !!useUsersStore().currentUser
|
const loggedIn = useUsersStore().loggedIn
|
||||||
|
|
||||||
|
const args = { timeline: timeline.name, credentials }
|
||||||
|
args[ARGUMENT_MAP[timeline.name]] = argument
|
||||||
|
|
||||||
if (older) {
|
if (older) {
|
||||||
// When minId = 0 we need to fetch without maxId param
|
// When minId = 0 we need to fetch without maxId param
|
||||||
args.maxId = maxId || timelineData.minId || null
|
args.maxId = maxId || timeline.minId || null
|
||||||
} else {
|
} else {
|
||||||
if (sinceId === undefined) {
|
if (sinceId === undefined) {
|
||||||
args.sinceId = timelineData.maxId
|
args.sinceId = timeline.maxId
|
||||||
} else if (sinceId !== null) {
|
} else if (sinceId !== null) {
|
||||||
args.sinceId = sinceId
|
args.sinceId = sinceId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
args.userId = userId
|
|
||||||
args.listId = listId
|
|
||||||
args.statusId = statusId
|
|
||||||
args.bookmarkFolderId = bookmarkFolderId
|
|
||||||
args.tag = tag
|
|
||||||
args.withMuted = !hideMutedPosts
|
args.withMuted = !hideMutedPosts
|
||||||
if (
|
if (loggedIn && REPLY_VISIBILITY_TIMELINES.has(timeline)) {
|
||||||
loggedIn &&
|
|
||||||
['friends', 'public', 'publicAndExternal', 'bubble'].includes(timeline)
|
|
||||||
) {
|
|
||||||
args.replyVisibility = replyVisibility
|
args.replyVisibility = replyVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
const numStatusesBeforeFetch = timelineData.statuses.length
|
const numStatusesBeforeFetch = timeline.statuses.size
|
||||||
|
|
||||||
return fetchTimeline(args)
|
return fetchTimeline(args)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
const { data: statuses, pagination } = response
|
const { data: statuses, pagination, timestamp } = response
|
||||||
if (
|
if (
|
||||||
!older &&
|
!older &&
|
||||||
statuses.length >= 20 &&
|
statuses.length >= 20 &&
|
||||||
!timelineData.loading &&
|
!timeline.loading &&
|
||||||
numStatusesBeforeFetch > 0
|
numStatusesBeforeFetch > 0
|
||||||
) {
|
) {
|
||||||
useStatusesStore().queueFlush({ timeline, id: timelineData.maxId })
|
useTimelinesStore().queueFlush(timeline.name, timeline.maxId)
|
||||||
}
|
}
|
||||||
update({
|
|
||||||
statuses,
|
const processed = useStatusesStore()
|
||||||
timeline,
|
.addNewStatuses({ statuses, timestamp })
|
||||||
showImmediately,
|
.filter(Boolean)
|
||||||
userId,
|
|
||||||
listId,
|
console.log(timeline.name, argument, showImmediately, processed.length)
|
||||||
pagination,
|
|
||||||
})
|
useTimelinesStore().addStatusesToTimeline(
|
||||||
|
timeline.name,
|
||||||
|
argument,
|
||||||
|
{
|
||||||
|
statuses,
|
||||||
|
showImmediately,
|
||||||
|
pagination,
|
||||||
|
}
|
||||||
|
)
|
||||||
return { statuses, pagination }
|
return { statuses, pagination }
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|
@ -108,48 +95,53 @@ const fetchAndUpdate = ({
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.finally(() => {
|
||||||
|
timeline.loading = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const startFetching = ({
|
const timelineFetcher = (timeline, argument, argumentKey, credentials) => {
|
||||||
timeline = 'friends',
|
const state = {
|
||||||
credentials,
|
interval: null
|
||||||
userId,
|
}
|
||||||
listId,
|
|
||||||
statusId,
|
const boundFetchAndUpdate = ({
|
||||||
bookmarkFolderId,
|
showImmediately,
|
||||||
tag,
|
maxId,
|
||||||
}) => {
|
sinceId,
|
||||||
const timelineData = useStatusesStore().timelines[camelCase(timeline)]
|
older,
|
||||||
const showImmediately = timelineData.visibleStatuses.size === 0
|
} = {}) => fetchAndUpdate({
|
||||||
console.log(timeline)
|
timeline,
|
||||||
timelineData.userId = userId
|
argument,
|
||||||
timelineData.listId = listId
|
argumentKey,
|
||||||
timelineData.bookmarkFolderId = bookmarkFolderId
|
credentials,
|
||||||
fetchAndUpdate({
|
}, {
|
||||||
timeline,
|
maxId,
|
||||||
credentials,
|
sinceId,
|
||||||
|
older,
|
||||||
showImmediately,
|
showImmediately,
|
||||||
userId,
|
|
||||||
listId,
|
|
||||||
statusId,
|
|
||||||
bookmarkFolderId,
|
|
||||||
tag,
|
|
||||||
})
|
})
|
||||||
const boundFetchAndUpdate = () =>
|
|
||||||
fetchAndUpdate({
|
const startFetching = () => {
|
||||||
timeline,
|
if (state.interval) throw new Error('Interval already exists!')
|
||||||
credentials,
|
|
||||||
userId,
|
boundFetchAndUpdate({
|
||||||
listId,
|
showImmediately: timeline.visibleStatusesIds.size === 0
|
||||||
statusId,
|
|
||||||
bookmarkFolderId,
|
|
||||||
tag,
|
|
||||||
})
|
})
|
||||||
return promiseInterval(boundFetchAndUpdate, 10000)
|
|
||||||
}
|
state.interval = promiseInterval(boundFetchAndUpdate, 10000)
|
||||||
const timelineFetcher = {
|
}
|
||||||
fetchAndUpdate,
|
|
||||||
startFetching,
|
const stopFetching = () => {
|
||||||
|
state.interval.stop()
|
||||||
|
state.interval = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
startFetching,
|
||||||
|
stopFetching,
|
||||||
|
fetchAndUpdate: boundFetchAndUpdate,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default timelineFetcher
|
export default timelineFetcher
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,7 @@ import {
|
||||||
isValidNotification,
|
isValidNotification,
|
||||||
maybeShowNotification,
|
maybeShowNotification,
|
||||||
} from '../services/notification_utils/notification_utils.js'
|
} from '../services/notification_utils/notification_utils.js'
|
||||||
import {
|
import { isStatusNotification } from '../services/notification_utils/notification_utils_sw.js'
|
||||||
isStatusNotification,
|
|
||||||
} from '../services/notification_utils/notification_utils_sw.js'
|
|
||||||
|
|
||||||
import { useI18nStore } from 'src/stores/i18n.js'
|
import { useI18nStore } from 'src/stores/i18n.js'
|
||||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { first, last, maxBy, minBy } from 'lodash'
|
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
|
|
@ -33,42 +32,12 @@ import {
|
||||||
unretweet,
|
unretweet,
|
||||||
} from 'src/api/user.js'
|
} from 'src/api/user.js'
|
||||||
|
|
||||||
const emptyTl = (userId) => ({
|
|
||||||
statuses: new Map(),
|
|
||||||
faves: [],
|
|
||||||
visibleStatuses: new Map(),
|
|
||||||
newStatusCount: 0,
|
|
||||||
maxId: '',
|
|
||||||
minId: '',
|
|
||||||
minVisibleId: 0,
|
|
||||||
loading: false,
|
|
||||||
followers: [],
|
|
||||||
friends: [],
|
|
||||||
userId,
|
|
||||||
flushMarker: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const defaultState = () => ({
|
export const defaultState = () => ({
|
||||||
allStatuses: new Map(),
|
allStatuses: new Map(),
|
||||||
timestamps: new WeakMap(),
|
timestamps: new WeakMap(),
|
||||||
scrobblesNextFetch: {},
|
scrobblesNextFetch: {},
|
||||||
conversations: new Map(),
|
conversations: new Map(),
|
||||||
favorites: new Set(),
|
favorites: new Set(),
|
||||||
timelines: {
|
|
||||||
mentions: emptyTl(),
|
|
||||||
public: emptyTl(),
|
|
||||||
user: emptyTl(),
|
|
||||||
userPinned: emptyTl(),
|
|
||||||
favorites: emptyTl(),
|
|
||||||
media: emptyTl(),
|
|
||||||
publicAndExternal: emptyTl(),
|
|
||||||
friends: emptyTl(),
|
|
||||||
tag: emptyTl(),
|
|
||||||
dms: emptyTl(),
|
|
||||||
bookmarks: emptyTl(),
|
|
||||||
list: emptyTl(),
|
|
||||||
bubble: emptyTl(),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const getLatestScrobble = (user) => {
|
const getLatestScrobble = (user) => {
|
||||||
|
|
@ -106,40 +75,19 @@ const getLatestScrobble = (user) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const USER_TIMELINES = new Set(['user', 'userPinned', 'media'])
|
|
||||||
|
|
||||||
export const useStatusesStore = defineStore('statuses', {
|
export const useStatusesStore = defineStore('statuses', {
|
||||||
state: defaultState,
|
state: defaultState,
|
||||||
actions: {
|
actions: {
|
||||||
addNewStatuses({
|
addNewStatuses({ statuses, user = {}, userId, timestamp }) {
|
||||||
statuses,
|
|
||||||
showImmediately = false,
|
|
||||||
timelineName,
|
|
||||||
user = {},
|
|
||||||
userId,
|
|
||||||
noIdUpdate = false,
|
|
||||||
pagination = {},
|
|
||||||
timestamp,
|
|
||||||
}) {
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
if (!Array.isArray(statuses)) {
|
if (!Array.isArray(statuses)) {
|
||||||
return false
|
throw new TypeError("Statuses aren't an array!")
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeline = this.timelines[timelineName]
|
// addStatus should always return "main" status,
|
||||||
|
// not "sub-status" i.e. retweeted/quoted/liked status
|
||||||
if (timeline && !noIdUpdate && statuses.length > 0) {
|
// in case of likes (which are not statuses) it should return null
|
||||||
this.updateTimelineExtremes(timeline, statuses, pagination)
|
const addStatus = (data) => {
|
||||||
}
|
|
||||||
|
|
||||||
// This makes sure that user timeline won't get data meant for other
|
|
||||||
// user. I.e. opening different user profiles makes request which could
|
|
||||||
// return data late after user already viewing different user profile
|
|
||||||
if (USER_TIMELINES.has(timelineName) && timeline.userId !== userId) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const addStatus = (data, showImmediately, addToTimeline = true) => {
|
|
||||||
getLatestScrobble(data.user)
|
getLatestScrobble(data.user)
|
||||||
|
|
||||||
const [status] = this.mergeOrAdd(this.allStatuses, data)
|
const [status] = this.mergeOrAdd(this.allStatuses, data)
|
||||||
|
|
@ -154,49 +102,9 @@ export const useStatusesStore = defineStore('statuses', {
|
||||||
conversations.set(conversationId, new Map([[status.id, status]]))
|
conversations.set(conversationId, new Map([[status.id, status]]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are mentioned in a post
|
// Work on quote
|
||||||
if (
|
|
||||||
status.type === 'status' &&
|
|
||||||
status.attentions.some(({ id }) => id === user.id)
|
|
||||||
) {
|
|
||||||
const mentions = this.timelines.mentions
|
|
||||||
|
|
||||||
// Add the mention to the mentions timeline
|
|
||||||
if (timeline !== mentions) {
|
|
||||||
const [, isNew] = this.mergeOrAdd(mentions.statuses, data)
|
|
||||||
if (isNew) mentions.newStatusCount += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.visibility === 'direct') {
|
|
||||||
const dms = this.timelines.dms
|
|
||||||
|
|
||||||
const [, isNew] = this.mergeOrAdd(dms.statuses, data)
|
|
||||||
if (isNew) dms.newStatusCount += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some statuses should only be added to the global status repository.
|
|
||||||
if (timeline && addToTimeline) {
|
|
||||||
// Decide if we should treat the status as new for this timeline.
|
|
||||||
const [status, isNew] = this.mergeOrAdd(timeline.statuses, data)
|
|
||||||
if (isNew) {
|
|
||||||
if (showImmediately) {
|
|
||||||
// Add it directly to the visibleStatuses, don't change
|
|
||||||
// newStatusCount
|
|
||||||
timeline.visibleStatuses.set(status.id, status)
|
|
||||||
} else {
|
|
||||||
// Just change newStatuscount
|
|
||||||
timeline.newStatusCount += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.quote) {
|
if (status.quote) {
|
||||||
addStatus(
|
status.quote = addStatus(status.quote)
|
||||||
status.quote,
|
|
||||||
/* showImmediately = */ false,
|
|
||||||
/* addToTimeline = */ false,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return status
|
return status
|
||||||
|
|
@ -204,41 +112,15 @@ export const useStatusesStore = defineStore('statuses', {
|
||||||
|
|
||||||
const processors = {
|
const processors = {
|
||||||
status: (status) => {
|
status: (status) => {
|
||||||
addStatus(status, showImmediately)
|
return addStatus(status)
|
||||||
},
|
},
|
||||||
edit: (status) => {
|
edit: (status) => {
|
||||||
addStatus(status, showImmediately)
|
return addStatus(status)
|
||||||
},
|
},
|
||||||
retweet: (status) => {
|
retweet: (status) => {
|
||||||
// RetweetedStatuses are never shown immediately
|
// RetweetedStatuses are never shown immediately
|
||||||
const retweetedStatus = addStatus(
|
if (status.retweeted_status) addStatus(status.retweeted_status)
|
||||||
status.retweeted_status,
|
return addStatus(status)
|
||||||
false,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
|
|
||||||
let retweet
|
|
||||||
// If the retweeted status is already there, don't add the retweet
|
|
||||||
// to the timeline.
|
|
||||||
if (
|
|
||||||
[...(timeline?.statuses.values() ?? [])].some((s) => {
|
|
||||||
if (s.retweeted_status) {
|
|
||||||
return (
|
|
||||||
s.id === retweetedStatus.id ||
|
|
||||||
s.retweeted_status.id === retweetedStatus.id
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return s.id === retweetedStatus.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
// Already have it visible (either as the original or another RT), don't add to timeline, don't show.
|
|
||||||
retweet = addStatus(status, false, false)
|
|
||||||
} else {
|
|
||||||
retweet = addStatus(status, showImmediately)
|
|
||||||
}
|
|
||||||
|
|
||||||
retweet.retweeted_status = retweetedStatus
|
|
||||||
},
|
},
|
||||||
favorite: (favorite) => {
|
favorite: (favorite) => {
|
||||||
// Only update if this is a new favorite.
|
// Only update if this is a new favorite.
|
||||||
|
|
@ -258,19 +140,22 @@ export const useStatusesStore = defineStore('statuses', {
|
||||||
}
|
}
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
},
|
},
|
||||||
follow: () => {
|
follow: () => {
|
||||||
// NOOP, it is known status but we don't do anything about it for now
|
// NOOP, it is known status but we don't do anything about it for now
|
||||||
|
return null
|
||||||
},
|
},
|
||||||
default: (unknown) => {
|
default: (unknown) => {
|
||||||
console.warn('unknown status type', unknown)
|
console.warn('unknown status type', unknown)
|
||||||
|
return null
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
statuses.forEach((status) => {
|
return statuses.map((status) => {
|
||||||
const type = status.type
|
const type = status.type
|
||||||
const processor = processors[type] ?? processors.default
|
const processor = processors[type] ?? processors.default
|
||||||
processor(status)
|
return processor(status)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
mergeOrAdd(map, status, timestamp) {
|
mergeOrAdd(map, status, timestamp) {
|
||||||
|
|
@ -307,21 +192,6 @@ export const useStatusesStore = defineStore('statuses', {
|
||||||
this.addNewStatuses({ statuses: [status], timestamp }),
|
this.addNewStatuses({ statuses: [status], timestamp }),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
fetchPinnedStatuses(userId) {
|
|
||||||
return fetchPinnedStatuses({
|
|
||||||
id: userId,
|
|
||||||
credentials: useOAuthStore().token,
|
|
||||||
}).then(({ data: statuses, timestamp }) =>
|
|
||||||
this.addNewStatuses({
|
|
||||||
statuses,
|
|
||||||
timeline: 'userPinned',
|
|
||||||
userId,
|
|
||||||
showImmediately: true,
|
|
||||||
noIdUpdate: true,
|
|
||||||
timestamp,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
fetchStatusSource(id) {
|
fetchStatusSource(id) {
|
||||||
return fetchStatusSource({
|
return fetchStatusSource({
|
||||||
id,
|
id,
|
||||||
|
|
@ -394,55 +264,6 @@ export const useStatusesStore = defineStore('statuses', {
|
||||||
status.emoji_reactions = emojiReactions
|
status.emoji_reactions = emojiReactions
|
||||||
},
|
},
|
||||||
|
|
||||||
// Queues & Timeline manip
|
|
||||||
updateTimelineExtremes(timeline, statuses, pagination = {}) {
|
|
||||||
// Can't use Math.min/max because it doesn't work with string (duh)
|
|
||||||
const minNew = pagination.maxId ?? minBy(statuses, 'id').id ?? ''
|
|
||||||
const maxNew = pagination.minId ?? maxBy(statuses, 'id').id ?? ''
|
|
||||||
|
|
||||||
const newer = maxNew > timeline.maxId || timeline.maxId === ''
|
|
||||||
const older = minNew < timeline.minId || timeline.minId === ''
|
|
||||||
|
|
||||||
if (newer) {
|
|
||||||
timeline.maxId = maxNew
|
|
||||||
}
|
|
||||||
if (older) {
|
|
||||||
timeline.minId = minNew
|
|
||||||
}
|
|
||||||
},
|
|
||||||
showNewStatuses(timelineName) {
|
|
||||||
const timeline = this.timelines[timelineName]
|
|
||||||
|
|
||||||
timeline.newStatusCount = 0
|
|
||||||
|
|
||||||
timeline.visibleStatuses = new Map(
|
|
||||||
[...timeline.statuses.entries()].slice(0, 50),
|
|
||||||
)
|
|
||||||
timeline.minVisibleId = last(timeline.visibleStatuses.keys())
|
|
||||||
timeline.minId = ''
|
|
||||||
timeline.maxId = ''
|
|
||||||
this.updateTimelineExtremes(timeline, [...timeline.statuses.values()])
|
|
||||||
},
|
|
||||||
resetStatuses() {
|
|
||||||
const emptyState = defaultState()
|
|
||||||
|
|
||||||
Object.entries(emptyState).forEach(([key, value]) => {
|
|
||||||
this[key] = value
|
|
||||||
})
|
|
||||||
},
|
|
||||||
clearTimeline({ timeline, excludeUserId = false }) {
|
|
||||||
const userId = excludeUserId ? this.timelines[timeline].userId : undefined
|
|
||||||
this.timelines[timeline] = emptyTl(userId)
|
|
||||||
},
|
|
||||||
queueFlush({ timeline, id }) {
|
|
||||||
this.timelines[timeline].flushMarker = id
|
|
||||||
},
|
|
||||||
queueFlushAll() {
|
|
||||||
Object.keys(this.timelines).forEach((timeline) => {
|
|
||||||
this.timelines[timeline].flushMarker = this.timelines[timeline].maxId
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
/// Favorite
|
/// Favorite
|
||||||
favorite(id) {
|
favorite(id) {
|
||||||
|
|
@ -770,25 +591,6 @@ export const useStatusesStore = defineStore('statuses', {
|
||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// Misc
|
|
||||||
removeUserStatuses({ timelineName, userId }) {
|
|
||||||
const timeline = this.timelines[timelineName]
|
|
||||||
|
|
||||||
timeline.statuses
|
|
||||||
.values()
|
|
||||||
.filter(({ user }) => user.id === userId)
|
|
||||||
.forEach(({ id }) => {
|
|
||||||
timeline.statuses.delete(id)
|
|
||||||
timeline.visibleStatuses.delete(id)
|
|
||||||
})
|
|
||||||
timeline.minVisibleId =
|
|
||||||
timeline.visibleStatuses.length > 0
|
|
||||||
? last(timeline.visibleStatuses).id
|
|
||||||
: 0
|
|
||||||
timeline.maxId =
|
|
||||||
timeline.statuses.length > 0 ? first(timeline.statuses).id : 0
|
|
||||||
},
|
|
||||||
setVirtualHeight({ statusId, height }) {
|
setVirtualHeight({ statusId, height }) {
|
||||||
this.allStatuses.get(statusId).virtualHeight = height
|
this.allStatuses.get(statusId).virtualHeight = height
|
||||||
},
|
},
|
||||||
|
|
@ -796,8 +598,5 @@ export const useStatusesStore = defineStore('statuses', {
|
||||||
const status = this.allStatuses.get(id)
|
const status = this.allStatuses.get(id)
|
||||||
status.poll = poll
|
status.poll = poll
|
||||||
},
|
},
|
||||||
setLoading({ timeline, value }) {
|
|
||||||
this.timelines[timeline].loading = value
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
233
src/stores/timelines.js
Normal file
233
src/stores/timelines.js
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
import { first, last, max, min } from 'lodash'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||||
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
|
import { useUsersStore } from 'src/stores/users.js'
|
||||||
|
|
||||||
|
import timelineFetcher from 'src/services/timeline_fetcher/timeline_fetcher.service.js'
|
||||||
|
|
||||||
|
const emptyTl = (name, argument = null) => {
|
||||||
|
const result = {
|
||||||
|
name,
|
||||||
|
statuses: new Map(),
|
||||||
|
visibleStatusesIds: new Set(),
|
||||||
|
newStatusCount: 0,
|
||||||
|
maxId: 0,
|
||||||
|
minId: 0,
|
||||||
|
minVisibleId: 0,
|
||||||
|
loading: false,
|
||||||
|
flushMarker: 0,
|
||||||
|
fetcher: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
const property = USER_TIMELINES.has(name) ? 'userId' : ARGUMENT_MAP[name]
|
||||||
|
|
||||||
|
if (property) {
|
||||||
|
result[property] = argument
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ARGUMENT_MAP = {
|
||||||
|
tag: 'tag',
|
||||||
|
list: 'listId',
|
||||||
|
bookmarks: 'bookmarkFolderId',
|
||||||
|
quotes: 'statusId',
|
||||||
|
search: 'query',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultState = () => {
|
||||||
|
return Object.fromEntries([
|
||||||
|
'mentions',
|
||||||
|
'public',
|
||||||
|
'user',
|
||||||
|
'userPinned',
|
||||||
|
'media',
|
||||||
|
'favorites',
|
||||||
|
'publicAndExternal',
|
||||||
|
'friends',
|
||||||
|
'tag',
|
||||||
|
'dms',
|
||||||
|
'bookmarks',
|
||||||
|
'list',
|
||||||
|
'bubble',
|
||||||
|
'quotes',
|
||||||
|
'search',
|
||||||
|
].map((name) => [name, emptyTl(name)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
const USER_TIMELINES = new Set(['user', 'userPinned', 'media', 'favorites'])
|
||||||
|
//const CUSTOM_SORT = new Set(['bookmarks', 'favorites'])
|
||||||
|
|
||||||
|
export const useTimelinesStore = defineStore('timelines', {
|
||||||
|
state: defaultState,
|
||||||
|
actions: {
|
||||||
|
addStatusesToTimeline(
|
||||||
|
timelineName,
|
||||||
|
argument,
|
||||||
|
{
|
||||||
|
statuses,
|
||||||
|
showImmediately = false,
|
||||||
|
noIdUpdate = false,
|
||||||
|
pagination = {},
|
||||||
|
nested = false,
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
if (statuses.length === 0) return
|
||||||
|
const timeline = this[timelineName]
|
||||||
|
|
||||||
|
// This makes sure that user timeline won't get data meant for other
|
||||||
|
// user. I.e. opening different user profiles makes request which could
|
||||||
|
// return data late after user already viewing different user profile
|
||||||
|
// Same can happen with tags etc.
|
||||||
|
const property = USER_TIMELINES.has(name) ? 'userId' : ARGUMENT_MAP[name]
|
||||||
|
|
||||||
|
if (property && timeline[property] !== argument) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!noIdUpdate) {
|
||||||
|
this.updateTimelineExtremes(
|
||||||
|
timeline,
|
||||||
|
statuses.map((x) => x.id),
|
||||||
|
pagination,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
statuses.forEach((status) => {
|
||||||
|
const isNew = !timeline.statuses.has(status.id)
|
||||||
|
timeline.statuses.set(status.id, status)
|
||||||
|
|
||||||
|
if (isNew) {
|
||||||
|
if (showImmediately) {
|
||||||
|
// Add it directly to the visibleStatuses, don't change
|
||||||
|
// newStatusCount
|
||||||
|
timeline.visibleStatusesIds.add(status.id)
|
||||||
|
} else {
|
||||||
|
// Just change newStatuscount
|
||||||
|
timeline.newStatusCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nested) return
|
||||||
|
// We are mentioned in a post
|
||||||
|
if (
|
||||||
|
status.type === 'status' &&
|
||||||
|
status.attentions.some(
|
||||||
|
({ id }) => id === useUsersStore().currentUser?.id,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// Add the mention to the mentions timeline
|
||||||
|
if (timeline !== this.mentions) {
|
||||||
|
this.addStatusesToTimeline('mentions', null, { statuses, nested: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.visibility === 'direct') {
|
||||||
|
if (timeline !== this.dms) {
|
||||||
|
this.addStatusesToTimeline('dms', null, { statuses, nested: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// Fetchers
|
||||||
|
startFetchingTimeline(timelineName, argument) {
|
||||||
|
const timeline = this[timelineName]
|
||||||
|
if (timeline.fetcher) return
|
||||||
|
|
||||||
|
if (
|
||||||
|
timelineName === 'favourites' &&
|
||||||
|
!useInstanceCapabilitiesStore().pleromaPublicFavouritesAvailable
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timeline.fetcher = timelineFetcher(
|
||||||
|
timeline,
|
||||||
|
argument,
|
||||||
|
ARGUMENT_MAP[timeline.name],
|
||||||
|
useOAuthStore().token,
|
||||||
|
)
|
||||||
|
|
||||||
|
timeline.fetcher.startFetching()
|
||||||
|
},
|
||||||
|
stopFetchingTimeline(timelineName) {
|
||||||
|
const timeline = this[timelineName]
|
||||||
|
timeline.fetcher?.stopFetching()
|
||||||
|
timeline.fetcher = null
|
||||||
|
},
|
||||||
|
|
||||||
|
// Queues & Timeline manip
|
||||||
|
updateTimelineExtremes(timeline, statuses, pagination = {}) {
|
||||||
|
// Can't use Math.min/max because it doesn't work with string (duh)
|
||||||
|
const minNew = pagination.maxId ?? min(...statuses) ?? ''
|
||||||
|
const maxNew = pagination.minId ?? max(...statuses) ?? ''
|
||||||
|
|
||||||
|
const newer = maxNew > timeline.maxId || timeline.maxId === ''
|
||||||
|
const older = minNew < timeline.minId || timeline.minId === ''
|
||||||
|
|
||||||
|
if (newer) {
|
||||||
|
timeline.maxId = maxNew
|
||||||
|
}
|
||||||
|
if (older) {
|
||||||
|
timeline.minId = minNew
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetStatuses() {
|
||||||
|
const emptyState = defaultState()
|
||||||
|
|
||||||
|
Object.entries(emptyState).forEach(([key, value]) => {
|
||||||
|
this[key] = value
|
||||||
|
})
|
||||||
|
},
|
||||||
|
showNewStatuses(timelineName) {
|
||||||
|
const timeline = this[timelineName]
|
||||||
|
|
||||||
|
timeline.newStatusCount = 0
|
||||||
|
|
||||||
|
timeline.visibleStatusesIds = new Set(
|
||||||
|
[...timeline.statuses.keys()].slice(0, 50),
|
||||||
|
)
|
||||||
|
timeline.minVisibleId = last(timeline.visibleStatusesIds.keys())
|
||||||
|
timeline.minId = ''
|
||||||
|
timeline.maxId = ''
|
||||||
|
|
||||||
|
this.updateTimelineExtremes(timeline, [...timeline.statuses.keys()])
|
||||||
|
},
|
||||||
|
clearTimeline(timeline, excludeUserId = false) {
|
||||||
|
const userId = excludeUserId ? this[timeline].userId : undefined
|
||||||
|
this.stopFetchingTimeline(timeline)
|
||||||
|
this[timeline] = emptyTl(timeline, userId)
|
||||||
|
},
|
||||||
|
queueFlush(timeline, id) {
|
||||||
|
this[timeline].flushMarker = id
|
||||||
|
},
|
||||||
|
queueFlushAll() {
|
||||||
|
Object.keys(this).forEach((timeline) => {
|
||||||
|
this[timeline].flushMarker = this[timeline].maxId
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// Misc
|
||||||
|
removeUserStatuses({ timelineName, userId }) {
|
||||||
|
const timeline = this.timelines[timelineName]
|
||||||
|
|
||||||
|
timeline.statuses
|
||||||
|
.values()
|
||||||
|
.filter(({ user }) => user.id === userId)
|
||||||
|
.forEach(({ id }) => {
|
||||||
|
timeline.statuses.delete(id)
|
||||||
|
timeline.visibleStatusesIds.delete(id)
|
||||||
|
})
|
||||||
|
timeline.minVisibleId =
|
||||||
|
timeline.visibleStatusesIds.size > 0
|
||||||
|
? last(timeline.visibleStatusesIds).id
|
||||||
|
: 0
|
||||||
|
timeline.maxId =
|
||||||
|
timeline.statuses.length > 0 ? first(timeline.statuses).id : 0
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
@ -23,6 +23,7 @@ import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||||
|
import { useTimelinesStore } from 'src/stores/timelines.js'
|
||||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
||||||
|
|
||||||
|
|
@ -667,7 +668,7 @@ export const useUsersStore = defineStore('users', {
|
||||||
|
|
||||||
const startPolling = () => {
|
const startPolling = () => {
|
||||||
// Start getting fresh posts.
|
// Start getting fresh posts.
|
||||||
dispatch('startFetchingTimeline', { timeline: 'friends' })
|
useTimelinesStore().startFetchingTimeline('friends')
|
||||||
|
|
||||||
// Start fetching notifications
|
// Start fetching notifications
|
||||||
dispatch('startFetchingNotifications')
|
dispatch('startFetchingNotifications')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue