diff --git a/app/javascript/flavours/glitch/features/collections/detail/collection_menu.tsx b/app/javascript/flavours/glitch/features/collections/detail/collection_menu.tsx index b140239645..4294625949 100644 --- a/app/javascript/flavours/glitch/features/collections/detail/collection_menu.tsx +++ b/app/javascript/flavours/glitch/features/collections/detail/collection_menu.tsx @@ -2,11 +2,13 @@ import { useCallback, useMemo } from 'react'; import { defineMessages, useIntl } from 'react-intl'; +import { useAccount } from '@/flavours/glitch/hooks/useAccount'; import MoreVertIcon from '@/material-icons/400-24px/more_vert.svg?react'; import { openModal } from 'flavours/glitch/actions/modal'; import type { ApiCollectionJSON } from 'flavours/glitch/api_types/collections'; import { Dropdown } from 'flavours/glitch/components/dropdown_menu'; import { IconButton } from 'flavours/glitch/components/icon_button'; +import { me } from 'flavours/glitch/initial_state'; import { useAppDispatch } from 'flavours/glitch/store'; import { messages as editorMessages } from '../editor'; @@ -16,10 +18,18 @@ const messages = defineMessages({ id: 'collections.view_collection', defaultMessage: 'View collection', }, + viewOtherCollections: { + id: 'collections.view_other_collections_by_user', + defaultMessage: 'View other collections by this user', + }, delete: { id: 'collections.delete_collection', defaultMessage: 'Delete collection', }, + report: { + id: 'collections.report_collection', + defaultMessage: 'Report this collection', + }, more: { id: 'status.more', defaultMessage: 'More' }, }); @@ -31,9 +41,11 @@ export const CollectionMenu: React.FC<{ const dispatch = useAppDispatch(); const intl = useIntl(); - const { id, name } = collection; + const { id, name, account_id } = collection; + const isOwnCollection = account_id === me; + const ownerAccount = useAccount(account_id); - const handleDeleteClick = useCallback(() => { + const openDeleteConfirmation = useCallback(() => { dispatch( openModal({ modalType: 'CONFIRM_DELETE_COLLECTION', @@ -45,34 +57,69 @@ export const CollectionMenu: React.FC<{ ); }, [dispatch, id, name]); - const menu = useMemo(() => { - const commonItems = [ - { - 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: handleDeleteClick, - dangerous: true, - }, - ]; + const openReportModal = useCallback(() => { + dispatch( + openModal({ + modalType: 'REPORT_COLLECTION', + modalProps: { + collection, + }, + }), + ); + }, [collection, dispatch]); - if (context === 'list') { - return [ - { text: intl.formatMessage(messages.view), to: `/collections/${id}` }, + const menu = useMemo(() => { + if (isOwnCollection) { + const commonItems = [ + { + text: intl.formatMessage(editorMessages.manageAccounts), + to: `/collections/${id}/edit`, + }, + { + text: intl.formatMessage(editorMessages.editDetails), + to: `/collections/${id}/edit/details`, + }, null, - ...commonItems, + { + text: intl.formatMessage(messages.delete), + action: openDeleteConfirmation, + dangerous: true, + }, + ]; + + if (context === 'list') { + return [ + { text: intl.formatMessage(messages.view), to: `/collections/${id}` }, + null, + ...commonItems, + ]; + } else { + return commonItems; + } + } else if (ownerAccount) { + return [ + { + text: intl.formatMessage(messages.viewOtherCollections), + to: `/@${ownerAccount.acct}/featured`, + }, + null, + { + text: intl.formatMessage(messages.report), + action: openReportModal, + }, ]; } else { - return commonItems; + return []; } - }, [intl, id, handleDeleteClick, context]); + }, [ + isOwnCollection, + intl, + id, + openDeleteConfirmation, + context, + ownerAccount, + openReportModal, + ]); return ( diff --git a/app/javascript/flavours/glitch/features/collections/detail/index.tsx b/app/javascript/flavours/glitch/features/collections/detail/index.tsx index eb47acc90e..4f061ddd07 100644 --- a/app/javascript/flavours/glitch/features/collections/detail/index.tsx +++ b/app/javascript/flavours/glitch/features/collections/detail/index.tsx @@ -79,7 +79,7 @@ const CollectionHeader: React.FC<{ collection: ApiCollectionJSON }> = ({ collection, }) => { const intl = useIntl(); - const { name, description, tag } = collection; + const { name, description, tag, account_id } = collection; const dispatch = useAppDispatch(); const handleShare = useCallback(() => { @@ -114,7 +114,7 @@ const CollectionHeader: React.FC<{ collection: ApiCollectionJSON }> = ({ {description &&

{description}

} diff --git a/app/javascript/flavours/glitch/features/report/comment.jsx b/app/javascript/flavours/glitch/features/report/comment.jsx deleted file mode 100644 index 40079b2c68..0000000000 --- a/app/javascript/flavours/glitch/features/report/comment.jsx +++ /dev/null @@ -1,121 +0,0 @@ -import PropTypes from 'prop-types'; -import { useCallback, useEffect, useRef } from 'react'; - -import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; - -import { createSelector } from '@reduxjs/toolkit'; -import { OrderedSet, List as ImmutableList } from 'immutable'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { shallowEqual } from 'react-redux'; - -import Toggle from 'react-toggle'; - -import { fetchAccount } from 'flavours/glitch/actions/accounts'; -import { Button } from 'flavours/glitch/components/button'; -import { useAppDispatch, useAppSelector } from 'flavours/glitch/store'; - -const messages = defineMessages({ - placeholder: { id: 'report.placeholder', defaultMessage: 'Type or paste additional comments' }, -}); - -const selectRepliedToAccountIds = createSelector( - [ - (state) => state.get('statuses'), - (_, statusIds) => statusIds, - ], - (statusesMap, statusIds) => statusIds.map((statusId) => statusesMap.getIn([statusId, 'in_reply_to_account_id'])), - { - resultEqualityCheck: shallowEqual, - } -); - -const Comment = ({ comment, domain, statusIds, isRemote, isSubmitting, selectedDomains, onSubmit, onChangeComment, onToggleDomain }) => { - const intl = useIntl(); - - const dispatch = useAppDispatch(); - const loadedRef = useRef(false); - - const handleClick = useCallback(() => onSubmit(), [onSubmit]); - const handleChange = useCallback((e) => onChangeComment(e.target.value), [onChangeComment]); - const handleToggleDomain = useCallback(e => onToggleDomain(e.target.value, e.target.checked), [onToggleDomain]); - - const handleKeyDown = useCallback((e) => { - if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { - handleClick(); - } - }, [handleClick]); - - // Memoize accountIds since we don't want it to trigger `useEffect` on each render - const accountIds = useAppSelector((state) => domain ? selectRepliedToAccountIds(state, statusIds) : ImmutableList()); - - // While we could memoize `availableDomains`, it is pretty inexpensive to recompute - const accountsMap = useAppSelector((state) => state.get('accounts')); - const availableDomains = domain ? OrderedSet([domain]).union(accountIds.map((accountId) => accountsMap.getIn([accountId, 'acct'], '').split('@')[1]).filter(domain => !!domain)) : OrderedSet(); - - useEffect(() => { - if (loadedRef.current) { - return; - } - - loadedRef.current = true; - - // First, pre-select known domains - availableDomains.forEach((domain) => { - onToggleDomain(domain, true); - }); - - // Then, fetch missing replied-to accounts - const unknownAccounts = OrderedSet(accountIds.filter(accountId => accountId && !accountsMap.has(accountId))); - unknownAccounts.forEach((accountId) => { - dispatch(fetchAccount(accountId)); - }); - }); - - return ( - <> -

- -