* Update screens * Update login tests * Remove done * Fix failing tests * Update screens and components * Check styles fix * Update tests * Prevent setState call after component unmounts * Add empty setButtons func to dummy navigator * Remove platform check * Remove Platform import * Update react-native-navigation version * Add separate showModalOverCurrentContext function * check-style fixes * Remove overriding of AppDelegate's window * Fix modal over current context animation * Add showSearchModal navigation action * Check-style fix * Address review comments * Update SettingsSidebar and children * Update EditProfile screen * Update SettingsSidebar * Keep track of latest componentId to appear * Track componentId in state to use in navigation actions * Update FlaggedPosts and children * Update RecentMentions * Update Settings * Store componentIds in ephemeral store * Update AttachmentButton * Remove unnecessary dismissModal * Fix typo * Upate NotificationSettings * Update NotificationSettingsAutoResponder * Update NotificationSettingsMentions * Update NotificationSettingsMobile * Update CreateChannel and children * Update MoreChannels * Update ChannelPeek and children * Update ChannelNavBar and children * Update ChannelPostList and children * Update ChannelInfo and children * Update ChannelAddMembers * Update ChannelMembers * Update EditChannel * Fix setting of top bar buttons * Update Channel screen and children * Update PinnedPosts * Update LongPost * Update PostOptions * Update PostTextboxBase * Update EditPost * Check-styles fix * Update DisplaySettings * Update Timezone * Update SelectTimezone * Update IntlWrapper and Notification * Update InteractiveDialog and children * Update ExpandedAnnouncementBanner * Update ClientUpgradeListener * Update MoreDirectMessages * Update SelectorScreen * Update UserProfile * Update Thread * Update About * Update ImagePreview * Update TextPreview * Update AddReaction * Update ReactionList and children * Update Code * Update TermsOfService * Update Permalink * Update Permalink * Update ErrorTeamsList * Update Search * Remove navigator prop * Fix setButtons calls * Remove navigationComponentId * Fix test * Handle in-app notifs in global event handler * Fix in-app notification handling * Fix typo * Fix tests * Auto dismiss in-app notif after 5 seconds * Fix typo * Update overlay dismissal * Animate in-app notif dismissal and allow gesture dismissal
264 lines
9.4 KiB
JavaScript
264 lines
9.4 KiB
JavaScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {Alert, AppState, Dimensions, Linking, NativeModules, Platform} from 'react-native';
|
|
import DeviceInfo from 'react-native-device-info';
|
|
import semver from 'semver';
|
|
|
|
import {setAppState, setServerVersion} from 'mattermost-redux/actions/general';
|
|
import {loadMe, logout} from 'mattermost-redux/actions/users';
|
|
import {close as closeWebSocket} from 'mattermost-redux/actions/websocket';
|
|
import {Client4} from 'mattermost-redux/client';
|
|
import {General} from 'mattermost-redux/constants';
|
|
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
|
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
|
|
|
import {setDeviceDimensions, setDeviceOrientation, setDeviceAsTablet, setStatusBarHeight} from 'app/actions/device';
|
|
import {selectDefaultChannel} from 'app/actions/views/channel';
|
|
import {showOverlay} from 'app/actions/navigation';
|
|
import {loadConfigAndLicense, setDeepLinkURL, startDataCleanup} from 'app/actions/views/root';
|
|
import {NavigationTypes, ViewTypes} from 'app/constants';
|
|
import {getTranslations} from 'app/i18n';
|
|
import mattermostManaged from 'app/mattermost_managed';
|
|
import PushNotifications from 'app/push_notifications';
|
|
import {getCurrentLocale} from 'app/selectors/i18n';
|
|
import {t} from 'app/utils/i18n';
|
|
import {deleteFileCache} from 'app/utils/file';
|
|
|
|
import LocalConfig from 'assets/config';
|
|
|
|
import {getAppCredentials, removeAppCredentials} from './credentials';
|
|
import emmProvider from './emm_provider';
|
|
|
|
const {StatusBarManager} = NativeModules;
|
|
const PROMPT_IN_APP_PIN_CODE_AFTER = 5 * 1000;
|
|
|
|
class GlobalEventHandler {
|
|
constructor() {
|
|
EventEmitter.on(NavigationTypes.NAVIGATION_RESET, this.onLogout);
|
|
EventEmitter.on(NavigationTypes.RESTART_APP, this.onRestartApp);
|
|
EventEmitter.on(General.SERVER_VERSION_CHANGED, this.onServerVersionChanged);
|
|
EventEmitter.on(General.CONFIG_CHANGED, this.onServerConfigChanged);
|
|
EventEmitter.on(General.SWITCH_TO_DEFAULT_CHANNEL, this.onSwitchToDefaultChannel);
|
|
this.turnOnInAppNotificationHandling();
|
|
Dimensions.addEventListener('change', this.onOrientationChange);
|
|
AppState.addEventListener('change', this.onAppStateChange);
|
|
Linking.addEventListener('url', this.onDeepLink);
|
|
}
|
|
|
|
appActive = async () => {
|
|
this.turnOnInAppNotificationHandling();
|
|
|
|
// if the app is being controlled by an EMM provider
|
|
if (emmProvider.enabled && emmProvider.inAppPinCode) {
|
|
const authExpired = (Date.now() - emmProvider.inBackgroundSince) >= PROMPT_IN_APP_PIN_CODE_AFTER;
|
|
|
|
// Once the app becomes active we check if the device needs to have a passcode set
|
|
const prompt = emmProvider.inBackgroundSince && authExpired; // if more than 5 minutes have passed prompt for passcode
|
|
await emmProvider.handleAuthentication(this.store, prompt);
|
|
}
|
|
|
|
emmProvider.inBackgroundSince = null;
|
|
};
|
|
|
|
appInactive = () => {
|
|
this.turnOffInAppNotificationHandling();
|
|
|
|
const {dispatch} = this.store;
|
|
|
|
// When the app is sent to the background we set the time when that happens
|
|
// and perform a data clean up to improve on performance
|
|
emmProvider.inBackgroundSince = Date.now();
|
|
|
|
dispatch(startDataCleanup());
|
|
};
|
|
|
|
configure = (opts) => {
|
|
this.store = opts.store;
|
|
this.launchApp = opts.launchApp;
|
|
|
|
const window = Dimensions.get('window');
|
|
this.onOrientationChange({window});
|
|
|
|
this.StatusBarSizeIOS = require('react-native-status-bar-size');
|
|
if (Platform.OS === 'ios') {
|
|
this.StatusBarSizeIOS.addEventListener('willChange', this.onStatusBarHeightChange);
|
|
|
|
StatusBarManager.getHeight(
|
|
(data) => {
|
|
this.onStatusBarHeightChange(data.height);
|
|
}
|
|
);
|
|
}
|
|
|
|
this.JavascriptAndNativeErrorHandler = require('app/utils/error_handling').default;
|
|
this.JavascriptAndNativeErrorHandler.initializeErrorHandling(this.store);
|
|
|
|
mattermostManaged.addEventListener('managedConfigDidChange', this.onManagedConfigurationChange);
|
|
};
|
|
|
|
configureAnalytics = (config) => {
|
|
const initAnalytics = require('app/utils/segment').init;
|
|
|
|
if (!__DEV__ && config && config.DiagnosticsEnabled === 'true' && config.DiagnosticId && LocalConfig.SegmentApiKey) {
|
|
initAnalytics(config);
|
|
} else {
|
|
global.analytics = null;
|
|
}
|
|
};
|
|
|
|
onAppStateChange = (appState) => {
|
|
const isActive = appState === 'active';
|
|
const isBackground = appState === 'background';
|
|
|
|
if (this.store) {
|
|
this.store.dispatch(setAppState(isActive));
|
|
}
|
|
|
|
if (isActive && emmProvider.previousAppState === 'background') {
|
|
this.appActive();
|
|
} else if (isBackground) {
|
|
this.appInactive();
|
|
}
|
|
|
|
emmProvider.previousAppState = appState;
|
|
};
|
|
|
|
onDeepLink = (event) => {
|
|
const {url} = event;
|
|
this.store.dispatch(setDeepLinkURL(url));
|
|
};
|
|
|
|
onManagedConfigurationChange = () => {
|
|
emmProvider.handleManagedConfig(this.store, true);
|
|
};
|
|
|
|
onServerConfigChanged = (config) => {
|
|
this.configureAnalytics(config);
|
|
};
|
|
|
|
onLogout = async () => {
|
|
this.store.dispatch(closeWebSocket(false));
|
|
this.store.dispatch(setServerVersion(''));
|
|
deleteFileCache();
|
|
removeAppCredentials();
|
|
|
|
PushNotifications.setApplicationIconBadgeNumber(0);
|
|
PushNotifications.cancelAllLocalNotifications(); // TODO: Only cancel the notification that belongs to this server
|
|
|
|
if (this.launchApp) {
|
|
this.launchApp();
|
|
}
|
|
};
|
|
|
|
onOrientationChange = (dimensions) => {
|
|
if (this.store) {
|
|
const {dispatch} = this.store;
|
|
if (DeviceInfo.isTablet()) {
|
|
dispatch(setDeviceAsTablet());
|
|
}
|
|
|
|
const {height, width} = dimensions.window;
|
|
const orientation = height > width ? 'PORTRAIT' : 'LANDSCAPE';
|
|
|
|
dispatch(setDeviceOrientation(orientation));
|
|
dispatch(setDeviceDimensions(height, width));
|
|
}
|
|
};
|
|
|
|
onRestartApp = async () => {
|
|
await this.store.dispatch(loadConfigAndLicense());
|
|
await this.store.dispatch(loadMe());
|
|
|
|
const window = Dimensions.get('window');
|
|
this.onOrientationChange({window});
|
|
|
|
if (Platform.OS === 'ios') {
|
|
StatusBarManager.getHeight(
|
|
(data) => {
|
|
this.onStatusBarHeightChange(data.height);
|
|
}
|
|
);
|
|
}
|
|
|
|
if (this.launchApp) {
|
|
this.launchApp();
|
|
}
|
|
};
|
|
|
|
onServerVersionChanged = async (serverVersion) => {
|
|
const {dispatch, getState} = this.store;
|
|
const state = getState();
|
|
const version = serverVersion.match(/^[0-9]*.[0-9]*.[0-9]*(-[a-zA-Z0-9.-]*)?/g)[0];
|
|
const locale = getCurrentLocale(state);
|
|
const translations = getTranslations(locale);
|
|
|
|
if (serverVersion) {
|
|
if (semver.valid(version) && semver.lt(version, LocalConfig.MinServerVersion)) {
|
|
Alert.alert(
|
|
translations[t('mobile.server_upgrade.title')],
|
|
translations[t('mobile.server_upgrade.description')],
|
|
[{
|
|
text: translations[t('mobile.server_upgrade.button')],
|
|
onPress: this.serverUpgradeNeeded,
|
|
}],
|
|
{cancelable: false}
|
|
);
|
|
} else if (state.entities.users && state.entities.users.currentUserId) {
|
|
dispatch(setServerVersion(serverVersion));
|
|
const data = await dispatch(loadConfigAndLicense());
|
|
this.configureAnalytics(data.config);
|
|
}
|
|
}
|
|
};
|
|
|
|
onStatusBarHeightChange = (nextStatusBarHeight) => {
|
|
this.store.dispatch(setStatusBarHeight(nextStatusBarHeight));
|
|
};
|
|
|
|
onSwitchToDefaultChannel = (teamId) => {
|
|
this.store.dispatch(selectDefaultChannel(teamId));
|
|
};
|
|
|
|
serverUpgradeNeeded = async () => {
|
|
const {dispatch} = this.store;
|
|
|
|
dispatch(setServerVersion(''));
|
|
Client4.serverVersion = '';
|
|
PushNotifications.setApplicationIconBadgeNumber(0);
|
|
PushNotifications.cancelAllLocalNotifications(); // TODO: Only cancel the notification that belongs to this server
|
|
|
|
const credentials = await getAppCredentials();
|
|
|
|
if (credentials) {
|
|
dispatch(logout());
|
|
}
|
|
};
|
|
|
|
turnOnInAppNotificationHandling = () => {
|
|
EventEmitter.on(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
|
|
}
|
|
|
|
turnOffInAppNotificationHandling = () => {
|
|
EventEmitter.off(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
|
|
}
|
|
|
|
handleInAppNotification = (notification) => {
|
|
const {data} = notification;
|
|
const {dispatch, getState} = this.store;
|
|
const state = getState();
|
|
const currentChannelId = getCurrentChannelId(state);
|
|
|
|
if (data && data.channel_id !== currentChannelId) {
|
|
const screen = 'Notification';
|
|
const passProps = {
|
|
notification,
|
|
};
|
|
|
|
EventEmitter.emit(NavigationTypes.NAVIGATION_SHOW_OVERLAY);
|
|
dispatch(showOverlay(screen, passProps));
|
|
}
|
|
};
|
|
}
|
|
|
|
export default new GlobalEventHandler();
|