diff --git a/src/components/avatar_list/avatar_list.js b/src/components/avatar_list/avatar_list.js new file mode 100644 index 00000000..d65125c2 --- /dev/null +++ b/src/components/avatar_list/avatar_list.js @@ -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 diff --git a/src/components/avatar_list/avatar_list.vue b/src/components/avatar_list/avatar_list.vue new file mode 100644 index 00000000..b14474ba --- /dev/null +++ b/src/components/avatar_list/avatar_list.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 30600f73..ffeb7244 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -139,6 +139,7 @@ const conversation = { }, setHighlight (id) { this.highlight = id + this.$store.dispatch('fetchFavsAndRepeats', id) }, getHighlight () { return this.isExpanded ? this.highlight : null diff --git a/src/components/status/status.js b/src/components/status/status.js index 0295cd04..f10eb2e4 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -7,11 +7,12 @@ import UserCard from '../user_card/user_card.vue' import UserAvatar from '../user_avatar/user_avatar.vue' import Gallery from '../gallery/gallery.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 fileType from 'src/services/file_type/file_type.service' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.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 = { name: 'Status', @@ -97,6 +98,10 @@ const Status = { return this.statusoid } }, + statusFromGlobalRepository () { + // NOTE: Consider to replace status with statusFromGlobalRepository + return this.$store.state.statuses.allStatusesObject[this.status.id] + }, loggedIn () { return !!this.$store.state.users.currentUser }, @@ -257,6 +262,14 @@ const Status = { return this.status.statusnet_html } return this.status.summary_html + '
' + 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: { @@ -268,7 +281,8 @@ const Status = { UserCard, UserAvatar, Gallery, - LinkPreview + LinkPreview, + AvatarList }, methods: { visibilityIcon (visibility) { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 690e8318..a5f7429c 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -133,6 +133,24 @@ + +
+
+
+ {{ $t('status.repeats') }} +
{{ statusFromGlobalRepository.rebloggedBy.length }}
+
+
+ {{ $t('status.favorites') }} +
{{ statusFromGlobalRepository.favoritedBy.length }}
+
+
+ +
+
+
+
+
@@ -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) { .status-el { .retweet-info { diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue index e0a34bd1..e6a8d458 100644 --- a/src/components/timeline/timeline.vue +++ b/src/components/timeline/timeline.vue @@ -16,7 +16,7 @@
- + commit( + 'addFavsAndRepeats', + { + id, + favoritedByUsers: favoritedByUsers.filter(_ => _), + rebloggedByUsers: rebloggedByUsers.filter(_ => _) + } + ) + ) } }, mutations diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index 6d789f47..c5e2280d 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -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_POST_STATUS_URL = '/api/v1/statuses' 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' import { each, map, concat, last } from 'lodash' @@ -712,6 +714,14 @@ const markNotificationsAsSeen = ({id, credentials}) => { }).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 = { verifyCredentials, fetchTimeline, @@ -761,7 +771,9 @@ const apiService = { approveUser, denyUser, suggestions, - markNotificationsAsSeen + markNotificationsAsSeen, + fetchFavoritedByUsers, + fetchRebloggedByUsers } export default apiService diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js index b6f070fe..d2b581ca 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -113,6 +113,9 @@ const backendInteractorService = (credentials) => { const deleteAccount = ({password}) => apiService.deleteAccount({credentials, password}) 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 = { fetchStatus, fetchConversation, @@ -154,7 +157,9 @@ const backendInteractorService = (credentials) => { changePassword, fetchFollowRequests, approveUser, - denyUser + denyUser, + fetchFavoritedByUsers, + fetchRebloggedByUsers } return backendInteractorServiceInstance