diff --git a/src/boot/after_store.js b/src/boot/after_store.js index a5f8c978..f5e84cbc 100644 --- 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/basic_user_card/basic_user_card.vue b/src/components/basic_user_card/basic_user_card.vue index 9b80c72b..8afe8b44 100644 --- a/src/components/basic_user_card/basic_user_card.vue +++ b/src/components/basic_user_card/basic_user_card.vue @@ -8,8 +8,8 @@
- - {{ user.name }} + + {{ user.name }}
@@ -52,6 +52,14 @@ width: 16px; vertical-align: middle; } + + &-value { + display: inline-block; + max-width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } } &-expanded-content { diff --git a/src/components/conversation-page/conversation-page.js b/src/components/conversation-page/conversation-page.js index 8f1ac3d9..1da70ce9 100644 --- a/src/components/conversation-page/conversation-page.js +++ b/src/components/conversation-page/conversation-page.js @@ -1,5 +1,4 @@ import Conversation from '../conversation/conversation.vue' -import { find } from 'lodash' const conversationPage = { components: { @@ -8,8 +7,8 @@ const conversationPage = { computed: { statusoid () { const id = this.$route.params.id - const statuses = this.$store.state.statuses.allStatuses - const status = find(statuses, {id}) + const statuses = this.$store.state.statuses.allStatusesObject + const status = statuses[id] return status } diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 48b8aaaa..e806be8e 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -1,4 +1,5 @@ import { reduce, filter } from 'lodash' +import { set } from 'vue' import Status from '../status/status.vue' const sortById = (a, b) => { @@ -25,7 +26,8 @@ const sortAndFilterConversation = (conversation) => { const conversation = { data () { return { - highlight: null + highlight: null, + converationStatusIds: [] } }, props: [ @@ -36,6 +38,15 @@ const conversation = { status () { return this.statusoid }, + idsToShow () { + if (this.converationStatusIds.length > 0) { + return this.converationStatusIds + } else if (this.statusId) { + return [this.statusId] + } else { + return [] + } + }, statusId () { if (this.statusoid.retweeted_status) { return this.statusoid.retweeted_status.id @@ -48,9 +59,11 @@ const conversation = { return [] } - const conversationId = this.status.statusnet_conversation_id - const statuses = this.$store.state.statuses.allStatuses - const conversation = filter(statuses, { statusnet_conversation_id: conversationId }) + const statusesObject = this.$store.state.statuses.allStatusesObject + const conversation = this.idsToShow.reduce((acc, id) => { + acc.push(statusesObject[id]) + return acc + }, []) return sortAndFilterConversation(conversation) }, replies () { @@ -83,9 +96,15 @@ const conversation = { methods: { fetchConversation () { if (this.status) { - const conversationId = this.status.statusnet_conversation_id - this.$store.state.api.backendInteractor.fetchConversation({id: conversationId}) - .then((statuses) => this.$store.dispatch('addNewStatuses', { statuses })) + this.$store.state.api.backendInteractor.fetchConversation({id: this.status.id}) + .then(({ancestors, descendants}) => { + this.$store.dispatch('addNewStatuses', { statuses: ancestors }) + this.$store.dispatch('addNewStatuses', { statuses: descendants }) + set(this, 'converationStatusIds', [].concat( + ancestors.map(_ => _.id), + this.statusId, + descendants.map(_ => _.id))) + }) .then(() => this.setHighlight(this.statusId)) } else { const id = this.$route.params.id diff --git a/src/components/follow_card/follow_card.vue b/src/components/follow_card/follow_card.vue index 9bd21cfd..9f314fd3 100644 --- a/src/components/follow_card/follow_card.vue +++ b/src/components/follow_card/follow_card.vue @@ -4,12 +4,12 @@ {{ isMe ? $t('user_card.its_you') : $t('user_card.follows_you') }} -
+ -
@@ -287,8 +287,6 @@ img { width: 24px; height: 24px; - border-radius: $fallback--avatarRadius; - border-radius: var(--avatarRadius, $fallback--avatarRadius); object-fit: contain; } diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js index b77c5197..1d5f75ed 100644 --- 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 index 17f1f1a1..33dad549 100644 --- a/src/components/settings/settings.vue +++ b/src/components/settings/settings.vue @@ -36,6 +36,10 @@

{{$t('nav.timeline')}}

    +
  • + + +
- - diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue index a1123638..c9e68808 100644 --- a/src/components/user_settings/user_settings.vue +++ b/src/components/user_settings/user_settings.vue @@ -192,6 +192,12 @@
+ +
+ + + +
diff --git a/src/i18n/en.json b/src/i18n/en.json index 68503f99..ecf9628f 100644 --- 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/lib/persisted_state.js b/src/lib/persisted_state.js index 7ab89c12..720ff706 100644 --- a/src/lib/persisted_state.js +++ b/src/lib/persisted_state.js @@ -60,6 +60,9 @@ export default function createPersistedState ({ merge({}, store.state, savedState) ) } + if (store.state.oauth.token) { + store.dispatch('loginUser', store.state.oauth.token) + } loaded = true } catch (e) { console.log("Couldn't load state") diff --git a/src/modules/config.js b/src/modules/config.js index 1c30c203..c5491c01 100644 --- 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 index 155aa2eb..f778ac4d 100644 --- 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/statuses.js b/src/modules/statuses.js index f14b8703..a16342e0 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -1,4 +1,5 @@ import { remove, slice, each, find, maxBy, minBy, merge, first, last, isArray, omitBy } from 'lodash' +import { set } from 'vue' import apiService from '../services/api/api.service.js' // import parse from '../services/status_parser/status_parser.js' @@ -82,7 +83,7 @@ const mergeOrAdd = (arr, obj, item) => { // This is a new item, prepare it prepareStatus(item) arr.push(item) - obj[item.id] = item + set(obj, item.id, item) return {item, new: true} } } diff --git a/src/modules/users.js b/src/modules/users.js index 1fe12fc8..5cfa128e 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -16,9 +16,9 @@ export const mergeOrAdd = (arr, obj, item) => { } else { // This is a new item, prepare it arr.push(item) - obj[item.id] = item + set(obj, item.id, item) if (item.screen_name && !item.screen_name.includes('@')) { - obj[item.screen_name.toLowerCase()] = item + set(obj, item.screen_name.toLowerCase(), item) } return { item, new: true } } @@ -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 index 9393e6f1..4ebfb7b7 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -8,19 +8,13 @@ const FAVORITE_URL = '/api/favorites/create' const UNFAVORITE_URL = '/api/favorites/destroy' const RETWEET_URL = '/api/statuses/retweet' const UNRETWEET_URL = '/api/statuses/unretweet' -const STATUS_UPDATE_URL = '/api/statuses/update.json' const STATUS_DELETE_URL = '/api/statuses/destroy' -const STATUS_URL = '/api/statuses/show' -const MEDIA_UPLOAD_URL = '/api/statusnet/media/upload' -const CONVERSATION_URL = '/api/statusnet/conversation' 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' @@ -29,8 +23,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' @@ -41,12 +33,22 @@ const SUGGESTIONS_URL = '/api/v1/suggestions' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' const MASTODON_USER_HOME_TIMELINE_URL = '/api/v1/timelines/home' +const MASTODON_STATUS_URL = id => `/api/v1/statuses/${id}` +const MASTODON_STATUS_CONTEXT_URL = id => `/api/v1/statuses/${id}/context` 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` +const MASTODON_POST_STATUS_URL = '/api/v1/statuses' +const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media' import { each, map } from 'lodash' -import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js' +import { parseStatus, parseUser, parseNotification, parseAttachment } from '../entity_normalizer/entity_normalizer.service.js' import 'whatwg-fetch' import { StatusCodeError } from '../errors/errors' @@ -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)) } @@ -313,8 +317,8 @@ const fetchFollowRequests = ({credentials}) => { } const fetchConversation = ({id, credentials}) => { - let url = `${CONVERSATION_URL}/${id}.json?count=100` - return fetch(url, { headers: authHeaders(credentials) }) + let urlContext = MASTODON_STATUS_CONTEXT_URL(id) + return fetch(urlContext, { headers: authHeaders(credentials) }) .then((data) => { if (data.ok) { return data @@ -322,11 +326,14 @@ const fetchConversation = ({id, credentials}) => { throw new Error('Error fetching timeline', data) }) .then((data) => data.json()) - .then((data) => data.map(parseStatus)) + .then(({ancestors, descendants}) => ({ + ancestors: ancestors.map(parseStatus), + descendants: descendants.map(parseStatus) + })) } const fetchStatus = ({id, credentials}) => { - let url = `${STATUS_URL}/${id}.json` + let url = MASTODON_STATUS_URL(id) return fetch(url, { headers: authHeaders(credentials) }) .then((data) => { if (data.ok) { @@ -338,23 +345,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: MASTODON_USER_HOME_TIMELINE_URL, @@ -390,6 +381,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}` @@ -450,23 +442,23 @@ const unretweet = ({ id, credentials }) => { }) } -const postStatus = ({credentials, status, spoilerText, visibility, sensitive, mediaIds, inReplyToStatusId, contentType, noAttachmentLinks}) => { - const idsText = mediaIds.join(',') +const postStatus = ({credentials, status, spoilerText, visibility, sensitive, mediaIds = [], inReplyToStatusId, contentType}) => { const form = new FormData() form.append('status', status) form.append('source', 'Pleroma FE') - if (noAttachmentLinks) form.append('no_attachment_links', noAttachmentLinks) if (spoilerText) form.append('spoiler_text', spoilerText) if (visibility) form.append('visibility', visibility) if (sensitive) form.append('sensitive', sensitive) if (contentType) form.append('content_type', contentType) - form.append('media_ids', idsText) + mediaIds.forEach(val => { + form.append('media_ids[]', val) + }) if (inReplyToStatusId) { - form.append('in_reply_to_status_id', inReplyToStatusId) + form.append('in_reply_to_id', inReplyToStatusId) } - return fetch(STATUS_UPDATE_URL, { + return fetch(MASTODON_POST_STATUS_URL, { body: form, method: 'POST', headers: authHeaders(credentials) @@ -491,13 +483,13 @@ const deleteStatus = ({ id, credentials }) => { } const uploadMedia = ({formData, credentials}) => { - return fetch(MEDIA_UPLOAD_URL, { + return fetch(MASTODON_MEDIA_UPLOAD_URL, { body: formData, method: 'POST', headers: authHeaders(credentials) }) - .then((response) => response.text()) - .then((text) => (new DOMParser()).parseFromString(text, 'application/xml')) + .then((data) => data.json()) + .then((data) => parseAttachment(data)) } const followImport = ({params, credentials}) => { @@ -538,24 +530,29 @@ const changePassword = ({credentials, password, newPassword, newPasswordConfirma } const fetchMutes = ({credentials}) => { - const url = '/api/qvitter/mutes.json' - - return fetch(url, { - headers: authHeaders(credentials) - }).then((data) => data.json()) + return promisedRequest(MASTODON_USER_MUTES_URL, { headers: authHeaders(credentials) }) + .then((users) => users.map(parseUser)) } -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 muteUser = ({id, credentials}) => { + return promisedRequest(MASTODON_MUTE_USER_URL(id), { + headers: authHeaders(credentials), + method: 'POST' }) } +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 +615,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 index cbd0b733..0f0bcddc 100644 --- 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/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index e831963a..5cac3463 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -128,14 +128,15 @@ export const parseUser = (data) => { return output } -const parseAttachment = (data) => { +export const parseAttachment = (data) => { const output = {} const masto = !data.hasOwnProperty('oembed') if (masto) { // Not exactly same... - output.mimetype = data.type + output.mimetype = data.pleroma ? data.pleroma.mime_type : data.type output.meta = data.meta // not present in BE yet + output.id = data.id } else { output.mimetype = data.mimetype // output.meta = ??? missing @@ -293,5 +294,5 @@ export const parseNotification = (data) => { const isNsfw = (status) => { const nsfwRegex = /#nsfw/i - return (status.tags || []).includes('nsfw') || !!status.text.match(nsfwRegex) + return (status.tags || []).includes('nsfw') || !!(status.text || '').match(nsfwRegex) } diff --git a/src/services/new_api/user_search.js b/src/services/new_api/user_search.js index ce7da88e..869afa9c 100644 --- a/src/services/new_api/user_search.js +++ b/src/services/new_api/user_search.js @@ -1,13 +1,16 @@ import utils from './utils.js' +import { parseUser } from '../entity_normalizer/entity_normalizer.service.js' const search = ({query, store}) => { return utils.request({ store, - url: '/api/pleroma/search_user', + url: '/api/v1/accounts/search', params: { - query + q: query } - }).then((data) => data.json()) + }) + .then((data) => data.json()) + .then((data) => data.map(parseUser)) } const UserSearch = { search diff --git a/src/services/status_poster/status_poster.service.js b/src/services/status_poster/status_poster.service.js index f1932bb6..e70b0f26 100644 --- a/src/services/status_poster/status_poster.service.js +++ b/src/services/status_poster/status_poster.service.js @@ -4,7 +4,7 @@ import apiService from '../api/api.service.js' const postStatus = ({ store, status, spoilerText, visibility, sensitive, media = [], inReplyToStatusId = undefined, contentType = 'text/plain' }) => { const mediaIds = map(media, 'id') - return apiService.postStatus({credentials: store.state.users.currentUser.credentials, status, spoilerText, visibility, sensitive, mediaIds, inReplyToStatusId, contentType, noAttachmentLinks: store.state.instance.noAttachmentLinks}) + return apiService.postStatus({credentials: store.state.users.currentUser.credentials, status, spoilerText, visibility, sensitive, mediaIds, inReplyToStatusId, contentType}) .then((data) => { if (!data.error) { store.dispatch('addNewStatuses', { @@ -26,25 +26,7 @@ const postStatus = ({ store, status, spoilerText, visibility, sensitive, media = const uploadMedia = ({ store, formData }) => { const credentials = store.state.users.currentUser.credentials - return apiService.uploadMedia({ credentials, formData }).then((xml) => { - // Firefox and Chrome treat method differently... - let link = xml.getElementsByTagName('link') - - if (link.length === 0) { - link = xml.getElementsByTagName('atom:link') - } - - link = link[0] - - const mediaData = { - id: xml.getElementsByTagName('media_id')[0].textContent, - url: xml.getElementsByTagName('media_url')[0].textContent, - image: link.getAttribute('href'), - mimetype: link.getAttribute('type') - } - - return mediaData - }) + return apiService.uploadMedia({ credentials, formData }) } const statusPosterService = { diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 6f99616f..8e954cdf 100644 --- 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