Add ability to update Group tags
This commit is contained in:
parent
2d52c8c3e4
commit
c5c5bd0d62
5 changed files with 105 additions and 59 deletions
|
@ -31,10 +31,14 @@ function useEntityActions<TEntity extends Entity = Entity, Data = any>(
|
|||
const { createEntity, isSubmitting: createSubmitting } =
|
||||
useCreateEntity<TEntity, Data>(path, (data) => api.post(endpoints.post!, data), opts);
|
||||
|
||||
const { createEntity: updateEntity, isSubmitting: updateSubmitting } =
|
||||
useCreateEntity<TEntity, Data>(path, (data) => api.patch(endpoints.patch!, data), opts);
|
||||
|
||||
return {
|
||||
createEntity,
|
||||
deleteEntity,
|
||||
isSubmitting: createSubmitting || deleteSubmitting,
|
||||
updateEntity,
|
||||
isSubmitting: createSubmitting || deleteSubmitting || updateSubmitting,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,11 @@ import { defineMessages, useIntl } from 'react-intl';
|
|||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { HStack, IconButton, Stack, Text, Tooltip } from 'soapbox/components/ui';
|
||||
import { importEntities } from 'soapbox/entity-store/actions';
|
||||
import { Entities } from 'soapbox/entity-store/entities';
|
||||
import { useAppDispatch } from 'soapbox/hooks';
|
||||
import { useUpdateGroupTag } from 'soapbox/hooks/api';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member';
|
||||
import toast from 'soapbox/toast';
|
||||
import { shortNumberFormat } from 'soapbox/utils/numbers';
|
||||
|
||||
|
@ -29,15 +33,26 @@ interface IGroupMemberListItem {
|
|||
|
||||
const GroupTagListItem = (props: IGroupMemberListItem) => {
|
||||
const { group, tag, isPinnable } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const intl = useIntl();
|
||||
const updateGroupTag = useUpdateGroupTag(group.id, tag.id);
|
||||
const { updateGroupTag } = useUpdateGroupTag(group.id, tag.id);
|
||||
|
||||
const isOwner = group.relationship?.role === GroupRoles.OWNER;
|
||||
const isAdmin = group.relationship?.role === GroupRoles.ADMIN;
|
||||
const canEdit = isOwner || isAdmin;
|
||||
|
||||
const toggleVisibility = () => {
|
||||
updateGroupTag({
|
||||
pinned: !tag.visible,
|
||||
group_tag_type: tag.visible ? 'hidden' : 'normal',
|
||||
}, {
|
||||
onSuccess(entity: GroupTag) {
|
||||
onSuccess() {
|
||||
const entity = {
|
||||
...tag,
|
||||
visible: !tag.visible,
|
||||
};
|
||||
dispatch(importEntities([entity], Entities.GROUP_TAGS));
|
||||
|
||||
toast.success(
|
||||
entity.visible ?
|
||||
intl.formatMessage(messages.visibleSuccess) :
|
||||
|
@ -49,9 +64,15 @@ const GroupTagListItem = (props: IGroupMemberListItem) => {
|
|||
|
||||
const togglePin = () => {
|
||||
updateGroupTag({
|
||||
pinned: !tag.pinned,
|
||||
group_tag_type: tag.pinned ? 'normal' : 'pinned',
|
||||
}, {
|
||||
onSuccess(entity: GroupTag) {
|
||||
onSuccess() {
|
||||
const entity = {
|
||||
...tag,
|
||||
pinned: !tag.pinned,
|
||||
};
|
||||
dispatch(importEntities([entity], Entities.GROUP_TAGS));
|
||||
|
||||
toast.success(
|
||||
entity.pinned ?
|
||||
intl.formatMessage(messages.pinSuccess) :
|
||||
|
@ -73,7 +94,7 @@ const GroupTagListItem = (props: IGroupMemberListItem) => {
|
|||
>
|
||||
<IconButton
|
||||
onClick={togglePin}
|
||||
transparent
|
||||
theme='transparent'
|
||||
src={
|
||||
tag.pinned ?
|
||||
require('@tabler/icons/pin-filled.svg') :
|
||||
|
@ -90,7 +111,7 @@ const GroupTagListItem = (props: IGroupMemberListItem) => {
|
|||
<Tooltip text={intl.formatMessage(messages.unpinTag)}>
|
||||
<IconButton
|
||||
onClick={togglePin}
|
||||
transparent
|
||||
theme='transparent'
|
||||
src={require('@tabler/icons/pin-filled.svg')}
|
||||
iconClassName='h-5 w-5 text-primary-500 dark:text-accent-blue'
|
||||
/>
|
||||
|
@ -106,12 +127,12 @@ const GroupTagListItem = (props: IGroupMemberListItem) => {
|
|||
<Stack>
|
||||
<Text
|
||||
weight='bold'
|
||||
theme={tag.visible ? 'default' : 'subtle'}
|
||||
theme={(tag.visible || !canEdit) ? 'default' : 'subtle'}
|
||||
className='group-hover:underline'
|
||||
>
|
||||
#{tag.name}
|
||||
</Text>
|
||||
<Text size='sm' theme={tag.visible ? 'muted' : 'subtle'}>
|
||||
<Text size='sm' theme={(tag.visible || !canEdit) ? 'muted' : 'subtle'}>
|
||||
{intl.formatMessage(messages.total)}:
|
||||
{' '}
|
||||
<Text size='sm' theme='inherit' weight='semibold' tag='span'>
|
||||
|
@ -121,30 +142,32 @@ const GroupTagListItem = (props: IGroupMemberListItem) => {
|
|||
</Stack>
|
||||
</Link>
|
||||
|
||||
<HStack alignItems='center' space={2}>
|
||||
{tag.visible ? (
|
||||
renderPinIcon()
|
||||
) : null}
|
||||
{canEdit ? (
|
||||
<HStack alignItems='center' space={2}>
|
||||
{tag.visible ? (
|
||||
renderPinIcon()
|
||||
) : null}
|
||||
|
||||
<Tooltip
|
||||
text={
|
||||
tag.visible ?
|
||||
intl.formatMessage(messages.hideTag) :
|
||||
intl.formatMessage(messages.showTag)
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
onClick={toggleVisibility}
|
||||
transparent
|
||||
src={
|
||||
<Tooltip
|
||||
text={
|
||||
tag.visible ?
|
||||
require('@tabler/icons/eye.svg') :
|
||||
require('@tabler/icons/eye-off.svg')
|
||||
intl.formatMessage(messages.hideTag) :
|
||||
intl.formatMessage(messages.showTag)
|
||||
}
|
||||
iconClassName='h-5 w-5 text-primary-500 dark:text-accent-blue'
|
||||
/>
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
>
|
||||
<IconButton
|
||||
onClick={toggleVisibility}
|
||||
theme='transparent'
|
||||
src={
|
||||
tag.visible ?
|
||||
require('@tabler/icons/eye.svg') :
|
||||
require('@tabler/icons/eye-off.svg')
|
||||
}
|
||||
iconClassName='h-5 w-5 text-primary-500 dark:text-accent-blue'
|
||||
/>
|
||||
</Tooltip>
|
||||
</HStack>
|
||||
) : null}
|
||||
</HStack>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import ScrollableList from 'soapbox/components/scrollable-list';
|
||||
import { Icon, Stack, Text } from 'soapbox/components/ui';
|
||||
import { useGroupTags } from 'soapbox/hooks/api';
|
||||
import { useGroup } from 'soapbox/queries/groups';
|
||||
|
||||
|
@ -26,28 +28,41 @@ const GroupTopics: React.FC<IGroupTopics> = (props) => {
|
|||
const isPinnable = pinnedTags.length < 3;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScrollableList
|
||||
scrollKey='group-tags'
|
||||
hasMore={hasNextPage}
|
||||
onLoadMore={fetchNextPage}
|
||||
isLoading={isLoading || !group}
|
||||
showLoading={!group || isLoading && tags.length === 0}
|
||||
placeholderComponent={PlaceholderAccount}
|
||||
placeholderCount={3}
|
||||
className='divide-y divide-solid divide-gray-300'
|
||||
itemClassName='py-3 last:pb-0'
|
||||
>
|
||||
{tags.map((tag) => (
|
||||
<GroupTagListItem
|
||||
key={tag.id}
|
||||
group={group as Group}
|
||||
isPinnable={isPinnable}
|
||||
tag={tag}
|
||||
/>
|
||||
))}
|
||||
</ScrollableList>
|
||||
</>
|
||||
<ScrollableList
|
||||
scrollKey='group-tags'
|
||||
hasMore={hasNextPage}
|
||||
onLoadMore={fetchNextPage}
|
||||
isLoading={isLoading || !group}
|
||||
showLoading={!group || isLoading && tags.length === 0}
|
||||
placeholderComponent={PlaceholderAccount}
|
||||
placeholderCount={3}
|
||||
className='divide-y divide-solid divide-gray-300'
|
||||
itemClassName='py-3 last:pb-0'
|
||||
emptyMessage={
|
||||
<Stack space={4} className='pt-6' justifyContent='center' alignItems='center'>
|
||||
<div className='rounded-full bg-gray-200 p-4 dark:bg-gray-800'>
|
||||
<Icon
|
||||
src={require('@tabler/icons/hash.svg')}
|
||||
className='h-6 w-6 text-gray-600'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Text theme='muted'>
|
||||
<FormattedMessage id='group.tags.empty' defaultMessage='There are no topics in this group yet.' />
|
||||
</Text>
|
||||
</Stack>
|
||||
}
|
||||
emptyMessageCard={false}
|
||||
>
|
||||
{tags.map((tag) => (
|
||||
<GroupTagListItem
|
||||
key={tag.id}
|
||||
group={group as Group}
|
||||
isPinnable={isPinnable}
|
||||
tag={tag}
|
||||
/>
|
||||
))}
|
||||
</ScrollableList>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import { Entities } from 'soapbox/entity-store/entities';
|
||||
import { useEntities } from 'soapbox/entity-store/hooks';
|
||||
import { useApi } from 'soapbox/hooks/useApi';
|
||||
import { groupTagSchema } from 'soapbox/schemas';
|
||||
|
||||
import type { GroupTag } from 'soapbox/schemas';
|
||||
|
||||
function useGroupTags(groupId: string) {
|
||||
const api = useApi();
|
||||
|
||||
const { entities, ...result } = useEntities<GroupTag>(
|
||||
[Entities.GROUP_TAGS, groupId],
|
||||
'/api/mock/groups/tags', // `api/v1/groups/${groupId}/tags`
|
||||
() => api.get(`api/v1/truth/trends/groups/${groupId}/tags`),
|
||||
{ schema: groupTagSchema },
|
||||
);
|
||||
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import { Entities } from 'soapbox/entity-store/entities';
|
||||
import { useEntityActions } from 'soapbox/entity-store/hooks';
|
||||
import { groupTagSchema } from 'soapbox/schemas';
|
||||
|
||||
import type { GroupTag } from 'soapbox/schemas';
|
||||
|
||||
function useUpdateGroupTag(groupId: string, tagId: string) {
|
||||
const { updateEntity } = useEntityActions<GroupTag>(
|
||||
const { updateEntity, ...rest } = useEntityActions<GroupTag>(
|
||||
[Entities.GROUP_TAGS, groupId, tagId],
|
||||
{ patch: `/api/mock/truth/groups/${groupId}/tags/${tagId}` },
|
||||
{ schema: groupTagSchema },
|
||||
{ patch: `/api/v1/groups/${groupId}/tags/${tagId}` },
|
||||
);
|
||||
|
||||
return updateEntity;
|
||||
return {
|
||||
updateGroupTag: updateEntity,
|
||||
...rest,
|
||||
};
|
||||
}
|
||||
|
||||
export { useUpdateGroupTag };
|
Loading…
Reference in a new issue