diff --git a/app/components/markdown/markdown.tsx b/app/components/markdown/markdown.tsx index f16237245..36bcd7f04 100644 --- a/app/components/markdown/markdown.tsx +++ b/app/components/markdown/markdown.tsx @@ -71,6 +71,7 @@ type MarkdownProps = { textStyles?: MarkdownTextStyles; theme: Theme; value?: string; + onLinkLongPress?: (url?: string) => void; } const getStyleSheet = makeStyleSheetFromTheme((theme) => { @@ -133,7 +134,7 @@ const Markdown = ({ enableInlineLatex, enableLatex, maxNodes, imagesMetadata, isEdited, isReplyPost, isSearchResult, layoutHeight, layoutWidth, location, mentionKeys, minimumHashtagLength = 3, onPostPress, postId, searchPatterns, - textStyles = {}, theme, value = '', baseParagraphStyle, + textStyles = {}, theme, value = '', baseParagraphStyle, onLinkLongPress, }: MarkdownProps) => { const style = getStyleSheet(theme); const managedConfig = useManagedConfig(); @@ -402,7 +403,10 @@ const Markdown = ({ const renderLink = ({children, href}: {children: ReactElement; href: string}) => { return ( - + {children} ); diff --git a/app/components/markdown/markdown_link/markdown_link.tsx b/app/components/markdown/markdown_link/markdown_link.tsx index 64dee57b8..4cc662260 100644 --- a/app/components/markdown/markdown_link/markdown_link.tsx +++ b/app/components/markdown/markdown_link/markdown_link.tsx @@ -23,6 +23,7 @@ type MarkdownLinkProps = { experimentalNormalizeMarkdownLinks: string; href: string; siteURL: string; + onLinkLongPress?: (url?: string) => void; } const style = StyleSheet.create({ @@ -44,7 +45,7 @@ const parseLinkLiteral = (literal: string) => { return parsed.href; }; -const MarkdownLink = ({children, experimentalNormalizeMarkdownLinks, href, siteURL}: MarkdownLinkProps) => { +const MarkdownLink = ({children, experimentalNormalizeMarkdownLinks, href, siteURL, onLinkLongPress}: MarkdownLinkProps) => { const intl = useIntl(); const {bottom} = useSafeAreaInsets(); const managedConfig = useManagedConfig(); @@ -109,6 +110,11 @@ const MarkdownLink = ({children, experimentalNormalizeMarkdownLinks, href, siteU const handleLongPress = useCallback(() => { if (managedConfig?.copyAndPasteProtection !== 'true') { + if (onLinkLongPress) { + onLinkLongPress(href); + return; + } + const renderContent = () => { return ( ({ }, })); +const style = StyleSheet.create({ + bottomSheet: { + flex: 1, + }, +}); + +const headerTestId = 'channel_info.extra.header'; + +const onCopy = async (text: string, isLink?: boolean) => { + Clipboard.setString(text); + await dismissBottomSheet(); + if ((Platform.OS === OS_VERSION.ANDROID && Number(Platform.Version) < ANDROID_33) || Platform.OS === OS_VERSION.IOS) { + showSnackBar({ + barType: isLink ? SNACK_BAR_TYPE.LINK_COPIED : SNACK_BAR_TYPE.TEXT_COPIED, + }); + } +}; + const Extra = ({channelId, createdAt, createdBy, customStatus, header, isCustomStatusEnabled}: Props) => { + const intl = useIntl(); + const {bottom} = useSafeAreaInsets(); const theme = useTheme(); + const managedConfig = useManagedConfig(); + const styles = getStyleSheet(theme); const blockStyles = getMarkdownBlockStyles(theme); const textStyles = getMarkdownTextStyles(theme); @@ -81,6 +114,70 @@ const Extra = ({channelId, createdAt, createdBy, customStatus, header, isCustomS ), }), [createdAt, createdBy, theme]); + const handleLongPress = useCallback((url?: string) => { + if (managedConfig?.copyAndPasteProtection !== 'true') { + const renderContent = () => ( + + { + onCopy(header!); + }} + testID={`${headerTestId}.bottom_sheet.copy_header_text`} + text={intl.formatMessage({ + id: 'mobile.markdown.copy_header', + defaultMessage: 'Copy header text', + })} + /> + {Boolean(url) && ( + { + onCopy(url!, true); + }} + testID={`${headerTestId}.bottom_sheet.copy_url`} + text={intl.formatMessage({ + id: 'mobile.markdown.link.copy_url', + defaultMessage: 'Copy URL', + })} + /> + )} + { + dismissBottomSheet(); + }} + testID={`${headerTestId}.bottom_sheet.cancel`} + text={intl.formatMessage({ + id: 'mobile.post.cancel', + defaultMessage: 'Cancel', + })} + /> + + ); + + bottomSheet({ + closeButtonId: 'close-markdown-link', + renderContent, + snapPoints: [1, bottomSheetSnapPoint(url ? 3 : 2, ITEM_HEIGHT, bottom)], + title: intl.formatMessage({id: 'post.options.title', defaultMessage: 'Options'}), + theme, + }); + } + }, [ + header, + bottom, + theme, + intl.formatMessage, + managedConfig?.copyAndPasteProtection, + ]); + + const touchableHandleLongPress = useCallback(() => handleLongPress(), [handleLongPress]); + return ( {isCustomStatusEnabled && Boolean(customStatus) && @@ -131,25 +228,32 @@ const Extra = ({channelId, createdAt, createdBy, customStatus, header, isCustomS id='channel_info.header' defaultMessage='Header:' style={styles.extraHeading} - testID='channel_info.extra.header' - /> - + + + } {Boolean(createdAt && createdBy) && diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 0f93b5a28..1e4606926 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -613,6 +613,7 @@ "mobile.managed.settings": "Go to settings", "mobile.markdown.code.copy_code": "Copy Code", "mobile.markdown.code.plusMoreLines": "+{count, number} more {count, plural, one {line} other {lines}}", + "mobile.markdown.copy_header": "Copy header text", "mobile.markdown.image.too_large": "Image exceeds max dimensions of {maxWidth} by {maxHeight}:", "mobile.markdown.link.copy_url": "Copy URL", "mobile.mention.copy_mention": "Copy Mention",