UNPKG

react-native-share

Version:
336 lines (306 loc) 9.63 kB
/* * * @format * @flow * */ import * as React from 'react'; import { View, StyleSheet, TouchableOpacity, BackHandler, NativeModules, Platform, PermissionsAndroid, } from 'react-native'; import type { ViewStyleProp } from 'react-native/Libraries/StyleSheet/StyleSheet'; import Overlay from './components/Overlay'; import Sheet from './components/Sheet'; import Button from './components/Button'; const ANDROID_KIT_KAT_SDK_VERSION = 19; const androidPermissionRequestRequired = parseInt(Platform.Version, 10) < ANDROID_KIT_KAT_SDK_VERSION; const styles = StyleSheet.create({ actionSheetContainer: { flex: 1, paddingTop: 10, paddingBottom: 0, justifyContent: 'flex-end', backgroundColor: 'rgba(0, 0, 0, 0.5)', }, buttonContainer: { overflow: 'hidden', backgroundColor: 'white', paddingBottom: 5, paddingTop: 5, }, }); type Props = { visible: boolean, onCancel: () => void, children: React.Node, style?: ViewStyleProp, overlayStyle?: ViewStyleProp, }; const shareSheetStyle = { flex: 1 }; class ShareSheet extends React.Component<Props> { backButtonHandler: () => boolean; componentDidMount() { this.backButtonHandler = this.backButtonHandler.bind(this); BackHandler.addEventListener('backPress', this.backButtonHandler); } componentWillUnmount() { BackHandler.removeEventListener('backPress', this.backButtonHandler); } backButtonHandler() { if (this.props.visible) { this.props.onCancel(); return true; } return false; } render() { const { style = {}, overlayStyle = {}, ...props } = this.props; return ( <Overlay visible={this.props.visible} {...props}> <View style={[styles.actionSheetContainer, overlayStyle]}> <TouchableOpacity style={shareSheetStyle} onPress={this.props.onCancel} /> <Sheet visible={this.props.visible}> <View style={[styles.buttonContainer, style]}>{this.props.children}</View> </Sheet> </View> </Overlay> ); } } type Options = { url?: string, urls?: Array<string>, filename?: string, filenames?: Array<string>, type?: string, message?: string, title?: string, subject?: string, recipient?: string, excludedActivityTypes?: string, failOnCancel?: boolean, showAppsToView?: boolean, saveToFiles?: boolean, appId?: string, }; type MultipleOptions = { url?: string, urls?: Array<string>, filename?: string, filenames?: Array<string>, type?: string, message?: string, title?: string, subject?: string, activityItemSources?: Array<ActivityItemSource>, excludedActivityTypes?: string, failOnCancel?: boolean, showAppsToView?: boolean, saveToFiles?: boolean, }; type ActivityType = | 'addToReadingList' | 'airDrop' | 'assignToContact' | 'copyToPasteBoard' | 'mail' | 'message' | 'openInIBooks' // iOS 9 or later | 'postToFacebook' | 'postToFlickr' | 'postToTencentWeibo' | 'postToTwitter' | 'postToVimeo' | 'postToWeibo' | 'print' | 'saveToCameraRoll' | 'markupAsPDF'; // iOS 11 or later type ActivityItem = { type: 'text' | 'url', content: string }; type LinkMetadata = { originalUrl?: string, url?: string, title?: string, icon?: string, image?: string, remoteVideoUrl?: string, video?: string, }; type ActivityItemSource = { placeholderItem: ActivityItem, item: { [ActivityType | string]: ?ActivityItem }, subject?: { [ActivityType | string]: string }, dataTypeIdentifier?: { [ActivityType | string]: string }, thumbnailImage?: { [ActivityType | string]: string }, linkMetadata?: LinkMetadata, }; type OpenReturn = { app?: string, dismissedAction?: boolean, ... }; type ShareSingleReturn = { message: string, isInstalled?: boolean, ... }; const requireAndAskPermissions = async (options: Options | MultipleOptions): Promise<any> => { if ((options.url || options.urls) && Platform.OS === 'android') { const urls: Array<string> = options.urls || (options.url ? [options.url] : []); try { const resultArr = await Promise.all( urls.map( (url) => new Promise((res, rej) => { NativeModules.RNShare.isBase64File( url, (e) => { rej(e); }, (isBase64) => { res(isBase64); }, ); }), ), ); const requirePermission = resultArr.includes(true); if (!requirePermission) { return Promise.resolve(true); } const hasPermission = await PermissionsAndroid.check( PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, ); if (hasPermission) { return Promise.resolve(true); } if (androidPermissionRequestRequired) { const result = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, ); if (result === PermissionsAndroid.RESULTS.GRANTED) { return Promise.resolve(); } throw new Error('Write Permission not available'); } } catch (e) { return Promise.reject(e); } } return Promise.resolve(true); }; class RNShare { static Button: any; static ShareSheet: RNShare.ShareSheet; static Overlay: any; static Sheet: any; static Social = { FACEBOOK: NativeModules.RNShare.FACEBOOK || 'facebook', FACEBOOK_STORIES: NativeModules.RNShare.FACEBOOK_STORIES || 'facebookstories', PAGESMANAGER: NativeModules.RNShare.PAGESMANAGER || 'pagesmanager', TWITTER: NativeModules.RNShare.TWITTER || 'twitter', WHATSAPP: NativeModules.RNShare.WHATSAPP || 'whatsapp', INSTAGRAM: NativeModules.RNShare.INSTAGRAM || 'instagram', INSTAGRAM_STORIES: NativeModules.RNShare.INSTAGRAM_STORIES || 'instagramstories', GOOGLEPLUS: NativeModules.RNShare.GOOGLEPLUS || 'googleplus', EMAIL: NativeModules.RNShare.EMAIL || 'email', PINTEREST: NativeModules.RNShare.PINTEREST || 'pinterest', LINKEDIN: NativeModules.RNShare.LINKEDIN || 'linkedin', SMS: NativeModules.RNShare.SMS || 'sms', }; static InstagramStories = { SHARE_BACKGROUND_IMAGE: NativeModules.RNShare.SHARE_BACKGROUND_IMAGE || 'shareBackgroundImage', SHARE_BACKGROUND_VIDEO: NativeModules.RNShare.SHARE_BACKGROUND_VIDEO || 'shareBackgroundVideo', SHARE_STICKER_IMAGE: NativeModules.RNShare.SHARE_STICKER_IMAGE || 'shareStickerImage', SHARE_BACKGROUND_AND_STICKER_IMAGE: NativeModules.RNShare.SHARE_BACKGROUND_AND_STICKER_IMAGE || 'shareBackgroundAndStickerImage', }; static FacebookStories = { SHARE_BACKGROUND_IMAGE: NativeModules.RNShare.SHARE_BACKGROUND_IMAGE || 'shareBackgroundImage', SHARE_STICKER_IMAGE: NativeModules.RNShare.SHARE_STICKER_IMAGE || 'shareStickerImage', SHARE_BACKGROUND_AND_STICKER_IMAGE: NativeModules.RNShare.SHARE_BACKGROUND_AND_STICKER_IMAGE || 'shareBackgroundAndStickerImage', }; static open(options: Options | MultipleOptions): Promise<OpenReturn> { return new Promise((resolve, reject) => { requireAndAskPermissions(options) .then(() => { if (Platform.OS === 'ios' && options.url && !options.urls) { // Backward compatibility with { Share } from react-native const url = options.url; delete options.url; options.urls = [url]; if (options.filename && !options.filenames) { options.filenames = [options.filename]; delete options.filename; } } NativeModules.RNShare.open( options, (e) => { return reject({ error: e }); }, (success, activityType) => { if (success) { return resolve({ app: activityType, message: activityType, }); } else if (options.failOnCancel === false) { return resolve({ dismissedAction: true, }); } else { reject(new Error('User did not share')); } }, ); }) .catch((e) => reject(e)); }); } static shareSingle(options: Options): Promise<ShareSingleReturn> { if (Platform.OS === 'ios' || Platform.OS === 'android') { return new Promise((resolve, reject) => { requireAndAskPermissions(options) .then(() => { NativeModules.RNShare.shareSingle( options, (e) => { return reject({ error: e }); }, (e, activityType) => { return resolve({ message: e, app: activityType, }); }, ); }) .catch((e) => reject(e)); }); } else { throw new Error('Not implemented'); } } static isPackageInstalled(packageName: string): Promise<ShareSingleReturn> { if (Platform.OS === 'android') { return new Promise((resolve, reject) => { NativeModules.RNShare.isPackageInstalled( packageName, (e) => { return reject({ error: e }); }, (isInstalled) => { return resolve({ isInstalled: isInstalled, message: 'Package is Installed', }); }, ); }); } else { throw new Error('Not implemented'); } } } export { Overlay, Sheet, Button, ShareSheet }; export default RNShare;