Keep the channel drawer opened on tablets (#2793)

* Keep the channel drawer opened on tablets

* update en.json after running mmjstool

* Set the channel drawer to width 220

* feedback review

* PM Review

* Fix snapshots
This commit is contained in:
Elias Nahum 2019-05-16 20:40:32 -04:00 committed by GitHub
parent d0071c21b0
commit 2b4574885b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 290 additions and 197 deletions

View file

@ -11,6 +11,7 @@ import {ImageContent} from 'rn-placeholder';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {DeviceTypes} from 'app/constants';
import CustomPropTypes from 'app/constants/custom_prop_types';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
@ -99,7 +100,12 @@ export default class ChannelLoader extends PureComponent {
};
render() {
const {channelIsLoading, maxRows, style: styleProp, theme} = this.props;
const {
channelIsLoading,
maxRows,
style: styleProp,
theme,
} = this.props;
if (!channelIsLoading) {
return null;
@ -107,9 +113,14 @@ export default class ChannelLoader extends PureComponent {
const style = getStyleSheet(theme);
const bg = this.props.backgroundColor || theme.centerChannelBg;
const containerStyle = [style.container];
if (DeviceTypes.IS_TABLET) {
containerStyle.push(style.tablet);
}
return (
<View style={[style.container, styleProp, {backgroundColor: bg}]}>
<View style={[containerStyle, styleProp, {backgroundColor: bg}]}>
{Array(maxRows).fill().map((item, index) => this.buildSections({
key: index,
style,
@ -125,12 +136,14 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
container: {
flex: 1,
},
tablet: {
...Platform.select({
android: {
top: 0,
paddingTop: 25,
},
ios: {
paddingTop: 15,
paddingTop: 30,
},
}),
},

View file

@ -156,6 +156,10 @@ export default class PostBodyAdditionalContent extends PureComponent {
const deviceSize = deviceWidth > deviceHeight ? deviceHeight : deviceWidth;
let maxWidth = deviceSize - 78;
if (maxWidth > MAX_YOUTUBE_IMAGE_WIDTH) {
maxWidth = MAX_YOUTUBE_IMAGE_WIDTH;
}
if (height <= MAX_YOUTUBE_IMAGE_HEIGHT) {
maxHeight = height;
} else {

View file

@ -37,6 +37,8 @@ export default class SearchBarIos extends PureComponent {
blurOnSubmit: PropTypes.bool,
value: PropTypes.string,
leftComponent: PropTypes.element,
searchIconCollapsedMargin: PropTypes.number,
searchIconExpandedMargin: PropTypes.number,
};
static defaultProps = {
@ -48,6 +50,8 @@ export default class SearchBarIos extends PureComponent {
onSelectionChange: () => true,
blurOnSubmit: true,
leftComponent: null,
searchIconCollapsedMargin: 10,
searchIconExpandedMargin: 10,
};
cancel = () => {
@ -102,8 +106,6 @@ export default class SearchBarIos extends PureComponent {
ref='search'
placeholderCollapsedMargin={33}
placeholderExpandedMargin={33}
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
shadowVisible={false}
onCancel={this.onCancel}
onChangeText={this.onChangeText}

View file

@ -76,6 +76,7 @@ export default class Search extends Component {
shadowRadius: PropTypes.number,
shadowVisible: PropTypes.bool,
leftComponent: PropTypes.element,
inputCollapsedMargin: PropTypes.number,
};
static defaultProps = {
@ -99,6 +100,7 @@ export default class Search extends Component {
shadowVisible: false,
value: '',
leftComponent: null,
inputCollapsedMargin: 10,
};
constructor(props) {
@ -310,7 +312,7 @@ export default class Search extends Component {
Animated.timing(
this.inputFocusWidthAnimated,
{
toValue: this.contentWidth - this.state.leftComponentWidth - 10,
toValue: this.contentWidth - this.state.leftComponentWidth - this.props.inputCollapsedMargin,
duration: 200,
}
),
@ -347,7 +349,7 @@ export default class Search extends Component {
Animated.timing(
this.iconSearchAnimated,
{
toValue: this.props.searchIconCollapsedMargin + this.state.leftComponentWidth,
toValue: (this.props.searchIconCollapsedMargin + this.state.leftComponentWidth),
duration: 200,
}
) : null),
@ -395,7 +397,7 @@ export default class Search extends Component {
</Animated.View> :
null
)}
<Animated.View style={{backgroundColor, right: this.inputFocusAnimated}}>
<Animated.View style={{backgroundColor, right: this.inputFocusAnimated, borderRadius: 2}}>
<AnimatedTextInput
ref='input_keyword'
style={[
@ -414,7 +416,6 @@ export default class Search extends Component {
shadowOpacity: this.shadowOpacityAnimated,
shadowRadius: this.props.shadowRadius,
},
]}
autoFocus={this.props.autoFocus}
editable={this.props.editable}

View file

@ -26,6 +26,9 @@ const VX_MAX = 0.1;
const IDLE = 'Idle';
const DRAGGING = 'Dragging';
const SETTLING = 'Settling';
const emptyObject = {};
export const TABLET_WIDTH = 250;
export type PropType = {
children: any,
@ -41,6 +44,7 @@ export type PropType = {
renderNavigationView: () => any,
statusBarBackgroundColor?: string,
useNativeAnimations?: boolean,
isTablet?: boolean,
};
export type StateType = {
@ -79,6 +83,7 @@ export default class DrawerLayout extends Component {
drawerWidth: 0,
drawerPosition: 'left',
useNativeAnimations: false,
isTablet: false,
};
static positions = {
@ -89,14 +94,42 @@ export default class DrawerLayout extends Component {
constructor(props: PropType, context: any) {
super(props, context);
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponder: this._shouldSetPanResponder,
onPanResponderGrant: this._panResponderGrant,
onPanResponderMove: this._panResponderMove,
onPanResponderTerminationRequest: () => false,
onPanResponderRelease: this._panResponderRelease,
onPanResponderTerminate: () => {},
});
this.canClose = true;
this.openValue = new Animated.Value(0);
this.state = {
accessibilityViewIsModal: false,
drawerShown: false,
openValue: new Animated.Value(0),
};
this.openValue.addListener(this.handleOpenValueChanged);
}
handleOpenValueChanged = ({ value }) => {
const drawerShown = value > 0;
const accessibilityViewIsModal = drawerShown;
if (drawerShown !== this.state.drawerShown) {
this.setState({ drawerShown, accessibilityViewIsModal });
}
if (this.props.keyboardDismissMode === 'on-drag') {
Keyboard.dismiss();
}
this._lastOpenValue = value;
if (this.props.onDrawerSlide) {
this.props.onDrawerSlide({ nativeEvent: { offset: value } });
}
};
getDrawerPosition() {
const { drawerPosition } = this.props;
const rtl = I18nManager.isRTL;
@ -105,38 +138,12 @@ export default class DrawerLayout extends Component {
: drawerPosition;
}
componentWillMount() {
const { openValue } = this.state;
renderDrawer = () => {
if (this.props.isTablet) {
return null;
}
openValue.addListener(({ value }) => {
const drawerShown = value > 0;
const accessibilityViewIsModal = drawerShown;
if (drawerShown !== this.state.drawerShown) {
this.setState({ drawerShown, accessibilityViewIsModal });
}
if (this.props.keyboardDismissMode === 'on-drag') {
Keyboard.dismiss();
}
this._lastOpenValue = value;
if (this.props.onDrawerSlide) {
this.props.onDrawerSlide({ nativeEvent: { offset: value } });
}
});
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponder: this._shouldSetPanResponder,
onPanResponderGrant: this._panResponderGrant,
onPanResponderMove: this._panResponderMove,
onPanResponderTerminationRequest: () => false,
onPanResponderRelease: this._panResponderRelease,
onPanResponderTerminate: () => {},
});
}
render() {
const { accessibilityViewIsModal, drawerShown, openValue } = this.state;
const { accessibilityViewIsModal, drawerShown } = this.state;
const {
drawerBackgroundColor,
@ -164,7 +171,7 @@ export default class DrawerLayout extends Component {
outputRange = [drawerWidth, 0];
}
const drawerTranslateX = openValue.interpolate({
const drawerTranslateX = this.openValue.interpolate({
inputRange: [0, 1],
outputRange,
extrapolate: 'clamp',
@ -174,7 +181,7 @@ export default class DrawerLayout extends Component {
};
/* Overlay styles */
const overlayOpacity = openValue.interpolate({
const overlayOpacity = this.openValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 0.7],
extrapolate: 'clamp',
@ -183,13 +190,7 @@ export default class DrawerLayout extends Component {
const pointerEvents = drawerShown ? 'auto' : 'none';
return (
<View
style={{ flex: 1, backgroundColor: 'transparent' }}
{...this._panResponder.panHandlers}
>
<Animated.View style={styles.main}>
{this.props.children}
</Animated.View>
<React.Fragment>
<TouchableWithoutFeedback
pointerEvents={pointerEvents}
onPress={this._onOverlayClick}
@ -207,8 +208,52 @@ export default class DrawerLayout extends Component {
animatedDrawerStyles,
]}
>
{this.props.renderNavigationView()}
{this.props.renderNavigationView(drawerWidth)}
</Animated.View>
</React.Fragment>
);
};
renderDrawerForTablet = () => {
const {accessibilityViewIsModal} = this.state;
const {
drawerWidth: width,
isTablet,
} = this.props;
if (isTablet) {
return (
<Animated.View
accessibilityViewIsModal={accessibilityViewIsModal}
style={[styles.tablet, {width}]}
>
{this.props.renderNavigationView(width)}
</Animated.View>
);
}
return null;
};
render() {
const {isTablet} = this.props;
const panHandlers = isTablet ? emptyObject : this._panResponder.panHandlers;
const containerStyles = [styles.container];
if (isTablet) {
containerStyles.push(styles.tabletContainer);
}
return (
<View
style={containerStyles}
{...panHandlers}
>
{this.renderDrawerForTablet()}
<Animated.View style={styles.main}>
{this.props.children}
</Animated.View>
{this.renderDrawer()}
</View>
);
}
@ -227,37 +272,42 @@ export default class DrawerLayout extends Component {
};
openDrawer = (options: DrawerMovementOptionType = {}) => {
this._emitStateChanged(SETTLING);
Animated.spring(this.state.openValue, {
toValue: 1,
bounciness: 0,
restSpeedThreshold: 0.1,
useNativeDriver: this.props.useNativeAnimations,
...options,
}).start(() => {
if (this.props.onDrawerOpen) {
telemetry.end(['channel:open_drawer']);
telemetry.save();
if (!this.props.isTablet) {
this._emitStateChanged(SETTLING);
Animated.spring(this.openValue, {
toValue: 1,
bounciness: 0,
restSpeedThreshold: 0.1,
useNativeDriver: this.props.useNativeAnimations,
...options,
}).start(() => {
this.handleOpenValueChanged({value: 1});
if (this.props.onDrawerOpen) {
telemetry.end(['channel:open_drawer']);
telemetry.save();
this.props.onDrawerOpen();
}
this._emitStateChanged(IDLE);
});
this.props.onDrawerOpen();
}
this._emitStateChanged(IDLE);
});
}
};
closeDrawer = (options: DrawerMovementOptionType = {}) => {
this._emitStateChanged(SETTLING);
Animated.spring(this.state.openValue, {
Animated.spring(this.openValue, {
toValue: 0,
bounciness: 0,
restSpeedThreshold: 1,
useNativeDriver: this.props.useNativeAnimations,
...options,
}).start(() => {
this.handleOpenValueChanged({value: 0});
if (this.props.onDrawerClose) {
telemetry.end(['channel:close_drawer']);
this.props.onDrawerClose();
}
this._emitStateChanged(IDLE);
});
};
@ -347,7 +397,7 @@ export default class DrawerLayout extends Component {
openValue = 0;
}
this.state.openValue.setValue(openValue);
this.openValue.setValue(openValue);
};
_panResponderRelease = (
@ -423,12 +473,23 @@ export default class DrawerLayout extends Component {
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'transparent',
},
tabletContainer: {
flexDirection: 'row',
},
drawer: {
position: 'absolute',
top: 0,
bottom: 0,
zIndex: 1001,
},
tablet: {
height: '100%',
zIndex: 0,
},
main: {
flex: 1,
zIndex: 0,
@ -437,7 +498,7 @@ const styles = StyleSheet.create({
backgroundColor: '#000',
position: 'absolute',
top: 0,
left: 0,
left: -350,
bottom: 0,
right: 0,
zIndex: 1000,

View file

@ -87,7 +87,7 @@ exports[`ChannelItem should match snapshot 1`] = `
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
@ -202,7 +202,7 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
@ -317,7 +317,7 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
@ -432,7 +432,7 @@ exports[`ChannelItem should match snapshot for deactivated user and is currentCh
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
@ -536,7 +536,7 @@ exports[`ChannelItem should match snapshot for deactivated user and is searchRes
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
@ -646,7 +646,7 @@ exports[`ChannelItem should match snapshot with draft 1`] = `
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {
@ -752,7 +752,7 @@ exports[`ChannelItem should match snapshot with mentions and muted 1`] = `
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"paddingRight": 10,
"textAlignVertical": "center",
},
Object {

View file

@ -241,7 +241,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
color: changeOpacity(theme.sidebarText, 0.4),
fontSize: 14,
fontWeight: '600',
paddingRight: 40,
paddingRight: 10,
height: '100%',
flex: 1,
textAlignVertical: 'center',

View file

@ -132,8 +132,9 @@ export default class ChannelsList extends PureComponent {
ref='search_bar'
placeholder={intl.formatMessage({id: 'mobile.channel_drawer.search', defaultMessage: 'Jump to...'})}
cancelTitle={intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}
inputCollapsedMargin={0}
backgroundColor='transparent'
inputHeight={34}
inputHeight={33}
inputStyle={searchBarInput}
placeholderTextColor={changeOpacity(theme.sidebarHeaderTextColor, 0.5)}
tintColorSearch={changeOpacity(theme.sidebarHeaderTextColor, 0.5)}
@ -144,6 +145,8 @@ export default class ChannelsList extends PureComponent {
onCancelButtonPress={this.cancelSearch}
onChangeText={this.onSearch}
onFocus={this.onSearchFocused}
searchIconCollapsedMargin={5}
searchIconExpandedMargin={5}
value={term}
leftComponent={(
<SwitchTeamsButton
@ -158,10 +161,8 @@ export default class ChannelsList extends PureComponent {
<View
style={styles.container}
>
<View style={styles.statusBar}>
<View style={styles.headerContainer}>
{title}
</View>
<View style={styles.headerContainer}>
{title}
</View>
{list}
</View>
@ -175,13 +176,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
backgroundColor: theme.sidebarBg,
flex: 1,
},
statusBar: {
backgroundColor: theme.sidebarHeaderBg,
},
headerContainer: {
alignItems: 'center',
paddingLeft: 10,
backgroundColor: theme.sidebarHeaderBg,
backgroundColor: theme.sidebarBg,
flexDirection: 'row',
borderBottomWidth: 1,
borderBottomColor: changeOpacity(theme.sidebarHeaderTextColor, 0.10),

View file

@ -12,9 +12,8 @@ import Swiper from 'app/components/swiper';
export default class DrawerSwiper extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
deviceWidth: PropTypes.number.isRequired,
drawerWidth: PropTypes.number.isRequired,
onPageSelected: PropTypes.func,
openDrawerOffset: PropTypes.number,
showTeams: PropTypes.bool.isRequired,
theme: PropTypes.object.isRequired,
drawerOpened: PropTypes.bool,
@ -25,11 +24,12 @@ export default class DrawerSwiper extends Component {
openDrawerOffset: 0,
};
swiperRef = React.createRef();
shouldComponentUpdate(nextProps) {
const {deviceWidth, openDrawerOffset, showTeams, theme} = this.props;
return nextProps.deviceWidth !== deviceWidth ||
const {drawerWidth, showTeams, theme} = this.props;
return nextProps.drawerWidth !== drawerWidth ||
nextProps.showTeams !== showTeams ||
nextProps.openDrawerOffset !== openDrawerOffset ||
nextProps.theme !== theme ||
nextProps.drawerOpened !== this.props.drawerOpened;
}
@ -40,15 +40,15 @@ export default class DrawerSwiper extends Component {
}
};
resetPage = () => {
if (this.refs.swiper) {
this.refs.swiper.scrollToIndex(1, false);
resetPage = (animated = false) => {
if (this.swiperRef?.current) {
this.swiperRef.current.scrollToIndex(1, animated);
}
};
scrollToStart = () => {
if (this.refs.swiper) {
this.refs.swiper.scrollToStart();
if (this.swiperRef?.current) {
this.swiperRef.current.scrollToStart();
}
};
@ -57,8 +57,8 @@ export default class DrawerSwiper extends Component {
};
showTeamsPage = () => {
if (this.refs.swiper) {
this.refs.swiper.scrollToIndex(0, true);
if (this.swiperRef?.current) {
this.swiperRef.current.scrollToIndex(0, true);
this.swiperPageSelected(0);
}
};
@ -66,8 +66,7 @@ export default class DrawerSwiper extends Component {
render() {
const {
children,
deviceWidth,
openDrawerOffset,
drawerWidth,
showTeams,
theme,
} = this.props;
@ -76,11 +75,11 @@ export default class DrawerSwiper extends Component {
return (
<Swiper
ref='swiper'
ref={this.swiperRef}
initialPage={initialPage}
onIndexChanged={this.swiperPageSelected}
paginationStyle={style.pagination}
width={deviceWidth - openDrawerOffset}
width={drawerWidth}
style={{backgroundColor: theme.sidebarBg}}
activeDotColor={theme.sidebarText}
dotColor={changeOpacity(theme.sidebarText, 0.5)}

View file

@ -5,13 +5,10 @@ import {connect} from 'react-redux';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getDimensions} from 'app/selectors/device';
import DraweSwiper from './drawer_swiper';
function mapStateToProps(state) {
return {
...getDimensions(state),
theme: getTheme(state),
};
}

View file

@ -11,7 +11,7 @@ import {getCurrentTeamId, getMyTeamsCount} from 'mattermost-redux/selectors/enti
import {setChannelDisplayName, setChannelLoading} from 'app/actions/views/channel';
import {makeDirectChannel} from 'app/actions/views/more_dms';
import {isLandscape, isTablet, getDimensions} from 'app/selectors/device';
import {isLandscape, getDimensions} from 'app/selectors/device';
import telemetry from 'app/telemetry';
import MainSidebar from './main_sidebar.js';
@ -42,7 +42,6 @@ function mapStateToProps(state) {
currentTeamId: getCurrentTeamId(state),
currentUserId,
isLandscape: isLandscape(state),
isTablet: isTablet(state),
teamsCount: getMyTeamsCount(state),
theme: getTheme(state),
};

View file

@ -15,7 +15,8 @@ import {General, WebsocketEvents} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import SafeAreaView from 'app/components/safe_area_view';
import DrawerLayout from 'app/components/sidebars/drawer_layout';
import DrawerLayout, {TABLET_WIDTH} from 'app/components/sidebars/drawer_layout';
import {DeviceTypes} from 'app/constants';
import tracker from 'app/utils/time_tracker';
import {t} from 'app/utils/i18n';
@ -43,7 +44,6 @@ export default class ChannelSidebar extends Component {
currentUserId: PropTypes.string.isRequired,
deviceWidth: PropTypes.number.isRequired,
isLandscape: PropTypes.bool.isRequired,
isTablet: PropTypes.bool.isRequired,
navigator: PropTypes.object,
teamsCount: PropTypes.number.isRequired,
theme: PropTypes.object.isRequired,
@ -57,11 +57,12 @@ export default class ChannelSidebar extends Component {
super(props);
let openDrawerOffset = DRAWER_INITIAL_OFFSET;
if (props.isLandscape || props.isTablet) {
if (props.isLandscape || DeviceTypes.IS_TABLET) {
openDrawerOffset = DRAWER_LANDSCAPE_OFFSET;
}
this.swiperIndex = 1;
this.drawerRef = React.createRef();
this.state = {
show: false,
openDrawerOffset,
@ -70,11 +71,8 @@ export default class ChannelSidebar extends Component {
};
}
componentWillMount() {
this.props.actions.getTeams();
}
componentDidMount() {
this.props.actions.getTeams();
EventEmitter.on('close_channel_drawer', this.closeChannelDrawer);
EventEmitter.on('renderDrawer', this.handleShowDrawerContent);
EventEmitter.on(WebsocketEvents.CHANNEL_UPDATED, this.handleUpdateTitle);
@ -83,12 +81,14 @@ export default class ChannelSidebar extends Component {
componentWillReceiveProps(nextProps) {
const {isLandscape} = this.props;
if (nextProps.isLandscape !== isLandscape) {
if (this.state.openDrawerOffset !== 0) {
let openDrawerOffset = DRAWER_INITIAL_OFFSET;
if (nextProps.isLandscape || this.props.isTablet) {
if (nextProps.isLandscape || DeviceTypes.IS_TABLET) {
openDrawerOffset = DRAWER_LANDSCAPE_OFFSET;
}
this.setState({openDrawerOffset});
}
}
@ -115,8 +115,8 @@ export default class ChannelSidebar extends Component {
}
handleAndroidBack = () => {
if (this.state.drawerOpened && this.refs.drawer) {
this.refs.drawer.closeDrawer();
if (this.state.drawerOpened && this.drawerRef?.current) {
this.drawerRef.current.closeDrawer();
return true;
}
@ -128,8 +128,10 @@ export default class ChannelSidebar extends Component {
};
closeChannelDrawer = () => {
if (this.state.drawerOpened && this.refs.drawer) {
this.refs.drawer.closeDrawer();
if (this.state.drawerOpened && this.drawerRef?.current) {
this.drawerRef.current.closeDrawer();
} else if (this.drawerSwiper && DeviceTypes.IS_TABLET) {
this.resetDrawer(true);
}
};
@ -162,8 +164,8 @@ export default class ChannelSidebar extends Component {
openChannelSidebar = () => {
this.props.blurPostTextBox();
if (this.refs.drawer) {
this.refs.drawer.openDrawer();
if (this.drawerRef?.current) {
this.drawerRef.current.openDrawer();
}
};
@ -255,12 +257,8 @@ export default class ChannelSidebar extends Component {
onPageSelected = (index) => {
this.swiperIndex = index;
if (this.refs.drawer) {
if (this.swiperIndex === 0) {
this.refs.drawer.canClose = false;
} else {
this.refs.drawer.canClose = true;
}
if (this.drawerRef?.current) {
this.drawerRef.current.canClose = this.swiperIndex !== 0;
}
};
@ -269,8 +267,8 @@ export default class ChannelSidebar extends Component {
};
onSearchStart = () => {
if (this.refs.drawer) {
this.refs.drawer.canClose = false;
if (this.drawerRef?.current) {
this.drawerRef.current.canClose = false;
}
this.setState({searching: true});
};
@ -287,7 +285,7 @@ export default class ChannelSidebar extends Component {
}
};
renderNavigationView = () => {
renderNavigationView = (drawerWidth) => {
const {
navigator,
teamsCount,
@ -350,6 +348,7 @@ export default class ChannelSidebar extends Component {
return (
<SafeAreaView
navBarBackgroundColor={theme.sidebarBg}
backgroundColor={theme.sidebarHeaderBg}
footerColor={theme.sidebarHeaderBg}
navigator={navigator}
@ -357,9 +356,9 @@ export default class ChannelSidebar extends Component {
<DrawerSwiper
ref={this.drawerSwiperRef}
onPageSelected={this.onPageSelected}
openDrawerOffset={openDrawerOffset}
showTeams={showTeams}
drawerOpened={this.state.drawerOpened}
drawerWidth={drawerWidth}
>
{lists}
</DrawerSwiper>
@ -370,15 +369,17 @@ export default class ChannelSidebar extends Component {
render() {
const {children, deviceWidth} = this.props;
const {openDrawerOffset} = this.state;
const drawerWidth = DeviceTypes.IS_TABLET ? TABLET_WIDTH : (deviceWidth - openDrawerOffset);
return (
<DrawerLayout
ref='drawer'
ref={this.drawerRef}
renderNavigationView={this.renderNavigationView}
onDrawerClose={this.handleDrawerClose}
onDrawerOpen={this.handleDrawerOpen}
drawerWidth={deviceWidth - openDrawerOffset}
drawerWidth={drawerWidth}
useNativeAnimations={true}
isTablet={DeviceTypes.IS_TABLET}
>
{children}
</DrawerLayout>

View file

@ -139,15 +139,13 @@ export default class TeamsList extends PureComponent {
return (
<View style={styles.container}>
<View style={styles.statusBar}>
<View style={styles.headerContainer}>
<FormattedText
id='mobile.drawer.teamsTitle'
defaultMessage='Teams'
style={styles.header}
/>
{moreAction}
</View>
<View style={styles.headerContainer}>
<FormattedText
id='mobile.drawer.teamsTitle'
defaultMessage='Teams'
style={styles.header}
/>
{moreAction}
</View>
<FlatList
data={teamIds}
@ -166,12 +164,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
backgroundColor: theme.sidebarBg,
flex: 1,
},
statusBar: {
backgroundColor: theme.sidebarHeaderBg,
},
headerContainer: {
alignItems: 'center',
backgroundColor: theme.sidebarHeaderBg,
backgroundColor: theme.sidebarBg,
flexDirection: 'row',
borderBottomWidth: 1,
borderBottomColor: changeOpacity(theme.sidebarHeaderTextColor, 0.10),

View file

@ -8,7 +8,7 @@ import {logout, setStatus} from 'mattermost-redux/actions/users';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentUser, getStatusForUserId} from 'mattermost-redux/selectors/entities/users';
import {getDimensions} from 'app/selectors/device';
import {isLandscape, getDimensions} from 'app/selectors/device';
import SettingsSidebar from './settings_sidebar';
@ -18,6 +18,7 @@ function mapStateToProps(state) {
return {
...getDimensions(state),
isLandscape: isLandscape(state),
currentUser,
status,
theme: getTheme(state),

View file

@ -19,7 +19,7 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
import SafeAreaView from 'app/components/safe_area_view';
import DrawerLayout from 'app/components/sidebars/drawer_layout';
import UserStatus from 'app/components/user_status';
import {NavigationTypes} from 'app/constants';
import {DeviceTypes, NavigationTypes} from 'app/constants';
import {confirmOutOfOfficeDisabled} from 'app/utils/status';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
@ -30,6 +30,7 @@ import UserInfo from './user_info';
import StatusLabel from './status_label';
const DRAWER_INITIAL_OFFSET = 80;
const DRAWER_TABLET_WIDTH = 300;
export default class SettingsDrawer extends PureComponent {
static propTypes = {
@ -41,6 +42,7 @@ export default class SettingsDrawer extends PureComponent {
children: PropTypes.node,
currentUser: PropTypes.object.isRequired,
deviceWidth: PropTypes.number.isRequired,
isLandscape: PropTypes.bool.isRequired,
navigator: PropTypes.object,
status: PropTypes.string,
theme: PropTypes.object.isRequired,
@ -349,6 +351,7 @@ export default class SettingsDrawer extends PureComponent {
render() {
const {children, deviceWidth} = this.props;
const drawerWidth = DeviceTypes.IS_TABLET ? DRAWER_TABLET_WIDTH : (deviceWidth - DRAWER_INITIAL_OFFSET);
return (
<DrawerLayout
@ -357,7 +360,7 @@ export default class SettingsDrawer extends PureComponent {
onDrawerClose={this.handleDrawerClose}
onDrawerOpen={this.handleDrawerOpen}
drawerPosition='right'
drawerWidth={deviceWidth - DRAWER_INITIAL_OFFSET}
drawerWidth={drawerWidth}
useNativeAnimations={true}
>
{children}
@ -373,7 +376,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.03),
},
wrapper: {
flex: 1,
paddingTop: 0,
},
block: {

View file

@ -149,8 +149,19 @@ const restartApp = async () => {
Navigation.dismissModal({animationType: 'none'});
try {
const window = Dimensions.get('window');
handleOrientationChange({window});
await store.dispatch(loadConfigAndLicense());
await store.dispatch(loadMe());
if (Platform.OS === 'ios') {
StatusBarManager.getHeight(
(data) => {
handleStatusBarHeightChange(data.height);
}
);
}
} catch (e) {
console.warn('Failed to load initial data while restarting', e); // eslint-disable-line no-console
}

View file

@ -5,7 +5,6 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {
PanResponder,
TouchableOpacity,
View,
} from 'react-native';
@ -21,7 +20,6 @@ import telemetry from 'app/telemetry';
import {getUnreadsInCurrentTeam} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentTeamId, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
class ChannelDrawerButton extends PureComponent {
static propTypes = {
@ -31,6 +29,7 @@ class ChannelDrawerButton extends PureComponent {
mentionCount: PropTypes.number,
myTeamMembers: PropTypes.object,
theme: PropTypes.object,
visible: PropTypes.bool,
};
static defaultProps = {
@ -38,36 +37,7 @@ class ChannelDrawerButton extends PureComponent {
theme: {},
messageCount: 0,
mentionCount: 0,
};
constructor(props) {
super(props);
this.state = {
opacity: 1,
};
}
componentWillMount() {
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onStartShouldSetResponderCapture: () => true,
onMoveShouldSetResponderCapture: () => true,
onResponderMove: () => false,
});
}
componentDidMount() {
EventEmitter.on('drawer_opacity', this.setOpacity);
}
componentWillUnmount() {
EventEmitter.off('drawer_opacity', this.setOpacity);
}
setOpacity = (value) => {
this.setState({opacity: value > 0 ? 0.1 : 1});
visible: true,
};
handlePress = preventDoubleTap(() => {
@ -82,7 +52,9 @@ class ChannelDrawerButton extends PureComponent {
messageCount,
myTeamMembers,
theme,
visible,
} = this.props;
const style = getStyleFromTheme(theme);
let mentions = mentionCount;
@ -102,7 +74,7 @@ class ChannelDrawerButton extends PureComponent {
}
let badge;
if (badgeCount) {
if (badgeCount && visible) {
badge = (
<Badge
style={style.badge}
@ -113,21 +85,28 @@ class ChannelDrawerButton extends PureComponent {
);
}
const icon = (
<Icon
name='md-menu'
size={25}
color={theme.sidebarHeaderTextColor}
/>
);
let icon;
let containerStyle;
if (visible) {
icon = (
<Icon
name='md-menu'
size={25}
color={theme.sidebarHeaderTextColor}
/>
);
containerStyle = style.container;
} else {
icon = (<View style={style.tabletIcon}/>);
containerStyle = style.tabletContainer;
}
return (
<TouchableOpacity
{...this.panResponder.panHandlers}
onPress={this.handlePress}
style={style.container}
style={containerStyle}
>
<View style={[style.wrapper, {opacity: this.state.opacity}]}>
<View style={[style.wrapper]}>
<View>
{icon}
{badge}
@ -143,6 +122,12 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
container: {
width: 55,
},
tabletContainer: {
width: 10,
},
tabletIcon: {
height: 25,
},
wrapper: {
alignItems: 'center',
flex: 1,

View file

@ -41,13 +41,15 @@ export default class ChannelNavBar extends PureComponent {
switch (Platform.OS) {
case 'android':
height = ANDROID_TOP_PORTRAIT;
if (isLandscape) {
if (DeviceTypes.IS_TABLET) {
height = ANDROID_TOP_LANDSCAPE;
}
break;
case 'ios':
height = IOS_TOP_PORTRAIT - STATUS_BAR_HEIGHT;
if (isLandscape) {
if (DeviceTypes.IS_TABLET && isLandscape) {
height -= 1;
} else if (isLandscape) {
height = IOS_TOP_LANDSCAPE;
}
@ -57,9 +59,17 @@ export default class ChannelNavBar extends PureComponent {
break;
}
let drawerButtonVisible = false;
if (!DeviceTypes.IS_TABLET) {
drawerButtonVisible = true;
}
return (
<View style={[style.header, padding, {height}]}>
<ChannelDrawerButton openDrawer={openChannelDrawer}/>
<ChannelDrawerButton
openDrawer={openChannelDrawer}
visible={drawerButtonVisible}
/>
<ChannelTitle onPress={onPress}/>
<ChannelSearchButton
navigator={navigator}

View file

@ -31,6 +31,8 @@ exports[`MoreChannels should match snapshot 1`] = `
onSearchButtonPress={[Function]}
onSelectionChange={[Function]}
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"

View file

@ -39,6 +39,8 @@ exports[`SelectorScreen should match snapshot for channels 1`] = `
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -127,6 +129,8 @@ exports[`SelectorScreen should match snapshot for channels 2`] = `
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -215,6 +219,8 @@ exports[`SelectorScreen should match snapshot for explicit options 1`] = `
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -310,6 +316,8 @@ exports[`SelectorScreen should match snapshot for searching 1`] = `
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -398,6 +406,8 @@ exports[`SelectorScreen should match snapshot for users 1`] = `
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"
@ -486,6 +496,8 @@ exports[`SelectorScreen should match snapshot for users 2`] = `
onSelectionChange={[Function]}
placeholder="Search"
placeholderTextColor="rgba(61,60,64,0.5)"
searchIconCollapsedMargin={10}
searchIconExpandedMargin={10}
tintColorDelete="rgba(61,60,64,0.5)"
tintColorSearch="rgba(61,60,64,0.5)"
titleCancelColor="#3d3c40"