Merge pull request #388 from ThibG/glitch-soc/federated-pinned-toots
Port WebUI toots/with reply/media tabs and pinned toots to glitch
This commit is contained in:
		
						commit
						a387f99659
					
				| 
						 | 
				
			
			@ -117,14 +117,15 @@ export function refreshTimeline(timelineId, path, params = {}) {
 | 
			
		|||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const refreshHomeTimeline         = () => refreshTimeline('home', '/api/v1/timelines/home');
 | 
			
		||||
export const refreshPublicTimeline       = () => refreshTimeline('public', '/api/v1/timelines/public');
 | 
			
		||||
export const refreshCommunityTimeline    = () => refreshTimeline('community', '/api/v1/timelines/public', { local: true });
 | 
			
		||||
export const refreshDirectTimeline       = () => refreshTimeline('direct', '/api/v1/timelines/direct');
 | 
			
		||||
export const refreshAccountTimeline      = accountId => refreshTimeline(`account:${accountId}`, `/api/v1/accounts/${accountId}/statuses`);
 | 
			
		||||
export const refreshAccountMediaTimeline = accountId => refreshTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
 | 
			
		||||
export const refreshHashtagTimeline      = hashtag => refreshTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
 | 
			
		||||
export const refreshListTimeline         = id => refreshTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`);
 | 
			
		||||
export const refreshHomeTimeline            = () => refreshTimeline('home', '/api/v1/timelines/home');
 | 
			
		||||
export const refreshPublicTimeline          = () => refreshTimeline('public', '/api/v1/timelines/public');
 | 
			
		||||
export const refreshCommunityTimeline       = () => refreshTimeline('community', '/api/v1/timelines/public', { local: true });
 | 
			
		||||
export const refreshDirectTimeline          = () => refreshTimeline('direct', '/api/v1/timelines/direct');
 | 
			
		||||
export const refreshAccountTimeline         = (accountId, withReplies) => refreshTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies });
 | 
			
		||||
export const refreshAccountFeaturedTimeline = accountId => refreshTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
 | 
			
		||||
export const refreshAccountMediaTimeline    = accountId => refreshTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
 | 
			
		||||
export const refreshHashtagTimeline         = hashtag => refreshTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
 | 
			
		||||
export const refreshListTimeline            = id => refreshTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`);
 | 
			
		||||
 | 
			
		||||
export function refreshTimelineFail(timeline, error, skipLoading) {
 | 
			
		||||
  return {
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +164,7 @@ export const expandHomeTimeline         = () => expandTimeline('home', '/api/v1/
 | 
			
		|||
export const expandPublicTimeline       = () => expandTimeline('public', '/api/v1/timelines/public');
 | 
			
		||||
export const expandCommunityTimeline    = () => expandTimeline('community', '/api/v1/timelines/public', { local: true });
 | 
			
		||||
export const expandDirectTimeline       = () => expandTimeline('direct', '/api/v1/timelines/direct');
 | 
			
		||||
export const expandAccountTimeline      = accountId => expandTimeline(`account:${accountId}`, `/api/v1/accounts/${accountId}/statuses`);
 | 
			
		||||
export const expandAccountTimeline      = (accountId, withReplies) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies })
 | 
			
		||||
export const expandAccountMediaTimeline = accountId => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { only_media: true });
 | 
			
		||||
