How to display an image of expo camera in React Native - javascript

I am working on camera in app, where I have took a picture and have convert into base64 now I want to display an image but did not able to display an image . Could someone please help me how to achieve this goal .
Thanks
takePicture = async () => {
if (this.camera) {
let photo = await this.camera.takePictureAsync({
base64: true,
});
this.props.cameraToggle(false);
this.props.image(photo)
console.log('### take picutre', photo)
}
}
Image Code that I want to render
<Image source={{uri:`data:image/png;base64,${cameraImage}`}} style={{width:100,height:100}}/>

I assume that you're using class-based components. You can render the image which is captured from camera by setting the response photo to a local state and conditionally rendering it.
import React from "react";
import { Image } from "react-native";
import { Camera } from "expo-camera";
import Constants from "expo-constants";
import * as ImagePicker from "expo-image-picker";
import * as Permissions from "expo-permissions";
class Cameras extends React.Component(props) {
state = {
captures: "",
};
takePicture = async () => {
if (this.camera) {
let photo = await this.camera.takePictureAsync({
base64: true,
});
this.props.cameraToggle(false);
this.setState({ captures: photo.uri });
}
};
render() {
const { captures } = this.state;
return (
<View flex={1}>
{this.state.captures ? (
<Image source={{ uri: captures }} style={{width:50,height:50}} />
) : (
<Camera {...pass in props} />
)}
</View>
);
}
}
export default Cameras;
Note:
A more clean approach would be to pass the state captures as a route param by navigating a different screen and rendering the image on that screen.

I am assuming you are storing value of photo in cameraImage.
I am sharing a working solution with you using expo image picker, give it a try
Your component
import * as ImagePicker from 'expo-image-picker';
import * as Permissions from 'expo-permissions';
<TouchableHighlight onPress={this._openCamera}>
<Text>Open Camera</Text>
</TouchableHighlight>
<Image source={this.state.mainImageUrl} />
Your function
_openCamera = async () => {
await Permissions.askAsync(Permissions.CAMERA);
try {
let result: any = await ImagePicker.launchCameraAsync({
base64: true,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.cancelled) {
this.setState({
mainImageUrl: { uri: `data:image/jpg;base64,${result.base64}` }
});
}
} catch (E) {
console.warn(E);
}
}

Your code:
let photo = await this.camera.takePictureAsync({
base64: true,
});
this.props.image(photo)
The takePictureAsync return an object, so your base64 data is in the field photo.base64.
Solution 2
You can store the URI into the state without base64.
const [photoUri, setPhotoUri] = React.useState(null);
const cameraRef = React.useRef(null);
const takePhoto = async () => {
setPhotoUri(null);
const camera = cameraRef.current;
if(!camera) return;
try {
const data = await camera.takePictureAsync();
setPhotoUri(data.uri);
} catch (error) {
console.log('error: ', error);
}
}
Then use it.
return (
<Image source={{uri: photoUri}} />
);
Hope it helps.

Related

How to i display only one chatcard for single room instead of multiple chatcard with same rooms?

How to i display only one chatcard for single room instead of multiple chatcard with same rooms? i am trying to develop a directs box like ig currently messages are displaying correctly with username and profilepic but I am facing a problem that if a user sends multiple messages to any user then multiple chatcard displaying in UI how can I fix this problem?
The only thing which is common is "RoomId" which is coming from backend how can I display that? i am getting RoomId from backend also I do have senderUsername that I want to display but how can I display each chatcard for one room?
i have added some images please check that from here you can see a sender name HelloWorld send 4 messages with 4 different chatcards how can display one chatcard for single room?
the RoomId is a room of the users who are chating
import { Text, View } from 'react-native'
import React, { useState, useEffect } from 'react'
import { ScrollView, TextInput } from 'react-native-gesture-handler'
import { Ionicons } from '#expo/vector-icons';
import { formHead } from '../../CommonCss/FormCss';
import ChatCard from '../../Cards/ChatCard';
import styles from './styles';
import AsyncStorage from '#react-native-async-storage/async-storage';
const MainChat = ({ navigation }) => {
const [keyword, setKeyword] = useState('')
const [chats, setChats] = useState({});
useEffect(() => {
const fetchData = async () => {
try {
const userDataString = await AsyncStorage.getItem('user');
const MyData = await JSON.parse(userDataString);
const response = await fetch(`http://10.0.2.2:3000/g`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
MyUserId: MyData.user._id
})
});
const data = await response.json();
let tempChats = {};
data.forEach((chat) => {
if (!tempChats[chat.username]) {
tempChats[chat.username] = {
username: chat.username,
messages: [chat.lastMessage],
};
} else {
tempChats[chat.username].messages.push(chat.lastMessage);
}
});
setChats(tempChats);
await AsyncStorage.setItem('chats', JSON.stringify(tempChats));
} catch (error) {
console.error(error);
}
};
(async () => {
const cachedChats = await AsyncStorage.getItem('chats');
if (cachedChats) {
setChats(JSON.parse(cachedChats));
} else {
fetchData();
}
})();
}, []);
return (
<ScrollView style={styles.container}>
<Ionicons name="arrow-back" size={24} color="grey" style={styles.backbtn}
onPress={() => navigation.navigate("home")}
/>
<View style={styles.searchSection}>
<Text style={formHead}>Your Chats</Text>
<TextInput placeholder='Search'
style={styles.searchbar}
onChangeText={(text) => setKeyword(text)}
/>
</View>
<View style={styles.ChatSection}>
{
chats
? Object.values(chats)
.filter((chat) => {
if (keyword === '') {
return chat;
} else if (
chat.username.toLowerCase().includes(keyword.toLowerCase())
|| chat.messages.some((message) => message.toLowerCase().includes(keyword.toLowerCase()))
) {
return chat;
}
return false;
})
.map((chat) => {
return <ChatCard key={chat.username} chat={chat} />;
})
: null
}
</View>
</ScrollView>
)
}
export default MainChat

