Allow addressing posts to lists
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
@ -1,10 +1,12 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { useIntl, defineMessages, IntlShape } from 'react-intl';
|
||||
|
||||
import { changeComposeFederated, changeComposeVisibility } from 'pl-fe/actions/compose';
|
||||
import { fetchLists } from 'pl-fe/actions/lists';
|
||||
import DropdownMenu, { MenuItem } from 'pl-fe/components/dropdown-menu';
|
||||
import { Button } from 'pl-fe/components/ui';
|
||||
import { useAppDispatch, useCompose, useFeatures } from 'pl-fe/hooks';
|
||||
import { getOrderedLists } from 'pl-fe/features/lists';
|
||||
import { useAppDispatch, useAppSelector, useCompose, useFeatures } from 'pl-fe/hooks';
|
||||
|
||||
import type { Features } from 'pl-api';
|
||||
|
||||
@ -21,6 +23,8 @@ const messages = defineMessages({
|
||||
direct_long: { id: 'privacy.direct.long', defaultMessage: 'Post to mentioned users only' },
|
||||
local_short: { id: 'privacy.local.short', defaultMessage: 'Local-only' },
|
||||
local_long: { id: 'privacy.local.long', defaultMessage: 'Only visible on your instance' },
|
||||
list_short: { id: 'privacy.list.short', defaultMessage: 'List only' },
|
||||
list_long: { id: 'privacy.list.long', defaultMessage: 'Visible to members of a list' },
|
||||
|
||||
change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust post privacy' },
|
||||
local: { id: 'privacy.local', defaultMessage: '{privacy} (local-only)' },
|
||||
@ -30,16 +34,58 @@ interface Option {
|
||||
icon: string;
|
||||
value: string;
|
||||
text: string;
|
||||
meta: string;
|
||||
meta?: string;
|
||||
items?: Array<Omit<Option, 'items'>>;
|
||||
}
|
||||
|
||||
const getItems = (features: Features, intl: IntlShape) => [
|
||||
{ icon: require('@tabler/icons/outline/world.svg'), value: 'public', text: intl.formatMessage(messages.public_short), meta: intl.formatMessage(messages.public_long) },
|
||||
{ icon: require('@tabler/icons/outline/lock-open.svg'), value: 'unlisted', text: intl.formatMessage(messages.unlisted_short), meta: intl.formatMessage(messages.unlisted_long) },
|
||||
{ icon: require('@tabler/icons/outline/lock.svg'), value: 'private', text: intl.formatMessage(messages.private_short), meta: intl.formatMessage(messages.private_long) },
|
||||
features.visibilityMutualsOnly ? { icon: require('@tabler/icons/outline/users-group.svg'), value: 'mutuals_only', text: intl.formatMessage(messages.mutuals_only_short), meta: intl.formatMessage(messages.mutuals_only_long) } : undefined,
|
||||
{ icon: require('@tabler/icons/outline/mail.svg'), value: 'direct', text: intl.formatMessage(messages.direct_short), meta: intl.formatMessage(messages.direct_long) },
|
||||
features.visibilityLocalOnly ? { icon: require('@tabler/icons/outline/affiliate.svg'), value: 'local', text: intl.formatMessage(messages.local_short), meta: intl.formatMessage(messages.local_long) } : undefined,
|
||||
const getItems = (features: Features, lists: ReturnType<typeof getOrderedLists>, intl: IntlShape) => [
|
||||
{
|
||||
icon: require('@tabler/icons/outline/world.svg'),
|
||||
value: 'public',
|
||||
text: intl.formatMessage(messages.public_short),
|
||||
meta: intl.formatMessage(messages.public_long),
|
||||
},
|
||||
{
|
||||
icon: require('@tabler/icons/outline/lock-open.svg'),
|
||||
value: 'unlisted',
|
||||
text: intl.formatMessage(messages.unlisted_short),
|
||||
meta: intl.formatMessage(messages.unlisted_long),
|
||||
},
|
||||
{
|
||||
icon: require('@tabler/icons/outline/lock.svg'),
|
||||
value: 'private',
|
||||
text: intl.formatMessage(messages.private_short),
|
||||
meta: intl.formatMessage(messages.private_long),
|
||||
},
|
||||
features.visibilityMutualsOnly ? {
|
||||
icon: require('@tabler/icons/outline/users-group.svg'),
|
||||
value: 'mutuals_only',
|
||||
text: intl.formatMessage(messages.mutuals_only_short),
|
||||
meta: intl.formatMessage(messages.mutuals_only_long),
|
||||
} : undefined,
|
||||
{
|
||||
icon: require('@tabler/icons/outline/mail.svg'),
|
||||
value: 'direct',
|
||||
text: intl.formatMessage(messages.direct_short),
|
||||
meta: intl.formatMessage(messages.direct_long),
|
||||
},
|
||||
features.visibilityLocalOnly ? {
|
||||
icon: require('@tabler/icons/outline/affiliate.svg'),
|
||||
value: 'local',
|
||||
text: intl.formatMessage(messages.local_short),
|
||||
meta: intl.formatMessage(messages.local_long),
|
||||
} : undefined,
|
||||
features.addressableLists && !lists.isEmpty() ? {
|
||||
icon: require('@tabler/icons/outline/list.svg'),
|
||||
value: '',
|
||||
items: lists.toArray().map((list) => ({
|
||||
icon: require('@tabler/icons/outline/list.svg'),
|
||||
value: `list:${list.id}`,
|
||||
text: list.title,
|
||||
})),
|
||||
text: intl.formatMessage(messages.list_short),
|
||||
meta: intl.formatMessage(messages.list_long),
|
||||
} as Option : undefined,
|
||||
].filter((option): option is Option => !!option);
|
||||
|
||||
interface IPrivacyDropdown {
|
||||
@ -54,14 +100,29 @@ const PrivacyDropdown: React.FC<IPrivacyDropdown> = ({
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const compose = useCompose(composeId);
|
||||
const lists = useAppSelector((state) => getOrderedLists(state));
|
||||
|
||||
const value = compose.privacy;
|
||||
const unavailable = compose.id;
|
||||
|
||||
const onChange = (value: string) => value && dispatch(changeComposeVisibility(composeId, value));
|
||||
const onChange = (value: string) => value && dispatch(changeComposeVisibility(composeId,
|
||||
value));
|
||||
|
||||
const options = getItems(features, intl);
|
||||
const items: Array<MenuItem> = options.map(item => ({ ...item, action: () => onChange(item.value), active: item.value === value }));
|
||||
const options = useMemo(() => getItems(features, lists, intl), [features, lists]);
|
||||
const items: Array<MenuItem> = options.map(item => ({
|
||||
...item,
|
||||
action: item.value ? () => onChange(item.value) : undefined,
|
||||
active: item.value === value || item.items?.some((item) => item.value === value),
|
||||
items: item.items?.map(item => ({
|
||||
...item,
|
||||
action: item.value ? () => onChange(item.value) : undefined,
|
||||
active: item.value === value,
|
||||
})),
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (features.addressableLists) dispatch(fetchLists());
|
||||
}, []);
|
||||
|
||||
if (features.localOnlyStatuses) items.push({
|
||||
icon: require('@tabler/icons/outline/affiliate.svg'),
|
||||
@ -72,12 +133,15 @@ const PrivacyDropdown: React.FC<IPrivacyDropdown> = ({
|
||||
onChange: () => dispatch(changeComposeFederated(composeId)),
|
||||
});
|
||||
|
||||
const valueOption = useMemo(() => [
|
||||
options,
|
||||
options.filter(option => option.items).map(option => option.items).flat(),
|
||||
].flat().find(item => item!.value === value), [value]);
|
||||
|
||||
if (unavailable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const valueOption = options.find(item => item.value === value);
|
||||
|
||||
return (
|
||||
<DropdownMenu items={items}>
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user