UNPKG

stream-chat-react-native-core

Version:

The official React Native and Expo components for Stream Chat, a service for building chat applications

632 lines 21.4 kB
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.renderText = exports.MarkdownReactiveScrollView = exports.ListOutput = void 0; var _toArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toArray")); var _react = _interopRequireWildcard(require("react")); var _reactNative = require("react-native"); var _reactNativeGestureHandler = require("react-native-gesture-handler"); var _reactNativeMarkdownPackage = _interopRequireDefault(require("react-native-markdown-package")); var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated")); var _simpleMarkdown = require("simple-markdown"); var _generateMarkdownText = require("./generateMarkdownText"); var _theme = require("../../../../theme"); var _utils = require("../../../../utils/utils"); var _jsxRuntime = require("react/jsx-runtime"); var _jsxFileName = "/home/runner/work/stream-chat-react-native/stream-chat-react-native/package/src/components/Message/MessageItemView/utils/renderText.tsx"; function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } var MarkdownReactiveScrollView = ({ children }) => { var scrollViewRef = (0, _reactNativeReanimated.useAnimatedRef)(); var contentWidth = (0, _reactNativeReanimated.useSharedValue)(0); var visibleContentWidth = (0, _reactNativeReanimated.useSharedValue)(0); var offsetBeforeScroll = (0, _reactNativeReanimated.useSharedValue)(0); var panGesture = _reactNativeGestureHandler.Gesture.Pan().activeOffsetX([-5, 5]).onUpdate(event => { var translationX = event.translationX; (0, _reactNativeReanimated.scrollTo)(scrollViewRef, offsetBeforeScroll.value - translationX, 0, false); }).onEnd(event => { var translationX = event.translationX; var velocityEffect = event.velocityX * 0.3; var finalPosition = (0, _reactNativeReanimated.clamp)(offsetBeforeScroll.value - translationX - velocityEffect, 0, contentWidth.value - visibleContentWidth.value); offsetBeforeScroll.value = finalPosition; (0, _reactNativeReanimated.scrollTo)(scrollViewRef, finalPosition, 0, true); }); return (0, _jsxRuntime.jsx)(_reactNative.View, { style: { width: '100%' }, children: (0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureDetector, { gesture: panGesture, children: (0, _jsxRuntime.jsx)(_reactNativeReanimated.default.ScrollView, { contentContainerStyle: { flexGrow: 1 }, horizontal: true, nestedScrollEnabled: true, onContentSizeChange: width => { contentWidth.value = width; }, onLayout: e => { visibleContentWidth.value = e.nativeEvent.layout.width; }, ref: scrollViewRef, scrollEnabled: false, children: children }) }) }); }; exports.MarkdownReactiveScrollView = MarkdownReactiveScrollView; var defaultMarkdownStyles = { codeBlock: { fontFamily: _reactNative.Platform.OS === 'ios' ? 'Courier' : 'Monospace', fontWeight: '500', marginVertical: _theme.primitives.spacingXs, fontSize: _theme.primitives.typographyFontSizeMd, lineHeight: _theme.primitives.typographyLineHeightNormal }, heading1: { fontSize: _theme.primitives.typographyFontSizeMd * 2, lineHeight: _theme.primitives.typographyFontSizeMd * 2 * 1.25 }, heading2: { fontSize: _theme.primitives.typographyFontSizeMd * 1.5, lineHeight: _theme.primitives.typographyFontSizeMd * 1.5 * 1.25 }, heading3: { fontSize: _theme.primitives.typographyFontSizeMd * 1.25, lineHeight: _theme.primitives.typographyFontSizeMd * 1.25 * 1.25 }, heading4: { fontSize: _theme.primitives.typographyFontSizeMd, lineHeight: _theme.primitives.typographyFontSizeMd * 1.25 }, heading5: { fontSize: _theme.primitives.typographyFontSizeMd * 0.875, lineHeight: _theme.primitives.typographyFontSizeMd * 0.875 * 1.25 }, heading6: { fontSize: _theme.primitives.typographyFontSizeMd * 0.75, lineHeight: _theme.primitives.typographyFontSizeMd * 0.75 * 1.25 }, inlineCode: { padding: _theme.primitives.spacingXxs, paddingHorizontal: _theme.primitives.spacingXxs, fontSize: _theme.primitives.typographyFontSizeMd, lineHeight: _theme.primitives.typographyLineHeightNormal }, list: { marginBottom: _theme.primitives.spacingXs, marginTop: _theme.primitives.spacingXs }, listItemNumber: { fontWeight: 'bold' }, listItemText: { flex: 0 }, listRow: { flexDirection: 'row' }, mentions: { fontWeight: _theme.primitives.typographyFontWeightRegular, fontSize: _theme.primitives.typographyFontSizeMd, lineHeight: _theme.primitives.typographyLineHeightNormal }, paragraph: { marginBottom: _theme.primitives.spacingXs, fontSize: _theme.primitives.typographyFontSizeMd, marginTop: _theme.primitives.spacingXs }, paragraphCenter: { marginBottom: _theme.primitives.spacingXs, fontSize: _theme.primitives.typographyFontSizeMd, marginTop: _theme.primitives.spacingXs }, paragraphWithImage: { marginBottom: _theme.primitives.spacingXs, marginTop: _theme.primitives.spacingXs }, table: { borderRadius: _theme.primitives.radiusXxs, borderWidth: 1, flex: 1, flexDirection: 'row' }, tableHeader: { flexDirection: 'row', justifyContent: 'space-around' }, tableHeaderCell: { fontWeight: '500' }, tableRow: { alignItems: 'center', justifyContent: 'space-around' }, tableRowCell: { flex: 1 } }; var mentionsParseFunction = (capture, parse, state) => ({ content: (0, _simpleMarkdown.parseInline)(parse, capture[0], state) }); var renderText = params => { var semantics = params.semantics, markdownRules = params.markdownRules, markdownStyles = params.markdownStyles, message = params.message, messageOverlay = params.messageOverlay, messageTextNumberOfLines = params.messageTextNumberOfLines, onLinkParams = params.onLink, onLongPressParam = params.onLongPress, onlyEmojis = params.onlyEmojis, onPressParam = params.onPress, preventPress = params.preventPress, isMyMessage = params.isMyMessage; var text = message.text; var markdownText = (0, _generateMarkdownText.generateMarkdownText)(text); var styles = { ...defaultMarkdownStyles, ...markdownStyles, paragraph: { ...(onlyEmojis ? {} : { lineHeight: _theme.primitives.typographyLineHeightNormal }), ...defaultMarkdownStyles.paragraph, ...markdownStyles?.paragraph }, paragraphCenter: { ...(onlyEmojis ? {} : { lineHeight: _theme.primitives.typographyLineHeightNormal }), ...defaultMarkdownStyles.paragraphCenter, ...markdownStyles?.paragraphCenter }, autolink: { fontSize: _theme.primitives.typographyFontSizeMd, lineHeight: _theme.primitives.typographyLineHeightNormal, ...defaultMarkdownStyles.autolink, color: semantics.textLink, ...markdownStyles?.autolink }, blockQuoteSection: { ...defaultMarkdownStyles.blockQuoteSection, flexDirection: 'row', padding: _theme.primitives.spacingXs, ...markdownStyles?.blockQuoteSection }, blockQuoteSectionBar: { ...defaultMarkdownStyles.blockQuoteSectionBar, backgroundColor: semantics.borderCoreStrong, marginRight: _theme.primitives.spacingXs, width: 2, ...markdownStyles?.blockQuoteSectionBar }, blockQuoteText: { fontSize: _theme.primitives.typographyFontSizeMd, lineHeight: _theme.primitives.typographyLineHeightNormal, ...defaultMarkdownStyles.blockQuoteText, ...markdownStyles?.blockQuoteText }, codeBlock: { ...defaultMarkdownStyles.codeBlock, backgroundColor: semantics.backgroundCoreSurfaceSubtle, color: semantics.textPrimary, padding: _theme.primitives.spacingXs, ...markdownStyles?.codeBlock }, inlineCode: { ...defaultMarkdownStyles.inlineCode, backgroundColor: semantics.backgroundCoreSurfaceSubtle, borderColor: semantics.borderCoreSubtle, color: semantics.accentError, ...markdownStyles?.inlineCode }, mentions: { ...defaultMarkdownStyles.mentions, color: semantics.chatTextMention, ...markdownStyles?.mentions }, table: { ...defaultMarkdownStyles.table, borderColor: semantics.borderCoreStrong, marginVertical: _theme.primitives.spacingXs, ...markdownStyles?.table }, tableHeader: { ...defaultMarkdownStyles.tableHeader, backgroundColor: semantics.backgroundCoreSurfaceSubtle, ...markdownStyles?.tableHeader }, tableHeaderCell: { fontSize: _theme.primitives.typographyFontSizeMd, lineHeight: _theme.primitives.typographyLineHeightNormal, ...defaultMarkdownStyles.tableHeaderCell, padding: _theme.primitives.spacingXxs, ...markdownStyles?.tableHeaderCell }, tableRow: { ...defaultMarkdownStyles.tableRow, ...markdownStyles?.tableRow }, tableRowCell: { ...defaultMarkdownStyles.tableRowCell, borderColor: semantics.borderCoreStrong, padding: _theme.primitives.spacingXxs, ...markdownStyles?.tableRowCell }, tableRowLast: { ...markdownStyles?.tableRowLast }, text: { fontSize: _theme.primitives.typographyFontSizeMd, ...(onlyEmojis ? {} : { lineHeight: _theme.primitives.typographyLineHeightNormal }), ...defaultMarkdownStyles.text, color: isMyMessage ? semantics.chatTextOutgoing : semantics.chatTextIncoming, ...markdownStyles?.text, writingDirection: _reactNative.I18nManager.isRTL ? 'rtl' : 'ltr' } }; var onLink = url => onLinkParams ? onLinkParams(url) : _reactNative.Linking.canOpenURL(url).then(canOpenUrl => canOpenUrl && _reactNative.Linking.openURL(url)); var previousLink; var linkReact = (node, output, { ...state }) => { var url; if (state?.withinLink && previousLink) { url = previousLink; } else { url = node.target; previousLink = node.target; } var onPress = event => { if (!preventPress && onPressParam) { onPressParam({ additionalInfo: { url }, defaultHandler: () => { onLink(url); }, emitter: 'textLink', event }); } }; var onLongPress = event => { if (!preventPress && onLongPressParam) { onLongPressParam({ additionalInfo: { url }, emitter: 'textLink', event }); } }; return (0, _jsxRuntime.jsx)(_reactNative.Text, { onLongPress: onLongPress, onPress: onPress, style: styles.autolink, suppressHighlighting: true, children: output(node.content, { ...state, withinLink: true }) }, state.key); }; var paragraphTextReact = (node, output, { ...state }) => { if (messageTextNumberOfLines !== undefined) { if (state.key === '0' || state.key === 0) { return (0, _jsxRuntime.jsx)(_reactNative.Text, { numberOfLines: messageTextNumberOfLines, style: styles.paragraph, children: output(node.content, state) }, state.key); } else { return null; } } return (0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.paragraph, children: output(node.content, state) }, state.key); }; var mentioned_channel = message.mentioned_channel, mentioned_group_ids = message.mentioned_group_ids, mentioned_groups = message.mentioned_groups, mentioned_here = message.mentioned_here, mentioned_roles = message.mentioned_roles, mentioned_users = message.mentioned_users; var mentionEntities = [...(mentioned_users ?? []).map(user => ({ ...user, mentionType: 'user' })), ...(mentioned_channel ? [{ id: 'channel', mentionType: 'channel', name: 'channel' }] : []), ...(mentioned_here ? [{ id: 'here', mentionType: 'here', name: 'here' }] : []), ...(mentioned_roles ?? []).map(role => ({ id: role, mentionType: 'role', name: role })), ...(mentioned_groups ?? (mentioned_group_ids ?? []).map(id => ({ id, name: id }))).map(group => ({ id: group.id, mentionType: 'user_group', name: group.name ?? group.id }))]; var mentionLookup = new Map(); for (var entity of mentionEntities) { var key = (entity.name ?? entity.id).toLowerCase(); if (!mentionLookup.has(key)) mentionLookup.set(key, entity); } var mentionTokens = mentionEntities.map(entity => entity.name ?? entity.id).filter(value => Boolean(value)).sort((a, b) => b.length - a.length).map(value => `@${(0, _utils.escapeRegExp)(value)}`).join('|'); var regEx = new RegExp(`^\\B(${mentionTokens})`, 'g'); var mentionsMatchFunction = source => regEx.exec(source); var colorForMentionType = mentionType => { switch (mentionType) { case 'user': return semantics.chatTextMentionUser; case 'channel': case 'here': return semantics.chatTextMentionBroadcast; case 'role': return semantics.chatTextMentionRole; case 'user_group': return semantics.chatTextMentionGroup; default: return semantics.chatTextMention; } }; var mentionsReact = (node, output, { ...state }) => { var matchedText = node.content[0]?.content; var matchedName = matchedText?.substring(1) ?? ''; var matchedEntity = mentionLookup.get(matchedName.toLowerCase()); var mentionedUser = matchedEntity?.mentionType === 'user' ? matchedEntity : undefined; var mentionColor = matchedEntity ? colorForMentionType(matchedEntity.mentionType) : semantics.chatTextMention; var onPress = event => { if (!preventPress && onPressParam) { onPressParam({ additionalInfo: { mentionedEntity: matchedEntity, user: mentionedUser }, emitter: 'textMention', event }); } }; var onLongPress = event => { if (!preventPress && onLongPressParam) { onLongPressParam({ additionalInfo: { mentionedEntity: matchedEntity, user: mentionedUser }, emitter: 'textMention', event }); } }; return (0, _jsxRuntime.jsx)(_reactNative.Text, { onLongPress: onLongPress, onPress: onPress, style: [styles.mentions, { color: mentionColor }], children: Array.isArray(node.content) ? node.content.reduce((acc, current) => acc + current.content, '') || '' : output(node.content, state) }, state.key); }; var listReact = (node, output, state) => (0, _jsxRuntime.jsx)(ListOutput, { node: node, output: output, state: state, styles: styles }, `list-${state.key}`); var codeBlockReact = (node, _, state) => (0, _jsxRuntime.jsx)(MarkdownReactiveScrollView, { children: (0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.codeBlock, children: node?.content?.trim() }) }, state.key); var tableReact = (node, output, state) => (0, _jsxRuntime.jsx)(MarkdownReactiveScrollView, { children: (0, _jsxRuntime.jsx)(MarkdownTable, { node: node, output: output, state: state, styles: styles }) }, state.key); var blockQuoteReact = (node, output, state) => (0, _jsxRuntime.jsxs)(_reactNative.View, { style: styles.blockQuoteSection, children: [(0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.blockQuoteSectionBar }), (0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.blockQuoteText, children: output(node.content, state) })] }, state.key); var customRules = { blockQuote: { react: blockQuoteReact }, codeBlock: { react: codeBlockReact }, image: { match: () => null }, link: { react: linkReact }, list: { react: listReact }, paragraph: messageTextNumberOfLines ? { react: paragraphTextReact } : {}, reflink: { match: () => null }, sublist: { react: listReact }, ...(mentionTokens ? { mentions: { match: mentionsMatchFunction, order: _simpleMarkdown.defaultRules.text.order - 0.5, parse: mentionsParseFunction, react: mentionsReact } } : {}), table: { react: tableReact } }; return (0, _jsxRuntime.jsx)(_reactNativeMarkdownPackage.default, { onLink: onLink, rules: { ...customRules, ...markdownRules }, styles: styles, children: markdownText }, `${JSON.stringify(mentionEntities)}-${onlyEmojis}-${messageOverlay ? JSON.stringify(markdownStyles) : undefined}-${JSON.stringify(semantics)}`); }; exports.renderText = renderText; var ListOutput = ({ node, output, state, styles }) => { var isSublist = state.withinList; var parentTypes = ['text', 'paragraph', 'strong']; return (0, _jsxRuntime.jsx)(_reactNative.View, { style: isSublist ? styles?.sublist : styles?.list, children: node.items.map((item, index) => { var indexAfterStart = node.start + index; if (item === null) { return (0, _jsxRuntime.jsx)(ListRow, { style: styles?.listRow, testID: "list-item", children: (0, _jsxRuntime.jsx)(Bullet, { index: node.ordered && indexAfterStart, style: node.ordered ? styles?.listItemNumber : styles?.listItemBullet }) }, index); } isSublist = item.length > 1 && item[1].type === 'list'; var isSublistWithinText = parentTypes.includes((item[0] ?? {}).type) && isSublist; var style = isSublistWithinText ? { marginBottom: 0 } : {}; return (0, _jsxRuntime.jsxs)(ListRow, { style: styles?.listRow, testID: "list-item", children: [(0, _jsxRuntime.jsx)(Bullet, { index: node.ordered && indexAfterStart, style: node.ordered ? styles?.listItemNumber : styles?.listItemBullet }), (0, _jsxRuntime.jsx)(ListItem, { style: [styles?.listItemText, style], children: output(item, state) }, 1)] }, index); }) }, state.key); }; exports.ListOutput = ListOutput; var Bullet = ({ index, style }) => (0, _jsxRuntime.jsx)(_reactNative.Text, { style: style, children: index ? `${index}. ` : '\u2022 ' }, 0); var ListRow = ({ children, style }) => (0, _jsxRuntime.jsx)(_reactNative.View, { style: style, children: children }); var ListItem = ({ children, style }) => (0, _jsxRuntime.jsx)(_reactNative.Text, { style: style, children: children }); var transpose = matrix => matrix[0].map((_, colIndex) => matrix.map(row => row[colIndex])); var MarkdownTable = ({ node, output, state, styles }) => { var content = (0, _react.useMemo)(() => { var nodeContent = [node?.header, ...(node?.cells ?? null)]; return transpose(nodeContent); }, [node?.cells, node?.header]); var columns = content?.map((column, idx) => (0, _jsxRuntime.jsx)(MarkdownTableColumn, { items: column, output: output, state: state, styles: styles }, `column-${idx}`)); return (0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.table, children: columns }, state.key); }; var MarkdownTableColumn = ({ items, output, state, styles }) => { var _items = (0, _toArray2.default)(items), headerCellContent = _items[0], columnCellContents = _arrayLikeToArray(_items).slice(1); var ColumnCell = (0, _react.useCallback)(({ content }) => content ? (0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.tableRow, children: (0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.tableRowCell, children: output(content, state) }) }) : null, [output, state, styles]); return (0, _jsxRuntime.jsxs)(_reactNative.View, { style: { flex: 1, flexDirection: 'column' }, children: [headerCellContent ? (0, _jsxRuntime.jsx)(_reactNative.View, { style: styles.tableHeader, children: (0, _jsxRuntime.jsx)(_reactNative.Text, { style: styles.tableHeaderCell, children: output(headerCellContent, state) }) }, -1) : null, columnCellContents && columnCellContents.map((content, idx) => (0, _jsxRuntime.jsx)(ColumnCell, { content: content }, `cell-${idx}`))] }); }; //# sourceMappingURL=renderText.js.map