[IMPROVE] - migrate RoomItem presentation layer

This commit is contained in:
AlexAlexandre 2021-07-29 15:12:41 -03:00
parent a533e5c827
commit 601a45a194
12 changed files with 209 additions and 207 deletions

View File

@ -1,18 +1,34 @@
import React from 'react'; import React from 'react';
import { Animated, View, Text } from 'react-native'; import { Animated, View, Text } from 'react-native';
import { RectButton } from 'react-native-gesture-handler'; import { RectButton } from 'react-native-gesture-handler';
import PropTypes from 'prop-types';
import I18n, { isRTL } from '../../i18n'; import I18n, { isRTL } from '../../i18n';
import styles, { ACTION_WIDTH, LONG_SWIPE } from './styles'; import styles, { ACTION_WIDTH, LONG_SWIPE } from './styles';
import { CustomIcon } from '../../lib/Icons'; import { CustomIcon } from '../../lib/Icons';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
interface ILeftActions {
theme: string;
transX: any;
isRead: boolean;
width: number;
onToggleReadPress(): void;
}
interface IRightActions {
theme: string;
transX: any;
favorite: boolean;
width: number;
toggleFav(): void;
onHidePress(): void;
}
const reverse = new Animated.Value(isRTL() ? -1 : 1); const reverse = new Animated.Value(isRTL() ? -1 : 1);
export const LeftActions = React.memo(({ export const LeftActions = React.memo(({
theme, transX, isRead, width, onToggleReadPress theme, transX, isRead, width, onToggleReadPress
}) => { }: ILeftActions) => {
const translateX = Animated.multiply( const translateX = Animated.multiply(
transX.interpolate({ transX.interpolate({
inputRange: [0, ACTION_WIDTH], inputRange: [0, ACTION_WIDTH],
@ -51,7 +67,7 @@ export const LeftActions = React.memo(({
export const RightActions = React.memo(({ export const RightActions = React.memo(({
transX, favorite, width, toggleFav, onHidePress, theme transX, favorite, width, toggleFav, onHidePress, theme
}) => { }: IRightActions) => {
const translateXFav = Animated.multiply( const translateXFav = Animated.multiply(
transX.interpolate({ transX.interpolate({
inputRange: [-width / 2, -ACTION_WIDTH * 2, 0], inputRange: [-width / 2, -ACTION_WIDTH * 2, 0],
@ -113,20 +129,3 @@ export const RightActions = React.memo(({
</View> </View>
); );
}); });
LeftActions.propTypes = {
theme: PropTypes.string,
transX: PropTypes.object,
isRead: PropTypes.bool,
width: PropTypes.number,
onToggleReadPress: PropTypes.func
};
RightActions.propTypes = {
theme: PropTypes.string,
transX: PropTypes.object,
favorite: PropTypes.bool,
width: PropTypes.number,
toggleFav: PropTypes.func,
onHidePress: PropTypes.func
};

View File

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import { dequal } from 'dequal'; import { dequal } from 'dequal';
import I18n from '../../i18n'; import I18n from '../../i18n';
@ -8,9 +7,27 @@ import Markdown from '../../containers/markdown';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants'; import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
interface ILastMessage {
theme: string;
lastMessage: {
u: any;
pinned: boolean;
t: string;
attachments: any;
msg: string;
e2e: string;
};
type: string;
showLastMessage: boolean;
username: string;
useRealName: boolean;
alert: boolean;
}
const formatMsg = ({ const formatMsg = ({
lastMessage, type, showLastMessage, username, useRealName lastMessage, type, showLastMessage, username, useRealName
}) => { }: Partial<ILastMessage>) => {
if (!showLastMessage) { if (!showLastMessage) {
return ''; return '';
} }
@ -45,11 +62,12 @@ const formatMsg = ({
return `${ prefix }${ lastMessage.msg }`; return `${ prefix }${ lastMessage.msg }`;
}; };
const arePropsEqual = (oldProps, newProps) => dequal(oldProps, newProps); const arePropsEqual = (oldProps: any, newProps: any) => dequal(oldProps, newProps);
const LastMessage = React.memo(({ const LastMessage = React.memo(({
lastMessage, type, showLastMessage, username, alert, useRealName, theme lastMessage, type, showLastMessage, username, alert, useRealName, theme
}) => ( }: ILastMessage) => (
// @ts-ignore
<Markdown <Markdown
msg={formatMsg({ msg={formatMsg({
lastMessage, type, showLastMessage, username, useRealName lastMessage, type, showLastMessage, username, useRealName
@ -63,14 +81,4 @@ const LastMessage = React.memo(({
/> />
), arePropsEqual); ), arePropsEqual);
LastMessage.propTypes = {
theme: PropTypes.string,
lastMessage: PropTypes.object,
type: PropTypes.string,
showLastMessage: PropTypes.bool,
username: PropTypes.string,
useRealName: PropTypes.bool,
alert: PropTypes.bool
};
export default LastMessage; export default LastMessage;

View File

@ -13,6 +13,53 @@ import Touchable from './Touchable';
import Tag from './Tag'; import Tag from './Tag';
import I18n from '../../i18n'; import I18n from '../../i18n';
interface IRoomItem {
rid: string;
type: string;
prid: string;
name: string;
avatar: string;
showLastMessage: boolean;
username: string;
avatarSize: number;
testID: string;
width: number;
status: string;
useRealName: boolean;
theme: string;
isFocused: boolean;
isGroupChat: boolean;
isRead: boolean;
teamMain: boolean;
date: string;
accessibilityLabel: string;
lastMessage: {
u: any;
pinned: boolean;
t: string;
attachments: any;
msg: string;
e2e: string;
};
favorite: boolean;
alert: boolean;
hideUnreadStatus: boolean;
unread: number;
userMentions: number;
groupMentions: number;
tunread: [];
tunreadUser: [];
tunreadGroup: [];
swipeEnabled: boolean;
toggleFav(): void;
toggleRead(): void;
onPress(): void;
onLongPress(): void;
hideChannel(): void;
autoJoin: boolean;
size?: number;
}
const RoomItem = ({ const RoomItem = ({
rid, rid,
type, type,
@ -50,7 +97,7 @@ const RoomItem = ({
hideChannel, hideChannel,
teamMain, teamMain,
autoJoin autoJoin
}) => ( }: IRoomItem) => (
<Touchable <Touchable
onPress={onPress} onPress={onPress}
onLongPress={onLongPress} onLongPress={onLongPress}
@ -158,49 +205,4 @@ const RoomItem = ({
</Touchable> </Touchable>
); );
RoomItem.propTypes = {
rid: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
prid: PropTypes.string,
name: PropTypes.string.isRequired,
avatar: PropTypes.string.isRequired,
showLastMessage: PropTypes.bool,
username: 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,
teamMain: 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,
tunread: PropTypes.array,
tunreadUser: PropTypes.array,
tunreadGroup: PropTypes.array,
swipeEnabled: PropTypes.bool,
toggleFav: PropTypes.func,
toggleRead: PropTypes.func,
onPress: PropTypes.func,
onLongPress: PropTypes.func,
hideChannel: PropTypes.func,
autoJoin: PropTypes.bool
};
RoomItem.defaultProps = {
avatarSize: 48,
status: 'offline',
swipeEnabled: true
};
export default RoomItem; export default RoomItem;

View File

@ -1,13 +1,17 @@
import React from 'react'; import React from 'react';
import { Text, View } from 'react-native'; import { Text, View } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { useTheme } from '../../theme'; import { useTheme } from '../../theme';
import styles from './styles'; import styles from './styles';
const Tag = React.memo(({ name, testID }) => { interface ITag {
const { theme } = useTheme(); name: string;
testID?: string;
}
const Tag = React.memo(({ name, testID }: ITag) => {
const { theme }: any = useTheme();
return ( return (
<View style={[styles.tagContainer, { backgroundColor: themes[theme].borderColor }]}> <View style={[styles.tagContainer, { backgroundColor: themes[theme].borderColor }]}>
@ -24,9 +28,4 @@ const Tag = React.memo(({ name, testID }) => {
); );
}); });
Tag.propTypes = {
name: PropTypes.string,
testID: PropTypes.string
};
export default Tag; export default Tag;

View File

@ -1,13 +1,17 @@
import React from 'react'; import React from 'react';
import { Text } from 'react-native'; import { Text } from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles'; import styles from './styles';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
const Title = React.memo(({ interface ITitle {
name, theme, hideUnreadStatus, alert name: string;
}) => ( theme: string;
hideUnreadStatus: boolean;
alert: boolean;
}
const Title = React.memo(({ name, theme, hideUnreadStatus, alert }: ITitle) => (
<Text <Text
style={[ style={[
styles.title, styles.title,
@ -21,11 +25,4 @@ const Title = React.memo(({
</Text> </Text>
)); ));
Title.propTypes = {
name: PropTypes.string,
theme: PropTypes.string,
hideUnreadStatus: PropTypes.bool,
alert: PropTypes.bool
};
export default Title; export default Title;

View File

@ -15,26 +15,34 @@ import { isRTL } from '../../i18n';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { LeftActions, RightActions } from './Actions'; import { LeftActions, RightActions } from './Actions';
class Touchable extends React.Component { interface ITouchableProps {
static propTypes = { children: JSX.Element;
type: PropTypes.string.isRequired, type: string;
onPress: PropTypes.func, onPress(): void;
onLongPress: PropTypes.func, onLongPress(): void;
testID: PropTypes.string, testID: string;
width: PropTypes.number, width: number;
favorite: PropTypes.bool, favorite: boolean;
isRead: PropTypes.bool, isRead: boolean;
rid: PropTypes.string, rid: string;
toggleFav: PropTypes.func, toggleFav({}?, {}?): void;
toggleRead: PropTypes.func, toggleRead({}?, {}?): void;
hideChannel: PropTypes.func, hideChannel({}?, {}?): void;
children: PropTypes.element, theme: string;
theme: PropTypes.string, isFocused: boolean;
isFocused: PropTypes.bool, swipeEnabled: boolean;
swipeEnabled: PropTypes.bool
} }
constructor(props) { class Touchable extends React.Component<ITouchableProps, any> {
private dragX: Animated.Value;
private rowOffSet: Animated.Value;
private reverse: Animated.Value;
private transX: Animated.AnimatedAddition;
private transXReverse: Animated.AnimatedMultiplication;
private _onGestureEvent: (...args: any[]) => void;
private _value: number;
constructor(props: ITouchableProps) {
super(props); super(props);
this.dragX = new Animated.Value(0); this.dragX = new Animated.Value(0);
this.rowOffSet = new Animated.Value(0); this.rowOffSet = new Animated.Value(0);
@ -56,20 +64,20 @@ class Touchable extends React.Component {
this._value = 0; this._value = 0;
} }
_onHandlerStateChange = ({ nativeEvent }) => { _onHandlerStateChange = ({ nativeEvent }: any) => {
if (nativeEvent.oldState === State.ACTIVE) { if (nativeEvent.oldState === State.ACTIVE) {
this._handleRelease(nativeEvent); this._handleRelease(nativeEvent);
} }
} }
onLongPressHandlerStateChange = ({ nativeEvent }) => { onLongPressHandlerStateChange = ({ nativeEvent }: any) => {
if (nativeEvent.state === State.ACTIVE) { if (nativeEvent.state === State.ACTIVE) {
this.onLongPress(); this.onLongPress();
} }
} }
_handleRelease = (nativeEvent) => { _handleRelease = (nativeEvent: any) => {
const { translationX } = nativeEvent; const { translationX } = nativeEvent;
const { rowState } = this.state; const { rowState } = this.state;
this._value += translationX; this._value += translationX;
@ -152,7 +160,7 @@ class Touchable extends React.Component {
this._animateRow(toValue); this._animateRow(toValue);
} }
_animateRow = (toValue) => { _animateRow = (toValue: any) => {
this.rowOffSet.setValue(this._value); this.rowOffSet.setValue(this._value);
this._value = toValue; this._value = toValue;
this.dragX.setValue(0); this.dragX.setValue(0);

View File

@ -1,18 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import RoomTypeIcon from '../../containers/RoomTypeIcon';
const TypeIcon = React.memo(({
type, prid, status, isGroupChat, teamMain
}) => <RoomTypeIcon type={prid ? 'discussion' : type} isGroupChat={isGroupChat} status={status} teamMain={teamMain} />);
TypeIcon.propTypes = {
type: PropTypes.string,
status: PropTypes.string,
prid: PropTypes.string,
isGroupChat: PropTypes.bool,
teamMain: PropTypes.bool
};
export default TypeIcon;

View File

@ -0,0 +1,17 @@
import React from 'react';
import RoomTypeIcon from '../../containers/RoomTypeIcon';
interface ITypeIcon {
type: string;
status: string;
prid: string;
isGroupChat: boolean;
teamMain: boolean;
theme?: string;
}
const TypeIcon = React.memo(({
type, prid, status, isGroupChat, teamMain
}: ITypeIcon) => <RoomTypeIcon type={prid ? 'discussion' : type} isGroupChat={isGroupChat} status={status} teamMain={teamMain} />);
export default TypeIcon;

View File

@ -1,14 +1,18 @@
import React from 'react'; import React from 'react';
import { Text } from 'react-native'; import { Text } from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles'; import styles from './styles';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import { capitalize } from '../../utils/room'; import { capitalize } from '../../utils/room';
const UpdatedAt = React.memo(({ interface IUpdatedAt {
date, theme, hideUnreadStatus, alert date: string;
}) => { theme: string;
hideUnreadStatus: boolean;
alert: boolean;
}
const UpdatedAt = React.memo(({ date, theme, hideUnreadStatus, alert }: IUpdatedAt) => {
if (!date) { if (!date) {
return null; return null;
} }
@ -38,11 +42,4 @@ const UpdatedAt = React.memo(({
); );
}); });
UpdatedAt.propTypes = {
date: PropTypes.string,
theme: PropTypes.string,
hideUnreadStatus: PropTypes.bool,
alert: PropTypes.bool
};
export default UpdatedAt; export default UpdatedAt;

View File

@ -1,11 +1,20 @@
import React from 'react'; import React from 'react';
import { View } from 'react-native'; import { View } from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles'; import styles from './styles';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import Avatar from '../../containers/Avatar'; import Avatar from '../../containers/Avatar';
interface IWrapper {
accessibilityLabel: string;
avatar: string;
avatarSize: number;
type: string;
theme: string;
rid: string;
children: JSX.Element;
}
const Wrapper = ({ const Wrapper = ({
accessibilityLabel, accessibilityLabel,
avatar, avatar,
@ -14,7 +23,7 @@ const Wrapper = ({
theme, theme,
rid, rid,
children children
}) => ( }: IWrapper) => (
<View <View
style={styles.container} style={styles.container}
accessibilityLabel={accessibilityLabel} accessibilityLabel={accessibilityLabel}
@ -39,14 +48,4 @@ const Wrapper = ({
</View> </View>
); );
Wrapper.propTypes = {
accessibilityLabel: PropTypes.string,
avatar: PropTypes.string,
avatarSize: PropTypes.number,
type: PropTypes.string,
theme: PropTypes.string,
rid: PropTypes.string,
children: PropTypes.element
};
export default Wrapper; export default Wrapper;

View File

@ -9,6 +9,33 @@ import RoomItem from './RoomItem';
export { ROW_HEIGHT }; export { ROW_HEIGHT };
interface IRoomItemContainerProps {
item: any;
showLastMessage: boolean;
id: string;
onPress({}?): void;
onLongPress({}?): void;
username: string;
avatarSize: number;
width: number;
status: string;
toggleFav(): void;
toggleRead(): void;
hideChannel(): void;
useRealName: boolean;
getUserPresence: Function;
connected: boolean;
theme: string;
isFocused: boolean;
getRoomTitle: Function;
getRoomAvatar: Function;
getIsGroupChat: Function;
getIsRead: Function;
swipeEnabled: boolean;
autoJoin: boolean;
}
const attrs = [ const attrs = [
'width', 'width',
'status', 'status',
@ -20,45 +47,11 @@ const attrs = [
'autoJoin' 'autoJoin'
]; ];
class RoomItemContainer extends React.Component { class RoomItemContainer extends React.Component<IRoomItemContainerProps, any> {
static propTypes = { private mounted: boolean;
item: PropTypes.object.isRequired, private roomSubscription: any;
showLastMessage: PropTypes.bool,
id: PropTypes.string,
onPress: PropTypes.func,
onLongPress: PropTypes.func,
username: PropTypes.string,
avatarSize: PropTypes.number,
width: PropTypes.number,
status: PropTypes.string,
toggleFav: PropTypes.func,
toggleRead: PropTypes.func,
hideChannel: PropTypes.func,
useRealName: PropTypes.bool,
getUserPresence: PropTypes.func,
connected: PropTypes.bool,
theme: PropTypes.string,
isFocused: PropTypes.bool,
getRoomTitle: PropTypes.func,
getRoomAvatar: PropTypes.func,
getIsGroupChat: PropTypes.func,
getIsRead: PropTypes.func,
swipeEnabled: PropTypes.bool,
autoJoin: PropTypes.bool
};
static defaultProps = { constructor(props: IRoomItemContainerProps) {
avatarSize: 48,
status: 'offline',
getUserPresence: () => {},
getRoomTitle: () => 'title',
getRoomAvatar: () => '',
getIsGroupChat: () => false,
getIsRead: () => false,
swipeEnabled: true
}
constructor(props) {
super(props); super(props);
this.mounted = false; this.mounted = false;
this.init(); this.init();
@ -72,12 +65,12 @@ class RoomItemContainer extends React.Component {
} }
} }
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps: any) {
const { props } = this; const { props }: any = this;
return !attrs.every(key => props[key] === nextProps[key]); return !attrs.every(key => props[key] === nextProps[key]);
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps: any) {
const { connected, getUserPresence, id } = this.props; const { connected, getUserPresence, id } = this.props;
if (prevProps.connected !== connected && connected && this.isDirect) { if (prevProps.connected !== connected && connected && this.isDirect) {
getUserPresence(id); getUserPresence(id);
@ -96,7 +89,7 @@ class RoomItemContainer extends React.Component {
} }
get isDirect() { get isDirect() {
const { item: { t }, id } = this.props; const { item: { t }, id }: any = this.props;
return t === 'd' && id && !this.isGroupChat; return t === 'd' && id && !this.isGroupChat;
} }
@ -165,6 +158,7 @@ class RoomItemContainer extends React.Component {
} }
return ( return (
// @ts-ignore
<RoomItem <RoomItem
name={name} name={name}
avatar={avatar} avatar={avatar}
@ -207,7 +201,7 @@ class RoomItemContainer extends React.Component {
} }
} }
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state: any, ownProps: any) => {
let status = 'loading'; let status = 'loading';
const { id, type, visitor = {} } = ownProps; const { id, type, visitor = {} } = ownProps;
if (state.meteor.connected) { if (state.meteor.connected) {

View File

@ -7,7 +7,7 @@ export const ACTION_WIDTH = 80;
export const SMALL_SWIPE = ACTION_WIDTH / 2; 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<any>({
flex: { flex: {
flex: 1 flex: 1
}, },