[IMPROVE] Create edited component for edited messages (#4048)
This commit is contained in:
parent
f60de94cba
commit
e0bfdee70c
|
@ -1,10 +1,9 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import { Image, StyleProp, Text, TextStyle } from 'react-native';
|
||||
import { Node, Parser } from 'commonmark';
|
||||
import { Parser } from 'commonmark';
|
||||
import Renderer from 'commonmark-react-renderer';
|
||||
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||
|
||||
import I18n from '../../i18n';
|
||||
import MarkdownLink from './Link';
|
||||
import MarkdownList from './List';
|
||||
import MarkdownListItem from './ListItem';
|
||||
|
@ -37,7 +36,6 @@ interface IMarkdownProps {
|
|||
baseUrl?: string;
|
||||
username?: string;
|
||||
tmid?: string;
|
||||
isEdited?: boolean;
|
||||
numberOfLines?: number;
|
||||
customEmojis?: boolean;
|
||||
useRealName?: boolean;
|
||||
|
@ -133,9 +131,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
|||
|
||||
table: this.renderTable,
|
||||
table_row: this.renderTableRow,
|
||||
table_cell: this.renderTableCell,
|
||||
|
||||
editedIndicator: this.renderEditedIndicator
|
||||
table_cell: this.renderTableCell
|
||||
},
|
||||
renderParagraphsInLists: true
|
||||
});
|
||||
|
@ -145,21 +141,6 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
|||
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 }) => {
|
||||
const { numberOfLines, style = [] } = this.props;
|
||||
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) }} />;
|
||||
};
|
||||
|
||||
renderEditedIndicator = () => {
|
||||
const { theme } = this.props;
|
||||
return <Text style={[styles.edited, { color: themes[theme].auxiliaryText }]}> ({I18n.t('edited')})</Text>;
|
||||
};
|
||||
|
||||
renderHeading = ({ children, level }: any) => {
|
||||
const { numberOfLines, theme } = this.props;
|
||||
// @ts-ignore
|
||||
|
@ -373,7 +349,6 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
|||
let ast = parser.parse(m);
|
||||
ast = mergeTextNodes(ast);
|
||||
this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3;
|
||||
this.editedMessage(ast);
|
||||
return this.renderer.render(ast);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,10 +94,6 @@ export default StyleSheet.create({
|
|||
fontSize: 16,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
edited: {
|
||||
fontSize: 14,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
heading1: {
|
||||
...sharedStyles.textBold,
|
||||
fontSize: 24
|
||||
|
|
|
@ -59,7 +59,6 @@ const Content = React.memo(
|
|||
getCustomEmoji={props.getCustomEmoji}
|
||||
enableMessageParser={user.enableMessageParserEarlyAdoption}
|
||||
username={user.username}
|
||||
isEdited={props.isEdited}
|
||||
channels={props.channels}
|
||||
mentions={props.mentions}
|
||||
navToRoomInfo={props.navToRoomInfo}
|
||||
|
@ -72,7 +71,7 @@ const Content = React.memo(
|
|||
}
|
||||
|
||||
// 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 = (
|
||||
<View style={styles.flex}>
|
||||
<View style={styles.contentContainer}>{content}</View>
|
||||
|
|
|
@ -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;
|
|
@ -17,7 +17,7 @@ const Encrypted = React.memo(({ type }: { type: string }) => {
|
|||
}
|
||||
|
||||
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} />
|
||||
</Touchable>
|
||||
);
|
||||
|
|
|
@ -20,6 +20,8 @@ import CallButton from './CallButton';
|
|||
import { themes } from '../../lib/constants';
|
||||
import { IMessage, IMessageInner, IMessageTouchable } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
import Edited from './Edited';
|
||||
import MessageError from './MessageError';
|
||||
|
||||
const MessageInner = React.memo((props: IMessageInner) => {
|
||||
const { attachments } = props;
|
||||
|
@ -102,6 +104,12 @@ const Message = React.memo((props: IMessage) => {
|
|||
<View style={[styles.messageContent, props.isHeader && styles.messageContentWithHeader]}>
|
||||
<MessageInner {...props} />
|
||||
</View>
|
||||
{!props.isHeader ? (
|
||||
<>
|
||||
<Edited isEdited={props.isEdited} />
|
||||
<MessageError hasError={props.hasError} />
|
||||
</>
|
||||
) : null}
|
||||
<ReadReceipt isReadReceiptEnabled={props.isReadReceiptEnabled} unread={props.unread || false} />
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
@ -18,8 +18,8 @@ const MessageError = React.memo(
|
|||
}
|
||||
|
||||
return (
|
||||
<Touchable onPress={onErrorPress} style={styles.errorButton} hitSlop={BUTTON_HIT_SLOP}>
|
||||
<CustomIcon name='warning' color={themes[theme].dangerColor} size={18} />
|
||||
<Touchable onPress={onErrorPress} style={styles.leftIcons} hitSlop={BUTTON_HIT_SLOP}>
|
||||
<CustomIcon name='warning' color={themes[theme].dangerColor} size={16} />
|
||||
</Touchable>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@ import { useTheme } from '../../theme';
|
|||
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread }: { isReadReceiptEnabled?: boolean; unread: boolean }) => {
|
||||
const { theme } = useTheme();
|
||||
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;
|
||||
});
|
||||
|
|
|
@ -11,6 +11,8 @@ import MessageContext from './Context';
|
|||
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME } from './utils';
|
||||
import { SubscriptionType } from '../../definitions';
|
||||
import { IRoomInfoParam } from '../../views/SearchMessagesView';
|
||||
import Edited from './Edited';
|
||||
import Encrypted from './Encrypted';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
|
@ -19,6 +21,10 @@ const styles = StyleSheet.create({
|
|||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
},
|
||||
actionIcons: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
username: {
|
||||
fontSize: 16,
|
||||
lineHeight: 22,
|
||||
|
@ -41,7 +47,7 @@ const styles = StyleSheet.create({
|
|||
|
||||
interface IMessageUser {
|
||||
isHeader?: boolean;
|
||||
hasError?: boolean;
|
||||
hasError: boolean;
|
||||
useRealName?: boolean;
|
||||
author?: {
|
||||
_id: string;
|
||||
|
@ -53,10 +59,11 @@ interface IMessageUser {
|
|||
timeFormat?: string;
|
||||
navToRoomInfo?: (navParam: IRoomInfoParam) => void;
|
||||
type: string;
|
||||
isEdited: boolean;
|
||||
}
|
||||
|
||||
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 { theme } = useTheme();
|
||||
|
||||
|
@ -100,8 +107,12 @@ const User = React.memo(
|
|||
{textContent}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.actionIcons}>
|
||||
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryTintColor }]}>{time}</Text>
|
||||
{hasError ? <MessageError hasError={hasError} {...props} /> : null}
|
||||
<Encrypted type={type} />
|
||||
<Edited isEdited={isEdited} />
|
||||
<MessageError hasError={hasError} {...props} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ export interface IMessageContent {
|
|||
isIgnored: boolean;
|
||||
type: string;
|
||||
comment?: string;
|
||||
hasError: boolean;
|
||||
isHeader: boolean;
|
||||
}
|
||||
|
||||
export interface IMessageEmoji {
|
||||
|
|
|
@ -74,10 +74,6 @@ export default StyleSheet.create({
|
|||
avatarSmall: {
|
||||
marginLeft: 16
|
||||
},
|
||||
errorButton: {
|
||||
paddingLeft: 10,
|
||||
paddingVertical: 5
|
||||
},
|
||||
buttonContainer: {
|
||||
marginTop: 8,
|
||||
flexDirection: 'row',
|
||||
|
@ -167,12 +163,13 @@ export default StyleSheet.create({
|
|||
threadBell: {
|
||||
marginLeft: 8
|
||||
},
|
||||
leftIcons: {
|
||||
paddingLeft: 5,
|
||||
paddingVertical: 5
|
||||
},
|
||||
readReceipt: {
|
||||
lineHeight: 20
|
||||
},
|
||||
encrypted: {
|
||||
justifyContent: 'center'
|
||||
},
|
||||
threadDetails: {
|
||||
flex: 1,
|
||||
marginLeft: 12
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -54,12 +54,6 @@ stories.add('Text', () => (
|
|||
</View>
|
||||
));
|
||||
|
||||
stories.add('Edited', () => (
|
||||
<View style={styles.container}>
|
||||
<Markdown msg='This is edited' theme={theme} isEdited />
|
||||
</View>
|
||||
));
|
||||
|
||||
stories.add('Preview', () => (
|
||||
<View style={styles.container}>
|
||||
<MarkdownPreview msg={longText} />
|
||||
|
|
|
@ -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', () => (
|
||||
<>
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -7245,8 +7245,8 @@ commondir@^1.0.1:
|
|||
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
||||
|
||||
"commonmark-react-renderer@git+https://github.com/RocketChat/commonmark-react-renderer.git":
|
||||
version "4.3.4"
|
||||
resolved "git+https://github.com/RocketChat/commonmark-react-renderer.git#1264ac7b1c13d9be3e2f67eec6702a3132f4fac2"
|
||||
version "4.3.6"
|
||||
resolved "git+https://github.com/RocketChat/commonmark-react-renderer.git#593b64e4829e1def9cceb3ffc7098b15e5df3064"
|
||||
dependencies:
|
||||
lodash.assign "^4.2.0"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
|
|
Loading…
Reference in New Issue