diff --git a/app/javascript/mastodon/actions/domain_blocks.js b/app/javascript/mastodon/actions/domain_blocks.js index 727f800af3..279ec1bef7 100644 --- a/app/javascript/mastodon/actions/domain_blocks.js +++ b/app/javascript/mastodon/actions/domain_blocks.js @@ -12,14 +12,6 @@ export const DOMAIN_BLOCK_FAIL = 'DOMAIN_BLOCK_FAIL'; export const DOMAIN_UNBLOCK_REQUEST = 'DOMAIN_UNBLOCK_REQUEST'; export const DOMAIN_UNBLOCK_FAIL = 'DOMAIN_UNBLOCK_FAIL'; -export const DOMAIN_BLOCKS_FETCH_REQUEST = 'DOMAIN_BLOCKS_FETCH_REQUEST'; -export const DOMAIN_BLOCKS_FETCH_SUCCESS = 'DOMAIN_BLOCKS_FETCH_SUCCESS'; -export const DOMAIN_BLOCKS_FETCH_FAIL = 'DOMAIN_BLOCKS_FETCH_FAIL'; - -export const DOMAIN_BLOCKS_EXPAND_REQUEST = 'DOMAIN_BLOCKS_EXPAND_REQUEST'; -export const DOMAIN_BLOCKS_EXPAND_SUCCESS = 'DOMAIN_BLOCKS_EXPAND_SUCCESS'; -export const DOMAIN_BLOCKS_EXPAND_FAIL = 'DOMAIN_BLOCKS_EXPAND_FAIL'; - export function blockDomain(domain) { return (dispatch, getState) => { dispatch(blockDomainRequest(domain)); @@ -79,80 +71,6 @@ export function unblockDomainFail(domain, error) { }; } -export function fetchDomainBlocks() { - return (dispatch) => { - dispatch(fetchDomainBlocksRequest()); - - api().get('/api/v1/domain_blocks').then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(fetchDomainBlocksSuccess(response.data, next ? next.uri : null)); - }).catch(err => { - dispatch(fetchDomainBlocksFail(err)); - }); - }; -} - -export function fetchDomainBlocksRequest() { - return { - type: DOMAIN_BLOCKS_FETCH_REQUEST, - }; -} - -export function fetchDomainBlocksSuccess(domains, next) { - return { - type: DOMAIN_BLOCKS_FETCH_SUCCESS, - domains, - next, - }; -} - -export function fetchDomainBlocksFail(error) { - return { - type: DOMAIN_BLOCKS_FETCH_FAIL, - error, - }; -} - -export function expandDomainBlocks() { - return (dispatch, getState) => { - const url = getState().getIn(['domain_lists', 'blocks', 'next']); - - if (!url) { - return; - } - - dispatch(expandDomainBlocksRequest()); - - api().get(url).then(response => { - const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(expandDomainBlocksSuccess(response.data, next ? next.uri : null)); - }).catch(err => { - dispatch(expandDomainBlocksFail(err)); - }); - }; -} - -export function expandDomainBlocksRequest() { - return { - type: DOMAIN_BLOCKS_EXPAND_REQUEST, - }; -} - -export function expandDomainBlocksSuccess(domains, next) { - return { - type: DOMAIN_BLOCKS_EXPAND_SUCCESS, - domains, - next, - }; -} - -export function expandDomainBlocksFail(error) { - return { - type: DOMAIN_BLOCKS_EXPAND_FAIL, - error, - }; -} - export const initDomainBlockModal = account => dispatch => dispatch(openModal({ modalType: 'DOMAIN_BLOCK', modalProps: { diff --git a/app/javascript/mastodon/api/domain_blocks.ts b/app/javascript/mastodon/api/domain_blocks.ts new file mode 100644 index 0000000000..4e153b0ee9 --- /dev/null +++ b/app/javascript/mastodon/api/domain_blocks.ts @@ -0,0 +1,13 @@ +import api, { getLinks } from 'mastodon/api'; + +export const apiGetDomainBlocks = async (url?: string) => { + const response = await api().request({ + method: 'GET', + url: url ?? '/api/v1/domain_blocks', + }); + + return { + domains: response.data, + links: getLinks(response), + }; +}; diff --git a/app/javascript/mastodon/components/domain.tsx b/app/javascript/mastodon/components/domain.tsx index aa64f0f8c3..0ccffac482 100644 --- a/app/javascript/mastodon/components/domain.tsx +++ b/app/javascript/mastodon/components/domain.tsx @@ -1,24 +1,15 @@ import { useCallback } from 'react'; -import { defineMessages, useIntl } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; -import LockOpenIcon from '@/material-icons/400-24px/lock_open.svg?react'; import { unblockDomain } from 'mastodon/actions/domain_blocks'; import { useAppDispatch } from 'mastodon/store'; -import { IconButton } from './icon_button'; - -const messages = defineMessages({ - unblockDomain: { - id: 'account.unblock_domain', - defaultMessage: 'Unblock domain {domain}', - }, -}); +import { Button } from './button'; export const Domain: React.FC<{ domain: string; }> = ({ domain }) => { - const intl = useIntl(); const dispatch = useAppDispatch(); const handleDomainUnblock = useCallback(() => { @@ -27,20 +18,17 @@ export const Domain: React.FC<{ return (
-
- - {domain} - +
+ {domain} +
-
- +
+
); diff --git a/app/javascript/mastodon/features/domain_blocks/index.jsx b/app/javascript/mastodon/features/domain_blocks/index.jsx deleted file mode 100644 index 3656596806..0000000000 --- a/app/javascript/mastodon/features/domain_blocks/index.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import PropTypes from 'prop-types'; - -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; - -import { Helmet } from 'react-helmet'; - -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { connect } from 'react-redux'; - -import { debounce } from 'lodash'; - -import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react'; -import { Domain } from 'mastodon/components/domain'; - -import { fetchDomainBlocks, expandDomainBlocks } from '../../actions/domain_blocks'; -import { LoadingIndicator } from '../../components/loading_indicator'; -import ScrollableList from '../../components/scrollable_list'; -import Column from '../ui/components/column'; - -const messages = defineMessages({ - heading: { id: 'column.domain_blocks', defaultMessage: 'Blocked domains' }, -}); - -const mapStateToProps = state => ({ - domains: state.getIn(['domain_lists', 'blocks', 'items']), - hasMore: !!state.getIn(['domain_lists', 'blocks', 'next']), -}); - -class Blocks extends ImmutablePureComponent { - - static propTypes = { - params: PropTypes.object.isRequired, - dispatch: PropTypes.func.isRequired, - hasMore: PropTypes.bool, - domains: ImmutablePropTypes.orderedSet, - intl: PropTypes.object.isRequired, - multiColumn: PropTypes.bool, - }; - - UNSAFE_componentWillMount () { - this.props.dispatch(fetchDomainBlocks()); - } - - handleLoadMore = debounce(() => { - this.props.dispatch(expandDomainBlocks()); - }, 300, { leading: true }); - - render () { - const { intl, domains, hasMore, multiColumn } = this.props; - - if (!domains) { - return ( - - - - ); - } - - const emptyMessage = ; - - return ( - - - {domains.map(domain => - , - )} - - - - - - - ); - } - -} - -export default connect(mapStateToProps)(injectIntl(Blocks)); diff --git a/app/javascript/mastodon/features/domain_blocks/index.tsx b/app/javascript/mastodon/features/domain_blocks/index.tsx new file mode 100644 index 0000000000..900aba4745 --- /dev/null +++ b/app/javascript/mastodon/features/domain_blocks/index.tsx @@ -0,0 +1,113 @@ +import { useEffect, useRef, useCallback, useState } from 'react'; + +import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; + +import { Helmet } from 'react-helmet'; + +import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react'; +import { apiGetDomainBlocks } from 'mastodon/api/domain_blocks'; +import { Column } from 'mastodon/components/column'; +import type { ColumnRef } from 'mastodon/components/column'; +import { ColumnHeader } from 'mastodon/components/column_header'; +import { Domain } from 'mastodon/components/domain'; +import ScrollableList from 'mastodon/components/scrollable_list'; + +const messages = defineMessages({ + heading: { id: 'column.domain_blocks', defaultMessage: 'Blocked domains' }, +}); + +const Blocks: React.FC<{ multiColumn: boolean }> = ({ multiColumn }) => { + const intl = useIntl(); + const [domains, setDomains] = useState([]); + const [loading, setLoading] = useState(false); + const [next, setNext] = useState(); + const hasMore = !!next; + const columnRef = useRef(null); + + useEffect(() => { + setLoading(true); + + void apiGetDomainBlocks() + .then(({ domains, links }) => { + const next = links.refs.find((link) => link.rel === 'next'); + + setLoading(false); + setDomains(domains); + setNext(next?.uri); + + return ''; + }) + .catch(() => { + setLoading(false); + }); + }, [setLoading, setDomains, setNext]); + + const handleLoadMore = useCallback(() => { + setLoading(true); + + void apiGetDomainBlocks(next) + .then(({ domains, links }) => { + const next = links.refs.find((link) => link.rel === 'next'); + + setLoading(false); + setDomains((previousDomains) => [...previousDomains, ...domains]); + setNext(next?.uri); + + return ''; + }) + .catch(() => { + setLoading(false); + }); + }, [setLoading, setDomains, setNext, next]); + + const handleHeaderClick = useCallback(() => { + columnRef.current?.scrollTop(); + }, []); + + const emptyMessage = ( + + ); + + return ( + + + + + {domains.map((domain) => ( + + ))} + + + + {intl.formatMessage(messages.heading)} + + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export default Blocks; diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 424983840e..ebd5412cf2 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -65,6 +65,7 @@ "account.statuses_counter": "{count, plural, one {{counter} post} other {{counter} posts}}", "account.unblock": "Unblock @{name}", "account.unblock_domain": "Unblock domain {domain}", + "account.unblock_domain_short": "Unblock", "account.unblock_short": "Unblock", "account.unendorse": "Don't feature on profile", "account.unfollow": "Unfollow", diff --git a/app/javascript/mastodon/reducers/domain_lists.js b/app/javascript/mastodon/reducers/domain_lists.js deleted file mode 100644 index 5f63c77f5d..0000000000 --- a/app/javascript/mastodon/reducers/domain_lists.js +++ /dev/null @@ -1,26 +0,0 @@ -import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable'; - -import { - DOMAIN_BLOCKS_FETCH_SUCCESS, - DOMAIN_BLOCKS_EXPAND_SUCCESS, - unblockDomainSuccess -} from '../actions/domain_blocks'; - -const initialState = ImmutableMap({ - blocks: ImmutableMap({ - items: ImmutableOrderedSet(), - }), -}); - -export default function domainLists(state = initialState, action) { - switch(action.type) { - case DOMAIN_BLOCKS_FETCH_SUCCESS: - return state.setIn(['blocks', 'items'], ImmutableOrderedSet(action.domains)).setIn(['blocks', 'next'], action.next); - case DOMAIN_BLOCKS_EXPAND_SUCCESS: - return state.updateIn(['blocks', 'items'], set => set.union(action.domains)).setIn(['blocks', 'next'], action.next); - case unblockDomainSuccess.type: - return state.updateIn(['blocks', 'items'], set => set.delete(action.payload.domain)); - default: - return state; - } -} diff --git a/app/javascript/mastodon/reducers/index.ts b/app/javascript/mastodon/reducers/index.ts index 08ec2e58a4..cd5f55a868 100644 --- a/app/javascript/mastodon/reducers/index.ts +++ b/app/javascript/mastodon/reducers/index.ts @@ -11,7 +11,6 @@ import { composeReducer } from './compose'; import contexts from './contexts'; import conversations from './conversations'; import custom_emojis from './custom_emojis'; -import domain_lists from './domain_lists'; import { dropdownMenuReducer } from './dropdown_menu'; import filters from './filters'; import followed_tags from './followed_tags'; @@ -49,7 +48,6 @@ const reducers = { loadingBar: loadingBarReducer, modal: modalReducer, user_lists, - domain_lists, status_lists, accounts: accountsReducer, accounts_map, diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 6b5cbaa08b..66513750e6 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1882,29 +1882,21 @@ body > [data-popper-placement] { } .domain { - padding: 10px; + padding: 16px; border-bottom: 1px solid var(--background-border-color); + display: flex; + align-items: center; + gap: 8px; - .domain__domain-name { + &__domain-name { flex: 1 1 auto; - display: block; color: $primary-text-color; - text-decoration: none; - font-size: 14px; + font-size: 15px; + line-height: 21px; font-weight: 500; } } -.domain__wrapper { - display: flex; -} - -.domain_buttons { - height: 18px; - padding: 10px; - white-space: nowrap; -} - .account { padding: 16px; border-bottom: 1px solid var(--background-border-color);