Merge commit 'e235c446c96280c015fb27f7eefe4e95a5a39892' into glitch-soc/merge-upstream
This commit is contained in:
commit
4bcd0caa79
70 files changed
+1060
-109
No files matched your search
+59
-3
@@ -2,17 +2,23 @@ import { Fragment, useCallback, useRef, useState } from 'react';
|
||||
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import { Button } from '@/mastodon/components/button';
|
||||
import { useRelationship } from '@/mastodon/hooks/useRelationship';
|
||||
import type { ApiCollectionJSON } from 'mastodon/api_types/collections';
|
||||
import { openModal } from 'mastodon/actions/modal';
|
||||
import type {
|
||||
ApiCollectionJSON,
|
||||
CollectionAccountItem,
|
||||
} from 'mastodon/api_types/collections';
|
||||
import { Account } from 'mastodon/components/account';
|
||||
import { Button } from 'mastodon/components/button';
|
||||
import { DisplayName } from 'mastodon/components/display_name';
|
||||
import {
|
||||
Article,
|
||||
ItemList,
|
||||
} from 'mastodon/components/scrollable_list/components';
|
||||
import { useAccount } from 'mastodon/hooks/useAccount';
|
||||
import { useDismissible } from 'mastodon/hooks/useDismissible';
|
||||
import { useRelationship } from 'mastodon/hooks/useRelationship';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
import { useAppDispatch } from 'mastodon/store';
|
||||
|
||||
import classes from './styles.module.scss';
|
||||
|
||||
@@ -62,6 +68,52 @@ const AccountItem: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const RevokeControls: React.FC<{
|
||||
collectionId: string;
|
||||
collectionItem: CollectionAccountItem;
|
||||
}> = ({ collectionId, collectionItem }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const confirmRevoke = useCallback(() => {
|
||||
void dispatch(
|
||||
openModal({
|
||||
modalType: 'REVOKE_COLLECTION_INCLUSION',
|
||||
modalProps: {
|
||||
collectionId,
|
||||
collectionItemId: collectionItem.id,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}, [collectionId, collectionItem.id, dispatch]);
|
||||
|
||||
const { wasDismissed, dismiss } = useDismissible(
|
||||
`collection-revoke-hint-${collectionItem.id}`,
|
||||
);
|
||||
|
||||
if (wasDismissed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.revokeControlWrapper}>
|
||||
<Button secondary onClick={dismiss}>
|
||||
<FormattedMessage
|
||||
id='collections.detail.accept_inclusion'
|
||||
defaultMessage='Okay'
|
||||
tagName={Fragment}
|
||||
/>
|
||||
</Button>
|
||||
<Button secondary onClick={confirmRevoke}>
|
||||
<FormattedMessage
|
||||
id='collections.detail.revoke_inclusion'
|
||||
defaultMessage='Remove me'
|
||||
tagName={Fragment}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SensitiveScreen: React.FC<{
|
||||
sensitive: boolean | undefined;
|
||||
focusTargetRef: React.RefObject<HTMLHeadingElement>;
|
||||
@@ -166,6 +218,10 @@ export const CollectionAccountsList: React.FC<{
|
||||
accountId={currentUserInCollection.account_id}
|
||||
collectionOwnerId={collection.account_id}
|
||||
/>
|
||||
<RevokeControls
|
||||
collectionId={collection.id}
|
||||
collectionItem={currentUserInCollection}
|
||||
/>
|
||||
</Article>
|
||||
<h3
|
||||
className={classes.columnSubheading}
|
||||
@@ -33,6 +33,10 @@ const messages = defineMessages({
|
||||
id: 'collections.report_collection',
|
||||
defaultMessage: 'Report this collection',
|
||||
},
|
||||
revoke: {
|
||||
id: 'collections.revoke_collection_inclusion',
|
||||
defaultMessage: 'Remove myself from this collection',
|
||||
},
|
||||
more: { id: 'status.more', defaultMessage: 'More' },
|
||||
});
|
||||
|
||||
@@ -71,6 +75,22 @@ export const CollectionMenu: React.FC<{
|
||||
);
|
||||
}, [collection, dispatch]);
|
||||
|
||||
const currentAccountInCollection = collection.items.find(
|
||||
(item) => item.account_id === me,
|
||||
);
|
||||
|
||||
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(() => {
|
||||
if (isOwnCollection) {
|
||||
const commonItems: MenuItem[] = [
|
||||
@@ -99,34 +119,43 @@ export const CollectionMenu: React.FC<{
|
||||
} else {
|
||||
return commonItems;
|
||||
}
|
||||
} else if (ownerAccount) {
|
||||
const items: MenuItem[] = [
|
||||
{
|
||||
text: intl.formatMessage(messages.report),
|
||||
action: openReportModal,
|
||||
},
|
||||
];
|
||||
const featuredCollectionsPath = `/@${ownerAccount.acct}/featured`;
|
||||
// Don't show menu link to featured collections while on that very page
|
||||
if (
|
||||
!matchPath(location.pathname, {
|
||||
path: featuredCollectionsPath,
|
||||
exact: true,
|
||||
})
|
||||
) {
|
||||
items.unshift(
|
||||
...[
|
||||
{
|
||||
text: intl.formatMessage(messages.viewOtherCollections),
|
||||
to: featuredCollectionsPath,
|
||||
},
|
||||
null,
|
||||
],
|
||||
);
|
||||
}
|
||||
return items;
|
||||
} else {
|
||||
return [];
|
||||
const items: MenuItem[] = [];
|
||||
|
||||
if (ownerAccount) {
|
||||
const featuredCollectionsPath = `/@${ownerAccount.acct}/featured`;
|
||||
// Don't show menu link to featured collections while on that very page
|
||||
if (
|
||||
!matchPath(location.pathname, {
|
||||
path: featuredCollectionsPath,
|
||||
exact: true,
|
||||
})
|
||||
) {
|
||||
items.push(
|
||||
...[
|
||||
{
|
||||
text: intl.formatMessage(messages.viewOtherCollections),
|
||||
to: featuredCollectionsPath,
|
||||
},
|
||||
null,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentAccountInCollection) {
|
||||
items.push({
|
||||
text: intl.formatMessage(messages.revoke),
|
||||
action: openRevokeConfirmation,
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
text: intl.formatMessage(messages.report),
|
||||
action: openReportModal,
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
}, [
|
||||
isOwnCollection,
|
||||
@@ -134,6 +163,8 @@ export const CollectionMenu: React.FC<{
|
||||
id,
|
||||
openDeleteConfirmation,
|
||||
context,
|
||||
currentAccountInCollection,
|
||||
openRevokeConfirmation,
|
||||
ownerAccount,
|
||||
openReportModal,
|
||||
]);
|
||||
|
||||
@@ -24,7 +24,7 @@ import { me } from 'mastodon/initial_state';
|
||||
import { fetchCollection } from 'mastodon/reducers/slices/collections';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
import { CollectionAccountsList } from './collection_list';
|
||||
import { CollectionAccountsList } from './accounts_list';
|
||||
import { CollectionMetaData } from './collection_list_item';
|
||||
import { CollectionMenu } from './collection_menu';
|
||||
import classes from './styles.module.scss';
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import { showAlert } from 'mastodon/actions/alerts';
|
||||
import type { BaseConfirmationModalProps } from 'mastodon/features/ui/components/confirmation_modals/confirmation_modal';
|
||||
import { ConfirmationModal } from 'mastodon/features/ui/components/confirmation_modals/confirmation_modal';
|
||||
import { revokeCollectionInclusion } from 'mastodon/reducers/slices/collections';
|
||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||
|
||||
const messages = defineMessages({
|
||||
revokeCollectionInclusionTitle: {
|
||||
id: 'confirmations.revoke_collection_inclusion.title',
|
||||
defaultMessage: 'Remove yourself from this collection?',
|
||||
},
|
||||
revokeCollectionInclusionMessage: {
|
||||
id: 'confirmations.revoke_collection_inclusion.message',
|
||||
defaultMessage:
|
||||
"This action is permanent, and the curator won't be able to re-add you to the collection later on.",
|
||||
},
|
||||
revokeCollectionInclusionConfirm: {
|
||||
id: 'confirmations.revoke_collection_inclusion.confirm',
|
||||
defaultMessage: 'Remove me',
|
||||
},
|
||||
});
|
||||
|
||||
export const RevokeCollectionInclusionModal: React.FC<
|
||||
{
|
||||
collectionId: string;
|
||||
collectionItemId: string;
|
||||
} & BaseConfirmationModalProps
|
||||
> = ({ collectionId, collectionItemId, onClose }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const collectionName = useAppSelector(
|
||||
(state) => state.collections.collections[collectionId]?.name,
|
||||
);
|
||||
|
||||
const onConfirm = useCallback(async () => {
|
||||
try {
|
||||
await dispatch(
|
||||
revokeCollectionInclusion({
|
||||
collectionId,
|
||||
itemId: collectionItemId,
|
||||
}),
|
||||
).unwrap();
|
||||
|
||||
dispatch(
|
||||
showAlert({
|
||||
message: intl.formatMessage(
|
||||
{
|
||||
id: 'collections.revoke_inclusion.confirmation',
|
||||
defaultMessage: 'You\'ve been removed from "{collection}"',
|
||||
},
|
||||
{
|
||||
collection: collectionName,
|
||||
},
|
||||
),
|
||||
}),
|
||||
);
|
||||
} catch {
|
||||
dispatch(
|
||||
showAlert({
|
||||
message: intl.formatMessage({
|
||||
id: 'collections.revoke_inclusion.error',
|
||||
defaultMessage: 'There was an error, please try again later.',
|
||||
}),
|
||||
}),
|
||||
);
|
||||
}
|
||||
}, [dispatch, collectionId, collectionName, collectionItemId, intl]);
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
title={intl.formatMessage(messages.revokeCollectionInclusionTitle)}
|
||||
message={intl.formatMessage(messages.revokeCollectionInclusionMessage)}
|
||||
confirm={intl.formatMessage(messages.revokeCollectionInclusionConfirm)}
|
||||
onConfirm={onConfirm}
|
||||
onClose={onClose}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -103,3 +103,24 @@
|
||||
line-height: 1.5;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.revokeControlWrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: -10px;
|
||||
padding-bottom: 16px;
|
||||
padding-inline: calc(26px + var(--avatar-width)) 16px;
|
||||
|
||||
:global(.button) {
|
||||
min-width: 30%;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
--avatar-width: 46px;
|
||||
|
||||
@container (width < 360px) {
|
||||
--avatar-width: 35px;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -21,7 +21,7 @@ interface ConfirmationModalProps {
|
||||
cancel?: React.ReactNode;
|
||||
secondary?: React.ReactNode;
|
||||
onSecondary?: () => void;
|
||||
onConfirm: () => void;
|
||||
onConfirm: () => void | Promise<void>;
|
||||
noCloseOnConfirm?: boolean;
|
||||
extraContent?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
@@ -56,7 +56,7 @@ export const ConfirmationModal: React.FC<
|
||||
onClose();
|
||||
}
|
||||
|
||||
onConfirm();
|
||||
void onConfirm();
|
||||
}, [onClose, onConfirm, noCloseOnConfirm]);
|
||||
|
||||
const handleSecondary = useCallback(() => {
|
||||
|
||||
@@ -80,6 +80,7 @@ export const MODAL_COMPONENTS = {
|
||||
'REPORT': ReportModal,
|
||||
'REPORT_COLLECTION': ReportCollectionModal,
|
||||
'SHARE_COLLECTION': () => import('@/mastodon/features/collections/detail/share_modal').then(module => ({ default: module.CollectionShareModal })),
|
||||
'REVOKE_COLLECTION_INCLUSION': () => import('@/mastodon/features/collections/detail/revoke_collection_inclusion_modal').then(module => ({ default: module.RevokeCollectionInclusionModal })),
|
||||
'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
|
||||
'EMBED': EmbedModal,
|
||||
'FOCAL_POINT': () => Promise.resolve({ default: AltTextModal }),
|
||||
|
||||
Reference in new issue
Block a user