v2 Suggestions: add AdminAPI, rearrange sidebars, check instance feature flag
This commit is contained in:
parent
e3566db690
commit
92439137c1
|
@ -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_SUCCESS = 'ADMIN_REMOVE_PERMISSION_GROUP_SUCCESS';
|
||||||
export const ADMIN_REMOVE_PERMISSION_GROUP_FAIL = 'ADMIN_REMOVE_PERMISSION_GROUP_FAIL';
|
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']));
|
const nicknamesFromIds = (getState, ids) => ids.map(id => getState().getIn(['accounts', id, 'acct']));
|
||||||
|
|
||||||
export function fetchConfig() {
|
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' },
|
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}' },
|
subscribe: { id: 'account.subscribe', defaultMessage: 'Subscribe to notifications from @{name}' },
|
||||||
unsubscribe: { id: 'account.unsubscribe', defaultMessage: 'Unsubscribe 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 => {
|
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) {
|
if (account.get('id') !== me) {
|
||||||
menu.push({
|
menu.push({
|
||||||
text: intl.formatMessage(messages.deactivateUser, { name: account.get('username') }),
|
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);
|
this.props.onDemoteToUser(this.props.account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSuggestUser = () => {
|
||||||
|
this.props.onSuggestUser(this.props.account);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUnsuggestUser = () => {
|
||||||
|
this.props.onUnsuggestUser(this.props.account);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { account, identity_proofs } = this.props;
|
const { account, identity_proofs } = this.props;
|
||||||
const moved = (account) ? account.get('moved') : false;
|
const moved = (account) ? account.get('moved') : false;
|
||||||
|
@ -148,6 +156,8 @@ export default class Header extends ImmutablePureComponent {
|
||||||
onPromoteToAdmin={this.handlePromoteToAdmin}
|
onPromoteToAdmin={this.handlePromoteToAdmin}
|
||||||
onPromoteToModerator={this.handlePromoteToModerator}
|
onPromoteToModerator={this.handlePromoteToModerator}
|
||||||
onDemoteToUser={this.handleDemoteToUser}
|
onDemoteToUser={this.handleDemoteToUser}
|
||||||
|
onSuggestUser={this.handleSuggestUser}
|
||||||
|
onUnsuggestUser={this.handleUnsuggestUser}
|
||||||
username={this.props.username}
|
username={this.props.username}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,6 +32,8 @@ import {
|
||||||
promoteToAdmin,
|
promoteToAdmin,
|
||||||
promoteToModerator,
|
promoteToModerator,
|
||||||
demoteToUser,
|
demoteToUser,
|
||||||
|
suggestUsers,
|
||||||
|
unsuggestUsers,
|
||||||
} from 'soapbox/actions/admin';
|
} from 'soapbox/actions/admin';
|
||||||
import { isAdmin } from 'soapbox/utils/accounts';
|
import { isAdmin } from 'soapbox/utils/accounts';
|
||||||
import snackbar from 'soapbox/actions/snackbar';
|
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' },
|
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' },
|
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' },
|
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 = () => {
|
const makeMapStateToProps = () => {
|
||||||
|
@ -213,6 +216,22 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
.then(() => dispatch(snackbar.success(message)))
|
.then(() => dispatch(snackbar.success(message)))
|
||||||
.catch(() => {});
|
.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));
|
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Header));
|
||||||
|
|
|
@ -62,6 +62,9 @@ class DefaultPage extends ImmutablePureComponent {
|
||||||
{Component => <Component key='sign-up-panel' />}
|
{Component => <Component key='sign-up-panel' />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
)}
|
)}
|
||||||
|
<BundleContainer fetchComponent={PromoPanel}>
|
||||||
|
{Component => <Component key='promo-panel' />}
|
||||||
|
</BundleContainer>
|
||||||
{showTrendsPanel && (
|
{showTrendsPanel && (
|
||||||
<BundleContainer fetchComponent={TrendsPanel}>
|
<BundleContainer fetchComponent={TrendsPanel}>
|
||||||
{Component => <Component limit={3} key='trends-panel' />}
|
{Component => <Component limit={3} key='trends-panel' />}
|
||||||
|
@ -72,9 +75,6 @@ class DefaultPage extends ImmutablePureComponent {
|
||||||
{Component => <Component limit={5} key='wtf-panel' />}
|
{Component => <Component limit={5} key='wtf-panel' />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
)}
|
)}
|
||||||
<BundleContainer fetchComponent={PromoPanel}>
|
|
||||||
{Component => <Component key='promo-panel' />}
|
|
||||||
</BundleContainer>
|
|
||||||
<LinkFooter key='link-footer' />
|
<LinkFooter key='link-footer' />
|
||||||
</Sticky>
|
</Sticky>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -96,16 +96,6 @@ class HomePage extends ImmutablePureComponent {
|
||||||
{Component => <Component key='sign-up-panel' />}
|
{Component => <Component key='sign-up-panel' />}
|
||||||
</BundleContainer>
|
</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}>
|
<BundleContainer fetchComponent={PromoPanel}>
|
||||||
{Component => <Component key='promo-panel' />}
|
{Component => <Component key='promo-panel' />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
|
@ -119,6 +109,16 @@ class HomePage extends ImmutablePureComponent {
|
||||||
{Component => <Component limit={cryptoLimit} key='crypto-panel' />}
|
{Component => <Component limit={cryptoLimit} key='crypto-panel' />}
|
||||||
</BundleContainer>
|
</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' />
|
<LinkFooter key='link-footer' />
|
||||||
</Sticky>
|
</Sticky>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -63,6 +63,9 @@ class StatusPage extends ImmutablePureComponent {
|
||||||
{Component => <Component key='sign-up-panel' />}
|
{Component => <Component key='sign-up-panel' />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
)}
|
)}
|
||||||
|
<BundleContainer fetchComponent={PromoPanel}>
|
||||||
|
{Component => <Component key='promo-panel' />}
|
||||||
|
</BundleContainer>
|
||||||
{showTrendsPanel && (
|
{showTrendsPanel && (
|
||||||
<BundleContainer fetchComponent={TrendsPanel}>
|
<BundleContainer fetchComponent={TrendsPanel}>
|
||||||
{Component => <Component limit={3} key='trends-panel' />}
|
{Component => <Component limit={3} key='trends-panel' />}
|
||||||
|
@ -73,9 +76,6 @@ class StatusPage extends ImmutablePureComponent {
|
||||||
{Component => <Component limit={5} key='wtf-panel' />}
|
{Component => <Component limit={5} key='wtf-panel' />}
|
||||||
</BundleContainer>
|
</BundleContainer>
|
||||||
)}
|
)}
|
||||||
<BundleContainer fetchComponent={PromoPanel}>
|
|
||||||
{Component => <Component key='promo-panel' />}
|
|
||||||
</BundleContainer>
|
|
||||||
<LinkFooter key='link-footer' />
|
<LinkFooter key='link-footer' />
|
||||||
</Sticky>
|
</Sticky>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -30,6 +30,10 @@ import {
|
||||||
ADMIN_USERS_DELETE_FAIL,
|
ADMIN_USERS_DELETE_FAIL,
|
||||||
ADMIN_USERS_DEACTIVATE_REQUEST,
|
ADMIN_USERS_DEACTIVATE_REQUEST,
|
||||||
ADMIN_USERS_DEACTIVATE_FAIL,
|
ADMIN_USERS_DEACTIVATE_FAIL,
|
||||||
|
ADMIN_USERS_SUGGEST_REQUEST,
|
||||||
|
ADMIN_USERS_SUGGEST_FAIL,
|
||||||
|
ADMIN_USERS_UNSUGGEST_REQUEST,
|
||||||
|
ADMIN_USERS_UNSUGGEST_FAIL,
|
||||||
} from 'soapbox/actions/admin';
|
} from 'soapbox/actions/admin';
|
||||||
|
|
||||||
const initialState = ImmutableMap();
|
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) {
|
export default function accounts(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case ACCOUNT_IMPORT:
|
case ACCOUNT_IMPORT:
|
||||||
|
@ -224,6 +236,12 @@ export default function accounts(state = initialState, action) {
|
||||||
return setActive(state, action.accountIds, true);
|
return setActive(state, action.accountIds, true);
|
||||||
case ADMIN_USERS_FETCH_SUCCESS:
|
case ADMIN_USERS_FETCH_SUCCESS:
|
||||||
return importAdminUsers(state, action.users);
|
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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,14 @@ export const getFeatures = createSelector([
|
||||||
v.software === MASTODON && gte(v.compatVersion, '2.1.0'),
|
v.software === MASTODON && gte(v.compatVersion, '2.1.0'),
|
||||||
v.software === PLEROMA && gte(v.version, '0.9.9'),
|
v.software === PLEROMA && gte(v.version, '0.9.9'),
|
||||||
]),
|
]),
|
||||||
suggestions: v.software === MASTODON && gte(v.compatVersion, '2.4.3'),
|
suggestions: any([
|
||||||
suggestionsV2: v.software === MASTODON && gte(v.compatVersion, '3.4.0'),
|
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'),
|
trends: v.software === MASTODON && gte(v.compatVersion, '3.0.0'),
|
||||||
mediaV2: any([
|
mediaV2: any([
|
||||||
v.software === MASTODON && gte(v.compatVersion, '3.1.3'),
|
v.software === MASTODON && gte(v.compatVersion, '3.1.3'),
|
||||||
|
|
Loading…
Reference in New Issue