diff --git a/src/api/hooks/admin/index.ts b/src/api/hooks/admin/index.ts index c34853340..0ca5e7514 100644 --- a/src/api/hooks/admin/index.ts +++ b/src/api/hooks/admin/index.ts @@ -1,11 +1,6 @@ -export { useCreateDomain, type CreateDomainParams } from './useCreateDomain'; -export { useCreateRelay } from './useCreateRelay'; -export { useDeleteDomain } from './useDeleteDomain'; -export { useDeleteRelay } from './useDeleteRelay'; export { useDomains } from './useDomains'; export { useModerationLog } from './useModerationLog'; export { useRelays } from './useRelays'; export { useRules } from './useRules'; export { useSuggest } from './useSuggest'; -export { useUpdateDomain } from './useUpdateDomain'; export { useVerify } from './useVerify'; \ No newline at end of file diff --git a/src/api/hooks/admin/useCreateRelay.ts b/src/api/hooks/admin/useCreateRelay.ts deleted file mode 100644 index d29f15a99..000000000 --- a/src/api/hooks/admin/useCreateRelay.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Entities } from 'soapbox/entity-store/entities'; -import { useCreateEntity } from 'soapbox/entity-store/hooks'; -import { useApi } from 'soapbox/hooks'; -import { relaySchema } from 'soapbox/schemas'; - -const useCreateRelay = () => { - const api = useApi(); - - const { createEntity, ...rest } = useCreateEntity([Entities.RELAYS], (relayUrl: string) => - api.post('/api/v1/pleroma/admin/relay', { relay_url: relayUrl }), { schema: relaySchema }); - - return { - createRelay: createEntity, - ...rest, - }; -}; - -export { useCreateRelay }; diff --git a/src/api/hooks/admin/useDeleteRelay.ts b/src/api/hooks/admin/useDeleteRelay.ts deleted file mode 100644 index b7f284025..000000000 --- a/src/api/hooks/admin/useDeleteRelay.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Entities } from 'soapbox/entity-store/entities'; -import { useDeleteEntity } from 'soapbox/entity-store/hooks'; -import { useApi } from 'soapbox/hooks'; - -const useDeleteRelay = () => { - const api = useApi(); - - const { deleteEntity, ...rest } = useDeleteEntity(Entities.RELAYS, (relayUrl: string) => - api.delete('/api/v1/pleroma/admin/relay', { - data: { relay_url: relayUrl }, - })); - - return { - mutate: deleteEntity, - ...rest, - }; -}; - -export { useDeleteRelay }; diff --git a/src/api/hooks/admin/useDomains.ts b/src/api/hooks/admin/useDomains.ts index 998edca46..aed6afe7c 100644 --- a/src/api/hooks/admin/useDomains.ts +++ b/src/api/hooks/admin/useDomains.ts @@ -1,8 +1,21 @@ -import { useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { useApi } from 'soapbox/hooks'; +import { queryClient } from 'soapbox/queries/client'; import { domainSchema, type Domain } from 'soapbox/schemas'; +import type { AxiosResponse } from 'axios'; + +interface CreateDomainParams { + domain: string; + public: boolean; +} + +interface UpdateDomainParams { + id: string; + public: boolean; +} + const useDomains = () => { const api = useApi(); @@ -14,12 +27,56 @@ const useDomains = () => { }; const result = useQuery>({ - queryKey: ['domains'], + queryKey: ['admin', 'domains'], queryFn: getDomains, placeholderData: [], }); - return result; + const { + mutate: createDomain, + isPending: isCreating, + } = useMutation({ + mutationFn: (params: CreateDomainParams) => api.post('/api/v1/pleroma/admin/domains', params), + retry: false, + onSuccess: ({ data }: AxiosResponse) => + queryClient.setQueryData(['admin', 'domains'], (prevResult: ReadonlyArray) => + [...prevResult, domainSchema.parse(data)], + ), + }); + + const { + mutate: updateDomain, + isPending: isUpdating, + } = useMutation({ + mutationFn: ({ id, ...params }: UpdateDomainParams) => api.patch(`/api/v1/pleroma/admin/domains/${id}`, params), + retry: false, + onSuccess: ({ data }: AxiosResponse) => + queryClient.setQueryData(['admin', 'domains'], (prevResult: ReadonlyArray) => + prevResult.map((domain) => domain.id === data.id ? domainSchema.parse(data) : domain), + ), + }); + + const { + mutate: deleteDomain, + isPending: isDeleting, + } = useMutation({ + mutationFn: (id: string) => api.delete(`/api/v1/pleroma/admin/domains/${id}`), + retry: false, + onSuccess: (_, id) => + queryClient.setQueryData(['admin', 'domains'], (prevResult: ReadonlyArray) => + prevResult.filter(({ id: domainId }) => domainId !== id), + ), + }); + + return { + ...result, + createDomain, + isCreating, + updateDomain, + isUpdating, + deleteDomain, + isDeleting, + }; }; export { useDomains }; diff --git a/src/api/hooks/admin/useModerationLog.ts b/src/api/hooks/admin/useModerationLog.ts index 8284c9b52..e87d0b1c3 100644 --- a/src/api/hooks/admin/useModerationLog.ts +++ b/src/api/hooks/admin/useModerationLog.ts @@ -25,7 +25,7 @@ const useModerationLog = () => { }; const queryInfo = useInfiniteQuery({ - queryKey: ['moderation_log'], + queryKey: ['admin', 'moderation_log'], queryFn: ({ pageParam }) => getModerationLog(pageParam), initialPageParam: 1, getNextPageParam: (page, allPages) => flattenPages(allPages)!.length >= page.total ? undefined : allPages.length + 1, diff --git a/src/api/hooks/admin/useRelays.ts b/src/api/hooks/admin/useRelays.ts index b6a0b1e26..ff1d1ecce 100644 --- a/src/api/hooks/admin/useRelays.ts +++ b/src/api/hooks/admin/useRelays.ts @@ -1,8 +1,11 @@ -import { useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { useApi } from 'soapbox/hooks'; +import { queryClient } from 'soapbox/queries/client'; import { relaySchema, type Relay } from 'soapbox/schemas'; +import type { AxiosResponse } from 'axios'; + const useRelays = () => { const api = useApi(); @@ -14,12 +17,44 @@ const useRelays = () => { }; const result = useQuery>({ - queryKey: ['relays'], + queryKey: ['admin', 'relays'], queryFn: getRelays, placeholderData: [], }); - return result; + const { + mutate: followRelay, + isPending: isPendingFollow, + } = useMutation({ + mutationFn: (relayUrl: string) => api.post('/api/v1/pleroma/admin/relays', { relay_url: relayUrl }), + retry: false, + onSuccess: ({ data }: AxiosResponse) => + queryClient.setQueryData(['admin', 'relays'], (prevResult: ReadonlyArray) => + [...prevResult, relaySchema.parse(data)], + ), + }); + + const { + mutate: unfollowRelay, + isPending: isPendingUnfollow, + } = useMutation({ + mutationFn: (relayUrl: string) => api.delete('/api/v1/pleroma/admin/relays', { + data: { relay_url: relayUrl }, + }), + retry: false, + onSuccess: (_, relayUrl) => + queryClient.setQueryData(['admin', 'relays'], (prevResult: ReadonlyArray) => + prevResult.filter(({ actor }) => actor !== relayUrl), + ), + }); + + return { + ...result, + followRelay, + isPendingFollow, + unfollowRelay, + isPendingUnfollow, + }; }; export { useRelays }; diff --git a/src/api/hooks/admin/useRules.ts b/src/api/hooks/admin/useRules.ts index a99c01935..87971e8fb 100644 --- a/src/api/hooks/admin/useRules.ts +++ b/src/api/hooks/admin/useRules.ts @@ -1,10 +1,11 @@ import { useMutation, useQuery } from '@tanstack/react-query'; -import { AxiosResponse } from 'axios'; import { useApi } from 'soapbox/hooks'; import { queryClient } from 'soapbox/queries/client'; import { adminRuleSchema, type AdminRule } from 'soapbox/schemas'; +import type { AxiosResponse } from 'axios'; + interface CreateRuleParams { priority?: number; text: string; diff --git a/src/api/index.ts b/src/api/index.ts index f71676145..dda1fa508 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -4,7 +4,7 @@ * @module soapbox/api */ -import axios, { AxiosInstance, AxiosResponse } from 'axios'; +import axios, { type AxiosInstance, type AxiosResponse } from 'axios'; import LinkHeader from 'http-link-header'; import { createSelector } from 'reselect'; diff --git a/src/features/admin/domains.tsx b/src/features/admin/domains.tsx index 923639259..e2455f3f8 100644 --- a/src/features/admin/domains.tsx +++ b/src/features/admin/domains.tsx @@ -1,8 +1,8 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { openModal } from 'soapbox/actions/modals'; -import { useDeleteDomain, useDomains } from 'soapbox/api/hooks/admin'; +import { useDomains } from 'soapbox/api/hooks/admin'; import { dateFormatOptions } from 'soapbox/components/relative-timestamp'; import ScrollableList from 'soapbox/components/scrollable-list'; import { Button, Column, HStack, Stack, Text } from 'soapbox/components/ui'; @@ -30,8 +30,7 @@ const Domain: React.FC = ({ domain }) => { const intl = useIntl(); const dispatch = useAppDispatch(); - const { mutate: deleteDomain } = useDeleteDomain(); - const { refetch } = useDomains(); + const { deleteDomain } = useDomains(); const handleEditDomain = (domain: DomainEntity) => () => { dispatch(openModal('EDIT_DOMAIN', { domainId: domain.id })); @@ -43,10 +42,11 @@ const Domain: React.FC = ({ domain }) => { message: intl.formatMessage(messages.deleteMessage), confirm: intl.formatMessage(messages.deleteConfirm), onConfirm: () => { - deleteDomain(domain.id).then(() => { - toast.success(messages.domainDeleteSuccess); - refetch(); - }).catch(() => {}); + deleteDomain(domain.id, { + onSuccess: () => { + toast.success(messages.domainDeleteSuccess); + }, + }); }, })); }; @@ -103,12 +103,16 @@ const Domains: React.FC = () => { const intl = useIntl(); const dispatch = useAppDispatch(); - const { data: domains, isFetching } = useDomains(); + const { data: domains, isFetching, refetch } = useDomains(); const handleCreateDomain = () => { dispatch(openModal('EDIT_DOMAIN')); }; + useEffect(() => { + if (!isFetching) refetch(); + }, []); + const emptyMessage = ; return ( diff --git a/src/features/admin/relays.tsx b/src/features/admin/relays.tsx index 11645c1ba..e3d06a9ba 100644 --- a/src/features/admin/relays.tsx +++ b/src/features/admin/relays.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; -import { useCreateRelay, useDeleteRelay, useRelays } from 'soapbox/api/hooks/admin'; +import { useRelays } from 'soapbox/api/hooks/admin'; import ScrollableList from 'soapbox/components/scrollable-list'; import { Button, Column, Form, HStack, Input, Stack, Text } from 'soapbox/components/ui'; import { useTextField } from 'soapbox/hooks/forms'; @@ -22,14 +22,14 @@ interface IRelay { } const Relay: React.FC = ({ relay }) => { - const { mutate: deleteRelay } = useDeleteRelay(); - const { refetch } = useRelays(); + const { unfollowRelay } = useRelays(); const handleDeleteRelay = () => () => { - deleteRelay(relay.actor).then(() => { - refetch(); - toast.success(messages.relayDeleteSuccess); - }).catch(() => {}); + unfollowRelay(relay.actor, { + onSuccess: () => { + toast.success(messages.relayDeleteSuccess); + }, + }); }; return ( @@ -64,18 +64,16 @@ const NewRelayForm: React.FC = () => { const name = useTextField(); - const { createRelay, isSubmitting } = useCreateRelay(); - const { refetch } = useRelays(); + const { followRelay, isPendingFollow } = useRelays(); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - createRelay(name.value, { + followRelay(name.value, { onSuccess() { toast.success(messages.createSuccess); - refetch(); }, onError() { - toast.success(messages.createFail); + toast.error(messages.createFail); }, }); }; @@ -91,13 +89,13 @@ const NewRelayForm: React.FC = () => {