-
Notifications
You must be signed in to change notification settings - Fork 3k
/
index.native.js
166 lines (152 loc) · 6.45 KB
/
index.native.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import React, {Component} from 'react';
import {TouchableWithoutFeedback, View} from 'react-native';
import PDF from 'react-native-pdf';
import styles from '../../styles/styles';
import * as StyleUtils from '../../styles/StyleUtils';
import FullScreenLoadingIndicator from '../FullscreenLoadingIndicator';
import KeyboardAvoidingView from '../KeyboardAvoidingView';
import PDFPasswordForm from './PDFPasswordForm';
import * as pdfViewPropTypes from './pdfViewPropTypes';
import compose from '../../libs/compose';
import withWindowDimensions from '../withWindowDimensions';
import withKeyboardState from '../withKeyboardState';
/**
* On the native layer, we use react-native-pdf/PDF to display PDFs. If a PDF is
* password-protected we render a PDFPasswordForm to request a password
* from the user.
*
* In order to render things nicely during a password challenge we need
* to keep track of additional state. In particular, the
* react-native-pdf/PDF component is both conditionally rendered and hidden
* depending upon the situation. It needs to be rerendered on each password
* submission because it doesn't dynamically handle updates to its
* password property. And we need to hide it during password challenges
* so that PDFPasswordForm doesn't bounce when react-native-pdf/PDF
* is (temporarily) rendered.
*/
class PDFView extends Component {
constructor(props) {
super(props);
this.state = {
shouldRequestPassword: false,
shouldAttemptPdfLoad: true,
shouldShowLoadingIndicator: true,
isPasswordInvalid: false,
password: '',
};
this.initiatePasswordChallenge = this.initiatePasswordChallenge.bind(this);
this.attemptPdfLoadWithPassword = this.attemptPdfLoadWithPassword.bind(this);
this.finishPdfLoad = this.finishPdfLoad.bind(this);
}
componentDidUpdate() {
this.props.onToggleKeyboard(this.props.isShown);
}
/**
* Initiate password challenge if message received from react-native-pdf/PDF
* indicates that a password is required or invalid.
*
* For a password challenge the message is "Password required or incorrect password."
* Note that the message doesn't specify whether the password is simply empty or
* invalid.
*
* @param {String} message
*/
initiatePasswordChallenge({message}) {
this.setState({shouldShowLoadingIndicator: false});
if (!message.match(/password/i)) {
return;
}
// Render password form, and don't render PDF and loading indicator.
this.setState({
shouldRequestPassword: true,
shouldAttemptPdfLoad: false,
});
// The message provided by react-native-pdf doesn't indicate whether this
// is an initial password request or if the password is invalid. So we just assume
// that if a password was already entered then it's an invalid password error.
if (this.state.password) {
this.setState({isPasswordInvalid: true});
}
}
/**
* When the password is submitted via PDFPasswordForm, save the password
* in state and attempt to load the PDF. Also show the loading indicator
* since react-native-pdf/PDF will need to reload the PDF.
*
* @param {String} password Password submitted via PDFPasswordForm
*/
attemptPdfLoadWithPassword(password) {
// Render react-native-pdf/PDF so that it can validate the password.
// Note that at this point in the password challenge, shouldRequestPassword is true.
// Thus react-native-pdf/PDF will be rendered - but not visible.
this.setState({
password,
shouldAttemptPdfLoad: true,
shouldShowLoadingIndicator: true,
});
}
/**
* After the PDF is successfully loaded hide PDFPasswordForm and the loading
* indicator.
*/
finishPdfLoad() {
this.setState({
shouldRequestPassword: false,
shouldShowLoadingIndicator: false,
});
}
render() {
const pdfStyles = [
styles.imageModalPDF,
StyleUtils.getWidthAndHeightStyle(this.props.windowWidth, this.props.windowHeight),
];
const touchableStyles = [
styles.flex1,
this.props.style,
styles.w100,
];
// If we haven't yet successfully validated the password and loaded the PDF,
// then we need to hide the react-native-pdf/PDF component so that PDFPasswordForm
// is positioned nicely. We're specifically hiding it because we still need to render
// the PDF component so that it can validate the password.
if (this.state.shouldRequestPassword) {
pdfStyles.push(styles.invisible);
}
const containerStyles = this.state.shouldRequestPassword && this.props.isSmallScreenWidth
? [styles.w100, styles.flex1]
: [styles.alignItemsCenter, styles.flex1];
return (
<View style={containerStyles}>
{this.state.shouldAttemptPdfLoad && (
<TouchableWithoutFeedback style={touchableStyles}>
<PDF
trustAllCerts={false}
renderActivityIndicator={() => <FullScreenLoadingIndicator />}
source={{uri: this.props.sourceURL}}
style={pdfStyles}
onError={this.initiatePasswordChallenge}
password={this.state.password}
onLoadComplete={this.finishPdfLoad}
/>
</TouchableWithoutFeedback>
)}
{this.state.shouldRequestPassword && (
<KeyboardAvoidingView>
<PDFPasswordForm
onSubmit={this.attemptPdfLoadWithPassword}
onPasswordUpdated={() => this.setState({isPasswordInvalid: false})}
isPasswordInvalid={this.state.isPasswordInvalid}
shouldShowLoadingIndicator={this.state.shouldShowLoadingIndicator}
/>
</KeyboardAvoidingView>
)}
</View>
);
}
}
PDFView.propTypes = pdfViewPropTypes.propTypes;
PDFView.defaultProps = pdfViewPropTypes.defaultProps;
export default compose(
withWindowDimensions,
withKeyboardState,
)(PDFView);