[FIX] Vertically centralize RoomItem when `Store_Last_Message` is disabled (#2363)

* Split RoomItem into container and component

* Refactor RoomItem

* Fix wrong status

* Tests

* Wrapper
This commit is contained in:
Diego Mello 2020-07-31 14:06:22 -03:00 committed by GitHub
parent 363cd13207
commit 34824e0765
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 680 additions and 381 deletions

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ const Status = React.memo(({
borderRadius: size, borderRadius: size,
width: size, width: size,
height: size, height: size,
backgroundColor: STATUS_COLORS[status], backgroundColor: STATUS_COLORS[status] ?? STATUS_COLORS.offline,
borderColor: themes[theme].backgroundColor borderColor: themes[theme].backgroundColor
} }
]} ]}

View File

@ -0,0 +1,186 @@
import React from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';
import styles from './styles';
import Wrapper from './Wrapper';
import UnreadBadge from './UnreadBadge';
import TypeIcon from './TypeIcon';
import LastMessage from './LastMessage';
import Title from './Title';
import UpdatedAt from './UpdatedAt';
import Touchable from './Touchable';
const RoomItem = ({
rid,
type,
prid,
name,
avatar,
width,
avatarSize,
baseUrl,
userId,
username,
token,
showLastMessage,
status,
useRealName,
theme,
isFocused,
isGroupChat,
isRead,
date,
accessibilityLabel,
favorite,
lastMessage,
alert,
hideUnreadStatus,
unread,
userMentions,
groupMentions,
roomUpdatedAt,
testID,
onPress,
toggleFav,
toggleRead,
hideChannel
}) => (
<Touchable
onPress={onPress}
width={width}
favorite={favorite}
toggleFav={toggleFav}
isRead={isRead}
rid={rid}
toggleRead={toggleRead}
hideChannel={hideChannel}
testID={testID}
type={type}
theme={theme}
isFocused={isFocused}
>
<Wrapper
accessibilityLabel={accessibilityLabel}
avatar={avatar}
avatarSize={avatarSize}
type={type}
baseUrl={baseUrl}
userId={userId}
token={token}
theme={theme}
>
{showLastMessage
? (
<>
<View style={styles.titleContainer}>
<TypeIcon
type={type}
prid={prid}
status={status}
isGroupChat={isGroupChat}
theme={theme}
/>
<Title
name={name}
theme={theme}
hideUnreadStatus={hideUnreadStatus}
alert={alert}
/>
<UpdatedAt
roomUpdatedAt={roomUpdatedAt}
date={date}
theme={theme}
hideUnreadStatus={hideUnreadStatus}
alert={alert}
/>
</View>
<View style={styles.row}>
<LastMessage
lastMessage={lastMessage}
type={type}
showLastMessage={showLastMessage}
username={username}
alert={alert && !hideUnreadStatus}
useRealName={useRealName}
theme={theme}
/>
<UnreadBadge
unread={unread}
userMentions={userMentions}
groupMentions={groupMentions}
theme={theme}
/>
</View>
</>
)
: (
<View style={[styles.titleContainer, styles.flex]}>
<TypeIcon
type={type}
prid={prid}
status={status}
isGroupChat={isGroupChat}
theme={theme}
/>
<Title
name={name}
theme={theme}
hideUnreadStatus={hideUnreadStatus}
alert={alert}
/>
<UnreadBadge
unread={unread}
userMentions={userMentions}
groupMentions={groupMentions}
theme={theme}
/>
</View>
)
}
</Wrapper>
</Touchable>
);
RoomItem.propTypes = {
rid: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
prid: PropTypes.string,
name: PropTypes.string.isRequired,
avatar: PropTypes.string.isRequired,
baseUrl: PropTypes.string.isRequired,
showLastMessage: PropTypes.bool,
userId: PropTypes.string,
username: PropTypes.string,
token: PropTypes.string,
avatarSize: PropTypes.number,
testID: PropTypes.string,
width: PropTypes.number,
status: PropTypes.string,
useRealName: PropTypes.bool,
theme: PropTypes.string,
isFocused: PropTypes.bool,
isGroupChat: PropTypes.bool,
isRead: PropTypes.bool,
date: PropTypes.string,
accessibilityLabel: PropTypes.string,
lastMessage: PropTypes.object,
favorite: PropTypes.bool,
alert: PropTypes.bool,
hideUnreadStatus: PropTypes.bool,
unread: PropTypes.number,
userMentions: PropTypes.number,
groupMentions: PropTypes.number,
roomUpdatedAt: PropTypes.instanceOf(Date),
toggleFav: PropTypes.func,
toggleRead: PropTypes.func,
onPress: PropTypes.func,
hideChannel: PropTypes.func
};
RoomItem.defaultProps = {
avatarSize: 48,
status: 'offline'
};
export default RoomItem;

