forked from Ivasoft/mattermost-mobile
[Gekidou] Markdown SVG & image size support (#6032)
* Support Markdown svg and custom size inline images * remove commonmark patches * move getMarkdownImageSize to @utils/markdown * Fix worklet not present while running unit tests that use calculateDimensions * Set max size for SVG * Set svg dimensions based on calculated size * feedback review
This commit is contained in:
@@ -26,7 +26,10 @@ import MarkdownTableImage from './markdown_table_image';
|
||||
import MarkdownTableRow, {MarkdownTableRowProps} from './markdown_table_row';
|
||||
import {addListItemIndices, combineTextNodes, highlightMentions, pullOutImages} from './transform';
|
||||
|
||||
import type {MarkdownBlockStyles, MarkdownTextStyles, UserMentionKey} from '@typings/global/markdown';
|
||||
import type {
|
||||
MarkdownAtMentionRenderer, MarkdownBaseRenderer, MarkdownBlockStyles, MarkdownChannelMentionRenderer,
|
||||
MarkdownEmojiRenderer, MarkdownImageRenderer, MarkdownTextStyles, UserMentionKey,
|
||||
} from '@typings/global/markdown';
|
||||
|
||||
type MarkdownProps = {
|
||||
autolinkedUrlSchemes?: string[];
|
||||
@@ -142,6 +145,7 @@ class Markdown extends PureComponent<MarkdownProps> {
|
||||
if (node.type === 'image') {
|
||||
extraProps.reactChildren = node.react.children;
|
||||
extraProps.linkDestination = node.linkDestination;
|
||||
extraProps.size = node.size;
|
||||
}
|
||||
|
||||
return extraProps;
|
||||
@@ -154,7 +158,7 @@ class Markdown extends PureComponent<MarkdownProps> {
|
||||
return contextStyles.length ? concatStyles(baseStyle, contextStyles) : baseStyle;
|
||||
};
|
||||
|
||||
renderText = ({context, literal}: any) => {
|
||||
renderText = ({context, literal}: MarkdownBaseRenderer) => {
|
||||
if (context.indexOf('image') !== -1) {
|
||||
// If this text is displayed, it will be styled by the image component
|
||||
return (
|
||||
@@ -177,12 +181,12 @@ class Markdown extends PureComponent<MarkdownProps> {
|
||||
);
|
||||
};
|
||||
|
||||
renderCodeSpan = ({context, literal}: {context: any; literal: any}) => {
|
||||
renderCodeSpan = ({context, literal}: MarkdownBaseRenderer) => {
|
||||
const {baseTextStyle, textStyles: {code}} = this.props;
|
||||
return <Text style={this.computeTextStyle([baseTextStyle, code], context)}>{literal}</Text>;
|
||||
};
|
||||
|
||||
renderImage = ({linkDestination, context, src}: {linkDestination?: string; context: string[]; src: string}) => {
|
||||
renderImage = ({linkDestination, context, src, size}: MarkdownImageRenderer) => {
|
||||
if (!this.props.imagesMetadata) {
|
||||
return null;
|
||||
}
|
||||
@@ -210,11 +214,12 @@ class Markdown extends PureComponent<MarkdownProps> {
|
||||
location={this.props.location}
|
||||
postId={this.props.postId!}
|
||||
source={src}
|
||||
sourceSize={size}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderAtMention = ({context, mentionName}: {context: string[]; mentionName: string}) => {
|
||||
renderAtMention = ({context, mentionName}: MarkdownAtMentionRenderer) => {
|
||||
if (this.props.disableAtMentions) {
|
||||
return this.renderText({context, literal: `@${mentionName}`});
|
||||
}
|
||||
@@ -234,7 +239,7 @@ class Markdown extends PureComponent<MarkdownProps> {
|
||||
);
|
||||
};
|
||||
|
||||
renderChannelLink = ({context, channelName}: {context: string[]; channelName: string}) => {
|
||||
renderChannelLink = ({context, channelName}: MarkdownChannelMentionRenderer) => {
|
||||
if (this.props.disableChannelLink) {
|
||||
return this.renderText({context, literal: `~${channelName}`});
|
||||
}
|
||||
@@ -249,7 +254,7 @@ class Markdown extends PureComponent<MarkdownProps> {
|
||||
);
|
||||
};
|
||||
|
||||
renderEmoji = ({context, emojiName, literal}: {context: string[]; emojiName: string; literal: string}) => {
|
||||
renderEmoji = ({context, emojiName, literal}: MarkdownEmojiRenderer) => {
|
||||
return (
|
||||
<Emoji
|
||||
emojiName={emojiName}
|
||||
|
||||
@@ -5,43 +5,47 @@ import {useManagedConfig} from '@mattermost/react-native-emm';
|
||||
import Clipboard from '@react-native-community/clipboard';
|
||||
import React, {useCallback, useMemo, useRef, useState} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Alert, Platform, StyleProp, StyleSheet, Text, TextStyle, View} from 'react-native';
|
||||
import {Alert, Platform, StyleProp, Text, TextStyle, View} from 'react-native';
|
||||
import {LongPressGestureHandler, TapGestureHandler} from 'react-native-gesture-handler';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import {SvgUri} from 'react-native-svg';
|
||||
import parseUrl from 'url-parse';
|
||||
|
||||
import {GalleryInit} from '@app/context/gallery';
|
||||
import {useGalleryItem} from '@app/hooks/gallery';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import FormattedText from '@components/formatted_text';
|
||||
import ProgressiveImage from '@components/progressive_image';
|
||||
import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item';
|
||||
import TouchableWithFeedback from '@components/touchable_with_feedback';
|
||||
import {GalleryInit} from '@context/gallery';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import {useGalleryItem} from '@hooks/gallery';
|
||||
import {bottomSheet, dismissBottomSheet} from '@screens/navigation';
|
||||
import {lookupMimeType} from '@utils/file';
|
||||
import {openGalleryAtIndex} from '@utils/gallery';
|
||||
import {fileToGalleryItem, openGalleryAtIndex} from '@utils/gallery';
|
||||
import {generateId} from '@utils/general';
|
||||
import {calculateDimensions, getViewPortWidth, isGifTooLarge} from '@utils/images';
|
||||
import {getMarkdownImageSize} from '@utils/markdown';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {normalizeProtocol, tryOpenURL} from '@utils/url';
|
||||
|
||||
type MarkdownImageProps = {
|
||||
disabled?: boolean;
|
||||
errorTextStyle: StyleProp<TextStyle>;
|
||||
imagesMetadata?: Record<string, PostImage>;
|
||||
imagesMetadata: Record<string, PostImage>;
|
||||
isReplyPost?: boolean;
|
||||
linkDestination?: string;
|
||||
location?: string;
|
||||
postId: string;
|
||||
source: string;
|
||||
sourceSize?: {width?: number; height?: number};
|
||||
}
|
||||
|
||||
const ANDROID_MAX_HEIGHT = 4096;
|
||||
const ANDROID_MAX_WIDTH = 4096;
|
||||
|
||||
const style = StyleSheet.create({
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
bottomSheet: {
|
||||
flex: 1,
|
||||
},
|
||||
@@ -52,21 +56,27 @@ const style = StyleSheet.create({
|
||||
container: {
|
||||
marginBottom: 5,
|
||||
},
|
||||
});
|
||||
svg: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06),
|
||||
borderRadius: 8,
|
||||
flex: 1,
|
||||
},
|
||||
}));
|
||||
|
||||
const MarkdownImage = ({
|
||||
disabled, errorTextStyle, imagesMetadata, isReplyPost = false,
|
||||
linkDestination, location, postId, source,
|
||||
linkDestination, location, postId, source, sourceSize,
|
||||
}: MarkdownImageProps) => {
|
||||
const intl = useIntl();
|
||||
const isTablet = useIsTablet();
|
||||
const theme = useTheme();
|
||||
const style = getStyleSheet(theme);
|
||||
const managedConfig = useManagedConfig();
|
||||
const genericFileId = useRef(generateId('uid')).current;
|
||||
const tapRef = useRef<TapGestureHandler>();
|
||||
const metadata = imagesMetadata?.[source] || Object.values(imagesMetadata || {})[0];
|
||||
const metadata = imagesMetadata?.[source] || Object.values(imagesMetadata || {})?.[0];
|
||||
const [failed, setFailed] = useState(isGifTooLarge(metadata));
|
||||
const originalSize = {width: metadata?.width || 0, height: metadata?.height || 0};
|
||||
const originalSize = getMarkdownImageSize(isReplyPost, isTablet, sourceSize, metadata);
|
||||
const serverUrl = useServerUrl();
|
||||
const galleryIdentifier = `${postId}-${genericFileId}-${location}`;
|
||||
const uri = useMemo(() => {
|
||||
@@ -80,7 +90,7 @@ const MarkdownImage = ({
|
||||
const fileInfo = useMemo(() => {
|
||||
const link = decodeURIComponent(uri);
|
||||
let filename = parseUrl(link.substr(link.lastIndexOf('/'))).pathname.replace('/', '');
|
||||
let extension = filename.split('.').pop();
|
||||
let extension = metadata.format || filename.split('.').pop();
|
||||
if (extension === filename) {
|
||||
const ext = filename.indexOf('.') === -1 ? '.png' : filename.substring(filename.lastIndexOf('.'));
|
||||
filename = `${filename}${ext}`;
|
||||
@@ -92,17 +102,17 @@ const MarkdownImage = ({
|
||||
name: filename,
|
||||
extension,
|
||||
has_preview_image: true,
|
||||
mime_type: lookupMimeType(filename),
|
||||
post_id: postId,
|
||||
uri: link,
|
||||
width: originalSize.width,
|
||||
height: originalSize.height,
|
||||
};
|
||||
}, []);
|
||||
} as FileInfo;
|
||||
}, [uri, originalSize, metadata, isReplyPost, isTablet]);
|
||||
|
||||
const handlePreviewImage = useCallback(() => {
|
||||
const item: GalleryItemType = {
|
||||
...fileInfo,
|
||||
mime_type: lookupMimeType(fileInfo.name),
|
||||
...fileToGalleryItem(fileInfo),
|
||||
type: 'image',
|
||||
};
|
||||
openGalleryAtIndex(galleryIdentifier, 0, [item]);
|
||||
@@ -184,6 +194,7 @@ const MarkdownImage = ({
|
||||
if (failed) {
|
||||
return (
|
||||
<CompassIcon
|
||||
color={theme.centerChannelColor}
|
||||
name='file-image-broken-outline-large'
|
||||
size={24}
|
||||
/>
|
||||
@@ -194,7 +205,7 @@ const MarkdownImage = ({
|
||||
if (height && width) {
|
||||
if (Platform.OS === 'android' && (height > ANDROID_MAX_HEIGHT || width > ANDROID_MAX_WIDTH)) {
|
||||
// Android has a cap on the max image size that can be displayed
|
||||
image = (
|
||||
return (
|
||||
<Text style={[errorTextStyle, style.container]}>
|
||||
<FormattedText
|
||||
id='mobile.markdown.image.too_large'
|
||||
@@ -207,8 +218,49 @@ const MarkdownImage = ({
|
||||
{' '}
|
||||
</Text>
|
||||
);
|
||||
} else if (fileInfo.extension === 'svg') {
|
||||
image = (
|
||||
<SvgUri
|
||||
uri={fileInfo.uri!}
|
||||
style={{flex: 1, backgroundColor: changeOpacity(theme.centerChannelColor, 0.06), borderRadius: 8}}
|
||||
width={width}
|
||||
height={height}
|
||||
|
||||
// @ts-expect-error onError missing in type definition
|
||||
onError={handleOnError}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
image = (
|
||||
<ProgressiveImage
|
||||
forwardRef={ref}
|
||||
id={fileInfo.id!}
|
||||
defaultSource={{uri: fileInfo.uri!}}
|
||||
onError={handleOnError}
|
||||
resizeMode='contain'
|
||||
style={{width, height}}
|
||||
/>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (image && linkDestination && !disabled) {
|
||||
return (
|
||||
<TouchableWithFeedback
|
||||
onPress={handleLinkPress}
|
||||
onLongPress={handleLinkLongPress}
|
||||
style={[{width, height}, style.container]}
|
||||
testID='markdown_image_link'
|
||||
>
|
||||
{image}
|
||||
</TouchableWithFeedback>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<GalleryInit galleryIdentifier={galleryIdentifier}>
|
||||
<Animated.View testID='markdown_image'>
|
||||
<LongPressGestureHandler
|
||||
enabled={!disabled}
|
||||
onGestureEvent={handleLinkLongPress}
|
||||
@@ -220,45 +272,12 @@ const MarkdownImage = ({
|
||||
onGestureEvent={onGestureEvent}
|
||||
ref={tapRef}
|
||||
>
|
||||
<Animated.View testID='markdown_image'>
|
||||
<ProgressiveImage
|
||||
forwardRef={ref}
|
||||
id={fileInfo.id}
|
||||
defaultSource={{uri: fileInfo.uri}}
|
||||
onError={handleOnError}
|
||||
resizeMode='contain'
|
||||
style={{width, height}}
|
||||
/>
|
||||
<Animated.View>
|
||||
{image}
|
||||
</Animated.View>
|
||||
</TapGestureHandler>
|
||||
</Animated.View>
|
||||
</LongPressGestureHandler>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (image && linkDestination && !disabled) {
|
||||
image = (
|
||||
<TouchableWithFeedback
|
||||
onPress={handleLinkPress}
|
||||
onLongPress={handleLinkLongPress}
|
||||
style={[{width, height}, style.container]}
|
||||
>
|
||||
<ProgressiveImage
|
||||
id={fileInfo.id}
|
||||
defaultSource={{uri: fileInfo.uri}}
|
||||
onError={handleOnError}
|
||||
resizeMode='contain'
|
||||
style={{width, height}}
|
||||
/>
|
||||
</TouchableWithFeedback>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<GalleryInit galleryIdentifier={galleryIdentifier}>
|
||||
<Animated.View testID='markdown_image'>
|
||||
{image}
|
||||
</Animated.View>
|
||||
</GalleryInit>
|
||||
);
|
||||
|
||||
@@ -41,6 +41,7 @@ function ImageRenderer({
|
||||
isActive={isPageActive}
|
||||
targetDimensions={targetDimensions}
|
||||
height={item.height}
|
||||
isSvg={item.extension === 'svg'}
|
||||
onStateChange={onPageStateChange}
|
||||
outerGestureHandlerRefs={pagerRefs}
|
||||
source={item.uri}
|
||||
|
||||
@@ -13,6 +13,7 @@ import Animated, {
|
||||
useSharedValue,
|
||||
withDecay, withSpring, WithSpringConfig, withTiming, WithTimingConfig,
|
||||
} from 'react-native-reanimated';
|
||||
import {SvgUri} from 'react-native-svg';
|
||||
|
||||
import {useAnimatedGestureHandler} from '@hooks/gallery';
|
||||
import {clamp, workletNoop} from '@utils/gallery';
|
||||
@@ -29,6 +30,10 @@ const styles = StyleSheet.create({
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
svg: {
|
||||
backgroundColor: '#FFF',
|
||||
borderRadius: 8,
|
||||
},
|
||||
});
|
||||
|
||||
const springConfig: WithSpringConfig = {
|
||||
@@ -66,6 +71,7 @@ export interface ImageTransformerProps extends ImageTransformerReusableProps {
|
||||
enabled?: boolean;
|
||||
height: number;
|
||||
isActive?: Animated.SharedValue<boolean>;
|
||||
isSvg: boolean;
|
||||
onStateChange?: (isActive: boolean) => void;
|
||||
outerGestureHandlerActive?: Animated.SharedValue<boolean>;
|
||||
outerGestureHandlerRefs?: Array<React.Ref<unknown>>;
|
||||
@@ -92,7 +98,7 @@ const ImageTransformer = (
|
||||
{
|
||||
enabled = true, height, isActive,
|
||||
onDoubleTap = workletNoop, onInteraction = workletNoop, onStateChange = workletNoop, onTap = workletNoop,
|
||||
outerGestureHandlerActive, outerGestureHandlerRefs = [], source,
|
||||
outerGestureHandlerActive, outerGestureHandlerRefs = [], source, isSvg,
|
||||
targetDimensions, width,
|
||||
}: ImageTransformerProps) => {
|
||||
const imageSource = typeof source === 'string' ? {uri: source} : source;
|
||||
@@ -524,6 +530,27 @@ const ImageTransformer = (
|
||||
};
|
||||
}, []);
|
||||
|
||||
let element;
|
||||
if (isSvg) {
|
||||
element = (
|
||||
<SvgUri
|
||||
uri={imageSource.uri!}
|
||||
style={styles.svg}
|
||||
width={Math.min(targetDimensions.width, targetDimensions.height)}
|
||||
height={Math.min(targetDimensions.width, targetDimensions.height)}
|
||||
onLayout={onLoadImageSuccess}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
element = (
|
||||
<FastImage
|
||||
onLoad={onLoadImageSuccess}
|
||||
source={imageSource}
|
||||
style={{width: targetWidth, height: targetHeight}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.container]}>
|
||||
<PinchGestureHandler
|
||||
@@ -564,11 +591,7 @@ const ImageTransformer = (
|
||||
onGestureEvent={onDoubleTapEvent}
|
||||
>
|
||||
<Animated.View style={animatedStyles}>
|
||||
<FastImage
|
||||
onLoad={onLoadImageSuccess}
|
||||
source={imageSource}
|
||||
style={{width: targetWidth, height: targetHeight}}
|
||||
/>
|
||||
{element}
|
||||
</Animated.View>
|
||||
</TapGestureHandler>
|
||||
</Animated.View>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Dimensions} from 'react-native';
|
||||
import 'react-native-reanimated';
|
||||
|
||||
import {View} from '@constants';
|
||||
import {IMAGE_MAX_HEIGHT, IMAGE_MIN_DIMENSION, MAX_GIF_SIZE, VIEWPORT_IMAGE_OFFSET, VIEWPORT_IMAGE_REPLY_OFFSET} from '@constants/image';
|
||||
|
||||
@@ -5,6 +5,8 @@ import {Platform, StyleSheet} from 'react-native';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import {getViewPortWidth} from '../images';
|
||||
|
||||
export function getCodeFont() {
|
||||
return Platform.OS === 'ios' ? 'Menlo' : 'monospace';
|
||||
}
|
||||
@@ -211,3 +213,46 @@ export function switchKeyboardForCodeBlocks(value: string, cursorPosition: numbe
|
||||
|
||||
return 'default';
|
||||
}
|
||||
|
||||
export const getMarkdownImageSize = (
|
||||
isReplyPost: boolean,
|
||||
isTablet: boolean,
|
||||
sourceSize?: SourceSize,
|
||||
knownSize?: PostImage,
|
||||
) => {
|
||||
let ratioW;
|
||||
let ratioH;
|
||||
|
||||
if (sourceSize?.width && sourceSize?.height) {
|
||||
// if the source image is set with HxW
|
||||
return {width: sourceSize.width, height: sourceSize.height};
|
||||
} else if (knownSize?.width && knownSize.height) {
|
||||
// If the metadata size is set calculate the ratio
|
||||
ratioW = knownSize.width > 0 ? knownSize.height / knownSize.width : 1;
|
||||
ratioH = knownSize.height > 0 ? knownSize.width / knownSize.height : 1;
|
||||
}
|
||||
|
||||
if (sourceSize?.width && !sourceSize.height && ratioW) {
|
||||
// If source Width is set calculate the height using the ratio
|
||||
return {width: sourceSize.width, height: sourceSize.width * ratioW};
|
||||
} else if (sourceSize?.height && !sourceSize.width && ratioH) {
|
||||
// If source Height is set calculate the width using the ratio
|
||||
return {width: sourceSize.height * ratioH, height: sourceSize.height};
|
||||
}
|
||||
|
||||
if (sourceSize?.width || sourceSize?.height) {
|
||||
// if at least one size is set and we do not have metadata (svg's)
|
||||
const width = sourceSize.width;
|
||||
const height = sourceSize.height;
|
||||
return {width: width || height, height: height || width};
|
||||
}
|
||||
|
||||
if (knownSize?.width && knownSize.height) {
|
||||
// When metadata values are set
|
||||
return {width: knownSize.width, height: knownSize.height};
|
||||
}
|
||||
|
||||
// When no metadata and source size is not specified (full size svg's)
|
||||
const width = getViewPortWidth(isReplyPost, isTablet);
|
||||
return {width, height: width};
|
||||
};
|
||||
|
||||
130
package-lock.json
generated
130
package-lock.json
generated
@@ -34,8 +34,8 @@
|
||||
"@rudderstack/rudder-sdk-react-native": "1.2.1",
|
||||
"@sentry/react-native": "3.2.13",
|
||||
"@stream-io/flat-list-mvcp": "0.10.1",
|
||||
"commonmark": "0.30.0",
|
||||
"commonmark-react-renderer": "4.3.5",
|
||||
"commonmark": "github:mattermost/commonmark.js#90a62d97ed2dbd2d4711a5adda327128f5827983",
|
||||
"commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d",
|
||||
"deep-equal": "2.0.5",
|
||||
"deepmerge": "4.2.2",
|
||||
"emoji-regex": "10.0.0",
|
||||
@@ -1977,6 +1977,18 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime-corejs3": {
|
||||
"version": "7.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz",
|
||||
"integrity": "sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==",
|
||||
"dependencies": {
|
||||
"core-js-pure": "^3.20.2",
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
|
||||
@@ -8554,13 +8566,15 @@
|
||||
},
|
||||
"node_modules/commonmark": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/commonmark/-/commonmark-0.30.0.tgz",
|
||||
"integrity": "sha512-j1yoUo4gxPND1JWV9xj5ELih0yMv1iCWDG6eEQIPLSWLxzCXiFoyS7kvB+WwU+tZMf4snwJMMtaubV0laFpiBA==",
|
||||
"resolved": "git+ssh://git@github.com/mattermost/commonmark.js.git#90a62d97ed2dbd2d4711a5adda327128f5827983",
|
||||
"integrity": "sha512-uOpLyASVi9LYtsMcpBrxizpzfsxS459oLGbs7oroeCmEEzhabxZZQcqVKPNfhCOF4Rh8gA/05m9UCOzjBAx9Ww==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"entities": "~2.0",
|
||||
"entities": "~3.0.1",
|
||||
"mdurl": "~1.0.1",
|
||||
"minimist": ">=1.2.2",
|
||||
"string.prototype.repeat": "^0.2.0"
|
||||
"minimist": "~1.2.5",
|
||||
"string.prototype.repeat": "^1.0.0",
|
||||
"xregexp": "5.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"commonmark": "bin/commonmark"
|
||||
@@ -8571,8 +8585,8 @@
|
||||
},
|
||||
"node_modules/commonmark-react-renderer": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/commonmark-react-renderer/-/commonmark-react-renderer-4.3.5.tgz",
|
||||
"integrity": "sha512-UwUgplz8kFSMCe9+Dg/BcV75lc7R/V6mvMYJq2p29i5aaIBd0252k9HeSGa2VtEPHfg2/trS9qC7iAxnO7r6ng==",
|
||||
"resolved": "git+ssh://git@github.com/mattermost/commonmark-react-renderer.git#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash.assign": "^4.2.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
@@ -8580,10 +8594,21 @@
|
||||
"xss-filters": "^1.2.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"commonmark": "^0.27.0 || ^0.26.0 || ^0.24.0",
|
||||
"commonmark": "^0.30.0",
|
||||
"react": ">=0.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/commonmark/node_modules/entities": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
|
||||
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/compare-versions": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz",
|
||||
@@ -8773,6 +8798,16 @@
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js-pure": {
|
||||
"version": "3.21.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz",
|
||||
"integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==",
|
||||
"hasInstallScript": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
@@ -21793,9 +21828,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/string.prototype.repeat": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz",
|
||||
"integrity": "sha1-q6Nt4I3O5qWjN9SbLqHaGyj8Ds8="
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
|
||||
"integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
|
||||
"dependencies": {
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.17.5"
|
||||
}
|
||||
},
|
||||
"node_modules/string.prototype.trimend": {
|
||||
"version": "1.0.4",
|
||||
@@ -24123,6 +24162,14 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xregexp": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-5.1.0.tgz",
|
||||
"integrity": "sha512-PynwUWtXnSZr8tpQlDPMZfPTyv78EYuA4oI959ukxcQ0a9O/lvndLVKy5wpImzzA26eMxpZmnAXJYiQA13AtWA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime-corejs3": "^7.14.9"
|
||||
}
|
||||
},
|
||||
"node_modules/xss-filters": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/xss-filters/-/xss-filters-1.2.7.tgz",
|
||||
@@ -25475,6 +25522,15 @@
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"@babel/runtime-corejs3": {
|
||||
"version": "7.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz",
|
||||
"integrity": "sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==",
|
||||
"requires": {
|
||||
"core-js-pure": "^3.20.2",
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
|
||||
@@ -30584,20 +30640,27 @@
|
||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
|
||||
},
|
||||
"commonmark": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/commonmark/-/commonmark-0.30.0.tgz",
|
||||
"integrity": "sha512-j1yoUo4gxPND1JWV9xj5ELih0yMv1iCWDG6eEQIPLSWLxzCXiFoyS7kvB+WwU+tZMf4snwJMMtaubV0laFpiBA==",
|
||||
"version": "git+ssh://git@github.com/mattermost/commonmark.js.git#90a62d97ed2dbd2d4711a5adda327128f5827983",
|
||||
"integrity": "sha512-uOpLyASVi9LYtsMcpBrxizpzfsxS459oLGbs7oroeCmEEzhabxZZQcqVKPNfhCOF4Rh8gA/05m9UCOzjBAx9Ww==",
|
||||
"from": "commonmark@github:mattermost/commonmark.js#90a62d97ed2dbd2d4711a5adda327128f5827983",
|
||||
"requires": {
|
||||
"entities": "~2.0",
|
||||
"entities": "~3.0.1",
|
||||
"mdurl": "~1.0.1",
|
||||
"minimist": ">=1.2.2",
|
||||
"string.prototype.repeat": "^0.2.0"
|
||||
"minimist": "~1.2.5",
|
||||
"string.prototype.repeat": "^1.0.0",
|
||||
"xregexp": "5.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"entities": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
|
||||
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"commonmark-react-renderer": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/commonmark-react-renderer/-/commonmark-react-renderer-4.3.5.tgz",
|
||||
"integrity": "sha512-UwUgplz8kFSMCe9+Dg/BcV75lc7R/V6mvMYJq2p29i5aaIBd0252k9HeSGa2VtEPHfg2/trS9qC7iAxnO7r6ng==",
|
||||
"version": "git+ssh://git@github.com/mattermost/commonmark-react-renderer.git#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d",
|
||||
"from": "commonmark-react-renderer@github:mattermost/commonmark-react-renderer#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d",
|
||||
"requires": {
|
||||
"lodash.assign": "^4.2.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
@@ -30776,6 +30839,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"core-js-pure": {
|
||||
"version": "3.21.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz",
|
||||
"integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ=="
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
@@ -40992,9 +41060,13 @@
|
||||
}
|
||||
},
|
||||
"string.prototype.repeat": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz",
|
||||
"integrity": "sha1-q6Nt4I3O5qWjN9SbLqHaGyj8Ds8="
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
|
||||
"integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
|
||||
"requires": {
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.17.5"
|
||||
}
|
||||
},
|
||||
"string.prototype.trimend": {
|
||||
"version": "1.0.4",
|
||||
@@ -42831,6 +42903,14 @@
|
||||
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.6.0.tgz",
|
||||
"integrity": "sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg=="
|
||||
},
|
||||
"xregexp": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-5.1.0.tgz",
|
||||
"integrity": "sha512-PynwUWtXnSZr8tpQlDPMZfPTyv78EYuA4oI959ukxcQ0a9O/lvndLVKy5wpImzzA26eMxpZmnAXJYiQA13AtWA==",
|
||||
"requires": {
|
||||
"@babel/runtime-corejs3": "^7.14.9"
|
||||
}
|
||||
},
|
||||
"xss-filters": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/xss-filters/-/xss-filters-1.2.7.tgz",
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
"@rudderstack/rudder-sdk-react-native": "1.2.1",
|
||||
"@sentry/react-native": "3.2.13",
|
||||
"@stream-io/flat-list-mvcp": "0.10.1",
|
||||
"commonmark": "0.30.0",
|
||||
"commonmark-react-renderer": "4.3.5",
|
||||
"commonmark": "github:mattermost/commonmark.js#90a62d97ed2dbd2d4711a5adda327128f5827983",
|
||||
"commonmark-react-renderer": "github:mattermost/commonmark-react-renderer#4e52e1725c0ef5b1e2ecfe9883220ec36c2eb67d",
|
||||
"deep-equal": "2.0.5",
|
||||
"deepmerge": "4.2.2",
|
||||
"emoji-regex": "10.0.0",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,351 +0,0 @@
|
||||
diff --git a/node_modules/commonmark-react-renderer/src/commonmark-react-renderer.js b/node_modules/commonmark-react-renderer/src/commonmark-react-renderer.js
|
||||
index 91b0001..05b80fa 100644
|
||||
--- a/node_modules/commonmark-react-renderer/src/commonmark-react-renderer.js
|
||||
+++ b/node_modules/commonmark-react-renderer/src/commonmark-react-renderer.js
|
||||
@@ -12,7 +12,12 @@ var typeAliases = {
|
||||
htmlblock: 'html_block',
|
||||
htmlinline: 'html_inline',
|
||||
codeblock: 'code_block',
|
||||
- hardbreak: 'linebreak'
|
||||
+ hardbreak: 'linebreak',
|
||||
+ atmention: 'at_mention',
|
||||
+ channellink: 'channel_link',
|
||||
+ editedindicator: 'edited_indicator',
|
||||
+ tableRow: 'table_row',
|
||||
+ tableCell: 'table_cell'
|
||||
};
|
||||
|
||||
var defaultRenderers = {
|
||||
@@ -24,6 +29,7 @@ var defaultRenderers = {
|
||||
link: 'a',
|
||||
paragraph: 'p',
|
||||
strong: 'strong',
|
||||
+ del: 'del',
|
||||
thematic_break: 'hr', // eslint-disable-line camelcase
|
||||
|
||||
html_block: HtmlRenderer, // eslint-disable-line camelcase
|
||||
@@ -52,7 +58,71 @@ var defaultRenderers = {
|
||||
},
|
||||
|
||||
text: null,
|
||||
- softbreak: null
|
||||
+ softbreak: null,
|
||||
+
|
||||
+ at_mention: function AtMention(props) {
|
||||
+ var newProps = getCoreProps(props);
|
||||
+ if (props.username) {
|
||||
+ props['data-mention-name'] = props.username;
|
||||
+ }
|
||||
+
|
||||
+ return createElement('span', newProps, props.children);
|
||||
+ },
|
||||
+ channel_link: function ChannelLink(props) {
|
||||
+ var newProps = getCoreProps(props);
|
||||
+ if (props.channelName) {
|
||||
+ props['data-channel-name'] = props.channelName;
|
||||
+ }
|
||||
+
|
||||
+ return createElement('span', newProps, props.children);
|
||||
+ },
|
||||
+ emoji: function Emoji(props) {
|
||||
+ var newProps = getCoreProps(props);
|
||||
+ if (props.emojiName) {
|
||||
+ props['data-emoji-name'] = props.emojiName;
|
||||
+ }
|
||||
+
|
||||
+ return createElement('span', newProps, props.children);
|
||||
+ },
|
||||
+ edited_indicator: null,
|
||||
+ hashtag: function Hashtag(props) {
|
||||
+ var newProps = getCoreProps(props);
|
||||
+ if (props.hashtag) {
|
||||
+ props['data-hashtag'] = props.hashtag;
|
||||
+ }
|
||||
+
|
||||
+ return createElement('span', newProps, props.children);
|
||||
+ },
|
||||
+ mention_highlight: function MentionHighlight(props) {
|
||||
+ var newProps = getCoreProps(props);
|
||||
+ newProps['data-mention-highlight'] = 'true';
|
||||
+ return createElement('span', newProps, props.children);
|
||||
+ },
|
||||
+ search_highlight: function SearchHighlight(props) {
|
||||
+ var newProps = getCoreProps(props);
|
||||
+ newProps['data-search-highlight'] = 'true';
|
||||
+ return createElement('span', newProps, props.children);
|
||||
+ },
|
||||
+
|
||||
+ table: function Table(props) {
|
||||
+ var childrenArray = React.Children.toArray(props.children);
|
||||
+
|
||||
+ var children = [createElement('thead', {'key': 'thead'}, childrenArray.slice(0, 1))];
|
||||
+ if (childrenArray.length > 1) {
|
||||
+ children.push(createElement('tbody', {'key': 'tbody'}, childrenArray.slice(1)));
|
||||
+ }
|
||||
+
|
||||
+ return createElement('table', getCoreProps(props), children);
|
||||
+ },
|
||||
+ table_row: 'tr',
|
||||
+ table_cell: function TableCell(props) {
|
||||
+ var newProps = getCoreProps(props);
|
||||
+ if (props.align) {
|
||||
+ newProps.className = 'align-' + props.align;
|
||||
+ }
|
||||
+
|
||||
+ return createElement('td', newProps, props.children);
|
||||
+ }
|
||||
};
|
||||
|
||||
var coreTypes = Object.keys(defaultRenderers);
|
||||
@@ -147,7 +217,7 @@ function flattenPosition(pos) {
|
||||
}
|
||||
|
||||
// For some nodes, we want to include more props than for others
|
||||
-function getNodeProps(node, key, opts, renderer) {
|
||||
+function getNodeProps(node, key, opts, renderer, context) {
|
||||
var props = { key: key }, undef;
|
||||
|
||||
// `sourcePos` is true if the user wants source information (line/column info from markdown source)
|
||||
@@ -194,16 +264,49 @@ function getNodeProps(node, key, opts, renderer) {
|
||||
|
||||
// Commonmark treats image description as children. We just want the text
|
||||
props.alt = node.react.children.join('');
|
||||
- node.react.children = undef;
|
||||
break;
|
||||
case 'list':
|
||||
props.start = node.listStart;
|
||||
props.type = node.listType;
|
||||
props.tight = node.listTight;
|
||||
break;
|
||||
+ case 'at_mention':
|
||||
+ props.mentionName = node.mentionName;
|
||||
+ break;
|
||||
+ case 'channel_link':
|
||||
+ props.channelName = node.channelName;
|
||||
+ break;
|
||||
+ case 'emoji':
|
||||
+ props.emojiName = node.emojiName;
|
||||
+ props.literal = node.literal;
|
||||
+ break;
|
||||
+ case 'hashtag':
|
||||
+ props.hashtag = node.hashtag;
|
||||
+ break;
|
||||
+ case 'paragraph':
|
||||
+ props.first = !(node._prev && node._prev.type === 'paragraph');
|
||||
+ props.last = !(node._next && node._next.type === 'paragraph');
|
||||
+ break;
|
||||
+ case 'edited_indicator':
|
||||
+ break;
|
||||
+ case 'table':
|
||||
+ props.numRows = countRows(node);
|
||||
+ props.numColumns = countColumns(node);
|
||||
+ break;
|
||||
+ case 'table_row':
|
||||
+ props.isHeading = node.isHeading;
|
||||
+ break;
|
||||
+ case 'table_cell':
|
||||
+ props.isHeading = node.isHeading;
|
||||
+ props.align = node.align;
|
||||
+ break;
|
||||
default:
|
||||
}
|
||||
|
||||
+ if (opts.getExtraPropsForNode) {
|
||||
+ props = Object.assign(props, opts.getExtraPropsForNode(node));
|
||||
+ }
|
||||
+
|
||||
if (typeof renderer !== 'string') {
|
||||
props.literal = node.literal;
|
||||
}
|
||||
@@ -213,9 +316,29 @@ function getNodeProps(node, key, opts, renderer) {
|
||||
props.children = children.reduce(reduceChildren, []) || null;
|
||||
}
|
||||
|
||||
+ props.context = context.slice();
|
||||
+
|
||||
return props;
|
||||
}
|
||||
|
||||
+function countChildren(node) {
|
||||
+ var count = 0;
|
||||
+
|
||||
+ for (var child = node.firstChild; child; child = child.next) {
|
||||
+ count += 1;
|
||||
+ }
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+function countRows(table) {
|
||||
+ return countChildren(table);
|
||||
+}
|
||||
+
|
||||
+function countColumns(table) {
|
||||
+ return countChildren(table.firstChild);
|
||||
+}
|
||||
+
|
||||
function getPosition(node) {
|
||||
if (!node) {
|
||||
return null;
|
||||
@@ -238,26 +361,23 @@ function renderNodes(block) {
|
||||
transformLinkUri: this.transformLinkUri,
|
||||
transformImageUri: this.transformImageUri,
|
||||
softBreak: this.softBreak,
|
||||
- linkTarget: this.linkTarget
|
||||
+ linkTarget: this.linkTarget,
|
||||
+ getExtraPropsForNode: this.getExtraPropsForNode
|
||||
};
|
||||
|
||||
- var e, node, entering, leaving, type, doc, key, nodeProps, prevPos, prevIndex = 0;
|
||||
+ var e;
|
||||
+ var doc;
|
||||
+ var context = [];
|
||||
+ var index = 0;
|
||||
while ((e = walker.next())) {
|
||||
- var pos = getPosition(e.node.sourcepos ? e.node : e.node.parent);
|
||||
- if (prevPos === pos) {
|
||||
- key = pos + prevIndex;
|
||||
- prevIndex++;
|
||||
- } else {
|
||||
- key = pos;
|
||||
- prevIndex = 0;
|
||||
- }
|
||||
+ var key = String(index);
|
||||
+ index += 1;
|
||||
|
||||
- prevPos = pos;
|
||||
- entering = e.entering;
|
||||
- leaving = !entering;
|
||||
- node = e.node;
|
||||
- type = normalizeTypeName(node.type);
|
||||
- nodeProps = null;
|
||||
+ var entering = e.entering;
|
||||
+ var leaving = !entering;
|
||||
+ var node = e.node;
|
||||
+ var type = normalizeTypeName(node.type);
|
||||
+ var nodeProps = null;
|
||||
|
||||
// If we have not assigned a document yet, assume the current node is just that
|
||||
if (!doc) {
|
||||
@@ -270,7 +390,7 @@ function renderNodes(block) {
|
||||
}
|
||||
|
||||
// In HTML, we don't want paragraphs inside of list items
|
||||
- if (type === 'paragraph' && isGrandChildOfList(node)) {
|
||||
+ if (!this.renderParagraphsInLists && type === 'paragraph' && isGrandChildOfList(node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -289,7 +409,7 @@ function renderNodes(block) {
|
||||
if (this.allowNode && (isCompleteParent || !node.isContainer)) {
|
||||
var nodeChildren = isCompleteParent ? node.react.children : [];
|
||||
|
||||
- nodeProps = getNodeProps(node, key, propOptions, renderer);
|
||||
+ nodeProps = getNodeProps(node, key, propOptions, renderer, context);
|
||||
disallowedByUser = !this.allowNode({
|
||||
type: pascalCase(type),
|
||||
renderer: this.renderers[type],
|
||||
@@ -298,6 +418,30 @@ function renderNodes(block) {
|
||||
});
|
||||
}
|
||||
|
||||
+ if (node.isContainer) {
|
||||
+ var contextType = node.type;
|
||||
+ if (node.level) {
|
||||
+ contextType = node.type + node.level;
|
||||
+ } else if (node.type === 'table_row' && node.parent.firstChild === node) {
|
||||
+ contextType = 'table_header_row';
|
||||
+ } else {
|
||||
+ contextType = node.type;
|
||||
+ }
|
||||
+
|
||||
+ if (entering) {
|
||||
+ context.push(contextType);
|
||||
+ } else {
|
||||
+ var popped = context.pop();
|
||||
+
|
||||
+ if (!popped) {
|
||||
+ throw new Error('Attempted to pop empty stack');
|
||||
+ } else if (!popped === contextType) {
|
||||
+ throw new Error('Popped context of type `' + pascalCase(popped) +
|
||||
+ '` when expecting context of type `' + pascalCase(contextType) + '`');
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
if (!isDocument && (disallowedByUser || disallowedByConfig)) {
|
||||
if (!this.unwrapDisallowed && entering && node.isContainer) {
|
||||
walker.resumeAt(node, false);
|
||||
@@ -313,15 +457,25 @@ function renderNodes(block) {
|
||||
);
|
||||
}
|
||||
|
||||
- if (node.isContainer && entering) {
|
||||
+ if (context.length > this.maxDepth) {
|
||||
+ // Do nothing, we should not regularly be nested this deeply and we don't want to cause React to
|
||||
+ // overflow the stack
|
||||
+ } else if (node.isContainer && entering) {
|
||||
node.react = {
|
||||
component: renderer,
|
||||
props: {},
|
||||
children: []
|
||||
};
|
||||
} else {
|
||||
- var childProps = nodeProps || getNodeProps(node, key, propOptions, renderer);
|
||||
- if (renderer) {
|
||||
+ var childProps = nodeProps || getNodeProps(node, key, propOptions, renderer, context);
|
||||
+ if (renderer === ReactRenderer.forwardChildren) {
|
||||
+ if (childProps.children) {
|
||||
+ for (var i = 0; i < childProps.children.length; i++) {
|
||||
+ var child = childProps.children[i];
|
||||
+ addChild(node, child);
|
||||
+ }
|
||||
+ }
|
||||
+ } else if (renderer) {
|
||||
childProps = typeof renderer === 'string'
|
||||
? childProps
|
||||
: assign(childProps, {nodeKey: childProps.key});
|
||||
@@ -341,6 +495,10 @@ function renderNodes(block) {
|
||||
}
|
||||
}
|
||||
|
||||
+ if (context.length !== 0) {
|
||||
+ throw new Error('Expected context to be empty after rendering, but has `' + context.join(', ') + '`');
|
||||
+ }
|
||||
+
|
||||
return doc.react.children;
|
||||
}
|
||||
|
||||
@@ -401,21 +559,31 @@ function ReactRenderer(options) {
|
||||
renderers: assign({}, defaultRenderers, normalizeRenderers(opts.renderers)),
|
||||
escapeHtml: Boolean(opts.escapeHtml),
|
||||
skipHtml: Boolean(opts.skipHtml),
|
||||
+ renderParagraphsInLists: Boolean(opts.renderParagraphsInLists),
|
||||
transformLinkUri: linkFilter,
|
||||
transformImageUri: imageFilter,
|
||||
allowNode: opts.allowNode,
|
||||
allowedTypes: allowedTypes,
|
||||
unwrapDisallowed: Boolean(opts.unwrapDisallowed),
|
||||
render: renderNodes,
|
||||
- linkTarget: opts.linkTarget || false
|
||||
+ linkTarget: opts.linkTarget || false,
|
||||
+ maxDepth: opts.maxDepth || 30,
|
||||
+ getExtraPropsForNode: opts.getExtraPropsForNode
|
||||
};
|
||||
}
|
||||
|
||||
+function forwardChildren(props) {
|
||||
+ return props.children;
|
||||
+}
|
||||
+
|
||||
ReactRenderer.uriTransformer = defaultLinkUriFilter;
|
||||
ReactRenderer.types = coreTypes.map(pascalCase);
|
||||
ReactRenderer.renderers = coreTypes.reduce(function(renderers, type) {
|
||||
renderers[pascalCase(type)] = defaultRenderers[type];
|
||||
return renderers;
|
||||
}, {});
|
||||
+ReactRenderer.countRows = countRows;
|
||||
+ReactRenderer.countColumns = countColumns;
|
||||
+ReactRenderer.forwardChildren = forwardChildren;
|
||||
|
||||
module.exports = ReactRenderer;
|
||||
23
patches/react-native-svg+12.1.1.patch
Normal file
23
patches/react-native-svg+12.1.1.patch
Normal file
@@ -0,0 +1,23 @@
|
||||
diff --git a/node_modules/react-native-svg/src/xml.tsx b/node_modules/react-native-svg/src/xml.tsx
|
||||
index 828f104..462be2e 100644
|
||||
--- a/node_modules/react-native-svg/src/xml.tsx
|
||||
+++ b/node_modules/react-native-svg/src/xml.tsx
|
||||
@@ -133,10 +133,17 @@ export function SvgUri(props: UriProps) {
|
||||
useEffect(() => {
|
||||
uri
|
||||
? fetchText(uri)
|
||||
- .then(setXml)
|
||||
+ .then((xml) => {
|
||||
+ if (xml && /xmlns="http:\/\/www.w3.org\/[0-9]*\/svg"/.test(xml)) {
|
||||
+ setXml(xml);
|
||||
+ return;
|
||||
+ }
|
||||
+ onError();
|
||||
+ })
|
||||
.catch(onError)
|
||||
: setXml(null);
|
||||
}, [onError, uri]);
|
||||
+
|
||||
return <SvgXml xml={xml} override={props} />;
|
||||
}
|
||||
|
||||
@@ -17,3 +17,32 @@ export type MarkdownBlockStyles = {
|
||||
export type MarkdownTextStyles = {
|
||||
[key: string]: TextStyle;
|
||||
};
|
||||
|
||||
export type MarkdownAtMentionRenderer = {
|
||||
context: string[];
|
||||
mentionName: string;
|
||||
}
|
||||
|
||||
export type MarkdownBaseRenderer = {
|
||||
context: string[];
|
||||
literal: string;
|
||||
}
|
||||
|
||||
export type MarkdownChannelMentionRenderer = {
|
||||
context: string[];
|
||||
channelName: string;
|
||||
}
|
||||
|
||||
export type MarkdownEmojiRenderer = MarkdownBaseRenderer & {
|
||||
emojiName: string;
|
||||
}
|
||||
|
||||
export type MarkdownImageRenderer = {
|
||||
linkDestination?: string;
|
||||
context: string[];
|
||||
src: string;
|
||||
size?: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
}
|
||||
|
||||
7
types/utils/markdown.d.ts
vendored
Normal file
7
types/utils/markdown.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
type SourceSize = {
|
||||
height?: number;
|
||||
width?: number;
|
||||
}
|
||||
Reference in New Issue
Block a user