logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe
commit: 01d05316998f90451e920f8ac8d4c264a13b0cd7
parent: f86a5dc80421f496a893efaa98f12f831da0adcb
Author: HJ <30-hj@users.noreply.git.pleroma.social>
Date:   Sun, 24 Mar 2019 17:15:21 +0000

Merge branch '227-manage-blocks-mutes' into 'develop'

Transition to MastoAPI: Mute & Block and Add Mutes management tab

See merge request pleroma/pleroma-fe!611

Diffstat:

Msrc/boot/after_store.js1+
Msrc/components/mute_card/mute_card.vue17+++++++++++++----
Msrc/components/settings/settings.js8++++++++
Msrc/components/settings/settings.vue4++++
Msrc/components/user_card/user_card.js22+++++++---------------
Msrc/components/user_card/user_card.vue4++--
Msrc/components/user_settings/user_settings.vue6++++++
Msrc/i18n/en.json1+
Msrc/modules/config.js1+
Msrc/modules/instance.js1+
Msrc/modules/users.js47+++++++++++++++++++++++++++++++----------------
Msrc/services/api/api.service.js91+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/services/backend_interactor_service/backend_interactor_service.js11+++++------
Msrc/services/timeline_fetcher/timeline_fetcher.service.js4++++
14 files changed, 127 insertions(+), 91 deletions(-)

