wip
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
ded94fda08
commit
00ea9e3cdf
9 changed files with 245 additions and 0 deletions
|
@ -1,2 +1,6 @@
|
||||||
|
export { useCreateDomain, CreateDomainParams } from './useCreateDomain';
|
||||||
|
export { useDeleteDomain } from './useDeleteDomain';
|
||||||
|
export { useDomains } from './useDomains';
|
||||||
export { useSuggest } from './useSuggest';
|
export { useSuggest } from './useSuggest';
|
||||||
|
export { useUpdateDomain } from './useUpdateDomain';
|
||||||
export { useVerify } from './useVerify';
|
export { useVerify } from './useVerify';
|
27
src/api/hooks/admin/useCreateDomain.ts
Normal file
27
src/api/hooks/admin/useCreateDomain.ts
Normal file
|
@ -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 };
|
26
src/api/hooks/admin/useDeleteDomain.ts
Normal file
26
src/api/hooks/admin/useDeleteDomain.ts
Normal file
|
@ -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 };
|
25
src/api/hooks/admin/useDomains.ts
Normal file
25
src/api/hooks/admin/useDomains.ts
Normal file
|
@ -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<any[]>('/api/v1/pleroma/admin/domains');
|
||||||
|
|
||||||
|
const normalizedData = data.map((domain) => domainSchema.parse(domain));
|
||||||
|
return normalizedData;
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = useQuery<ReadonlyArray<Domain>>({
|
||||||
|
queryKey: ['domains'],
|
||||||
|
queryFn: getDomains,
|
||||||
|
placeholderData: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { useDomains };
|
24
src/api/hooks/admin/useUpdateDomain.ts
Normal file
24
src/api/hooks/admin/useUpdateDomain.ts
Normal file
|
@ -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 };
|
|
@ -2,6 +2,7 @@ import type * as Schemas from 'soapbox/schemas';
|
||||||
|
|
||||||
enum Entities {
|
enum Entities {
|
||||||
ACCOUNTS = 'Accounts',
|
ACCOUNTS = 'Accounts',
|
||||||
|
DOMAINS = 'Domains',
|
||||||
GROUPS = 'Groups',
|
GROUPS = 'Groups',
|
||||||
GROUP_MEMBERSHIPS = 'GroupMemberships',
|
GROUP_MEMBERSHIPS = 'GroupMemberships',
|
||||||
GROUP_MUTES = 'GroupMutes',
|
GROUP_MUTES = 'GroupMutes',
|
||||||
|
@ -14,6 +15,7 @@ enum Entities {
|
||||||
|
|
||||||
interface EntityTypes {
|
interface EntityTypes {
|
||||||
[Entities.ACCOUNTS]: Schemas.Account;
|
[Entities.ACCOUNTS]: Schemas.Account;
|
||||||
|
[Entities.DOMAINS]: Schemas.Domain;
|
||||||
[Entities.GROUPS]: Schemas.Group;
|
[Entities.GROUPS]: Schemas.Group;
|
||||||
[Entities.GROUP_MEMBERSHIPS]: Schemas.GroupMember;
|
[Entities.GROUP_MEMBERSHIPS]: Schemas.GroupMember;
|
||||||
[Entities.GROUP_RELATIONSHIPS]: Schemas.GroupRelationship;
|
[Entities.GROUP_RELATIONSHIPS]: Schemas.GroupRelationship;
|
||||||
|
|
125
src/features/admin/domains.tsx
Normal file
125
src/features/admin/domains.tsx
Normal file
|
@ -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<IDomain> = ({ 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 (
|
||||||
|
// <div key={announcement.id} className='rounded-lg bg-gray-100 p-4 dark:bg-primary-800'>
|
||||||
|
// <Stack space={2}>
|
||||||
|
// <Text dangerouslySetInnerHTML={{ __html: announcement.contentHtml }} />
|
||||||
|
// {(announcement.starts_at || announcement.ends_at || announcement.all_day) && (
|
||||||
|
// <HStack space={2} wrap>
|
||||||
|
// {announcement.starts_at && (
|
||||||
|
// <Text size='sm'>
|
||||||
|
// <Text tag='span' size='sm' weight='medium'>
|
||||||
|
// <FormattedMessage id='admin.announcements.starts_at' defaultMessage='Starts at:' />
|
||||||
|
// </Text>
|
||||||
|
// {' '}
|
||||||
|
// <FormattedDate value={announcement.starts_at} year='2-digit' month='short' day='2-digit' weekday='short' />
|
||||||
|
// </Text>
|
||||||
|
// )}
|
||||||
|
// {announcement.ends_at && (
|
||||||
|
// <Text size='sm'>
|
||||||
|
// <Text tag='span' size='sm' weight='medium'>
|
||||||
|
// <FormattedMessage id='admin.announcements.ends_at' defaultMessage='Ends at:' />
|
||||||
|
// </Text>
|
||||||
|
// {' '}
|
||||||
|
// <FormattedDate value={announcement.ends_at} year='2-digit' month='short' day='2-digit' weekday='short' />
|
||||||
|
// </Text>
|
||||||
|
// )}
|
||||||
|
// {announcement.all_day && (
|
||||||
|
// <Text weight='medium' size='sm'>
|
||||||
|
// <FormattedMessage id='admin.announcements.all_day' defaultMessage='All day' />
|
||||||
|
// </Text>
|
||||||
|
// )}
|
||||||
|
// </HStack>
|
||||||
|
// )}
|
||||||
|
// <HStack justifyContent='end' space={2}>
|
||||||
|
// <Button theme='primary' onClick={handleEditAnnouncement(announcement)}>
|
||||||
|
// <FormattedMessage id='admin.announcements.edit' defaultMessage='Edit' />
|
||||||
|
// </Button>
|
||||||
|
// <Button theme='primary' onClick={handleDeleteAnnouncement(announcement.id)}>
|
||||||
|
// <FormattedMessage id='admin.announcements.delete' defaultMessage='Delete' />
|
||||||
|
// </Button>
|
||||||
|
// </HStack>
|
||||||
|
// </Stack>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
const Domains: React.FC = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const { data: domains, isFetching } = useDomains();
|
||||||
|
|
||||||
|
// const handleCreateAnnouncement = () => {
|
||||||
|
// dispatch(initAnnouncementModal());
|
||||||
|
// };
|
||||||
|
|
||||||
|
const emptyMessage = <FormattedMessage id='empty_column.admin.domains' defaultMessage='There are no domains yet.' />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column label={intl.formatMessage(messages.heading)}>
|
||||||
|
<Stack className='gap-4'>
|
||||||
|
{/* <Button
|
||||||
|
className='sm:w-fit sm:self-end'
|
||||||
|
icon={require('@tabler/icons/plus.svg')}
|
||||||
|
onClick={handleCreateDomain}
|
||||||
|
theme='secondary'
|
||||||
|
block
|
||||||
|
>
|
||||||
|
<FormattedMessage id='admin.domains.action' defaultMessage='Create domain' />
|
||||||
|
</Button> */}
|
||||||
|
<ScrollableList
|
||||||
|
scrollKey='domains'
|
||||||
|
emptyMessage={emptyMessage}
|
||||||
|
itemClassName='py-3 first:pt-0 last:pb-0'
|
||||||
|
isLoading={isFetching}
|
||||||
|
showLoading={isFetching && !domains?.length}
|
||||||
|
>
|
||||||
|
{domains?.map((domain) => (
|
||||||
|
<Domain key={domain.id} domain={domain} />
|
||||||
|
))}
|
||||||
|
</ScrollableList>
|
||||||
|
</Stack>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Domain;
|
11
src/schemas/domain.ts
Normal file
11
src/schemas/domain.ts
Normal file
|
@ -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<typeof domainSchema>
|
||||||
|
|
||||||
|
export { domainSchema, type Domain };
|
|
@ -3,6 +3,7 @@ export { attachmentSchema, type Attachment } from './attachment';
|
||||||
export { cardSchema, type Card } from './card';
|
export { cardSchema, type Card } from './card';
|
||||||
export { chatMessageSchema, type ChatMessage } from './chat-message';
|
export { chatMessageSchema, type ChatMessage } from './chat-message';
|
||||||
export { customEmojiSchema, type CustomEmoji } from './custom-emoji';
|
export { customEmojiSchema, type CustomEmoji } from './custom-emoji';
|
||||||
|
export { domainSchema, type Domain } from './domain';
|
||||||
export { emojiReactionSchema, type EmojiReaction } from './emoji-reaction';
|
export { emojiReactionSchema, type EmojiReaction } from './emoji-reaction';
|
||||||
export { groupSchema, type Group } from './group';
|
export { groupSchema, type Group } from './group';
|
||||||
export { groupMemberSchema, type GroupMember } from './group-member';
|
export { groupMemberSchema, type GroupMember } from './group-member';
|
||||||
|
|
Loading…
Reference in a new issue