Difficulties handling asynchronous taks using image-picker and copying files in react-native

The following component CapturePhoto is used to take a photo using react-native Image Picker, once a photo is taken, I copy the photo file to a specific path that I pass as prop to this component from its parent ( a gallery of images that is a list of the CapturePhoto component) The reason I am copying the photo is that I cannot find another way to specify the path to Image picker as option.
The code works for taking a photo and copying the file image to the specific path but the photo is not being displayed until I take a second photo (delay)
I am learning react-native and Javascript at the same time, I know it's not the good way but I don't have time to learn Javascript first!
Any guidance provided would be much appreciated!
import React, { useState } from 'react';
import { TouchableOpacity, View, Image, StyleSheet, Text } from 'react-native';
import * as ImagePicker from 'react-native-image-picker';
import placeHolder from "./placeHolder.png"
export default function CapturePhoto({ photoPath }) {
let uri;
let decodedURI;
const [pickerResponse, setPickerResponse] = useState(null);
var RNFS = require('react-native-fs');
const copyPhoto = (source, destination) => {
if (source === undefined || destination === undefined) {
return;
} else {
RNFS.copyFile(source, destination)
.then((result) => {
console.log("\n\n>>>>>>>>>>>>The photo has been copied ")
}).catch((error) => {
console.log("\n\n>>>>>>>>>>>>Copy photo failed: ", error)
})
}
}
const onCameraPress = async () => {
const options = {
saveToPhotos: true,
mediaType: 'photo',
includeBase64: false,
};
ImagePicker.launchCamera(options, setPickerResponse);
uri = pickerResponse?.assets && pickerResponse.assets[0].uri;
console.log("\n\n>>>>>>>>>>>>destination photo: ", photoPath);
decodedURI = decodeURIComponent(uri)
await uri ? copyPhoto(decodedURI, photoPath) : console.log("\n\n>>>>>>>>>>>>The photo has not been copied ")
}
return (
<View style={{
justifyContent: 'center',
alignItems: 'center',
marginTop: 10
}}>
<TouchableOpacity onPress={() => onCameraPress()}>
<Image
style={styles.avatarImage}
source={uri ? { uri } : placeHolder}
/>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
avatarImage: {
height: 260,
width: 260,
overflow: 'hidden',
margin: 10
}
});
You don't actually need any states for saving image picker response. If you want to save the uri of the image which was clicked you can have a state for it and add the code for it inside copyPhoto() after file has been copied.
var RNFS = require('react-native-fs'); This statement should not be inside function. Everytime the component re-renders it will require it again. Add it like you've added import statements.
You should change your functions like this.
const copyPhoto = (source, destination) => {
if (source === undefined || destination === undefined) {
return;
} else {
RNFS.copyFile(source, destination)
.then((result) => {
console.log("\n\n>>>>>>>>>>>>The photo has been copied ");
})
.catch((error) => {
console.log("\n\n>>>>>>>>>>>>Copy photo failed: ", error);
});
}
};
const onCameraPress = async () => {
const options = {
saveToPhotos: true,
mediaType: "photo",
includeBase64: false
};
const result = await ImagePicker.launchCamera(options);
//if the user cancelled the process
if (result.didCancel) {
return alert("You cancelled the process");
}
const {assets} = result;
if (assets.length > 0) {
const decodedUri = decodeURIComponent(assets[0].uri);
copyPhoto(decodedUri, photoPath);
}
};

