├── .gitattributes ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── index.js └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # OSX 3 | # 4 | .DS_Store 5 | 6 | # node.js 7 | # 8 | node_modules/ 9 | npm-debug.log 10 | yarn-error.log 11 | .history 12 | package-lock.json 13 | 14 | 15 | # Xcode 16 | # 17 | build/ 18 | *.pbxuser 19 | !default.pbxuser 20 | *.mode1v3 21 | !default.mode1v3 22 | *.mode2v3 23 | !default.mode2v3 24 | *.perspectivev3 25 | !default.perspectivev3 26 | xcuserdata 27 | *.xccheckout 28 | *.moved-aside 29 | DerivedData 30 | *.hmap 31 | *.ipa 32 | *.xcuserstate 33 | project.xcworkspace 34 | 35 | 36 | # Android/IntelliJ 37 | # 38 | build/ 39 | .idea 40 | .gradle 41 | local.properties 42 | *.iml 43 | 44 | # BUCK 45 | buck-out/ 46 | \.buckd/ 47 | *.keystore 48 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Maher Zaidoune 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # react-native-url-preview 🐜 3 | 4 | [![NPM](https://nodei.co/npm/react-native-url-preview.png)](https://www.npmjs.com/package/react-native-url-preview) 5 | 6 | 7 | Parses text and wraps URLs , transform the url to a beautiful link preview 8 | 9 | ## Getting started 🐜 10 | 11 | `$ npm install react-native-url-preview --save` 12 | 13 | ## Usage 🐜 14 | ```javascript 15 | import RNUrlPreview from 'react-native-url-preview'; 16 | 17 | 18 | ``` 19 | 20 | ## Examples🐜 21 | 22 | Please refer to the [react-native-url-preview example](https://github.com/maherzaidoune/RNUrlPreviewExample) provided to see how `react-native-url-preview` can be used . 23 | 24 | ## Demo🐜 25 | 26 |

27 |     28 | 29 |