diff --git a/src/boot/after_store.js b/src/boot/after_store.js @@ -97,6 +97,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => { copyInstanceOption('showInstanceSpecificPanel') copyInstanceOption('scopeOptionsEnabled') copyInstanceOption('formattingOptionsEnabled') + copyInstanceOption('hideMutedPosts') copyInstanceOption('collapseMessageWithSubject') copyInstanceOption('loginMethod') copyInstanceOption('scopeCopy') diff --git a/src/components/mute_card/mute_card.vue b/src/components/mute_card/mute_card.vue @@ -1,6 +1,6 @@ <template> <basic-user-card :user="user"> - <template slot="secondary-area"> + <div class="mute-card-content-container"> <button class="btn btn-default" @click="unmuteUser" :disabled="progress" v-if="muted"> <template v-if="progress"> {{ $t('user_card.unmute_progress') }} @@ -17,8 +17,18 @@ {{ $t('user_card.mute') }} </template> </button> - </template> + </div> </basic-user-card> </template> -<script src="./mute_card.js"></script>- \ No newline at end of file +<script src="./mute_card.js"></script> + +<style lang="scss"> +.mute-card-content-container { + margin-top: 0.5em; + text-align: right; + button { + width: 10em; + } +} +</style> diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js @@ -47,6 +47,11 @@ const settings = { pauseOnUnfocusedLocal: user.pauseOnUnfocused, hoverPreviewLocal: user.hoverPreview, + hideMutedPostsLocal: typeof user.hideMutedPosts === 'undefined' + ? instance.hideMutedPosts + : user.hideMutedPosts, + hideMutedPostsDefault: this.$t('settings.values.' + instance.hideMutedPosts), + collapseMessageWithSubjectLocal: typeof user.collapseMessageWithSubject === 'undefined' ? instance.collapseMessageWithSubject : user.collapseMessageWithSubject, @@ -177,6 +182,9 @@ const settings = { value = filter(value.split('\n'), (word) => trim(word).length > 0) this.$store.dispatch('setOption', { name: 'muteWords', value }) }, + hideMutedPostsLocal (value) { + this.$store.dispatch('setOption', { name: 'hideMutedPosts', value }) + }, collapseMessageWithSubjectLocal (value) { this.$store.dispatch('setOption', { name: 'collapseMessageWithSubject', value }) }, diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue @@ -37,6 +37,10 @@ <h2>{{$t('nav.timeline')}}</h2> <ul class="setting-list"> <li> + <input type="checkbox" id="hideMutedPosts" v-model="hideMutedPostsLocal"> + <label for="hideMutedPosts">{{$t('settings.hide_muted_posts')}} {{$t('settings.instance_default', { value: hideMutedPostsDefault })}}</label> + </li> + <li> <input type="checkbox" id="collapseMessageWithSubject" v-model="collapseMessageWithSubjectLocal"> <label for="collapseMessageWithSubject"> {{$t('settings.collapse_subject')}} {{$t('settings.instance_default', { value: collapseMessageWithSubjectDefault })}} diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js @@ -121,24 +121,16 @@ export default { }) }, blockUser () { - const store = this.$store - store.state.api.backendInteractor.blockUser(this.user.id) - .then((blockedUser) => { - store.commit('addNewUsers', [blockedUser]) - store.commit('removeStatus', { timeline: 'friends', userId: this.user.id }) - store.commit('removeStatus', { timeline: 'public', userId: this.user.id }) - store.commit('removeStatus', { timeline: 'publicAndExternal', userId: this.user.id }) - }) + this.$store.dispatch('blockUser', this.user.id) }, unblockUser () { - const store = this.$store - store.state.api.backendInteractor.unblockUser(this.user.id) - .then((unblockedUser) => store.commit('addNewUsers', [unblockedUser])) + this.$store.dispatch('unblockUser', this.user.id) }, - toggleMute () { - const store = this.$store - store.commit('setMuted', {user: this.user, muted: !this.user.muted}) - store.state.api.backendInteractor.setUserMute(this.user) + muteUser () { + this.$store.dispatch('muteUser', this.user.id) + }, + unmuteUser () { + this.$store.dispatch('unmuteUser', this.user.id) }, setProfileView (v) { if (this.switcher) { diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue @@ -74,12 +74,12 @@ </div> <div class='mute' v-if='isOtherUser && loggedIn'> <span v-if='user.muted'> - <button @click="toggleMute" class="pressed"> + <button @click="unmuteUser" class="pressed"> {{ $t('user_card.muted') }} </button> </span> <span v-if='!user.muted'> - <button @click="toggleMute"> + <button @click="muteUser"> {{ $t('user_card.mute') }} </button> </span> diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue @@ -192,6 +192,12 @@ <template slot="empty">{{$t('settings.no_blocks')}}</template> </block-list> </div> + + <div :label="$t('settings.mutes_tab')"> + <mute-list :refresh="true"> + <template slot="empty">{{$t('settings.no_mutes')}}</template> + </mute-list> + </div> </tab-switcher> </div> </div> diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -153,6 +153,7 @@ "general": "General", "hide_attachments_in_convo": "Hide attachments in conversations", "hide_attachments_in_tl": "Hide attachments in timeline", + "hide_muted_posts": "Hide posts of muted users", "max_thumbnails": "Maximum amount of thumbnails per post", "hide_isp": "Hide instance-specific panel", "preload_images": "Preload images", diff --git a/src/modules/config.js b/src/modules/config.js @@ -5,6 +5,7 @@ const browserLocale = (window.navigator.language || 'en').split('-')[0] const defaultState = { colors: {}, + hideMutedPosts: undefined, // instance default collapseMessageWithSubject: undefined, // instance default hideAttachments: false, hideAttachmentsInConv: false, diff --git a/src/modules/instance.js b/src/modules/instance.js @@ -18,6 +18,7 @@ const defaultState = { scopeOptionsEnabled: true, formattingOptionsEnabled: false, alwaysShowSubjectInput: true, + hideMutedPosts: false, collapseMessageWithSubject: false, hidePostStats: false, hideUserStats: false, diff --git a/src/modules/users.js b/src/modules/users.js @@ -102,10 +102,20 @@ export const mutations = { } }) }, - saveBlocks (state, blockIds) { + updateBlocks (state, blockedUsers) { + // Reset statusnet_blocking of all fetched users + each(state.users, (user) => { user.statusnet_blocking = false }) + each(blockedUsers, (user) => mergeOrAdd(state.users, state.usersObject, user)) + }, + saveBlockIds (state, blockIds) { state.currentUser.blockIds = blockIds }, - saveMutes (state, muteIds) { + updateMutes (state, mutedUsers) { + // Reset muted of all fetched users + each(state.users, (user) => { user.muted = false }) + each(mutedUsers, (user) => mergeOrAdd(state.users, state.usersObject, user)) + }, + saveMuteIds (state, muteIds) { state.currentUser.muteIds = muteIds }, setUserForStatus (state, status) { @@ -172,34 +182,39 @@ const users = { fetchBlocks (store) { return store.rootState.api.backendInteractor.fetchBlocks() .then((blocks) => { - store.commit('saveBlocks', map(blocks, 'id')) - store.commit('addNewUsers', blocks) + store.commit('saveBlockIds', map(blocks, 'id')) + store.commit('updateBlocks', blocks) return blocks }) }, - blockUser (store, id) { - return store.rootState.api.backendInteractor.blockUser(id) - .then((user) => store.commit('addNewUsers', [user])) + blockUser (store, userId) { + return store.rootState.api.backendInteractor.blockUser(userId) + .then((relationship) => { + store.commit('updateUserRelationship', [relationship]) + store.commit('removeStatus', { timeline: 'friends', userId }) + store.commit('removeStatus', { timeline: 'public', userId }) + store.commit('removeStatus', { timeline: 'publicAndExternal', userId }) + }) }, unblockUser (store, id) { return store.rootState.api.backendInteractor.unblockUser(id) - .then((user) => store.commit('addNewUsers', [user])) + .then((relationship) => store.commit('updateUserRelationship', [relationship])) }, fetchMutes (store) { return store.rootState.api.backendInteractor.fetchMutes() - .then((mutedUsers) => { - each(mutedUsers, (user) => { user.muted = true }) - store.commit('addNewUsers', mutedUsers) - store.commit('saveMutes', map(mutedUsers, 'id')) + .then((mutes) => { + store.commit('updateMutes', mutes) + store.commit('saveMuteIds', map(mutes, 'id')) + return mutes }) }, muteUser (store, id) { - return store.state.api.backendInteractor.setUserMute({ id, muted: true }) - .then((user) => store.commit('addNewUsers', [user])) + return store.rootState.api.backendInteractor.muteUser(id) + .then((relationship) => store.commit('updateUserRelationship', [relationship])) }, unmuteUser (store, id) { - return store.state.api.backendInteractor.setUserMute({ id, muted: false }) - .then((user) => store.commit('addNewUsers', [user])) + return store.rootState.api.backendInteractor.unmuteUser(id) + .then((relationship) => store.commit('updateUserRelationship', [relationship])) }, addFriends ({ rootState, commit }, fetchBy) { return new Promise((resolve, reject) => { diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js @@ -18,10 +18,8 @@ const MENTIONS_URL = '/api/statuses/mentions.json' const DM_TIMELINE_URL = '/api/statuses/dm_timeline.json' const FOLLOWERS_URL = '/api/statuses/followers.json' const FRIENDS_URL = '/api/statuses/friends.json' -const BLOCKS_URL = '/api/statuses/blocks.json' const FOLLOWING_URL = '/api/friendships/create.json' const UNFOLLOWING_URL = '/api/friendships/destroy.json' -const QVITTER_USER_PREF_URL = '/api/qvitter/set_profile_pref.json' const REGISTRATION_URL = '/api/account/register.json' const AVATAR_UPDATE_URL = '/api/qvitter/update_avatar.json' const BG_UPDATE_URL = '/api/qvitter/update_background_image.json' @@ -30,8 +28,6 @@ const PROFILE_UPDATE_URL = '/api/account/update_profile.json' const EXTERNAL_PROFILE_URL = '/api/externalprofile/show.json' const QVITTER_USER_NOTIFICATIONS_URL = '/api/qvitter/statuses/notifications.json' const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json' -const BLOCKING_URL = '/api/blocks/create.json' -const UNBLOCKING_URL = '/api/blocks/destroy.json' const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import' const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account' const CHANGE_PASSWORD_URL = '/api/pleroma/change_password' @@ -44,6 +40,12 @@ const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' const MASTODON_USER_URL = '/api/v1/accounts' const MASTODON_USER_RELATIONSHIPS_URL = '/api/v1/accounts/relationships' const MASTODON_USER_TIMELINE_URL = id => `/api/v1/accounts/${id}/statuses` +const MASTODON_USER_BLOCKS_URL = '/api/v1/blocks/' +const MASTODON_USER_MUTES_URL = '/api/v1/mutes/' +const MASTODON_BLOCK_USER_URL = id => `/api/v1/accounts/${id}/block` +const MASTODON_UNBLOCK_USER_URL = id => `/api/v1/accounts/${id}/unblock` +const MASTODON_MUTE_USER_URL = id => `/api/v1/accounts/${id}/mute` +const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute` import { each, map } from 'lodash' import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js' @@ -60,6 +62,19 @@ let fetch = (url, options) => { return oldfetch(fullUrl, options) } +const promisedRequest = (url, options) => { + return fetch(url, options) + .then((response) => { + return new Promise((resolve, reject) => response.json() + .then((json) => { + if (!response.ok) { + return reject(new StatusCodeError(response.status, json, { url, options }, response)) + } + return resolve(json) + })) + }) +} + // Params // cropH // cropW @@ -212,16 +227,14 @@ const unfollowUser = ({id, credentials}) => { } const blockUser = ({id, credentials}) => { - let url = `${BLOCKING_URL}?user_id=${id}` - return fetch(url, { + return fetch(MASTODON_BLOCK_USER_URL(id), { headers: authHeaders(credentials), method: 'POST' }).then((data) => data.json()) } const unblockUser = ({id, credentials}) => { - let url = `${UNBLOCKING_URL}?user_id=${id}` - return fetch(url, { + return fetch(MASTODON_UNBLOCK_USER_URL(id), { headers: authHeaders(credentials), method: 'POST' }).then((data) => data.json()) @@ -245,16 +258,7 @@ const denyUser = ({id, credentials}) => { const fetchUser = ({id, credentials}) => { let url = `${MASTODON_USER_URL}/${id}` - return fetch(url, { headers: authHeaders(credentials) }) - .then((response) => { - return new Promise((resolve, reject) => response.json() - .then((json) => { - if (!response.ok) { - return reject(new StatusCodeError(response.status, json, { url }, response)) - } - return resolve(json) - })) - }) + return promisedRequest(url, { headers: authHeaders(credentials) }) .then((data) => parseUser(data)) } @@ -338,23 +342,7 @@ const fetchStatus = ({id, credentials}) => { .then((data) => parseStatus(data)) } -const setUserMute = ({id, credentials, muted = true}) => { - const form = new FormData() - - const muteInteger = muted ? 1 : 0 - - form.append('namespace', 'qvitter') - form.append('data', muteInteger) - form.append('topic', `mute:${id}`) - - return fetch(QVITTER_USER_PREF_URL, { - method: 'POST', - headers: authHeaders(credentials), - body: form - }) -} - -const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false}) => { +const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false, withMuted = false}) => { const timelineUrls = { public: PUBLIC_TIMELINE_URL, friends: FRIENDS_TIMELINE_URL, @@ -390,6 +378,7 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use } params.push(['count', 20]) + params.push(['with_muted', withMuted]) const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') url += `?${queryString}` @@ -538,24 +527,29 @@ const changePassword = ({credentials, password, newPassword, newPasswordConfirma } const fetchMutes = ({credentials}) => { - const url = '/api/qvitter/mutes.json' + return promisedRequest(MASTODON_USER_MUTES_URL, { headers: authHeaders(credentials) }) + .then((users) => users.map(parseUser)) +} - return fetch(url, { - headers: authHeaders(credentials) - }).then((data) => data.json()) +const muteUser = ({id, credentials}) => { + return promisedRequest(MASTODON_MUTE_USER_URL(id), { + headers: authHeaders(credentials), + method: 'POST' + }) } -const fetchBlocks = ({page, credentials}) => { - return fetch(BLOCKS_URL, { - headers: authHeaders(credentials) - }).then((data) => { - if (data.ok) { - return data.json() - } - throw new Error('Error fetching blocks', data) +const unmuteUser = ({id, credentials}) => { + return promisedRequest(MASTODON_UNMUTE_USER_URL(id), { + headers: authHeaders(credentials), + method: 'POST' }) } +const fetchBlocks = ({credentials}) => { + return promisedRequest(MASTODON_USER_BLOCKS_URL, { headers: authHeaders(credentials) }) + .then((users) => users.map(parseUser)) +} + const fetchOAuthTokens = ({credentials}) => { const url = '/api/oauth_tokens.json' @@ -618,8 +612,9 @@ const apiService = { deleteStatus, uploadMedia, fetchAllFollowing, - setUserMute, fetchMutes, + muteUser, + unmuteUser, fetchBlocks, fetchOAuthTokens, revokeOAuthToken, diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js @@ -62,12 +62,10 @@ const backendInteractorService = (credentials) => { return timelineFetcherService.startFetching({timeline, store, credentials, userId, tag}) } - const setUserMute = ({id, muted = true}) => { - return apiService.setUserMute({id, muted, credentials}) - } - const fetchMutes = () => apiService.fetchMutes({credentials}) - const fetchBlocks = (params) => apiService.fetchBlocks({credentials, ...params}) + const muteUser = (id) => apiService.muteUser({credentials, id}) + const unmuteUser = (id) => apiService.unmuteUser({credentials, id}) + const fetchBlocks = () => apiService.fetchBlocks({credentials}) const fetchFollowRequests = () => apiService.fetchFollowRequests({credentials}) const fetchOAuthTokens = () => apiService.fetchOAuthTokens({credentials}) const revokeOAuthToken = (id) => apiService.revokeOAuthToken({id, credentials}) @@ -100,8 +98,9 @@ const backendInteractorService = (credentials) => { fetchAllFollowing, verifyCredentials: apiService.verifyCredentials, startFetching, - setUserMute, fetchMutes, + muteUser, + unmuteUser, fetchBlocks, fetchOAuthTokens, revokeOAuthToken, diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -19,6 +19,9 @@ const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false const args = { timeline, credentials } const rootState = store.rootState || store.state const timelineData = rootState.statuses.timelines[camelCase(timeline)] + const hideMutedPosts = typeof rootState.config.hideMutedPosts === 'undefined' + ? rootState.instance.hideMutedPosts + : rootState.config.hideMutedPosts if (older) { args['until'] = until || timelineData.minId @@ -28,6 +31,7 @@ const fetchAndUpdate = ({store, credentials, timeline = 'friends', older = false args['userId'] = userId args['tag'] = tag + args['withMuted'] = !hideMutedPosts const numStatusesBeforeFetch = timelineData.statuses.length