diff --git a/shared/app/nav.desktop.js b/shared/app/nav.desktop.js index 720e8e748013..dc83bc36f06b 100644 --- a/shared/app/nav.desktop.js +++ b/shared/app/nav.desktop.js @@ -32,8 +32,8 @@ function Nav(props: Props) { badgeNumbers={props.navBadges.toJS()} />} - {visibleScreen.component} - {layerScreens.map(r => r.leafComponent)} + {visibleScreen.component({isActive: true})} + {layerScreens.map(r => r.leafComponent({isActive: true}))}
{![chatTab, loginTab].includes(props.routeSelected) && diff --git a/shared/app/nav.native.js b/shared/app/nav.native.js index 7b2f0b98d788..f4423aba7417 100644 --- a/shared/app/nav.native.js +++ b/shared/app/nav.native.js @@ -25,7 +25,7 @@ type OwnProps = RouteProps<{}, {}> type CardStackShimProps = { mode?: 'modal', - renderRoute: (route: RenderRouteResult) => React$Element<*>, + renderRoute: (route: RenderRouteResult, isActive: boolean) => React$Element<*>, onNavigateBack: () => void, stack: RouteRenderStack, hidden?: boolean, @@ -41,8 +41,8 @@ class CardStackShim extends Component<*, CardStackShimProps, *> { getComponentForRouteName = () => this.RenderRouteShim RenderRouteShim = ({navigation}) => { - const route = navigation.state.params - return this.props.renderRoute(route) + const {route, isActive} = navigation.state.params + return this.props.renderRoute(route, isActive) } _dispatchShim = (action: NavigationAction) => { @@ -67,9 +67,12 @@ class CardStackShim extends Component<*, CardStackShimProps, *> { state: { index: stack.size - 1, routes: stack - .map(route => { + .map((route, index) => { const routeName = route.path.join('/') - return {key: routeName, routeName, params: route} + // Immutable.Stack indexes go from N-1(top/front)...0(bottom/back) + // The bottom/back item of the stack is our top (active) screen + const isActive = !this.props.hidden && index === 0 + return {key: routeName, routeName, params: {route, isActive}} }) .toArray(), }, @@ -114,11 +117,11 @@ const barStyle = ({showStatusBarDarkContent, underStatusBar}) => { return 'dark-content' } -function renderStackRoute(route) { +function renderStackRoute(route, isActive) { const {underStatusBar, hideStatusBar, showStatusBarDarkContent} = route.tags // Skip extra view if no statusbar if (hideStatusBar) { - return route.component + return route.component({isActive}) } return ( @@ -129,7 +132,7 @@ function renderStackRoute(route) { backgroundColor="rgba(0, 26, 51, 0.25)" barStyle={barStyle({showStatusBarDarkContent, underStatusBar})} /> - {route.component} + {route.component({isActive})} ) } @@ -222,7 +225,7 @@ function Nav(props: Props) { const mainScreens = baseScreens.takeUntil(fullscreenPred) const fullScreens = baseScreens.skipUntil(fullscreenPred).unshift({ path: ['main'], - component: , + component: () => , tags: {underStatusBar: true}, // don't pad nav stack (child screens have own padding) }) @@ -235,7 +238,7 @@ function Nav(props: Props) { /> ) const layerScreens = props.routeStack.filter(r => r.tags.layerOnTop) - const layers = layerScreens.map(r => r.leafComponent) + const layers = layerScreens.map(r => r.leafComponent({isActive: true})) // If we have layers, lets add an extra box, else lets just pass through if (layers.count()) { diff --git a/shared/chat/conversation/container.js b/shared/chat/conversation/container.js index 466ff715b825..d2fdbda9abf1 100644 --- a/shared/chat/conversation/container.js +++ b/shared/chat/conversation/container.js @@ -5,7 +5,7 @@ import * as Creators from '../../actions/chat/creators' import Conversation from './index' import NoConversation from './no-conversation' import Rekey from './rekey/container' -import {connect} from 'react-redux' +import pausableConnect from '../../util/pausable-connect' import {getProfile} from '../../actions/tracker' import {withState, withHandlers, compose, branch, renderNothing, renderComponent} from 'recompose' import {selectedSearchIdHoc} from '../../searchv3/helpers' @@ -117,7 +117,7 @@ const mergeProps = (stateProps: StateProps, dispatchProps: DispatchProps) => { } export default compose( - connect(mapStateToProps, mapDispatchToProps, mergeProps), + pausableConnect(mapStateToProps, mapDispatchToProps, mergeProps), branch((props: Props) => !props.selectedConversationIDKey, renderNothing), branch( (props: Props) => props.selectedConversationIDKey === Constants.nothingSelected && !props.inSearch, diff --git a/shared/chat/inbox/container.js b/shared/chat/inbox/container.js index 8aa92ea30b83..adce46071f99 100644 --- a/shared/chat/inbox/container.js +++ b/shared/chat/inbox/container.js @@ -2,7 +2,7 @@ import * as I from 'immutable' import * as Constants from '../../constants/chat' import Inbox from './index' -import {connect} from 'react-redux' +import pausableConnect from '../../util/pausable-connect' import {createSelectorCreator, defaultMemoize} from 'reselect' import {loadInbox, newChat, untrustedInboxVisible} from '../../actions/chat/creators' import {compose, lifecycle} from 'recompose' @@ -61,7 +61,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ const throttleHelper = throttle(cb => cb(), 60 * 1000) export default compose( - connect(mapStateToProps, mapDispatchToProps), + pausableConnect(mapStateToProps, mapDispatchToProps), lifecycle({ componentDidMount: function() { throttleHelper(() => { diff --git a/shared/chat/render.desktop.js b/shared/chat/render.desktop.js index e7de82c3a6a9..a5f11338c2d1 100644 --- a/shared/chat/render.desktop.js +++ b/shared/chat/render.desktop.js @@ -3,9 +3,9 @@ import React from 'react' import Inbox from './inbox/container' import {globalStyles} from '../styles' -const Render = ({children}: {children: any}) => ( +const Render = ({isActive, children}: {isActive: boolean, children: any}) => (
- + {children}
) diff --git a/shared/folders/index.js b/shared/folders/index.js index 9a041d292ee3..6ad19980ba21 100644 --- a/shared/folders/index.js +++ b/shared/folders/index.js @@ -1,7 +1,7 @@ // @flow import React, {Component} from 'react' import Render from './render' -import {connect} from 'react-redux' +import pausableConnect from '../util/pausable-connect' import {favoriteList} from '../actions/favorite' import {openInKBFS} from '../actions/kbfs' import {openTlfInChat} from '../actions/chat' @@ -70,7 +70,7 @@ const mapDispatchToProps = (dispatch: any, {routePath, routeState, setRouteState onToggleShowIgnored: () => setRouteState({showingIgnored: !routeState.showingIgnored}), }) -const ConnectedFolders = connect(mapStateToProps, mapDispatchToProps)(Folders) +const ConnectedFolders = pausableConnect(mapStateToProps, mapDispatchToProps)(Folders) export function PrivateFolders(props: FoldersRouteProps) { return diff --git a/shared/profile/container.js b/shared/profile/container.js index d29f161f9bc8..712aab02a8ed 100644 --- a/shared/profile/container.js +++ b/shared/profile/container.js @@ -11,7 +11,7 @@ import { checkProof, } from '../actions/profile' import {searchSuggestions} from '../actions/searchv3/creators' -import {connect} from 'react-redux' +import pausableConnect from '../util/pausable-connect' import {getProfile, updateTrackers, onFollow, onUnfollow, openProofUrl} from '../actions/tracker' import {isLoading} from '../constants/tracker' import {isTesting} from '../local-debug' @@ -55,7 +55,7 @@ class ProfileContainer extends PureComponent, void> { } } -export default connect( +export default pausableConnect( (state, {routeProps, routeState, routePath}: OwnProps) => { const myUsername = state.config.username const username = routeProps.username ? routeProps.username : myUsername diff --git a/shared/route-tree/render-route.js b/shared/route-tree/render-route.js index e8ef257847a0..cb7a7a11e45f 100644 --- a/shared/route-tree/render-route.js +++ b/shared/route-tree/render-route.js @@ -10,8 +10,8 @@ import type {RouteDefNode, RouteStateNode, Path} from './' type _RenderRouteResultParams = { path: I.List, tags: LeafTags, - component: React$Element, - leafComponent: React$Element, + component: ({isActive: boolean}) => React$Element, + leafComponent: ({isActive: boolean}) => React$Element, } export const RenderRouteResult: ( @@ -54,6 +54,7 @@ export type RouteProps = { } type RenderRouteNodeProps = { + isActive: boolean, isContainer: boolean, routeDef: RouteDefNode, routeState: RouteStateNode, @@ -68,10 +69,21 @@ type RenderRouteNodeProps = { // shouldComponentUpdate (via PureComponent). class RenderRouteNode extends PureComponent<*, RenderRouteNodeProps<*>, *> { render() { - const {isContainer, routeDef, routeState, setRouteState, path, leafTags, stack, children} = this.props + const { + isActive, + isContainer, + routeDef, + routeState, + setRouteState, + path, + leafTags, + stack, + children, + } = this.props const RouteComponent = isContainer ? routeDef.containerComponent : routeDef.component return ( - r.update('component', child => ( + r.update('component', child => ({isActive}) => ( - {child} + {child({isActive})} )) ) @@ -154,8 +167,9 @@ function renderRouteStack({ if (routeDef.component) { // If this path has a leaf component to render, add it to the stack. - const routeComponent = ( + const routeComponent = ({isActive}) => ( , * // renderRouteStack gives us a stack of all views down the current route path. // This component renders the bottom (currently visible) one. var viewStack = renderRouteStack({...this.props, path: I.List()}) - return viewStack.last().component + return viewStack.last().component({isActive: true}) } } diff --git a/shared/settings/index.js b/shared/settings/index.js index b9c1f0a157ba..b014d44507af 100644 --- a/shared/settings/index.js +++ b/shared/settings/index.js @@ -1,13 +1,13 @@ // @flow import SettingsContainer from './render' -import {connect} from 'react-redux' +import pausableConnect from '../util/pausable-connect' import {switchTo} from '../actions/route-tree' import {logout} from '../actions/login/creators' import type {RouteProps} from '../route-tree/render-route' // $FlowIssue type this connector -export default connect( +export default pausableConnect( (state, {routeSelected, routeLeafTags}: RouteProps<{}, {}>) => ({ badgeNumbers: {}, // TODO add badging logic selectedTab: routeSelected, diff --git a/shared/util/pausable-connect.js b/shared/util/pausable-connect.js new file mode 100644 index 000000000000..0c9b9fc38fef --- /dev/null +++ b/shared/util/pausable-connect.js @@ -0,0 +1,21 @@ +// @flow +import {createConnect} from 'react-redux/src/connect/connect' +import defaultSelectorFactory from 'react-redux/src/connect/selectorFactory' + +import typeof {connect} from 'react-redux' + +function selectorFactory(dispatch, factoryOptions) { + const selector = defaultSelectorFactory(dispatch, factoryOptions) + let cachedResult + const pausableSelector = function(state, ownProps) { + if (ownProps.isActive || cachedResult === undefined) { + cachedResult = selector(state, ownProps) + } + return cachedResult + } + return pausableSelector +} + +const pausableConnect: connect = createConnect({selectorFactory}) + +export default pausableConnect