mattermost-mobile/app/screens/entry/entry.js
Stan Chan 583d6b74ee Timezone feature (#1456)
* Add automatic and manual timezone support

Update users timezone and scrollTo selected timezone in SelectTimezone coponent

Clean styles for SelectTimezone screen

Add auto-timezone update when login or enter app

Hide timezone feature behind config

Fix requested changes

Parse SupportedTimezone from config

Add localTime and localized post stamps

Add trailing commas

Include all timezone util methods from redux

Remove EnableTimezoneSelection flag

WIP get timezones from server

Pull supportedTimezones from api

Minor fixes

Remove wrapWithPreventDoubleTap

Revert back to react-intl formatDate

Include timeZone prop in FormattedTime

Refactor Timezone row into component

Minor cosmetic changes

Add minimum server support for the timezone feature

Move getSupportedTimezones to componentWillMount

Move autoUpdateTimezone function to handleSuccessfulLogin

Specify user timezone in profile_header

Remove format props from FormattedTime

Add ExperimentalTimezone flag

Replace Client().getServerVersion() with entities.general.serverVersion

Add isTimezoneEnabled helper function

Move isMinimumServerVersion to utils/timezone.js

* Fix style errors

* Remove date-time-format-timezone polyfill

* Feedback changes

* Use timezone selector from redux

* Explicitly pass hour12 props to intl.formatDate

* Update package-lock

* Revert iOS project file changes

* Fix license header

* Include timezone related paths in modulePaths

* Fix license header

* Fix minor issue with rebasing

* Fix issue with getconfig in GeneralSettings

* Update package-lock
2018-06-27 06:55:03 -04:00

302 lines
8.6 KiB
JavaScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
View,
AppState,
Platform,
} from 'react-native';
import DeviceInfo from 'react-native-device-info';
import {Client4} from 'mattermost-redux/client';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {
app,
store,
} from 'app/mattermost';
import {loadFromPushNotification} from 'app/actions/views/root';
import {ViewTypes} from 'app/constants';
import PushNotifications from 'app/push_notifications';
import {stripTrailingSlashes} from 'app/utils/url';
import {wrapWithContextProvider} from 'app/utils/wrap_context_provider';
import ChannelLoader from 'app/components/channel_loader';
import EmptyToolbar from 'app/components/start/empty_toolbar';
import Loading from 'app/components/loading';
import SafeAreaView from 'app/components/safe_area_view';
import StatusBar from 'app/components/status_bar';
const lazyLoadSelectServer = () => {
return require('app/screens/select_server').default;
};
const lazyLoadChannel = () => {
return require('app/screens/channel').default;
};
const lazyLoadPushNotifications = () => {
return require('app/utils/push_notifications').configurePushNotifications;
};
const lazyLoadReplyPushNotifications = () => {
return require('app/utils/push_notifications').onPushNotificationReply;
};
/**
* Entry Component:
* With very little overhead navigate to
* - Login or
* - Channel Component
*
* The idea is to render something to the user as soon as possible
*/
export default class Entry extends PureComponent {
static propTypes = {
config: PropTypes.object,
theme: PropTypes.object,
navigator: PropTypes.object,
isLandscape: PropTypes.bool,
hydrationComplete: PropTypes.bool,
enableTimezone: PropTypes.bool,
deviceTimezone: PropTypes.string,
initializeModules: PropTypes.func.isRequired,
actions: PropTypes.shape({
setDeviceToken: PropTypes.func.isRequired,
}).isRequired,
};
constructor(props) {
super(props);
this.state = {
launchLogin: false,
launchChannel: false,
};
}
componentDidMount() {
Client4.setUserAgent(DeviceInfo.getUserAgent());
this.unsubscribeFromStore = store.subscribe(this.listenForHydration);
EventEmitter.on(ViewTypes.LAUNCH_LOGIN, this.handleLaunchLogin);
EventEmitter.on(ViewTypes.LAUNCH_CHANNEL, this.handleLaunchChannel);
}
componentWillUnmount() {
EventEmitter.off(ViewTypes.LAUNCH_LOGIN, this.handleLaunchLogin);
EventEmitter.off(ViewTypes.LAUNCH_CHANNEL, this.handleLaunchChannel);
}
handleLaunchLogin = (initializeModules) => {
this.setState({launchLogin: true});
if (initializeModules) {
this.props.initializeModules();
}
};
handleLaunchChannel = (initializeModules) => {
this.setState({launchChannel: true});
if (initializeModules) {
this.props.initializeModules();
}
};
listenForHydration = () => {
const {
actions: {
autoUpdateTimezone,
},
enableTimezone,
deviceTimezone,
} = this.props;
const {getState} = store;
const state = getState();
if (!app.isNotificationsConfigured) {
this.configurePushNotifications();
}
if (state.views.root.hydrationComplete) {
this.unsubscribeFromStore();
if (enableTimezone) {
autoUpdateTimezone(deviceTimezone);
}
this.setAppCredentials();
this.setStartupThemes();
this.handleNotification();
if (Platform.OS === 'android') {
this.launchForAndroid();
return;
}
this.launchForiOS();
}
};
configurePushNotifications = () => {
const configureNotifications = lazyLoadPushNotifications();
configureNotifications();
};
setAppCredentials = () => {
const {
actions: {
setDeviceToken,
},
} = this.props;
const {getState} = store;
const state = getState();
const {credentials} = state.entities.general;
const {currentUserId} = state.entities.users;
if (app.deviceToken) {
setDeviceToken(app.deviceToken);
}
if (credentials.token && credentials.url) {
Client4.setToken(credentials.token);
Client4.setUrl(stripTrailingSlashes(credentials.url));
} else if (app.waitForRehydration) {
app.waitForRehydration = false;
}
if (currentUserId) {
Client4.setUserId(currentUserId);
}
app.setAppCredentials(app.deviceToken, currentUserId, credentials.token, credentials.url);
};
setStartupThemes = () => {
const {theme} = this.props;
if (app.toolbarBackground === theme.sidebarHeaderBg) {
return;
}
app.setStartupThemes(
theme.sidebarHeaderBg,
theme.sidebarHeaderTextColor,
theme.centerChannelBg
);
};
handleNotification = async () => {
const notification = PushNotifications.getNotification();
// If notification exists, it means that the app was started through a reply
// and the app was not sitting in the background nor opened
if (notification || app.replyNotificationData) {
const onPushNotificationReply = lazyLoadReplyPushNotifications();
const notificationData = notification || app.replyNotificationData;
const {data, text, badge, completed} = notificationData;
// if the notification has a completed property it means that we are replying to a notification
// and in case it doesn't it means we just opened the notification
if (completed) {
onPushNotificationReply(data, text, badge, completed);
} else {
await store.dispatch(loadFromPushNotification(notification));
}
PushNotifications.resetNotification();
}
};
launchForAndroid = () => {
app.launchApp();
};
launchForiOS = () => {
const appNotActive = AppState.currentState !== 'active';
if (appNotActive) {
// for iOS replying from push notification starts the app in the background
app.setShouldRelaunchWhenActive(true);
} else {
app.launchApp();
}
};
renderLogin = () => {
const SelectServer = lazyLoadSelectServer();
const props = {
allowOtherServers: app.allowOtherServers,
navigator: this.props.navigator,
};
return wrapWithContextProvider(SelectServer)(props);
};
renderChannel = () => {
const ChannelScreen = lazyLoadChannel();
const props = {
navigator: this.props.navigator,
};
return wrapWithContextProvider(ChannelScreen, false)(props);
};
render() {
const {
navigator,
isLandscape,
} = this.props;
if (this.state.launchLogin) {
return this.renderLogin();
}
if (this.state.launchChannel) {
return this.renderChannel();
}
let toolbar = null;
let loading = null;
const backgroundColor = app.appBackground ? app.appBackground : '#ffff';
if (app.token && app.toolbarBackground) {
const toolbarTheme = {
sidebarHeaderBg: app.toolbarBackground,
sidebarHeaderTextColor: app.toolbarTextColor,
};
toolbar = (
<View>
<StatusBar headerColor={app.toolbarBackground}/>
<EmptyToolbar
theme={toolbarTheme}
isLandscape={isLandscape}
/>
</View>
);
loading = (
<ChannelLoader
backgroundColor={backgroundColor}
channelIsLoading={true}
/>
);
} else {
loading = <Loading/>;
}
return (
<SafeAreaView
navBarBackgroundColor={app.toolbarBackground}
backgroundColor={backgroundColor}
navigator={navigator}
>
{toolbar}
{loading}
</SafeAreaView>
);
}
}