export const expandHashtagTimeline      = hashtag => expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`);
 | 
			
		||||
export const expandListTimeline         = id => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ export default class StatusList extends ImmutablePureComponent {
 | 
			
		|||
  static propTypes = {
 | 
			
		||||
    scrollKey: PropTypes.string.isRequired,
 | 
			
		||||
    statusIds: ImmutablePropTypes.list.isRequired,
 | 
			
		||||
    featuredStatusIds: ImmutablePropTypes.list,
 | 
			
		||||
    onScrollToBottom: PropTypes.func,
 | 
			
		||||
    onScrollToTop: PropTypes.func,
 | 
			
		||||
    onScroll: PropTypes.func,
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +51,7 @@ export default class StatusList extends ImmutablePureComponent {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  render () {
 | 
			
		||||
    const { statusIds, ...other }  = this.props;
 | 
			
		||||
    const { statusIds, featuredStatusIds, ...other }  = this.props;
 | 
			
		||||
    const { isLoading, isPartial } = other;
 | 
			
		||||
 | 
			
		||||
    if (isPartial) {
 | 
			
		||||
| 
						 | 
				
			
			@ -68,8 +69,8 @@ export default class StatusList extends ImmutablePureComponent {
 | 
			
		|||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const scrollableContent = (isLoading || statusIds.size > 0) ? (
 | 
			
		||||
      statusIds.map((statusId) => (
 | 
			
		||||
    let scrollableContent = (isLoading || statusIds.size > 0) ? (
 | 
			
		||||
      statusIds.map(statusId => (
 | 
			
		||||
        <StatusContainer
 | 
			
		||||
          key={statusId}
 | 
			
		||||
          id={statusId}
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +80,18 @@ export default class StatusList extends ImmutablePureComponent {
 | 
			
		|||
      ))
 | 
			
		||||
    ) : null;
 | 
			
		||||
 | 
			
		||||
    if (scrollableContent && featuredStatusIds) {
 | 
			
		||||
      scrollableContent = featuredStatusIds.map(statusId => (
 | 
			
		||||
        <StatusContainer
 | 
			
		||||
          key={`f-${statusId}`}
 | 
			
		||||
          id={statusId}
 | 
			
		||||
          featured
 | 
			
		||||
          onMoveUp={this.handleMoveUp}
 | 
			
		||||
          onMoveDown={this.handleMoveDown}
 | 
			
		||||
        />
 | 
			
		||||
      )).concat(scrollableContent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <ScrollableList {...other} ref={this.setRef}>
 | 
			
		||||
        {scrollableContent}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,6 +34,10 @@ export default class StatusPrepend extends React.PureComponent {
 | 
			
		|||
      </a>
 | 
			
		||||
    );
 | 
			
		||||
    switch (type) {
 | 
			
		||||
    case 'featured':
 | 
			
		||||
      return (
 | 
			
		||||
        <FormattedMessage id='status.pinned' defaultMessage='Pinned toot' />
 | 
			
		||||
      );
 | 
			
		||||
    case 'reblogged_by':
 | 
			
		||||
      return (
 | 
			
		||||
        <FormattedMessage
 | 
			
		||||
| 
						 | 
				
			
			@ -67,11 +71,11 @@ export default class StatusPrepend extends React.PureComponent {
 | 
			
		|||
    const { type } = this.props;
 | 
			
		||||
 | 
			
		||||
    return !type ? null : (
 | 
			
		||||
      <aside className={type === 'reblogged_by' ? 'status__prepend' : 'notification__message'}>
 | 
			
		||||
        <div className={type === 'reblogged_by' ? 'status__prepend-icon-wrapper' : 'notification__favourite-icon-wrapper'}>
 | 
			
		||||
      <aside className={type === 'reblogged_by' || type === 'featured' ? 'status__prepend' : 'notification__message'}>
 | 
			
		||||
        <div className={type === 'reblogged_by' || type === 'featured' ? 'status__prepend-icon-wrapper' : 'notification__favourite-icon-wrapper'}>
 | 
			
		||||
          <i
 | 
			
		||||
            className={`fa fa-fw fa-${
 | 
			
		||||
              type === 'favourite' ? 'star star-icon' : 'retweet'
 | 
			
		||||
              type === 'favourite' ? 'star star-icon' : (type === 'featured' ? 'thumb-tack' : 'retweet')
 | 
			
		||||
            } status__prepend-icon`}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,10 @@ const makeMapStateToProps = () => {
 | 
			
		|||
    let account = undefined;
 | 
			
		||||
    let prepend = undefined;
 | 
			
		||||
 | 
			
		||||
    if (reblogStatus !== null && typeof reblogStatus === 'object') {
 | 
			
		||||
    if (props.featured) {
 | 
			
		||||
      account = status.get('account');
 | 
			
		||||
      prepend = 'featured';
 | 
			
		||||
    } else if (reblogStatus !== null && typeof reblogStatus === 'object') {
 | 
			
		||||
      account = status.get('account');
 | 
			
		||||
      status = reblogStatus;
 | 
			
		||||
      prepend = 'reblogged_by';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,11 +53,11 @@ export default class ActionBar extends React.PureComponent {
 | 
			
		|||
    let extraInfo = '';
 | 
			
		||||
 | 
			
		||||
    menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention });
 | 
			
		||||
 | 
			
		||||
    if ('share' in navigator) {
 | 
			
		||||
      menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare });
 | 
			
		||||
    }
 | 
			
		||||
    menu.push(null);
 | 
			
		||||
    menu.push({ text: intl.formatMessage(messages.media), to: `/accounts/${account.get('id')}/media` });
 | 
			
		||||
 | 
			
		||||
    menu.push(null);
 | 
			
		||||
 | 
			
		||||
    if (account.get('id') === me) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,24 +12,26 @@ export default class MediaItem extends ImmutablePureComponent {
 | 
			
		|||
  render () {
 | 
			
		||||
    const { media } = this.props;
 | 
			
		||||
    const status = media.get('status');
 | 
			
		||||
    const focusX = media.getIn(['meta', 'focus', 'x']);
 | 
			
		||||
    const focusY = media.getIn(['meta', 'focus', 'y']);
 | 
			
		||||
    const x = ((focusX /  2) + .5) * 100;
 | 
			
		||||
    const y = ((focusY / -2) + .5) * 100;
 | 
			
		||||
    const style = {};
 | 
			
		||||
 | 
			
		||||
    let content, style;
 | 
			
		||||
    let content;
 | 
			
		||||
 | 
			
		||||
    if (media.get('type') === 'gifv') {
 | 
			
		||||
      content = <span className='media-gallery__gifv__label'>GIF</span>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!status.get('sensitive')) {
 | 
			
		||||
      style = { backgroundImage: `url(${media.get('preview_url')})` };
 | 
			
		||||
      style.backgroundImage    = `url(${media.get('preview_url')})`;
 | 
			
		||||
      style.backgroundPosition = `${x}% ${y}%`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div className='account-gallery__item'>
 | 
			
		||||
        <Permalink
 | 
			
		||||
          to={`/statuses/${status.get('id')}`}
 | 
			
		||||
          href={status.get('url')}
 | 
			
		||||
          style={style}
 | 
			
		||||
        >
 | 
			
		||||
        <Permalink to={`/statuses/${status.get('id')}`} href={status.get('url')} style={style}>
 | 
			
		||||
          {content}
 | 
			
		||||
        </Permalink>
 | 
			
		||||
      </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
			
		|||
import { getAccountGallery } from 'flavours/glitch/selectors';
 | 
			
		||||
import MediaItem from './components/media_item';
 | 
			
		||||
import HeaderContainer from 'flavours/glitch/features/account_timeline/containers/header_container';
 | 
			
		||||
import { FormattedMessage } from 'react-intl';
 | 
			
		||||
import { ScrollContainer } from 'react-router-scroll-4';
 | 
			
		||||
import LoadMore from 'flavours/glitch/components/load_more';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -89,10 +88,6 @@ export default class AccountGallery extends ImmutablePureComponent {
 | 
			
		|||
          <div className='scrollable' onScroll={this.handleScroll}>
 | 
			
		||||
            <HeaderContainer accountId={this.props.params.accountId} />
 | 
			
		||||
 | 
			
		||||
            <div className='account-section-headline'>
 | 
			
		||||
              <FormattedMessage id='account.media' defaultMessage='Media' />
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div className='account-gallery__container'>
 | 
			
		||||
              {medias.map(media =>
 | 
			
		||||
                (<MediaItem
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,8 @@ import InnerHeader from 'flavours/glitch/features/account/components/header';
 | 
			
		|||
import ActionBar from 'flavours/glitch/features/account/components/action_bar';
 | 
			
		||||
import MissingIndicator from 'flavours/glitch/components/missing_indicator';
 | 
			
		||||
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
			
		||||
import { FormattedMessage } from 'react-intl';
 | 
			
		||||
import { NavLink } from 'react-router-dom';
 | 
			
		||||
 | 
			
		||||
export default class Header extends ImmutablePureComponent {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -18,6 +20,7 @@ export default class Header extends ImmutablePureComponent {
 | 
			
		|||
    onMute: PropTypes.func.isRequired,
 | 
			
		||||
    onBlockDomain: PropTypes.func.isRequired,
 | 
			
		||||
    onUnblockDomain: PropTypes.func.isRequired,
 | 
			
		||||
    hideTabs: PropTypes.bool,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  static contextTypes = {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +68,7 @@ export default class Header extends ImmutablePureComponent {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  render () {
 | 
			
		||||
    const { account } = this.props;
 | 
			
		||||
    const { account, hideTabs } = this.props;
 | 
			
		||||
 | 
			
		||||
    if (account === null) {
 | 
			
		||||
      return <MissingIndicator />;
 | 
			
		||||
| 
						 | 
				
			
			@ -89,6 +92,14 @@ export default class Header extends ImmutablePureComponent {
 | 
			
		|||
          onBlockDomain={this.handleBlockDomain}
 | 
			
		||||
          onUnblockDomain={this.handleUnblockDomain}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        {!hideTabs && (
 | 
			
		||||
          <div className='account__section-headline'>
 | 
			
		||||
            <NavLink exact to={`/accounts/${account.get('id')}`}><FormattedMessage id='account.posts' defaultMessage='Toots' /></NavLink>
 | 
			
		||||
            <NavLink exact to={`/accounts/${account.get('id')}/with_replies`}><FormattedMessage id='account.posts_with_replies' defaultMessage='Toots with replies' /></NavLink>
 | 
			
		||||
            <NavLink exact to={`/accounts/${account.get('id')}/media`}><FormattedMessage id='account.media' defaultMessage='Media' /></NavLink>
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ import { connect } from 'react-redux';
 | 
			
		|||
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
import { fetchAccount } from 'flavours/glitch/actions/accounts';
 | 
			
		||||
import { refreshAccountTimeline, expandAccountTimeline } from 'flavours/glitch/actions/timelines';
 | 
			
		||||
import { refreshAccountTimeline, refreshAccountFeaturedTimeline, expandAccountTimeline } from 'flavours/glitch/actions/timelines';
 | 
			
		||||
import StatusList from '../../components/status_list';
 | 
			
		||||
import LoadingIndicator from '../../components/loading_indicator';
 | 
			
		||||
import Column from '../ui/components/column';
 | 
			
		||||
| 
						 | 
				
			
			@ -12,11 +12,16 @@ import ColumnBackButton from '../../components/column_back_button';
 | 
			
		|||
import { List as ImmutableList } from 'immutable';
 | 
			
		||||
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
			
		||||
 | 
			
		||||
const mapStateToProps = (state, props) => ({
 | 
			
		||||
  statusIds: state.getIn(['timelines', `account:${props.params.accountId}`, 'items'], ImmutableList()),
 | 
			
		||||
  isLoading: state.getIn(['timelines', `account:${props.params.accountId}`, 'isLoading']),
 | 
			
		||||
  hasMore: !!state.getIn(['timelines', `account:${props.params.accountId}`, 'next']),
 | 
			
		||||
});
 | 
			
		||||
const mapStateToProps = (state, { params: { accountId }, withReplies = false }) => {
 | 
			
		||||
  const path = withReplies ? `${accountId}:with_replies` : accountId;
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    statusIds: state.getIn(['timelines', `account:${path}`, 'items'], ImmutableList()),
 | 
			
		||||
    featuredStatusIds: withReplies ? ImmutableList() : state.getIn(['timelines', `account:${accountId}:pinned`, 'items'], ImmutableList()),
 | 
			
		||||
    isLoading: state.getIn(['timelines', `account:${path}`, 'isLoading']),
 | 
			
		||||
    hasMore: !!state.getIn(['timelines', `account:${path}`, 'next']),
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@connect(mapStateToProps)
 | 
			
		||||
export default class AccountTimeline extends ImmutablePureComponent {
 | 
			
		||||
| 
						 | 
				
			
			@ -25,30 +30,36 @@ export default class AccountTimeline extends ImmutablePureComponent {
 | 
			
		|||
    params: PropTypes.object.isRequired,
 | 
			
		||||
    dispatch: PropTypes.func.isRequired,
 | 
			
		||||
    statusIds: ImmutablePropTypes.list,
 | 
			
		||||
    featuredStatusIds: ImmutablePropTypes.list,
 | 
			
		||||
    isLoading: PropTypes.bool,
 | 
			
		||||
    hasMore: PropTypes.bool,
 | 
			
		||||
    withReplies: PropTypes.bool,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  componentWillMount () {
 | 
			
		||||
    this.props.dispatch(fetchAccount(this.props.params.accountId));
 | 
			
		||||
    this.props.dispatch(refreshAccountTimeline(this.props.params.accountId));
 | 
			
		||||
    const { params: { accountId }, withReplies } = this.props;
 | 
			
		||||
 | 
			
		||||
    this.props.dispatch(fetchAccount(accountId));
 | 
			
		||||
    this.props.dispatch(refreshAccountFeaturedTimeline(accountId));
 | 
			
		||||
    this.props.dispatch(refreshAccountTimeline(accountId, withReplies));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentWillReceiveProps (nextProps) {
 | 
			
		||||
    if (nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) {
 | 
			
		||||
    if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) {
 | 
			
		||||
      this.props.dispatch(fetchAccount(nextProps.params.accountId));
 | 
			
		||||
      this.props.dispatch(refreshAccountTimeline(nextProps.params.accountId));
 | 
			
		||||
      this.props.dispatch(refreshAccountFeaturedTimeline(nextProps.params.accountId));
 | 
			
		||||
      this.props.dispatch(refreshAccountTimeline(nextProps.params.accountId, nextProps.params.withReplies));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleScrollToBottom = () => {
 | 
			
		||||
    if (!this.props.isLoading && this.props.hasMore) {
 | 
			
		||||
      this.props.dispatch(expandAccountTimeline(this.props.params.accountId));
 | 
			
		||||
      this.props.dispatch(expandAccountTimeline(this.props.params.accountId, this.props.withReplies));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render () {
 | 
			
		||||
    const { statusIds, isLoading, hasMore } = this.props;
 | 
			
		||||
    const { statusIds, featuredStatusIds, isLoading, hasMore } = this.props;
 | 
			
		||||
 | 
			
		||||
    if (!statusIds && isLoading) {
 | 
			
		||||
      return (
 | 
			
		||||
| 
						 | 
				
			
			@ -66,6 +77,7 @@ export default class AccountTimeline extends ImmutablePureComponent {
 | 
			
		|||
          prepend={<HeaderContainer accountId={this.props.params.accountId} />}
 | 
			
		||||
          scrollKey='account_timeline'
 | 
			
		||||
          statusIds={statusIds}
 | 
			
		||||
          featuredStatusIds={featuredStatusIds}
 | 
			
		||||
          isLoading={isLoading}
 | 
			
		||||
          hasMore={hasMore}
 | 
			
		||||
          onScrollToBottom={this.handleScrollToBottom}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ export default class Followers extends ImmutablePureComponent {
 | 
			
		|||
        <ScrollContainer scrollKey='followers'>
 | 
			
		||||
          <div className='scrollable' onScroll={this.handleScroll}>
 | 
			
		||||
            <div className='followers'>
 | 
			
		||||
              <HeaderContainer accountId={this.props.params.accountId} />
 | 
			
		||||
              <HeaderContainer accountId={this.props.params.accountId} hideTabs />
 | 
			
		||||
              {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
 | 
			
		||||
              {loadMore}
 | 
			
		||||
            </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ export default class Following extends ImmutablePureComponent {
 | 
			
		|||
        <ScrollContainer scrollKey='following'>
 | 
			
		||||
          <div className='scrollable' onScroll={this.handleScroll}>
 | 
			
		||||
            <div className='following'>
 | 
			
		||||
              <HeaderContainer accountId={this.props.params.accountId} />
 | 
			
		||||
              <HeaderContainer accountId={this.props.params.accountId} hideTabs />
 | 
			
		||||
              {accountIds.map(id => <AccountContainer key={id} id={id} withNote={false} />)}
 | 
			
		||||
              {loadMore}
 | 
			
		||||
            </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -431,6 +431,7 @@ export default class UI extends React.Component {
 | 
			
		|||
              <WrappedRoute path='/statuses/:statusId/favourites' component={Favourites} content={children} />
 | 
			
		||||
 | 
			
		||||
              <WrappedRoute path='/accounts/:accountId' exact component={AccountTimeline} content={children} />
 | 
			
		||||
              <WrappedRoute path='/accounts/:accountId/with_replies' component={AccountTimeline} content={children} componentParams={{ withReplies: true }} />
 | 
			
		||||
              <WrappedRoute path='/accounts/:accountId/followers' component={Followers} content={children} />
 | 
			
		||||
              <WrappedRoute path='/accounts/:accountId/following' component={Following} content={children} />
 | 
			
		||||
              <WrappedRoute path='/accounts/:accountId/media' component={AccountGallery} content={children} />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -380,64 +380,96 @@
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
.account-gallery__container {
 | 
			
		||||
  margin: -2px;
 | 
			
		||||
  padding: 4px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  justify-content: center;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
  padding: 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.account-gallery__item {
 | 
			
		||||
  flex: 1 1 auto;
 | 
			
		||||
  width: calc(100% / 3 - 4px);
 | 
			
		||||
  height: 95px;
 | 
			
		||||
  margin: 2px;
 | 
			
		||||
  flex-grow: 1;
 | 
			
		||||
  width: 50%;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  position: relative;
 | 
			
		||||
 | 
			
		||||
  &::before {
 | 
			
		||||
    content: "";
 | 
			
		||||
    display: block;
 | 
			
		||||
    padding-top: 100%;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  a {
 | 
			
		||||
    display: block;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    width: calc(100% - 4px);
 | 
			
		||||
    height: calc(100% - 4px);
 | 
			
		||||
    margin: 2px;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    background-color: $base-overlay-background;
 | 
			
		||||
    background-size: cover;
 | 
			
		||||
    background-position: center;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    color: inherit;
 | 
			
		||||
    text-decoration: none;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
 | 
			
		||||
    &:hover,
 | 
			
		||||
    &:active,
 | 
			
		||||
    &:focus {
 | 
			
		||||
      outline: 0;
 | 
			
		||||
 | 
			
		||||
      &::before {
 | 
			
		||||
        content: "";
 | 
			
		||||
        display: block;
 | 
			
		||||
        width: 100%;
 | 
			
		||||
        height: 100%;
 | 
			
		||||
        background: rgba($base-overlay-background, 0.3);
 | 
			
		||||
        border-radius: 4px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.account-section-headline {
 | 
			
		||||
  color: $ui-base-lighter-color;
 | 
			
		||||
  background: lighten($ui-base-color, 2%);
 | 
			
		||||
  border-bottom: 1px solid lighten($ui-base-color, 4%);
 | 
			
		||||
  padding: 15px 10px;
 | 
			
		||||
  font-size: 14px;
 | 
			
		||||
  font-weight: 500;
 | 
			
		||||
  position: relative;
 | 
			
		||||
.account__section-headline {
 | 
			
		||||
  background: darken($ui-base-color, 4%);
 | 
			
		||||
  border-bottom: 1px solid lighten($ui-base-color, 8%);
 | 
			
		||||
  cursor: default;
 | 
			
		||||
  display: flex;
 | 
			
		||||
 | 
			
		||||
  &::before,
 | 
			
		||||
  &::after {
 | 
			
		||||
  a {
 | 
			
		||||
    display: block;
 | 
			
		||||
    content: "";
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    bottom: 0;
 | 
			
		||||
    left: 18px;
 | 
			
		||||
    width: 0;
 | 
			
		||||
    height: 0;
 | 
			
		||||
    border-style: solid;
 | 
			
		||||
    border-width: 0 10px 10px;
 | 
			
		||||
    border-color: transparent transparent lighten($ui-base-color, 4%);
 | 
			
		||||
  }
 | 
			
		||||
    flex: 1 1 auto;
 | 
			
		||||
    color: $ui-primary-color;
 | 
			
		||||
    padding: 15px 0;
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    font-weight: 500;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    text-decoration: none;
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
  &::after {
 | 
			
		||||
    bottom: -1px;
 | 
			
		||||
    border-color: transparent transparent $ui-base-color;
 | 
			
		||||
    &.active {
 | 
			
		||||
      color: $ui-secondary-color;
 | 
			
		||||
 | 
			
		||||
      &::before,
 | 
			
		||||
      &::after {
 | 
			
		||||
        display: block;
 | 
			
		||||
        content: "";
 | 
			
		||||
        position: absolute;
 | 
			
		||||
        bottom: 0;
 | 
			
		||||
        left: 50%;
 | 
			
		||||
        width: 0;
 | 
			
		||||
        height: 0;
 | 
			
		||||
        transform: translateX(-50%);
 | 
			
		||||
        border-style: solid;
 | 
			
		||||
        border-width: 0 10px 10px;
 | 
			
		||||
        border-color: transparent transparent lighten($ui-base-color, 8%);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      &::after {
 | 
			
		||||
        bottom: -1px;
 | 
			
		||||
        border-color: transparent transparent $ui-base-color;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,6 +102,7 @@
 | 
			
		|||
  &.standalone {
 | 
			
		||||
    .media-gallery__item-gifv-thumbnail {
 | 
			
		||||
      transform: none;
 | 
			
		||||
      top: 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,14 +35,19 @@ export class WrappedRoute extends React.Component {
 | 
			
		|||
    component: PropTypes.func.isRequired,
 | 
			
		||||
    content: PropTypes.node,
 | 
			
		||||
    multiColumn: PropTypes.bool,
 | 
			
		||||
    componentParams: PropTypes.object,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static defaultProps = {
 | 
			
		||||
    componentParams: {},
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  renderComponent = ({ match }) => {
 | 
			
		||||
    const { component, content, multiColumn } = this.props;
 | 
			
		||||
    const { component, content, multiColumn, componentParams } = this.props;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <BundleContainer fetchComponent={component} loading={this.renderLoading} error={this.renderError}>
 | 
			
		||||
        {Component => <Component params={match.params} multiColumn={multiColumn}>{content}</Component>}
 | 
			
		||||
        {Component => <Component params={match.params} multiColumn={multiColumn} {...componentParams}>{content}</Component>}
 | 
			
		||||
      </BundleContainer>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue