Merge branch 'feature/following_reblogs' into 'develop'

[#664]  implementes to hide/show reblogs from a specific user.

See merge request pleroma/pleroma-fe!956
This commit is contained in:
HJ 2019-10-15 06:26:12 +00:00
commit d2feba1487
12 changed files with 264 additions and 131 deletions

View File

@ -0,0 +1,35 @@
import ProgressButton from '../progress_button/progress_button.vue'
const AccountActions = {
props: [
'user'
],
data () {
return { }
},
components: {
ProgressButton
},
methods: {
showRepeats () {
this.$store.dispatch('showReblogs', this.user.id)
},
hideRepeats () {
this.$store.dispatch('hideReblogs', this.user.id)
},
blockUser () {
this.$store.dispatch('blockUser', this.user.id)
},
unblockUser () {
this.$store.dispatch('unblockUser', this.user.id)
},
reportUser () {
this.$store.dispatch('openUserReportingModal', this.user.id)
},
mentionUser () {
this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user })
}
}
}
export default AccountActions

View File

@ -0,0 +1,93 @@
<template>
<div class="account-actions">
<v-popover
trigger="click"
class="account-tools-popover"
:container="false"
placement="bottom-end"
:offset="5"
>
<div slot="popover">
<div class="dropdown-menu">
<button
class="btn btn-default btn-block dropdown-item"
@click="mentionUser"
>
{{ $t('user_card.mention') }}
</button>
<template v-if="user.following">
<div
role="separator"
class="dropdown-divider"
/>
<button
v-if="user.showing_reblogs"
class="btn btn-default dropdown-item"
@click="hideRepeats"
>
{{ $t('user_card.hide_repeats') }}
</button>
<button
v-if="!user.showing_reblogs"
class="btn btn-default dropdown-item"
@click="showRepeats"
>
{{ $t('user_card.show_repeats') }}
</button>
</template>
<div
role="separator"
class="dropdown-divider"
/>
<button
v-if="user.statusnet_blocking"
class="btn btn-default btn-block dropdown-item"
@click="unblockUser"
>
{{ $t('user_card.unblock') }}
</button>
<button
v-else
class="btn btn-default btn-block dropdown-item"
@click="blockUser"
>
{{ $t('user_card.block') }}
</button>
<button
class="btn btn-default btn-block dropdown-item"
@click="reportUser"
>
{{ $t('user_card.report') }}
</button>
</div>
</div>
<div class="btn btn-default ellipsis-button">
<i class="icon-ellipsis trigger-button" />
</div>
</v-popover>
</div>
</template>
<script src="./account_actions.js"></script>
<style lang="scss">
@import '../../_variables.scss';
@import '../popper/popper.scss';
.account-actions {
margin: 0 .8em;
}
.account-actions button.dropdown-item {
margin-left: 0;
}
.account-actions .trigger-button {
color: $fallback--lightText;
color: var(--lightText, $fallback--lightText);
opacity: .8;
cursor: pointer;
&:hover {
color: $fallback--text;
color: var(--text, $fallback--text);
}
}
</style>

View File

@ -0,0 +1,53 @@
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
export default {
props: ['user'],
data () {
return {
inProgress: false
}
},
computed: {
isPressed () {
return this.inProgress || this.user.following
},
title () {
if (this.inProgress || this.user.following) {
return this.$t('user_card.follow_unfollow')
} else if (this.user.requested) {
return this.$t('user_card.follow_again')
} else {
return this.$t('user_card.follow')
}
},
label () {
if (this.inProgress) {
return this.$t('user_card.follow_progress')
} else if (this.user.following) {
return this.$t('user_card.following')
} else if (this.user.requested) {
return this.$t('user_card.follow_sent')
} else {
return this.$t('user_card.follow')
}
}
},
methods: {
onClick () {
this.user.following ? this.unfollow() : this.follow()
},
follow () {
this.inProgress = true
requestFollow(this.user, this.$store).then(() => {
this.inProgress = false
})
},
unfollow () {
const store = this.$store
this.inProgress = true
requestUnfollow(this.user, store).then(() => {
this.inProgress = false
store.commit('removeStatus', { timeline: 'friends', userId: this.user.id })
})
}
}
}