Redux-triggered function not registering component current state

TL;DR
When a store change triggers a component function, the current component state is ignored/reset, not letting me use its state data to feed the triggered function.
Full Description
This react-native app has a button located in a heading Appbar stack navigator, which must trigger a function that the currently focused Screen has.
The thing is that this screen is very deep within the navigation scheme, thus I decided to use Redux to directly notify the screen that the button has been pressed.
This also means that every time that this button is pressed and a store slice gets dispatched, I can trigger any function only depending on the Screen implementation.
If i use the very same function from a button within the component it works perfectly. However if I call the same function from the redux store change i get this log:
Console Behavior
# component loaded
false
# started writing, this is the component state
h
he
hel
hell
hello
#header button 'create' state change detected
true
#content as viewed by the onPressPublish function
content: ""
Error 400 - Cannot save empty content
#store reset for further use
false
Appbar
export const AppBarStackNavigator = (props) => {
const { toggle } = useSelector(toolbarSelector);
const handleCreatePress = () => {
dispatch(setCreate({ pressed: true }));
}
return (
<Appbar.Header
style={{ backgroundColor: theme.colors.background, elevation: 0 }}
>
<Button
icon="seed"
mode="contained"
// disabled={!contentProps.valid}
onPress={handleCreatePress}
labelStyle={{ color: 'white' }}
style={{
width: 115,
borderRadius: 50,
alignSelf: "flex-end"
}}>
Sembrar
</Button>
</Appbar.Header>
);
}
Store
import { createSlice } from "#reduxjs/toolkit";
export const toolbarSlice = createSlice({
name: 'toolbar',
initialState: {
create: false
},
reducers: {
setCreate(state, action) {
state.create = action.payload.pressed;
}
}
})
export const { setCreate } = toolbarSlice.actions;
export const toolbarSelector = state => state.toolbar
export default toolbarSlice.reducer;
The navigationally-deep component
import { toolbarSelector, setCreate } from '../store/toolbar';
import { useSelector } from 'react-redux';
// import { useFocusEffect, TabActions, useNavigation } from '#react-navigation/native';
export const DeepComponent = (props) => {
const theme = useTheme();
const { create } = useSelector(toolbarSelector);
return (
<ChildComponent {...props} create={create} setCreate={setCreate} style={{ backgroundColor: theme.colors.background }} />
);
};
Its child (where the function is)
import { useDispatch, useSelector } from 'react-redux';
export const ChildComponent = (props) => {
const dispatch = useDispatch();
const [content, setContent] = useState(''));
let payload = {};
const onPressPublish = async () => {
try {
console.log(payload);
payload = {
...payload,
content,
// images <- other component states
}
console.log(payload);
const seed = await api.writeOne(payload);
} catch (error) {
console.log(error);
Alert.alert('Could not publish :(', error.message);
}
navigation.goBack();
}
useEffect(() => {
console.log(props.create)
console.log(content)
if (props.create) {
console.log(content)
onPressPublish();
}
return () => {
dispatch(props.setCreate({ pressed: false }));
};
}, [props.create])
const onTextChange = (value, props) => {
// ...
setContent(value);
// ...
}
return (
<TextInput
mode='flat'
placeholder={inputPlaceholder}
multiline
onChangeText={text => onTextChange(text, props)}
keyboardShouldPersistTaps={true}
autoFocus
clearButtonMode='while-editing'>
<ParsedContent content={content} />
</TextInput>
<Button
disabled={!contentProps.valid}
onPress={onPressPublish}>
{buttonText}
</Button>
)
}
Here are some suggestions to change the code, you still have not provided any code in your question that would make sense (like the payload variable) but this may give you an idea where to go.
When you create an app with create-react-app you should have a linter that tells you when you have missing dependencies in hooks, you should not ignore these warnings:
//create onPressPublish when component mounts
const onPressPublish = useCallback(async (content) => {
try {
//removed payload as it makes no sense to create it
// and never use it anywhere
//removed assigning to seed becasue it is never used anywhere
await api.writeOne({ content });
} catch (error) {
console.log(error);
Alert.alert('Could not publish :(', error.message);
}
navigation.goBack();
}, []);
const { create, setCreate } = props;
useEffect(() => {
if (create) {
//passing content
onPressPublish(content);
}
return () => {
dispatch(setCreate({ pressed: false }));
};
}, [
//correct dependencies without linter warnings
content,
dispatch,
onPressPublish,
create,
setCreate,
]);

