diff --git a/src/api/hooks/admin/index.ts b/src/api/hooks/admin/index.ts index ef4dc082d..3103666e3 100644 --- a/src/api/hooks/admin/index.ts +++ b/src/api/hooks/admin/index.ts @@ -1,2 +1,6 @@ +export { useCreateDomain, CreateDomainParams } from './useCreateDomain'; +export { useDeleteDomain } from './useDeleteDomain'; +export { useDomains } from './useDomains'; 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/useCreateDomain.ts b/src/api/hooks/admin/useCreateDomain.ts new file mode 100644 index 000000000..8d1aed2dc --- /dev/null +++ b/src/api/hooks/admin/useCreateDomain.ts @@ -0,0 +1,27 @@ +import { Entities } from 'soapbox/entity-store/entities'; +import { useCreateEntity } from 'soapbox/entity-store/hooks'; +import { useApi } from 'soapbox/hooks'; +import { domainSchema } from 'soapbox/schemas'; + +interface CreateDomainParams { + domain: string; + public: boolean; +} + +const useCreateDomain = () => { + const api = useApi(); + + const { createEntity, ...rest } = useCreateEntity([Entities.DOMAINS], (params: CreateDomainParams) => + api.post('/api/v1/pleroma/admin/domains', params, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }), { schema: domainSchema }); + + return { + createDomain: createEntity, + ...rest, + }; +}; + +export { useCreateDomain, type CreateDomainParams }; diff --git a/src/api/hooks/admin/useDeleteDomain.ts b/src/api/hooks/admin/useDeleteDomain.ts new file mode 100644 index 000000000..3975209e0 --- /dev/null +++ b/src/api/hooks/admin/useDeleteDomain.ts @@ -0,0 +1,26 @@ +import { Entities } from 'soapbox/entity-store/entities'; +import { useDeleteEntity } from 'soapbox/entity-store/hooks'; +import { useApi } from 'soapbox/hooks'; + +interface DeleteDomainParams { + domain: string; + public: boolean; +} + +const useDeleteDomain = () => { + const api = useApi(); + + const { deleteEntity, ...rest } = useDeleteEntity(Entities.DOMAINS, (id: string) => + api.delete(`/api/v1/pleroma/admin/domains/${id}`, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + })); + + return { + mutate: deleteEntity, + ...rest, + }; +}; + +export { useDeleteDomain, type DeleteDomainParams }; diff --git a/src/api/hooks/admin/useDomains.ts b/src/api/hooks/admin/useDomains.ts new file mode 100644 index 000000000..aec7775f6 --- /dev/null +++ b/src/api/hooks/admin/useDomains.ts @@ -0,0 +1,25 @@ +import { useQuery } from '@tanstack/react-query'; + +import { useApi } from 'soapbox/hooks'; +import { domainSchema, type Domain } from 'soapbox/schemas'; + +const useDomains = () => { + const api = useApi(); + + const getDomains = async () => { + const { data } = await api.get('/api/v1/pleroma/admin/domains'); + + const normalizedData = data.map((domain) => domainSchema.parse(domain)); + return normalizedData; + }; + + const result = useQuery>({ + queryKey: ['domains'], + queryFn: getDomains, + placeholderData: [], + }); + + return result; +}; + +export { useDomains }; diff --git a/src/api/hooks/admin/useUpdateDomain.ts b/src/api/hooks/admin/useUpdateDomain.ts new file mode 100644 index 000000000..f64e510f6 --- /dev/null +++ b/src/api/hooks/admin/useUpdateDomain.ts @@ -0,0 +1,24 @@ +import { Entities } from 'soapbox/entity-store/entities'; +import { useCreateEntity } from 'soapbox/entity-store/hooks'; +import { useApi } from 'soapbox/hooks'; +import { domainSchema } from 'soapbox/schemas'; + +import type { CreateDomainParams } from '.'; + +const useUpdateDomain = (id: string) => { + const api = useApi(); + + const { createEntity, ...rest } = useCreateEntity([Entities.DOMAINS], (params: CreateDomainParams) => + api.patch(`/api/v1/pleroma/admin/domains/${id}`, params, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }), { schema: domainSchema }); + + return { + updateDomain: createEntity, + ...rest, + }; +}; + +export { useUpdateDomain }; diff --git a/src/entity-store/entities.ts b/src/entity-store/entities.ts index f81239dca..f3f882026 100644 --- a/src/entity-store/entities.ts +++ b/src/entity-store/entities.ts @@ -2,6 +2,7 @@ import type * as Schemas from 'soapbox/schemas'; enum Entities { ACCOUNTS = 'Accounts', + DOMAINS = 'Domains', GROUPS = 'Groups', GROUP_MEMBERSHIPS = 'GroupMemberships', GROUP_MUTES = 'GroupMutes', @@ -14,6 +15,7 @@ enum Entities { interface EntityTypes { [Entities.ACCOUNTS]: Schemas.Account; + [Entities.DOMAINS]: Schemas.Domain; [Entities.GROUPS]: Schemas.Group; [Entities.GROUP_MEMBERSHIPS]: Schemas.GroupMember; [Entities.GROUP_RELATIONSHIPS]: Schemas.GroupRelationship; diff --git a/src/features/admin/domains.tsx b/src/features/admin/domains.tsx new file mode 100644 index 000000000..40c82431c --- /dev/null +++ b/src/features/admin/domains.tsx @@ -0,0 +1,125 @@ +import React, { useEffect } from 'react'; +import { FormattedDate, FormattedMessage, defineMessages, useIntl } from 'react-intl'; + +import { deleteAnnouncement, fetchAdminAnnouncements, initAnnouncementModal } from 'soapbox/actions/admin'; +import { openModal } from 'soapbox/actions/modals'; +import { useDomains } from 'soapbox/api/hooks/admin'; +import ScrollableList from 'soapbox/components/scrollable-list'; +import { Button, Column, HStack, Stack, Text } from 'soapbox/components/ui'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import type { Domain } from 'soapbox/schemas'; +import { Announcement as AnnouncementEntity } from 'soapbox/types/entities'; + +const messages = defineMessages({ + heading: { id: 'column.admin.domains', defaultMessage: 'Domains' }, + deleteConfirm: { id: 'confirmations.admin.delete_domain.confirm', defaultMessage: 'Delete' }, + deleteHeading: { id: 'confirmations.admin.delete_domain.heading', defaultMessage: 'Delete domain' }, + deleteMessage: { id: 'confirmations.admin.delete_domain.message', defaultMessage: 'Are you sure you want to delete the domain?' }, +}); + +interface IDomain { + domain: Domain; +} + +// const Domain: React.FC = ({ domain }) => { +// const intl = useIntl(); +// const dispatch = useAppDispatch(); + +// const handleEditDomain = (domain: Domain) => () => { +// dispatch(initDomainModal(domain)); +// }; + +// const handleDeleteDomain = (id: string) => () => { +// dispatch(openModal('CONFIRM', { +// heading: intl.formatMessage(messages.deleteHeading), +// message: intl.formatMessage(messages.deleteMessage), +// confirm: intl.formatMessage(messages.deleteConfirm), +// onConfirm: () => dispatch(deleteDomain(id)), +// })); +// }; + +// return ( +//
+// +// +// {(announcement.starts_at || announcement.ends_at || announcement.all_day) && ( +// +// {announcement.starts_at && ( +// +// +// +// +// {' '} +// +// +// )} +// {announcement.ends_at && ( +// +// +// +// +// {' '} +// +// +// )} +// {announcement.all_day && ( +// +// +// +// )} +// +// )} +// +// +// +// +// +//
+// ); +// }; + +const Domains: React.FC = () => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + + const { data: domains, isFetching } = useDomains(); + + // const handleCreateAnnouncement = () => { + // dispatch(initAnnouncementModal()); + // }; + + const emptyMessage = ; + + return ( + + + {/* */} + + {domains?.map((domain) => ( + + ))} + + + + ); +}; + +export default Domain; diff --git a/src/schemas/domain.ts b/src/schemas/domain.ts new file mode 100644 index 000000000..beea77108 --- /dev/null +++ b/src/schemas/domain.ts @@ -0,0 +1,11 @@ +import z from 'zod'; + +const domainSchema = z.object({ + domain: z.string().catch(''), + id: z.coerce.string(), + public: z.boolean().catch(false), +}); + +type Domain = z.infer + +export { domainSchema, type Domain }; diff --git a/src/schemas/index.ts b/src/schemas/index.ts index 2fd3cc3de..e68d423d4 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -3,6 +3,7 @@ export { attachmentSchema, type Attachment } from './attachment'; export { cardSchema, type Card } from './card'; export { chatMessageSchema, type ChatMessage } from './chat-message'; export { customEmojiSchema, type CustomEmoji } from './custom-emoji'; +export { domainSchema, type Domain } from './domain'; export { emojiReactionSchema, type EmojiReaction } from './emoji-reaction'; export { groupSchema, type Group } from './group'; export { groupMemberSchema, type GroupMember } from './group-member';