Add list of followed hashtags to profile

This commit is contained in:
FloatingGhost 2023-01-01 20:11:07 +00:00 committed by hynet-mel
parent 0e71597e56
commit 79d506e331
7 changed files with 147 additions and 13 deletions

View file

@ -0,0 +1,27 @@
<template>
<div class="followed-tag-card">
<h3>
<router-link :to="{ name: 'tag-timeline', params: {tag: tag.name}}">
#{{ tag.name }}
</router-link>
</h3>
</div>
</template>
<script>
export default {
name: 'FollowedTagCard',
props: {
tag: {
type: Object,
required: true
}
},
}
</script>
<style scoped>
.followed-tag-card {
margin-left: 1rem;
}
</style>

View file

@ -13,6 +13,7 @@ import {
faCircleNotch,
faCircleCheck
} from '@fortawesome/free-solid-svg-icons'
import FollowedTagCard from '../followed_tag_card/FollowedTagCard.vue'
library.add(
faCircleNotch,
@ -35,6 +36,14 @@ const FriendList = withLoadMore({
additionalPropNames: ['userId']
})(List)
const FollowedTagList = withLoadMore({
fetch: (props, $store) => $store.dispatch('fetchFollowedTags', props.userId),
select: (props, $store) => get($store.getters.findUser(props.userId), 'followedTagIds', []).map(id => $store.getters.findTag(id)),
destroy: (props, $store) => $store.dispatch('clearFollowedTags', props.userId),
childPropName: 'items',
additionalPropNames: ['userId']
})(List)
const isUserPage = ({ name }) => name === 'user-profile' || name === 'external-user-profile'
const UserProfile = {
@ -202,6 +211,7 @@ const UserProfile = {
}
},
components: {
FollowedTagCard,
UserCard,
Timeline,
FollowerList,
@ -209,7 +219,8 @@ const UserProfile = {
FollowCard,
TabSwitcher,
Conversation,
RichContent
RichContent,
FollowedTagList,
}
}

View file

@ -104,6 +104,14 @@
v-if="followsTabVisible"
key="followees"
:label="$t('user_card.followees')"
>
<tab-switcher
:active-tab="users"
:render-only-focused="true"
>
<div
key="users"
:label="$t('user_card.followed_users')"
>
<FriendList :user-id="userId">
<template #item="{item}">
@ -111,6 +119,23 @@
</template>
</FriendList>
</div>
<div
key="tags"
v-if="isUs"
:label="$t('user_card.followed_tags')"
>
<FollowedTagList
:user-id="userId"
:following="false"
:get-key="(item) => item.name"
>
<template #item="{item}">
<FollowedTagCard :tag="item" />
</template>
</FollowedTagList>
</div>
</tab-switcher>
</div>
<div
v-if="followersTabVisible"
key="followers"

View file

@ -59,7 +59,8 @@ const withLoadMore = ({
this.loading = false
this.bottomedOut = isEmpty(newEntries)
})
.catch(() => {
.catch((e) => {
console.error(e)
this.loading = false
this.error = true
})

View file

@ -1139,6 +1139,8 @@
"follow_unfollow": "Unfollow this jerk",
"followees": "Following",
"followers": "Followers",
"followed_tags": "Followed hashtags",
"followed_users": "Followed users",
"following": "Following!",
"follows_you": "Follows you!",
"hidden": "Hidden",

View file

@ -5,9 +5,9 @@ import { compact, map, each, mergeWith, last, concat, uniq, isArray } from 'loda
import { registerPushNotifications, unregisterPushNotifications } from '../services/push/push.js'
// TODO: Unify with mergeOrAdd in statuses.js
export const mergeOrAdd = (arr, obj, item) => {
export const mergeOrAdd = (arr, obj, item, key = 'id') => {
if (!item) { return false }
const oldItem = obj[item.id]
const oldItem = obj[item[key]]
if (oldItem) {
// We already have this, so only merge the new info.
mergeWith(oldItem, item, mergeArrayLength)
@ -15,7 +15,7 @@ export const mergeOrAdd = (arr, obj, item) => {
} else {
// This is a new item, prepare it
arr.push(item)
obj[item.id] = item
obj[item[key]] = item
if (item.screen_name && !item.screen_name.includes('@')) {
obj[item.screen_name.toLowerCase()] = item
}
@ -157,6 +157,14 @@ export const mutations = {
const user = state.usersObject[id]
user.followerIds = uniq(concat(user.followerIds || [], followerIds))
},
saveFollowedTagIds (state, { id, followedTagIds }) {
const user = state.usersObject[id]
user.followedTagIds = uniq(concat(user.followedTagIds || [], followedTagIds))
},
saveFollowedTagPagination (state, { id, pagination }) {
const user = state.usersObject[id]
user.followedTagPagination = pagination
},
// Because frontend doesn't have a reason to keep these stuff in memory
// outside of viewing someones user profile.
clearFriends (state, userId) {
@ -171,6 +179,12 @@ export const mutations = {
user['followerIds'] = []
}
},
clearFollowedTags (state, userId) {
const user = state.usersObject[userId]
if (user) {
user['followedTagIds'] = []
}
},
addNewUsers (state, users) {
each(users, (user) => {
if (user.relationship) {
@ -179,6 +193,11 @@ export const mutations = {
mergeOrAdd(state.users, state.usersObject, user)
})
},
addNewTags (state, tags) {
each(tags, (tag) => {
mergeOrAdd(state.tags, state.tagsObject, tag, 'name')
})
},
updateUserRelationship (state, relationships) {
relationships.forEach((relationship) => {
state.relationships[relationship.id] = relationship
@ -271,7 +290,11 @@ export const getters = {
relationship: state => id => {
const rel = id && state.relationships[id]
return rel || { id, loading: true }
}
},
findTag: state => query => {
const result = state.tagsObject[query]
return result
},
}
export const defaultState = {
@ -282,7 +305,9 @@ export const defaultState = {
usersObject: {},
signUpPending: false,
signUpErrors: [],
relationships: {}
relationships: {},
tags: [],
tagsObject: {}
}
const users = {
@ -402,12 +427,27 @@ const users = {
return followers
})
},
fetchFollowedTags ({ rootState, commit }, id) {
const user = rootState.users.usersObject[id]
const pagination = user.followedTagPagination
return rootState.api.backendInteractor.getFollowedHashtags({ pagination })
.then(({ data: tags, pagination }) => {
commit('addNewTags', tags)
commit('saveFollowedTagIds', { id, followedTagIds: tags.map(tag => tag.name) })
commit('saveFollowedTagPagination', { id, pagination })
return tags
})
},
clearFriends ({ commit }, userId) {
commit('clearFriends', userId)
},
clearFollowers ({ commit }, userId) {
commit('clearFollowers', userId)
},
clearFollowedTags ({ commit }, userId) {
commit('clearFollowedTags', userId)
},
subscribeUser ({ rootState, commit }, id) {
return rootState.api.backendInteractor.subscribeUser({ id })
.then((relationship) => commit('updateUserRelationship', [relationship]))
@ -437,6 +477,9 @@ const users = {
addNewUsers ({ commit }, users) {
commit('addNewUsers', users)
},
addNewTags ({ commit }, tags) {
commit('addNewTags', tags)
},
addNewStatuses (store, { statuses }) {
const users = map(statuses, 'user')
const retweetedUsers = compact(map(statuses, 'retweeted_status.user'))

View file

@ -1,6 +1,7 @@
import { each, map, concat, last, get } from 'lodash'
import { parseStatus, parseSource, parseUser, parseNotification, parseReport, parseAttachment, parseLinkHeaderPagination } from '../entity_normalizer/entity_normalizer.service.js'
import { RegistrationError, StatusCodeError } from '../errors/errors'
import { Url } from 'url'
/* eslint-env browser */
const MUTES_IMPORT_URL = '/api/pleroma/mutes_import'
@ -111,6 +112,7 @@ const AKKOMA_SETTING_PROFILE_LIST = `/api/v1/akkoma/frontend_settings/pleroma-fe
const MASTODON_TAG_URL = (name) => `/api/v1/tags/${name}`
const MASTODON_FOLLOW_TAG_URL = (name) => `/api/v1/tags/${name}/follow`
const MASTODON_UNFOLLOW_TAG_URL = (name) => `/api/v1/tags/${name}/unfollow`
const MASTODON_FOLLOWED_TAGS_URL = '/api/v1/followed_tags'
const oldfetch = window.fetch
@ -1575,6 +1577,28 @@ const unfollowHashtag = ({ tag, credentials }) => {
})
}
const getFollowedHashtags = ({ credentials, pagination: savedPagination }) => {
const queryParams = new URLSearchParams()
if (savedPagination?.maxId) {
queryParams.append('max_id', savedPagination.maxId)
}
const url = `${MASTODON_FOLLOWED_TAGS_URL}?${queryParams.toString()}`
let pagination = {};
return fetch(url, {
credentials
}).then((data) => {
pagination = parseLinkHeaderPagination(data.headers.get('Link'), {
flakeId: false
});
return data.json()
}).then((data) => {
return {
pagination,
data
}
});
}
export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => {
return Object.entries({
...(credentials
@ -1813,7 +1837,8 @@ const apiService = {
deleteNoteFromReport,
getHashtag,
followHashtag,
unfollowHashtag
unfollowHashtag,
getFollowedHashtags,
}
export default apiService