WebView Full Page Screenshot in React Native

How to take full page screenshot of webview in react native? Already tried "react-native-view-shot " Link but it only takes screenshot of the visible area.
Someone please help.
Thanks
For future readers:
Below is the final script after many attempts, which takes full page screenshot from Webview. I have used ProgressWebView instead of Webview. You can use Webview if you want.
This code works in functional components.
Note: When the page is fully loaded then click on the Take Screenshot button
import React, { useState } from 'react';
import { View, Button } from 'react-native';
import { captureRef } from "react-native-view-shot";
import ProgressWebView from "react-native-progress-webview";
import json5 from 'json5'
import { Dimensions } from 'react-native';
const Report = () => {
const [componentHeight, setComponentHeight] = useState(0)
const [globalComponentHeight, setGlobalComponentHeight] = useState(0)
const [componentHeightFlex, setComponentHeightFlex] = useState(1)
let url = 'https://stackoverflow.com/questions/63708244/webview-full-page-screenshot-in-react-native'
let webview = null;
let count = 0
const injectJS = _ => {
const script = `
let method${count} = _ => {
let documentHeight = document.body.scrollHeight
let data = {componentHeight: documentHeight}
window.ReactNativeWebView.postMessage(JSON.stringify(data))
}
method${count}()`
webview.injectJavaScript(script)
count++
}
const takeScreenshot = _ => {
console.log(globalComponentHeight)
const {height} = Dimensions.get("window")
console.log(height)
if(globalComponentHeight <= height) setComponentHeight(height)
else setComponentHeight(globalComponentHeight)
setComponentHeightFlex(null)
setTimeout(_ => {
captureRef(webview, {
format: "png",
quality: 0.9,
result: "base64"
}).then(
_screenshot => {
console.log(_screenshot)
//First save your screenshot from _screenshot(base64 string). You can send base64 string to your server and save
//Then make the component default as below
setComponentHeight(0)
setComponentHeightFlex(1)
},
error => console.error("Oops, screenshot failed", error)
);
}, 100)
}
return (
<View style={{ marginTop: 40, flex: 1, display: 'flex' }}>
<Button mode='contained' onPress={takeScreenshot} title="Take Screenshot"/>
<View
style={{
height: componentHeight,
flex: componentHeightFlex
}}
>
<ProgressWebView
ref={ref => {
if (ref != null)
webview = ref
}}
bounces={false}
style={{ position: 'relative' }}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={true}
source={{ uri: url }}
startInLoadingState={true}
onLoad={e => injectJS()}
onMessage={e => {
let data = json5.parse(e.nativeEvent.data)
// console.log(data)
setGlobalComponentHeight(parseInt(data.componentHeight))
}}
></ProgressWebView>
</View>
</View>
);
}
export default Report

How to save an array of Items in AsyncStorage React Native?

What i want to make is a bookmark list, but i don't know how i can save an array of items in AsyncStorage, my skills are basic on react.
I just need the function to save posts (post image, title and id) when i press the button "Save to bookmark".
export default class PostDetails extends Component {
constructor(props) {
super(props);
const {params} = props.navigation.state;
this.state = {
item: params.item
};
}
render() {
const {item} = this.state;
return (
<Image source={{uri: item.post_image}}/>
<Text> {item.post_id} </Text>
<Text> {item.post_title} </Text>
<Button>
<Text> Save to Bookmark </Text>
</Button>
);
}
}
I think you want to use JSON.stringify(arrayToSave); see the docs for JSON.stringify(). It will convert the array to a JSON string that can be saved to AsyncStorage and then can be retrieved at a later stage.
const saveBookmarks = async (bookmarksArray) => {
try {
const bookmarksString = JSON.stringify(bookmarksArray);
await AsyncStorage.setItem('#MyStore:bookmarks',bookmarksString);
} catch (error) {
// Error saving data
}
};
<Button onClick={() => saveBookmarks(yourArray)} />
To retrieve it you can use const theSavedArray = JSON.parse(stringFromAsyncStorage); the docs for JSON.parse()
try {
const bookmarksString = await AsyncStorage.getItem('#MyStore:bookmarks');
if (bookmarksString !== null){
// We have data!!
const bookmarksArray = JSON.parse(bookmarksString);
}
} catch (error) {
// Error retrieving data
}

Categories