[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,
width: size,
height: size,
backgroundColor: STATUS_COLORS[status],
backgroundColor: STATUS_COLORS[status] ?? STATUS_COLORS.offline,
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 PropTypes from 'prop-types';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
import Avatar from '../../containers/Avatar';
import I18n from '../../i18n';
import styles, { ROW_HEIGHT } from './styles';
import UnreadBadge from './UnreadBadge';
import TypeIcon from './TypeIcon';
import LastMessage from './LastMessage';
import { capitalize, formatDate } from '../../utils/room';
import Touchable from './Touchable';
import { themes } from '../../constants/colors';
import { ROW_HEIGHT } from './styles';
import { formatDate } from '../../utils/room';
import RoomItem from './RoomItem';
export { ROW_HEIGHT };
@ -27,7 +21,7 @@ const attrs = [
const arePropsEqual = (oldProps, newProps) => attrs.every(key => oldProps[key] === newProps[key]);
const RoomItem = React.memo(({
const RoomItemContainer = React.memo(({
item,
onPress,
width,
@ -97,12 +91,18 @@ const RoomItem = React.memo(({
}
return (
<Touchable
<RoomItem
name={name}
avatar={avatar}
isGroupChat={isGroupChat}
isRead={isRead}
onPress={_onPress}
date={date}
accessibilityLabel={accessibilityLabel}
userMentions={item.userMentions}
width={width}
favorite={item.f}
toggleFav={toggleFav}
isRead={isRead}
rid={item.rid}
toggleRead={toggleRead}
hideChannel={hideChannel}
@ -110,96 +110,26 @@ const RoomItem = React.memo(({
type={item.t}
theme={theme}
isFocused={isFocused}
>
<View
style={styles.container}
accessibilityLabel={accessibilityLabel}
>
<Avatar
text={avatar}
size={avatarSize}
type={item.t}
baseUrl={baseUrl}
style={styles.avatar}
userId={userId}
token={token}
/>
<View
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>
size={avatarSize}
baseUrl={baseUrl}
userId={userId}
token={token}
prid={item.prid}
status={status}
hideUnreadStatus={item.hideUnreadStatus}
alert={item.alert}
roomUpdatedAt={item.roomUpdatedAt}
lastMessage={item.lastMessage}
showLastMessage={showLastMessage}
username={username}
useRealName={useRealName}
unread={item.unread}
groupMentions={item.groupMentions}
/>
);
}, arePropsEqual);
RoomItem.propTypes = {
RoomItemContainer.propTypes = {
item: PropTypes.object.isRequired,
baseUrl: PropTypes.string.isRequired,
showLastMessage: PropTypes.bool,
@ -226,7 +156,7 @@ RoomItem.propTypes = {
getIsRead: PropTypes.func
};
RoomItem.defaultProps = {
RoomItemContainer.defaultProps = {
avatarSize: 48,
status: 'offline',
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 default StyleSheet.create({
flex: {
flex: 1
},
container: {
flexDirection: 'row',
alignItems: 'center',

View File

@ -16,7 +16,9 @@ configure(() => {
// Refer to https://github.com/storybooks/storybook/tree/master/app/react-native#start-command-parameters
// 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 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 { themes } from '../../app/constants/colors';
import RoomItemComponent from '../../app/presentation/RoomItem';
import RoomItemComponent from '../../app/presentation/RoomItem/RoomItem';
import StoriesSeparator from './StoriesSeparator';
const date = '2017-10-10T10:00:00Z';
const baseUrl = 'https://open.rocket.chat';
const { width } = Dimensions.get('window');
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 => (
<RoomItemComponent
id='abc'
rid='abc'
type='d'
name='rocket.cat'
_updatedAt={date}
avatar='rocket.cat'
baseUrl={baseUrl}
width={width}
theme={_theme}
{...updatedAt}
{...props}
/>
);
@ -36,9 +47,9 @@ export default ({ theme }) => {
<RoomItem />
<Separator title='User' />
<RoomItem name='diego.mello' />
<RoomItem name='diego.mello' avatar='diego.mello' />
<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' />
@ -46,33 +57,26 @@ export default ({ theme }) => {
<RoomItem type='c' />
<RoomItem type='p' />
<RoomItem type='l' />
<RoomItem type='discussion' />
<RoomItem type='d' isGroupChat />
<RoomItem type='&' />
{/* We can't add date stories because it breaks jest snapshots
<Separator title='Date' />
<RoomItem
_updatedAt={moment()}
/>
<RoomItem
_updatedAt={moment().subtract(1, 'day')}
/>
<RoomItem
_updatedAt={moment().subtract(5, 'day')}
/>
<RoomItem
_updatedAt={moment().subtract(30, 'day')}
/> */}
<Separator title='User status' />
<RoomItem status='online' />
<RoomItem status='away' />
<RoomItem status='busy' />
<RoomItem status='offline' />
<RoomItem status='wrong' />
<Separator title='Alerts' />
<RoomItem alert />
<RoomItem alert unread={1} />
<RoomItem alert unread={1000} />
<RoomItem alert unread={1} userMentions={1} />
<RoomItem alert unread={1000} userMentions={1} />
<RoomItem alert name='general' unread={1} type='c' />
<RoomItem alert name='general' unread={1000} type='c' />
<RoomItem alert name='general' unread={1} userMentions={1} type='c' />
<RoomItem alert name='general' unread={1000} userMentions={1} type='c' />
<RoomItem alert name='unread' unread={1} />
<RoomItem alert name='unread' unread={1000} />
<RoomItem alert name='user mentions' unread={1} userMentions={1} />
<RoomItem alert name='user mentions' unread={1000} userMentions={1} />
<RoomItem alert name='group mentions' unread={1} groupMentions={1} />
<RoomItem alert name='group mentions' unread={1000} groupMentions={1} />
<RoomItem name='user mentions > group mentions' alert unread={1000} userMentions={1} groupMentions={1} />
<Separator title='Last Message' />
<RoomItem
@ -95,37 +99,29 @@ export default ({ theme }) => {
},
msg: '1'
}}
username='diego.mello'
/>
<RoomItem
showLastMessage
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'
}}
lastMessage={lastMessage}
/>
<RoomItem
showLastMessage
alert
unread={1}
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'
}}
lastMessage={lastMessage}
/>
<RoomItem
showLastMessage
alert
unread={1000}
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'
}}
lastMessage={lastMessage}
/>
<RoomItem
showLastMessage
alert
unread={1000}
lastMessage={lastMessage}
/>
</ScrollView>
);

View File

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

View File

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