diff --git a/app/soapbox/actions/accounts.ts b/app/soapbox/actions/accounts.ts index 31c5c6717..0b7f4e0cc 100644 --- a/app/soapbox/actions/accounts.ts +++ b/app/soapbox/actions/accounts.ts @@ -130,6 +130,8 @@ const maybeRedirectLogin = (error: AxiosError, history?: History) => { } }; +const noOp = () => new Promise(f => f(undefined)); + const createAccount = (params: Record) => (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: ACCOUNT_CREATE_REQUEST, params }); @@ -815,11 +817,11 @@ const rejectFollowRequestFail = (id: string, error: AxiosError) => ({ const pinAccount = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; + if (!isLoggedIn(getState)) return dispatch(noOp); dispatch(pinAccountRequest(id)); - api(getState).post(`/api/v1/accounts/${id}/pin`).then(response => { + return api(getState).post(`/api/v1/accounts/${id}/pin`).then(response => { dispatch(pinAccountSuccess(response.data)); }).catch(error => { dispatch(pinAccountFail(error)); @@ -828,11 +830,11 @@ const pinAccount = (id: string) => const unpinAccount = (id: string) => (dispatch: AppDispatch, getState: () => RootState) => { - if (!isLoggedIn(getState)) return; + if (!isLoggedIn(getState)) return dispatch(noOp); dispatch(unpinAccountRequest(id)); - api(getState).post(`/api/v1/accounts/${id}/unpin`).then(response => { + return api(getState).post(`/api/v1/accounts/${id}/unpin`).then(response => { dispatch(unpinAccountSuccess(response.data)); }).catch(error => { dispatch(unpinAccountFail(error)); diff --git a/app/soapbox/features/account/components/header.js b/app/soapbox/features/account/components/header.js index 081d346bd..d1d2d3235 100644 --- a/app/soapbox/features/account/components/header.js +++ b/app/soapbox/features/account/components/header.js @@ -257,7 +257,12 @@ class Header extends ImmutablePureComponent { }); } - // menu.push({ text: intl.formatMessage(account.relationship?.endorsed ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle }); + menu.push({ + text: intl.formatMessage(account.relationship?.endorsed ? messages.unendorse : messages.endorse), + action: this.props.onEndorseToggle, + icon: require('@tabler/icons/user-check.svg'), + }); + menu.push(null); } else if (features.lists && features.unrestrictedLists) { menu.push({ diff --git a/app/soapbox/features/account_timeline/containers/header_container.js b/app/soapbox/features/account_timeline/containers/header_container.js index f7a8d7ba8..2189b9a46 100644 --- a/app/soapbox/features/account_timeline/containers/header_container.js +++ b/app/soapbox/features/account_timeline/containers/header_container.js @@ -58,6 +58,8 @@ const messages = defineMessages({ userSuggested: { id: 'admin.users.user_suggested_message', defaultMessage: '@{acct} was suggested' }, userUnsuggested: { id: 'admin.users.user_unsuggested_message', defaultMessage: '@{acct} was unsuggested' }, removeFromFollowersConfirm: { id: 'confirmations.remove_from_followers.confirm', defaultMessage: 'Remove' }, + userEndorsed: { id: 'account.endorse.success', defaultMessage: 'You are now featuring @{acct} on your profile' }, + userUnendorsed: { id: 'account.unendorse.success', defaultMessage: 'You are no longer featuring @{acct}' }, }); const makeMapStateToProps = () => { @@ -144,9 +146,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onEndorseToggle(account) { if (account.relationship?.endorsed) { - dispatch(unpinAccount(account.get('id'))); + dispatch(unpinAccount(account.get('id'))) + .then(() => dispatch(snackbar.success(intl.formatMessage(messages.userUnendorsed, { acct: account.acct })))) + .catch(() => {}); } else { - dispatch(pinAccount(account.get('id'))); + dispatch(pinAccount(account.get('id'))) + .then(() => dispatch(snackbar.success(intl.formatMessage(messages.userEndorsed, { acct: account.acct })))) + .catch(() => {}); } }, diff --git a/app/soapbox/features/ui/components/pinned_accounts_panel.tsx b/app/soapbox/features/ui/components/pinned_accounts_panel.tsx index 1f5d3ebb6..ba8498237 100644 --- a/app/soapbox/features/ui/components/pinned_accounts_panel.tsx +++ b/app/soapbox/features/ui/components/pinned_accounts_panel.tsx @@ -5,6 +5,8 @@ import { FormattedMessage } from 'react-intl'; import { fetchPinnedAccounts } from 'soapbox/actions/accounts'; import { Widget } from 'soapbox/components/ui'; import AccountContainer from 'soapbox/containers/account_container'; +import BundleContainer from 'soapbox/features/ui/containers/bundle_container'; +import { WhoToFollowPanel } from 'soapbox/features/ui/util/async-components'; import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; import type { Account } from 'soapbox/types/entities'; @@ -23,7 +25,11 @@ const PinnedAccountsPanel: React.FC = ({ account, limit }) }, []); if (pinned.isEmpty()) { - return null; + return ( + + {Component => } + + ); } return ( diff --git a/app/soapbox/features/ui/components/subscription-button.tsx b/app/soapbox/features/ui/components/subscription-button.tsx index 810704fd6..b94430977 100644 --- a/app/soapbox/features/ui/components/subscription-button.tsx +++ b/app/soapbox/features/ui/components/subscription-button.tsx @@ -17,8 +17,8 @@ const messages = defineMessages({ unsubscribe: { id: 'account.unsubscribe', defaultMessage: 'Unsubscribe to notifications from @{name}' }, subscribeSuccess: { id: 'account.subscribe.success', defaultMessage: 'You have subscribed to this account.' }, unsubscribeSuccess: { id: 'account.unsubscribe.success', defaultMessage: 'You have unsubscribed from this account.' }, - subscribeFailure: { id: 'account.subscribe.failure', defaultMessage: 'An error occurred trying to subscribed to this account.' }, - unsubscribeFailure: { id: 'account.unsubscribe.failure', defaultMessage: 'An error occurred trying to unsubscribed to this account.' }, + subscribeFailure: { id: 'account.subscribe.failure', defaultMessage: 'An error occurred trying to subscribe to this account.' }, + unsubscribeFailure: { id: 'account.unsubscribe.failure', defaultMessage: 'An error occurred trying to unsubscribe to this account.' }, }); interface ISubscriptionButton { diff --git a/app/soapbox/pages/profile_page.tsx b/app/soapbox/pages/profile_page.tsx index 4a209303d..e188b897f 100644 --- a/app/soapbox/pages/profile_page.tsx +++ b/app/soapbox/pages/profile_page.tsx @@ -11,10 +11,11 @@ import { ProfileFieldsPanel, SignUpPanel, CtaBanner, + PinnedAccountsPanel, } from 'soapbox/features/ui/util/async-components'; import { useAppSelector, useFeatures, useSoapboxConfig } from 'soapbox/hooks'; import { findAccountByUsername } from 'soapbox/selectors'; -import { getAcct } from 'soapbox/utils/accounts'; +import { getAcct, isLocal } from 'soapbox/utils/accounts'; import { Column, Layout, Tabs } from '../components/ui'; import HeaderContainer from '../features/account_timeline/containers/header_container'; @@ -159,7 +160,11 @@ const ProfilePage: React.FC = ({ params, children }) => { {Component => } )} - {features.suggestions && ( + {(features.accountEndorsements && account && isLocal(account)) ? ( + + {Component => } + + ) : features.suggestions && ( {Component => }