Skip to content
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

[No QA] 17885 custom user mentions renderer #18167

Merged
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const customHTMLElementModels = {
tagName: 'strong',
mixedUAStyles: {whiteSpace: 'pre'},
}),
'mention-user': defaultHTMLElementModels.span.extend({tagName: 'mention-user'}),
};

const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText]};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from 'react';
import _ from 'underscore';
import Navigation from '../../../libs/Navigation/Navigation';
import ROUTES from '../../../ROUTES';
import Text from '../../Text';
import Tooltip from '../../Tooltip';
import htmlRendererPropTypes from './htmlRendererPropTypes';
import withCurrentUserPersonalDetails from '../../withCurrentUserPersonalDetails';
import personalDetailsPropType from '../../../pages/personalDetailsPropType';
import * as StyleUtils from '../../../styles/StyleUtils';
import InlineCodeBlock from '../../InlineCodeBlock';
import styles from '../../../styles/styles';

const propTypes = {
...htmlRendererPropTypes,

/* Stores info about currently logged in user */
currentUserPersonalDetails: personalDetailsPropType.isRequired,
};

/**
* Navigates to user details screen based on email
* @param {String} email
* @returns {void}
* */
const showUserDetails = email => Navigation.navigate(ROUTES.getDetailsRoute(email));
puneetlath marked this conversation as resolved.
Show resolved Hide resolved

const MentionUserRenderer = (props) => {
const defaultRendererProps = _.omit(props, ['TDefaultRenderer', 'style']);

// We need to remove the leading @ from data as it is not part of the login
const loginWhithoutLeadingAt = props.tnode.data.slice(1);
const isOurMention = loginWhithoutLeadingAt === props.currentUserPersonalDetails.login;
puneetlath marked this conversation as resolved.
Show resolved Hide resolved

return (
<Tooltip absolute text={loginWhithoutLeadingAt}>
<Text onPress={() => showUserDetails(loginWhithoutLeadingAt)}>

{/* We are using here workaround from CodeRenderer.js in order to apply borderRadius and padding for nested Text */}
<InlineCodeBlock
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we just use the WrapperText component here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I preferred using the InlineCodeBlock as it is already covering the web and native implementations in the separate files. When we would be using WrapperText we should implement MentionUserRenderer separately for native and web, as WrapperText for web is overkill, when normal Text is working.

defaultRendererProps={defaultRendererProps}
TDefaultRenderer={props.TDefaultRenderer}
boxModelStyle={StyleUtils.getUserMentionStyle(isOurMention)}
textStyle={StyleUtils.getUserMentionTextStyle(isOurMention)}
codeFirstWordStyle={styles.mentionFirstWordStyle}
codeLastWordStyle={styles.mentionLastWordStyle}
key={props.key}
/>
</Text>
</Tooltip>
);
};

MentionUserRenderer.propTypes = propTypes;
MentionUserRenderer.displayName = 'MentionUserRenderer';

export default withCurrentUserPersonalDetails(MentionUserRenderer);
2 changes: 2 additions & 0 deletions src/components/HTMLEngineProvider/HTMLRenderers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import AnchorRenderer from './AnchorRenderer';
import CodeRenderer from './CodeRenderer';
import EditedRenderer from './EditedRenderer';
import ImageRenderer from './ImageRenderer';
import MentionUserRenderer from './MentionUserRenderer';
import PreRenderer from './PreRenderer';

/**
Expand All @@ -16,4 +17,5 @@ export default {
// Custom tag renderers
edited: EditedRenderer,
pre: PreRenderer,
'mention-user': MentionUserRenderer,
puneetlath marked this conversation as resolved.
Show resolved Hide resolved
};
17 changes: 15 additions & 2 deletions src/components/InlineCodeBlock/WrappedText.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,21 @@ const propTypes = {
/** Style for each word(Token) in the text, remember that token also includes whitespaces among words */
// eslint-disable-next-line react/forbid-prop-types
wordStyles: PropTypes.arrayOf(PropTypes.object),

/** Style for first word(Token) in the text */
// eslint-disable-next-line react/forbid-prop-types
codeFirstWordStyle: PropTypes.object,

/** Style for last word(Token) in the text */
// eslint-disable-next-line react/forbid-prop-types
codeLastWordStyle: PropTypes.object,
};

const defaultProps = {
textStyles: [],
wordStyles: [],
codeFirstWordStyle: undefined,
codeLastWordStyle: undefined,
};