View File

@ -0,0 +1,31 @@
import React from 'react';
import { Text } from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
const Title = React.memo(({
name, theme, hideUnreadStatus, alert
}) => (
<Text
style={[
styles.title,
alert && !hideUnreadStatus && styles.alert,
{ color: themes[theme].titleText }
]}
ellipsizeMode='tail'
numberOfLines={1}
>
{name}
</Text>
));
Title.propTypes = {
name: PropTypes.string,
theme: PropTypes.string,
hideUnreadStatus: PropTypes.bool,
alert: PropTypes.bool
};
export default Title;

View File

@ -0,0 +1,49 @@
import React from 'react';
import { Text } from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
import { capitalize } from '../../utils/room';
const UpdatedAt = React.memo(({
roomUpdatedAt, date, theme, hideUnreadStatus, alert
}) => {
if (!roomUpdatedAt) {
return null;
}
return (
<Text
style={[
styles.date,
{
color:
themes[theme]
.auxiliaryText
},
alert && !hideUnreadStatus && [
styles.updateAlert,
{
color:
themes[theme]
.tintColor
}
]
]}
ellipsizeMode='tail'
numberOfLines={1}
>
{capitalize(date)}
</Text>
);
});
UpdatedAt.propTypes = {
roomUpdatedAt: PropTypes.instanceOf(Date),
date: PropTypes.string,
theme: PropTypes.string,
hideUnreadStatus: PropTypes.bool,
alert: PropTypes.bool
};
export default UpdatedAt;

View File

@ -0,0 +1,58 @@
import React from 'react';
import { View } from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
import Avatar from '../../containers/Avatar';
const RoomItemInner = ({
accessibilityLabel,
avatar,
avatarSize,
type,
baseUrl,
userId,
token,
theme,
children
}) => (
<View
style={styles.container}
accessibilityLabel={accessibilityLabel}
>
<Avatar
text={avatar}
size={avatarSize}
type={type}
baseUrl={baseUrl}
style={styles.avatar}
userId={userId}
token={token}
/>
<View
style={[
styles.centerContainer,
{
borderColor: themes[theme].separatorColor
}
]}
>
{children}
</View>
</View>
);
RoomItemInner.propTypes = {
accessibilityLabel: PropTypes.string,
avatar: PropTypes.string,
avatarSize: PropTypes.number,
type: PropTypes.string,
baseUrl: PropTypes.string,
userId: PropTypes.string,
token: PropTypes.string,
theme: PropTypes.string,
children: PropTypes.element
};
export default RoomItemInner;

View File

