Merge branch 'feature/updating_page_scroll' of https://code.gab.com/gab/social/gab-social into develop
This commit is contained in:
commit
4ee4d7c8e8
@ -154,8 +154,6 @@ export function submitCompose(routerHistory) {
|
|||||||
}).then(function (response) {
|
}).then(function (response) {
|
||||||
if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0 && routerHistory) {
|
if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0 && routerHistory) {
|
||||||
routerHistory.push('/messages');
|
routerHistory.push('/messages');
|
||||||
} else if (routerHistory && routerHistory.location.pathname === '/posts/new' && window.history.state) {
|
|
||||||
routerHistory.goBack();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(insertIntoTagHistory(response.data.tags, status));
|
dispatch(insertIntoTagHistory(response.data.tags, status));
|
||||||
|
@ -7,6 +7,7 @@ export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
|||||||
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
|
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
|
||||||
export const TIMELINE_UPDATE_QUEUE = 'TIMELINE_UPDATE_QUEUE';
|
export const TIMELINE_UPDATE_QUEUE = 'TIMELINE_UPDATE_QUEUE';
|
||||||
export const TIMELINE_DEQUEUE = 'TIMELINE_DEQUEUE';
|
export const TIMELINE_DEQUEUE = 'TIMELINE_DEQUEUE';
|
||||||
|
export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
|
||||||
|
|
||||||
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
|
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
|
||||||
export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
|
export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
|
||||||
@ -210,3 +211,11 @@ export function disconnectTimeline(timeline) {
|
|||||||
timeline,
|
timeline,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function scrollTopTimeline(timeline, top) {
|
||||||
|
return {
|
||||||
|
type: TIMELINE_SCROLL_TOP,
|
||||||
|
timeline,
|
||||||
|
top,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -26,6 +26,8 @@ export default class ScrollableList extends PureComponent {
|
|||||||
alwaysPrepend: PropTypes.bool,
|
alwaysPrepend: PropTypes.bool,
|
||||||
emptyMessage: PropTypes.node,
|
emptyMessage: PropTypes.node,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
|
onScrollToTop: PropTypes.func,
|
||||||
|
onScroll: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -40,9 +42,9 @@ export default class ScrollableList extends PureComponent {
|
|||||||
scrollToTopOnMouseIdle = false;
|
scrollToTopOnMouseIdle = false;
|
||||||
|
|
||||||
setScrollTop = newScrollTop => {
|
setScrollTop = newScrollTop => {
|
||||||
if (this.node.scrollTop !== newScrollTop) {
|
if (this.documentElement.scrollTop !== newScrollTop) {
|
||||||
this.lastScrollWasSynthetic = true;
|
this.lastScrollWasSynthetic = true;
|
||||||
this.node.scrollTop = newScrollTop;
|
this.documentElement.scrollTop = newScrollTop;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -60,7 +62,7 @@ export default class ScrollableList extends PureComponent {
|
|||||||
this.clearMouseIdleTimer();
|
this.clearMouseIdleTimer();
|
||||||
this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY);
|
this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY);
|
||||||
|
|
||||||
if (!this.mouseMovedRecently && this.node.scrollTop === 0) {
|
if (!this.mouseMovedRecently && this.documentElement.scrollTop === 0) {
|
||||||
// Only set if we just started moving and are scrolled to the top.
|
// Only set if we just started moving and are scrolled to the top.
|
||||||
this.scrollToTopOnMouseIdle = true;
|
this.scrollToTopOnMouseIdle = true;
|
||||||
}
|
}
|
||||||
@ -79,19 +81,25 @@ export default class ScrollableList extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
|
this.window = window;
|
||||||
|
this.documentElement = document.documentElement;
|
||||||
|
|
||||||
|
this.attachScrollListener();
|
||||||
this.attachIntersectionObserver();
|
this.attachIntersectionObserver();
|
||||||
|
// Handle initial scroll posiiton
|
||||||
|
this.handleScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
getScrollPosition = () => {
|
getScrollPosition = () => {
|
||||||
if (this.node && (this.node.scrollTop > 0 || this.mouseMovedRecently)) {
|
if (this.documentElement && (this.documentElement.scrollTop > 0 || this.mouseMovedRecently)) {
|
||||||
return { height: this.node.scrollHeight, top: this.node.scrollTop };
|
return { height: this.documentElement.scrollHeight, top: this.documentElement.scrollTop };
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateScrollBottom = (snapshot) => {
|
updateScrollBottom = (snapshot) => {
|
||||||
const newScrollTop = this.node.scrollHeight - snapshot;
|
const newScrollTop = this.documentElement.scrollHeight - snapshot;
|
||||||
|
|
||||||
this.setScrollTop(newScrollTop);
|
this.setScrollTop(newScrollTop);
|
||||||
}
|
}
|
||||||
@ -100,7 +108,61 @@ export default class ScrollableList extends PureComponent {
|
|||||||
// Reset the scroll position when a new child comes in in order not to
|
// Reset the scroll position when a new child comes in in order not to
|
||||||
// jerk the scrollbar around if you're already scrolled down the page.
|
// jerk the scrollbar around if you're already scrolled down the page.
|
||||||
if (snapshot !== null) {
|
if (snapshot !== null) {
|
||||||
this.setScrollTop(this.node.scrollHeight - snapshot);
|
this.setScrollTop(this.documentElement.scrollHeight - snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attachScrollListener () {
|
||||||
|
this.window.addEventListener('scroll', this.handleScroll);
|
||||||
|
this.window.addEventListener('wheel', this.handleWheel);
|
||||||
|
}
|
||||||
|
|
||||||
|
detachScrollListener () {
|
||||||
|
this.window.removeEventListener('scroll', this.handleScroll);
|
||||||
|
this.window.removeEventListener('wheel', this.handleWheel);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleScroll = throttle(() => {
|
||||||
|
if (this.window) {
|
||||||
|
const { scrollTop, scrollHeight, clientHeight } = this.documentElement;
|
||||||
|
const offset = scrollHeight - scrollTop - clientHeight;
|
||||||
|
|
||||||
|
if (600 > offset && this.props.onLoadMore && this.props.hasMore && !this.props.isLoading) {
|
||||||
|
this.props.onLoadMore();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scrollTop < 100 && this.props.onScrollToTop) {
|
||||||
|
this.props.onScrollToTop();
|
||||||
|
} else if (this.props.onScroll) {
|
||||||
|
this.props.onScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.lastScrollWasSynthetic) {
|
||||||
|
// If the last scroll wasn't caused by setScrollTop(), assume it was
|
||||||
|
// intentional and cancel any pending scroll reset on mouse idle
|
||||||
|
this.scrollToTopOnMouseIdle = false;
|
||||||
|
}
|
||||||
|
this.lastScrollWasSynthetic = false;
|
||||||
|
}
|
||||||
|
}, 150, {
|
||||||
|
trailing: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
handleWheel = throttle(() => {
|
||||||
|
this.scrollToTopOnMouseIdle = false;
|
||||||
|
}, 150, {
|
||||||
|
trailing: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
getSnapshotBeforeUpdate (prevProps) {
|
||||||
|
const someItemInserted = React.Children.count(prevProps.children) > 0 &&
|
||||||
|
React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
|
||||||
|
this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props);
|
||||||
|
|
||||||
|
if (someItemInserted && (this.documentElement.scrollTop > 0 || this.mouseMovedRecently)) {
|
||||||
|
return this.documentElement.scrollHeight - this.documentElement.scrollTop;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,10 +178,7 @@ export default class ScrollableList extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
attachIntersectionObserver () {
|
attachIntersectionObserver () {
|
||||||
this.intersectionObserverWrapper.connect({
|
this.intersectionObserverWrapper.connect();
|
||||||
root: this.node,
|
|
||||||
rootMargin: '300% 0px',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
detachIntersectionObserver () {
|
detachIntersectionObserver () {
|
||||||
@ -139,10 +198,6 @@ export default class ScrollableList extends PureComponent {
|
|||||||
return firstChild && firstChild.key;
|
return firstChild && firstChild.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
setRef = (c) => {
|
|
||||||
this.node = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleLoadMore = e => {
|
handleLoadMore = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onLoadMore();
|
this.props.onLoadMore();
|
||||||
@ -159,7 +214,7 @@ export default class ScrollableList extends PureComponent {
|
|||||||
|
|
||||||
if (showLoading) {
|
if (showLoading) {
|
||||||
scrollableArea = (
|
scrollableArea = (
|
||||||
<div className='slist slist--flex' ref={this.setRef}>
|
<div className='slist slist--flex'>
|
||||||
<div role='feed' className='item-list'>
|
<div role='feed' className='item-list'>
|
||||||
{prepend}
|
{prepend}
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,6 +25,8 @@ export default class StatusList extends ImmutablePureComponent {
|
|||||||
timelineId: PropTypes.string,
|
timelineId: PropTypes.string,
|
||||||
queuedItemSize: PropTypes.number,
|
queuedItemSize: PropTypes.number,
|
||||||
onDequeueTimeline: PropTypes.func,
|
onDequeueTimeline: PropTypes.func,
|
||||||
|
onScrollToTop: PropTypes.func,
|
||||||
|
onScroll: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -133,7 +135,7 @@ export default class StatusList extends ImmutablePureComponent {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
<TimelineQueueButtonHeader key='timeline-queue-button-header' onClick={this.handleDequeueTimeline} count={totalQueuedItemsCount} itemType='gab' />,
|
<TimelineQueueButtonHeader key='timeline-queue-button-header' onClick={this.handleDequeueTimeline} count={totalQueuedItemsCount} itemType='gab' />,
|
||||||
<ScrollableList key='scrollable-list' {...other} showLoading={isLoading && statusIds.size === 0} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
|
<ScrollableList key='scrollable-list' {...other} isLoading={isLoading} showLoading={isLoading && statusIds.size === 0} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
|
||||||
{scrollableContent}
|
{scrollableContent}
|
||||||
</ScrollableList>
|
</ScrollableList>
|
||||||
];
|
];
|
||||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { shortNumberFormat } from '../utils/numbers';
|
import { shortNumberFormat } from '../utils/numbers';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
export default class TimelineQueueButtonHeader extends React.PureComponent {
|
export default class TimelineQueueButtonHeader extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -18,19 +19,21 @@ export default class TimelineQueueButtonHeader extends React.PureComponent {
|
|||||||
render () {
|
render () {
|
||||||
const { count, itemType, onClick } = this.props;
|
const { count, itemType, onClick } = this.props;
|
||||||
|
|
||||||
if (count <= 0) return null;
|
const classes = classNames('timeline-queue-header', {
|
||||||
|
'hidden': (count <= 0)
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='timeline-queue-header'>
|
<div className={classes}>
|
||||||
<a className='timeline-queue-header__btn' onClick={onClick}>
|
<a className='timeline-queue-header__btn' onClick={onClick}>
|
||||||
<FormattedMessage
|
{(count > 0) && <FormattedMessage
|
||||||
id='timeline_queue.label'
|
id='timeline_queue.label'
|
||||||
defaultMessage='Click to see {count} new {type}'
|
defaultMessage='Click to see {count} new {type}'
|
||||||
values={{
|
values={{
|
||||||
count: shortNumberFormat(count),
|
count: shortNumberFormat(count),
|
||||||
type: count == 1 ? itemType : `${itemType}s`,
|
type: count == 1 ? itemType : `${itemType}s`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -12,7 +12,6 @@ import Column from '../ui/components/column';
|
|||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { getAccountGallery } from 'gabsocial/selectors';
|
import { getAccountGallery } from 'gabsocial/selectors';
|
||||||
import MediaItem from './components/media_item';
|
import MediaItem from './components/media_item';
|
||||||
import { ScrollContainer } from 'react-router-scroll-4';
|
|
||||||
import LoadMore from 'gabsocial/components/load_more';
|
import LoadMore from 'gabsocial/components/load_more';
|
||||||
import MissingIndicator from 'gabsocial/components/missing_indicator';
|
import MissingIndicator from 'gabsocial/components/missing_indicator';
|
||||||
import { openModal } from 'gabsocial/actions/modal';
|
import { openModal } from 'gabsocial/actions/modal';
|
||||||
@ -189,46 +188,44 @@ class AccountGallery extends ImmutablePureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Column>
|
<Column>
|
||||||
<ScrollContainer scrollKey='account_gallery'>
|
<div className='slist slist--flex' onScroll={this.handleScroll}>
|
||||||
<div className='slist slist--flex' onScroll={this.handleScroll}>
|
<div className='account__section-headline'>
|
||||||
<div className='account__section-headline'>
|
<div style={{width: '100%', display: 'flex'}}>
|
||||||
<div style={{width: '100%', display: 'flex'}}>
|
<NavLink exact to={`/${accountUsername}`}>
|
||||||
<NavLink exact to={`/${accountUsername}`}>
|
<FormattedMessage id='account.posts' defaultMessage='Gabs' />
|
||||||
<FormattedMessage id='account.posts' defaultMessage='Gabs' />
|
</NavLink>
|
||||||
</NavLink>
|
<NavLink exact to={`/${accountUsername}/with_replies`}>
|
||||||
<NavLink exact to={`/${accountUsername}/with_replies`}>
|
<FormattedMessage id='account.posts_with_replies' defaultMessage='Gabs and replies' />
|
||||||
<FormattedMessage id='account.posts_with_replies' defaultMessage='Gabs and replies' />
|
</NavLink>
|
||||||
</NavLink>
|
<NavLink exact to={`/${accountUsername}/media`}>
|
||||||
<NavLink exact to={`/${accountUsername}/media`}>
|
<FormattedMessage id='account.media' defaultMessage='Media' />
|
||||||
<FormattedMessage id='account.media' defaultMessage='Media' />
|
</NavLink>
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div role='feed' className='account-gallery__container' ref={this.handleRef}>
|
|
||||||
{attachments.map((attachment, index) => attachment === null ? (
|
|
||||||
<LoadMoreMedia key={'more:' + attachments.getIn(index + 1, 'id')} maxId={index > 0 ? attachments.getIn(index - 1, 'id') : null} onLoadMore={this.handleLoadMore} />
|
|
||||||
) : (
|
|
||||||
<MediaItem key={attachment.get('id')} attachment={attachment} displayWidth={width} onOpenMedia={this.handleOpenMedia} />
|
|
||||||
))}
|
|
||||||
|
|
||||||
{
|
|
||||||
attachments.size == 0 &&
|
|
||||||
<div className='empty-column-indicator'>
|
|
||||||
<FormattedMessage id='account_gallery.none' defaultMessage='No media to show.'/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
{loadOlder}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isLoading && attachments.size === 0 && (
|
|
||||||
<div className='slist__append'>
|
|
||||||
<LoadingIndicator />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</ScrollContainer>
|
|
||||||
|
<div role='feed' className='account-gallery__container' ref={this.handleRef}>
|
||||||
|
{attachments.map((attachment, index) => attachment === null ? (
|
||||||
|
<LoadMoreMedia key={'more:' + attachments.getIn(index + 1, 'id')} maxId={index > 0 ? attachments.getIn(index - 1, 'id') : null} onLoadMore={this.handleLoadMore} />
|
||||||
|
) : (
|
||||||
|
<MediaItem key={attachment.get('id')} attachment={attachment} displayWidth={width} onOpenMedia={this.handleOpenMedia} />
|
||||||
|
))}
|
||||||
|
|
||||||
|
{
|
||||||
|
attachments.size == 0 &&
|
||||||
|
<div className='empty-column-indicator'>
|
||||||
|
<FormattedMessage id='account_gallery.none' defaultMessage='No media to show.'/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{loadOlder}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isLoading && attachments.size === 0 && (
|
||||||
|
<div className='slist__append'>
|
||||||
|
<LoadingIndicator />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -174,14 +174,6 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
|
|
||||||
this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd);
|
this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd);
|
||||||
this.autosuggestTextarea.textarea.focus();
|
this.autosuggestTextarea.textarea.focus();
|
||||||
} else if(prevProps.isSubmitting && !this.props.isSubmitting) {
|
|
||||||
this.autosuggestTextarea.textarea.focus();
|
|
||||||
} else if (this.props.spoiler !== prevProps.spoiler) {
|
|
||||||
if (this.props.spoiler) {
|
|
||||||
this.spoilerText.input.focus();
|
|
||||||
} else {
|
|
||||||
this.autosuggestTextarea.textarea.focus();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,6 @@ import {
|
|||||||
import { initMuteModal } from '../../actions/mutes';
|
import { initMuteModal } from '../../actions/mutes';
|
||||||
import { initReport } from '../../actions/reports';
|
import { initReport } from '../../actions/reports';
|
||||||
import { makeGetStatus } from '../../selectors';
|
import { makeGetStatus } from '../../selectors';
|
||||||
import { ScrollContainer } from 'react-router-scroll-4';
|
|
||||||
import ColumnHeader from '../../components/column_header';
|
import ColumnHeader from '../../components/column_header';
|
||||||
import StatusContainer from '../../containers/status_container';
|
import StatusContainer from '../../containers/status_container';
|
||||||
import { openModal } from '../../actions/modal';
|
import { openModal } from '../../actions/modal';
|
||||||
@ -471,43 +470,41 @@ class Status extends ImmutablePureComponent {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
<ScrollContainer scrollKey='thread'>
|
<div ref={this.setRef}>
|
||||||
<div ref={this.setRef}>
|
{ancestors}
|
||||||
{ancestors}
|
|
||||||
|
|
||||||
<HotKeys handlers={handlers}>
|
<HotKeys handlers={handlers}>
|
||||||
<div className={classNames('focusable', 'detailed-status__wrapper')} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
|
<div className={classNames('focusable', 'detailed-status__wrapper')} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
|
||||||
<DetailedStatus
|
<DetailedStatus
|
||||||
status={status}
|
status={status}
|
||||||
onOpenVideo={this.handleOpenVideo}
|
onOpenVideo={this.handleOpenVideo}
|
||||||
onOpenMedia={this.handleOpenMedia}
|
onOpenMedia={this.handleOpenMedia}
|
||||||
onToggleHidden={this.handleToggleHidden}
|
onToggleHidden={this.handleToggleHidden}
|
||||||
domain={domain}
|
domain={domain}
|
||||||
showMedia={this.state.showMedia}
|
showMedia={this.state.showMedia}
|
||||||
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ActionBar
|
<ActionBar
|
||||||
status={status}
|
status={status}
|
||||||
onReply={this.handleReplyClick}
|
onReply={this.handleReplyClick}
|
||||||
onFavourite={this.handleFavouriteClick}
|
onFavourite={this.handleFavouriteClick}
|
||||||
onReblog={this.handleReblogClick}
|
onReblog={this.handleReblogClick}
|
||||||
onDelete={this.handleDeleteClick}
|
onDelete={this.handleDeleteClick}
|
||||||
onDirect={this.handleDirectClick}
|
onDirect={this.handleDirectClick}
|
||||||
onMention={this.handleMentionClick}
|
onMention={this.handleMentionClick}
|
||||||
onMute={this.handleMuteClick}
|
onMute={this.handleMuteClick}
|
||||||
onMuteConversation={this.handleConversationMuteClick}
|
onMuteConversation={this.handleConversationMuteClick}
|
||||||
onBlock={this.handleBlockClick}
|
onBlock={this.handleBlockClick}
|
||||||
onReport={this.handleReport}
|
onReport={this.handleReport}
|
||||||
onPin={this.handlePin}
|
onPin={this.handlePin}
|
||||||
onEmbed={this.handleEmbed}
|
onEmbed={this.handleEmbed}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</HotKeys>
|
</HotKeys>
|
||||||
|
|
||||||
{descendants}
|
{descendants}
|
||||||
</div>
|
</div>
|
||||||
</ScrollContainer>
|
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||||||
import ReactSwipeableViews from 'react-swipeable-views';
|
import ReactSwipeableViews from 'react-swipeable-views';
|
||||||
import { links, getIndex, getLink } from './tabs_bar';
|
import { links, getIndex, getLink } from './tabs_bar';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { me } from 'gabsocial/initial_state';
|
||||||
|
|
||||||
import BundleContainer from '../containers/bundle_container';
|
import BundleContainer from '../containers/bundle_container';
|
||||||
import ColumnLoading from './column_loading';
|
import ColumnLoading from './column_loading';
|
||||||
@ -64,8 +65,7 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||||||
{layout.RIGHT}
|
{layout.RIGHT}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{me && floatingActionButton}
|
||||||
{floatingActionButton}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { createSelector } from 'reselect';
|
|||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import { me } from '../../../initial_state';
|
import { me } from '../../../initial_state';
|
||||||
import { dequeueTimeline } from 'gabsocial/actions/timelines';
|
import { dequeueTimeline } from 'gabsocial/actions/timelines';
|
||||||
|
import { scrollTopTimeline } from '../../../actions/timelines';
|
||||||
|
|
||||||
const makeGetStatusIds = () => createSelector([
|
const makeGetStatusIds = () => createSelector([
|
||||||
(state, { type }) => state.getIn(['settings', type], ImmutableMap()),
|
(state, { type }) => state.getIn(['settings', type], ImmutableMap()),
|
||||||
@ -45,6 +46,12 @@ const mapDispatchToProps = (dispatch, ownProps) => ({
|
|||||||
onDequeueTimeline(timelineId) {
|
onDequeueTimeline(timelineId) {
|
||||||
dispatch(dequeueTimeline(timelineId, ownProps.onLoadMore));
|
dispatch(dequeueTimeline(timelineId, ownProps.onLoadMore));
|
||||||
},
|
},
|
||||||
|
onScrollToTop: debounce(() => {
|
||||||
|
dispatch(scrollTopTimeline(ownProps.timelineId, true));
|
||||||
|
}, 100),
|
||||||
|
onScroll: debounce(() => {
|
||||||
|
dispatch(scrollTopTimeline(ownProps.timelineId, false));
|
||||||
|
}, 100),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(StatusList);
|
export default connect(mapStateToProps, mapDispatchToProps)(StatusList);
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
TIMELINE_UPDATE_QUEUE,
|
TIMELINE_UPDATE_QUEUE,
|
||||||
TIMELINE_DEQUEUE,
|
TIMELINE_DEQUEUE,
|
||||||
MAX_QUEUED_ITEMS,
|
MAX_QUEUED_ITEMS,
|
||||||
|
TIMELINE_SCROLL_TOP,
|
||||||
} from '../actions/timelines';
|
} from '../actions/timelines';
|
||||||
import {
|
import {
|
||||||
ACCOUNT_BLOCK_SUCCESS,
|
ACCOUNT_BLOCK_SUCCESS,
|
||||||
@ -137,6 +138,13 @@ const filterTimelines = (state, relationship, statuses) => {
|
|||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateTop = (state, timeline, top) => {
|
||||||
|
return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
|
||||||
|
if (top) mMap.set('unread', 0);
|
||||||
|
mMap.set('top', top);
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
const filterTimeline = (timeline, state, relationship, statuses) =>
|
const filterTimeline = (timeline, state, relationship, statuses) =>
|
||||||
state.updateIn([timeline, 'items'], ImmutableList(), list =>
|
state.updateIn([timeline, 'items'], ImmutableList(), list =>
|
||||||
list.filterNot(statusId =>
|
list.filterNot(statusId =>
|
||||||
@ -171,6 +179,8 @@ export default function timelines(state = initialState, action) {
|
|||||||
return filterTimeline('home', state, action.relationship, action.statuses);
|
return filterTimeline('home', state, action.relationship, action.statuses);
|
||||||
case TIMELINE_CONNECT:
|
case TIMELINE_CONNECT:
|
||||||
return state.update(action.timeline, initialTimeline, map => map.set('online', true));
|
return state.update(action.timeline, initialTimeline, map => map.set('online', true));
|
||||||
|
case TIMELINE_SCROLL_TOP:
|
||||||
|
return updateTop(state, action.timeline, action.top);
|
||||||
case TIMELINE_DISCONNECT:
|
case TIMELINE_DISCONNECT:
|
||||||
return state.update(
|
return state.update(
|
||||||
action.timeline,
|
action.timeline,
|
||||||
|
@ -48,7 +48,6 @@ body {
|
|||||||
&.app-body {
|
&.app-body {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
@ -5108,21 +5108,31 @@ noscript {
|
|||||||
.timeline-queue-header {
|
.timeline-queue-header {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 52px;
|
max-height: 46px;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: darken($ui-base-color, 8%);
|
background-color: darken($ui-base-color, 8%);
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
border-top: 1px solid;
|
border-top: 1px solid;
|
||||||
border-color: darken($ui-base-color, 4%);
|
border-color: darken($ui-base-color, 4%);
|
||||||
|
transition: max-height 2.5s ease;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
max-height: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
&__btn {
|
&__btn {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 52px;
|
line-height: 46px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: $secondary-text-color;
|
color: $secondary-text-color;
|
||||||
|
|
||||||
|
span {
|
||||||
|
height: 46px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user