Add Landscape support for both platforms (#909)
* Landscape support * Fix image rotation on Android * Fix landscape mode for login and login options * Fix previewer will receive props * Move device dimensions and others to redux * Fix unit tests * Include orientation and tablet in the store
This commit is contained in:
parent
59762e4fac
commit
4995a76f2c
57 changed files with 673 additions and 323 deletions
6
Makefile
6
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 \
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@
|
|||
<service android:name=".NotificationReplyService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name="com.reactnativenavigation.controllers.NavigationActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
55
app/actions/device/index.js
Normal file
55
app/actions/device/index.js
Normal file
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 = (
|
||||
<View style={style.swiperContent}>
|
||||
|
|
@ -268,7 +299,7 @@ export default class ChannelDrawer extends PureComponent {
|
|||
|
||||
return (
|
||||
<DrawerSwiper
|
||||
ref='swiper'
|
||||
ref={this.drawerSwiperRef}
|
||||
onPageSelected={this.onPageSelected}
|
||||
openDrawerOffset={openDrawerOffset}
|
||||
showTeams={showTeams}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
// 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 Swiper from 'react-native-swiper';
|
||||
|
||||
import {changeOpacity} from 'app/utils/theme';
|
||||
|
||||
export default class DrawerSwiper extends PureComponent {
|
||||
static propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
deviceHeight: PropTypes.number.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
isLandscape: PropTypes.bool.isRequired,
|
||||
onPageSelected: PropTypes.func,
|
||||
openDrawerOffset: PropTypes.number,
|
||||
showTeams: PropTypes.bool.isRequired,
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onPageSelected: () => 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 (
|
||||
<Swiper
|
||||
ref='swiper'
|
||||
horizontal={true}
|
||||
loop={false}
|
||||
index={this.state.index}
|
||||
onMomentumScrollEnd={this.swiperPageSelected}
|
||||
paginationStyle={[{position: 'absolute'}, pagination]}
|
||||
width={deviceWidth - openDrawerOffset}
|
||||
height={deviceHeight}
|
||||
style={{backgroundColor: theme.sidebarBg}}
|
||||
activeDotColor={theme.sidebarText}
|
||||
dotColor={changeOpacity(theme.sidebarText, 0.5)}
|
||||
removeClippedSubviews={true}
|
||||
automaticallyAdjustContentInsets={true}
|
||||
scrollEnabled={showTeams}
|
||||
showsPagination={showTeams}
|
||||
keyboardShouldPersistTaps={'always'}
|
||||
>
|
||||
{children}
|
||||
</Swiper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 (
|
||||
<Swiper
|
||||
ref='swiper'
|
||||
horizontal={true}
|
||||
loop={false}
|
||||
index={1}
|
||||
onMomentumScrollEnd={this.swiperPageSelected}
|
||||
paginationStyle={[{position: 'absolute'}, pagination]}
|
||||
width={deviceWidth - openDrawerOffset}
|
||||
height={deviceHeight}
|
||||
style={{backgroundColor: theme.sidebarBg}}
|
||||
activeDotColor={theme.sidebarText}
|
||||
dotColor={changeOpacity(theme.sidebarText, 0.5)}
|
||||
removeClippedSubviews={true}
|
||||
automaticallyAdjustContentInsets={true}
|
||||
scrollEnabled={showTeams}
|
||||
showsPagination={showTeams}
|
||||
keyboardShouldPersistTaps={'always'}
|
||||
>
|
||||
{children}
|
||||
</Swiper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null, null, {withRef: true})(DraweSwiper);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
|||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderEmojis = (emojis, index) => {
|
||||
const {theme} = this.props;
|
||||
|
|
@ -157,13 +165,13 @@ class EmojiPicker extends PureComponent {
|
|||
})}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
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)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
|||
</View>
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.props.channelIsLoading || (!this.props.files.length && !this.props.filesUploadingForCurrentChannel)) {
|
||||
|
|
@ -107,7 +105,7 @@ export default class FileUploadPreview extends PureComponent {
|
|||
|
||||
return (
|
||||
<KeyboardLayout>
|
||||
<View style={[style.container]}>
|
||||
<View style={[style.container, {height: this.props.deviceHeight}]}>
|
||||
<ScrollView
|
||||
horizontal={true}
|
||||
style={style.scrollView}
|
||||
|
|
@ -124,7 +122,6 @@ export default class FileUploadPreview extends PureComponent {
|
|||
const style = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
height: deviceHeight,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
position: 'absolute',
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {createSelector} from 'reselect';
|
|||
|
||||
import {handleRemoveFile, retryFileUpload} from 'app/actions/views/file_upload';
|
||||
import {addFileToFetchCache} from 'app/actions/views/file_preview';
|
||||
import {getDimensions} from 'app/selectors/device';
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
|
||||
import FileUploadPreview from './file_upload_preview';
|
||||
|
|
@ -25,10 +26,13 @@ const checkForFileUploadingInChannel = createSelector(
|
|||
);
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
const {deviceHeight} = getDimensions(state);
|
||||
|
||||
return {
|
||||
...ownProps,
|
||||
channelIsLoading: state.views.channel.loading,
|
||||
createPostRequestStatus: state.requests.posts.createPost.status,
|
||||
deviceHeight,
|
||||
fetchCache: state.views.fetchCache,
|
||||
filesUploadingForCurrentChannel: checkForFileUploadingInChannel(state, ownProps.channelId, ownProps.rootId),
|
||||
theme: getTheme(state)
|
||||
|
|
|
|||
|
|
@ -6,20 +6,20 @@ import {connect} from 'react-redux';
|
|||
|
||||
import {close as closeWebSocket, init as initWebSocket} from 'mattermost-redux/actions/websocket';
|
||||
|
||||
import {getConnection} from 'app/selectors/device';
|
||||
|
||||
import OfflineIndicator from './offline_indicator';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
const {websocket} = state.requests.general;
|
||||
const {appState} = state.entities.general;
|
||||
const {connection} = state.views;
|
||||
|
||||
const webSocketStatus = websocket.status;
|
||||
const isConnecting = websocket.error >= 2;
|
||||
|
||||
return {
|
||||
appState,
|
||||
isConnecting,
|
||||
isOnline: connection,
|
||||
isOnline: getConnection(state),
|
||||
webSocketStatus,
|
||||
...ownProps
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<View
|
||||
style={[style.container, {width, height}]}
|
||||
style={style.container}
|
||||
>
|
||||
<Animatable.View
|
||||
ref='view'
|
||||
|
|
@ -170,9 +168,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
container: {
|
||||
position: 'absolute',
|
||||
backgroundColor: changeOpacity('#000', 0.3),
|
||||
height: '100%',
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 10
|
||||
zIndex: 10,
|
||||
width: '100%'
|
||||
},
|
||||
wrapper: {
|
||||
flex: 1,
|
||||
|
|
|
|||
12
app/constants/device.js
Normal file
12
app/constants/device.js
Normal file
|
|
@ -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
|
||||
});
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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: ''
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
30
app/reducers/device/dimension.js
Normal file
30
app/reducers/device/dimension.js
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
||||
18
app/reducers/device/index.js
Normal file
18
app/reducers/device/index.js
Normal file
|
|
@ -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
|
||||
});
|
||||
14
app/reducers/device/is_tablet.js
Normal file
14
app/reducers/device/is_tablet.js
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
||||
14
app/reducers/device/orientation.js
Normal file
14
app/reducers/device/orientation.js
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
||||
14
app/reducers/device/status_bar.js
Normal file
14
app/reducers/device/status_bar.js
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
>
|
||||
<StatusBar/>
|
||||
<View>
|
||||
<OfflineIndicator/>
|
||||
<View style={style.header}>
|
||||
<ChannelDrawerButton/>
|
||||
<ChannelTitle
|
||||
onPress={this.goToChannelInfo}
|
||||
/>
|
||||
<ChannelSearchButton navigator={navigator}/>
|
||||
</View>
|
||||
</View>
|
||||
<KeyboardLayout
|
||||
behavior='padding'
|
||||
style={style.keyboardLayout}
|
||||
|
|
@ -216,16 +225,6 @@ class Channel extends PureComponent {
|
|||
navigator={navigator}
|
||||
/>
|
||||
</KeyboardLayout>
|
||||
<View style={style.headerContainer}>
|
||||
<View style={style.header}>
|
||||
<ChannelDrawerButton/>
|
||||
<ChannelTitle
|
||||
onPress={this.goToChannelInfo}
|
||||
/>
|
||||
<ChannelSearchButton navigator={navigator}/>
|
||||
</View>
|
||||
<OfflineIndicator/>
|
||||
</View>
|
||||
</ChannelDrawer>
|
||||
);
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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%'
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<TouchableOpacity
|
||||
onPress={() => 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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
|||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
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 (
|
||||
<View style={[styles.container, {height: containerHeight}]}>
|
||||
<AnimatedView style={[styles.downloader, {top: this.state.downloaderTop}]}>
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
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 (
|
||||
<View
|
||||
style={[style.wrapper, {height: this.state.deviceHeight, width: this.state.deviceWidth}]}
|
||||
onLayout={this.onLayout}
|
||||
>
|
||||
<View style={[style.wrapper, {height: this.props.deviceHeight, width: this.props.deviceWidth}]}>
|
||||
<AnimatedView
|
||||
style={[this.state.drag.getLayout(), {opacity: this.state.wrapperViewOpacity}]}
|
||||
{...this.mainViewPanResponder.panHandlers}
|
||||
|
|
@ -332,7 +324,6 @@ export default class ImagePreview extends PureComponent {
|
|||
bounces={false}
|
||||
onScroll={this.handleScroll}
|
||||
scrollEventThrottle={2}
|
||||
|
||||
>
|
||||
{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 (
|
||||
<View
|
||||
key={file.id}
|
||||
style={[style.pageWrapper, {height: this.state.deviceHeight, width: this.state.deviceWidth}]}
|
||||
style={[style.pageWrapper, {height: this.props.deviceHeight, width: this.props.deviceWidth}]}
|
||||
>
|
||||
{component}
|
||||
</View>
|
||||
|
|
@ -380,7 +371,7 @@ export default class ImagePreview extends PureComponent {
|
|||
})}
|
||||
</ScrollView>
|
||||
<AnimatedView
|
||||
style={[style.footerHeaderWrapper, {height: this.state.deviceHeight, width: this.state.deviceWidth, opacity: this.state.footerOpacity}]}
|
||||
style={[style.footerHeaderWrapper, {height: this.props.deviceHeight, width: this.props.deviceWidth, opacity: this.state.footerOpacity}]}
|
||||
pointerEvents='box-none'
|
||||
>
|
||||
<View style={style.header}>
|
||||
|
|
@ -417,6 +408,8 @@ export default class ImagePreview extends PureComponent {
|
|||
<Downloader
|
||||
show={this.state.showDownloader}
|
||||
file={this.props.files[this.state.currentFile]}
|
||||
deviceHeight={this.props.deviceHeight}
|
||||
deviceWidth={this.props.deviceWidth}
|
||||
onDownloadCancel={this.hideDownloader}
|
||||
onDownloadStart={this.hideDownloader}
|
||||
onDownloadSuccess={this.hideDownloader}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {connect} from 'react-redux';
|
|||
import {Platform} from 'react-native';
|
||||
|
||||
import {addFileToFetchCache} from 'app/actions/views/file_preview';
|
||||
import {getDimensions, getStatusBarHeight} from 'app/selectors/device';
|
||||
import {getTheme} from 'app/selectors/preferences';
|
||||
import {canDownloadFilesOnMobile} from 'mattermost-redux/selectors/entities/general';
|
||||
import {makeGetFilesForPost} from 'mattermost-redux/selectors/entities/files';
|
||||
|
|
@ -19,11 +20,12 @@ function makeMapStateToProps() {
|
|||
return function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps,
|
||||
...getDimensions(state),
|
||||
canDownloadFiles: canDownloadFilesOnMobile(state),
|
||||
fetchCache: state.views.fetchCache,
|
||||
files: getFilesForPost(state, ownProps.postId),
|
||||
theme: getTheme(state),
|
||||
statusBarHeight: Platform.OS === 'ios' ? state.views.root.statusBarHeight : STATUSBAR_HEIGHT
|
||||
statusBarHeight: Platform.OS === 'ios' ? getStatusBarHeight(state) : STATUSBAR_HEIGHT
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,6 +90,14 @@ export default class Previewer extends Component {
|
|||
this.setShrink();
|
||||
} else if (!this.props.shrink && nextProps.shrink) {
|
||||
this.setShrink(true);
|
||||
} else if (
|
||||
nextProps.imageHeight !== this.props.imageHeight ||
|
||||
nextProps.imageWidth !== this.props.imageWidth
|
||||
) {
|
||||
this.setState({
|
||||
imageHeight: new Animated.Value(nextProps.imageHeight),
|
||||
imageWidth: new Animated.Value(nextProps.imageWidth)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +256,7 @@ export default class Previewer extends Component {
|
|||
<View
|
||||
{...this.panResponder.panHandlers}
|
||||
onResponderRelease={this.handleResponderRelease}
|
||||
style={[style.fileImageWrapper, {height: wrapperHeight, width: wrapperWidth}]}
|
||||
style={[style.fileImageWrapper, {height: '100%', width: '100%'}]}
|
||||
>
|
||||
<AnimatedView style={{height: imageHeight, width: this.state.imageWidth, backgroundColor: '#000', opacity}}>
|
||||
<ImageView
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import UserProfile from 'app/screens/user_profile';
|
|||
|
||||
import IntlWrapper from 'app/components/root';
|
||||
|
||||
function wrapWithContextProvider(Comp, excludeEvents = false) {
|
||||
function wrapWithContextProvider(Comp, excludeEvents = true) {
|
||||
return (props) => { //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);
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<KeyboardAvoidingView
|
||||
behavior='padding'
|
||||
style={{flex: 1}}
|
||||
keyboardVerticalOffset={Platform.OS === 'ios' ? 65 : 0}
|
||||
>
|
||||
<View style={{flex: 1}}>
|
||||
<StatusBar/>
|
||||
<TouchableWithoutFeedback onPress={this.blur}>
|
||||
<View style={[GlobalStyles.container, GlobalStyles.signupContainer]}>
|
||||
<KeyboardAwareScrollView
|
||||
ref={this.scrollRef}
|
||||
style={style.container}
|
||||
contentContainerStyle={style.innerContainer}
|
||||
>
|
||||
<Image
|
||||
source={logo}
|
||||
/>
|
||||
|
|
@ -347,6 +348,7 @@ class Login extends PureComponent {
|
|||
returnKeyType='next'
|
||||
underlineColorAndroid='transparent'
|
||||
onSubmitEditing={this.passwordFocus}
|
||||
blurOnSubmit={false}
|
||||
/>
|
||||
<TextInput
|
||||
ref={this.passwordRef}
|
||||
|
|
@ -362,11 +364,25 @@ class Login extends PureComponent {
|
|||
onSubmitEditing={this.preSignIn}
|
||||
/>
|
||||
{proceed}
|
||||
</View>
|
||||
</KeyboardAwareScrollView>
|
||||
</TouchableWithoutFeedback>
|
||||
</KeyboardAvoidingView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const style = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
flex: 1
|
||||
},
|
||||
innerContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 50
|
||||
}
|
||||
});
|
||||
|
||||
export default injectIntl(Login);
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<View style={[GlobalStyles.container, GlobalStyles.signupContainer]}>
|
||||
<ScrollView
|
||||
style={style.container}
|
||||
contentContainerStyle={style.innerContainer}
|
||||
ref={this.scrollRef}
|
||||
>
|
||||
<StatusBar/>
|
||||
<Image
|
||||
source={logo}
|
||||
|
|
@ -196,9 +218,23 @@ class LoginOptions extends PureComponent {
|
|||
{this.renderLdapOption()}
|
||||
{this.renderGitlabOption()}
|
||||
{this.renderSamlOption()}
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const style = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
flex: 1
|
||||
},
|
||||
innerContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 50
|
||||
}
|
||||
});
|
||||
|
||||
export default injectIntl(LoginOptions);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<View style={style.container}>
|
||||
<View style={[style.container, {width: deviceWidth}]}>
|
||||
<TouchableOpacity
|
||||
style={{flex: 1, flexDirection: 'row'}}
|
||||
onPress={this.notificationTapped}
|
||||
|
|
@ -208,7 +209,6 @@ const style = StyleSheet.create({
|
|||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
paddingHorizontal: 10,
|
||||
width: Dimensions.get('window').width,
|
||||
...Platform.select({
|
||||
android: {
|
||||
height: 68
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@
|
|||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {getDimensions} from 'app/selectors/device';
|
||||
|
||||
import OptionsModal from './options_modal';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
...ownProps
|
||||
...ownProps,
|
||||
...getDimensions(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import React, {PureComponent} from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Animated,
|
||||
Dimensions,
|
||||
StyleSheet,
|
||||
TouchableWithoutFeedback,
|
||||
View
|
||||
|
|
@ -19,12 +18,13 @@ import {emptyFunction} from 'app/utils/general';
|
|||
import OptionsModalList from './options_modal_list';
|
||||
|
||||
const {View: AnimatedView} = Animated;
|
||||
const {height: deviceHeight, width: deviceWidth} = Dimensions.get('window');
|
||||
const DURATION = 200;
|
||||
|
||||
export default class OptionsModal extends PureComponent {
|
||||
static propTypes = {
|
||||
items: PropTypes.array.isRequired,
|
||||
deviceHeight: PropTypes.number.isRequired,
|
||||
deviceWidth: PropTypes.number.isRequired,
|
||||
navigator: PropTypes.object,
|
||||
onCancelPress: PropTypes.func,
|
||||
title: PropTypes.oneOfType([
|
||||
|
|
@ -35,12 +35,16 @@ export default class OptionsModal extends PureComponent {
|
|||
|
||||
static defaultProps = {
|
||||
onCancelPress: emptyFunction
|
||||
}
|
||||
|
||||
state = {
|
||||
top: new Animated.Value(deviceHeight)
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
top: new Animated.Value(props.deviceHeight)
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
EventEmitter.on(NavigationTypes.NAVIGATION_CLOSE_MODAL, this.close);
|
||||
Animated.timing(this.state.top, {
|
||||
|
|
@ -56,11 +60,11 @@ export default class OptionsModal extends PureComponent {
|
|||
handleCancel = () => {
|
||||
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 (
|
||||
<TouchableWithoutFeedback onPress={this.close}>
|
||||
<View style={style.wrapper}>
|
||||
<AnimatedView style={{height: deviceHeight, left: 0, top: this.state.top, width: deviceWidth}}>
|
||||
<AnimatedView style={{height: this.props.deviceHeight, left: 0, top: this.state.top, width: this.props.deviceWidth}}>
|
||||
<OptionsModalList
|
||||
items={items}
|
||||
onCancelPress={this.handleCancel}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import React, {Component} from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import {injectIntl, intlShape} from 'react-intl';
|
||||
import {
|
||||
Dimensions,
|
||||
Keyboard,
|
||||
Platform,
|
||||
SectionList,
|
||||
|
|
@ -609,7 +608,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
|||
return {
|
||||
header: {
|
||||
backgroundColor: theme.sidebarHeaderBg,
|
||||
width: Dimensions.get('window').width,
|
||||
width: '100%',
|
||||
...Platform.select({
|
||||
android: {
|
||||
height: 46,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
};
|
||||
|
|
|
|||
22
app/selectors/device.js
Normal file
22
app/selectors/device.js
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 = "<group>"; };
|
||||
7F6877AA1E7835E50094B63F /* ToolTipMenu.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ToolTipMenu.xcodeproj; path = "../node_modules/react-native-tooltip/ToolTipMenu.xcodeproj"; sourceTree = "<group>"; };
|
||||
7FC200BC1EBB65100099331B /* ReactNativeNavigation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeNavigation.xcodeproj; path = "../node_modules/react-native-navigation/ios/ReactNativeNavigation.xcodeproj"; sourceTree = "<group>"; };
|
||||
7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNImagePicker.xcodeproj; path = "../node_modules/react-native-image-picker/ios/RNImagePicker.xcodeproj"; sourceTree = "<group>"; };
|
||||
7FEB10961F6101710039A015 /* BlurAppScreen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BlurAppScreen.h; path = Mattermost/BlurAppScreen.h; sourceTree = "<group>"; };
|
||||
7FEB10971F6101710039A015 /* BlurAppScreen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BlurAppScreen.m; path = Mattermost/BlurAppScreen.m; sourceTree = "<group>"; };
|
||||
7FEB10991F61019C0039A015 /* MattermostManaged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MattermostManaged.h; path = Mattermost/MattermostManaged.h; sourceTree = "<group>"; };
|
||||
|
|
@ -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 = "<group>";
|
||||
};
|
||||
7FDB92761F706F45006CDFD1 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7FDB92A71F706F45006CDFD1 /* libRNImagePicker.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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', {
|
||||
|
|
|
|||
Loading…
Reference in a new issue