I have a function that stores videos in firebase storage. In the function, I have a snapshot listener that listens to the upload progress and calls another function, "UploadingProgress" to update a state variable that holds the "percentage". This is the function below:
feedUploadProgress: async (Data, UploadingProgress, mediaIndex) => {
try {
let url = null;
var uploadTask = Data.videoRef.put(Data.video, Data.metadata); // not await as we want to async put files
await new Promise((resolve, reject) => {
uploadTask.on(
"state_changed", // or 'state_changed'
(snapshot) => {
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
UploadingProgress(
true,
mediaIndex,
snapshot.bytesTransferred,
snapshot.totalBytes
);
},
(error) => {
console.log(error.code);
url = false;
reject(error.code);
switch (error.code) {
case "storage/unauthorized":
// User doesn't have permission to access the object
break;
case "storage/canceled":
// User canceled the upload
break;
case "storage/unknown":
// Unknown error occurred, inspect error.serverResponse
break;
}
},
() => {
// Upload completed successfully, now we can get the download URL
uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => {
url = downloadURL;
UploadingProgress(false, null, 0, 10000000);
//resolve(downloadURL);
});
}
);
});
return url;
} catch (error) {
console.log("Error #uploadProgress: ", error);
}
},
And UploadProgress:
const showUploading = (status, mediaIndex, bytesTransferred, totalBytes) => {
if (status) {
//if the status (which is snapshot listner status) is true, then will show upload progress
if (!isUploading) {
setIsUploading(true);
let tempMediaPercentages = new Map();
tempMediaPercentages.set(mediaIndex, bytesTransferred / totalBytes);
setMediaPercentages(tempMediaPercentages);
console.log(mediaIndex, tempMediaPercentages);
}
} else {
setIsUploading(false);
setUploadPercentage(0);
setMediaPercentages(new Map()); // resets the media percentages
}
};
The state variable I'm trying to manipulate is "mediaPercentages", a map but in the scenario where I have 2 async calls on "feedUploadProgress" the state changes aren't made but instead of 1 state being updated by each of these async calls, what I get instead is seemingly 2 different states that aren't aware of the changes made by each separate function call.
In this screen shot below I console log the entire state which should contain 2 key-value pairs, 0 & 1, but as the map is getting changed it makes seemingly independent changes rather than having 1 map containing 2 key-value pairs together.
Related
needing a guide for how to layout functionality for a React Native app that's pairing with an ESP32 that will eventually feed back weight readings using read Characteristic, and be able to toggle a DI via write to a characteristic.
i can currently scan and connect to the ESP32 and show the values from the ESP32 (random changing values for now) and also toggle the LED via changing a hardcoded value. But i want to be able to do this via a button in the app.
const scanDevices = () => {
//set isLoading to true to show activity Indicator
setIsLoading(true);
//scan for devices, (UUIDs, ScanOptions(error, device))
manager.startDeviceScan(null, null, (error, device) => {
if (error) {
console.log("Error in scanning", error.message)
return;
}
if (device) {
//if a device is scanned, add the name & id details into the scannedDevice object via reducer
dispatch({type: 'DEVICE_ADD', payload: {name: device.name, id: device.id}});
}
});
//end scan after 3 seconds, stop the activity indicator swirly thing
setTimeout(() => {
console.log("Scan timeout after 5 seconds");
manager.stopDeviceScan();
setIsLoading(false);
}, 5000);
};
const deviceConnect = (device) => {
console.log("Connecting to:", device.name, device.id);
setIsConnected(true);
setConnectedDevice(device);
manager.connectToDevice(device.id)
.then((device) => {
console.log("Discovering all services & chars");
return device.discoverAllServicesAndCharacteristics()
}).then((device) => {
// console.log("Write Value inside deviceConnect:", writeValue)
console.log("Device:", device.name, "has been connected.");
return deviceNotifications(device, writeValue);
}).catch((error) => {
console.log("device connect error:", device.name, error)
//JSON.stringify(error)
});
};
const deviceNotifications = async (device, writeValue) => {
const service = "af493e2a-f002-11eb-9a03-0242ac130003";
const characteristicTX = "af49423a-f002-11eb-9a03-0242ac130003";
const characteristicRX = "af49414a-f002-11eb-9a03-0242ac130003";
if (device) {
try {
device.monitorCharacteristicForService(service, characteristicTX, (error, characteristic) => {
if (error) {
console.log(error);
} else {
setCharacteristicValue(() => {
return [{id: uuid.v4(), value: (base64.decode(characteristic.value))}];
})}
});
device.writeCharacteristicWithResponseForService(service, characteristicRX, base64.encode(writeValue));
console.log("Writing to RX:", writeValue);
}
catch (err) {
console.log("deviceNotification catch error:", err);
}
};
}
I'm getting pretty confused trying to sort through the [ble-plx documentation][1] ([github wiki][2])
Currently the only way i can get the LED to turn on/off, is i have the LED toggle section inside the deviceNotifications async function and have to manually change the value that's being encoded and written in the code itself, rather than from the App UI using an useState value.
I tried using the useState toggle off a button (which toggled the value and logged out OK), and then re-calling the deviceConnect function, but the commented out console.log in the .then promise section didn't work past the first one, returning which turned the LED on (writing 'A' to the characteristic).
thanks for any help in advance, i know a lot of these ble-plx questions go unanswered.
//this is at a top level inside the main function
const [writeValue, setWriteValue] = useState('A');
const toggleLED = () => {
if (writeValue == 'B') {
setWriteValue('A');
console.log("Toggling write value:", writeValue);
} else {
setWriteValue('B')
console.log("Toggling write value", writeValue)
};
};
[1]: https://dotintent.github.io/react-native-ble-plx/
[2]: https://github.com/dotintent/react-native-ble-plx/wiki
[3]: https://www.polidea.com/blog/ReactNative_and_Bluetooth_to_An_Other_level/
Here's a brief overview of the process that I need help with:
Client uploads images to Firebase Storage at users/displayName/uid/ - this step is clear and done. No help needed on this one.
Then, client presses "request a quote" button which saves the URLs of the images (the ones that were uploaded to Storage in step 1) to Firebase Firestore - this step is clear and done. No help needed on this one.
After the URLs are saved to the Firestore, I want those images from Step 1 to be moved within the same bucket of the Storage to users/displayName/uid/order1. So, basically, from users/displayName/uid/ to users/displayName/uid/order1. - I need help with writing out the right Javascript code for this action. I tried the code snippet from below but it does not seem to work, not sure what is wrong.
At some point in the future, the same client will need to upload more images thus repeating the first 3 steps above. However, on the 3rd step, I will need his images to be moved from users/displayName/uid/ to users/displayName/uid/order2. The problem lies in me not knowing how to render the last part of the directory as order2 and not the same order1 as in the 3rd step. The number after "order" will basically need to increase every time the client repeats the steps. I have no idea of how to write out the right code for this. Please help.
If it helps, here are my functions that upload images to Firebase Storage and Firebase Firestore:
// Upload to Storage
handleUpload = () => {
this.state.files.forEach((file) => {
const storageRef = firebase.storage().ref(`users/${this.state.displayName}/${this.state.uid}/${file.name}`);
var task = storageRef.put(file)
// Progress
task.on("state_changed", snapshot => {
const progress = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 100
);
this.setState({ progress });
},
// Error
error => {
console.log(error);
},
// Additional function to update state with all files uploaded
() => {
firebase.storage()
.ref(`users/${this.state.displayName}/${this.state.uid}`)
.child(file.name)
.getDownloadURL()
.then(url => {
this.setState(state => {
const urls = [...state.urls, url];
return {
urls
};
});
});
// Empty file upload
this.setState({ progress: 0 })
this.setState({ files: [] })
}
);
})
};
// Saving to Firestore - PRESS GET A QUOTE TO ACTIVATE
async saveToFirestore() {
// Getting it all from storage first
const listRef = firebase.storage().ref(`users/${this.state.displayName}/${this.state.uid}`)
const res = await listRef.listAll()
const urlPromises = res.items.map((itemRef) => {
return itemRef.getDownloadURL()
})
const urls = await Promise.all(urlPromises)
// Then, we save it all to Firestore
firebase.firestore().collection('Documents/').doc(this.state.displayName).set({
documents: urls,
quote: 'pending',
name: this.state.displayName,
email: this.state.email,
emailVerified: this.state.emailVerified,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
userId: this.state.uid
})
.then(() => {
const listRef = firebase.storage().ref(`users/${this.state.displayName}/${this.state.uid}`)
listRef.listAll().then((res) => {
console.log(res)
res.items.forEach(function (item) {
console.log(item)
firebase.storage().ref(`users/${this.state.displayName}/${this.state.uid}/order1/${item.name}`).put(item);
});
}).catch(function (error) {
// Uh-oh, an error occurred!
});
this.setState({ quote: "pending" })
firebase.firestore().collection('Documents/').doc(this.state.displayName).get().then((doc) => {
if (doc.exists) {
doc.data().documents.forEach(url => {
this.setState(state => {
const documents = [...state.documents, url];
return {
documents
};
});
})
this.setState({ createdAt: doc.data().createdAt.toDate().toString() })
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function (error) {
console.log("Error getting document:", error);
});
})
.catch((error) => {
console.error("Error writing document: ", error);
});
}
Please let me know if anything else is needed.
Thank you very much!
As far as I can tell, you have two parts to your question:
Determine the folder for the next order
Write all files from the root to this folder
I'll focus on the first step in this answer.
Determine the folder for the next order
To determine the next order? folder, we'll get a list of all files/folders from Storage, and then filter them to determine the highest number in there:
var ref = firebase.storage().ref("62849565");
ref.listAll().then(function(res) {
// Determine the next order folder
const orderFolders = res.prefixes.filter((folder) => folder.name.match(/^order(\d+)$/));
let highestOrderNumber = 0;
orderFolders.forEach((folder) => {
const match = folder.name.match(/^order(\d+)$/);
const number = parseInt(match[1]);
if (number > highestOrderNumber) {
highestOrderNumber = number;
}
})
const nextOrderFolderPrefix = "order" + (highestOrderNumber+1);
// Move the files from the root to the new folder
res.items.forEach(function(itemRef) {
// TODO: read file from root and write it to the next order folder
});
}).catch(function(error) {
console.error(error);
});
(Also see this jsbin where I created/tested this code)
In the above:
The res.prefixes.filter((folder) => folder.name.match(/^order(\d+)$/)) ensures we only consider folders following the order1 naming pattern.
We then loop over the folders to find the highest number.
We them determine the path for the next folder.
Write all files from the root to the new folder
The TODO in this code is to move the actual file. There is no operation in Cloud Storage (or its Firebase SDK) to move a file. So you will have to do a sequence of read the file, write it to its new location, and removing it from its original location. I recommend searching around a bit, as I'm pretty sure this must have been asked before.
I have created a Cloud Function that trigger on any new file upload in Firebase Storage. Once successful upload function will update its metadata, but even though setting new metadata with 'setMetadata()' is not getting applied. There is no error during the process and but on checking for updated metadata, the new one is not reflecting.
exports.onImageUpload = functions.storage.object().onFinalize(async (object) => {
const storageRef = admin.storage().bucket(object.bucket);
var metadata = {
'uploader': 'unknown'
}
await storageRef.file(object.name).setMetadata(metadata).then(function(data) {
console.log('Success');
console.log(data);
return;
}).catch(function(error) {
console.log(error);
return ;
});
return;
});
There is no error, and on Cloud Function log its printing 'Success' message. Also "metageneration: '2'" property also got updated, which means it should have updated metadata with new values, but it didn't.
The problem comes from the fact that if you want to set custom key/value pairs they must be in the metadata key of the object you pass to the setMetadata() method, i.e. the metadata object in your case. This is explained in the API Reference Documentation for node.js.
So the following will work:
exports.onImageUpload = functions.storage.object().onFinalize(async (object) => {
const storageRef = admin.storage().bucket(object.bucket);
var metadata = {
metadata: {
'uploader': 'unknown'
}
}
try {
const setFileMetadataResponse = await storageRef.file(object.name).setMetadata(metadata);
console.log('Success');
console.log(setFileMetadataResponse[0]);
return null;
} catch (error) {
console.log(error);
return null;
}
});
I am trying to write a back-end in NodeJS for a newsreader app. The idea is to check a number of RSS feeds every few minutes, save new items to a database, and emit an event to connected users when a new item is added to the database.
I am having trouble writing a module which saves to the database. The desired behaviour is as follows:
Take the parsed RSS feeds, which are ordered in an array from newest to oldest, as input
For each item, starting with the newest, attempt to save it in the database
If the save was successful, log 'Save successful' to the console and attempt the next item
If the save fails, because the item already exists in the database, stop running the module.
The database model is already configured so the database does reject it when I try to save an item which already exists. However, the code below never logs the successful saves, and if I drop the database, it only saves the first document and then gives me a duplicate key error.
Any idea what I am doing wrong?
// Module
var { mongoose } = require('../db/mongoose');
var { Item } = require('../models/item');
var { scrape } = require('./scrape')
var { parse } = require('./parse')
var updateNewsDatabase = function() {
return new Promise((resolve, reject) => {
console.log('Scraping now')
scrape().then((rssFeeds) => {
var output = parse(rssFeeds);
saveEachNewsItem(output)
.catch((e) => {
console.log('Error:', e);
resolve()
})
})
});
}
async function saveEachNewsItem(newsItems) {
for (let item of newsItems) {
console.log('Attempting to save document')
var itemToSave = new Item(item);
await itemToSave.save()
.then((err, docs) => {
if (docs) {
console.log('Saved document to database')
}
if (err) {
throw new Error(err)
}
});
}
}
module.exports = { updateNewsDatabase }
In this part of your code, you are throwing exception and it makes it stop the for loop.
await itemToSave.save()
.then((err, docs) => {
if (docs) {
console.log('Saved document to database')
}
if (err) {
throw new Error(err)
}
});
I am having a node called Events in Firebase. It consists of child objects like: address, description, longitude, latitude. Before a user deletes an event node I want to copy it to the same database to a node called eventsDeleted.
This is the code for deleting the node:
removeEvent(eventId, groupId) {
return new Promise((resolve, reject)=> {
this.eventRef.child(groupId).child(eventId).remove();
resolve();
});
}
This is the code for creating the node:
addEvent(data:any) {
console.log('Form data', data.group);
let localEventRef = firebase.database().ref('events').child(data.group.split(',')[1]);
let storageRef = firebase.storage().ref();
let file = data.image;
let uploadTask = storageRef.child('eventImages/' + UuidGenerator.generateUUID()).put(file);
uploadTask.on('state_changed', function (snapshot) {
}, function (error) {
// Handle unsuccessful uploads
console.error(error);
}, function () {
// Handle successful uploads on complete
let downloadURL = uploadTask.snapshot.downloadURL;
let keyOfNewEvent = localEventRef.push(
new Event(
null,
firebase.app().auth().currentUser.uid,
data.description,
data.location.address,
0
)
).key;
localEventRef.child(keyOfNewEvent).update({eventId: keyOfNewEvent});
});
}
Never mind the code for uploading an image. I just need a way to copy that entire node if possible then paste it somewhere in the database. Thanks in advance.
When the user clicks delete, make sure to get the object that's being deleted, if you were to query your database for that object you can use the .once to retrieve the object otherwise you can just jump to the removeEvent function directly.
localEventRef.child(keyOfEvent).once("value", function(snapshot) {
//one data is returned, you can then call the removeEvent fn
let eventToBeRemoved = snapshot.val();
//assuming you have the eventid, groupid then.
removeEvent(eventId, groupId, eventToBeRemoved);
});
removeEvent(eventId, groupId, eventObjectToBeRemoved) {
//firebase 3.x comes with a promise method
firebase.database().ref('eventsDeleted/' + groupId + '/' + eventId )
.set({...eventObjectToBeRemoved})
.then(function () {
eventRef.child(groupId).child(eventId).remove();//you can now remove
})
.catch(function (error) {
console.error("error occured trying to add to deletedEvents", error);
});
});
}