[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 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 (
|
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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
@ -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} />
|
||||||
|
|
|
@ -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
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue