From d78c28f2f3cc6a70b70fe0d58f294bdd29970c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Henriques?= Date: Thu, 7 Dec 2023 16:41:14 +0000 Subject: [PATCH] Migrate QRShare to function component --- .../QRShare/QRShareWithDownload/index.js | 78 +++++----- .../QRShareWithDownload/index.native.js | 59 ++++---- src/components/QRShare/index.js | 139 +++++++++--------- src/components/QRShare/propTypes.js | 14 +- 4 files changed, 157 insertions(+), 133 deletions(-) diff --git a/src/components/QRShare/QRShareWithDownload/index.js b/src/components/QRShare/QRShareWithDownload/index.js index 688ec15040ac..bf18a8eedaa4 100644 --- a/src/components/QRShare/QRShareWithDownload/index.js +++ b/src/components/QRShare/QRShareWithDownload/index.js @@ -1,43 +1,53 @@ -import React, {Component} from 'react'; -import {withNetwork} from '@components/OnyxProvider'; +import React, {forwardRef, useImperativeHandle, useRef} from 'react'; import getQrCodeFileName from '@components/QRShare/getQrCodeDownloadFileName'; import {qrShareDefaultProps, qrSharePropTypes} from '@components/QRShare/propTypes'; +import useNetwork from '@hooks/useNetwork'; import fileDownload from '@libs/fileDownload'; import QRShare from '..'; -class QRShareWithDownload extends Component { - qrShareRef = React.createRef(); - - constructor(props) { - super(props); - - this.download = this.download.bind(this); - } - - download() { - return new Promise((resolve, reject) => { - // eslint-disable-next-line es/no-optional-chaining - const svg = this.qrShareRef.current?.getSvg(); - if (svg == null) { - return reject(); - } - - svg.toDataURL((dataURL) => resolve(fileDownload(dataURL, getQrCodeFileName(this.props.title)))); - }); - } - - render() { - return ( - - ); - } +function QRShareWithDownload({innerRef, ...props}) { + const {isOffline} = useNetwork(); + const qrShareRef = useRef(null); + + useImperativeHandle( + innerRef, + () => ({ + download: () => + new Promise((resolve, reject) => { + // eslint-disable-next-line es/no-optional-chaining + const svg = qrShareRef.current?.getSvg(); + if (svg == null) { + return reject(); + } + + svg.toDataURL((dataURL) => resolve(fileDownload(dataURL, getQrCodeFileName(props.title)))); + }), + }), + [props.title], + ); + + return ( + + ); } + QRShareWithDownload.propTypes = qrSharePropTypes; QRShareWithDownload.defaultProps = qrShareDefaultProps; +QRShareWithDownload.displayName = 'QRShareWithDownload'; + +const QRShareWithDownloadWithRef = forwardRef((props, ref) => ( + +)); + +QRShareWithDownloadWithRef.displayName = 'QRShareWithDownloadWithRef'; -export default withNetwork()(QRShareWithDownload); +export default QRShareWithDownloadWithRef; diff --git a/src/components/QRShare/QRShareWithDownload/index.native.js b/src/components/QRShare/QRShareWithDownload/index.native.js index bf67b0955812..e64c7b69df4a 100644 --- a/src/components/QRShare/QRShareWithDownload/index.native.js +++ b/src/components/QRShare/QRShareWithDownload/index.native.js @@ -1,37 +1,46 @@ -import React, {Component} from 'react'; +import React, {forwardRef, useImperativeHandle, useRef} from 'react'; import ViewShot from 'react-native-view-shot'; -import {withNetwork} from '@components/OnyxProvider'; import getQrCodeFileName from '@components/QRShare/getQrCodeDownloadFileName'; import {qrShareDefaultProps, qrSharePropTypes} from '@components/QRShare/propTypes'; +import useNetwork from '@hooks/useNetwork'; import fileDownload from '@libs/fileDownload'; import QRShare from '..'; -class QRShareWithDownload extends Component { - qrCodeScreenshotRef = React.createRef(); +function QRShareWithDownload({innerRef, ...props}) { + const {isOffline} = useNetwork(); + const qrCodeScreenshotRef = useRef(null); - constructor(props) { - super(props); + useImperativeHandle( + innerRef, + () => ({ + download: () => qrCodeScreenshotRef.current.capture().then((uri) => fileDownload(uri, getQrCodeFileName(props.title))), + }), + [props.title], + ); - this.download = this.download.bind(this); - } - - download() { - return this.qrCodeScreenshotRef.current.capture().then((uri) => fileDownload(uri, getQrCodeFileName(this.props.title))); - } - - render() { - return ( - - - - ); - } + return ( + + + + ); } + QRShareWithDownload.propTypes = qrSharePropTypes; QRShareWithDownload.defaultProps = qrShareDefaultProps; +QRShareWithDownload.displayName = 'QRShareWithDownload'; + +const QRShareWithDownloadWithRef = forwardRef((props, ref) => ( + +)); + +QRShareWithDownloadWithRef.displayName = 'QRShareWithDownloadWithRef'; -export default withNetwork()(QRShareWithDownload); +export default QRShareWithDownloadWithRef; diff --git a/src/components/QRShare/index.js b/src/components/QRShare/index.js index be242cdb184f..f7bb5cb5f788 100644 --- a/src/components/QRShare/index.js +++ b/src/components/QRShare/index.js @@ -1,96 +1,91 @@ -import React, {Component} from 'react'; +import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; import ExpensifyWordmark from '@assets/images/expensify-wordmark.svg'; import QRCode from '@components/QRCode'; import Text from '@components/Text'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withTheme, {withThemePropTypes} from '@components/withTheme'; -import withThemeStyles, {withThemeStylesPropTypes} from '@components/withThemeStyles'; -import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; -import compose from '@libs/compose'; +import useTheme from '@styles/themes/useTheme'; +import useThemeStyles from '@styles/useThemeStyles'; import variables from '@styles/variables'; import {qrShareDefaultProps, qrSharePropTypes} from './propTypes'; -const propTypes = { - ...qrSharePropTypes, - ...windowDimensionsPropTypes, - ...withLocalizePropTypes, - ...withThemeStylesPropTypes, - ...withThemePropTypes, -}; +function QRShare({innerRef, url, title, subtitle, logo, logoRatio, logoMarginRatio}) { + const styles = useThemeStyles(); + const theme = useTheme(); -class QRShare extends Component { - constructor(props) { - super(props); + const [qrCodeSize, setQrCodeSize] = useState(1); + const svgRef = useRef(null); - this.state = { - qrCodeSize: 1, - }; + useImperativeHandle( + innerRef, + () => ({ + getSvg: () => svgRef.current, + }), + [], + ); - this.onLayout = this.onLayout.bind(this); - this.getSvg = this.getSvg.bind(this); - } - - onLayout(event) { + const onLayout = (event) => { const containerWidth = event.nativeEvent.layout.width - variables.qrShareHorizontalPadding * 2 || 0; + setQrCodeSize(Math.max(1, containerWidth)); + }; - this.setState({ - qrCodeSize: Math.max(1, containerWidth), - }); - } + return ( + + + + - getSvg() { - return this.svg; - } + (svgRef.current = svg)} + url={url} + logo={logo} + size={qrCodeSize} + logoRatio={logoRatio} + logoMarginRatio={logoMarginRatio} + /> - render() { - return ( - - - - - - (this.svg = svg)} - url={this.props.url} - logo={this.props.logo} - size={this.state.qrCodeSize} - logoRatio={this.props.logoRatio} - logoMarginRatio={this.props.logoMarginRatio} - /> + {title} + + {!_.isEmpty(subtitle) && ( - {this.props.title} + {subtitle} - - {!_.isEmpty(this.props.subtitle) && ( - - {this.props.subtitle} - - )} - - ); - } + )} + + ); } -QRShare.propTypes = propTypes; + +QRShare.propTypes = qrSharePropTypes; QRShare.defaultProps = qrShareDefaultProps; +QRShare.displayName = 'QRShare'; + +const QRShareWithRef = forwardRef((props, ref) => ( + +)); + +QRShareWithRef.displayName = 'QRShareWithRef'; -export default compose(withLocalize, withWindowDimensions, withThemeStyles, withTheme)(QRShare); +export default QRShareWithRef; diff --git a/src/components/QRShare/propTypes.js b/src/components/QRShare/propTypes.js index 69a9fcce2938..28275bc724b1 100644 --- a/src/components/QRShare/propTypes.js +++ b/src/components/QRShare/propTypes.js @@ -5,31 +5,41 @@ const qrSharePropTypes = { * The QR code URL */ url: PropTypes.string.isRequired, + /** * The title that is displayed below the QR Code (usually the user or report name) */ title: PropTypes.string.isRequired, + /** * The subtitle which will be shown below the title (usually user email or workspace name) * */ subtitle: PropTypes.string, + /** * The logo which will be display in the middle of the QR code */ logo: PropTypes.oneOfType([PropTypes.shape({uri: PropTypes.string}), PropTypes.number, PropTypes.string]), + /** * The size ratio of logo to QR code */ logoRatio: PropTypes.number, + /** * The size ratio of margin around logo to QR code */ logoMarginRatio: PropTypes.number, + + /** + * Forwarded ref + */ + innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), }; -const defaultProps = { +const qrShareDefaultProps = { subtitle: undefined, logo: undefined, }; -export {qrSharePropTypes, defaultProps as qrShareDefaultProps}; +export {qrSharePropTypes, qrShareDefaultProps};