From 978b3749a7343590687229bec9513d9e0e22e449 Mon Sep 17 00:00:00 2001 From: Bruno Babic Date: Mon, 15 Apr 2019 15:55:48 +0200 Subject: [PATCH] v0.23.20 --- .npmignore | 3 +- components/Button.js | 8 +- components/DropDownMenu/DropDownMenu.js | 17 +-- components/DropDownMenu/DropDownModal.js | 17 +-- components/FormGroup.js | 4 +- components/GridRow.js | 7 +- components/HorizontalPager/HorizontalPager.js | 10 +- components/HorizontalPager/Page.js | 2 +- components/ImageGallery/ImageGalleryBase.js | 16 +-- components/ImageGalleryOverlay.js | 19 +-- components/ImagePreview.js | 4 +- components/InlineGallery.js | 4 +- components/LinearGradient.js | 4 +- components/ListView.js | 8 +- components/LoadingIndicator.js | 6 +- components/NavigationBar/NavigationBar.js | 73 +++++----- .../NavigationBar/NavigationBarAnimations.js | 2 +- components/NavigationBar/composeChildren.js | 4 +- components/PageIndicators.js | 5 +- components/RichMedia.js | 1 + components/ScrollView/ScrollDriverProvider.js | 8 +- components/ScrollView/ScrollView.js | 5 +- components/ShareButton.js | 6 +- components/Spinner.js | 4 +- components/Switch.js | 10 +- components/Text.js | 4 +- components/TextInput.js | 4 +- components/Touchable.js | 12 +- components/TouchableNativeFeedback.js | 4 +- components/TouchableOpacity.js | 4 +- components/Video/Video.js | 6 +- components/View.js | 6 +- const.js | 4 + examples/RestaurantsApp/App.js | 4 +- examples/RestaurantsApp/redux.js | 4 +- .../screens/RestaurantDetails.js | 9 +- .../RestaurantsApp/screens/Restaurants.js | 12 +- .../RestaurantsApp/screens/RestaurantsList.js | 20 +-- examples/components/Buttons.js | 10 +- examples/components/Cards.js | 15 +- examples/components/Dividers.js | 6 +- examples/components/DropDownMenus.js | 13 +- examples/components/Examples.js | 9 +- examples/components/FormComponents.js | 18 +-- examples/components/Headers.js | 18 +-- examples/components/HorizontalPagers.js | 13 +- examples/components/ImageGalleries.js | 6 +- examples/components/Images.js | 2 +- examples/components/InlineGalleries.js | 6 +- examples/components/NavigationBars.js | 16 +-- examples/components/Rows.js | 16 +-- examples/components/Spinners.js | 6 +- examples/components/Tiles.js | 22 ++- examples/components/Typography.js | 9 +- examples/components/Videos.js | 4 +- examples/create-react-native-app/App.js | 4 +- helpers/device-selector.js | 19 ++- html/Html.js | 4 +- html/components/Gallery.js | 4 +- html/components/Image.js | 4 +- html/components/SimpleHtml.js | 128 ++++++++++++++++++ html/elements/A.js | 4 +- html/elements/Inline.js | 4 +- html/index.js | 2 + html/services/getEmptyObjectKeys.js | 11 ++ index.js | 3 + navigation/NavigationBar.js | 4 + navigation/NavigationBarView.js | 2 +- .../navbar-image.composer.js | 3 +- navigation/index.js | 2 + package.json | 7 +- theme.js | 28 +++- 72 files changed, 439 insertions(+), 323 deletions(-) create mode 100644 html/components/SimpleHtml.js create mode 100644 html/services/getEmptyObjectKeys.js diff --git a/.npmignore b/.npmignore index ff5562e6..507302d6 100644 --- a/.npmignore +++ b/.npmignore @@ -4,8 +4,9 @@ node_modules # Ignore test files *_tests_ -# Ignore the example app +# Ignore the example apps examples/RestaurantsApp/ +examples/create-react-native-app/ # Ignore local/config files .editorconfig diff --git a/components/Button.js b/components/Button.js index fc620c90..7f32e9be 100644 --- a/components/Button.js +++ b/components/Button.js @@ -1,12 +1,10 @@ -import React, { Component } from 'react'; -import { - TouchableOpacity, -} from 'react-native'; +import React, { PureComponent } from 'react'; +import { TouchableOpacity } from 'react-native'; import { connectStyle } from '@shoutem/theme'; import { connectAnimation } from '@shoutem/animation'; -class Button extends Component { +class Button extends PureComponent { render() { // The underlayColor is not a valid RN style // property, so we have to unset it here. diff --git a/components/DropDownMenu/DropDownMenu.js b/components/DropDownMenu/DropDownMenu.js index 52635fdf..1a2f22cc 100644 --- a/components/DropDownMenu/DropDownMenu.js +++ b/components/DropDownMenu/DropDownMenu.js @@ -1,21 +1,18 @@ -import React, { - Component, -} from 'react'; +import React, { PureComponent } from 'react'; import _ from 'lodash'; + import { connectStyle } from '@shoutem/theme'; -import { - Button, - Icon, - Text, - View, -} from '../../index'; +import { Button } from '../Button'; +import { Icon } from '../Icon'; +import { Text } from '../Text'; +import { View } from '../View'; import { DropDownModal } from './DropDownModal'; const modalSpecificProps = ['visible', 'onClose']; const dropDownMenuPropTypes = { ..._.omit(DropDownModal.propTypes, modalSpecificProps) }; -class DropDownMenu extends Component { +class DropDownMenu extends PureComponent { /** * @see DropDownModal.propTypes */ diff --git a/components/DropDownMenu/DropDownModal.js b/components/DropDownMenu/DropDownModal.js index 4b6b42f1..fc3fab48 100644 --- a/components/DropDownMenu/DropDownModal.js +++ b/components/DropDownMenu/DropDownModal.js @@ -1,7 +1,5 @@ import PropTypes from 'prop-types'; -import React, { - Component, -} from 'react'; +import React, { PureComponent } from 'react'; import { Modal, ListView, @@ -11,6 +9,9 @@ import { } from 'react-native'; import _ from 'lodash'; +import { connectStyle, changeColorAlpha } from '@shoutem/theme'; +import { TimingDriver, FadeIn, ZoomOut } from '@shoutem/animation'; + import { Button } from '../Button'; import { Icon } from '../Icon'; import { Text } from '../Text'; @@ -18,17 +19,9 @@ import { View } from '../View'; import { LinearGradient } from '../LinearGradient'; import { TouchableOpacity } from '../TouchableOpacity'; -import { connectStyle, changeColorAlpha } from '@shoutem/theme'; - -import { - TimingDriver, - FadeIn, - ZoomOut, -} from '@shoutem/animation'; - const window = Dimensions.get('window'); -class DropDownModal extends Component { +class DropDownModal extends PureComponent { static propTypes = { /** * Callback that is called when dropdown option is selected diff --git a/components/FormGroup.js b/components/FormGroup.js index 3c654b35..87dbef38 100644 --- a/components/FormGroup.js +++ b/components/FormGroup.js @@ -1,11 +1,11 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { connectStyle } from '@shoutem/theme'; import { connectAnimation } from '@shoutem/animation'; import { View } from './View'; -class FormGroup extends Component { +class FormGroup extends PureComponent { render() { return ( diff --git a/components/GridRow.js b/components/GridRow.js index c7293fab..2589fefa 100644 --- a/components/GridRow.js +++ b/components/GridRow.js @@ -1,15 +1,16 @@ import PropTypes from 'prop-types'; -import React, { Children } from 'react'; +import React, { PureComponent, Children } from 'react'; import { View as RNView, ViewPropTypes, } from 'react-native'; import _ from 'lodash'; -import { View } from './View'; import { connectStyle } from '@shoutem/theme'; import { connectAnimation } from '@shoutem/animation'; +import { View } from './View'; + /** * Renders empty placeholder views to fill any empty space * left by missing views within a row. This is necessary so that @@ -24,7 +25,7 @@ function renderPlaceholderViews(count) { // Ref needed // eslint-disable-next-line react/prefer-stateless-function -class GridRow extends React.Component { +class GridRow extends PureComponent { render() { const { children, columns } = this.props; const missingElementsCount = columns - Children.count(children); diff --git a/components/HorizontalPager/HorizontalPager.js b/components/HorizontalPager/HorizontalPager.js index ac46901d..214bb985 100644 --- a/components/HorizontalPager/HorizontalPager.js +++ b/components/HorizontalPager/HorizontalPager.js @@ -1,20 +1,16 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { ScrollView, InteractionManager, LayoutAnimation, } from 'react-native'; - import _ from 'lodash'; import { connectStyle } from '@shoutem/theme'; -import { - View, -} from '../../index'; - +import { View } from '../View'; import { Page } from './Page'; /** @@ -28,7 +24,7 @@ import { Page } from './Page'; * ScrollView and ViewPagerAndroid for this matter. * */ -class HorizontalPager extends Component { +class HorizontalPager extends PureComponent { static propTypes = { // Prop defining whether the Pager will bounce back // when user tries to swipe beyond end of content (iOS only) diff --git a/components/HorizontalPager/Page.js b/components/HorizontalPager/Page.js index 75414940..1643f4ae 100644 --- a/components/HorizontalPager/Page.js +++ b/components/HorizontalPager/Page.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { View } from '../../index'; +import { View } from '../View'; /** * A HorizontalPager page. This component is used in diff --git a/components/ImageGallery/ImageGalleryBase.js b/components/ImageGallery/ImageGalleryBase.js index 582a18b1..5b596f5f 100644 --- a/components/ImageGallery/ImageGalleryBase.js +++ b/components/ImageGallery/ImageGalleryBase.js @@ -1,20 +1,16 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; - +import React, { PureComponent } from 'react'; import _ from 'lodash'; -import { - View, - HorizontalPager, - LoadingIndicator, - Image, -} from '../../index'; - +import { View } from '../View'; +import { HorizontalPager } from '../HorizontalPager'; +import { LoadingIndicator } from '../LoadingIndicator'; +import { Image } from '../Image'; const IMAGE_PREVIEW_MODE = 'imagePreview'; const IMAGE_GALLERY_MODE = 'gallery'; -export class ImageGalleryBase extends Component { +export class ImageGalleryBase extends PureComponent { /** * The image preview mode is the mode in which * the user can zoom in/out and pan the image around. diff --git a/components/ImageGalleryOverlay.js b/components/ImageGalleryOverlay.js index fde23726..08a57dd5 100644 --- a/components/ImageGalleryOverlay.js +++ b/components/ImageGalleryOverlay.js @@ -1,19 +1,14 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { - ScrollView, -} from 'react-native'; +import React, { PureComponent } from 'react'; +import { ScrollView } from 'react-native'; import { connectStyle } from '@shoutem/theme'; import { connectAnimation } from '@shoutem/animation'; -import { - View, - Subtitle, - Caption, - Icon, - TouchableOpacity, -} from './../index'; +import { Caption, Subtitle } from './Text'; +import { Icon } from './Icon'; +import { View } from './View'; +import { TouchableOpacity } from './TouchableOpacity'; const DESCRIPTION_LENGTH_TRIM_LIMIT = 90; @@ -22,7 +17,7 @@ const DESCRIPTION_LENGTH_TRIM_LIMIT = 90; * images in a gallery. It can display a title and * a description of an image. */ -class ImageGalleryOverlay extends Component { +class ImageGalleryOverlay extends PureComponent { static propTypes = { title: PropTypes.string, description: PropTypes.string, diff --git a/components/ImagePreview.js b/components/ImagePreview.js index 2b28037f..aba01efb 100644 --- a/components/ImagePreview.js +++ b/components/ImagePreview.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { View, Modal, @@ -29,7 +29,7 @@ const CLOSE_ICON_SIZE = 25; * Renders an ImagePreview which shows an inline image preview. * When clicked, the image is displayed in full screen. */ -class ImagePreview extends Component { +class ImagePreview extends PureComponent { constructor(props) { super(props); this.onPressCloseButton = this.onPressCloseButton.bind(this); diff --git a/components/InlineGallery.js b/components/InlineGallery.js index af4d4aef..37e1e694 100644 --- a/components/InlineGallery.js +++ b/components/InlineGallery.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import _ from 'lodash'; import { connectStyle } from '@shoutem/theme'; @@ -10,7 +10,7 @@ import { Image } from './Image'; import { HorizontalPager } from './HorizontalPager/HorizontalPager'; import { LoadingIndicator } from './LoadingIndicator'; -class InlineGallery extends Component { +class InlineGallery extends PureComponent { static propTypes = { // Array containing objects with image data (shape defined below) data: PropTypes.arrayOf( diff --git a/components/LinearGradient.js b/components/LinearGradient.js index 587208af..551a7ac6 100644 --- a/components/LinearGradient.js +++ b/components/LinearGradient.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { PureComponent } from 'react'; import RNLinearGradient from 'react-native-linear-gradient'; import _ from 'lodash'; import { connectAnimation } from '@shoutem/animation'; @@ -6,7 +6,7 @@ import { connectStyle } from '@shoutem/theme'; const RNLinearGradientPropsKeys = Object.keys(RNLinearGradient.propTypes); -class LinearGradient extends React.Component { +class LinearGradient extends PureComponent { render () { const { props } = this; diff --git a/components/ListView.js b/components/ListView.js index 1ea88b6e..98166538 100644 --- a/components/ListView.js +++ b/components/ListView.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { Component } from 'react'; import { View, ListView as RNListView, @@ -8,9 +8,11 @@ import { Platform, ScrollView, } from 'react-native'; +import _ from 'lodash'; + import { connectStyle } from '@shoutem/theme'; + import { Spinner } from './Spinner'; -import _ from 'lodash'; const scrollViewProps = _.keys(ScrollView.propTypes); @@ -64,7 +66,7 @@ class ListDataSource { } } -class ListView extends React.Component { +class ListView extends Component { static propTypes = { autoHideHeader: PropTypes.bool, style: PropTypes.object, diff --git a/components/LoadingIndicator.js b/components/LoadingIndicator.js index bd21824b..db391eda 100644 --- a/components/LoadingIndicator.js +++ b/components/LoadingIndicator.js @@ -1,6 +1,4 @@ -import React, { - Component, -} from 'react'; +import React, { PureComponent } from 'react'; import { connectStyle } from '@shoutem/theme'; @@ -10,7 +8,7 @@ import { Spinner } from './Spinner'; /** * Renders a loading indicator (spinner) that fits into available space (container) */ -class LoadingIndicator extends Component { +class LoadingIndicator extends PureComponent { render() { return ( diff --git a/components/NavigationBar/NavigationBar.js b/components/NavigationBar/NavigationBar.js index 1d7f2ba7..eca7ca7a 100644 --- a/components/NavigationBar/NavigationBar.js +++ b/components/NavigationBar/NavigationBar.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { StatusBar, Animated, @@ -14,6 +14,7 @@ import color from 'tinycolor2'; import { connectStyle } from '@shoutem/theme'; import { connectAnimation } from '@shoutem/animation'; +import { Device } from '../../helpers'; import composeChildren from './composeChildren'; function getBackgroundColor(style) { @@ -24,41 +25,8 @@ function getBackgroundColor(style) { return styleWithBg && styleWithBg.backgroundColor || 'transparent'; } -function setStatusBarStyle(backgroundColor) { - function chooseBarStyle(bgColor) { - return color(bgColor).isDark() ? 'light-content' : 'default'; - } - - function setStyle(bgColor) { - const { statusBarColor } = this.props; - - const color = statusBarColor || bgColor; - - if (Platform.OS === 'android') { - StatusBar.setBackgroundColor('rgba(0, 0, 0, 0.2)'); - } else { - const barStyle = chooseBarStyle(color); - StatusBar.setBarStyle(barStyle); - } - } - - // This is little bit hacky, but is the only way - // to determine the current value of interpolated Animated.Value - // Other way would be to ask developer to provide Animated.Value - // used to interpolate backgroundColor. But this way developer doesn't - // have to concern about status bar if he animates navigation bar color - if (backgroundColor && backgroundColor._parent instanceof Animated.Value) { - backgroundColor._parent.addListener((animation) => { - setStyle(backgroundColor._interpolation(animation.value)); - }); - setStyle(backgroundColor._interpolation(0)); - } else { - setStyle(backgroundColor); - } -} - // eslint-disable-next-line react/prefer-stateless-function -class NavigationBar extends Component { +class NavigationBar extends PureComponent { static propTypes = { leftComponent: PropTypes.node, centerComponent: PropTypes.node, @@ -72,6 +40,39 @@ class NavigationBar extends Component { id: 'default', }; + setStatusBarStyle(backgroundColor) { + function chooseBarStyle(bgColor) { + return color(bgColor).isDark() ? 'light-content' : 'default'; + } + + function setStyle(bgColor) { + const statusBarColor = _.get(this.props, 'statusBarColor', bgColor); + + const color = statusBarColor || bgColor; + + if (Platform.OS === 'android') { + StatusBar.setBackgroundColor('rgba(0, 0, 0, 0.2)'); + } else { + const barStyle = chooseBarStyle(color); + StatusBar.setBarStyle(barStyle); + } + } + + // This is little bit hacky, but is the only way + // to determine the current value of interpolated Animated.Value + // Other way would be to ask developer to provide Animated.Value + // used to interpolate backgroundColor. But this way developer doesn't + // have to concern about status bar if he animates navigation bar color + if (backgroundColor && backgroundColor._parent instanceof Animated.Value) { + backgroundColor._parent.addListener((animation) => { + setStyle(backgroundColor._interpolation(animation.value)); + }); + setStyle(backgroundColor._interpolation(0)); + } else { + setStyle(backgroundColor); + } + } + renderStatusBar() { const { style } = this.props; @@ -91,7 +92,7 @@ class NavigationBar extends Component { } = this.props; const backgroundColor = getBackgroundColor(style); - setStatusBarStyle(backgroundColor); + this.setStatusBarStyle(backgroundColor); // Key must be set to render new screen NavigationBar return ( diff --git a/components/NavigationBar/NavigationBarAnimations.js b/components/NavigationBar/NavigationBarAnimations.js index 6ef3eef6..03145187 100644 --- a/components/NavigationBar/NavigationBarAnimations.js +++ b/components/NavigationBar/NavigationBarAnimations.js @@ -1,4 +1,4 @@ -import * as _ from 'lodash'; +import _ from 'lodash'; class ColorAnimation { constructor(options) { diff --git a/components/NavigationBar/composeChildren.js b/components/NavigationBar/composeChildren.js index d68ee98f..8b004fe6 100644 --- a/components/NavigationBar/composeChildren.js +++ b/components/NavigationBar/composeChildren.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { Button } from '../Button'; import { Title } from '../Text'; @@ -74,7 +74,7 @@ function skipUndefined(objValue, srcValue) { } // eslint-disable-next-line react/prefer-stateless-function -const composeChildren = NavigationBarComponent => class extends Component { +const composeChildren = NavigationBarComponent => class extends PureComponent { render() { const newProps = {}; _.forEach(this.props, (value, key) => { diff --git a/components/PageIndicators.js b/components/PageIndicators.js index d37b9d2e..2bb122de 100644 --- a/components/PageIndicators.js +++ b/components/PageIndicators.js @@ -1,13 +1,14 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { connectStyle } from '@shoutem/theme'; + import { View } from './View'; /** * Renders Page indicators (dots) */ -class PageIndicators extends Component { +class PageIndicators extends PureComponent { static propTypes = { // ActiveIndex: number defining which page indicator will be rendered as active (selected) activeIndex: PropTypes.number, diff --git a/components/RichMedia.js b/components/RichMedia.js index cb0a09e8..81c979be 100644 --- a/components/RichMedia.js +++ b/components/RichMedia.js @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; + import { Html } from '../html'; export default function RichMedia({ body }) { diff --git a/components/ScrollView/ScrollDriverProvider.js b/components/ScrollView/ScrollDriverProvider.js index cb9e4206..7d7e3a78 100644 --- a/components/ScrollView/ScrollDriverProvider.js +++ b/components/ScrollView/ScrollDriverProvider.js @@ -1,14 +1,16 @@ import PropTypes from 'prop-types'; -import React, { Component, Children } from 'react'; +import React, { PureComponent, Children } from 'react'; +import _ from 'lodash'; + import { DriverShape, ScrollDriver } from '@shoutem/animation'; -import * as _ from 'lodash'; + /** * Use this component if you want to share animation driver between unreachable siblings. * Just wrap their parent component with it. We use it to share an instance of ScrollDriver * between Screen and NavigationBar automatically. ScrollView from @shoutem/ui uses it to * register its driver. */ -export class ScrollDriverProvider extends Component { +export class ScrollDriverProvider extends PureComponent { static childContextTypes = { driverProvider: PropTypes.object, animationDriver: DriverShape, diff --git a/components/ScrollView/ScrollView.js b/components/ScrollView/ScrollView.js index f27e6434..6401630e 100644 --- a/components/ScrollView/ScrollView.js +++ b/components/ScrollView/ScrollView.js @@ -1,7 +1,8 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { Animated } from 'react-native'; import _ from 'lodash'; + import { connectStyle } from '@shoutem/theme'; import { ScrollDriver, DriverShape } from '@shoutem/animation'; @@ -11,7 +12,7 @@ import { Device } from '../../helpers'; const isTabBarOnScreen = true; const IPHONE_X_HOME_INDICATOR_PADDING = isTabBarOnScreen ? 0 : 34; -class ScrollView extends Component { +class ScrollView extends PureComponent { static propTypes = { ...Animated.ScrollView.propTypes, }; diff --git a/components/ShareButton.js b/components/ShareButton.js index f345f797..ecc464ad 100644 --- a/components/ShareButton.js +++ b/components/ShareButton.js @@ -1,7 +1,5 @@ import PropTypes from 'prop-types'; -import React, { - Component, -} from 'react'; +import React, { PureComponent } from 'react'; import { Share, Platform } from 'react-native'; @@ -20,7 +18,7 @@ const { string } = PropTypes; * It should have the style of its underlying button. That's why it's not connected to style * or animation. */ -class ShareButton extends Component { +class ShareButton extends PureComponent { static propTypes = { // Animation name for share icon animationName: string, diff --git a/components/Spinner.js b/components/Spinner.js index 05050c9d..a72e2fb8 100644 --- a/components/Spinner.js +++ b/components/Spinner.js @@ -1,12 +1,12 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { PureComponent } from 'react'; import { ActivityIndicator, } from 'react-native'; import { connectStyle } from '@shoutem/theme'; -class Spinner extends React.Component { +class Spinner extends PureComponent { render() { const { style } = this.props; const indicatorStyle = { ...style }; diff --git a/components/Switch.js b/components/Switch.js index 1dd7d361..73cee1a6 100644 --- a/components/Switch.js +++ b/components/Switch.js @@ -1,18 +1,16 @@ import PropTypes from 'prop-types'; -import React, { - Component, -} from 'react'; +import React, { PureComponent } from 'react'; import { TouchableWithoutFeedback } from 'react-native'; -import { View } from '@shoutem/ui'; - import { connectStyle } from '@shoutem/theme'; import { connectAnimation, TimingDriver } from '@shoutem/animation'; +import { View } from './View'; + const { bool, func, object, shape } = PropTypes; -class Switch extends Component { +class Switch extends PureComponent { static propTypes = { // True when switch is on, false otherwise value: bool, diff --git a/components/Text.js b/components/Text.js index 47a3dfdb..4a84d1ad 100644 --- a/components/Text.js +++ b/components/Text.js @@ -1,10 +1,10 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { Text as RNText } from 'react-native'; import { connectStyle } from '@shoutem/theme'; import { connectAnimation } from '@shoutem/animation'; -class Text extends Component { +class Text extends PureComponent { render() { return ( diff --git a/components/TextInput.js b/components/TextInput.js index 6a2cebbe..96238b64 100644 --- a/components/TextInput.js +++ b/components/TextInput.js @@ -1,11 +1,11 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { TextInput as RNTextInput } from 'react-native'; import { connectStyle } from '@shoutem/theme'; import { connectAnimation } from '@shoutem/animation'; -class TextInput extends Component { +class TextInput extends PureComponent { render() { const { props } = this; const style = { diff --git a/components/Touchable.js b/components/Touchable.js index 5e9a8bf8..fa7efe39 100644 --- a/components/Touchable.js +++ b/components/Touchable.js @@ -1,14 +1,12 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { PureComponent } from 'react'; import { Platform } from 'react-native'; import { connectStyle } from '@shoutem/theme'; -import { - TouchableOpacity, - TouchableNativeFeedback, - View, -} from '../index'; +import { View } from './View'; +import { TouchableOpacity } from './TouchableOpacity'; +import { TouchableNativeFeedback } from './TouchableNativeFeedback'; /** * A universal touchable component with a @@ -17,7 +15,7 @@ import { * iOS, and a TouchableNativeFeedback on * Android. */ -class Touchable extends React.Component { +class Touchable extends PureComponent { static propTypes = { ...TouchableOpacity.propTypes, ...TouchableNativeFeedback.propTypes, diff --git a/components/TouchableNativeFeedback.js b/components/TouchableNativeFeedback.js index 5b75c027..76f8e5f5 100644 --- a/components/TouchableNativeFeedback.js +++ b/components/TouchableNativeFeedback.js @@ -1,10 +1,10 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { PureComponent } from 'react'; import { TouchableNativeFeedback as RNTouchableNativeFeedback } from 'react-native'; import { connectStyle } from '@shoutem/theme'; -class TouchableNativeFeedback extends React.Component { +class TouchableNativeFeedback extends PureComponent { static propTypes = { ...RNTouchableNativeFeedback.propTypes, style: PropTypes.shape({ diff --git a/components/TouchableOpacity.js b/components/TouchableOpacity.js index da5c4b88..d4d56d11 100644 --- a/components/TouchableOpacity.js +++ b/components/TouchableOpacity.js @@ -1,9 +1,9 @@ -import React from 'react'; +import React, { PureComponent } from 'react'; import { TouchableOpacity as RNTouchableOpacity } from 'react-native'; import { connectStyle } from '@shoutem/theme'; -class TouchableOpacity extends React.Component { +class TouchableOpacity extends PureComponent { render() { const props = this.props; diff --git a/components/Video/Video.js b/components/Video/Video.js index 734537fa..d99e1b37 100644 --- a/components/Video/Video.js +++ b/components/Video/Video.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { PureComponent } from 'react'; import { View, @@ -8,6 +8,7 @@ import { import { connectStyle } from '@shoutem/theme'; import { connectAnimation } from '@shoutem/animation'; + import VideoSourceReader from './VideoSourceReader'; function createSourceObject(source, playerParams, poster) { @@ -40,7 +41,7 @@ function createSourceObject(source, playerParams, poster) { * * @returns {*} */ -class Video extends React.Component { +class Video extends PureComponent { static propTypes = { width: PropTypes.number, height: PropTypes.number, @@ -75,6 +76,7 @@ class Video extends React.Component { style={{width, height}} source={createSourceObject(source, playerParams, poster)} scrollEnabled={false} + originWhitelist={['*']} /> ); diff --git a/components/View.js b/components/View.js index 8735a771..76a8a84d 100644 --- a/components/View.js +++ b/components/View.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { View as RNView, ViewPropTypes, @@ -8,9 +8,9 @@ import { import { connectStyle } from '@shoutem/theme'; import { connectAnimation } from '@shoutem/animation'; -import { LinearGradient } from '../components/LinearGradient'; +import { LinearGradient } from './LinearGradient'; -class View extends Component { +class View extends PureComponent { render() { const style = { ...this.props.style }; let gradient = null; diff --git a/const.js b/const.js index 8dc8b0c4..92d289e0 100644 --- a/const.js +++ b/const.js @@ -1,4 +1,8 @@ export const NAVIGATION_HEADER_HEIGHT = 64; + export const IPHONE_X_NOTCH_PADDING = 30; export const IPHONE_X_HOME_INDICATOR_PADDING = 34; export const IPHONE_X_LONG_SIDE = 812; + +export const IPHONE_XR_NOTCH_PADDING = 34; +export const IPHONE_XR_LONG_SIDE = 896; diff --git a/examples/RestaurantsApp/App.js b/examples/RestaurantsApp/App.js index 374ef80b..f168c102 100644 --- a/examples/RestaurantsApp/App.js +++ b/examples/RestaurantsApp/App.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { createStore, applyMiddleware } from 'redux'; import { Provider } from 'react-redux'; import thunk from 'redux-thunk'; @@ -9,7 +9,7 @@ import Restaurants from './screens/Restaurants'; const createStoreWithMiddleware = applyMiddleware(thunk)(createStore); const store = createStoreWithMiddleware(reducer); -export default class App extends Component { +export default class App extends PureComponent { render() { return ( diff --git a/examples/RestaurantsApp/redux.js b/examples/RestaurantsApp/redux.js index dbbd4d8a..d975f599 100644 --- a/examples/RestaurantsApp/redux.js +++ b/examples/RestaurantsApp/redux.js @@ -1,8 +1,6 @@ import { combineReducers } from 'redux'; -import NavigationExperimental from 'react-native-navigation-experimental-compat'; - -const NavigationStateUtils = NavigationExperimental.StateUtils; +import { StateUtils as NavigationStateUtils } from 'react-native-navigation-experimental-compat'; const NAV_PUSH = 'NAV_PUSH'; const NAV_POP = 'NAV_POP'; diff --git a/examples/RestaurantsApp/screens/RestaurantDetails.js b/examples/RestaurantsApp/screens/RestaurantDetails.js index 27111cb6..95081d06 100644 --- a/examples/RestaurantsApp/screens/RestaurantDetails.js +++ b/examples/RestaurantsApp/screens/RestaurantDetails.js @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; + import { ScrollView, Icon, @@ -14,11 +15,9 @@ import { Screen, } from '@shoutem/ui'; -import { - NavigationBar, -} from '@shoutem/ui/navigation'; +import { NavigationBar } from 'shoutem.navigation'; -export default class RestaurantDetails extends Component { +export default class RestaurantDetails extends PureComponent { static propTypes = { restaurant: PropTypes.object, }; diff --git a/examples/RestaurantsApp/screens/Restaurants.js b/examples/RestaurantsApp/screens/Restaurants.js index 7a21c78e..0bc70447 100644 --- a/examples/RestaurantsApp/screens/Restaurants.js +++ b/examples/RestaurantsApp/screens/Restaurants.js @@ -1,17 +1,14 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; -import { - CardStack, - NavigationBar, -} from '@shoutem/ui/navigation'; +import { CardStack, NavigationBar } from 'shoutem.navigation'; -import { navigatePop } from '../redux'; import RestaurantsList from './RestaurantsList'; import RestaurantDetails from './RestaurantDetails'; +import { navigatePop } from '../redux'; -class Restaurants extends Component { +class Restaurants extends PureComponent { static propTypes = { onNavigateBack: PropTypes.func.isRequired, navigationState: PropTypes.object, @@ -29,6 +26,7 @@ class Restaurants extends Component { const { route } = props.scene; let Screen = route.key === 'RestaurantDetails' ? RestaurantDetails : RestaurantsList; + return (); } diff --git a/examples/RestaurantsApp/screens/RestaurantsList.js b/examples/RestaurantsApp/screens/RestaurantsList.js index 596457b3..7b807d56 100644 --- a/examples/RestaurantsApp/screens/RestaurantsList.js +++ b/examples/RestaurantsApp/screens/RestaurantsList.js @@ -1,5 +1,7 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; +import { connect } from 'react-redux'; + import { ImageBackground, ListView, @@ -11,20 +13,18 @@ import { Divider, } from '@shoutem/ui'; -import { - NavigationBar, -} from '@shoutem/ui/navigation'; -import { connect } from 'react-redux'; +import { NavigationBar } from 'shoutem.navigation'; import { navigatePush } from '../redux'; -class RestaurantsList extends Component { +class RestaurantsList extends PureComponent { static propTypes = { onButtonPress: PropTypes.func, }; constructor(props) { super(props); + this.renderRow = this.renderRow.bind(this); } @@ -55,10 +55,10 @@ class RestaurantsList extends Component { return ( - this.renderRow(restaurant)} - /> + this.renderRow(restaurant)} + /> ); } diff --git a/examples/components/Buttons.js b/examples/components/Buttons.js index 944cb4a8..734ca12a 100644 --- a/examples/components/Buttons.js +++ b/examples/components/Buttons.js @@ -1,12 +1,10 @@ import React from 'react'; +import { Icon } from '../../components/Icon'; +import { Text } from '../../components/Text'; +import { View } from '../../components/View'; +import { Button } from '../../components/Button'; import { Stage } from './Stage'; -import { - View, - Button, - Icon, - Text, -} from '../../index'; export function Buttons() { return ( diff --git a/examples/components/Cards.js b/examples/components/Cards.js index 4f1ff4e6..fbd13d4e 100644 --- a/examples/components/Cards.js +++ b/examples/components/Cards.js @@ -1,15 +1,12 @@ import React from 'react'; +import { Caption, Subtitle } from '../../components/Text'; +import { View } from '../../components/View'; +import { Card } from '../../components/Card'; +import { Image } from '../../components/Image'; +import { Icon } from '../../components/Icon'; +import { Button } from '../../components/Button'; import { Stage } from './Stage'; -import { - View, - Card, - Image, - Subtitle, - Caption, - Icon, - Button, -} from '../../index'; export function Cards() { return ( diff --git a/examples/components/Dividers.js b/examples/components/Dividers.js index b09d938e..3cb865df 100644 --- a/examples/components/Dividers.js +++ b/examples/components/Dividers.js @@ -1,11 +1,9 @@ import React from 'react'; +import { Caption } from '../../components/Text'; import { View } from '../../components/View'; +import { Divider } from '../../components/Divider'; import { Stage } from './Stage'; -import { - Caption, - Divider, -} from '../../index'; export function Dividers() { return ( diff --git a/examples/components/DropDownMenus.js b/examples/components/DropDownMenus.js index c3c21546..944f13fb 100644 --- a/examples/components/DropDownMenus.js +++ b/examples/components/DropDownMenus.js @@ -1,13 +1,10 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; +import { Caption } from '../../components/Text'; import { View } from '../../components/View'; -import { Stage } from './Stage'; +import { FormGroup } from '../../components/FormGroup'; import { DropDownMenu } from '../../components/DropDownMenu'; - -import { - Caption, - FormGroup, -} from '../../index'; +import { Stage } from './Stage'; const options = [ { @@ -28,7 +25,7 @@ const options = [ const emptyOption = { id: '', name: 'Select'}; const optionsWithEmptyOption = [emptyOption, ...options]; -export class DropDownMenus extends Component { +export class DropDownMenus extends PureComponent { constructor() { super(); this.state = { diff --git a/examples/components/Examples.js b/examples/components/Examples.js index 32295e7e..965b35e7 100644 --- a/examples/components/Examples.js +++ b/examples/components/Examples.js @@ -1,6 +1,9 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; -import { Screen, DropDownMenu, Divider, ScrollView, } from '../../index'; +import { Screen } from '../../components/Screen'; +import { Divider } from '../../components/Divider'; +import { ScrollView } from '../../components/ScrollView'; +import { DropDownMenu } from '../../components/DropDownMenu'; import { Typography } from './Typography'; import { Dividers } from './Dividers'; @@ -32,7 +35,7 @@ const examples = [ { title: 'Form Components', component: FormComponents }, ]; -export class Examples extends Component { +export class Examples extends PureComponent { constructor() { super(); this.state = { diff --git a/examples/components/FormComponents.js b/examples/components/FormComponents.js index 0743aba7..806dafb9 100644 --- a/examples/components/FormComponents.js +++ b/examples/components/FormComponents.js @@ -1,17 +1,13 @@ -import React, { - Component, -} from 'react'; +import React, { PureComponent } from 'react'; +import { Caption } from '../../components/Text'; +import { View } from '../../components/View'; +import { Switch } from '../../components/Switch'; +import { FormGroup } from '../../components/FormGroup'; +import { TextInput } from '../../components/TextInput'; import { Stage } from './Stage'; -import { - Caption, - FormGroup, - Switch, - View, - TextInput, -} from '../../index'; -export class FormComponents extends Component { +export class FormComponents extends PureComponent { constructor() { super(); this.state = { diff --git a/examples/components/Headers.js b/examples/components/Headers.js index 79001f21..735062c4 100644 --- a/examples/components/Headers.js +++ b/examples/components/Headers.js @@ -1,18 +1,12 @@ import React from 'react'; +import { Text, Caption, Subtitle, Title, Heading } from '../../components/Text'; +import { Icon } from '../../components/Icon'; +import { View } from '../../components/View'; +import { Tile } from '../../components/Tile'; +import { Button } from '../../components/Button'; +import { Overlay } from '../../components/Overlay'; import { Stage } from './Stage'; -import { - Heading, - View, - Tile, - Text, - Title, - Subtitle, - Caption, - Icon, - Overlay, - Button, -} from '../../index'; export function Headers() { return ( diff --git a/examples/components/HorizontalPagers.js b/examples/components/HorizontalPagers.js index 960964b3..a473611e 100644 --- a/examples/components/HorizontalPagers.js +++ b/examples/components/HorizontalPagers.js @@ -1,15 +1,12 @@ import React from 'react'; import { Dimensions } from 'react-native'; +import { Caption, Subtitle } from '../../Text'; +import { View } from '../../components/View'; +import { HorizontalPager } from '../../components/HorizontalPager'; +import { Tile } from '../../Tile'; +import { ImageBackground } from '../../ImageBackground'; import { Stage } from './Stage'; -import { - View, - HorizontalPager, - Tile, - ImageBackground, - Subtitle, - Caption, -} from '../../index'; const window = Dimensions.get('window'); diff --git a/examples/components/ImageGalleries.js b/examples/components/ImageGalleries.js index ed2ae343..a582edc1 100644 --- a/examples/components/ImageGalleries.js +++ b/examples/components/ImageGalleries.js @@ -1,11 +1,9 @@ import React from 'react'; import { Dimensions } from 'react-native'; +import { View } from '../../components/View'; +import { ImageGallery } from '../../components/ImageGallery'; import { Stage } from './Stage'; -import { - View, - ImageGallery, -} from '../../index'; const window = Dimensions.get('window'); diff --git a/examples/components/Images.js b/examples/components/Images.js index 2b2daa20..677e8dfe 100644 --- a/examples/components/Images.js +++ b/examples/components/Images.js @@ -1,8 +1,8 @@ import React from 'react'; import { View } from '../../components/View'; -import { Stage } from './Stage'; import { Image } from '../../components/Image'; +import { Stage } from './Stage'; export function Images() { return ( diff --git a/examples/components/InlineGalleries.js b/examples/components/InlineGalleries.js index 90dc8dbc..5f38b1a2 100644 --- a/examples/components/InlineGalleries.js +++ b/examples/components/InlineGalleries.js @@ -1,11 +1,9 @@ import React from 'react'; import { Dimensions } from 'react-native'; +import { View } from '../../components/View'; +import { InlineGallery } from '../../components/InlineGallery'; import { Stage } from './Stage'; -import { - View, - InlineGallery, -} from '../../index'; const window = Dimensions.get('window'); diff --git a/examples/components/NavigationBars.js b/examples/components/NavigationBars.js index 66e2fab0..d69579d0 100644 --- a/examples/components/NavigationBars.js +++ b/examples/components/NavigationBars.js @@ -1,18 +1,14 @@ import React from 'react'; import { Dimensions } from 'react-native'; +import { Text, Title, Heading } from '../../components/Text'; import { View } from '../../components/View'; +import { Icon } from '../../components/Icon'; +import { Button } from '../../components/Button'; +import { DropDownMenu } from '../../components/DropDownMenu'; +import { NavigationBar } from '../../components/NavigationBar'; +import { ImageBackground } from '../../components/ImageBackground'; import { Stage } from './Stage'; -import { - Heading, - NavigationBar, - Title, - Text, - ImageBackground, - Button, - Icon, - DropDownMenu, -} from '../../index'; const window = Dimensions.get('window'); diff --git a/examples/components/Rows.js b/examples/components/Rows.js index e541fa0e..7cb247e6 100644 --- a/examples/components/Rows.js +++ b/examples/components/Rows.js @@ -1,16 +1,12 @@ import React from 'react'; +import { Text, Caption, Subtitle } from '../../components/Text'; +import { Row } from '../../components/Row'; +import { Icon } from '../../components/Icon'; +import { View } from '../../components/View'; +import { Image } from '../../components/Image'; +import { Button } from '../../components/Button'; import { Stage } from './Stage'; -import { - View, - Row, - Text, - Subtitle, - Caption, - Image, - Button, - Icon, -} from '../../index'; export function Rows() { return ( diff --git a/examples/components/Spinners.js b/examples/components/Spinners.js index 73bc7572..7610bbcd 100644 --- a/examples/components/Spinners.js +++ b/examples/components/Spinners.js @@ -1,10 +1,8 @@ import React from 'react'; +import { View } from '../../components/View'; +import { Spinner } from '../../components/Spinner'; import { Stage } from './Stage'; -import { - View, - Spinner, -} from '../../index'; export function Spinners() { return ( diff --git a/examples/components/Tiles.js b/examples/components/Tiles.js index 2af6de29..7f48685a 100644 --- a/examples/components/Tiles.js +++ b/examples/components/Tiles.js @@ -1,20 +1,14 @@ import React from 'react'; +import { Text, Caption, Subtitle, Title, Heading } from '../../components/Text'; +import { Icon } from '../../components/Icon'; +import { Tile } from '../../components/Tile'; +import { View } from '../../components/View'; +import { Image } from '../../components/Image'; +import { Button } from '../../components/Button'; +import { Overlay } from '../../components/Overlay'; +import { ImageBackground } from '../../components/ImageBackground'; import { Stage } from './Stage'; -import { - Heading, - View, - Tile, - Image, - ImageBackground, - Text, - Title, - Subtitle, - Caption, - Icon, - Overlay, - Button, -} from '../../index'; export function Tiles() { return ( diff --git a/examples/components/Typography.js b/examples/components/Typography.js index f371df52..9d34db93 100644 --- a/examples/components/Typography.js +++ b/examples/components/Typography.js @@ -1,15 +1,8 @@ import React from 'react'; +import { Text, Caption, Subtitle, Title, Heading } from '../../components/Text'; import { View } from '../../components/View'; import { Stage } from './Stage'; -import { - Text, - Heading, - Title, - Subtitle, - Description, - Caption, -} from '../../components/Text'; export function Typography() { return ( diff --git a/examples/components/Videos.js b/examples/components/Videos.js index 9e090549..1baf2e06 100644 --- a/examples/components/Videos.js +++ b/examples/components/Videos.js @@ -1,10 +1,10 @@ import React from 'react'; +import { WebView } from 'react-native'; import { View } from '../../components/View'; -import { Stage } from './Stage'; import { Image } from '../../components/Image'; import { Video } from '../../components/Video' -import { WebView } from 'react-native'; +import { Stage } from './Stage'; export function Videos() { return ( diff --git a/examples/create-react-native-app/App.js b/examples/create-react-native-app/App.js index 1012e6f4..1164987f 100644 --- a/examples/create-react-native-app/App.js +++ b/examples/create-react-native-app/App.js @@ -1,9 +1,9 @@ -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { StatusBar } from 'react-native'; import { Font, AppLoading } from 'expo'; import { View, Examples } from '@shoutem/ui'; -export default class App extends React.Component { +export default class App extends PureComponent { state = { fontsAreLoaded: false, }; diff --git a/helpers/device-selector.js b/helpers/device-selector.js index 801aeaa4..0c111194 100644 --- a/helpers/device-selector.js +++ b/helpers/device-selector.js @@ -1,16 +1,24 @@ import { Platform, Dimensions } from 'react-native'; import _ from 'lodash'; -import { IPHONE_X_LONG_SIDE } from '../const'; +import { + IPHONE_X_LONG_SIDE, + IPHONE_XR_LONG_SIDE, +} from '../const'; const { OS, isPad, isTVOS } = Platform; const { width, height } = Dimensions.get('window'); -const dimensionsMatch = ( +const xDimensionsMatch = ( (height === IPHONE_X_LONG_SIDE) || (width === IPHONE_X_LONG_SIDE) ); -const isIphoneX = (OS === 'ios' && !isPad && !isTVOS && dimensionsMatch); +const xrDimensionsMatch = ( + (height === IPHONE_XR_LONG_SIDE) || (width === IPHONE_XR_LONG_SIDE) +); + +const isIphoneX = (OS === 'ios' && !isPad && !isTVOS && xDimensionsMatch); +const isIphoneXR = (OS === 'ios' && !isPad && !isTVOS && xrDimensionsMatch); /** * Receives settings for different devices @@ -26,10 +34,15 @@ function select(settings) { return settings.iPhoneX; } + if (settings.iPhoneXR && isIphoneXR) { + return settings.iPhoneXR; + } + return _.get(settings, 'default'); } export const Device = { isIphoneX, + isIphoneXR, select, } diff --git a/html/Html.js b/html/Html.js index 293806d6..3b104020 100644 --- a/html/Html.js +++ b/html/Html.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { Platform, InteractionManager } from 'react-native'; import _ from 'lodash'; @@ -19,7 +19,7 @@ const defaultElementSettings = { display: Display.BLOCK, }; -class Html extends Component { +class Html extends PureComponent { static propTypes = { body: PropTypes.string.isRequired, renderElement: PropTypes.func, diff --git a/html/components/Gallery.js b/html/components/Gallery.js index 8d777275..1bb6706b 100644 --- a/html/components/Gallery.js +++ b/html/components/Gallery.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { InlineGallery } from '../../components/InlineGallery'; @@ -7,7 +7,7 @@ import { InlineGallery } from '../../components/InlineGallery'; * Use to render a HTML gallery component. * Style interface correspond to InlineGallery from @shoutem/ui. */ -export default class Gallery extends Component { +export default class Gallery extends PureComponent { static propTypes = { ...InlineGallery.propTypes, handlePhotoPress: PropTypes.func, diff --git a/html/components/Image.js b/html/components/Image.js index 3245b6c6..a1c7d8bf 100644 --- a/html/components/Image.js +++ b/html/components/Image.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { PureComponent } from 'react'; import { Image as RNImage } from 'react-native'; import { connectStyle } from '@shoutem/theme'; import _ from 'lodash'; @@ -14,7 +14,7 @@ import { Lightbox } from '../../components/Lightbox'; * Image is not going to be shown before dimensions are determined, * this component will determine the Image dimensions before rendering an image. */ -export default class HtmlImage extends Component { +export default class HtmlImage extends PureComponent { static propTypes = { ...RNImage.propTypes, lightbox: PropTypes.bool, diff --git a/html/components/SimpleHtml.js b/html/components/SimpleHtml.js new file mode 100644 index 00000000..abf46287 --- /dev/null +++ b/html/components/SimpleHtml.js @@ -0,0 +1,128 @@ +import PropTypes from 'prop-types'; +import { Dimensions, Linking } from 'react-native'; +import React, { PureComponent } from 'react'; +import _ from 'lodash'; +import HTML from 'react-native-render-html'; + +import { + cssStringToObject, + cssObjectToString, +} from 'react-native-render-html/src/HTMLStyles'; + +import { connectStyle } from '@shoutem/theme'; +import { View } from '../../components/View'; +import { Text } from '../../components/Text'; + +import getEmptyObjectKeys from '../services/getEmptyObjectKeys'; + +class SimpleHtml extends PureComponent { + static propTypes = { + body: PropTypes.string, + style: PropTypes.object, + customTagStyles: PropTypes.object, + customHandleLinkPress: PropTypes.func, + }; + + constructor(props) { + super(props); + + this.onLinkPress = this.onLinkPress.bind(this); + this.alterNode = this.alterNode.bind(this); + this.renderUnorderedListPrefix = this.renderUnorderedListPrefix.bind(this); + this.renderOrderedListPrefix = this.renderOrderedListPrefix.bind(this); + } + + onLinkPress(evt, href) { + const { customHandleLinkPress } = this.props; + + return customHandleLinkPress ? customHandleLinkPress(href) : Linking.openURL(href); + } + + /** + * Removes empty (therefore invalid) style attribute properties + * Scales down objects with specified width and height if too large + */ + alterNode(node) { + const { style } = this.props; + + const styleAttrib = _.get(node, 'attribs.style', '').trim(); + const nodeWidth = _.get(node, 'attribs.width', false); + + if (!styleAttrib && !nodeWidth) { + return false; + } + + const paddingValue = style.container.paddingLeft * 2; + const maxWidth = Dimensions.get('window').width - paddingValue; + const nodeHeight = _.get(node, 'attribus.height'); + const nodeRatio = nodeWidth/nodeHeight; + const resolvedWidth = (nodeWidth > maxWidth) ? maxWidth : nodeWidth; + const resolvedHeight = Math.round(resolvedWidth*nodeRatio); + + const nodeStyle = cssStringToObject(styleAttrib); + const invalidKeys = getEmptyObjectKeys(nodeStyle); + + if (invalidKeys.length || nodeWidth) { + const styleFiltered = _.omit(style, invalidKeys); + node.attribs.style = cssObjectToString(styleFiltered); + node.attribs.width = resolvedWidth; + node.attribs.height = resolvedHeight; + + return node; + } + + return false; + } + + renderUnorderedListPrefix(htmlAttribs, children, convertedCSSStyles, passProps) { + const { style } = this.props; + + return ( + + ); + } + + renderOrderedListPrefix(htmlAttribs, children, convertedCSSStyles, passProps) { + const { style } = this.props; + + return ( + {passProps.index + 1}. + ); + } + + render() { + const { style, body, customTagStyles } = this.props; + + const paddingValue = style.container.paddingLeft * 2; + const maxWidth = Dimensions.get('window').width - paddingValue; + + const tagStyles = { + ...style.tags, + ...customTagStyles, + } + + const listPrefixRenderers = { + ul: this.renderUnorderedListPrefix, + ol: this.renderOrderedListPrefix, + } + + const htmlProps = { + html: body, + imagesMaxWidth: maxWidth, + staticContentMaxWidth: maxWidth, + tagsStyles: tagStyles, + ignoredStyles: ['font-family', 'letter-spacing', 'transform'], + onLinkPress: this.onLinkPress, + alterNode: this.alterNode, + listsPrefixesRenderers: listPrefixRenderers, + }; + + return ( + + + + ); + } +} + +export default connectStyle('shoutem.ui.SimpleHtml')(SimpleHtml); diff --git a/html/elements/A.js b/html/elements/A.js index 7f3db5be..e63c0678 100644 --- a/html/elements/A.js +++ b/html/elements/A.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { PureComponent } from 'react'; import { Linking } from 'react-native'; import { connectStyle } from '@shoutem/theme'; import _ from 'lodash'; @@ -8,7 +8,7 @@ import { ElementPropTypes, combineMappers, mapElementProps } from '../Html'; import { isImg } from '../elements/Img'; import { Inline } from './Inline'; -class A extends React.Component { +class A extends PureComponent { static propTypes = { ...ElementPropTypes, handleLinkPress: PropTypes.func, diff --git a/html/elements/Inline.js b/html/elements/Inline.js index ddd8db27..05fa3e6d 100644 --- a/html/elements/Inline.js +++ b/html/elements/Inline.js @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { PureComponent } from 'react'; import _ from 'lodash'; import { View } from '../../components/View'; @@ -129,7 +129,7 @@ function renderGroupedChildren(groupedChildren, renderElement, style) { * @returns {component} * @constructor */ -export class Inline extends React.Component { +export class Inline extends PureComponent { static defaultProps = { style: {}, }; diff --git a/html/index.js b/html/index.js index efa43ae6..6f23697a 100644 --- a/html/index.js +++ b/html/index.js @@ -14,6 +14,7 @@ import Html, { renderChildElements, renderChildren, } from './Html'; +import SimpleHtml from './components/SimpleHtml'; import Gallery from './components/Gallery'; import Image from './components/Image'; import Inline, { InlineSettings } from './elements/Inline'; @@ -97,4 +98,5 @@ export { // Components Gallery, Image, + SimpleHtml, }; diff --git a/html/services/getEmptyObjectKeys.js b/html/services/getEmptyObjectKeys.js new file mode 100644 index 00000000..abf0e42f --- /dev/null +++ b/html/services/getEmptyObjectKeys.js @@ -0,0 +1,11 @@ +export default function getEmptyObjectKeys(obj) { + return Object.keys(obj).reduce((arr, key) => { + const val = String(obj[key]).trim(); + + if (!val) { + arr.push(key); + } + + return arr; + }, []); +} diff --git a/index.js b/index.js index eecdaf4d..5e7d73d3 100644 --- a/index.js +++ b/index.js @@ -43,6 +43,7 @@ export { LoadingIndicator } from './components/LoadingIndicator'; export { PageIndicators } from './components/PageIndicators'; export { default as RichMedia } from './components/RichMedia'; export { Html } from './html'; +export { SimpleHtml } from './html'; export { ShareButton } from './components/ShareButton'; export { LinearGradient } from './components/LinearGradient'; @@ -70,6 +71,8 @@ export { Device } from './helpers'; export { IPHONE_X_HOME_INDICATOR_PADDING, IPHONE_X_NOTCH_PADDING, + IPHONE_XR_NOTCH_PADDING, NAVIGATION_HEADER_HEIGHT, IPHONE_X_LONG_SIDE, + IPHONE_XR_LONG_SIDE, } from './const'; diff --git a/navigation/NavigationBar.js b/navigation/NavigationBar.js index 3a48eb1f..ad43e076 100644 --- a/navigation/NavigationBar.js +++ b/navigation/NavigationBar.js @@ -49,6 +49,10 @@ class NavigationBar extends Component { this.setNavBarProps(nextProps); } + componentDidMount() { + console.warn("NavigationBar from '@shoutem/ui/navigation' is deprecated and will soon be removed. Use NavigationBar from '@shoutem/ui' instead."); + } + componentWillUnmount() { // The parent screen is being unmounted, we can cleanup now const { getScene, clearNavBarProps } = this.context; diff --git a/navigation/NavigationBarView.js b/navigation/NavigationBarView.js index 4cb8a632..0ea16123 100644 --- a/navigation/NavigationBarView.js +++ b/navigation/NavigationBarView.js @@ -173,7 +173,7 @@ class NavigationBarView extends PureComponent { } setStatusBarStyleIos(statusBarColor, backgroundColor, hasImage) { - if (isAnimatedStyleValue(backgroundColor) && !Device.isIphoneX) { + if (isAnimatedStyleValue(backgroundColor) && !Device.isIphoneX && !Device.isIphoneXR) { // If the backgroundColor is animated, we want to listen for // color changes, so that we can update the bar style as the // animation runs. diff --git a/navigation/children-composers/navbar-image.composer.js b/navigation/children-composers/navbar-image.composer.js index 8d6151ba..5d055019 100644 --- a/navigation/children-composers/navbar-image.composer.js +++ b/navigation/children-composers/navbar-image.composer.js @@ -2,7 +2,6 @@ import React from 'react'; import _ from 'lodash'; import { NavigationBar } from '../NavigationBar'; import { View, Image, Device } from '../../index'; -import { IPHONE_X_NOTCH_PADDING } from '../../const'; const imageFitContainer = navBarProps => (NavigationBar.fitContainer || navBarProps.fitContainer); @@ -29,7 +28,7 @@ const createNavBarBackgroundImage = navBarProps => () => { const navigationBarImage = (NavigationBar.globalNavigationBarImage || navBarProps.navigationBarImage); const statusBarColor = _.get(navBarProps, 'style.statusBar.backgroundColor', '#000'); - const statusBarHeight = _.get(navBarProps, 'style.statusBar.height', IPHONE_X_NOTCH_PADDING); + const statusBarHeight = _.get(navBarProps, 'style.statusBar.height'); const backgroundImage = ( ({ listContent: { paddingBottom: Device.select({ iPhoneX: IPHONE_X_HOMEBAR_SCROLL_PADDING, + iPhoneXR: IPHONE_X_HOMEBAR_SCROLL_PADDING, default: 0, }) }, @@ -1422,7 +1425,11 @@ export default (variables = defaultThemeVariables) => ({ statusBar: { backgroundColor: variables.statusBarColor, - height: IPHONE_X_NOTCH_PADDING, + height: Device.select({ + iPhoneX: IPHONE_X_NOTCH_PADDING, + iPhoneXR: IPHONE_XR_NOTCH_PADDING, + default: 0, + }), }, container: { [INCLUDE]: ['fillParent'], @@ -1552,6 +1559,7 @@ export default (variables = defaultThemeVariables) => ({ position: 'absolute', top: Device.select({ iPhoneX: 6, + iPhoneXR: 8, default: Platform.OS === 'android' ? 0 : -4, }), left: 0, @@ -1560,12 +1568,17 @@ export default (variables = defaultThemeVariables) => ({ }, statusBar: { backgroundColor: variables.statusBarColor, - height: IPHONE_X_NOTCH_PADDING, + height: Device.select({ + iPhoneX: IPHONE_X_NOTCH_PADDING, + iPhoneXR: IPHONE_XR_NOTCH_PADDING, + default: 0, + }), }, screenBackground: variables.backgroundColor, navigationBarImage: { marginTop: Device.select({ iPhoneX: IPHONE_X_NOTCH_PADDING, + iPhoneXR: IPHONE_XR_NOTCH_PADDING, default: 0, }), flex: 1, @@ -1977,6 +1990,11 @@ export default (variables = defaultThemeVariables) => ({ text: { }, }, + 'shoutem.ui.SimpleHtml': { + container: { + padding: variables.mediumGutter, + }, + }, 'shoutem.ui.Html': { container: { backgroundColor: variables.paperColor, @@ -2458,6 +2476,10 @@ export default (variables = defaultThemeVariables) => ({ }, 'shoutem.ui.Lightbox': { + '.container': { + flex: 1, + resizeMode: 'contain', + }, 'shoutem.ui.Image': { '.preview': { flex: 1,