diff --git a/app/soapbox/actions/aliases.js b/app/soapbox/actions/aliases.js index e28817681..3b898ae10 100644 --- a/app/soapbox/actions/aliases.js +++ b/app/soapbox/actions/aliases.js @@ -93,7 +93,7 @@ export const changeAliasesSuggestions = value => ({ value, }); -export const addToAliases = (intl, account) => (dispatch, getState) => { +export const addToAliases = (account) => (dispatch, getState) => { if (!isLoggedIn(getState)) return; const state = getState(); @@ -108,7 +108,7 @@ export const addToAliases = (intl, account) => (dispatch, getState) => { api(getState).patch('/api/v1/accounts/update_credentials', { also_known_as: [...alsoKnownAs, account.getIn(['pleroma', 'ap_id'])] }) .then((response => { - dispatch(snackbar.success(intl.formatMessage(messages.createSuccess))); + dispatch(snackbar.success(messages.createSuccess)); dispatch(addToAliasesSuccess); dispatch(patchMeSuccess(response.data)); })) @@ -123,7 +123,7 @@ export const addToAliases = (intl, account) => (dispatch, getState) => { alias: account.get('acct'), }) .then(response => { - dispatch(snackbar.success(intl.formatMessage(messages.createSuccess))); + dispatch(snackbar.success(messages.createSuccess)); dispatch(addToAliasesSuccess); dispatch(fetchAliases); }) @@ -143,7 +143,7 @@ export const addToAliasesFail = error => ({ error, }); -export const removeFromAliases = (intl, account) => (dispatch, getState) => { +export const removeFromAliases = (account) => (dispatch, getState) => { if (!isLoggedIn(getState)) return; const state = getState(); @@ -158,7 +158,7 @@ export const removeFromAliases = (intl, account) => (dispatch, getState) => { api(getState).patch('/api/v1/accounts/update_credentials', { also_known_as: alsoKnownAs.filter(id => id !== account) }) .then(response => { - dispatch(snackbar.success(intl.formatMessage(messages.removeSuccess))); + dispatch(snackbar.success(messages.removeSuccess)); dispatch(removeFromAliasesSuccess); dispatch(patchMeSuccess(response.data)); }) @@ -175,7 +175,7 @@ export const removeFromAliases = (intl, account) => (dispatch, getState) => { }, }) .then(response => { - dispatch(snackbar.success(intl.formatMessage(messages.removeSuccess))); + dispatch(snackbar.success(messages.removeSuccess)); dispatch(removeFromAliasesSuccess); dispatch(fetchAliases); }) diff --git a/app/soapbox/features/admin/moderation_log.js b/app/soapbox/features/admin/moderation_log.js deleted file mode 100644 index a4f50ddbb..000000000 --- a/app/soapbox/features/admin/moderation_log.js +++ /dev/null @@ -1,95 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { defineMessages, injectIntl, FormattedDate } from 'react-intl'; -import { connect } from 'react-redux'; - -import { fetchModerationLog } from 'soapbox/actions/admin'; -import ScrollableList from 'soapbox/components/scrollable_list'; - -import Column from '../ui/components/column'; - -const messages = defineMessages({ - heading: { id: 'column.admin.moderation_log', defaultMessage: 'Moderation Log' }, - emptyMessage: { id: 'admin.moderation_log.empty_message', defaultMessage: 'You have not performed any moderation actions yet. When you do, a history will be shown here.' }, -}); - -const mapStateToProps = state => ({ - items: state.getIn(['admin_log', 'index']).map(i => state.getIn(['admin_log', 'items', String(i)])), - hasMore: state.getIn(['admin_log', 'total'], 0) - state.getIn(['admin_log', 'index']).count() > 0, -}); - -export default @connect(mapStateToProps) -@injectIntl -class ModerationLog extends ImmutablePureComponent { - - static propTypes = { - intl: PropTypes.object.isRequired, - list: ImmutablePropTypes.list, - }; - - state = { - isLoading: true, - lastPage: 0, - } - - componentDidMount() { - const { dispatch } = this.props; - dispatch(fetchModerationLog()) - .then(data => this.setState({ - isLoading: false, - lastPage: 1, - })) - .catch(() => {}); - } - - handleLoadMore = () => { - const page = this.state.lastPage + 1; - - this.setState({ isLoading: true }); - this.props.dispatch(fetchModerationLog({ page })) - .then(data => this.setState({ - isLoading: false, - lastPage: page, - })) - .catch(() => {}); - } - - render() { - const { intl, items, hasMore } = this.props; - const { isLoading } = this.state; - const showLoading = isLoading && items.count() === 0; - - return ( - - - {items.map((item, i) => ( -
-
{item.get('message')}
-
- -
-
- ))} -
-
- ); - } - -} diff --git a/app/soapbox/features/admin/moderation_log.tsx b/app/soapbox/features/admin/moderation_log.tsx new file mode 100644 index 000000000..83163a382 --- /dev/null +++ b/app/soapbox/features/admin/moderation_log.tsx @@ -0,0 +1,80 @@ +import React, { useEffect, useState } from 'react'; +import { defineMessages, FormattedDate, useIntl } from 'react-intl'; + +import { fetchModerationLog } from 'soapbox/actions/admin'; +import ScrollableList from 'soapbox/components/scrollable_list'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; + +import Column from '../ui/components/column'; + +import type { Map as ImmutableMap } from 'immutable'; + +const messages = defineMessages({ + heading: { id: 'column.admin.moderation_log', defaultMessage: 'Moderation Log' }, + emptyMessage: { id: 'admin.moderation_log.empty_message', defaultMessage: 'You have not performed any moderation actions yet. When you do, a history will be shown here.' }, +}); + +const ModerationLog = () => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + + const items = useAppSelector((state) => state.admin_log.get('index').map((i: number) => state.admin_log.getIn(['items', String(i)]))) as ImmutableMap; + const hasMore = useAppSelector((state) => state.admin_log.get('total', 0) - state.admin_log.get('index').count() > 0); + + const [isLoading, setIsLoading] = useState(true); + const [lastPage, setLastPage] = useState(0); + + const showLoading = isLoading && items.count() === 0; + + useEffect(() => { + dispatch(fetchModerationLog()) + .then(() => { + setIsLoading(false); + setLastPage(1); + }) + .catch(() => {}); + }, []); + + const handleLoadMore = () => { + const page = lastPage + 1; + + setIsLoading(true); + dispatch(fetchModerationLog({ page })) + .then(() => { + setIsLoading(false); + setLastPage(page); + }).catch(() => {}); + }; + + return ( + + + {items.map((item, i) => ( +
+
{item.get('message')}
+
+ +
+
+ ))} +
+
+ ); +}; + +export default ModerationLog; diff --git a/app/soapbox/features/aliases/components/account.js b/app/soapbox/features/aliases/components/account.js deleted file mode 100644 index 8d0ccfbe3..000000000 --- a/app/soapbox/features/aliases/components/account.js +++ /dev/null @@ -1,92 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { defineMessages, injectIntl } from 'react-intl'; -import { connect } from 'react-redux'; - -import { addToAliases } from 'soapbox/actions/aliases'; -import Avatar from 'soapbox/components/avatar'; -import DisplayName from 'soapbox/components/display-name'; -import IconButton from 'soapbox/components/icon_button'; -import { makeGetAccount } from 'soapbox/selectors'; -import { getFeatures } from 'soapbox/utils/features'; - -const messages = defineMessages({ - add: { id: 'aliases.account.add', defaultMessage: 'Create alias' }, -}); - -const makeMapStateToProps = () => { - const getAccount = makeGetAccount(); - - const mapStateToProps = (state, { accountId, added, aliases }) => { - const me = state.get('me'); - - const instance = state.get('instance'); - const features = getFeatures(instance); - - const account = getAccount(state, accountId); - const apId = account.getIn(['pleroma', 'ap_id']); - const name = features.accountMoving ? account.get('acct') : apId; - - return { - account, - apId, - added: typeof added === 'undefined' ? aliases.includes(name) : added, - me, - }; - }; - - return mapStateToProps; -}; - -const mapDispatchToProps = (dispatch) => ({ - onAdd: (intl, apId) => dispatch(addToAliases(intl, apId)), -}); - -export default @connect(makeMapStateToProps, mapDispatchToProps) -@injectIntl -class Account extends ImmutablePureComponent { - - static propTypes = { - account: ImmutablePropTypes.record.isRequired, - apId: PropTypes.string.isRequired, - intl: PropTypes.object.isRequired, - onAdd: PropTypes.func.isRequired, - added: PropTypes.bool, - }; - - static defaultProps = { - added: false, - }; - - handleOnAdd = () => this.props.onAdd(this.props.intl, this.props.account); - - render() { - const { account, accountId, intl, added, me } = this.props; - - let button; - - if (!added && accountId !== me) { - button = ( -
- -
- ); - } - - return ( -
-
-
-
- -
- - {button} -
-
- ); - } - -} diff --git a/app/soapbox/features/aliases/components/account.tsx b/app/soapbox/features/aliases/components/account.tsx new file mode 100644 index 000000000..8fe0a64a7 --- /dev/null +++ b/app/soapbox/features/aliases/components/account.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { defineMessages, useIntl } from 'react-intl'; + +import { addToAliases } from 'soapbox/actions/aliases'; +import Avatar from 'soapbox/components/avatar'; +import DisplayName from 'soapbox/components/display-name'; +import IconButton from 'soapbox/components/icon_button'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { makeGetAccount } from 'soapbox/selectors'; +import { getFeatures } from 'soapbox/utils/features'; + +import type { List as ImmutableList } from 'immutable'; + +const messages = defineMessages({ + add: { id: 'aliases.account.add', defaultMessage: 'Create alias' }, +}); + +const getAccount = makeGetAccount(); + +interface IAccount { + accountId: string, + aliases: ImmutableList +} + +const Account: React.FC = ({ accountId, aliases }) => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + + const account = useAppSelector((state) => getAccount(state, accountId)); + const added = useAppSelector((state) => { + const instance = state.instance; + const features = getFeatures(instance); + + const account = getAccount(state, accountId); + const apId = account?.pleroma.get('ap_id'); + const name = features.accountMoving ? account?.acct : apId; + + return aliases.includes(name); + }); + const me = useAppSelector((state) => state.me); + + const handleOnAdd = () => dispatch(addToAliases(account)); + + if (!account) return null; + + let button; + + if (!added && accountId !== me) { + button = ( +
+ +
+ ); + } + + return ( +
+
+
+
+ +
+ + {button} +
+
+ ); +}; + +export default Account; diff --git a/app/soapbox/features/aliases/index.js b/app/soapbox/features/aliases/index.js deleted file mode 100644 index f2bd512bc..000000000 --- a/app/soapbox/features/aliases/index.js +++ /dev/null @@ -1,115 +0,0 @@ -import { List as ImmutableList } from 'immutable'; -import React from 'react'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; -import { connect } from 'react-redux'; - -import { fetchAliases, removeFromAliases } from 'soapbox/actions/aliases'; -import Icon from 'soapbox/components/icon'; -import ScrollableList from 'soapbox/components/scrollable_list'; -import { CardHeader, CardTitle, Column, HStack, Text } from 'soapbox/components/ui'; -import { makeGetAccount } from 'soapbox/selectors'; -import { getFeatures } from 'soapbox/utils/features'; - - -import Account from './components/account'; -import Search from './components/search'; - -const messages = defineMessages({ - heading: { id: 'column.aliases', defaultMessage: 'Account aliases' }, - subheading_add_new: { id: 'column.aliases.subheading_add_new', defaultMessage: 'Add New Alias' }, - create_error: { id: 'column.aliases.create_error', defaultMessage: 'Error creating alias' }, - delete_error: { id: 'column.aliases.delete_error', defaultMessage: 'Error deleting alias' }, - subheading_aliases: { id: 'column.aliases.subheading_aliases', defaultMessage: 'Current aliases' }, - delete: { id: 'column.aliases.delete', defaultMessage: 'Delete' }, -}); - -const makeMapStateToProps = () => { - const getAccount = makeGetAccount(); - - const mapStateToProps = state => { - const me = state.get('me'); - const account = getAccount(state, me); - - const instance = state.get('instance'); - const features = getFeatures(instance); - - let aliases; - - if (features.accountMoving) aliases = state.getIn(['aliases', 'aliases', 'items'], ImmutableList()); - else aliases = account.getIn(['pleroma', 'also_known_as']); - - return { - aliases, - searchAccountIds: state.getIn(['aliases', 'suggestions', 'items']), - loaded: state.getIn(['aliases', 'suggestions', 'loaded']), - }; - }; - - return mapStateToProps; -}; - -export default @connect(makeMapStateToProps) -@injectIntl -class Aliases extends ImmutablePureComponent { - - componentDidMount = e => { - const { dispatch } = this.props; - dispatch(fetchAliases); - } - - handleFilterDelete = e => { - const { dispatch, intl } = this.props; - dispatch(removeFromAliases(intl, e.currentTarget.dataset.value)); - } - - render() { - const { intl, aliases, searchAccountIds, loaded } = this.props; - - const emptyMessage = ; - - return ( - - - - - - { - loaded && searchAccountIds.size === 0 ? ( -
- -
- ) : ( -
- {searchAccountIds.map(accountId => )} -
- ) - } - - - -
- - {aliases.map((alias, i) => ( - -
- - {' '} - {alias} -
-
- - -
-
- ))} -
-
-
- ); - } - -} diff --git a/app/soapbox/features/aliases/index.tsx b/app/soapbox/features/aliases/index.tsx new file mode 100644 index 000000000..1e1484c83 --- /dev/null +++ b/app/soapbox/features/aliases/index.tsx @@ -0,0 +1,98 @@ +import { List as ImmutableList } from 'immutable'; +import React, { useEffect } from 'react'; +import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; + +import { fetchAliases, removeFromAliases } from 'soapbox/actions/aliases'; +import Icon from 'soapbox/components/icon'; +import ScrollableList from 'soapbox/components/scrollable_list'; +import { CardHeader, CardTitle, Column, HStack, Text } from 'soapbox/components/ui'; +import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; +import { makeGetAccount } from 'soapbox/selectors'; +import { getFeatures } from 'soapbox/utils/features'; + +import Account from './components/account'; +import Search from './components/search'; + +const messages = defineMessages({ + heading: { id: 'column.aliases', defaultMessage: 'Account aliases' }, + subheading_add_new: { id: 'column.aliases.subheading_add_new', defaultMessage: 'Add New Alias' }, + create_error: { id: 'column.aliases.create_error', defaultMessage: 'Error creating alias' }, + delete_error: { id: 'column.aliases.delete_error', defaultMessage: 'Error deleting alias' }, + subheading_aliases: { id: 'column.aliases.subheading_aliases', defaultMessage: 'Current aliases' }, + delete: { id: 'column.aliases.delete', defaultMessage: 'Delete' }, +}); + +const getAccount = makeGetAccount(); + +const Aliases = () => { + const intl = useIntl(); + const dispatch = useAppDispatch(); + + const aliases = useAppSelector((state) => { + const me = state.me as string; + const account = getAccount(state, me); + + const instance = state.instance; + const features = getFeatures(instance); + + if (features.accountMoving) return state.aliases.getIn(['aliases', 'items'], ImmutableList()); + return account!.pleroma.get('also_known_as'); + }) as ImmutableList; + const searchAccountIds = useAppSelector((state) => state.aliases.getIn(['suggestions', 'items'])) as ImmutableList; + const loaded = useAppSelector((state) => state.aliases.getIn(['suggestions', 'loaded'])); + + useEffect(() => { + dispatch(fetchAliases); + }, []); + + const handleFilterDelete: React.MouseEventHandler = e => { + dispatch(removeFromAliases(e.currentTarget.dataset.value)); + }; + + const emptyMessage = ; + + return ( + + + + + + { + loaded && searchAccountIds.size === 0 ? ( +
+ +
+ ) : ( +
+ {searchAccountIds.map(accountId => )} +
+ ) + } + + + +
+ + {aliases.map((alias, i) => ( + +
+ + {' '} + {alias} +
+
+ + +
+
+ ))} +
+
+
+ ); +}; + +export default Aliases;