Merge branch 'account-endorsements' into 'develop'
User endorsements See merge request soapbox-pub/soapbox-fe!982
This commit is contained in:
commit
fe3e74d71d
|
@ -57,6 +57,10 @@ export const ACCOUNT_UNPIN_REQUEST = 'ACCOUNT_UNPIN_REQUEST';
|
|||
export const ACCOUNT_UNPIN_SUCCESS = 'ACCOUNT_UNPIN_SUCCESS';
|
||||
export const ACCOUNT_UNPIN_FAIL = 'ACCOUNT_UNPIN_FAIL';
|
||||
|
||||
export const PINNED_ACCOUNTS_FETCH_REQUEST = 'PINNED_ACCOUNTS_FETCH_REQUEST';
|
||||
export const PINNED_ACCOUNTS_FETCH_SUCCESS = 'PINNED_ACCOUNTS_FETCH_SUCCESS';
|
||||
export const PINNED_ACCOUNTS_FETCH_FAIL = 'PINNED_ACCOUNTS_FETCH_FAIL';
|
||||
|
||||
export const ACCOUNT_SEARCH_REQUEST = 'ACCOUNT_SEARCH_REQUEST';
|
||||
export const ACCOUNT_SEARCH_SUCCESS = 'ACCOUNT_SEARCH_SUCCESS';
|
||||
export const ACCOUNT_SEARCH_FAIL = 'ACCOUNT_SEARCH_FAIL';
|
||||
|
@ -962,6 +966,43 @@ export function unpinAccountFail(error) {
|
|||
};
|
||||
}
|
||||
|
||||
export function fetchPinnedAccounts(id) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(fetchPinnedAccountsRequest(id));
|
||||
|
||||
api(getState).get(`/api/v1/pleroma/accounts/${id}/endorsements`).then(response => {
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(fetchPinnedAccountsSuccess(id, response.data, null));
|
||||
}).catch(error => {
|
||||
dispatch(fetchPinnedAccountsFail(id, error));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPinnedAccountsRequest(id) {
|
||||
return {
|
||||
type: PINNED_ACCOUNTS_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPinnedAccountsSuccess(id, accounts, next) {
|
||||
return {
|
||||
type: PINNED_ACCOUNTS_FETCH_SUCCESS,
|
||||
id,
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPinnedAccountsFail(id, error) {
|
||||
return {
|
||||
type: PINNED_ACCOUNTS_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
export function accountSearch(params, cancelToken) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: ACCOUNT_SEARCH_REQUEST, params });
|
||||
|
|
|
@ -281,7 +281,14 @@ class Header extends ImmutablePureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
// menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });
|
||||
if (features.accountEndorsements) {
|
||||
menu.push({
|
||||
text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse),
|
||||
action: this.props.onEndorseToggle,
|
||||
icon: require('@tabler/icons/icons/user-check.svg'),
|
||||
});
|
||||
}
|
||||
|
||||
menu.push(null);
|
||||
} else if (features.lists && features.unrestrictedLists) {
|
||||
menu.push({
|
||||
|
|
|
@ -22,7 +22,7 @@ export default class Header extends ImmutablePureComponent {
|
|||
onMute: PropTypes.func.isRequired,
|
||||
onBlockDomain: PropTypes.func.isRequired,
|
||||
onUnblockDomain: PropTypes.func.isRequired,
|
||||
// onEndorseToggle: PropTypes.func.isRequired,
|
||||
onEndorseToggle: PropTypes.func.isRequired,
|
||||
onAddToList: PropTypes.func.isRequired,
|
||||
username: PropTypes.string,
|
||||
};
|
||||
|
@ -83,9 +83,9 @@ export default class Header extends ImmutablePureComponent {
|
|||
this.props.onChat(this.props.account, this.context.router.history);
|
||||
}
|
||||
|
||||
// handleEndorseToggle = () => {
|
||||
// this.props.onEndorseToggle(this.props.account);
|
||||
// }
|
||||
handleEndorseToggle = () => {
|
||||
this.props.onEndorseToggle(this.props.account);
|
||||
}
|
||||
|
||||
handleAddToList = () => {
|
||||
this.props.onAddToList(this.props.account);
|
||||
|
|
|
@ -24,8 +24,8 @@ import {
|
|||
blockAccount,
|
||||
unblockAccount,
|
||||
unmuteAccount,
|
||||
// pinAccount,
|
||||
// unpinAccount,
|
||||
pinAccount,
|
||||
unpinAccount,
|
||||
subscribeAccount,
|
||||
unsubscribeAccount,
|
||||
} from '../../../actions/accounts';
|
||||
|
@ -130,13 +130,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
}
|
||||
},
|
||||
|
||||
// onEndorseToggle(account) {
|
||||
// if (account.getIn(['relationship', 'endorsed'])) {
|
||||
// dispatch(unpinAccount(account.get('id')));
|
||||
// } else {
|
||||
// dispatch(pinAccount(account.get('id')));
|
||||
// }
|
||||
// },
|
||||
onEndorseToggle(account) {
|
||||
if (account.getIn(['relationship', 'endorsed'])) {
|
||||
dispatch(unpinAccount(account.get('id')));
|
||||
} else {
|
||||
dispatch(pinAccount(account.get('id')));
|
||||
}
|
||||
},
|
||||
|
||||
onReport(account) {
|
||||
dispatch(initReport(account));
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
import { List as ImmutableList } from 'immutable';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Icon from 'soapbox/components/icon';
|
||||
|
||||
import { fetchPinnedAccounts } from '../../../actions/accounts';
|
||||
import AccountContainer from '../../../containers/account_container';
|
||||
|
||||
class PinnedAccountsPanel extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
pinned: ImmutablePropTypes.list.isRequired,
|
||||
fetchPinned: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchPinned();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account } = this.props;
|
||||
const pinned = this.props.pinned.slice(0, this.props.limit);
|
||||
|
||||
if (pinned.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='wtf-panel'>
|
||||
<div className='wtf-panel-header'>
|
||||
<Icon src={require('@tabler/icons/icons/users.svg')} className='wtf-panel-header__icon' />
|
||||
<span className='wtf-panel-header__label'>
|
||||
<FormattedMessage
|
||||
id='pinned_accounts.title'
|
||||
defaultMessage='{name}’s choices'
|
||||
values={{
|
||||
name: account.get('display_name_html'),
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div className='wtf-panel__content'>
|
||||
<div className='wtf-panel__list'>
|
||||
{pinned && pinned.map(suggestion => (
|
||||
<AccountContainer
|
||||
key={suggestion}
|
||||
id={suggestion}
|
||||
withRelationship={false}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, { account }) => ({
|
||||
pinned: state.getIn(['user_lists', 'pinned', account.get('id'), 'items'], ImmutableList()),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { account }) => {
|
||||
return {
|
||||
fetchPinned: () => dispatch(fetchPinnedAccounts(account.get('id'))),
|
||||
};
|
||||
};
|
||||
|
||||
export default injectIntl(
|
||||
connect(mapStateToProps, mapDispatchToProps, null, {
|
||||
forwardRef: true,
|
||||
},
|
||||
)(PinnedAccountsPanel));
|
|
@ -338,6 +338,10 @@ export function ProfileMediaPanel() {
|
|||
return import(/* webpackChunkName: "features/account_gallery" */'../components/profile_media_panel');
|
||||
}
|
||||
|
||||
export function PinnedAccountsPanel() {
|
||||
return import(/* webpackChunkName: "features/pinned_accounts]" */'../components/pinned_accounts_panel');
|
||||
}
|
||||
|
||||
export function InstanceInfoPanel() {
|
||||
return import(/* webpackChunkName: "features/remote_timeline" */'../components/instance_info_panel');
|
||||
}
|
||||
|
|
|
@ -680,6 +680,7 @@
|
|||
"password_reset.confirmation": "Sprawdź swoją pocztę e-mail, aby potwierdzić.",
|
||||
"password_reset.fields.username_placeholder": "Adres e-mail lub nazwa użytkownika",
|
||||
"password_reset.reset": "Resetuj hasło",
|
||||
"pinned_accounts.title": "Polecani przez {name}",
|
||||
"pinned_statuses.none": "Brak przypięć do pokazania.",
|
||||
"poll.closed": "Zamknięte",
|
||||
"poll.refresh": "Odśwież",
|
||||
|
|
|
@ -13,15 +13,15 @@ import {
|
|||
SignUpPanel,
|
||||
ProfileInfoPanel,
|
||||
ProfileMediaPanel,
|
||||
PinnedAccountsPanel,
|
||||
} from 'soapbox/features/ui/util/async-components';
|
||||
import { findAccountByUsername } from 'soapbox/selectors';
|
||||
import { getAcct } from 'soapbox/utils/accounts';
|
||||
import { findAccountByUsername, makeGetAccount } from 'soapbox/selectors';
|
||||
import { getAcct, isLocal } from 'soapbox/utils/accounts';
|
||||
import { getFeatures } from 'soapbox/utils/features';
|
||||
import { displayFqn } from 'soapbox/utils/state';
|
||||
|
||||
import HeaderContainer from '../features/account_timeline/containers/header_container';
|
||||
import LinkFooter from '../features/ui/components/link_footer';
|
||||
import { makeGetAccount } from '../selectors';
|
||||
|
||||
const mapStateToProps = (state, { params, withReplies = false }) => {
|
||||
const username = params.username || '';
|
||||
|
@ -118,7 +118,11 @@ class ProfilePage extends ImmutablePureComponent {
|
|||
{Component => <Component account={account} />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
{features.suggestions && (
|
||||
{account && features.accountEndorsements && isLocal(account) ? (
|
||||
<BundleContainer fetchComponent={PinnedAccountsPanel}>
|
||||
{Component => <Component account={account} />}
|
||||
</BundleContainer>
|
||||
) : features.suggestions && (
|
||||
<BundleContainer fetchComponent={WhoToFollowPanel}>
|
||||
{Component => <Component />}
|
||||
</BundleContainer>
|
||||
|
|
|
@ -15,6 +15,7 @@ describe('user_lists reducer', () => {
|
|||
mutes: ImmutableMap(),
|
||||
groups: ImmutableMap(),
|
||||
groups_removed_accounts: ImmutableMap(),
|
||||
pinned: ImmutableMap(),
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
FOLLOW_REQUESTS_EXPAND_SUCCESS,
|
||||
FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
|
||||
FOLLOW_REQUEST_REJECT_SUCCESS,
|
||||
PINNED_ACCOUNTS_FETCH_SUCCESS,
|
||||
} from '../actions/accounts';
|
||||
import {
|
||||
BLOCKS_FETCH_SUCCESS,
|
||||
|
@ -53,6 +54,7 @@ const initialState = ImmutableMap({
|
|||
mutes: ImmutableMap(),
|
||||
groups: ImmutableMap(),
|
||||
groups_removed_accounts: ImmutableMap(),
|
||||
pinned: ImmutableMap(),
|
||||
});
|
||||
|
||||
const normalizeList = (state, type, id, accounts, next) => {
|
||||
|
@ -127,6 +129,8 @@ export default function userLists(state = initialState, action) {
|
|||
return appendToList(state, 'groups_removed_accounts', action.id, action.accounts, action.next);
|
||||
case GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS:
|
||||
return state.updateIn(['groups_removed_accounts', action.groupId, 'items'], list => list.filterNot(item => item === action.id));
|
||||
case PINNED_ACCOUNTS_FETCH_SUCCESS:
|
||||
return normalizeList(state, 'pinned', action.id, action.accounts, action.next);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ export const getFeatures = createSelector([
|
|||
]),
|
||||
remoteInteractionsAPI: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
||||
explicitAddressing: v.software === PLEROMA && gte(v.version, '1.0.0'),
|
||||
accountEndorsements: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
&.svg-icon {
|
||||
width: 20px;
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue