How to DIsplay images from Firebase Storage in React Native in using snapshot? - react-native

I am new at React Native and using Expo, I was able to successfully set up an app that uploads pictures to Firebase Storage, but now I'm having trouble showing those images on the app(Homescreen).
How do I pull/Display the latest images into a FlatList or similar scrollable component? I've looked through StackOverflow for previous answers, but have had no luck.
Thank you For Any Help!
HomeS.js:
export default class HomeS extends React. Component {
renderPost = post => {
return (
<View style={styles.feedItem}>
<Image source={post.avatar} style={styles.avatar} />
<View style={{ flex: 1 }}>
<View style={{ flexDirection: "row", justifyContent: "space-between", alignItems: "center" }}>
<View>
<Text style={styles.name}>{post.name}</Text>
<Text style={styles.timestamp}>{moment(post.timestamp).fromNow()}</Text>
</View>
<Feather name="more-horizontal" size={24} color="#73788B" />
</View>
<Text style={styles.post}>{post.text}</Text>
<Image source={post.image} style={styles.postImage} resizeMode="cover" />
<View style={{ flexDirection: "row" }}>
<Feather name="heart" size={24} color="#73788B" style={{marginRight:16}} />
<Ionicons name="chatbox" size={24} color="#73788B" />
</View>
</View>
</View>
);
};
constructor(props){
super(props);
this.state=({
posts:[],
newtext:'',
loading:false,
});
this.ref = firebase.firestore().collection('posts').orderBy("timestamp", "desc");
}
componentDidMount() {
const {imageName} = this.state;
let imageRef = firebase.storage().ref('photos' + imageName);
imageRef
.getDownloadURL()
.then((url) => {
//from url you can fetched the uploaded image easily
this.setState({profileImageUrl: url});
})
.catch((e) => console.log('getting downloadURL of image error => ', e));
this.unsubscribe = this.ref.onSnapshot((querySnapshot => {
const example = [];
querySnapshot.forEach((doc, index)=>{
example.push({
name: doc.data().name, //Work
id: doc.data().id, //Work
text: doc.data().text, //Work
timestamp: doc.data().timestamp, //Work
imageRef: doc.data().imageRef // Not Working
});
});
this.setState({
posts:example,
loading: false,
});
}));
}
onPressPost = () => {
this.ref.add({
textname : this.props.text,localUri: this.state.image
}).then((data)=>{
console.log(`adding data = ${data}`);
this.setState({
newtext:'',
image:null,
loading:true
});
}).catch((error)=>{
console.log(`error to add doc = ${error}`);
this.setState({
newtext:'',
loading:true
});
});
}
render() {
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>Feed</Text>
</View>
<FlatList
style={styles.feed}
data={this.state.posts}
renderItem={({ item }) => this.renderPost(item)}
keyExtractor={item => item.id}
showsVerticalScrollIndicator={false}
></FlatList>
</View>
);
}

to display images in firebase storage you wil follow these steps:-
upload image to firebase
get download link
use download link in image source
first you will get file object from input type file
<input type="file" onchange={(e)=>(this.uploadImage(e))}
upload image & get download url
uploadImage = async (e) => {
var files = e.target.files
var image = files[0]
const Buffer = await image.arrayBuffer() // convert img to buffer
var storageRef = firebase.storage().ref('/MyPix')
var picPath = "pic_awesome.jpg"
var ref = storageRef.child(picPath)
var metadata = { contentType: 'image/jpeg', public: true }
await ref.put(Buffer, metadata)
var downloadUrl = await ref.getDownloadURL()
console.log('Download Url :: ' + downloadUrl)
return downloadUrl;
}
now display image from download url
<Image
style={{width: 50, height: 50}}
source={{uri: this.state.ImageUrl}}
/>
now let me tell you a performant way of uploading mutiple images
const imageList = ['https://imgpath.jpg','https://imgpath2.jpg']
const requests = imageList.map((image) => {
return uploadImage(image) // upload image method is given above
})
console.log(`start uploading All images in imageList in parallel`)
const response = await Promise.all(requests)
response.map((image)=> saveToFireStore(image))
now save download url to firestore
import { addDoc, collection , getFirestore } from "firebase/firestore";
...
const saveToFireStore = async (downloadUrl) => {
const db = getFirestore();
const docRef = await addDoc(collection(db, "users"), {
userId: "123",
userName: "Mathison",
profilePic: downloadUrl
});
}
now lets come to your final homejs component where we get images from frestore & display in listview
import { collection, getDocs } from "firebase/firestore";
...
componentDidMount = () => {
// get Images from Firestore & save To State
const querySnapshot = await getDocs(collection(db, "users"));
querySnapshot.forEach((doc) => {
example.push({
name: doc.data().name, //Work
id: doc.data().id, //Work
text: doc.data().text, //Work
timestamp: doc.data().timestamp, //Work
imageRef: doc.data().profilePic // saved in saveToFireStore(): Working
});
})
this.setState({
posts:example,
loading: false,
});
}
now other methods render & renderPost will start working as they are because user object has images now
render() {
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>Feed</Text>
</View>
<FlatList
style={styles.feed}
data={this.state.posts}
renderItem={({ item }) => this.renderPost(item)}
keyExtractor={item => item.id}
showsVerticalScrollIndicator={false}
></FlatList>
</View>
);
}
display image from user object
renderPost = post => {
return (
<Image source = {{uri:post.imageRef}} />
)
}

Related

improving elements styles to make a full screen scan

I will need a helping hand to edit this page. i have all the elements but i need help styling.
I would like to have the camera (the image you see is the typical emulator camera, that's why it makes an image) in full screen and from above at the top, the message in red and the 'autocomplete.
If you want, to explain better, I would like to respect the image below: autocomplete at the top left above the camera in full screen.
would it be possible for you to help me, I'm getting a little confused. I tried to do a snack but failed. I will add it later if i can.
const autocompletes = [...Array(10).keys()];
const apiUrl = "https://5b927fd14c818e001456e967.mockapi.io/branches";
class Tickets extends Component {
constructor(props) {
super(props);
this.state = {
Press: false,
hasCameraPermission: null,
reference: '',
lastScannedUrl:null,
displayArray: []
};
}
initListData = async () => {
let list = await getProductByRef(1);
if (list) {
this.setState({
displayArray: list,
reference: list.reference
});
}
// console.log('reference dans initListData =', list.reference)
};
async UNSAFE_componentWillMount() {
this.initListData();
// console.log('reference dans le state =', this.state.reference)
};
componentDidMount() {
this.getPermissionsAsync();
}
getPermissionsAsync = async () => {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === "granted" });
};
_onPress_Scan = () => {
this.setState({
Press: true
});
}
handleBarCodeScanned = ({ type, data }) => {
this.setState({ Press: false, scanned: true, reference: data });
this.props.navigation.navigate('ProductDetails', {reference : parseInt(this.state.state.reference)})
};
renderBarcodeReader = () => {
const { hasCameraPermission, scanned } = this.state;
if (hasCameraPermission === null) {
return <Text>{i18n.t("scan.request")}</Text>;
}
if (hasCameraPermission === false) {
return <Text>{i18n.t("scan.noaccess")}</Text>;
}
return (
<View
style={{
flex: 1,
...StyleSheet.absoluteFillObject
}}
>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : this.handleBarCodeScanned}
style={{ flex:1, height:'100%', ...StyleSheet.absoluteFillObject}}
/>
{scanned && (
<Button
title={"Tap to Scan Again"}
onPress={() => this.setState({ scanned: false })}
/>
)}
</View>
);
}
handleSelectItem(item, index) {
const {onDropdownClose} = this.props;
onDropdownClose();
console.log(item);
}
render() {
const { hasCameraPermission, scanned, Press } = this.state;
let marker = null;
const {scrollToInput, onDropdownClose, onDropdownShow} = this.props;
// console.log('displayArray', this.state.displayArray, 'reference', this.state.displayArray.reference)
return (
<View style={styles.container}>
{Press ? (
<View style={{flex:1}}>
<View style={styles.dropdownContainerStyle}>
<Autocomplete
key={shortid.generate()}
containerStyle={styles.autocompleteContainer}
inputStyle={{ borderWidth: 1, borderColor: '#F78400'}}
placeholder={i18n.t("tickets.warning")}
pickerStyle={styles.autocompletePicker}
scrollStyle={styles.autocompleteScroll}
scrollToInput={ev => scrollToInput(ev)}
handleSelectItem={(item, id) => this.handleSelectItem(item, id)}
onDropdownClose={() => onDropdownClose()}
onDropdownShow={() => onDropdownShow()}
fetchDataUrl={apiUrl}
minimumCharactersCount={2}
highlightText
valueExtractor={item => item.name}
rightContent
rightTextExtractor={item => item.properties}
/>
</View>
{this.renderBarcodeReader()}
</View>
) : (
<View style={{flex:1, justifyContent:'center', alignItems:'center'}}>
<Button
color="#F78400"
title={i18n.t("scan.scan")}
onPress={this._onPress_Scan}>
</Button>
</View>
)}
</View>
);
}
}
export default Tickets;
This gives me (after pressing the button) :
SNACK CODE TEST
I notice You are using a component from Expo called BarCodeScanner
There's a github issue open about the fact that this component is not possible to be styled for full screen: https://github.com/expo/expo/issues/5212
However one user proposes a good solution: replace BarCodeScanner with Camera and use barcodescannersettings
Here's a link for the answer on the gitHub issue: https://github.com/expo/expo/issues/5212#issuecomment-653478266
Your code should look something like:
renderBarcodeReader = () => {
const { hasCameraPermission, scanned } = this.state;
[ ... ] // the rest of your code here
return (
<View
style={{
flex: 1,
...StyleSheet.absoluteFillObject
}}
>
<Camera
onBarCodeScanned={scanned ? undefined : this.handleBarCodeScanned}
style={{ flex:1}}
barCodeScannerSettings={{
barCodeTypes: [BarCodeScanner.Constants.BarCodeType.qr],
}}
/>
</View>
);
}

