aboutsummaryrefslogtreecommitdiff
path: root/src/services/api/api.service.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/services/api/api.service.js')
-rw-r--r--src/services/api/api.service.js857
1 files changed, 610 insertions, 247 deletions
diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js
index 2de87026..887d7d7a 100644
--- a/src/services/api/api.service.js
+++ b/src/services/api/api.service.js
@@ -1,53 +1,75 @@
+import { each, map, concat, last } from 'lodash'
+import { parseStatus, parseUser, parseNotification, parseAttachment } from '../entity_normalizer/entity_normalizer.service.js'
+import 'whatwg-fetch'
+import { RegistrationError, StatusCodeError } from '../errors/errors'
+
/* eslint-env browser */
-const LOGIN_URL = '/api/account/verify_credentials.json'
-const FRIENDS_TIMELINE_URL = '/api/statuses/friends_timeline.json'
-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 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'
-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 BLOCKING_URL = '/api/blocks/create.json'
-const UNBLOCKING_URL = '/api/blocks/destroy.json'
-const USER_URL = '/api/users/show.json'
+const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import'
const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import'
const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account'
const CHANGE_PASSWORD_URL = '/api/pleroma/change_password'
-const FOLLOW_REQUESTS_URL = '/api/pleroma/friend_requests'
-const APPROVE_USER_URL = '/api/pleroma/friendships/approve'
-const DENY_USER_URL = '/api/pleroma/friendships/deny'
+const TAG_USER_URL = '/api/pleroma/admin/users/tag'
+const PERMISSION_GROUP_URL = (screenName, right) => `/api/pleroma/admin/users/${screenName}/permission_group/${right}`
+const ACTIVATION_STATUS_URL = screenName => `/api/pleroma/admin/users/${screenName}/activation_status`
+const ADMIN_USERS_URL = '/api/pleroma/admin/users'
const SUGGESTIONS_URL = '/api/v1/suggestions'
+const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
-const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites'
+const MFA_SETTINGS_URL = '/api/pleroma/profile/mfa'
+const MFA_BACKUP_CODES_URL = '/api/pleroma/profile/mfa/backup_codes'
-import { each, map } from 'lodash'
-import { parseStatus, parseUser, parseNotification } from '../entity_normalizer/entity_normalizer.service.js'
-import 'whatwg-fetch'
-import { StatusCodeError } from '../errors/errors'
+const MFA_SETUP_OTP_URL = '/api/pleroma/profile/mfa/setup/totp'
+const MFA_CONFIRM_OTP_URL = '/api/pleroma/profile/mfa/confirm/totp'
+const MFA_DISABLE_OTP_URL = '/api/pleroma/profile/mfa/totp'
+
+const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials'
+const MASTODON_REGISTRATION_URL = '/api/v1/accounts'
+const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites'
+const MASTODON_USER_NOTIFICATIONS_URL = '/api/v1/notifications'
+const MASTODON_FAVORITE_URL = id => `/api/v1/statuses/${id}/favourite`
+const MASTODON_UNFAVORITE_URL = id => `/api/v1/statuses/${id}/unfavourite`
+const MASTODON_RETWEET_URL = id => `/api/v1/statuses/${id}/reblog`
+const MASTODON_UNRETWEET_URL = id => `/api/v1/statuses/${id}/unreblog`
+const MASTODON_DELETE_URL = id => `/api/v1/statuses/${id}`
+const MASTODON_FOLLOW_URL = id => `/api/v1/accounts/${id}/follow`
+const MASTODON_UNFOLLOW_URL = id => `/api/v1/accounts/${id}/unfollow`
+const MASTODON_FOLLOWING_URL = id => `/api/v1/accounts/${id}/following`
+const MASTODON_FOLLOWERS_URL = id => `/api/v1/accounts/${id}/followers`
+const MASTODON_FOLLOW_REQUESTS_URL = '/api/v1/follow_requests'
+const MASTODON_APPROVE_USER_URL = id => `/api/v1/follow_requests/${id}/authorize`
+const MASTODON_DENY_USER_URL = id => `/api/v1/follow_requests/${id}/reject`
+const MASTODON_DIRECT_MESSAGES_TIMELINE_URL = '/api/v1/timelines/direct'
+const MASTODON_PUBLIC_TIMELINE = '/api/v1/timelines/public'
+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_TAG_TIMELINE_URL = tag => `/api/v1/timelines/tag/${tag}`
+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_SUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/subscribe`
+const MASTODON_UNSUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/unsubscribe`
+const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
+const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
+const MASTODON_VOTE_URL = id => `/api/v1/polls/${id}/votes`
+const MASTODON_POLL_URL = id => `/api/v1/polls/${id}`
+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'
+const MASTODON_REPORT_USER_URL = '/api/v1/reports'
+const MASTODON_PIN_OWN_STATUS = id => `/api/v1/statuses/${id}/pin`
+const MASTODON_UNPIN_OWN_STATUS = id => `/api/v1/statuses/${id}/unpin`
+const MASTODON_MUTE_CONVERSATION = id => `/api/v1/statuses/${id}/mute`
+const MASTODON_UNMUTE_CONVERSATION = id => `/api/v1/statuses/${id}/unmute`
+const MASTODON_SEARCH_2 = `/api/v2/search`
+const MASTODON_USER_SEARCH_URL = '/api/v1/accounts/search'
const oldfetch = window.fetch
@@ -59,94 +81,96 @@ let fetch = (url, options) => {
return oldfetch(fullUrl, options)
}
-// Params
-// cropH
-// cropW
-// cropX
-// cropY
-// img (base 64 encodend data url)
-const updateAvatar = ({credentials, params}) => {
- let url = AVATAR_UPDATE_URL
+const promisedRequest = ({ method, url, params, payload, credentials, headers = {} }) => {
+ const options = {
+ method,
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ ...headers
+ }
+ }
+ if (params) {
+ url += '?' + Object.entries(params)
+ .map(([key, value]) => encodeURIComponent(key) + '=' + encodeURIComponent(value))
+ .join('&')
+ }
+ if (payload) {
+ options.body = JSON.stringify(payload)
+ }
+ if (credentials) {
+ options.headers = {
+ ...options.headers,
+ ...authHeaders(credentials)
+ }
+ }
+ 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)
+ }))
+ })
+}
+const updateNotificationSettings = ({ credentials, settings }) => {
const form = new FormData()
- each(params, (value, key) => {
- if (value) {
- form.append(key, value)
- }
+ each(settings, (value, key) => {
+ form.append(key, value)
})
- return fetch(url, {
+ return fetch(NOTIFICATION_SETTINGS_URL, {
headers: authHeaders(credentials),
- method: 'POST',
+ method: 'PUT',
body: form
}).then((data) => data.json())
}
-const updateBg = ({credentials, params}) => {
- let url = BG_UPDATE_URL
-
+const updateAvatar = ({ credentials, avatar }) => {
const form = new FormData()
-
- each(params, (value, key) => {
- if (value) {
- form.append(key, value)
- }
- })
-
- return fetch(url, {
+ form.append('avatar', avatar)
+ return fetch(MASTODON_PROFILE_UPDATE_URL, {
headers: authHeaders(credentials),
- method: 'POST',
+ method: 'PATCH',
body: form
}).then((data) => data.json())
+ .then((data) => parseUser(data))
}
-// Params
-// height
-// width
-// offset_left
-// offset_top
-// banner (base 64 encodend data url)
-const updateBanner = ({credentials, params}) => {
- let url = BANNER_UPDATE_URL
-
+const updateBg = ({ credentials, background }) => {
const form = new FormData()
-
- each(params, (value, key) => {
- if (value) {
- form.append(key, value)
- }
- })
-
- return fetch(url, {
+ form.append('pleroma_background_image', background)
+ return fetch(MASTODON_PROFILE_UPDATE_URL, {
headers: authHeaders(credentials),
- method: 'POST',
+ method: 'PATCH',
body: form
- }).then((data) => data.json())
+ })
+ .then((data) => data.json())
+ .then((data) => parseUser(data))
}
-// Params
-// name
-// url
-// location
-// description
-const updateProfile = ({credentials, params}) => {
- // Always include these fields, because they might be empty or false
- const fields = ['description', 'locked', 'no_rich_text', 'hide_follows', 'hide_followers', 'show_role']
- let url = PROFILE_UPDATE_URL
-
+const updateBanner = ({ credentials, banner }) => {
const form = new FormData()
-
- each(params, (value, key) => {
- if (fields.includes(key) || value) {
- form.append(key, value)
- }
- })
- return fetch(url, {
+ form.append('header', banner)
+ return fetch(MASTODON_PROFILE_UPDATE_URL, {
headers: authHeaders(credentials),
- method: 'POST',
+ method: 'PATCH',
body: form
}).then((data) => data.json())
+ .then((data) => parseUser(data))
+}
+
+const updateProfile = ({ credentials, params }) => {
+ return promisedRequest({
+ url: MASTODON_PROFILE_UPDATE_URL,
+ method: 'PATCH',
+ payload: params,
+ credentials
+ }).then((data) => parseUser(data))
}
// Params needed:
@@ -161,19 +185,28 @@ const updateProfile = ({credentials, params}) => {
// homepage
// location
// token
-const register = (params) => {
- const form = new FormData()
-
- each(params, (value, key) => {
- if (value) {
- form.append(key, value)
- }
- })
-
- return fetch(REGISTRATION_URL, {
+const register = ({ params, credentials }) => {
+ const { nickname, ...rest } = params
+ return fetch(MASTODON_REGISTRATION_URL, {
method: 'POST',
- body: form
+ headers: {
+ ...authHeaders(credentials),
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ nickname,
+ locale: 'en_US',
+ agreement: true,
+ ...rest
+ })
})
+ .then((response) => {
+ if (response.ok) {
+ return response.json()
+ } else {
+ return response.json().then((error) => { throw new RegistrationError(error) })
+ }
+ })
}
const getCaptcha = () => fetch('/api/pleroma/captcha').then(resp => resp.json())
@@ -186,64 +219,80 @@ const authHeaders = (accessToken) => {
}
}
-const externalProfile = ({profileUrl, credentials}) => {
- let url = `${EXTERNAL_PROFILE_URL}?profileurl=${profileUrl}`
+const followUser = ({ id, credentials }) => {
+ let url = MASTODON_FOLLOW_URL(id)
return fetch(url, {
headers: authHeaders(credentials),
- method: 'GET'
+ method: 'POST'
}).then((data) => data.json())
}
-const followUser = ({id, credentials}) => {
- let url = `${FOLLOWING_URL}?user_id=${id}`
+const unfollowUser = ({ id, credentials }) => {
+ let url = MASTODON_UNFOLLOW_URL(id)
return fetch(url, {
headers: authHeaders(credentials),
method: 'POST'
}).then((data) => data.json())
}
-const unfollowUser = ({id, credentials}) => {
- let url = `${UNFOLLOWING_URL}?user_id=${id}`
- return fetch(url, {
- headers: authHeaders(credentials),
- method: 'POST'
- }).then((data) => data.json())
+const pinOwnStatus = ({ id, credentials }) => {
+ return promisedRequest({ url: MASTODON_PIN_OWN_STATUS(id), credentials, method: 'POST' })
+ .then((data) => parseStatus(data))
}
-const blockUser = ({id, credentials}) => {
- let url = `${BLOCKING_URL}?user_id=${id}`
- return fetch(url, {
+const unpinOwnStatus = ({ id, credentials }) => {
+ return promisedRequest({ url: MASTODON_UNPIN_OWN_STATUS(id), credentials, method: 'POST' })
+ .then((data) => parseStatus(data))
+}
+
+const muteConversation = ({ id, credentials }) => {
+ return promisedRequest({ url: MASTODON_MUTE_CONVERSATION(id), credentials, method: 'POST' })
+ .then((data) => parseStatus(data))
+}
+
+const unmuteConversation = ({ id, credentials }) => {
+ return promisedRequest({ url: MASTODON_UNMUTE_CONVERSATION(id), credentials, method: 'POST' })
+ .then((data) => parseStatus(data))
+}
+
+const blockUser = ({ id, credentials }) => {
+ 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, {
+const unblockUser = ({ id, credentials }) => {
+ return fetch(MASTODON_UNBLOCK_USER_URL(id), {
headers: authHeaders(credentials),
method: 'POST'
}).then((data) => data.json())
}
-const approveUser = ({id, credentials}) => {
- let url = `${APPROVE_USER_URL}?user_id=${id}`
+const approveUser = ({ id, credentials }) => {
+ let url = MASTODON_APPROVE_USER_URL(id)
return fetch(url, {
headers: authHeaders(credentials),
method: 'POST'
}).then((data) => data.json())
}
-const denyUser = ({id, credentials}) => {
- let url = `${DENY_USER_URL}?user_id=${id}`
+const denyUser = ({ id, credentials }) => {
+ let url = MASTODON_DENY_USER_URL(id)
return fetch(url, {
headers: authHeaders(credentials),
method: 'POST'
}).then((data) => data.json())
}
-const fetchUser = ({id, credentials}) => {
- let url = `${USER_URL}?user_id=${id}`
+const fetchUser = ({ id, credentials }) => {
+ let url = `${MASTODON_USER_URL}/${id}`
+ return promisedRequest({ url, 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()
@@ -254,52 +303,66 @@ const fetchUser = ({id, credentials}) => {
return resolve(json)
}))
})
- .then((data) => parseUser(data))
}
-const fetchFriends = ({id, page, credentials}) => {
- let url = `${FRIENDS_URL}?user_id=${id}`
- if (page) {
- url = url + `&page=${page}`
- }
- return fetch(url, { headers: authHeaders(credentials) })
- .then((data) => data.json())
- .then((data) => data.map(parseUser))
-}
+const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) => {
+ let url = MASTODON_FOLLOWING_URL(id)
+ const args = [
+ maxId && `max_id=${maxId}`,
+ sinceId && `since_id=${sinceId}`,
+ limit && `limit=${limit}`
+ ].filter(_ => _).join('&')
-const exportFriends = ({id, credentials}) => {
- let url = `${FRIENDS_URL}?user_id=${id}&all=true`
+ url = url + (args ? '?' + args : '')
return fetch(url, { headers: authHeaders(credentials) })
.then((data) => data.json())
.then((data) => data.map(parseUser))
}
-const fetchFollowers = ({id, page, credentials}) => {
- let url = `${FOLLOWERS_URL}?user_id=${id}`
- if (page) {
- url = url + `&page=${page}`
- }
- return fetch(url, { headers: authHeaders(credentials) })
- .then((data) => data.json())
- .then((data) => data.map(parseUser))
+const exportFriends = ({ id, credentials }) => {
+ return new Promise(async (resolve, reject) => {
+ try {
+ let friends = []
+ let more = true
+ while (more) {
+ const maxId = friends.length > 0 ? last(friends).id : undefined
+ const users = await fetchFriends({ id, maxId, credentials })
+ friends = concat(friends, users)
+ if (users.length === 0) {
+ more = false
+ }
+ }
+ resolve(friends)
+ } catch (err) {
+ reject(err)
+ }
+ })
}
-const fetchAllFollowing = ({username, credentials}) => {
- const url = `${ALL_FOLLOWING_URL}/${username}.json`
+const fetchFollowers = ({ id, maxId, sinceId, limit = 20, credentials }) => {
+ let url = MASTODON_FOLLOWERS_URL(id)
+ const args = [
+ maxId && `max_id=${maxId}`,
+ sinceId && `since_id=${sinceId}`,
+ limit && `limit=${limit}`
+ ].filter(_ => _).join('&')
+
+ url += args ? '?' + args : ''
return fetch(url, { headers: authHeaders(credentials) })
.then((data) => data.json())
.then((data) => data.map(parseUser))
}
-const fetchFollowRequests = ({credentials}) => {
- const url = FOLLOW_REQUESTS_URL
+const fetchFollowRequests = ({ credentials }) => {
+ const url = MASTODON_FOLLOW_REQUESTS_URL
return fetch(url, { headers: authHeaders(credentials) })
.then((data) => data.json())
+ .then((data) => data.map(parseUser))
}
-const fetchConversation = ({id, credentials}) => {
- let url = `${CONVERSATION_URL}/${id}.json?count=100`
- return fetch(url, { headers: authHeaders(credentials) })
+const fetchConversation = ({ id, credentials }) => {
+ let urlContext = MASTODON_STATUS_CONTEXT_URL(id)
+ return fetch(urlContext, { headers: authHeaders(credentials) })
.then((data) => {
if (data.ok) {
return data
@@ -307,11 +370,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`
+const fetchStatus = ({ id, credentials }) => {
+ let url = MASTODON_STATUS_URL(id)
return fetch(url, { headers: authHeaders(credentials) })
.then((data) => {
if (data.ok) {
@@ -323,57 +389,136 @@ const fetchStatus = ({id, credentials}) => {
.then((data) => parseStatus(data))
}
-const setUserMute = ({id, credentials, muted = true}) => {
- const form = new FormData()
+const tagUser = ({ tag, credentials, ...options }) => {
+ const screenName = options.screen_name
+ const form = {
+ nicknames: [screenName],
+ tags: [tag]
+ }
- const muteInteger = muted ? 1 : 0
+ const headers = authHeaders(credentials)
+ headers['Content-Type'] = 'application/json'
- form.append('namespace', 'qvitter')
- form.append('data', muteInteger)
- form.append('topic', `mute:${id}`)
+ return fetch(TAG_USER_URL, {
+ method: 'PUT',
+ headers: headers,
+ body: JSON.stringify(form)
+ })
+}
+
+const untagUser = ({ tag, credentials, ...options }) => {
+ const screenName = options.screen_name
+ const body = {
+ nicknames: [screenName],
+ tags: [tag]
+ }
- return fetch(QVITTER_USER_PREF_URL, {
+ const headers = authHeaders(credentials)
+ headers['Content-Type'] = 'application/json'
+
+ return fetch(TAG_USER_URL, {
+ method: 'DELETE',
+ headers: headers,
+ body: JSON.stringify(body)
+ })
+}
+
+const addRight = ({ right, credentials, ...user }) => {
+ const screenName = user.screen_name
+
+ return fetch(PERMISSION_GROUP_URL(screenName, right), {
method: 'POST',
headers: authHeaders(credentials),
- body: form
+ body: {}
})
}
-const fetchTimeline = ({timeline, credentials, since = false, until = false, userId = false, tag = false}) => {
+const deleteRight = ({ right, credentials, ...user }) => {
+ const screenName = user.screen_name
+
+ return fetch(PERMISSION_GROUP_URL(screenName, right), {
+ method: 'DELETE',
+ headers: authHeaders(credentials),
+ body: {}
+ })
+}
+
+const setActivationStatus = ({ status, credentials, ...user }) => {
+ const screenName = user.screen_name
+ const body = {
+ status: status
+ }
+
+ const headers = authHeaders(credentials)
+ headers['Content-Type'] = 'application/json'
+
+ return fetch(ACTIVATION_STATUS_URL(screenName), {
+ method: 'PUT',
+ headers: headers,
+ body: JSON.stringify(body)
+ })
+}
+
+const deleteUser = ({ credentials, ...user }) => {
+ const screenName = user.screen_name
+ const headers = authHeaders(credentials)
+
+ return fetch(`${ADMIN_USERS_URL}?nickname=${screenName}`, {
+ method: 'DELETE',
+ headers: headers
+ })
+}
+
+const fetchTimeline = ({
+ timeline,
+ credentials,
+ since = false,
+ until = false,
+ userId = false,
+ tag = false,
+ withMuted = false
+}) => {
const timelineUrls = {
- public: PUBLIC_TIMELINE_URL,
- friends: FRIENDS_TIMELINE_URL,
- mentions: MENTIONS_URL,
- 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,
+ public: MASTODON_PUBLIC_TIMELINE,
+ friends: MASTODON_USER_HOME_TIMELINE_URL,
+ dms: MASTODON_DIRECT_MESSAGES_TIMELINE_URL,
+ notifications: MASTODON_USER_NOTIFICATIONS_URL,
+ 'publicAndExternal': MASTODON_PUBLIC_TIMELINE,
+ user: MASTODON_USER_TIMELINE_URL,
+ media: MASTODON_USER_TIMELINE_URL,
favorites: MASTODON_USER_FAVORITES_TIMELINE_URL,
- tag: TAG_TIMELINE_URL
+ tag: MASTODON_TAG_TIMELINE_URL
}
const isNotifications = timeline === 'notifications'
const params = []
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`
+ url = url(tag)
}
if (timeline === 'media') {
params.push(['only_media', 1])
}
+ if (timeline === 'public') {
+ params.push(['local', true])
+ }
+ if (timeline === 'public' || timeline === 'publicAndExternal') {
+ params.push(['only_media', false])
+ }
params.push(['count', 20])
+ params.push(['with_muted', withMuted])
const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
url += `?${queryString}`
@@ -389,9 +534,14 @@ const fetchTimeline = ({timeline, credentials, since = false, until = false, use
.then((data) => data.map(isNotifications ? parseNotification : parseStatus))
}
+const fetchPinnedStatuses = ({ id, credentials }) => {
+ const url = MASTODON_USER_TIMELINE_URL(id) + '?pinned=true'
+ return promisedRequest({ url, credentials })
+ .then((data) => data.map(parseStatus))
+}
+
const verifyCredentials = (user) => {
- return fetch(LOGIN_URL, {
- method: 'POST',
+ return fetch(MASTODON_LOGIN_URL, {
headers: authHeaders(user)
})
.then((response) => {
@@ -407,50 +557,66 @@ const verifyCredentials = (user) => {
}
const favorite = ({ id, credentials }) => {
- return fetch(`${FAVORITE_URL}/${id}.json`, {
- headers: authHeaders(credentials),
- method: 'POST'
- })
+ return promisedRequest({ url: MASTODON_FAVORITE_URL(id), method: 'POST', credentials })
+ .then((data) => parseStatus(data))
}
const unfavorite = ({ id, credentials }) => {
- return fetch(`${UNFAVORITE_URL}/${id}.json`, {
- headers: authHeaders(credentials),
- method: 'POST'
- })
+ return promisedRequest({ url: MASTODON_UNFAVORITE_URL(id), method: 'POST', credentials })
+ .then((data) => parseStatus(data))
}
const retweet = ({ id, credentials }) => {
- return fetch(`${RETWEET_URL}/${id}.json`, {
- headers: authHeaders(credentials),
- method: 'POST'
- })
+ return promisedRequest({ url: MASTODON_RETWEET_URL(id), method: 'POST', credentials })
+ .then((data) => parseStatus(data))
}
const unretweet = ({ id, credentials }) => {
- return fetch(`${UNRETWEET_URL}/${id}.json`, {
- headers: authHeaders(credentials),
- method: 'POST'
- })
+ return promisedRequest({ url: MASTODON_UNRETWEET_URL(id), method: 'POST', 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,
+ poll,
+ mediaIds = [],
+ inReplyToStatusId,
+ contentType
+}) => {
const form = new FormData()
+ const pollOptions = poll.options || []
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 (pollOptions.some(option => option !== '')) {
+ const normalizedPoll = {
+ expires_in: poll.expiresIn,
+ multiple: poll.multiple
+ }
+ Object.keys(normalizedPoll).forEach(key => {
+ form.append(`poll[${key}]`, normalizedPoll[key])
+ })
+
+ pollOptions.forEach(option => {
+ form.append('poll[options][]', option)
+ })
+ }
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)
@@ -468,32 +634,45 @@ const postStatus = ({credentials, status, spoilerText, visibility, sensitive, me
}
const deleteStatus = ({ id, credentials }) => {
- return fetch(`${STATUS_DELETE_URL}/${id}.json`, {
+ return fetch(MASTODON_DELETE_URL(id), {
headers: authHeaders(credentials),
- method: 'POST'
+ method: 'DELETE'
+ })
+}
+
+const uploadMedia = ({ formData, credentials }) => {
+ return fetch(MASTODON_MEDIA_UPLOAD_URL, {
+ body: formData,
+ method: 'POST',
+ headers: authHeaders(credentials)
})
+ .then((data) => data.json())
+ .then((data) => parseAttachment(data))
}
-const uploadMedia = ({formData, credentials}) => {
- return fetch(MEDIA_UPLOAD_URL, {
+const importBlocks = ({ file, credentials }) => {
+ const formData = new FormData()
+ formData.append('list', file)
+ return fetch(BLOCKS_IMPORT_URL, {
body: formData,
method: 'POST',
headers: authHeaders(credentials)
})
- .then((response) => response.text())
- .then((text) => (new DOMParser()).parseFromString(text, 'application/xml'))
+ .then((response) => response.ok)
}
-const followImport = ({params, credentials}) => {
+const importFollows = ({ file, credentials }) => {
+ const formData = new FormData()
+ formData.append('list', file)
return fetch(FOLLOW_IMPORT_URL, {
- body: params,
+ body: formData,
method: 'POST',
headers: authHeaders(credentials)
})
.then((response) => response.ok)
}
-const deleteAccount = ({credentials, password}) => {
+const deleteAccount = ({ credentials, password }) => {
const form = new FormData()
form.append('password', password)
@@ -506,7 +685,7 @@ const deleteAccount = ({credentials, password}) => {
.then((response) => response.json())
}
-const changePassword = ({credentials, password, newPassword, newPasswordConfirmation}) => {
+const changePassword = ({ credentials, password, newPassword, newPasswordConfirmation }) => {
const form = new FormData()
form.append('password', password)
@@ -521,34 +700,91 @@ const changePassword = ({credentials, password, newPassword, newPasswordConfirma
.then((response) => response.json())
}
-const fetchMutes = ({credentials}) => {
- const url = '/api/qvitter/mutes.json'
-
- return fetch(url, {
- headers: authHeaders(credentials)
+const settingsMFA = ({ credentials }) => {
+ return fetch(MFA_SETTINGS_URL, {
+ headers: authHeaders(credentials),
+ method: 'GET'
}).then((data) => data.json())
}
-const fetchBlocks = ({page, credentials}) => {
- return fetch(BLOCKS_URL, {
+const mfaDisableOTP = ({ credentials, password }) => {
+ const form = new FormData()
+
+ form.append('password', password)
+
+ return fetch(MFA_DISABLE_OTP_URL, {
+ body: form,
+ method: 'DELETE',
headers: authHeaders(credentials)
- }).then((data) => {
- if (data.ok) {
- return data.json()
- }
- throw new Error('Error fetching blocks', data)
})
+ .then((response) => response.json())
}
-const fetchOAuthTokens = ({credentials}) => {
+const mfaConfirmOTP = ({ credentials, password, token }) => {
+ const form = new FormData()
+
+ form.append('password', password)
+ form.append('code', token)
+
+ return fetch(MFA_CONFIRM_OTP_URL, {
+ body: form,
+ headers: authHeaders(credentials),
+ method: 'POST'
+ }).then((data) => data.json())
+}
+const mfaSetupOTP = ({ credentials }) => {
+ return fetch(MFA_SETUP_OTP_URL, {
+ headers: authHeaders(credentials),
+ method: 'GET'
+ }).then((data) => data.json())
+}
+const generateMfaBackupCodes = ({ credentials }) => {
+ return fetch(MFA_BACKUP_CODES_URL, {
+ headers: authHeaders(credentials),
+ method: 'GET'
+ }).then((data) => data.json())
+}
+
+const fetchMutes = ({ credentials }) => {
+ return promisedRequest({ url: MASTODON_USER_MUTES_URL, credentials })
+ .then((users) => users.map(parseUser))
+}
+
+const muteUser = ({ id, credentials }) => {
+ return promisedRequest({ url: MASTODON_MUTE_USER_URL(id), credentials, method: 'POST' })
+}
+
+const unmuteUser = ({ id, credentials }) => {
+ return promisedRequest({ url: MASTODON_UNMUTE_USER_URL(id), credentials, method: 'POST' })
+}
+
+const subscribeUser = ({ id, credentials }) => {
+ return promisedRequest({ url: MASTODON_SUBSCRIBE_USER(id), credentials, method: 'POST' })
+}
+
+const unsubscribeUser = ({ id, credentials }) => {
+ return promisedRequest({ url: MASTODON_UNSUBSCRIBE_USER(id), credentials, method: 'POST' })
+}
+
+const fetchBlocks = ({ credentials }) => {
+ return promisedRequest({ url: MASTODON_USER_BLOCKS_URL, 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}) => {
+const revokeOAuthToken = ({ id, credentials }) => {
const url = `/api/oauth_tokens/${id}`
return fetch(url, {
@@ -557,13 +793,13 @@ const revokeOAuthToken = ({id, credentials}) => {
})
}
-const suggestions = ({credentials}) => {
+const suggestions = ({ credentials }) => {
return fetch(SUGGESTIONS_URL, {
headers: authHeaders(credentials)
}).then((data) => data.json())
}
-const markNotificationsAsSeen = ({id, credentials}) => {
+const markNotificationsAsSeen = ({ id, credentials }) => {
const body = new FormData()
body.append('latest_id', id)
@@ -575,9 +811,110 @@ const markNotificationsAsSeen = ({id, credentials}) => {
}).then((data) => data.json())
}
+const vote = ({ pollId, choices, credentials }) => {
+ const form = new FormData()
+ form.append('choices', choices)
+
+ return promisedRequest({
+ url: MASTODON_VOTE_URL(encodeURIComponent(pollId)),
+ method: 'POST',
+ credentials,
+ payload: {
+ choices: choices
+ }
+ })
+}
+
+const fetchPoll = ({ pollId, credentials }) => {
+ return promisedRequest(
+ {
+ url: MASTODON_POLL_URL(encodeURIComponent(pollId)),
+ method: 'GET',
+ credentials
+ }
+ )
+}
+
+const fetchFavoritedByUsers = ({ id }) => {
+ return promisedRequest({ url: MASTODON_STATUS_FAVORITEDBY_URL(id) }).then((users) => users.map(parseUser))
+}
+
+const fetchRebloggedByUsers = ({ id }) => {
+ return promisedRequest({ url: MASTODON_STATUS_REBLOGGEDBY_URL(id) }).then((users) => users.map(parseUser))
+}
+
+const reportUser = ({ credentials, userId, statusIds, comment, forward }) => {
+ return promisedRequest({
+ url: MASTODON_REPORT_USER_URL,
+ method: 'POST',
+ payload: {
+ 'account_id': userId,
+ 'status_ids': statusIds,
+ comment,
+ forward
+ },
+ credentials
+ })
+}
+
+const searchUsers = ({ credentials, query }) => {
+ return promisedRequest({
+ url: MASTODON_USER_SEARCH_URL,
+ params: {
+ q: query,
+ resolve: true
+ },
+ credentials
+ })
+ .then((data) => data.map(parseUser))
+}
+
+const search2 = ({ credentials, q, resolve, limit, offset, following }) => {
+ let url = MASTODON_SEARCH_2
+ let params = []
+
+ if (q) {
+ params.push(['q', encodeURIComponent(q)])
+ }
+
+ if (resolve) {
+ params.push(['resolve', resolve])
+ }
+
+ if (limit) {
+ params.push(['limit', limit])
+ }
+
+ if (offset) {
+ params.push(['offset', offset])
+ }
+
+ if (following) {
+ params.push(['following', true])
+ }
+
+ let queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
+ url += `?${queryString}`
+
+ return fetch(url, { headers: authHeaders(credentials) })
+ .then((data) => {
+ if (data.ok) {
+ return data
+ }
+ throw new Error('Error fetching search result', data)
+ })
+ .then((data) => { return data.json() })
+ .then((data) => {
+ data.accounts = data.accounts.slice(0, limit).map(u => parseUser(u))
+ data.statuses = data.statuses.slice(0, limit).map(s => parseStatus(s))
+ return data
+ })
+}
+
const apiService = {
verifyCredentials,
fetchTimeline,
+ fetchPinnedStatuses,
fetchConversation,
fetchStatus,
fetchFriends,
@@ -585,9 +922,14 @@ const apiService = {
fetchFollowers,
followUser,
unfollowUser,
+ pinOwnStatus,
+ unpinOwnStatus,
+ muteConversation,
+ unmuteConversation,
blockUser,
unblockUser,
fetchUser,
+ fetchUserRelationship,
favorite,
unfavorite,
retweet,
@@ -595,27 +937,48 @@ const apiService = {
postStatus,
deleteStatus,
uploadMedia,
- fetchAllFollowing,
- setUserMute,
fetchMutes,
+ muteUser,
+ unmuteUser,
+ subscribeUser,
+ unsubscribeUser,
fetchBlocks,
fetchOAuthTokens,
revokeOAuthToken,
+ tagUser,
+ untagUser,
+ deleteUser,
+ addRight,
+ deleteRight,
+ setActivationStatus,
register,
getCaptcha,
updateAvatar,
updateBg,
updateProfile,
updateBanner,
- externalProfile,
- followImport,
+ importBlocks,
+ importFollows,
deleteAccount,
changePassword,
+ settingsMFA,
+ mfaDisableOTP,
+ generateMfaBackupCodes,
+ mfaSetupOTP,
+ mfaConfirmOTP,
fetchFollowRequests,
approveUser,
denyUser,
suggestions,
- markNotificationsAsSeen
+ markNotificationsAsSeen,
+ vote,
+ fetchPoll,
+ fetchFavoritedByUsers,
+ fetchRebloggedByUsers,
+ reportUser,
+ updateNotificationSettings,
+ search2,
+ searchUsers
}
export default apiService