Merge branch 'message_on_followers_diff' into 'master'

Display label at end of follows/followers lists when follows/followers count > follows/followers list.size Fixes #2

Closes #2

See merge request soapbox-pub/soapbox-fe!19
stable/1.0.x
Alex Gleason 2020-05-28 00:16:42 +00:00
commit 387c26939a
6 zmienionych plików z 160 dodań i 5 usunięć

Wyświetl plik

@ -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);
}
}
}

Wyświetl plik

@ -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>

Wyświetl plik

@ -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.' />}
> >

Wyświetl plik

@ -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." />}
> >

Wyświetl plik

@ -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;
};

Wyświetl plik

@ -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;