30 | 31 | ## Customization 🐜 32 | 33 | | Parameter | Required? | Default | Type | Description | 34 | | :----------------------- | :-------: | :------------------------- | :-------- | :----------------------------------------------------- | 35 | | text | YES | Null | `string` | The text that is parsed and where the URL is retrieved | 36 | | title | NO | True | `Boolean` | determine whether the URL title is displyed or not | 37 | | description | NO | True | `Boolean` | determine whether the URL description is displyed or not | 38 | | titleStyle | NO | defaultStyle | `style` | self explanatory i believe | 39 | | containerStyle | NO | defaultStyle | `style` | you can pass a custom container style | 40 | | imageStyle | NO | defaultStyle | `style` | you can pass a custom image style | 41 | | faviconStyle | NO | defaultStyle | `style` | you can pass a custom favicon style | 42 | | textContainerStyle | NO | defaultStyle | `style` | you can pass a custom style for the text container | 43 | | descriptionStyle | NO | defaultStyle | `style` | self explanatory i believe | 44 | | titleNumberOfLines | NO | 2 | `number` | self explanatory i believe | 45 | | descriptionNumberOfLines | NO | Ipad?4:3 | `number` | self explanatory i believe | 46 | | imageProps | NO | `{ resizeMode: "contain"}` | `object` | you can pass a custom props to image | 47 | | requestOptions | NO | `{}` | `object` | pass additional options to url preview request 48 | | onLoad | NO | `() => {}` | `function`| callback called when url preview data is loaded | 49 | | onError | NO | `() => {}` | `function`| callback called if url preview fails to load | 50 | ## Credits 🐜 51 | 52 | - Thanks to [marouan frih](https://github.com/Madm0x) for the REGEX 53 | - extract information from a URL with [link-preview-js](https://github.com/ospfranco/link-preview-js) 54 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {getLinkPreview} from 'link-preview-js'; 3 | import PropTypes from 'prop-types'; 4 | import {Image, Linking, Platform, Text, TouchableOpacity, View, ViewPropTypes} from 'react-native'; 5 | 6 | const REGEX = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/g; 7 | 8 | export default class RNUrlPreview extends React.PureComponent { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | isUri: false, 13 | linkTitle: undefined, 14 | linkDesc: undefined, 15 | linkFavicon: undefined, 16 | linkImg: undefined, 17 | }; 18 | this.getPreview(props.text, props.requestOptions); 19 | } 20 | 21 | getPreview = (text, options) => { 22 | const {onError, onLoad} = this.props; 23 | getLinkPreview(text, options) 24 | .then(data => { 25 | onLoad(data); 26 | this.setState({ 27 | isUri: true, 28 | linkTitle: data.title ? data.title : undefined, 29 | linkDesc: data.description ? data.description : undefined, 30 | linkImg: 31 | data.images && data.images.length > 0 32 | ? data.images.find(function(element) { 33 | return element.includes('.png') || element.includes('.jpg') || element.includes('.jpeg'); 34 | }) 35 | : undefined, 36 | linkFavicon: data.favicons && data.favicons.length > 0 ? data.favicons[data.favicons.length - 1] : undefined, 37 | }); 38 | }) 39 | .catch(error => { 40 | onError(error); 41 | this.setState({isUri: false}); 42 | }); 43 | }; 44 | 45 | componentDidUpdate(nextProps) { 46 | if (nextProps.text !== this.props.text) { 47 | this.getPreview(nextProps.text); 48 | } else if (nextProps.text == null) { 49 | this.setState({isUri: false}); 50 | } 51 | } 52 | 53 | _onLinkPressed = () => { 54 | Linking.openURL(this.props.text.match(REGEX)[0]); 55 | }; 56 | 57 | renderImage = (imageLink, faviconLink, imageStyle, faviconStyle, imageProps) => { 58 | return imageLink ? ( 59 | 60 | ) : faviconLink ? ( 61 | 62 | ) : null; 63 | }; 64 | renderText = (showTitle, showDescription, title, description, textContainerStyle, titleStyle, descriptionStyle, titleNumberOfLines, descriptionNumberOfLines) => { 65 | return ( 66 | 67 | {showTitle && ( 68 | 69 | {title} 70 | 71 | )} 72 | {showDescription && ( 73 | 74 | {description} 75 | 76 | )} 77 | 78 | ); 79 | }; 80 | renderLinkPreview = ( 81 | containerStyle, 82 | imageLink, 83 | faviconLink, 84 | imageStyle, 85 | faviconStyle, 86 | showTitle, 87 | showDescription, 88 | title, 89 | description, 90 | textContainerStyle, 91 | titleStyle, 92 | descriptionStyle, 93 | titleNumberOfLines, 94 | descriptionNumberOfLines, 95 | imageProps, 96 | ) => { 97 | return ( 98 | this._onLinkPressed()}> 99 | {this.renderImage(imageLink, faviconLink, imageStyle, faviconStyle, imageProps)} 100 | {this.renderText(showTitle, showDescription, title, description, textContainerStyle, titleStyle, descriptionStyle, titleNumberOfLines, descriptionNumberOfLines)} 101 | 102 | ); 103 | }; 104 | 105 | render() { 106 | const { 107 | text, 108 | containerStyle, 109 | imageStyle, 110 | faviconStyle, 111 | textContainerStyle, 112 | title, 113 | description, 114 | titleStyle, 115 | titleNumberOfLines, 116 | descriptionStyle, 117 | descriptionNumberOfLines, 118 | imageProps, 119 | } = this.props; 120 | return this.state.isUri 121 | ? this.renderLinkPreview( 122 | containerStyle, 123 | this.state.linkImg, 124 | this.state.linkFavicon, 125 | imageStyle, 126 | faviconStyle, 127 | title, 128 | description, 129 | this.state.linkTitle, 130 | this.state.linkDesc, 131 | textContainerStyle, 132 | titleStyle, 133 | descriptionStyle, 134 | titleNumberOfLines, 135 | descriptionNumberOfLines, 136 | imageProps, 137 | ) 138 | : null; 139 | } 140 | } 141 | 142 | const styles = { 143 | containerStyle: { 144 | flexDirection: 'row', 145 | }, 146 | }; 147 | 148 | RNUrlPreview.defaultProps = { 149 | onLoad: () => {}, 150 | onError: () => {}, 151 | text: null, 152 | requestOptions: {}, 153 | containerStyle: { 154 | backgroundColor: 'rgba(239, 239, 244,0.62)', 155 | alignItems: 'center', 156 | }, 157 | imageStyle: { 158 | width: Platform.isPad ? 160 : 110, 159 | height: Platform.isPad ? 160 : 110, 160 | paddingRight: 10, 161 | paddingLeft: 10, 162 | }, 163 | faviconStyle: { 164 | width: 40, 165 | height: 40, 166 | paddingRight: 10, 167 | paddingLeft: 10, 168 | }, 169 | textContainerStyle: { 170 | flex: 1, 171 | justifyContent: 'flex-start', 172 | alignItems: 'flex-start', 173 | padding: 10, 174 | }, 175 | title: true, 176 | description: true, 177 | titleStyle: { 178 | fontSize: 17, 179 | color: '#000', 180 | marginRight: 10, 181 | marginBottom: 5, 182 | alignSelf: 'flex-start', 183 | }, 184 | titleNumberOfLines: 2, 185 | descriptionStyle: { 186 | fontSize: 14, 187 | color: '#81848A', 188 | marginRight: 10, 189 | alignSelf: 'flex-start', 190 | }, 191 | descriptionNumberOfLines: Platform.isPad ? 4 : 3, 192 | imageProps: {resizeMode: 'contain'}, 193 | }; 194 | 195 | RNUrlPreview.propTypes = { 196 | onLoad: PropTypes.func, 197 | onError: PropTypes.func, 198 | text: PropTypes.string, 199 | containerStyle: ViewPropTypes ? ViewPropTypes.style : PropTypes.object, 200 | imageStyle: ViewPropTypes ? ViewPropTypes.style : PropTypes.object, 201 | faviconStyle: ViewPropTypes ? ViewPropTypes.style : PropTypes.object, 202 | textContainerStyle: ViewPropTypes ? ViewPropTypes.style : PropTypes.object, 203 | title: PropTypes.bool, 204 | description: PropTypes.bool, 205 | titleStyle: Text.propTypes ? Text.propTypes.style : PropTypes.object, 206 | titleNumberOfLines: Text.propTypes ? Text.propTypes.numberOfLines : PropTypes.number, 207 | descriptionStyle: Text.propTypes ? Text.propTypes.style : PropTypes.object, 208 | descriptionNumberOfLines: Text.propTypes ? Text.propTypes.numberOfLines : PropTypes.number, 209 | requestOptions: PropTypes.shape({ 210 | headers: PropTypes.objectOf(PropTypes.string), 211 | imagesPropertyType: PropTypes.string, 212 | proxyUrl: PropTypes.string 213 | }) 214 | }; 215 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-url-preview", 3 | "version": "1.1.9", 4 | "description": "react native url previewer", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "react-native", 11 | "react-component", 12 | "ios", 13 | "android", 14 | "url preview", 15 | "youtube video preview", 16 | "link preview" 17 | ], 18 | "author": "Zaidoune Maher", 19 | "license": "MIT", 20 | "peerDependencies": { 21 | "prop-types": "*", 22 | "react": "*", 23 | "react-native": "*", 24 | "react-native-windows": "0.41.0-rc.1" 25 | }, 26 | "dependencies": { 27 | "link-preview-js": "2.0.4" 28 | }, 29 | "devDependencies": {}, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/maherzaidoune/react-native-url-preview.git" 33 | }, 34 | "bugs": { 35 | "url": "https://github.com/maherzaidoune/react-native-url-preview/issues" 36 | }, 37 | "homepage": "https://github.com/maherzaidoune/react-native-url-preview#readme" 38 | } 39 | --------------------------------------------------------------------------------