Regression: right icons overlapping when users name is long (#4224)
Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
3265a8395d
commit
1ce7d5cf6a
|
@ -1,10 +1,10 @@
|
||||||
import React, { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
|
|
||||||
import { CustomIcon } from '../CustomIcon';
|
import { CustomIcon } from '../../../CustomIcon';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../../../theme';
|
||||||
import { themes } from '../../lib/constants';
|
import { themes } from '../../../../lib/constants';
|
||||||
import styles from './styles';
|
import styles from '../../styles';
|
||||||
|
|
||||||
const Edited = memo(({ isEdited, testID }: { isEdited: boolean; testID?: string }) => {
|
const Edited = memo(({ isEdited, testID }: { isEdited: boolean; testID?: string }) => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
|
@ -14,7 +14,7 @@ const Edited = memo(({ isEdited, testID }: { isEdited: boolean; testID?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View testID={testID} style={styles.leftIcons}>
|
<View testID={testID} style={styles.rightIcons}>
|
||||||
<CustomIcon name='edit' size={16} color={themes[theme].auxiliaryText} />
|
<CustomIcon name='edit' size={16} color={themes[theme].auxiliaryText} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React, { useContext } from 'react';
|
||||||
|
|
||||||
|
import Touchable from '../../Touchable';
|
||||||
|
import { CustomIcon } from '../../../CustomIcon';
|
||||||
|
import { BUTTON_HIT_SLOP } from '../../utils';
|
||||||
|
import MessageContext from '../../Context';
|
||||||
|
import styles from '../../styles';
|
||||||
|
import { useTheme } from '../../../../theme';
|
||||||
|
import { E2E_MESSAGE_TYPE, themes } from '../../../../lib/constants';
|
||||||
|
|
||||||
|
const Encrypted = React.memo(({ type }: { type: string }) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
const { onEncryptedPress } = useContext(MessageContext);
|
||||||
|
|
||||||
|
if (type !== E2E_MESSAGE_TYPE) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Touchable onPress={onEncryptedPress} style={styles.rightIcons} hitSlop={BUTTON_HIT_SLOP}>
|
||||||
|
<CustomIcon name='encrypted' size={16} color={themes[theme].auxiliaryText} />
|
||||||
|
</Touchable>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Encrypted;
|
|
@ -1,12 +1,12 @@
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
|
||||||
import Touchable from './Touchable';
|
import Touchable from '../../Touchable';
|
||||||
import { CustomIcon } from '../CustomIcon';
|
import { CustomIcon } from '../../../CustomIcon';
|
||||||
import styles from './styles';
|
import styles from '../../styles';
|
||||||
import { BUTTON_HIT_SLOP } from './utils';
|
import { BUTTON_HIT_SLOP } from '../../utils';
|
||||||
import { themes } from '../../lib/constants';
|
import { themes } from '../../../../lib/constants';
|
||||||
import MessageContext from './Context';
|
import MessageContext from '../../Context';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../../../theme';
|
||||||
|
|
||||||
const MessageError = React.memo(
|
const MessageError = React.memo(
|
||||||
({ hasError }: { hasError: boolean }) => {
|
({ hasError }: { hasError: boolean }) => {
|
||||||
|
@ -18,7 +18,7 @@ const MessageError = React.memo(
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Touchable onPress={onErrorPress} style={styles.leftIcons} hitSlop={BUTTON_HIT_SLOP}>
|
<Touchable onPress={onErrorPress} style={styles.rightIcons} hitSlop={BUTTON_HIT_SLOP}>
|
||||||
<CustomIcon name='warning' color={themes[theme].dangerColor} size={16} />
|
<CustomIcon name='warning' color={themes[theme].dangerColor} size={16} />
|
||||||
</Touchable>
|
</Touchable>
|
||||||
);
|
);
|
|
@ -1,14 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { themes } from '../../lib/constants';
|
import { themes } from '../../../../lib/constants';
|
||||||
import { CustomIcon } from '../CustomIcon';
|
import { CustomIcon } from '../../../CustomIcon';
|
||||||
import styles from './styles';
|
import styles from '../../styles';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../../../theme';
|
||||||
|
|
||||||
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread }: { isReadReceiptEnabled?: boolean; unread: boolean }) => {
|
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread }: { isReadReceiptEnabled?: boolean; unread: boolean }) => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
if (isReadReceiptEnabled && !unread && unread !== null) {
|
if (isReadReceiptEnabled && !unread && unread !== null) {
|
||||||
return <CustomIcon name='check' color={themes[theme].tintColor} size={16} style={[styles.leftIcons, styles.readReceipt]} />;
|
return <CustomIcon name='check' color={themes[theme].tintColor} size={16} style={styles.rightIcons} />;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
|
@ -0,0 +1,34 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
|
||||||
|
import Encrypted from './Encrypted';
|
||||||
|
import Edited from './Edited';
|
||||||
|
import MessageError from './MessageError';
|
||||||
|
import ReadReceipt from './ReadReceipt';
|
||||||
|
import { MessageType } from '../../../../definitions';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
actionIcons: {
|
||||||
|
flexDirection: 'row'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IRightIcons {
|
||||||
|
type: MessageType;
|
||||||
|
msg?: string;
|
||||||
|
isEdited: boolean;
|
||||||
|
isReadReceiptEnabled: boolean;
|
||||||
|
unread: boolean;
|
||||||
|
hasError: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RightIcons = ({ type, msg, isEdited, hasError, isReadReceiptEnabled, unread }: IRightIcons) => (
|
||||||
|
<View style={styles.actionIcons}>
|
||||||
|
<Encrypted type={type} />
|
||||||
|
<Edited testID={`${msg}-edited`} isEdited={isEdited} />
|
||||||
|
<MessageError hasError={hasError} />
|
||||||
|
<ReadReceipt isReadReceiptEnabled={isReadReceiptEnabled} unread={unread || false} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default RightIcons;
|
|
@ -8,10 +8,9 @@ import Markdown, { MarkdownPreview } from '../markdown';
|
||||||
import User from './User';
|
import User from './User';
|
||||||
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME, getInfoMessage } from './utils';
|
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME, getInfoMessage } from './utils';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
import Encrypted from './Encrypted';
|
|
||||||
import { IMessageContent } from './interfaces';
|
import { IMessageContent } from './interfaces';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { E2E_MESSAGE_TYPE, themes } from '../../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
|
|
||||||
const Content = React.memo(
|
const Content = React.memo(
|
||||||
(props: IMessageContent) => {
|
(props: IMessageContent) => {
|
||||||
|
@ -70,16 +69,6 @@ const Content = React.memo(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a encrypted message and is not a preview
|
|
||||||
if (props.type === E2E_MESSAGE_TYPE && !isPreview && !props.isHeader) {
|
|
||||||
content = (
|
|
||||||
<View style={styles.flex}>
|
|
||||||
<View style={styles.contentContainer}>{content}</View>
|
|
||||||
<Encrypted type={props.type} />
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.isIgnored) {
|
if (props.isIgnored) {
|
||||||
content = <Text style={[styles.textInfo, { color: themes[theme].auxiliaryText }]}>{I18n.t('Message_Ignored')}</Text>;
|
content = <Text style={[styles.textInfo, { color: themes[theme].auxiliaryText }]}>{I18n.t('Message_Ignored')}</Text>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import React, { useContext } from 'react';
|
|
||||||
|
|
||||||
import Touchable from './Touchable';
|
|
||||||
import { CustomIcon } from '../CustomIcon';
|
|
||||||
import { BUTTON_HIT_SLOP } from './utils';
|
|
||||||
import MessageContext from './Context';
|
|
||||||
import styles from './styles';
|
|
||||||
import { useTheme } from '../../theme';
|
|
||||||
import { E2E_MESSAGE_TYPE, themes } from '../../lib/constants';
|
|
||||||
|
|
||||||
const Encrypted = React.memo(({ type }: { type: string }) => {
|
|
||||||
const { theme } = useTheme();
|
|
||||||
const { onEncryptedPress } = useContext(MessageContext);
|
|
||||||
|
|
||||||
if (type !== E2E_MESSAGE_TYPE) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Touchable onPress={onEncryptedPress} style={styles.leftIcons} hitSlop={BUTTON_HIT_SLOP}>
|
|
||||||
<CustomIcon name='encrypted' size={16} color={themes[theme].auxiliaryText} />
|
|
||||||
</Touchable>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Encrypted;
|
|
|
@ -15,13 +15,11 @@ import Reactions from './Reactions';
|
||||||
import Broadcast from './Broadcast';
|
import Broadcast from './Broadcast';
|
||||||
import Discussion from './Discussion';
|
import Discussion from './Discussion';
|
||||||
import Content from './Content';
|
import Content from './Content';
|
||||||
import ReadReceipt from './ReadReceipt';
|
|
||||||
import CallButton from './CallButton';
|
import CallButton from './CallButton';
|
||||||
import { themes } from '../../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
import { IMessage, IMessageInner, IMessageTouchable } from './interfaces';
|
import { IMessage, IMessageInner, IMessageTouchable } from './interfaces';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import Edited from './Edited';
|
import RightIcons from './Components/RightIcons';
|
||||||
import MessageError from './MessageError';
|
|
||||||
|
|
||||||
const MessageInner = React.memo((props: IMessageInner) => {
|
const MessageInner = React.memo((props: IMessageInner) => {
|
||||||
const { attachments } = props;
|
const { attachments } = props;
|
||||||
|
@ -105,12 +103,15 @@ const Message = React.memo((props: IMessage) => {
|
||||||
<MessageInner {...props} />
|
<MessageInner {...props} />
|
||||||
</View>
|
</View>
|
||||||
{!props.isHeader ? (
|
{!props.isHeader ? (
|
||||||
<>
|
<RightIcons
|
||||||
<Edited testID={`${props.msg}-edited`} isEdited={props.isEdited} />
|
type={props.type}
|
||||||
<MessageError hasError={props.hasError} />
|
msg={props.msg}
|
||||||
</>
|
isEdited={props.isEdited}
|
||||||
|
hasError={props.hasError}
|
||||||
|
isReadReceiptEnabled={props.isReadReceiptEnabled || false}
|
||||||
|
unread={props.unread || false}
|
||||||
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<ReadReceipt isReadReceiptEnabled={props.isReadReceiptEnabled} unread={props.unread || false} />
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,15 +4,13 @@ import moment from 'moment';
|
||||||
|
|
||||||
import { themes } from '../../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import MessageError from './MessageError';
|
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import messageStyles from './styles';
|
import messageStyles from './styles';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME } from './utils';
|
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME } from './utils';
|
||||||
import { SubscriptionType } from '../../definitions';
|
import { MessageType, SubscriptionType } from '../../definitions';
|
||||||
import { IRoomInfoParam } from '../../views/SearchMessagesView';
|
import { IRoomInfoParam } from '../../views/SearchMessagesView';
|
||||||
import Edited from './Edited';
|
import RightIcons from './Components/RightIcons';
|
||||||
import Encrypted from './Encrypted';
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -36,6 +34,7 @@ const styles = StyleSheet.create({
|
||||||
...sharedStyles.textMedium
|
...sharedStyles.textMedium
|
||||||
},
|
},
|
||||||
titleContainer: {
|
titleContainer: {
|
||||||
|
flexShrink: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
},
|
},
|
||||||
|
@ -58,8 +57,10 @@ interface IMessageUser {
|
||||||
ts?: Date;
|
ts?: Date;
|
||||||
timeFormat?: string;
|
timeFormat?: string;
|
||||||
navToRoomInfo?: (navParam: IRoomInfoParam) => void;
|
navToRoomInfo?: (navParam: IRoomInfoParam) => void;
|
||||||
type: string;
|
type: MessageType;
|
||||||
isEdited: boolean;
|
isEdited: boolean;
|
||||||
|
isReadReceiptEnabled?: boolean;
|
||||||
|
unread?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const User = React.memo(
|
const User = React.memo(
|
||||||
|
@ -67,7 +68,7 @@ const User = React.memo(
|
||||||
const { user } = useContext(MessageContext);
|
const { user } = useContext(MessageContext);
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
|
|
||||||
if (isHeader || hasError) {
|
if (isHeader) {
|
||||||
const username = (useRealName && author?.name) || author?.username;
|
const username = (useRealName && author?.name) || author?.username;
|
||||||
const aliasUsername = alias ? (
|
const aliasUsername = alias ? (
|
||||||
<Text style={[styles.alias, { color: themes[theme].auxiliaryText }]}> @{username}</Text>
|
<Text style={[styles.alias, { color: themes[theme].auxiliaryText }]}> @{username}</Text>
|
||||||
|
@ -108,11 +109,13 @@ const User = React.memo(
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<View style={styles.actionIcons}>
|
<RightIcons
|
||||||
<Encrypted type={type} />
|
type={type}
|
||||||
<Edited isEdited={isEdited} />
|
isEdited={isEdited}
|
||||||
<MessageError hasError={hasError} {...props} />
|
hasError={hasError}
|
||||||
</View>
|
isReadReceiptEnabled={props.isReadReceiptEnabled || false}
|
||||||
|
unread={props.unread || false}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ export interface IMessageContent {
|
||||||
navToRoomInfo: (navParam: IRoomInfoParam) => void;
|
navToRoomInfo: (navParam: IRoomInfoParam) => void;
|
||||||
useRealName?: boolean;
|
useRealName?: boolean;
|
||||||
isIgnored: boolean;
|
isIgnored: boolean;
|
||||||
type: string;
|
type: MessageType;
|
||||||
comment?: string;
|
comment?: string;
|
||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
isHeader: boolean;
|
isHeader: boolean;
|
||||||
|
|
|
@ -163,12 +163,8 @@ export default StyleSheet.create({
|
||||||
threadBell: {
|
threadBell: {
|
||||||
marginLeft: 8
|
marginLeft: 8
|
||||||
},
|
},
|
||||||
leftIcons: {
|
rightIcons: {
|
||||||
paddingLeft: 5,
|
paddingLeft: 5
|
||||||
paddingVertical: 5
|
|
||||||
},
|
|
||||||
readReceipt: {
|
|
||||||
lineHeight: 20
|
|
||||||
},
|
},
|
||||||
threadDetails: {
|
threadDetails: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -960,7 +960,7 @@ SPEC CHECKSUMS:
|
||||||
EXVideoThumbnails: 442c3abadb51a81551a3b53705b7560de390e6f7
|
EXVideoThumbnails: 442c3abadb51a81551a3b53705b7560de390e6f7
|
||||||
EXWebBrowser: 76783ba5dcb8699237746ecf41a9643d428a4cc5
|
EXWebBrowser: 76783ba5dcb8699237746ecf41a9643d428a4cc5
|
||||||
FBLazyVector: c9b6dfcde9b3d497793c40d4ccbfbfb05092e0df
|
FBLazyVector: c9b6dfcde9b3d497793c40d4ccbfbfb05092e0df
|
||||||
FBReactNativeSpec: c39f7fc0cd6cc64f0a2a5beffc64b1aa5d42740e
|
FBReactNativeSpec: addc4f0e6ab00dc628fe91de8bfca4601762673a
|
||||||
Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
|
Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
|
||||||
FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4
|
FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4
|
||||||
FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085
|
FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { Provider } from 'react-redux';
|
||||||
import { storiesOf } from '@storybook/react-native';
|
import { storiesOf } from '@storybook/react-native';
|
||||||
|
|
||||||
import MessageComponent from '../../app/containers/message/Message';
|
import MessageComponent from '../../app/containers/message/Message';
|
||||||
import { messagesStatus, themes } from '../../app/lib/constants';
|
import { E2E_MESSAGE_TYPE, messagesStatus, themes } from '../../app/lib/constants';
|
||||||
import MessageSeparator from '../../app/views/RoomView/Separator';
|
import MessageSeparator from '../../app/views/RoomView/Separator';
|
||||||
import MessageContext from '../../app/containers/message/Context';
|
import MessageContext from '../../app/containers/message/Context';
|
||||||
import { store } from './index';
|
import { store } from './index';
|
||||||
|
@ -23,6 +23,12 @@ const author = {
|
||||||
_id: 'userid',
|
_id: 'userid',
|
||||||
username: 'diego.mello'
|
username: 'diego.mello'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const longNameAuthor = {
|
||||||
|
_id: 'userid',
|
||||||
|
username: 'Long name user looooong name user'
|
||||||
|
};
|
||||||
|
|
||||||
const baseUrl = 'https://open.rocket.chat';
|
const baseUrl = 'https://open.rocket.chat';
|
||||||
const date = new Date(2017, 10, 10, 10);
|
const date = new Date(2017, 10, 10, 10);
|
||||||
const longText =
|
const longText =
|
||||||
|
@ -863,3 +869,45 @@ stories.add('Thumbnail from server', () => (
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
stories.add('Long Name user', () => (
|
||||||
|
<>
|
||||||
|
<Message msg={'this is a normal message'} author={longNameAuthor} />
|
||||||
|
<Message msg={'Edited message'} author={longNameAuthor} isEdited />
|
||||||
|
<Message msg={'Encrypted message'} author={longNameAuthor} type={E2E_MESSAGE_TYPE} />
|
||||||
|
<Message msg={'Error message'} author={longNameAuthor} hasError />
|
||||||
|
<Message msg={'Message with read receipt'} author={longNameAuthor} isReadReceiptEnabled read />
|
||||||
|
<Message msg={'Message with read receipt'} author={longNameAuthor} isReadReceiptEnabled read type={E2E_MESSAGE_TYPE} />
|
||||||
|
<Message
|
||||||
|
msg={'Show all icons '}
|
||||||
|
author={longNameAuthor}
|
||||||
|
isEdited
|
||||||
|
type={E2E_MESSAGE_TYPE}
|
||||||
|
hasError
|
||||||
|
isReadReceiptEnabled
|
||||||
|
read
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Message
|
||||||
|
msg={longText}
|
||||||
|
author={longNameAuthor}
|
||||||
|
isHeader={false}
|
||||||
|
isEdited
|
||||||
|
type={E2E_MESSAGE_TYPE}
|
||||||
|
hasError
|
||||||
|
isReadReceiptEnabled
|
||||||
|
read
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Message
|
||||||
|
msg='small message'
|
||||||
|
author={longNameAuthor}
|
||||||
|
isHeader={false}
|
||||||
|
isEdited
|
||||||
|
type={E2E_MESSAGE_TYPE}
|
||||||
|
hasError
|
||||||
|
isReadReceiptEnabled
|
||||||
|
read
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
));
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue