mattermost-mobile/app/components/jumbo_emoji/index.tsx
Avinash Lingaloo e8ce78f39d
MM-36721 - [GEKIDOU] Porting Markdown components (#5586)
* Started with Channel Post List

* Added markdown hashtag

* Added TouchableWithFeedback component

* Added utils/bottom_sheet

* Removed BottomSheet in favor of future SlideUpPanel

* Added markdown_block_quote

* Added markdown_list_item

* Added markdown_list

* Added MarkDownTableCell component

* Markdown_table - in progress - need to verify TS

* Added markdown_table

* Update Podfile.lock

* Added deep_linking constant

* Added utils/draft

* Update config to include ExperimentalNormalizeMarkdownLinks

* Added markdown_link

* Added markdown_table_row

* Added ProgressiveImage and RetriableImage components and images utils

* Converted Retriable component to functional component

* Added type definition for commonmark

* Continuing with markdown TS

* Markdown - Typing props [ in progress ]

* Fix boolean flag with mardown block quote

* Adding observable config to markdown_link

* TS Fixes [ in progress ]

* TS fixes

* TS fixes - TextStyles

* Update markdown.tsx

* TS fixes on markdown

* TS Fixes - AtMention component

* AtMention [ IN PROGRESS ]

* Add markdown support

* Fix emoji and jumboEmoji on iOS

* Fix handleMyTeam operator

* Fix navigation style based on theme

* Fix iOS MattermostManaged deleteDatabse return error type

* wrap setNavigationStackStyles under a requestAnimationFrame

* Add preventDoubleTap to channel mention

* Increase double tap to 750ms

* Fix handleReceivedPostsInChannel chunk query

* Set initial navigation theme

* Swizzle FastImage on iOS

* fix preventDoubleTap test

Co-authored-by: Avinash Lingaloo <>
Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2021-08-02 20:30:17 +04:00

140 lines
4.2 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Node, Parser} from 'commonmark';
import Renderer from 'commonmark-react-renderer';
import React, {ReactElement, useRef} from 'react';
import {Platform, StyleProp, StyleSheet, Text, TextStyle, View} from 'react-native';
import Emoji from '@components/emoji';
import FormattedText from '@components/formatted_text';
import {useTheme} from '@context/theme';
import {blendColors, concatStyles, makeStyleSheetFromTheme} from '@utils/theme';
type JumboEmojiProps = {
baseTextStyle: StyleProp<TextStyle>;
isEdited?: boolean;
value: string;
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
// Android has trouble giving text transparency depending on how it's nested,
// so we calculate the resulting colour manually
const editedOpacity = Platform.select({
ios: 0.3,
android: 1.0,
});
const editedColor = Platform.select({
ios: theme.centerChannelColor,
android: blendColors(theme.centerChannelBg, theme.centerChannelColor, 0.3),
});
return {
block: {
alignItems: 'flex-start',
flexDirection: 'row',
flexWrap: 'wrap',
},
editedIndicatorText: {
color: editedColor,
opacity: editedOpacity,
},
jumboEmoji: {
fontSize: 50,
lineHeight: 60,
},
};
});
const JumboEmoji = ({baseTextStyle, isEdited, value}: JumboEmojiProps) => {
const theme = useTheme();
const style = getStyleSheet(theme);
const renderEmoji = ({emojiName, literal}: {context: string[]; emojiName: string; literal: string}) => {
const flat = StyleSheet.flatten(style.jumboEmoji);
const size = flat.lineHeight - flat.fontSize;
return (
<Emoji
emojiName={emojiName}
literal={literal}
testID='markdown_emoji'
textStyle={concatStyles(baseTextStyle, style.jumboEmoji)}
customEmojiStyle={Platform.select({android: {marginRight: size, top: size}})}
/>
);
};
const renderParagraph = ({children}: {children: ReactElement}) => {
return (
<View style={style.block}><Text>{children}</Text></View>
);
};
const renderText = ({literal}: {literal: string}) => {
return <Text style={baseTextStyle}>{literal}</Text>;
};
const renderNewLine = () => {
return <Text style={baseTextStyle}>{'\n'}</Text>;
};
const renderEditedIndicator = ({context}: {context: string[]}) => {
let spacer = '';
if (context[0] === 'paragraph') {
spacer = ' ';
}
const styles = [
baseTextStyle,
style.editedIndicatorText,
];
return (
<Text style={styles}>
{spacer}
<FormattedText
id='post_message_view.edited'
defaultMessage='(edited)'
/>
</Text>
);
};
const createRenderer = () => {
const renderers: any = {
editedIndicator: renderEditedIndicator,
emoji: renderEmoji,
paragraph: renderParagraph,
document: renderParagraph,
text: renderText,
hardbreak: renderNewLine,
softBreak: renderNewLine,
};
return new Renderer({
renderers,
renderParagraphsInLists: true,
});
};
const parser = useRef(new Parser()).current;
const renderer = useRef(createRenderer()).current;
const ast = parser.parse(value.replace(/\n*$/, ''));
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);
}
}
return renderer.render(ast) as ReactElement;
};
export default JumboEmoji;