diff --git a/src/components/PDFView/index.js b/src/components/PDFView/index.js
index e18c52b06972..a56f85f73450 100644
--- a/src/components/PDFView/index.js
+++ b/src/components/PDFView/index.js
@@ -9,10 +9,15 @@ import _ from 'underscore';
import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import Text from '@components/Text';
-import withLocalize from '@components/withLocalize';
+import usePrevious from '@hooks/usePrevious';
+import useLocalize from '@components/useLocalize';
+import useKeyboardState from '@hooks/useKeyboardState';
+import useWindowDimensions from '@hooks/useWindowDimensions';
+import compose from '@libs/compose';
+import useStyleUtils from '@styles/useStyleUtils';
+import useThemeStyles from '@styles/useThemeStyles';
import withThemeStyles from '@components/withThemeStyles';
import withWindowDimensions from '@components/withWindowDimensions';
-import compose from '@libs/compose';
import Log from '@libs/Log';
import variables from '@styles/variables';
import * as CanvasSize from '@userActions/CanvasSize';
@@ -32,46 +37,48 @@ const PAGE_BORDER = 9;
*/
const LARGE_SCREEN_SIDE_SPACING = 40;
-class PDFView extends Component {
- constructor(props) {
- super(props);
- this.state = {
- numPages: null,
- pageViewports: [],
- containerWidth: props.windowWidth,
- containerHeight: props.windowHeight,
- shouldRequestPassword: false,
- isPasswordInvalid: false,
- isKeyboardOpen: false,
- };
- this.onDocumentLoadSuccess = this.onDocumentLoadSuccess.bind(this);
- this.initiatePasswordChallenge = this.initiatePasswordChallenge.bind(this);
- this.attemptPDFLoad = this.attemptPDFLoad.bind(this);
- this.toggleKeyboardOnSmallScreens = this.toggleKeyboardOnSmallScreens.bind(this);
- this.calculatePageHeight = this.calculatePageHeight.bind(this);
- this.calculatePageWidth = this.calculatePageWidth.bind(this);
- this.renderPage = this.renderPage.bind(this);
- this.getDevicePixelRatio = _.memoize(this.getDevicePixelRatio);
- this.setListAttributes = this.setListAttributes.bind(this);
+function PDFView(props) {
+ const {windowWidth, windowHeight, isSmallScreenWidth} = useWindowDimensions();
+ const {translate} = useLocalize();
+ const styles = useThemeStyles();
+ const isKeyboardShown = useKeyboardState();
+ const StyleUtils = useStyleUtils();
+ const [numPages, setNumPages] = null;
+ const [pageViewports, setPageViewports] = [];
+ const [containerWidth, setContainerWidth] = windowWidth;
+ const [containerHeight, setContainerHeight] = windowHeight;
+ const [shouldRequestPassword, setShouldRequestPassword] = false;
+ const [isPasswordInvalid, setIsPasswordInvalid] = false;
+ const [isKeyboardOpen, setIsKeyboardOpen] = false;
+ let onPasswordCallback;
- const workerBlob = new Blob([pdfWorkerSource], {type: 'text/javascript'});
- pdfjs.GlobalWorkerOptions.workerSrc = URL.createObjectURL(workerBlob);
- this.retrieveCanvasLimits();
+ /**
+ * On small screens notify parent that the keyboard has opened or closed.
+ *
+ * @param {Boolean} keyboardState True if keyboard is open
+ */
+ function toggleKeyboardOnSmallScreens(keyboardState) {
+ if (isSmallScreenWidth) {
+ return;
+ }
+ setIsKeyboardOpen(keyboardState);
+ onToggleKeyboard(keyboardState);
}
- componentDidUpdate(prevProps) {
+ useEffect(() => {
+
// Use window height changes to toggle the keyboard. To maintain keyboard state
// on all platforms we also use focus/blur events. So we need to make sure here
// that we avoid redundant keyboard toggling.
// Minus 100px is needed to make sure that when the internet connection is
// disabled in android chrome and a small 'No internet connection' text box appears,
// we do not take it as a sign to open the keyboard
- if (!this.state.isKeyboardOpen && this.props.windowHeight < prevProps.windowHeight - 100) {
- this.toggleKeyboardOnSmallScreens(true);
- } else if (this.state.isKeyboardOpen && this.props.windowHeight > prevProps.windowHeight) {
- this.toggleKeyboardOnSmallScreens(false);
+ if (!isKeyboardOpen && windowHeight < usePrevious(windowHeight) - 100) {
+ toggleKeyboardOnSmallScreens(true);
+ } else if (isKeyboardOpen && windowHeight > usePrevious(windowHeight)) {
+ toggleKeyboardOnSmallScreens(false);
}
- }
+ });
/**
* Upon successful document load, combine an array of page viewports,
@@ -84,22 +91,20 @@ class PDFView extends Component {
* @param {Function} pdf.getPage - A method to get page by its number. It requires to have the context. It should be the pdf itself.
* @memberof PDFView
*/
- onDocumentLoadSuccess(pdf) {
- const {numPages} = pdf;
+ function onDocumentLoadSuccess(pdf) {
+ const {numberOfPages} = pdf;
Promise.all(
- _.times(numPages, (index) => {
+ _.times(numberOfPages, (index) => {
const pageNumber = index + 1;
return pdf.getPage(pageNumber).then((page) => page.getViewport({scale: 1}));
}),
- ).then((pageViewports) => {
- this.setState({
- pageViewports,
- numPages,
- shouldRequestPassword: false,
- isPasswordInvalid: false,
- });
+ ).then((pgViewports) => {
+ setPageViewports(pgViewports);
+ setNumPages(numberOfPages);
+ setShouldRequestPassword(false);
+ setIsPasswordInvalid(false);
});
}
@@ -108,7 +113,7 @@ class PDFView extends Component {
* It unblocks a default scroll by keyboard of browsers.
* @param {Object|undefined} ref
*/
- setListAttributes(ref) {
+ function setListAttributes(ref) {
if (!ref) {
return;
}
@@ -127,15 +132,28 @@ class PDFView extends Component {
* @param {Number} height of the page
* @returns {Number} devicePixelRatio for this page on this platform
*/
- getDevicePixelRatio(width, height) {
+ function getDevicePixelRatio(width, height) {
const nbPixels = width * height;
- const ratioHeight = this.props.maxCanvasHeight / height;
- const ratioWidth = this.props.maxCanvasWidth / width;
- const ratioArea = Math.sqrt(this.props.maxCanvasArea / nbPixels);
+ const ratioHeight = props.maxCanvasHeight / height;
+ const ratioWidth = props.maxCanvasWidth / width;
+ const ratioArea = Math.sqrt(props.maxCanvasArea / nbPixels);
const ratio = Math.min(ratioHeight, ratioArea, ratioWidth);
return ratio > window.devicePixelRatio ? undefined : ratio;
}
+ /**
+ * Calculates a proper page width.
+ * It depends on a screen size. Also, the app should take into account the page borders.
+ * @returns {Number}
+ */
+ function calculatePageWidth() {
+ const pdfContainerWidth = containerWidth;
+ const pageWidthOnLargeScreen = Math.min(pdfContainerWidth - LARGE_SCREEN_SIDE_SPACING * 2, variables.pdfPageMaxWidth);
+ const pageWidth = isSmallScreenWidth ? containerWidth : pageWidthOnLargeScreen;
+
+ return pageWidth + PAGE_BORDER * 2;
+ }
+
/**
* Calculates a proper page height. The method should be called only when there are page viewports.
* It is based on a ratio between the specific page viewport width and provided page width.
@@ -143,34 +161,21 @@ class PDFView extends Component {
* @param {Number} pageIndex
* @returns {Number}
*/
- calculatePageHeight(pageIndex) {
- if (this.state.pageViewports.length === 0) {
+ function calculatePageHeight(pageIndex) {
+ if (pageViewports.length === 0) {
Log.warn('Dev error: calculatePageHeight() in PDFView called too early');
return 0;
}
- const pageViewport = this.state.pageViewports[pageIndex];
- const pageWidth = this.calculatePageWidth();
+ const pageViewport = pageViewports[pageIndex];
+ const pageWidth = calculatePageWidth();
const scale = pageWidth / pageViewport.width;
const actualHeight = pageViewport.height * scale + PAGE_BORDER * 2;
return actualHeight;
}
- /**
- * Calculates a proper page width.
- * It depends on a screen size. Also, the app should take into account the page borders.
- * @returns {Number}
- */
- calculatePageWidth() {
- const pdfContainerWidth = this.state.containerWidth;
- const pageWidthOnLargeScreen = Math.min(pdfContainerWidth - LARGE_SCREEN_SIDE_SPACING * 2, variables.pdfPageMaxWidth);
- const pageWidth = this.props.isSmallScreenWidth ? this.state.containerWidth : pageWidthOnLargeScreen;
-
- return pageWidth + PAGE_BORDER * 2;
- }
-
/**
* Initiate password challenge process. The react-pdf/Document
* component calls this handler to indicate that a PDF requires a
@@ -183,13 +188,14 @@ class PDFView extends Component {
* @param {Function} callback Callback used to send password to react-pdf
* @param {Number} reason Reason code for password request
*/
- initiatePasswordChallenge(callback, reason) {
- this.onPasswordCallback = callback;
+ function initiatePasswordChallenge(callback, reason) {
+ onPasswordCallback = callback;
if (reason === CONST.PDF_PASSWORD_FORM.REACT_PDF_PASSWORD_RESPONSES.NEED_PASSWORD) {
- this.setState({shouldRequestPassword: true});
+ setShouldRequestPassword(true);
} else if (reason === CONST.PDF_PASSWORD_FORM.REACT_PDF_PASSWORD_RESPONSES.INCORRECT_PASSWORD) {
- this.setState({shouldRequestPassword: true, isPasswordInvalid: true});
+ setShouldRequestPassword(true);
+ setIsPasswordInvalid(true);
}
}
@@ -199,40 +205,31 @@ class PDFView extends Component {
*
* @param {String} password Password to send via callback to react-pdf
*/
- attemptPDFLoad(password) {
- this.onPasswordCallback(password);
- }
-
- /**
- * On small screens notify parent that the keyboard has opened or closed.
- *
- * @param {Boolean} isKeyboardOpen True if keyboard is open
- */
- toggleKeyboardOnSmallScreens(isKeyboardOpen) {
- if (!this.props.isSmallScreenWidth) {
- return;
- }
- this.setState({isKeyboardOpen});
- this.props.onToggleKeyboard(isKeyboardOpen);
+ function attemptPDFLoad(password) {
+ onPasswordCallback(password);
}
/**
* Verify that the canvas limits have been calculated already, if not calculate them and put them in Onyx
*/
- retrieveCanvasLimits() {
- if (!this.props.maxCanvasArea) {
+ function retrieveCanvasLimits() {
+ if (!props.maxCanvasArea) {
CanvasSize.retrieveMaxCanvasArea();
}
- if (!this.props.maxCanvasHeight) {
+ if (!props.maxCanvasHeight) {
CanvasSize.retrieveMaxCanvasHeight();
}
- if (!this.props.maxCanvasWidth) {
+ if (!props.maxCanvasWidth) {
CanvasSize.retrieveMaxCanvasWidth();
}
}
+ const workerBlob = new Blob([pdfWorkerSource], {type: 'text/javascript'});
+ pdfjs.GlobalWorkerOptions.workerSrc = URL.createObjectURL(workerBlob);
+ retrieveCanvasLimits();
+
/**
* Render a specific page based on its index.
* The method includes a wrapper to apply virtualized styles.
@@ -241,10 +238,10 @@ class PDFView extends Component {
* @param {Object} page.style virtualized styles
* @returns {JSX.Element}
*/
- renderPage({index, style}) {
- const pageWidth = this.calculatePageWidth();
- const pageHeight = this.calculatePageHeight(index);
- const devicePixelRatio = this.getDevicePixelRatio(pageWidth, pageHeight);
+ function renderPage({index, style}) {
+ const pageWidth = calculatePageWidth();
+ const pageHeight = calculatePageHeight(index);
+ const devicePixelRatio = getDevicePixelRatio(pageWidth, pageHeight);
return (
@@ -262,15 +259,14 @@ class PDFView extends Component {
}
renderPDFView() {
- const styles = this.props.themeStyles;
const pageWidth = this.calculatePageWidth();
const outerContainerStyle = [styles.w100, styles.h100, styles.justifyContentCenter, styles.alignItemsCenter];
// If we're requesting a password then we need to hide - but still render -
// the PDF component.
- const pdfContainerStyle = this.state.shouldRequestPassword
- ? [styles.PDFView, styles.noSelect, this.props.style, styles.invisible]
- : [styles.PDFView, styles.noSelect, this.props.style];
+ const pdfContainerStyle = shouldRequestPassword
+ ? [styles.PDFView, styles.noSelect, props.style, styles.invisible]
+ : [styles.PDFView, styles.noSelect, props.style];
return (
@@ -281,42 +277,46 @@ class PDFView extends Component {
nativeEvent: {
layout: {width, height},
},
- }) => this.setState({containerWidth: width, containerHeight: height})}
+ }) => {
+ setContainerWidth(width);
+ setContainerHeight(height);
+ }
+ }
>
{this.props.translate('attachmentView.failedToLoadPDF')}}
+ error={{translate('attachmentView.failedToLoadPDF')}}
loading={}
- file={this.props.sourceURL}
+ file={props.sourceURL}
options={{
cMapUrl: 'cmaps/',
cMapPacked: true,
}}
externalLinkTarget="_blank"
- onLoadSuccess={this.onDocumentLoadSuccess}
- onPassword={this.initiatePasswordChallenge}
+ onLoadSuccess={() => onDocumentLoadSuccess}
+ onPassword={() => initiatePasswordChallenge}
>
- {this.state.pageViewports.length > 0 && (
+ {pageViewports.length > 0 && (
setListAttributes}
style={styles.PDFViewList}
- width={this.props.isSmallScreenWidth ? pageWidth : this.state.containerWidth}
- height={this.state.containerHeight}
- estimatedItemSize={this.calculatePageHeight(0)}
- itemCount={this.state.numPages}
- itemSize={this.calculatePageHeight}
+ width={isSmallScreenWidth ? pageWidth : containerWidth}
+ height={containerHeight}
+ estimatedItemSize={calculatePageHeight(0)}
+ itemCount={() => numPages}
+ itemSize={() => calculatePageHeight}
>
- {this.renderPage}
+ {renderPage}
)}
- {this.state.shouldRequestPassword && (
+ {shouldRequestPassword && (
this.setState({isPasswordInvalid: false})}
- isPasswordInvalid={this.state.isPasswordInvalid}
- onPasswordFieldFocused={this.toggleKeyboardOnSmallScreens}
+ isFocused={props.isFocused}
+ onSubmit={() => attemptPDFLoad}
+ onPasswordUpdated={() => setIsPasswordInvalid(false)}
+ isPasswordInvalid={isPasswordInvalid}
+ onPasswordFieldFocused={() => toggleKeyboardOnSmallScreens}
/>
)}
@@ -324,18 +324,18 @@ class PDFView extends Component {
}
render() {
- const styles = this.props.themeStyles;
- return this.props.onPress ? (
+ const styles = props.themeStyles;
+ return props.onPress ? (
- {this.renderPDFView()}
+ {renderPDFView()}
) : (
- this.renderPDFView()
+ renderPDFView()
);
}
}
@@ -344,9 +344,6 @@ PDFView.propTypes = pdfViewPropTypes.propTypes;
PDFView.defaultProps = pdfViewPropTypes.defaultProps;
export default compose(
- withLocalize,
- withWindowDimensions,
- withThemeStyles,
withOnyx({
maxCanvasArea: {
key: ONYXKEYS.MAX_CANVAS_AREA,