View File

@ -0,0 +1,13 @@
<template>
<button
class="btn btn-default follow-button"
:class="{ pressed: isPressed }"
:disabled="inProgress"
:title="title"
@click="onClick"
>
{{ label }}
</button>
</template>
<script src="./follow_button.js"></script>

View File

@ -1,13 +1,16 @@
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
import RemoteFollow from '../remote_follow/remote_follow.vue' import RemoteFollow from '../remote_follow/remote_follow.vue'
import ProgressButton from '../progress_button/progress_button.vue' import ProgressButton from '../progress_button/progress_button.vue'
import FollowButton from '../follow_button/follow_button.vue'
import ModerationTools from '../moderation_tools/moderation_tools.vue' import ModerationTools from '../moderation_tools/moderation_tools.vue'
import AccountActions from '../account_actions/account_actions.vue'
import { hex2rgb } from '../../services/color_convert/color_convert.js' import { hex2rgb } from '../../services/color_convert/color_convert.js'
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
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'
export default { export default {
props: [ 'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar' ], props: [
'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar'
],
data () { data () {
return { return {
followRequestInProgress: false, followRequestInProgress: false,
@ -96,30 +99,11 @@ export default {
UserAvatar, UserAvatar,
RemoteFollow, RemoteFollow,
ModerationTools, ModerationTools,
ProgressButton AccountActions,
ProgressButton,
FollowButton
}, },
methods: { methods: {
followUser () {
const store = this.$store
this.followRequestInProgress = true
requestFollow(this.user, store).then(() => {
this.followRequestInProgress = false
})
},
unfollowUser () {
const store = this.$store
this.followRequestInProgress = true
requestUnfollow(this.user, store).then(() => {
this.followRequestInProgress = false
store.commit('removeStatus', { timeline: 'friends', userId: this.user.id })
})
},
blockUser () {
this.$store.dispatch('blockUser', this.user.id)
},
unblockUser () {
this.$store.dispatch('unblockUser', this.user.id)
},
muteUser () { muteUser () {
this.$store.dispatch('muteUser', this.user.id) this.$store.dispatch('muteUser', this.user.id)
}, },
@ -147,10 +131,10 @@ export default {
} }
}, },
userProfileLink (user) { userProfileLink (user) {
return generateProfileLink(user.id, user.screen_name, this.$store.state.instance.restrictedNicknames) return generateProfileLink(
}, user.id, user.screen_name,
reportUser () { this.$store.state.instance.restrictedNicknames
this.$store.dispatch('openUserReportingModal', this.user.id) )
}, },
zoomAvatar () { zoomAvatar () {
const attachment = { const attachment = {
@ -159,9 +143,6 @@ export default {
} }
this.$store.dispatch('setMedia', [attachment]) this.$store.dispatch('setMedia', [attachment])
this.$store.dispatch('setCurrent', attachment) this.$store.dispatch('setCurrent', attachment)
},
mentionUser () {
this.$store.dispatch('openPostStatusModal', { replyTo: true, repliedUser: this.user })
} }
} }
} }

View File

@ -66,8 +66,11 @@
> >
<i class="icon-link-ext usersettings" /> <i class="icon-link-ext usersettings" />
</a> </a>
<AccountActions
v-if="isOtherUser && loggedIn"
:user="user"
/>
</div> </div>
<div class="bottom-line"> <div class="bottom-line">
<router-link <router-link
class="user-screen-name" class="user-screen-name"
@ -135,72 +138,27 @@
v-if="loggedIn && isOtherUser" v-if="loggedIn && isOtherUser"
class="user-interactions" class="user-interactions"
> >
<div v-if="!user.following"> <div class="btn-group">
<button <FollowButton :user="user" />
class="btn btn-default btn-block" <template v-if="user.following">
:disabled="followRequestInProgress" <ProgressButton
:title="user.requested ? $t('user_card.follow_again') : ''" v-if="!user.subscribed"
@click="followUser" class="btn btn-default"
> :click="subscribeUser"
<template v-if="followRequestInProgress"> :title="$t('user_card.subscribe')"
{{ $t('user_card.follow_progress') }} >
</template> <i class="icon-bell-alt" />
<template v-else-if="user.requested"> </ProgressButton>
{{ $t('user_card.follow_sent') }} <ProgressButton
</template> v-else
<template v-else> class="btn btn-default pressed"
{{ $t('user_card.follow') }} :click="unsubscribeUser"
</template> :title="$t('user_card.unsubscribe')"
</button> >
<i class="icon-bell-ringing-o" />
</ProgressButton>
</template>
</div> </div>
<div v-else-if="followRequestInProgress">
<button
class="btn btn-default btn-block pressed"
disabled
:title="$t('user_card.follow_unfollow')"
@click="unfollowUser"
>
{{ $t('user_card.follow_progress') }}
</button>
</div>
<div
v-else
class="btn-group"
>
<button
class="btn btn-default pressed"
:title="$t('user_card.follow_unfollow')"
@click="unfollowUser"
>
{{ $t('user_card.following') }}
</button>
<ProgressButton
v-if="!user.subscribed"
class="btn btn-default"
:click="subscribeUser"
:title="$t('user_card.subscribe')"
>
<i class="icon-bell-alt" />
</ProgressButton>
<ProgressButton
v-else
class="btn btn-default pressed"
:click="unsubscribeUser"
:title="$t('user_card.unsubscribe')"
>
<i class="icon-bell-ringing-o" />
</ProgressButton>
</div>
<div>
<button
class="btn btn-default btn-block"
@click="mentionUser"
>
{{ $t('user_card.mention') }}
</button>
</div>
<div> <div>
<button <button
v-if="user.muted" v-if="user.muted"
@ -217,33 +175,6 @@
{{ $t('user_card.mute') }} {{ $t('user_card.mute') }}
</button> </button>
</div> </div>
<div>
<button
v-if="user.statusnet_blocking"
class="btn btn-default btn-block pressed"
@click="unblockUser"
>
{{ $t('user_card.blocked') }}
</button>
<button
v-else
class="btn btn-default btn-block"
@click="blockUser"
>
{{ $t('user_card.block') }}
</button>
</div>
<div>
<button
class="btn btn-default btn-block"
@click="reportUser"
>
{{ $t('user_card.report') }}
</button>
</div>
<ModerationTools <ModerationTools
v-if="loggedIn.role === &quot;admin&quot;" v-if="loggedIn.role === &quot;admin&quot;"
:user="user" :user="user"
@ -587,13 +518,12 @@
position: relative; position: relative;
display: flex; display: flex;
flex-flow: row wrap; flex-flow: row wrap;
justify-content: space-between;
margin-right: -.75em; margin-right: -.75em;
> * { > * {
flex: 1 0 0;
margin: 0 .75em .6em 0; margin: 0 .75em .6em 0;
white-space: nowrap; white-space: nowrap;
min-width: 95px;
} }
button { button {

View File

@ -555,6 +555,8 @@
"unmute": "Unmute", "unmute": "Unmute",
"unmute_progress": "Unmuting...", "unmute_progress": "Unmuting...",
"mute_progress": "Muting...", "mute_progress": "Muting...",
"hide_repeats": "Hide repeats",
"show_repeats": "Show repeats",
"admin_menu": { "admin_menu": {
"moderation": "Moderation", "moderation": "Moderation",
"grant_admin": "Grant Admin", "grant_admin": "Grant Admin",

View File

@ -60,6 +60,18 @@ const unmuteUser = (store, id) => {
.then((relationship) => store.commit('updateUserRelationship', [relationship])) .then((relationship) => store.commit('updateUserRelationship', [relationship]))
} }
const hideReblogs = (store, userId) => {
return store.rootState.api.backendInteractor.followUser({ id: userId, reblogs: false })
.then((relationship) => {
store.commit('updateUserRelationship', [relationship])
})
}
const showReblogs = (store, userId) => {
return store.rootState.api.backendInteractor.followUser({ id: userId, reblogs: true })
.then((relationship) => store.commit('updateUserRelationship', [relationship]))
}
export const mutations = { export const mutations = {
setMuted (state, { user: { id }, muted }) { setMuted (state, { user: { id }, muted }) {
const user = state.usersObject[id] const user = state.usersObject[id]
@ -135,6 +147,7 @@ export const mutations = {
user.muted = relationship.muting user.muted = relationship.muting
user.statusnet_blocking = relationship.blocking user.statusnet_blocking = relationship.blocking
user.subscribed = relationship.subscribing user.subscribed = relationship.subscribing
user.showing_reblogs = relationship.showing_reblogs
} }
}) })
}, },
@ -272,6 +285,12 @@ const users = {
unmuteUser (store, id) { unmuteUser (store, id) {
return unmuteUser(store, id) return unmuteUser(store, id)
}, },
hideReblogs (store, id) {
return hideReblogs(store, id)
},
showReblogs (store, id) {
return showReblogs(store, id)
},
muteUsers (store, ids = []) { muteUsers (store, ids = []) {
return Promise.all(ids.map(id => muteUser(store, id))) return Promise.all(ids.map(id => muteUser(store, id)))
}, },

View File

@ -219,10 +219,16 @@ const authHeaders = (accessToken) => {
} }
} }
const followUser = ({ id, credentials }) => { const followUser = ({ id, credentials, ...options }) => {
let url = MASTODON_FOLLOW_URL(id) let url = MASTODON_FOLLOW_URL(id)
const form = {}
if (options.reblogs !== undefined) { form['reblogs'] = options.reblogs }
return fetch(url, { return fetch(url, {
headers: authHeaders(credentials), body: JSON.stringify(form),
headers: {
...authHeaders(credentials),
'Content-Type': 'application/json'
},
method: 'POST' method: 'POST'
}).then((data) => data.json()) }).then((data) => data.json())
} }

