wonday / react-native-pdf

A <Pdf /> component for react-native
MIT License
1.59k stars 545 forks source link

react-native Share bug on iOS #559

Open awatt-cox opened 3 years ago

awatt-cox commented 3 years ago

What react-native version are you using? I have tried this on react-native versions 0.61.2, 0.63.4, and 0.64.0

What react-native-pdf version are you using? 6.2.2

What platform does your issue occur on? (android/ios/both) iOS

Describe your issue as precisely as possible : I have implemented react-native-pdf and I need to add the ability to Share the PDF on iOS and Android. I implemented react-native's built-in Share feature to allow users to share the downloaded PDF data:

The share works correctly on Android for both text/SMS and email (user is able to share PDF directly)

The share works on iOS FOR MESSAGES ONLY. When I try to share the PDF via Mail, the user is unable to enter any values in the To:, Cc:, Bcc:, or Subject: fields of the email. Each time a user selects one of these fields on the email, the keyboard flashes briefly and then the field loses focus, which dismisses the keyboard.

There seems to be some sort of conflict between react-native-pdf and react-native's Share component that causes this issue. Please note that it only seems to be a problem when sharing to Mail on iOS. The PDF data attachment is not the issue - I'm unable to share anything on iOS mail when the screen implements react-native-pdf. Sharing works correctly on all other screens in the app.

Show us the code you are using?

import React, { useState } from 'react'; import { View, TouchableOpacity, Share, Text, StyleSheet } from 'react-native'; import Pdf from 'react-native-pdf'; import colors from '../assets/styles/colors'; import { getSessionCache } from '../service/apollo/ApolloCache'; import { manheimMobileFlag } from '../Utilities/Utilities';

const PdfScreen = props => { const { url, downloadPDF, useCookies } = props.navigation.state.params.data const [pdfFilePath, setPdfFilePath] = useState(null);

const getHeaderCookies = () => {
    const storedCookies = getSessionCache();
    const { authTkt, userBearerToken, expirationTime, contactGuid } = storedCookies;
    const mobileAppFlag = manheimMobileFlag();
    const cookies = `bearerToken=${userBearerToken}; bearerTokenExp=${expirationTime}; auth_tkt=${authTkt}; contactguid=${contactGuid}; ${mobileAppFlag};`;
    return cookies;
};

const onShare = async () => {
    if (pdfFilePath) {
        try {
            await Share.share({
                url: pdfFilePath, // this doesn't work correctly on iOS Mail app
            });
        } catch (error) {
            alert(error.message);
        }
    }
};

let source = { uri: url };
if (useCookies) {
    // add cookies to request:
    source = {
        ...source,
        headers: {
            Cookie: getHeaderCookies(),
        },
    };
}
return (
    <View style={{ width: '100%', height: '100%' }}>
        <Pdf
            source={source}
            onLoadComplete={(numberOfPages, filePath) => {
                // numberOfPages must be first param
                setPdfFilePath(filePath);
            }}
            style={styles.pdfView}
        />
        {downloadPDF && (
            <TouchableOpacity onPress={onShare} style={styles.shareButton}>
                <Text style={styles.shareButtonText}>Share</Text>
            </TouchableOpacity>
        )}
    </View>
);

};

const styles = StyleSheet.create({ pdfView: { flexGrow: 1, }, shareButton: { backgroundColor: colors.Blue, padding: 15, borderRadius: 4, margin: 8, }, shareButtonText: { color: colors.White, fontSize: 15, fontWeight: '700', textTransform: 'uppercase', textAlign: 'center', }, });

export default PdfScreen;

afilp commented 2 years ago

Is this still a bug? Did you find a solution? Thank you!

awatt-cox commented 2 years ago

I fixed it by adding code to download the file to the local file system and then display it from there. The downside to this approach is that you are basically downloading the file twice: once for display in a webview and once for sharing. I only needed PDF sharing capability on iOS, so I don't know if this code works on Android. Use at your own risk:

import React, { useState } from 'react'; import { View, Share, Text, Alert, StyleSheet, Platform, } from 'react-native'; import { TouchableOpacityDebounce } from '../shared_components/DebouncedTouchables'; import WebView from 'react-native-webview'; import RNFetchBlob from 'rn-fetch-blob'; import colors from '../assets/styles/colors'; import { getSessionCache } from '../service/apollo/ApolloCache'; import { manheimMobileFlag } from '../Utilities/Utilities';

const PdfShareScreen = props => { if (Platform.OS !== 'ios') { Alert.alert('Error', 'This screen is not compatible with Android'); } const { url } = props.navigation.state.params.data

const [pdfFilePath, setPdfFilePath] = useState(null);

const getHeaderCookies = () => {
    const storedCookies = getSessionCache();
    const { authTkt, userBearerToken, expirationTime, contactGuid } = storedCookies;
    const mobileAppFlag = manheimMobileFlag();
    const cookies = `bearerToken=${userBearerToken}; bearerTokenExp=${expirationTime}; auth_tkt=${authTkt}; contactguid=${contactGuid}; ${mobileAppFlag};`;
    return cookies;
};

const sharePdf = async () => {
    if (pdfFilePath) {
        try {
            await Share.share({
                url: pdfFilePath,
            });
        } catch (error) {
            Alert.alert('Error', error.message);
        }
    }
};

const downloadFile = () => {
    const { dirs } = RNFetchBlob.fs;
    const dirToSave = dirs.DocumentDir;

    const configOptions = {
        fileCache: true,
        title: 'doc',
        path: `${dirToSave}/doc.pdf`,
        appendExt: 'pdf',
    };

    // console.log('The file saved to 23233', configfb, dirs);

    RNFetchBlob.config(configOptions)
        .fetch('GET', url, {
            Cookie: getHeaderCookies(),
        })
        .then(res => {
            RNFetchBlob.fs.writeFile(configOptions.path, res.data, 'base64');
            // RNFetchBlob.ios.previewDocument(configfb.path);
            setPdfFilePath(configOptions.path);
        })
        .catch(e => {
            setisdownloaded(true)
            showSnackbar(e.message);
            console.log('The file saved to ERROR', e.message)
        });
};

downloadFile(url);
const source = { uri: url };

return (
    <View style={{ width: '100%', height: '100%' }}>
        <WebView
            startInLoadingState={true} // this can cause problems in Android. Leaving here for now.
            source={source}
            javaScriptEnabledAndroid={true}
            automaticallyAdjustContentInsets={true}
            sharedCookiesEnabled={true} 
            style={styles.pdfView}
        />
        {pdfFilePath ? (
            <TouchableOpacityDebounce onPress={sharePdf} style={styles.shareButton}>
                <Text style={styles.shareButtonText}>Share</Text>
            </TouchableOpacityDebounce>
        ) : (
            <View style={styles.shareButton}>
                <Text style={styles.shareButtonText}>Preparing File...</Text>
            </View>
        )}
    </View>
);

};

const styles = StyleSheet.create({ pdfView: { flexGrow: 1, backgroundColor: colors.White, }, shareButton: { backgroundColor: colors.Blue, padding: 15, borderRadius: 4, margin: 8, }, shareButtonText: { color: colors.White, fontSize: 15, fontWeight: '700', textTransform: 'uppercase', textAlign: 'center', }, });

export default PdfShareScreen;