i think it's usable now
This commit is contained in:
parent
5b9ab6b1ea
commit
37f501108b
42 changed files with 296 additions and 1295 deletions
|
|
@ -1,15 +1,11 @@
|
|||
import AuthForm from 'src/components/auth_form/auth_form.js'
|
||||
import BookmarkTimeline from 'src/components/bookmark_timeline/bookmark_timeline.vue'
|
||||
import BubbleTimeline from 'src/components/bubble_timeline/bubble_timeline.vue'
|
||||
import ConversationPage from 'src/components/conversation-page/conversation-page.vue'
|
||||
import DMs from 'src/components/dm_timeline/dm_timeline.vue'
|
||||
import FriendsTimeline from 'src/components/friends_timeline/friends_timeline.vue'
|
||||
import NavPanel from 'src/components/nav_panel/nav_panel.vue'
|
||||
import PublicAndExternalTimeline from 'src/components/public_and_external_timeline/public_and_external_timeline.vue'
|
||||
import PublicTimeline from 'src/components/public_timeline/public_timeline.vue'
|
||||
import QuotesTimeline from 'src/components/quotes_timeline/quotes_timeline.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 { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
|
|
@ -42,22 +38,38 @@ export default (store) => {
|
|||
{
|
||||
name: 'public-external-timeline',
|
||||
path: '/main/all',
|
||||
component: PublicAndExternalTimeline,
|
||||
component: Timeline,
|
||||
props: () => ({
|
||||
timelineName: 'publicAndExternal',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'public-timeline',
|
||||
path: '/main/public',
|
||||
component: PublicTimeline,
|
||||
component: Timeline,
|
||||
props: () => ({
|
||||
timelineName: 'public',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'friends',
|
||||
path: '/main/friends',
|
||||
component: FriendsTimeline,
|
||||
component: Timeline,
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
props: () => ({
|
||||
timelineName: 'friends',
|
||||
}),
|
||||
},
|
||||
{ name: 'tag-timeline', path: '/tag/:tag', component: TagTimeline },
|
||||
{ name: 'bookmarks', path: '/bookmarks', component: BookmarkTimeline },
|
||||
{ name: 'bubble', path: '/bubble', component: BubbleTimeline },
|
||||
{
|
||||
name: 'bubble',
|
||||
path: '/bubble',
|
||||
component: Timeline,
|
||||
props: () => ({
|
||||
timelineName: 'bubble',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'conversation',
|
||||
path: '/notice/:id',
|
||||
|
|
@ -105,8 +117,11 @@ export default (store) => {
|
|||
{
|
||||
name: 'dms',
|
||||
path: '/users/:username/dms',
|
||||
component: DMs,
|
||||
component: Timeline,
|
||||
beforeEnter: validateAuthenticatedRoute,
|
||||
props: () => ({
|
||||
timelineName: 'dms',
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'registration',
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
const Bookmarks = {
|
||||
created() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'bookmarks' })
|
||||
useStatusesStore().clearTimeline({ timeline: 'bookmarks' })
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'bookmarks',
|
||||
bookmarkFolderId: this.folderId || null,
|
||||
|
|
@ -21,7 +23,7 @@ const Bookmarks = {
|
|||
},
|
||||
watch: {
|
||||
folderId() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'bookmarks' })
|
||||
useStatusesStore().clearTimeline({ timeline: 'bookmarks' })
|
||||
this.$store.dispatch('stopFetchingTimeline', 'bookmarks')
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'bookmarks',
|
||||
|
|
@ -30,7 +32,7 @@ const Bookmarks = {
|
|||
},
|
||||
},
|
||||
unmounted() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'bookmarks' })
|
||||
useStatusesStore().clearTimeline({ timeline: 'bookmarks' })
|
||||
this.$store.dispatch('stopFetchingTimeline', 'bookmarks')
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.bubble')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'bubble'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./bubble_timeline.js"></script>
|
||||
|
|
@ -19,6 +19,7 @@ import UserPopover from 'src/components/user_popover/user_popover.vue'
|
|||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
|
@ -100,9 +101,9 @@ const ChatMessage = {
|
|||
return !this.message.in_reply_to_status_id
|
||||
},
|
||||
customReplyTo() {
|
||||
return this.$store.state.statuses.allStatusesObject[
|
||||
this.message.in_reply_to_status_id
|
||||
]
|
||||
return useStatusesStore().allStatuses.get(
|
||||
this.message.in_reply_to_status_id,
|
||||
)
|
||||
},
|
||||
replyToName() {
|
||||
if (this.message.in_reply_to_screen_name) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import { useChatsStore } from 'src/stores/chats.js'
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import {
|
||||
|
|
@ -122,7 +123,7 @@ const Chat = {
|
|||
},
|
||||
computed: {
|
||||
conversationId() {
|
||||
const status = this.$store.state.statuses.allStatusesObject[this.statusId]
|
||||
const status = useStatusesStore().allStatuses.get(this.statusId)
|
||||
return get(
|
||||
status,
|
||||
'retweeted_status.statusnet_conversation_id',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { clone, filter, findIndex, get, reduce } from 'lodash'
|
||||
import { get, reduce } from 'lodash'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
|
|
@ -9,9 +9,10 @@ import QuickViewSettings from 'src/components/quick_view_settings/quick_view_set
|
|||
import RichContent from 'src/components/rich_content/rich_content.jsx'
|
||||
import ThreadTree from 'src/components/thread_tree/thread_tree.vue'
|
||||
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import { fetchConversation, fetchStatus } from 'src/api/public.js'
|
||||
import { WSConnectionStatus } from 'src/api/websocket.js'
|
||||
|
|
@ -51,20 +52,6 @@ const sortById = (a, b) => {
|
|||
}
|
||||
}
|
||||
|
||||
const sortAndFilterConversation = (conversation, statusoid) => {
|
||||
if (statusoid.type === 'retweet') {
|
||||
conversation = filter(
|
||||
conversation,
|
||||
(status) =>
|
||||
status.type === 'retweet' ||
|
||||
status.id !== statusoid.retweeted_status.id,
|
||||
)
|
||||
} else {
|
||||
conversation = filter(conversation, (status) => status.type !== 'retweet')
|
||||
}
|
||||
return conversation.filter(Boolean).sort(sortById)
|
||||
}
|
||||
|
||||
const conversation = {
|
||||
props: {
|
||||
statusId: {
|
||||
|
|
@ -166,7 +153,7 @@ const conversation = {
|
|||
return this.virtualHidden && this.suspendable
|
||||
},
|
||||
status() {
|
||||
return this.$store.state.statuses.allStatusesObject[this.statusId]
|
||||
return useStatusesStore().allStatuses.get(this.statusId)
|
||||
},
|
||||
originalStatusId() {
|
||||
if (this.status.retweeted_status) {
|
||||
|
|
@ -187,15 +174,11 @@ const conversation = {
|
|||
return [this.status]
|
||||
}
|
||||
|
||||
const conversation = clone(
|
||||
this.$store.state.statuses.conversationsObject[this.conversationId],
|
||||
const conversation = useStatusesStore().conversations.get(
|
||||
this.conversationId,
|
||||
)
|
||||
const statusIndex = findIndex(conversation, { id: this.originalStatusId })
|
||||
if (statusIndex !== -1) {
|
||||
conversation[statusIndex] = this.status
|
||||
}
|
||||
|
||||
return sortAndFilterConversation(conversation, this.status)
|
||||
return [...conversation.values()].toSorted(sortById)
|
||||
},
|
||||
statusMap() {
|
||||
return this.conversation.reduce((res, s) => {
|
||||
|
|
@ -441,7 +424,7 @@ const conversation = {
|
|||
}
|
||||
},
|
||||
virtualHidden() {
|
||||
this.$store.dispatch('setVirtualHeight', {
|
||||
useStatusesStore().setVirtualHeight({
|
||||
statusId: this.statusId,
|
||||
height: `${this.$el.clientHeight}px`,
|
||||
})
|
||||
|
|
@ -453,9 +436,12 @@ const conversation = {
|
|||
fetchConversation({
|
||||
id: this.statusId,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: { ancestors, descendants } }) => {
|
||||
this.$store.dispatch('addNewStatuses', { statuses: ancestors })
|
||||
this.$store.dispatch('addNewStatuses', { statuses: descendants })
|
||||
}).then(({ data: { ancestors, descendants }, timestamp }) => {
|
||||
useStatusesStore().addNewStatuses({ statuses: ancestors, timestamp })
|
||||
useStatusesStore().addNewStatuses({
|
||||
statuses: descendants,
|
||||
timestamp,
|
||||
})
|
||||
this.setFocused(this.originalStatusId)
|
||||
})
|
||||
} else {
|
||||
|
|
@ -482,17 +468,17 @@ const conversation = {
|
|||
this.focused = id
|
||||
|
||||
if (!this.streamingEnabled) {
|
||||
this.$store.dispatch('fetchStatus', id)
|
||||
useStatusesStore().fetchStatus(id)
|
||||
}
|
||||
|
||||
this.$store.dispatch('fetchFavsAndRepeats', id)
|
||||
this.$store.dispatch('fetchEmojiReactionsBy', id)
|
||||
useStatusesStore().fetchFavsAndRepeats(id)
|
||||
useStatusesStore().fetchEmojiReactionsBy(id)
|
||||
},
|
||||
toggleExpanded() {
|
||||
this.expanded = !this.expanded
|
||||
},
|
||||
getConversationId(statusId) {
|
||||
const status = this.$store.state.statuses.allStatusesObject[statusId]
|
||||
const status = useStatusesStore().allStatuses.get(statusId)
|
||||
return get(
|
||||
status,
|
||||
'retweeted_status.statusnet_conversation_id',
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const DMs = {
|
||||
computed: {
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.dms
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
}
|
||||
|
||||
export default DMs
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.dms')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'dms'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./dm_timeline.js"></script>
|
||||
|
|
@ -6,6 +6,7 @@ import PostStatusForm from 'src/components/post_status_form/post_status_form.vue
|
|||
import StatusContent from 'src/components/status_content/status_content.vue'
|
||||
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faPollH } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -65,7 +66,7 @@ const Draft = {
|
|||
},
|
||||
refStatus() {
|
||||
return this.draft.refId
|
||||
? this.$store.state.statuses.allStatusesObject[this.draft.refId]
|
||||
? useStatusesStore().allStatuses.get(this.draft.refId)
|
||||
: undefined
|
||||
},
|
||||
localCollapseSubjectDefault() {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import UserListPopover from 'src/components/user_list_popover/user_list_popover.
|
|||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
|
@ -62,10 +63,7 @@ const EmojiReactions = {
|
|||
async fetchEmojiReactionsByIfMissing() {
|
||||
const hasNoAccounts = this.status.emoji_reactions.find((r) => !r.accounts)
|
||||
if (hasNoAccounts) {
|
||||
return await this.$store.dispatch(
|
||||
'fetchEmojiReactionsBy',
|
||||
this.status.id,
|
||||
)
|
||||
return await useStatusesStore().fetchEmojiReactionsBy(this.status.id)
|
||||
}
|
||||
},
|
||||
reactWith(emoji) {
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const FriendsTimeline = {
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
computed: {
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.friends
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default FriendsTimeline
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.timeline')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'friends'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./friends_timeline.js"></script>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
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() {
|
||||
|
|
@ -21,7 +22,7 @@ const ListsTimeline = {
|
|||
if (route.name === 'lists-timeline' && route.params.id !== this.listId) {
|
||||
this.listId = route.params.id
|
||||
this.$store.dispatch('stopFetchingTimeline', 'list')
|
||||
this.$store.commit('clearTimeline', { timeline: 'list' })
|
||||
useStatusesStore().clearTimeline({ timeline: 'list' })
|
||||
useListsStore().fetchList({ listId: this.listId })
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'list',
|
||||
|
|
@ -40,7 +41,7 @@ const ListsTimeline = {
|
|||
},
|
||||
unmounted() {
|
||||
this.$store.dispatch('stopFetchingTimeline', 'list')
|
||||
this.$store.commit('clearTimeline', { timeline: 'list' })
|
||||
useStatusesStore().clearTimeline({ timeline: 'list' })
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const PublicAndExternalTimeline = {
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
computed: {
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.publicAndExternal
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'publicAndExternal',
|
||||
})
|
||||
},
|
||||
unmounted() {
|
||||
this.$store.dispatch('stopFetchingTimeline', 'publicAndExternal')
|
||||
},
|
||||
}
|
||||
|
||||
export default PublicAndExternalTimeline
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.twkn')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'publicAndExternal'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./public_and_external_timeline.js"></script>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
const PublicTimeline = {
|
||||
components: {
|
||||
Timeline,
|
||||
},
|
||||
computed: {
|
||||
timeline() {
|
||||
return this.$store.state.statuses.timelines.public
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('startFetchingTimeline', { timeline: 'public' })
|
||||
},
|
||||
unmounted() {
|
||||
this.$store.dispatch('stopFetchingTimeline', 'public')
|
||||
},
|
||||
}
|
||||
|
||||
export default PublicTimeline
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<template>
|
||||
<Timeline
|
||||
:title="$t('nav.public_tl')"
|
||||
:timeline="timeline"
|
||||
:timeline-name="'public'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script src="./public_timeline.js"></script>
|
||||
|
|
@ -5,6 +5,7 @@ import Popover from 'src/components/popover/popover.vue'
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useLocalConfigStore } from 'src/stores/local_config.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
|
|
@ -27,7 +28,7 @@ const QuickFilterSettings = {
|
|||
path: 'replyVisibility',
|
||||
value: visibility,
|
||||
})
|
||||
this.$store.dispatch('queueFlushAll')
|
||||
useStatusesStore().queueFlushAll()
|
||||
},
|
||||
openTab(tab) {
|
||||
useInterfaceStore().openSettingsModalTab(tab)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
|
|
@ -45,7 +47,7 @@ export default {
|
|||
computed: {
|
||||
quotedStatus() {
|
||||
return this.statusId
|
||||
? this.$store.state.statuses.allStatusesObject[this.statusId]
|
||||
? useStatusesStore().allStatuses.get(this.statusId)
|
||||
: undefined
|
||||
},
|
||||
shouldDisplayQuote() {
|
||||
|
|
@ -79,8 +81,8 @@ export default {
|
|||
this.fetchAttempted = true
|
||||
this.fetching = true
|
||||
this.$emit('loading', true)
|
||||
this.$store
|
||||
.dispatch('fetchStatus', this.statusId)
|
||||
useStatusesStore()
|
||||
.fetchStatus(this.statusId)
|
||||
.then(() => {
|
||||
this.displayQuote = true
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
const QuotesTimeline = {
|
||||
created() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'quotes' })
|
||||
useStatusesStore().clearTimeline({ timeline: 'tag' })
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'quotes',
|
||||
statusId: this.statusId,
|
||||
|
|
@ -21,7 +23,7 @@ const QuotesTimeline = {
|
|||
},
|
||||
watch: {
|
||||
statusId() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'quotes' })
|
||||
useStatusesStore().clearTimeline({ timeline: 'tag' })
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'quotes',
|
||||
statusId: this.statusId,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import Conversation from 'src/components/conversation/conversation.vue'
|
|||
import FollowCard from 'src/components/follow_card/follow_card.vue'
|
||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
|
@ -39,11 +40,11 @@ const Search = {
|
|||
return this.userIds.map((userId) => useUsersStore().findUser(userId))
|
||||
},
|
||||
visibleStatuses() {
|
||||
const allStatusesObject = this.$store.state.statuses.allStatusesObject
|
||||
const allStatuses = useStatusesStore().allStatuses
|
||||
|
||||
return this.statuses.filter(
|
||||
(status) =>
|
||||
allStatusesObject[status.id] && !allStatusesObject[status.id].deleted,
|
||||
allStatuses.has(status.id) && !allStatuses.get(status.id).deleted,
|
||||
)
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import UnitSetting from '../helpers/unit_setting.vue'
|
|||
|
||||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
const ClutterTab = {
|
||||
components: {
|
||||
|
|
@ -35,7 +36,7 @@ const ClutterTab = {
|
|||
// Updating nested properties
|
||||
watch: {
|
||||
replyVisibility() {
|
||||
this.$store.dispatch('queueFlushAll')
|
||||
useStatusesStore().queueFlushAll()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import UnitSetting from '../helpers/unit_setting.vue'
|
|||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
|
||||
import {
|
||||
|
|
@ -265,7 +266,7 @@ const FilteringTab = {
|
|||
// Updating nested properties
|
||||
watch: {
|
||||
replyVisibility() {
|
||||
this.$store.dispatch('queueFlushAll')
|
||||
useStatusesStore().queueFlushAll()
|
||||
},
|
||||
muteFiltersObject() {
|
||||
this.muteFiltersDraftObject = cloneDeep(
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import {
|
|||
import { useInstanceStore } from 'src/stores/instance.js'
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
|
@ -206,7 +207,7 @@ const Status = {
|
|||
},
|
||||
statusFromGlobalRepository() {
|
||||
// NOTE: Consider to replace status with statusFromGlobalRepository
|
||||
return this.$store.state.statuses.allStatusesObject[this.status.id]
|
||||
return useStatusesStore().allStatuses.get(this.status.id)
|
||||
},
|
||||
loggedIn() {
|
||||
return !!this.currentUser
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { useInstanceStore } from 'src/stores/instance.js'
|
|||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useReportsStore } from 'src/stores/reports.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useStatusHistoryStore } from 'src/stores/statusHistory.js'
|
||||
|
||||
const PRIVATE_SCOPES = new Set(['private', 'direct'])
|
||||
|
|
@ -52,7 +53,7 @@ export const BUTTONS = [
|
|||
(currentUser.id === status.user.id ||
|
||||
!PRIVATE_SCOPES.has(status.visibility)),
|
||||
toggleable: true,
|
||||
confirm: ({ status, getters }) =>
|
||||
confirm: ({ status }) =>
|
||||
!status.repeated && useMergedConfigStore().mergedConfig.modalOnRepeat,
|
||||
confirmStrings: {
|
||||
title: 'status.repeat_confirm_title',
|
||||
|
|
@ -60,11 +61,11 @@ export const BUTTONS = [
|
|||
confirm: 'status.repeat_confirm_accept_button',
|
||||
cancel: 'status.repeat_confirm_cancel_button',
|
||||
},
|
||||
action({ status, dispatch }) {
|
||||
action({ status }) {
|
||||
if (!status.repeated) {
|
||||
return dispatch('retweet', { id: status.id })
|
||||
return useStatusesStore().retweet(status.id)
|
||||
} else {
|
||||
return dispatch('unretweet', { id: status.id })
|
||||
return useStatusesStore().unretweet(status.id)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -82,11 +83,11 @@ export const BUTTONS = [
|
|||
counter: ({ status }) => status.fave_num,
|
||||
anonLink: true,
|
||||
toggleable: true,
|
||||
action({ status, dispatch }) {
|
||||
action({ status }) {
|
||||
if (!status.favorited) {
|
||||
return dispatch('favorite', { id: status.id })
|
||||
return useStatusesStore().favorite(status.id)
|
||||
} else {
|
||||
return dispatch('unfavorite', { id: status.id })
|
||||
return useStatusesStore().unfavorite(status.id)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -112,7 +113,7 @@ export const BUTTONS = [
|
|||
if: ({ loggedIn }) => loggedIn,
|
||||
toggleable: false,
|
||||
dropdown: true,
|
||||
action({ status, dispatch, emit }) {
|
||||
action({ status, emit }) {
|
||||
/* prevent hiding */
|
||||
},
|
||||
},
|
||||
|
|
@ -130,11 +131,11 @@ export const BUTTONS = [
|
|||
PUBLIC_SCOPES.has(status.visibility)
|
||||
)
|
||||
},
|
||||
action({ status, dispatch }) {
|
||||
action({ status }) {
|
||||
if (status.pinned) {
|
||||
return dispatch('unpinStatus', status.id)
|
||||
return useStatusesStore().unpinStatus(status.id)
|
||||
} else {
|
||||
return dispatch('pinStatus', status.id)
|
||||
return useStatusesStore().pinStatus(status.id)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -150,11 +151,11 @@ export const BUTTONS = [
|
|||
label: ({ status }) =>
|
||||
status.bookmarked ? 'status.unbookmark' : 'status.bookmark',
|
||||
if: ({ loggedIn }) => loggedIn,
|
||||
action({ status, dispatch }) {
|
||||
action({ status }) {
|
||||
if (status.bookmarked) {
|
||||
return dispatch('unbookmark', { id: status.id })
|
||||
return useStatusesStore().unbookmark(status.id)
|
||||
} else {
|
||||
return dispatch('bookmark', { id: status.id })
|
||||
return useStatusesStore().bookmark(status.id)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -165,7 +166,7 @@ export const BUTTONS = [
|
|||
name: 'editHistory',
|
||||
icon: 'history',
|
||||
label: 'status.status_history',
|
||||
if({ status, state }) {
|
||||
if({ status }) {
|
||||
return (
|
||||
useInstanceCapabilitiesStore().editingAvailable &&
|
||||
status.edited_at !== null
|
||||
|
|
@ -196,26 +197,28 @@ export const BUTTONS = [
|
|||
name: 'edit',
|
||||
icon: 'pen',
|
||||
label: 'status.edit',
|
||||
if({ status, loggedIn, currentUser, state }) {
|
||||
if({ status, loggedIn, currentUser }) {
|
||||
return (
|
||||
loggedIn &&
|
||||
useInstanceCapabilitiesStore().editingAvailable &&
|
||||
status.user.id === currentUser.id
|
||||
)
|
||||
},
|
||||
action({ dispatch, status }) {
|
||||
return dispatch('fetchStatusSource', { id: status.id }).then((data) =>
|
||||
useEditStatusStore().openEditStatusModal({
|
||||
statusId: status.id,
|
||||
statusSubject: data.spoiler_text,
|
||||
statusText: data.text,
|
||||
statusIsSensitive: status.nsfw,
|
||||
statusPoll: status.poll,
|
||||
statusFiles: [...status.attachments],
|
||||
statusVisibility: status.visibility,
|
||||
statusContentType: data.content_type,
|
||||
}),
|
||||
)
|
||||
action({ status }) {
|
||||
return useStatusesStore()
|
||||
.fetchStatusSource(status.id)
|
||||
.then((data) =>
|
||||
useEditStatusStore().openEditStatusModal({
|
||||
statusId: status.id,
|
||||
statusSubject: data.spoiler_text,
|
||||
statusText: data.text,
|
||||
statusIsSensitive: status.nsfw,
|
||||
statusPoll: status.poll,
|
||||
statusFiles: [...status.attachments],
|
||||
statusVisibility: status.visibility,
|
||||
statusContentType: data.content_type,
|
||||
}),
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -260,15 +263,15 @@ export const BUTTONS = [
|
|||
currentUser.privileges.has('messages_delete'))
|
||||
)
|
||||
},
|
||||
confirm: ({ getters }) => useMergedConfigStore().mergedConfig.modalOnDelete,
|
||||
confirm: () => useMergedConfigStore().mergedConfig.modalOnDelete,
|
||||
confirmStrings: {
|
||||
title: 'status.delete_confirm_title',
|
||||
body: 'status.delete_confirm',
|
||||
confirm: 'status.delete_confirm_accept_button',
|
||||
cancel: 'status.delete_confirm_cancel_button',
|
||||
},
|
||||
action({ dispatch, status }) {
|
||||
return dispatch('deleteStatus', { id: status.id })
|
||||
action({ status }) {
|
||||
return useStatusesStore().deleteStatus(status.id)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -287,7 +290,7 @@ export const BUTTONS = [
|
|||
},
|
||||
toggleable: false,
|
||||
dropdown: true,
|
||||
action({ status, dispatch, emit }) {
|
||||
action({ status, emit }) {
|
||||
/* prevent hiding */
|
||||
},
|
||||
},
|
||||
|
|
@ -298,7 +301,7 @@ export const BUTTONS = [
|
|||
name: 'share',
|
||||
icon: 'share-alt',
|
||||
label: 'status.copy_link',
|
||||
action({ state, status, router }) {
|
||||
action({ status, router }) {
|
||||
navigator.clipboard.writeText(
|
||||
[
|
||||
useInstanceStore().server,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { get } from 'lodash'
|
|||
|
||||
import Modal from 'src/components/modal/modal.vue'
|
||||
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useStatusHistoryStore } from 'src/stores/statusHistory.js'
|
||||
|
||||
const StatusHistoryModal = {
|
||||
|
|
@ -50,9 +51,11 @@ const StatusHistoryModal = {
|
|||
this.statuses = []
|
||||
},
|
||||
fetchStatusHistory() {
|
||||
this.$store.dispatch('fetchStatusHistory', this.params).then((data) => {
|
||||
this.statuses = data
|
||||
})
|
||||
useStatusesStore()
|
||||
.fetchStatusHistory(this.params)
|
||||
.then((data) => {
|
||||
this.statuses = data
|
||||
})
|
||||
},
|
||||
closeModal() {
|
||||
useStatusHistoryStore().closeStatusHistoryModal()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { find } from 'lodash'
|
||||
|
||||
import Popover from 'src/components/popover/popover.vue'
|
||||
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ const StatusPopover = {
|
|||
},
|
||||
computed: {
|
||||
status() {
|
||||
return find(this.$store.state.statuses.allStatuses, { id: this.statusId })
|
||||
return useStatusesStore().allStatuses.get(this.statusId)
|
||||
},
|
||||
},
|
||||
components: {
|
||||
|
|
@ -30,8 +30,8 @@ const StatusPopover = {
|
|||
this.error = true
|
||||
return
|
||||
}
|
||||
this.$store
|
||||
.dispatch('fetchStatus', this.statusId)
|
||||
useStatusesStore()
|
||||
.fetchStatus(this.statusId)
|
||||
.then(() => (this.error = false))
|
||||
.catch(() => (this.error = true))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import Timeline from 'src/components/timeline/timeline.vue'
|
||||
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
const TagTimeline = {
|
||||
created() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'tag' })
|
||||
useStatusesStore().clearTimeline({ timeline: 'tag' })
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'tag',
|
||||
tag: this.tag,
|
||||
|
|
@ -21,7 +23,7 @@ const TagTimeline = {
|
|||
},
|
||||
watch: {
|
||||
tag() {
|
||||
this.$store.commit('clearTimeline', { timeline: 'tag' })
|
||||
useStatusesStore().clearTimeline({ timeline: 'tag' })
|
||||
this.$store.dispatch('startFetchingTimeline', {
|
||||
timeline: 'tag',
|
||||
tag: this.tag,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ 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 { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import timelineFetcher from 'src/services/timeline_fetcher/timeline_fetcher.service.js'
|
||||
|
|
@ -26,21 +27,19 @@ import {
|
|||
library.add(faCircleNotch, faCog, faMinus, faArrowUp, faCirclePlus, faCheck)
|
||||
|
||||
const Timeline = {
|
||||
props: [
|
||||
'timeline',
|
||||
'timelineName',
|
||||
'title',
|
||||
'userId',
|
||||
'listId',
|
||||
'statusId',
|
||||
'bookmarkFolderId',
|
||||
'tag',
|
||||
'embedded',
|
||||
'count',
|
||||
'pinnedStatusIds',
|
||||
'inProfile',
|
||||
'footerSlipgate', // reference to an element where we should put our footer
|
||||
],
|
||||
props: {
|
||||
timelineName: String,
|
||||
userId: String,
|
||||
listId: String,
|
||||
statusId: String,
|
||||
bookmarkFolderId: String,
|
||||
tag: String,
|
||||
embedded: Boolean,
|
||||
count: Number,
|
||||
pinnedStatusIds: Set,
|
||||
inProfile: Boolean,
|
||||
footerSlipgate: Object, // reference to an element where we should put our footer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showScrollTop: false,
|
||||
|
|
@ -59,8 +58,11 @@ const Timeline = {
|
|||
QuickViewSettings,
|
||||
},
|
||||
computed: {
|
||||
timeline() {
|
||||
return useStatusesStore().timelines[this.timelineName]
|
||||
},
|
||||
filteredVisibleStatuses() {
|
||||
return this.timeline.visibleStatuses.filter(
|
||||
return [...this.timeline.visibleStatuses.values()].filter(
|
||||
(status) =>
|
||||
this.timelineName !== 'user' ||
|
||||
(status.id >= this.timeline.minId &&
|
||||
|
|
@ -116,13 +118,13 @@ const Timeline = {
|
|||
return keyBy(this.pinnedStatusIds)
|
||||
},
|
||||
statusesToDisplay() {
|
||||
const amount = this.timeline.visibleStatuses.length
|
||||
const amount = this.timeline.visibleStatuses.size
|
||||
const statusesPerSide = Math.ceil(Math.max(3, window.innerHeight / 80))
|
||||
const nonPinnedIndex =
|
||||
this.virtualScrollIndex - this.filteredPinnedStatusIds.length
|
||||
const min = Math.max(0, nonPinnedIndex - statusesPerSide)
|
||||
const max = Math.min(amount, nonPinnedIndex + statusesPerSide)
|
||||
return this.timeline.visibleStatuses.slice(min, max).map((_) => _.id)
|
||||
return new Set([...this.timeline.visibleStatuses.keys()].slice(min, max))
|
||||
},
|
||||
virtualScrollingEnabled() {
|
||||
return useMergedConfigStore().mergedConfig.virtualScrolling
|
||||
|
|
@ -143,7 +145,6 @@ const Timeline = {
|
|||
}
|
||||
|
||||
timelineFetcher.fetchAndUpdate({
|
||||
store,
|
||||
credentials,
|
||||
timeline: this.timelineName,
|
||||
showImmediately,
|
||||
|
|
@ -175,7 +176,7 @@ const Timeline = {
|
|||
this.handleVisibilityChange,
|
||||
false,
|
||||
)
|
||||
this.$store.commit('setLoading', {
|
||||
useStatusesStore().setLoading({
|
||||
timeline: this.timelineName,
|
||||
value: false,
|
||||
})
|
||||
|
|
@ -197,30 +198,31 @@ const Timeline = {
|
|||
},
|
||||
showNewStatuses() {
|
||||
if (this.timeline.flushMarker !== 0) {
|
||||
this.$store.commit('clearTimeline', {
|
||||
useStatusesStore().clearTimeline({
|
||||
timeline: this.timelineName,
|
||||
excludeUserId: true,
|
||||
})
|
||||
this.$store.commit('queueFlush', { timeline: this.timelineName, id: 0 })
|
||||
useStatusesStore().queueFlush({ timeline: this.timelineName, id: 0 })
|
||||
if (this.timelineName === 'user') {
|
||||
this.$store.dispatch('fetchPinnedStatuses', this.userId)
|
||||
}
|
||||
this.fetchOlderStatuses()
|
||||
} else {
|
||||
this.blockClicksTemporarily()
|
||||
this.$store.commit('showNewStatuses', { timeline: this.timelineName })
|
||||
useStatusesStore().showNewStatuses(this.timelineName)
|
||||
this.paused = false
|
||||
}
|
||||
window.scrollTo({ top: 0 })
|
||||
},
|
||||
fetchOlderStatuses: throttle(
|
||||
function () {
|
||||
const store = this.$store
|
||||
const credentials = useUsersStore().currentUser.credentials
|
||||
store.commit('setLoading', { timeline: this.timelineName, value: true })
|
||||
useStatusesStore().setLoading({
|
||||
timeline: this.timelineName,
|
||||
value: true,
|
||||
})
|
||||
timelineFetcher
|
||||
.fetchAndUpdate({
|
||||
store,
|
||||
credentials,
|
||||
timeline: this.timelineName,
|
||||
older: true,
|
||||
|
|
@ -237,7 +239,7 @@ const Timeline = {
|
|||
}
|
||||
})
|
||||
.finally(() =>
|
||||
store.commit('setLoading', {
|
||||
useStatusesStore().setLoading({
|
||||
timeline: this.timelineName,
|
||||
value: false,
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@
|
|||
:status-id="status.id"
|
||||
:in-profile="inProfile"
|
||||
:profile-user-id="userId"
|
||||
:virtual-hidden="virtualScrollingEnabled && !statusesToDisplay.includes(status.id)"
|
||||
:virtual-hidden="virtualScrollingEnabled && !statusesToDisplay.has(status.id)"
|
||||
collapsable
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import UserCard from 'src/components/user_card/user_card.vue'
|
|||
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 { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
|
@ -106,7 +107,9 @@ const UserProfile = {
|
|||
const startFetchingTimeline = (timeline, userId) => {
|
||||
// Clear timeline only if load another user's profile
|
||||
if (userId !== this.$store.state.statuses.timelines[timeline].userId) {
|
||||
this.$store.commit('clearTimeline', { timeline })
|
||||
useStatusesStore().clearTimeline({ timeline: 'user' })
|
||||
useStatusesStore().clearTimeline({ timeline: 'userPinned' })
|
||||
useStatusesStore().clearTimeline({ timeline: 'media' })
|
||||
}
|
||||
this.$store.dispatch('startFetchingTimeline', { timeline, userId })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { useInterfaceStore } from 'src/stores/interface.js'
|
|||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useShoutStore } from 'src/stores/shout.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import { fetchTimeline } from 'src/api/timelines.js'
|
||||
import {
|
||||
|
|
@ -123,14 +124,16 @@ const api = {
|
|||
data: message.notification,
|
||||
})
|
||||
} else if (message.event === 'update') {
|
||||
dispatch('addNewStatuses', {
|
||||
useStatusesStore().addNewStatuses({
|
||||
timestamp: Date.now(),
|
||||
statuses: [message.status],
|
||||
userId: false,
|
||||
showImmediately: timelineData.visibleStatuses.length === 0,
|
||||
timeline: 'friends',
|
||||
})
|
||||
} else if (message.event === 'status.update') {
|
||||
dispatch('addNewStatuses', {
|
||||
useStatusesStore().addNewStatuses({
|
||||
timestamp: Date.now(),
|
||||
statuses: [message.status],
|
||||
userId: false,
|
||||
showImmediately:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import api from './api.js'
|
||||
import drafts from './drafts.js'
|
||||
import profileConfig from './profileConfig.js'
|
||||
import statuses from './statuses.js'
|
||||
|
||||
export default {
|
||||
statuses,
|
||||
api,
|
||||
profileConfig,
|
||||
drafts,
|
||||
|
|
|
|||
|
|
@ -1,906 +0,0 @@
|
|||
import {
|
||||
each,
|
||||
find,
|
||||
findIndex,
|
||||
first,
|
||||
last,
|
||||
maxBy,
|
||||
merge,
|
||||
minBy,
|
||||
omitBy,
|
||||
remove,
|
||||
} from 'lodash'
|
||||
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import {
|
||||
fetchEmojiReactions,
|
||||
fetchFavoritedByUsers,
|
||||
fetchPinnedStatuses,
|
||||
fetchRebloggedByUsers,
|
||||
fetchScrobbles,
|
||||
fetchStatus,
|
||||
fetchStatusHistory,
|
||||
fetchStatusSource,
|
||||
search2,
|
||||
} from 'src/api/public.js'
|
||||
import {
|
||||
bookmarkStatus,
|
||||
deleteStatus,
|
||||
favorite,
|
||||
muteConversation,
|
||||
pinOwnStatus,
|
||||
reactWithEmoji,
|
||||
retweet,
|
||||
unbookmarkStatus,
|
||||
unfavorite,
|
||||
unmuteConversation,
|
||||
unpinOwnStatus,
|
||||
unreactWithEmoji,
|
||||
unretweet,
|
||||
} from 'src/api/user.js'
|
||||
|
||||
const emptyTl = (userId = 0) => ({
|
||||
statuses: [],
|
||||
statusesObject: {},
|
||||
faves: [],
|
||||
visibleStatuses: [],
|
||||
visibleStatusesObject: {},
|
||||
newStatusCount: 0,
|
||||
maxId: '',
|
||||
minId: '',
|
||||
minVisibleId: 0,
|
||||
loading: false,
|
||||
followers: [],
|
||||
friends: [],
|
||||
userId,
|
||||
flushMarker: 0,
|
||||
})
|
||||
|
||||
export const defaultState = () => ({
|
||||
allStatuses: [],
|
||||
scrobblesNextFetch: {},
|
||||
allStatusesObject: {},
|
||||
conversationsObject: {},
|
||||
maxId: '',
|
||||
favorites: new Set(),
|
||||
timelines: {
|
||||
mentions: emptyTl(),
|
||||
public: emptyTl(),
|
||||
user: emptyTl(),
|
||||
favorites: emptyTl(),
|
||||
media: emptyTl(),
|
||||
publicAndExternal: emptyTl(),
|
||||
friends: emptyTl(),
|
||||
tag: emptyTl(),
|
||||
dms: emptyTl(),
|
||||
bookmarks: emptyTl(),
|
||||
list: emptyTl(),
|
||||
bubble: emptyTl(),
|
||||
},
|
||||
})
|
||||
|
||||
export const prepareStatus = (status) => {
|
||||
// Set deleted flag
|
||||
status.deleted = false
|
||||
|
||||
// To make the array reactive
|
||||
status.attachments = status.attachments || []
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
const mergeOrAdd = (arr, obj, item) => {
|
||||
const oldItem = obj[item.id]
|
||||
|
||||
if (oldItem) {
|
||||
// We already have this, so only merge the new info.
|
||||
// We ignore null values to avoid overwriting existing properties with missing data
|
||||
// we also skip 'user' because that is handled by users module
|
||||
merge(
|
||||
oldItem,
|
||||
omitBy(item, (v, k) => v === null || k === 'user'),
|
||||
)
|
||||
// Reactivity fix.
|
||||
oldItem.attachments.splice(oldItem.attachments.length)
|
||||
return { item: oldItem, new: false }
|
||||
} else {
|
||||
// This is a new item, prepare it
|
||||
prepareStatus(item)
|
||||
arr.push(item)
|
||||
obj[item.id] = item
|
||||
return { item, new: true }
|
||||
}
|
||||
}
|
||||
|
||||
const sortById = (a, b) => {
|
||||
const seqA = Number(a.id)
|
||||
const seqB = Number(b.id)
|
||||
const isSeqA = !Number.isNaN(seqA)
|
||||
const isSeqB = !Number.isNaN(seqB)
|
||||
if (isSeqA && isSeqB) {
|
||||
return seqA > seqB ? -1 : 1
|
||||
} else if (isSeqA && !isSeqB) {
|
||||
return 1
|
||||
} else if (!isSeqA && isSeqB) {
|
||||
return -1
|
||||
} else {
|
||||
return a.id > b.id ? -1 : 1
|
||||
}
|
||||
}
|
||||
|
||||
const sortTimeline = (timeline) => {
|
||||
timeline.visibleStatuses = timeline.visibleStatuses.sort(sortById)
|
||||
timeline.statuses = timeline.statuses.sort(sortById)
|
||||
timeline.minVisibleId = last(timeline.visibleStatuses)?.id
|
||||
return timeline
|
||||
}
|
||||
|
||||
const getLatestScrobble = (state, user) => {
|
||||
const scrobblesSupport =
|
||||
useInstanceCapabilitiesStore().pleromaScrobblesAvailable
|
||||
|
||||
if (!scrobblesSupport || !user.name || user.id === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
state.scrobblesNextFetch[user.id] &&
|
||||
state.scrobblesNextFetch[user.id] > Date.now()
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
state.scrobblesNextFetch[user.id] = Date.now() + 24 * 60 * 60 * 1000
|
||||
if (!scrobblesSupport) return
|
||||
fetchScrobbles({ accountId: user.id })
|
||||
.then(({ data: scrobbles }) => {
|
||||
if (scrobbles?.error) {
|
||||
useInstanceCapabilitiesStore().set('pleromaScrobblesAvailable', false)
|
||||
return
|
||||
}
|
||||
|
||||
if (scrobbles.length > 0) {
|
||||
user.latestScrobble = scrobbles[0]
|
||||
|
||||
state.scrobblesNextFetch[user.id] = Date.now() + 60 * 1000
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.warn('cannot fetch scrobbles', e)
|
||||
})
|
||||
}
|
||||
|
||||
// Add status to the global storages (arrays and objects maintaining statuses) except timelines
|
||||
const addStatusToGlobalStorage = (state, data) => {
|
||||
getLatestScrobble(state, data.user)
|
||||
const result = mergeOrAdd(state.allStatuses, state.allStatusesObject, data)
|
||||
if (result.new) {
|
||||
// Add to conversation
|
||||
const status = result.item
|
||||
const conversationsObject = state.conversationsObject
|
||||
const conversationId = status.statusnet_conversation_id
|
||||
if (conversationsObject[conversationId]) {
|
||||
conversationsObject[conversationId].push(status)
|
||||
} else {
|
||||
conversationsObject[conversationId] = [status]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const addNewStatuses = (
|
||||
state,
|
||||
{
|
||||
statuses,
|
||||
showImmediately = false,
|
||||
timeline,
|
||||
user = {},
|
||||
noIdUpdate = false,
|
||||
userId,
|
||||
pagination = {},
|
||||
},
|
||||
) => {
|
||||
// Sanity check
|
||||
if (!Array.isArray(statuses)) {
|
||||
return false
|
||||
}
|
||||
|
||||
const allStatuses = state.allStatuses
|
||||
const timelineObject = state.timelines[timeline]
|
||||
|
||||
// Mismatch between API pagination and our internal minId/maxId tracking systems:
|
||||
// pagination.maxId is the oldest of the returned statuses when fetching older,
|
||||
// and pagination.minId is the newest when fetching newer. The names come directly
|
||||
// from the arguments they're supposed to be passed as for the next fetch.
|
||||
const minNew =
|
||||
pagination.maxId || (statuses.length > 0 ? minBy(statuses, 'id').id : 0)
|
||||
const maxNew =
|
||||
pagination.minId || (statuses.length > 0 ? maxBy(statuses, 'id').id : 0)
|
||||
|
||||
const newer =
|
||||
timeline &&
|
||||
(maxNew > timelineObject.maxId || timelineObject.maxId === '') &&
|
||||
statuses.length > 0
|
||||
const older =
|
||||
timeline &&
|
||||
(minNew < timelineObject.minId || timelineObject.minId === '') &&
|
||||
statuses.length > 0
|
||||
|
||||
if (!noIdUpdate && newer) {
|
||||
timelineObject.maxId = maxNew
|
||||
}
|
||||
if (!noIdUpdate && older) {
|
||||
timelineObject.minId = minNew
|
||||
}
|
||||
|
||||
// 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 (
|
||||
(timeline === 'user' || timeline === 'media') &&
|
||||
timelineObject.userId !== userId
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const addStatus = (data, showImmediately, addToTimeline = true) => {
|
||||
const result = addStatusToGlobalStorage(state, data)
|
||||
const status = result.item
|
||||
|
||||
if (result.new) {
|
||||
// We are mentioned in a post
|
||||
if (
|
||||
status.type === 'status' &&
|
||||
find(status.attentions, { id: user.id })
|
||||
) {
|
||||
const mentions = state.timelines.mentions
|
||||
|
||||
// Add the mention to the mentions timeline
|
||||
if (timelineObject !== mentions) {
|
||||
mergeOrAdd(mentions.statuses, mentions.statusesObject, status)
|
||||
mentions.newStatusCount += 1
|
||||
|
||||
sortTimeline(mentions)
|
||||
}
|
||||
}
|
||||
if (status.visibility === 'direct') {
|
||||
const dms = state.timelines.dms
|
||||
|
||||
mergeOrAdd(dms.statuses, dms.statusesObject, status)
|
||||
dms.newStatusCount += 1
|
||||
|
||||
sortTimeline(dms)
|
||||
}
|
||||
}
|
||||
|
||||
// Decide if we should treat the status as new for this timeline.
|
||||
let resultForCurrentTimeline
|
||||
// Some statuses should only be added to the global status repository.
|
||||
if (timeline && addToTimeline) {
|
||||
resultForCurrentTimeline = mergeOrAdd(
|
||||
timelineObject.statuses,
|
||||
timelineObject.statusesObject,
|
||||
status,
|
||||
)
|
||||
}
|
||||
|
||||
if (timeline && showImmediately) {
|
||||
// Add it directly to the visibleStatuses, don't change
|
||||
// newStatusCount
|
||||
mergeOrAdd(
|
||||
timelineObject.visibleStatuses,
|
||||
timelineObject.visibleStatusesObject,
|
||||
status,
|
||||
)
|
||||
} else if (timeline && addToTimeline && resultForCurrentTimeline.new) {
|
||||
// Just change newStatuscount
|
||||
timelineObject.newStatusCount += 1
|
||||
}
|
||||
|
||||
if (status.quote) {
|
||||
addStatus(
|
||||
status.quote,
|
||||
/* showImmediately = */ false,
|
||||
/* addToTimeline = */ false,
|
||||
)
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
const favoriteStatus = (favorite) => {
|
||||
const status = find(allStatuses, { id: favorite.in_reply_to_status_id })
|
||||
if (status) {
|
||||
// This is our favorite, so the relevant bit.
|
||||
if (favorite.user.id === user.id) {
|
||||
status.favorited = true
|
||||
} else {
|
||||
status.fave_num += 1
|
||||
}
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
const processors = {
|
||||
status: (status) => {
|
||||
addStatus(status, showImmediately)
|
||||
},
|
||||
edit: (status) => {
|
||||
addStatus(status, showImmediately)
|
||||
},
|
||||
retweet: (status) => {
|
||||
// RetweetedStatuses are never shown immediately
|
||||
const retweetedStatus = addStatus(status.retweeted_status, false, false)
|
||||
|
||||
let retweet
|
||||
// If the retweeted status is already there, don't add the retweet
|
||||
// to the timeline.
|
||||
if (
|
||||
timeline &&
|
||||
find(timelineObject.statuses, (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) => {
|
||||
// Only update if this is a new favorite.
|
||||
// Ignore our own favorites because we get info about likes as response to like request
|
||||
if (!state.favorites.has(favorite.id)) {
|
||||
state.favorites.add(favorite.id)
|
||||
favoriteStatus(favorite)
|
||||
}
|
||||
},
|
||||
follow: () => {
|
||||
// NOOP, it is known status but we don't do anything about it for now
|
||||
},
|
||||
default: (unknown) => {
|
||||
console.warn('unknown status type', unknown)
|
||||
},
|
||||
}
|
||||
|
||||
each(statuses, (status) => {
|
||||
const type = status.type
|
||||
const processor = processors[type] || processors.default
|
||||
processor(status)
|
||||
})
|
||||
|
||||
// Keep the visible statuses sorted
|
||||
if (timeline && !(timeline === 'bookmarks')) {
|
||||
sortTimeline(timelineObject)
|
||||
}
|
||||
}
|
||||
|
||||
const removeStatus = (state, { timeline, userId }) => {
|
||||
const timelineObject = state.timelines[timeline]
|
||||
if (userId) {
|
||||
remove(timelineObject.statuses, { user: { id: userId } })
|
||||
remove(timelineObject.visibleStatuses, { user: { id: userId } })
|
||||
timelineObject.minVisibleId =
|
||||
timelineObject.visibleStatuses.length > 0
|
||||
? last(timelineObject.visibleStatuses).id
|
||||
: 0
|
||||
timelineObject.maxId =
|
||||
timelineObject.statuses.length > 0 ? first(timelineObject.statuses).id : 0
|
||||
}
|
||||
}
|
||||
|
||||
export const mutations = {
|
||||
addNewStatuses,
|
||||
removeStatus,
|
||||
showNewStatuses(state, { timeline }) {
|
||||
const oldTimeline = state.timelines[timeline]
|
||||
|
||||
oldTimeline.newStatusCount = 0
|
||||
oldTimeline.visibleStatuses = oldTimeline.statuses.slice(0, 50)
|
||||
oldTimeline.minVisibleId = last(oldTimeline.visibleStatuses).id
|
||||
oldTimeline.minId = oldTimeline.minVisibleId
|
||||
oldTimeline.visibleStatusesObject = {}
|
||||
each(oldTimeline.visibleStatuses, (status) => {
|
||||
oldTimeline.visibleStatusesObject[status.id] = status
|
||||
})
|
||||
},
|
||||
resetStatuses(state) {
|
||||
const emptyState = defaultState()
|
||||
Object.entries(emptyState).forEach(([key, value]) => {
|
||||
state[key] = value
|
||||
})
|
||||
},
|
||||
clearTimeline(state, { timeline, excludeUserId = false }) {
|
||||
const userId = excludeUserId ? state.timelines[timeline].userId : undefined
|
||||
state.timelines[timeline] = emptyTl(userId)
|
||||
},
|
||||
setFavorited(state, { status, value }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
|
||||
if (newStatus.favorited !== value) {
|
||||
if (value) {
|
||||
newStatus.fave_num++
|
||||
} else {
|
||||
newStatus.fave_num--
|
||||
}
|
||||
}
|
||||
|
||||
newStatus.favorited = value
|
||||
},
|
||||
setFavoritedConfirm(state, { status, user }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
newStatus.favorited = status.favorited
|
||||
newStatus.fave_num = status.fave_num
|
||||
const index = findIndex(newStatus.favoritedBy, { id: user.id })
|
||||
if (index !== -1 && !newStatus.favorited) {
|
||||
newStatus.favoritedBy.splice(index, 1)
|
||||
} else if (index === -1 && newStatus.favorited) {
|
||||
newStatus.favoritedBy.push(user)
|
||||
}
|
||||
},
|
||||
setMutedStatus(state, status) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
newStatus.thread_muted = status.thread_muted
|
||||
|
||||
if (newStatus.thread_muted !== undefined) {
|
||||
state.conversationsObject[newStatus.statusnet_conversation_id].forEach(
|
||||
(status) => {
|
||||
status.thread_muted = newStatus.thread_muted
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
setRetweeted(state, { status, value }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
|
||||
if (newStatus.repeated !== value) {
|
||||
if (value) {
|
||||
newStatus.repeat_num++
|
||||
} else {
|
||||
newStatus.repeat_num--
|
||||
}
|
||||
}
|
||||
|
||||
newStatus.repeated = value
|
||||
},
|
||||
setRetweetedConfirm(state, { status, user }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
newStatus.repeated = status.repeated
|
||||
newStatus.repeat_num = status.repeat_num
|
||||
const index = findIndex(newStatus.rebloggedBy, { id: user.id })
|
||||
if (index !== -1 && !newStatus.repeated) {
|
||||
newStatus.rebloggedBy.splice(index, 1)
|
||||
} else if (index === -1 && newStatus.repeated) {
|
||||
newStatus.rebloggedBy.push(user)
|
||||
}
|
||||
},
|
||||
setBookmarked(state, { status, value }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
newStatus.bookmarked = value
|
||||
newStatus.bookmark_folder_id = status.bookmark_folder_id
|
||||
},
|
||||
setBookmarkedConfirm(state, { status }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
newStatus.bookmarked = status.bookmarked
|
||||
if (status.pleroma)
|
||||
newStatus.bookmark_folder_id = status.pleroma.bookmark_folder
|
||||
},
|
||||
setDeleted(state, { status }) {
|
||||
const newStatus = state.allStatusesObject[status.id]
|
||||
if (newStatus) newStatus.deleted = true
|
||||
},
|
||||
setManyDeleted(state, condition) {
|
||||
Object.values(state.allStatusesObject).forEach((status) => {
|
||||
if (condition(status)) {
|
||||
status.deleted = true
|
||||
}
|
||||
})
|
||||
},
|
||||
setLoading(state, { timeline, value }) {
|
||||
state.timelines[timeline].loading = value
|
||||
},
|
||||
setNsfw(state, { id, nsfw }) {
|
||||
const newStatus = state.allStatusesObject[id]
|
||||
newStatus.nsfw = nsfw
|
||||
},
|
||||
queueFlush(state, { timeline, id }) {
|
||||
state.timelines[timeline].flushMarker = id
|
||||
},
|
||||
queueFlushAll(state) {
|
||||
Object.keys(state.timelines).forEach((timeline) => {
|
||||
state.timelines[timeline].flushMarker = state.timelines[timeline].maxId
|
||||
})
|
||||
},
|
||||
addRepeats(state, { id, rebloggedByUsers, currentUser }) {
|
||||
const newStatus = state.allStatusesObject[id]
|
||||
newStatus.rebloggedBy = rebloggedByUsers.filter(Boolean)
|
||||
// repeats stats can be incorrect based on polling condition, let's update them using the most recent data
|
||||
newStatus.repeat_num = newStatus.rebloggedBy.length
|
||||
newStatus.repeated = !!newStatus.rebloggedBy.find(
|
||||
({ id }) => currentUser.id === id,
|
||||
)
|
||||
},
|
||||
addFavs(state, { id, favoritedByUsers, currentUser }) {
|
||||
const newStatus = state.allStatusesObject[id]
|
||||
newStatus.favoritedBy = favoritedByUsers.filter(Boolean)
|
||||
// favorites stats can be incorrect based on polling condition, let's update them using the most recent data
|
||||
newStatus.fave_num = newStatus.favoritedBy.length
|
||||
newStatus.favorited = !!newStatus.favoritedBy.find(
|
||||
({ id }) => currentUser.id === id,
|
||||
)
|
||||
},
|
||||
addEmojiReactionsBy(state, { id, emojiReactions }) {
|
||||
const status = state.allStatusesObject[id]
|
||||
status.emoji_reactions = emojiReactions
|
||||
},
|
||||
addOwnReaction(state, { id, emoji, currentUser }) {
|
||||
const status = state.allStatusesObject[id]
|
||||
const reactionIndex = findIndex(status.emoji_reactions, { name: emoji })
|
||||
const reaction = status.emoji_reactions[reactionIndex] || {
|
||||
name: emoji,
|
||||
count: 0,
|
||||
accounts: [],
|
||||
}
|
||||
|
||||
const newReaction = {
|
||||
...reaction,
|
||||
count: reaction.count + 1,
|
||||
me: true,
|
||||
accounts: [...reaction.accounts, currentUser],
|
||||
}
|
||||
|
||||
// Update count of existing reaction if it exists, otherwise append at the end
|
||||
if (reactionIndex >= 0) {
|
||||
status.emoji_reactions[reactionIndex] = newReaction
|
||||
} else {
|
||||
status.emoji_reactions = [...status.emoji_reactions, newReaction]
|
||||
}
|
||||
},
|
||||
removeOwnReaction(state, { id, emoji, currentUser }) {
|
||||
const status = state.allStatusesObject[id]
|
||||
const reactionIndex = findIndex(status.emoji_reactions, { name: emoji })
|
||||
if (reactionIndex < 0) return
|
||||
|
||||
const reaction = status.emoji_reactions[reactionIndex]
|
||||
const accounts = reaction.accounts || []
|
||||
|
||||
const newReaction = {
|
||||
...reaction,
|
||||
count: reaction.count - 1,
|
||||
me: false,
|
||||
accounts: accounts.filter((acc) => acc.id !== currentUser.id),
|
||||
}
|
||||
|
||||
if (newReaction.count > 0) {
|
||||
status.emoji_reactions[reactionIndex] = newReaction
|
||||
} else {
|
||||
status.emoji_reactions = status.emoji_reactions.filter(
|
||||
(r) => r.name !== emoji,
|
||||
)
|
||||
}
|
||||
},
|
||||
updateStatusWithPoll(state, { id, poll }) {
|
||||
const status = state.allStatusesObject[id]
|
||||
status.poll = poll
|
||||
},
|
||||
setVirtualHeight(state, { statusId, height }) {
|
||||
state.allStatusesObject[statusId].virtualHeight = height
|
||||
},
|
||||
}
|
||||
|
||||
const statuses = {
|
||||
state: defaultState(),
|
||||
actions: {
|
||||
addNewStatuses(
|
||||
{ rootState, commit },
|
||||
{
|
||||
statuses,
|
||||
showImmediately = false,
|
||||
timeline = false,
|
||||
noIdUpdate = false,
|
||||
userId,
|
||||
pagination,
|
||||
},
|
||||
) {
|
||||
return commit('addNewStatuses', {
|
||||
statuses,
|
||||
showImmediately,
|
||||
timeline,
|
||||
noIdUpdate,
|
||||
user: useUsersStore().currentUser,
|
||||
userId,
|
||||
pagination,
|
||||
})
|
||||
},
|
||||
fetchStatus({ rootState, dispatch }, id) {
|
||||
return fetchStatus({ id }).then(({ data: status }) =>
|
||||
dispatch('addNewStatuses', { statuses: [status] }),
|
||||
)
|
||||
},
|
||||
fetchStatusSource({ rootState }, status) {
|
||||
return fetchStatusSource({
|
||||
id: status.id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data }) => data)
|
||||
},
|
||||
fetchStatusHistory(_, status) {
|
||||
return fetchStatusHistory({ status }).then(({ data }) => data)
|
||||
},
|
||||
deleteStatus({ rootState, commit }, status) {
|
||||
deleteStatus({
|
||||
id: status.id,
|
||||
credentials: useOAuthStore().token,
|
||||
})
|
||||
.then(() => {
|
||||
commit('setDeleted', { status })
|
||||
})
|
||||
.catch((e) => {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'error',
|
||||
messageKey: 'status.delete_error',
|
||||
messageArgs: [e.message],
|
||||
timeout: 5000,
|
||||
})
|
||||
})
|
||||
},
|
||||
deleteStatusById({ rootState, commit }, id) {
|
||||
const status = rootState.statuses.allStatusesObject[id]
|
||||
commit('setDeleted', { status })
|
||||
},
|
||||
markStatusesAsDeleted({ commit }, condition) {
|
||||
commit('setManyDeleted', condition)
|
||||
},
|
||||
favorite({ rootState, commit }, status) {
|
||||
// Optimistic favoriting...
|
||||
commit('setFavorited', { status, value: true })
|
||||
favorite({
|
||||
id: status.id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status }) =>
|
||||
commit('setFavoritedConfirm', {
|
||||
status,
|
||||
user: useUsersStore().currentUser,
|
||||
}),
|
||||
)
|
||||
},
|
||||
unfavorite({ rootState, commit }, status) {
|
||||
// Optimistic unfavoriting...
|
||||
commit('setFavorited', { status, value: false })
|
||||
unfavorite({
|
||||
id: status.id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status }) =>
|
||||
commit('setFavoritedConfirm', {
|
||||
status,
|
||||
user: useUsersStore().currentUser,
|
||||
}),
|
||||
)
|
||||
},
|
||||
fetchPinnedStatuses({ rootState, dispatch }, userId) {
|
||||
fetchPinnedStatuses({
|
||||
id: userId,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: statuses }) =>
|
||||
dispatch('addNewStatuses', {
|
||||
statuses,
|
||||
timeline: 'user',
|
||||
userId,
|
||||
showImmediately: true,
|
||||
noIdUpdate: true,
|
||||
}),
|
||||
)
|
||||
},
|
||||
pinStatus({ rootState, dispatch }, statusId) {
|
||||
return pinOwnStatus({
|
||||
id: statusId,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status }) =>
|
||||
dispatch('addNewStatuses', { statuses: [status] }),
|
||||
)
|
||||
},
|
||||
unpinStatus({ rootState, dispatch }, statusId) {
|
||||
return unpinOwnStatus({
|
||||
id: statusId,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status }) =>
|
||||
dispatch('addNewStatuses', { statuses: [status] }),
|
||||
)
|
||||
},
|
||||
muteConversation({ rootState, commit }, { id: statusId }) {
|
||||
return muteConversation({
|
||||
id: statusId,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status }) => commit('setMutedStatus', status))
|
||||
},
|
||||
unmuteConversation({ rootState, commit }, { id: statusId }) {
|
||||
return unmuteConversation({
|
||||
id: statusId,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status }) => commit('setMutedStatus', status))
|
||||
},
|
||||
retweet({ rootState, commit }, status) {
|
||||
// Optimistic retweeting...
|
||||
commit('setRetweeted', { status, value: true })
|
||||
retweet({
|
||||
id: status.id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status }) =>
|
||||
commit('setRetweetedConfirm', {
|
||||
status: status.retweeted_status,
|
||||
user: useUsersStore().currentUser,
|
||||
}),
|
||||
)
|
||||
},
|
||||
unretweet({ rootState, commit }, status) {
|
||||
// Optimistic unretweeting...
|
||||
commit('setRetweeted', { status, value: false })
|
||||
unretweet({
|
||||
id: status.id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status }) =>
|
||||
commit('setRetweetedConfirm', {
|
||||
status,
|
||||
user: useUsersStore().currentUser,
|
||||
}),
|
||||
)
|
||||
},
|
||||
bookmark({ rootState, commit }, status) {
|
||||
commit('setBookmarked', { status, value: true })
|
||||
bookmarkStatus({
|
||||
id: status.id,
|
||||
folder_id: status.bookmark_folder_id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status }) => {
|
||||
commit('setBookmarkedConfirm', { status })
|
||||
})
|
||||
},
|
||||
unbookmark({ rootState, commit }, status) {
|
||||
commit('setBookmarked', { status, value: false })
|
||||
unbookmarkStatus({
|
||||
id: status.id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status }) => {
|
||||
commit('setBookmarkedConfirm', { status })
|
||||
})
|
||||
},
|
||||
queueFlush({ commit }, { timeline, id }) {
|
||||
commit('queueFlush', { timeline, id })
|
||||
},
|
||||
queueFlushAll({ commit }) {
|
||||
commit('queueFlushAll')
|
||||
},
|
||||
fetchFavsAndRepeats({ rootState, commit }, id) {
|
||||
Promise.all([
|
||||
fetchFavoritedByUsers({
|
||||
id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data }) => data),
|
||||
fetchRebloggedByUsers({
|
||||
id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data }) => data),
|
||||
]).then(([favoritedByUsers, rebloggedByUsers]) => {
|
||||
commit('addFavs', {
|
||||
id,
|
||||
favoritedByUsers,
|
||||
currentUser: useUsersStore().currentUser,
|
||||
})
|
||||
commit('addRepeats', {
|
||||
id,
|
||||
rebloggedByUsers,
|
||||
currentUser: useUsersStore().currentUser,
|
||||
})
|
||||
})
|
||||
},
|
||||
reactWithEmoji({ rootState, dispatch, commit }, { id, emoji }) {
|
||||
const currentUser = useUsersStore().currentUser
|
||||
if (!currentUser) return
|
||||
|
||||
commit('addOwnReaction', { id, emoji, currentUser })
|
||||
reactWithEmoji({
|
||||
id,
|
||||
emoji,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(() => {
|
||||
dispatch('fetchEmojiReactionsBy', id)
|
||||
})
|
||||
},
|
||||
unreactWithEmoji({ rootState, dispatch, commit }, { id, emoji }) {
|
||||
const currentUser = useUsersStore().currentUser
|
||||
if (!currentUser) return
|
||||
|
||||
commit('removeOwnReaction', { id, emoji, currentUser })
|
||||
unreactWithEmoji({
|
||||
id,
|
||||
emoji,
|
||||
currentUser: useUsersStore().currentUser,
|
||||
}).then(() => {
|
||||
dispatch('fetchEmojiReactionsBy', id)
|
||||
})
|
||||
},
|
||||
fetchEmojiReactionsBy({ rootState, commit }, id) {
|
||||
return fetchEmojiReactions({
|
||||
id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: emojiReactions }) => {
|
||||
commit('addEmojiReactionsBy', {
|
||||
id,
|
||||
emojiReactions,
|
||||
currentUser: useUsersStore().currentUser,
|
||||
})
|
||||
})
|
||||
},
|
||||
fetchFavs({ rootState, commit }, id) {
|
||||
fetchFavoritedByUsers({
|
||||
id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: favoritedByUsers }) =>
|
||||
commit('addFavs', {
|
||||
id,
|
||||
favoritedByUsers,
|
||||
currentUser: useUsersStore().currentUser,
|
||||
}),
|
||||
)
|
||||
},
|
||||
fetchRepeats({ rootState, commit }, id) {
|
||||
fetchRebloggedByUsers({
|
||||
id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: rebloggedByUsers }) =>
|
||||
commit('addRepeats', {
|
||||
id,
|
||||
rebloggedByUsers,
|
||||
currentUser: useUsersStore().currentUser,
|
||||
}),
|
||||
)
|
||||
},
|
||||
search(store, { q, resolve, limit, offset, following, type }) {
|
||||
return search2({
|
||||
q,
|
||||
resolve,
|
||||
limit,
|
||||
offset,
|
||||
following,
|
||||
type,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then((result) => {
|
||||
const { data, ...rest } = result
|
||||
useUsersStore().addNewUsers({
|
||||
...rest,
|
||||
data: data.accounts,
|
||||
})
|
||||
useUsersStore().addNewUsers({
|
||||
...rest,
|
||||
data: data.statuses.map((s) => s.user).filter(Boolean),
|
||||
})
|
||||
store.commit('addNewStatuses', {
|
||||
statuses: data.statuses,
|
||||
})
|
||||
|
||||
data.statuses = data.statuses.map(
|
||||
(s) => store.state.allStatusesObject[s.id],
|
||||
)
|
||||
return data
|
||||
})
|
||||
},
|
||||
setVirtualHeight({ commit }, { statusId, height }) {
|
||||
commit('setVirtualHeight', { statusId, height })
|
||||
},
|
||||
},
|
||||
mutations,
|
||||
}
|
||||
|
||||
export default statuses
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { map } from 'lodash'
|
||||
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import {
|
||||
|
|
@ -38,9 +39,10 @@ const postStatus = ({
|
|||
poll,
|
||||
preview,
|
||||
idempotencyKey,
|
||||
}).then(({ data }) => {
|
||||
}).then(({ data, timestamp }) => {
|
||||
if (!preview)
|
||||
store.dispatch('addNewStatuses', {
|
||||
useStatusesStore().addNewStatuses({
|
||||
timestamp,
|
||||
statuses: [data],
|
||||
timeline: 'friends',
|
||||
showImmediately: true,
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ 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 { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
import { fetchTimeline } from 'src/api/timelines.js'
|
||||
|
||||
const update = ({
|
||||
store,
|
||||
statuses,
|
||||
timeline,
|
||||
showImmediately,
|
||||
|
|
@ -20,8 +20,8 @@ const update = ({
|
|||
}) => {
|
||||
const ccTimeline = camelCase(timeline)
|
||||
|
||||
store.dispatch('addNewStatuses', {
|
||||
timeline: ccTimeline,
|
||||
useStatusesStore().addNewStatuses({
|
||||
timelineName: ccTimeline,
|
||||
userId,
|
||||
listId,
|
||||
statuses,
|
||||
|
|
@ -31,7 +31,6 @@ const update = ({
|
|||
}
|
||||
|
||||
const fetchAndUpdate = ({
|
||||
store,
|
||||
credentials,
|
||||
timeline = 'friends',
|
||||
older = false,
|
||||
|
|
@ -45,8 +44,7 @@ const fetchAndUpdate = ({
|
|||
sinceId,
|
||||
}) => {
|
||||
const args = { timeline, credentials }
|
||||
const rootState = store.rootState || store.state
|
||||
const timelineData = rootState.statuses.timelines[camelCase(timeline)]
|
||||
const timelineData = useStatusesStore().timelines[camelCase(timeline)]
|
||||
const { hideMutedPosts, replyVisibility } =
|
||||
useMergedConfigStore().mergedConfig
|
||||
const loggedIn = !!useUsersStore().currentUser
|
||||
|
|
@ -86,10 +84,9 @@ const fetchAndUpdate = ({
|
|||
!timelineData.loading &&
|
||||
numStatusesBeforeFetch > 0
|
||||
) {
|
||||
store.dispatch('queueFlush', { timeline, id: timelineData.maxId })
|
||||
useStatusesStore().queueFlush({ timeline, id: timelineData.maxId })
|
||||
}
|
||||
update({
|
||||
store,
|
||||
statuses,
|
||||
timeline,
|
||||
showImmediately,
|
||||
|
|
@ -116,23 +113,21 @@ const fetchAndUpdate = ({
|
|||
const startFetching = ({
|
||||
timeline = 'friends',
|
||||
credentials,
|
||||
store,
|
||||
userId,
|
||||
listId,
|
||||
statusId,
|
||||
bookmarkFolderId,
|
||||
tag,
|
||||
}) => {
|
||||
const rootState = store.rootState || store.state
|
||||
const timelineData = rootState.statuses.timelines[camelCase(timeline)]
|
||||
const showImmediately = timelineData.visibleStatuses.length === 0
|
||||
const timelineData = useStatusesStore().timelines[camelCase(timeline)]
|
||||
const showImmediately = timelineData.visibleStatuses.size === 0
|
||||
console.log(timeline)
|
||||
timelineData.userId = userId
|
||||
timelineData.listId = listId
|
||||
timelineData.bookmarkFolderId = bookmarkFolderId
|
||||
fetchAndUpdate({
|
||||
timeline,
|
||||
credentials,
|
||||
store,
|
||||
showImmediately,
|
||||
userId,
|
||||
listId,
|
||||
|
|
@ -144,7 +139,6 @@ const startFetching = ({
|
|||
fetchAndUpdate({
|
||||
timeline,
|
||||
credentials,
|
||||
store,
|
||||
userId,
|
||||
listId,
|
||||
statusId,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { cloneDeep, differenceWith, get, isEqual, set } from 'lodash'
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import {
|
||||
addNewEmojiFile,
|
||||
|
|
@ -362,6 +363,7 @@ export const useAdminSettingsStore = defineStore('adminSettings', {
|
|||
async fetchStatuses(opts) {
|
||||
const {
|
||||
data: { total, activities },
|
||||
timestamp,
|
||||
} = await listStatuses({
|
||||
credentials: useOAuthStore().token,
|
||||
opts,
|
||||
|
|
@ -369,7 +371,7 @@ export const useAdminSettingsStore = defineStore('adminSettings', {
|
|||
|
||||
const statuses = activities.map(parseStatus)
|
||||
|
||||
await window.vuex.dispatch('addNewStatuses', { statuses })
|
||||
useStatusesStore().addNewStatuses({ statuses, timestamp })
|
||||
|
||||
return {
|
||||
items: statuses,
|
||||
|
|
@ -377,13 +379,13 @@ export const useAdminSettingsStore = defineStore('adminSettings', {
|
|||
}
|
||||
},
|
||||
async changeStatusScope(opts) {
|
||||
const { data } = await changeStatusScope({
|
||||
const { data, timestamp } = await changeStatusScope({
|
||||
credentials: useOAuthStore().token,
|
||||
opts,
|
||||
})
|
||||
const status = parseStatus(data)
|
||||
|
||||
await window.vuex.dispatch('addNewStatuses', { statuses: [status] })
|
||||
useStatusesStore().addNewStatuses({ statuses: [status], timestamp })
|
||||
},
|
||||
|
||||
// Users stuff
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import { useI18nStore } from 'src/stores/i18n.js'
|
|||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useReportsStore } from 'src/stores/reports.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
|
|
@ -80,7 +81,7 @@ export const useNotificationsStore = defineStore('notifications', {
|
|||
)
|
||||
|
||||
// Synchronous commit to add all the statuses
|
||||
window.vuex.commit('addNewStatuses', {
|
||||
useStatusesStore().addNewStatuses({
|
||||
timestamp,
|
||||
statuses: statusNotifications.map(
|
||||
(notification) => notification.status,
|
||||
|
|
@ -90,7 +91,7 @@ export const useNotificationsStore = defineStore('notifications', {
|
|||
// Update references to statuses in notifications to ones in the store
|
||||
statusNotifications.forEach((notification) => {
|
||||
const id = notification.status.id
|
||||
const referenceStatus = window.vuex.state.statuses.allStatusesObject[id]
|
||||
const referenceStatus = useStatusesStore().allStatuses.get(id)
|
||||
|
||||
if (referenceStatus) {
|
||||
notification.status = referenceStatus
|
||||
|
|
@ -103,7 +104,7 @@ export const useNotificationsStore = defineStore('notifications', {
|
|||
}
|
||||
|
||||
if (notification.type === 'pleroma:emoji_reaction') {
|
||||
window.vuex.dispatch('fetchEmojiReactionsBy', notification.status.id)
|
||||
useStatusesStore().fetchEmojiReactionsBy(notification.status.id)
|
||||
}
|
||||
|
||||
// Only add a new notification if we don't have one for the same action
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { defineStore } from 'pinia'
|
|||
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import { setReportState } from 'src/api/admin.js'
|
||||
|
||||
|
|
@ -18,8 +19,8 @@ export const useReportsStore = defineStore('reports', {
|
|||
}),
|
||||
actions: {
|
||||
openUserReportingModal({ userId, statusIds = [] }) {
|
||||
const preTickedStatuses = statusIds.map(
|
||||
(id) => window.vuex.state.statuses.allStatusesObject[id],
|
||||
const preTickedStatuses = statusIds.map((id) =>
|
||||
useStatusesStore().allStatuses.get(id),
|
||||
)
|
||||
const preTickedIds = statusIds
|
||||
const statuses = preTickedStatuses.concat(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { each, first, last, maxBy, merge, minBy, omitBy, remove } from 'lodash'
|
||||
import { first, last, maxBy, minBy } from 'lodash'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
|
|
@ -33,7 +33,7 @@ import {
|
|||
unretweet,
|
||||
} from 'src/api/user.js'
|
||||
|
||||
const emptyTl = () => ({
|
||||
const emptyTl = (userId) => ({
|
||||
statuses: new Map(),
|
||||
faves: [],
|
||||
visibleStatuses: new Map(),
|
||||
|
|
@ -71,49 +71,6 @@ export const defaultState = () => ({
|
|||
},
|
||||
})
|
||||
|
||||
const mergeOrAdd = (map, status, timestamp) => {
|
||||
const existing = map.get(status.id)
|
||||
const oldTimestamp = this.timestamps.get(existing)
|
||||
|
||||
const { user: unused0, ...old } = existing ?? {}
|
||||
const { user: statusUser, ...neu } = status
|
||||
|
||||
const [user] = useUsersStore().addNewUsers({ data: statusUser, timestamp })
|
||||
|
||||
existing.user = user
|
||||
|
||||
// implicit: if oldTimestamp is undefined this will still be false
|
||||
if (oldTimestamp > timestamp) return [existing, false] // not overwriting old data with new
|
||||
|
||||
const newStatus = {
|
||||
...old,
|
||||
...neu,
|
||||
user,
|
||||
}
|
||||
|
||||
map.set(item.id, item)
|
||||
|
||||
this.timestamps.set(newStatus, timestamp)
|
||||
|
||||
return [map.get(item.id), true]
|
||||
}
|
||||
|
||||
const sortById = (a, b) => {
|
||||
const seqA = Number(a.id)
|
||||
const seqB = Number(b.id)
|
||||
const isSeqA = !Number.isNaN(seqA)
|
||||
const isSeqB = !Number.isNaN(seqB)
|
||||
if (isSeqA && isSeqB) {
|
||||
return seqA > seqB ? -1 : 1
|
||||
} else if (isSeqA && !isSeqB) {
|
||||
return 1
|
||||
} else if (!isSeqA && isSeqB) {
|
||||
return -1
|
||||
} else {
|
||||
return a.id > b.id ? -1 : 1
|
||||
}
|
||||
}
|
||||
|
||||
const getLatestScrobble = (user) => {
|
||||
const scrobblesSupport =
|
||||
useInstanceCapabilitiesStore().pleromaScrobblesAvailable
|
||||
|
|
@ -149,7 +106,6 @@ const getLatestScrobble = (user) => {
|
|||
})
|
||||
}
|
||||
|
||||
|
||||
const USER_TIMELINES = new Set(['user', 'userPinned', 'media'])
|
||||
|
||||
export const useStatusesStore = defineStore('statuses', {
|
||||
|
|
@ -160,8 +116,8 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
showImmediately = false,
|
||||
timelineName,
|
||||
user = {},
|
||||
userId,
|
||||
noIdUpdate = false,
|
||||
Id,
|
||||
pagination = {},
|
||||
timestamp,
|
||||
}) {
|
||||
|
|
@ -179,17 +135,14 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
// 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
|
||||
) {
|
||||
if (USER_TIMELINES.has(timelineName) && timeline.userId !== userId) {
|
||||
return
|
||||
}
|
||||
|
||||
const addStatus = (data, showImmediately, addToTimeline = true) => {
|
||||
getLatestScrobble(data.user)
|
||||
|
||||
const [status] = mergeOrAdd(this.allStatuses, data)
|
||||
const [status] = this.mergeOrAdd(this.allStatuses, data)
|
||||
|
||||
// Add to conversation
|
||||
const conversations = this.conversations
|
||||
|
|
@ -210,7 +163,7 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
|
||||
// Add the mention to the mentions timeline
|
||||
if (timeline !== mentions) {
|
||||
const [, isNew] = mergeOrAdd(mentions.statuses, status)
|
||||
const [, isNew] = this.mergeOrAdd(mentions.statuses, data)
|
||||
if (isNew) mentions.newStatusCount += 1
|
||||
}
|
||||
}
|
||||
|
|
@ -218,21 +171,23 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
if (status.visibility === 'direct') {
|
||||
const dms = this.timelines.dms
|
||||
|
||||
const [, isNew] = mergeOrAdd(dms.statuses, status)
|
||||
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] = mergeOrAdd(timeline.statuses, status)
|
||||
if (showImmediately) {
|
||||
// Add it directly to the visibleStatuses, don't change
|
||||
// newStatusCount
|
||||
timeline.visibleStatuses.add(status.id, status)
|
||||
} else {
|
||||
// Just change newStatuscount
|
||||
timeline.newStatusCount += 1
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -256,12 +211,17 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
},
|
||||
retweet: (status) => {
|
||||
// RetweetedStatuses are never shown immediately
|
||||
const retweetedStatus = addStatus(status.retweeted_status, false, false)
|
||||
const retweetedStatus = addStatus(
|
||||
status.retweeted_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 (
|
||||
[...(timeline?.statuses.values() ?? [])].some((s) => {
|
||||
if (s.retweeted_status) {
|
||||
return (
|
||||
s.id === retweetedStatus.id ||
|
||||
|
|
@ -313,6 +273,35 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
processor(status)
|
||||
})
|
||||
},
|
||||
mergeOrAdd(map, status, timestamp) {
|
||||
const existing = map.get(status.id) ?? {}
|
||||
const oldTimestamp = this.timestamps.get(existing)
|
||||
|
||||
const { user: unused0, ...old } = existing
|
||||
const { user: statusUser, ...neu } = status
|
||||
|
||||
const [user] = useUsersStore().addNewUsers({
|
||||
data: statusUser,
|
||||
timestamp,
|
||||
})
|
||||
|
||||
existing.user = user // reactive update in case we return old
|
||||
|
||||
// implicit: if oldTimestamp is undefined this will still be false
|
||||
if (oldTimestamp > timestamp) return [existing, false] // not overwriting old data with new
|
||||
|
||||
const newStatus = {
|
||||
...old,
|
||||
...neu,
|
||||
user,
|
||||
}
|
||||
|
||||
map.set(newStatus.id, newStatus)
|
||||
|
||||
this.timestamps.set(newStatus, timestamp)
|
||||
|
||||
return [map.get(newStatus.id), true]
|
||||
},
|
||||
fetchStatus(id) {
|
||||
return fetchStatus({ id }).then(({ data: status, timestamp }) =>
|
||||
this.addNewStatuses({ statuses: [status], timestamp }),
|
||||
|
|
@ -376,10 +365,7 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
)
|
||||
},
|
||||
fetchFavsAndRepeats(id) {
|
||||
return Promise.all([
|
||||
this.fetchFavs(id),
|
||||
this.fetchRepeats(id),
|
||||
])
|
||||
return Promise.all([this.fetchFavs(id), this.fetchRepeats(id)])
|
||||
},
|
||||
|
||||
// Updates
|
||||
|
|
@ -414,8 +400,8 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
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 === '')
|
||||
const newer = maxNew > timeline.maxId || timeline.maxId === ''
|
||||
const older = minNew < timeline.minId || timeline.minId === ''
|
||||
|
||||
if (newer) {
|
||||
timeline.maxId = maxNew
|
||||
|
|
@ -429,8 +415,10 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
|
||||
timeline.newStatusCount = 0
|
||||
|
||||
timeline.visibleStatuses = new Map([...timeline.statuses.entries()].slice(0, 50))
|
||||
timeline.minVisibleId = last(timeline.visibleStatuses).id
|
||||
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()])
|
||||
|
|
@ -442,9 +430,9 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
this[key] = value
|
||||
})
|
||||
},
|
||||
clearTimeline(state, { timeline, excludeUserId = false }) {
|
||||
const userId = excludeUserId ? state.timelines[timeline].userId : undefined
|
||||
state.timelines[timeline] = emptyTl(userId)
|
||||
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
|
||||
|
|
@ -650,8 +638,8 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
},
|
||||
setBookmarked({ id, value, bookmark_folder_id }) {
|
||||
const status = this.allStatuses.get(id)
|
||||
newStatus.bookmarked = value
|
||||
newStatus.bookmark_folder_id = value ? bookmark_folder_id : null
|
||||
status.bookmarked = value
|
||||
status.bookmark_folder_id = value ? bookmark_folder_id : null
|
||||
},
|
||||
|
||||
/// Mute
|
||||
|
|
@ -659,25 +647,29 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
return muteConversation({
|
||||
id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status, timestamp }) => {
|
||||
this.addNewStatuses({
|
||||
statuses: [status],
|
||||
timestamp,
|
||||
})
|
||||
.then(({ data: status, timestamp }) => {
|
||||
this.addNewStatuses({
|
||||
statuses: [status],
|
||||
timestamp,
|
||||
})
|
||||
return status
|
||||
})
|
||||
return status
|
||||
}).then((status) => this.setMutedStatus(status))
|
||||
.then((status) => this.setMutedStatus(status))
|
||||
},
|
||||
unmuteConversation(id) {
|
||||
return unmuteConversation({
|
||||
id,
|
||||
credentials: useOAuthStore().token,
|
||||
}).then(({ data: status, timestamp }) => {
|
||||
this.addNewStatuses({
|
||||
statuses: [status],
|
||||
timestamp,
|
||||
})
|
||||
.then(({ data: status, timestamp }) => {
|
||||
this.addNewStatuses({
|
||||
statuses: [status],
|
||||
timestamp,
|
||||
})
|
||||
return status
|
||||
})
|
||||
return status
|
||||
}).then((status) => this.setMutedStatus(status))
|
||||
.then((status) => this.setMutedStatus(status))
|
||||
},
|
||||
setMutedStatus({ id, thread_muted }) {
|
||||
// Setting thread_muted flag on all other known statuses
|
||||
|
|
@ -686,11 +678,11 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
newStatus.thread_muted = thread_muted
|
||||
|
||||
if (newStatus.thread_muted !== undefined) {
|
||||
state.conversations.get(newStatus.statusnet_conversation_id).forEach(
|
||||
(status) => {
|
||||
this.conversations
|
||||
.get(newStatus.statusnet_conversation_id)
|
||||
.forEach((status) => {
|
||||
status.thread_muted = thread_muted
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -774,9 +766,7 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
statuses: data.statuses,
|
||||
})
|
||||
|
||||
data.statuses = data.statuses.map(
|
||||
(s) => this.allStatuses.get(s.id),
|
||||
)
|
||||
data.statuses = data.statuses.map((s) => this.allStatuses.get(s.id))
|
||||
return data
|
||||
})
|
||||
},
|
||||
|
|
@ -785,8 +775,7 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
removeUserStatuses({ timelineName, userId }) {
|
||||
const timeline = this.timelines[timelineName]
|
||||
|
||||
timeline
|
||||
.statuses
|
||||
timeline.statuses
|
||||
.values()
|
||||
.filter(({ user }) => user.id === userId)
|
||||
.forEach(({ id }) => {
|
||||
|
|
@ -795,8 +784,8 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
})
|
||||
timeline.minVisibleId =
|
||||
timeline.visibleStatuses.length > 0
|
||||
? last(timeline.visibleStatuses).id
|
||||
: 0
|
||||
? last(timeline.visibleStatuses).id
|
||||
: 0
|
||||
timeline.maxId =
|
||||
timeline.statuses.length > 0 ? first(timeline.statuses).id : 0
|
||||
},
|
||||
|
|
@ -807,8 +796,8 @@ export const useStatusesStore = defineStore('statuses', {
|
|||
const status = this.allStatuses.get(id)
|
||||
status.poll = poll
|
||||
},
|
||||
setLoading(state, { timeline, value }) {
|
||||
state.timelines[timeline].loading = value
|
||||
setLoading({ timeline, value }) {
|
||||
this.timelines[timeline].loading = value
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.j
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
import { useListsStore } from 'src/stores/lists.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useNotificationsStore } from 'src/stores/notifications.js'
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useSyncConfigStore } from 'src/stores/sync_config.js'
|
||||
import { useUserHighlightStore } from 'src/stores/user_highlight.js'
|
||||
|
||||
|
|
@ -144,12 +146,12 @@ export const useUsersStore = defineStore('users', {
|
|||
const { data, timestamp } = response
|
||||
const users = Array.isArray(data) ? data : [data]
|
||||
|
||||
users.forEach((user) => {
|
||||
return users.map((user) => {
|
||||
const existing = this.users.get(user.id) ?? {}
|
||||
const oldTimestamp = this.timestamps.get(existing)
|
||||
|
||||
// implicit: if oldTimestamp is undefined this will still be false
|
||||
if (oldTimestamp > timestamp) return // not overwriting old data with new
|
||||
if (oldTimestamp > timestamp) return existing // not overwriting old data with new
|
||||
|
||||
const { relationship: unused0, ...old } = existing
|
||||
const { relationship: unused1, ...neu } = user
|
||||
|
|
@ -163,6 +165,8 @@ export const useUsersStore = defineStore('users', {
|
|||
if (user.id === this.currentUser.id) {
|
||||
this.currentUser = newUser
|
||||
}
|
||||
|
||||
return this.users.get(user.id)
|
||||
})
|
||||
},
|
||||
updateUserRelationship(relationships) {
|
||||
|
|
@ -594,7 +598,7 @@ export const useUsersStore = defineStore('users', {
|
|||
useBookmarkFoldersStore().stopFetching()
|
||||
store.dispatch('stopFetchingFollowRequests')
|
||||
store.commit('clearNotifications')
|
||||
store.commit('resetStatuses')
|
||||
useStatusesStore().resetStatuses()
|
||||
useChatsStore().resetChats()
|
||||
oauth.clearToken()
|
||||
Cookies.remove('__Host-pleroma_key', { path: '/' })
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { mountOpts } from '../../../fixtures/setup_test'
|
|||
|
||||
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
|
||||
import { useMergedConfigStore } from 'src/stores/merged_config.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
import { useUsersStore } from 'src/stores/users.js'
|
||||
|
||||
const currentUser = {
|
||||
|
|
@ -37,7 +38,7 @@ const replyMountOpts = (props) =>
|
|||
props,
|
||||
afterStore(store) {
|
||||
useUsersStore().currentUser = currentUser
|
||||
store.state.statuses.allStatusesObject = {
|
||||
useStatusesStore().allStatuses = {
|
||||
[repliedStatus.id]: repliedStatus,
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue