[IMPROVE] Create edited component for edited messages (#4048)

This commit is contained in:
Alex Junior 2022-05-10 14:40:08 -03:00 committed by GitHub
parent f60de94cba
commit e0bfdee70c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 105 additions and 97 deletions

View File

@ -1,10 +1,9 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { Image, StyleProp, Text, TextStyle } from 'react-native'; import { Image, StyleProp, Text, TextStyle } from 'react-native';
import { Node, Parser } from 'commonmark'; import { Parser } from 'commonmark';
import Renderer from 'commonmark-react-renderer'; import Renderer from 'commonmark-react-renderer';
import { MarkdownAST } from '@rocket.chat/message-parser'; import { MarkdownAST } from '@rocket.chat/message-parser';
import I18n from '../../i18n';
import MarkdownLink from './Link'; import MarkdownLink from './Link';
import MarkdownList from './List'; import MarkdownList from './List';
import MarkdownListItem from './ListItem'; import MarkdownListItem from './ListItem';
@ -37,7 +36,6 @@ interface IMarkdownProps {
baseUrl?: string; baseUrl?: string;
username?: string; username?: string;
tmid?: string; tmid?: string;
isEdited?: boolean;
numberOfLines?: number; numberOfLines?: number;
customEmojis?: boolean; customEmojis?: boolean;
useRealName?: boolean; useRealName?: boolean;
@ -133,9 +131,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
table: this.renderTable, table: this.renderTable,
table_row: this.renderTableRow, table_row: this.renderTableRow,
table_cell: this.renderTableCell, table_cell: this.renderTableCell
editedIndicator: this.renderEditedIndicator
}, },
renderParagraphsInLists: true renderParagraphsInLists: true
}); });
@ -145,21 +141,6 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
return !!enableMessageParser && !!md; return !!enableMessageParser && !!md;
} }
editedMessage = (ast: any) => {
const { isEdited } = this.props;
if (isEdited) {
const editIndicatorNode = new Node('edited_indicator');
if (ast.lastChild && ['heading', 'paragraph'].includes(ast.lastChild.type)) {
ast.lastChild.appendChild(editIndicatorNode);
} else {
const node = new Node('paragraph');
node.appendChild(editIndicatorNode);
ast.appendChild(node);
}
}
};
renderText = ({ context, literal }: { context: []; literal: string }) => { renderText = ({ context, literal }: { context: []; literal: string }) => {
const { numberOfLines, style = [] } = this.props; const { numberOfLines, style = [] } = this.props;
const defaultStyle = [this.isMessageContainsOnlyEmoji ? styles.textBig : {}, ...context.map(type => styles[type])]; const defaultStyle = [this.isMessageContainsOnlyEmoji ? styles.textBig : {}, ...context.map(type => styles[type])];
@ -274,11 +255,6 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
return <Image style={styles.inlineImage} source={{ uri: encodeURI(src) }} />; return <Image style={styles.inlineImage} source={{ uri: encodeURI(src) }} />;
}; };
renderEditedIndicator = () => {
const { theme } = this.props;
return <Text style={[styles.edited, { color: themes[theme].auxiliaryText }]}> ({I18n.t('edited')})</Text>;
};
renderHeading = ({ children, level }: any) => { renderHeading = ({ children, level }: any) => {
const { numberOfLines, theme } = this.props; const { numberOfLines, theme } = this.props;
// @ts-ignore // @ts-ignore
@ -373,7 +349,6 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
let ast = parser.parse(m); let ast = parser.parse(m);
ast = mergeTextNodes(ast); ast = mergeTextNodes(ast);
this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3; this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3;
this.editedMessage(ast);
return this.renderer.render(ast); return this.renderer.render(ast);
} }
} }

View File

@ -94,10 +94,6 @@ export default StyleSheet.create({
fontSize: 16, fontSize: 16,
...sharedStyles.textRegular ...sharedStyles.textRegular
}, },
edited: {
fontSize: 14,
...sharedStyles.textRegular
},
heading1: { heading1: {
...sharedStyles.textBold, ...sharedStyles.textBold,
fontSize: 24 fontSize: 24

View File

@ -59,7 +59,6 @@ const Content = React.memo(
getCustomEmoji={props.getCustomEmoji} getCustomEmoji={props.getCustomEmoji}
enableMessageParser={user.enableMessageParserEarlyAdoption} enableMessageParser={user.enableMessageParserEarlyAdoption}
username={user.username} username={user.username}
isEdited={props.isEdited}
channels={props.channels} channels={props.channels}
mentions={props.mentions} mentions={props.mentions}
navToRoomInfo={props.navToRoomInfo} navToRoomInfo={props.navToRoomInfo}
@ -72,7 +71,7 @@ const Content = React.memo(
} }
// If this is a encrypted message and is not a preview // If this is a encrypted message and is not a preview
if (props.type === E2E_MESSAGE_TYPE && !isPreview) { if (props.type === E2E_MESSAGE_TYPE && !isPreview && !props.isHeader) {
content = ( content = (
<View style={styles.flex}> <View style={styles.flex}>
<View style={styles.contentContainer}>{content}</View> <View style={styles.contentContainer}>{content}</View>

View File

@ -0,0 +1,23 @@
import React, { memo } from 'react';
import { View } from 'react-native';
import { CustomIcon } from '../CustomIcon';
import { useTheme } from '../../theme';
import { themes } from '../../lib/constants';
import styles from './styles';
const Edited = memo(({ isEdited }: { isEdited: boolean }) => {
const { theme } = useTheme();
if (!isEdited) {
return null;
}
return (
<View style={styles.leftIcons}>
<CustomIcon name='edit' size={16} color={themes[theme].auxiliaryText} />
</View>
);
});
export default Edited;

View File

@ -17,7 +17,7 @@ const Encrypted = React.memo(({ type }: { type: string }) => {
} }
return ( return (
<Touchable onPress={onEncryptedPress} style={styles.encrypted} hitSlop={BUTTON_HIT_SLOP}> <Touchable onPress={onEncryptedPress} style={styles.leftIcons} hitSlop={BUTTON_HIT_SLOP}>
<CustomIcon name='encrypted' size={16} color={themes[theme].auxiliaryText} /> <CustomIcon name='encrypted' size={16} color={themes[theme].auxiliaryText} />
</Touchable> </Touchable>
); );

View File

@ -20,6 +20,8 @@ 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 MessageError from './MessageError';
const MessageInner = React.memo((props: IMessageInner) => { const MessageInner = React.memo((props: IMessageInner) => {
const { attachments } = props; const { attachments } = props;
@ -102,6 +104,12 @@ const Message = React.memo((props: IMessage) => {
<View style={[styles.messageContent, props.isHeader && styles.messageContentWithHeader]}> <View style={[styles.messageContent, props.isHeader && styles.messageContentWithHeader]}>
<MessageInner {...props} /> <MessageInner {...props} />
</View> </View>
{!props.isHeader ? (
<>
<Edited isEdited={props.isEdited} />
<MessageError hasError={props.hasError} />
</>
) : null}
<ReadReceipt isReadReceiptEnabled={props.isReadReceiptEnabled} unread={props.unread || false} /> <ReadReceipt isReadReceiptEnabled={props.isReadReceiptEnabled} unread={props.unread || false} />
</View> </View>
</View> </View>

View File

@ -18,8 +18,8 @@ const MessageError = React.memo(
} }
return ( return (
<Touchable onPress={onErrorPress} style={styles.errorButton} hitSlop={BUTTON_HIT_SLOP}> <Touchable onPress={onErrorPress} style={styles.leftIcons} hitSlop={BUTTON_HIT_SLOP}>
<CustomIcon name='warning' color={themes[theme].dangerColor} size={18} /> <CustomIcon name='warning' color={themes[theme].dangerColor} size={16} />
</Touchable> </Touchable>
); );
}, },

View File

@ -8,7 +8,7 @@ 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={15} style={styles.readReceipt} />; return <CustomIcon name='check' color={themes[theme].tintColor} size={16} style={[styles.leftIcons, styles.readReceipt]} />;
} }
return null; return null;
}); });

View File

@ -11,6 +11,8 @@ 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 { SubscriptionType } from '../../definitions';
import { IRoomInfoParam } from '../../views/SearchMessagesView'; import { IRoomInfoParam } from '../../views/SearchMessagesView';
import Edited from './Edited';
import Encrypted from './Encrypted';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -19,6 +21,10 @@ const styles = StyleSheet.create({
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: 'center' alignItems: 'center'
}, },
actionIcons: {
flexDirection: 'row',
alignItems: 'center'
},
username: { username: {
fontSize: 16, fontSize: 16,
lineHeight: 22, lineHeight: 22,
@ -41,7 +47,7 @@ const styles = StyleSheet.create({
interface IMessageUser { interface IMessageUser {
isHeader?: boolean; isHeader?: boolean;
hasError?: boolean; hasError: boolean;
useRealName?: boolean; useRealName?: boolean;
author?: { author?: {
_id: string; _id: string;
@ -53,10 +59,11 @@ interface IMessageUser {
timeFormat?: string; timeFormat?: string;
navToRoomInfo?: (navParam: IRoomInfoParam) => void; navToRoomInfo?: (navParam: IRoomInfoParam) => void;
type: string; type: string;
isEdited: boolean;
} }
const User = React.memo( const User = React.memo(
({ isHeader, useRealName, author, alias, ts, timeFormat, hasError, navToRoomInfo, type, ...props }: IMessageUser) => { ({ isHeader, useRealName, author, alias, ts, timeFormat, hasError, navToRoomInfo, type, isEdited, ...props }: IMessageUser) => {
const { user } = useContext(MessageContext); const { user } = useContext(MessageContext);
const { theme } = useTheme(); const { theme } = useTheme();
@ -100,8 +107,12 @@ const User = React.memo(
{textContent} {textContent}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text> <View style={styles.actionIcons}>
{hasError ? <MessageError hasError={hasError} {...props} /> : null} <Text style={[messageStyles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text>
<Encrypted type={type} />
<Edited isEdited={isEdited} />
<MessageError hasError={hasError} {...props} />
</View>
</View> </View>
); );
} }

View File

@ -60,6 +60,8 @@ export interface IMessageContent {
isIgnored: boolean; isIgnored: boolean;
type: string; type: string;
comment?: string; comment?: string;
hasError: boolean;
isHeader: boolean;
} }
export interface IMessageEmoji { export interface IMessageEmoji {

View File

@ -74,10 +74,6 @@ export default StyleSheet.create({
avatarSmall: { avatarSmall: {
marginLeft: 16 marginLeft: 16
}, },
errorButton: {
paddingLeft: 10,
paddingVertical: 5
},
buttonContainer: { buttonContainer: {
marginTop: 8, marginTop: 8,
flexDirection: 'row', flexDirection: 'row',
@ -167,12 +163,13 @@ export default StyleSheet.create({
threadBell: { threadBell: {
marginLeft: 8 marginLeft: 8
}, },
leftIcons: {
paddingLeft: 5,
paddingVertical: 5
},
readReceipt: { readReceipt: {
lineHeight: 20 lineHeight: 20
}, },
encrypted: {
justifyContent: 'center'
},
threadDetails: { threadDetails: {
flex: 1, flex: 1,
marginLeft: 12 marginLeft: 12

File diff suppressed because one or more lines are too long

View File

@ -54,12 +54,6 @@ stories.add('Text', () => (
</View> </View>
)); ));
stories.add('Edited', () => (
<View style={styles.container}>
<Markdown msg='This is edited' theme={theme} isEdited />
</View>
));
stories.add('Preview', () => ( stories.add('Preview', () => (
<View style={styles.container}> <View style={styles.container}>
<MarkdownPreview msg={longText} /> <MarkdownPreview msg={longText} />

View File

@ -118,7 +118,12 @@ stories.add('With alias', () => (
</> </>
)); ));
stories.add('Edited', () => <Message msg='Message' edited />); stories.add('Edited', () => (
<>
<Message msg='Message header' isEdited />
<Message msg='Message without header' isEdited isHeader={false} />
</>
));
stories.add('Encrypted', () => ( stories.add('Encrypted', () => (
<> <>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7245,8 +7245,8 @@ commondir@^1.0.1:
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
"commonmark-react-renderer@git+https://github.com/RocketChat/commonmark-react-renderer.git": "commonmark-react-renderer@git+https://github.com/RocketChat/commonmark-react-renderer.git":
version "4.3.4" version "4.3.6"
resolved "git+https://github.com/RocketChat/commonmark-react-renderer.git#1264ac7b1c13d9be3e2f67eec6702a3132f4fac2" resolved "git+https://github.com/RocketChat/commonmark-react-renderer.git#593b64e4829e1def9cceb3ffc7098b15e5df3064"
dependencies: dependencies:
lodash.assign "^4.2.0" lodash.assign "^4.2.0"
lodash.isplainobject "^4.0.6" lodash.isplainobject "^4.0.6"