[MM 15552] Determine number of placeholder rows from height (#2827)

* Calculate maxRows in ChannelLoader component

* Update rn-placeholder

* Fix style check
This commit is contained in:
Miguel Alatzar 2019-05-24 09:01:02 -07:00 committed by Harrison Healey
parent 05a1c7c3d8
commit e1c5536da8
6 changed files with 463 additions and 74 deletions

View file

@ -0,0 +1,383 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ChannelLoader should match snapshot 1`] = `
<View
onLayout={[Function]}
style={
Array [
Object {
"flex": 1,
},
undefined,
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
<View
style={
Array [
Object {
"backgroundColor": "#ffffff",
"flex": 1,
"flexDirection": "row",
"marginVertical": 10,
"paddingLeft": 12,
"paddingRight": 20,
},
Object {
"backgroundColor": "#ffffff",
},
]
}
>
<ImageContent
animate="fade"
color="rgba(61,60,64,0.2)"
firstLineWidth="80%"
hasRadius={true}
lineNumber={3}
lineSpacing={5}
size={32}
textSize={14}
/>
</View>
</View>
`;

View file

@ -4,17 +4,20 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
Platform,
View,
Dimensions,
} from 'react-native';
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';
function calculateMaxRows(height) {
return Math.round(height / 100);
}
export default class ChannelLoader extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
@ -23,28 +26,35 @@ export default class ChannelLoader extends PureComponent {
}).isRequired,
backgroundColor: PropTypes.string,
channelIsLoading: PropTypes.bool.isRequired,
maxRows: PropTypes.number,
style: CustomPropTypes.Style,
theme: PropTypes.object.isRequired,
height: PropTypes.number,
};
static defaultProps = {
maxRows: 6,
};
constructor(props) {
super(props);
state = {
switch: false,
};
const height = props.height || Dimensions.get('window').height;
const maxRows = calculateMaxRows(height);
this.state = {
switch: false,
maxRows,
};
}
static getDerivedStateFromProps(nextProps, prevState) {
const state = {};
if (nextProps.height) {
state.maxRows = calculateMaxRows(nextProps.height);
}
if (!nextProps.channelIsLoading && prevState.switch) {
return {
switch: false,
channel: null,
};
state.switch = false;
state.channel = null;
}
return null;
return Object.keys(state) ? state : null;
}
componentDidMount() {
@ -99,10 +109,15 @@ export default class ChannelLoader extends PureComponent {
}
};
handleLayout = (e) => {
const {height} = e.nativeEvent.layout;
const maxRows = calculateMaxRows(height);
this.setState({maxRows});
}
render() {
const {
channelIsLoading,
maxRows,
style: styleProp,
theme,
} = this.props;
@ -113,15 +128,13 @@ 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={[containerStyle, styleProp, {backgroundColor: bg}]}>
{Array(maxRows).fill().map((item, index) => this.buildSections({
<View
style={[style.container, styleProp, {backgroundColor: bg}]}
onLayout={this.handleLayout}
>
{Array(this.state.maxRows).fill().map((item, index) => this.buildSections({
key: index,
style,
bg,
@ -137,16 +150,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
container: {
flex: 1,
},
tablet: {
...Platform.select({
android: {
paddingTop: 25,
},
ios: {
paddingTop: 30,
},
}),
},
section: {
backgroundColor: theme.centerChannelBg,
flexDirection: 'row',

View file

@ -0,0 +1,29 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import Preferences from 'mattermost-redux/constants/preferences';
import ChannelLoader from './channel_loader';
jest.mock('rn-placeholder', () => ({
ImageContent: () => {},
}));
describe('ChannelLoader', () => {
const baseProps = {
channelIsLoading: true,
theme: Preferences.THEMES.default,
actions: {
handleSelectChannel: jest.fn(),
setChannelLoading: jest.fn(),
},
};
test('should match snapshot', () => {
const wrapper = shallow(<ChannelLoader {...baseProps}/>);
expect(wrapper.getElement()).toMatchSnapshot();
});
});

View file

@ -28,7 +28,7 @@ import SettingsSidebar from 'app/components/sidebars/settings';
import NetworkIndicator from 'app/components/network_indicator';
import SafeAreaView from 'app/components/safe_area_view';
import StatusBar from 'app/components/status_bar';
import {DeviceTypes, ViewTypes} from 'app/constants';
import {DeviceTypes} from 'app/constants';
import {preventDoubleTap} from 'app/utils/tap';
import PostTextbox from 'app/components/post_textbox';
import PushNotifications from 'app/push_notifications';
@ -40,14 +40,6 @@ import telemetry from 'app/telemetry';
import ChannelNavBar from './channel_nav_bar';
import ChannelPostList from './channel_post_list';
const {
ANDROID_TOP_LANDSCAPE,
ANDROID_TOP_PORTRAIT,
IOS_TOP_LANDSCAPE,
IOS_TOP_PORTRAIT,
IOSX_TOP_PORTRAIT,
} = ViewTypes;
const CHANNEL_POST_TEXTBOX_CURSOR_CHANGE = 'onChannelTextBoxCursorChange';
const CHANNEL_POST_TEXTBOX_VALUE_CHANGE = 'onChannelTextBoxValueChange';
@ -169,32 +161,6 @@ export default class Channel extends PureComponent {
}
};
channelLoaderDimensions = () => {
const {isLandscape} = this.props;
let top = 0;
let {height} = Dimensions.get('window');
switch (Platform.OS) {
case 'android':
if (isLandscape) {
top = ANDROID_TOP_LANDSCAPE;
} else {
top = ANDROID_TOP_PORTRAIT;
height -= 84;
}
break;
case 'ios':
if (isLandscape) {
top = IOS_TOP_LANDSCAPE;
} else {
height = DeviceTypes.IS_IPHONE_X ? (height - IOSX_TOP_PORTRAIT) : (height - IOS_TOP_PORTRAIT);
top = DeviceTypes.IS_IPHONE_X ? IOSX_TOP_PORTRAIT : IOS_TOP_PORTRAIT;
}
break;
}
return {height, top};
};
channelSidebarRef = (ref) => {
if (ref) {
this.channelSidebar = ref;
@ -316,6 +282,8 @@ export default class Channel extends PureComponent {
theme,
} = this.props;
const {height} = Dimensions.get('window');
if (!currentChannelId) {
if (channelsRequestFailed) {
const PostListRetry = require('app/components/post_list_retry').default;
@ -333,7 +301,7 @@ export default class Channel extends PureComponent {
<View style={style.flex}>
<EmptyToolbar
theme={theme}
isLandscape={this.props.isLandscape}
isLandscape={isLandscape}
/>
<Loading channelIsLoading={true}/>
</View>
@ -341,7 +309,10 @@ export default class Channel extends PureComponent {
);
}
const loaderDimensions = this.channelLoaderDimensions();
const channelLoaderStyle = [style.channelLoader, {height}];
if (Platform.OS === 'ios' && (DeviceTypes.IS_IPHONE_X || DeviceTypes.IS_TABLET)) {
channelLoaderStyle.push(style.iOSHomeIndicator);
}
return (
<MainSidebar
@ -375,8 +346,8 @@ export default class Channel extends PureComponent {
valueEvent={CHANNEL_POST_TEXTBOX_VALUE_CHANGE}
/>
<ChannelLoader
style={[style.channelLoader, loaderDimensions]}
maxRows={isLandscape ? 4 : 6}
height={height}
style={channelLoaderStyle}
/>
{LocalConfig.EnableMobileClientUpgrade && <ClientUpgradeListener navigator={navigator}/>}
</SafeAreaView>
@ -410,4 +381,7 @@ const style = StyleSheet.create({
width: '100%',
flex: 1,
},
iOSHomeIndicator: {
paddingBottom: 5,
},
});

4
package-lock.json generated
View file

@ -16790,8 +16790,8 @@
"dev": true
},
"rn-placeholder": {
"version": "github:mattermost/rn-placeholder#0389b32e0c7e2f88ea21b4bd978b5834fb09ab1d",
"from": "github:mattermost/rn-placeholder#0389b32e0c7e2f88ea21b4bd978b5834fb09ab1d",
"version": "github:mattermost/rn-placeholder#02c629c65d0123a2eee623ada0fd17186415d3c3",
"from": "github:mattermost/rn-placeholder#02c629c65d0123a2eee623ada0fd17186415d3c3",
"requires": {
"prop-types": "15.6.2"
},

View file

@ -67,7 +67,7 @@
"redux-thunk": "2.3.0",
"reselect": "4.0.0",
"rn-fetch-blob": "github:mattermost/react-native-fetch-blob#d5a1efb3166f3d42e4f316b4be99da82fdc159b4",
"rn-placeholder": "github:mattermost/rn-placeholder#0389b32e0c7e2f88ea21b4bd978b5834fb09ab1d",
"rn-placeholder": "github:mattermost/rn-placeholder#02c629c65d0123a2eee623ada0fd17186415d3c3",
"semver": "6.0.0",
"shallow-equals": "1.0.0",
"tinycolor2": "1.4.1",