From 32c68a92213f17fb5d3fbaf855c84c4b53b892bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Tue, 6 Aug 2024 19:52:36 +0200 Subject: [PATCH] Migrate to external library for interacting with API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- package.json | 1 - src/actions/accounts.ts | 34 +++--- src/actions/aliases.ts | 76 ++----------- src/actions/auth.ts | 14 +-- src/actions/backups.ts | 5 +- src/actions/bookmarks.ts | 6 +- src/actions/circle.ts | 10 +- src/actions/compose.ts | 2 +- src/actions/domain-blocks.ts | 2 +- src/actions/emoji-reacts.ts | 66 +---------- src/actions/events.ts | 83 ++++++-------- src/actions/external-auth.ts | 11 +- src/actions/favourites.ts | 26 ++--- src/actions/importer/index.ts | 14 --- src/actions/interactions.ts | 6 +- src/actions/me.ts | 4 +- src/actions/media.ts | 2 +- src/actions/notifications.ts | 18 +-- src/actions/oauth.ts | 6 +- src/actions/push-subscriptions.ts | 7 +- src/actions/scheduled-statuses.ts | 4 +- src/actions/search.ts | 1 - src/actions/settings.ts | 2 +- src/actions/status-quotes.ts | 24 ++-- src/actions/statuses.ts | 65 +++++------ src/actions/suggestions.ts | 4 +- src/actions/tags.ts | 10 +- src/api/hooks/accounts/useAccount.ts | 2 +- src/api/hooks/accounts/useAccountList.ts | 8 +- src/api/hooks/accounts/useAccountLookup.ts | 2 +- src/api/hooks/accounts/useRelationship.ts | 4 +- src/api/hooks/accounts/useRelationships.ts | 3 +- .../groups/useCancelMembershipRequest.ts | 22 ---- src/api/hooks/groups/useCreateGroup.ts | 13 +-- src/api/hooks/groups/useDeleteGroupStatus.ts | 4 +- src/api/hooks/groups/useGroup.ts | 4 +- src/api/hooks/groups/useGroupMedia.ts | 2 +- src/api/hooks/groups/useGroupMembers.ts | 2 +- .../groups/useGroupMembershipRequests.ts | 6 +- src/api/hooks/groups/useGroupRelationship.ts | 2 +- src/api/hooks/groups/useGroupRelationships.ts | 2 +- src/api/hooks/groups/useGroups.ts | 2 +- src/api/hooks/groups/useUpdateGroup.ts | 11 +- src/api/hooks/index.ts | 1 - src/api/hooks/statuses/useBookmarkFolders.ts | 2 +- .../hooks/statuses/useCreateBookmarkFolder.ts | 5 +- .../hooks/statuses/useDeleteBookmarkFolder.ts | 13 ++- .../hooks/statuses/useUpdateBookmarkFolder.ts | 5 +- src/components/hashtag.tsx | 6 +- src/components/status-action-bar.tsx | 4 +- src/components/status.tsx | 2 +- src/entity-store/hooks/types.ts | 2 +- src/entity-store/hooks/useBatchedEntities.ts | 8 +- src/entity-store/hooks/useEntities.ts | 19 ++-- src/entity-store/hooks/useEntity.ts | 4 +- src/entity-store/types.ts | 6 +- src/features/account/components/header.tsx | 2 +- .../components/registration-mode-picker.tsx | 2 +- .../auth-login/components/login-page.tsx | 2 +- .../components/chat-search/chat-search.tsx | 4 +- .../compose/components/privacy-dropdown.tsx | 4 +- src/features/developers/apps/create.tsx | 4 +- .../components/draft-status.tsx | 2 +- .../event/components/event-header.tsx | 4 +- src/features/event/event-information.tsx | 4 +- .../group/components/group-action-button.tsx | 7 +- .../notifications/components/notification.tsx | 30 ++--- .../status/components/detailed-status.tsx | 2 +- .../modals/select-bookmark-folder-modal.tsx | 2 +- src/normalizers/account.ts | 45 +------- src/normalizers/attachment.ts | 1 + src/normalizers/status.ts | 24 ++-- src/queries/accounts.ts | 2 +- src/queries/chats.ts | 86 +++++---------- src/queries/relationships.ts | 6 +- src/queries/suggestions.ts | 6 +- src/reducers/instance.ts | 4 +- src/reducers/status-lists.ts | 4 +- src/reducers/statuses.ts | 4 +- src/reducers/user-lists.ts | 4 +- src/schemas/index.ts | 10 +- src/schemas/notification.ts | 103 ------------------ src/sentry.ts | 4 +- src/utils/features.ts | 3 +- src/utils/notification.ts | 12 +- src/utils/queries.ts | 4 +- src/utils/quirks.ts | 2 +- src/utils/scopes.ts | 2 +- yarn.lock | 10 +- 89 files changed, 364 insertions(+), 710 deletions(-) delete mode 100644 src/api/hooks/groups/useCancelMembershipRequest.ts delete mode 100644 src/schemas/notification.ts diff --git a/package.json b/package.json index 3bc9d36bc..3ef9bce45 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,6 @@ "@types/uuid": "^9.0.0", "@vitejs/plugin-react": "^4.0.4", "@webbtc/webln-types": "^3.0.0", - "async-dispatch": "^1.0.10", "autoprefixer": "^10.4.15", "babel-plugin-formatjs": "^10.5.6", "babel-plugin-preval": "^5.1.0", diff --git a/src/actions/accounts.ts b/src/actions/accounts.ts index 0c09f9ab7..e66bdfbe8 100644 --- a/src/actions/accounts.ts +++ b/src/actions/accounts.ts @@ -13,7 +13,7 @@ import { } from './importer'; import type { Map as ImmutableMap } from 'immutable'; -import type { Account, PaginatedResponse } from 'pl-api'; +import type { Account, CreateAccountParams, PaginatedResponse } from 'pl-api'; import type { AppDispatch, RootState } from 'soapbox/store'; import type { APIEntity, Status } from 'soapbox/types/entities'; import type { History } from 'soapbox/types/history'; @@ -119,10 +119,10 @@ const maybeRedirectLogin = (error: { response: PlfeResponse }, history?: History const noOp = () => new Promise(f => f(undefined)); -const createAccount = (params: Record) => +const createAccount = (params: CreateAccountParams) => async (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: ACCOUNT_CREATE_REQUEST, params }); - return getClient(getState()).accounts.createAccount(params).then((token) => + return getClient(getState()).settings.createAccount(params).then((token) => dispatch({ type: ACCOUNT_CREATE_SUCCESS, params, token }), ).catch(error => { dispatch({ type: ACCOUNT_CREATE_FAIL, error, params }); @@ -215,7 +215,7 @@ const blockAccount = (id: string) => dispatch(blockAccountRequest(id)); - return getClient(getState()).accounts.blockAccount(id) + return getClient(getState()).filtering.blockAccount(id) .then(response => { dispatch(importEntities([response], Entities.RELATIONSHIPS)); // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers @@ -229,7 +229,7 @@ const unblockAccount = (id: string) => dispatch(unblockAccountRequest(id)); - return getClient(getState()).accounts.unblockAccount(id) + return getClient(getState()).filtering.unblockAccount(id) .then(response => { dispatch(importEntities([response], Entities.RELATIONSHIPS)); return dispatch(unblockAccountSuccess(response)); @@ -291,7 +291,7 @@ const muteAccount = (id: string, notifications?: boolean, duration = 0) => } } - return client.accounts.muteAccount(id, params) + return client.filtering.muteAccount(id, params) .then(response => { dispatch(importEntities([response], Entities.RELATIONSHIPS)); // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers @@ -306,7 +306,7 @@ const unmuteAccount = (id: string) => dispatch(unmuteAccountRequest(id)); - return getClient(getState()).accounts.unmuteAccount(id) + return getClient(getState()).filtering.unmuteAccount(id) .then(response => { dispatch(importEntities([response], Entities.RELATIONSHIPS)); return dispatch(unmuteAccountSuccess(response)); @@ -558,7 +558,7 @@ const fetchFollowRequests = () => dispatch(fetchFollowRequestsRequest()); - return getClient(getState()).accounts.getFollowRequests() + return getClient(getState()).myAccount.getFollowRequests() .then(response => { dispatch(importFetchedAccounts(response.items)); dispatch(fetchFollowRequestsSuccess(response.items, response.next)); @@ -618,7 +618,7 @@ const authorizeFollowRequest = (id: string) => dispatch(authorizeFollowRequestRequest(id)); - return getClient(getState()).accounts.acceptFollowRequest(id) + return getClient(getState()).myAccount.acceptFollowRequest(id) .then(() => dispatch(authorizeFollowRequestSuccess(id))) .catch(error => dispatch(authorizeFollowRequestFail(id, error))); }; @@ -645,7 +645,7 @@ const rejectFollowRequest = (id: string) => dispatch(rejectFollowRequestRequest(id)); - return getClient(getState()).accounts.rejectFollowRequest(id) + return getClient(getState()).myAccount.rejectFollowRequest(id) .then(() => dispatch(rejectFollowRequestSuccess(id))) .catch(error => dispatch(rejectFollowRequestFail(id, error))); }; @@ -740,9 +740,9 @@ const fetchPinnedAccounts = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(fetchPinnedAccountsRequest(id)); - return getClient(getState).request(`/api/v1/pleroma/accounts/${id}/endorsements`).then(response => { - dispatch(importFetchedAccounts(response.json)); - dispatch(fetchPinnedAccountsSuccess(id, response.json, null)); + return getClient(getState).accounts.getAccountEndorsements(id).then(response => { + dispatch(importFetchedAccounts(response)); + dispatch(fetchPinnedAccountsSuccess(id, response, null)); }).catch(error => { dispatch(fetchPinnedAccountsFail(id, error)); }); @@ -801,16 +801,16 @@ const fetchBirthdayReminders = (month: number, day: number) => dispatch({ type: BIRTHDAY_REMINDERS_FETCH_REQUEST, day, month, id: me }); - return getClient(getState).request('/api/v1/pleroma/birthdays', { params: { day, month } }).then(response => { - dispatch(importFetchedAccounts(response.json)); + return getClient(getState).accounts.getBirthdays(day, month).then(response => { + dispatch(importFetchedAccounts(response)); dispatch({ type: BIRTHDAY_REMINDERS_FETCH_SUCCESS, - accounts: response.json, + accounts: response, day, month, id: me, }); - }).catch(error => { + }).catch(() => { dispatch({ type: BIRTHDAY_REMINDERS_FETCH_FAIL, day, month, id: me }); }); }; diff --git a/src/actions/aliases.ts b/src/actions/aliases.ts index 2bf13863e..aaf737466 100644 --- a/src/actions/aliases.ts +++ b/src/actions/aliases.ts @@ -7,7 +7,6 @@ import { getFeatures } from 'soapbox/utils/features'; import { getClient } from '../api'; import { importFetchedAccounts } from './importer'; -import { patchMeSuccess } from './me'; import type { Account } from 'soapbox/schemas'; import type { AppDispatch, RootState } from 'soapbox/store'; @@ -35,18 +34,11 @@ const messages = defineMessages({ const fetchAliases = (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; - const state = getState(); - - const instance = state.instance; - const features = getFeatures(instance); - - if (!features.accountMoving) return; - dispatch(fetchAliasesRequest()); - return getClient(getState).request('/api/pleroma/aliases') + return getClient(getState).settings.getAccountAliases() .then(response => { - dispatch(fetchAliasesSuccess(response.json.aliases)); + dispatch(fetchAliasesSuccess(response.aliases)); }) .catch(err => dispatch(fetchAliasesFail(err))); }; @@ -99,31 +91,13 @@ const addToAliases = (account: Account) => const instance = state.instance; const features = getFeatures(instance); - if (!features.accountMoving) { - const me = state.me as string; - const alsoKnownAs = state.accounts_meta[me]?.pleroma?.also_known_as ?? []; - - dispatch(addToAliasesRequest()); - - return getClient(getState()).accounts.updateCredentials({ also_known_as: [...alsoKnownAs, account.pleroma?.ap_id] }) - .then((response => { - toast.success(messages.createSuccess); - dispatch(addToAliasesSuccess); - dispatch(patchMeSuccess(response)); - })) - .catch(err => dispatch(addToAliasesFail(err))); - - } - dispatch(addToAliasesRequest()); - return getClient(getState).request('/api/pleroma/aliases', { - method: 'PUT', body: { alias: account.acct }, - }).then(() => { - toast.success(messages.createSuccess); - dispatch(addToAliasesSuccess); - dispatch(fetchAliases); - }) + return getClient(getState).settings.addAccountAlias(account.acct).then(() => { + toast.success(messages.createSuccess); + dispatch(addToAliasesSuccess); + dispatch(fetchAliases); + }) .catch(err => dispatch(fetchAliasesFail(err))); }; @@ -143,39 +117,13 @@ const addToAliasesFail = (error: unknown) => ({ const removeFromAliases = (account: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; - const state = getState(); - - const instance = state.instance; - const features = getFeatures(instance); - - if (!features.accountMoving) { - const me = state.me as string; - const alsoKnownAs = state.accounts_meta[me]?.pleroma?.also_known_as ?? []; - - dispatch(removeFromAliasesRequest()); - - return getClient(getState()).accounts.updateCredentials({ - also_known_as: alsoKnownAs.filter((id: string) => id !== account), - }).then(response => { - toast.success(messages.removeSuccess); - dispatch(removeFromAliasesSuccess); - dispatch(patchMeSuccess(response)); - }) - .catch(err => dispatch(removeFromAliasesFail(err))); - - return; - } - dispatch(addToAliasesRequest()); - return getClient(getState).request('/api/pleroma/aliases', { - method: 'DELETE', body: { alias: account }, - }).then(() => { - toast.success(messages.removeSuccess); - dispatch(removeFromAliasesSuccess); - dispatch(fetchAliases); - }) - .catch(err => dispatch(fetchAliasesFail(err))); + return getClient(getState).settings.deleteAccountAlias(account).then(() => { + toast.success(messages.removeSuccess); + dispatch(removeFromAliasesSuccess); + dispatch(fetchAliases); + }).catch(err => dispatch(fetchAliasesFail(err))); }; const removeFromAliasesRequest = () => ({ diff --git a/src/actions/auth.ts b/src/actions/auth.ts index 5585933ac..97b99f2a0 100644 --- a/src/actions/auth.ts +++ b/src/actions/auth.ts @@ -6,7 +6,7 @@ * @see module:soapbox/actions/oauth * @see module:soapbox/actions/security */ -import { PlApiClient } from 'pl-api'; +import { PlApiClient, type CreateAccountParams, type Token } from 'pl-api'; import { defineMessages } from 'react-intl'; import { createAccount } from 'soapbox/actions/accounts'; @@ -120,7 +120,7 @@ const createUserToken = (username: string, password: string) => }; return dispatch(obtainOAuthToken(params)) - .then((token: Record) => dispatch(authLoggedIn(token))); + .then((token) => dispatch(authLoggedIn(token))); }; const otpVerify = (code: string, mfa_token: string) => @@ -145,13 +145,13 @@ const otpVerify = (code: string, mfa_token: string) => const verifyCredentials = (token: string, accountUrl?: string) => (dispatch: AppDispatch, getState: () => RootState) => { - const baseURL = parseBaseURL(accountUrl); + const baseURL = parseBaseURL(accountUrl) || BuildConfig.BACKEND_URL; dispatch({ type: VERIFY_CREDENTIALS_REQUEST, token }); const client = new PlApiClient(baseURL, token, { fetchInstance: false }); - return client.accounts.verifyCredentials().then((account) => { + return client.settings.verifyCredentials().then((account) => { dispatch(importFetchedAccount(account)); dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account }); if (account.id === getState().me) dispatch(fetchMeSuccess(account)); @@ -259,13 +259,13 @@ const fetchOwnAccounts = () => }); }; -const register = (params: Record) => +const register = (params: CreateAccountParams) => (dispatch: AppDispatch) => { params.fullname = params.username; return dispatch(createAppAndToken()) .then(() => dispatch(createAccount(params))) - .then(({ token }: { token: Record }) => { + .then(({ token }: { token: Token }) => { dispatch(startOnboarding()); return dispatch(authLoggedIn(token)); }); @@ -274,7 +274,7 @@ const register = (params: Record) => const fetchCaptcha = () => (_dispatch: AppDispatch, getState: () => RootState) => getClient(getState).request('/api/pleroma/captcha'); -const authLoggedIn = (token: Record) => +const authLoggedIn = (token: Token) => (dispatch: AppDispatch) => { dispatch({ type: AUTH_LOGGED_IN, token }); return token; diff --git a/src/actions/backups.ts b/src/actions/backups.ts index b935b4b45..6c0fac0b2 100644 --- a/src/actions/backups.ts +++ b/src/actions/backups.ts @@ -13,7 +13,8 @@ const BACKUPS_CREATE_FAIL = 'BACKUPS_CREATE_FAIL'; const fetchBackups = () => (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: BACKUPS_FETCH_REQUEST }); - return getClient(getState).request('/api/v1/pleroma/backups').then(({ json: backups }) => + + return getClient(getState).settings.getBackups().then((backups) => dispatch({ type: BACKUPS_FETCH_SUCCESS, backups }), ).catch(error => { dispatch({ type: BACKUPS_FETCH_FAIL, error }); @@ -23,7 +24,7 @@ const fetchBackups = () => const createBackup = () => (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: BACKUPS_CREATE_REQUEST }); - return getClient(getState).request('/api/v1/pleroma/backups').then(({ json: backups }) => + return getClient(getState).settings.createBackup().then((backups) => dispatch({ type: BACKUPS_CREATE_SUCCESS, backups }), ).catch(error => { dispatch({ type: BACKUPS_CREATE_FAIL, error }); diff --git a/src/actions/bookmarks.ts b/src/actions/bookmarks.ts index 92a9de498..930512196 100644 --- a/src/actions/bookmarks.ts +++ b/src/actions/bookmarks.ts @@ -1,10 +1,10 @@ -import { getClient, getNextLink } from '../api'; +import { getClient } from '../api'; import { importFetchedStatuses } from './importer'; +import type { PaginatedResponse, Status } from 'pl-api'; import type { AppDispatch, RootState } from 'soapbox/store'; import type { APIEntity } from 'soapbox/types/entities'; -import type { PaginatedResponse, Status } from 'pl-api'; const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST'; const BOOKMARKED_STATUSES_FETCH_SUCCESS = 'BOOKMARKED_STATUSES_FETCH_SUCCESS'; @@ -24,7 +24,7 @@ const fetchBookmarkedStatuses = (folderId?: string) => dispatch(fetchBookmarkedStatusesRequest(folderId)); - return getClient(getState()).accounts.getBookmarks({ folder_id: folderId }).then(response => { + return getClient(getState()).myAccount.getBookmarks({ folder_id: folderId }).then(response => { dispatch(importFetchedStatuses(response.items)); return dispatch(fetchBookmarkedStatusesSuccess(response.items, response.next, folderId)); }).catch(error => { diff --git a/src/actions/circle.ts b/src/actions/circle.ts index c9106b8d4..9f1ebd6ef 100644 --- a/src/actions/circle.ts +++ b/src/actions/circle.ts @@ -36,7 +36,7 @@ const processCircle = (setProgress: (progress: { }; }; - const fetchStatuses = async (next?: () => Promise>) => { + const fetchStatuses = async (next: (() => Promise>) | null) => { const response = await (next?.() || client.accounts.getAccountStatuses(me, { limit: 40 })); response.items.forEach((status) => { @@ -63,8 +63,8 @@ const processCircle = (setProgress: (progress: { return response.next; }; - const fetchFavourites = async (next?: () => Promise>) => { // limit 40 - const response = await (next?.() || client.accounts.getFavourites({ limit: 40 })); + const fetchFavourites = async (next: (() => Promise>) | null) => { // limit 40 + const response = await (next?.() || client.myAccount.getFavourites({ limit: 40 })); response.items.forEach((status) => { if (status.account.id === me) return; @@ -81,13 +81,13 @@ const processCircle = (setProgress: (progress: { return response.next; }; - for (let next: (() => Promise>) | null | undefined, i = 0; i < 20; i++) { + for (let next: (() => Promise>) | null = null, i = 0; i < 20; i++) { next = await fetchStatuses(next); setProgress({ state: 'fetchingStatuses', progress: (i / 20) * 40 }); if (!next) break; } - for (let next: (() => Promise>) | null | undefined, i = 0; i < 20; i++) { + for (let next: (() => Promise>) | null = null, i = 0; i < 20; i++) { next = await fetchFavourites(next); setProgress({ state: 'fetchingFavourites', progress: 40 + (i / 20) * 40 }); if (!next) break; diff --git a/src/actions/compose.ts b/src/actions/compose.ts index 17e78e079..9320e11a0 100644 --- a/src/actions/compose.ts +++ b/src/actions/compose.ts @@ -567,7 +567,7 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, composeId, cancelFetchComposeSuggestions = new AbortController(); } - return getClient(getState()).accounts.searchAccounts(token.slice(1), { resolve: false, limit: 10 }) // WIP: signal + return getClient(getState()).accounts.searchAccounts(token.slice(1), { resolve: false, limit: 10 }, { signal }) .then(response => { dispatch(importFetchedAccounts(response)); dispatch(readyComposeSuggestionsAccounts(composeId, token, response)); diff --git a/src/actions/domain-blocks.ts b/src/actions/domain-blocks.ts index d94bb75b5..60b2a7291 100644 --- a/src/actions/domain-blocks.ts +++ b/src/actions/domain-blocks.ts @@ -1,7 +1,7 @@ import { Entities } from 'soapbox/entity-store/entities'; import { isLoggedIn } from 'soapbox/utils/auth'; -import { getClient, getNextLink } from '../api'; +import { getClient } from '../api'; import type { PaginatedResponse } from 'pl-api'; import type { EntityStore } from 'soapbox/entity-store/types'; diff --git a/src/actions/emoji-reacts.ts b/src/actions/emoji-reacts.ts index 69ef8dd4a..0f122c21f 100644 --- a/src/actions/emoji-reacts.ts +++ b/src/actions/emoji-reacts.ts @@ -4,11 +4,11 @@ import { isLoggedIn } from 'soapbox/utils/auth'; import { getClient } from '../api'; -import { importFetchedAccounts, importFetchedStatus } from './importer'; +import { importFetchedStatus } from './importer'; import { favourite, unfavourite } from './interactions'; import type { AppDispatch, RootState } from 'soapbox/store'; -import type { APIEntity, EmojiReaction, Status } from 'soapbox/types/entities'; +import type { EmojiReaction, Status } from 'soapbox/types/entities'; const EMOJI_REACT_REQUEST = 'EMOJI_REACT_REQUEST'; const EMOJI_REACT_SUCCESS = 'EMOJI_REACT_SUCCESS'; @@ -18,10 +18,6 @@ const UNEMOJI_REACT_REQUEST = 'UNEMOJI_REACT_REQUEST'; const UNEMOJI_REACT_SUCCESS = 'UNEMOJI_REACT_SUCCESS'; const UNEMOJI_REACT_FAIL = 'UNEMOJI_REACT_FAIL'; -const EMOJI_REACTS_FETCH_REQUEST = 'EMOJI_REACTS_FETCH_REQUEST'; -const EMOJI_REACTS_FETCH_SUCCESS = 'EMOJI_REACTS_FETCH_SUCCESS'; -const EMOJI_REACTS_FETCH_FAIL = 'EMOJI_REACTS_FETCH_FAIL'; - const noOp = () => () => new Promise(f => f(undefined)); const simpleEmojiReact = (status: Status, emoji: string, custom?: string) => @@ -49,36 +45,14 @@ const simpleEmojiReact = (status: Status, emoji: string, custom?: string) => }); }; -const fetchEmojiReacts = (id: string, emoji: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return dispatch(noOp()); - - dispatch(fetchEmojiReactsRequest(id, emoji)); - - const url = emoji - ? `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` - : `/api/v1/pleroma/statuses/${id}/reactions`; - - return getClient(getState).request(url).then(response => { - response.json.forEach((emojiReact: APIEntity) => { - dispatch(importFetchedAccounts(emojiReact.accounts)); - }); - dispatch(fetchEmojiReactsSuccess(id, response.json)); - }).catch(error => { - dispatch(fetchEmojiReactsFail(id, error)); - }); - }; - const emojiReact = (status: Status, emoji: string, custom?: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return dispatch(noOp()); dispatch(emojiReactRequest(status, emoji, custom)); - return getClient(getState).request(`/api/v1/pleroma/statuses/${status.id}/reactions/${emoji}`, { - method: 'PUT', - }).then((response) => { - dispatch(importFetchedStatus(response.json)); + return getClient(getState).statuses.createStatusReaction(status.id, emoji).then((response) => { + dispatch(importFetchedStatus(response)); dispatch(emojiReactSuccess(status, emoji)); }).catch((error) => { dispatch(emojiReactFail(status, emoji, error)); @@ -91,35 +65,14 @@ const unEmojiReact = (status: Status, emoji: string) => dispatch(unEmojiReactRequest(status, emoji)); - return getClient(getState).request(`/api/v1/pleroma/statuses/${status.id}/reactions/${emoji}`, { - method: 'DELETE', - - }).then(response => { - dispatch(importFetchedStatus(response.json)); + return getClient(getState).statuses.deleteStatusReaction(status.id, emoji).then(response => { + dispatch(importFetchedStatus(response)); dispatch(unEmojiReactSuccess(status, emoji)); }).catch(error => { dispatch(unEmojiReactFail(status, emoji, error)); }); }; -const fetchEmojiReactsRequest = (id: string, emoji: string) => ({ - type: EMOJI_REACTS_FETCH_REQUEST, - id, - emoji, -}); - -const fetchEmojiReactsSuccess = (id: string, emojiReacts: APIEntity[]) => ({ - type: EMOJI_REACTS_FETCH_SUCCESS, - id, - emojiReacts, -}); - -const fetchEmojiReactsFail = (id: string, error: unknown) => ({ - type: EMOJI_REACTS_FETCH_FAIL, - id, - error, -}); - const emojiReactRequest = (status: Status, emoji: string, custom?: string) => ({ type: EMOJI_REACT_REQUEST, status, @@ -172,16 +125,9 @@ export { UNEMOJI_REACT_REQUEST, UNEMOJI_REACT_SUCCESS, UNEMOJI_REACT_FAIL, - EMOJI_REACTS_FETCH_REQUEST, - EMOJI_REACTS_FETCH_SUCCESS, - EMOJI_REACTS_FETCH_FAIL, simpleEmojiReact, - fetchEmojiReacts, emojiReact, unEmojiReact, - fetchEmojiReactsRequest, - fetchEmojiReactsSuccess, - fetchEmojiReactsFail, emojiReactRequest, emojiReactSuccess, emojiReactFail, diff --git a/src/actions/events.ts b/src/actions/events.ts index d9eea2dce..02458c5a9 100644 --- a/src/actions/events.ts +++ b/src/actions/events.ts @@ -1,6 +1,6 @@ import { defineMessages, IntlShape } from 'react-intl'; -import { getClient, getNextLink } from 'soapbox/api'; +import { getClient } from 'soapbox/api'; import toast from 'soapbox/toast'; import { importFetchedAccounts, importFetchedStatus, importFetchedStatuses } from './importer'; @@ -12,6 +12,7 @@ import { STATUS_FETCH_SOURCE_SUCCESS, } from './statuses'; +import type { Account, CreateEventParams, PaginatedResponse } from 'pl-api'; import type { ReducerStatus } from 'soapbox/reducers/statuses'; import type { AppDispatch, RootState } from 'soapbox/store'; import type { APIEntity, Status as StatusEntity } from 'soapbox/types/entities'; @@ -97,7 +98,7 @@ const messages = defineMessages({ const locationSearch = (query: string, signal?: AbortSignal) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: LOCATION_SEARCH_REQUEST, query }); - return getClient(getState).request('/api/v1/pleroma/search/location', { params: { q: query }, signal }).then(({ json: locations }) => { + return getClient(getState).search.searchLocation(query, { signal }).then((locations) => { dispatch({ type: LOCATION_SEARCH_SUCCESS, locations }); return locations; }).catch(error => { @@ -211,7 +212,7 @@ const submitEvent = () => dispatch(submitEventRequest()); - const params: Record = { + const params: CreateEventParams = { name, status, start_time: startTime, @@ -219,14 +220,11 @@ const submitEvent = () => content_type: 'text/markdown', }; - if (endTime) params.end_time = endTime; - if (banner) params.banner_id = banner.id; + if (endTime) params.end_time = endTime; + if (banner) params.banner_id = banner.id; if (location) params.location_id = location.origin_id; - return getClient(getState).request(id === null ? '/api/v1/pleroma/events' : `/api/v1/pleroma/events/${id}`, { - method: id === null ? 'POST' : 'PUT', - body: params, - }).then(({ json: data }) => { + return (id === null ? getClient(state).events.createEvent(params) : getClient(state).events.editEvent(id, params)).then((data) => { dispatch(closeModal('COMPOSE_EVENT')); dispatch(importFetchedStatus(data)); dispatch(submitEventSuccess(data)); @@ -266,14 +264,11 @@ const joinEvent = (id: string, participationMessage?: string) => dispatch(joinEventRequest(status)); - return getClient(getState).request(`/api/v1/pleroma/events/${id}/join`, { - method: 'POST', - body: { participation_message: participationMessage }, - }).then(({ json: data }) => { + return getClient(getState).events.joinEvent(id, participationMessage).then((data) => { dispatch(importFetchedStatus(data)); dispatch(joinEventSuccess(data)); toast.success( - data.pleroma.event?.join_state === 'pending' ? messages.joinRequestSuccess : messages.joinSuccess, + data.event?.join_state === 'pending' ? messages.joinRequestSuccess : messages.joinSuccess, { actionLabel: messages.view, actionLink: `/@${data.account.acct}/events/${data.id}`, @@ -311,9 +306,7 @@ const leaveEvent = (id: string) => dispatch(leaveEventRequest(status)); - return getClient(getState).request(`/api/v1/pleroma/events/${id}/leave`, { - method: 'POST', - }).then(({ json: data }) => { + return getClient(getState).events.leaveEvent(id).then((data) => { dispatch(importFetchedStatus(data)); dispatch(leaveEventSuccess(data)); }).catch((error) => { @@ -341,10 +334,9 @@ const fetchEventParticipations = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(fetchEventParticipationsRequest(id)); - return getClient(getState).request(`/api/v1/pleroma/events/${id}/participations`).then(response => { - const next = getNextLink(response); - dispatch(importFetchedAccounts(response.json)); - return dispatch(fetchEventParticipationsSuccess(id, response.json, next || null)); + return getClient(getState).events.getEventParticipations(id).then(response => { + dispatch(importFetchedAccounts(response.items)); + return dispatch(fetchEventParticipationsSuccess(id, response.items, response.next)); }).catch(error => { dispatch(fetchEventParticipationsFail(id, error)); }); @@ -355,7 +347,7 @@ const fetchEventParticipationsRequest = (id: string) => ({ id, }); -const fetchEventParticipationsSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({ +const fetchEventParticipationsSuccess = (id: string, accounts: APIEntity[], next: (() => Promise>) | null) => ({ type: EVENT_PARTICIPATIONS_FETCH_SUCCESS, id, accounts, @@ -380,7 +372,7 @@ const expandEventParticipations = (id: string) => return next().then(response => { dispatch(importFetchedAccounts(response.items)); - return dispatch(expandEventParticipationsSuccess(id, response.items, response.next || null)); + return dispatch(expandEventParticipationsSuccess(id, response.items, response.next)); }).catch(error => { dispatch(expandEventParticipationsFail(id, error)); }); @@ -391,7 +383,7 @@ const expandEventParticipationsRequest = (id: string) => ({ id, }); -const expandEventParticipationsSuccess = (id: string, accounts: APIEntity[], next: string | null) => ({ +const expandEventParticipationsSuccess = (id: string, accounts: APIEntity[], next: (() => Promise>) | null) => ({ type: EVENT_PARTICIPATIONS_EXPAND_SUCCESS, id, accounts, @@ -408,10 +400,9 @@ const fetchEventParticipationRequests = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(fetchEventParticipationRequestsRequest(id)); - return getClient(getState).request(`/api/v1/pleroma/events/${id}/participation_requests`).then(response => { - const next = getNextLink(response); - dispatch(importFetchedAccounts(response.json.map(({ account }: APIEntity) => account))); - return dispatch(fetchEventParticipationRequestsSuccess(id, response.json, next || null)); + return getClient(getState).events.getEventParticipationRequests(id).then(response => { + dispatch(importFetchedAccounts(response.items.map(({ account }: APIEntity) => account))); + return dispatch(fetchEventParticipationRequestsSuccess(id, response.items, response.next)); }).catch(error => { dispatch(fetchEventParticipationRequestsFail(id, error)); }); @@ -422,7 +413,7 @@ const fetchEventParticipationRequestsRequest = (id: string) => ({ id, }); -const fetchEventParticipationRequestsSuccess = (id: string, participations: APIEntity[], next: string | null) => ({ +const fetchEventParticipationRequestsSuccess = (id: string, participations: APIEntity[], next: (() => Promise>) | null) => ({ type: EVENT_PARTICIPATION_REQUESTS_FETCH_SUCCESS, id, participations, @@ -437,18 +428,17 @@ const fetchEventParticipationRequestsFail = (id: string, error: unknown) => ({ const expandEventParticipationRequests = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { - const url = getState().user_lists.event_participations.get(id)?.next || null; + const next = getState().user_lists.event_participation_requests.get(id)?.next || null; - if (url === null) { + if (next === null) { return dispatch(noOp); } dispatch(expandEventParticipationRequestsRequest(id)); - return getClient(getState).request(url).then(response => { - const next = getNextLink(response); - dispatch(importFetchedAccounts(response.json.map(({ account }: APIEntity) => account))); - return dispatch(expandEventParticipationRequestsSuccess(id, response.json, next || null)); + return next().then(response => { + dispatch(importFetchedAccounts(response.items.map(({ account }: APIEntity) => account))); + return dispatch(expandEventParticipationRequestsSuccess(id, response.items, response.next)); }).catch(error => { dispatch(expandEventParticipationRequestsFail(id, error)); }); @@ -459,7 +449,7 @@ const expandEventParticipationRequestsRequest = (id: string) => ({ id, }); -const expandEventParticipationRequestsSuccess = (id: string, participations: APIEntity[], next: string | null) => ({ +const expandEventParticipationRequestsSuccess = (id: string, participations: APIEntity[], next: (() => Promise>) | null) => ({ type: EVENT_PARTICIPATION_REQUESTS_EXPAND_SUCCESS, id, participations, @@ -476,9 +466,7 @@ const authorizeEventParticipationRequest = (id: string, accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(authorizeEventParticipationRequestRequest(id, accountId)); - return getClient(getState).request(`/api/v1/pleroma/events/${id}/participation_requests/${accountId}/authorize`, { - method: 'POST', - }).then(() => { + return getClient(getState).events.acceptEventParticipationRequest(id, accountId).then(() => { dispatch(authorizeEventParticipationRequestSuccess(id, accountId)); toast.success(messages.authorized); }).catch(error => dispatch(authorizeEventParticipationRequestFail(id, accountId, error))); @@ -507,9 +495,7 @@ const rejectEventParticipationRequest = (id: string, accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(rejectEventParticipationRequestRequest(id, accountId)); - return getClient(getState).request(`/api/v1/pleroma/events/${id}/participation_requests/${accountId}/reject`, { - method: 'POST', - }).then(() => { + return getClient(getState).events.rejectEventParticipationRequest(id, accountId).then(() => { dispatch(rejectEventParticipationRequestSuccess(id, accountId)); toast.success(messages.rejected); }).catch(error => dispatch(rejectEventParticipationRequestFail(id, accountId, error))); @@ -536,7 +522,7 @@ const rejectEventParticipationRequestFail = (id: string, accountId: string, erro const fetchEventIcs = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => - getClient(getState).request(`/api/v1/pleroma/events/${id}/ics`); + getClient(getState).events.getEventIcs(id); const cancelEventCompose = () => ({ type: EVENT_COMPOSE_CANCEL, @@ -577,7 +563,7 @@ const fetchRecentEvents = () => dispatch({ type: RECENT_EVENTS_FETCH_REQUEST }); return getClient(getState()).timelines.publicTimeline({ - only_events: true + only_events: true, }).then(response => { dispatch(importFetchedStatuses(response.items)); dispatch({ @@ -598,13 +584,12 @@ const fetchJoinedEvents = () => dispatch({ type: JOINED_EVENTS_FETCH_REQUEST }); - getClient(getState).request('/api/v1/pleroma/events/joined_events').then(response => { - const next = getNextLink(response); - dispatch(importFetchedStatuses(response.json)); + getClient(getState).events.getJoinedEvents().then(response => { + dispatch(importFetchedStatuses(response.items)); dispatch({ type: JOINED_EVENTS_FETCH_SUCCESS, - statuses: response.json, - next: next || null, + statuses: response.items, + next: response.next, }); }).catch(error => { dispatch({ type: JOINED_EVENTS_FETCH_FAIL, error }); diff --git a/src/actions/external-auth.ts b/src/actions/external-auth.ts index 2f83ce90d..6b0f51f8f 100644 --- a/src/actions/external-auth.ts +++ b/src/actions/external-auth.ts @@ -6,12 +6,11 @@ * @see module:soapbox/actions/oauth */ -import { PlApiClient } from 'pl-api'; +import { instanceSchema, PlApiClient, type Instance } from 'pl-api'; import { createApp } from 'soapbox/actions/apps'; import { authLoggedIn, verifyCredentials, switchAccount } from 'soapbox/actions/auth'; import { obtainOAuthToken } from 'soapbox/actions/oauth'; -import { instanceSchema, type Instance } from 'soapbox/schemas'; import { parseBaseURL } from 'soapbox/utils/auth'; import sourceCode from 'soapbox/utils/code'; import { getQuirks } from 'soapbox/utils/quirks'; @@ -84,7 +83,7 @@ const loginWithCode = (code: string) => const baseURL = localStorage.getItem('plfe:external:baseurl')!; const scope = localStorage.getItem('plfe:external:scopes')!; - const params: Record = { + const params = { client_id, client_secret, redirect_uri, @@ -94,9 +93,9 @@ const loginWithCode = (code: string) => }; return dispatch(obtainOAuthToken(params, baseURL)) - .then((token: Record) => dispatch(authLoggedIn(token))) - .then(({ access_token }: any) => dispatch(verifyCredentials(access_token as string, baseURL))) - .then((account: { id: string }) => dispatch(switchAccount(account.id))) + .then((token) => dispatch(authLoggedIn(token))) + .then(({ access_token }) => dispatch(verifyCredentials(access_token as string, baseURL))) + .then((account) => dispatch(switchAccount(account.id))) .then(() => window.location.href = '/'); }; diff --git a/src/actions/favourites.ts b/src/actions/favourites.ts index 48660c4d7..470206751 100644 --- a/src/actions/favourites.ts +++ b/src/actions/favourites.ts @@ -1,6 +1,6 @@ import { isLoggedIn } from 'soapbox/utils/auth'; -import { getClient, getNextLink } from '../api'; +import { getClient } from '../api'; import { importFetchedStatuses } from './importer'; @@ -34,7 +34,7 @@ const fetchFavouritedStatuses = () => dispatch(fetchFavouritedStatusesRequest()); - return getClient(getState()).accounts.getFavourites().then(response => { + return getClient(getState()).myAccount.getFavourites().then(response => { dispatch(importFetchedStatuses(response.items)); dispatch(fetchFavouritedStatusesSuccess(response.items, response.next)); }).catch(error => { @@ -105,10 +105,9 @@ const fetchAccountFavouritedStatuses = (accountId: string) => dispatch(fetchAccountFavouritedStatusesRequest(accountId)); - return getClient(getState).request(`/api/v1/pleroma/accounts/${accountId}/favourites`).then(response => { - const next = getNextLink(response); - dispatch(importFetchedStatuses(response.json)); - dispatch(fetchAccountFavouritedStatusesSuccess(accountId, response.json, next || null)); + return getClient(getState).accounts.getAccountFavourites(accountId).then(response => { + dispatch(importFetchedStatuses(response.items)); + dispatch(fetchAccountFavouritedStatusesSuccess(accountId, response.items, response.next)); }).catch(error => { dispatch(fetchAccountFavouritedStatusesFail(accountId, error)); }); @@ -120,7 +119,7 @@ const fetchAccountFavouritedStatusesRequest = (accountId: string) => ({ skipLoading: true, }); -const fetchAccountFavouritedStatusesSuccess = (accountId: string, statuses: APIEntity, next: string | null) => ({ +const fetchAccountFavouritedStatusesSuccess = (accountId: string, statuses: APIEntity, next: (() => Promise>) | null) => ({ type: ACCOUNT_FAVOURITED_STATUSES_FETCH_SUCCESS, accountId, statuses, @@ -139,18 +138,17 @@ const expandAccountFavouritedStatuses = (accountId: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; - const url = getState().status_lists.get(`favourites:${accountId}`)?.next || null; + const next = getState().status_lists.get(`favourites:${accountId}`)?.next || null; - if (url === null || getState().status_lists.get(`favourites:${accountId}`)?.isLoading) { + if (next === null || getState().status_lists.get(`favourites:${accountId}`)?.isLoading) { return; } dispatch(expandAccountFavouritedStatusesRequest(accountId)); - return getClient(getState).request(url).then(response => { - const next = getNextLink(response); - dispatch(importFetchedStatuses(response.json)); - dispatch(expandAccountFavouritedStatusesSuccess(accountId, response.json, next || null)); + return next().then(response => { + dispatch(importFetchedStatuses(response.items)); + dispatch(expandAccountFavouritedStatusesSuccess(accountId, response.items, response.next)); }).catch(error => { dispatch(expandAccountFavouritedStatusesFail(accountId, error)); }); @@ -161,7 +159,7 @@ const expandAccountFavouritedStatusesRequest = (accountId: string) => ({ accountId, }); -const expandAccountFavouritedStatusesSuccess = (accountId: string, statuses: APIEntity[], next: string | null) => ({ +const expandAccountFavouritedStatusesSuccess = (accountId: string, statuses: APIEntity[], next: (() => Promise>) | null) => ({ type: ACCOUNT_FAVOURITED_STATUSES_EXPAND_SUCCESS, accountId, statuses, diff --git a/src/actions/importer/index.ts b/src/actions/importer/index.ts index c839d0d28..ec361438c 100644 --- a/src/actions/importer/index.ts +++ b/src/actions/importer/index.ts @@ -82,21 +82,11 @@ const importFetchedStatus = (status: APIEntity, idempotencyKey?: string) => dispatch(importFetchedStatus(status.quote)); } - // Pleroma quotes - if (status.pleroma?.quote?.id) { - dispatch(importFetchedStatus(status.pleroma.quote)); - } - // Fedibird quote from reblog if (status.reblog?.quote?.id) { dispatch(importFetchedStatus(status.reblog.quote)); } - // Pleroma quote from reblog - if (status.reblog?.pleroma?.quote?.id) { - dispatch(importFetchedStatus(status.reblog.pleroma.quote)); - } - if (status.poll?.id) { dispatch(importFetchedPoll(status.poll)); } @@ -146,10 +136,6 @@ const importFetchedStatuses = (statuses: APIEntity[]) => (dispatch: AppDispatch) processStatus(status.quote); } - if (status.pleroma?.quote?.id) { - processStatus(status.pleroma.quote); - } - if (status.poll?.id) { polls.push(status.poll); } diff --git a/src/actions/interactions.ts b/src/actions/interactions.ts index d20bc556c..e12aba821 100644 --- a/src/actions/interactions.ts +++ b/src/actions/interactions.ts @@ -551,9 +551,9 @@ const fetchReactions = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(fetchReactionsRequest(id)); - return getClient(getState).request(`/api/v1/pleroma/statuses/${id}/reactions`).then(response => { - dispatch(importFetchedAccounts((response.json as APIEntity[]).map(({ accounts }) => accounts).flat())); - dispatch(fetchReactionsSuccess(id, response.json)); + return getClient(getState).statuses.getStatusReactions(id).then(response => { + dispatch(importFetchedAccounts((response).map(({ accounts }) => accounts).flat())); + dispatch(fetchReactionsSuccess(id, response)); }).catch(error => { dispatch(fetchReactionsFail(id, error)); }); diff --git a/src/actions/me.ts b/src/actions/me.ts index 26c8c9ae5..96c265a06 100644 --- a/src/actions/me.ts +++ b/src/actions/me.ts @@ -10,7 +10,7 @@ import { getClient } from '../api'; import { loadCredentials } from './auth'; import { importFetchedAccount } from './importer'; -import type { Account } from 'soapbox/schemas'; +import type { Account } from 'pl-api'; import type { AppDispatch, RootState } from 'soapbox/store'; import type { APIEntity } from 'soapbox/types/entities'; @@ -72,7 +72,7 @@ const patchMe = (params: Record, isFormData = false) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(patchMeRequest()); - return getClient(getState()).accounts.updateCredentials(params) + return getClient(getState()).settings.updateCredentials(params) .then(response => { persistAuthAccount(response, params); dispatch(patchMeSuccess(response)); diff --git a/src/actions/media.ts b/src/actions/media.ts index 2e4ce5217..3d8c7e0df 100644 --- a/src/actions/media.ts +++ b/src/actions/media.ts @@ -26,7 +26,7 @@ const updateMedia = (mediaId: string, params: Record) => const uploadMedia = (body: UploadMediaParams, onUploadProgress: (e: ProgressEvent) => void = noOp) => (dispatch: AppDispatch, getState: () => RootState) => - getClient(getState()).media.uploadMedia(body, onUploadProgress); + getClient(getState()).media.uploadMedia(body, { onUploadProgress }); const uploadFile = ( file: File, diff --git a/src/actions/notifications.ts b/src/actions/notifications.ts index 71e518919..cb5ca6fe6 100644 --- a/src/actions/notifications.ts +++ b/src/actions/notifications.ts @@ -48,12 +48,12 @@ const MAX_QUEUED_NOTIFICATIONS = 40; const FILTER_TYPES = { all: undefined, mention: ['mention'], - favourite: ['favourite', 'pleroma:emoji_reaction'], + favourite: ['favourite', 'emoji_reaction'], reblog: ['reblog'], poll: ['poll'], status: ['status'], follow: ['follow', 'follow_request'], - events: ['pleroma:event_reminder', 'pleroma:participation_request', 'pleroma:participation_accepted'], + events: ['event_reminder', 'participation_request', 'participation_accepted'], }; type FilterType = keyof typeof FILTER_TYPES; @@ -189,10 +189,10 @@ const STATUS_NOTIFICATION_TYPES = [ 'favourite', 'reblog', // WIP separate notifications for each reaction? - // 'pleroma:emoji_reaction', - 'pleroma:event_reminder', - 'pleroma:participation_accepted', - 'pleroma:participation_request', + // 'emoji_reaction', + 'event_reminder', + 'participation_accepted', + 'participation_request', ]; const deduplicateNotifications = (notifications: any[]) => { @@ -272,7 +272,8 @@ const expandNotifications = ({ maxId }: Record = {}, done: () => an dispatch(expandNotificationsRequest(isLoadingMore)); - return getClient(state).notifications.getNotifications(params).then(response => { // WIP signal + return getClient(state).notifications.getNotifications(params, { signal: abortExpandNotifications.signal }).then(response => { + console.log(response); const entries = (response.items).reduce((acc, item) => { if (item.account?.id) { @@ -280,7 +281,7 @@ const expandNotifications = ({ maxId }: Record = {}, done: () => an } // Used by Move notification - if (item.target?.id) { + if (item.type === 'move' && item.target.id) { acc.accounts[item.target.id] = item.target; } @@ -300,6 +301,7 @@ const expandNotifications = ({ maxId }: Record = {}, done: () => an fetchRelatedRelationships(dispatch, response.items); done(); }).catch(error => { + console.log(error); dispatch(expandNotificationsFail(error, isLoadingMore)); done(); }); diff --git a/src/actions/oauth.ts b/src/actions/oauth.ts index 56b9ebb27..f7a82f5d0 100644 --- a/src/actions/oauth.ts +++ b/src/actions/oauth.ts @@ -6,7 +6,7 @@ * @see module:soapbox/actions/auth */ -import { PlApiClient } from 'pl-api'; +import { PlApiClient, type GetTokenParams, type RevokeTokenParams } from 'pl-api'; import * as BuildConfig from 'soapbox/build-config'; import { getBaseURL } from 'soapbox/utils/state'; @@ -21,7 +21,7 @@ const OAUTH_TOKEN_REVOKE_REQUEST = 'OAUTH_TOKEN_REVOKE_REQUEST'; const OAUTH_TOKEN_REVOKE_SUCCESS = 'OAUTH_TOKEN_REVOKE_SUCCESS'; const OAUTH_TOKEN_REVOKE_FAIL = 'OAUTH_TOKEN_REVOKE_FAIL'; -const obtainOAuthToken = (params: Record, baseURL?: string) => +const obtainOAuthToken = (params: GetTokenParams, baseURL?: string) => (dispatch: AppDispatch) => { dispatch({ type: OAUTH_TOKEN_CREATE_REQUEST, params }); const client = new PlApiClient(baseURL || BuildConfig.BACKEND_URL || '', undefined, { fetchInstance: false }); @@ -35,7 +35,7 @@ const obtainOAuthToken = (params: Record, baseURL?: }); }; -const revokeOAuthToken = (params: Record) => +const revokeOAuthToken = (params: RevokeTokenParams) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: OAUTH_TOKEN_REVOKE_REQUEST, params }); const baseURL = getBaseURL(getState()); diff --git a/src/actions/push-subscriptions.ts b/src/actions/push-subscriptions.ts index f3777664e..a3726adb0 100644 --- a/src/actions/push-subscriptions.ts +++ b/src/actions/push-subscriptions.ts @@ -1,5 +1,8 @@ import { getClient } from '../api'; +import type { CreatePushNotificationsSubscriptionParams } from 'pl-api'; +import type { AppDispatch, RootState } from 'soapbox/store'; + const PUSH_SUBSCRIPTION_CREATE_REQUEST = 'PUSH_SUBSCRIPTION_CREATE_REQUEST'; const PUSH_SUBSCRIPTION_CREATE_SUCCESS = 'PUSH_SUBSCRIPTION_CREATE_SUCCESS'; const PUSH_SUBSCRIPTION_CREATE_FAIL = 'PUSH_SUBSCRIPTION_CREATE_FAIL'; @@ -16,9 +19,7 @@ const PUSH_SUBSCRIPTION_DELETE_REQUEST = 'PUSH_SUBSCRIPTION_DELETE_REQUEST'; const PUSH_SUBSCRIPTION_DELETE_SUCCESS = 'PUSH_SUBSCRIPTION_DELETE_SUCCESS'; const PUSH_SUBSCRIPTION_DELETE_FAIL = 'PUSH_SUBSCRIPTION_DELETE_FAIL'; -import type { AppDispatch, RootState } from 'soapbox/store'; - -const createPushSubscription = (params: Record) => +const createPushSubscription = (params: CreatePushNotificationsSubscriptionParams) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: PUSH_SUBSCRIPTION_CREATE_REQUEST, params }); return getClient(getState).pushNotifications.createSubscription(params) diff --git a/src/actions/scheduled-statuses.ts b/src/actions/scheduled-statuses.ts index d19baaaff..00908a645 100644 --- a/src/actions/scheduled-statuses.ts +++ b/src/actions/scheduled-statuses.ts @@ -1,6 +1,6 @@ import { getFeatures } from 'soapbox/utils/features'; -import { getClient, getNextLink } from '../api'; +import { getClient } from '../api'; import type { PaginatedResponse, ScheduledStatus } from 'pl-api'; import type { AppDispatch, RootState } from 'soapbox/store'; @@ -67,7 +67,7 @@ const fetchScheduledStatusesFail = (error: unknown) => ({ const expandScheduledStatuses = () => (dispatch: AppDispatch, getState: () => RootState) => { - const next = getState().status_lists.get('scheduled_statuses')?.next || null; + const next = getState().status_lists.get('scheduled_statuses')?.next as any as () => Promise> || null; if (next === null || getState().status_lists.get('scheduled_statuses')?.isLoading) { return; diff --git a/src/actions/search.ts b/src/actions/search.ts index 8281a0a19..0a39c4443 100644 --- a/src/actions/search.ts +++ b/src/actions/search.ts @@ -129,7 +129,6 @@ const expandSearch = (type: SearchFilter) => (dispatch: AppDispatch, getState: ( if (accountId) params.account_id = accountId; return getClient(getState()).search.search(value, params).then(response => { - if (response.accounts) { dispatch(importFetchedAccounts(response.accounts)); } diff --git a/src/actions/settings.ts b/src/actions/settings.ts index 1af7b84bf..d645f5f3b 100644 --- a/src/actions/settings.ts +++ b/src/actions/settings.ts @@ -80,7 +80,7 @@ const defaultSettings = ImmutableMap({ mention: false, poll: false, move: false, - 'pleroma:emoji_reaction': false, + emoji_reaction: false, }), }), diff --git a/src/actions/status-quotes.ts b/src/actions/status-quotes.ts index d9919a3c9..9b8ba6338 100644 --- a/src/actions/status-quotes.ts +++ b/src/actions/status-quotes.ts @@ -1,4 +1,4 @@ -import { getClient, getNextLink } from '../api'; +import { getClient } from '../api'; import { importFetchedStatuses } from './importer'; @@ -25,14 +25,13 @@ const fetchStatusQuotes = (statusId: string) => type: STATUS_QUOTES_FETCH_REQUEST, }); - return getClient(getState).request(`/api/v1/pleroma/statuses/${statusId}/quotes`).then(response => { - const next = getNextLink(response); - dispatch(importFetchedStatuses(response.json)); + return getClient(getState).statuses.getStatusQuotes(statusId).then(response => { + dispatch(importFetchedStatuses(response.items)); return dispatch({ type: STATUS_QUOTES_FETCH_SUCCESS, statusId, - statuses: response.json, - next: next || null, + statuses: response.items, + next: response.next, }); }).catch(error => { dispatch({ @@ -45,9 +44,9 @@ const fetchStatusQuotes = (statusId: string) => const expandStatusQuotes = (statusId: string) => (dispatch: AppDispatch, getState: () => RootState) => { - const url = getState().status_lists.getIn([`quotes:${statusId}`, 'next'], null) as string | null; + const next = getState().status_lists.get(`quotes:${statusId}`)?.next || null; - if (url === null || getState().status_lists.getIn([`quotes:${statusId}`, 'isLoading'])) { + if (next === null || getState().status_lists.getIn([`quotes:${statusId}`, 'isLoading'])) { return dispatch(noOp); } @@ -56,14 +55,13 @@ const expandStatusQuotes = (statusId: string) => statusId, }); - return getClient(getState).request(url).then(response => { - const next = getNextLink(response); - dispatch(importFetchedStatuses(response.json)); + return next().then(response => { + dispatch(importFetchedStatuses(response.items)); dispatch({ type: STATUS_QUOTES_EXPAND_SUCCESS, statusId, - statuses: response.json, - next: next || null, + statuses: response.items, + next: response.next, }); }).catch(error => { dispatch({ diff --git a/src/actions/statuses.ts b/src/actions/statuses.ts index 64dffaf82..b7b9ea2cd 100644 --- a/src/actions/statuses.ts +++ b/src/actions/statuses.ts @@ -10,6 +10,7 @@ import { openModal } from './modals'; import { getSettings } from './settings'; import { deleteFromTimelines } from './timelines'; +import type { CreateStatusParams } from 'pl-api'; import type { IntlShape } from 'react-intl'; import type { AppDispatch, RootState } from 'soapbox/store'; import type { APIEntity, Status } from 'soapbox/types/entities'; @@ -57,42 +58,42 @@ const STATUS_LANGUAGE_CHANGE = 'STATUS_LANGUAGE_CHANGE'; const statusExists = (getState: () => RootState, statusId: string) => (getState().statuses.get(statusId) || null) !== null; -const createStatus = (params: Record, idempotencyKey: string, statusId: string | null) => +const createStatus = (params: CreateStatusParams, idempotencyKey: string, statusId: string | null) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: STATUS_CREATE_REQUEST, params, idempotencyKey, editing: !!statusId }); return (statusId === null ? getClient(getState()).statuses.createStatus(params) : getClient(getState()).statuses.editStatus(statusId, params)) .then((status) => { // The backend might still be processing the rich media attachment - if (!status.card && shouldHaveCard(status)) { - status.expectsCard = true; - } + if (!status.card && shouldHaveCard(status)) { + status.expectsCard = true; + } - dispatch(importFetchedStatus(status, idempotencyKey)); - dispatch({ type: STATUS_CREATE_SUCCESS, status, params, idempotencyKey, editing: !!statusId }); + dispatch(importFetchedStatus(status, idempotencyKey)); + dispatch({ type: STATUS_CREATE_SUCCESS, status, params, idempotencyKey, editing: !!statusId }); - // Poll the backend for the updated card - if (status.expectsCard) { - const delay = 1000; + // Poll the backend for the updated card + if (status.expectsCard) { + const delay = 1000; - const poll = (retries = 5) => { - return getClient(getState()).statuses.getStatus(status.id).then(response => { - if (response.card) { - dispatch(importFetchedStatus(response)); - } else if (retries > 0 && response) { - setTimeout(() => poll(retries - 1), delay); - } - }).catch(console.error); - }; + const poll = (retries = 5) => { + return getClient(getState()).statuses.getStatus(status.id).then(response => { + if (response.card) { + dispatch(importFetchedStatus(response)); + } else if (retries > 0 && response) { + setTimeout(() => poll(retries - 1), delay); + } + }).catch(console.error); + }; - setTimeout(() => poll(), delay); - } + setTimeout(() => poll(), delay); + } - return status; - }).catch(error => { - dispatch({ type: STATUS_CREATE_FAIL, error, params, idempotencyKey, editing: !!statusId }); - throw error; - }); + return status; + }).catch(error => { + dispatch({ type: STATUS_CREATE_FAIL, error, params, idempotencyKey, editing: !!statusId }); + throw error; + }); }; const editStatus = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { @@ -146,14 +147,14 @@ const deleteStatus = (id: string, withRedraft = false) => dispatch({ type: STATUS_DELETE_REQUEST, params: status }); return getClient(getState()).statuses.deleteStatus(id).then(response => { - dispatch({ type: STATUS_DELETE_SUCCESS, id }); - dispatch(deleteFromTimelines(id)); + dispatch({ type: STATUS_DELETE_SUCCESS, id }); + dispatch(deleteFromTimelines(id)); - if (withRedraft) { - dispatch(setComposeToStatus(status, response.text || '', response.spoiler_text, response.pleroma?.content_type, withRedraft)); - dispatch(openModal('COMPOSE')); - } - }) + if (withRedraft) { + dispatch(setComposeToStatus(status, response.text || '', response.spoiler_text, response.content_type, withRedraft)); + dispatch(openModal('COMPOSE')); + } + }) .catch(error => { dispatch({ type: STATUS_DELETE_FAIL, params: status, error }); }); diff --git a/src/actions/suggestions.ts b/src/actions/suggestions.ts index 5c82004c1..c297f2909 100644 --- a/src/actions/suggestions.ts +++ b/src/actions/suggestions.ts @@ -25,7 +25,7 @@ const fetchSuggestions = (limit = 50) => if (client.features.suggestions) { dispatch({ type: SUGGESTIONS_FETCH_REQUEST, skipLoading: true }); - return getClient(getState).accounts.getSuggestions(limit).then((suggestions) => { + return getClient(getState).myAccount.getSuggestions(limit).then((suggestions) => { const accounts = suggestions.map(({ account }) => account); dispatch(importFetchedAccounts(accounts)); @@ -56,7 +56,7 @@ const dismissSuggestion = (accountId: string) => id: accountId, }); - return getClient(getState).accounts.dismissSuggestions(accountId); + return getClient(getState).myAccount.dismissSuggestions(accountId); }; export { diff --git a/src/actions/tags.ts b/src/actions/tags.ts index 19e2fff64..69e545c71 100644 --- a/src/actions/tags.ts +++ b/src/actions/tags.ts @@ -1,4 +1,4 @@ -import { getClient, getNextLink } from '../api'; +import { getClient } from '../api'; import type { PaginatedResponse, Tag } from 'pl-api'; import type { AppDispatch, RootState } from 'soapbox/store'; @@ -27,7 +27,7 @@ const FOLLOWED_HASHTAGS_EXPAND_FAIL = 'FOLLOWED_HASHTAGS_EXPAND_FAIL'; const fetchHashtag = (name: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(fetchHashtagRequest()); - return getClient(getState()).accounts.getTag(name).then((data) => { + return getClient(getState()).myAccount.getTag(name).then((data) => { dispatch(fetchHashtagSuccess(name, data)); }).catch(err => { dispatch(fetchHashtagFail(err)); @@ -52,7 +52,7 @@ const fetchHashtagFail = (error: unknown) => ({ const followHashtag = (name: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(followHashtagRequest(name)); - return getClient(getState()).accounts.followTag(name).then((data) => { + return getClient(getState()).myAccount.followTag(name).then((data) => { dispatch(followHashtagSuccess(name, data)); }).catch(err => { dispatch(followHashtagFail(name, err)); @@ -79,7 +79,7 @@ const followHashtagFail = (name: string, error: unknown) => ({ const unfollowHashtag = (name: string) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(unfollowHashtagRequest(name)); - return getClient(getState()).accounts.unfollowTag(name).then((data) => { + return getClient(getState()).myAccount.unfollowTag(name).then((data) => { dispatch(unfollowHashtagSuccess(name, data)); }).catch(err => { dispatch(unfollowHashtagFail(name, err)); @@ -106,7 +106,7 @@ const unfollowHashtagFail = (name: string, error: unknown) => ({ const fetchFollowedHashtags = () => (dispatch: AppDispatch, getState: () => RootState) => { dispatch(fetchFollowedHashtagsRequest()); - return getClient(getState()).accounts.getFollowedTags().then(response => { + return getClient(getState()).myAccount.getFollowedTags().then(response => { dispatch(fetchFollowedHashtagsSuccess(response.items, response.next)); }).catch(err => { dispatch(fetchFollowedHashtagsFail(err)); diff --git a/src/api/hooks/accounts/useAccount.ts b/src/api/hooks/accounts/useAccount.ts index d83e52967..b9fbc3865 100644 --- a/src/api/hooks/accounts/useAccount.ts +++ b/src/api/hooks/accounts/useAccount.ts @@ -21,7 +21,7 @@ const useAccount = (accountId?: string, opts: UseAccountOpts = {}) => { const { entity, isUnauthorized, ...result } = useEntity( [Entities.ACCOUNTS, accountId!], - () => client.request(`/api/v1/accounts/${accountId}`), + () => client.accounts.getAccount(accountId!), { schema: accountSchema, enabled: !!accountId }, ); diff --git a/src/api/hooks/accounts/useAccountList.ts b/src/api/hooks/accounts/useAccountList.ts index f085f4270..0cdfbe759 100644 --- a/src/api/hooks/accounts/useAccountList.ts +++ b/src/api/hooks/accounts/useAccountList.ts @@ -33,12 +33,12 @@ const useAccountList = (listKey: string[], entityFn: EntityFn, opts: useAc const useBlocks = () => { const client = useClient(); - return useAccountList(['blocks'], () => client.request('/api/v1/blocks')); + return useAccountList(['blocks'], () => client.filtering.getBlocks()); }; const useMutes = () => { const client = useClient(); - return useAccountList(['mutes'], () => client.request('/api/v1/mutes')); + return useAccountList(['mutes'], () => client.filtering.getMutes()); }; const useFollowing = (accountId: string | undefined) => { @@ -46,7 +46,7 @@ const useFollowing = (accountId: string | undefined) => { return useAccountList( [accountId!, 'following'], - () => client.request(`/api/v1/accounts/${accountId}/following`), + () => client.accounts.getAccountFollowing(accountId), { enabled: !!accountId }, ); }; @@ -56,7 +56,7 @@ const useFollowers = (accountId: string | undefined) => { return useAccountList( [accountId!, 'followers'], - () => client.request(`/api/v1/accounts/${accountId}/followers`), + () => client.accounts.getAccountFollowers(accountId), { enabled: !!accountId }, ); }; diff --git a/src/api/hooks/accounts/useAccountLookup.ts b/src/api/hooks/accounts/useAccountLookup.ts index 8963c792f..a16f4e0d1 100644 --- a/src/api/hooks/accounts/useAccountLookup.ts +++ b/src/api/hooks/accounts/useAccountLookup.ts @@ -22,7 +22,7 @@ const useAccountLookup = (acct: string | undefined, opts: UseAccountLookupOpts = const { entity: account, isUnauthorized, ...result } = useEntityLookup( Entities.ACCOUNTS, (account) => account.acct.toLowerCase() === acct?.toLowerCase(), - () => client.request(`/api/v1/accounts/lookup?acct=${acct}`), + () => client.accounts.lookupAccount(acct), { schema: accountSchema, enabled: !!acct }, ); diff --git a/src/api/hooks/accounts/useRelationship.ts b/src/api/hooks/accounts/useRelationship.ts index ffc4957f2..3464600a6 100644 --- a/src/api/hooks/accounts/useRelationship.ts +++ b/src/api/hooks/accounts/useRelationship.ts @@ -2,8 +2,8 @@ import { z } from 'zod'; import { Entities } from 'soapbox/entity-store/entities'; import { useEntity } from 'soapbox/entity-store/hooks'; -import { type Relationship, relationshipSchema } from 'soapbox/schemas'; import { useClient } from 'soapbox/hooks'; +import { type Relationship, relationshipSchema } from 'soapbox/schemas'; interface UseRelationshipOpts { enabled?: boolean; @@ -15,7 +15,7 @@ const useRelationship = (accountId: string | undefined, opts: UseRelationshipOpt const { entity: relationship, ...result } = useEntity( [Entities.RELATIONSHIPS, accountId!], - () => client.request(`/api/v1/accounts/relationships?id[]=${accountId}`), + () => client.accounts.getRelationships([accountId]), { enabled: enabled && !!accountId, schema: z.array(relationshipSchema).nonempty().transform(arr => arr[0]), diff --git a/src/api/hooks/accounts/useRelationships.ts b/src/api/hooks/accounts/useRelationships.ts index 6d4264bd4..7777b32a0 100644 --- a/src/api/hooks/accounts/useRelationships.ts +++ b/src/api/hooks/accounts/useRelationships.ts @@ -7,8 +7,7 @@ const useRelationships = (listKey: string[], ids: string[]) => { const client = useClient(); const { isLoggedIn } = useLoggedIn(); - const fetchRelationships = (ids: string[]) => - client.request('/api/v1/accounts/relationships', { params: { ids } }); + const fetchRelationships = (ids: string[]) => client.accounts.getRelationships(ids); const { entityMap: relationships, ...result } = useBatchedEntities( [Entities.RELATIONSHIPS, ...listKey], diff --git a/src/api/hooks/groups/useCancelMembershipRequest.ts b/src/api/hooks/groups/useCancelMembershipRequest.ts deleted file mode 100644 index c253c5e0d..000000000 --- a/src/api/hooks/groups/useCancelMembershipRequest.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Entities } from 'soapbox/entity-store/entities'; -import { useCreateEntity } from 'soapbox/entity-store/hooks'; -import { useClient, useOwnAccount } from 'soapbox/hooks'; - -import type { Group } from 'soapbox/schemas'; - -const useCancelMembershipRequest = (group: Group) => { - const client = useClient(); - const { account: me } = useOwnAccount(); - - const { createEntity, isSubmitting } = useCreateEntity( - [Entities.GROUP_RELATIONSHIPS], - () => client.request(`/client.request/v1/groups/${group.id}/membership_requests/${me?.id}/reject`, { method: 'POST' }), - ); - - return { - mutate: createEntity, - isSubmitting, - }; -}; - -export { useCancelMembershipRequest }; diff --git a/src/api/hooks/groups/useCreateGroup.ts b/src/api/hooks/groups/useCreateGroup.ts index 69412e22a..9e9a0c0fd 100644 --- a/src/api/hooks/groups/useCreateGroup.ts +++ b/src/api/hooks/groups/useCreateGroup.ts @@ -1,8 +1,8 @@ import { serialize } from 'object-to-formdata'; -import { useClient } from 'soapbox/hooks'; import { Entities } from 'soapbox/entity-store/entities'; import { useCreateEntity } from 'soapbox/entity-store/hooks'; +import { useClient } from 'soapbox/hooks'; import { groupSchema } from 'soapbox/schemas'; interface CreateGroupParams { @@ -18,12 +18,11 @@ interface CreateGroupParams { const useCreateGroup = () => { const client = useClient(); - const { createEntity, ...rest } = useCreateEntity([Entities.GROUPS, 'search', ''], (params: CreateGroupParams) => - client.request('/api/v1/groups', { - method: 'POST', - contentType: '', - body: params, - }), { schema: groupSchema }); + const { createEntity, ...rest } = useCreateEntity( + [Entities.GROUPS, 'search', ''], + (params: CreateGroupParams) => client.experimental.groups.createGroup(params), + { schema: groupSchema }, + ); return { createGroup: createEntity, diff --git a/src/api/hooks/groups/useDeleteGroupStatus.ts b/src/api/hooks/groups/useDeleteGroupStatus.ts index 5056c47ec..d826ff5fc 100644 --- a/src/api/hooks/groups/useDeleteGroupStatus.ts +++ b/src/api/hooks/groups/useDeleteGroupStatus.ts @@ -1,6 +1,6 @@ -import { useClient } from 'soapbox/hooks'; import { Entities } from 'soapbox/entity-store/entities'; import { useDeleteEntity } from 'soapbox/entity-store/hooks'; +import { useClient } from 'soapbox/hooks'; import type { Group } from 'soapbox/schemas'; @@ -8,7 +8,7 @@ const useDeleteGroupStatus = (group: Group, statusId: string) => { const client = useClient(); const { deleteEntity, isSubmitting } = useDeleteEntity( Entities.STATUSES, - () => client.request(`/api/v1/groups/${group.id}/statuses/${statusId}`, { method: 'DELETE' }), + () => client.experimental.groups.deleteGroupStatus(group.id, statusId), ); return { diff --git a/src/api/hooks/groups/useGroup.ts b/src/api/hooks/groups/useGroup.ts index 222b29147..2466b1a7e 100644 --- a/src/api/hooks/groups/useGroup.ts +++ b/src/api/hooks/groups/useGroup.ts @@ -1,9 +1,9 @@ import { useEffect } from 'react'; import { useHistory } from 'react-router-dom'; -import { useClient } from 'soapbox/hooks'; import { Entities } from 'soapbox/entity-store/entities'; import { useEntity } from 'soapbox/entity-store/hooks'; +import { useClient } from 'soapbox/hooks'; import { type Group, groupSchema } from 'soapbox/schemas'; import { useGroupRelationship } from './useGroupRelationship'; @@ -14,7 +14,7 @@ const useGroup = (groupId: string, refetch = true) => { const { entity: group, isUnauthorized, ...result } = useEntity( [Entities.GROUPS, groupId], - () => client.request(`/api/v1/groups/${groupId}`), + () => client.experimental.groups.getGroup(groupId), { schema: groupSchema, refetch, diff --git a/src/api/hooks/groups/useGroupMedia.ts b/src/api/hooks/groups/useGroupMedia.ts index 179d4f183..a9301ed5a 100644 --- a/src/api/hooks/groups/useGroupMedia.ts +++ b/src/api/hooks/groups/useGroupMedia.ts @@ -11,7 +11,7 @@ const useGroupMedia = (groupId: string) => { return useEntities( [Entities.STATUSES, 'groupMedia', groupId], - () => client.request(`/api/v1/timelines/group/${groupId}?only_media=true`), + () => client.timelines.groupTimeline(groupId, { only_media: true }), { schema: statusSchema }) ; }; diff --git a/src/api/hooks/groups/useGroupMembers.ts b/src/api/hooks/groups/useGroupMembers.ts index 916fb0fb4..b1fdbef01 100644 --- a/src/api/hooks/groups/useGroupMembers.ts +++ b/src/api/hooks/groups/useGroupMembers.ts @@ -9,7 +9,7 @@ const useGroupMembers = (groupId: string, role: GroupRoles) => { const { entities, ...result } = useEntities( [Entities.GROUP_MEMBERSHIPS, groupId, role], - () => client.request(`/api/v1/groups/${groupId}/memberships?role=${role}`), + () => client.experimental.groups.getGroupMemberships(groupId, role), { schema: groupMemberSchema }, ); diff --git a/src/api/hooks/groups/useGroupMembershipRequests.ts b/src/api/hooks/groups/useGroupMembershipRequests.ts index 10080a7ee..68baf9a3c 100644 --- a/src/api/hooks/groups/useGroupMembershipRequests.ts +++ b/src/api/hooks/groups/useGroupMembershipRequests.ts @@ -16,7 +16,7 @@ const useGroupMembershipRequests = (groupId: string) => { const { entities, invalidate, fetchEntities, ...rest } = useEntities( path, - () => client.request(`/api/v1/groups/${groupId}/membership_requests`), + () => client.experimental.groups.getGroupMembershipRequests(groupId), { schema: accountSchema, enabled: relationship?.role === GroupRoles.OWNER || relationship?.role === GroupRoles.ADMIN, @@ -24,13 +24,13 @@ const useGroupMembershipRequests = (groupId: string) => { ); const { dismissEntity: authorize } = useDismissEntity(path, async (accountId: string) => { - const response = await client.request(`/api/v1/groups/${groupId}/membership_requests/${accountId}/authorize`, { method: 'POST' }); + const response = await client.experimental.groups.acceptGroupMembershipRequest(groupId, accountId); invalidate(); return response; }); const { dismissEntity: reject } = useDismissEntity(path, async (accountId: string) => { - const response = await client.request(`/api/v1/groups/${groupId}/membership_requests/${accountId}/reject`, { method: 'POST' }); + const response = await client.experimental.groups.rejectGroupMembershipRequest(groupId, accountId); invalidate(); return response; }); diff --git a/src/api/hooks/groups/useGroupRelationship.ts b/src/api/hooks/groups/useGroupRelationship.ts index 8eb50964f..28771a0da 100644 --- a/src/api/hooks/groups/useGroupRelationship.ts +++ b/src/api/hooks/groups/useGroupRelationship.ts @@ -10,7 +10,7 @@ const useGroupRelationship = (groupId: string | undefined) => { const { entity: groupRelationship, ...result } = useEntity( [Entities.GROUP_RELATIONSHIPS, groupId!], - () => client.request(`/api/v1/groups/relationships?id[]=${groupId}`), + () => client.experimental.groups.getGroupRelationships([groupId]), { enabled: !!groupId, schema: z.array(groupRelationshipSchema).nonempty().transform(arr => arr[0]), diff --git a/src/api/hooks/groups/useGroupRelationships.ts b/src/api/hooks/groups/useGroupRelationships.ts index b77ab4955..9a9b51339 100644 --- a/src/api/hooks/groups/useGroupRelationships.ts +++ b/src/api/hooks/groups/useGroupRelationships.ts @@ -8,7 +8,7 @@ const useGroupRelationships = (listKey: string[], ids: string[]) => { const { isLoggedIn } = useLoggedIn(); const fetchGroupRelationships = (ids: string[]) => - client.request('/api/v1/groups/relationships', { params: { id: ids } }); + client.experimental.groups.getGroupRelationships(ids); const { entityMap: relationships, ...result } = useBatchedEntities( [Entities.RELATIONSHIPS, ...listKey], diff --git a/src/api/hooks/groups/useGroups.ts b/src/api/hooks/groups/useGroups.ts index f44083741..1debce6ab 100644 --- a/src/api/hooks/groups/useGroups.ts +++ b/src/api/hooks/groups/useGroups.ts @@ -12,7 +12,7 @@ const useGroups = () => { const { entities, ...result } = useEntities( [Entities.GROUPS, 'search', ''], - () => client.request('/api/v1/groups'), + () => client.experimental.groups.getGroups(), { enabled: features.groups, schema: groupSchema }, ); const { relationships } = useGroupRelationships( diff --git a/src/api/hooks/groups/useUpdateGroup.ts b/src/api/hooks/groups/useUpdateGroup.ts index 9902bc2f1..eb81ba1d4 100644 --- a/src/api/hooks/groups/useUpdateGroup.ts +++ b/src/api/hooks/groups/useUpdateGroup.ts @@ -15,12 +15,11 @@ interface UpdateGroupParams { const useUpdateGroup = (groupId: string) => { const client = useClient(); - const { createEntity, ...rest } = useCreateEntity([Entities.GROUPS], (params: UpdateGroupParams) => - client.request(`/api/v1/groups/${groupId}`, { - method: 'PUT', - contentType: '', - body: params, - }), { schema: groupSchema }); + const { createEntity, ...rest } = useCreateEntity( + [Entities.GROUPS], + (params: UpdateGroupParams) => client.experimental.groups.updateGroup(groupId, params), + { schema: groupSchema }, + ); return { updateGroup: createEntity, diff --git a/src/api/hooks/index.ts b/src/api/hooks/index.ts index d2ea782e8..c86eff78e 100644 --- a/src/api/hooks/index.ts +++ b/src/api/hooks/index.ts @@ -13,7 +13,6 @@ export { useRelationships } from './accounts/useRelationships'; // Groups export { useBlockGroupMember } from './groups/useBlockGroupMember'; -export { useCancelMembershipRequest } from './groups/useCancelMembershipRequest'; export { useCreateGroup, type CreateGroupParams } from './groups/useCreateGroup'; export { useDeleteGroup } from './groups/useDeleteGroup'; export { useDemoteGroupMember } from './groups/useDemoteGroupMember'; diff --git a/src/api/hooks/statuses/useBookmarkFolders.ts b/src/api/hooks/statuses/useBookmarkFolders.ts index e7285e4be..816353cdb 100644 --- a/src/api/hooks/statuses/useBookmarkFolders.ts +++ b/src/api/hooks/statuses/useBookmarkFolders.ts @@ -10,7 +10,7 @@ const useBookmarkFolders = () => { const { entities, ...result } = useEntities( [Entities.BOOKMARK_FOLDERS], - () => client.request('/api/v1/pleroma/bookmark_folders'), + () => client.myAccount.getBookmarkFolders, { enabled: features.bookmarkFolders, schema: bookmarkFolderSchema }, ); diff --git a/src/api/hooks/statuses/useCreateBookmarkFolder.ts b/src/api/hooks/statuses/useCreateBookmarkFolder.ts index 200dd9394..f13f55424 100644 --- a/src/api/hooks/statuses/useCreateBookmarkFolder.ts +++ b/src/api/hooks/statuses/useCreateBookmarkFolder.ts @@ -14,10 +14,7 @@ const useCreateBookmarkFolder = () => { const { createEntity, ...rest } = useCreateEntity( [Entities.BOOKMARK_FOLDERS], (params: CreateBookmarkFolderParams) => - client.request('/api/v1/pleroma/bookmark_folders', { - method: 'POST', - body: JSON.stringify(params), - }), + client.myAccount.createBookmarkFolder(params), { schema: bookmarkFolderSchema }, ); diff --git a/src/api/hooks/statuses/useDeleteBookmarkFolder.ts b/src/api/hooks/statuses/useDeleteBookmarkFolder.ts index bf0bdc2d4..2ec9d36d7 100644 --- a/src/api/hooks/statuses/useDeleteBookmarkFolder.ts +++ b/src/api/hooks/statuses/useDeleteBookmarkFolder.ts @@ -1,10 +1,13 @@ import { Entities } from 'soapbox/entity-store/entities'; -import { useEntityActions } from 'soapbox/entity-store/hooks'; +import { useDeleteEntity } from 'soapbox/entity-store/hooks'; +import { useClient } from 'soapbox/hooks'; -const useDeleteBookmarkFolder = () => { - const { deleteEntity, isSubmitting } = useEntityActions( - [Entities.BOOKMARK_FOLDERS], - { delete: '/api/v1/pleroma/bookmark_folders/:id' }, +const useDeleteBookmarkFolder = (folderId: string) => { + const client = useClient(); + + const { deleteEntity, isSubmitting } = useDeleteEntity( + Entities.BOOKMARK_FOLDERS, + () => client.myAccount.deleteBookmarkFolder(folderId), ); return { diff --git a/src/api/hooks/statuses/useUpdateBookmarkFolder.ts b/src/api/hooks/statuses/useUpdateBookmarkFolder.ts index 8dc3f9893..2c852a729 100644 --- a/src/api/hooks/statuses/useUpdateBookmarkFolder.ts +++ b/src/api/hooks/statuses/useUpdateBookmarkFolder.ts @@ -14,10 +14,7 @@ const useUpdateBookmarkFolder = (folderId: string) => { const { createEntity, ...rest } = useCreateEntity( [Entities.BOOKMARK_FOLDERS], (params: UpdateBookmarkFolderParams) => - client.request(`/api/v1/pleroma/bookmark_folders/${folderId}`, { - method: 'PATCH', - body: JSON.stringify(params), - }), + client.myAccount.updateBookmarkFolder(folderId, params), { schema: bookmarkFolderSchema }, ); diff --git a/src/components/hashtag.tsx b/src/components/hashtag.tsx index 9771e59db..37d2424b3 100644 --- a/src/components/hashtag.tsx +++ b/src/components/hashtag.tsx @@ -7,14 +7,14 @@ import { shortNumberFormat } from '../utils/numbers'; import { HStack, Stack, Text } from './ui'; -import type { Tag } from 'soapbox/types/entities'; +import type { Tag } from 'pl-api'; interface IHashtag { hashtag: Tag; } const Hashtag: React.FC = ({ hashtag }) => { - const count = Number(hashtag.history?.get(0)?.accounts); + const count = Number(hashtag.history?.[0]?.accounts); return ( @@ -42,7 +42,7 @@ const Hashtag: React.FC = ({ hashtag }) => { +day.uses).toArray()} + data={hashtag.history.reverse().map((day) => +day.uses)} > diff --git a/src/components/status-action-bar.tsx b/src/components/status-action-bar.tsx index a6788616e..df88f76a4 100644 --- a/src/components/status-action-bar.tsx +++ b/src/components/status-action-bar.tsx @@ -278,7 +278,7 @@ const StatusActionBar: React.FC = ({ const account = status.account; getOrCreateChatByAccountId(account.id) - .then(({ json: chat }) => history.push(`/chats/${chat.id}`)) + .then((chat) => history.push(`/chats/${chat.id}`)) .catch(() => {}); }; @@ -429,7 +429,7 @@ const StatusActionBar: React.FC = ({ if (features.bookmarkFolders && fromBookmarks) { menu.push({ - text: intl.formatMessage(status.pleroma.get('bookmark_folder') ? messages.bookmarkChangeFolder : messages.bookmarkSetFolder), + text: intl.formatMessage(status.bookmark_folder ? messages.bookmarkChangeFolder : messages.bookmarkSetFolder), action: handleBookmarkFolderClick, icon: require('@tabler/icons/outline/folders.svg'), }); diff --git a/src/components/status.tsx b/src/components/status.tsx index f8f945287..92cc6b3e1 100644 --- a/src/components/status.tsx +++ b/src/components/status.tsx @@ -361,7 +361,7 @@ const Status: React.FC = (props) => { let quote; if (actualStatus.quote) { - if (actualStatus.pleroma.get('quote_visible', true) === false) { + if ((actualStatus.quote_visible ?? true) === false) { quote = (

diff --git a/src/entity-store/hooks/types.ts b/src/entity-store/hooks/types.ts index 41a039b71..ae48e6660 100644 --- a/src/entity-store/hooks/types.ts +++ b/src/entity-store/hooks/types.ts @@ -35,7 +35,7 @@ interface EntityCallbacks { * Passed into hooks to make requests. * Must return a response. */ -type EntityFn = (value: T) => Promise +type EntityFn = (value: T) => Promise export type { EntitySchema, diff --git a/src/entity-store/hooks/useBatchedEntities.ts b/src/entity-store/hooks/useBatchedEntities.ts index f1d9aefa5..7c4889989 100644 --- a/src/entity-store/hooks/useBatchedEntities.ts +++ b/src/entity-store/hooks/useBatchedEntities.ts @@ -23,7 +23,7 @@ interface UseBatchedEntitiesOpts { const useBatchedEntities = ( expandedPath: ExpandedEntitiesPath, ids: string[], - entityFn: EntityFn, + entityFn: EntityFn, opts: UseBatchedEntitiesOpts = {}, ) => { const getState = useGetState(); @@ -54,10 +54,10 @@ const useBatchedEntities = ( dispatch(entitiesFetchRequest(entityType, listKey)); try { const response = await entityFn(filteredIds); - const entities = filteredArray(schema).parse(response.json); + const entities = filteredArray(schema).parse(response); dispatch(entitiesFetchSuccess(entities, entityType, listKey, 'end', { - next: undefined, - prev: undefined, + next: null, + prev: null, totalCount: undefined, fetching: false, fetched: true, diff --git a/src/entity-store/hooks/useEntities.ts b/src/entity-store/hooks/useEntities.ts index 58ad1b616..4e2aac28e 100644 --- a/src/entity-store/hooks/useEntities.ts +++ b/src/entity-store/hooks/useEntities.ts @@ -1,13 +1,11 @@ import { useEffect } from 'react'; import z from 'zod'; -import { getNextLink, getPrevLink } from 'soapbox/api'; import { useClient } from 'soapbox/hooks'; import { useAppDispatch } from 'soapbox/hooks/useAppDispatch'; import { useAppSelector } from 'soapbox/hooks/useAppSelector'; import { useGetState } from 'soapbox/hooks/useGetState'; import { filteredArray } from 'soapbox/schemas/utils'; -import { realNumberSchema } from 'soapbox/utils/numbers'; import { entitiesFetchFail, entitiesFetchRequest, entitiesFetchSuccess, invalidateEntityList } from '../actions'; import { selectEntities, selectListState, useListState } from '../selectors'; @@ -16,6 +14,7 @@ import { parseEntitiesPath } from './utils'; import type { EntityFn, EntitySchema, ExpandedEntitiesPath } from './types'; import type { Entity } from '../types'; +import type { PaginatedResponse } from 'pl-api'; /** Additional options for the hook. */ interface UseEntitiesOpts { @@ -58,7 +57,7 @@ const useEntities = ( const next = useListState(path, 'next'); const prev = useListState(path, 'prev'); - const fetchPage = async(req: EntityFn, pos: 'start' | 'end', overwrite = false): Promise => { + const fetchPage = async(req: () => Promise>, pos: 'start' | 'end', overwrite = false): Promise => { // Get `isFetching` state from the store again to prevent race conditions. const isFetching = selectListState(getState(), path, 'fetching'); if (isFetching) return; @@ -66,14 +65,12 @@ const useEntities = ( dispatch(entitiesFetchRequest(entityType, listKey)); try { const response = await req(); - const entities = filteredArray(schema).parse(response.json); - const parsedCount = realNumberSchema.safeParse(response.headers.get('x-total-count')); - const totalCount = parsedCount.success ? parsedCount.data : undefined; + const entities = filteredArray(schema).parse(response); dispatch(entitiesFetchSuccess(entities, entityType, listKey, pos, { - next: getNextLink(response), - prev: getPrevLink(response), - totalCount: Number(totalCount) >= entities.length ? totalCount : undefined, + next: response.next, + prev: response.previous, + totalCount: undefined, fetching: false, fetched: true, error: null, @@ -91,13 +88,13 @@ const useEntities = ( const fetchNextPage = async(): Promise => { if (next) { - await fetchPage(() => client.request(next), 'end'); + await fetchPage(() => next(), 'end'); } }; const fetchPreviousPage = async(): Promise => { if (prev) { - await fetchPage(() => client.request(prev), 'start'); + await fetchPage(() => prev(), 'start'); } }; diff --git a/src/entity-store/hooks/useEntity.ts b/src/entity-store/hooks/useEntity.ts index 70001005f..1c03a0ab8 100644 --- a/src/entity-store/hooks/useEntity.ts +++ b/src/entity-store/hooks/useEntity.ts @@ -24,7 +24,7 @@ interface UseEntityOpts { const useEntity = ( path: EntityPath, - entityFn: EntityFn, + entityFn: EntityFn, opts: UseEntityOpts = {}, ) => { const [isFetching, setPromise] = useLoading(true); @@ -46,7 +46,7 @@ const useEntity = ( const fetchEntity = async () => { try { const response = await setPromise(entityFn()); - const entity = schema.parse(response.json); + const entity = schema.parse(response); dispatch(importEntities([entity], entityType)); } catch (e) { setError(e); diff --git a/src/entity-store/types.ts b/src/entity-store/types.ts index 09e926b93..e60af9911 100644 --- a/src/entity-store/types.ts +++ b/src/entity-store/types.ts @@ -1,3 +1,5 @@ +import type { PaginatedResponse } from 'pl-api'; + /** A Mastodon API entity. */ interface Entity { /** Unique ID for the entity (usually the primary key in the database). */ @@ -20,9 +22,9 @@ interface EntityList { /** Fetch state for an entity list. */ interface EntityListState { /** Next URL for pagination, if any. */ - next: string | undefined; + next: (() => Promise>) | null; /** Previous URL for pagination, if any. */ - prev: string | undefined; + prev: (() => Promise>) | null; /** Total number of items according to the API. */ totalCount: number | undefined; /** Error returned from the API, if any. */ diff --git a/src/features/account/components/header.tsx b/src/features/account/components/header.tsx index a5adf9925..2b4d021bd 100644 --- a/src/features/account/components/header.tsx +++ b/src/features/account/components/header.tsx @@ -100,7 +100,7 @@ const Header: React.FC = ({ account }) => { toast.error(data?.error); }, onSuccess: (response) => { - history.push(`/chats/${response.json.id}`); + history.push(`/chats/${response.id}`); queryClient.invalidateQueries({ queryKey: ['chats', 'search'], }); diff --git a/src/features/admin/components/registration-mode-picker.tsx b/src/features/admin/components/registration-mode-picker.tsx index 64c0f61bf..9111d5283 100644 --- a/src/features/admin/components/registration-mode-picker.tsx +++ b/src/features/admin/components/registration-mode-picker.tsx @@ -4,7 +4,7 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; import { updateConfig } from 'soapbox/actions/admin'; import { RadioGroup, RadioItem } from 'soapbox/components/radio'; import { useAppDispatch, useInstance } from 'soapbox/hooks'; -import { Instance } from 'soapbox/schemas'; +import { Instance } from 'pl-api'; import toast from 'soapbox/toast'; type RegistrationMode = 'open' | 'approval' | 'closed'; diff --git a/src/features/auth-login/components/login-page.tsx b/src/features/auth-login/components/login-page.tsx index 987f37527..78d712d59 100644 --- a/src/features/auth-login/components/login-page.tsx +++ b/src/features/auth-login/components/login-page.tsx @@ -38,7 +38,7 @@ const LoginPage = () => { const handleSubmit: React.FormEventHandler = (event) => { const { username, password } = getFormData(event.target as HTMLFormElement); dispatch(logIn(username, password)) - .then(({ access_token }) => dispatch(verifyCredentials(access_token as string))) + .then(({ access_token }) => dispatch(verifyCredentials(access_token))) // Refetch the instance for authenticated fetch .then(async (account) => { await dispatch(fetchInstance()); diff --git a/src/features/chats/components/chat-search/chat-search.tsx b/src/features/chats/components/chat-search/chat-search.tsx index 7eb74fb47..7712166fe 100644 --- a/src/features/chats/components/chat-search/chat-search.tsx +++ b/src/features/chats/components/chat-search/chat-search.tsx @@ -52,9 +52,9 @@ const ChatSearch = (props: IChatSearch) => { }, onSuccess: (response) => { if (isMainPage) { - history.push(`/chats/${response.json.id}`); + history.push(`/chats/${response.id}`); } else { - changeScreen(ChatWidgetScreens.CHAT, response.json.id); + changeScreen(ChatWidgetScreens.CHAT, response.id); } queryClient.invalidateQueries({ queryKey: ['chats', 'search'] }); diff --git a/src/features/compose/components/privacy-dropdown.tsx b/src/features/compose/components/privacy-dropdown.tsx index a194aac21..d83ac24be 100644 --- a/src/features/compose/components/privacy-dropdown.tsx +++ b/src/features/compose/components/privacy-dropdown.tsx @@ -245,7 +245,7 @@ const PrivacyDropdown: React.FC = ({ { icon: require('@tabler/icons/outline/lock.svg'), value: 'private', text: intl.formatMessage(messages.private_short), meta: intl.formatMessage(messages.private_long) }, features.mutualsOnlyStatuses ? { icon: require('@tabler/icons/outline/users-group.svg'), value: 'mutuals_only', text: intl.formatMessage(messages.mutuals_only_short), meta: intl.formatMessage(messages.mutuals_only_long) } : undefined, { icon: require('@tabler/icons/outline/mail.svg'), value: 'direct', text: intl.formatMessage(messages.direct_short), meta: intl.formatMessage(messages.direct_long) }, - features.localOnlyStatuses && v.software === PLEROMA ? { icon: require('@tabler/icons/outline/affiliate.svg'), value: 'local', text: intl.formatMessage(messages.local_short), meta: intl.formatMessage(messages.local_long) } : undefined, + features.visibilityLocalOnly ? { icon: require('@tabler/icons/outline/affiliate.svg'), value: 'local', text: intl.formatMessage(messages.local_short), meta: intl.formatMessage(messages.local_long) } : undefined, ].filter((option): option is Option => !!option); const onChange = (value: string | null) => value && dispatch(changeComposeVisibility(composeId, value)); @@ -352,7 +352,7 @@ const PrivacyDropdown: React.FC = ({ onClose={handleClose} onChange={onChange} placement={placement} - showFederated={features.localOnlyStatuses && v.software === GOTOSOCIAL} + showFederated={features.localOnlyStatuses} federated={compose.federated} onChangeFederated={onChangeFederated} /> diff --git a/src/features/developers/apps/create.tsx b/src/features/developers/apps/create.tsx index e57bfc9f4..53d4e093d 100644 --- a/src/features/developers/apps/create.tsx +++ b/src/features/developers/apps/create.tsx @@ -7,6 +7,8 @@ import { Column, Button, Form, FormActions, FormGroup, Input, Stack, Text, Texta import { useAppDispatch, useOwnAccount } from 'soapbox/hooks'; import { getBaseURL } from 'soapbox/utils/accounts'; +import type { Token } from 'pl-api'; + const messages = defineMessages({ heading: { id: 'column.app_create', defaultMessage: 'Create app' }, namePlaceholder: { id: 'app_create.name_placeholder', defaultMessage: 'e.g. \'Soapbox\'' }, @@ -28,7 +30,7 @@ const CreateApp: React.FC = () => { const { account } = useOwnAccount(); const [app, setApp] = useState | null>(null); - const [token, setToken] = useState(null); + const [token, setToken] = useState(null); const [isLoading, setLoading] = useState(false); const [params, setParams] = useState(BLANK_PARAMS); diff --git a/src/features/draft-statuses/components/draft-status.tsx b/src/features/draft-statuses/components/draft-status.tsx index 608ff9cf5..704b5f382 100644 --- a/src/features/draft-statuses/components/draft-status.tsx +++ b/src/features/draft-statuses/components/draft-status.tsx @@ -35,7 +35,7 @@ const DraftStatus: React.FC = ({ draftStatus, ...other }) => { let quote; if (status.quote) { - if (status.pleroma.get('quote_visible', true) === false) { + if ((status.quote_visible ?? true) === false) { quote = (

diff --git a/src/features/event/components/event-header.tsx b/src/features/event/components/event-header.tsx index 6214fcdf2..41a0cb47b 100644 --- a/src/features/event/components/event-header.tsx +++ b/src/features/event/components/event-header.tsx @@ -104,7 +104,7 @@ const EventHeader: React.FC = ({ status }) => { }; const handleExportClick = () => { - dispatch(fetchEventIcs(status.id)).then(({ data }) => { + dispatch(fetchEventIcs(status.id)).then((data) => { download(data, 'calendar.ics'); }).catch(() => {}); }; @@ -152,7 +152,7 @@ const EventHeader: React.FC = ({ status }) => { const handleChatClick = () => { getOrCreateChatByAccountId(account.id) - .then(({ json: chat }) => history.push(`/chats/${chat.id}`)) + .then((chat) => history.push(`/chats/${chat.id}`)) .catch(() => {}); }; diff --git a/src/features/event/event-information.tsx b/src/features/event/event-information.tsx index 2e805905a..451b5baeb 100644 --- a/src/features/event/event-information.tsx +++ b/src/features/event/event-information.tsx @@ -147,7 +147,7 @@ const EventInformation: React.FC = ({ params }) => { }, [status]); const renderLinks = useCallback(() => { - if (!status.event?.links.size) return null; + if (!status.event?.links?.size) return null; return ( @@ -189,7 +189,7 @@ const EventInformation: React.FC = ({ params }) => { - {status.quote && status.pleroma.get('quote_visible', true) && ( + {status.quote && (status.quote_visible ?? true) && ( )} diff --git a/src/features/group/components/group-action-button.tsx b/src/features/group/components/group-action-button.tsx index c456b935c..66c982615 100644 --- a/src/features/group/components/group-action-button.tsx +++ b/src/features/group/components/group-action-button.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { openModal } from 'soapbox/actions/modals'; -import { useCancelMembershipRequest, useJoinGroup, useLeaveGroup } from 'soapbox/api/hooks'; +import { useJoinGroup, useLeaveGroup } from 'soapbox/api/hooks'; import { Button } from 'soapbox/components/ui'; import { importEntities } from 'soapbox/entity-store/actions'; import { Entities } from 'soapbox/entity-store/entities'; @@ -31,7 +31,6 @@ const GroupActionButton = ({ group }: IGroupActionButton) => { const joinGroup = useJoinGroup(group); const leaveGroup = useLeaveGroup(group); - const cancelRequest = useCancelMembershipRequest(group); const isRequested = group.relationship?.requested; const isNonMember = !group.relationship?.member && !isRequested; @@ -69,7 +68,7 @@ const GroupActionButton = ({ group }: IGroupActionButton) => { }), })); - const onCancelRequest = () => cancelRequest.mutate({}, { + const onCancelRequest = () => leaveGroup.mutate(group.relationship?.id as string, { onSuccess() { const entity = { ...group.relationship as GroupRelationship, @@ -109,7 +108,7 @@ const GroupActionButton = ({ group }: IGroupActionButton) => { diff --git a/src/features/notifications/components/notification.tsx b/src/features/notifications/components/notification.tsx index 694ae0c5b..aeda46f28 100644 --- a/src/features/notifications/components/notification.tsx +++ b/src/features/notifications/components/notification.tsx @@ -49,12 +49,12 @@ const icons: Record = { status: require('@tabler/icons/outline/bell-ringing.svg'), poll: require('@tabler/icons/outline/chart-bar.svg'), move: require('@tabler/icons/outline/briefcase.svg'), - 'pleroma:chat_mention': require('@tabler/icons/outline/messages.svg'), - 'pleroma:emoji_reaction': require('@tabler/icons/outline/mood-happy.svg'), + chat_mention: require('@tabler/icons/outline/messages.svg'), + emoji_reaction: require('@tabler/icons/outline/mood-happy.svg'), update: require('@tabler/icons/outline/pencil.svg'), - 'pleroma:event_reminder': require('@tabler/icons/outline/calendar-time.svg'), - 'pleroma:participation_request': require('@tabler/icons/outline/calendar-event.svg'), - 'pleroma:participation_accepted': require('@tabler/icons/outline/calendar-event.svg'), + event_reminder: require('@tabler/icons/outline/calendar-time.svg'), + participation_request: require('@tabler/icons/outline/calendar-event.svg'), + participation_accepted: require('@tabler/icons/outline/calendar-event.svg'), }; const messages: Record = defineMessages({ @@ -90,11 +90,11 @@ const messages: Record = defineMessages({ id: 'notification.move', defaultMessage: '{name} moved to {targetName}', }, - 'pleroma:chat_mention': { + chat_mention: { id: 'notification.pleroma:chat_mention', defaultMessage: '{name} sent you a message', }, - 'pleroma:emoji_reaction': { + emoji_reaction: { id: 'notification.pleroma:emoji_reaction', defaultMessage: '{name} reacted to your post', }, @@ -102,15 +102,15 @@ const messages: Record = defineMessages({ id: 'notification.update', defaultMessage: '{name} edited a post you interacted with', }, - 'pleroma:event_reminder': { + event_reminder: { id: 'notification.pleroma:event_reminder', defaultMessage: 'An event you are participating in starts soon', }, - 'pleroma:participation_request': { + participation_request: { id: 'notification.pleroma:participation_request', defaultMessage: '{name} wants to join your event', }, - 'pleroma:participation_accepted': { + participation_accepted: { id: 'notification.pleroma:participation_accepted', defaultMessage: 'You were accepted to join the event', }, @@ -257,7 +257,7 @@ const Notification: React.FC = (props) => { }; const renderIcon = (): React.ReactNode => { - if (type === 'pleroma:emoji_reaction' && notification.emoji) { + if (type === 'emoji_reaction' && notification.emoji) { return ( = (props) => { case 'status': case 'poll': case 'update': - case 'pleroma:emoji_reaction': - case 'pleroma:event_reminder': - case 'pleroma:participation_accepted': - case 'pleroma:participation_request': + case 'emoji_reaction': + case 'event_reminder': + case 'participation_accepted': + case 'participation_request': return status && typeof status === 'object' ? ( = ({ let quote; if (actualStatus.quote) { - if (actualStatus.pleroma.get('quote_visible', true) === false) { + if (actualStatus.quote_visible === false) { quote = (

diff --git a/src/features/ui/components/modals/select-bookmark-folder-modal.tsx b/src/features/ui/components/modals/select-bookmark-folder-modal.tsx index 397eeff05..7ab95cfdc 100644 --- a/src/features/ui/components/modals/select-bookmark-folder-modal.tsx +++ b/src/features/ui/components/modals/select-bookmark-folder-modal.tsx @@ -21,7 +21,7 @@ const SelectBookmarkFolderModal: React.FC = ({ statu const status = useAppSelector(state => getStatus(state, { id: statusId })) as StatusEntity; const dispatch = useAppDispatch(); - const [selectedFolder, setSelectedFolder] = useState(status.pleroma.get('bookmark_folder')); + const [selectedFolder, setSelectedFolder] = useState(status.bookmark_folder); const { isFetching, bookmarkFolders } = useBookmarkFolders(); diff --git a/src/normalizers/account.ts b/src/normalizers/account.ts index 81ceed945..4fc2dd075 100644 --- a/src/normalizers/account.ts +++ b/src/normalizers/account.ts @@ -53,10 +53,10 @@ const AccountRecord = ImmutableRecord({ verified: false, // Internal fields - admin: false, + is_admin: false, + is_moderator: false, display_name_html: '', domain: '', - moderator: false, note_emojified: '', note_plain: '', relationship: null as Relationship | null, @@ -125,16 +125,6 @@ const normalizeEmojis = (entity: ImmutableMap) => { return entity.set('emojis', emojis); }; -/** Normalize Pleroma/Fedibird birthday */ -const normalizeBirthday = (account: ImmutableMap) => { - const birthday = [ - account.getIn(['pleroma', 'birthday']), - account.getIn(['other_settings', 'birthday']), - ].find(Boolean); - - return account.set('birthday', birthday); -}; - /** Get Pleroma tags */ const getTags = (account: ImmutableMap): ImmutableList => { const tags = account.getIn(['pleroma', 'tags']); @@ -156,14 +146,6 @@ const normalizeDonor = (account: ImmutableMap) => { return account.setIn(['pleroma', 'tags'], updated); }; -/** Normalize Fedibird/Pleroma location */ -const normalizeLocation = (account: ImmutableMap) => - account.update('location', location => [ - location, - account.getIn(['pleroma', 'location']), - account.getIn(['other_settings', 'location']), - ].find(Boolean)); - /** Set username from acct, if applicable */ const fixUsername = (account: ImmutableMap) => { const acct = account.get('acct') || ''; @@ -227,26 +209,15 @@ const normalizeFqn = (account: ImmutableMap) => { return account.set('fqn', fqn); }; -const normalizeFavicon = (account: ImmutableMap) => { - const favicon = account.getIn(['pleroma', 'favicon']) || ''; - return account.set('favicon', favicon); -}; - const addDomain = (account: ImmutableMap) => { const domain = account.get('fqn', '').split('@')[1] || ''; return account.set('domain', domain); }; const addStaffFields = (account: ImmutableMap) => { - const admin = account.getIn(['pleroma', 'is_admin']) === true; - const moderator = account.getIn(['pleroma', 'is_moderator']) === true; - const staff = admin || moderator; + const staff = account.get('is_admin') || account.get('is_moderator'); - return account.merge({ - admin, - moderator, - staff, - }); + return account.merge({ staff }); }; const normalizeDiscoverable = (account: ImmutableMap) => { @@ -254,10 +225,6 @@ const normalizeDiscoverable = (account: ImmutableMap) => { return account.set('discoverable', discoverable); }; -/** Normalize message acceptance. */ -const normalizeMessageAcceptance = (account: ImmutableMap) => - account.set('accepts_chat_messages', account.getIn(['pleroma', 'accepts_chat_messages'])); - /** Normalize undefined/null birthday to empty string. */ const fixBirthday = (account: ImmutableMap) => { const birthday = account.get('birthday'); @@ -282,12 +249,8 @@ const normalizeAccount = (account: Record) => AccountRecord( normalizeFields(account); normalizeVerified(account); normalizeDonor(account); - normalizeBirthday(account); - normalizeLocation(account); normalizeFqn(account); - normalizeFavicon(account); normalizeDiscoverable(account); - normalizeMessageAcceptance(account); addDomain(account); addStaffFields(account); fixUsername(account); diff --git a/src/normalizers/attachment.ts b/src/normalizers/attachment.ts index 3b6ff6a35..1a48bcaf6 100644 --- a/src/normalizers/attachment.ts +++ b/src/normalizers/attachment.ts @@ -22,6 +22,7 @@ const AttachmentRecord = ImmutableRecord({ remote_url: null as string | null, type: 'unknown', url: '', + mime_type: null as string | null, // Internal fields // TODO: Remove these? They're set in selectors/index.js diff --git a/src/normalizers/status.ts b/src/normalizers/status.ts index 6d130d496..dca0df13f 100644 --- a/src/normalizers/status.ts +++ b/src/normalizers/status.ts @@ -48,6 +48,7 @@ const StatusRecord = ImmutableRecord({ application: null as ImmutableMap | null, approval_status: 'approved' as StatusApprovalStatus, bookmarked: false, + bookmark_folder: null as string | null, card: null as Card | null, content: '', content_map: null as ImmutableMap | null, @@ -71,6 +72,8 @@ const StatusRecord = ImmutableRecord({ pleroma: ImmutableMap(), poll: null as EmbeddedEntity, quote: null as EmbeddedEntity, + quote_url: null as string | null, + quote_visible: null as boolean | null, quotes_count: 0, reactions: null as ImmutableList | null, reblog: null as EmbeddedEntity, @@ -183,17 +186,14 @@ const addSelfMention = (status: ImmutableMap) => { // Move the quote to the top-level const fixQuote = (status: ImmutableMap) => status.withMutations(status => { - status.update('quote', quote => quote || status.getIn(['pleroma', 'quote']) || null); - status.deleteIn(['pleroma', 'quote']); - status.update('quotes_count', quotes_count => quotes_count || status.getIn(['pleroma', 'quotes_count'], 0)); - status.deleteIn(['pleroma', 'quotes_count']); + status.update('quote', quote => quote || null); + status.update('quotes_count', quotes_count => quotes_count || 0); }); // Move the translation to the top-level const fixTranslation = (status: ImmutableMap) => { return status.withMutations(status => { - status.update('translation', translation => translation || status.getIn(['pleroma', 'translation']) || null); - status.deleteIn(['pleroma', 'translation']); + status.update('translation', translation => translation || null); }); }; @@ -206,7 +206,7 @@ const fixSensitivity = (status: ImmutableMap) => { // Normalize event const normalizeEvent = (status: ImmutableMap) => { - if (status.getIn(['pleroma', 'event'])) { + if (status.get('event')) { const firstAttachment = status.get('media_attachments').first(); let banner = null; let mediaAttachments = status.get('media_attachments'); @@ -216,11 +216,11 @@ const normalizeEvent = (status: ImmutableMap) => { mediaAttachments = mediaAttachments.shift(); } - const links = mediaAttachments.filter((attachment: Attachment) => attachment.pleroma.get('mime_type') === 'text/html'); - mediaAttachments = mediaAttachments.filter((attachment: Attachment) => attachment.pleroma.get('mime_type') !== 'text/html'); + const links = mediaAttachments.filter((attachment: Attachment) => attachment.get('mime_type') === 'text/html'); + mediaAttachments = mediaAttachments.filter((attachment: Attachment) => attachment.get('mime_type') !== 'text/html'); const event = EventRecord( - (status.getIn(['pleroma', 'event']) as ImmutableMap) + (status.get('event') as ImmutableMap) .set('banner', banner) .set('links', links), ); @@ -233,7 +233,7 @@ const normalizeEvent = (status: ImmutableMap) => { /** Normalize emojis. */ const normalizeEmojis = (status: ImmutableMap) => { - const data = ImmutableList>(status.getIn(['pleroma', 'emoji_reactions']) || status.get('reactions')); + const data = ImmutableList>(status.get('emoji_reactions') || status.get('reactions')); const reactions = filteredArray(emojiReactionSchema).parse(data.toJS()); if (reactions) { @@ -289,7 +289,7 @@ const parseAccounts = (status: ImmutableMap) => { const parseGroup = (status: ImmutableMap) => { try { - const group = groupSchema.parse(status.get('group', status.getIn(['pleroma', 'group'])).toJS()); + const group = groupSchema.parse(status.get('group').toJS()); return status.set('group', group); } catch (_e) { return status.set('group', null); diff --git a/src/queries/accounts.ts b/src/queries/accounts.ts index a202104e5..60cba117e 100644 --- a/src/queries/accounts.ts +++ b/src/queries/accounts.ts @@ -38,7 +38,7 @@ const useUpdateCredentials = () => { const dispatch = useAppDispatch(); return useMutation({ - mutationFn: (data: UpdateCredentialsData) => client.accounts.updateCredentials(data), + mutationFn: (data: UpdateCredentialsData) => client.settings.updateCredentials(data), onMutate(variables) { const cachedAccount = account; dispatch(patchMeSuccess({ ...account, ...variables })); diff --git a/src/queries/chats.ts b/src/queries/chats.ts index 8b8ca2196..974042c53 100644 --- a/src/queries/chats.ts +++ b/src/queries/chats.ts @@ -2,18 +2,17 @@ import { InfiniteData, keepPreviousData, useInfiniteQuery, useMutation, useQuery import sumBy from 'lodash/sumBy'; import { importFetchedAccount, importFetchedAccounts } from 'soapbox/actions/importer'; -import { getNextLink } from 'soapbox/api'; import { ChatWidgetScreens, useChatContext } from 'soapbox/contexts/chat-context'; import { useStatContext } from 'soapbox/contexts/stat-context'; import { useAppDispatch, useAppSelector, useClient, useFeatures, useLoggedIn, useOwnAccount } from 'soapbox/hooks'; import { normalizeChatMessage } from 'soapbox/normalizers'; -import { ChatMessage } from 'soapbox/types/entities'; import { reOrderChatListItems } from 'soapbox/utils/chats'; import { flattenPages, PaginatedResult, updatePageItem } from 'soapbox/utils/queries'; import { queryClient } from './client'; import { useFetchRelationships } from './relationships'; +import type { Chat, ChatMessage, PaginatedResponse } from 'pl-api'; import type { Account } from 'soapbox/schemas'; interface IChat { @@ -40,21 +39,10 @@ const useChatMessages = (chat: IChat) => { const client = useClient(); const isBlocked = useAppSelector((state) => state.getIn(['relationships', chat.account.id, 'blocked_by'])); - const getChatMessages = async (chatId: string, pageParam?: any): Promise> => { - const nextPageLink = pageParam?.link; - const uri = nextPageLink || `/api/v1/pleroma/chats/${chatId}/messages`; - const response = await client.request(uri); - const { json: data } = response; + const getChatMessages = async (chatId: string, pageParam?: Pick, 'next'>): Promise> => { + const response = await (pageParam?.next ? pageParam.next() : client.chats.getChatMessages(chatId)); - const link = getNextLink(response); - const hasMore = !!link; - const result = data.map(normalizeChatMessage); - - return { - result, - link, - hasMore, - }; + return response; }; const queryInfo = useInfiniteQuery({ @@ -63,14 +51,8 @@ const useChatMessages = (chat: IChat) => { enabled: !isBlocked, gcTime: 0, staleTime: 0, - initialPageParam: { link: undefined as string | undefined }, - getNextPageParam: (config) => { - if (config.hasMore) { - return { link: config.link }; - } - - return undefined; - }, + initialPageParam: { next: null as (() => Promise>) | null }, + getNextPageParam: (config) => config, }); const data = flattenPages(queryInfo.data)?.reverse(); @@ -89,26 +71,17 @@ const useChats = () => { const fetchRelationships = useFetchRelationships(); const { me } = useLoggedIn(); - const getChats = async (pageParam?: any): Promise> => { - const nextPageLink = pageParam?.link; - const uri = nextPageLink || '/api/v2/pleroma/chats'; - const response = await client.request(uri); - const { json: data } = response; + const getChats = async (pageParam?: Pick, 'next'>): Promise> => { + const response = await (pageParam?.next || client.chats.getChats)(); + const { items } = response; - const link = getNextLink(response); - const hasMore = !!link; - - setUnreadChatsCount(Number(response.headers.get('x-unread-messages-count')) || sumBy(data, (chat) => chat.unread)); + setUnreadChatsCount(sumBy(data, (chat) => chat.unread)); // Set the relationships to these users in the redux store. - fetchRelationships.mutate({ accountIds: data.map((item) => item.account.id) }); - dispatch(importFetchedAccounts(data.map((item) => item.account))); + fetchRelationships.mutate({ accountIds: items.map((item) => item.account.id) }); + dispatch(importFetchedAccounts(items.map((item) => item.account))); - return { - result: data, - hasMore, - link, - }; + return response; }; const queryInfo = useInfiniteQuery({ @@ -116,14 +89,8 @@ const useChats = () => { queryFn: ({ pageParam }) => getChats(pageParam), placeholderData: keepPreviousData, enabled: features.chats && !!me, - initialPageParam: { link: undefined as string | undefined }, - getNextPageParam: (config) => { - if (config.hasMore) { - return { link: config.link }; - } - - return undefined; - }, + initialPageParam: { next: null as (() => Promise>) | null }, + getNextPageParam: (config) => config, }); const data = flattenPages(queryInfo.data); @@ -134,7 +101,7 @@ const useChats = () => { }; const getOrCreateChatByAccountId = (accountId: string) => - client.request(`/api/v1/pleroma/chats/by-account-id/${accountId}`, { method: 'POST' }); + client.chats.createChat(accountId); return { chatsQuery, getOrCreateChatByAccountId }; }; @@ -146,7 +113,7 @@ const useChat = (chatId?: string) => { const getChat = async () => { if (chatId) { - const { json: data } = await client.request(`/api/v1/pleroma/chats/${chatId}`); + const data = await client.chats.getChat(chatId); fetchRelationships.mutate({ accountIds: [data.account.id] }); dispatch(importFetchedAccount(data.account)); @@ -155,7 +122,7 @@ const useChat = (chatId?: string) => { } }; - return useQuery({ + return useQuery({ queryKey: ChatKeys.chat(chatId), queryFn: getChat, gcTime: 0, @@ -172,8 +139,8 @@ const useChatActions = (chatId: string) => { const { chat, changeScreen } = useChatContext(); const markChatAsRead = async (lastReadId: string) => - client.request(`/api/v1/pleroma/chats/${chatId}/read`, { body: { last_read_id: lastReadId } }) - .then(({ json: data }) => { + client.chats.markChatAsRead(chatId, lastReadId) + .then((data) => { updatePageItem(['chats', 'search'], data, (o, n) => o.id === n.id); const queryData = queryClient.getQueryData>>(['chats', 'search']); @@ -194,10 +161,7 @@ const useChatActions = (chatId: string) => { const createChatMessage = useMutation({ mutationFn: ({ chatId, content, mediaId }: { chatId: string; content: string; mediaId?: string }) => - client.request(`/api/v1/pleroma/chats/${chatId}/messages`, { - method: 'POST', - body: { content, media_id: mediaId }, - }), + client.chats.createChatMessage(chatId, { content, media_id: mediaId }), retry: false, onMutate: async (variables) => { // Cancel any outgoing refetches (so they don't overwrite our optimistic update) @@ -244,21 +208,21 @@ const useChatActions = (chatId: string) => { queryClient.setQueryData(['chats', 'messages', variables.chatId], context.prevChatMessages); }, onSuccess: (response: any, variables, context) => { - const nextChat = { ...chat, last_message: response.json }; + const nextChat = { ...chat, last_message: response }; updatePageItem(['chats', 'search'], nextChat, (o, n) => o.id === n.id); updatePageItem( ChatKeys.chatMessages(variables.chatId), - normalizeChatMessage(response.json), + normalizeChatMessage(response), (o) => o.id === context.pendingId, ); reOrderChatListItems(); }, }); const deleteChatMessage = (chatMessageId: string) => - client.request(`/api/v1/pleroma/chats/${chatId}/messages/${chatMessageId}`, { method: 'DELETE' }); + client.chats.deleteChatMessage(chatId, chatMessageId); const deleteChat = useMutation({ - mutationFn: () => client.request(`/api/v1/pleroma/chats/${chatId}`, { method: 'DELETE' }), + mutationFn: () => client.chats.deleteChat(chatId), onSuccess() { changeScreen(ChatWidgetScreens.INBOX); queryClient.invalidateQueries({ queryKey: ChatKeys.chatMessages(chatId) }); diff --git a/src/queries/relationships.ts b/src/queries/relationships.ts index 93d61fca0..81c236785 100644 --- a/src/queries/relationships.ts +++ b/src/queries/relationships.ts @@ -9,12 +9,10 @@ const useFetchRelationships = () => { return useMutation({ mutationFn: ({ accountIds }: { accountIds: string[]}) => { - const ids = accountIds.map((id) => `id[]=${id}`).join('&'); - - return client.request(`/api/v1/accounts/relationships?${ids}`); + return client.accounts.getRelationships(accountIds); }, onSuccess(response) { - dispatch(fetchRelationshipsSuccess(response.json)); + dispatch(fetchRelationshipsSuccess(response)); }, onError(error) { dispatch(fetchRelationshipsFail(error)); diff --git a/src/queries/suggestions.ts b/src/queries/suggestions.ts index 74e486d80..e37c2b579 100644 --- a/src/queries/suggestions.ts +++ b/src/queries/suggestions.ts @@ -15,7 +15,7 @@ const useSuggestions = () => { const dispatch = useAppDispatch(); const getSuggestions = async () => { - const response = await client.accounts.getSuggestions(); + const response = await client.myAccount.getSuggestions(); const accounts = response.map(({ account }) => account); const accountIds = accounts.map((account) => account.id); @@ -43,7 +43,7 @@ const useDismissSuggestion = () => { const client = useClient(); return useMutation({ - mutationFn: (accountId: string) => client.accounts.dismissSuggestions(accountId), + mutationFn: (accountId: string) => client.myAccount.dismissSuggestions(accountId), onMutate(accountId: string) { removePageItem(SuggestionKeys.suggestions, accountId, (o: any, n: any) => o.account === n); }, @@ -55,7 +55,7 @@ const useOnboardingSuggestions = () => { const dispatch = useAppDispatch(); const getSuggestions = async () => { - const response = await client.accounts.getSuggestions(); + const response = await client.myAccount.getSuggestions(); const accounts = response.map(({ account }) => account); const accountIds = accounts.map((account) => account.id); diff --git a/src/reducers/instance.ts b/src/reducers/instance.ts index 7687acdc2..4d67fd830 100644 --- a/src/reducers/instance.ts +++ b/src/reducers/instance.ts @@ -75,7 +75,7 @@ const getHost = (instance: { domain: string }) => { } }; -const persistInstance = ({ instance }: { instance: { domain: string } }, host: string | null = getHost(instance)) => { +const persistInstance = (instance: { domain: string }, host: string | null = getHost(instance)) => { if (host) { KVStore.setItem(`instance:${host}`, instance).catch(console.error); } @@ -89,7 +89,7 @@ const handleInstanceFetchFail = (state: Instance, error: Record) => } }; -const instance = (state = initialState, action: AnyAction) => { +const instance = (state = initialState, action: AnyAction): Instance => { switch (action.type) { case PLEROMA_PRELOAD_IMPORT: return preloadImport(state, action, '/api/v1/instance'); diff --git a/src/reducers/status-lists.ts b/src/reducers/status-lists.ts index 99e0aa7a3..4d6cd4243 100644 --- a/src/reducers/status-lists.ts +++ b/src/reducers/status-lists.ts @@ -135,7 +135,7 @@ const maybeAppendScheduledStatus = (state: State, status: APIEntity) => { const addBookmarkToLists = (state: State, status: APIEntity) => { state = prependOneToList(state, 'bookmarks', status); - const folderId = status.pleroma.bookmark_folder; + const folderId = status.bookmark_folder; if (folderId) { return prependOneToList(state, `bookmarks:${folderId}`, status); } @@ -144,7 +144,7 @@ const addBookmarkToLists = (state: State, status: APIEntity) => { const removeBookmarkFromLists = (state: State, status: StatusEntity) => { state = removeOneFromList(state, 'bookmarks', status); - const folderId = status.pleroma.get('bookmark_folder'); + const folderId = status.bookmark_folder; if (folderId) { return removeOneFromList(state, `bookmarks:${folderId}`, status); } diff --git a/src/reducers/statuses.ts b/src/reducers/statuses.ts index c5e26c549..da3879c6e 100644 --- a/src/reducers/statuses.ts +++ b/src/reducers/statuses.ts @@ -130,7 +130,7 @@ const calculateStatus = ( }; // Check whether a status is a quote by secondary characteristics -const isQuote = (status: StatusRecord) => Boolean(status.pleroma.get('quote_url')); +const isQuote = (status: StatusRecord) => Boolean(status.quote_url); // Preserve translation if an existing status already has it const fixTranslation = (status: StatusRecord, oldStatus?: StatusRecord): StatusRecord => { @@ -147,7 +147,7 @@ const fixQuote = (status: StatusRecord, oldStatus?: StatusRecord): StatusRecord if (oldStatus && !status.quote && isQuote(status)) { return status .set('quote', oldStatus.quote) - .updateIn(['pleroma', 'quote_visible'], visible => visible || oldStatus.pleroma.get('quote_visible')); + .updateIn(['pleroma', 'quote_visible'], visible => visible || oldStatus.quote_visible); } else { return status; } diff --git a/src/reducers/user-lists.ts b/src/reducers/user-lists.ts index bfbe18619..cf257ac35 100644 --- a/src/reducers/user-lists.ts +++ b/src/reducers/user-lists.ts @@ -71,7 +71,7 @@ const ReactionRecord = ImmutableRecord({ }); const ReactionListRecord = ImmutableRecord({ - next: null as string | null, + next: null as (() => Promise>) | null, items: ImmutableOrderedSet(), isLoading: false, }); @@ -82,7 +82,7 @@ const ParticipationRequestRecord = ImmutableRecord({ }); const ParticipationRequestListRecord = ImmutableRecord({ - next: null as string | null, + next: null as (() => Promise>) | null, items: ImmutableOrderedSet(), isLoading: false, }); diff --git a/src/schemas/index.ts b/src/schemas/index.ts index 50ad669df..cb7f28bd2 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -1,8 +1,10 @@ +import { notificationSchema } from 'pl-api'; + export { accountSchema, type Account } from './account'; export { announcementSchema, adminAnnouncementSchema, type Announcement, type AdminAnnouncement } from './announcement'; export { announcementReactionSchema, type AnnouncementReaction } from './announcement-reaction'; export { attachmentSchema, type Attachment } from './attachment'; -export { bookmarkFolderSchema, type BookmarkFolder } from './bookmark-folder'; +export { bookmarkFolderSchema, type BookmarkFolder } from 'pl-api'; export { cardSchema, type Card } from './card'; export { chatMessageSchema, type ChatMessage } from './chat-message'; export { customEmojiSchema, type CustomEmoji } from './custom-emoji'; @@ -11,14 +13,14 @@ export { emojiReactionSchema, type EmojiReaction } from './emoji-reaction'; export { groupSchema, type Group } from './group'; export { groupMemberSchema, type GroupMember } from './group-member'; export { groupRelationshipSchema, type GroupRelationship } from './group-relationship'; -export { instanceSchema, type Instance } from './instance'; +export { instanceSchema, type Instance } from 'pl-api'; export { mentionSchema, type Mention } from './mention'; export { moderationLogEntrySchema, type ModerationLogEntry } from './moderation-log-entry'; -export { notificationSchema, type Notification } from './notification'; +export { notificationSchema, type Notification } from 'pl-api'; export { pollSchema, type Poll, type PollOption } from './poll'; export { relationshipSchema, type Relationship } from './relationship'; export { relaySchema, type Relay } from './relay'; export { ruleSchema, adminRuleSchema, type Rule, type AdminRule } from './rule'; export { statusSchema, type Status } from './status'; -export { tagSchema, type Tag } from './tag'; +export { tagSchema, type Tag } from 'pl-api'; export { tombstoneSchema, type Tombstone } from './tombstone'; diff --git a/src/schemas/notification.ts b/src/schemas/notification.ts deleted file mode 100644 index 535bce068..000000000 --- a/src/schemas/notification.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { z } from 'zod'; - -import { accountSchema } from './account'; -import { chatMessageSchema } from './chat-message'; -import { statusSchema } from './status'; -import { emojiSchema } from './utils'; - -const baseNotificationSchema = z.object({ - account: accountSchema, - created_at: z.string().datetime().catch(new Date().toUTCString()), - id: z.string(), - type: z.string(), -}); - -const mentionNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('mention'), - status: statusSchema, -}); - -const statusNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('status'), - status: statusSchema, -}); - -const reblogNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('reblog'), - status: statusSchema, -}); - -const followNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('follow'), -}); - -const followRequestNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('follow_request'), -}); - -const favouriteNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('favourite'), - status: statusSchema, -}); - -const pollNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('poll'), - status: statusSchema, -}); - -const updateNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('update'), - status: statusSchema, -}); - -const moveNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('move'), - target: accountSchema, -}); - -const chatMessageNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('chat_message'), - chat_message: chatMessageSchema, -}); - -const emojiReactionNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('pleroma:emoji_reaction'), - emoji: emojiSchema, - emoji_url: z.string().url().optional().catch(undefined), -}); - -const eventReminderNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('pleroma:event_reminder'), - status: statusSchema, -}); - -const participationRequestNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('pleroma:participation_request'), - status: statusSchema, -}); - -const participationAcceptedNotificationSchema = baseNotificationSchema.extend({ - type: z.literal('pleroma:participation_accepted'), - status: statusSchema, -}); - -const notificationSchema = z.discriminatedUnion('type', [ - mentionNotificationSchema, - statusNotificationSchema, - reblogNotificationSchema, - followNotificationSchema, - followRequestNotificationSchema, - favouriteNotificationSchema, - pollNotificationSchema, - updateNotificationSchema, - moveNotificationSchema, - chatMessageNotificationSchema, - emojiReactionNotificationSchema, - eventReminderNotificationSchema, - participationRequestNotificationSchema, - participationAcceptedNotificationSchema, -]); - -type Notification = z.infer; - -export { notificationSchema, type Notification }; \ No newline at end of file diff --git a/src/sentry.ts b/src/sentry.ts index bcf45b3be..6794a4254 100644 --- a/src/sentry.ts +++ b/src/sentry.ts @@ -1,7 +1,7 @@ import { NODE_ENV } from 'soapbox/build-config'; import sourceCode from 'soapbox/utils/code'; -import type { Account } from './schemas'; +import type { Account } from 'pl-api'; import type { CaptureContext, UserFeedback } from '@sentry/types'; import type { SetOptional } from 'type-fest'; @@ -46,7 +46,7 @@ const startSentry = async (dsn: string): Promise => { }; /** Associate the account with Sentry events. */ -const setSentryAccount = async (account: Account): Promise => { +const setSentryAccount = async (account: Pick): Promise => { const Sentry = await import('@sentry/react'); Sentry.setUser({ diff --git a/src/utils/features.ts b/src/utils/features.ts index 66c98bcd1..075e350ae 100644 --- a/src/utils/features.ts +++ b/src/utils/features.ts @@ -6,7 +6,8 @@ import lt from 'semver/functions/lt'; import semverParse from 'semver/functions/parse'; import { custom } from 'soapbox/custom'; -import { type Instance } from 'soapbox/schemas'; + +import type { Instance } from 'pl-api'; /** Import custom overrides, if exists */ const overrides = custom('features'); diff --git a/src/utils/notification.ts b/src/utils/notification.ts index 366a93c87..243e5f19e 100644 --- a/src/utils/notification.ts +++ b/src/utils/notification.ts @@ -8,17 +8,17 @@ const NOTIFICATION_TYPES = [ 'poll', 'status', 'move', - 'pleroma:chat_mention', - 'pleroma:emoji_reaction', + 'chat_mention', + 'emoji_reaction', 'update', - 'pleroma:event_reminder', - 'pleroma:participation_request', - 'pleroma:participation_accepted', + 'event_reminder', + 'participation_request', + 'participation_accepted', ] as const; /** Notification types to exclude from the "All" filter by default. */ const EXCLUDE_TYPES = [ - 'pleroma:chat_mention', + 'chat_mention', ] as const; type NotificationType = typeof NOTIFICATION_TYPES[number]; diff --git a/src/utils/queries.ts b/src/utils/queries.ts index 7d9b2ddc7..da84ad46a 100644 --- a/src/utils/queries.ts +++ b/src/utils/queries.ts @@ -24,9 +24,9 @@ const deduplicateById = (entities: T[]): T[] => { }; /** Flatten paginated results into a single array. */ -const flattenPages = (queryData: InfiniteData> | undefined) => { +const flattenPages = (queryData: InfiniteData | PaginatedResponse> | undefined) => { const data = queryData?.pages.reduce( - (prev: T[], curr) => [...prev, ...curr.result], + (prev: T[], curr) => [...prev, ...((curr as any).result || (curr as any).items)], [], ); diff --git a/src/utils/quirks.ts b/src/utils/quirks.ts index db54239d6..6d578a10a 100644 --- a/src/utils/quirks.ts +++ b/src/utils/quirks.ts @@ -3,7 +3,7 @@ import { createSelector } from 'reselect'; import { parseVersion, MITRA } from './features'; -import type { Instance } from 'soapbox/schemas'; +import type { Instance } from 'pl-api'; /** For solving bugs between API implementations. */ const getQuirks = createSelector([ diff --git a/src/utils/scopes.ts b/src/utils/scopes.ts index 9a6175952..f92f0d8b5 100644 --- a/src/utils/scopes.ts +++ b/src/utils/scopes.ts @@ -1,7 +1,7 @@ import { PLEROMA, parseVersion } from './features'; -import type { Instance } from 'soapbox/schemas'; +import type { Instance } from 'pl-api'; import type { RootState } from 'soapbox/store'; /** diff --git a/yarn.lock b/yarn.lock index d267a275b..663e99dca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3690,13 +3690,6 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-dispatch@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/async-dispatch/-/async-dispatch-1.0.10.tgz#f90261f302874579e64b04348ceb27850fffc8dd" - integrity sha512-KXKexfJr+LS3njrbn8sNnRh5w0pXvM2QboWB12MpmZ3Amr3wIT8FAe6MXf5+JylBOlcxqz1oCyVUY4VKNkUvDA== - dependencies: - rollup "^2.23.0" - async@^3.2.3: version "3.2.4" resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" @@ -8397,6 +8390,7 @@ pkg-types@^1.0.3: dependencies: blurhash "^2.0.5" http-link-header "^1.1.3" + lodash "^4.17.21" object-to-formdata "^4.5.1" query-string "^9.1.0" semver "^7.6.3" @@ -9522,7 +9516,7 @@ rollup-plugin-visualizer@^5.9.2: source-map "^0.7.4" yargs "^17.5.1" -rollup@^2.23.0, rollup@^2.43.1: +rollup@^2.43.1: version "2.79.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==