From 38b1be12910b92bbcbf286616d227b3e1edef881 Mon Sep 17 00:00:00 2001 From: Bettenbuk Zoltan Date: Wed, 13 Feb 2019 10:39:40 +0100 Subject: [PATCH] [RN] Extract AvatarListItem --- .../base/react/components/_.native.js | 2 + react/features/base/react/components/index.js | 2 + .../react/components/native/AvatarListItem.js | 203 ++++++++++++++++++ .../native/NavigateSectionListItem.js | 64 +----- .../base/react/components/native/index.js | 3 + .../base/react/components/native/styles.js | 9 +- 6 files changed, 223 insertions(+), 60 deletions(-) create mode 100644 react/features/base/react/components/native/AvatarListItem.js diff --git a/react/features/base/react/components/_.native.js b/react/features/base/react/components/_.native.js index 738c4d2b8..a32ec6061 100644 --- a/react/features/base/react/components/_.native.js +++ b/react/features/base/react/components/_.native.js @@ -1 +1,3 @@ +// @flow + export * from './native'; diff --git a/react/features/base/react/components/index.js b/react/features/base/react/components/index.js index 0020d19af..b8ef0a4c8 100644 --- a/react/features/base/react/components/index.js +++ b/react/features/base/react/components/index.js @@ -1,3 +1,5 @@ +// @flow + export * from './_'; export { default as AbstractPage } from './AbstractPage'; export { default as NavigateSectionList } from './NavigateSectionList'; diff --git a/react/features/base/react/components/native/AvatarListItem.js b/react/features/base/react/components/native/AvatarListItem.js new file mode 100644 index 000000000..86b31e2f0 --- /dev/null +++ b/react/features/base/react/components/native/AvatarListItem.js @@ -0,0 +1,203 @@ +// @flow + +import React, { Component } from 'react'; +import { Text } from 'react-native'; + +import { Icon } from '../../../font-icons'; +import { Avatar } from '../../../participants'; +import { StyleType } from '../../../styles'; + +import { type Item } from '../../Types'; + +import Container from './Container'; +import styles, { AVATAR_SIZE, UNDERLAY_COLOR } from './styles'; + +type Props = { + + /** + * Preferred size of the avatar. + */ + avatarSize?: number, + + /** + * External style to be applied to the avatar (icon). + */ + avatarStyle?: StyleType, + + /** + * External style to be applied to the avatar (text). + */ + avatarTextStyle?: StyleType, + + /** + * Children of the component. + */ + children?: ?React$Element<*>, + + /** + * item containing data to be rendered + */ + item: Item, + + /** + * External style prop to be applied to the extra lines. + */ + linesStyle?: StyleType, + + /** + * Function to invoke on press. + */ + onPress: ?Function, + + /** + * External style prop to be applied to the title. + */ + titleStyle?: StyleType +}; + +/** + * Implements a list item with an avatar rendered for it. + */ +export default class AvatarListItem extends Component { + /** + * Constructor of the component. + * + * @inheritdoc + */ + constructor(props: Props) { + super(props); + + this._renderItemLine = this._renderItemLine.bind(this); + } + + /** + * Implements {@code Component#render}. + * + * @inheritdoc + */ + render() { + const { + avatarSize = AVATAR_SIZE, + avatarStyle, + avatarTextStyle + } = this.props; + const { avatar, colorBase, lines, title } = this.props.item; + const avatarStyles = { + ...styles.avatar, + ...this._getAvatarColor(colorBase), + ...avatarStyle, + borderRadius: avatarSize / 2, + height: avatarSize, + width: avatarSize + }; + + const isAvatarURL = Boolean(avatar && avatar.match(/^http[s]*:\/\//i)); + + return ( + + + + { + isAvatarURL && + } + + { + Boolean(avatar && !isAvatarURL) && + } + + { + !avatar && + { title.substr(0, 1).toUpperCase() } + + } + + + + + { title } + + {this._renderItemLines(lines)} + + { this.props.children } + + ); + } + + /** + * Returns a style (color) based on the string that determines the color of + * the avatar. + * + * @param {string} colorBase - The string that is the base of the color. + * @private + * @returns {Object} + */ + _getAvatarColor(colorBase) { + if (!colorBase) { + return null; + } + let nameHash = 0; + + for (let i = 0; i < colorBase.length; i++) { + nameHash += colorBase.codePointAt(i); + } + + return styles[`avatarColor${(nameHash % 5) + 1}`]; + } + + _renderItemLine: (string, number) => React$Node; + + /** + * Renders a single line from the additional lines. + * + * @param {string} line - The line text. + * @param {number} index - The index of the line. + * @private + * @returns {React$Node} + */ + _renderItemLine(line, index) { + if (!line) { + return null; + } + + return ( + + {line} + + ); + } + + _renderItemLines: Array => Array; + + /** + * Renders the additional item lines, if any. + * + * @param {Array} lines - The lines to render. + * @private + * @returns {Array} + */ + _renderItemLines(lines) { + return lines && lines.length ? lines.map(this._renderItemLine) : null; + } +} diff --git a/react/features/base/react/components/native/NavigateSectionListItem.js b/react/features/base/react/components/native/NavigateSectionListItem.js index b7c6a43b5..7b3037ed4 100644 --- a/react/features/base/react/components/native/NavigateSectionListItem.js +++ b/react/features/base/react/components/native/NavigateSectionListItem.js @@ -7,9 +7,11 @@ import { ColorPalette } from '../../../styles'; import Container from './Container'; import Text from './Text'; -import styles, { UNDERLAY_COLOR } from './styles'; +import styles from './styles'; import type { Item } from '../../Types'; +import AvatarListItem from './AvatarListItem'; + type Props = { /** @@ -48,34 +50,11 @@ export default class NavigateSectionListItem extends Component { */ constructor(props: Props) { super(props); - this._getAvatarColor = this._getAvatarColor.bind(this); + this._renderItemLine = this._renderItemLine.bind(this); this._renderItemLines = this._renderItemLines.bind(this); } - _getAvatarColor: string => Object; - - /** - * Returns a style (color) based on the string that determines the color of - * the avatar. - * - * @param {string} colorBase - The string that is the base of the color. - * @private - * @returns {Object} - */ - _getAvatarColor(colorBase) { - if (!colorBase) { - return null; - } - let nameHash = 0; - - for (let i = 0; i < colorBase.length; i++) { - nameHash += colorBase.codePointAt(i); - } - - return styles[`avatarColor${(nameHash % 5) + 1}`]; - } - _renderItemLine: (string, number) => React$Node; /** @@ -138,12 +117,8 @@ export default class NavigateSectionListItem extends Component { * @returns {ReactElement} */ render() { - const { slideActions } = this.props; - const { id, colorBase, lines, title } = this.props.item; - const avatarStyles = { - ...styles.avatar, - ...this._getAvatarColor(colorBase) - }; + const { item, slideActions } = this.props; + const { id } = item; let right; // NOTE: The {@code Swipeout} component has an onPress prop encapsulated @@ -165,31 +140,12 @@ export default class NavigateSectionListItem extends Component { - - - - - {title.substr(0, 1).toUpperCase()} - - - - - - {title} - - {this._renderItemLines(lines)} - + { this.props.secondaryAction && this._renderSecondaryAction() } - + ); } diff --git a/react/features/base/react/components/native/index.js b/react/features/base/react/components/native/index.js index 1cfc6fbe3..a02da2eec 100644 --- a/react/features/base/react/components/native/index.js +++ b/react/features/base/react/components/native/index.js @@ -1,3 +1,6 @@ +// @flow + +export { default as AvatarListItem } from './AvatarListItem'; export { default as BackButton } from './BackButton'; export { default as Container } from './Container'; export { default as Header } from './Header'; diff --git a/react/features/base/react/components/native/styles.js b/react/features/base/react/components/native/styles.js index 09e8cbb7b..9b3badaf4 100644 --- a/react/features/base/react/components/native/styles.js +++ b/react/features/base/react/components/native/styles.js @@ -5,7 +5,6 @@ import { StyleSheet } from 'react-native'; import { BoxModel, ColorPalette, createStyleSheet } from '../../../styles'; const AVATAR_OPACITY = 0.4; -const AVATAR_SIZE = 65; const HEADER_COLOR = ColorPalette.blue; // Header height is from Android guidelines. Also, this looks good. @@ -13,6 +12,7 @@ const HEADER_HEIGHT = 56; const OVERLAY_FONT_COLOR = 'rgba(255, 255, 255, 0.6)'; const SECONDARY_ACTION_BUTTON_SIZE = 30; +export const AVATAR_SIZE = 65; export const HEADER_PADDING = BoxModel.padding; export const STATUSBAR_COLOR = ColorPalette.blueHighlight; export const SIDEBAR_WIDTH = 250; @@ -153,10 +153,7 @@ const SECTION_LIST_STYLES = { avatar: { alignItems: 'center', backgroundColor: `rgba(23, 160, 219, ${AVATAR_OPACITY})`, - borderRadius: AVATAR_SIZE, - height: AVATAR_SIZE, - justifyContent: 'center', - width: AVATAR_SIZE + justifyContent: 'center' }, /** @@ -200,7 +197,7 @@ const SECTION_LIST_STYLES = { avatarContent: { backgroundColor: 'rgba(0, 0, 0, 0)', color: OVERLAY_FONT_COLOR, - fontSize: 32, + fontSize: Math.floor(AVATAR_SIZE / 2), fontWeight: '100', textAlign: 'center' },