View File

@ -31,8 +31,8 @@ const backendInteractorService = credentials => {
return apiService.fetchUserRelationship({ id, credentials }) return apiService.fetchUserRelationship({ id, credentials })
} }
const followUser = (id) => { const followUser = ({ id, reblogs }) => {
return apiService.followUser({ credentials, id }) return apiService.followUser({ credentials, id, reblogs })
} }
const unfollowUser = (id) => { const unfollowUser = (id) => {

View File

@ -69,6 +69,7 @@ export const parseUser = (data) => {
output.following = relationship.following output.following = relationship.following
output.statusnet_blocking = relationship.blocking output.statusnet_blocking = relationship.blocking
output.muted = relationship.muting output.muted = relationship.muting
output.showing_reblogs = relationship.showing_reblogs
output.subscribed = relationship.subscribing output.subscribed = relationship.subscribing
} }

View File

@ -14,7 +14,7 @@ const fetchUser = (attempt, user, store) => new Promise((resolve, reject) => {
}) })
export const requestFollow = (user, store) => new Promise((resolve, reject) => { export const requestFollow = (user, store) => new Promise((resolve, reject) => {
store.state.api.backendInteractor.followUser(user.id) store.state.api.backendInteractor.followUser({ id: user.id })
.then((updated) => { .then((updated) => {
store.commit('updateUserRelationship', [updated]) store.commit('updateUserRelationship', [updated])