diff --git a/packages/pl-fe/src/actions/accounts.test.ts b/packages/pl-fe/src/actions/accounts.test.ts index 8b6b5f10b..3edbc65fb 100644 --- a/packages/pl-fe/src/actions/accounts.test.ts +++ b/packages/pl-fe/src/actions/accounts.test.ts @@ -12,7 +12,6 @@ import { fetchAccountByUsername, fetchRelationships, muteAccount, - removeFromFollowers, unblockAccount, unmuteAccount, } from './accounts'; @@ -775,72 +774,6 @@ describe('unsubscribeAccount()', () => { }); }); -describe('removeFromFollowers()', () => { - const id = '1'; - - describe('when logged out', () => { - beforeEach(() => { - const state = { ...rootState, me: null }; - store = mockStore(state); - }); - - it('should do nothing', async() => { - await store.dispatch(removeFromFollowers(id)); - const actions = store.getActions(); - - expect(actions).toEqual([]); - }); - }); - - describe('when logged in', () => { - beforeEach(() => { - const state = { ...rootState, me: '123' }; - store = mockStore(state); - }); - - describe('with a successful API request', () => { - beforeEach(() => { - __stub((mock) => { - mock.onPost(`/api/v1/accounts/${id}/remove_from_followers`).reply(200, {}); - }); - }); - - it('should dispatch the correct actions', async() => { - const expectedActions = [ - { type: 'ACCOUNT_REMOVE_FROM_FOLLOWERS_REQUEST', id }, - { - type: 'ACCOUNT_REMOVE_FROM_FOLLOWERS_SUCCESS', - relationship: {}, - }, - ]; - await store.dispatch(removeFromFollowers(id)); - const actions = store.getActions(); - - expect(actions).toEqual(expectedActions); - }); - }); - - describe('with an unsuccessful API request', () => { - beforeEach(() => { - __stub((mock) => { - mock.onPost(`/api/v1/accounts/${id}/remove_from_followers`).networkError(); - }); - }); - - it('should dispatch the correct actions', async() => { - const expectedActions = [ - { type: 'ACCOUNT_REMOVE_FROM_FOLLOWERS_REQUEST', id }, - { type: 'ACCOUNT_REMOVE_FROM_FOLLOWERS_FAIL', id, error: new Error('Network Error') }, - ]; - await store.dispatch(removeFromFollowers(id)); - const actions = store.getActions(); - - expect(actions).toEqual(expectedActions); - }); - }); - }); -}); - describe('fetchRelationships()', () => { const id = '1'; diff --git a/packages/pl-fe/src/actions/accounts.ts b/packages/pl-fe/src/actions/accounts.ts index 71b74a1d6..9b62d21fb 100644 --- a/packages/pl-fe/src/actions/accounts.ts +++ b/packages/pl-fe/src/actions/accounts.ts @@ -1,9 +1,4 @@ -import { - type UpdateNotificationSettingsParams, - type CreateAccountParams, - type Relationship, - type MuteAccountParams, -} from 'pl-api'; +import { type CreateAccountParams, type Relationship } from 'pl-api'; import { queryClient } from 'pl-fe/queries/client'; import { selectAccount } from 'pl-fe/selectors'; @@ -13,13 +8,11 @@ import { getClient, type PlfeResponse } from '../api'; import { importEntities } from './importer'; -import type { MinifiedSuggestion } from 'pl-fe/queries/trends/use-suggested-accounts'; import type { MinifiedStatus } from 'pl-fe/reducers/statuses'; import type { AppDispatch, RootState } from 'pl-fe/store'; import type { History } from 'pl-fe/types/history'; const ACCOUNT_BLOCK_SUCCESS = 'ACCOUNT_BLOCK_SUCCESS' as const; - const ACCOUNT_MUTE_SUCCESS = 'ACCOUNT_MUTE_SUCCESS' as const; const maybeRedirectLogin = (error: { response: PlfeResponse }, history?: History) => { @@ -29,8 +22,6 @@ const maybeRedirectLogin = (error: { response: PlfeResponse }, history?: History } }; -const noOp = () => new Promise(f => f(undefined)); - const createAccount = (params: CreateAccountParams) => async (dispatch: AppDispatch, getState: () => RootState) => getClient(getState()).settings.createAccount(params).then((response) => @@ -84,88 +75,6 @@ const fetchAccountByUsername = (username: string, history?: History) => } }; -const blockAccount = (accountId: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return null; - - return getClient(getState).filtering.blockAccount(accountId) - .then(response => { - dispatch(importEntities({ relationships: [response] })); - - queryClient.setQueryData>(['suggestions'], suggestions => suggestions - ? suggestions.filter((suggestion) => suggestion.account_id !== accountId) - : undefined); - - // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers - return dispatch(blockAccountSuccess(response, getState().statuses)); - }); - }; - -const unblockAccount = (accountId: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return null; - - return getClient(getState).filtering.unblockAccount(accountId) - .then(response => { - dispatch(importEntities({ relationships: [response] })); - }); - }; - -const blockAccountSuccess = (relationship: Relationship, statuses: Record) => ({ - type: ACCOUNT_BLOCK_SUCCESS, - relationship, - statuses, -}); - -const muteAccount = (accountId: string, notifications?: boolean, duration = 0) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return null; - - const client = getClient(getState); - - const params: MuteAccountParams = { - notifications, - }; - - if (duration) { - params.duration = duration; - } - - return client.filtering.muteAccount(accountId, params) - .then(response => { - dispatch(importEntities({ relationships: [response] })); - - queryClient.setQueryData>(['suggestions'], suggestions => suggestions - ? suggestions.filter((suggestion) => suggestion.account_id !== accountId) - : undefined); - - // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers - return dispatch(muteAccountSuccess(response, getState().statuses)); - }); - }; - -const unmuteAccount = (accountId: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return null; - - return getClient(getState()).filtering.unmuteAccount(accountId) - .then(response => dispatch(importEntities({ relationships: [response] }))); - }; - -const muteAccountSuccess = (relationship: Relationship, statuses: Record) => ({ - type: ACCOUNT_MUTE_SUCCESS, - relationship, - statuses, -}); - -const removeFromFollowers = (accountId: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return null; - - return getClient(getState()).accounts.removeAccountFromFollowers(accountId) - .then(response => dispatch(importEntities({ relationships: [response] }))); - }; - const fetchRelationships = (accountIds: string[]) => (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return null; @@ -180,28 +89,6 @@ const fetchRelationships = (accountIds: string[]) => .then(response => dispatch(importEntities({ relationships: response }))); }; -const pinAccount = (accountId: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return dispatch(noOp); - - return getClient(getState).accounts.pinAccount(accountId).then(response => - dispatch(importEntities({ relationships: [response] })), - ); - }; - -const unpinAccount = (accountId: string) => - (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return dispatch(noOp); - - return getClient(getState).accounts.unpinAccount(accountId).then(response => - dispatch(importEntities({ relationships: [response] })), - ); - }; - -const updateNotificationSettings = (params: UpdateNotificationSettingsParams) => - (dispatch: AppDispatch, getState: () => RootState) => - getClient(getState).settings.updateNotificationSettings(params).then((data) => ({ params, data })); - const accountLookup = (acct: string, signal?: AbortSignal) => (dispatch: AppDispatch, getState: () => RootState) => getClient(getState()).accounts.lookupAccount(acct, { signal }).then((account) => { @@ -209,13 +96,11 @@ const accountLookup = (acct: string, signal?: AbortSignal) => return account; }); -const biteAccount = (accountId: string) => - (dispatch: AppDispatch, getState: () => RootState) => - getClient(getState).accounts.biteAccount(accountId); - -type AccountsAction = - | ReturnType - | ReturnType; +type AccountsAction = { + type: typeof ACCOUNT_BLOCK_SUCCESS | typeof ACCOUNT_MUTE_SUCCESS; + relationship: Relationship; + statuses: Record; + }; export { ACCOUNT_BLOCK_SUCCESS, @@ -223,16 +108,7 @@ export { createAccount, fetchAccount, fetchAccountByUsername, - blockAccount, - unblockAccount, - muteAccount, - unmuteAccount, - removeFromFollowers, fetchRelationships, - pinAccount, - unpinAccount, - updateNotificationSettings, accountLookup, - biteAccount, type AccountsAction, }; diff --git a/packages/pl-fe/src/components/dropdown-menu/dropdown-menu.tsx b/packages/pl-fe/src/components/dropdown-menu/dropdown-menu.tsx index a9c9fe444..9d7e9141c 100644 --- a/packages/pl-fe/src/components/dropdown-menu/dropdown-menu.tsx +++ b/packages/pl-fe/src/components/dropdown-menu/dropdown-menu.tsx @@ -158,7 +158,7 @@ const DropdownMenuContent: React.FC = ({ handleClose, item {Component && } {(items?.length || touchscreen) && renderItems(items)} -
+
{tab !== undefined && ( <> diff --git a/packages/pl-fe/src/components/status-action-bar.tsx b/packages/pl-fe/src/components/status-action-bar.tsx index d7995f8d5..6d7afe41d 100644 --- a/packages/pl-fe/src/components/status-action-bar.tsx +++ b/packages/pl-fe/src/components/status-action-bar.tsx @@ -3,7 +3,6 @@ import React, { useCallback, useMemo } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { useHistory, useRouteMatch } from 'react-router-dom'; -import { blockAccount } from 'pl-fe/actions/accounts'; import { redactStatus } from 'pl-fe/actions/admin'; import { directCompose, mentionCompose, quoteCompose, replyCompose } from 'pl-fe/actions/compose'; import { emojiReact, unEmojiReact } from 'pl-fe/actions/emoji-reacts'; @@ -24,6 +23,7 @@ import { useClient } from 'pl-fe/hooks/use-client'; import { useFeatures } from 'pl-fe/hooks/use-features'; import { useInstance } from 'pl-fe/hooks/use-instance'; import { useOwnAccount } from 'pl-fe/hooks/use-own-account'; +import { useBlockAccountMutation, useUnblockAccountMutation } from 'pl-fe/queries/accounts/use-relationship'; import { useChats } from 'pl-fe/queries/chats'; import { useBlockGroupUserMutation } from 'pl-fe/queries/groups/use-group-blocks'; import { useCustomEmojis } from 'pl-fe/queries/instance/use-custom-emojis'; @@ -50,6 +50,7 @@ const messages = defineMessages({ adminAccount: { id: 'status.admin_account', defaultMessage: 'Moderate @{name}' }, admin_status: { id: 'status.admin_status', defaultMessage: 'Open this post in the moderation interface' }, block: { id: 'account.block', defaultMessage: 'Block @{name}' }, + unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, blocked: { id: 'group.group_mod_block.success', defaultMessage: '@{name} is banned' }, blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block and report' }, blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' }, @@ -579,6 +580,8 @@ const MenuButton: React.FC = ({ const { mutate: unbookmarkStatus } = useUnbookmarkStatus(status.id); const { mutate: pinStatus } = usePinStatus(status?.id!); const { mutate: unpinStatus } = useUnpinStatus(status?.id!); + const { mutate: blockAccount } = useBlockAccountMutation(status.account_id); + const { mutate: unblockAccount } = useUnblockAccountMutation(status.account_id); const { groupRelationship } = useGroupRelationship(status.group_id || undefined); const features = useFeatures(); @@ -691,15 +694,19 @@ const MenuButton: React.FC = ({ heading: , message: @{account.acct} }} />, confirm: intl.formatMessage(messages.blockConfirm), - onConfirm: () => dispatch(blockAccount(account.id)), + onConfirm: () => blockAccount(), secondary: intl.formatMessage(messages.blockAndReport), onSecondary: () => { - dispatch(blockAccount(account.id)); + blockAccount(); dispatch(initReport(ReportableEntities.STATUS, account, { status })); }, }); }; + const handleUnblockClick: React.EventHandler = (e) => { + unblockAccount(); + }; + const handleEmbed = () => { openModal('EMBED', { url: status.url, @@ -973,11 +980,19 @@ const MenuButton: React.FC = ({ action: handleMuteClick, icon: require('@phosphor-icons/core/regular/speaker-x.svg'), }); - menu.push({ - text: intl.formatMessage(messages.block, { name: username }), - action: handleBlockClick, - icon: require('@phosphor-icons/core/regular/prohibit.svg'), - }); + if (status.account.relationship?.blocking) { + menu.push({ + text: intl.formatMessage(messages.unblock, { name: username }), + action: handleUnblockClick, + icon: require('@phosphor-icons/core/regular/prohibit.svg'), + }); + } else { + menu.push({ + text: intl.formatMessage(messages.block, { name: username }), + action: handleBlockClick, + icon: require('@phosphor-icons/core/regular/prohibit.svg'), + }); + } menu.push({ text: intl.formatMessage(messages.report, { name: username }), action: handleReport, diff --git a/packages/pl-fe/src/features/account/components/header.tsx b/packages/pl-fe/src/features/account/components/header.tsx index 9417b294d..eeed116c5 100644 --- a/packages/pl-fe/src/features/account/components/header.tsx +++ b/packages/pl-fe/src/features/account/components/header.tsx @@ -6,7 +6,6 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; import * as v from 'valibot'; -import { biteAccount, blockAccount, pinAccount, removeFromFollowers, unblockAccount, unmuteAccount, unpinAccount } from 'pl-fe/actions/accounts'; import { mentionCompose, directCompose } from 'pl-fe/actions/compose'; import { initReport, ReportableEntities } from 'pl-fe/actions/reports'; import Account from 'pl-fe/components/account'; @@ -29,7 +28,15 @@ import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useClient } from 'pl-fe/hooks/use-client'; import { useFeatures } from 'pl-fe/hooks/use-features'; import { useOwnAccount } from 'pl-fe/hooks/use-own-account'; -import { useFollowMutation } from 'pl-fe/queries/accounts/use-relationship'; +import { + useBlockAccountMutation, + useFollowAccountMutation, + usePinAccountMutation, + useRemoveAccountFromFollowersMutation, + useUnblockAccountMutation, + useUnmuteAccountMutation, + useUnpinAccountMutation, +} from 'pl-fe/queries/accounts/use-relationship'; import { useChats } from 'pl-fe/queries/chats'; import { queryClient } from 'pl-fe/queries/client'; import { blockDomainMutationOptions, unblockDomainMutationOptions } from 'pl-fe/queries/settings/domain-blocks'; @@ -134,7 +141,13 @@ const Header: React.FC = ({ account }) => { const features = useFeatures(); const { account: ownAccount } = useOwnAccount(); - const { mutate: follow } = useFollowMutation(account?.id!); + const { mutate: followAccount } = useFollowAccountMutation(account?.id!); + const { mutate: blockAccount } = useBlockAccountMutation(account?.id!); + const { mutate: unblockAccount } = useUnblockAccountMutation(account?.id!); + const { mutate: unmuteAccount } = useUnmuteAccountMutation(account?.id!); + const { mutate: pinAccount } = usePinAccountMutation(account?.id!); + const { mutate: unpinAccount } = useUnpinAccountMutation(account?.id!); + const { mutate: removeFromFollowers } = useRemoveAccountFromFollowersMutation(account?.id!); const { openModal } = useModalsActions(); const settings = useSettings(); @@ -181,16 +194,16 @@ const Header: React.FC = ({ account }) => { const onBlock = () => { if (account.relationship?.blocking) { - dispatch(unblockAccount(account.id)); + unblockAccount(); } else { openModal('CONFIRM', { heading: , message: @{account.acct} }} />, confirm: intl.formatMessage(messages.blockConfirm), - onConfirm: () => dispatch(blockAccount(account.id)), + onConfirm: () => blockAccount(), secondary: intl.formatMessage(messages.blockAndReport), onSecondary: () => { - dispatch(blockAccount(account.id)); + blockAccount(); dispatch(initReport(ReportableEntities.ACCOUNT, account)); }, }); @@ -207,26 +220,26 @@ const Header: React.FC = ({ account }) => { const onReblogToggle = () => { if (account.relationship?.showing_reblogs) { - follow({ reblogs: false }); + followAccount({ reblogs: false }); } else { - follow({ reblogs: true }); + followAccount({ reblogs: true }); } }; const onEndorseToggle = () => { if (account.relationship?.endorsed) { - dispatch(unpinAccount(account.id)) - .then(() => toast.success(intl.formatMessage(messages.userUnendorsed, { acct: account.acct }))) - .catch(() => { }); + unpinAccount(undefined, { + onSuccess: () => toast.success(intl.formatMessage(messages.userUnendorsed, { acct: account.acct })), + }); } else { - dispatch(pinAccount(account.id)) - .then(() => toast.success(intl.formatMessage(messages.userEndorsed, { acct: account.acct }))) - .catch(() => { }); + pinAccount(undefined, { + onSuccess: () => toast.success(intl.formatMessage(messages.userEndorsed, { acct: account.acct })), + }); } }; const onBite = () => { - dispatch(biteAccount(account.id)) + client.accounts.biteAccount(account.id) .then(() => toast.success(intl.formatMessage(messages.userBit, { acct: account.acct }))) .catch(() => toast.error(intl.formatMessage(messages.userBiteFail, { acct: account.acct }))); }; @@ -243,7 +256,7 @@ const Header: React.FC = ({ account }) => { const onMute = () => { if (account.relationship?.muting) { - dispatch(unmuteAccount(account.id)); + unmuteAccount(); } else { openModal('MUTE', { accountId: account.id }); } @@ -279,10 +292,10 @@ const Header: React.FC = ({ account }) => { heading: @{account.acct} }} />, message: @{account.acct} }} />, confirm: intl.formatMessage(messages.removeFromFollowersConfirm), - onConfirm: () => dispatch(removeFromFollowers(account.id)), + onConfirm: () => removeFromFollowers(), }); } else { - dispatch(removeFromFollowers(account.id)); + removeFromFollowers(); } }; diff --git a/packages/pl-fe/src/features/chats/components/chat-composer.tsx b/packages/pl-fe/src/features/chats/components/chat-composer.tsx index dd7a3a709..c46f383b9 100644 --- a/packages/pl-fe/src/features/chats/components/chat-composer.tsx +++ b/packages/pl-fe/src/features/chats/components/chat-composer.tsx @@ -1,7 +1,6 @@ import React, { useState } from 'react'; import { defineMessages, IntlShape, useIntl } from 'react-intl'; -import { unblockAccount } from 'pl-fe/actions/accounts'; import Button from 'pl-fe/components/ui/button'; import Combobox, { ComboboxInput, ComboboxList, ComboboxOption, ComboboxPopover } from 'pl-fe/components/ui/combobox'; import HStack from 'pl-fe/components/ui/hstack'; @@ -11,9 +10,8 @@ import Text from 'pl-fe/components/ui/text'; import { useChatContext } from 'pl-fe/contexts/chat-context'; import UploadButton from 'pl-fe/features/compose/components/upload-button'; import emojiSearch from 'pl-fe/features/emoji/search'; -import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useInstance } from 'pl-fe/hooks/use-instance'; -import { useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship'; +import { useRelationshipQuery, useUnblockAccountMutation } from 'pl-fe/queries/accounts/use-relationship'; import { useModalsActions } from 'pl-fe/stores/modals'; import { textAtCursorMatchesToken } from 'pl-fe/utils/suggestions'; @@ -76,11 +74,11 @@ const ChatComposer = React.forwardRef uploadProgress, }, ref) => { const intl = useIntl(); - const dispatch = useAppDispatch(); const { openModal } = useModalsActions(); const { chat } = useChatContext(); const { data: relationship } = useRelationshipQuery(chat?.account.id); + const { mutate: unblockAccount } = useUnblockAccountMutation(chat?.account.id!); const isBlocked = relationship?.blocked_by && false; const isBlocking = relationship?.blocking && false; @@ -146,7 +144,7 @@ const ChatComposer = React.forwardRef message: intl.formatMessage(messages.unblockMessage), confirm: intl.formatMessage(messages.unblockConfirm), confirmationTheme: 'primary', - onConfirm: () => dispatch(unblockAccount(chat?.account.id as string)), + onConfirm: () => unblockAccount(), }); }; diff --git a/packages/pl-fe/src/features/chats/components/chat-page/components/chat-page-main.tsx b/packages/pl-fe/src/features/chats/components/chat-page/components/chat-page-main.tsx index 64fdecc7e..346f7b68f 100644 --- a/packages/pl-fe/src/features/chats/components/chat-page/components/chat-page-main.tsx +++ b/packages/pl-fe/src/features/chats/components/chat-page/components/chat-page-main.tsx @@ -2,7 +2,6 @@ import React, { useRef } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { Link, useHistory, useParams } from 'react-router-dom'; -import { blockAccount, unblockAccount } from 'pl-fe/actions/accounts'; import DropdownMenu, { type Menu } from 'pl-fe/components/dropdown-menu'; import Avatar from 'pl-fe/components/ui/avatar'; import HStack from 'pl-fe/components/ui/hstack'; @@ -11,9 +10,8 @@ import Stack from 'pl-fe/components/ui/stack'; import Text from 'pl-fe/components/ui/text'; import VerificationBadge from 'pl-fe/components/verification-badge'; import { useChatContext } from 'pl-fe/contexts/chat-context'; -import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useFeatures } from 'pl-fe/hooks/use-features'; -import { useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship'; +import { useBlockAccountMutation, useUnblockAccountMutation, useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship'; import { useChat, useChatActions, useChats } from 'pl-fe/queries/chats'; import { useModalsActions } from 'pl-fe/stores/modals'; @@ -38,7 +36,6 @@ const messages = defineMessages({ }); const ChatPageMain = () => { - const dispatch = useAppDispatch(); const intl = useIntl(); const features = useFeatures(); const history = useHistory(); @@ -50,6 +47,9 @@ const ChatPageMain = () => { const { currentChatId } = useChatContext(); const { chatsQuery: { data: chats, isLoading } } = useChats(); + const { mutate: blockAccount } = useBlockAccountMutation(chat?.account.id!); + const { mutate: unblockAccount } = useUnblockAccountMutation(chat?.account.id!); + const inputRef = useRef(null); const { deleteChat } = useChatActions(chat?.id as string); @@ -62,7 +62,7 @@ const ChatPageMain = () => { message: intl.formatMessage(messages.blockMessage), confirm: intl.formatMessage(messages.blockConfirm), confirmationTheme: 'primary', - onConfirm: () => dispatch(blockAccount(chat?.account.id as string)), + onConfirm: () => blockAccount(), }); }; @@ -72,7 +72,7 @@ const ChatPageMain = () => { message: intl.formatMessage(messages.unblockMessage), confirm: intl.formatMessage(messages.unblockConfirm), confirmationTheme: 'primary', - onConfirm: () => dispatch(unblockAccount(chat?.account.id as string)), + onConfirm: () => unblockAccount(), }); }; diff --git a/packages/pl-fe/src/features/chats/components/chat-widget/chat-settings.tsx b/packages/pl-fe/src/features/chats/components/chat-widget/chat-settings.tsx index f59f74f2d..6d637c89b 100644 --- a/packages/pl-fe/src/features/chats/components/chat-widget/chat-settings.tsx +++ b/packages/pl-fe/src/features/chats/components/chat-widget/chat-settings.tsx @@ -1,16 +1,14 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { blockAccount, unblockAccount } from 'pl-fe/actions/accounts'; import Avatar from 'pl-fe/components/ui/avatar'; import HStack from 'pl-fe/components/ui/hstack'; import Icon from 'pl-fe/components/ui/icon'; import Stack from 'pl-fe/components/ui/stack'; import Text from 'pl-fe/components/ui/text'; import { ChatWidgetScreens, useChatContext } from 'pl-fe/contexts/chat-context'; -import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useFeatures } from 'pl-fe/hooks/use-features'; -import { useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship'; +import { useBlockAccountMutation, useUnblockAccountMutation, useRelationshipQuery } from 'pl-fe/queries/accounts/use-relationship'; import { useChatActions } from 'pl-fe/queries/chats'; import { useModalsActions } from 'pl-fe/stores/modals'; @@ -33,7 +31,6 @@ const messages = defineMessages({ }); const ChatSettings = () => { - const dispatch = useAppDispatch(); const intl = useIntl(); const features = useFeatures(); @@ -41,6 +38,9 @@ const ChatSettings = () => { const { chat, changeScreen, toggleChatPane } = useChatContext(); const { deleteChat } = useChatActions(chat?.id as string); + const { mutate: blockAccount } = useBlockAccountMutation(chat?.account.id!); + const { mutate: unblockAccount } = useUnblockAccountMutation(chat?.account.id!); + const isBlocked = !!useRelationshipQuery(chat?.account.id).data?.blocked_by; const closeSettings = () => { @@ -58,7 +58,7 @@ const ChatSettings = () => { message: intl.formatMessage(messages.blockMessage), confirm: intl.formatMessage(messages.blockConfirm), confirmationTheme: 'primary', - onConfirm: () => dispatch(blockAccount(chat?.account.id as string)), + onConfirm: () => blockAccount(), }); }; @@ -68,7 +68,7 @@ const ChatSettings = () => { message: intl.formatMessage(messages.unblockMessage), confirm: intl.formatMessage(messages.unblockConfirm), confirmationTheme: 'primary', - onConfirm: () => dispatch(unblockAccount(chat?.account.id as string)), + onConfirm: () => unblockAccount(), }); }; diff --git a/packages/pl-fe/src/features/event/components/event-header.tsx b/packages/pl-fe/src/features/event/components/event-header.tsx index 99cb88897..03627ac49 100644 --- a/packages/pl-fe/src/features/event/components/event-header.tsx +++ b/packages/pl-fe/src/features/event/components/event-header.tsx @@ -2,7 +2,6 @@ import React from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import { Link, useHistory } from 'react-router-dom'; -import { blockAccount } from 'pl-fe/actions/accounts'; import { directCompose, mentionCompose, quoteCompose } from 'pl-fe/actions/compose'; import { fetchEventIcs } from 'pl-fe/actions/events'; import { deleteStatusModal, toggleStatusSensitivityModal } from 'pl-fe/actions/moderation'; @@ -21,6 +20,7 @@ import Emojify from 'pl-fe/features/emoji/emojify'; import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useFeatures } from 'pl-fe/hooks/use-features'; import { useOwnAccount } from 'pl-fe/hooks/use-own-account'; +import { useBlockAccountMutation } from 'pl-fe/queries/accounts/use-relationship'; import { useChats } from 'pl-fe/queries/chats'; import { useBookmarkStatus, usePinStatus, useReblogStatus, useUnbookmarkStatus, useUnpinStatus, useUnreblogStatus } from 'pl-fe/queries/statuses/use-status-interactions'; import { useModalsActions } from 'pl-fe/stores/modals'; @@ -95,6 +95,7 @@ const EventHeader: React.FC = ({ status }) => { const { mutate: unbookmarkStatus } = useUnbookmarkStatus(status?.id!); const { mutate: pinStatus } = usePinStatus(status?.id!); const { mutate: unpinStatus } = useUnpinStatus(status?.id!); + const { mutate: blockAccount } = useBlockAccountMutation(status?.account.id!); if (!status || !status.event) { return ( @@ -191,10 +192,10 @@ const EventHeader: React.FC = ({ status }) => { heading: , message: @{account.acct} }} />, confirm: intl.formatMessage(messages.blockConfirm), - onConfirm: () => dispatch(blockAccount(account.id)), + onConfirm: () => blockAccount(), secondary: intl.formatMessage(messages.blockAndReport), onSecondary: () => { - dispatch(blockAccount(account.id)); + blockAccount(); dispatch(initReport(ReportableEntities.STATUS, account, { status })); }, }); diff --git a/packages/pl-fe/src/features/ui/components/action-button.tsx b/packages/pl-fe/src/features/ui/components/action-button.tsx index d8f99498e..0f7c93433 100644 --- a/packages/pl-fe/src/features/ui/components/action-button.tsx +++ b/packages/pl-fe/src/features/ui/components/action-button.tsx @@ -1,21 +1,22 @@ import React from 'react'; import { defineMessages, useIntl } from 'react-intl'; -import { - blockAccount, - unblockAccount, - muteAccount, - unmuteAccount, - biteAccount, -} from 'pl-fe/actions/accounts'; import Button from 'pl-fe/components/ui/button'; import HStack from 'pl-fe/components/ui/hstack'; import Spinner from 'pl-fe/components/ui/spinner'; -import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; +import { useClient } from 'pl-fe/hooks/use-client'; import { useFeatures } from 'pl-fe/hooks/use-features'; import { useLoggedIn } from 'pl-fe/hooks/use-logged-in'; import { useAcceptFollowRequestMutation, useRejectFollowRequestMutation } from 'pl-fe/queries/accounts/use-follow-requests'; -import { useRelationshipQuery, useFollowMutation, useUnfollowMutation } from 'pl-fe/queries/accounts/use-relationship'; +import { + useRelationshipQuery, + useBlockAccountMutation, + useUnblockAccountMutation, + useMuteAccountMutation, + useUnmuteAccountMutation, + useFollowAccountMutation, + useUnfollowAccountMutation, +} from 'pl-fe/queries/accounts/use-relationship'; import { useModalsActions } from 'pl-fe/stores/modals'; import toast from 'pl-fe/toast'; @@ -55,15 +56,19 @@ interface IActionButton { * `actionType` prop. */ const ActionButton: React.FC = ({ account, actionType, small = true }) => { - const dispatch = useAppDispatch(); const features = useFeatures(); const intl = useIntl(); + const client = useClient(); const { openModal } = useModalsActions(); const { isLoggedIn, me } = useLoggedIn(); - const { mutate: follow, isPending: isPendingFollow } = useFollowMutation(account.id); - const { mutate: unfollow, isPending: isPendingUnfollow } = useUnfollowMutation(account.id); + const { mutate: followAccount, isPending: isPendingFollow } = useFollowAccountMutation(account.id); + const { mutate: unfollowAccount, isPending: isPendingUnfollow } = useUnfollowAccountMutation(account.id); + const { mutate: blockAccount } = useBlockAccountMutation(account.id); + const { mutate: unblockAccount } = useUnblockAccountMutation(account.id); + const { mutate: muteAccount } = useMuteAccountMutation(account.id); + const { mutate: unmuteAccount } = useUnmuteAccountMutation(account.id); const { data: relationship, isLoading } = useRelationshipQuery(account.id); @@ -72,25 +77,25 @@ const ActionButton: React.FC = ({ account, actionType, small = tr const handleFollow = () => { if (relationship?.following || relationship?.requested) { - unfollow(); + unfollowAccount(); } else { - follow(undefined); + followAccount(undefined); } }; const handleBlock = () => { if (relationship?.blocking) { - dispatch(unblockAccount(account.id)); + unblockAccount(); } else { - dispatch(blockAccount(account.id)); + blockAccount(); } }; const handleMute = () => { if (relationship?.muting) { - dispatch(unmuteAccount(account.id)); + unmuteAccount(); } else { - dispatch(muteAccount(account.id)); + muteAccount(undefined); } }; @@ -103,7 +108,7 @@ const ActionButton: React.FC = ({ account, actionType, small = tr }; const handleBite = () => { - dispatch(biteAccount(account.id)) + client.accounts.biteAccount(account.id) .then(() => toast.success(intl.formatMessage(messages.userBit, { acct: account.acct }))) .catch(() => toast.error(intl.formatMessage(messages.userBiteFail, { acct: account.acct }))); }; diff --git a/packages/pl-fe/src/features/ui/components/subscription-button.tsx b/packages/pl-fe/src/features/ui/components/subscription-button.tsx index ec7423b6e..4b326ada6 100644 --- a/packages/pl-fe/src/features/ui/components/subscription-button.tsx +++ b/packages/pl-fe/src/features/ui/components/subscription-button.tsx @@ -3,7 +3,7 @@ import { defineMessages, useIntl } from 'react-intl'; import IconButton from 'pl-fe/components/ui/icon-button'; import { useFeatures } from 'pl-fe/hooks/use-features'; -import { useFollowMutation } from 'pl-fe/queries/accounts/use-relationship'; +import { useFollowAccountMutation } from 'pl-fe/queries/accounts/use-relationship'; import toast from 'pl-fe/toast'; import type { Account as AccountEntity } from 'pl-fe/normalizers/account'; @@ -24,7 +24,7 @@ interface ISubscriptionButton { const SubscriptionButton = ({ account }: ISubscriptionButton) => { const features = useFeatures(); const intl = useIntl(); - const { mutate: follow, isPending } = useFollowMutation(account.id); + const { mutate: follow, isPending } = useFollowAccountMutation(account.id); const isFollowing = account.relationship?.following; const isRequested = account.relationship?.requested; diff --git a/packages/pl-fe/src/modals/mute-modal.tsx b/packages/pl-fe/src/modals/mute-modal.tsx index a4bf00a3d..7729cff7b 100644 --- a/packages/pl-fe/src/modals/mute-modal.tsx +++ b/packages/pl-fe/src/modals/mute-modal.tsx @@ -1,7 +1,6 @@ import React, { useState } from 'react'; import { FormattedMessage } from 'react-intl'; -import { muteAccount } from 'pl-fe/actions/accounts'; import { useAccount } from 'pl-fe/api/hooks/accounts/use-account'; import HStack from 'pl-fe/components/ui/hstack'; import Modal from 'pl-fe/components/ui/modal'; @@ -9,8 +8,8 @@ import Stack from 'pl-fe/components/ui/stack'; import Text from 'pl-fe/components/ui/text'; import Toggle from 'pl-fe/components/ui/toggle'; import DurationSelector from 'pl-fe/features/compose/components/polls/duration-selector'; -import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useFeatures } from 'pl-fe/hooks/use-features'; +import { useMuteAccountMutation } from 'pl-fe/queries/accounts/use-relationship'; import type { BaseModalProps } from 'pl-fe/features/ui/components/modal-root'; @@ -19,21 +18,23 @@ interface MuteModalProps { } const MuteModal: React.FC = ({ accountId, onClose }) => { - const dispatch = useAppDispatch(); - const { account } = useAccount(accountId || undefined); const [notifications, setNotifications] = useState(true); const [duration, setDuration] = useState(0); const [isSubmitting, setIsSubmitting] = useState(false); const mutesDuration = useFeatures().mutesDuration; + const { mutate: muteAccount } = useMuteAccountMutation(accountId); + if (!account) return null; const handleClick = () => { setIsSubmitting(true); - dispatch(muteAccount(account.id, notifications, duration))?.then(() => { - setIsSubmitting(false); - onClose('MUTE'); + muteAccount({ notifications, duration }, { + onSuccess: () => { + setIsSubmitting(false); + onClose('MUTE'); + }, }); }; diff --git a/packages/pl-fe/src/modals/report-modal/index.tsx b/packages/pl-fe/src/modals/report-modal/index.tsx index e6d9d73f9..b68d34706 100644 --- a/packages/pl-fe/src/modals/report-modal/index.tsx +++ b/packages/pl-fe/src/modals/report-modal/index.tsx @@ -1,7 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; -import { blockAccount } from 'pl-fe/actions/accounts'; import { submitReport, ReportableEntities } from 'pl-fe/actions/reports'; import { fetchAccountTimeline } from 'pl-fe/actions/timelines'; import { useAccount } from 'pl-fe/api/hooks/accounts/use-account'; @@ -15,6 +14,7 @@ import AccountContainer from 'pl-fe/containers/account-container'; import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useAppSelector } from 'pl-fe/hooks/use-app-selector'; import { useInstance } from 'pl-fe/hooks/use-instance'; +import { useBlockAccountMutation } from 'pl-fe/queries/accounts/use-relationship'; import ConfirmationStep from './steps/confirmation-step'; import OtherActionsStep from './steps/other-actions-step'; @@ -81,6 +81,8 @@ const ReportModal: React.FC = ({ onClose, acc const { account } = useAccount(accountId || undefined); + const { mutate: blockAccount } = useBlockAccountMutation(accountId); + const [block, setBlock] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const { rules } = useInstance(); @@ -109,7 +111,7 @@ const ReportModal: React.FC = ({ onClose, acc }); if (block && account) { - dispatch(blockAccount(account.id)); + blockAccount(); } }; diff --git a/packages/pl-fe/src/pages/dashboard/report.tsx b/packages/pl-fe/src/pages/dashboard/report.tsx index 95d84f60b..abd531c56 100644 --- a/packages/pl-fe/src/pages/dashboard/report.tsx +++ b/packages/pl-fe/src/pages/dashboard/report.tsx @@ -175,7 +175,7 @@ const ReportPage: React.FC = (props) => { - + diff --git a/packages/pl-fe/src/pages/settings/edit-profile.tsx b/packages/pl-fe/src/pages/settings/edit-profile.tsx index d1476081a..b5d471b47 100644 --- a/packages/pl-fe/src/pages/settings/edit-profile.tsx +++ b/packages/pl-fe/src/pages/settings/edit-profile.tsx @@ -3,7 +3,6 @@ import { type CredentialAccount, GOTOSOCIAL } from 'pl-api'; import React, { useState, useEffect } from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; -import { updateNotificationSettings } from 'pl-fe/actions/accounts'; import { patchMe } from 'pl-fe/actions/me'; import BirthdayInput from 'pl-fe/components/birthday-input'; import List, { ListItem } from 'pl-fe/components/list'; @@ -244,9 +243,9 @@ const EditProfilePage: React.FC = () => { if (features.muteStrangers) { promises.push( - dispatch(updateNotificationSettings({ + client.settings.updateNotificationSettings({ block_from_strangers: muteStrangers, - })).catch(console.error), + }).catch(console.error), ); } diff --git a/packages/pl-fe/src/queries/accounts/use-relationship.ts b/packages/pl-fe/src/queries/accounts/use-relationship.ts index 36be78230..347188957 100644 --- a/packages/pl-fe/src/queries/accounts/use-relationship.ts +++ b/packages/pl-fe/src/queries/accounts/use-relationship.ts @@ -1,9 +1,32 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS, type AccountsAction } from 'pl-fe/actions/accounts'; +import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useClient } from 'pl-fe/hooks/use-client'; import { useLoggedIn } from 'pl-fe/hooks/use-logged-in'; -import type { FollowAccountParams, Relationship } from 'pl-api'; +import type { MinifiedSuggestion } from '../trends/use-suggested-accounts'; +import type { FollowAccountParams, MuteAccountParams, Relationship } from 'pl-api'; + +const updateRelationship = (accountId: string, changes: Partial | ((relationship: Relationship) => Relationship), queryClient: ReturnType) => { + const previousRelationship = queryClient.getQueryData(['accountRelationships', accountId]); + if (!previousRelationship) return; + + const newRelationship = typeof changes === 'function' ? changes(previousRelationship) : { ...previousRelationship, ...changes }; + queryClient.setQueryData(['accountRelationships', accountId], newRelationship); + + return { previousRelationship }; +}; + +const restorePreviousRelationship = ( + accountId: string, + context: { previousRelationship?: Relationship } | undefined, + queryClient: ReturnType, +) => { + if (context?.previousRelationship) { + queryClient.setQueryData(['accountRelationships', accountId], context.previousRelationship); + } +}; const useRelationshipQuery = (accountId?: string) => { const client = useClient(); @@ -16,7 +39,7 @@ const useRelationshipQuery = (accountId?: string) => { }); }; -const useFollowMutation = (accountId: string) => { +const useFollowAccountMutation = (accountId: string) => { const client = useClient(); const queryClient = useQueryClient(); @@ -24,64 +47,204 @@ const useFollowMutation = (accountId: string) => { mutationKey: ['accountRelationships', accountId], mutationFn: (params?: FollowAccountParams) => client.accounts.followAccount(accountId, params), onMutate: (params) => { - const previousRelationship = queryClient.getQueryData(['accountRelationships', accountId])!; - - if (!previousRelationship) return; - - const newRelationship: Relationship = { - ...previousRelationship, - requested: !previousRelationship.following, - notifying: params?.notify ?? previousRelationship.notifying, - showing_reblogs: params?.reblogs ?? previousRelationship.showing_reblogs, - }; - - queryClient.setQueryData(['accountRelationships', accountId], newRelationship); - - return { previousRelationship }; - }, - onError: (_err, _variables, context) => { - if (context?.previousRelationship) { - queryClient.setQueryData(['accountRelationships', accountId], context.previousRelationship); - } + return updateRelationship(accountId, (relationship) => ({ + ...relationship, + requested: !relationship.following, + notifying: params?.notify ?? relationship.notifying, + showing_reblogs: params?.reblogs ?? relationship.showing_reblogs, + }), queryClient); }, + onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient), onSuccess: (data) => { queryClient.setQueryData(['accountRelationships', accountId], data); }, }); }; -const useUnfollowMutation = (accountId: string) => { +const useUnfollowAccountMutation = (accountId: string) => { const client = useClient(); const queryClient = useQueryClient(); return useMutation({ mutationKey: ['accountRelationships', accountId], mutationFn: () => client.accounts.unfollowAccount(accountId), - onMutate: () => { - const previousRelationship = queryClient.getQueryData(['accountRelationships', accountId])!; - - if (!previousRelationship) return; - const newRelationship: Relationship = { - ...previousRelationship, - following: false, - requested: false, - notifying: false, - showing_reblogs: false, - }; - - queryClient.setQueryData(['accountRelationships', accountId], newRelationship); - - return { previousRelationship }; - }, - onError: (_err, _variables, context) => { - if (context?.previousRelationship) { - queryClient.setQueryData(['accountRelationships', accountId], context.previousRelationship); - } - }, + onMutate: () => updateRelationship(accountId, { + following: false, + requested: false, + notifying: false, + showing_reblogs: false, + }, queryClient), + onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient), onSuccess: (data) => { queryClient.setQueryData(['accountRelationships', accountId], data); }, }); }; -export { useRelationshipQuery, useFollowMutation, useUnfollowMutation }; +const useBlockAccountMutation = (accountId: string) => { + const client = useClient(); + const queryClient = useQueryClient(); + const dispatch = useAppDispatch(); + + return useMutation({ + mutationKey: ['accountRelationships', accountId], + mutationFn: () => client.filtering.blockAccount(accountId), + onMutate: () => updateRelationship(accountId, { + blocking: true, + followed_by: false, + following: false, + notifying: false, + requested: false, + }, queryClient), + onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient), + onSuccess: (data) => { + queryClient.setQueryData(['accountRelationships', accountId], data); + + queryClient.setQueryData>(['suggestions'], suggestions => suggestions + ? suggestions.filter((suggestion) => suggestion.account_id !== accountId) + : undefined); + + // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers + return dispatch((dispatch, getState) => dispatch({ + type: ACCOUNT_BLOCK_SUCCESS, + relationship: data, + statuses: getState().statuses, + })); + }, + }); +}; + +const useUnblockAccountMutation = (accountId: string) => { + const client = useClient(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationKey: ['accountRelationships', accountId], + mutationFn: () => client.filtering.unblockAccount(accountId), + onMutate: () => updateRelationship(accountId, { + blocking: true, + }, queryClient), + onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient), + onSuccess: (data) => { + queryClient.setQueryData(['accountRelationships', accountId], data); + }, + }); +}; + +const useMuteAccountMutation = (accountId: string) => { + const client = useClient(); + const queryClient = useQueryClient(); + const dispatch = useAppDispatch(); + + return useMutation({ + mutationKey: ['accountRelationships', accountId], + mutationFn: (params?: MuteAccountParams) => client.filtering.muteAccount(accountId, params), + onMutate: () => updateRelationship(accountId, { + muting: true, + }, queryClient), + onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient), + onSuccess: (data) => { + queryClient.setQueryData(['accountRelationships', accountId], data); + + queryClient.setQueryData>(['suggestions'], suggestions => suggestions + ? suggestions.filter((suggestion) => suggestion.account_id !== accountId) + : undefined); + + // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers + return dispatch((dispatch, getState) => dispatch({ + type: ACCOUNT_MUTE_SUCCESS, + relationship: data, + statuses: getState().statuses, + })); + }, + }); +}; + +const useUnmuteAccountMutation = (accountId: string) => { + const client = useClient(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationKey: ['accountRelationships', accountId], + mutationFn: () => client.filtering.unmuteAccount(accountId), + onMutate: () => updateRelationship(accountId, { + muting: false, + }, queryClient), + onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient), + onSuccess: (data) => { + queryClient.setQueryData(['accountRelationships', accountId], data); + }, + }); +}; + +const usePinAccountMutation = (accountId: string) => { + const client = useClient(); + const queryClient = useQueryClient(); + const { me } = useLoggedIn(); + + return useMutation({ + mutationKey: ['accountRelationships', accountId], + mutationFn: () => client.accounts.pinAccount(accountId), + onMutate: () => updateRelationship(accountId, { + endorsed: true, + }, queryClient), + onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient), + onSuccess: (data) => { + queryClient.setQueryData(['accountRelationships', accountId], data); + queryClient.invalidateQueries({ + queryKey: ['accountsLists', 'endorsedAccounts', me], + }); + }, + }); +}; + +const useUnpinAccountMutation = (accountId: string) => { + const client = useClient(); + const queryClient = useQueryClient(); + const { me } = useLoggedIn(); + + return useMutation({ + mutationKey: ['accountRelationships', accountId], + mutationFn: () => client.accounts.unpinAccount(accountId), + onMutate: () => updateRelationship(accountId, { + endorsed: false, + }, queryClient), + onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient), + onSuccess: (data) => { + queryClient.setQueryData(['accountRelationships', accountId], data); + queryClient.invalidateQueries({ + queryKey: ['accountsLists', 'endorsedAccounts', me], + }); + }, + }); +}; + +const useRemoveAccountFromFollowersMutation = (accountId: string) => { + const client = useClient(); + const queryClient = useQueryClient(); + + return useMutation({ + mutationKey: ['accountRelationships', accountId], + mutationFn: () => client.accounts.removeAccountFromFollowers(accountId), + onMutate: () => updateRelationship(accountId, { + followed_by: false, + }, queryClient), + onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient), + onSuccess: (data) => { + queryClient.setQueryData(['accountRelationships', accountId], data); + }, + }); +}; + +export { + useRelationshipQuery, + useFollowAccountMutation, + useUnfollowAccountMutation, + useBlockAccountMutation, + useUnblockAccountMutation, + useMuteAccountMutation, + useUnmuteAccountMutation, + usePinAccountMutation, + useUnpinAccountMutation, + useRemoveAccountFromFollowersMutation, +};