Groups: make authorize/reject use component state, update designs

This commit is contained in:
Alex Gleason
2023-03-20 17:46:10 -05:00
parent 1d9ed41fec
commit 7c7855e7a1
3 changed files with 54 additions and 36 deletions

View File

@ -24,7 +24,7 @@ const GroupMembers: React.FC<IGroupMembers> = (props) => {
const { groupMembers: owners, isFetching: isFetchingOwners } = useGroupMembers(groupId, GroupRoles.OWNER);
const { groupMembers: admins, isFetching: isFetchingAdmins } = useGroupMembers(groupId, GroupRoles.ADMIN);
const { groupMembers: users, isFetching: isFetchingUsers, fetchNextPage, hasNextPage } = useGroupMembers(groupId, GroupRoles.USER);
const { entities: pending, isFetching: isFetchingPending } = useGroupMembershipRequests(groupId);
const { accounts: pending, isFetching: isFetchingPending } = useGroupMembershipRequests(groupId);
const isLoading = isFetchingGroup || isFetchingOwners || isFetchingAdmins || isFetchingUsers || isFetchingPending;

View File

@ -1,11 +1,11 @@
import React from 'react';
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
import { authorizeGroupMembershipRequest, rejectGroupMembershipRequest } from 'soapbox/actions/groups';
import Account from 'soapbox/components/account';
import { AuthorizeRejectButtons } from 'soapbox/components/authorize-reject-buttons';
import ScrollableList from 'soapbox/components/scrollable-list';
import { Button, Column, HStack, Spinner } from 'soapbox/components/ui';
import { useAppDispatch, useGroup } from 'soapbox/hooks';
import { Column, HStack, Spinner } from 'soapbox/components/ui';
import { useGroup } from 'soapbox/hooks';
import { useGroupMembershipRequests } from 'soapbox/hooks/api/groups/useGroupMembershipRequests';
import toast from 'soapbox/toast';
@ -19,50 +19,44 @@ const messages = defineMessages({
heading: { id: 'column.group_pending_requests', defaultMessage: 'Pending requests' },
authorize: { id: 'group.group_mod_authorize', defaultMessage: 'Accept' },
authorized: { id: 'group.group_mod_authorize.success', defaultMessage: 'Accepted @{name} to group' },
authorizeFail: { id: 'group.group_mod_authorize.fail', defaultMessage: 'Failed to approve @{name}' },
reject: { id: 'group.group_mod_reject', defaultMessage: 'Reject' },
rejected: { id: 'group.group_mod_reject.success', defaultMessage: 'Rejected @{name} from group' },
rejectFail: { id: 'group.group_mod_reject.fail', defaultMessage: 'Failed to reject @{name}' },
});
interface IMembershipRequest {
account: AccountEntity
groupId: string
onAuthorize(accountId: string): Promise<unknown>
onReject(accountId: string): Promise<unknown>
}
const MembershipRequest: React.FC<IMembershipRequest> = ({ account, groupId }) => {
const MembershipRequest: React.FC<IMembershipRequest> = ({ account, onAuthorize, onReject }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
if (!account) return null;
const handleAuthorize = () =>
dispatch(authorizeGroupMembershipRequest(groupId, account.id)).then(() => {
toast.success(intl.formatMessage(messages.authorized, { name: account.acct }));
});
function handleAuthorize(accountId: string) {
return onAuthorize(accountId)
.catch(() => toast.error(intl.formatMessage(messages.authorizeFail, { name: account.username })));
}
const handleReject = () =>
dispatch(rejectGroupMembershipRequest(groupId, account.id)).then(() => {
toast.success(intl.formatMessage(messages.rejected, { name: account.acct }));
});
function handleReject(accountId: string) {
return onReject(accountId)
.catch(() => toast.error(intl.formatMessage(messages.rejectFail, { name: account.username })));
}
return (
<HStack space={1} alignItems='center' justifyContent='between' className='p-2.5'>
<div className='w-full'>
<Account account={account} withRelationship={false} />
</div>
<HStack space={2}>
<Button
theme='secondary'
size='sm'
text={intl.formatMessage(messages.authorize)}
onClick={handleAuthorize}
/>
<Button
theme='danger'
size='sm'
text={intl.formatMessage(messages.reject)}
onClick={handleReject}
/>
</HStack>
<AuthorizeRejectButtons
id={account.id}
onAuthorize={handleAuthorize}
onReject={handleReject}
/>
</HStack>
);
};
@ -77,7 +71,7 @@ const GroupMembershipRequests: React.FC<IGroupMembershipRequests> = ({ params })
const id = params?.id;
const { group } = useGroup(id);
const { entities: accounts, isLoading } = useGroupMembershipRequests(id);
const { accounts, isLoading, authorize, reject } = useGroupMembershipRequests(id);
if (!group || !group.relationship || isLoading) {
return (
@ -91,17 +85,23 @@ const GroupMembershipRequests: React.FC<IGroupMembershipRequests> = ({ params })
return (<ColumnForbidden />);
}
const emptyMessage = <FormattedMessage id='empty_column.group_membership_requests' defaultMessage='There are no pending membership requests for this group.' />;
const handleAuthorize = (accountId: string) => authorize(accountId);
const handleReject = (accountId: string) => reject(accountId);
return (
<Column label={intl.formatMessage(messages.heading)} backHref={`/groups/${id}/manage`}>
<ScrollableList
scrollKey='group_membership_requests'
emptyMessage={emptyMessage}
emptyMessage={<FormattedMessage id='empty_column.group_membership_requests' defaultMessage='There are no pending membership requests for this group.' />}
>
{accounts.map((account) =>
<MembershipRequest key={account.id} account={account} groupId={id} />,
)}
{accounts.map((account) => (
<MembershipRequest
key={account.id}
account={account}
onAuthorize={handleAuthorize}
onReject={handleReject}
/>
))}
</ScrollableList>
</Column>
);

View File

@ -1,13 +1,31 @@
import { Entities } from 'soapbox/entity-store/entities';
import { useEntities } from 'soapbox/entity-store/hooks';
import { useApi } from 'soapbox/hooks/useApi';
import { accountSchema } from 'soapbox/schemas';
function useGroupMembershipRequests(groupId: string) {
return useEntities(
const api = useApi();
function authorize(accountId: string) {
return api.post(`/api/v1/groups/${groupId}/membership_requests/${accountId}/authorize`);
}
function reject(accountId: string) {
return api.post(`/api/v1/groups/${groupId}/membership_requests/${accountId}/reject`);
}
const { entities, ...rest } = useEntities(
[Entities.ACCOUNTS, 'membership_requests', groupId],
`/api/v1/groups/${groupId}/membership_requests`,
{ schema: accountSchema },
);
return {
accounts: entities,
authorize,
reject,
...rest,
};
}
export { useGroupMembershipRequests };