Merge branch 'brendenbice1222/pleroma-fe-issues/pleroma-fe-202-show-boosted-users' into 'develop'
Show favoriting and repeating users in hilighted status See merge request pleroma/pleroma-fe!768
This commit is contained in:
commit
a954f56e34
11 changed files with 178 additions and 5 deletions
15
src/components/avatar_list/avatar_list.js
Normal file
15
src/components/avatar_list/avatar_list.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
|
|
||||||
|
const AvatarList = {
|
||||||
|
props: ['avatars'],
|
||||||
|
computed: {
|
||||||
|
slicedAvatars () {
|
||||||
|
return this.avatars ? this.avatars.slice(0, 15) : []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
UserAvatar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AvatarList
|
38
src/components/avatar_list/avatar_list.vue
Normal file
38
src/components/avatar_list/avatar_list.vue
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<div class="avatars">
|
||||||
|
<div class="avatars-item" v-for="avatar in slicedAvatars">
|
||||||
|
<UserAvatar :src="avatar.profile_image_url" class="avatar-small" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script src="./avatar_list.js" ></script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '../../_variables.scss';
|
||||||
|
|
||||||
|
.avatars {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
// For hiding overflowing elements
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: 24px;
|
||||||
|
|
||||||
|
.avatars-item {
|
||||||
|
margin: 0 0 5px 5px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-small {
|
||||||
|
border-radius: $fallback--avatarAltRadius;
|
||||||
|
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -139,6 +139,7 @@ const conversation = {
|
||||||
},
|
},
|
||||||
setHighlight (id) {
|
setHighlight (id) {
|
||||||
this.highlight = id
|
this.highlight = id
|
||||||
|
this.$store.dispatch('fetchFavsAndRepeats', id)
|
||||||
},
|
},
|
||||||
getHighlight () {
|
getHighlight () {
|
||||||
return this.isExpanded ? this.highlight : null
|
return this.isExpanded ? this.highlight : null
|
||||||
|
|
|
@ -7,11 +7,12 @@ import UserCard from '../user_card/user_card.vue'
|
||||||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||||
import Gallery from '../gallery/gallery.vue'
|
import Gallery from '../gallery/gallery.vue'
|
||||||
import LinkPreview from '../link-preview/link-preview.vue'
|
import LinkPreview from '../link-preview/link-preview.vue'
|
||||||
|
import AvatarList from '../avatar_list/avatar_list.vue'
|
||||||
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
||||||
import fileType from 'src/services/file_type/file_type.service'
|
import fileType from 'src/services/file_type/file_type.service'
|
||||||
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
||||||
import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
|
import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
|
||||||
import { filter, find, unescape } from 'lodash'
|
import { filter, find, unescape, uniqBy } from 'lodash'
|
||||||
|
|
||||||
const Status = {
|
const Status = {
|
||||||
name: 'Status',
|
name: 'Status',
|
||||||
|
@ -97,6 +98,10 @@ const Status = {
|
||||||
return this.statusoid
|
return this.statusoid
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
statusFromGlobalRepository () {
|
||||||
|
// NOTE: Consider to replace status with statusFromGlobalRepository
|
||||||
|
return this.$store.state.statuses.allStatusesObject[this.status.id]
|
||||||
|
},
|
||||||
loggedIn () {
|
loggedIn () {
|
||||||
return !!this.$store.state.users.currentUser
|
return !!this.$store.state.users.currentUser
|
||||||
},
|
},
|
||||||
|
@ -257,6 +262,14 @@ const Status = {
|
||||||
return this.status.statusnet_html
|
return this.status.statusnet_html
|
||||||
}
|
}
|
||||||
return this.status.summary_html + '<br />' + this.status.statusnet_html
|
return this.status.summary_html + '<br />' + this.status.statusnet_html
|
||||||
|
},
|
||||||
|
combinedFavsAndRepeatsAvatars () {
|
||||||
|
// Use the status from the global status repository since favs and repeats are saved in it
|
||||||
|
const combinedAvatars = [].concat(
|
||||||
|
this.statusFromGlobalRepository.favoritedBy,
|
||||||
|
this.statusFromGlobalRepository.rebloggedBy
|
||||||
|
)
|
||||||
|
return uniqBy(combinedAvatars, 'id')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -268,7 +281,8 @@ const Status = {
|
||||||
UserCard,
|
UserCard,
|
||||||
UserAvatar,
|
UserAvatar,
|
||||||
Gallery,
|
Gallery,
|
||||||
LinkPreview
|
LinkPreview,
|
||||||
|
AvatarList
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
visibilityIcon (visibility) {
|
visibilityIcon (visibility) {
|
||||||
|
|
|
@ -133,6 +133,24 @@
|
||||||
<link-preview :card="status.card" :size="attachmentSize" :nsfw="nsfwClickthrough" />
|
<link-preview :card="status.card" :size="attachmentSize" :nsfw="nsfwClickthrough" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<transition name="fade">
|
||||||
|
<div class="favs-repeated-users" v-if="combinedFavsAndRepeatsAvatars.length > 0 && isFocused">
|
||||||
|
<div class="stats">
|
||||||
|
<div class="stat-count" v-if="statusFromGlobalRepository.rebloggedBy && statusFromGlobalRepository.rebloggedBy.length > 0">
|
||||||
|
<a class="stat-title">{{ $t('status.repeats') }}</a>
|
||||||
|
<div class="stat-number">{{ statusFromGlobalRepository.rebloggedBy.length }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-count" v-if="statusFromGlobalRepository.favoritedBy && statusFromGlobalRepository.favoritedBy.length > 0">
|
||||||
|
<a class="stat-title">{{ $t('status.favorites') }}</a>
|
||||||
|
<div class="stat-number">{{ statusFromGlobalRepository.favoritedBy.length }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="avatar-row">
|
||||||
|
<AvatarList :avatars='combinedFavsAndRepeatsAvatars'></AvatarList>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
|
||||||
<div v-if="!noHeading && !isPreview" class='status-actions media-body'>
|
<div v-if="!noHeading && !isPreview" class='status-actions media-body'>
|
||||||
<div v-if="loggedIn">
|
<div v-if="loggedIn">
|
||||||
<i class="button-icon icon-reply" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')" :class="{'icon-reply-active': replying}"></i>
|
<i class="button-icon icon-reply" v-on:click.prevent="toggleReplying" :title="$t('tool_tip.reply')" :class="{'icon-reply-active': replying}"></i>
|
||||||
|
@ -612,6 +630,50 @@ a.unmute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.favs-repeated-users {
|
||||||
|
margin-top: $status-margin;
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
line-height: 1em;
|
||||||
|
|
||||||
|
.stat-count {
|
||||||
|
margin-right: $status-margin;
|
||||||
|
|
||||||
|
.stat-title {
|
||||||
|
color: var(--faint, $fallback--faint);
|
||||||
|
font-size: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-row {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 1px;
|
||||||
|
left: 0;
|
||||||
|
background-color: var(--faint, $fallback--faint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media all and (max-width: 800px) {
|
@media all and (max-width: 800px) {
|
||||||
.status-el {
|
.status-el {
|
||||||
.retweet-info {
|
.retweet-info {
|
||||||
|
|
|
@ -394,6 +394,8 @@
|
||||||
"no_statuses": "No statuses"
|
"no_statuses": "No statuses"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
|
"favorites": "Favorites",
|
||||||
|
"repeats": "Repeats",
|
||||||
"reply_to": "Reply to",
|
"reply_to": "Reply to",
|
||||||
"replies_list": "Replies:"
|
"replies_list": "Replies:"
|
||||||
},
|
},
|
||||||
|
|
|
@ -222,6 +222,8 @@
|
||||||
"no_more_statuses": "Ei enempää viestejä"
|
"no_more_statuses": "Ei enempää viestejä"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
|
"favorites": "Tykkäykset",
|
||||||
|
"repeats": "Toistot",
|
||||||
"reply_to": "Vastaus",
|
"reply_to": "Vastaus",
|
||||||
"replies_list": "Vastaukset:"
|
"replies_list": "Vastaukset:"
|
||||||
},
|
},
|
||||||
|
|
|
@ -459,6 +459,13 @@ export const mutations = {
|
||||||
},
|
},
|
||||||
queueFlush (state, { timeline, id }) {
|
queueFlush (state, { timeline, id }) {
|
||||||
state.timelines[timeline].flushMarker = id
|
state.timelines[timeline].flushMarker = id
|
||||||
|
},
|
||||||
|
addFavsAndRepeats (state, { id, favoritedByUsers, rebloggedByUsers }) {
|
||||||
|
state.allStatusesObject[id] = {
|
||||||
|
...state.allStatusesObject[id],
|
||||||
|
favoritedBy: favoritedByUsers,
|
||||||
|
rebloggedBy: rebloggedByUsers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,6 +531,21 @@ const statuses = {
|
||||||
id: rootState.statuses.notifications.maxId,
|
id: rootState.statuses.notifications.maxId,
|
||||||
credentials: rootState.users.currentUser.credentials
|
credentials: rootState.users.currentUser.credentials
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
fetchFavsAndRepeats ({ rootState, commit }, id) {
|
||||||
|
Promise.all([
|
||||||
|
rootState.api.backendInteractor.fetchFavoritedByUsers(id),
|
||||||
|
rootState.api.backendInteractor.fetchRebloggedByUsers(id)
|
||||||
|
]).then(([favoritedByUsers, rebloggedByUsers]) =>
|
||||||
|
commit(
|
||||||
|
'addFavsAndRepeats',
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
favoritedByUsers: favoritedByUsers.filter(_ => _),
|
||||||
|
rebloggedByUsers: rebloggedByUsers.filter(_ => _)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mutations
|
mutations
|
||||||
|
|
|
@ -47,6 +47,8 @@ const MASTODON_MUTE_USER_URL = id => `/api/v1/accounts/${id}/mute`
|
||||||
const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute`
|
const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute`
|
||||||
const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
|
const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
|
||||||
const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
|
const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
|
||||||
|
const MASTODON_STATUS_FAVORITEDBY_URL = id => `/api/v1/statuses/${id}/favourited_by`
|
||||||
|
const MASTODON_STATUS_REBLOGGEDBY_URL = id => `/api/v1/statuses/${id}/reblogged_by`
|
||||||
const MASTODON_PROFILE_UPDATE_URL = '/api/v1/accounts/update_credentials'
|
const MASTODON_PROFILE_UPDATE_URL = '/api/v1/accounts/update_credentials'
|
||||||
|
|
||||||
import { each, map, concat, last } from 'lodash'
|
import { each, map, concat, last } from 'lodash'
|
||||||
|
@ -712,6 +714,14 @@ const markNotificationsAsSeen = ({id, credentials}) => {
|
||||||
}).then((data) => data.json())
|
}).then((data) => data.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchFavoritedByUsers = ({id}) => {
|
||||||
|
return promisedRequest(MASTODON_STATUS_FAVORITEDBY_URL(id)).then((users) => users.map(parseUser))
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchRebloggedByUsers = ({id}) => {
|
||||||
|
return promisedRequest(MASTODON_STATUS_REBLOGGEDBY_URL(id)).then((users) => users.map(parseUser))
|
||||||
|
}
|
||||||
|
|
||||||
const apiService = {
|
const apiService = {
|
||||||
verifyCredentials,
|
verifyCredentials,
|
||||||
fetchTimeline,
|
fetchTimeline,
|
||||||
|
@ -761,7 +771,9 @@ const apiService = {
|
||||||
approveUser,
|
approveUser,
|
||||||
denyUser,
|
denyUser,
|
||||||
suggestions,
|
suggestions,
|
||||||
markNotificationsAsSeen
|
markNotificationsAsSeen,
|
||||||
|
fetchFavoritedByUsers,
|
||||||
|
fetchRebloggedByUsers
|
||||||
}
|
}
|
||||||
|
|
||||||
export default apiService
|
export default apiService
|
||||||
|
|
|
@ -113,6 +113,9 @@ const backendInteractorService = (credentials) => {
|
||||||
const deleteAccount = ({password}) => apiService.deleteAccount({credentials, password})
|
const deleteAccount = ({password}) => apiService.deleteAccount({credentials, password})
|
||||||
const changePassword = ({password, newPassword, newPasswordConfirmation}) => apiService.changePassword({credentials, password, newPassword, newPasswordConfirmation})
|
const changePassword = ({password, newPassword, newPasswordConfirmation}) => apiService.changePassword({credentials, password, newPassword, newPasswordConfirmation})
|
||||||
|
|
||||||
|
const fetchFavoritedByUsers = (id) => apiService.fetchFavoritedByUsers({id})
|
||||||
|
const fetchRebloggedByUsers = (id) => apiService.fetchRebloggedByUsers({id})
|
||||||
|
|
||||||
const backendInteractorServiceInstance = {
|
const backendInteractorServiceInstance = {
|
||||||
fetchStatus,
|
fetchStatus,
|
||||||
fetchConversation,
|
fetchConversation,
|
||||||
|
@ -154,7 +157,9 @@ const backendInteractorService = (credentials) => {
|
||||||
changePassword,
|
changePassword,
|
||||||
fetchFollowRequests,
|
fetchFollowRequests,
|
||||||
approveUser,
|
approveUser,
|
||||||
denyUser
|
denyUser,
|
||||||
|
fetchFavoritedByUsers,
|
||||||
|
fetchRebloggedByUsers
|
||||||
}
|
}
|
||||||
|
|
||||||
return backendInteractorServiceInstance
|
return backendInteractorServiceInstance
|
||||||
|
|
Loading…
Reference in a new issue