182 lines
5.2 KiB
TypeScript
182 lines
5.2 KiB
TypeScript
import { useEffect } from 'react';
|
|
|
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
|
|
import {
|
|
Switch,
|
|
Route,
|
|
useParams,
|
|
useRouteMatch,
|
|
matchPath,
|
|
useLocation,
|
|
} from 'react-router-dom';
|
|
|
|
import { Helmet } from '@unhead/react/helmet';
|
|
|
|
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
|
|
import { Callout } from 'mastodon/components/callout';
|
|
import { Column } from 'mastodon/components/column';
|
|
import { ColumnHeader } from 'mastodon/components/column_header';
|
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
|
import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator';
|
|
import { useIdentity } from 'mastodon/identity_context';
|
|
import { initialState } from 'mastodon/initial_state';
|
|
import {
|
|
collectionEditorActions,
|
|
fetchCollection,
|
|
} from 'mastodon/reducers/slices/collections';
|
|
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
|
|
|
import { useCollectionsCreatedBy } from '../overview/created_by_account';
|
|
|
|
import { CollectionAccounts } from './accounts';
|
|
import { CollectionDetails } from './details';
|
|
import classes from './styles.module.scss';
|
|
|
|
export const messages = defineMessages({
|
|
create: {
|
|
id: 'collections.create_collection',
|
|
defaultMessage: 'Create collection',
|
|
},
|
|
newCollection: {
|
|
id: 'collections.new_collection',
|
|
defaultMessage: 'New collection',
|
|
},
|
|
editDetails: {
|
|
id: 'collections.edit_details',
|
|
defaultMessage: 'Edit details',
|
|
},
|
|
manageAccounts: {
|
|
id: 'collections.manage_accounts',
|
|
defaultMessage: 'Manage accounts',
|
|
},
|
|
});
|
|
|
|
function usePageTitle(id: string | null) {
|
|
const { path } = useRouteMatch();
|
|
const location = useLocation();
|
|
|
|
if (!id) {
|
|
return messages.newCollection;
|
|
}
|
|
|
|
if (matchPath(location.pathname, { path, exact: true })) {
|
|
return messages.manageAccounts;
|
|
} else if (matchPath(location.pathname, { path: `${path}/details` })) {
|
|
return messages.editDetails;
|
|
} else {
|
|
throw new Error('No page title defined for route');
|
|
}
|
|
}
|
|
|
|
export const userCollectionLimit = initialState?.role?.collection_limit ?? 0;
|
|
|
|
export const CollectionEditorPage: React.FC<{
|
|
multiColumn?: boolean;
|
|
}> = ({ multiColumn }) => {
|
|
const intl = useIntl();
|
|
const dispatch = useAppDispatch();
|
|
const { accountId, signedIn } = useIdentity();
|
|
const { id = null } = useParams<{ id?: string }>();
|
|
const { path } = useRouteMatch();
|
|
const collection = useAppSelector((state) =>
|
|
id ? state.collections.collections[id] : undefined,
|
|
);
|
|
const editorStateId = useAppSelector((state) => state.collections.editor.id);
|
|
const isEditMode = !!id;
|
|
|
|
// When creating a new collection, we load the current account's collections
|
|
// to determine if they're allowed to create more.
|
|
const { collections: collectionList, status: collectionListStatus } =
|
|
useCollectionsCreatedBy(isEditMode ? null : accountId);
|
|
|
|
const isLoading =
|
|
(isEditMode && !collection) ||
|
|
(!isEditMode && collectionListStatus === 'loading');
|
|
|
|
const canCreateMoreCollections =
|
|
signedIn && (isEditMode || collectionList.length < userCollectionLimit);
|
|
|
|
useEffect(() => {
|
|
if (id && signedIn) {
|
|
void dispatch(fetchCollection({ collectionId: id }));
|
|
}
|
|
}, [dispatch, id, signedIn]);
|
|
|
|
useEffect(() => {
|
|
if (id !== editorStateId) {
|
|
void dispatch(collectionEditorActions.reset());
|
|
}
|
|
}, [dispatch, editorStateId, id]);
|
|
|
|
useEffect(() => {
|
|
if (collection) {
|
|
void dispatch(collectionEditorActions.init(collection));
|
|
}
|
|
}, [dispatch, collection]);
|
|
|
|
const pageTitle = intl.formatMessage(usePageTitle(id));
|
|
|
|
return (
|
|
<Column bindToDocument={!multiColumn} label={pageTitle}>
|
|
<ColumnHeader
|
|
title={pageTitle}
|
|
icon='list-ul'
|
|
iconComponent={ListAltIcon}
|
|
multiColumn={multiColumn}
|
|
showBackButton
|
|
/>
|
|
|
|
<div className='scrollable'>
|
|
{isLoading ? (
|
|
<LoadingIndicator />
|
|
) : !signedIn ? (
|
|
<NotSignedInIndicator />
|
|
) : canCreateMoreCollections ? (
|
|
<Switch>
|
|
<Route
|
|
exact
|
|
path={path}
|
|
// eslint-disable-next-line react/jsx-no-bind
|
|
render={() => <CollectionAccounts collection={collection} />}
|
|
/>
|
|
<Route
|
|
exact
|
|
path={`${path}/details`}
|
|
// eslint-disable-next-line react/jsx-no-bind
|
|
render={() => <CollectionDetails />}
|
|
/>
|
|
</Switch>
|
|
) : (
|
|
<MaxCollectionsCallout className={classes.maxCollectionsError} />
|
|
)}
|
|
</div>
|
|
|
|
<Helmet>
|
|
<title>{pageTitle}</title>
|
|
<meta name='robots' content='noindex' />
|
|
</Helmet>
|
|
</Column>
|
|
);
|
|
};
|
|
|
|
export const MaxCollectionsCallout: React.FC<{ className?: string }> = ({
|
|
className,
|
|
}) => (
|
|
<Callout
|
|
className={className}
|
|
title={
|
|
<FormattedMessage
|
|
id='collections.maximum_collection_count_reached'
|
|
defaultMessage='You have created the maximum number of collections'
|
|
/>
|
|
}
|
|
>
|
|
<FormattedMessage
|
|
id='collections.maximum_collection_count_description'
|
|
defaultMessage='Your server allows creation of up to {count} collections.'
|
|
values={{ count: userCollectionLimit }}
|
|
/>
|
|
</Callout>
|
|
);
|