[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:
parent
05a1c7c3d8
commit
e1c5536da8
6 changed files with 463 additions and 74 deletions
|
|
@ -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>
|
||||
`;
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
29
app/components/channel_loader/channel_loader.test.js
Normal file
29
app/components/channel_loader/channel_loader.test.js
Normal 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();
|
||||
});
|
||||
});
|
||||
|
|
@ -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
4
package-lock.json
generated
|
|
@ -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"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue