Merge remote-tracking branch 'origin/develop' into group-lookup
This commit is contained in:
175
app/soapbox/features/group/components/group-tag-list-item.tsx
Normal file
175
app/soapbox/features/group/components/group-tag-list-item.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
import React from 'react';
|
||||
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';
|
||||
|
||||
import type { Group, GroupTag } from 'soapbox/schemas';
|
||||
|
||||
const messages = defineMessages({
|
||||
hideTag: { id: 'group.tags.hide', defaultMessage: 'Hide topic' },
|
||||
showTag: { id: 'group.tags.show', defaultMessage: 'Show topic' },
|
||||
total: { id: 'group.tags.total', defaultMessage: 'Total Posts' },
|
||||
pinTag: { id: 'group.tags.pin', defaultMessage: 'Pin topic' },
|
||||
unpinTag: { id: 'group.tags.unpin', defaultMessage: 'Unpin topic' },
|
||||
pinSuccess: { id: 'group.tags.pin.success', defaultMessage: 'Pinned!' },
|
||||
unpinSuccess: { id: 'group.tags.unpin.success', defaultMessage: 'Unpinned!' },
|
||||
visibleSuccess: { id: 'group.tags.visible.success', defaultMessage: 'Topic marked as visible' },
|
||||
hiddenSuccess: { id: 'group.tags.hidden.success', defaultMessage: 'Topic marked as hidden' },
|
||||
});
|
||||
|
||||
interface IGroupMemberListItem {
|
||||
tag: GroupTag
|
||||
group: Group
|
||||
isPinnable: boolean
|
||||
}
|
||||
|
||||
const GroupTagListItem = (props: IGroupMemberListItem) => {
|
||||
const { group, tag, isPinnable } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const intl = useIntl();
|
||||
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({
|
||||
group_tag_type: tag.visible ? 'hidden' : 'normal',
|
||||
}, {
|
||||
onSuccess() {
|
||||
const entity = {
|
||||
...tag,
|
||||
visible: !tag.visible,
|
||||
};
|
||||
dispatch(importEntities([entity], Entities.GROUP_TAGS));
|
||||
|
||||
toast.success(
|
||||
entity.visible ?
|
||||
intl.formatMessage(messages.visibleSuccess) :
|
||||
intl.formatMessage(messages.hiddenSuccess),
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const togglePin = () => {
|
||||
updateGroupTag({
|
||||
group_tag_type: tag.pinned ? 'normal' : 'pinned',
|
||||
}, {
|
||||
onSuccess() {
|
||||
const entity = {
|
||||
...tag,
|
||||
pinned: !tag.pinned,
|
||||
};
|
||||
dispatch(importEntities([entity], Entities.GROUP_TAGS));
|
||||
|
||||
toast.success(
|
||||
entity.pinned ?
|
||||
intl.formatMessage(messages.pinSuccess) :
|
||||
intl.formatMessage(messages.unpinSuccess),
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const renderPinIcon = () => {
|
||||
if (isPinnable) {
|
||||
return (
|
||||
<Tooltip
|
||||
text={
|
||||
tag.pinned ?
|
||||
intl.formatMessage(messages.unpinTag) :
|
||||
intl.formatMessage(messages.pinTag)
|
||||
}
|
||||
>
|
||||
<IconButton
|
||||
onClick={togglePin}
|
||||
theme='transparent'
|
||||
src={
|
||||
tag.pinned ?
|
||||
require('@tabler/icons/pin-filled.svg') :
|
||||
require('@tabler/icons/pin.svg')
|
||||
}
|
||||
iconClassName='h-5 w-5 text-primary-500 dark:text-accent-blue'
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isPinnable && tag.pinned) {
|
||||
return (
|
||||
<Tooltip text={intl.formatMessage(messages.unpinTag)}>
|
||||
<IconButton
|
||||
onClick={togglePin}
|
||||
theme='transparent'
|
||||
src={require('@tabler/icons/pin-filled.svg')}
|
||||
iconClassName='h-5 w-5 text-primary-500 dark:text-accent-blue'
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<HStack alignItems='center' justifyContent='between'>
|
||||
<Link to={`/groups/${group.id}/tag/${tag.id}`} className='group grow'>
|
||||
<Stack>
|
||||
<Text
|
||||
weight='bold'
|
||||
theme={(tag.visible || !canEdit) ? 'default' : 'subtle'}
|
||||
className='group-hover:underline'
|
||||
>
|
||||
#{tag.name}
|
||||
</Text>
|
||||
<Text size='sm' theme={(tag.visible || !canEdit) ? 'muted' : 'subtle'}>
|
||||
{intl.formatMessage(messages.total)}:
|
||||
{' '}
|
||||
<Text size='sm' theme='inherit' weight='semibold' tag='span'>
|
||||
{shortNumberFormat(tag.uses)}
|
||||
</Text>
|
||||
</Text>
|
||||
</Stack>
|
||||
</Link>
|
||||
|
||||
{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}
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
export default GroupTagListItem;
|
||||
@@ -3,6 +3,8 @@ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { Input, Streamfield } from 'soapbox/components/ui';
|
||||
|
||||
import type { StreamfieldComponent } from 'soapbox/components/ui/streamfield/streamfield';
|
||||
|
||||
const messages = defineMessages({
|
||||
hashtagPlaceholder: { id: 'manage_group.fields.hashtag_placeholder', defaultMessage: 'Add a topic' },
|
||||
});
|
||||
@@ -30,12 +32,7 @@ const GroupTagsField: React.FC<IGroupTagsField> = ({ tags, onChange, onAddItem,
|
||||
);
|
||||
};
|
||||
|
||||
interface IHashtagField {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
}
|
||||
|
||||
const HashtagField: React.FC<IHashtagField> = ({ value, onChange }) => {
|
||||
const HashtagField: StreamfieldComponent<string> = ({ value, onChange, autoFocus = false }) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const handleChange: React.ChangeEventHandler<HTMLInputElement> = ({ target }) => {
|
||||
@@ -49,6 +46,7 @@ const HashtagField: React.FC<IHashtagField> = ({ value, onChange }) => {
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
placeholder={intl.formatMessage(messages.hashtagPlaceholder)}
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user