Merge branch 'suggestions' into 'develop'
v2 Suggestions: add AdminAPI, rearrange sidebars, check instance feature flag See merge request soapbox-pub/soapbox-fe!892
This commit is contained in:
commit
1e0f76558a
|
@ -62,6 +62,14 @@ export const ADMIN_REMOVE_PERMISSION_GROUP_REQUEST = 'ADMIN_REMOVE_PERMISSION_GR
|
|||
export const ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS = 'ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS';
|
||||
export const ADMIN_REMOVE_PERMISSION_GROUP_FAIL = 'ADMIN_REMOVE_PERMISSION_GROUP_FAIL';
|
||||
|
||||
export const ADMIN_USERS_SUGGEST_REQUEST = 'ADMIN_USERS_SUGGEST_REQUEST';
|
||||
export const ADMIN_USERS_SUGGEST_SUCCESS = 'ADMIN_USERS_SUGGEST_SUCCESS';
|
||||
export const ADMIN_USERS_SUGGEST_FAIL = 'ADMIN_USERS_SUGGEST_FAIL';
|
||||
|
||||
export const ADMIN_USERS_UNSUGGEST_REQUEST = 'ADMIN_USERS_UNSUGGEST_REQUEST';
|
||||
export const ADMIN_USERS_UNSUGGEST_SUCCESS = 'ADMIN_USERS_UNSUGGEST_SUCCESS';
|
||||
export const ADMIN_USERS_UNSUGGEST_FAIL = 'ADMIN_USERS_UNSUGGEST_FAIL';
|
||||
|
||||
const nicknamesFromIds = (getState, ids) => ids.map(id => getState().getIn(['accounts', id, 'acct']));
|
||||
|
||||
export function fetchConfig() {
|
||||
|
@ -319,3 +327,31 @@ export function demoteToUser(accountId) {
|
|||
]);
|
||||
};
|
||||
}
|
||||
|
||||
export function suggestUsers(accountIds) {
|
||||
return (dispatch, getState) => {
|
||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||
dispatch({ type: ADMIN_USERS_SUGGEST_REQUEST, accountIds });
|
||||
return api(getState)
|
||||
.patch('/api/pleroma/admin/users/suggest', { nicknames })
|
||||
.then(({ data: { users } }) => {
|
||||
dispatch({ type: ADMIN_USERS_SUGGEST_SUCCESS, users, accountIds });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_SUGGEST_FAIL, error, accountIds });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function unsuggestUsers(accountIds) {
|
||||
return (dispatch, getState) => {
|
||||
const nicknames = nicknamesFromIds(getState, accountIds);
|
||||
dispatch({ type: ADMIN_USERS_UNSUGGEST_REQUEST, accountIds });
|
||||
return api(getState)
|
||||
.patch('/api/pleroma/admin/users/unsuggest', { nicknames })
|
||||
.then(({ data: { users } }) => {
|
||||
dispatch({ type: ADMIN_USERS_UNSUGGEST_SUCCESS, users, accountIds });
|
||||
}).catch(error => {
|
||||
dispatch({ type: ADMIN_USERS_UNSUGGEST_FAIL, error, accountIds });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -68,6 +68,8 @@ const messages = defineMessages({
|
|||
demoteToUser: { id: 'admin.users.actions.demote_to_user', defaultMessage: 'Demote @{name} to a regular user' },
|
||||
subscribe: { id: 'account.subscribe', defaultMessage: 'Subscribe to notifications from @{name}' },
|
||||
unsubscribe: { id: 'account.unsubscribe', defaultMessage: 'Unsubscribe to notifications from @{name}' },
|
||||
suggestUser: { id: 'admin.users.actions.suggest_user', defaultMessage: 'Suggest @{name}' },
|
||||
unsuggestUser: { id: 'admin.users.actions.unsuggest_user', defaultMessage: 'Unsuggest @{name}' },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => {
|
||||
|
@ -405,6 +407,20 @@ class Header extends ImmutablePureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
if (account.getIn(['pleroma', 'is_suggested'])) {
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.unsuggestUser, { name: account.get('username') }),
|
||||
action: this.props.onUnsuggestUser,
|
||||
icon: require('@tabler/icons/icons/user-x.svg'),
|
||||
});
|
||||
} else {
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.suggestUser, { name: account.get('username') }),
|
||||
action: this.props.onSuggestUser,
|
||||
icon: require('@tabler/icons/icons/user-check.svg'),
|
||||
});
|
||||
}
|
||||
|
||||
if (account.get('id') !== me) {
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.deactivateUser, { name: account.get('username') }),
|
||||
|
|
|
@ -117,6 +117,14 @@ export default class Header extends ImmutablePureComponent {
|
|||
this.props.onDemoteToUser(this.props.account);
|
||||
}
|
||||
|
||||
handleSuggestUser = () => {
|
||||
this.props.onSuggestUser(this.props.account);
|
||||
}
|
||||
|
||||
handleUnsuggestUser = () => {
|
||||
this.props.onUnsuggestUser(this.props.account);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { account, identity_proofs } = this.props;
|
||||
const moved = (account) ? account.get('moved') : false;
|
||||
|
@ -148,6 +156,8 @@ export default class Header extends ImmutablePureComponent {
|
|||
onPromoteToAdmin={this.handlePromoteToAdmin}
|
||||
onPromoteToModerator={this.handlePromoteToModerator}
|
||||
onDemoteToUser={this.handleDemoteToUser}
|
||||
onSuggestUser={this.handleSuggestUser}
|
||||
onUnsuggestUser={this.handleUnsuggestUser}
|
||||
username={this.props.username}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -32,6 +32,8 @@ import {
|
|||
promoteToAdmin,
|
||||
promoteToModerator,
|
||||
demoteToUser,
|
||||
suggestUsers,
|
||||
unsuggestUsers,
|
||||
} from 'soapbox/actions/admin';
|
||||
import { isAdmin } from 'soapbox/utils/accounts';
|
||||
import snackbar from 'soapbox/actions/snackbar';
|
||||
|
@ -47,7 +49,8 @@ const messages = defineMessages({
|
|||
promotedToModerator: { id: 'admin.users.actions.promote_to_moderator_message', defaultMessage: '@{acct} was promoted to a moderator' },
|
||||
demotedToModerator: { id: 'admin.users.actions.demote_to_moderator_message', defaultMessage: '@{acct} was demoted to a moderator' },
|
||||
demotedToUser: { id: 'admin.users.actions.demote_to_user_message', defaultMessage: '@{acct} was demoted to a regular user' },
|
||||
|
||||
userSuggested: { id: 'admin.users.user_suggested_message', defaultMessage: '@{acct} was suggested' },
|
||||
userUnsuggested: { id: 'admin.users.user_unsuggested_message', defaultMessage: '@{acct} was unsuggested' },
|
||||
});
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
|
@ -213,6 +216,22 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
.then(() => dispatch(snackbar.success(message)))
|
||||
.catch(() => {});
|
||||
},
|
||||
|
||||
onSuggestUser(account) {
|
||||
const message = intl.formatMessage(messages.userSuggested, { acct: account.get('acct') });
|
||||
|
||||
dispatch(suggestUsers([account.get('id')]))
|
||||
.then(() => dispatch(snackbar.success(message)))
|
||||
.catch(() => {});
|
||||
},
|
||||
|
||||
onUnsuggestUser(account) {
|
||||
const message = intl.formatMessage(messages.userUnsuggested, { acct: account.get('acct') });
|
||||
|
||||
dispatch(unsuggestUsers([account.get('id')]))
|
||||
.then(() => dispatch(snackbar.success(message)))
|
||||
.catch(() => {});
|
||||
},
|
||||
});
|
||||
|
||||
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));
|
||||
|
|
|
@ -62,6 +62,9 @@ class DefaultPage extends ImmutablePureComponent {
|
|||
{Component => <Component key='sign-up-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
<BundleContainer fetchComponent={PromoPanel}>
|
||||
{Component => <Component key='promo-panel' />}
|
||||
</BundleContainer>
|
||||
{showTrendsPanel && (
|
||||
<BundleContainer fetchComponent={TrendsPanel}>
|
||||
{Component => <Component limit={3} key='trends-panel' />}
|
||||
|
@ -72,9 +75,6 @@ class DefaultPage extends ImmutablePureComponent {
|
|||
{Component => <Component limit={5} key='wtf-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
<BundleContainer fetchComponent={PromoPanel}>
|
||||
{Component => <Component key='promo-panel' />}
|
||||
</BundleContainer>
|
||||
<LinkFooter key='link-footer' />
|
||||
</Sticky>
|
||||
</div>
|
||||
|
|
|
@ -96,16 +96,6 @@ class HomePage extends ImmutablePureComponent {
|
|||
{Component => <Component key='sign-up-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
{showTrendsPanel && (
|
||||
<BundleContainer fetchComponent={TrendsPanel}>
|
||||
{Component => <Component limit={3} key='trends-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
{showWhoToFollowPanel && (
|
||||
<BundleContainer fetchComponent={WhoToFollowPanel}>
|
||||
{Component => <Component limit={5} key='wtf-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
<BundleContainer fetchComponent={PromoPanel}>
|
||||
{Component => <Component key='promo-panel' />}
|
||||
</BundleContainer>
|
||||
|
@ -119,6 +109,16 @@ class HomePage extends ImmutablePureComponent {
|
|||
{Component => <Component limit={cryptoLimit} key='crypto-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
{showTrendsPanel && (
|
||||
<BundleContainer fetchComponent={TrendsPanel}>
|
||||
{Component => <Component limit={3} key='trends-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
{showWhoToFollowPanel && (
|
||||
<BundleContainer fetchComponent={WhoToFollowPanel}>
|
||||
{Component => <Component limit={5} key='wtf-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
<LinkFooter key='link-footer' />
|
||||
</Sticky>
|
||||
</div>
|
||||
|
|
|
@ -63,6 +63,9 @@ class StatusPage extends ImmutablePureComponent {
|
|||
{Component => <Component key='sign-up-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
<BundleContainer fetchComponent={PromoPanel}>
|
||||
{Component => <Component key='promo-panel' />}
|
||||
</BundleContainer>
|
||||
{showTrendsPanel && (
|
||||
<BundleContainer fetchComponent={TrendsPanel}>
|
||||
{Component => <Component limit={3} key='trends-panel' />}
|
||||
|
@ -73,9 +76,6 @@ class StatusPage extends ImmutablePureComponent {
|
|||
{Component => <Component limit={5} key='wtf-panel' />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
<BundleContainer fetchComponent={PromoPanel}>
|
||||
{Component => <Component key='promo-panel' />}
|
||||
</BundleContainer>
|
||||
<LinkFooter key='link-footer' />
|
||||
</Sticky>
|
||||
</div>
|
||||
|
|
|
@ -30,6 +30,10 @@ import {
|
|||
ADMIN_USERS_DELETE_FAIL,
|
||||
ADMIN_USERS_DEACTIVATE_REQUEST,
|
||||
ADMIN_USERS_DEACTIVATE_FAIL,
|
||||
ADMIN_USERS_SUGGEST_REQUEST,
|
||||
ADMIN_USERS_SUGGEST_FAIL,
|
||||
ADMIN_USERS_UNSUGGEST_REQUEST,
|
||||
ADMIN_USERS_UNSUGGEST_FAIL,
|
||||
} from 'soapbox/actions/admin';
|
||||
|
||||
const initialState = ImmutableMap();
|
||||
|
@ -185,6 +189,14 @@ const importAdminUsers = (state, adminUsers) => {
|
|||
});
|
||||
};
|
||||
|
||||
const setSuggested = (state, accountIds, isSuggested) => {
|
||||
return state.withMutations(state => {
|
||||
accountIds.forEach(id => {
|
||||
state.setIn([id, 'pleroma', 'is_suggested'], isSuggested);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default function accounts(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case ACCOUNT_IMPORT:
|
||||
|
@ -224,6 +236,12 @@ export default function accounts(state = initialState, action) {
|
|||
return setActive(state, action.accountIds, true);
|
||||
case ADMIN_USERS_FETCH_SUCCESS:
|
||||
return importAdminUsers(state, action.users);
|
||||
case ADMIN_USERS_SUGGEST_REQUEST:
|
||||
case ADMIN_USERS_UNSUGGEST_FAIL:
|
||||
return setSuggested(state, action.accountIds, true);
|
||||
case ADMIN_USERS_UNSUGGEST_REQUEST:
|
||||
case ADMIN_USERS_SUGGEST_FAIL:
|
||||
return setSuggested(state, action.accountIds, false);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -24,8 +24,14 @@ export const getFeatures = createSelector([
|
|||
v.software === MASTODON && gte(v.compatVersion, '2.1.0'),
|
||||
v.software === PLEROMA && gte(v.version, '0.9.9'),
|
||||
]),
|
||||
suggestions: v.software === MASTODON && gte(v.compatVersion, '2.4.3'),
|
||||
suggestionsV2: v.software === MASTODON && gte(v.compatVersion, '3.4.0'),
|
||||
suggestions: any([
|
||||
v.software === MASTODON && gte(v.compatVersion, '2.4.3'),
|
||||
features.includes('v2_suggestions'),
|
||||
]),
|
||||
suggestionsV2: any([
|
||||
v.software === MASTODON && gte(v.compatVersion, '3.4.0'),
|
||||
features.includes('v2_suggestions'),
|
||||
]),
|
||||
trends: v.software === MASTODON && gte(v.compatVersion, '3.0.0'),
|
||||
mediaV2: any([
|
||||
v.software === MASTODON && gte(v.compatVersion, '3.1.3'),
|
||||
|
|
Loading…
Reference in New Issue