@ -1,17 +1,11 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { View, Text } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import Avatar from '../../containers/Avatar';
import I18n from '../../i18n'; import I18n from '../../i18n';
import styles, { ROW_HEIGHT } from './styles'; import { ROW_HEIGHT } from './styles';
import UnreadBadge from './UnreadBadge'; import { formatDate } from '../../utils/room';
import TypeIcon from './TypeIcon'; import RoomItem from './RoomItem';
import LastMessage from './LastMessage';
import { capitalize, formatDate } from '../../utils/room';
import Touchable from './Touchable';
import { themes } from '../../constants/colors';
export { ROW_HEIGHT }; export { ROW_HEIGHT };
@ -27,7 +21,7 @@ const attrs = [
const arePropsEqual = (oldProps, newProps) => attrs.every(key => oldProps[key] === newProps[key]); const arePropsEqual = (oldProps, newProps) => attrs.every(key => oldProps[key] === newProps[key]);
const RoomItem = React.memo(({ const RoomItemContainer = React.memo(({
item, item,
onPress, onPress,
width, width,
@ -97,12 +91,18 @@ const RoomItem = React.memo(({
} }
return ( return (
<Touchable <RoomItem
name={name}
avatar={avatar}
isGroupChat={isGroupChat}
isRead={isRead}
onPress={_onPress} onPress={_onPress}
date={date}
accessibilityLabel={accessibilityLabel}
userMentions={item.userMentions}
width={width} width={width}
favorite={item.f} favorite={item.f}
toggleFav={toggleFav} toggleFav={toggleFav}
isRead={isRead}
rid={item.rid} rid={item.rid}
toggleRead={toggleRead} toggleRead={toggleRead}
hideChannel={hideChannel} hideChannel={hideChannel}
@ -110,96 +110,26 @@ const RoomItem = React.memo(({
type={item.t} type={item.t}
theme={theme} theme={theme}
isFocused={isFocused} isFocused={isFocused}
> size={avatarSize}
<View baseUrl={baseUrl}
style={styles.container} userId={userId}
accessibilityLabel={accessibilityLabel} token={token}
> prid={item.prid}
<Avatar status={status}
text={avatar} hideUnreadStatus={item.hideUnreadStatus}
size={avatarSize} alert={item.alert}
type={item.t} roomUpdatedAt={item.roomUpdatedAt}
baseUrl={baseUrl} lastMessage={item.lastMessage}
style={styles.avatar} showLastMessage={showLastMessage}
userId={userId} username={username}
token={token} useRealName={useRealName}
/> unread={item.unread}
<View groupMentions={item.groupMentions}
style={[ />
styles.centerContainer,
{
borderColor: themes[theme].separatorColor
}
]}
>
<View style={styles.titleContainer}>
<TypeIcon
type={item.t}
prid={item.prid}
status={status}
isGroupChat={isGroupChat}
theme={theme}
/>
<Text
style={[
styles.title,
item.alert && !item.hideUnreadStatus && styles.alert,
{ color: themes[theme].titleText }
]}
ellipsizeMode='tail'
numberOfLines={1}
>
{name}
</Text>
{item.roomUpdatedAt ? (
<Text
style={[
styles.date,
{
color:
themes[theme]
.auxiliaryText
},
item.alert && !item.hideUnreadStatus && [
styles.updateAlert,
{
color:
themes[theme]
.tintColor
}
]
]}
ellipsizeMode='tail'
numberOfLines={1}
>
{capitalize(date)}
</Text>
) : null}
</View>
<View style={styles.row}>
<LastMessage
lastMessage={item.lastMessage}
type={item.t}
showLastMessage={showLastMessage}
username={username}
alert={item.alert && !item.hideUnreadStatus}
useRealName={useRealName}
theme={theme}
/>
<UnreadBadge
unread={item.unread}
userMentions={item.userMentions}
groupMentions={item.groupMentions}
theme={theme}
/>
</View>
</View>
</View>
</Touchable>
); );
}, arePropsEqual); }, arePropsEqual);
RoomItem.propTypes = { RoomItemContainer.propTypes = {
item: PropTypes.object.isRequired, item: PropTypes.object.isRequired,
baseUrl: PropTypes.string.isRequired, baseUrl: PropTypes.string.isRequired,
showLastMessage: PropTypes.bool, showLastMessage: PropTypes.bool,
@ -226,7 +156,7 @@ RoomItem.propTypes = {
getIsRead: PropTypes.func getIsRead: PropTypes.func
}; };
RoomItem.defaultProps = { RoomItemContainer.defaultProps = {
avatarSize: 48, avatarSize: 48,
status: 'offline', status: 'offline',
getUserPresence: () => {}, getUserPresence: () => {},
@ -252,4 +182,4 @@ const mapStateToProps = (state, ownProps) => {
}; };
}; };
export default connect(mapStateToProps)(RoomItem); export default connect(mapStateToProps)(RoomItemContainer);

View File

@ -8,6 +8,9 @@ export const SMALL_SWIPE = ACTION_WIDTH / 2;
export const LONG_SWIPE = ACTION_WIDTH * 3; export const LONG_SWIPE = ACTION_WIDTH * 3;
export default StyleSheet.create({ export default StyleSheet.create({
flex: {
flex: 1
},
container: { container: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',

View File

@ -16,7 +16,9 @@ configure(() => {
// Refer to https://github.com/storybooks/storybook/tree/master/app/react-native#start-command-parameters // Refer to https://github.com/storybooks/storybook/tree/master/app/react-native#start-command-parameters
// To find allowed options for getStorybookUI // To find allowed options for getStorybookUI
const StorybookUIRoot = getStorybookUI({}); const StorybookUIRoot = getStorybookUI({
asyncStorage: null
});
// If you are using React Native vanilla and after installation you don't see your app name here, write it manually. // If you are using React Native vanilla and after installation you don't see your app name here, write it manually.
// If you use Expo you can safely remove this line. // If you use Expo you can safely remove this line.

View File

@ -3,23 +3,34 @@ import { ScrollView, Dimensions } from 'react-native';
// import moment from 'moment'; // import moment from 'moment';
import { themes } from '../../app/constants/colors'; import { themes } from '../../app/constants/colors';
import RoomItemComponent from '../../app/presentation/RoomItem'; import RoomItemComponent from '../../app/presentation/RoomItem/RoomItem';
import StoriesSeparator from './StoriesSeparator'; import StoriesSeparator from './StoriesSeparator';
const date = '2017-10-10T10:00:00Z';
const baseUrl = 'https://open.rocket.chat'; const baseUrl = 'https://open.rocket.chat';
const { width } = Dimensions.get('window'); const { width } = Dimensions.get('window');
let _theme = 'light'; let _theme = 'light';
const longText = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries';
const lastMessage = {
u: {
username: 'diego.mello'
},
msg: longText
};
const updatedAt = {
date: '10:00',
roomUpdatedAt: new Date('2020-01-01')
};
const RoomItem = props => ( const RoomItem = props => (
<RoomItemComponent <RoomItemComponent
id='abc' rid='abc'
type='d' type='d'
name='rocket.cat' name='rocket.cat'
_updatedAt={date} avatar='rocket.cat'
baseUrl={baseUrl} baseUrl={baseUrl}
width={width} width={width}
theme={_theme} theme={_theme}
{...updatedAt}
{...props} {...props}
/> />
); );
@ -36,9 +47,9 @@ export default ({ theme }) => {
<RoomItem /> <RoomItem />
<Separator title='User' /> <Separator title='User' />
<RoomItem name='diego.mello' /> <RoomItem name='diego.mello' avatar='diego.mello' />
<RoomItem <RoomItem
name="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries" name={longText}
/> />
<Separator title='Type' /> <Separator title='Type' />
@ -46,33 +57,26 @@ export default ({ theme }) => {
<RoomItem type='c' /> <RoomItem type='c' />
<RoomItem type='p' /> <RoomItem type='p' />
<RoomItem type='l' /> <RoomItem type='l' />
<RoomItem type='discussion' />
<RoomItem type='d' isGroupChat />
<RoomItem type='&' /> <RoomItem type='&' />
{/* We can't add date stories because it breaks jest snapshots <Separator title='User status' />
<Separator title='Date' /> <RoomItem status='online' />
<RoomItem <RoomItem status='away' />
_updatedAt={moment()} <RoomItem status='busy' />
/> <RoomItem status='offline' />
<RoomItem <RoomItem status='wrong' />
_updatedAt={moment().subtract(1, 'day')}
/>
<RoomItem
_updatedAt={moment().subtract(5, 'day')}
/>
<RoomItem
_updatedAt={moment().subtract(30, 'day')}
/> */}
<Separator title='Alerts' /> <Separator title='Alerts' />
<RoomItem alert /> <RoomItem alert />
<RoomItem alert unread={1} /> <RoomItem alert name='unread' unread={1} />
<RoomItem alert unread={1000} /> <RoomItem alert name='unread' unread={1000} />
<RoomItem alert unread={1} userMentions={1} /> <RoomItem alert name='user mentions' unread={1} userMentions={1} />
<RoomItem alert unread={1000} userMentions={1} /> <RoomItem alert name='user mentions' unread={1000} userMentions={1} />
<RoomItem alert name='general' unread={1} type='c' /> <RoomItem alert name='group mentions' unread={1} groupMentions={1} />
<RoomItem alert name='general' unread={1000} type='c' /> <RoomItem alert name='group mentions' unread={1000} groupMentions={1} />
<RoomItem alert name='general' unread={1} userMentions={1} type='c' /> <RoomItem name='user mentions > group mentions' alert unread={1000} userMentions={1} groupMentions={1} />
<RoomItem alert name='general' unread={1000} userMentions={1} type='c' />
<Separator title='Last Message' /> <Separator title='Last Message' />
<RoomItem <RoomItem
@ -95,37 +99,29 @@ export default ({ theme }) => {
}, },
msg: '1' msg: '1'
}} }}
username='diego.mello'
/> />
<RoomItem <RoomItem
showLastMessage showLastMessage
lastMessage={{ lastMessage={lastMessage}
u: {
username: 'diego.mello'
},
msg: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries'
}}
/> />
<RoomItem <RoomItem
showLastMessage showLastMessage
alert alert
unread={1} unread={1}
lastMessage={{ lastMessage={lastMessage}
u: {
username: 'diego.mello'
},
msg: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries'
}}
/> />
<RoomItem <RoomItem
showLastMessage showLastMessage
alert alert
unread={1000} unread={1000}
lastMessage={{ lastMessage={lastMessage}
u: { />
username: 'diego.mello' <RoomItem
}, showLastMessage
msg: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries' alert
}} unread={1000}
lastMessage={lastMessage}
/> />
</ScrollView> </ScrollView>
); );

View File

@ -6,7 +6,7 @@ import { themes } from '../../app/constants/colors';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
separator: { separator: {
marginTop: 30, marginVertical: 30,
marginLeft: 10, marginLeft: 10,
fontSize: 20, fontSize: 20,
fontWeight: '300' fontWeight: '300'
@ -18,8 +18,7 @@ const Separator = ({ title, style, theme }) => (
style={[ style={[
styles.separator, styles.separator,
{ {
color: themes[theme].titleText, color: themes[theme].titleText
backgroundColor: themes[theme].backgroundColor
}, },
style style
]} ]}

View File

@ -1,10 +1,10 @@
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */ /* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
import React from 'react'; import React from 'react';
// import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
// import { createStore, combineReducers } from 'redux'; import { createStore, combineReducers } from 'redux';
import { storiesOf } from '@storybook/react-native'; import { storiesOf } from '@storybook/react-native';
// import RoomItem from './RoomItem'; import RoomItem from './RoomItem';
import Message from './Message'; import Message from './Message';
import UiKitMessage from './UiKitMessage'; import UiKitMessage from './UiKitMessage';
import UiKitModal from './UiKitModal'; import UiKitModal from './UiKitModal';
@ -24,17 +24,17 @@ const user = {
// Change here to see themed storybook // Change here to see themed storybook
const theme = 'light'; const theme = 'light';
// const reducers = combineReducers({ const reducers = combineReducers({
// settings: () => ({}), settings: () => ({}),
// login: () => ({ login: () => ({
// user: { user: {
// username: 'diego.mello' username: 'diego.mello'
// } }
// }), }),
// meteor: () => ({ connected: true }), meteor: () => ({ connected: true }),
// activeUsers: () => ({ abc: { status: 'online', statusText: 'dog' } }) activeUsers: () => ({ abc: { status: 'online', statusText: 'dog' } })
// }); });
// const store = createStore(reducers); const store = createStore(reducers);
const messageDecorator = story => ( const messageDecorator = story => (
<MessageContext.Provider <MessageContext.Provider
@ -55,9 +55,9 @@ const messageDecorator = story => (
</MessageContext.Provider> </MessageContext.Provider>
); );
// storiesOf('RoomItem', module) storiesOf('RoomItem', module)
// .addDecorator(story => <Provider store={store}>{story()}</Provider>) .addDecorator(story => <Provider store={store}>{story()}</Provider>)
// .add('list roomitem', () => <RoomItem theme={theme} />); .add('list roomitem', () => <RoomItem theme={theme} />);
storiesOf('Message', module) storiesOf('Message', module)
.addDecorator(messageDecorator) .addDecorator(messageDecorator)
.add('list message', () => <Message theme={theme} />); .add('list message', () => <Message theme={theme} />);