--- id: advanced-usage title: Advanced Usage --- We didn't add any dependencies for working with files and images, since there are a couple of them and you might want to use different ones. ## Images In this example, we will use [react-native-image-picker](https://github.com/react-native-image-picker/react-native-image-picker), follow the instructions there to install it. After it is done we can use the image picker to select an image and send it as a message (full example with images and files can be found [here](#putting-it-all-together)): ```ts // ... import { launchImageLibrary } from 'react-native-image-picker' const App = () => { // ... const handleImageSelection = () => { launchImageLibrary( { includeBase64: true, maxWidth: 1440, mediaType: 'photo', quality: 0.7, }, (response) => { if (response.base64) { const imageMessage: MessageType.Image = { authorId: userId, height: response.height, id: uuidv4(), imageName: response.fileName ?? response.uri?.split('/').pop() ?? '🖼', size: response.fileSize ?? 0, timestamp: Math.floor(Date.now() / 1000), type: 'image', uri: `data:image/*;base64,${response.base64}`, width: response.width, } addMessage(imageMessage) } } ) } return ( ) } export default App ``` Similar to the text message, you will need to create an image message using data from the image picker. In this example, we use `base64` just for demo purposes, but for the backend service, you will upload the image first and then send the received URL using the `uri` property. To keep the UI clean, the image message renders in two different ways, if the aspect ratio is too low or too high it renders like a file message, so you don't see a narrow line on the UI. The second way is a classic image in the chat. Go give it a try. :::tip You can use this URL https://bit.ly/2P0cn2g to test the file message presentation, remove height and width from the `imageMessage` so the library will calculate it automatically and replace `uri`'s data with this URL. ::: On tap, images will be previewed inside an interactive image gallery. ## Files In this example, we will use [react-native-document-picker](https://github.com/rnmods/react-native-document-picker), follow the instructions there to install it. After it is done we can use the file picker to select a file and send it as a message (full example with images and files can be found [here](#putting-it-all-together)): ```ts // ... import DocumentPicker from 'react-native-document-picker' const App = () => { // ... const handleFileSelection = async () => { try { const response = await DocumentPicker.pick({ type: [DocumentPicker.types.allFiles], }) const fileMessage: MessageType.File = { authorId: userId, fileName: response.name, id: uuidv4(), mimeType: response.type, size: response.size, timestamp: Math.floor(Date.now() / 1000), type: 'file', uri: response.uri, } addMessage(fileMessage) } catch (err) { if (!DocumentPicker.isCancel(err)) { // Handle error } } } return ( ) } export default App ``` Similar to the text message, you will need to create a file message using data from the document picker. In this example, `uri` will point to the local filesystem just for demo purposes, but for the backend service, you will upload the file first and then send the received URL using the `uri` property. ### Opening a file Right now, nothing will happen when a user taps on a file message, we will need to add another dependency. In this case, let's add [react-native-file-viewer](https://github.com/vinzscam/react-native-file-viewer). As usual, follow the instructions there to install it. Now we can open a file: ```ts // ... import FileViewer from 'react-native-file-viewer' const App = () => { // ... const handleFilePress = async (message: MessageType.File) => { try { await FileViewer.open(message.uri, { showOpenWithDialog: true }) } catch {} } return ( ) } export default App ``` ## Link preview Link preview works automatically, we created a separate library for that, you can found it [here](https://github.com/flyerhq/react-native-link-preview). Usually, however, you'll want to save the preview data so it stays the same, you can do that using `onPreviewDataFetched` callback: ```ts // ... import { PreviewData } from '@flyerhq/react-native-link-preview' const App = () => { // ... const handlePreviewDataFetched = ({ message, previewData, }: { message: MessageType.Text previewData: PreviewData }) => { setMessages( messages.map((m) => m.id === message.id ? { ...m, previewData } : m ) ) } return ( ) } export default App ``` ## Putting it all together Now to choose between images and files from a single button we will use another dependency - [react-native-action-sheet](https://github.com/expo/react-native-action-sheet), please follow the instructions there to install it. If you skipped previous sections and want to use this example, remember to install - [react-native-image-picker](https://github.com/react-native-image-picker/react-native-image-picker), [react-native-document-picker](https://github.com/rnmods/react-native-document-picker) and [react-native-file-viewer](https://github.com/vinzscam/react-native-file-viewer). This is a drop-in example, everything should work if you had installed all the dependencies. :::important Don't forget to wrap your app with `ActionSheetProvider` from `@expo/react-native-action-sheet`. Remember that it should be done outside a component provided in this example since we are using the `useActionSheet` hook which can't be used before the component is wrapped in `ActionSheetProvider`. You can create another component called `AppContainer.tsx` and wrap `` in the `ActionSheetProvider` there. ::: ```ts import { useActionSheet } from '@expo/react-native-action-sheet' import { Chat, MessageType } from '@flyerhq/react-native-chat-ui' import { PreviewData } from '@flyerhq/react-native-link-preview' import React, { useState } from 'react' import DocumentPicker from 'react-native-document-picker' import FileViewer from 'react-native-file-viewer' import { SafeAreaProvider } from 'react-native-safe-area-context' import { launchImageLibrary } from 'react-native-image-picker' // For the testing purposes, you should probably use https://github.com/uuidjs/uuid const uuidv4 = () => { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = Math.floor(Math.random() * 16) const v = c === 'x' ? r : (r % 4) + 8 return v.toString(16) }) } const App = () => { const userId = '06c33e8b-e835-4736-80f4-63f44b66666c' const { showActionSheetWithOptions } = useActionSheet() const [messages, setMessages] = useState([]) const addMessage = (message: MessageType.Any) => { setMessages([{ ...message, status: 'read' }, ...messages]) } const handleAttachmentPress = () => { showActionSheetWithOptions( { options: ['Photo', 'File', 'Cancel'], cancelButtonIndex: 2, }, (buttonIndex) => { switch (buttonIndex) { case 0: handleImageSelection() break case 1: handleFileSelection() break } } ) } const handleFilePress = async (message: MessageType.File) => { try { await FileViewer.open(message.uri, { showOpenWithDialog: true }) } catch {} } const handleFileSelection = async () => { try { const response = await DocumentPicker.pick({ type: [DocumentPicker.types.allFiles], }) const fileMessage: MessageType.File = { authorId: userId, fileName: response.name, id: uuidv4(), mimeType: response.type, size: response.size, timestamp: Math.floor(Date.now() / 1000), type: 'file', uri: response.uri, } addMessage(fileMessage) } catch (err) { if (!DocumentPicker.isCancel(err)) { // Handle error } } } const handleImageSelection = () => { launchImageLibrary( { includeBase64: true, maxWidth: 1440, mediaType: 'photo', quality: 0.7, }, (response) => { if (response.base64) { const imageMessage: MessageType.Image = { authorId: userId, height: response.height, id: uuidv4(), imageName: response.fileName ?? response.uri?.split('/').pop() ?? '🖼', size: response.fileSize ?? 0, timestamp: Math.floor(Date.now() / 1000), type: 'image', uri: `data:image/*;base64,${response.base64}`, width: response.width, } addMessage(imageMessage) } } ) } const handlePreviewDataFetched = ({ message, previewData, }: { message: MessageType.Text previewData: PreviewData }) => { setMessages( messages.map((m) => m.id === message.id ? { ...m, previewData } : m ) ) } const handleSendPress = (message: MessageType.PartialText) => { const textMessage: MessageType.Text = { authorId: userId, id: uuidv4(), text: message.text, timestamp: Math.floor(Date.now() / 1000), type: 'text', } addMessage(textMessage) } return ( // Remove this provider if already registered elsewhere // or you have React Navigation set up ) } export default App ```