const WrappedText = (props) => {
Expand All @@ -43,6 +53,9 @@ const WrappedText = (props) => {
}

const textMatrix = getTextMatrix(props.children);
const codeFirstWordStyle = props.codeFirstWordStyle || styles.codeFirstWordStyle;
const codeLastWordStyle = props.codeLastWordStyle || styles.codeLastWordStyle;

return (
<>
{_.map(textMatrix, (rowText, rowIndex) => (
Expand All @@ -61,8 +74,8 @@ const WrappedText = (props) => {
<View
style={[
props.wordStyles,
colIndex === 0 && styles.codeFirstWordStyle,
colIndex === rowText.length - 1 && styles.codeLastWordStyle,
colIndex === 0 && codeFirstWordStyle,
colIndex === rowText.length - 1 && codeLastWordStyle,
]}
>
<Text style={props.textStyles}>{colText}</Text>
Expand Down
2 changes: 2 additions & 0 deletions src/components/InlineCodeBlock/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const InlineCodeBlock = (props) => {
props.boxModelStyle,
styles.codeWordStyle,
]}
codeFirstWordStyle={props.codeFirstWordStyle}
codeLastWordStyle={props.codeLastWordStyle}
>
{props.defaultRendererProps.tnode.data}
</WrappedText>
Expand Down
6 changes: 6 additions & 0 deletions src/components/InlineCodeBlock/inlineCodeBlockPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ const inlineCodeBlockPropTypes = {
defaultRendererProps: PropTypes.object.isRequired,
boxModelStyle: PropTypes.any.isRequired,
textStyle: PropTypes.any.isRequired,

/** Style for first word(Token) in the text */
codeFirstWordStyle: PropTypes.object,

/** Style for last word(Token) in the text */
codeLastWordStyle: PropTypes.object,
};

export default inlineCodeBlockPropTypes;
30 changes: 30 additions & 0 deletions src/styles/StyleUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,34 @@ function getGoogleListViewStyle(shouldDisplayBorder) {
};
}

/**
* Returns style object for the user mention component based on whether the mention is ours or not.
* @param {Boolean} isOurMention
* @returns {Object}
*/
function getUserMentionStyle(isOurMention) {
const backgroundColor = isOurMention ? themeColors.ourMentionBG : themeColors.mentionBG;
return {
backgroundColor,
borderRadius: variables.componentBorderRadiusSmall,
paddingHorizontal: 2,
};
}

/**
* Returns text color for the user mention text based on whether the mention is ours or not.
* @param {Boolean} isOurMention
* @returns {Object}
*/
function getUserMentionTextStyle(isOurMention) {
return {
color: isOurMention ? themeColors.ourMentionText : themeColors.mentionText,

// font size is set to 13 to be in line with the font size used for code fragments so workaround will look better
fontSize: variables.fontSizeLabel,
};
}

export {
getAvatarSize,
getAvatarStyle,
Expand Down Expand Up @@ -1069,4 +1097,6 @@ export {
getFontSizeStyle,
getSignInWordmarkWidthStyle,
getGoogleListViewStyle,
getUserMentionStyle,
getUserMentionTextStyle,
};
4 changes: 4 additions & 0 deletions src/styles/colors.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,17 @@ export default {
midnight: '#002140',

// Brand Colors from Figma
blue100: '#B0D9FF',
blue200: '#8DC8FF',
blue400: '#0185FF',
blue600: '#0164BF',
blue700: '#003C73',
blue800: '#002140',

green100: '#B1F2D6',
green200: '#8EECC4',
green400: '#03D47C',
green600: '#008C59',
green700: '#085239',
green800: '#002E22',

Expand Down
12 changes: 12 additions & 0 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -2410,6 +2410,18 @@ const styles = {
paddingRight: 5,
},

mentionFirstWordStyle: {
paddingLeft: 2,
borderTopRightRadius: 4,
borderBottomRightRadius: 4,
},

mentionLastWordStyle: {
paddingRight: 2,
borderTopLeftRadius: 4,
borderBottomLeftRadius: 4,
},

fullScreenLoading: {
backgroundColor: themeColors.componentBG,
opacity: 0.8,
Expand Down
4 changes: 4 additions & 0 deletions src/styles/themes/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ const darkTheme = {
reactionActive: '#003C73',
badgeAdHoc: colors.pink600,
badgeAdHocHover: colors.pink700,
mentionText: colors.blue100,
mentionBG: colors.blue600,
ourMentionText: colors.green100,
ourMentionBG: colors.green600,
};

const oldTheme = {
Expand Down