moderately usable state

This commit is contained in:
Henry Jameson 2026-08-18 21:35:06 +03:00
commit d0c3eafa84
15 changed files with 570 additions and 338 deletions

View file

@ -1,8 +1,9 @@
import { defineStore } from 'pinia'
import {
fetchScrobbles,
} from 'src/api/public.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useUsersStore } from 'src/stores/users.js'
import { fetchScrobbles } from 'src/api/public.js'
export const defaultState = () => ({
scrobblesNextFetch: new Map(),
@ -13,7 +14,7 @@ export const useScrobblesStore = defineStore('scrobbles', {
actions: {
getLatestScrobble(userId) {
const scrobblesSupport =
useInstanceCapabilitiesStore().pleromaScrobblesAvailable
useInstanceCapabilitiesStore().pleromaScrobblesAvailable
if (!scrobblesSupport) {
return
@ -29,12 +30,12 @@ export const useScrobblesStore = defineStore('scrobbles', {
.then(({ data: scrobbles }) => {
useUsersStore().findUser(userId).latestScrobble = scrobbles[0]
this.scrobblesNextFetch.set(user.id, Date.now() + 60 * 1000)
this.scrobblesNextFetch.set(userId, Date.now() + 60 * 1000)
})
.catch((e) => {
useInstanceCapabilitiesStore().set('pleromaScrobblesAvailable', false)
console.warn('cannot fetch scrobbles', e)
})
}
}
},
},
})

55
src/stores/search.js Normal file
View file

@ -0,0 +1,55 @@
import { defineStore } from 'pinia'
import { useOAuthStore } from 'src/stores/oauth.js'
import { useStatusesStore } from 'src/stores/statuses.js'
import { useUsersStore } from 'src/stores/users.js'
import { search2 } from 'src/api/public.js'
export const useSearchStore = defineStore('search', {
actions: {
async search({ q, resolve, limit, offset, following, type }) {
const { data, ...rest } = await search2({
q,
resolve,
limit,
offset,
following,
type,
credentials: useOAuthStore().token,
})
const { accounts, statuses } = data
useUsersStore().addNewUsers({
...rest,
data: accounts,
})
useStatusesStore().addNewStatuses({
...rest,
statuses,
})
const output = {}
output.statuses = statuses.map((s) =>
useStatusesStore().allStatuses.get(s.id),
)
output.accounts = accounts.map((s) => useUsersStore().findUser(s.id))
return output
},
// Search
searchUsers({ query }) {
return searchUsers({
query,
credentials: useOAuthStore().token,
}).then((result) => {
const { data } = result
useUsersStore().addNewUsers(result)
return data.map((s) => useUsersStore().findUser(s.id))
})
},
},
})

View file

@ -1,6 +1,5 @@
import { defineStore } from 'pinia'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useInterfaceStore } from 'src/stores/interface.js'
import { useOAuthStore } from 'src/stores/oauth.js'
import { useStreamingStore } from 'src/stores/streaming.js'
@ -240,6 +239,10 @@ export const useStatusesStore = defineStore('statuses', {
const status = this.allStatuses.get(id)
status.emoji_reactions = emojiReactions
},
updateStatusWithPoll(id, poll) {
const status = this.allStatuses.get(id)
status.poll = poll
},
// Actions
requestInteract({ name, id, optimisticCall, apiCall, argument, value }) {
@ -524,49 +527,20 @@ export const useStatusesStore = defineStore('statuses', {
// For when blocking a user
wipeUserStatuses(userId) {
const removed = new Set()
this.allStatuses.forEach((status) => {
if (status.user.id === userId) {
this.allStatuses.delete(status.id)
removed.add(status.id)
}
})
return removed
},
// Search
search({ 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),
})
this.addNewStatuses({
statuses: data.statuses,
})
data.statuses = data.statuses.map((s) => this.allStatuses.get(s.id))
return data
})
},
// Misc
setVirtualHeight({ statusId, height }) {
this.allStatuses.get(statusId).virtualHeight = height
},
updateStatusWithPoll({ id, poll }) {
const status = this.allStatuses.get(id)
status.poll = poll
},
},
})

View file

