diff --git a/Makefile b/Makefile
index fc9716e35..99d340836 100644
--- a/Makefile
+++ b/Makefile
@@ -20,8 +20,11 @@ POD := $(shell command -v pod 2> /dev/null)
.podinstall:
ifdef POD
- @echo Getting CocoaPods dependencies;
+ @echo Getting Cocoapods dependencies;
@cd ios && pod install;
+else
+ @echo "Cocoapods is not installed https://cocoapods.org/"
+ @exit 1
endif
@touch $@
@@ -117,6 +120,7 @@ post-install:
@sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-messageformat/package.json
@sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-relativeformat/package.json
@sed -i'' -e 's|"./locale-data/complete.js": false|"./locale-data/complete.js": "./locale-data/complete.js"|g' node_modules/intl/package.json
+ @sed -i'' -e 's|auto("auto", Configuration.ORIENTATION_UNDEFINED, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);|auto("auto", Configuration.ORIENTATION_UNDEFINED, ActivityInfo.SCREEN_ORIENTATION_SENSOR);|g' node_modules/react-native-navigation/android/app/src/main/java/com/reactnativenavigation/params/Orientation.java
start-packager:
@if [ $(shell ps -e | grep -i "cli.js start" | grep -civ grep) -eq 0 ]; then \
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index be5c57796..ba36bb1a4 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -53,6 +53,9 @@
+
diff --git a/android/app/src/main/java/com/mattermost/rnbeta/NotificationsLifecycleFacade.java b/android/app/src/main/java/com/mattermost/rnbeta/NotificationsLifecycleFacade.java
index 8faba1f5a..66568d287 100644
--- a/android/app/src/main/java/com/mattermost/rnbeta/NotificationsLifecycleFacade.java
+++ b/android/app/src/main/java/com/mattermost/rnbeta/NotificationsLifecycleFacade.java
@@ -1,6 +1,7 @@
package com.mattermost.rnbeta;
import android.app.Activity;
+import android.content.pm.ActivityInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.RestrictionsManager;
@@ -10,6 +11,7 @@ import android.os.Bundle;
import android.util.Log;
import android.util.ArraySet;
import android.view.WindowManager.LayoutParams;
+import android.content.res.Configuration;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
@@ -143,6 +145,15 @@ public class NotificationsLifecycleFacade extends ActivityCallbacks implements A
mListeners.remove(listener);
}
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (mVisibleActivity != null) {
+ Intent intent = new Intent("onConfigurationChanged");
+ intent.putExtra("newConfig", newConfig);
+ mVisibleActivity.sendBroadcast(intent);
+ }
+ }
+
private synchronized void switchToVisible(Activity activity) {
if (mVisibleActivity == null) {
mVisibleActivity = activity;
diff --git a/app/actions/device/index.js b/app/actions/device/index.js
new file mode 100644
index 000000000..faf5f214d
--- /dev/null
+++ b/app/actions/device/index.js
@@ -0,0 +1,55 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {Dimensions} from 'react-native';
+
+import {DeviceTypes} from 'app/constants';
+
+export function calculateDeviceDimensions() {
+ const {height, width} = Dimensions.get('window');
+ return {
+ type: DeviceTypes.DEVICE_DIMENSIONS_CHANGED,
+ data: {
+ deviceHeight: height,
+ deviceWidth: width
+ }
+ };
+}
+
+export function connection(isOnline) {
+ return async (dispatch, getState) => {
+ dispatch({
+ type: DeviceTypes.CONNECTION_CHANGED,
+ data: isOnline
+ }, getState);
+ };
+}
+
+export function setStatusBarHeight(height = 20) {
+ return {
+ type: DeviceTypes.STATUSBAR_HEIGHT_CHANGED,
+ data: height
+ };
+}
+
+export function setDeviceOrientation(orientation) {
+ return {
+ type: DeviceTypes.DEVICE_ORIENTATION_CHANGED,
+ data: orientation
+ };
+}
+
+export function setDeviceAsTablet() {
+ return {
+ type: DeviceTypes.DEVICE_TYPE_CHANGED,
+ data: true
+ };
+}
+
+export default {
+ calculateDeviceDimensions,
+ connection,
+ setDeviceOrientation,
+ setDeviceAsTablet,
+ setStatusBarHeight
+};
diff --git a/app/actions/views/connection.js b/app/actions/views/connection.js
deleted file mode 100644
index 67c9d9e09..000000000
--- a/app/actions/views/connection.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {ViewTypes} from 'app/constants';
-
-export function connection(isOnline) {
- return async (dispatch, getState) => {
- dispatch({
- type: ViewTypes.CONNECTION_CHANGED,
- data: isOnline
- }, getState);
- };
-}
diff --git a/app/actions/views/root.js b/app/actions/views/root.js
index e60dfb8ba..9caed27b4 100644
--- a/app/actions/views/root.js
+++ b/app/actions/views/root.js
@@ -68,13 +68,6 @@ export function goToNotification(notification) {
};
}
-export function setStatusBarHeight(height = 20) {
- return {
- type: ViewTypes.STATUSBAR_HEIGHT_CHANGED,
- data: height
- };
-}
-
export function purgeOfflineStore() {
return {type: General.OFFLINE_STORE_PURGE};
}
@@ -84,5 +77,5 @@ export default {
queueNotification,
clearNotification,
goToNotification,
- setStatusBarHeight
+ purgeOfflineStore
};
diff --git a/app/components/channel_drawer/channel_drawer.js b/app/components/channel_drawer/channel_drawer.js
index 4b67a273f..9c254c69d 100644
--- a/app/components/channel_drawer/channel_drawer.js
+++ b/app/components/channel_drawer/channel_drawer.js
@@ -22,6 +22,7 @@ import {General} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
const DRAWER_INITIAL_OFFSET = 40;
+const DRAWER_LANDSCAPE_OFFSET = 150;
export default class ChannelDrawer extends PureComponent {
static propTypes = {
@@ -39,19 +40,29 @@ export default class ChannelDrawer extends PureComponent {
currentChannelId: PropTypes.string.isRequired,
currentTeamId: PropTypes.string.isRequired,
currentUserId: PropTypes.string.isRequired,
+ isLandscape: PropTypes.bool.isRequired,
+ isTablet: PropTypes.bool.isRequired,
intl: PropTypes.object.isRequired,
navigator: PropTypes.object,
teamsCount: PropTypes.number.isRequired,
theme: PropTypes.object.isRequired
};
- state = {
- openDrawer: false,
- openDrawerOffset: DRAWER_INITIAL_OFFSET
- };
-
swiperIndex = 1;
+ constructor(props) {
+ super(props);
+
+ let openDrawerOffset = DRAWER_INITIAL_OFFSET;
+ if (props.isLandscape || props.isTablet) {
+ openDrawerOffset = DRAWER_LANDSCAPE_OFFSET;
+ }
+ this.state = {
+ openDrawer: false,
+ openDrawerOffset
+ };
+ }
+
componentWillMount() {
this.props.actions.getTeams();
}
@@ -62,6 +73,17 @@ export default class ChannelDrawer extends PureComponent {
BackHandler.addEventListener('hardwareBackPress', this.handleAndroidBack);
}
+ componentWillReceiveProps(nextProps) {
+ const {isLandscape, isTablet} = this.props;
+ if (nextProps.isLandscape !== isLandscape || nextProps.isTablet || isTablet) {
+ let openDrawerOffset = DRAWER_INITIAL_OFFSET;
+ if (nextProps.isLandscape || nextProps.isTablet) {
+ openDrawerOffset = DRAWER_LANDSCAPE_OFFSET;
+ }
+ this.setState({openDrawerOffset});
+ }
+ }
+
componentWillUnmount() {
EventEmitter.off('open_channel_drawer', this.openChannelDrawer);
EventEmitter.off('close_channel_drawer', this.closeChannelDrawer);
@@ -81,6 +103,10 @@ export default class ChannelDrawer extends PureComponent {
this.setState({openDrawer: false});
};
+ drawerSwiperRef = (ref) => {
+ this.drawerSwiper = ref;
+ };
+
handleDrawerClose = () => {
this.resetDrawer();
@@ -93,7 +119,7 @@ export default class ChannelDrawer extends PureComponent {
};
handleDrawerOpen = () => {
- if (this.state.openDrawerOffset === DRAWER_INITIAL_OFFSET) {
+ if (this.state.openDrawerOffset !== 0) {
Keyboard.dismiss();
}
};
@@ -210,8 +236,13 @@ export default class ChannelDrawer extends PureComponent {
onSearchEnds = () => {
//hack to update the drawer when the offset changes
+ const {isLandscape, isTablet} = this.props;
this.refs.drawer._syncAfterUpdate = true; //eslint-disable-line no-underscore-dangle
- this.setState({openDrawerOffset: DRAWER_INITIAL_OFFSET});
+ let openDrawerOffset = DRAWER_INITIAL_OFFSET;
+ if (isLandscape || isTablet) {
+ openDrawerOffset = DRAWER_LANDSCAPE_OFFSET;
+ }
+ this.setState({openDrawerOffset});
};
onSearchStart = () => {
@@ -221,13 +252,13 @@ export default class ChannelDrawer extends PureComponent {
showTeams = () => {
if (this.swiperIndex === 1 && this.props.teamsCount > 1) {
- this.refs.swiper.showTeamsPage();
+ this.drawerSwiper.getWrappedInstance().showTeamsPage();
}
};
resetDrawer = () => {
if (this.swiperIndex !== 1) {
- this.refs.swiper.resetPage();
+ this.drawerSwiper.getWrappedInstance().resetPage();
}
};
@@ -242,7 +273,7 @@ export default class ChannelDrawer extends PureComponent {
openDrawerOffset
} = this.state;
- const showTeams = openDrawerOffset === DRAWER_INITIAL_OFFSET && teamsCount > 1;
+ const showTeams = openDrawerOffset !== 0 && teamsCount > 1;
const teams = (
@@ -268,7 +299,7 @@ export default class ChannelDrawer extends PureComponent {
return (
true,
+ openDrawerOffset: 0
+ };
+
+ state = {
+ index: 1
+ };
+
+ componentWillReceiveProps(nextProps) {
+ if (this.refs.swiper) {
+ if (nextProps.openDrawerOffset !== this.props.openDrawerOffset || nextProps.isLandscape !== this.props.isLandscape) {
+ this.refs.swiper.initialRender = true;
+ if (this.state.index === 1) {
+ this.resetPage();
+ }
+ }
+ }
+ }
+
+ swiperPageSelected = (e, state, context) => {
+ this.props.onPageSelected(context.state.index);
+ this.setState({index: context.state.index});
+ };
+
+ showTeamsPage = () => {
+ this.refs.swiper.scrollBy(-1, true);
+ };
+
+ resetPage = () => {
+ this.refs.swiper.scrollBy(1, false);
+ };
+
+ render() {
+ const {
+ children,
+ deviceHeight,
+ deviceWidth,
+ openDrawerOffset,
+ showTeams,
+ theme
+ } = this.props;
+
+ const pagination = {bottom: 0};
+ if (showTeams) {
+ pagination.bottom = 0;
+ }
+
+ return (
+
+ {children}
+
+ );
+ }
+}
diff --git a/app/components/channel_drawer/drawer_swipper/index.js b/app/components/channel_drawer/drawer_swipper/index.js
index faea416c3..9b2bc08d2 100644
--- a/app/components/channel_drawer/drawer_swipper/index.js
+++ b/app/components/channel_drawer/drawer_swipper/index.js
@@ -1,82 +1,18 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import React, {PureComponent} from 'react';
-import PropTypes from 'prop-types';
-import {Dimensions} from 'react-native';
-import Swiper from 'react-native-swiper';
+import {connect} from 'react-redux';
-import {changeOpacity} from 'app/utils/theme';
+import {getDimensions, isLandscape} from 'app/selectors/device';
-export default class DrawerSwiper extends PureComponent {
- static propTypes = {
- children: PropTypes.node.isRequired,
- onPageSelected: PropTypes.func,
- openDrawerOffset: PropTypes.number,
- showTeams: PropTypes.bool.isRequired,
- theme: PropTypes.object.isRequired
+import DraweSwiper from './drawer_swiper';
+
+function mapStateToProps(state, ownProps) {
+ return {
+ ...ownProps,
+ ...getDimensions(state),
+ isLandscape: isLandscape(state)
};
-
- static defaultProps = {
- onPageSelected: () => true,
- openDrawerOffset: 0
- };
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.openDrawerOffset !== this.props.openDrawerOffset && this.refs.swiper) {
- this.refs.swiper.initialRender = true;
- }
- }
-
- swiperPageSelected = (e, state, context) => {
- this.props.onPageSelected(context.state.index);
- };
-
- showTeamsPage = () => {
- this.refs.swiper.scrollBy(-1, true);
- };
-
- resetPage = () => {
- this.refs.swiper.scrollBy(1, false);
- };
-
- render() {
- const {
- children,
- openDrawerOffset,
- showTeams,
- theme
- } = this.props;
-
- const pagination = {bottom: 0};
- if (showTeams) {
- pagination.bottom = 0;
- }
-
- // Get the dimensions here so when the orientation changes we get the right dimensions
- const {height: deviceHeight, width: deviceWidth} = Dimensions.get('window');
-
- return (
-
- {children}
-
- );
- }
}
+
+export default connect(mapStateToProps, null, null, {withRef: true})(DraweSwiper);
diff --git a/app/components/channel_drawer/index.js b/app/components/channel_drawer/index.js
index 97da0ed02..7858984e6 100644
--- a/app/components/channel_drawer/index.js
+++ b/app/components/channel_drawer/index.js
@@ -11,6 +11,7 @@ import {getCurrentTeamId, getTeamMemberships} from 'mattermost-redux/selectors/e
import {handleSelectChannel, setChannelDisplayName, setChannelLoading} from 'app/actions/views/channel';
import {makeDirectChannel} from 'app/actions/views/more_dms';
+import {isLandscape, isTablet} from 'app/selectors/device';
import {getTheme} from 'app/selectors/preferences';
import ChannelDrawer from './channel_drawer.js';
@@ -23,6 +24,8 @@ function mapStateToProps(state, ownProps) {
currentTeamId: getCurrentTeamId(state),
currentChannelId: getCurrentChannelId(state),
currentUserId,
+ isLandscape: isLandscape(state),
+ isTablet: isTablet(state),
teamsCount: Object.keys(getTeamMemberships(state)).length,
theme: getTheme(state)
};
diff --git a/app/components/emoji_picker/emoji_picker.js b/app/components/emoji_picker/emoji_picker.js
index d23dd7986..27c22dd5e 100644
--- a/app/components/emoji_picker/emoji_picker.js
+++ b/app/components/emoji_picker/emoji_picker.js
@@ -5,7 +5,6 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {injectIntl, intlShape} from 'react-intl';
import {
- Dimensions,
SectionList,
TouchableOpacity,
View
@@ -17,7 +16,6 @@ import SearchBar from 'app/components/search_bar';
import {emptyFunction} from 'app/utils/general';
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
-const {width: deviceWidth} = Dimensions.get('window');
const EMOJI_SIZE = 30;
const EMOJI_GUTTER = 7.5;
const SECTION_MARGIN = 15;
@@ -25,6 +23,7 @@ const SECTION_MARGIN = 15;
class EmojiPicker extends PureComponent {
static propTypes = {
emojis: PropTypes.array.isRequired,
+ deviceWidth: PropTypes.number.isRequired,
intl: intlShape.isRequired,
onEmojiPress: PropTypes.func,
theme: PropTypes.object.isRequired
@@ -43,10 +42,19 @@ class EmojiPicker extends PureComponent {
this.state = {
emojis: props.emojis,
- searchTerm: ''
+ searchTerm: '',
+ width: props.deviceWidth - (SECTION_MARGIN * 2)
};
}
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.deviceWidth !== this.props.deviceWidth) {
+ this.setState({
+ width: nextProps.deviceWidth - (SECTION_MARGIN * 2)
+ });
+ }
+ }
+
changeSearchTerm = (text) => {
this.setState({
searchTerm: text
@@ -67,11 +75,11 @@ class EmojiPicker extends PureComponent {
emojis: this.props.emojis,
searchTerm: ''
});
- }
+ };
filterEmojiAliases = (aliases, searchTerm) => {
return aliases.findIndex((alias) => alias.includes(searchTerm)) !== -1;
- }
+ };
searchEmojis = (searchTerm) => {
const {emojis} = this.props;
@@ -106,7 +114,7 @@ class EmojiPicker extends PureComponent {
});
return nextEmojis;
- }
+ };
renderSectionHeader = ({section}) => {
const {theme} = this.props;
@@ -121,7 +129,7 @@ class EmojiPicker extends PureComponent {
/>
);
- }
+ };
renderEmojis = (emojis, index) => {
const {theme} = this.props;
@@ -157,13 +165,13 @@ class EmojiPicker extends PureComponent {
})}
);
- }
+ };
renderItem = ({item}) => {
const {theme} = this.props;
const styles = getStyleSheetFromTheme(theme);
- const numColumns = Number(((deviceWidth - (SECTION_MARGIN * 2)) / (EMOJI_SIZE + (EMOJI_GUTTER * 2))).toFixed(0));
+ const numColumns = Number((this.state.width / (EMOJI_SIZE + (EMOJI_GUTTER * 2))).toFixed(0));
const slices = item.items.reduce((slice, emoji, emojiIndex) => {
if (emojiIndex % numColumns === 0 && emojiIndex !== 0) {
@@ -180,7 +188,7 @@ class EmojiPicker extends PureComponent {
{slices.map(this.renderEmojis)}
);
- }
+ };
render() {
const {intl, theme} = this.props;
@@ -253,8 +261,7 @@ const getStyleSheetFromTheme = makeStyleSheetFromTheme((theme) => {
marginRight: 0
},
listView: {
- backgroundColor: theme.centerChannelBg,
- width: deviceWidth - (SECTION_MARGIN * 2)
+ backgroundColor: theme.centerChannelBg
},
searchBar: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
diff --git a/app/components/emoji_picker/index.js b/app/components/emoji_picker/index.js
index 38d05a9ef..e43943835 100644
--- a/app/components/emoji_picker/index.js
+++ b/app/components/emoji_picker/index.js
@@ -6,6 +6,7 @@ import {createSelector} from 'reselect';
import {getCustomEmojisByName} from 'mattermost-redux/selectors/entities/emojis';
+import {getDimensions} from 'app/selectors/device';
import {getTheme} from 'app/selectors/preferences';
import {CategoryNames, Emojis, EmojiIndicesByCategory} from 'app/utils/emojis';
@@ -98,9 +99,11 @@ const getEmojisBySection = createSelector(
function mapStateToProps(state) {
const emojis = getEmojisBySection(state);
+ const {deviceWidth} = getDimensions(state);
return {
emojis,
+ deviceWidth,
theme: getTheme(state)
};
}
diff --git a/app/components/file_upload_preview/file_upload_preview.js b/app/components/file_upload_preview/file_upload_preview.js
index 9844c00a0..246c9bf04 100644
--- a/app/components/file_upload_preview/file_upload_preview.js
+++ b/app/components/file_upload_preview/file_upload_preview.js
@@ -4,7 +4,6 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
- Dimensions,
Platform,
ScrollView,
StyleSheet,
@@ -17,8 +16,6 @@ import FileAttachmentImage from 'app/components/file_attachment_list/file_attach
import FileAttachmentIcon from 'app/components/file_attachment_list/file_attachment_icon';
import KeyboardLayout from 'app/components/layout/keyboard_layout';
-const {height: deviceHeight} = Dimensions.get('window');
-
export default class FileUploadPreview extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
@@ -29,6 +26,7 @@ export default class FileUploadPreview extends PureComponent {
channelId: PropTypes.string.isRequired,
channelIsLoading: PropTypes.bool,
createPostRequestStatus: PropTypes.string.isRequired,
+ deviceHeight: PropTypes.number.isRequired,
fetchCache: PropTypes.object.isRequired,
files: PropTypes.array.isRequired,
inputHeight: PropTypes.number.isRequired,
@@ -98,7 +96,7 @@ export default class FileUploadPreview extends PureComponent {
);
});
- }
+ };
render() {
if (this.props.channelIsLoading || (!this.props.files.length && !this.props.filesUploadingForCurrentChannel)) {
@@ -107,7 +105,7 @@ export default class FileUploadPreview extends PureComponent {
return (
-
+
= 2;
return {
appState,
isConnecting,
- isOnline: connection,
+ isOnline: getConnection(state),
webSocketStatus,
...ownProps
};
diff --git a/app/components/post_body_additional_content/index.js b/app/components/post_body_additional_content/index.js
index 5f5c95dbf..03700017d 100644
--- a/app/components/post_body_additional_content/index.js
+++ b/app/components/post_body_additional_content/index.js
@@ -12,6 +12,7 @@ import {getOpenGraphMetadataForUrl} from 'mattermost-redux/selectors/entities/po
import {getBool} from 'mattermost-redux/selectors/entities/preferences';
import {ViewTypes} from 'app/constants';
+import {getDimensions} from 'app/selectors/device';
import {getTheme} from 'app/selectors/preferences';
import {extractFirstLink} from 'app/utils/url';
@@ -24,6 +25,7 @@ function mapStateToProps(state, ownProps) {
return {
...ownProps,
+ ...getDimensions(state),
config,
link,
openGraphData: getOpenGraphMetadataForUrl(state, link),
diff --git a/app/components/post_body_additional_content/post_body_additional_content.js b/app/components/post_body_additional_content/post_body_additional_content.js
index 6b27be4fd..8fa69f9af 100644
--- a/app/components/post_body_additional_content/post_body_additional_content.js
+++ b/app/components/post_body_additional_content/post_body_additional_content.js
@@ -4,7 +4,6 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
- Dimensions,
Image,
Linking,
Platform,
@@ -12,7 +11,7 @@ import {
TouchableWithoutFeedback,
View
} from 'react-native';
-import Orientation from 'react-native-orientation';
+
import {YouTubeStandaloneAndroid, YouTubeStandaloneIOS} from 'react-native-youtube';
import youTubeVideoId from 'youtube-video-id';
import youtubePlayIcon from 'assets/images/icons/youtube-play-icon.png';
@@ -29,6 +28,8 @@ export default class PostBodyAdditionalContent extends PureComponent {
baseTextStyle: CustomPropTypes.Style,
blockStyles: PropTypes.object,
config: PropTypes.object,
+ deviceHeight: PropTypes.number.isRequired,
+ deviceWidth: PropTypes.number.isRequired,
link: PropTypes.string,
message: PropTypes.string.isRequired,
navigator: PropTypes.object.isRequired,
@@ -71,9 +72,10 @@ export default class PostBodyAdditionalContent extends PureComponent {
}
calculateDimensions = (width, height) => {
- const {width: deviceWidth} = Dimensions.get('window');
+ const {deviceHeight, deviceWidth} = this.props;
let maxHeight = MAX_IMAGE_HEIGHT;
- let maxWidth = deviceWidth - 68;
+ const deviceSize = deviceWidth > deviceHeight ? deviceHeight : deviceWidth;
+ let maxWidth = deviceSize - 78;
if (height <= MAX_IMAGE_HEIGHT) {
maxHeight = height;
@@ -218,11 +220,8 @@ export default class PostBodyAdditionalContent extends PureComponent {
const {link} = this.props;
const videoId = youTubeVideoId(link);
- Orientation.unlockAllOrientations();
if (Platform.OS === 'ios') {
- YouTubeStandaloneIOS.playVideo(videoId).then(() => {
- Orientation.lockToPortrait();
- });
+ YouTubeStandaloneIOS.playVideo(videoId);
} else {
const {config} = this.props;
@@ -231,12 +230,9 @@ export default class PostBodyAdditionalContent extends PureComponent {
apiKey: config.GoogleDeveloperKey,
videoId,
autoplay: true
- }).then(() => {
- Orientation.lockToPortrait();
});
} else {
Linking.openURL(link);
- Orientation.lockToPortrait();
}
}
};
diff --git a/app/components/search_preview/search_preview.js b/app/components/search_preview/search_preview.js
index 09eb9d0a1..96c2b53d4 100644
--- a/app/components/search_preview/search_preview.js
+++ b/app/components/search_preview/search_preview.js
@@ -4,7 +4,6 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
- Dimensions,
Platform,
Text,
TouchableOpacity,
@@ -90,7 +89,6 @@ export default class SearchPreview extends PureComponent {
render() {
const {channelName, currentUserId, posts, theme} = this.props;
- const {height, width} = Dimensions.get('window');
const style = getStyleSheet(theme);
let postList;
@@ -113,7 +111,7 @@ export default class SearchPreview extends PureComponent {
return (
{
container: {
position: 'absolute',
backgroundColor: changeOpacity('#000', 0.3),
+ height: '100%',
top: 0,
left: 0,
- zIndex: 10
+ zIndex: 10,
+ width: '100%'
},
wrapper: {
flex: 1,
diff --git a/app/constants/device.js b/app/constants/device.js
new file mode 100644
index 000000000..8f0b096b7
--- /dev/null
+++ b/app/constants/device.js
@@ -0,0 +1,12 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import keyMirror from 'mattermost-redux/utils/key_mirror';
+
+export default keyMirror({
+ CONNECTION_CHANGED: null,
+ DEVICE_DIMENSIONS_CHANGED: null,
+ DEVICE_TYPE_CHANGED: null,
+ DEVICE_ORIENTATION_CHANGED: null,
+ STATUSBAR_HEIGHT_CHANGED: null
+});
diff --git a/app/constants/index.js b/app/constants/index.js
index 91bc7f280..7269a537d 100644
--- a/app/constants/index.js
+++ b/app/constants/index.js
@@ -1,10 +1,12 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import DeviceTypes from './device';
import NavigationTypes from './navigation';
import ViewTypes from './view';
export {
+ DeviceTypes,
NavigationTypes,
ViewTypes
};
diff --git a/app/constants/view.js b/app/constants/view.js
index f2e5dc6f0..14bf61412 100644
--- a/app/constants/view.js
+++ b/app/constants/view.js
@@ -20,8 +20,6 @@ const ViewTypes = keyMirror({
NOTIFICATION_IN_APP: null,
NOTIFICATION_TAPPED: null,
- CONNECTION_CHANGED: null,
-
SET_POST_DRAFT: null,
SET_COMMENT_DRAFT: null,
@@ -47,9 +45,7 @@ const ViewTypes = keyMirror({
INCREASE_POST_VISIBILITY: null,
RECEIVED_FOCUSED_POST: null,
- LOADING_POSTS: null,
-
- STATUSBAR_HEIGHT_CHANGED: null
+ LOADING_POSTS: null
});
export default {
diff --git a/app/initial_state.js b/app/initial_state.js
index c3d869a61..9e57b2905 100644
--- a/app/initial_state.js
+++ b/app/initial_state.js
@@ -246,6 +246,9 @@ const state = {
}
}
},
+ device: {
+ connection: true
+ },
navigation: '',
views: {
channel: {
@@ -253,7 +256,6 @@ const state = {
loading: false,
tooltipVisible: false
},
- connection: true,
fetchCache: {},
i18n: {
locale: ''
diff --git a/app/mattermost.js b/app/mattermost.js
index e7dfc262d..3adbb7eb3 100644
--- a/app/mattermost.js
+++ b/app/mattermost.js
@@ -29,11 +29,16 @@ import {close as closeWebSocket} from 'mattermost-redux/actions/websocket';
import {Client, Client4} from 'mattermost-redux/client';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
+import {
+ calculateDeviceDimensions,
+ setDeviceOrientation,
+ setDeviceAsTablet,
+ setStatusBarHeight
+} from 'app/actions/device';
import {
goToNotification,
loadConfigAndLicense,
queueNotification,
- setStatusBarHeight,
purgeOfflineStore
} from 'app/actions/views/root';
import {setChannelDisplayName} from 'app/actions/views/channel';
@@ -69,7 +74,7 @@ export default class Mattermost {
this.isConfigured = false;
this.allowOtherServers = true;
- Orientation.lockToPortrait();
+ Orientation.unlockAllOrientations();
initializeSentry();
@@ -80,7 +85,7 @@ export default class Mattermost {
EventEmitter.on(NavigationTypes.NAVIGATION_RESET, this.handleReset);
EventEmitter.on(General.DEFAULT_CHANNEL, this.handleResetDisplayName);
EventEmitter.on(NavigationTypes.RESTART_APP, this.restartApp);
-
+ Orientation.addOrientationListener(this.orientationDidChange);
mattermostManaged.addEventListener('managedConfigDidChange', this.handleManagedConfig);
this.handleAppStateChange(AppState.currentState);
@@ -373,6 +378,10 @@ export default class Mattermost {
// We need to wait for hydration to occur before load the router.
listenForHydration = () => {
const state = this.store.getState();
+ Orientation.getOrientation((orientation) => {
+ this.orientationDidChange(orientation);
+ });
+
if (state.views.root.hydrationComplete) {
this.unsubscribeFromStore();
@@ -498,6 +507,17 @@ export default class Mattermost {
}
};
+ orientationDidChange = (orientation) => {
+ const {dispatch} = this.store;
+ dispatch(setDeviceOrientation(orientation));
+ if (DeviceInfo.isTablet()) {
+ dispatch(setDeviceAsTablet());
+ }
+ setTimeout(() => {
+ dispatch(calculateDeviceDimensions());
+ }, 100);
+ };
+
resetBadgeAndVersion = () => {
const {dispatch, getState} = this.store;
Client4.serverVersion = '';
@@ -558,6 +578,9 @@ export default class Mattermost {
passProps: {
allowOtherServers: this.allowOtherServers
},
+ appStyle: {
+ orientation: 'auto'
+ },
animationType
});
};
diff --git a/app/reducers/views/connection.js b/app/reducers/device/connection.js
similarity index 81%
rename from app/reducers/views/connection.js
rename to app/reducers/device/connection.js
index 76fbb34a9..76f7fbe28 100644
--- a/app/reducers/views/connection.js
+++ b/app/reducers/device/connection.js
@@ -2,11 +2,11 @@
// See License.txt for license information.
import {UserTypes} from 'mattermost-redux/action_types';
-import {ViewTypes} from 'app/constants';
+import {DeviceTypes} from 'app/constants';
export default function connection(state = true, action) {
switch (action.type) {
- case ViewTypes.CONNECTION_CHANGED:
+ case DeviceTypes.CONNECTION_CHANGED:
return action.data;
case UserTypes.LOGOUT_SUCCESS:
diff --git a/app/reducers/device/dimension.js b/app/reducers/device/dimension.js
new file mode 100644
index 000000000..16b2b668a
--- /dev/null
+++ b/app/reducers/device/dimension.js
@@ -0,0 +1,30 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {Dimensions} from 'react-native';
+
+import {UserTypes} from 'mattermost-redux/action_types';
+import {DeviceTypes} from 'app/constants';
+
+const {height, width} = Dimensions.get('window');
+const initialState = {
+ deviceHeight: height,
+ deviceWidth: width
+};
+
+export default function dimension(state = initialState, action) {
+ switch (action.type) {
+ case DeviceTypes.DEVICE_DIMENSIONS_CHANGED: {
+ const {data} = action;
+ if (state.deviceWidth !== data.deviceWidth || state.deviceHeight !== data.deviceHeight) {
+ return {...data};
+ }
+ break;
+ }
+ case UserTypes.LOGOUT_SUCCESS:
+ return initialState;
+ }
+
+ return state;
+}
+
diff --git a/app/reducers/device/index.js b/app/reducers/device/index.js
new file mode 100644
index 000000000..afbf13377
--- /dev/null
+++ b/app/reducers/device/index.js
@@ -0,0 +1,18 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {combineReducers} from 'redux';
+
+import connection from './connection';
+import dimension from './dimension';
+import isTablet from './is_tablet';
+import orientation from './orientation';
+import statusBarHeight from './status_bar';
+
+export default combineReducers({
+ connection,
+ dimension,
+ isTablet,
+ orientation,
+ statusBarHeight
+});
diff --git a/app/reducers/device/is_tablet.js b/app/reducers/device/is_tablet.js
new file mode 100644
index 000000000..d079d73ee
--- /dev/null
+++ b/app/reducers/device/is_tablet.js
@@ -0,0 +1,14 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {DeviceTypes} from 'app/constants';
+
+export default function isTablet(state = false, action) {
+ switch (action.type) {
+ case DeviceTypes.DEVICE_TYPE_CHANGED:
+ return action.data;
+ }
+
+ return state;
+}
+
diff --git a/app/reducers/device/orientation.js b/app/reducers/device/orientation.js
new file mode 100644
index 000000000..8b1ebab2e
--- /dev/null
+++ b/app/reducers/device/orientation.js
@@ -0,0 +1,14 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {DeviceTypes} from 'app/constants';
+
+export default function orientation(state = 'PORTRAIT', action) {
+ switch (action.type) {
+ case DeviceTypes.DEVICE_ORIENTATION_CHANGED:
+ return action.data;
+ }
+
+ return state;
+}
+
diff --git a/app/reducers/device/status_bar.js b/app/reducers/device/status_bar.js
new file mode 100644
index 000000000..1b1859ec2
--- /dev/null
+++ b/app/reducers/device/status_bar.js
@@ -0,0 +1,14 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {DeviceTypes} from 'app/constants';
+
+export default function height(state = 0, action) {
+ switch (action.type) {
+ case DeviceTypes.STATUSBAR_HEIGHT_CHANGED:
+ return action.data;
+ default:
+ return state;
+ }
+}
+
diff --git a/app/reducers/index.js b/app/reducers/index.js
index 430da8526..e80bcabcf 100644
--- a/app/reducers/index.js
+++ b/app/reducers/index.js
@@ -1,10 +1,12 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import device from './device';
import navigation from './navigation';
import views from './views';
export default {
+ device,
navigation,
views
};
diff --git a/app/reducers/views/index.js b/app/reducers/views/index.js
index a0caf1b15..38ed2978f 100644
--- a/app/reducers/views/index.js
+++ b/app/reducers/views/index.js
@@ -4,7 +4,6 @@
import {combineReducers} from 'redux';
import channel from './channel';
-import connection from './connection';
import fetchCache from './fetch_cache';
import i18n from './i18n';
import login from './login';
@@ -16,7 +15,6 @@ import team from './team';
import thread from './thread';
export default combineReducers({
- connection,
channel,
fetchCache,
i18n,
diff --git a/app/reducers/views/root.js b/app/reducers/views/root.js
index c07316acd..bd24bdba7 100644
--- a/app/reducers/views/root.js
+++ b/app/reducers/views/root.js
@@ -37,18 +37,8 @@ function purge(state = false, action) {
}
}
-function statusBarHeight(state = 20, action) {
- switch (action.type) {
- case ViewTypes.STATUSBAR_HEIGHT_CHANGED:
- return action.data;
- default:
- return state;
- }
-}
-
export default combineReducers({
appInitializing,
hydrationComplete,
- purge,
- statusBarHeight
+ purge
});
diff --git a/app/screens/channel/channel.js b/app/screens/channel/channel.js
index 29bcf1253..2ce355ffe 100644
--- a/app/screens/channel/channel.js
+++ b/app/screens/channel/channel.js
@@ -5,7 +5,6 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {injectIntl, intlShape} from 'react-intl';
import {
- Dimensions,
NetInfo,
Platform,
View
@@ -199,6 +198,16 @@ class Channel extends PureComponent {
navigator={navigator}
>
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
);
}
@@ -233,17 +232,12 @@ class Channel extends PureComponent {
const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
return {
- headerContainer: {
- flex: 1,
- position: 'absolute'
- },
header: {
backgroundColor: theme.sidebarHeaderBg,
flexDirection: 'row',
justifyContent: 'flex-start',
- width: Dimensions.get('window').width,
+ width: '100%',
zIndex: 10,
- elevation: 2,
...Platform.select({
android: {
height: 46
@@ -255,15 +249,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
})
},
postList: {
- flex: 1,
- ...Platform.select({
- android: {
- marginTop: 46
- },
- ios: {
- marginTop: 64
- }
- })
+ flex: 1
},
loading: {
backgroundColor: theme.centerChannelBg,
@@ -272,6 +258,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
keyboardLayout: {
backgroundColor: theme.centerChannelBg,
flex: 1,
+ zIndex: -1,
paddingBottom: 0
}
};
diff --git a/app/screens/channel/channel_drawer_button.js b/app/screens/channel/channel_drawer_button.js
index 858a71aa8..6d79b0a17 100644
--- a/app/screens/channel/channel_drawer_button.js
+++ b/app/screens/channel/channel_drawer_button.js
@@ -25,7 +25,6 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
class ChannelDrawerButton extends PureComponent {
static propTypes = {
- applicationInitializing: PropTypes.bool.isRequired,
currentTeamId: PropTypes.string.isRequired,
myTeamMembers: PropTypes.object,
theme: PropTypes.object,
@@ -80,7 +79,6 @@ class ChannelDrawerButton extends PureComponent {
render() {
const {
- applicationInitializing,
currentTeamId,
mentionCount,
messageCount,
@@ -89,10 +87,6 @@ class ChannelDrawerButton extends PureComponent {
} = this.props;
const style = getStyleFromTheme(theme);
- if (applicationInitializing) {
- return null;
- }
-
let mentions = mentionCount;
let messages = messageCount;
@@ -184,7 +178,6 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
function mapStateToProps(state) {
return {
- applicationInitializing: state.views.root.appInitializing,
currentTeamId: getCurrentTeamId(state),
myTeamMembers: getTeamMemberships(state),
theme: getTheme(state),
diff --git a/app/screens/channel/channel_post_list/channel_post_list.js b/app/screens/channel/channel_post_list/channel_post_list.js
index ce29c4306..485ced103 100644
--- a/app/screens/channel/channel_post_list/channel_post_list.js
+++ b/app/screens/channel/channel_post_list/channel_post_list.js
@@ -6,7 +6,6 @@ import PropTypes from 'prop-types';
import {injectIntl, intlShape} from 'react-intl';
import {
Animated,
- Dimensions,
Platform,
StyleSheet,
View
@@ -207,7 +206,6 @@ class ChannelPostList extends PureComponent {
}
const refreshIndicatorDimensions = {
- width: Dimensions.get('window').width,
height: retryMessageHeight
};
@@ -234,7 +232,8 @@ const style = StyleSheet.create({
paddingHorizontal: 10,
position: 'absolute',
top: 0,
- overflow: 'hidden'
+ overflow: 'hidden',
+ width: '100%'
}
});
diff --git a/app/screens/channel/channel_post_list/index.js b/app/screens/channel/channel_post_list/index.js
index 3317d1611..cbfa4bd34 100644
--- a/app/screens/channel/channel_post_list/index.js
+++ b/app/screens/channel/channel_post_list/index.js
@@ -9,6 +9,7 @@ import {RequestStatus} from 'mattermost-redux/constants';
import {makeGetPostsInChannel} from 'mattermost-redux/selectors/entities/posts';
import {getMyCurrentChannelMembership, makeGetChannel} from 'mattermost-redux/selectors/entities/channels';
import {loadPostsIfNecessaryWithRetry, loadThreadIfNecessary, increasePostVisibility, refreshChannelWithRetry} from 'app/actions/views/channel';
+import {getConnection} from 'app/selectors/device';
import {getTheme} from 'app/selectors/preferences';
import ChannelPostList from './channel_post_list';
@@ -22,7 +23,7 @@ function makeMapStateToProps() {
const {getPosts, getPostsRetryAttempts, getPostsSince, getPostsSinceRetryAttempts} = state.requests.posts;
const posts = getPostsInChannel(state, channelId) || [];
const {websocket: websocketRequest} = state.requests.general;
- const {connection: networkOnline} = state.views;
+ const networkOnline = getConnection(state);
const webSocketOnline = websocketRequest.status === RequestStatus.SUCCESS;
let getPostsStatus;
diff --git a/app/screens/channel/channel_search_button.js b/app/screens/channel/channel_search_button.js
index 4946d54cc..ec9615eaa 100644
--- a/app/screens/channel/channel_search_button.js
+++ b/app/screens/channel/channel_search_button.js
@@ -29,7 +29,6 @@ class ChannelSearchButton extends PureComponent {
clearSearch: PropTypes.func.isRequired,
handlePostDraftChanged: PropTypes.func.isRequired
}).isRequired,
- applicationInitializing: PropTypes.bool.isRequired,
navigator: PropTypes.object,
theme: PropTypes.object
};
@@ -57,14 +56,9 @@ class ChannelSearchButton extends PureComponent {
render() {
const {
- applicationInitializing,
theme
} = this.props;
- if (applicationInitializing) {
- return null;
- }
-
return (
preventDoubleTap(this.handlePress, this)}
@@ -98,7 +92,6 @@ const style = StyleSheet.create({
function mapStateToProps(state, ownProps) {
return {
- applicationInitializing: state.views.root.appInitializing,
theme: getTheme(state),
...ownProps
};
diff --git a/app/screens/channel/channel_title.js b/app/screens/channel/channel_title.js
index a3d943a69..6a35fca16 100644
--- a/app/screens/channel/channel_title.js
+++ b/app/screens/channel/channel_title.js
@@ -16,10 +16,6 @@ import {getCurrentChannel} from 'mattermost-redux/selectors/entities/channels';
import {getTheme} from 'app/selectors/preferences';
function ChannelTitle(props) {
- if (props.applicationInitializing) {
- return null;
- }
-
const channelName = props.displayName || props.currentChannel.display_name;
let icon;
if (channelName) {
@@ -56,7 +52,6 @@ function ChannelTitle(props) {
}
ChannelTitle.propTypes = {
- applicationInitializing: PropTypes.bool.isRequired,
currentChannel: PropTypes.object,
displayName: PropTypes.string,
onPress: PropTypes.func,
@@ -71,7 +66,6 @@ ChannelTitle.defaultProps = {
function mapStateToProps(state) {
return {
- applicationInitializing: state.views.root.appInitializing,
currentChannel: getCurrentChannel(state) || {},
displayName: state.views.channel.displayName,
theme: getTheme(state)
diff --git a/app/screens/channel/index.js b/app/screens/channel/index.js
index 34907a99e..2b013ac24 100644
--- a/app/screens/channel/index.js
+++ b/app/screens/channel/index.js
@@ -9,8 +9,9 @@ import {
loadProfilesAndTeamMembersForDMSidebar,
selectInitialChannel
} from 'app/actions/views/channel';
-import {connection} from 'app/actions/views/connection';
+import {connection} from 'app/actions/device';
import {selectFirstAvailableTeam} from 'app/actions/views/select_team';
+import {getStatusBarHeight} from 'app/selectors/device';
import {getTheme} from 'app/selectors/preferences';
import {startPeriodicStatusUpdates, stopPeriodicStatusUpdates} from 'mattermost-redux/actions/users';
@@ -27,7 +28,6 @@ import Channel from './channel';
function mapStateToProps(state, ownProps) {
const {websocket} = state.requests.general;
const {myChannels: channelsRequest} = state.requests.channels;
- const {statusBarHeight} = state.views.root;
return {
...ownProps,
@@ -35,7 +35,7 @@ function mapStateToProps(state, ownProps) {
currentChannelId: getCurrentChannelId(state),
theme: getTheme(state),
webSocketRequest: websocket,
- statusBarHeight,
+ statusBarHeight: getStatusBarHeight(state),
channelsRequestStatus: channelsRequest.status
};
}
diff --git a/app/screens/code/code.js b/app/screens/code/code.js
index 9551db6c1..ff4f36efe 100644
--- a/app/screens/code/code.js
+++ b/app/screens/code/code.js
@@ -4,7 +4,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import {
- Dimensions,
ScrollView,
StyleSheet,
Text,
@@ -14,10 +13,6 @@ import {
import {getCodeFont} from 'app/utils/markdown';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
-const {
- width: deviceWidth
-} = Dimensions.get('window');
-
export default class Code extends React.PureComponent {
static propTypes = {
theme: PropTypes.object.isRequired,
@@ -26,7 +21,7 @@ export default class Code extends React.PureComponent {
countLines = (content) => {
return content.split('\n').length;
- }
+ };
render() {
const style = getStyleSheet(this.props.theme);
@@ -100,7 +95,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
codeContainer: {
flexGrow: 0,
flexShrink: 1,
- width: deviceWidth
+ width: '100%'
},
code: {
paddingHorizontal: 6,
diff --git a/app/screens/image_preview/downloader.ios.js b/app/screens/image_preview/downloader.ios.js
index a523d331f..f08e4ebe1 100644
--- a/app/screens/image_preview/downloader.ios.js
+++ b/app/screens/image_preview/downloader.ios.js
@@ -6,7 +6,6 @@ import PropTypes from 'prop-types';
import {
Animated,
CameraRoll,
- Dimensions,
StyleSheet,
Text,
TouchableOpacity,
@@ -22,10 +21,11 @@ import FormattedText from 'app/components/formatted_text';
import {emptyFunction} from 'app/utils/general';
const {View: AnimatedView} = Animated;
-const {height: deviceHeight, width: deviceWidth} = Dimensions.get('window');
export default class Downloader extends PureComponent {
static propTypes = {
+ deviceHeight: PropTypes.number.isRequired,
+ deviceWidth: PropTypes.number.isRequired,
file: PropTypes.object.isRequired,
onDownloadCancel: PropTypes.func,
onDownloadSuccess: PropTypes.func,
@@ -38,9 +38,13 @@ export default class Downloader extends PureComponent {
show: false
};
- state = {
- downloaderTop: new Animated.Value(deviceHeight),
- progress: 0
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ downloaderTop: new Animated.Value(props.deviceHeight),
+ progress: 0
+ };
}
componentWillReceiveProps(nextProps) {
@@ -52,10 +56,27 @@ export default class Downloader extends PureComponent {
});
} else if (!nextProps.show && this.props.show) {
this.toggleDownloader(false);
+ } else if (this.props.deviceHeight !== nextProps.deviceHeight) {
+ this.recenterDownloader(nextProps);
}
}
+ recenterDownloader = (props) => {
+ const {deviceHeight, show} = props;
+ const top = show ? (deviceHeight / 2) - 100 : deviceHeight;
+
+ Animated.sequence([
+ Animated.delay(200),
+ Animated.spring(this.state.downloaderTop, {
+ toValue: top,
+ tension: 8,
+ friction: 5
+ })
+ ]).start();
+ };
+
toggleDownloader = (show = true) => {
+ const {deviceHeight} = this.props;
const top = show ? (deviceHeight / 2) - 100 : deviceHeight;
Animated.spring(this.state.downloaderTop, {
@@ -67,7 +88,7 @@ export default class Downloader extends PureComponent {
this.startDownload();
}
});
- }
+ };
startDownload = async () => {
try {
@@ -181,15 +202,19 @@ export default class Downloader extends PureComponent {
);
- }
+ };
render() {
const {show} = this.props;
+ if (!show) {
+ return null;
+ }
+
const {didCancel, progress} = this.state;
const trueProgress = didCancel ? 0 : progress;
- const containerHeight = show ? deviceHeight : 0;
+ const containerHeight = show ? '100%' : 0;
return (
@@ -241,11 +266,11 @@ const styles = StyleSheet.create({
position: 'absolute',
top: 0,
left: 0,
- width: deviceWidth
+ width: '100%'
},
downloader: {
alignItems: 'center',
- left: (deviceWidth / 2) - 118,
+ alignSelf: 'center',
height: 220,
width: 236,
borderRadius: 8,
diff --git a/app/screens/image_preview/image_preview.js b/app/screens/image_preview/image_preview.js
index 40a35d046..b4d2352e7 100644
--- a/app/screens/image_preview/image_preview.js
+++ b/app/screens/image_preview/image_preview.js
@@ -5,7 +5,6 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
Animated,
- Dimensions,
InteractionManager,
PanResponder,
Platform,
@@ -18,7 +17,6 @@ import {
} from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import LinearGradient from 'react-native-linear-gradient';
-import Orientation from 'react-native-orientation';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
@@ -30,7 +28,6 @@ import Downloader from './downloader';
import Previewer from './previewer';
const {View: AnimatedView} = Animated;
-const {height: deviceHeight, width: deviceWidth} = Dimensions.get('window');
const DRAG_VERTICAL_THRESHOLD_START = 25; // When do we want to start capturing the drag
const DRAG_VERTICAL_THRESHOLD_END = 100; // When do we want to navigate back
const DRAG_HORIZONTAL_THRESHOLD = 50; // Make sure that it's not a sloppy horizontal swipe
@@ -46,6 +43,8 @@ export default class ImagePreview extends PureComponent {
addFileToFetchCache: PropTypes.func.isRequired
}),
canDownloadFiles: PropTypes.bool.isRequired,
+ deviceHeight: PropTypes.number.isRequired,
+ deviceWidth: PropTypes.number.isRequired,
fetchCache: PropTypes.object.isRequired,
fileId: PropTypes.string.isRequired,
files: PropTypes.array.isRequired,
@@ -60,10 +59,9 @@ export default class ImagePreview extends PureComponent {
this.zoomableImages = {};
const currentFile = props.files.findIndex((file) => file.id === props.fileId);
+
this.state = {
currentFile,
- deviceHeight: deviceHeight - STATUSBAR_HEIGHT,
- deviceWidth,
drag: new Animated.ValueXY(),
footerOpacity: new Animated.Value(1),
pagingEnabled: true,
@@ -85,7 +83,6 @@ export default class ImagePreview extends PureComponent {
}
componentDidMount() {
- Orientation.unlockAllOrientations();
InteractionManager.runAfterInteractions(() => {
this.scrollView.scrollTo({x: (this.state.currentFile) * this.state.deviceWidth, animated: false});
Animated.timing(this.state.wrapperViewOpacity, {
@@ -95,8 +92,15 @@ export default class ImagePreview extends PureComponent {
});
}
+ componentWillReceiveProps(nextProps) {
+ if (this.props.deviceWidth !== nextProps.deviceWidth && Platform.OS === 'android') {
+ InteractionManager.runAfterInteractions(() => {
+ this.scrollView.scrollTo({x: (this.state.currentFile * nextProps.deviceWidth), animated: false});
+ });
+ }
+ }
+
componentWillUnmount() {
- Orientation.lockToPortrait();
if (Platform.OS === 'ios') {
StatusBar.setHidden(false, 'fade');
}
@@ -145,7 +149,7 @@ export default class ImagePreview extends PureComponent {
handleImageDoubleTap = (x, y) => {
this.zoomableImages[this.state.currentFile].toggleZoom(x, y);
- }
+ };
setHeaderAndFileInfoVisible = (show) => {
this.setState({
@@ -165,7 +169,7 @@ export default class ImagePreview extends PureComponent {
};
handleScroll = (event) => {
- const offset = event.nativeEvent.contentOffset.x / this.state.deviceWidth;
+ const offset = event.nativeEvent.contentOffset.x / this.props.deviceWidth;
const wholeNumber = Number((offset).toFixed(0));
if (Math.abs(offset - wholeNumber) < 0.01) {
this.setState({
@@ -184,15 +188,6 @@ export default class ImagePreview extends PureComponent {
this.scrollView = c;
};
- onLayout = (event) => {
- if (event.nativeEvent.layout.width !== this.state.deviceWidth) {
- this.setState({
- deviceHeight: event.nativeEvent.layout.height,
- deviceWidth: event.nativeEvent.layout.width
- });
- }
- };
-
imageIsZooming = (zooming) => {
if (zooming !== this.state.isZooming) {
this.setHeaderAndFileInfoVisible(!zooming);
@@ -212,7 +207,7 @@ export default class ImagePreview extends PureComponent {
} else {
this.showIOSDownloadOptions();
}
- }
+ };
showIOSDownloadOptions = () => {
this.setHeaderAndFileInfoVisible(false);
@@ -244,7 +239,7 @@ export default class ImagePreview extends PureComponent {
modalPresentationStyle: 'overCurrentContext'
}
});
- }
+ };
showDownloader = () => {
EventEmitter.emit(NavigationTypes.NAVIGATION_CLOSE_MODAL);
@@ -252,14 +247,14 @@ export default class ImagePreview extends PureComponent {
this.setState({
showDownloader: true
});
- }
+ };
hideDownloader = (hideFileInfo = true) => {
this.setState({showDownloader: false});
if (hideFileInfo) {
this.setHeaderAndFileInfoVisible(true);
}
- }
+ };
renderDownloadButton = () => {
const {canDownloadFiles, files} = this.props;
@@ -297,10 +292,10 @@ export default class ImagePreview extends PureComponent {
{icon}
);
- }
+ };
render() {
- const maxImageHeight = this.state.deviceHeight - STATUSBAR_HEIGHT;
+ const maxImageHeight = this.props.deviceHeight - STATUSBAR_HEIGHT;
const marginStyle = {
...Platform.select({
@@ -314,10 +309,7 @@ export default class ImagePreview extends PureComponent {
};
return (
-
+
{this.props.files.map((file, index) => {
let component;
@@ -347,10 +338,10 @@ export default class ImagePreview extends PureComponent {
file={file}
theme={this.props.theme}
imageHeight={Math.min(maxImageHeight, file.height)}
- imageWidth={Math.min(this.state.deviceWidth, file.width)}
+ imageWidth={Math.min(this.props.deviceWidth, file.width)}
shrink={this.state.shouldShrinkImages}
- wrapperHeight={this.state.deviceHeight}
- wrapperWidth={this.state.deviceWidth}
+ wrapperHeight={this.props.deviceHeight}
+ wrapperWidth={this.props.deviceWidth}
onImageTap={this.handleImageTap}
onImageDoubleTap={this.handleImageDoubleTap}
onZoom={this.imageIsZooming}
@@ -372,7 +363,7 @@ export default class ImagePreview extends PureComponent {
return (
{component}
@@ -380,7 +371,7 @@ export default class ImagePreview extends PureComponent {
})}
@@ -417,6 +408,8 @@ export default class ImagePreview extends PureComponent {
{ //eslint-disable-line react/display-name
const {navigator} = props; //eslint-disable-line react/prop-types
return (
@@ -58,7 +58,7 @@ export function registerScreens(store, Provider) {
Navigation.registerComponent('About', () => wrapWithContextProvider(About), store, Provider);
Navigation.registerComponent('AddReaction', () => wrapWithContextProvider(AddReaction), store, Provider);
Navigation.registerComponent('AdvancedSettings', () => wrapWithContextProvider(AdvancedSettings), store, Provider);
- Navigation.registerComponent('Channel', () => wrapWithContextProvider(Channel), store, Provider);
+ Navigation.registerComponent('Channel', () => wrapWithContextProvider(Channel, false), store, Provider);
Navigation.registerComponent('ChannelAddMembers', () => wrapWithContextProvider(ChannelAddMembers), store, Provider);
Navigation.registerComponent('ChannelInfo', () => wrapWithContextProvider(ChannelInfo), store, Provider);
Navigation.registerComponent('ChannelMembers', () => wrapWithContextProvider(ChannelMembers), store, Provider);
@@ -73,7 +73,7 @@ export function registerScreens(store, Provider) {
Navigation.registerComponent('MoreChannels', () => wrapWithContextProvider(MoreChannels), store, Provider);
Navigation.registerComponent('MoreDirectMessages', () => wrapWithContextProvider(MoreDirectMessages), store, Provider);
Navigation.registerComponent('OptionsModal', () => wrapWithContextProvider(OptionsModal), store, Provider);
- Navigation.registerComponent('Notification', () => wrapWithContextProvider(Notification, true), store, Provider);
+ Navigation.registerComponent('Notification', () => wrapWithContextProvider(Notification), store, Provider);
Navigation.registerComponent('NotificationSettings', () => wrapWithContextProvider(NotificationSettings), store, Provider);
Navigation.registerComponent('NotificationSettingsEmail', () => wrapWithContextProvider(NotificationSettingsEmail), store, Provider);
Navigation.registerComponent('NotificationSettingsMentions', () => wrapWithContextProvider(NotificationSettingsMentions), store, Provider);
diff --git a/app/screens/login/login.js b/app/screens/login/login.js
index 1dd537733..5e30feb77 100644
--- a/app/screens/login/login.js
+++ b/app/screens/login/login.js
@@ -9,14 +9,15 @@ import {
Image,
InteractionManager,
Keyboard,
- KeyboardAvoidingView,
- Platform,
+ StyleSheet,
Text,
TextInput,
TouchableWithoutFeedback,
View
} from 'react-native';
import Button from 'react-native-button';
+import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
+import Orientation from 'react-native-orientation';
import ErrorText from 'app/components/error_text';
import FormattedText from 'app/components/formatted_text';
@@ -57,10 +58,8 @@ class Login extends PureComponent {
};
}
- componentDidMount() {
- if (Platform.OS === 'android') {
- Keyboard.addListener('keyboardDidHide', this.handleAndroidKeyboard);
- }
+ componentWillMount() {
+ Orientation.addOrientationListener(this.orientationDidChange);
}
componentWillReceiveProps(nextProps) {
@@ -70,9 +69,7 @@ class Login extends PureComponent {
}
componentWillUnmount() {
- if (Platform.OS === 'android') {
- Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard);
- }
+ Orientation.removeOrientationListener(this.orientationDidChange);
}
goToLoadTeam = (expiresAt) => {
@@ -123,10 +120,6 @@ class Login extends PureComponent {
});
};
- handleAndroidKeyboard = () => {
- this.blur();
- };
-
blur = () => {
this.loginId.blur();
this.passwd.blur();
@@ -286,6 +279,14 @@ class Login extends PureComponent {
this.passwd.focus();
};
+ orientationDidChange = () => {
+ this.scroll.scrollToPosition(0, 0, true);
+ };
+
+ scrollRef = (ref) => {
+ this.scroll = ref;
+ };
+
render() {
const isLoading = this.props.loginRequest.status === RequestStatus.STARTED;
@@ -313,14 +314,14 @@ class Login extends PureComponent {
}
return (
-
+
-
+
@@ -347,6 +348,7 @@ class Login extends PureComponent {
returnKeyType='next'
underlineColorAndroid='transparent'
onSubmitEditing={this.passwordFocus}
+ blurOnSubmit={false}
/>
{proceed}
-
+
-
+
);
}
}
+const style = StyleSheet.create({
+ container: {
+ backgroundColor: '#FFFFFF',
+ flex: 1
+ },
+ innerContainer: {
+ alignItems: 'center',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ paddingHorizontal: 15,
+ paddingVertical: 50
+ }
+});
+
export default injectIntl(Login);
diff --git a/app/screens/login_options/login_options.js b/app/screens/login_options/login_options.js
index 8fada460f..6c846c29e 100644
--- a/app/screens/login_options/login_options.js
+++ b/app/screens/login_options/login_options.js
@@ -6,10 +6,12 @@ import PropTypes from 'prop-types';
import {injectIntl, intlShape} from 'react-intl';
import {
Image,
- Text,
- View
+ ScrollView,
+ StyleSheet,
+ Text
} from 'react-native';
import Button from 'react-native-button';
+import Orientation from 'react-native-orientation';
import semver from 'semver';
import {ViewTypes} from 'app/constants';
@@ -31,6 +33,14 @@ class LoginOptions extends PureComponent {
theme: PropTypes.object
};
+ componentWillMount() {
+ Orientation.addOrientationListener(this.orientationDidChange);
+ }
+
+ componentWillUnmount() {
+ Orientation.removeOrientationListener(this.orientationDidChange);
+ }
+
goToLogin = () => {
const {intl, navigator, theme} = this.props;
navigator.push({
@@ -66,6 +76,10 @@ class LoginOptions extends PureComponent {
});
};
+ orientationDidChange = () => {
+ this.scroll.scrollTo({x: 0, y: 0, animated: true});
+ };
+
renderEmailOption = () => {
const {config} = this.props;
if (config.EnableSignInWithEmail === 'true' || config.EnableSignInWithUsername === 'true') {
@@ -172,9 +186,17 @@ class LoginOptions extends PureComponent {
return null;
};
+ scrollRef = (ref) => {
+ this.scroll = ref;
+ };
+
render() {
return (
-
+
+
);
}
}
+const style = StyleSheet.create({
+ container: {
+ backgroundColor: '#FFFFFF',
+ flex: 1
+ },
+ innerContainer: {
+ alignItems: 'center',
+ flexDirection: 'column',
+ justifyContent: 'center',
+ paddingHorizontal: 15,
+ paddingVertical: 50
+ }
+});
+
export default injectIntl(LoginOptions);
diff --git a/app/screens/notification/index.js b/app/screens/notification/index.js
index 3514e0b5b..e233f283b 100644
--- a/app/screens/notification/index.js
+++ b/app/screens/notification/index.js
@@ -5,6 +5,7 @@ import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {goToNotification} from 'app/actions/views/root';
+import {getDimensions} from 'app/selectors/device';
import {getTheme} from 'app/selectors/preferences';
import {getChannel} from 'mattermost-redux/selectors/entities/channels';
@@ -15,6 +16,7 @@ import Notification from './notification';
function mapStateToProps(state, ownProps) {
const {data} = ownProps.notification;
+ const {deviceWidth} = getDimensions(state);
let user;
if (data.sender_id) {
@@ -30,6 +32,7 @@ function mapStateToProps(state, ownProps) {
...ownProps,
config: state.entities.general.config,
channel,
+ deviceWidth,
user,
teammateNameDisplay: getTeammateNameDisplaySetting(state),
theme: getTheme(state)
diff --git a/app/screens/notification/notification.js b/app/screens/notification/notification.js
index 330f7dbed..4b2966797 100644
--- a/app/screens/notification/notification.js
+++ b/app/screens/notification/notification.js
@@ -4,7 +4,6 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
- Dimensions,
Image,
Platform,
StyleSheet,
@@ -32,6 +31,7 @@ export default class Notification extends PureComponent {
}).isRequired,
channel: PropTypes.object,
config: PropTypes.object,
+ deviceWidth: PropTypes.number.isRequired,
notification: PropTypes.object.isRequired,
teammateNameDisplay: PropTypes.string,
navigator: PropTypes.object,
@@ -161,7 +161,8 @@ export default class Notification extends PureComponent {
};
render() {
- const {message} = this.props.notification;
+ const {deviceWidth, notification} = this.props;
+ const {message} = notification;
if (message) {
const msg = message.split(':');
@@ -172,7 +173,7 @@ export default class Notification extends PureComponent {
const icon = this.getNotificationIcon();
return (
-
+
{
this.props.onCancelPress();
this.close();
- }
+ };
close = () => {
Animated.timing(this.state.top, {
- toValue: deviceHeight,
+ toValue: this.props.deviceHeight,
duration: DURATION
}).start(() => {
this.props.navigator.dismissModal({
@@ -78,7 +82,7 @@ export default class OptionsModal extends PureComponent {
return (
-
+
{
return {
header: {
backgroundColor: theme.sidebarHeaderBg,
- width: Dimensions.get('window').width,
+ width: '100%',
...Platform.select({
android: {
height: 46,
diff --git a/app/screens/thread/index.js b/app/screens/thread/index.js
index a5dc30f5b..f91deab2d 100644
--- a/app/screens/thread/index.js
+++ b/app/screens/thread/index.js
@@ -5,6 +5,7 @@ import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {handleCommentDraftChanged} from 'app/actions/views/thread';
+import {getStatusBarHeight} from 'app/selectors/device';
import {getTheme} from 'app/selectors/preferences';
import {selectPost} from 'mattermost-redux/actions/posts';
@@ -21,7 +22,6 @@ function makeMapStateToProps() {
return function mapStateToProps(state, ownProps) {
const posts = getPostsForThread(state, ownProps);
const threadDraft = state.views.thread.drafts[ownProps.rootId];
- const {statusBarHeight} = state.views.root;
return {
...ownProps,
@@ -31,7 +31,7 @@ function makeMapStateToProps() {
draft: threadDraft.draft,
files: threadDraft.files,
posts,
- statusBarHeight,
+ statusBarHeight: getStatusBarHeight(state),
theme: getTheme(state)
};
};
diff --git a/app/selectors/device.js b/app/selectors/device.js
new file mode 100644
index 000000000..566779cdc
--- /dev/null
+++ b/app/selectors/device.js
@@ -0,0 +1,22 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+export function getDimensions(state) {
+ return state.device.dimension;
+}
+
+export function getConnection(state) {
+ return state.device.connection;
+}
+
+export function getStatusBarHeight(state) {
+ return state.device.statusBarHeight;
+}
+
+export function isLandscape(state) {
+ return state.device.orientation === 'LANDSCAPE';
+}
+
+export function isTablet(state) {
+ return state.device.isTablet;
+}
diff --git a/app/store/index.js b/app/store/index.js
index b99af2981..7c45644cc 100644
--- a/app/store/index.js
+++ b/app/store/index.js
@@ -160,7 +160,7 @@ export default function configureAppStore(initialState) {
autoRehydrate: {
log: false
},
- blacklist: ['navigation', 'offline', 'requests'],
+ blacklist: ['device', 'navigation', 'offline', 'requests'],
debounce: 500,
transforms: [
setTransformer,
diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj
index 16f86dc15..a16900fd3 100644
--- a/ios/Mattermost.xcodeproj/project.pbxproj
+++ b/ios/Mattermost.xcodeproj/project.pbxproj
@@ -24,7 +24,6 @@
140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
1BCA51319AC6442991C6A208 /* Zocial.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0A091BF1A3D04650AD306A0D /* Zocial.ttf */; };
- 2A167658767AAB5161E3271B /* libPods-Mattermost.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 65FD5EA57EBAE06106094B2F /* libPods-Mattermost.a */; };
2B4C9B708010475DA575B81D /* SimpleLineIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F1F071EE85494E269A50AE88 /* SimpleLineIcons.ttf */; };
2D5296A8926B4D7FBAF2D6E2 /* OpenSans-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6561AEAC21CC40B8A72ABB93 /* OpenSans-Light.ttf */; };
374634801E8480C2005E1244 /* libRCTOrientation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 374634671E848085005E1244 /* libRCTOrientation.a */; };
@@ -60,6 +59,7 @@
7F6877B31E7836070094B63F /* libToolTipMenu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F6877B01E7835E50094B63F /* libToolTipMenu.a */; };
7FBB5E9B1E1F5A4B000DE18A /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDF290C1E1F4B4E00DBBE56 /* libRNVectorIcons.a */; };
7FC200E81EBB65370099331B /* libReactNativeNavigation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FC200DF1EBB65100099331B /* libReactNativeNavigation.a */; };
+ 7FDB92B11F706F58006CDFD1 /* libRNImagePicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDB92A71F706F45006CDFD1 /* libRNImagePicker.a */; };
7FEB10981F6101710039A015 /* BlurAppScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB10971F6101710039A015 /* BlurAppScreen.m */; };
7FEB109D1F61019C0039A015 /* MattermostManaged.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB109A1F61019C0039A015 /* MattermostManaged.m */; };
7FEB109E1F61019C0039A015 /* UIImage+ImageEffects.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB109C1F61019C0039A015 /* UIImage+ImageEffects.m */; };
@@ -67,6 +67,7 @@
895C9A56B94A45C1BAF568FE /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AC6EB561E1F64C17A69D2FAD /* Entypo.ttf */; };
9358B95F95184EE0A4DCE629 /* OpenSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D4B1B363C2414DA19C1AC521 /* OpenSans-Bold.ttf */; };
A08D512E7ADC40CCAD055A9E /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BC977883E2624E05975CA65B /* OpenSans-Regular.ttf */; };
+ A9B746D2CFA9CEBFB8AE2B5B /* libPods-Mattermost.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 65FD5EA57EBAE06106094B2F /* libPods-Mattermost.a */; };
C035DB50ED2045F09923FFAE /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 04AA5E8EF3B54735A11E3B95 /* MaterialCommunityIcons.ttf */; };
C337E8708D2845C6A8DBB47E /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2DCFD31D3F4A4154822AB532 /* Ionicons.ttf */; };
D719A67137964F08BE47A5FC /* OpenSans-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3647DF63D6764CF093375861 /* OpenSans-ExtraBold.ttf */; };
@@ -441,6 +442,13 @@
remoteGlobalIDString = D8AFADBD1BEE6F3F00A4592D;
remoteInfo = ReactNativeNavigation;
};
+ 7FDB92A61F706F45006CDFD1 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 014A3B5C1C6CF33500B6D375;
+ remoteInfo = RNImagePicker;
+ };
7FDF28E01E1F4B1F00DBBE56 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 2CBE9C0FB56E4FDA96C30792 /* RNSVG.xcodeproj */;
@@ -543,6 +551,7 @@
7F63D2C21E6DD98A001FAE12 /* Mattermost.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Mattermost.entitlements; path = Mattermost/Mattermost.entitlements; sourceTree = ""; };
7F6877AA1E7835E50094B63F /* ToolTipMenu.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ToolTipMenu.xcodeproj; path = "../node_modules/react-native-tooltip/ToolTipMenu.xcodeproj"; sourceTree = ""; };
7FC200BC1EBB65100099331B /* ReactNativeNavigation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeNavigation.xcodeproj; path = "../node_modules/react-native-navigation/ios/ReactNativeNavigation.xcodeproj"; sourceTree = ""; };
+ 7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNImagePicker.xcodeproj; path = "../node_modules/react-native-image-picker/ios/RNImagePicker.xcodeproj"; sourceTree = ""; };
7FEB10961F6101710039A015 /* BlurAppScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BlurAppScreen.h; path = Mattermost/BlurAppScreen.h; sourceTree = ""; };
7FEB10971F6101710039A015 /* BlurAppScreen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BlurAppScreen.m; path = Mattermost/BlurAppScreen.m; sourceTree = ""; };
7FEB10991F61019C0039A015 /* MattermostManaged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MattermostManaged.h; path = Mattermost/MattermostManaged.h; sourceTree = ""; };
@@ -615,9 +624,10 @@
DEBCF44F1F46413BB407D316 /* libz.tbd in Frameworks */,
7F43D5DD1F6BF95F001FC614 /* libRNCookieManagerIOS.a in Frameworks */,
7F43D6401F6BFA82001FC614 /* libRCTPushNotification.a in Frameworks */,
+ 7FDB92B11F706F58006CDFD1 /* libRNImagePicker.a in Frameworks */,
7F43D5DE1F6BF96A001FC614 /* libRCTYouTube.a in Frameworks */,
7F43D6061F6BF9EB001FC614 /* libPods-Mattermost.a in Frameworks */,
- 2A167658767AAB5161E3271B /* libPods-Mattermost.a in Frameworks */,
+ A9B746D2CFA9CEBFB8AE2B5B /* libPods-Mattermost.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -975,6 +985,14 @@
name = Products;
sourceTree = "";
};
+ 7FDB92761F706F45006CDFD1 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 7FDB92A71F706F45006CDFD1 /* libRNImagePicker.a */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
7FDF28C41E1F4B1F00DBBE56 /* Products */ = {
isa = PBXGroup;
children = (
@@ -995,6 +1013,7 @@
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
+ 7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */,
37ABD3971F4CE13B001FDE6B /* ART.xcodeproj */,
3752184A1F4B9E980035444B /* RCTCameraRoll.xcodeproj */,
7F26919B1EE1DC51007574FE /* RNNotifications.xcodeproj */,
@@ -1244,6 +1263,10 @@
ProductGroup = 3752181C1F4B9E320035444B /* Products */;
ProjectRef = 546CB0A5BB8F453890CBA559 /* RNFetchBlob.xcodeproj */;
},
+ {
+ ProductGroup = 7FDB92761F706F45006CDFD1 /* Products */;
+ ProjectRef = 7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */;
+ },
{
ProductGroup = 7F8C49621F3DFC30003A22BA /* Products */;
ProjectRef = D0281D64B98143668D6AD42B /* RNLocalAuth.xcodeproj */;
@@ -1639,6 +1662,13 @@
remoteRef = 7FC200DE1EBB65100099331B /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
+ 7FDB92A71F706F45006CDFD1 /* libRNImagePicker.a */ = {
+ isa = PBXReferenceProxy;
+ fileType = archive.ar;
+ path = libRNImagePicker.a;
+ remoteRef = 7FDB92A61F706F45006CDFD1 /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
7FDF28E11E1F4B1F00DBBE56 /* libRNSVG.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@@ -1957,6 +1987,7 @@
PRODUCT_NAME = Mattermost;
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
+ TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
@@ -2002,6 +2033,7 @@
PRODUCT_NAME = Mattermost;
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
+ TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
diff --git a/test/setup.js b/test/setup.js
index 7f2cf7c68..9464a8e50 100644
--- a/test/setup.js
+++ b/test/setup.js
@@ -16,6 +16,11 @@ mockery.enable({
warnOnUnregistered: false
});
mockery.registerMock('react-native', {
+ Dimensions: {
+ get: () => {
+ return {width: 0, height: 0}
+ }
+ },
NativeModules: {}
});
mockery.registerMock('react-native-device-info', {