| @@ -35,6 +35,7 @@ export default class ScrollableList extends PureComponent { | ||||
|     alwaysPrepend: PropTypes.bool, | ||||
|     emptyMessage: PropTypes.node, | ||||
|     children: PropTypes.node, | ||||
|     bindToDocument: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   static defaultProps = { | ||||
| @@ -50,7 +51,9 @@ export default class ScrollableList extends PureComponent { | ||||
|  | ||||
|   handleScroll = throttle(() => { | ||||
|     if (this.node) { | ||||
|       const { scrollTop, scrollHeight, clientHeight } = this.node; | ||||
|       const scrollTop = this.getScrollTop(); | ||||
|       const scrollHeight = this.getScrollHeight(); | ||||
|       const clientHeight = this.getClientHeight(); | ||||
|       const offset = scrollHeight - scrollTop - clientHeight; | ||||
|  | ||||
|       if (400 > offset && this.props.onLoadMore && this.props.hasMore && !this.props.isLoading) { | ||||
| @@ -80,9 +83,14 @@ export default class ScrollableList extends PureComponent { | ||||
|   scrollToTopOnMouseIdle = false; | ||||
|  | ||||
|   setScrollTop = newScrollTop => { | ||||
|     if (this.node.scrollTop !== newScrollTop) { | ||||
|     if (this.getScrollTop() !== newScrollTop) { | ||||
|       this.lastScrollWasSynthetic = true; | ||||
|       this.node.scrollTop = newScrollTop; | ||||
|  | ||||
|       if (this.props.bindToDocument) { | ||||
|         document.scrollingElement.scrollTop = newScrollTop; | ||||
|       } else { | ||||
|         this.node.scrollTop = newScrollTop; | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
|  | ||||
| @@ -100,7 +108,7 @@ export default class ScrollableList extends PureComponent { | ||||
|     this.clearMouseIdleTimer(); | ||||
|     this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY); | ||||
|  | ||||
|     if (!this.mouseMovedRecently && this.node.scrollTop === 0) { | ||||
|     if (!this.mouseMovedRecently && this.getScrollTop() === 0) { | ||||
|       // Only set if we just started moving and are scrolled to the top. | ||||
|       this.scrollToTopOnMouseIdle = true; | ||||
|     } | ||||
| @@ -135,15 +143,27 @@ export default class ScrollableList extends PureComponent { | ||||
|   } | ||||
|  | ||||
|   getScrollPosition = () => { | ||||
|     if (this.node && (this.node.scrollTop > 0 || this.mouseMovedRecently)) { | ||||
|       return { height: this.node.scrollHeight, top: this.node.scrollTop }; | ||||
|     if (this.node && (this.getScrollTop() > 0 || this.mouseMovedRecently)) { | ||||
|       return { height: this.getScrollHeight(), top: this.getScrollTop() }; | ||||
|     } else { | ||||
|       return null; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   getScrollTop = () => { | ||||
|     return this.props.bindToDocument ? document.scrollingElement.scrollTop : this.node.scrollTop; | ||||
|   } | ||||
|  | ||||
|   getScrollHeight = () => { | ||||
|     return this.props.bindToDocument ? document.scrollingElement.scrollHeight : this.node.scrollHeight; | ||||
|   } | ||||
|  | ||||
|   getClientHeight = () => { | ||||
|     return this.props.bindToDocument ? document.scrollingElement.clientHeight : this.node.clientHeight; | ||||
|   } | ||||
|  | ||||
|   updateScrollBottom = (snapshot) => { | ||||
|     const newScrollTop = this.node.scrollHeight - snapshot; | ||||
|     const newScrollTop = this.getScrollHeight() - snapshot; | ||||
|  | ||||
|     this.setScrollTop(newScrollTop); | ||||
|   } | ||||
| @@ -153,8 +173,8 @@ export default class ScrollableList extends PureComponent { | ||||
|       React.Children.count(prevProps.children) < React.Children.count(this.props.children) && | ||||
|       this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props); | ||||
|  | ||||
|     if (someItemInserted && (this.node.scrollTop > 0 || this.mouseMovedRecently)) { | ||||
|       return this.node.scrollHeight - this.node.scrollTop; | ||||
|     if (someItemInserted && (this.getScrollTop() > 0 || this.mouseMovedRecently)) { | ||||
|       return this.getScrollHeight() - this.getScrollTop(); | ||||
|     } else { | ||||
|       return null; | ||||
|     } | ||||
| @@ -164,7 +184,7 @@ export default class ScrollableList extends PureComponent { | ||||
|     // 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. | ||||
|     if (snapshot !== null) { | ||||
|       this.setScrollTop(this.node.scrollHeight - snapshot); | ||||
|       this.setScrollTop(this.getScrollHeight() - snapshot); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -197,13 +217,23 @@ export default class ScrollableList extends PureComponent { | ||||
|   } | ||||
|  | ||||
|   attachScrollListener () { | ||||
|     this.node.addEventListener('scroll', this.handleScroll); | ||||
|     this.node.addEventListener('wheel', this.handleWheel); | ||||
|     if (this.props.bindToDocument) { | ||||
|       document.addEventListener('scroll', this.handleScroll); | ||||
|       document.addEventListener('wheel', this.handleWheel); | ||||
|     } else { | ||||
|       this.node.addEventListener('scroll', this.handleScroll); | ||||
|       this.node.addEventListener('wheel', this.handleWheel); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   detachScrollListener () { | ||||
|     this.node.removeEventListener('scroll', this.handleScroll); | ||||
|     this.node.removeEventListener('wheel', this.handleWheel); | ||||
|     if (this.props.bindToDocument) { | ||||
|       document.removeEventListener('scroll', this.handleScroll); | ||||
|       document.removeEventListener('wheel', this.handleWheel); | ||||
|     } else { | ||||
|       this.node.removeEventListener('scroll', this.handleScroll); | ||||
|       this.node.removeEventListener('wheel', this.handleWheel); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   getFirstChildKey (props) { | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import Video from '../features/video'; | ||||
| import Card from '../features/status/components/card'; | ||||
| import Poll from 'mastodon/components/poll'; | ||||
| import ModalRoot from '../components/modal_root'; | ||||
| import { getScrollbarWidth } from '../features/ui/components/modal_root'; | ||||
| import MediaModal from '../features/ui/components/media_modal'; | ||||
| import { List as ImmutableList, fromJS } from 'immutable'; | ||||
|  | ||||
| @@ -31,6 +32,8 @@ export default class MediaContainer extends PureComponent { | ||||
|  | ||||
|   handleOpenMedia = (media, index) => { | ||||
|     document.body.classList.add('with-modals--active'); | ||||
|     document.documentElement.style.marginRight = `${getScrollbarWidth()}px`; | ||||
|  | ||||
|     this.setState({ media, index }); | ||||
|   } | ||||
|  | ||||
| @@ -38,11 +41,15 @@ export default class MediaContainer extends PureComponent { | ||||
|     const media = ImmutableList([video]); | ||||
|  | ||||
|     document.body.classList.add('with-modals--active'); | ||||
|     document.documentElement.style.marginRight = `${getScrollbarWidth()}px`; | ||||
|  | ||||
|     this.setState({ media, time }); | ||||
|   } | ||||
|  | ||||
|   handleCloseMedia = () => { | ||||
|     document.body.classList.remove('with-modals--active'); | ||||
|     document.documentElement.style.marginRight = 0; | ||||
|  | ||||
|     this.setState({ media: null, index: null, time: null }); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -44,6 +44,7 @@ class AccountTimeline extends ImmutablePureComponent { | ||||
|     withReplies: PropTypes.bool, | ||||
|     blockedBy: PropTypes.bool, | ||||
|     isAccount: PropTypes.bool, | ||||
|     multiColumn: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
| @@ -77,7 +78,7 @@ class AccountTimeline extends ImmutablePureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, isAccount } = this.props; | ||||
|     const { shouldUpdateScroll, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, isAccount, multiColumn } = this.props; | ||||
|  | ||||
|     if (!isAccount) { | ||||
|       return ( | ||||
| @@ -112,6 +113,7 @@ class AccountTimeline extends ImmutablePureComponent { | ||||
|           onLoadMore={this.handleLoadMore} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           emptyMessage={emptyMessage} | ||||
|           bindToDocument={!multiColumn} | ||||
|         /> | ||||
|       </Column> | ||||
|     ); | ||||
|   | ||||
| @@ -32,6 +32,7 @@ class Blocks extends ImmutablePureComponent { | ||||
|     accountIds: ImmutablePropTypes.list, | ||||
|     hasMore: PropTypes.bool, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     multiColumn: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
| @@ -43,7 +44,7 @@ class Blocks extends ImmutablePureComponent { | ||||
|   }, 300, { leading: true }); | ||||
|  | ||||
|   render () { | ||||
|     const { intl, accountIds, shouldUpdateScroll, hasMore } = this.props; | ||||
|     const { intl, accountIds, shouldUpdateScroll, hasMore, multiColumn } = this.props; | ||||
|  | ||||
|     if (!accountIds) { | ||||
|       return ( | ||||
| @@ -64,6 +65,7 @@ class Blocks extends ImmutablePureComponent { | ||||
|           hasMore={hasMore} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           emptyMessage={emptyMessage} | ||||
|           bindToDocument={!multiColumn} | ||||
|         > | ||||
|           {accountIds.map(id => | ||||
|             <AccountContainer key={id} id={id} /> | ||||
|   | ||||
| @@ -126,6 +126,7 @@ class CommunityTimeline extends React.PureComponent { | ||||
|           onLoadMore={this.handleLoadMore} | ||||
|           emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           bindToDocument={!multiColumn} | ||||
|         /> | ||||
|       </Column> | ||||
|     ); | ||||
|   | ||||
| @@ -33,6 +33,7 @@ class Blocks extends ImmutablePureComponent { | ||||
|     hasMore: PropTypes.bool, | ||||
|     domains: ImmutablePropTypes.orderedSet, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     multiColumn: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
| @@ -44,7 +45,7 @@ class Blocks extends ImmutablePureComponent { | ||||
|   }, 300, { leading: true }); | ||||
|  | ||||
|   render () { | ||||
|     const { intl, domains, shouldUpdateScroll, hasMore } = this.props; | ||||
|     const { intl, domains, shouldUpdateScroll, hasMore, multiColumn } = this.props; | ||||
|  | ||||
|     if (!domains) { | ||||
|       return ( | ||||
| @@ -65,6 +66,7 @@ class Blocks extends ImmutablePureComponent { | ||||
|           hasMore={hasMore} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           emptyMessage={emptyMessage} | ||||
|           bindToDocument={!multiColumn} | ||||
|         > | ||||
|           {domains.map(domain => | ||||
|             <DomainContainer key={domain} domain={domain} /> | ||||
|   | ||||
| @@ -95,6 +95,7 @@ class Favourites extends ImmutablePureComponent { | ||||
|           onLoadMore={this.handleLoadMore} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           emptyMessage={emptyMessage} | ||||
|           bindToDocument={!multiColumn} | ||||
|         /> | ||||
|       </Column> | ||||
|     ); | ||||
|   | ||||
| @@ -23,6 +23,7 @@ class Favourites extends ImmutablePureComponent { | ||||
|     dispatch: PropTypes.func.isRequired, | ||||
|     shouldUpdateScroll: PropTypes.func, | ||||
|     accountIds: ImmutablePropTypes.list, | ||||
|     multiColumn: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
| @@ -36,7 +37,7 @@ class Favourites extends ImmutablePureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { shouldUpdateScroll, accountIds } = this.props; | ||||
|     const { shouldUpdateScroll, accountIds, multiColumn } = this.props; | ||||
|  | ||||
|     if (!accountIds) { | ||||
|       return ( | ||||
| @@ -56,6 +57,7 @@ class Favourites extends ImmutablePureComponent { | ||||
|           scrollKey='favourites' | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           emptyMessage={emptyMessage} | ||||
|           bindToDocument={!multiColumn} | ||||
|         > | ||||
|           {accountIds.map(id => | ||||
|             <AccountContainer key={id} id={id} withNote={false} /> | ||||
|   | ||||
| @@ -32,6 +32,7 @@ class FollowRequests extends ImmutablePureComponent { | ||||
|     hasMore: PropTypes.bool, | ||||
|     accountIds: ImmutablePropTypes.list, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     multiColumn: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
| @@ -43,7 +44,7 @@ class FollowRequests extends ImmutablePureComponent { | ||||
|   }, 300, { leading: true }); | ||||
|  | ||||
|   render () { | ||||
|     const { intl, shouldUpdateScroll, accountIds, hasMore } = this.props; | ||||
|     const { intl, shouldUpdateScroll, accountIds, hasMore, multiColumn } = this.props; | ||||
|  | ||||
|     if (!accountIds) { | ||||
|       return ( | ||||
| @@ -64,6 +65,7 @@ class FollowRequests extends ImmutablePureComponent { | ||||
|           hasMore={hasMore} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           emptyMessage={emptyMessage} | ||||
|           bindToDocument={!multiColumn} | ||||
|         > | ||||
|           {accountIds.map(id => | ||||
|             <AccountAuthorizeContainer key={id} id={id} /> | ||||
|   | ||||
| @@ -36,6 +36,7 @@ class Followers extends ImmutablePureComponent { | ||||
|     hasMore: PropTypes.bool, | ||||
|     blockedBy: PropTypes.bool, | ||||
|     isAccount: PropTypes.bool, | ||||
|     multiColumn: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
| @@ -55,7 +56,7 @@ class Followers extends ImmutablePureComponent { | ||||
|   }, 300, { leading: true }); | ||||
|  | ||||
|   render () { | ||||
|     const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount } = this.props; | ||||
|     const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn } = this.props; | ||||
|  | ||||
|     if (!isAccount) { | ||||
|       return ( | ||||
| @@ -87,6 +88,7 @@ class Followers extends ImmutablePureComponent { | ||||
|           prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />} | ||||
|           alwaysPrepend | ||||
|           emptyMessage={emptyMessage} | ||||
|           bindToDocument={!multiColumn} | ||||
|         > | ||||
|           {blockedBy ? [] : accountIds.map(id => | ||||
|             <AccountContainer key={id} id={id} withNote={false} /> | ||||
|   | ||||
| @@ -36,6 +36,7 @@ class Following extends ImmutablePureComponent { | ||||
|     hasMore: PropTypes.bool, | ||||
|     blockedBy: PropTypes.bool, | ||||
|     isAccount: PropTypes.bool, | ||||
|     multiColumn: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
| @@ -55,7 +56,7 @@ class Following extends ImmutablePureComponent { | ||||
|   }, 300, { leading: true }); | ||||
|  | ||||
|   render () { | ||||
|     const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount } = this.props; | ||||
|     const { shouldUpdateScroll, accountIds, hasMore, blockedBy, isAccount, multiColumn } = this.props; | ||||
|  | ||||
|     if (!isAccount) { | ||||
|       return ( | ||||
| @@ -87,6 +88,7 @@ class Following extends ImmutablePureComponent { | ||||
|           prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />} | ||||
|           alwaysPrepend | ||||
|           emptyMessage={emptyMessage} | ||||
|           bindToDocument={!multiColumn} | ||||
|         > | ||||
|           {blockedBy ? [] : accountIds.map(id => | ||||
|             <AccountContainer key={id} id={id} withNote={false} /> | ||||
|   | ||||
| @@ -157,6 +157,7 @@ class HashtagTimeline extends React.PureComponent { | ||||
|           onLoadMore={this.handleLoadMore} | ||||
|           emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           bindToDocument={!multiColumn} | ||||
|         /> | ||||
|       </Column> | ||||
|     ); | ||||
|   | ||||
| @@ -119,6 +119,7 @@ class HomeTimeline extends React.PureComponent { | ||||
|           timelineId='home' | ||||
|           emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage='Your home timeline is empty! Visit {public} or use search to get started and meet other users.' values={{ public: <Link to='/timelines/public'><FormattedMessage id='empty_column.home.public_timeline' defaultMessage='the public timeline' /></Link> }} />} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           bindToDocument={!multiColumn} | ||||
|         /> | ||||
|       </Column> | ||||
|     ); | ||||
|   | ||||
| @@ -184,6 +184,7 @@ class ListTimeline extends React.PureComponent { | ||||
|           onLoadMore={this.handleLoadMore} | ||||
|           emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.' />} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           bindToDocument={!multiColumn} | ||||
|         /> | ||||
|       </Column> | ||||
|     ); | ||||
|   | ||||
| @@ -40,6 +40,7 @@ class Lists extends ImmutablePureComponent { | ||||
|     dispatch: PropTypes.func.isRequired, | ||||
|     lists: ImmutablePropTypes.list, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     multiColumn: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
| @@ -47,7 +48,7 @@ class Lists extends ImmutablePureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { intl, shouldUpdateScroll, lists } = this.props; | ||||
|     const { intl, shouldUpdateScroll, lists, multiColumn } = this.props; | ||||
|  | ||||
|     if (!lists) { | ||||
|       return ( | ||||
| @@ -70,6 +71,7 @@ class Lists extends ImmutablePureComponent { | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           emptyMessage={emptyMessage} | ||||
|           prepend={<ColumnSubheading text={intl.formatMessage(messages.subheading)} />} | ||||
|           bindToDocument={!multiColumn} | ||||
|         > | ||||
|           {lists.map(list => | ||||
|             <ColumnLink key={list.get('id')} to={`/timelines/list/${list.get('id')}`} icon='list-ul' text={list.get('title')} /> | ||||
|   | ||||
| @@ -32,6 +32,7 @@ class Mutes extends ImmutablePureComponent { | ||||
|     hasMore: PropTypes.bool, | ||||
|     accountIds: ImmutablePropTypes.list, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     multiColumn: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
| @@ -43,7 +44,7 @@ class Mutes extends ImmutablePureComponent { | ||||
|   }, 300, { leading: true }); | ||||
|  | ||||
|   render () { | ||||
|     const { intl, shouldUpdateScroll, hasMore, accountIds } = this.props; | ||||
|     const { intl, shouldUpdateScroll, hasMore, accountIds, multiColumn } = this.props; | ||||
|  | ||||
|     if (!accountIds) { | ||||
|       return ( | ||||
| @@ -64,6 +65,7 @@ class Mutes extends ImmutablePureComponent { | ||||
|           hasMore={hasMore} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           emptyMessage={emptyMessage} | ||||
|           bindToDocument={!multiColumn} | ||||
|         > | ||||
|           {accountIds.map(id => | ||||
|             <AccountContainer key={id} id={id} /> | ||||
|   | ||||
| @@ -191,6 +191,7 @@ class Notifications extends React.PureComponent { | ||||
|         onScrollToTop={this.handleScrollToTop} | ||||
|         onScroll={this.handleScroll} | ||||
|         shouldUpdateScroll={shouldUpdateScroll} | ||||
|         bindToDocument={!multiColumn} | ||||
|       > | ||||
|         {scrollableContent} | ||||
|       </ScrollableList> | ||||
|   | ||||
| @@ -28,6 +28,7 @@ class PinnedStatuses extends ImmutablePureComponent { | ||||
|     statusIds: ImmutablePropTypes.list.isRequired, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     hasMore: PropTypes.bool.isRequired, | ||||
|     multiColumn: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
| @@ -43,7 +44,7 @@ class PinnedStatuses extends ImmutablePureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { intl, shouldUpdateScroll, statusIds, hasMore } = this.props; | ||||
|     const { intl, shouldUpdateScroll, statusIds, hasMore, multiColumn } = this.props; | ||||
|  | ||||
|     return ( | ||||
|       <Column icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}> | ||||
| @@ -53,6 +54,7 @@ class PinnedStatuses extends ImmutablePureComponent { | ||||
|           scrollKey='pinned_statuses' | ||||
|           hasMore={hasMore} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           bindToDocument={!multiColumn} | ||||
|         /> | ||||
|       </Column> | ||||
|     ); | ||||
|   | ||||
| @@ -126,6 +126,7 @@ class PublicTimeline extends React.PureComponent { | ||||
|           scrollKey={`public_timeline-${columnId}`} | ||||
|           emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up' />} | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           bindToDocument={!multiColumn} | ||||
|         /> | ||||
|       </Column> | ||||
|     ); | ||||
|   | ||||
| @@ -23,6 +23,7 @@ class Reblogs extends ImmutablePureComponent { | ||||
|     dispatch: PropTypes.func.isRequired, | ||||
|     shouldUpdateScroll: PropTypes.func, | ||||
|     accountIds: ImmutablePropTypes.list, | ||||
|     multiColumn: PropTypes.bool, | ||||
|   }; | ||||
|  | ||||
|   componentWillMount () { | ||||
| @@ -36,7 +37,7 @@ class Reblogs extends ImmutablePureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { shouldUpdateScroll, accountIds } = this.props; | ||||
|     const { shouldUpdateScroll, accountIds, multiColumn } = this.props; | ||||
|  | ||||
|     if (!accountIds) { | ||||
|       return ( | ||||
| @@ -56,6 +57,7 @@ class Reblogs extends ImmutablePureComponent { | ||||
|           scrollKey='reblogs' | ||||
|           shouldUpdateScroll={shouldUpdateScroll} | ||||
|           emptyMessage={emptyMessage} | ||||
|           bindToDocument={!multiColumn} | ||||
|         > | ||||
|           {accountIds.map(id => | ||||
|             <AccountContainer key={id} id={id} withNote={false} /> | ||||
|   | ||||
| @@ -32,6 +32,28 @@ const MODAL_COMPONENTS = { | ||||
|   'LIST_ADDER':ListAdder, | ||||
| }; | ||||
|  | ||||
| let cachedScrollbarWidth = null; | ||||
|  | ||||
| export const getScrollbarWidth = () => { | ||||
|   if (cachedScrollbarWidth !== null) { | ||||
|     return cachedScrollbarWidth; | ||||
|   } | ||||
|  | ||||
|   const outer = document.createElement('div'); | ||||
|   outer.style.visibility = 'hidden'; | ||||
|   outer.style.overflow = 'scroll'; | ||||
|   document.body.appendChild(outer); | ||||
|  | ||||
|   const inner = document.createElement('div'); | ||||
|   outer.appendChild(inner); | ||||
|  | ||||
|   const scrollbarWidth = outer.offsetWidth - inner.offsetWidth; | ||||
|   cachedScrollbarWidth = scrollbarWidth; | ||||
|   outer.parentNode.removeChild(outer); | ||||
|  | ||||
|   return scrollbarWidth; | ||||
| }; | ||||
|  | ||||
| export default class ModalRoot extends React.PureComponent { | ||||
|  | ||||
|   static propTypes = { | ||||
| @@ -47,8 +69,10 @@ export default class ModalRoot extends React.PureComponent { | ||||
|   componentDidUpdate (prevProps, prevState, { visible }) { | ||||
|     if (visible) { | ||||
|       document.body.classList.add('with-modals--active'); | ||||
|       document.documentElement.style.marginRight = `${getScrollbarWidth()}px`; | ||||
|     } else { | ||||
|       document.body.classList.remove('with-modals--active'); | ||||
|       document.documentElement.style.marginRight = 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -110,12 +110,25 @@ class SwitchingColumnsArea extends React.PureComponent { | ||||
|  | ||||
|   componentWillMount () { | ||||
|     window.addEventListener('resize', this.handleResize, { passive: true }); | ||||
|  | ||||
|     if (this.state.mobile || forceSingleColumn) { | ||||
|       document.body.classList.toggle('layout-single-column', true); | ||||
|       document.body.classList.toggle('layout-multiple-columns', false); | ||||
|     } else { | ||||
|       document.body.classList.toggle('layout-single-column', false); | ||||
|       document.body.classList.toggle('layout-multiple-columns', true); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   componentDidUpdate (prevProps) { | ||||
|   componentDidUpdate (prevProps, prevState) { | ||||
|     if (![this.props.location.pathname, '/'].includes(prevProps.location.pathname)) { | ||||
|       this.node.handleChildrenContentChange(); | ||||
|     } | ||||
|  | ||||
|     if (prevState.mobile !== this.state.mobile && !forceSingleColumn) { | ||||
|       document.body.classList.toggle('layout-single-column', this.state.mobile); | ||||
|       document.body.classList.toggle('layout-multiple-columns', !this.state.mobile); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   componentWillUnmount () { | ||||
|   | ||||
| @@ -108,14 +108,6 @@ function main() { | ||||
|     if (parallaxComponents.length > 0 ) { | ||||
|       new Rellax('.parallax', { speed: -1 }); | ||||
|     } | ||||
|  | ||||
|     if (document.body.classList.contains('with-modals')) { | ||||
|       const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth; | ||||
|       const scrollbarWidthStyle = document.createElement('style'); | ||||
|       scrollbarWidthStyle.id = 'scrollbar-width'; | ||||
|       document.head.appendChild(scrollbarWidthStyle); | ||||
|       scrollbarWidthStyle.sheet.insertRule(`body.with-modals--active { margin-right: ${scrollbarWidth}px; }`, 0); | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   delegate(document, '.webapp-btn', 'click', ({ target, button }) => { | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|  | ||||
| body { | ||||
|   font-family: $font-sans-serif, sans-serif; | ||||
|   background: darken($ui-base-color, 8%); | ||||
|   background: darken($ui-base-color, 7%); | ||||
|   font-size: 13px; | ||||
|   line-height: 18px; | ||||
|   font-weight: 400; | ||||
| @@ -35,11 +35,19 @@ body { | ||||
|   } | ||||
|  | ||||
|   &.app-body { | ||||
|     position: absolute; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     padding: 0; | ||||
|     background: $ui-base-color; | ||||
|  | ||||
|     &.layout-single-column { | ||||
|       height: auto; | ||||
|       min-height: 100%; | ||||
|       overflow-y: scroll; | ||||
|     } | ||||
|  | ||||
|     &.layout-multiple-columns { | ||||
|       position: absolute; | ||||
|       width: 100%; | ||||
|       height: 100%; | ||||
|     } | ||||
|  | ||||
|     &.with-modals--active { | ||||
|       overflow-y: hidden; | ||||
| @@ -56,7 +64,6 @@ body { | ||||
|  | ||||
|     &--active { | ||||
|       overflow-y: hidden; | ||||
|       margin-right: 13px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -134,9 +141,22 @@ button { | ||||
|   & > div { | ||||
|     display: flex; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|     outline: 0 !important; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .layout-single-column .app-holder { | ||||
|   &, | ||||
|   & > div { | ||||
|     min-height: 100%; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .layout-multiple-columns .app-holder { | ||||
|   &, | ||||
|   & > div { | ||||
|     height: 100%; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1804,6 +1804,7 @@ a.account__display-name { | ||||
|     justify-content: center; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     min-height: 100vh; | ||||
|  | ||||
|     &__pane { | ||||
|       height: 100%; | ||||
| @@ -1817,6 +1818,7 @@ a.account__display-name { | ||||
|       } | ||||
|  | ||||
|       &__inner { | ||||
|         position: fixed; | ||||
|         width: 285px; | ||||
|         pointer-events: auto; | ||||
|         height: 100%; | ||||
| @@ -1871,7 +1873,6 @@ a.account__display-name { | ||||
|   flex-direction: column; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   background: darken($ui-base-color, 7%); | ||||
| } | ||||
|  | ||||
| .drawer { | ||||
| @@ -2012,6 +2013,10 @@ a.account__display-name { | ||||
|     top: 15px; | ||||
|   } | ||||
|  | ||||
|   .scrollable { | ||||
|     overflow: visible; | ||||
|   } | ||||
|  | ||||
|   @media screen and (min-width: $no-gap-breakpoint) { | ||||
|     padding: 10px 0; | ||||
|   } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user