user profile work

This commit is contained in:
Henry Jameson 2026-08-11 20:25:43 +03:00
commit 1f3b9a3905
7 changed files with 53 additions and 58 deletions

View file

@ -115,6 +115,7 @@ export const fetchTimeline = ({
publicAndExternal: MASTODON_PUBLIC_TIMELINE,
dms: MASTODON_DIRECT_MESSAGES_TIMELINE_URL,
user: MASTODON_USER_TIMELINE_URL,
userPinned: MASTODON_USER_TIMELINE_URL,
media: MASTODON_USER_TIMELINE_URL,
list: MASTODON_LIST_TIMELINE_URL,
favorites: MASTODON_USER_FAVORITES_TIMELINE_URL,
@ -130,6 +131,7 @@ export const fetchTimeline = ({
const twoArgs = new Set([
'user',
'userPinned',
'media',
'list',
'publicFavorites',
@ -147,6 +149,7 @@ export const fetchTimeline = ({
const id = (() => {
switch (timeline) {
case 'user':
case 'userPinned':
case 'media':
return userId
case 'list':
@ -163,6 +166,9 @@ export const fetchTimeline = ({
if (timeline === 'media') {
params.onlyMedia = true
}
if (timeline === 'userPinned') {
params.pinned = true
}
if (timeline === 'public') {
params.local = true
}

View file

@ -28,12 +28,11 @@ library.add(faCircleNotch, faCog, faMinus, faArrowUp, faCirclePlus, faCheck)
const Timeline = {
props: {
timelineRef: Object,
argument: String,
embedded: Boolean,
count: Number,
pinnedStatusIds: Set,
inProfile: Boolean,
footerSlipgate: Object, // reference to an element where we should put our footer
embedded: Boolean,
inProfile: Boolean,
skipPinned: Boolean,
},
data() {
return {
@ -58,17 +57,8 @@ const Timeline = {
},
filteredVisibleStatuses() {
return [...this.timeline.visibleStatusesIds.keys()]
.filter(
(id) =>
this.timelineRef.name !== 'user' ||
(id >= this.timeline.minId && id <= this.timeline.maxId),
)
.map((id) => this.timeline.statuses.get(id))
},
filteredPinnedStatusIds() {
return (this.pinnedStatusIds || []).filter(
(statusId) => this.timeline.statusesObject[statusId],
)
.filter(({ pinned }) => this.skipPinned ? !pinned : true)
},
newStatusCount() {
return this.timeline.newStatusCount
@ -109,17 +99,11 @@ const Timeline = {
),
}
},
// id map of statuses which need to be hidden in the main list due to pinning logic
pinnedStatusIdsObject() {
return keyBy(this.pinnedStatusIds)
},
statusesToDisplay() {
const amount = this.timeline.visibleStatusesIds.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)
const min = Math.max(0, this.virtualScrollIndex - statusesPerSide)
const max = Math.min(amount, this.virtualScrollIndex + statusesPerSide)
return new Set(
[...this.timeline.visibleStatusesIds.keys()].slice(min, max),
)
@ -159,6 +143,11 @@ const Timeline = {
},
methods: {
timelineChange(newTimeline, oldTimeline) {
// TODO this might not be necessary if we optimize mergeOrAdd
const sameName = newTimeline?.name === oldTimeline?.name
const sameArgument = newTimeline?.argument === oldTimeline?.argument
if (sameName && sameArgument) return
if (oldTimeline && oldTimeline.name !== 'friends') {
useTimelinesStore().clearTimeline(oldTimeline.name)
}

View file

@ -71,17 +71,6 @@
class="timeline"
role="feed"
>
<Conversation
v-for="statusId in filteredPinnedStatusIds"
:key="statusId + '-pinned'"
role="listitem"
class="status-fadein"
:status-id="statusId"
:pinned-status-ids-object="pinnedStatusIdsObject"
:in-profile="inProfile"
:profile-user-id="userId"
collapsable
/>
<Conversation
v-for="status in filteredVisibleStatuses"
:key="status.id"

View file

@ -9,7 +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 { useTimelinesStore } from 'src/stores/statuses.js'
import { useTimelinesStore } from 'src/stores/timelines.js'
import { useUsersStore } from 'src/stores/users.js'
import { library } from '@fortawesome/fontawesome-svg-core'
@ -38,7 +38,6 @@ const UserProfile = {
useInterfaceStore().setForeignProfileBackground(this.user?.background_image)
},
unmounted() {
this.stopFetching()
useInterfaceStore().setForeignProfileBackground(null)
this.$store.dispatch('clearFollowers', this.userId)
this.$store.dispatch('clearFriends', this.userId)
@ -101,6 +100,7 @@ const UserProfile = {
},
load(userNameOrId) {
const loadById = (userId) => {
console.log('LOAD', userId)
this.userId = userId
}
@ -119,10 +119,11 @@ const UserProfile = {
if (user) {
loadById(user.id)
} else {
;(maybeId
? this.$store.dispatch('fetchUser', maybeId)
: this.$store.dispatch('fetchUserByName', maybeName)
)
const promise = maybeId
? useUsersStore().fetchUser(maybeId)
: useUsersStore().fetchUserByName(maybeName)
promise
.then(({ id }) => loadById(id))
.catch((reason) => {
const errorMessage = get(reason, 'error.error')
@ -138,6 +139,7 @@ const UserProfile = {
}
},
switchUser(userNameOrId) {
console.log('USER SWITCH')
this.load(userNameOrId)
},
onTabSwitch(tab) {

View file

@ -14,22 +14,32 @@
/>
</div>
<tab-switcher
v-if="userId"
:active-tab="tab"
:render-only-focused="true"
:on-switch="onTabSwitch"
>
<div
key="statuses"
class="statuses"
:label="$t('user_card.statuses')"
:count="user.statuses_count"
:title="$t('user_profile.timeline_title')"
>
<!--
<Timeline
key="statuses"
:embedded="true"
timeline-name="user"
:argument="userId"
:in-profile="true"
:timeline-ref="{ name: 'userPinned', argument: userId }"
embedded
in-profile
:footer-slipgate="footerRef"
/>
-->
<Timeline
:timeline-ref="{ name: 'user', argument: userId }"
embedded
in-profile
skip-pinned
:footer-slipgate="footerRef"
/>
</div>
@ -71,24 +81,23 @@
<Timeline
key="media"
:label="$t('user_card.media')"
:disabled="!media.visibleStatuses.length"
:embedded="true"
:disabled="!media.visibleStatusesIds.size"
:title="$t('user_card.media')"
timeline-name="media"
:argument="userId"
:in-profile="true"
:timeline-ref="{ name: 'media', argument: userId }"
embedded
in-profile
:footer-slipgate="footerRef"
/>
<Timeline
v-if="favoritesTabVisible"
key="favorites"
:label="$t('user_card.favorites')"
:disabled="!favorites.visibleStatuses.length"
:embedded="true"
:disabled="!favorites.visibleStatusesIds.size"
:title="$t('user_card.favorites')"
timeline-name="favorites"
:timeline-ref="{ name: 'favorites', argument: userId }"
:argument="isUs ? undefined : userId"
:in-profile="true"
embedded
in-profile
:footer-slipgate="footerRef"
/>
</tab-switcher>

View file

@ -100,7 +100,7 @@ const fetchAndUpdate = ({
})
}
const timelineFetcher = (timeline, argument, argumentKey, credentials) => {
const timelineFetcher = (timeline, argument, credentials) => {
const state = {
interval: null
}
@ -113,7 +113,6 @@ const timelineFetcher = (timeline, argument, argumentKey, credentials) => {
} = {}) => fetchAndUpdate({
timeline,
argument,
argumentKey,
credentials,
}, {
maxId,

View file

@ -21,7 +21,7 @@ const emptyTl = (name, argument = null) => {
fetcher: null,
}
const property = USER_TIMELINES.has(name) ? 'userId' : ARGUMENT_MAP[name]
const property = ARGUMENT_MAP[name]
if (property) {
result[property] = argument
@ -36,6 +36,9 @@ export const ARGUMENT_MAP = {
bookmarks: 'bookmarkFolderId',
quotes: 'statusId',
search: 'query',
user: 'userId',
userPinned: 'userId',
media: 'userId',
}
export const defaultState = () => {
@ -58,7 +61,6 @@ export const defaultState = () => {
].map((name) => [name, emptyTl(name)]))
}
const USER_TIMELINES = new Set(['user', 'userPinned', 'media', 'favorites'])
//const CUSTOM_SORT = new Set(['bookmarks', 'favorites'])
export const useTimelinesStore = defineStore('timelines', {
@ -82,7 +84,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 = USER_TIMELINES.has(name) ? 'userId' : ARGUMENT_MAP[name]
const property = ARGUMENT_MAP[name]
if (property && timeline[property] !== argument) {
return
@ -148,7 +150,6 @@ export const useTimelinesStore = defineStore('timelines', {
timeline.fetcher = timelineFetcher(
timeline,
argument,
ARGUMENT_MAP[timeline.name],
useOAuthStore().token,
)