forked from cybrespace/mastodon
Fix load more feature on the Account media gallery (#3293)
* Add load more button for large screens * Fix `next` state value on the first loading * Don't load if `isLoading || !hasMore` * Start load on near the bottom
This commit is contained in:
parent
3b59f9c6c2
commit
807c192fcf
|
@ -17,6 +17,7 @@ import MediaItem from './components/media_item';
|
||||||
import HeaderContainer from '../account_timeline/containers/header_container';
|
import HeaderContainer from '../account_timeline/containers/header_container';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { ScrollContainer } from 'react-router-scroll';
|
import { ScrollContainer } from 'react-router-scroll';
|
||||||
|
import LoadMore from '../../components/load_more';
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => ({
|
||||||
medias: getAccountGallery(state, Number(props.params.accountId)),
|
medias: getAccountGallery(state, Number(props.params.accountId)),
|
||||||
|
@ -48,16 +49,30 @@ class AccountGallery extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleScroll = (e) => {
|
handleScrollToBottom = () => {
|
||||||
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
if (this.props.hasMore) {
|
||||||
|
|
||||||
if (scrollTop === scrollHeight - clientHeight) {
|
|
||||||
this.props.dispatch(expandAccountMediaTimeline(Number(this.props.params.accountId)));
|
this.props.dispatch(expandAccountMediaTimeline(Number(this.props.params.accountId)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleScroll = (e) => {
|
||||||
|
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
||||||
|
const offset = scrollHeight - scrollTop - clientHeight;
|
||||||
|
|
||||||
|
if (150 > offset && !this.props.isLoading) {
|
||||||
|
this.handleScrollToBottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLoadMore = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
this.handleScrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { medias, autoPlayGif, isLoading } = this.props;
|
const { medias, autoPlayGif, isLoading, hasMore } = this.props;
|
||||||
|
|
||||||
|
let loadMore = null;
|
||||||
|
|
||||||
if (!medias && isLoading) {
|
if (!medias && isLoading) {
|
||||||
return (
|
return (
|
||||||
|
@ -67,6 +82,10 @@ class AccountGallery extends ImmutablePureComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isLoading && medias.size > 0 && hasMore) {
|
||||||
|
loadMore = <LoadMore onClick={this.handleLoadMore} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column>
|
||||||
<ColumnBackButton />
|
<ColumnBackButton />
|
||||||
|
@ -87,6 +106,7 @@ class AccountGallery extends ImmutablePureComponent {
|
||||||
autoPlayGif={autoPlayGif}
|
autoPlayGif={autoPlayGif}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{loadMore}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
|
|
|
@ -155,7 +155,7 @@ const normalizeAccountTimeline = (state, accountId, statuses, replace = false) =
|
||||||
.update('items', Immutable.List(), list => (replace ? ids : ids.concat(list))));
|
.update('items', Immutable.List(), list => (replace ? ids : ids.concat(list))));
|
||||||
};
|
};
|
||||||
|
|
||||||
const normalizeAccountMediaTimeline = (state, accountId, statuses, next) => {
|
const normalizeAccountMediaTimeline = (state, accountId, statuses) => {
|
||||||
let ids = Immutable.List();
|
let ids = Immutable.List();
|
||||||
|
|
||||||
statuses.forEach((status, i) => {
|
statuses.forEach((status, i) => {
|
||||||
|
@ -165,7 +165,7 @@ const normalizeAccountMediaTimeline = (state, accountId, statuses, next) => {
|
||||||
|
|
||||||
return state.updateIn(['accounts_media_timelines', accountId], Immutable.Map(), map => map
|
return state.updateIn(['accounts_media_timelines', accountId], Immutable.Map(), map => map
|
||||||
.set('isLoading', false)
|
.set('isLoading', false)
|
||||||
.set('next', next)
|
.set('next', true)
|
||||||
.update('items', Immutable.List(), list => ids.concat(list)));
|
.update('items', Immutable.List(), list => ids.concat(list)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ export default function timelines(state = initialState, action) {
|
||||||
case ACCOUNT_MEDIA_TIMELINE_EXPAND_FAIL:
|
case ACCOUNT_MEDIA_TIMELINE_EXPAND_FAIL:
|
||||||
return state.updateIn(['accounts_media_timelines', action.id], Immutable.Map(), map => map.set('isLoading', false));
|
return state.updateIn(['accounts_media_timelines', action.id], Immutable.Map(), map => map.set('isLoading', false));
|
||||||
case ACCOUNT_MEDIA_TIMELINE_FETCH_SUCCESS:
|
case ACCOUNT_MEDIA_TIMELINE_FETCH_SUCCESS:
|
||||||
return normalizeAccountMediaTimeline(state, action.id, Immutable.fromJS(action.statuses), action.next);
|
return normalizeAccountMediaTimeline(state, action.id, Immutable.fromJS(action.statuses));
|
||||||
case ACCOUNT_MEDIA_TIMELINE_EXPAND_SUCCESS:
|
case ACCOUNT_MEDIA_TIMELINE_EXPAND_SUCCESS:
|
||||||
return appendNormalizedAccountMediaTimeline(state, action.id, Immutable.fromJS(action.statuses), action.next);
|
return appendNormalizedAccountMediaTimeline(state, action.id, Immutable.fromJS(action.statuses), action.next);
|
||||||
case ACCOUNT_BLOCK_SUCCESS:
|
case ACCOUNT_BLOCK_SUCCESS:
|
||||||
|
|
|
@ -1962,6 +1962,7 @@ button.icon-button.active i.fa-retweet {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
clear: both;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: lighten($ui-base-color, 2%);
|
background: lighten($ui-base-color, 2%);
|
||||||
|
|
Loading…
Reference in New Issue