mirror of https://github.com/mastodon/mastodon
				
				
				
			Adds featured tab to web (#34405)
							parent
							
								
									678c8dfeec
								
							
						
					
					
						commit
						d43bfa95aa
					
				@ -1,25 +1,6 @@
 | 
			
		||||
import { Switch, Route } from 'react-router-dom';
 | 
			
		||||
 | 
			
		||||
import AccountNavigation from 'mastodon/features/account/navigation';
 | 
			
		||||
import Trends from 'mastodon/features/getting_started/containers/trends_container';
 | 
			
		||||
import { showTrends } from 'mastodon/initial_state';
 | 
			
		||||
 | 
			
		||||
const DefaultNavigation: React.FC = () => (showTrends ? <Trends /> : null);
 | 
			
		||||
 | 
			
		||||
export const NavigationPortal: React.FC = () => (
 | 
			
		||||
  <div className='navigation-panel__portal'>
 | 
			
		||||
    <Switch>
 | 
			
		||||
      <Route path='/@:acct' exact component={AccountNavigation} />
 | 
			
		||||
      <Route
 | 
			
		||||
        path='/@:acct/tagged/:tagged?'
 | 
			
		||||
        exact
 | 
			
		||||
        component={AccountNavigation}
 | 
			
		||||
      />
 | 
			
		||||
      <Route path='/@:acct/with_replies' exact component={AccountNavigation} />
 | 
			
		||||
      <Route path='/@:acct/followers' exact component={AccountNavigation} />
 | 
			
		||||
      <Route path='/@:acct/following' exact component={AccountNavigation} />
 | 
			
		||||
      <Route path='/@:acct/media' exact component={AccountNavigation} />
 | 
			
		||||
      <Route component={DefaultNavigation} />
 | 
			
		||||
    </Switch>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div className='navigation-panel__portal'>{showTrends && <Trends />}</div>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,43 @@
 | 
			
		||||
import { FormattedMessage } from 'react-intl';
 | 
			
		||||
 | 
			
		||||
import { useAppSelector } from 'mastodon/store';
 | 
			
		||||
 | 
			
		||||
import { TimelineHint } from './timeline_hint';
 | 
			
		||||
 | 
			
		||||
interface RemoteHintProps {
 | 
			
		||||
  accountId?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const RemoteHint: React.FC<RemoteHintProps> = ({ accountId }) => {
 | 
			
		||||
  const account = useAppSelector((state) =>
 | 
			
		||||
    accountId ? state.accounts.get(accountId) : undefined,
 | 
			
		||||
  );
 | 
			
		||||
  const domain = account?.acct ? account.acct.split('@')[1] : undefined;
 | 
			
		||||
  if (
 | 
			
		||||
    !account ||
 | 
			
		||||
    !account.url ||
 | 
			
		||||
    account.acct !== account.username ||
 | 
			
		||||
    !domain
 | 
			
		||||
  ) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <TimelineHint
 | 
			
		||||
      url={account.url}
 | 
			
		||||
      message={
 | 
			
		||||
        <FormattedMessage
 | 
			
		||||
          id='hints.profiles.posts_may_be_missing'
 | 
			
		||||
          defaultMessage='Some posts from this profile may be missing.'
 | 
			
		||||
        />
 | 
			
		||||
      }
 | 
			
		||||
      label={
 | 
			
		||||
        <FormattedMessage
 | 
			
		||||
          id='hints.profiles.see_more_posts'
 | 
			
		||||
          defaultMessage='See more posts on {domain}'
 | 
			
		||||
          values={{ domain: <strong>{domain}</strong> }}
 | 
			
		||||
        />
 | 
			
		||||
      }
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@ -1,51 +0,0 @@
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
 | 
			
		||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 | 
			
		||||
 | 
			
		||||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
			
		||||
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
			
		||||
 | 
			
		||||
import { Hashtag } from 'mastodon/components/hashtag';
 | 
			
		||||
 | 
			
		||||
const messages = defineMessages({
 | 
			
		||||
  lastStatusAt: { id: 'account.featured_tags.last_status_at', defaultMessage: 'Last post on {date}' },
 | 
			
		||||
  empty: { id: 'account.featured_tags.last_status_never', defaultMessage: 'No posts' },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
class FeaturedTags extends ImmutablePureComponent {
 | 
			
		||||
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    account: ImmutablePropTypes.record,
 | 
			
		||||
    featuredTags: ImmutablePropTypes.list,
 | 
			
		||||
    tagged: PropTypes.string,
 | 
			
		||||
    intl: PropTypes.object.isRequired,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  render () {
 | 
			
		||||
    const { account, featuredTags, intl } = this.props;
 | 
			
		||||
 | 
			
		||||
    if (!account || account.get('suspended') || featuredTags.isEmpty()) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div className='getting-started__trends'>
 | 
			
		||||
        <h4><FormattedMessage id='account.featured_tags.title' defaultMessage="{name}'s featured hashtags" values={{ name: <bdi dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /> }} /></h4>
 | 
			
		||||
 | 
			
		||||
        {featuredTags.take(3).map(featuredTag => (
 | 
			
		||||
          <Hashtag
 | 
			
		||||
            key={featuredTag.get('name')}
 | 
			
		||||
            name={featuredTag.get('name')}
 | 
			
		||||
            to={`/@${account.get('acct')}/tagged/${featuredTag.get('name')}`}
 | 
			
		||||
            uses={featuredTag.get('statuses_count') * 1}
 | 
			
		||||
            withGraph={false}
 | 
			
		||||
            description={((featuredTag.get('statuses_count') * 1) > 0) ? intl.formatMessage(messages.lastStatusAt, { date: intl.formatDate(featuredTag.get('last_status_at'), { month: 'short', day: '2-digit' }) }) : intl.formatMessage(messages.empty)}
 | 
			
		||||
          />
 | 
			
		||||
        ))}
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default injectIntl(FeaturedTags);
 | 
			
		||||
@ -1,17 +0,0 @@
 | 
			
		||||
import { List as ImmutableList } from 'immutable';
 | 
			
		||||
import { connect } from 'react-redux';
 | 
			
		||||
 | 
			
		||||
import { makeGetAccount } from 'mastodon/selectors';
 | 
			
		||||
 | 
			
		||||
import FeaturedTags from '../components/featured_tags';
 | 
			
		||||
 | 
			
		||||
const mapStateToProps = () => {
 | 
			
		||||
  const getAccount = makeGetAccount();
 | 
			
		||||
 | 
			
		||||
  return (state, { accountId }) => ({
 | 
			
		||||
    account: getAccount(state, accountId),
 | 
			
		||||
    featuredTags: state.getIn(['user_lists', 'featured_tags', accountId, 'items'], ImmutableList()),
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default connect(mapStateToProps)(FeaturedTags);
 | 
			
		||||
@ -1,52 +0,0 @@
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { PureComponent } from 'react';
 | 
			
		||||
 | 
			
		||||
import { connect } from 'react-redux';
 | 
			
		||||
 | 
			
		||||
import FeaturedTags from 'mastodon/features/account/containers/featured_tags_container';
 | 
			
		||||
import { normalizeForLookup } from 'mastodon/reducers/accounts_map';
 | 
			
		||||
 | 
			
		||||
const mapStateToProps = (state, { match: { params: { acct } } }) => {
 | 
			
		||||
  const accountId = state.getIn(['accounts_map', normalizeForLookup(acct)]);
 | 
			
		||||
 | 
			
		||||
  if (!accountId) {
 | 
			
		||||
    return {
 | 
			
		||||
      isLoading: true,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    accountId,
 | 
			
		||||
    isLoading: false,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AccountNavigation extends PureComponent {
 | 
			
		||||
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    match: PropTypes.shape({
 | 
			
		||||
      params: PropTypes.shape({
 | 
			
		||||
        acct: PropTypes.string,
 | 
			
		||||
        tagged: PropTypes.string,
 | 
			
		||||
      }).isRequired,
 | 
			
		||||
    }).isRequired,
 | 
			
		||||
 | 
			
		||||
    accountId: PropTypes.string,
 | 
			
		||||
    isLoading: PropTypes.bool,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  render () {
 | 
			
		||||
    const { accountId, isLoading, match: { params: { tagged } } } = this.props;
 | 
			
		||||
 | 
			
		||||
    if (isLoading) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <FeaturedTags accountId={accountId} tagged={tagged} />
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default connect(mapStateToProps)(AccountNavigation);
 | 
			
		||||
@ -0,0 +1,50 @@
 | 
			
		||||
import { FormattedMessage } from 'react-intl';
 | 
			
		||||
 | 
			
		||||
import { LimitedAccountHint } from 'mastodon/features/account_timeline/components/limited_account_hint';
 | 
			
		||||
 | 
			
		||||
interface EmptyMessageProps {
 | 
			
		||||
  suspended: boolean;
 | 
			
		||||
  hidden: boolean;
 | 
			
		||||
  blockedBy: boolean;
 | 
			
		||||
  accountId?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const EmptyMessage: React.FC<EmptyMessageProps> = ({
 | 
			
		||||
  accountId,
 | 
			
		||||
  suspended,
 | 
			
		||||
  hidden,
 | 
			
		||||
  blockedBy,
 | 
			
		||||
}) => {
 | 
			
		||||
  if (!accountId) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let message: React.ReactNode = null;
 | 
			
		||||
 | 
			
		||||
  if (suspended) {
 | 
			
		||||
    message = (
 | 
			
		||||
      <FormattedMessage
 | 
			
		||||
        id='empty_column.account_suspended'
 | 
			
		||||
        defaultMessage='Account suspended'
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
  } else if (hidden) {
 | 
			
		||||
    message = <LimitedAccountHint accountId={accountId} />;
 | 
			
		||||
  } else if (blockedBy) {
 | 
			
		||||
    message = (
 | 
			
		||||
      <FormattedMessage
 | 
			
		||||
        id='empty_column.account_unavailable'
 | 
			
		||||
        defaultMessage='Profile unavailable'
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
  } else {
 | 
			
		||||
    message = (
 | 
			
		||||
      <FormattedMessage
 | 
			
		||||
        id='empty_column.account_featured'
 | 
			
		||||
        defaultMessage='This list is empty'
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return <div className='empty-column-indicator'>{message}</div>;
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,51 @@
 | 
			
		||||
import { defineMessages, useIntl } from 'react-intl';
 | 
			
		||||
 | 
			
		||||
import type { Map as ImmutableMap } from 'immutable';
 | 
			
		||||
 | 
			
		||||
import { Hashtag } from 'mastodon/components/hashtag';
 | 
			
		||||
 | 
			
		||||
export type TagMap = ImmutableMap<
 | 
			
		||||
  'id' | 'name' | 'url' | 'statuses_count' | 'last_status_at' | 'accountId',
 | 
			
		||||
  string | null
 | 
			
		||||
>;
 | 
			
		||||
 | 
			
		||||
interface FeaturedTagProps {
 | 
			
		||||
  tag: TagMap;
 | 
			
		||||
  account: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const messages = defineMessages({
 | 
			
		||||
  lastStatusAt: {
 | 
			
		||||
    id: 'account.featured_tags.last_status_at',
 | 
			
		||||
    defaultMessage: 'Last post on {date}',
 | 
			
		||||
  },
 | 
			
		||||
  empty: {
 | 
			
		||||
    id: 'account.featured_tags.last_status_never',
 | 
			
		||||
    defaultMessage: 'No posts',
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const FeaturedTag: React.FC<FeaturedTagProps> = ({ tag, account }) => {
 | 
			
		||||
  const intl = useIntl();
 | 
			
		||||
  const name = tag.get('name') ?? '';
 | 
			
		||||
  const count = Number.parseInt(tag.get('statuses_count') ?? '');
 | 
			
		||||
  return (
 | 
			
		||||
    <Hashtag
 | 
			
		||||
      key={name}
 | 
			
		||||
      name={name}
 | 
			
		||||
      to={`/@${account}/tagged/${name}`}
 | 
			
		||||
      uses={count}
 | 
			
		||||
      withGraph={false}
 | 
			
		||||
      description={
 | 
			
		||||
        count > 0
 | 
			
		||||
          ? intl.formatMessage(messages.lastStatusAt, {
 | 
			
		||||
              date: intl.formatDate(tag.get('last_status_at') ?? '', {
 | 
			
		||||
                month: 'short',
 | 
			
		||||
                day: '2-digit',
 | 
			
		||||
              }),
 | 
			
		||||
            })
 | 
			
		||||
          : intl.formatMessage(messages.empty)
 | 
			
		||||
      }
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,156 @@
 | 
			
		||||
import { useEffect } from 'react';
 | 
			
		||||
 | 
			
		||||
import { FormattedMessage } from 'react-intl';
 | 
			
		||||
 | 
			
		||||
import { useParams } from 'react-router';
 | 
			
		||||
 | 
			
		||||
import type { Map as ImmutableMap } from 'immutable';
 | 
			
		||||
import { List as ImmutableList } from 'immutable';
 | 
			
		||||
 | 
			
		||||
import { fetchFeaturedTags } from 'mastodon/actions/featured_tags';
 | 
			
		||||
import { expandAccountFeaturedTimeline } from 'mastodon/actions/timelines';
 | 
			
		||||
import { ColumnBackButton } from 'mastodon/components/column_back_button';
 | 
			
		||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
 | 
			
		||||
import { RemoteHint } from 'mastodon/components/remote_hint';
 | 
			
		||||
import StatusContainer from 'mastodon/containers/status_container';
 | 
			
		||||
import { useAccountId } from 'mastodon/hooks/useAccountId';
 | 
			
		||||
import { useAccountVisibility } from 'mastodon/hooks/useAccountVisibility';
 | 
			
		||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
 | 
			
		||||
 | 
			
		||||
import { AccountHeader } from '../account_timeline/components/account_header';
 | 
			
		||||
import Column from '../ui/components/column';
 | 
			
		||||
 | 
			
		||||
import { EmptyMessage } from './components/empty_message';
 | 
			
		||||
import { FeaturedTag } from './components/featured_tag';
 | 
			
		||||
import type { TagMap } from './components/featured_tag';
 | 
			
		||||
 | 
			
		||||
interface Params {
 | 
			
		||||
  acct?: string;
 | 
			
		||||
  id?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AccountFeatured = () => {
 | 
			
		||||
  const accountId = useAccountId();
 | 
			
		||||
  const { suspended, blockedBy, hidden } = useAccountVisibility(accountId);
 | 
			
		||||
  const forceEmptyState = suspended || blockedBy || hidden;
 | 
			
		||||
  const { acct = '' } = useParams<Params>();
 | 
			
		||||
 | 
			
		||||
  const dispatch = useAppDispatch();
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (accountId) {
 | 
			
		||||
      void dispatch(expandAccountFeaturedTimeline(accountId));
 | 
			
		||||
      dispatch(fetchFeaturedTags(accountId));
 | 
			
		||||
    }
 | 
			
		||||
  }, [accountId, dispatch]);
 | 
			
		||||
 | 
			
		||||
  const isLoading = useAppSelector(
 | 
			
		||||
    (state) =>
 | 
			
		||||
      !accountId ||
 | 
			
		||||
      !!(state.timelines as ImmutableMap<string, unknown>).getIn([
 | 
			
		||||
        `account:${accountId}:pinned`,
 | 
			
		||||
        'isLoading',
 | 
			
		||||
      ]) ||
 | 
			
		||||
      !!state.user_lists.getIn(['featured_tags', accountId, 'isLoading']),
 | 
			
		||||
  );
 | 
			
		||||
  const featuredTags = useAppSelector(
 | 
			
		||||
    (state) =>
 | 
			
		||||
      state.user_lists.getIn(
 | 
			
		||||
        ['featured_tags', accountId, 'items'],
 | 
			
		||||
        ImmutableList(),
 | 
			
		||||
      ) as ImmutableList<TagMap>,
 | 
			
		||||
  );
 | 
			
		||||
  const featuredStatusIds = useAppSelector(
 | 
			
		||||
    (state) =>
 | 
			
		||||
      (state.timelines as ImmutableMap<string, unknown>).getIn(
 | 
			
		||||
        [`account:${accountId}:pinned`, 'items'],
 | 
			
		||||
        ImmutableList(),
 | 
			
		||||
      ) as ImmutableList<string>,
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  if (isLoading) {
 | 
			
		||||
    return (
 | 
			
		||||
      <AccountFeaturedWrapper accountId={accountId}>
 | 
			
		||||
        <div className='scrollable__append'>
 | 
			
		||||
          <LoadingIndicator />
 | 
			
		||||
        </div>
 | 
			
		||||
      </AccountFeaturedWrapper>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (featuredStatusIds.isEmpty() && featuredTags.isEmpty()) {
 | 
			
		||||
    return (
 | 
			
		||||
      <AccountFeaturedWrapper accountId={accountId}>
 | 
			
		||||
        <EmptyMessage
 | 
			
		||||
          blockedBy={blockedBy}
 | 
			
		||||
          hidden={hidden}
 | 
			
		||||
          suspended={suspended}
 | 
			
		||||
          accountId={accountId}
 | 
			
		||||
        />
 | 
			
		||||
        <RemoteHint accountId={accountId} />
 | 
			
		||||
      </AccountFeaturedWrapper>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Column>
 | 
			
		||||
      <ColumnBackButton />
 | 
			
		||||
 | 
			
		||||
      <div className='scrollable scrollable--flex'>
 | 
			
		||||
        {accountId && (
 | 
			
		||||
          <AccountHeader accountId={accountId} hideTabs={forceEmptyState} />
 | 
			
		||||
        )}
 | 
			
		||||
        {!featuredTags.isEmpty() && (
 | 
			
		||||
          <>
 | 
			
		||||
            <h4 className='column-subheading'>
 | 
			
		||||
              <FormattedMessage
 | 
			
		||||
                id='account.featured.hashtags'
 | 
			
		||||
                defaultMessage='Hashtags'
 | 
			
		||||
              />
 | 
			
		||||
            </h4>
 | 
			
		||||
            {featuredTags.map((tag) => (
 | 
			
		||||
              <FeaturedTag key={tag.get('id')} tag={tag} account={acct} />
 | 
			
		||||
            ))}
 | 
			
		||||
          </>
 | 
			
		||||
        )}
 | 
			
		||||
        {!featuredStatusIds.isEmpty() && (
 | 
			
		||||
          <>
 | 
			
		||||
            <h4 className='column-subheading'>
 | 
			
		||||
              <FormattedMessage
 | 
			
		||||
                id='account.featured.posts'
 | 
			
		||||
                defaultMessage='Posts'
 | 
			
		||||
              />
 | 
			
		||||
            </h4>
 | 
			
		||||
            {featuredStatusIds.map((statusId) => (
 | 
			
		||||
              <StatusContainer
 | 
			
		||||
                key={`f-${statusId}`}
 | 
			
		||||
                // @ts-expect-error inferred props are wrong
 | 
			
		||||
                id={statusId}
 | 
			
		||||
                contextType='account'
 | 
			
		||||
              />
 | 
			
		||||
            ))}
 | 
			
		||||
          </>
 | 
			
		||||
        )}
 | 
			
		||||
        <RemoteHint accountId={accountId} />
 | 
			
		||||
      </div>
 | 
			
		||||
    </Column>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AccountFeaturedWrapper = ({
 | 
			
		||||
  children,
 | 
			
		||||
  accountId,
 | 
			
		||||
}: React.PropsWithChildren<{ accountId?: string }>) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <Column>
 | 
			
		||||
      <ColumnBackButton />
 | 
			
		||||
      <div className='scrollable scrollable--flex'>
 | 
			
		||||
        {accountId && <AccountHeader accountId={accountId} />}
 | 
			
		||||
        {children}
 | 
			
		||||
      </div>
 | 
			
		||||
    </Column>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line import/no-default-export
 | 
			
		||||
export default AccountFeatured;
 | 
			
		||||
@ -0,0 +1,37 @@
 | 
			
		||||
import { useEffect } from 'react';
 | 
			
		||||
 | 
			
		||||
import { useParams } from 'react-router';
 | 
			
		||||
 | 
			
		||||
import { fetchAccount, lookupAccount } from 'mastodon/actions/accounts';
 | 
			
		||||
import { normalizeForLookup } from 'mastodon/reducers/accounts_map';
 | 
			
		||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
 | 
			
		||||
 | 
			
		||||
interface Params {
 | 
			
		||||
  acct?: string;
 | 
			
		||||
  id?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useAccountId() {
 | 
			
		||||
  const { acct, id } = useParams<Params>();
 | 
			
		||||
  const accountId = useAppSelector(
 | 
			
		||||
    (state) =>
 | 
			
		||||
      id ??
 | 
			
		||||
      (state.accounts_map.get(normalizeForLookup(acct)) as string | undefined),
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const account = useAppSelector((state) =>
 | 
			
		||||
    accountId ? state.accounts.get(accountId) : undefined,
 | 
			
		||||
  );
 | 
			
		||||
  const isAccount = !!account;
 | 
			
		||||
 | 
			
		||||
  const dispatch = useAppDispatch();
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (!accountId) {
 | 
			
		||||
      dispatch(lookupAccount(acct));
 | 
			
		||||
    } else if (!isAccount) {
 | 
			
		||||
      dispatch(fetchAccount(accountId));
 | 
			
		||||
    }
 | 
			
		||||
  }, [dispatch, accountId, acct, isAccount]);
 | 
			
		||||
 | 
			
		||||
  return accountId;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,20 @@
 | 
			
		||||
import { getAccountHidden } from 'mastodon/selectors/accounts';
 | 
			
		||||
import { useAppSelector } from 'mastodon/store';
 | 
			
		||||
 | 
			
		||||
export function useAccountVisibility(accountId?: string) {
 | 
			
		||||
  const blockedBy = useAppSelector(
 | 
			
		||||
    (state) => !!state.relationships.getIn([accountId, 'blocked_by'], false),
 | 
			
		||||
  );
 | 
			
		||||
  const suspended = useAppSelector(
 | 
			
		||||
    (state) => !!state.accounts.getIn([accountId, 'suspended'], false),
 | 
			
		||||
  );
 | 
			
		||||
  const hidden = useAppSelector((state) =>
 | 
			
		||||
    accountId ? Boolean(getAccountHidden(state, accountId)) : false,
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    blockedBy,
 | 
			
		||||
    suspended,
 | 
			
		||||
    hidden,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue