220 lines
5.8 KiB
TypeScript
220 lines
5.8 KiB
TypeScript
import { useCallback, useMemo } from 'react';
|
|
|
|
import { defineMessages, useIntl } from 'react-intl';
|
|
|
|
import { showAlert } from '@/mastodon/actions/alerts';
|
|
import { initBlockModal } from '@/mastodon/actions/blocks';
|
|
import { useAccount } from '@/mastodon/hooks/useAccount';
|
|
import MoreVertIcon from '@/material-icons/400-24px/more_vert.svg?react';
|
|
import { openModal } from 'mastodon/actions/modal';
|
|
import type { ApiCollectionJSON } from 'mastodon/api_types/collections';
|
|
import { Dropdown } from 'mastodon/components/dropdown_menu';
|
|
import { IconButton } from 'mastodon/components/icon_button';
|
|
import { me } from 'mastodon/initial_state';
|
|
import type { MenuItem } from 'mastodon/models/dropdown_menu';
|
|
import { useAppDispatch } from 'mastodon/store';
|
|
|
|
import { messages as editorMessages } from '../editor';
|
|
import { getCollectionPath } from '../utils';
|
|
|
|
const messages = defineMessages({
|
|
view: {
|
|
id: 'collections.view_collection',
|
|
defaultMessage: 'View collection',
|
|
},
|
|
share: {
|
|
id: 'collections.share_short',
|
|
defaultMessage: 'Share',
|
|
},
|
|
copyLink: {
|
|
id: 'collections.copy_link',
|
|
defaultMessage: 'Copy link',
|
|
},
|
|
copyLinkConfirmation: {
|
|
id: 'collections.copy_link_confirmation',
|
|
defaultMessage: 'Copied collection link to clipboard',
|
|
},
|
|
delete: {
|
|
id: 'collections.delete_collection',
|
|
defaultMessage: 'Delete collection',
|
|
},
|
|
report: {
|
|
id: 'collections.report_collection',
|
|
defaultMessage: 'Report this collection',
|
|
},
|
|
blockOwner: {
|
|
id: 'collections.block_collection_owner',
|
|
defaultMessage: 'Block account',
|
|
},
|
|
revoke: {
|
|
id: 'collections.revoke_collection_inclusion',
|
|
defaultMessage: 'Remove myself from this collection',
|
|
},
|
|
more: { id: 'status.more', defaultMessage: 'More' },
|
|
});
|
|
|
|
export const CollectionMenu: React.FC<{
|
|
collection: ApiCollectionJSON;
|
|
context: 'list' | 'notifications' | 'collection';
|
|
className?: string;
|
|
}> = ({ collection, context, className }) => {
|
|
const dispatch = useAppDispatch();
|
|
const intl = useIntl();
|
|
|
|
const { id, name, account_id, items } = collection;
|
|
const ownerAccount = useAccount(account_id);
|
|
const isOwnCollection = account_id === me;
|
|
const currentAccountInCollection = items.find(
|
|
(item) => item.account_id === me,
|
|
);
|
|
|
|
const openShareModal = useCallback(() => {
|
|
dispatch(
|
|
openModal({
|
|
modalType: 'SHARE_COLLECTION',
|
|
modalProps: {
|
|
collection,
|
|
},
|
|
}),
|
|
);
|
|
}, [collection, dispatch]);
|
|
|
|
const openDeleteConfirmation = useCallback(() => {
|
|
dispatch(
|
|
openModal({
|
|
modalType: 'CONFIRM_DELETE_COLLECTION',
|
|
modalProps: {
|
|
name,
|
|
id,
|
|
},
|
|
}),
|
|
);
|
|
}, [dispatch, id, name]);
|
|
|
|
const openReportModal = useCallback(() => {
|
|
dispatch(
|
|
openModal({
|
|
modalType: 'REPORT_COLLECTION',
|
|
modalProps: {
|
|
collection,
|
|
},
|
|
}),
|
|
);
|
|
}, [collection, dispatch]);
|
|
|
|
const openBlockModal = useCallback(() => {
|
|
dispatch(initBlockModal(ownerAccount));
|
|
}, [ownerAccount, dispatch]);
|
|
|
|
const openRevokeConfirmation = useCallback(() => {
|
|
void dispatch(
|
|
openModal({
|
|
modalType: 'REVOKE_COLLECTION_INCLUSION',
|
|
modalProps: {
|
|
collectionId: collection.id,
|
|
collectionItemId: currentAccountInCollection?.id,
|
|
},
|
|
}),
|
|
);
|
|
}, [collection.id, currentAccountInCollection?.id, dispatch]);
|
|
|
|
const menu = useMemo(() => {
|
|
const viewCollectionItem: MenuItem = {
|
|
text: intl.formatMessage(messages.view),
|
|
to: getCollectionPath(id),
|
|
};
|
|
const shareItems: MenuItem[] = [
|
|
{
|
|
text: intl.formatMessage(messages.share),
|
|
action: openShareModal,
|
|
},
|
|
{
|
|
text: intl.formatMessage(messages.copyLink),
|
|
action: () => {
|
|
void navigator.clipboard.writeText(collection.url);
|
|
dispatch(showAlert({ message: messages.copyLinkConfirmation }));
|
|
},
|
|
},
|
|
];
|
|
|
|
if (isOwnCollection) {
|
|
const ownerItems: MenuItem[] = [
|
|
...shareItems,
|
|
null,
|
|
{
|
|
text: intl.formatMessage(editorMessages.manageAccounts),
|
|
to: `/collections/${id}/edit`,
|
|
},
|
|
{
|
|
text: intl.formatMessage(editorMessages.editDetails),
|
|
to: `/collections/${id}/edit/details`,
|
|
},
|
|
null,
|
|
{
|
|
text: intl.formatMessage(messages.delete),
|
|
action: openDeleteConfirmation,
|
|
dangerous: true,
|
|
},
|
|
];
|
|
|
|
if (context === 'list') {
|
|
return [viewCollectionItem, ...ownerItems];
|
|
} else {
|
|
return ownerItems;
|
|
}
|
|
} else {
|
|
const nonOwnerItems: MenuItem[] = [...shareItems, null];
|
|
|
|
// Collection notifications already have a prominent 'Remove me' button
|
|
if (currentAccountInCollection && context !== 'notifications') {
|
|
nonOwnerItems.push({
|
|
text: intl.formatMessage(messages.revoke),
|
|
action: openRevokeConfirmation,
|
|
});
|
|
}
|
|
|
|
nonOwnerItems.push({
|
|
text: intl.formatMessage(messages.report),
|
|
action: openReportModal,
|
|
});
|
|
|
|
if (currentAccountInCollection) {
|
|
nonOwnerItems.push({
|
|
text: intl.formatMessage(messages.blockOwner),
|
|
action: openBlockModal,
|
|
});
|
|
}
|
|
|
|
if (context !== 'collection') {
|
|
return [viewCollectionItem, ...nonOwnerItems];
|
|
}
|
|
|
|
return nonOwnerItems;
|
|
}
|
|
}, [
|
|
intl,
|
|
id,
|
|
openShareModal,
|
|
isOwnCollection,
|
|
collection.url,
|
|
dispatch,
|
|
openDeleteConfirmation,
|
|
context,
|
|
currentAccountInCollection,
|
|
openReportModal,
|
|
openRevokeConfirmation,
|
|
openBlockModal,
|
|
]);
|
|
|
|
return (
|
|
<Dropdown scrollKey='collections' items={menu}>
|
|
<IconButton
|
|
icon='menu-icon'
|
|
iconComponent={MoreVertIcon}
|
|
title={intl.formatMessage(messages.more)}
|
|
className={className}
|
|
/>
|
|
</Dropdown>
|
|
);
|
|
};
|