[Glitch] Add more actions to collections notifications & context menus
Port 543db6d24c8764fe3975a138c777ffb1c28ae29e to glitch-soc Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
parent
e0a34e6d89
commit
87c66c8b96
@ -4,6 +4,8 @@ import { defineMessages, useIntl } from 'react-intl';
|
|||||||
|
|
||||||
import { matchPath } from 'react-router';
|
import { matchPath } from 'react-router';
|
||||||
|
|
||||||
|
import { showAlert } from '@/flavours/glitch/actions/alerts';
|
||||||
|
import { initBlockModal } from '@/flavours/glitch/actions/blocks';
|
||||||
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
|
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
|
||||||
import MoreVertIcon from '@/material-icons/400-24px/more_vert.svg?react';
|
import MoreVertIcon from '@/material-icons/400-24px/more_vert.svg?react';
|
||||||
import { openModal } from 'flavours/glitch/actions/modal';
|
import { openModal } from 'flavours/glitch/actions/modal';
|
||||||
@ -21,6 +23,18 @@ const messages = defineMessages({
|
|||||||
id: 'collections.view_collection',
|
id: 'collections.view_collection',
|
||||||
defaultMessage: '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',
|
||||||
|
},
|
||||||
viewOtherCollections: {
|
viewOtherCollections: {
|
||||||
id: 'collections.view_other_collections_by_user',
|
id: 'collections.view_other_collections_by_user',
|
||||||
defaultMessage: 'View other collections by this user',
|
defaultMessage: 'View other collections by this user',
|
||||||
@ -33,6 +47,10 @@ const messages = defineMessages({
|
|||||||
id: 'collections.report_collection',
|
id: 'collections.report_collection',
|
||||||
defaultMessage: 'Report this collection',
|
defaultMessage: 'Report this collection',
|
||||||
},
|
},
|
||||||
|
blockOwner: {
|
||||||
|
id: 'collections.block_collection_owner',
|
||||||
|
defaultMessage: 'Block account',
|
||||||
|
},
|
||||||
revoke: {
|
revoke: {
|
||||||
id: 'collections.revoke_collection_inclusion',
|
id: 'collections.revoke_collection_inclusion',
|
||||||
defaultMessage: 'Remove myself from this collection',
|
defaultMessage: 'Remove myself from this collection',
|
||||||
@ -42,15 +60,29 @@ const messages = defineMessages({
|
|||||||
|
|
||||||
export const CollectionMenu: React.FC<{
|
export const CollectionMenu: React.FC<{
|
||||||
collection: ApiCollectionJSON;
|
collection: ApiCollectionJSON;
|
||||||
context: 'list' | 'collection';
|
context: 'list' | 'notifications' | 'collection';
|
||||||
className?: string;
|
className?: string;
|
||||||
}> = ({ collection, context, className }) => {
|
}> = ({ collection, context, className }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const { id, name, account_id } = collection;
|
const { id, name, account_id, items } = collection;
|
||||||
const isOwnCollection = account_id === me;
|
|
||||||
const ownerAccount = useAccount(account_id);
|
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(() => {
|
const openDeleteConfirmation = useCallback(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -75,9 +107,9 @@ export const CollectionMenu: React.FC<{
|
|||||||
);
|
);
|
||||||
}, [collection, dispatch]);
|
}, [collection, dispatch]);
|
||||||
|
|
||||||
const currentAccountInCollection = collection.items.find(
|
const openBlockModal = useCallback(() => {
|
||||||
(item) => item.account_id === me,
|
dispatch(initBlockModal(ownerAccount));
|
||||||
);
|
}, [ownerAccount, dispatch]);
|
||||||
|
|
||||||
const openRevokeConfirmation = useCallback(() => {
|
const openRevokeConfirmation = useCallback(() => {
|
||||||
void dispatch(
|
void dispatch(
|
||||||
@ -92,8 +124,28 @@ export const CollectionMenu: React.FC<{
|
|||||||
}, [collection.id, currentAccountInCollection?.id, dispatch]);
|
}, [collection.id, currentAccountInCollection?.id, dispatch]);
|
||||||
|
|
||||||
const menu = useMemo(() => {
|
const menu = useMemo(() => {
|
||||||
|
const viewCollectionItem: MenuItem = {
|
||||||
|
text: intl.formatMessage(messages.view),
|
||||||
|
to: `/collections/${id}`,
|
||||||
|
};
|
||||||
|
const shareItems: MenuItem[] = [
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(messages.share),
|
||||||
|
action: openShareModal,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: intl.formatMessage(messages.copyLink),
|
||||||
|
action: () => {
|
||||||
|
void navigator.clipboard.writeText(`/collections/${id}`);
|
||||||
|
dispatch(showAlert({ message: messages.copyLinkConfirmation }));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
if (isOwnCollection) {
|
if (isOwnCollection) {
|
||||||
const commonItems: MenuItem[] = [
|
const ownerItems: MenuItem[] = [
|
||||||
|
...shareItems,
|
||||||
|
null,
|
||||||
{
|
{
|
||||||
text: intl.formatMessage(editorMessages.manageAccounts),
|
text: intl.formatMessage(editorMessages.manageAccounts),
|
||||||
to: `/collections/${id}/edit`,
|
to: `/collections/${id}/edit`,
|
||||||
@ -111,18 +163,14 @@ export const CollectionMenu: React.FC<{
|
|||||||
];
|
];
|
||||||
|
|
||||||
if (context === 'list') {
|
if (context === 'list') {
|
||||||
return [
|
return [viewCollectionItem, ...ownerItems];
|
||||||
{ text: intl.formatMessage(messages.view), to: `/collections/${id}` },
|
|
||||||
null,
|
|
||||||
...commonItems,
|
|
||||||
];
|
|
||||||
} else {
|
} else {
|
||||||
return commonItems;
|
return ownerItems;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const items: MenuItem[] = [];
|
const nonOwnerItems: MenuItem[] = [viewCollectionItem, ...shareItems];
|
||||||
|
|
||||||
if (ownerAccount) {
|
if (context !== 'notifications' && ownerAccount) {
|
||||||
const featuredCollectionsPath = `/@${ownerAccount.acct}/featured`;
|
const featuredCollectionsPath = `/@${ownerAccount.acct}/featured`;
|
||||||
// Don't show menu link to featured collections while on that very page
|
// Don't show menu link to featured collections while on that very page
|
||||||
if (
|
if (
|
||||||
@ -131,42 +179,50 @@ export const CollectionMenu: React.FC<{
|
|||||||
exact: true,
|
exact: true,
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
items.push(
|
nonOwnerItems.push({
|
||||||
...[
|
text: intl.formatMessage(messages.viewOtherCollections),
|
||||||
{
|
to: featuredCollectionsPath,
|
||||||
text: intl.formatMessage(messages.viewOtherCollections),
|
});
|
||||||
to: featuredCollectionsPath,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentAccountInCollection) {
|
nonOwnerItems.push(null);
|
||||||
items.push({
|
|
||||||
|
// Collection notifications already have a prominent 'Remove me' button
|
||||||
|
if (currentAccountInCollection && context !== 'notifications') {
|
||||||
|
nonOwnerItems.push({
|
||||||
text: intl.formatMessage(messages.revoke),
|
text: intl.formatMessage(messages.revoke),
|
||||||
action: openRevokeConfirmation,
|
action: openRevokeConfirmation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push({
|
nonOwnerItems.push({
|
||||||
text: intl.formatMessage(messages.report),
|
text: intl.formatMessage(messages.report),
|
||||||
action: openReportModal,
|
action: openReportModal,
|
||||||
});
|
});
|
||||||
|
|
||||||
return items;
|
if (currentAccountInCollection) {
|
||||||
|
nonOwnerItems.push({
|
||||||
|
text: intl.formatMessage(messages.blockOwner),
|
||||||
|
action: openBlockModal,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonOwnerItems;
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
isOwnCollection,
|
|
||||||
intl,
|
intl,
|
||||||
id,
|
id,
|
||||||
|
openShareModal,
|
||||||
|
isOwnCollection,
|
||||||
|
dispatch,
|
||||||
openDeleteConfirmation,
|
openDeleteConfirmation,
|
||||||
context,
|
context,
|
||||||
currentAccountInCollection,
|
|
||||||
openRevokeConfirmation,
|
|
||||||
ownerAccount,
|
ownerAccount,
|
||||||
|
currentAccountInCollection,
|
||||||
openReportModal,
|
openReportModal,
|
||||||
|
openBlockModal,
|
||||||
|
openRevokeConfirmation,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -115,7 +115,7 @@ const CollectionHeader: React.FC<{ collection: ApiCollectionJSON }> = ({
|
|||||||
);
|
);
|
||||||
const isCurrentUserInCollection = !isOwnCollection && currentUserIndex > -1;
|
const isCurrentUserInCollection = !isOwnCollection && currentUserIndex > -1;
|
||||||
|
|
||||||
const handleShare = useCallback(() => {
|
const openShareModal = useCallback(() => {
|
||||||
dispatch(
|
dispatch(
|
||||||
openModal({
|
openModal({
|
||||||
modalType: 'SHARE_COLLECTION',
|
modalType: 'SHARE_COLLECTION',
|
||||||
@ -132,9 +132,9 @@ const CollectionHeader: React.FC<{ collection: ApiCollectionJSON }> = ({
|
|||||||
if (isNewCollection) {
|
if (isNewCollection) {
|
||||||
// Replace with current pathname to clear `newCollection` state
|
// Replace with current pathname to clear `newCollection` state
|
||||||
history.replace(location.pathname);
|
history.replace(location.pathname);
|
||||||
handleShare();
|
openShareModal();
|
||||||
}
|
}
|
||||||
}, [history, handleShare, isNewCollection, location.pathname]);
|
}, [history, openShareModal, isNewCollection, location.pathname]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className={classes.header}>
|
<header className={classes.header}>
|
||||||
@ -150,7 +150,7 @@ const CollectionHeader: React.FC<{ collection: ApiCollectionJSON }> = ({
|
|||||||
icon='share-icon'
|
icon='share-icon'
|
||||||
title={intl.formatMessage(messages.share)}
|
title={intl.formatMessage(messages.share)}
|
||||||
className={classes.iconButton}
|
className={classes.iconButton}
|
||||||
onClick={handleShare}
|
onClick={openShareModal}
|
||||||
/>
|
/>
|
||||||
<CollectionMenu
|
<CollectionMenu
|
||||||
context='collection'
|
context='collection'
|
||||||
|
|||||||
@ -0,0 +1,17 @@
|
|||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuButton {
|
||||||
|
box-sizing: content-box;
|
||||||
|
margin-inline-start: auto;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--color-border-primary);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,8 +2,12 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { DisplayNameSimple } from '@/flavours/glitch/components/display_name/simple';
|
import { Button } from '@/flavours/glitch/components/button';
|
||||||
|
import { LinkedDisplayName } from '@/flavours/glitch/components/display_name';
|
||||||
import { Icon } from '@/flavours/glitch/components/icon';
|
import { Icon } from '@/flavours/glitch/components/icon';
|
||||||
|
import { CollectionMenu } from '@/flavours/glitch/features/collections/components/collection_menu';
|
||||||
|
import { CollectionPreviewCard } from '@/flavours/glitch/features/collections/components/collection_preview_card';
|
||||||
|
import { useConfirmRevoke } from '@/flavours/glitch/features/collections/detail/revoke_collection_inclusion_modal';
|
||||||
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
|
import { useAccount } from '@/flavours/glitch/hooks/useAccount';
|
||||||
import CollectionsFilledIcon from '@/material-icons/400-24px/category-fill.svg?react';
|
import CollectionsFilledIcon from '@/material-icons/400-24px/category-fill.svg?react';
|
||||||
import type {
|
import type {
|
||||||
@ -11,7 +15,7 @@ import type {
|
|||||||
NotificationGroupCollectionUpdate,
|
NotificationGroupCollectionUpdate,
|
||||||
} from 'flavours/glitch/models/notification_group';
|
} from 'flavours/glitch/models/notification_group';
|
||||||
|
|
||||||
import { CollectionPreviewCard } from '../../collections/components/collection_preview_card';
|
import classes from './notification_collection.module.scss';
|
||||||
|
|
||||||
export const NotificationCollection: React.FC<{
|
export const NotificationCollection: React.FC<{
|
||||||
notification:
|
notification:
|
||||||
@ -21,6 +25,7 @@ export const NotificationCollection: React.FC<{
|
|||||||
}> = ({ notification, unread }) => {
|
}> = ({ notification, unread }) => {
|
||||||
const { collection, type } = notification;
|
const { collection, type } = notification;
|
||||||
const collectionCreatorAccount = useAccount(collection.account_id);
|
const collectionCreatorAccount = useAccount(collection.account_id);
|
||||||
|
const confirmRevoke = useConfirmRevoke(collection);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -43,7 +48,12 @@ export const NotificationCollection: React.FC<{
|
|||||||
defaultMessage='{name} added you to a collection'
|
defaultMessage='{name} added you to a collection'
|
||||||
values={{
|
values={{
|
||||||
name: (
|
name: (
|
||||||
<DisplayNameSimple account={collectionCreatorAccount} />
|
<LinkedDisplayName
|
||||||
|
displayProps={{
|
||||||
|
variant: 'simple',
|
||||||
|
account: collectionCreatorAccount,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -54,7 +64,12 @@ export const NotificationCollection: React.FC<{
|
|||||||
defaultMessage='{name} edited a collection you’re in'
|
defaultMessage='{name} edited a collection you’re in'
|
||||||
values={{
|
values={{
|
||||||
name: (
|
name: (
|
||||||
<DisplayNameSimple account={collectionCreatorAccount} />
|
<LinkedDisplayName
|
||||||
|
displayProps={{
|
||||||
|
variant: 'simple',
|
||||||
|
account: collectionCreatorAccount,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -63,6 +78,26 @@ export const NotificationCollection: React.FC<{
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CollectionPreviewCard collection={collection} />
|
<CollectionPreviewCard collection={collection} />
|
||||||
|
|
||||||
|
<div className={classes.actions}>
|
||||||
|
<Button
|
||||||
|
compact
|
||||||
|
secondary
|
||||||
|
className='button--destructive'
|
||||||
|
onClick={confirmRevoke}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='collections.detail.revoke_inclusion'
|
||||||
|
defaultMessage='Remove me'
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<CollectionMenu
|
||||||
|
context='notifications'
|
||||||
|
collection={collection}
|
||||||
|
className={classes.menuButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user