From cf845fed3824d3e3587ce9b2ad752c2b3f0a2a76 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 24 Apr 2017 11:49:08 +0900 Subject: [PATCH] Hide some components rather than unmounting (#2271) Hide some components rather than unmounting them to allow to show again quickly and keep the view state such as the scrolled offset. --- .../components/components/status_list.jsx | 19 ++- .../components/containers/mastodon.jsx | 127 +++++++++++++++++- .../features/account_timeline/index.jsx | 1 + .../features/community_timeline/index.jsx | 2 +- .../features/favourited_statuses/index.jsx | 2 +- .../features/hashtag_timeline/index.jsx | 2 +- .../features/home_timeline/index.jsx | 2 +- .../features/notifications/index.jsx | 30 ++--- .../features/public_timeline/index.jsx | 2 +- .../ui/containers/status_list_container.jsx | 2 + .../components/features/ui/index.jsx | 6 +- app/assets/stylesheets/components.scss | 13 +- app/assets/stylesheets/containers.scss | 12 +- 13 files changed, 167 insertions(+), 53 deletions(-) diff --git a/app/assets/javascripts/components/components/status_list.jsx b/app/assets/javascripts/components/components/status_list.jsx index dc2a9509d..517c8fe5d 100644 --- a/app/assets/javascripts/components/components/status_list.jsx +++ b/app/assets/javascripts/components/components/status_list.jsx @@ -60,7 +60,7 @@ class StatusList extends React.PureComponent { } render () { - const { statusIds, onScrollToBottom, trackScroll, isLoading, isUnread, hasMore, prepend, emptyMessage } = this.props; + const { statusIds, onScrollToBottom, scrollKey, shouldUpdateScroll, isLoading, isUnread, hasMore, prepend, emptyMessage } = this.props; let loadMore = ''; let scrollableArea = ''; @@ -98,25 +98,22 @@ class StatusList extends React.PureComponent { ); } - if (trackScroll) { - return ( - - {scrollableArea} - - ); - } else { - return scrollableArea; - } + return ( + + {scrollableArea} + + ); } } StatusList.propTypes = { + scrollKey: PropTypes.string.isRequired, statusIds: ImmutablePropTypes.list.isRequired, onScrollToBottom: PropTypes.func, onScrollToTop: PropTypes.func, onScroll: PropTypes.func, - trackScroll: PropTypes.bool, + shouldUpdateScroll: PropTypes.func, isLoading: PropTypes.bool, isUnread: PropTypes.bool, hasMore: PropTypes.bool, diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index e2b91e5dd..c85a353ee 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -99,6 +99,125 @@ addLocaleData([ ...id, ]); +const getTopWhenReplacing = (previous, { location }) => location && location.action === 'REPLACE' && [0, 0]; + +const hiddenColumnContainerStyle = { + position: 'absolute', + left: '0', + top: '0', + visibility: 'hidden' +}; + +class Container extends React.PureComponent { + + constructor(props) { + super(props); + + this.state = { + renderedPersistents: [], + unrenderedPersistents: [], + }; + } + + componentWillMount () { + this.unlistenHistory = null; + + this.setState(() => { + return { + mountImpersistent: false, + renderedPersistents: [], + unrenderedPersistents: [ + {pathname: '/timelines/home', component: HomeTimeline}, + {pathname: '/timelines/public', component: PublicTimeline}, + {pathname: '/timelines/public/local', component: CommunityTimeline}, + + {pathname: '/notifications', component: Notifications}, + {pathname: '/favourites', component: FavouritedStatuses} + ], + }; + }, () => { + if (this.unlistenHistory) { + return; + } + + this.unlistenHistory = browserHistory.listen(location => { + const pathname = location.pathname.replace(/\/$/, '').toLowerCase(); + + this.setState(oldState => { + let persistentMatched = false; + + const newState = { + renderedPersistents: oldState.renderedPersistents.map(persistent => { + const givenMatched = persistent.pathname === pathname; + + if (givenMatched) { + persistentMatched = true; + } + + return { + hidden: !givenMatched, + pathname: persistent.pathname, + component: persistent.component + }; + }), + }; + + if (!persistentMatched) { + newState.unrenderedPersistents = []; + + oldState.unrenderedPersistents.forEach(persistent => { + if (persistent.pathname === pathname) { + persistentMatched = true; + + newState.renderedPersistents.push({ + hidden: false, + pathname: persistent.pathname, + component: persistent.component + }); + } else { + newState.unrenderedPersistents.push(persistent); + } + }); + } + + newState.mountImpersistent = !persistentMatched; + + return newState; + }); + }); + }); + } + + componentWillUnmount () { + if (this.unlistenHistory) { + this.unlistenHistory(); + } + + this.unlistenHistory = "done"; + } + + render () { + // Hide some components rather than unmounting them to allow to show again + // quickly and keep the view state such as the scrolled offset. + const persistentsView = this.state.renderedPersistents.map((persistent) => +
+ +
+ ); + + return ( + + {this.state.mountImpersistent && this.props.children} + {persistentsView} + + ); + } +} + +Container.propTypes = { + children: PropTypes.node, +}; + class Mastodon extends React.Component { componentDidMount() { @@ -160,18 +279,12 @@ class Mastodon extends React.Component { - + - - - - - - diff --git a/app/assets/javascripts/components/features/account_timeline/index.jsx b/app/assets/javascripts/components/features/account_timeline/index.jsx index 4987a2364..a06de3d21 100644 --- a/app/assets/javascripts/components/features/account_timeline/index.jsx +++ b/app/assets/javascripts/components/features/account_timeline/index.jsx @@ -62,6 +62,7 @@ class AccountTimeline extends React.PureComponent { } + scrollKey='account_timeline' statusIds={statusIds} isLoading={isLoading} hasMore={hasMore} diff --git a/app/assets/javascripts/components/features/community_timeline/index.jsx b/app/assets/javascripts/components/features/community_timeline/index.jsx index c2d8bf2ed..3877888ba 100644 --- a/app/assets/javascripts/components/features/community_timeline/index.jsx +++ b/app/assets/javascripts/components/features/community_timeline/index.jsx @@ -77,7 +77,7 @@ class CommunityTimeline extends React.PureComponent { return ( - } /> + } /> ); } diff --git a/app/assets/javascripts/components/features/favourited_statuses/index.jsx b/app/assets/javascripts/components/features/favourited_statuses/index.jsx index d6f53bf33..bc45ace51 100644 --- a/app/assets/javascripts/components/features/favourited_statuses/index.jsx +++ b/app/assets/javascripts/components/features/favourited_statuses/index.jsx @@ -47,7 +47,7 @@ class Favourites extends React.PureComponent { return ( - + ); } diff --git a/app/assets/javascripts/components/features/hashtag_timeline/index.jsx b/app/assets/javascripts/components/features/hashtag_timeline/index.jsx index 5c091e17f..0575e9214 100644 --- a/app/assets/javascripts/components/features/hashtag_timeline/index.jsx +++ b/app/assets/javascripts/components/features/hashtag_timeline/index.jsx @@ -71,7 +71,7 @@ class HashtagTimeline extends React.PureComponent { return ( - } /> + } /> ); } diff --git a/app/assets/javascripts/components/features/home_timeline/index.jsx b/app/assets/javascripts/components/features/home_timeline/index.jsx index 6b986171e..52b94690d 100644 --- a/app/assets/javascripts/components/features/home_timeline/index.jsx +++ b/app/assets/javascripts/components/features/home_timeline/index.jsx @@ -22,7 +22,7 @@ class HomeTimeline extends React.PureComponent { return ( - }} />} /> + }} />} /> ); } diff --git a/app/assets/javascripts/components/features/notifications/index.jsx b/app/assets/javascripts/components/features/notifications/index.jsx index 14c00b9ce..da3ce2f62 100644 --- a/app/assets/javascripts/components/features/notifications/index.jsx +++ b/app/assets/javascripts/components/features/notifications/index.jsx @@ -80,7 +80,7 @@ class Notifications extends React.PureComponent { } render () { - const { intl, notifications, trackScroll, isLoading, isUnread } = this.props; + const { intl, notifications, shouldUpdateScroll, isLoading, isUnread } = this.props; let loadMore = ''; let scrollableArea = ''; @@ -113,25 +113,15 @@ class Notifications extends React.PureComponent { ); } - if (trackScroll) { - return ( - - - - - {scrollableArea} - - - ); - } else { - return ( - - - + return ( + + + + {scrollableArea} - - ); - } + + + ); } } @@ -139,7 +129,7 @@ class Notifications extends React.PureComponent { Notifications.propTypes = { notifications: ImmutablePropTypes.list.isRequired, dispatch: PropTypes.func.isRequired, - trackScroll: PropTypes.bool, + shouldUpdateScroll: PropTypes.func, intl: PropTypes.object.isRequired, isLoading: PropTypes.bool, isUnread: PropTypes.bool diff --git a/app/assets/javascripts/components/features/public_timeline/index.jsx b/app/assets/javascripts/components/features/public_timeline/index.jsx index fa7a2db8e..53be13686 100644 --- a/app/assets/javascripts/components/features/public_timeline/index.jsx +++ b/app/assets/javascripts/components/features/public_timeline/index.jsx @@ -77,7 +77,7 @@ class PublicTimeline extends React.PureComponent { return ( - } /> + } /> ); } diff --git a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx index 4c33f2b61..1599000b5 100644 --- a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx @@ -40,6 +40,8 @@ const makeMapStateToProps = () => { const getStatusIds = makeGetStatusIds(); const mapStateToProps = (state, props) => ({ + scrollKey: props.scrollKey, + shouldUpdateScroll: props.shouldUpdateScroll, statusIds: getStatusIds(state, props), isLoading: state.getIn(['timelines', props.type, 'isLoading'], true), isUnread: state.getIn(['timelines', props.type, 'unread']) > 0, diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/assets/javascripts/components/features/ui/index.jsx index 1f35842d9..c92b9751b 100644 --- a/app/assets/javascripts/components/features/ui/index.jsx +++ b/app/assets/javascripts/components/features/ui/index.jsx @@ -127,9 +127,9 @@ class UI extends React.PureComponent { mountedColumns = ( - - - {children} + false} /> + false} /> +
{children}
); } diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index feab81366..0c8379be4 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -89,11 +89,11 @@ border: none; background: transparent; cursor: pointer; - transition: all 100ms ease-in; + transition: color 100ms ease-in; &:hover, &:active, &:focus { color: lighten($color1, 33%); - transition: all 200ms ease-out; + transition: color 200ms ease-out; } &.disabled { @@ -152,11 +152,11 @@ padding: 0 3px; line-height: 27px; outline: 0; - transition: all 100ms ease-in; + transition: color 100ms ease-in; &:hover, &:active, &:focus { color: lighten($color1, 26%); - transition: all 200ms ease-out; + transition: color 200ms ease-out; } &.disabled { @@ -1100,6 +1100,7 @@ a.status__content__spoiler-link { flex-direction: row; justify-content: flex-start; overflow-x: auto; + position: relative; } @media screen and (min-width: 360px) { @@ -1257,11 +1258,11 @@ a.status__content__spoiler-link { flex-direction: row; a { - transition: all 100ms ease-in; + transition: background 100ms ease-in; &:hover { background: lighten($color1, 3%); - transition: all 200ms ease-out; + transition: background 200ms ease-out; } } } diff --git a/app/assets/stylesheets/containers.scss b/app/assets/stylesheets/containers.scss index 43705b19c..6f339f998 100644 --- a/app/assets/stylesheets/containers.scss +++ b/app/assets/stylesheets/containers.scss @@ -9,6 +9,16 @@ } } +.mastodon-column-container { + display: flex; + height: 100%; + width: 100%; + + // 707568 - height 100% doesn't work on child of a flex item - chromium - Monorail + // https://bugs.chromium.org/p/chromium/issues/detail?id=707568 + flex: 1 1 auto; +} + .logo-container { max-width: 400px; margin: 100px auto; @@ -40,7 +50,7 @@ img { opacity: 0.8; - transition: all 0.8s ease; + transition: opacity 0.8s ease; } &:hover {