Compare commits

...

5 Commits
v1 ... v1.50.0

Author SHA1 Message Date
Mattermost Build
87d37a44fd Bump version 1.50.0 build 387 (#6045) (#6046)
* Bump app version number to  1.50.0

* Bump app build number to  387

(cherry picked from commit 2349aa0441)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2022-03-09 16:45:09 -03:00
Mattermost Build
4a82c28f25 MM-41988 Updated links to legacy domain about.mm.com (#5962) (#6036) 2022-03-07 17:38:45 -03:00
Mattermost Build
46f683f28f Markdown svg & image to respect size if available (#6030) (#6034)
(cherry picked from commit c9ce61d28b)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2022-03-07 15:36:58 -03:00
Elias Nahum
3b63beb9e0 dependencies audit 2022-03-06 07:30:34 -03:00
Elias Nahum
4d07f49933 Fix friendly date flaky test 2022-03-06 07:30:22 -03:00
24 changed files with 372 additions and 7128 deletions

View File

@@ -1,12 +1,12 @@
Per Mattermost guidelines, GitHub issues are for bug reports: <http://www.mattermost.org/filing-issues/>.
Per Mattermost guidelines, GitHub issues are for bug reports: <https://handbook.mattermost.com/contributors/contributors/ways-to-contribute>.
For troubleshooting see: http://forum.mattermost.org/.
For feature proposals see: http://www.mattermost.org/feature-requests/
For troubleshooting see: https://forum.mattermost.com/.
For feature proposals see: https://www.mattermost.com/feature-ideas/
If you've found a bug--something appears unintentional--please follow these steps:
1. Confirm youre filing a new issue. [Search existing tickets in Jira](https://mattermost.atlassian.net/jira/software/c/projects/MM/issues/) to ensure that the ticket does not already exist.
2. Confirm your issue does not involve security. Otherwise, please see our [Responsible Disclosure Policy](https://about.mattermost.com/report-security-issue/).
2. Confirm your issue does not involve security. Otherwise, please see our [Responsible Disclosure Policy](https://mattermost.com/security-vulnerability-report/).
3. [File a new issue](https://github.com/mattermost/mattermost-mobile/issues/new) using the format below. Mattermost will confirm steps to reproduce and file in Jira, or ask for more details if there is trouble reproducing it. If there's already an existing bug in Jira, it will be linked back to the GitHub issue so you can track when it gets fixed.
#### Summary

View File

@@ -1,4 +1,4 @@
Submit feature requests to http://www.mattermost.org/feature-requests/. File non-security related bugs here in the following format:
Submit feature requests to https://www.mattermost.com/feature-ideas/. File non-security related bugs here in the following format:
#### Summary
Issue in one concise sentence.

View File

@@ -17,7 +17,7 @@ Security updates
Mattermost has a mandatory upgrade policy, and updates are only provided for the latest release. Critical updates are delivered as dot releases. Details on security updates are announced 30 days after the availability of the update.
For more details about the security content of past releases, see the [Security Updates](https://mattermost.com/security-updates/) page on the Mattermost website. For timely notifications about new security updates, subscribe to the [Security Bulletins Mailing List](https://about.mattermost.com/security-bulletin).
For more details about the security content of past releases, see the [Security Updates](https://mattermost.com/security-updates/) page on the Mattermost website. For timely notifications about new security updates, subscribe to the [Security Bulletins Mailing List](https://mattermost.com/security-updates/#sign-up).
Contributing to this policy
---------------------------

View File

@@ -131,8 +131,8 @@ android {
applicationId "com.mattermost.rnbeta"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 386
versionName "1.49.1"
versionCode 387
versionName "1.50.0"
multiDexEnabled = true
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -66,7 +66,7 @@ describe('Friendly Date', () => {
expect(daysAgoMaxText.getByText('30 days ago')).toBeTruthy();
const monthsAgo = new Date();
monthsAgo.setMonth(monthsAgo.getMonth() - 2);
monthsAgo.setDate(monthsAgo.getDate() - 60);
const monthsAgoText = renderWithIntl(
<FriendlyDate value={monthsAgo}/>,
);

View File

@@ -146,6 +146,7 @@ export default class Markdown extends PureComponent {
if (node.type === 'image') {
extraProps.reactChildren = node.react.children;
extraProps.linkDestination = node.linkDestination;
extraProps.size = node.size;
}
return extraProps;
@@ -183,7 +184,7 @@ export default class Markdown extends PureComponent {
return <Text style={this.computeTextStyle([this.props.baseTextStyle, this.props.textStyles.code], context)}>{literal}</Text>;
};
renderImage = ({linkDestination, reactChildren, context, src}) => {
renderImage = ({linkDestination, reactChildren, context, src, size}) => {
if (!this.props.imagesMetadata) {
return null;
}
@@ -211,6 +212,7 @@ export default class Markdown extends PureComponent {
isReplyPost={this.props.isReplyPost}
postId={this.props.postId}
source={src}
sourceSize={size}
>
{reactChildren}
</MarkdownImage>

View File

@@ -22,15 +22,24 @@ import ImageViewPort from '@components/image_viewport';
import ProgressiveImage from '@components/progressive_image';
import TouchableWithFeedback from '@components/touchable_with_feedback';
import mattermostManaged from '@mattermost-managed';
import {changeOpacity, makeStyleFromTheme} from '@mm-redux/utils/theme_utils';
import EphemeralStore from '@store/ephemeral_store';
import BottomSheet from '@utils/bottom_sheet';
import {generateId} from '@utils/file';
import {openGalleryAtIndex} from '@utils/gallery';
import {calculateDimensions, getViewPortWidth, isGifTooLarge} from '@utils/images';
import {getMarkdownImageSize} from '@utils/markdown';
import {normalizeProtocol, tryOpenURL} from '@utils/url';
const ANDROID_MAX_HEIGHT = 4096;
const ANDROID_MAX_WIDTH = 4096;
const getStyleSheet = makeStyleFromTheme((theme) => ({
svg: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.06),
borderRadius: 8,
flex: 1,
},
}));
export default class MarkdownImage extends ImageViewPort {
static propTypes = {
@@ -42,6 +51,7 @@ export default class MarkdownImage extends ImageViewPort {
linkDestination: PropTypes.string,
postId: PropTypes.string,
source: PropTypes.string.isRequired,
sourceSize: PropTypes.object,
theme: PropTypes.object,
};
@@ -53,10 +63,12 @@ export default class MarkdownImage extends ImageViewPort {
super(props);
const metadata = props.imagesMetadata?.[props.source] || Object.values(props.imagesMetadata || {})?.[0];
const size = getMarkdownImageSize(props.isReplyPost, this.hasPermanentSidebar(), props.sourceSize, metadata);
this.fileId = generateId();
this.state = {
originalHeight: metadata?.height || 0,
originalWidth: metadata?.width || 0,
originalHeight: size.height,
originalWidth: size.width,
failed: isGifTooLarge(metadata),
format: metadata?.format,
uri: null,
@@ -207,6 +219,18 @@ export default class MarkdownImage extends ImageViewPort {
{this.props.children}
</Text>
);
} else if (fileInfo?.format === 'svg') {
const style = getStyleSheet(this.props.theme);
image = (
<SvgUri
uri={fileInfo.uri}
style={style.svg}
width={width}
height={height}
onError={this.handleSizeFailed}
/>
);
} else {
// React Native complains if we try to pass resizeMode as a style
const source = fileInfo.uri ? {uri: fileInfo.uri} : null;
@@ -225,14 +249,6 @@ export default class MarkdownImage extends ImageViewPort {
</TouchableWithFeedback>
);
}
} else if (fileInfo?.format === 'svg') {
image = (
<SvgUri
uri={fileInfo.uri}
style={{flex: 1}}
onError={this.handleSizeFailed}
/>
);
}
if (image && this.props.linkDestination) {

View File

@@ -112,6 +112,8 @@ const MarkTableImage = ({disable, imagesMetadata, postId, serverURL, source, the
<SvgUri
uri={source}
style={styles.container}
width={width}
height={height}
//@ts-expect-error onError not defined in the types
onError={onLoadFailed}

View File

@@ -2,6 +2,6 @@
// See LICENSE.txt for license information.
export default {
TERMS_OF_SERVICE: 'https://about.mattermost.com/default-terms/',
PRIVACY_POLICY: 'https://about.mattermost.com/default-privacy-policy/',
TERMS_OF_SERVICE: 'https://mattermost.com/terms-of-use/',
PRIVACY_POLICY: 'https://mattermost.com/privacy-policy/',
};

View File

@@ -3,6 +3,7 @@
import {Platform, StyleSheet} from 'react-native';
import {getViewPortWidth} from '@utils/images';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
export function getCodeFont() {
@@ -206,3 +207,41 @@ export function switchKeyboardForCodeBlocks(value, cursorPosition) {
return 'default';
}
export const getMarkdownImageSize = (isReplyPost, isTablet, sourceSize, knownSize) => {
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};
};

View File

@@ -2,12 +2,12 @@
"DefaultServerUrl": "",
"TestServerUrl": "http://localhost:8065",
"ShowErrorsList": false,
"EELearnURL": "about.mattermost.com",
"EELearnURL": "mattermost.com",
"TeamEditionLearnURL": "mattermost.org",
"AboutTeamURL": "http://www.mattermost.org/",
"AboutTeamURL": "https://mattermost.com/",
"AboutEnterpriseURL": "https://mattermost.com/",
"PlatformNoticeURL": "https://mattermost.com/platform-notice-txt/",
"MobileNoticeURL": "https://about.mattermost.com/mobile-notice-txt/",
"PlatformNoticeURL": "https://github.com/mattermost/mattermost-server/blob/master/NOTICE.txt",
"MobileNoticeURL": "https://github.com/mattermost/mattermost-mobile/blob/master/NOTICE.txt",
"RudderApiKey": "",
"AutoSelectServerUrl": false,

View File

@@ -189,11 +189,11 @@
"ShowFullName": true
},
"SupportSettings": {
"TermsOfServiceLink": "https://about.mattermost.com/default-terms/",
"PrivacyPolicyLink": "https://about.mattermost.com/default-privacy-policy/",
"AboutLink": "https://about.mattermost.com/default-about/",
"HelpLink": "https://about.mattermost.com/default-help/",
"ReportAProblemLink": "https://about.mattermost.com/default-report-a-problem/",
"TermsOfServiceLink": "https://mattermost.com/terms-of-use/",
"PrivacyPolicyLink": "https://mattermost.com/privacy-policy/",
"AboutLink": "https://mattermost.com/default-about/",
"HelpLink": "https://mattermost.com/default-help/",
"ReportAProblemLink": "https://mattermost.com/default-report-a-problem/",
"SupportEmail": "feedback@mattermost.com",
"CustomTermsOfServiceEnabled": false,
"CustomTermsOfServiceReAcceptancePeriod": 365,
@@ -233,8 +233,8 @@
},
"NativeAppSettings": {
"AppDownloadLink": "https://mattermost.com/download/#mattermostApps",
"AndroidAppDownloadLink": "https://about.mattermost.com/mattermost-android-app/",
"IosAppDownloadLink": "https://about.mattermost.com/mattermost-ios-app/"
"AndroidAppDownloadLink": "https://mattermost.com/mattermost-android-app/",
"IosAppDownloadLink": "https://mattermost.com/mattermost-ios-app/"
},
"ClusterSettings": {
"Enable": false

View File

@@ -9,7 +9,7 @@ function getFullDialog(triggerId, webhookBaseUrl) {
callback_id: 'somecallbackid',
title: 'Title for Full Dialog Test',
icon_url:
'http://www.mattermost.org/wp-content/uploads/2016/04/icon.png',
'https://mattermost.com/wp-content/uploads/2022/02/icon.png',
elements: [
{
display_name: 'Display Name',
@@ -181,7 +181,7 @@ function getSimpleDialog(triggerId, webhookBaseUrl) {
callback_id: 'somecallbackid',
title: 'Title for Dialog Test without elements',
icon_url:
'http://www.mattermost.org/wp-content/uploads/2016/04/icon.png',
'https://mattermost.com/wp-content/uploads/2022/02/icon.png',
submit_label: 'Submit Test',
notify_on_cancel: true,
state: 'somestate',
@@ -197,7 +197,7 @@ function getUserAndChannelDialog(triggerId, webhookBaseUrl) {
callback_id: 'somecallbackid',
title: 'Title for Dialog Test with user and channel element',
icon_url:
'http://www.mattermost.org/wp-content/uploads/2016/04/icon.png',
'https://mattermost.com/wp-content/uploads/2022/02/icon.png',
submit_label: 'Submit Test',
notify_on_cancel: true,
state: 'somestate',
@@ -243,7 +243,7 @@ function getBooleanDialog(triggerId, webhookBaseUrl) {
callback_id: 'somecallbackid',
title: 'Title for Dialog Test with boolean element',
icon_url:
'http://www.mattermost.org/wp-content/uploads/2016/04/icon.png',
'https://mattermost.com/wp-content/uploads/2022/02/icon.png',
submit_label: 'Submit Test',
notify_on_cancel: true,
state: 'somestate',

View File

@@ -8,17 +8,17 @@ GEM
artifactory (3.0.15)
atomos (0.1.3)
aws-eventstream (1.2.0)
aws-partitions (1.554.0)
aws-sdk-core (3.126.1)
aws-partitions (1.564.0)
aws-sdk-core (3.129.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.54.0)
aws-sdk-core (~> 3, >= 3.126.0)
aws-sdk-kms (1.55.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.112.0)
aws-sdk-core (~> 3, >= 3.126.0)
aws-sdk-s3 (1.113.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.4.0)
@@ -37,7 +37,7 @@ GEM
dotenv (2.7.6)
emoji_regex (3.2.3)
excon (0.91.0)
faraday (1.9.3)
faraday (1.10.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
@@ -142,8 +142,8 @@ GEM
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.1.1)
faraday (>= 0.17.3, < 2.0)
googleauth (1.1.2)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
@@ -153,19 +153,19 @@ GEM
http-cookie (1.0.4)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.6.0)
jmespath (1.6.1)
json (2.6.1)
jwt (2.3.0)
memoist (0.16.2)
mini_magick (4.11.0)
mini_mime (1.1.2)
mini_portile2 (2.7.1)
mini_portile2 (2.8.0)
multi_json (1.15.0)
multipart-post (2.0.0)
nanaimo (0.3.0)
naturally (2.2.1)
nokogiri (1.13.1)
mini_portile2 (~> 2.7.0)
nokogiri (1.13.3)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
optparse (0.1.1)
os (1.1.4)
@@ -183,9 +183,9 @@ GEM
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.3)
signet (0.16.0)
signet (0.16.1)
addressable (~> 2.8)
faraday (>= 0.17.3, < 2.0)
faraday (>= 0.17.5, < 3.0)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.8)

View File

@@ -1 +1 @@
https://forum.mattermost.org/c/general/trouble-shoot
https://forum.mattermost.com/c/trouble-shoot/16

View File

@@ -909,7 +909,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 386;
CURRENT_PROJECT_VERSION = 387;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
@@ -951,7 +951,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 386;
CURRENT_PROJECT_VERSION = 387;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;

View File

@@ -21,7 +21,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.49.1</string>
<string>1.50.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@@ -37,7 +37,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>386</string>
<string>387</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>

View File

@@ -19,9 +19,9 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.49.1</string>
<string>1.50.0</string>
<key>CFBundleVersion</key>
<string>386</string>
<string>387</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>

View File

@@ -19,9 +19,9 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.49.1</string>
<string>1.50.0</string>
<key>CFBundleVersion</key>
<string>386</string>
<string>387</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>

480
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "mattermost-mobile",
"version": "1.49.1",
"version": "1.50.0",
"description": "Mattermost Mobile with React Native",
"repository": "git@github.com:mattermost/mattermost-mobile.git",
"author": "Mattermost, Inc.",
@@ -25,8 +25,8 @@
"array.prototype.flat": "1.2.5",
"base-64": "1.0.0",
"buffer": "6.0.3",
"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",
@@ -97,7 +97,7 @@
"serialize-error": "9.0.0",
"shallow-equals": "1.0.0",
"tinycolor2": "1.4.2",
"url-parse": "1.5.8"
"url-parse": "1.5.10"
},
"devDependencies": {
"@babel/cli": "7.16.8",

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -1,14 +1,14 @@
diff --git a/node_modules/react-native-svg/src/xml.tsx b/node_modules/react-native-svg/src/xml.tsx
index 828f104..b480cee 100644
index 828f104..462be2e 100644
--- a/node_modules/react-native-svg/src/xml.tsx
+++ b/node_modules/react-native-svg/src/xml.tsx
@@ -133,7 +133,13 @@ export function SvgUri(props: UriProps) {
@@ -133,10 +133,17 @@ export function SvgUri(props: UriProps) {
useEffect(() => {
uri
? fetchText(uri)
- .then(setXml)
+ .then((xml) => {
+ if (xml) {
+ if (xml && /xmlns="http:\/\/www.w3.org\/[0-9]*\/svg"/.test(xml)) {
+ setXml(xml);
+ return;
+ }
@@ -17,3 +17,7 @@ index 828f104..b480cee 100644
.catch(onError)
: setXml(null);
}, [onError, uri]);
+
return <SvgXml xml={xml} override={props} />;
}