@ -12,16 +12,17 @@ import timelineFetcher from 'src/services/timeline_fetcher/timeline_fetcher.serv
const emptyTl = (name, argument = null) => {
const result = {
name,
statuses: new Map(),
visibleStatusesIds: new Set(),
order: [],
statusIds: new Set(),
visibleStatusIds: new Set(),
newStatusCount: 0,
maxId: '',
minId: '',
minVisibleId: '',
loading: false,
streaming: false,
flushMarker: 0,
fetcher: null,
socket: null,
}
const property = ARGUMENT_MAP[name]
@ -88,6 +89,11 @@ export const useTimelinesStore = defineStore('timelines', {
return
}
const property = ARGUMENT_MAP[timelineName]
if (property) {
timeline[property] = argument
}
timeline.fetcher = timelineFetcher(
timeline,
argument,
@ -156,7 +162,11 @@ export const useTimelinesStore = defineStore('timelines', {
},
deactivateAll() {
TIMELINES.forEach((name) => {
this.deactivate(name, true)
try {
this.deactivate(name, true)
} catch (e) {
console.error(`Failed to deactivate timeline ${name}`)
}
})
},
@ -169,7 +179,7 @@ export const useTimelinesStore = defineStore('timelines', {
showImmediately = false,
noIdUpdate = false,
pagination = {},
nested = false,
older = false
},
) {
if (statuses.length === 0) return
@ -179,7 +189,7 @@ export const useTimelinesStore = defineStore('timelines', {
// 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 = ARGUMENT_MAP[name]
const property = ARGUMENT_MAP[timelineName]
if (property && timeline[property] !== argument) {
return
@ -188,59 +198,36 @@ export const useTimelinesStore = defineStore('timelines', {
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)
const filtered = statuses.filter((id) => !timeline.statusIds.has(id))
if (older) {
timeline.order.push(...filtered)
} else {
timeline.order.unshift(...filtered)
}
statuses.forEach((statusId) => {
const isNew = !timeline.statusIds.has(statusId)
timeline.statusIds.add(statusId)
if (isNew) {
if (showImmediately) {
// Add it directly to the visibleStatuses, don't change
// newStatusCount
timeline.visibleStatusesIds.add(status.id)
timeline.visibleStatusIds.add(statusId)
} 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: [status],
nested: true,
})
}
}
if (status.visibility === 'direct') {
if (timeline !== this.dms) {
this.addStatusesToTimeline('dms', null, {
statuses: [status],
nested: true,
})
}
}
})
},
onStreamMessage(timeline, argument, event) {
// This relies on statuses store to process this event first
const status = useStatusesStore().allStatuses.get(event.data.status.id)
this.addStatusesToTimeline(timeline, argument, {
statuses: [status],
statuses: [event.data.status.id],
})
},
@ -278,10 +265,10 @@ export const useTimelinesStore = defineStore('timelines', {
},
// Queues & Timeline manip
updateTimelineExtremes(timeline, statuses, pagination = {}) {
updateTimelineExtremes(timeline, 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 minNew = pagination.maxId ?? last(timeline.order) ?? ''
const maxNew = pagination.minId ?? first(timeline.order) ?? ''
const newer = maxNew > timeline.maxId
const older = minNew < timeline.minId
@ -292,20 +279,17 @@ export const useTimelinesStore = defineStore('timelines', {
if (older || timeline.minId === '') {
timeline.minId = minNew
}
this.syncOrder(timeline)
},
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()])
timeline.visibleStatusIds = new Set([...timeline.statusIds])
},
syncOrder(timeline) {
timeline.order = timeline.order.filter((id) => timeline.statusIds.has(id))
},
queueFlush(timeline, id) {
this[timeline].flushMarker = id
@ -317,23 +301,16 @@ export const useTimelinesStore = defineStore('timelines', {
},
// Misc
wipeUserStatuses(userId) {
wipeStatuses(ids) {
TIMELINES.forEach((timelineName) => {
const timeline = this.timelines[timelineName]
const timeline = this[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
ids.forEach((id) => {
timeline.statusIds.delete(id)
timeline.visibleStatusIds.delete(id)
})
this.syncOrder(timeline)
})
},
},

View file

@ -746,17 +746,6 @@ export const useUsersStore = defineStore('users', {
useInterfaceStore().onLogout()
})
},
// Search
searchUsers({ query }) {
return searchUsers({
query,
credentials: useOAuthStore().token,
}).then(({ data: users }) => {
this.addNewUsers(users)
return users
})
},
},
persist: {
paths: ['lastLoginName'],