-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Markdown in task titles #54165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Markdown in task titles #54165
Changes from all commits
1e900c2
83c1f02
6b2dbe7
8d850d6
f15c822
325eff0
d474b5e
4f2ad8d
1ef5238
94d7ee9
f4bc960
9169bb4
6e9fa91
e684a61
4daa846
4d6d79d
cc33f33
8c1d40e
6f0cd06
7d0a349
1714937
96e5668
65b649a
556f195
d41a830
38c69a7
75554a3
4b01221
c2500de
7d8e3c4
7c53869
97a12d0
517a9c0
42161e4
c64126c
3876179
a372369
96ae24d
5f234ac
2d63ceb
0e758f8
4fbb1b3
4a32291
1567e14
26e12ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,8 +8,8 @@ import * as HTMLEngineUtils from '@components/HTMLEngineProvider/htmlEngineUtils | |
| import Text from '@components/Text'; | ||
| import useEnvironment from '@hooks/useEnvironment'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import {getInternalExpensifyPath, getInternalNewExpensifyPath, openLink} from '@libs/actions/Link'; | ||
| import tryResolveUrlFromApiRoot from '@libs/tryResolveUrlFromApiRoot'; | ||
| import * as Link from '@userActions/Link'; | ||
| import CONST from '@src/CONST'; | ||
|
|
||
| type AnchorRendererProps = CustomRendererProps<TBlock> & { | ||
|
|
@@ -27,22 +27,24 @@ function AnchorRenderer({tnode, style, key}: AnchorRendererProps) { | |
| const displayName = tNodeChild && 'data' in tNodeChild && typeof tNodeChild.data === 'string' ? tNodeChild.data : ''; | ||
| const attrHref = htmlAttribs.href || htmlAttribs[CONST.ATTACHMENT_SOURCE_ATTRIBUTE] || ''; | ||
| const parentStyle = tnode.parent?.styles?.nativeTextRet ?? {}; | ||
| const internalNewExpensifyPath = Link.getInternalNewExpensifyPath(attrHref); | ||
| const internalExpensifyPath = Link.getInternalExpensifyPath(attrHref); | ||
| const internalNewExpensifyPath = getInternalNewExpensifyPath(attrHref); | ||
| const internalExpensifyPath = getInternalExpensifyPath(attrHref); | ||
| const isVideo = attrHref && Str.isVideo(attrHref); | ||
| const linkHasImage = tnode.tagName === 'a' && tnode.children.some((child) => child.tagName === 'img'); | ||
|
|
||
| const isDeleted = HTMLEngineUtils.isDeletedNode(tnode); | ||
| const isChildOfTaskTitle = HTMLEngineUtils.isChildOfTaskTitle(tnode); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: Why is this check needed?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need that because we want to apply
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmmm did you know that in React Native, the only styles that actually cascade like css are text styles? So you might be able to just alter the text style higher up in the component tree and have it cascade down.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In our case, we need to override the text styles that have been directly applied to the text. |
||
|
|
||
| const textDecorationLineStyle = isDeleted ? styles.underlineLineThrough : {}; | ||
|
|
||
| if (!HTMLEngineUtils.isChildOfComment(tnode)) { | ||
| if (!HTMLEngineUtils.isChildOfComment(tnode) && !isChildOfTaskTitle) { | ||
| // This is not a comment from a chat, the AnchorForCommentsOnly uses a Pressable to create a context menu on right click. | ||
| // We don't have this behaviour in other links in NewDot | ||
| // TODO: We should use TextLink, but I'm leaving it as Text for now because TextLink breaks the alignment in Android. | ||
| return ( | ||
| <Text | ||
| style={styles.link} | ||
| onPress={() => Link.openLink(attrHref, environmentURL, isAttachment)} | ||
| onPress={() => openLink(attrHref, environmentURL, isAttachment)} | ||
| suppressHighlighting | ||
| > | ||
| <TNodeChildrenRenderer tnode={tnode} /> | ||
|
|
@@ -70,10 +72,10 @@ function AnchorRenderer({tnode, style, key}: AnchorRendererProps) { | |
| // eslint-disable-next-line react/jsx-props-no-multi-spaces | ||
| target={htmlAttribs.target || '_blank'} | ||
| rel={htmlAttribs.rel || 'noopener noreferrer'} | ||
| style={[style, parentStyle, textDecorationLineStyle, styles.textUnderlinePositionUnder, styles.textDecorationSkipInkNone]} | ||
| style={[style, parentStyle, textDecorationLineStyle, styles.textUnderlinePositionUnder, styles.textDecorationSkipInkNone, isChildOfTaskTitle && styles.taskTitleMenuItem]} | ||
| key={key} | ||
| // Only pass the press handler for internal links. For public links or whitelisted internal links fallback to default link handling | ||
| onPress={internalNewExpensifyPath || internalExpensifyPath ? () => Link.openLink(attrHref, environmentURL, isAttachment) : undefined} | ||
| onPress={internalNewExpensifyPath || internalExpensifyPath ? () => openLink(attrHref, environmentURL, isAttachment) : undefined} | ||
| linkHasImage={linkHasImage} | ||
| > | ||
| <TNodeChildrenRenderer | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import React from 'react'; | ||
| import type {CustomRendererProps, TPhrasing, TText} from 'react-native-render-html'; | ||
| import {TNodeChildrenRenderer} from 'react-native-render-html'; | ||
| import * as HTMLEngineUtils from '@components/HTMLEngineProvider/htmlEngineUtils'; | ||
| import Text from '@components/Text'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
|
|
||
| function EMRenderer({tnode}: CustomRendererProps<TText | TPhrasing>) { | ||
| const styles = useThemeStyles(); | ||
| const isChildOfTaskTitle = HTMLEngineUtils.isChildOfTaskTitle(tnode); | ||
|
|
||
| return 'data' in tnode ? ( | ||
| <Text style={[styles.webViewStyles.baseFontStyle, styles.webViewStyles.tagStyles.em, isChildOfTaskTitle && styles.taskTitleMenuItemItalic]}>{tnode.data}</Text> | ||
| ) : ( | ||
| <TNodeChildrenRenderer | ||
| tnode={tnode} | ||
| renderChild={(props) => { | ||
| return ( | ||
| <Text | ||
| style={[styles.webViewStyles.baseFontStyle, styles.strong, isChildOfTaskTitle && styles.taskTitleMenuItem]} | ||
| key={props.key} | ||
| > | ||
| {props.childElement} | ||
| </Text> | ||
| ); | ||
| }} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| EMRenderer.displayName = 'EMRenderer'; | ||
|
|
||
| export default EMRenderer; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import React from 'react'; | ||
| import type {CustomRendererProps, TPhrasing, TText} from 'react-native-render-html'; | ||
| import {TNodeChildrenRenderer} from 'react-native-render-html'; | ||
| import * as HTMLEngineUtils from '@components/HTMLEngineProvider/htmlEngineUtils'; | ||
| import Text from '@components/Text'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
|
|
||
| function HeadingRenderer({tnode}: CustomRendererProps<TText | TPhrasing>) { | ||
| const styles = useThemeStyles(); | ||
| const isChildOfTaskTitle = HTMLEngineUtils.isChildOfTaskTitle(tnode); | ||
|
|
||
| return ( | ||
| <TNodeChildrenRenderer | ||
| tnode={tnode} | ||
| renderChild={(props) => { | ||
| return ( | ||
| <Text | ||
| style={[styles.webViewStyles.baseFontStyle, styles.h1, isChildOfTaskTitle && styles.taskTitleMenuItem]} | ||
| key={props.key} | ||
| > | ||
| {props.childElement} | ||
| </Text> | ||
| ); | ||
| }} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| HeadingRenderer.displayName = 'HeadingRenderer'; | ||
|
|
||
| export default HeadingRenderer; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import React from 'react'; | ||
| import type {CustomRendererProps, TPhrasing, TText} from 'react-native-render-html'; | ||
| import {TNodeChildrenRenderer} from 'react-native-render-html'; | ||
| import * as HTMLEngineUtils from '@components/HTMLEngineProvider/htmlEngineUtils'; | ||
| import Text from '@components/Text'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
|
|
||
| function StrongRenderer({tnode}: CustomRendererProps<TText | TPhrasing>) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| const styles = useThemeStyles(); | ||
| const isChildOfTaskTitle = HTMLEngineUtils.isChildOfTaskTitle(tnode); | ||
|
|
||
| return 'data' in tnode ? ( | ||
| <Text style={[styles.webViewStyles.baseFontStyle, styles.strong, isChildOfTaskTitle && styles.taskTitleMenuItem]}>{tnode.data}</Text> | ||
| ) : ( | ||
| <TNodeChildrenRenderer | ||
| tnode={tnode} | ||
| renderChild={(props) => { | ||
| return ( | ||
| <Text | ||
| style={[styles.webViewStyles.baseFontStyle, styles.strong, isChildOfTaskTitle && styles.taskTitleMenuItem]} | ||
| key={props.key} | ||
| > | ||
| {props.childElement} | ||
| </Text> | ||
| ); | ||
| }} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| StrongRenderer.displayName = 'StrongRenderer'; | ||
|
|
||
| export default StrongRenderer; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import React from 'react'; | ||
| import type {CustomRendererProps, TPhrasing, TText} from 'react-native-render-html'; | ||
| import {TNodeChildrenRenderer} from 'react-native-render-html'; | ||
| import Text from '@components/Text'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
|
|
||
| function TaskTitleRenderer({tnode}: CustomRendererProps<TText | TPhrasing>) { | ||
| const styles = useThemeStyles(); | ||
|
|
||
| return ( | ||
| <TNodeChildrenRenderer | ||
| tnode={tnode} | ||
| renderChild={(props) => { | ||
| return ( | ||
| <Text | ||
| style={[styles.taskTitleMenuItem]} | ||
| key={props.key} | ||
| > | ||
| {props.childElement} | ||
| </Text> | ||
| ); | ||
| }} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| TaskTitleRenderer.displayName = 'TaskTitleRenderer'; | ||
|
|
||
| export default TaskTitleRenderer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we didn't check the styles appropriately, please check #58341