fetch API call in react native is not working when I load the screeen

I have made an App in react native. My app makes API calls to my webserver and then Displays information based on that. The problem Is when I first load this screen... I get the loading screen and the information is display in the way it is supposed to but when I leave the screen and then comeback to the screen, it shows nothing and my array containing the items is empty, hence I think I am having problems with the API call when I leave the screen and then come back.
I am using React navigation 5 in My App.
export default function ({ navigation }) {
const [openQueries, setOpenQueries] = useState([]);
const [isLoading, seIsLoading] = useState(true);
const open_queries = [];
function getOpenQueries() {
var retrieveData = async () => {
try {
var value = await AsyncStorage.getItem("user");
var data = JSON.parse(value);
return data.user._id;
} catch (error) {
alert(error);
}
};
retrieveData().then((user) => {
fetch(URL + "/api/contact/open_queries", {
method: "POST",
body: "user=" + user + "&status=open",
headers: { "Content-type": "application/x-www-form-urlencoded" },
})
.then((response) => {
return response.json();
})
.then((responseJson) => {
if (responseJson.error === null) {
setOpenQueries(responseJson.open_queries);
seIsLoading(false);
}
});
});
}
getOpenQueries();
openQueries.forEach((query) => {
open_queries.push(
<TouchableOpacity
onPress={() =>
navigation.navigate("Chat", {
id: query._id,
title: query.title,
query: query,
showInput: true,
})
}
>
<View style={styles.inboxItem}>
<Text style={styles.inboxTitle}>{query.title}</Text>
<Text style={styles.inboxSubtext}>
{query.chats[query.chats.length - 1].chat}
</Text>
<View style={styles.lineBreak}></View>
</View>
</TouchableOpacity>
);
});
return (
<SafeAreaView style={styles.container}>
<Text style={styles.title}>Contacts</Text>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate("NewQuery")}
>
<Text style={styles.text}>Start a new query</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate("ClosedQueries")}>
<View style={styles.button}>
<Text style={styles.text}>Closed Queries</Text>
</View>
</TouchableOpacity>
<Text style={styles.subTitle}>Open Queries</Text>
{isLoading ? (
<View style={styles.loader}>
<Code />
</View>
) : (
<ScrollView style={{ paddingTop: 10 }}>
{openQueries.length > 0 ? (
open_queries
) : (
<Text style={styles.noQuery}>No Open Queries found</Text>
)}
</ScrollView>
)}
<ScrollView></ScrollView>
<BottomNavigation navigation={navigation} active={"contact"} />
</SafeAreaView>
);
}
Try this way
export default function ({ navigation }) {
const [openQueries, setOpenQueries] = useState([]);
const [isLoading, seIsLoading] = useState(true);
const [open_queries_views, setOpenQueriesViews] = useState([]);
function getOpenQueries() {
....
}
// Similar to componentDidMount
useEffect(() => {
getOpenQueries();
});
function renderViews(){
const open_queries = [];
openQueries.forEach((query) => {
open_queries.push(
<TouchableOpacity> ... </TouchableOpacity>
);
});
setOpenQueriesViews(open_queries); // set state here to auto reflect on view
}
return (
<SafeAreaView style={styles.container}>
....
<ScrollView style={{ paddingTop: 10 }}>
{open_queries_views.length > 0 ? (
open_queries_views
) : (
<Text style={styles.noQuery}>No Open Queries found</Text>
)}
</ScrollView>
.....
</SafeAreaView>
);
}

