Fix login transition (#410)
* Fix login transition * Add instant transition to modal * Fix transition to show loader on first login * Add loader to load_team * Remove back button
This commit is contained in:
parent
5d997ae22b
commit
bc18d60883
15 changed files with 103 additions and 31 deletions
|
|
@ -303,8 +303,9 @@ export function leaveChannel(channel, reset = false) {
|
|||
};
|
||||
}
|
||||
|
||||
export function setChannelLoading() {
|
||||
export function setChannelLoading(loading = true) {
|
||||
return {
|
||||
type: ViewTypes.SET_CHANNEL_LOADER
|
||||
type: ViewTypes.SET_CHANNEL_LOADER,
|
||||
loading
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,36 @@
|
|||
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
import {NavigationTypes, ViewTypes} from 'app/constants';
|
||||
import Routes from 'app/navigation/routes';
|
||||
|
||||
export function goToChannelView() {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: NavigationTypes.NAVIGATION_RESET,
|
||||
routes: [Routes.ChannelView]
|
||||
}, getState);
|
||||
const state = getState();
|
||||
if (state.views.login.loginId) {
|
||||
dispatch({
|
||||
type: NavigationTypes.NAVIGATION_REPLACE_AT_ROOT,
|
||||
route: Routes.ChannelView
|
||||
}, getState);
|
||||
|
||||
dispatch({
|
||||
type: NavigationTypes.NAVIGATION_CLOSE_MODAL
|
||||
}, getState);
|
||||
|
||||
setTimeout(() => {
|
||||
dispatch({
|
||||
type: ViewTypes.APPLICATION_INITIALIZED
|
||||
}, getState);
|
||||
}, 800); // modal close takes 400ms
|
||||
} else {
|
||||
dispatch({
|
||||
type: NavigationTypes.NAVIGATION_RESET,
|
||||
routes: [Routes.ChannelView]
|
||||
}, getState);
|
||||
|
||||
dispatch({
|
||||
type: ViewTypes.APPLICATION_INITIALIZED
|
||||
}, getState);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,8 @@ import {markChannelAsRead, viewChannel} from 'mattermost-redux/actions/channels'
|
|||
export function goToSelectServer() {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: NavigationTypes.NAVIGATION_RESET,
|
||||
routes: [Routes.SelectServer],
|
||||
index: 0
|
||||
type: NavigationTypes.NAVIGATION_MODAL,
|
||||
route: Routes.SelectServer
|
||||
}, getState);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ const NavigationTypes = keyMirror({
|
|||
NAVIGATION_JUMP: null,
|
||||
NAVIGATION_JUMP_TO_INDEX: null,
|
||||
NAVIGATION_REPLACE: null,
|
||||
NAVIGATION_REPLACE_AT_ROOT: null,
|
||||
NAVIGATION_RESET: null,
|
||||
NAVIGATION_MODAL: null,
|
||||
NAVIGATION_CLOSE_MODAL: null,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
import keyMirror from 'mattermost-redux/utils/key_mirror';
|
||||
|
||||
const ViewTypes = keyMirror({
|
||||
APPLICATION_INITIALIZED: null,
|
||||
|
||||
SERVER_URL_CHANGED: null,
|
||||
|
||||
LOGIN_ID_CHANGED: null,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
import React, {PropTypes, PureComponent} from 'react';
|
||||
import {
|
||||
Animated
|
||||
Animated,
|
||||
InteractionManager
|
||||
} from 'react-native';
|
||||
|
||||
const {View: AnimatedView} = Animated;
|
||||
|
|
@ -21,11 +22,13 @@ export default class NavigationModal extends PureComponent {
|
|||
]),
|
||||
deviceHeight: PropTypes.number.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
duration: PropTypes.number,
|
||||
show: PropTypes.bool
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
animationType: ANIMATION_TYPES.SlideFromBottom,
|
||||
duration: 400,
|
||||
show: false
|
||||
}
|
||||
|
||||
|
|
@ -62,9 +65,13 @@ export default class NavigationModal extends PureComponent {
|
|||
const animationType = nextProps.show ? nextProps.animationType : this.props.animationType;
|
||||
|
||||
if (animationType === ANIMATION_TYPES.SlideFromBottom) {
|
||||
this.slideFromBottomAnimationRunner(nextProps);
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this.slideFromBottomAnimationRunner(nextProps);
|
||||
});
|
||||
} else if (animationType === ANIMATION_TYPES.Fade) {
|
||||
this.fadeAnimationRunner(nextProps);
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
this.fadeAnimationRunner(nextProps);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +88,7 @@ export default class NavigationModal extends PureComponent {
|
|||
});
|
||||
const setOpacityToFull = Animated.timing(this.state.opacity, {
|
||||
toValue: 1,
|
||||
duration: 400
|
||||
duration: nextProps.duration
|
||||
});
|
||||
Animated.sequence([
|
||||
setOpacityToZero,
|
||||
|
|
@ -99,7 +106,7 @@ export default class NavigationModal extends PureComponent {
|
|||
} else {
|
||||
const setOpacityToZero = Animated.timing(this.state.opacity, {
|
||||
toValue: 0,
|
||||
duration: 400
|
||||
duration: nextProps.duration
|
||||
});
|
||||
const setTopToDeviceHeight = Animated.timing(this.state.top, {
|
||||
toValue: this.props.deviceHeight,
|
||||
|
|
@ -130,7 +137,7 @@ export default class NavigationModal extends PureComponent {
|
|||
|
||||
Animated.timing(this.state.top, {
|
||||
toValue: animateValue,
|
||||
duration: 400
|
||||
duration: nextProps.duration
|
||||
}).start(() => {
|
||||
// Once the scene has finished sliding down we can release the child scene
|
||||
// which will unmount the scene correctly.
|
||||
|
|
|
|||
|
|
@ -75,7 +75,12 @@ class Router extends Component {
|
|||
return {};
|
||||
}
|
||||
|
||||
return Object.assign({}, route.navigationProps, route.component.navigationProps);
|
||||
let component = route.component;
|
||||
if (!component) {
|
||||
component = {};
|
||||
}
|
||||
|
||||
return Object.assign({}, route.navigationProps, component.navigationProps);
|
||||
};
|
||||
|
||||
renderTransition = (transitionProps) => {
|
||||
|
|
@ -326,6 +331,7 @@ class Router extends Component {
|
|||
animationType={modalNavigationProps.modalAnimationType}
|
||||
deviceHeight={this.state.deviceHeight}
|
||||
deviceWidth={this.state.deviceWidth}
|
||||
duration={modalNavigationProps.duration}
|
||||
show={modalVisible}
|
||||
>
|
||||
<NavigationExperimental.Transitioner
|
||||
|
|
|
|||
|
|
@ -187,7 +187,9 @@ export const Routes = {
|
|||
key: 'SelectServer',
|
||||
component: SelectServer,
|
||||
navigationProps: {
|
||||
title: {id: 'mobile.routes.enterServerUrl', defaultMessage: 'Enter Server URL'}
|
||||
title: {id: 'mobile.routes.enterServerUrl', defaultMessage: 'Enter Server URL'},
|
||||
duration: 0,
|
||||
modalAnimationType: 'fade'
|
||||
}
|
||||
},
|
||||
SelectTeam: {
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ export default function(state = initialState, action) {
|
|||
case NavigationTypes.NAVIGATION_RESET:
|
||||
return {
|
||||
...state,
|
||||
...NavigationExperimental.StateUtils.reset(state, action.routes, action.index),
|
||||
...NavigationExperimental.StateUtils.reset(state, action.routes),
|
||||
modal: {
|
||||
index: 0,
|
||||
requestClose: false,
|
||||
|
|
@ -174,6 +174,9 @@ export default function(state = initialState, action) {
|
|||
return NavigationExperimental.StateUtils.replaceAtIndex(state, state.index, action.route);
|
||||
}
|
||||
|
||||
case NavigationTypes.NAVIGATION_REPLACE_AT_ROOT:
|
||||
return NavigationExperimental.StateUtils.replaceAtIndex(state, state.index, action.route);
|
||||
|
||||
case NavigationTypes.NAVIGATION_MODAL: {
|
||||
const modal = {
|
||||
index: 0,
|
||||
|
|
|
|||
|
|
@ -118,13 +118,23 @@ function loading(state = false, action) {
|
|||
case ChannelTypes.SELECT_CHANNEL:
|
||||
return false;
|
||||
case ViewTypes.SET_CHANNEL_LOADER:
|
||||
return true;
|
||||
return action.loading;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
function appInitializing(state = true, action) {
|
||||
switch (action.type) {
|
||||
case ViewTypes.APPLICATION_INITIALIZED:
|
||||
return false;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default combineReducers({
|
||||
appInitializing,
|
||||
drafts,
|
||||
loading
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export default class ChannelPostList extends PureComponent {
|
|||
getPostsBefore: PropTypes.func.isRequired,
|
||||
goToThread: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
applicationInitializing: PropTypes.bool.isRequired,
|
||||
channel: PropTypes.object.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
channelIsLoading: PropTypes.bool,
|
||||
|
|
@ -64,7 +65,9 @@ export default class ChannelPostList extends PureComponent {
|
|||
if (hasFirstPost) {
|
||||
this.setState({hasFirstPost});
|
||||
}
|
||||
this.loaderAnimationRunner();
|
||||
if (!nextProps.applicationInitializing) {
|
||||
this.loaderAnimationRunner();
|
||||
}
|
||||
} else {
|
||||
this.setState({
|
||||
didInitialPostsLoad: false,
|
||||
|
|
@ -112,9 +115,9 @@ export default class ChannelPostList extends PureComponent {
|
|||
};
|
||||
|
||||
render() {
|
||||
const {channelIsLoading, posts, postsRequests, theme} = this.props;
|
||||
const {applicationInitializing, channelIsLoading, posts, postsRequests, theme} = this.props;
|
||||
let component;
|
||||
if (!channelIsLoading && posts && (postsRequests.getPosts.status !== RequestStatus.STARTED || !this.state.didInitialPostsLoad)) {
|
||||
if (!applicationInitializing && !channelIsLoading && posts && (postsRequests.getPosts.status !== RequestStatus.STARTED || !this.state.didInitialPostsLoad)) {
|
||||
component = (
|
||||
<PostList
|
||||
posts={posts}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ const getPostsInCurrentChannelWithReplyProps = createSelector(
|
|||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps,
|
||||
applicationInitializing: state.views.channel.appInitializing,
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
channelIsLoading: state.views.channel.loading,
|
||||
myMember: getCurrentChannelMembership(state),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {PropTypes, PureComponent} from 'react';
|
||||
import React, {PropTypes, PureComponent} from 'react';
|
||||
import {View} from 'react-native';
|
||||
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
|
||||
import ChannelLoader from 'app/components/channel_loader';
|
||||
|
||||
export default class LoadTeam extends PureComponent {
|
||||
static propTypes = {
|
||||
notification: PropTypes.object,
|
||||
|
|
@ -17,9 +20,14 @@ export default class LoadTeam extends PureComponent {
|
|||
goToChannelView: PropTypes.func.isRequired,
|
||||
goToNotification: PropTypes.func.isRequired,
|
||||
handleTeamChange: PropTypes.func.isRequired
|
||||
}).isRequired
|
||||
}).isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static navigationProps = {
|
||||
renderBackButton: () => null
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {notification, currentTeam, myMembers, teams} = this.props;
|
||||
const {clearNotification, goToNotification} = this.props.actions;
|
||||
|
|
@ -55,6 +63,10 @@ export default class LoadTeam extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<ChannelLoader theme={this.props.theme}/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,18 +2,22 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
import {goToChannelView} from 'app/actions/views/load_team';
|
||||
import {clearNotification, goToNotification} from 'app/actions/views/root';
|
||||
import {handleTeamChange} from 'app/actions/views/select_team';
|
||||
import {getCurrentTeam} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
|
||||
import navigationSceneConnect from '../navigationSceneConnect';
|
||||
|
||||
import LoadTeam from './load_team.js';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
config: state.entities.general.config,
|
||||
theme: getTheme(state),
|
||||
teamsRequest: state.requests.teams.allTeams,
|
||||
teams: state.entities.teams.teams,
|
||||
currentTeam: getCurrentTeam(state),
|
||||
|
|
@ -33,4 +37,4 @@ function mapDispatchToProps(dispatch) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(LoadTeam);
|
||||
export default navigationSceneConnect(mapStateToProps, mapDispatchToProps)(LoadTeam);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import {SplashScreenTypes} from 'app/constants';
|
|||
import ErrorText from 'app/components/error_text';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import KeyboardLayout from 'app/components/layout/keyboard_layout';
|
||||
import Loading from 'app/components/loading';
|
||||
import TextInputWithLocalizedPlaceholder from 'app/components/text_input_with_localized_placeholder';
|
||||
import {GlobalStyles} from 'app/styles';
|
||||
import {isValidUrl, stripTrailingSlashes} from 'app/utils/url';
|
||||
|
|
@ -116,7 +115,7 @@ export default class SelectServer extends PureComponent {
|
|||
licenseRequest.status === RequestStatus.STARTED;
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading/>;
|
||||
return null;
|
||||
}
|
||||
|
||||
const error = pingRequest.error || configRequest.error || licenseRequest.error;
|
||||
|
|
|
|||
Loading…
Reference in a new issue