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_SUCCESS = 'ACCOUNT_UNPIN_SUCCESS';
|
||||||
export const ACCOUNT_UNPIN_FAIL = 'ACCOUNT_UNPIN_FAIL';
|
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_REQUEST = 'ACCOUNT_SEARCH_REQUEST';
|
||||||
export const ACCOUNT_SEARCH_SUCCESS = 'ACCOUNT_SEARCH_SUCCESS';
|
export const ACCOUNT_SEARCH_SUCCESS = 'ACCOUNT_SEARCH_SUCCESS';
|
||||||
export const ACCOUNT_SEARCH_FAIL = 'ACCOUNT_SEARCH_FAIL';
|
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) {
|
export function accountSearch(params, cancelToken) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch({ type: ACCOUNT_SEARCH_REQUEST, params });
|
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);
|
menu.push(null);
|
||||||
} else if (features.lists && features.unrestrictedLists) {
|
} else if (features.lists && features.unrestrictedLists) {
|
||||||
menu.push({
|
menu.push({
|
||||||
|
|
|
@ -22,7 +22,7 @@ export default class Header extends ImmutablePureComponent {
|
||||||
onMute: PropTypes.func.isRequired,
|
onMute: PropTypes.func.isRequired,
|
||||||
onBlockDomain: PropTypes.func.isRequired,
|
onBlockDomain: PropTypes.func.isRequired,
|
||||||
onUnblockDomain: PropTypes.func.isRequired,
|
onUnblockDomain: PropTypes.func.isRequired,
|
||||||
// onEndorseToggle: PropTypes.func.isRequired,
|
onEndorseToggle: PropTypes.func.isRequired,
|
||||||
onAddToList: PropTypes.func.isRequired,
|
onAddToList: PropTypes.func.isRequired,
|
||||||
username: PropTypes.string,
|
username: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
@ -83,9 +83,9 @@ export default class Header extends ImmutablePureComponent {
|
||||||
this.props.onChat(this.props.account, this.context.router.history);
|
this.props.onChat(this.props.account, this.context.router.history);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleEndorseToggle = () => {
|
handleEndorseToggle = () => {
|
||||||
// this.props.onEndorseToggle(this.props.account);
|
this.props.onEndorseToggle(this.props.account);
|
||||||
// }
|
}
|
||||||
|
|
||||||
handleAddToList = () => {
|
handleAddToList = () => {
|
||||||
this.props.onAddToList(this.props.account);
|
this.props.onAddToList(this.props.account);
|
||||||
|
|
|
@ -24,8 +24,8 @@ import {
|
||||||
blockAccount,
|
blockAccount,
|
||||||
unblockAccount,
|
unblockAccount,
|
||||||
unmuteAccount,
|
unmuteAccount,
|
||||||
// pinAccount,
|
pinAccount,
|
||||||
// unpinAccount,
|
unpinAccount,
|
||||||
subscribeAccount,
|
subscribeAccount,
|
||||||
unsubscribeAccount,
|
unsubscribeAccount,
|
||||||
} from '../../../actions/accounts';
|
} from '../../../actions/accounts';
|
||||||
|
@ -130,13 +130,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// onEndorseToggle(account) {
|
onEndorseToggle(account) {
|
||||||
// if (account.getIn(['relationship', 'endorsed'])) {
|
if (account.getIn(['relationship', 'endorsed'])) {
|
||||||
// dispatch(unpinAccount(account.get('id')));
|
dispatch(unpinAccount(account.get('id')));
|
||||||
// } else {
|
} else {
|
||||||
// dispatch(pinAccount(account.get('id')));
|
dispatch(pinAccount(account.get('id')));
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
|
|
||||||
onReport(account) {
|
onReport(account) {
|
||||||
dispatch(initReport(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');
|
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() {
|
export function InstanceInfoPanel() {
|
||||||
return import(/* webpackChunkName: "features/remote_timeline" */'../components/instance_info_panel');
|
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.confirmation": "Sprawdź swoją pocztę e-mail, aby potwierdzić.",
|
||||||
"password_reset.fields.username_placeholder": "Adres e-mail lub nazwa użytkownika",
|
"password_reset.fields.username_placeholder": "Adres e-mail lub nazwa użytkownika",
|
||||||
"password_reset.reset": "Resetuj hasło",
|
"password_reset.reset": "Resetuj hasło",
|
||||||
|
"pinned_accounts.title": "Polecani przez {name}",
|
||||||
"pinned_statuses.none": "Brak przypięć do pokazania.",
|
"pinned_statuses.none": "Brak przypięć do pokazania.",
|
||||||
"poll.closed": "Zamknięte",
|
"poll.closed": "Zamknięte",
|
||||||
"poll.refresh": "Odśwież",
|
"poll.refresh": "Odśwież",
|
||||||
|
|
|
@ -13,15 +13,15 @@ import {
|
||||||
SignUpPanel,
|
SignUpPanel,
|
||||||
ProfileInfoPanel,
|
ProfileInfoPanel,
|
||||||
ProfileMediaPanel,
|
ProfileMediaPanel,
|
||||||
|
PinnedAccountsPanel,
|
||||||
} from 'soapbox/features/ui/util/async-components';
|
} from 'soapbox/features/ui/util/async-components';
|
||||||
import { findAccountByUsername } from 'soapbox/selectors';
|
import { findAccountByUsername, makeGetAccount } from 'soapbox/selectors';
|
||||||
import { getAcct } from 'soapbox/utils/accounts';
|
import { getAcct, isLocal } from 'soapbox/utils/accounts';
|
||||||
import { getFeatures } from 'soapbox/utils/features';
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
import { displayFqn } from 'soapbox/utils/state';
|
import { displayFqn } from 'soapbox/utils/state';
|
||||||
|
|
||||||
import HeaderContainer from '../features/account_timeline/containers/header_container';
|
import HeaderContainer from '../features/account_timeline/containers/header_container';
|
||||||
import LinkFooter from '../features/ui/components/link_footer';
|
import LinkFooter from '../features/ui/components/link_footer';
|
||||||
import { makeGetAccount } from '../selectors';
|
|
||||||
|
|
||||||
const mapStateToProps = (state, { params, withReplies = false }) => {
|
const mapStateToProps = (state, { params, withReplies = false }) => {
|
||||||
const username = params.username || '';
|
const username = params.username || '';
|
||||||
|
@ -118,7 +118,11 @@ class ProfilePage extends ImmutablePureComponent {
|
||||||
{Component => <Component account={account} />}
|
{Component => <Component account={account} />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
)}
|
)}
|
||||||
{features.suggestions && (
|
{account && features.accountEndorsements && isLocal(account) ? (
|
||||||
|
<BundleContainer fetchComponent={PinnedAccountsPanel}>
|
||||||
|
{Component => <Component account={account} />}
|
||||||
|
</BundleContainer>
|
||||||
|
) : features.suggestions && (
|
||||||
<BundleContainer fetchComponent={WhoToFollowPanel}>
|
<BundleContainer fetchComponent={WhoToFollowPanel}>
|
||||||
{Component => <Component />}
|
{Component => <Component />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
|
|
|
@ -15,6 +15,7 @@ describe('user_lists reducer', () => {
|
||||||
mutes: ImmutableMap(),
|
mutes: ImmutableMap(),
|
||||||
groups: ImmutableMap(),
|
groups: ImmutableMap(),
|
||||||
groups_removed_accounts: ImmutableMap(),
|
groups_removed_accounts: ImmutableMap(),
|
||||||
|
pinned: ImmutableMap(),
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
FOLLOW_REQUESTS_EXPAND_SUCCESS,
|
FOLLOW_REQUESTS_EXPAND_SUCCESS,
|
||||||
FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
|
FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
|
||||||
FOLLOW_REQUEST_REJECT_SUCCESS,
|
FOLLOW_REQUEST_REJECT_SUCCESS,
|
||||||
|
PINNED_ACCOUNTS_FETCH_SUCCESS,
|
||||||
} from '../actions/accounts';
|
} from '../actions/accounts';
|
||||||
import {
|
import {
|
||||||
BLOCKS_FETCH_SUCCESS,
|
BLOCKS_FETCH_SUCCESS,
|
||||||
|
@ -53,6 +54,7 @@ const initialState = ImmutableMap({
|
||||||
mutes: ImmutableMap(),
|
mutes: ImmutableMap(),
|
||||||
groups: ImmutableMap(),
|
groups: ImmutableMap(),
|
||||||
groups_removed_accounts: ImmutableMap(),
|
groups_removed_accounts: ImmutableMap(),
|
||||||
|
pinned: ImmutableMap(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const normalizeList = (state, type, id, accounts, next) => {
|
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);
|
return appendToList(state, 'groups_removed_accounts', action.id, action.accounts, action.next);
|
||||||
case GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS:
|
case GROUP_REMOVED_ACCOUNTS_REMOVE_SUCCESS:
|
||||||
return state.updateIn(['groups_removed_accounts', action.groupId, 'items'], list => list.filterNot(item => item === action.id));
|
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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,7 @@ export const getFeatures = createSelector([
|
||||||
]),
|
]),
|
||||||
remoteInteractionsAPI: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
remoteInteractionsAPI: v.software === PLEROMA && gte(v.version, '2.4.50'),
|
||||||
explicitAddressing: v.software === PLEROMA && gte(v.version, '1.0.0'),
|
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 {
|
&.svg-icon {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
|
min-width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue