Display label at end of follows/followers list when count > list.size
This commit is contained in:
parent
70a3e53db4
commit
4a5525adf0
|
@ -0,0 +1,93 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
export default class MoreFollows extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
visible: PropTypes.bool,
|
||||||
|
moreFollows: PropTypes.number,
|
||||||
|
isFollowing: PropTypes.bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
visible: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { moreFollows, isFollowing, visible } = this.props;
|
||||||
|
|
||||||
|
if (isFollowing === true && moreFollows === 1) {
|
||||||
|
return (
|
||||||
|
<div className='morefollows-indicator'>
|
||||||
|
<div>
|
||||||
|
<div className='morefollows-indicator__label' style={{ visibility: visible ? 'visible' : 'hidden' }}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='morefollows_indicator.follows_1_label'
|
||||||
|
tagName='strong'
|
||||||
|
defaultMessage='...and {moreFollows} more follow on a remote site.'
|
||||||
|
values={{
|
||||||
|
moreFollows: <span>{moreFollows}</span>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (isFollowing && moreFollows > 1) {
|
||||||
|
return (
|
||||||
|
<div className='morefollows-indicator'>
|
||||||
|
<div>
|
||||||
|
<div className='morefollows-indicator__label' style={{ visibility: visible ? 'visible' : 'hidden' }}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='morefollows_indicator.follows_2_label'
|
||||||
|
tagName='strong'
|
||||||
|
defaultMessage='...and {moreFollows} more follows on remote sites.'
|
||||||
|
values={{
|
||||||
|
moreFollows: <span>{moreFollows}</span>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (!isFollowing && moreFollows === 1) {
|
||||||
|
return (
|
||||||
|
<div className='morefollows-indicator'>
|
||||||
|
<div>
|
||||||
|
<div className='morefollows-indicator__label' style={{ visibility: visible ? 'visible' : 'hidden' }}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='morefollows_indicator.followers_1_label'
|
||||||
|
tagName='strong'
|
||||||
|
defaultMessage='...and {moreFollows} more follower on a remote site.'
|
||||||
|
values={{
|
||||||
|
moreFollows: <span>{moreFollows}</span>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (!isFollowing && moreFollows > 1) {
|
||||||
|
return (
|
||||||
|
<div className='morefollows-indicator'>
|
||||||
|
<div>
|
||||||
|
<div className='morefollows-indicator__label' style={{ visibility: visible ? 'visible' : 'hidden' }}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='morefollows_indicator.followers_2_label'
|
||||||
|
tagName='strong'
|
||||||
|
defaultMessage='...and {moreFollows} more followers on remote sites.'
|
||||||
|
values={{
|
||||||
|
moreFollows: <span>{moreFollows}</span>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import React, { PureComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container';
|
import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container';
|
||||||
import LoadMore from './load_more';
|
import LoadMore from './load_more';
|
||||||
|
import MoreFollows from './more_follows';
|
||||||
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper';
|
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper';
|
||||||
import { throttle } from 'lodash';
|
import { throttle } from 'lodash';
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
|
@ -21,16 +22,18 @@ export default class ScrollableList extends PureComponent {
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
showLoading: PropTypes.bool,
|
showLoading: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
|
hasMoreFollows: PropTypes.number,
|
||||||
prepend: PropTypes.node,
|
prepend: PropTypes.node,
|
||||||
alwaysPrepend: PropTypes.bool,
|
alwaysPrepend: PropTypes.bool,
|
||||||
emptyMessage: PropTypes.node,
|
emptyMessage: PropTypes.node,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
onScrollToTop: PropTypes.func,
|
onScrollToTop: PropTypes.func,
|
||||||
onScroll: PropTypes.func,
|
onScroll: PropTypes.func,
|
||||||
|
isFollowing: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
cachedMediaWidth: 250, // Default media/card width using default Gab Social theme
|
cachedMediaWidth: 250, // Default media/card width using default theme
|
||||||
};
|
};
|
||||||
|
|
||||||
intersectionObserverWrapper = new IntersectionObserverWrapper();
|
intersectionObserverWrapper = new IntersectionObserverWrapper();
|
||||||
|
@ -205,12 +208,13 @@ export default class ScrollableList extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { children, scrollKey, showLoading, isLoading, hasMore, prepend, alwaysPrepend, emptyMessage, onLoadMore } = this.props;
|
const { children, scrollKey, showLoading, isLoading, hasMore, hasMoreFollows, isFollowing, prepend, alwaysPrepend, emptyMessage, onLoadMore } = this.props;
|
||||||
const childrenCount = React.Children.count(children);
|
const childrenCount = React.Children.count(children);
|
||||||
|
|
||||||
const trackScroll = true; //placeholder
|
const trackScroll = true; //placeholder
|
||||||
|
|
||||||
const loadMore = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
|
const loadMore = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
|
||||||
|
const moreFollows = (hasMoreFollows !== undefined && isFollowing !== undefined) ? <MoreFollows visible={!isLoading} moreFollows={hasMoreFollows} isFollowing={isFollowing} /> : null;
|
||||||
let scrollableArea = null;
|
let scrollableArea = null;
|
||||||
|
|
||||||
if (showLoading) {
|
if (showLoading) {
|
||||||
|
@ -248,7 +252,7 @@ export default class ScrollableList extends PureComponent {
|
||||||
})}
|
})}
|
||||||
</IntersectionObserverArticleContainer>
|
</IntersectionObserverArticleContainer>
|
||||||
))}
|
))}
|
||||||
|
{moreFollows}
|
||||||
{loadMore}
|
{loadMore}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,6 +16,7 @@ import AccountContainer from '../../containers/account_container';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../ui/components/column';
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
import MissingIndicator from 'gabsocial/components/missing_indicator';
|
import MissingIndicator from 'gabsocial/components/missing_indicator';
|
||||||
|
import { getHasMoreFollowsCount } from 'gabsocial/utils/accounts';
|
||||||
|
|
||||||
const mapStateToProps = (state, { params: { username }, withReplies = false }) => {
|
const mapStateToProps = (state, { params: { username }, withReplies = false }) => {
|
||||||
const me = state.get('me');
|
const me = state.get('me');
|
||||||
|
@ -30,6 +31,7 @@ const mapStateToProps = (state, { params: { username }, withReplies = false }) =
|
||||||
accountId = account ? account.getIn(['id'], null) : -1;
|
accountId = account ? account.getIn(['id'], null) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasMoreFollows = getHasMoreFollowsCount(state, accountId, false);
|
||||||
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false);
|
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false);
|
||||||
const isLocked = state.getIn(['accounts', accountId, 'locked'], false);
|
const isLocked = state.getIn(['accounts', accountId, 'locked'], false);
|
||||||
const isFollowing = state.getIn(['relationships', accountId, 'following'], false);
|
const isFollowing = state.getIn(['relationships', accountId, 'following'], false);
|
||||||
|
@ -41,6 +43,7 @@ const mapStateToProps = (state, { params: { username }, withReplies = false }) =
|
||||||
isAccount: !!state.getIn(['accounts', accountId]),
|
isAccount: !!state.getIn(['accounts', accountId]),
|
||||||
accountIds: state.getIn(['user_lists', 'followers', accountId, 'items']),
|
accountIds: state.getIn(['user_lists', 'followers', accountId, 'items']),
|
||||||
hasMore: !!state.getIn(['user_lists', 'followers', accountId, 'next']),
|
hasMore: !!state.getIn(['user_lists', 'followers', accountId, 'next']),
|
||||||
|
hasMoreFollows,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -52,6 +55,7 @@ class Followers extends ImmutablePureComponent {
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
accountIds: ImmutablePropTypes.list,
|
accountIds: ImmutablePropTypes.list,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
|
hasMoreFollows: PropTypes.number,
|
||||||
isAccount: PropTypes.bool,
|
isAccount: PropTypes.bool,
|
||||||
unavailable: PropTypes.bool,
|
unavailable: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
@ -81,7 +85,8 @@ class Followers extends ImmutablePureComponent {
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { accountIds, hasMore, isAccount, accountId, unavailable } = this.props;
|
const { accountIds, hasMore, hasMoreFollows, isAccount, accountId, unavailable } = this.props;
|
||||||
|
const isFollowing = false;
|
||||||
|
|
||||||
if (!isAccount && accountId !== -1) {
|
if (!isAccount && accountId !== -1) {
|
||||||
return (
|
return (
|
||||||
|
@ -114,6 +119,8 @@ class Followers extends ImmutablePureComponent {
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='followers'
|
scrollKey='followers'
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
|
hasMoreFollows={hasMoreFollows}
|
||||||
|
isFollowing={isFollowing}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
emptyMessage={<FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />}
|
emptyMessage={<FormattedMessage id='account.followers.empty' defaultMessage='No one follows this user yet.' />}
|
||||||
>
|
>
|
||||||
|
|
|
@ -16,6 +16,7 @@ import AccountContainer from '../../containers/account_container';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../ui/components/column';
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
import MissingIndicator from 'gabsocial/components/missing_indicator';
|
import MissingIndicator from 'gabsocial/components/missing_indicator';
|
||||||
|
import { getHasMoreFollowsCount } from 'gabsocial/utils/accounts';
|
||||||
|
|
||||||
const mapStateToProps = (state, { params: { username }, withReplies = false }) => {
|
const mapStateToProps = (state, { params: { username }, withReplies = false }) => {
|
||||||
const me = state.get('me');
|
const me = state.get('me');
|
||||||
|
@ -30,6 +31,7 @@ const mapStateToProps = (state, { params: { username }, withReplies = false }) =
|
||||||
accountId = account ? account.getIn(['id'], null) : -1;
|
accountId = account ? account.getIn(['id'], null) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasMoreFollows = getHasMoreFollowsCount(state, accountId, true);
|
||||||
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false);
|
const isBlocked = state.getIn(['relationships', accountId, 'blocked_by'], false);
|
||||||
const isLocked = state.getIn(['accounts', accountId, 'locked'], false);
|
const isLocked = state.getIn(['accounts', accountId, 'locked'], false);
|
||||||
const isFollowing = state.getIn(['relationships', accountId, 'following'], false);
|
const isFollowing = state.getIn(['relationships', accountId, 'following'], false);
|
||||||
|
@ -41,6 +43,7 @@ const mapStateToProps = (state, { params: { username }, withReplies = false }) =
|
||||||
isAccount: !!state.getIn(['accounts', accountId]),
|
isAccount: !!state.getIn(['accounts', accountId]),
|
||||||
accountIds: state.getIn(['user_lists', 'following', accountId, 'items']),
|
accountIds: state.getIn(['user_lists', 'following', accountId, 'items']),
|
||||||
hasMore: !!state.getIn(['user_lists', 'following', accountId, 'next']),
|
hasMore: !!state.getIn(['user_lists', 'following', accountId, 'next']),
|
||||||
|
hasMoreFollows,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,6 +57,7 @@ class Following extends ImmutablePureComponent {
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isAccount: PropTypes.bool,
|
isAccount: PropTypes.bool,
|
||||||
unavailable: PropTypes.bool,
|
unavailable: PropTypes.bool,
|
||||||
|
hasMoreFollows: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
|
@ -81,7 +85,8 @@ class Following extends ImmutablePureComponent {
|
||||||
}, 300, { leading: true });
|
}, 300, { leading: true });
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { accountIds, hasMore, isAccount, accountId, unavailable } = this.props;
|
const { accountIds, hasMore, isAccount, hasMoreFollows, accountId, unavailable } = this.props;
|
||||||
|
const isFollowing = true;
|
||||||
|
|
||||||
if (!isAccount && accountId !== -1) {
|
if (!isAccount && accountId !== -1) {
|
||||||
return (
|
return (
|
||||||
|
@ -114,6 +119,8 @@ class Following extends ImmutablePureComponent {
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
scrollKey='following'
|
scrollKey='following'
|
||||||
hasMore={hasMore}
|
hasMore={hasMore}
|
||||||
|
hasMoreFollows={hasMoreFollows}
|
||||||
|
isFollowing={isFollowing}
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
emptyMessage={<FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />}
|
emptyMessage={<FormattedMessage id='account.follows.empty' defaultMessage="This user doesn't follow anyone yet." />}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Map as ImmutableMap } from 'immutable';
|
import { Map as ImmutableMap } from 'immutable';
|
||||||
|
import { List as ImmutableList } from 'immutable';
|
||||||
|
|
||||||
export const getDomain = account => {
|
export const getDomain = account => {
|
||||||
let re = /https?:\/\/(.*?)\//i;
|
let re = /https?:\/\/(.*?)\//i;
|
||||||
|
@ -28,3 +29,18 @@ export const isAdmin = account => (
|
||||||
export const isModerator = account => (
|
export const isModerator = account => (
|
||||||
account.getIn(['pleroma', 'is_moderator']) === true
|
account.getIn(['pleroma', 'is_moderator']) === true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getHasMoreFollowsCount = (state, accountId, isFollowing) => {
|
||||||
|
let moreFollowsCount = undefined; //variable text in defaultMessage with null value preventing rendering
|
||||||
|
let emptyList = ImmutableList();
|
||||||
|
if (isFollowing) {
|
||||||
|
let followingList = ImmutableList();
|
||||||
|
followingList = state.getIn(['user_lists', 'following', accountId, 'items'], emptyList);
|
||||||
|
moreFollowsCount = parseInt(state.getIn(['accounts_counters', accountId, 'following_count']), 0) - followingList.size;
|
||||||
|
} else {
|
||||||
|
let followersList = ImmutableList();
|
||||||
|
followersList = state.getIn(['user_lists', 'followers', accountId, 'items'], emptyList);
|
||||||
|
moreFollowsCount = parseInt(state.getIn(['accounts_counters', accountId, 'followers_count']), 0) - followersList.size;
|
||||||
|
}
|
||||||
|
return moreFollowsCount;
|
||||||
|
};
|
||||||
|
|
|
@ -2339,6 +2339,34 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.morefollows-indicator {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $dark-text-color;
|
||||||
|
background: $ui-base-color;
|
||||||
|
cursor: default;
|
||||||
|
display: flex;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
strong {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: $dark-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.columns-area--mobile .column {@include gab-container-standards();}
|
.columns-area--mobile .column {@include gab-container-standards();}
|
||||||
.column-header__wrapper {
|
.column-header__wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
Loading…
Reference in New Issue