diff options
Diffstat (limited to 'src/services')
| -rw-r--r-- | src/services/api/api.service.js | 142 | ||||
| -rw-r--r-- | src/services/backend_interactor_service/backend_interactor_service.js | 16 | ||||
| -rw-r--r-- | src/services/entity_normalizer/entity_normalizer.service.js | 50 | ||||
| -rw-r--r-- | src/services/new_api/user_search.js | 9 | ||||
| -rw-r--r-- | src/services/status_poster/status_poster.service.js | 22 | ||||
| -rw-r--r-- | src/services/timeline_fetcher/timeline_fetcher.service.js | 4 | ||||
| -rw-r--r-- | src/services/version/version.service.js | 6 |
7 files changed, 145 insertions, 104 deletions
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js index e2f44a2b..eac151eb 100644 --- a/src/services/api/api.service.js +++ b/src/services/api/api.service.js @@ -5,25 +5,18 @@ const ALL_FOLLOWING_URL = '/api/qvitter/allfollowing' const PUBLIC_TIMELINE_URL = '/api/statuses/public_timeline.json' const PUBLIC_AND_EXTERNAL_TIMELINE_URL = '/api/statuses/public_and_external_timeline.json' const TAG_TIMELINE_URL = '/api/statusnet/tags/timeline' -const STATUS_UPDATE_URL = '/api/statuses/update.json' -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 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' const BANNER_UPDATE_URL = '/api/account/update_profile_banner.json' const PROFILE_UPDATE_URL = '/api/account/update_profile.json' const EXTERNAL_PROFILE_URL = '/api/externalprofile/show.json' -const QVITTER_USER_TIMELINE_URL = '/api/qvitter/statuses/user_timeline.json' const QVITTER_USER_NOTIFICATIONS_URL = '/api/qvitter/statuses/notifications.json' const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json' -const USER_URL = '/api/users/show.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,9 +37,22 @@ const MASTODON_BLOCK_URL = id => `/api/v1/accounts/${id}/block` const MASTODON_UNBLOCK_URL = id => `/api/v1/accounts/${id}/unblock` const MASTODON_MUTE_URL = id => `/api/v1/accounts/${id}/mute` const MASTODON_UNMUTE_URL = id => `/api/v1/accounts/${id}/unmute` +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 +66,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 +231,14 @@ const unfollowUser = ({id, credentials}) => { } const blockUser = ({id, credentials}) => { - let url = MASTODON_BLOCK_URL(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 = MASTODON_UNBLOCK_URL(id) - return fetch(url, { + return fetch(MASTODON_UNBLOCK_USER_URL(id), { headers: authHeaders(credentials), method: 'POST' }).then((data) => data.json()) @@ -244,7 +261,13 @@ const denyUser = ({id, credentials}) => { } const fetchUser = ({id, credentials}) => { - let url = `${USER_URL}?user_id=${id}` + let url = `${MASTODON_USER_URL}/${id}` + return promisedRequest(url, { headers: authHeaders(credentials) }) + .then((data) => parseUser(data)) +} + +const fetchUserRelationship = ({id, credentials}) => { + let url = `${MASTODON_USER_RELATIONSHIPS_URL}/?id=${id}` return fetch(url, { headers: authHeaders(credentials) }) .then((response) => { return new Promise((resolve, reject) => response.json() @@ -255,7 +278,6 @@ const fetchUser = ({id, credentials}) => { return resolve(json) })) }) - .then((data) => parseUser(data)) } const fetchFriends = ({id, page, credentials}) => { @@ -299,8 +321,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 @@ -308,11 +330,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) { @@ -324,16 +349,7 @@ const fetchStatus = ({id, credentials}) => { .then((data) => parseStatus(data)) } -const setUserMute = ({id, credentials, muted = true}) => { - const url = muted ? MASTODON_MUTE_URL(id) : MASTODON_UNMUTE_URL(id) - - return fetch(url, { - method: 'POST', - headers: authHeaders(credentials) - }) -} - -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, @@ -341,8 +357,8 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use dms: DM_TIMELINE_URL, notifications: QVITTER_USER_NOTIFICATIONS_URL, 'publicAndExternal': PUBLIC_AND_EXTERNAL_TIMELINE_URL, - user: QVITTER_USER_TIMELINE_URL, - media: QVITTER_USER_TIMELINE_URL, + user: MASTODON_USER_TIMELINE_URL, + media: MASTODON_USER_TIMELINE_URL, favorites: MASTODON_USER_FAVORITES_TIMELINE_URL, tag: TAG_TIMELINE_URL } @@ -351,15 +367,16 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use let url = timelineUrls[timeline] + if (timeline === 'user' || timeline === 'media') { + url = url(userId) + } + if (since) { params.push(['since_id', since]) } if (until) { params.push(['max_id', until]) } - if (userId) { - params.push(['user_id', userId]) - } if (tag) { url += `/${tag}.json` } @@ -368,6 +385,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}` @@ -460,23 +478,23 @@ const unretweet = ({ id, credentials }) => { .then((data) => parseStatus(data)) } -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) @@ -501,13 +519,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}) => { @@ -548,30 +566,40 @@ 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' return fetch(url, { headers: authHeaders(credentials) - }).then((data) => data.json()) + }).then((data) => { + if (data.ok) { + return data.json() + } + throw new Error('Error fetching auth tokens', data) + }) } const revokeOAuthToken = ({id, credentials}) => { @@ -614,6 +642,7 @@ const apiService = { blockUser, unblockUser, fetchUser, + fetchUserRelationship, favorite, unfavorite, retweet, @@ -622,8 +651,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 7e972d7b..0f0bcddc 100644 --- a/src/services/backend_interactor_service/backend_interactor_service.js +++ b/src/services/backend_interactor_service/backend_interactor_service.js @@ -30,6 +30,10 @@ const backendInteractorService = (credentials) => { return apiService.fetchUser({id, credentials}) } + const fetchUserRelationship = ({id}) => { + return apiService.fetchUserRelationship({id, credentials}) + } + const followUser = (id) => { return apiService.followUser({credentials, id}) } @@ -58,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}) @@ -92,11 +94,13 @@ const backendInteractorService = (credentials) => { blockUser, unblockUser, fetchUser, + fetchUserRelationship, 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 d20ce77f..5cac3463 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -39,11 +39,11 @@ export const parseUser = (data) => { return output } - output.name = null // missing - output.name_html = data.display_name + // output.name = ??? missing + output.name_html = addEmojis(data.display_name, data.emojis) - output.description = null // missing - output.description_html = data.note + // output.description = ??? missing + output.description_html = addEmojis(data.note, data.emojis) // Utilize avatar_static for gif avatars? output.profile_image_url = data.avatar @@ -59,10 +59,14 @@ export const parseUser = (data) => { output.statusnet_profile_url = data.url if (data.pleroma) { - const pleroma = data.pleroma - output.follows_you = pleroma.follows_you - output.statusnet_blocking = pleroma.statusnet_blocking - output.muted = pleroma.muted + const relationship = data.pleroma.relationship + + if (relationship) { + output.follows_you = relationship.followed_by + output.following = relationship.following + output.statusnet_blocking = relationship.blocking + output.muted = relationship.muting + } } // Missing, trying to recover @@ -83,7 +87,7 @@ export const parseUser = (data) => { output.friends_count = data.friends_count - output.bot = null // missing + // output.bot = ??? missing output.statusnet_profile_url = data.statusnet_profile_url @@ -124,17 +128,18 @@ 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 = null // missing + // output.meta = ??? missing } output.url = data.url @@ -142,6 +147,14 @@ const parseAttachment = (data) => { return output } +export const addEmojis = (string, emojis) => { + return emojis.reduce((acc, emoji) => { + return acc.replace( + new RegExp(`:${emoji.shortcode}:`, 'g'), + `<img src='${emoji.url}' alt='${emoji.shortcode}' class='emoji' />` + ) + }, string) +} export const parseStatus = (data) => { const output = {} @@ -157,7 +170,7 @@ export const parseStatus = (data) => { output.type = data.reblog ? 'retweet' : 'status' output.nsfw = data.sensitive - output.statusnet_html = data.content + output.statusnet_html = addEmojis(data.content, data.emojis) // Not exactly the same but works? output.text = data.content @@ -166,7 +179,7 @@ export const parseStatus = (data) => { output.in_reply_to_user_id = data.in_reply_to_account_id // Missing!! fix in UI? - output.in_reply_to_screen_name = null + // output.in_reply_to_screen_name = ??? // Not exactly the same but works output.statusnet_conversation_id = data.id @@ -176,11 +189,10 @@ export const parseStatus = (data) => { } output.summary = data.spoiler_text - output.summary_html = data.spoiler_text + output.summary_html = addEmojis(data.spoiler_text, data.emojis) output.external_url = data.url - // FIXME missing!! - output.is_local = false + // output.is_local = ??? missing } else { output.favorited = data.favorited output.fave_num = data.fave_num @@ -259,7 +271,7 @@ export const parseNotification = (data) => { if (masto) { output.type = mastoDict[data.type] || data.type - output.seen = null // missing + // output.seen = ??? missing output.status = parseStatus(data.status) output.action = output.status // not sure output.from_profile = parseUser(data.account) @@ -282,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 diff --git a/src/services/version/version.service.js b/src/services/version/version.service.js new file mode 100644 index 00000000..a750b0dd --- /dev/null +++ b/src/services/version/version.service.js @@ -0,0 +1,6 @@ + +export const extractCommit = versionString => { + const regex = /-g(\w+)$/i + const matches = versionString.match(regex) + return matches ? matches[1] : '' +} |