Flatlist is not reading "data" variable set using hooks in react native

I'm using hooks in react native project and setting data variable with referenced firebase database, when I'm console logging this data variable, It is showing null at first and within few seconds it shows fetched data, but flatlist is not reading it. Please correct my mistake. Here is my code:
RecommenTab = () => {
useFirebaseQuery = ref => {
const [docState, setDocState] = useState({
isLoading: true,
data: null
});
useEffect(() => {
return ref.on("value", snapshot => {
setDocState({
isLoading: false,
data: snapshot
});
});
}, []);
return docState;
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
userList = () => {
const ref = database.ref("users");
const { isLoading, data } = this.useFirebaseQuery(ref);
if (data != []) {
return (
<SafeAreaView style={{ marginTop: 20 }}>
{console.log(data)}
<Text>SHOW</Text>
<FlatList
data={data}
renderItem={({ item }) => (
<View>
<Text>Work</Text>
<ListItem
title={item.name}
subtitle={item.username}
leftAvatar={{
rounded: true,
source: { uri: item.avatar }
}}
/>
</View>
)}
keyExtractor={item => item.id}
ItemSeparatorComponent={this.renderSeparator}
/>
</SafeAreaView>
);
}
};
return <View>{this.userList()}</View>;
};

How to refresh/re-render flatlist on react-native?

im trying to refresh my flatlist from some page without going back to the principal menu, but it doesnt work.
I've already readed about extraData, but it doesnt work either.
Basiclly my program is like that:
I have a page called "passwords" and i add some passwords there from another page called "add passwords". When i click to add a password, i want to refresh the flatlist from the page "passwords" to show me the password that i just added.
This is my code from the page "add passwords"
...
state = {
arr: [],
local: '',
password: '',
obj: {
local: '',
password: ''
},
count: 1,
texto: ''
};
componentDidMount() {
//Here is the Trick
const { navigation } = this.props;
//Adding an event listner om focus
//So whenever the screen will have focus it will set the state to zero
this.focusListener = navigation.addListener('didFocus', () => {
this.setState({ count: 0 });
});
}
storeItem(item) {
try {
//we want to wait for the Promise returned by AsyncStorage.setItem()
//to be resolved to the actual value before returning the value~
console.log(item)
var joined = this.state.arr.concat(item);
console.log(joined)
this.setState({ arr: joined })
AsyncStorage.setItem('array', JSON.stringify(joined));
console.log(this.state.arr)
} catch (error) {
console.log(error.message);
}
}
componentWillMount() {
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
array ? this.setState({ arr: item }) : null;
console.log(item)
})
}
render() {
return (
<View style={styles.container}>
<TextInput
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => this.setState({ local: text })}
value={this.state.local}
/>
<TextInput
secureTextEntry={true}
style={{ height: 40, borderColor: 'gray', borderWidth: 1 }}
onChangeText={(text) => this.setState({ password: text })}
value={this.state.password}
/>
<Button title='Adicionar'
onPress={() => this.storeItem({ local: this.state.local, password: this.state.password }) + alert("Adicionado com sucesso!") + this.props.navigation.navigate('Passwords')}
></Button>
</View>
);
}
}
And this is my page "passwords" where i want to refresh
componentWillMount() {
const { navigation } = this.props;
this.willFocusListener = navigation.addListener(
'willFocus',
() => {
this.setState({ count: 10 })
}
)
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
item ? this.setState({ arr: item }) : null;
console.log(this.state.arr)
})
}
renderItem = ({ item }) => (
<View style={{ flexDirection: 'row' }} style={styles.passwordContainer}>
<Text> {item.local} </Text>
<Text> {item.password} </Text>
</View>
)
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.arr}
renderItem={this.renderItem}
extraData={this.state} //this is what i tryied
/>
</View>
);
You can use your listener to update the state.
componentWillMount() {
this.willFocusListener = navigation.addListener(
'willFocus',
() => this.updateData()
}
updateData = () => {
this.setState({ count: 10 });
AsyncStorage.getItem('array').then(array => {
item = JSON.parse(array)
item ? this.setState({ arr: item }) : null;
console.log(this.state.arr)
});
}
Any state changes will rerender items.

How to get ref in flat list item onpress?

I am trying to capture screen with react-native-view-shot. On press this.refs.viewShot.capture showing undefined.
Here is my code
Flat list code:
<FlatList
ref={(list) => this.myFlatList = list}
data={this.state.newsListArray}
keyExtractor={this._keyExtractor}
renderItem={this.renderRowItem}
/>
render on press link:
<TouchableOpacity onPress={ () => {
Platform.OS === 'ios' ?
this._captureScreenIos('5c63f7307518134a2aa288ce') :
this._captureScreenAndroid('5c63f7307518134a2aa288ce')
}}>
<View style={{flexDirection:'row'}}>
<Icon name="share-alt" size={16} color="#ffb6cf" />
<Text style={{paddingLeft:6,fontSize:12,fontWeight:'500'}}>Share News</Text>
</View>
</TouchableOpacity>
And that's the function:
_captureScreenIos = (refId) => {
console.log("Clicked for IOS");
this.changeLoaderStatus();
var thisFun = this;
var viewShotRef = 'viewShot-5c63f7307518134a2aa288ce';
this.myFlatList.viewShot.capture({width: 2048 / PixelRatio.get(), height: 2048 / PixelRatio.get()}).then(res => {
RNFetchBlob.fs.readFile(res, 'base64').then((base64data) => {
console.log("base64data",base64data)
let base64Image = `data:image/jpeg;base64,${base64data}`;
const shareOptions = {
title: "My Beauty Squad",
//message: "Download my beauty squad with below link."+ "\n" + "https://itunes.apple.com/uk/app/my-beauty-squad/id1454212046?mt=8" ,
url: base64Image,
subject: "Share news feed"
};
Share.open(shareOptions);
thisFun.changeLoaderStatus();
})
}).catch(error => {
console.log(error, 'this is error');
this.changeLoaderStatus();
})
}
Please let me know if anyone having a solution for the same.
**This is my app screen **
It's blur when we have long list items.
Try this:
import { captureRef } from react-native-view-shot
constructor(props) {
super(props);
this.refs = {};
}
renderItem = ({item, index}) => (
<TouchableOpacity
onPress={ () => {
captureRef(this.refs[`${index}`], options).then(.....)
}
>
<View
style={{flexDirection:'row'}}
ref={shot => this.refs[`${index}`] = shot}
>
...........
</View>
</TouchableOpacity>
)
React Native View Shot
I hope it help you.
That is a good amount of code. Try https://reactnativecode.com/take-screenshot-of-app-programmatically/
setting the state and try passing in the object you are referencing.
export default class App extends Component {
constructor(){
super();
this.state={
imageURI : 'https://reactnativecode.com/wp-content/uploads/2018/02/motorcycle.jpg'
}
}
captureScreenFunction=()=>{
captureScreen({
format: "jpg",
quality: 0.8
})
.then(
uri => this.setState({ imageURI : uri }),
error => console.error("Oops, Something Went Wrong", error)
);
}
Here is answer:
constructor(props) {
this.screenshot = {};
}
This is my function:
_captureScreenIos(itemId) {
this.changeLoaderStatus();
var thisFun = this;
var viewShotRef = itemId;
captureRef(this.screenshot[itemId],{format: 'jpg',quality: 0.8}).then(res => {
RNFetchBlob.fs.readFile(res, 'base64').then((base64data) => {
console.log("base64data",base64data)
let base64Image = `data:image/jpeg;base64,${base64data}`;
const shareOptions = {
title: "My Beauty Squad",
//message: "Download my beauty squad with below link."+ "\n" + "https://itunes.apple.com/uk/app/my-beauty-squad/id1454212046?mt=8" ,
url: base64Image,
subject: "Share news feed"
};
Share.open(shareOptions);
thisFun.changeLoaderStatus();
})
}).catch(error => {
console.log(error, 'this is error');
this.changeLoaderStatus();
})
}
This is the view:
<View collapsable={false} ref={(shot) => { this.screenshot[itemId] = shot; }} >
//some content here
<TouchableOpacity onPress={ () => {
Platform.OS === 'ios' ?
this._captureScreenIos(itemData.item._id) :
this._captureScreenAndroid(itemData.item._id)
}}>
<View style={{flexDirection:'row'}}>
<Icon name="share-alt" size={16} color="#ffb6cf" />
<Text style={{paddingLeft:6,fontSize:12,fontWeight:'500'}}>Share News</Text>
</View>
</TouchableOpacity>
</View>