diff --git a/src/services/api/admin.js b/src/api/admin.js similarity index 100% rename from src/services/api/admin.js rename to src/api/admin.js diff --git a/src/api/chats.js b/src/api/chats.js new file mode 100644 index 000000000..4a0c55752 --- /dev/null +++ b/src/api/chats.js @@ -0,0 +1,86 @@ +import { parseChat } from 'src/services/entity_normalizer/entity_normalizer.service.js' +import { paramsString, promisedRequest } from './helpers.js' + +const PLEROMA_CHATS_URL = '/api/v1/pleroma/chats' +const PLEROMA_CHAT_URL = (id) => `/api/v1/pleroma/chats/by-account-id/${id}` +const PLEROMA_CHAT_MESSAGES_URL = (id, { maxId, sinceId, limit }) => + `/api/v1/pleroma/chats/${id}/messages${paramsString({ maxId, sinceId, limit })}` +const PLEROMA_CHAT_READ_URL = (id) => `/api/v1/pleroma/chats/${id}/read` +const PLEROMA_DELETE_CHAT_MESSAGE_URL = (chatId, messageId) => + `/api/v1/pleroma/chats/${chatId}/messages/${messageId}` + +export const chats = ({ credentials }) => + promisedRequest({ + url: PLEROMA_CHATS_URL, + credentials, + }).then((data) => ({ + chatList: data.map(parseChat).filter((c) => c), + })) + +export const getOrCreateChat = ({ accountId, credentials }) => + promisedRequest({ + url: PLEROMA_CHAT_URL(accountId), + method: 'POST', + credentials, + }) + +export const chatMessages = ({ + id, + credentials, + maxId, + sinceId, + limit = 20, +}) => { + return promisedRequest({ + url: PLEROMA_CHAT_MESSAGES_URL(id, { maxId, sinceId, limit }), + method: 'GET', + credentials, + }) +} + +export const sendChatMessage = ({ + id, + content, + mediaId = null, + idempotencyKey, + credentials, +}) => { + const payload = { + content, + } + + if (mediaId) { + payload.media_id = mediaId + } + + const headers = {} + + if (idempotencyKey) { + headers['idempotency-key'] = idempotencyKey + } + + return promisedRequest({ + url: PLEROMA_CHAT_MESSAGES_URL(id), + method: 'POST', + payload, + credentials, + headers, + }) +} + +export const readChat = ({ id, lastReadId, credentials }) => + promisedRequest({ + url: PLEROMA_CHAT_READ_URL(id), + method: 'POST', + payload: { + last_read_id: lastReadId, + }, + credentials, + }) + +export const deleteChatMessage = ({ chatId, messageId, credentials }) => + promisedRequest({ + url: PLEROMA_DELETE_CHAT_MESSAGE_URL(chatId, messageId), + method: 'DELETE', + credentials, + }) diff --git a/src/services/api/helpers.js b/src/api/helpers.js similarity index 95% rename from src/services/api/helpers.js rename to src/api/helpers.js index c01f2958c..1e8f257fb 100644 --- a/src/services/api/helpers.js +++ b/src/api/helpers.js @@ -83,9 +83,11 @@ export const promisedRequest = async ({ ...headers, }, } + if (!formData) { options.headers['Content-Type'] = 'application/json' } + if (params) { url += '?' + @@ -112,15 +114,6 @@ export const promisedRequest = async ({ // 204 is "No content", which fails to parse json (as you'd might think) if (response.ok && response.status === 204) return { _response: response } - if (!response.ok) { - throw new StatusCodeError( - response.status, - json, - { url, options }, - response, - ) - } - try { const json = await response.json() @@ -133,6 +126,15 @@ export const promisedRequest = async ({ json._response = response + if (!response.ok) { + throw new StatusCodeError( + response.status, + json, + { url, options }, + response, + ) + } + return json } catch (error) { throw new StatusCodeError( diff --git a/src/api/public.js b/src/api/public.js new file mode 100644 index 000000000..b0aa68140 --- /dev/null +++ b/src/api/public.js @@ -0,0 +1,601 @@ +import { concat, each, last, map } from 'lodash' + +import { + parseAttachment, + parseChat, + parseLinkHeaderPagination, + parseNotification, + parseSource, + parseStatus, + parseUser, +} from 'src/services/entity_normalizer/entity_normalizer.service.js' +import { paramsString, promisedRequest } from './helpers.js' + +import { RegistrationError, StatusCodeError } from 'src/services/errors/errors' + +const SUGGESTIONS_URL = '/api/v1/suggestions' +/* eslint-env browser */ +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_FOLLOWING_URL = ( + id, + { minId, maxId, sinceId, limit, withRelationships }, +) => + `/api/v1/accounts/${id}/following${paramsString({ minId, maxId, sinceId, limit, withRelationships })}` +const MASTODON_FOLLOWERS_URL = ( + id, + { minId, maxId, sinceId, limit, withRelationships }, +) => + `/api/v1/accounts/${id}/followers${paramsString({ minId, maxId, sinceId, limit, withRelationships })}` +const MASTODON_USER_HOME_TIMELINE_URL = '/api/v1/timelines/home' +const MASTODON_LIST_TIMELINE_URL = (id) => `/api/v1/timelines/list/${id}` +const MASTODON_BOOKMARK_TIMELINE_URL = '/api/v1/bookmarks' +const MASTODON_DIRECT_MESSAGES_TIMELINE_URL = '/api/v1/timelines/direct' +const MASTODON_PUBLIC_TIMELINE = '/api/v1/timelines/public' +export const MASTODON_STATUS_URL = (id) => `/api/v1/statuses/${id}` +const MASTODON_STATUS_CONTEXT_URL = (id) => `/api/v1/statuses/${id}/context` +const MASTODON_STATUS_SOURCE_URL = (id) => `/api/v1/statuses/${id}/source` +const MASTODON_STATUS_HISTORY_URL = (id) => `/api/v1/statuses/${id}/history` +const MASTODON_USER_URL = '/api/v1/accounts' +const MASTODON_USER_LOOKUP_URL = '/api/v1/accounts/lookup' +const MASTODON_USER_TIMELINE_URL = (id) => `/api/v1/accounts/${id}/statuses` +const MASTODON_TAG_TIMELINE_URL = (tag) => `/api/v1/timelines/tag/${tag}` +const AKKOMA_BUBBLE_TIMELINE_URL = '/api/v1/timelines/bubble' +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_SEARCH_2 = '/api/v2/search' +const MASTODON_USER_SEARCH_URL = '/api/v1/accounts/search' +const MASTODON_STREAMING = '/api/v1/streaming' +const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers' +const MASTODON_ANNOUNCEMENTS_URL = '/api/v1/announcements' +const PLEROMA_EMOJI_REACTIONS_URL = (id) => + `/api/v1/pleroma/statuses/${id}/reactions` +const PLEROMA_SCROBBLES_URL = (id, { maxId, sinceId, minId, limit, offset }) => + `/api/v1/pleroma/accounts/${id}/scrobbles${paramsString({ maxId, sinceId, minId, limit, offset })}` +const PLEROMA_STATUS_QUOTES_URL = (id) => + `/api/v1/pleroma/statuses/${id}/quotes` +const PLEROMA_USER_FAVORITES_TIMELINE_URL = (id) => + `/api/v1/pleroma/accounts/${id}/favourites` + +const EMOJI_PACKS_URL = (page, pageSize) => + `/api/v1/pleroma/emoji/packs${paramsString({ page, pageSize })}` + +// Params needed: +// nickname +// email +// fullname +// password +// password_confirm +// +// Optional +// bio +// homepage +// location +// token +// language +export const register = ({ params, credentials }) => { + const { nickname, ...rest } = params + return promisedRequest({ + url: MASTODON_REGISTRATION_URL, + method: 'POST', + credentials, + payload: { + nickname, + locale: 'en_US', + agreement: true, + ...rest, + }, + }) +} + +export const getCaptcha = () => + promisedRequest({ + url: '/api/pleroma/captcha', + }) + +export const fetchUser = ({ id, credentials }) => + promisedRequest({ + url: `${MASTODON_USER_URL}/${id}`, + credentials, + }).then((data) => parseUser(data)) + +export const fetchUserByName = ({ name, credentials }) => + promisedRequest({ + url: MASTODON_USER_LOOKUP_URL, + credentials, + params: { acct: name }, + }) + .then((data) => data.id) + .catch((error) => { + if (error && error.statusCode === 404) { + // Either the backend does not support lookup endpoint, + // or there is no user with such name. Fallback and treat name as id. + return name + } else { + throw error + } + }) + .then((id) => fetchUser({ id, credentials })) + +export const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) => + promisedRequest({ + url: MASTODON_FOLLOWING_URL(id, { maxId, sinceId, limit }), + credentials, + }).then((data) => data.map(parseUser)) + +export const fetchFollowers = ({ + id, + maxId, + sinceId, + limit = 20, + credentials, +}) => + promisedRequest({ + url: MASTODON_FOLLOWERS_URL(id, { + maxId, + sinceId, + limit, + withRelationships: true, + }), + credentials, + }).then((data) => data.map(parseUser)) + +export const fetchConversation = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_STATUS_CONTEXT_URL(id), + credentials, + }) + .then(({ ancestors, descendants }) => ({ + ancestors: ancestors.map(parseStatus), + descendants: descendants.map(parseStatus), + })) + .catch((error) => { + throw new Error('Error fetching timeline', error) + }) + +export const fetchStatus = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_STATUS_URL(id), + credentials, + }) + .then((data) => parseStatus(data)) + .catch((error) => { + throw new Error('Error fetching timeline', error) + }) + +export const fetchStatusSource = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_STATUS_SOURCE_URL(id), + credentials, + }) + .then((data) => parseSource(data)) + .catch((error) => { + throw new Error('Error fetching timeline', error) + }) + +export const fetchStatusHistory = ({ status, credentials }) => + promisedRequest({ + url: MASTODON_STATUS_HISTORY_URL(status.id), + credentials, + }).then((data) => { + data.reverse() + return data.map((item) => { + item.originalStatus = status + return parseStatus(item) + }) + }) + +export const fetchTimeline = ({ + timeline, + credentials, + sinceId, + minId, + maxId, + userId, + listId, + statusId, + tag, + withMuted, + replyVisibility = 'all', + includeTypes = [], + bookmarkFolderId, +}) => { + const timelineUrls = { + 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, + list: MASTODON_LIST_TIMELINE_URL, + favorites: MASTODON_USER_FAVORITES_TIMELINE_URL, + publicFavorites: PLEROMA_USER_FAVORITES_TIMELINE_URL, + tag: MASTODON_TAG_TIMELINE_URL, + bookmarks: MASTODON_BOOKMARK_TIMELINE_URL, + quotes: PLEROMA_STATUS_QUOTES_URL, + bubble: AKKOMA_BUBBLE_TIMELINE_URL, + } + + const isNotifications = timeline === 'notifications' + const params = { + minId, + sinceId, + maxId, + limit: 20, + } + + let url = timelineUrls[timeline] + + if (timeline === 'favorites' && userId) { + url = timelineUrls.publicFavorites(userId) + } + + if (timeline === 'user' || timeline === 'media') { + url = url(userId) + } + + if (timeline === 'list') { + url = url(listId) + } + + if (timeline === 'quotes') { + url = url(statusId) + } + + if (tag) { + url = url(tag) + } + + if (timeline === 'media') { + params.onlyMedia = 1 + } + if (timeline === 'public') { + params.local = true + } + if (timeline === 'public' || timeline === 'publicAndExternal') { + params.onlyMedia = false + } + if (timeline !== 'favorites' && timeline !== 'bookmarks') { + params.withMuted = withMuted + } + if (replyVisibility !== 'all') { + params.replyVisibility = replyVisibility + } + if (includeTypes.size > 0) { + params.includeTypes = includeTypes + } + if (timeline === 'bookmarks' && bookmarkFolderId) { + params.folderId = bookmarkFolderId + } + + return promisedRequest({ + url: url + paramsString(params), + credentials, + }).then(async (data) => { + const pagination = parseLinkHeaderPagination( + data._response.headers.get('Link'), + { + flakeId: timeline !== 'bookmarks' && timeline !== 'notifications', + }, + ) + + return { + data: data.map(isNotifications ? parseNotification : parseStatus), + pagination, + } + }) +} + +export const listEmojiPacks = ({ page, pageSize, credentials }) => + promisedRequest({ + url: EMOJI_PACKS_URL(page, pageSize), + }) + +export const fetchPinnedStatuses = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_USER_TIMELINE_URL(id) + '?pinned=true', + credentials, + }).then((data) => data.map(parseStatus)) + +export const verifyCredentials = ({ credentials }) => + promisedRequest({ + url: MASTODON_LOGIN_URL, + credentials, + }).then((data) => (data.error ? data : parseUser(data))) + +export const suggestions = ({ credentials }) => + promisedRequest({ + url: SUGGESTIONS_URL, + credentials, + }) + +export const fetchPoll = ({ pollId, credentials }) => + promisedRequest({ + url: MASTODON_POLL_URL(encodeURIComponent(pollId)), + method: 'GET', + credentials, + }) + +export const fetchFavoritedByUsers = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_STATUS_FAVORITEDBY_URL(id), + method: 'GET', + credentials, + }).then((users) => users.map(parseUser)) + +export const fetchRebloggedByUsers = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_STATUS_REBLOGGEDBY_URL(id), + method: 'GET', + credentials, + }).then((users) => users.map(parseUser)) + +export const fetchEmojiReactions = ({ id, credentials }) => + promisedRequest({ + url: PLEROMA_EMOJI_REACTIONS_URL(id), + credentials, + }).then((reactions) => + reactions.map((r) => { + r.accounts = r.accounts.map(parseUser) + return r + }), + ) + +export const searchUsers = ({ credentials, query }) => + promisedRequest({ + url: MASTODON_USER_SEARCH_URL, + params: { + q: query, + resolve: true, + }, + credentials, + }).then((data) => data.map(parseUser)) + +export const search2 = ({ + credentials, + q, + resolve, + limit, + offset, + following, + type, +}) => { + let url = MASTODON_SEARCH_2 + const 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]) + } + + if (type) { + params.push(['type', type]) + } + + params.push(['with_relationships', true]) + + const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join( + '&', + ) + url += `?${queryString}` + + return promisedRequest({ + url, + credentials, + }) + .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 + }) + .catch((error) => { + throw new Error('Error fetching timeline', error) + }) +} + +export const fetchKnownDomains = ({ credentials }) => + promisedRequest({ url: MASTODON_KNOWN_DOMAIN_LIST_URL, credentials }) + +export const getAnnouncements = ({ credentials }) => + promisedRequest({ url: MASTODON_ANNOUNCEMENTS_URL, credentials }) + +export const getMastodonSocketURI = ( + { credentials, stream, args = {} }, + base, +) => { + const url = new URL(MASTODON_STREAMING, base) + if (credentials) { + url.searchParams.append('access_token', credentials) + } + if (stream) { + url.searchParams.append('stream', stream) + } + Object.entries(args).forEach(([key, val]) => { + url.searchParams.append(key, val) + }) + return url +} + +const MASTODON_STREAMING_EVENTS = new Set([ + 'update', + 'notification', + 'delete', + 'filters_changed', + 'status.update', +]) + +const PLEROMA_STREAMING_EVENTS = new Set([ + 'pleroma:chat_update', + 'pleroma:respond', +]) + +// A thin wrapper around WebSocket API that allows adding a pre-processor to it +// Uses EventTarget and a CustomEvent to proxy events +export const ProcessedWS = ({ + url, + preprocessor = handleMastoWS, + id = 'Unknown', + credentials, +}) => { + const eventTarget = new EventTarget() + const socket = new WebSocket(url) + if (!socket) throw new Error(`Failed to create socket ${id}`) + const proxy = (original, eventName, processor = (a) => a) => { + original.addEventListener(eventName, (eventData) => { + eventTarget.dispatchEvent( + new CustomEvent(eventName, { detail: processor(eventData) }), + ) + }) + } + socket.addEventListener('open', (wsEvent) => { + console.debug(`[WS][${id}] Socket connected`, wsEvent) + if (credentials) { + socket.send( + JSON.stringify({ + type: 'pleroma:authenticate', + token: credentials, + }), + ) + } + }) + socket.addEventListener('error', (wsEvent) => { + console.debug(`[WS][${id}] Socket errored`, wsEvent) + }) + socket.addEventListener('close', (wsEvent) => { + console.debug( + `[WS][${id}] Socket disconnected with code ${wsEvent.code}`, + wsEvent, + ) + }) + // Commented code reason: very spammy, uncomment to enable message debug logging + /* + socket.addEventListener('message', (wsEvent) => { + console.debug( + `[WS][${id}] Message received`, + wsEvent + ) + }) + /**/ + + const onAuthenticated = () => { + eventTarget.dispatchEvent(new CustomEvent('pleroma:authenticated')) + } + + proxy(socket, 'open') + proxy(socket, 'close') + proxy(socket, 'message', (event) => preprocessor(event, { onAuthenticated })) + proxy(socket, 'error') + + // 1000 = Normal Closure + eventTarget.close = () => { + socket.close(1000, 'Shutting down socket') + } + eventTarget.getState = () => socket.readyState + eventTarget.subscribe = (stream, args = {}) => { + console.debug(`[WS][${id}] Subscribing to stream ${stream} with args`, args) + socket.send( + JSON.stringify({ + type: 'subscribe', + stream, + ...args, + }), + ) + } + eventTarget.unsubscribe = (stream, args = {}) => { + console.debug( + `[WS][${id}] Unsubscribing from stream ${stream} with args`, + args, + ) + socket.send( + JSON.stringify({ + type: 'unsubscribe', + stream, + ...args, + }), + ) + } + + return eventTarget +} + +export const handleMastoWS = ( + wsEvent, + { + onAuthenticated = () => { + /* no-op */ + }, + } = {}, +) => { + const { data } = wsEvent + if (!data) return + const parsedEvent = JSON.parse(data) + const { event, payload } = parsedEvent + if ( + MASTODON_STREAMING_EVENTS.has(event) || + PLEROMA_STREAMING_EVENTS.has(event) + ) { + // MastoBE and PleromaBE both send payload for delete as a PLAIN string + if (event === 'delete') { + return { event, id: payload } + } + const data = payload ? JSON.parse(payload) : null + if (event === 'pleroma:respond') { + if (data.type === 'pleroma:authenticate') { + if (data.result === 'success') { + console.debug('[WS] Successfully authenticated') + onAuthenticated() + } else { + console.error('[WS] Unable to authenticate:', data.error) + wsEvent.target.close() + } + } + return null + } else if (event === 'update') { + return { event, status: parseStatus(data) } + } else if (event === 'status.update') { + return { event, status: parseStatus(data) } + } else if (event === 'notification') { + return { event, notification: parseNotification(data) } + } else if (event === 'pleroma:chat_update') { + return { event, chatUpdate: parseChat(data) } + } + } else { + console.warn('Unknown event', wsEvent) + return null + } +} + +export const WSConnectionStatus = Object.freeze({ + JOINED: 1, + CLOSED: 2, + ERROR: 3, + DISABLED: 4, + STARTING: 5, + STARTING_INITIAL: 6, +}) + +export const fetchScrobbles = ({ accountId, limit = 1 }) => + promisedRequest({ + url: PLEROMA_SCROBBLES_URL(accountId, { limit }), + }) diff --git a/src/services/api/api.service.js b/src/api/user.js similarity index 56% rename from src/services/api/api.service.js rename to src/api/user.js index 864123f5f..b2bffe057 100644 --- a/src/services/api/api.service.js +++ b/src/api/user.js @@ -2,18 +2,14 @@ import { concat, each, last, map } from 'lodash' import { parseAttachment, - parseChat, - parseLinkHeaderPagination, parseNotification, parseSource, parseStatus, parseUser, -} from '../entity_normalizer/entity_normalizer.service.js' +} from 'src/services/entity_normalizer/entity_normalizer.service.js' import { paramsString, promisedRequest } from './helpers.js' +import { fetchFriends, MASTODON_STATUS_URL } from './public.js' -import { RegistrationError, StatusCodeError } from 'src/services/errors/errors' - -/* eslint-env browser */ const MUTES_IMPORT_URL = '/api/pleroma/mutes_import' const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import' const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import' @@ -22,7 +18,6 @@ const CHANGE_EMAIL_URL = '/api/pleroma/change_email' const CHANGE_PASSWORD_URL = '/api/pleroma/change_password' const MOVE_ACCOUNT_URL = '/api/pleroma/move_account' const ALIASES_URL = '/api/pleroma/aliases' -const SUGGESTIONS_URL = '/api/v1/suggestions' const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings' const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read' @@ -33,10 +28,6 @@ const MFA_SETUP_OTP_URL = '/api/pleroma/accounts/mfa/setup/totp' const MFA_CONFIRM_OTP_URL = '/api/pleroma/accounts/mfa/confirm/totp' const MFA_DISABLE_OTP_URL = '/api/pleroma/accounts/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_DISMISS_NOTIFICATION_URL = (id) => `/api/v1/notifications/${id}/dismiss` const MASTODON_FAVORITE_URL = (id) => `/api/v1/statuses/${id}/favourite` @@ -46,39 +37,16 @@ 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, - { minId, maxId, sinceId, limit, withRelationships }, -) => - `/api/v1/accounts/${id}/following${paramsString({ minId, maxId, sinceId, limit, withRelationships })}` -const MASTODON_FOLLOWERS_URL = ( - id, - { minId, maxId, sinceId, limit, withRelationships }, -) => - `/api/v1/accounts/${id}/followers${paramsString({ minId, maxId, sinceId, limit, withRelationships })}` + 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_STATUS_SOURCE_URL = (id) => `/api/v1/statuses/${id}/source` -const MASTODON_STATUS_HISTORY_URL = (id) => `/api/v1/statuses/${id}/history` -const MASTODON_USER_URL = '/api/v1/accounts' -const MASTODON_USER_LOOKUP_URL = '/api/v1/accounts/lookup' const MASTODON_USER_RELATIONSHIPS_URL = ({ id, withSuspended }) => `/api/v1/accounts/relationships/${paramsString({ id, withSuspended })}` -const MASTODON_USER_TIMELINE_URL = (id) => `/api/v1/accounts/${id}/statuses` const MASTODON_USER_IN_LISTS = (id) => `/api/v1/accounts/${id}/lists` const MASTODON_LIST_URL = (id) => `/api/v1/lists/${id}` -const MASTODON_LIST_TIMELINE_URL = (id) => `/api/v1/timelines/list/${id}` const MASTODON_LIST_ACCOUNTS_URL = (id) => `/api/v1/lists/${id}/accounts` -const MASTODON_TAG_TIMELINE_URL = (tag) => `/api/v1/timelines/tag/${tag}` -const MASTODON_BOOKMARK_TIMELINE_URL = '/api/v1/bookmarks' -const AKKOMA_BUBBLE_TIMELINE_URL = '/api/v1/timelines/bubble' const MASTODON_USER_BLOCKS_URL = ({ maxId, sinceId, @@ -106,192 +74,84 @@ const MASTODON_UNBOOKMARK_STATUS_URL = (id) => 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 MASTODON_DOMAIN_BLOCKS_URL = '/api/v1/domain_blocks' const MASTODON_LISTS_URL = '/api/v1/lists' -const MASTODON_STREAMING = '/api/v1/streaming' -const MASTODON_KNOWN_DOMAIN_LIST_URL = '/api/v1/instance/peers' -const MASTODON_ANNOUNCEMENTS_URL = '/api/v1/announcements' const MASTODON_ANNOUNCEMENTS_DISMISS_URL = (id) => `/api/v1/announcements/${id}/dismiss` -const PLEROMA_EMOJI_REACTIONS_URL = (id) => - `/api/v1/pleroma/statuses/${id}/reactions` const PLEROMA_EMOJI_REACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` const PLEROMA_EMOJI_UNREACT_URL = (id, emoji) => `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` -const PLEROMA_CHATS_URL = '/api/v1/pleroma/chats' -const PLEROMA_CHAT_URL = (id) => `/api/v1/pleroma/chats/by-account-id/${id}` -const PLEROMA_CHAT_MESSAGES_URL = (id, { maxId, sinceId, limit }) => - `/api/v1/pleroma/chats/${id}/messages${paramsString({ maxId, sinceId, limit })}` -const PLEROMA_CHAT_READ_URL = (id) => `/api/v1/pleroma/chats/${id}/read` -const PLEROMA_DELETE_CHAT_MESSAGE_URL = (chatId, messageId) => - `/api/v1/pleroma/chats/${chatId}/messages/${messageId}` const PLEROMA_BACKUP_URL = '/api/v1/pleroma/backups' -const PLEROMA_SCROBBLES_URL = (id, { maxId, sinceId, minId, limit, offset }) => - `/api/v1/pleroma/accounts/${id}/scrobbles${paramsString({ maxId, sinceId, minId, limit, offset })}` -const PLEROMA_STATUS_QUOTES_URL = (id) => - `/api/v1/pleroma/statuses/${id}/quotes` -const PLEROMA_USER_FAVORITES_TIMELINE_URL = (id) => - `/api/v1/pleroma/accounts/${id}/favourites` const PLEROMA_BOOKMARK_FOLDERS_URL = '/api/v1/pleroma/bookmark_folders' const PLEROMA_BOOKMARK_FOLDER_URL = (id) => `/api/v1/pleroma/bookmark_folders/${id}` -const EMOJI_PACKS_URL = (page, pageSize) => - `/api/v1/pleroma/emoji/packs${paramsString({ page, pageSize })}` - -export const updateNotificationSettings = ({ credentials, settings }) => { - return promisedRequest({ - url: NOTIFICATION_SETTINGS_URL, - credentials, - method: 'PUT', - payload: settings, - }) -} - -export const updateProfileImages = ({ - credentials, - avatar = null, - avatarName = null, - banner = null, - background = null, -}) => { - const form = new FormData() - if (avatar !== null) { - if (avatarName !== null) { - form.append('avatar', avatar, avatarName) - } else { - form.append('avatar', avatar) - } - } - if (banner !== null) form.append('header', banner) - if (background !== null) form.append('pleroma_background_image', background) - return promisedRequest({ - url: MASTODON_PROFILE_UPDATE_URL, - credentials, - method: 'PATCH', - formData: form, - }).then((data) => { - if (data.error) { - throw new Error(data.error) - } - return parseUser(data) - }) -} - -export const updateProfile = ({ credentials, params }) => { - const formData = new FormData() - - for (const name in params) { - if (name === 'fields_attributes') { - params[name].forEach((param, i) => { - formData.append(name + `[${i}][name]`, param.name) - formData.append(name + `[${i}][value]`, param.value) - }) - } else { - if (typeof params[name] === 'object') { - console.warn( - 'Object detected in updateProfile API call. This will not work, use updateProfileJSON instead.', - ) - console.warn('Object:\n' + JSON.stringify(params[name], null, 2)) - } - formData.append(name, params[name]) - } - } - - return promisedRequest({ - url: MASTODON_PROFILE_UPDATE_URL, - credentials, - method: 'PATCH', - formData, - }).then((data) => parseUser(data)) -} - -export const updateProfileJSON = ({ credentials, params }) => +// #Posts +export const favorite = ({ id, credentials }) => promisedRequest({ - url: MASTODON_PROFILE_UPDATE_URL, - credentials, - payload: params, - method: 'PATCH', - }).then((data) => parseUser(data)) - -// Params needed: -// nickname -// email -// fullname -// password -// password_confirm -// -// Optional -// bio -// homepage -// location -// token -// language -export const register = ({ params, credentials }) => { - const { nickname, ...rest } = params - return promisedRequest({ - url: MASTODON_REGISTRATION_URL, + url: MASTODON_FAVORITE_URL(id), method: 'POST', credentials, + }).then((data) => parseStatus(data)) + +export const unfavorite = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_UNFAVORITE_URL(id), + method: 'POST', + credentials, + }).then((data) => parseStatus(data)) + +export const retweet = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_RETWEET_URL(id), + method: 'POST', + credentials, + }).then((data) => parseStatus(data)) + +export const unretweet = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_UNRETWEET_URL(id), + method: 'POST', + credentials, + }).then((data) => parseStatus(data)) + +export const reactWithEmoji = ({ id, emoji, credentials }) => + promisedRequest({ + url: PLEROMA_EMOJI_REACT_URL(id, emoji), + method: 'PUT', + credentials, + }).then(parseStatus) + +export const unreactWithEmoji = ({ id, emoji, credentials }) => + promisedRequest({ + url: PLEROMA_EMOJI_UNREACT_URL(id, emoji), + method: 'DELETE', + credentials, + }).then(parseStatus) + +export const bookmarkStatus = ({ id, credentials, ...options }) => + promisedRequest({ + url: MASTODON_BOOKMARK_STATUS_URL(id), + credentials, + method: 'POST', payload: { - nickname, - locale: 'en_US', - agreement: true, - ...rest, + folder_id: options.folder_id, }, }) -} -export const getCaptcha = () => +export const unbookmarkStatus = ({ id, credentials }) => promisedRequest({ - url: '/api/pleroma/captcha', - }) - -export const followUser = ({ id, credentials, ...options }) => { - const payload = {} - - if (options.reblogs !== undefined) { - payload.reblogs = options.reblogs - } - - if (options.notify !== undefined) { - payload.notify = options.notify - } - - return promisedRequest({ - url: MASTODON_FOLLOW_URL(id), - payload, + url: MASTODON_UNBOOKMARK_STATUS_URL(id), credentials, method: 'POST', }) -} - -export const unfollowUser = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_UNFOLLOW_URL(id), - credentials, - method: 'POST', - }) - -export const fetchUserInLists = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_USER_IN_LISTS(id), - credentials, - }) export const pinOwnStatus = ({ id, credentials }) => promisedRequest({ @@ -321,410 +181,21 @@ export const unmuteConversation = ({ id, credentials }) => method: 'POST', }).then((data) => parseStatus(data)) -export const blockUser = ({ id, expiresIn, credentials }) => { - const payload = {} - if (expiresIn) { - payload.duration = expiresIn - } +export const vote = ({ pollId, choices, credentials }) => { + const form = new FormData() + form.append('choices', choices) return promisedRequest({ - url: MASTODON_BLOCK_USER_URL(id), - credentials, + url: MASTODON_VOTE_URL(encodeURIComponent(pollId)), method: 'POST', - payload, - }) -} - -export const unblockUser = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_UNBLOCK_USER_URL(id), - credentials, - method: 'POST', - }) - -export const removeUserFromFollowers = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_REMOVE_USER_FROM_FOLLOWERS(id), - credentials, - method: 'POST', - }) - -export const editUserNote = ({ id, credentials, comment }) => - promisedRequest({ - url: MASTODON_USER_NOTE_URL(id), credentials, payload: { - comment, + choices, }, - method: 'POST', - }) - -export const approveUser = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_APPROVE_USER_URL(id), - credentials, - method: 'POST', - }) - -export const denyUser = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_DENY_USER_URL(id), - credentials, - method: 'POST', - }) - -export const fetchUser = ({ id, credentials }) => - promisedRequest({ - url: `${MASTODON_USER_URL}/${id}`, - credentials, - }).then((data) => parseUser(data)) - -export const fetchUserByName = ({ name, credentials }) => - promisedRequest({ - url: MASTODON_USER_LOOKUP_URL, - credentials, - params: { acct: name }, - }) - .then((data) => data.id) - .catch((error) => { - if (error && error.statusCode === 404) { - // Either the backend does not support lookup endpoint, - // or there is no user with such name. Fallback and treat name as id. - return name - } else { - throw error - } - }) - .then((id) => fetchUser({ id, credentials })) - -export const fetchUserRelationship = ({ id, withSuspended, credentials }) => - promisedRequest({ - url: MASTODON_USER_RELATIONSHIPS_URL({ id, withSuspended }), - credentials, - }) - -export const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) => - promisedRequest({ - url: MASTODON_FOLLOWING_URL(id, { maxId, sinceId, limit }), - credentials, - }).then((data) => data.map(parseUser)) - -export const exportFriends = ({ id, credentials }) => { - // biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO refactor this - 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, - withRelationships: true, - }) - friends = concat(friends, users) - if (users.length === 0) { - more = false - } - } - resolve(friends) - } catch (err) { - reject(err) - } }) } -export const fetchFollowers = ({ - id, - maxId, - sinceId, - limit = 20, - credentials, -}) => - promisedRequest({ - url: MASTODON_FOLLOWERS_URL(id, { - maxId, - sinceId, - limit, - withRelationships: true, - }), - credentials, - }).then((data) => data.map(parseUser)) - -export const fetchFollowRequests = ({ credentials }) => - promisedRequest({ - url: MASTODON_FOLLOW_REQUESTS_URL, - credentials, - }).then((data) => data.map(parseUser)) - -export const fetchLists = ({ credentials }) => - promisedRequest({ - url: MASTODON_LISTS_URL, - credentials, - }) - -export const createList = ({ title, credentials }) => - promisedRequest({ - url: MASTODON_LISTS_URL, - credentials, - method: 'POST', - payload: { title }, - }) - -export const getList = ({ listId, credentials }) => - promisedRequest({ - url: MASTODON_LIST_URL(listId), - credentials, - }) - -export const updateList = ({ listId, title, credentials }) => - promisedRequest({ - url: MASTODON_LIST_URL(listId), - - credentials, - method: 'PUT', - payload: { title }, - }) - -export const getListAccounts = ({ listId, credentials }) => - promisedRequest({ - url: MASTODON_LIST_ACCOUNTS_URL(listId), - credentials, - }).then((data) => data.map(({ id }) => id)) - -export const addAccountsToList = ({ listId, accountIds, credentials }) => - promisedRequest({ - url: MASTODON_LIST_ACCOUNTS_URL(listId), - credentials, - method: 'POST', - payload: { account_ids: accountIds }, - }) - -export const removeAccountsFromList = ({ listId, accountIds, credentials }) => - promisedRequest({ - url: MASTODON_LIST_ACCOUNTS_URL(listId), - credentials, - method: 'DELETE', - payload: { account_ids: accountIds }, - }) - -export const deleteList = ({ listId, credentials }) => - promisedRequest({ - url: MASTODON_LIST_URL(listId), - method: 'DELETE', - credentials, - }) - -export const fetchConversation = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_STATUS_CONTEXT_URL(id), - credentials, - }) - .then(({ ancestors, descendants }) => ({ - ancestors: ancestors.map(parseStatus), - descendants: descendants.map(parseStatus), - })) - .catch((error) => { - throw new Error('Error fetching timeline', error) - }) - -export const fetchStatus = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_STATUS_URL(id), - credentials, - }) - .then((data) => parseStatus(data)) - .catch((error) => { - throw new Error('Error fetching timeline', error) - }) - -export const fetchStatusSource = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_STATUS_SOURCE_URL(id), - credentials, - }) - .then((data) => parseSource(data)) - .catch((error) => { - throw new Error('Error fetching timeline', error) - }) - -export const fetchStatusHistory = ({ status, credentials }) => - promisedRequest({ - url: MASTODON_STATUS_HISTORY_URL(status.id), - credentials, - }).then((data) => { - data.reverse() - return data.map((item) => { - item.originalStatus = status - return parseStatus(item) - }) - }) - -export const fetchTimeline = ({ - timeline, - credentials, - sinceId, - minId, - maxId, - userId, - listId, - statusId, - tag, - withMuted, - replyVisibility = 'all', - includeTypes = [], - bookmarkFolderId, -}) => { - const timelineUrls = { - 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, - list: MASTODON_LIST_TIMELINE_URL, - favorites: MASTODON_USER_FAVORITES_TIMELINE_URL, - publicFavorites: PLEROMA_USER_FAVORITES_TIMELINE_URL, - tag: MASTODON_TAG_TIMELINE_URL, - bookmarks: MASTODON_BOOKMARK_TIMELINE_URL, - quotes: PLEROMA_STATUS_QUOTES_URL, - bubble: AKKOMA_BUBBLE_TIMELINE_URL, - } - - const isNotifications = timeline === 'notifications' - const params = { - minId, - sinceId, - maxId, - limit: 20, - } - - let url = timelineUrls[timeline] - - if (timeline === 'favorites' && userId) { - url = timelineUrls.publicFavorites(userId) - } - - if (timeline === 'user' || timeline === 'media') { - url = url(userId) - } - - if (timeline === 'list') { - url = url(listId) - } - - if (timeline === 'quotes') { - url = url(statusId) - } - - if (tag) { - url = url(tag) - } - - if (timeline === 'media') { - params.onlyMedia = 1 - } - if (timeline === 'public') { - params.local = true - } - if (timeline === 'public' || timeline === 'publicAndExternal') { - params.onlyMedia = false - } - if (timeline !== 'favorites' && timeline !== 'bookmarks') { - params.withMuted = withMuted - } - if (replyVisibility !== 'all') { - params.replyVisibility = replyVisibility - } - if (includeTypes.size > 0) { - params.includeTypes = includeTypes - } - if (timeline === 'bookmarks' && bookmarkFolderId) { - params.folderId = bookmarkFolderId - } - - return promisedRequest({ - url: url + paramsString(params), - credentials, - }).then(async (data) => { - const pagination = parseLinkHeaderPagination( - data._response.headers.get('Link'), - { - flakeId: timeline !== 'bookmarks' && timeline !== 'notifications', - }, - ) - - return { - data: data.map(isNotifications ? parseNotification : parseStatus), - pagination, - } - }) -} - -export const listEmojiPacks = ({ page, pageSize, credentials }) => - promisedRequest({ - url: EMOJI_PACKS_URL(page, pageSize), - }) - -export const fetchPinnedStatuses = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_USER_TIMELINE_URL(id) + '?pinned=true', - credentials, - }).then((data) => data.map(parseStatus)) - -export const verifyCredentials = ({ credentials }) => - promisedRequest({ - url: MASTODON_LOGIN_URL, - credentials, - }).then((data) => (data.error ? data : parseUser(data))) - -export const favorite = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_FAVORITE_URL(id), - method: 'POST', - credentials, - }).then((data) => parseStatus(data)) - -export const unfavorite = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_UNFAVORITE_URL(id), - method: 'POST', - credentials, - }).then((data) => parseStatus(data)) - -export const retweet = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_RETWEET_URL(id), - method: 'POST', - credentials, - }).then((data) => parseStatus(data)) - -export const unretweet = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_UNRETWEET_URL(id), - method: 'POST', - credentials, - }).then((data) => parseStatus(data)) - -export const bookmarkStatus = ({ id, credentials, ...options }) => - promisedRequest({ - url: MASTODON_BOOKMARK_STATUS_URL(id), - credentials, - method: 'POST', - payload: { - folder_id: options.folder_id, - }, - }) - -export const unbookmarkStatus = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_UNBOOKMARK_STATUS_URL(id), - credentials, - method: 'POST', - }) - +// #Posting export const postStatus = ({ credentials, status, @@ -856,6 +327,44 @@ export const setMediaDescription = ({ id, description, credentials }) => }, }).then((data) => parseAttachment(data)) +// #Notifications +export const dismissNotification = ({ credentials, id }) => + promisedRequest({ + url: MASTODON_DISMISS_NOTIFICATION_URL(id), + method: 'POST', + payload: { id }, + credentials, + }) + +export const dismissAnnouncement = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_ANNOUNCEMENTS_DISMISS_URL(id), + credentials, + method: 'POST', + }) + +export const markNotificationsAsSeen = ({ + id, + credentials, + single = false, +}) => { + const formData = new FormData() + + if (single) { + formData.append('id', id) + } else { + formData.append('max_id', id) + } + + return promisedRequest({ + url: NOTIFICATION_READ_URL, + formData, + credentials, + method: 'POST', + }) +} + +// #Imports export const importMutes = ({ file, credentials }) => { const formData = new FormData() formData.append('list', file) @@ -889,19 +398,108 @@ export const importFollows = ({ file, credentials }) => { }).then((response) => response.ok) } -export const deleteAccount = ({ credentials, password }) => { - const formData = new FormData() - - formData.append('password', password) - - return promisedRequest({ - url: DELETE_ACCOUNT_URL, - formData, - method: 'POST', - credentials, +export const exportFriends = ({ id, credentials }) => { + // biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO refactor this + 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, + withRelationships: true, + }) + friends = concat(friends, users) + if (users.length === 0) { + more = false + } + } + resolve(friends) + } catch (err) { + reject(err) + } }) } +// #Profile settings +export const updateNotificationSettings = ({ credentials, settings }) => { + return promisedRequest({ + url: NOTIFICATION_SETTINGS_URL, + credentials, + method: 'PUT', + payload: settings, + }) +} + +export const updateProfileImages = ({ + credentials, + avatar = null, + avatarName = null, + banner = null, + background = null, +}) => { + const form = new FormData() + if (avatar !== null) { + if (avatarName !== null) { + form.append('avatar', avatar, avatarName) + } else { + form.append('avatar', avatar) + } + } + if (banner !== null) form.append('header', banner) + if (background !== null) form.append('pleroma_background_image', background) + return promisedRequest({ + url: MASTODON_PROFILE_UPDATE_URL, + credentials, + method: 'PATCH', + formData: form, + }).then((data) => { + if (data.error) { + throw new Error(data.error) + } + return parseUser(data) + }) +} + +export const updateProfile = ({ credentials, params }) => { + const formData = new FormData() + + for (const name in params) { + if (name === 'fields_attributes') { + params[name].forEach((param, i) => { + formData.append(name + `[${i}][name]`, param.name) + formData.append(name + `[${i}][value]`, param.value) + }) + } else { + if (typeof params[name] === 'object') { + console.warn( + 'Object detected in updateProfile API call. This will not work, use updateProfileJSON instead.', + ) + console.warn('Object:\n' + JSON.stringify(params[name], null, 2)) + } + formData.append(name, params[name]) + } + } + + return promisedRequest({ + url: MASTODON_PROFILE_UPDATE_URL, + credentials, + method: 'PATCH', + formData, + }).then((data) => parseUser(data)) +} + +export const updateProfileJSON = ({ credentials, params }) => + promisedRequest({ + url: MASTODON_PROFILE_UPDATE_URL, + credentials, + payload: params, + method: 'PATCH', + }).then((data) => parseUser(data)) + export const changeEmail = ({ credentials, email, password }) => { const form = new FormData() @@ -930,32 +528,6 @@ export const moveAccount = ({ credentials, password, targetAccount }) => { }) } -export const addAlias = ({ credentials, alias }) => - promisedRequest({ - url: ALIASES_URL, - method: 'PUT', - credentials, - payload: { alias }, - }) - -export const deleteAlias = ({ credentials, alias }) => - promisedRequest({ - url: ALIASES_URL, - method: 'DELETE', - credentials, - payload: { alias }, - }) - -export const listAliases = ({ credentials }) => - promisedRequest({ - url: ALIASES_URL, - method: 'GET', - credentials, - params: { - _cacheBooster: new Date().getTime(), - }, - }) - export const changePassword = ({ credentials, password, @@ -976,6 +548,7 @@ export const changePassword = ({ }) } +// #MFA export const settingsMFA = ({ credentials }) => promisedRequest({ url: MFA_SETTINGS_URL, @@ -1022,6 +595,108 @@ export const generateMfaBackupCodes = ({ credentials }) => method: 'GET', }) +// #Aliases +export const addAlias = ({ credentials, alias }) => + promisedRequest({ + url: ALIASES_URL, + method: 'PUT', + credentials, + payload: { alias }, + }) + +export const deleteAlias = ({ credentials, alias }) => + promisedRequest({ + url: ALIASES_URL, + method: 'DELETE', + credentials, + payload: { alias }, + }) + +export const listAliases = ({ credentials }) => + promisedRequest({ + url: ALIASES_URL, + method: 'GET', + credentials, + params: { + _cacheBooster: new Date().getTime(), + }, + }) + +// User manipulation +export const fetchUserRelationship = ({ id, withSuspended, credentials }) => + promisedRequest({ + url: MASTODON_USER_RELATIONSHIPS_URL({ id, withSuspended }), + credentials, + }) + +export const followUser = ({ id, credentials, ...options }) => { + const payload = {} + + if (options.reblogs !== undefined) { + payload.reblogs = options.reblogs + } + + if (options.notify !== undefined) { + payload.notify = options.notify + } + + return promisedRequest({ + url: MASTODON_FOLLOW_URL(id), + payload, + credentials, + method: 'POST', + }) +} + +export const unfollowUser = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_UNFOLLOW_URL(id), + credentials, + method: 'POST', + }) +export const fetchUserInLists = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_USER_IN_LISTS(id), + credentials, + }) + +export const removeUserFromFollowers = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_REMOVE_USER_FROM_FOLLOWERS(id), + credentials, + method: 'POST', + }) + +export const fetchFollowRequests = ({ credentials }) => + promisedRequest({ + url: MASTODON_FOLLOW_REQUESTS_URL, + credentials, + }).then((data) => data.map(parseUser)) + +export const approveUser = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_APPROVE_USER_URL(id), + credentials, + method: 'POST', + }) + +export const denyUser = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_DENY_USER_URL(id), + credentials, + method: 'POST', + }) + +export const editUserNote = ({ id, credentials, comment }) => + promisedRequest({ + url: MASTODON_USER_NOTE_URL(id), + credentials, + payload: { + comment, + }, + method: 'POST', + }) + export const fetchMutes = ({ maxId, credentials }) => promisedRequest({ url: MASTODON_USER_MUTES_URL({ maxId, withRelationships: true }), @@ -1055,122 +730,26 @@ export const fetchBlocks = ({ maxId, credentials }) => credentials, }).then((users) => users.map(parseUser)) -export const addBackup = ({ credentials }) => - promisedRequest({ - url: PLEROMA_BACKUP_URL, - method: 'POST', - credentials, - }) - -export const listBackups = ({ credentials }) => - promisedRequest({ - url: PLEROMA_BACKUP_URL, - method: 'GET', - credentials, - params: { - _cacheBooster: new Date().getTime(), - }, - }) - -export const fetchOAuthTokens = ({ credentials }) => - promisedRequest({ - url: '/api/oauth_tokens.json', - credentials, - }) - -export const revokeOAuthToken = ({ id, credentials }) => - promisedRequest({ - url: `/api/oauth_tokens/${id}`, - credentials, - method: 'DELETE', - }) - -export const suggestions = ({ credentials }) => - promisedRequest({ - url: SUGGESTIONS_URL, - credentials, - }) - -export const markNotificationsAsSeen = ({ - id, - credentials, - single = false, -}) => { - const formData = new FormData() - - if (single) { - formData.append('id', id) - } else { - formData.append('max_id', id) +export const blockUser = ({ id, expiresIn, credentials }) => { + const payload = {} + if (expiresIn) { + payload.duration = expiresIn } return promisedRequest({ - url: NOTIFICATION_READ_URL, - formData, + url: MASTODON_BLOCK_USER_URL(id), credentials, method: 'POST', + payload, }) } -export const vote = ({ pollId, choices, credentials }) => { - const form = new FormData() - form.append('choices', choices) - - return promisedRequest({ - url: MASTODON_VOTE_URL(encodeURIComponent(pollId)), +export const unblockUser = ({ id, credentials }) => + promisedRequest({ + url: MASTODON_UNBLOCK_USER_URL(id), + credentials, method: 'POST', - credentials, - payload: { - choices, - }, }) -} - -export const fetchPoll = ({ pollId, credentials }) => - promisedRequest({ - url: MASTODON_POLL_URL(encodeURIComponent(pollId)), - method: 'GET', - credentials, - }) - -export const fetchFavoritedByUsers = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_STATUS_FAVORITEDBY_URL(id), - method: 'GET', - credentials, - }).then((users) => users.map(parseUser)) - -export const fetchRebloggedByUsers = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_STATUS_REBLOGGEDBY_URL(id), - method: 'GET', - credentials, - }).then((users) => users.map(parseUser)) - -export const fetchEmojiReactions = ({ id, credentials }) => - promisedRequest({ - url: PLEROMA_EMOJI_REACTIONS_URL(id), - credentials, - }).then((reactions) => - reactions.map((r) => { - r.accounts = r.accounts.map(parseUser) - return r - }), - ) - -export const reactWithEmoji = ({ id, emoji, credentials }) => - promisedRequest({ - url: PLEROMA_EMOJI_REACT_URL(id, emoji), - method: 'PUT', - credentials, - }).then(parseStatus) - -export const unreactWithEmoji = ({ id, emoji, credentials }) => - promisedRequest({ - url: PLEROMA_EMOJI_UNREACT_URL(id, emoji), - method: 'DELETE', - credentials, - }).then(parseStatus) export const reportUser = ({ credentials, @@ -1191,76 +770,7 @@ export const reportUser = ({ credentials, }) -export const searchUsers = ({ credentials, query }) => - promisedRequest({ - url: MASTODON_USER_SEARCH_URL, - params: { - q: query, - resolve: true, - }, - credentials, - }).then((data) => data.map(parseUser)) - -export const search2 = ({ - credentials, - q, - resolve, - limit, - offset, - following, - type, -}) => { - let url = MASTODON_SEARCH_2 - const 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]) - } - - if (type) { - params.push(['type', type]) - } - - params.push(['with_relationships', true]) - - const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join( - '&', - ) - url += `?${queryString}` - - return promisedRequest({ - url, - credentials, - }) - .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 - }) - .catch((error) => { - throw new Error('Error fetching timeline', error) - }) -} - -export const fetchKnownDomains = ({ credentials }) => - promisedRequest({ url: MASTODON_KNOWN_DOMAIN_LIST_URL, credentials }) - +// #Domain mutes export const fetchDomainMutes = ({ credentials }) => promisedRequest({ url: MASTODON_DOMAIN_BLOCKS_URL, credentials }) @@ -1280,280 +790,98 @@ export const unmuteDomain = ({ domain, credentials }) => credentials, }) -export const dismissNotification = ({ credentials, id }) => +// #Backups +export const addBackup = ({ credentials }) => promisedRequest({ - url: MASTODON_DISMISS_NOTIFICATION_URL(id), - method: 'POST', - payload: { id }, - credentials, - }) - -export const getAnnouncements = ({ credentials }) => - promisedRequest({ url: MASTODON_ANNOUNCEMENTS_URL, credentials }) - -export const dismissAnnouncement = ({ id, credentials }) => - promisedRequest({ - url: MASTODON_ANNOUNCEMENTS_DISMISS_URL(id), - credentials, - method: 'POST', - }) - -export const getMastodonSocketURI = ( - { credentials, stream, args = {} }, - base, -) => { - const url = new URL(MASTODON_STREAMING, base) - if (credentials) { - url.searchParams.append('access_token', credentials) - } - if (stream) { - url.searchParams.append('stream', stream) - } - Object.entries(args).forEach(([key, val]) => { - url.searchParams.append(key, val) - }) - return url -} - -const MASTODON_STREAMING_EVENTS = new Set([ - 'update', - 'notification', - 'delete', - 'filters_changed', - 'status.update', -]) - -const PLEROMA_STREAMING_EVENTS = new Set([ - 'pleroma:chat_update', - 'pleroma:respond', -]) - -// A thin wrapper around WebSocket API that allows adding a pre-processor to it -// Uses EventTarget and a CustomEvent to proxy events -export const ProcessedWS = ({ - url, - preprocessor = handleMastoWS, - id = 'Unknown', - credentials, -}) => { - const eventTarget = new EventTarget() - const socket = new WebSocket(url) - if (!socket) throw new Error(`Failed to create socket ${id}`) - const proxy = (original, eventName, processor = (a) => a) => { - original.addEventListener(eventName, (eventData) => { - eventTarget.dispatchEvent( - new CustomEvent(eventName, { detail: processor(eventData) }), - ) - }) - } - socket.addEventListener('open', (wsEvent) => { - console.debug(`[WS][${id}] Socket connected`, wsEvent) - if (credentials) { - socket.send( - JSON.stringify({ - type: 'pleroma:authenticate', - token: credentials, - }), - ) - } - }) - socket.addEventListener('error', (wsEvent) => { - console.debug(`[WS][${id}] Socket errored`, wsEvent) - }) - socket.addEventListener('close', (wsEvent) => { - console.debug( - `[WS][${id}] Socket disconnected with code ${wsEvent.code}`, - wsEvent, - ) - }) - // Commented code reason: very spammy, uncomment to enable message debug logging - /* - socket.addEventListener('message', (wsEvent) => { - console.debug( - `[WS][${id}] Message received`, - wsEvent - ) - }) - /**/ - - const onAuthenticated = () => { - eventTarget.dispatchEvent(new CustomEvent('pleroma:authenticated')) - } - - proxy(socket, 'open') - proxy(socket, 'close') - proxy(socket, 'message', (event) => preprocessor(event, { onAuthenticated })) - proxy(socket, 'error') - - // 1000 = Normal Closure - eventTarget.close = () => { - socket.close(1000, 'Shutting down socket') - } - eventTarget.getState = () => socket.readyState - eventTarget.subscribe = (stream, args = {}) => { - console.debug(`[WS][${id}] Subscribing to stream ${stream} with args`, args) - socket.send( - JSON.stringify({ - type: 'subscribe', - stream, - ...args, - }), - ) - } - eventTarget.unsubscribe = (stream, args = {}) => { - console.debug( - `[WS][${id}] Unsubscribing from stream ${stream} with args`, - args, - ) - socket.send( - JSON.stringify({ - type: 'unsubscribe', - stream, - ...args, - }), - ) - } - - return eventTarget -} - -export const handleMastoWS = ( - wsEvent, - { - onAuthenticated = () => { - /* no-op */ - }, - } = {}, -) => { - const { data } = wsEvent - if (!data) return - const parsedEvent = JSON.parse(data) - const { event, payload } = parsedEvent - if ( - MASTODON_STREAMING_EVENTS.has(event) || - PLEROMA_STREAMING_EVENTS.has(event) - ) { - // MastoBE and PleromaBE both send payload for delete as a PLAIN string - if (event === 'delete') { - return { event, id: payload } - } - const data = payload ? JSON.parse(payload) : null - if (event === 'pleroma:respond') { - if (data.type === 'pleroma:authenticate') { - if (data.result === 'success') { - console.debug('[WS] Successfully authenticated') - onAuthenticated() - } else { - console.error('[WS] Unable to authenticate:', data.error) - wsEvent.target.close() - } - } - return null - } else if (event === 'update') { - return { event, status: parseStatus(data) } - } else if (event === 'status.update') { - return { event, status: parseStatus(data) } - } else if (event === 'notification') { - return { event, notification: parseNotification(data) } - } else if (event === 'pleroma:chat_update') { - return { event, chatUpdate: parseChat(data) } - } - } else { - console.warn('Unknown event', wsEvent) - return null - } -} - -export const WSConnectionStatus = Object.freeze({ - JOINED: 1, - CLOSED: 2, - ERROR: 3, - DISABLED: 4, - STARTING: 5, - STARTING_INITIAL: 6, -}) - -export const chats = ({ credentials }) => - promisedRequest({ - url: PLEROMA_CHATS_URL, - credentials, - }).then((data) => ({ - chatList: data.map(parseChat).filter((c) => c), - })) - -export const getOrCreateChat = ({ accountId, credentials }) => - promisedRequest({ - url: PLEROMA_CHAT_URL(accountId), + url: PLEROMA_BACKUP_URL, method: 'POST', credentials, }) -export const chatMessages = ({ - id, - credentials, - maxId, - sinceId, - limit = 20, -}) => { - return promisedRequest({ - url: PLEROMA_CHAT_MESSAGES_URL(id, { maxId, sinceId, limit }), +export const listBackups = ({ credentials }) => + promisedRequest({ + url: PLEROMA_BACKUP_URL, method: 'GET', credentials, - }) -} - -export const sendChatMessage = ({ - id, - content, - mediaId = null, - idempotencyKey, - credentials, -}) => { - const payload = { - content, - } - - if (mediaId) { - payload.media_id = mediaId - } - - const headers = {} - - if (idempotencyKey) { - headers['idempotency-key'] = idempotencyKey - } - - return promisedRequest({ - url: PLEROMA_CHAT_MESSAGES_URL(id), - method: 'POST', - payload, - credentials, - headers, - }) -} - -export const readChat = ({ id, lastReadId, credentials }) => - promisedRequest({ - url: PLEROMA_CHAT_READ_URL(id), - method: 'POST', - payload: { - last_read_id: lastReadId, + params: { + _cacheBooster: new Date().getTime(), }, + }) + +// #OAuth +export const fetchOAuthTokens = ({ credentials }) => + promisedRequest({ + url: '/api/oauth_tokens.json', credentials, }) -export const deleteChatMessage = ({ chatId, messageId, credentials }) => +export const revokeOAuthToken = ({ id, credentials }) => promisedRequest({ - url: PLEROMA_DELETE_CHAT_MESSAGE_URL(chatId, messageId), + url: `/api/oauth_tokens/${id}`, + credentials, + method: 'DELETE', + }) + +// #Lists +export const fetchLists = ({ credentials }) => + promisedRequest({ + url: MASTODON_LISTS_URL, + credentials, + }) + +export const createList = ({ title, credentials }) => + promisedRequest({ + url: MASTODON_LISTS_URL, + credentials, + method: 'POST', + payload: { title }, + }) + +export const getList = ({ listId, credentials }) => + promisedRequest({ + url: MASTODON_LIST_URL(listId), + credentials, + }) + +export const updateList = ({ listId, title, credentials }) => + promisedRequest({ + url: MASTODON_LIST_URL(listId), + + credentials, + method: 'PUT', + payload: { title }, + }) + +export const getListAccounts = ({ listId, credentials }) => + promisedRequest({ + url: MASTODON_LIST_ACCOUNTS_URL(listId), + credentials, + }).then((data) => data.map(({ id }) => id)) + +export const addAccountsToList = ({ listId, accountIds, credentials }) => + promisedRequest({ + url: MASTODON_LIST_ACCOUNTS_URL(listId), + credentials, + method: 'POST', + payload: { account_ids: accountIds }, + }) + +export const removeAccountsFromList = ({ listId, accountIds, credentials }) => + promisedRequest({ + url: MASTODON_LIST_ACCOUNTS_URL(listId), + credentials, + method: 'DELETE', + payload: { account_ids: accountIds }, + }) + +export const deleteList = ({ listId, credentials }) => + promisedRequest({ + url: MASTODON_LIST_URL(listId), method: 'DELETE', credentials, }) -export const fetchScrobbles = ({ accountId, limit = 1 }) => - promisedRequest({ - url: PLEROMA_SCROBBLES_URL(accountId, { limit }), - }) - +// #Bookmarks export const fetchBookmarkFolders = ({ credentials }) => promisedRequest({ url: PLEROMA_BOOKMARK_FOLDERS_URL, @@ -1582,3 +910,17 @@ export const deleteBookmarkFolder = ({ folderId, credentials }) => method: 'DELETE', credentials, }) + +// #So long and thanks for all the fish +export const deleteAccount = ({ credentials, password }) => { + const formData = new FormData() + + formData.append('password', password) + + return promisedRequest({ + url: DELETE_ACCOUNT_URL, + formData, + method: 'POST', + credentials, + }) +} diff --git a/src/components/bookmark_folder_edit/bookmark_folder_edit.js b/src/components/bookmark_folder_edit/bookmark_folder_edit.js index bc30790f6..748070495 100644 --- a/src/components/bookmark_folder_edit/bookmark_folder_edit.js +++ b/src/components/bookmark_folder_edit/bookmark_folder_edit.js @@ -4,7 +4,7 @@ import { useBookmarkFoldersStore } from 'src/stores/bookmark_folders.js' import { useInterfaceStore } from 'src/stores/interface.js' import { useOAuthStore } from 'src/stores/oauth.js' -import { fetchBookmarkFolders } from 'src/services/api/api.service.js' +import { fetchBookmarkFolders } from 'src/api/user.js' const BookmarkFolderEdit = { data() { diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js index bd71c2b5d..1b80b6321 100644 --- a/src/components/chat/chat.js +++ b/src/components/chat/chat.js @@ -5,7 +5,7 @@ import { mapGetters, mapState } from 'vuex' import ChatMessage from 'src/components/chat_message/chat_message.vue' import ChatTitle from 'src/components/chat_title/chat_title.vue' import PostStatusForm from 'src/components/post_status_form/post_status_form.vue' -import { WSConnectionStatus } from '../../services/api/api.service.js' +import { WSConnectionStatus } from 'src/api/public.js' import chatService from '../../services/chat_service/chat_service.js' import { buildFakeMessage } from '../../services/chat_utils/chat_utils.js' import { promiseInterval } from '../../services/promise_interval/promise_interval.js' @@ -23,7 +23,7 @@ import { chatMessages, getOrCreateChat, sendChatMessage, -} from 'src/services/api/api.service.js' +} from 'src/api/chats.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faChevronDown, faChevronLeft } from '@fortawesome/free-solid-svg-icons' diff --git a/src/components/chat_new/chat_new.js b/src/components/chat_new/chat_new.js index 7b08d3163..ec2656e5c 100644 --- a/src/components/chat_new/chat_new.js +++ b/src/components/chat_new/chat_new.js @@ -5,7 +5,7 @@ import UserAvatar from 'src/components/user_avatar/user_avatar.vue' import { useOAuthStore } from 'src/stores/oauth.js' -import { chats } from 'src/services/api/api.service.js' +import { chats } from 'src/api/chats.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faChevronLeft, faSearch } from '@fortawesome/free-solid-svg-icons' diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 6bc0b7ad0..30c437459 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -5,13 +5,13 @@ import { mapState } from 'vuex' import QuickFilterSettings from 'src/components/quick_filter_settings/quick_filter_settings.vue' import QuickViewSettings from 'src/components/quick_view_settings/quick_view_settings.vue' import ThreadTree from 'src/components/thread_tree/thread_tree.vue' -import { WSConnectionStatus } from '../../services/api/api.service.js' +import { WSConnectionStatus } from 'src/api/public.js' +import { useOAuthStore } from 'src/stores/oauth.js' import { useInterfaceStore } from 'src/stores/interface' import { useMergedConfigStore } from 'src/stores/merged_config.js' -import { useOAuthStore } from 'src/stores/oauth.js' -import { fetchConversation, fetchStatus } from 'src/services/api/api.service.js' +import { fetchConversation, fetchStatus } from 'src/api/public.js' import { library } from '@fortawesome/fontawesome-svg-core' import { diff --git a/src/components/follow_request_card/follow_request_card.js b/src/components/follow_request_card/follow_request_card.js index 178f36894..c2e10c242 100644 --- a/src/components/follow_request_card/follow_request_card.js +++ b/src/components/follow_request_card/follow_request_card.js @@ -6,7 +6,7 @@ import BasicUserCard from '../basic_user_card/basic_user_card.vue' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useOAuthStore } from 'src/stores/oauth.js' -import { approveUser, denyUser } from 'src/services/api/api.service.js' +import { approveUser, denyUser } from 'src/api/user.js' const FollowRequestCard = { props: ['user'], diff --git a/src/components/notification/notification.js b/src/components/notification/notification.js index 8fc77d91d..b6ee27f9c 100644 --- a/src/components/notification/notification.js +++ b/src/components/notification/notification.js @@ -18,7 +18,7 @@ import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useOAuthStore } from 'src/stores/oauth.js' import { useUserHighlightStore } from 'src/stores/user_highlight.js' -import { approveUser, denyUser } from 'src/services/api/api.service.js' +import { approveUser, denyUser } from 'src/api/user.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import { library } from '@fortawesome/fontawesome-svg-core' diff --git a/src/components/remote_user_resolver/remote_user_resolver.js b/src/components/remote_user_resolver/remote_user_resolver.js index bdef5b2a4..b44629fcf 100644 --- a/src/components/remote_user_resolver/remote_user_resolver.js +++ b/src/components/remote_user_resolver/remote_user_resolver.js @@ -1,6 +1,6 @@ import { useOAuthStore } from 'src/stores/oauth.js' -import { fetchUser } from 'src/services/api/api.service.js' +import { fetchUser } from 'src/api/public.js' const RemoteUserResolver = { data: () => ({ diff --git a/src/components/settings_modal/helpers/emoji_editing_popover.vue b/src/components/settings_modal/helpers/emoji_editing_popover.vue index 6cee8cebe..d3db657d3 100644 --- a/src/components/settings_modal/helpers/emoji_editing_popover.vue +++ b/src/components/settings_modal/helpers/emoji_editing_popover.vue @@ -159,7 +159,7 @@ import { addNewEmojiFile, deleteEmojiFile, updateEmojiFile, -} from 'src/services/api/admin.js' +} from 'src/api/admin.js' export default { components: { diff --git a/src/components/settings_modal/tabs/appearance_tab.js b/src/components/settings_modal/tabs/appearance_tab.js index 77686b736..f159c6090 100644 --- a/src/components/settings_modal/tabs/appearance_tab.js +++ b/src/components/settings_modal/tabs/appearance_tab.js @@ -10,11 +10,11 @@ import SharedComputedObject from '../helpers/shared_computed_object.js' import UnitSetting from '../helpers/unit_setting.vue' import Preview from './old_theme_tab/theme_preview.vue' +import { useOAuthStore } from 'src/stores/oauth.js' import { useInstanceStore } from 'src/stores/instance.js' import { normalizeThemeData, useInterfaceStore } from 'src/stores/interface.js' -import { useOAuthStore } from 'src/stores/oauth.js' -import { updateProfileImages } from 'src/services/api/api.service.js' +import { updateProfileImages } from 'src/api/user.js' import { newImporter } from 'src/services/export_import/export_import.js' import { adoptStyleSheets, diff --git a/src/components/settings_modal/tabs/composing_tab.js b/src/components/settings_modal/tabs/composing_tab.js index 90d80cbf6..0ad4f4cf0 100644 --- a/src/components/settings_modal/tabs/composing_tab.js +++ b/src/components/settings_modal/tabs/composing_tab.js @@ -11,13 +11,13 @@ import IntegerSetting from '../helpers/integer_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' import UnitSetting from '../helpers/unit_setting.vue' +import { useOAuthStore } from 'src/stores/oauth.js' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' -import { useOAuthStore } from 'src/stores/oauth.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' -import { updateProfile } from 'src/services/api/api.service.js' +import { updateProfile } from 'src/api/user.js' import localeService from 'src/services/locale/locale.service.js' import { cacheKey, clearCache, emojiCacheKey } from 'src/services/sw/sw.js' diff --git a/src/components/settings_modal/tabs/data_import_export_tab.js b/src/components/settings_modal/tabs/data_import_export_tab.js index 05af7d27c..a89657658 100644 --- a/src/components/settings_modal/tabs/data_import_export_tab.js +++ b/src/components/settings_modal/tabs/data_import_export_tab.js @@ -16,7 +16,7 @@ import { importFollows, importMutes, listBackups, -} from 'src/services/api/api.service.js' +} from 'src/api/user.js' const DataImportExportTab = { data() { diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index 893833866..e2302c774 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -8,14 +8,14 @@ import FloatSetting from '../helpers/float_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' import UnitSetting from '../helpers/unit_setting.vue' +import { useOAuthStore } from 'src/stores/oauth.js' import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useLocalConfigStore } from 'src/stores/local_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' -import { useOAuthStore } from 'src/stores/oauth.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' -import { updateProfile } from 'src/services/api/api.service.js' +import { updateProfile } from 'src/api/user.js' import localeService from 'src/services/locale/locale.service.js' const GeneralTab = { diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.js b/src/components/settings_modal/tabs/mutes_and_blocks_tab.js index d1f5bff12..cb945012f 100644 --- a/src/components/settings_modal/tabs/mutes_and_blocks_tab.js +++ b/src/components/settings_modal/tabs/mutes_and_blocks_tab.js @@ -13,7 +13,7 @@ import { useInstanceStore } from 'src/stores/instance.js' import { useOAuthStore } from 'src/stores/oauth.js' import { useOAuthTokensStore } from 'src/stores/oauth_tokens.js' -import { importBlocks, importFollows } from 'src/services/api/api.service.js' +import { importBlocks, importFollows } from 'src/api/user.js' const MutesAndBlocks = { data() { diff --git a/src/components/settings_modal/tabs/notifications_tab.js b/src/components/settings_modal/tabs/notifications_tab.js index 6ef8e0ab7..a666d42e3 100644 --- a/src/components/settings_modal/tabs/notifications_tab.js +++ b/src/components/settings_modal/tabs/notifications_tab.js @@ -3,7 +3,7 @@ import SharedComputedObject from '../helpers/shared_computed_object.js' import { useOAuthStore } from 'src/stores/oauth.js' -import { updateNotificationSettings } from 'src/services/api/api.service.js' +import { updateNotificationSettings } from 'src/api/user.js' const NotificationsTab = { data() { diff --git a/src/components/settings_modal/tabs/profile_tab.js b/src/components/settings_modal/tabs/profile_tab.js index 586ca30a8..d30a82bf9 100644 --- a/src/components/settings_modal/tabs/profile_tab.js +++ b/src/components/settings_modal/tabs/profile_tab.js @@ -5,7 +5,7 @@ import SharedComputedObject from '../helpers/shared_computed_object.js' import { useOAuthStore } from 'src/stores/oauth.js' -import { updateProfile } from 'src/services/api/api.service.js' +import { updateProfile } from 'src/api/user.js' import { library } from '@fortawesome/fontawesome-svg-core' import { diff --git a/src/components/settings_modal/tabs/security_tab/mfa.js b/src/components/settings_modal/tabs/security_tab/mfa.js index bba2a2ff9..12c464719 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa.js +++ b/src/components/settings_modal/tabs/security_tab/mfa.js @@ -12,7 +12,7 @@ import { mfaConfirmOTP, mfaSetupOTP, settingsMFA, -} from 'src/services/api/api.service.js' +} from 'src/api/user.js' const Mfa = { data: () => ({ diff --git a/src/components/settings_modal/tabs/security_tab/mfa_totp.js b/src/components/settings_modal/tabs/security_tab/mfa_totp.js index b7a1426ab..a22e88070 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa_totp.js +++ b/src/components/settings_modal/tabs/security_tab/mfa_totp.js @@ -4,7 +4,7 @@ import Confirm from './confirm.vue' import { useOAuthStore } from 'src/stores/oauth.js' -import { mfaDisableOTP } from 'src/services/api/api.service.js' +import { mfaDisableOTP } from 'src/api/user.js' export default { props: ['settings'], diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.js b/src/components/settings_modal/tabs/security_tab/security_tab.js index ca959ded5..30a8b580e 100644 --- a/src/components/settings_modal/tabs/security_tab/security_tab.js +++ b/src/components/settings_modal/tabs/security_tab/security_tab.js @@ -15,7 +15,7 @@ import { deleteAlias, listAliases, moveAccount, -} from 'src/services/api/api.service.js' +} from 'src/api/user.js' import localeService from 'src/services/locale/locale.service.js' const SecurityTab = { diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js index 1a35c019b..2f10c5ebc 100644 --- a/src/components/user_card/user_card.js +++ b/src/components/user_card/user_card.js @@ -26,7 +26,7 @@ import { useOAuthStore } from 'src/stores/oauth.js' import { usePostStatusStore } from 'src/stores/post_status' import { useUserHighlightStore } from 'src/stores/user_highlight.js' -import { updateProfile } from 'src/services/api/api.service.js' +import { updateProfile } from 'src/api/user.js' import { propsToNative } from 'src/services/attributes_helper/attributes_helper.service.js' import localeService from 'src/services/locale/locale.service.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' diff --git a/src/components/user_reporting_modal/user_reporting_modal.js b/src/components/user_reporting_modal/user_reporting_modal.js index 49e569f23..9659022d4 100644 --- a/src/components/user_reporting_modal/user_reporting_modal.js +++ b/src/components/user_reporting_modal/user_reporting_modal.js @@ -8,7 +8,7 @@ import UserLink from 'src/components/user_link/user_link.vue' import { useOAuthStore } from 'src/stores/oauth.js' import { useReportsStore } from 'src/stores/reports.js' -import { reportUser } from 'src/services/api/api.service.js' +import { reportUser } from 'src/api/user.js' const UserReportingModal = { components: { diff --git a/src/components/who_to_follow/who_to_follow.js b/src/components/who_to_follow/who_to_follow.js index b7bb24b45..3c0b53b0d 100644 --- a/src/components/who_to_follow/who_to_follow.js +++ b/src/components/who_to_follow/who_to_follow.js @@ -3,7 +3,7 @@ import FollowCard from 'src/components/follow_card/follow_card.vue' import { useInstanceStore } from 'src/stores/instance.js' import { useOAuthStore } from 'src/stores/oauth.js' -import { fetchUser, suggestions } from 'src/services/api/api.service.js' +import { fetchUser, suggestions } from 'src/api/public.js' const WhoToFollow = { components: { diff --git a/src/components/who_to_follow_panel/who_to_follow_panel.js b/src/components/who_to_follow_panel/who_to_follow_panel.js index fa1c12278..32f9dc918 100644 --- a/src/components/who_to_follow_panel/who_to_follow_panel.js +++ b/src/components/who_to_follow_panel/who_to_follow_panel.js @@ -4,7 +4,7 @@ import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useOAuthStore } from 'src/stores/oauth.js' -import { fetchUser, suggestions } from 'src/services/api/api.service.js' +import { fetchUser, suggestions } from 'src/api/public.js' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' function showWhoToFollow(panel, reply) { diff --git a/src/modules/api.js b/src/modules/api.js index c7de7a070..2dd5d23df 100644 --- a/src/modules/api.js +++ b/src/modules/api.js @@ -1,6 +1,6 @@ import { Socket } from 'phoenix' -import { WSConnectionStatus } from '../services/api/api.service.js' +import { WSConnectionStatus } from 'src/api/public.js' import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js' import { useInstanceStore } from 'src/stores/instance.js' @@ -13,7 +13,7 @@ import { fetchTimeline, getMastodonSocketURI, ProcessedWS, -} from 'src/services/api/api.service.js' +} from 'src/api/public.js' import followRequestFetcher from 'src/services/follow_request_fetcher/follow_request_fetcher.service' import notificationsFetcher from 'src/services/notifications_fetcher/notifications_fetcher.service.js' import timelineFetcher from 'src/services/timeline_fetcher/timeline_fetcher.service.js' diff --git a/src/modules/chats.js b/src/modules/chats.js index 4ad82eccd..abc035f09 100644 --- a/src/modules/chats.js +++ b/src/modules/chats.js @@ -11,11 +11,7 @@ import { promiseInterval } from '../services/promise_interval/promise_interval.j import { useOAuthStore } from 'src/stores/oauth.js' -import { - chats, - deleteChatMessage, - readChat, -} from 'src/services/api/api.service.js' +import { chats, deleteChatMessage, readChat } from 'src/api/chats.js' const emptyChatList = () => ({ data: [], diff --git a/src/modules/notifications.js b/src/modules/notifications.js index 3d5f787cf..d89cc79ba 100644 --- a/src/modules/notifications.js +++ b/src/modules/notifications.js @@ -1,4 +1,4 @@ -import { markNotificationsAsSeen } from '../services/api/api.service.js' +import { markNotificationsAsSeen } from 'src/api/user.js' import { closeAllDesktopNotifications, closeDesktopNotification, @@ -15,7 +15,7 @@ import { useOAuthStore } from 'src/stores/oauth.js' import { useReportsStore } from 'src/stores/reports.js' import { useSyncConfigStore } from 'src/stores/sync_config.js' -import { dismissNotification } from 'src/services/api/api.service.js' +import { dismissNotification } from 'src/api/user.js' const emptyNotifications = () => ({ desktopNotificationSilence: true, diff --git a/src/modules/profileConfig.js b/src/modules/profileConfig.js index 0f125e976..28536a2f4 100644 --- a/src/modules/profileConfig.js +++ b/src/modules/profileConfig.js @@ -5,7 +5,7 @@ import { useOAuthStore } from 'src/stores/oauth.js' import { updateNotificationSettings, updateProfile, -} from 'src/services/api/api.service.js' +} from 'src/api/user.js' const defaultApi = ({ rootState, commit }, { path, value }) => { const params = {} diff --git a/src/modules/statuses.js b/src/modules/statuses.js index ef6aedcbc..a8cecbecc 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -13,10 +13,11 @@ import { slice, } from 'lodash' +import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' +import { useInterfaceStore } from 'src/stores/interface.js' +import { useOAuthStore } from 'src/stores/oauth.js' + import { - bookmarkStatus, - deleteStatus, - favorite, fetchEmojiReactions, fetchFavoritedByUsers, fetchPinnedStatuses, @@ -25,22 +26,23 @@ import { fetchStatus, fetchStatusHistory, fetchStatusSource, + search2, +} from 'src/api/public.js' +import { + bookmarkStatus, + deleteStatus, + favorite, muteConversation, pinOwnStatus, reactWithEmoji, retweet, - search2, unbookmarkStatus, unfavorite, unmuteConversation, unpinOwnStatus, unreactWithEmoji, unretweet, -} from '../services/api/api.service.js' - -import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' -import { useInterfaceStore } from 'src/stores/interface.js' -import { useOAuthStore } from 'src/stores/oauth.js' +} from 'src/api/user.js' const emptyTl = (userId = 0) => ({ statuses: [], diff --git a/src/modules/users.js b/src/modules/users.js index 471c19289..0dc1f6aeb 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -9,7 +9,7 @@ import { uniq, } from 'lodash' -import { register } from '../services/api/api.service.js' +import { register } from 'src/api/public.js' import oauthApi from '../services/new_api/oauth.js' import { registerPushNotifications, @@ -32,21 +32,23 @@ import { useSyncConfigStore } from 'src/stores/sync_config.js' import { useUserHighlightStore } from 'src/stores/user_highlight.js' import { - fetchBlocks, - fetchDomainMutes, fetchFollowers, fetchFriends, - fetchMutes, fetchUser, fetchUserByName, + getCaptcha, + searchUsers, + verifyCredentials, +} from 'src/api/public.js' +import { + fetchBlocks, + fetchDomainMutes, + fetchMutes, fetchUserInLists, fetchUserRelationship, followUser, - getCaptcha, muteUser, - searchUsers, - verifyCredentials, -} from 'src/services/api/api.service.js' +} from 'src/api/user.js' // TODO: Unify with mergeOrAdd in statuses.js export const mergeOrAdd = (arr, obj, item) => { diff --git a/src/services/follow_manipulate/follow_manipulate.js b/src/services/follow_manipulate/follow_manipulate.js index 1a3b2395c..31b1686e9 100644 --- a/src/services/follow_manipulate/follow_manipulate.js +++ b/src/services/follow_manipulate/follow_manipulate.js @@ -4,7 +4,7 @@ import { fetchUserRelationship, followUser, unfollowUser, -} from 'src/services/api/api.service.js' +} from 'src/api/user.js' const fetchRelationship = (attempt, userId, store) => new Promise((resolve, reject) => { diff --git a/src/services/follow_request_fetcher/follow_request_fetcher.service.js b/src/services/follow_request_fetcher/follow_request_fetcher.service.js index 015eb55ed..ecb6cb7dd 100644 --- a/src/services/follow_request_fetcher/follow_request_fetcher.service.js +++ b/src/services/follow_request_fetcher/follow_request_fetcher.service.js @@ -1,5 +1,5 @@ -import { fetchFollowRequests } from '../api/api.service.js' -import { promiseInterval } from '../promise_interval/promise_interval.js' +import { fetchFollowRequests } from 'src/api/user.js' +import { promiseInterval } from 'src/services/promise_interval/promise_interval.js' const fetchAndUpdate = ({ store, credentials }) => { return fetchFollowRequests({ credentials }) diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js index e8fa3d16d..6258b0ebe 100644 --- a/src/services/notifications_fetcher/notifications_fetcher.service.js +++ b/src/services/notifications_fetcher/notifications_fetcher.service.js @@ -1,4 +1,4 @@ -import { fetchTimeline } from '../api/api.service.js' +import { fetchTimeline } from 'src/api/public.js' import { promiseInterval } from '../promise_interval/promise_interval.js' import { useInstanceStore } from 'src/stores/instance.js' diff --git a/src/services/status_poster/status_poster.service.js b/src/services/status_poster/status_poster.service.js index 1a814ec7c..ddc4a2f9a 100644 --- a/src/services/status_poster/status_poster.service.js +++ b/src/services/status_poster/status_poster.service.js @@ -5,7 +5,7 @@ import { postStatus as apiPostStatus, setMediaDescription as apiSetMediaDescription, uploadMedia as apiUploadMedia, -} from '../api/api.service.js' +} from 'src/api/user.js' const postStatus = ({ store, diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js index 510e97ab0..b2ed40e47 100644 --- a/src/services/timeline_fetcher/timeline_fetcher.service.js +++ b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -1,6 +1,6 @@ import { camelCase } from 'lodash' -import { fetchTimeline } from '../api/api.service.js' +import { fetchTimeline } from 'src/api/public.js' import { promiseInterval } from '../promise_interval/promise_interval.js' import { useInstanceStore } from 'src/stores/instance.js' diff --git a/src/stores/admin_settings.js b/src/stores/admin_settings.js index 884ac37a1..a437d56ca 100644 --- a/src/stores/admin_settings.js +++ b/src/stores/admin_settings.js @@ -31,8 +31,8 @@ import { setUsersRight, setUsersSuggestionStatus, setUsersTags, -} from 'src/services/api/admin.js' -import { listEmojiPacks } from 'src/services/api/api.service.js' +} from 'src/api/admin.js' +import { listEmojiPacks } from 'src/api/public.js' import { parseStatus } from 'src/services/entity_normalizer/entity_normalizer.service.js' export const defaultState = { @@ -551,7 +551,7 @@ export const useAdminSettingsStore = defineStore('adminSettings', { listEmojiPacks(params) { return listEmojiPacks({ ...params, - credentials: useOAuthStore().token, + credentials: useOAuthStore().token }) }, listRemoteEmojiPacks(params) { diff --git a/src/stores/announcements.js b/src/stores/announcements.js index 87d83813a..2b738b905 100644 --- a/src/stores/announcements.js +++ b/src/stores/announcements.js @@ -7,11 +7,9 @@ import { deleteAnnouncement, editAnnouncement, postAnnouncement, -} from 'src/services/api/admin.js' -import { - dismissAnnouncement, - getAnnouncements, -} from 'src/services/api/api.service.js' +} from 'src/api/admin.js' +import { getAnnouncements } from 'src/api/public.js' +import { dismissAnnouncement } from 'src/api/user.js' const FETCH_ANNOUNCEMENT_INTERVAL_MS = 1000 * 60 * 5 diff --git a/src/stores/bookmark_folders.js b/src/stores/bookmark_folders.js index 3e16121b1..928ad13a6 100644 --- a/src/stores/bookmark_folders.js +++ b/src/stores/bookmark_folders.js @@ -8,7 +8,7 @@ import { deleteBookmarkFolder, fetchBookmarkFolders, updateBookmarkFolder, -} from 'src/services/api/api.service.js' +} from 'src/api/user.js' import { promiseInterval } from 'src/services/promise_interval/promise_interval.js' export const useBookmarkFoldersStore = defineStore('bookmarkFolders', { diff --git a/src/stores/emoji.js b/src/stores/emoji.js index 079dedb9f..aefa79342 100644 --- a/src/stores/emoji.js +++ b/src/stores/emoji.js @@ -1,11 +1,11 @@ import { merge } from 'lodash' import { defineStore } from 'pinia' -import { useInstanceStore } from 'src/stores/instance.js' import { useOAuthStore } from 'src/stores/oauth.js' +import { useInstanceStore } from 'src/stores/instance.js' import { ensureFinalFallback } from 'src/i18n/languages.js' -import { listEmojiPacks } from 'src/services/api/api.service.js' +import { listEmojiPacks } from 'src/api/public.js' import { annotationsLoader } from 'virtual:pleroma-fe/emoji-annotations' @@ -188,8 +188,7 @@ export const useEmojiStore = defineStore('emoji', { this.adminPacksLocalLoading = true this.adminPacksLocal = await this.getAdminPacks( useInstanceStore().server, - (params) => - listEmojiPacks({ + (params) => listEmojiPacks({ ...params, credentials: useOAuthStore().token, }), @@ -222,13 +221,14 @@ export const useEmojiStore = defineStore('emoji', { instance, page: i, pageSize, - }).then((pageData) => { - if (pageData.error !== undefined) { - return Promise.reject(pageData.error) - } + }) + .then((pageData) => { + if (pageData.error !== undefined) { + return Promise.reject(pageData.error) + } - return pageData.packs - }), + return pageData.packs + }), ) } diff --git a/src/stores/instance.js b/src/stores/instance.js index 2f9bf3c44..8abb8078a 100644 --- a/src/stores/instance.js +++ b/src/stores/instance.js @@ -11,7 +11,7 @@ import { LOCAL_DEFAULT_CONFIG_DEFINITIONS, validateSetting, } from '../modules/default_config_state.js' -import { fetchKnownDomains } from '../services/api/api.service.js' +import { fetchKnownDomains } from 'src/api/public.js' import { useInterfaceStore } from 'src/stores/interface.js' diff --git a/src/stores/lists.js b/src/stores/lists.js index c361b6e5e..20529f06b 100644 --- a/src/stores/lists.js +++ b/src/stores/lists.js @@ -12,7 +12,7 @@ import { getListAccounts, removeAccountsFromList, updateList, -} from 'src/services/api/api.service.js' +} from 'src/api/user.js' import { promiseInterval } from 'src/services/promise_interval/promise_interval.js' export const useListsStore = defineStore('lists', { diff --git a/src/stores/oauth_tokens.js b/src/stores/oauth_tokens.js index 3f9138cec..bb9bbccbf 100644 --- a/src/stores/oauth_tokens.js +++ b/src/stores/oauth_tokens.js @@ -2,10 +2,7 @@ import { defineStore } from 'pinia' import { useOAuthStore } from 'src/stores/oauth.js' -import { - fetchOAuthTokens, - revokeOAuthToken, -} from 'src/services/api/api.service.js' +import { fetchOAuthTokens, revokeOAuthToken } from 'src/api/user.js' export const useOAuthTokensStore = defineStore('oauthTokens', { state: () => ({ diff --git a/src/stores/polls.js b/src/stores/polls.js index 78ccd059b..f2eafacfb 100644 --- a/src/stores/polls.js +++ b/src/stores/polls.js @@ -3,7 +3,8 @@ import { defineStore } from 'pinia' import { useOAuthStore } from 'src/stores/oauth.js' -import { fetchPoll, vote } from 'src/services/api/api.service.js' +import { fetchPoll } from 'src/api/public.js' +import { vote } from 'src/api/user.js' export const usePollsStore = defineStore('polls', { state: () => ({ diff --git a/src/stores/reports.js b/src/stores/reports.js index 2fea2e8e6..7d319d8e3 100644 --- a/src/stores/reports.js +++ b/src/stores/reports.js @@ -4,7 +4,7 @@ import { defineStore } from 'pinia' import { useInterfaceStore } from 'src/stores/interface.js' import { useOAuthStore } from 'src/stores/oauth.js' -import { setReportState } from 'src/services/api/admin.js' +import { setReportState } from 'src/api/admin.js' export const useReportsStore = defineStore('reports', { state: () => ({ diff --git a/src/stores/sync_config.js b/src/stores/sync_config.js index 348579b72..5ff42adaf 100644 --- a/src/stores/sync_config.js +++ b/src/stores/sync_config.js @@ -20,9 +20,9 @@ import { toRaw } from 'vue' import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js' +import { useOAuthStore } from 'src/stores/oauth.js' import { useInstanceStore } from 'src/stores/instance.js' import { useLocalConfigStore } from 'src/stores/local_config.js' -import { useOAuthStore } from 'src/stores/oauth.js' import { storage } from 'src/lib/storage.js' import { @@ -32,7 +32,7 @@ import { validateSetting, } from 'src/modules/default_config_state.js' import { oldDefaultConfigSync } from 'src/modules/old_default_config_state.js' -import { updateProfileJSON } from 'src/services/api/api.service.js' +import { updateProfileJSON } from 'src/api/user.js' export const VERSION = 2 export const NEW_USER_DATE = new Date('2026-03-16') // date of writing this, basically diff --git a/src/stores/user_highlight.js b/src/stores/user_highlight.js index b2cd680f5..41570521f 100644 --- a/src/stores/user_highlight.js +++ b/src/stores/user_highlight.js @@ -17,7 +17,7 @@ import { toRaw } from 'vue' import { useOAuthStore } from 'src/stores/oauth.js' import { storage } from 'src/lib/storage.js' -import { updateProfileJSON } from 'src/services/api/api.service.js' +import { updateProfileJSON } from 'src/api/user.js' export const NEW_USER_DATE = new Date('2022-08-04') // date of writing this, basically diff --git a/test/unit/specs/services/api/helpers.spec.js b/test/unit/specs/services/api/helpers.spec.js index 3ffb83c70..b1219bd46 100644 --- a/test/unit/specs/services/api/helpers.spec.js +++ b/test/unit/specs/services/api/helpers.spec.js @@ -1,4 +1,4 @@ -import { paramsString } from 'src/services/api/helpers.js' +import { paramsString } from 'src/api/helpers.js' describe('API Helpers', () => { describe.only('paramsString', () => {