How do I get user details in Firebase Storage? - javascript

I'm a new programmer and very new to firebase and I'm trying to get the current user files info to display on the screen, it seems that my problem is that I can get the URL and the metadata separately, how do I combine them? how can I take everything at once?
I need to show the file name, date, time, link to download.
const getUserFiles = async () => {
if (!userUID) {
return null;
}
let listRef = storageRef.child(userUID);
listRef.listAll().then(res => {
// res.prefixes.forEach((item) => {
// });
res.items.forEach(item => {
item.getMetadata().then(item => {
var file = {
name: item.name.toString(),
timeCreated: item.timeCreated.toString(),
link: '',
};
myFiles.push(file);
});
});
res.items.forEach(item => {
let counter = 0;
item.getDownloadURL().then(url => {
myFiles[counter].link = url.toString();
});
});
});
console.log(myFiles);
};
the current method don't work! and notice that the userUID its only the uid without the user (local state)
Thanks!

The problem is with the asynchronous calls. You're making an async call in forEach and forEach expects a synchronous function.
You can change the logic to use for-of instead.
See below:
const getUserFiles = async () => {
if (!userUID) {
return null;
}
let listRef = storageRef.child(userUID);
const res = await listRef.listAll();
for (const itemRef of res.items) {
const itemMetadata = await itemRef.getMetadata();
const url = await itemRef.getDownloadUrl();
var file = {
name: itemMetadata.name.toString(),
timeCreated: itemMetadata.timeCreated.toString(),
link: url,
};
myFiles.push(file);
}
console.log(myFiles);
}

Related

Firebase Cloud Functions Async

I am making a function for firebase cloud functions, I want a function to be called every time a new document is created in "posts". I want this function to perform the tasks that I put inside the "onCeatePost" function.
The problem I have is that I'm not sure if this is the correct way to structure such a function.
In several firebase examples I have seen that it is always called return _; or return null; at the end of a task, but I don't know how to structure the function so that all the tasks are carried out, could someone help me to restructure my function or tell me what is wrong please.
There are several if statements in the function, if the created publication does not comply with them, I would like it to skip them but continue with the other tasks that I put inside the function.
I don't know if it's too much to ask, but I'm new to this language and I haven't been able to find the answer I'm looking for. Thank you!
exports.onPostCreate = functions.firestore.document("/posts/{postId}").onCreate(async (snap) => {
const post = snap.data();
if (post) {
try {
const topic = post.topic;
const contentForFeed = post.contentForFeed;
const uid = post.uid;
const previous = post.prev;
await db.collection("users").doc(uid).update({"stats.posts": admin.firestore.FieldValue.increment(1)});
if (topic) {
await db.collection("topics").doc(topic.id).collection("user-authors").doc(uid).set({"date": snap.createTime});
}
if (contentForFeed == true) {
const userPath = db.collection("users").doc(uid);
await userPath.update({"stats.lastUpdate": snap.createTime});
}
if (previous) {
const previousId = previous.id;
const previousUid = previous.uid;
const refPrev = db.collection("posts").doc(previousId);
await db.runTransaction(async (t) => {
const doc = await t.get(refPrev);
const priority = doc.data().stats.date;
const newDate = new admin.firestore.Timestamp(priority.seconds + 120, priority.nanoseconds);
await db.collection("posts").doc(previousId).update({"newDate": newDate});
});
if (previousUid != uid) {
const path = db.collection("users").doc(uid).collection("user-posts");
const dataToSet = {"timestamp": snap.createTime, "uid": uid, "postId": onReplyToPostId};
await path(dataToSet);
}
}
} catch (err) {
functions.logger.log(err);
}
} else {
return null;
}
});
You'll find below the adapted code (untested) with 4 corrections.
Here are explanations for the two most important ones:
(Correction 2) In a transaction you need to use the transaction's update() method and not the "standard one"
(Correction 4) When all the asynchronous work is complete you need to return a value or a Promise. See this documntation page for more details.
exports.onPostCreate = functions.firestore
.document('/posts/{postId}')
.onCreate(async (snap) => {
const post = snap.data();
if (post) {
try {
const topic = post.topic;
const contentForFeed = post.contentForFeed;
const uid = post.uid;
const previous = post.prev;
await db
.collection('users')
.doc(uid)
.update({
'stats.posts': admin.firestore.FieldValue.increment(1),
});
if (topic) {
await db
.collection('topics')
.doc(topic.id)
.collection('user-authors')
.doc(uid)
.set({ date: snap.createTime });
}
if (contentForFeed == true) {
const userPath = db.collection('users').doc(uid);
await userPath.update({ 'stats.lastUpdate': snap.createTime });
}
let previousUid; // <= Correction 1
if (previous) {
const previousId = previous.id;
previousUid = previous.uid; // <= Correction 1
const refPrev = db.collection('posts').doc(previousId);
await db.runTransaction(async (t) => {
const doc = await t.get(refPrev);
const priority = doc.data().stats.date;
const newDate = new admin.firestore.Timestamp(
priority.seconds + 120,
priority.nanoseconds
);
t.update(refPrev, { newDate: newDate }); // <= Correction 2
});
if (previousUid != uid) {
const path = db
.collection('users')
.doc(uid)
.collection('user-posts');
const dataToSet = {
timestamp: snap.createTime,
uid: uid,
postId: onReplyToPostId,
};
await path.add(dataToSet); // <= Correction 3
}
}
return null; // <= Correction 4
} catch (err) {
functions.logger.log(err);
}
} else {
return null;
}
});

Wait for angularfire storage upload

I'm doing the "complete your profile" part of my app and I want to upload some images to Firebase.
It is all done in 2 methods located in my "auth" service. I'm having issues getting the data from the uploads, this is the code so far:
async updateUserProfile(
profilePicture: File,
name: string,
birthdate: Date,
countryCode: string,
photoID: File
) {
let updatedAppUser: authenticatedUser;
this.appUser.pipe(take(1)).subscribe((currentAppUser) => {
updatedAppUser = currentAppUser;
});
const uploadPackage = new FormData();
uploadPackage.append(updatedAppUser.UID, profilePicture);
uploadPackage.append(updatedAppUser.UID + "_", photoID);
let uploadedData = await this.fileUpload(uploadPackage);
let profilePicturePath: string;
let photoIDPath: string;
//**********************************************
//HERE IS THE PROBLEM-- I THINK THIS IS WRONG
//**********************************************
if (uploadedData) {
profilePicturePath = uploadedData[0];
photoIDPath = uploadedData[1];
}
//TO-DO: call backend and update the user profile
//after success from backend call
//console.log("photoID Path: ", photoIDPath);
updatedAppUser.showKYC = false;
updatedAppUser.userProfilePicture = profilePicturePath;
updatedAppUser.isPendingValidation = true;
updatedAppUser.userName = name;
updatedAppUser.userBirthdate = birthdate;
updatedAppUser.userCountryCode = countryCode;
//save to local storage
this.storeAuthData(updatedAppUser);
//new updated appuser
this.appUser.next(updatedAppUser);
}
And this is the method I'm using to upload data to Firebase:
private async fileUpload(data: FormData) {
const filePaths: string[] = [];
const promises: AngularFireUploadTask[] = [];
for (const value of data.entries()) {
const uploadTask = this.firebaseStorage.ref(value[0]).put(value[1]);
promises.push(uploadTask);
}
const promiseArray = await Promise.all(promises);
if (promiseArray) {
promiseArray.forEach(async (filePromise) => {
filePaths.push(await filePromise.ref.getDownloadURL());
});
return filePaths;
}
}
I'd probably use a second Promise.all for the download URL retrievals, and remove the use of await since it makes things confusing:
private async fileUpload(data: FormData) {
const promises: AngularFireUploadTask[] = [];
for (const value of data.entries()) {
const uploadTask = this.firebaseStorage.ref(value[0]).put(value[1]);
promises.push(uploadTask);
}
Promise.all(promises).then((tasksArray) => {
const filePaths = tasksArray.map((task) => task.ref.getDownloadURL());
return Promise.all(filePaths);
}
}

Vue and Tensorflow: Save classifier examples to localstorage

I'm using #tensorflow-models/knn-classifier to classify my models and #tensorflow-models/mobilenet to study new models.
methods: {
async init() {
// load the load mobilenet and create a KnnClassifier
this.classifier = knnClassifier.create();
this.mobilenet = await mobilenetModule.load();
},
async addExample() {
let selected = document.getElementById("options");
this.class = selected.options[selected.selectedIndex].value;
const img = tf.browser.fromPixels(this.$children[0].webcam.webcamElement);
const logits = this.mobilenet.infer(img, "conv_preds");
this.classifier.addExample(logits, parseInt(this.class));
}
How can I save to localStorage my examples, which I added to the classifier and then load them in init() method? Because currently, I'm losing all my models after the page refresh.
Sorry maybe for the wrong terminology, I'm so new in Tensorflow js.
So, after small research I managed to save and load data with the next methods:
async toDatasetObject(dataset) {
const result = await Promise.all(
Object.entries(dataset).map(async ([classId, value]) => {
const data = await value.data();
return {
label: Number(classId),
data: Array.from(data),
shape: value.shape
};
})
);
return result;
},
fromDatasetObject(datasetObject) {
return Object.entries(datasetObject).reduce(
(result, [indexString, { data, shape }]) => {
const tensor = tf.tensor2d(data, shape);
const index = Number(indexString);
result[index] = tensor;
return result;
},
{}
);
},
And then I just load it:
this.classifier.setClassifierDataset(
this.fromDatasetObject(JSON.parse(localStorage.getItem("my-data")))
);

Firebase database ref on not updating at first

I'm building a chat app with react native and firebase.
When the user sign in, previous messages have to load at first launch but it provides empty arrays to promise at first then when I connect again, it shows up...
I'm using snack and I have to change code to see the chat histroy(In order to update the device).
How can I load at first launch?
edit:
firebase structure
userName:
-rooms:
-otherUser:
-messages
-name
Code:
firebase.database().ref(`userName/rooms`).on('child_added', snapshot => callback(parse(snapshot)));;
const parse = snapshot => {
//console.log(snapshot);
const { name } = snapshot.val();
const { key: _id } = snapshot;
const rooms = {
_id,
name
};
return rooms;
};
And this is happening in my useEffect:
const loadRequiredMaterials = async () => {
return new Promise(async (resolve, reject) => {
//var loadedRooms = [...rooms]
var loadedRooms = []
await on(oldRooms => {
loadedRooms = [oldRooms, ...loadedRooms]
});
await resolve([loadedRooms]);
})
}
if (firebase.auth().currentUser) {
console.log(firebase.auth().currentUser)
loadRequiredMaterials()
.then((result)=> {
console.log(result)
setRooms(result[0]);
})
}

(Vuejs, Vuetify) How to avoid, loading objects twice form an API?

Im kind of an Beginner within Vuejs. Im Creating a Site which shows content that is loaded from the Backend into the Frontend. Therfore, I use Axios to connect to the API with this code:
contentList: [],
};
const mutations = {
setContent (state) {
axios
.get("http://backendapi/content")
.then(res => {
const data = res.data;
for (let key in data) {
const object = data[key];
state.contentList.push(object)
}
});
}
};
const actions = {
initContent: ({commit}) =>{
commit('setContent');
}
};
and on my Page i load the Contentlist when mounted:
mounted() {
this.$store.dispatch('initContent');
this.content = this.$store.getters.contentList
}
But the Problem is, every Time i go to another Page and back to this Page, the Content is loaded again into the contentList and everithing ist doubled.
Can someone explain, how to write this in "good Code" and avoiding loading everything double?
Thank you
You can check if already have the content on your list before making the request.
setContent (state) {
if (state.contentList.length == 0){
axios
.get("http://backendapi/content")
.then(res => {
const data = res.data;
for (let key in data) {
const object = data[key];
state.contentList.push(object)
}
});
}
}
or if you want to update each time just make sure the variable is reset each time.
axios
.get("http://backendapi/content")
.then(res => {
const data = res.data;
let contentList = [];
for (let key in data) {
const object = data[key];
contentList.push(object);
}
state.contentList = contentList;
});
Just check whether the content is already loaded before doing an axis call. Also the action is meant to execute the axios call:
const mutations = {
setContent (state, data) {
state.contentList = data
}
};
const actions = {
async initContent: ({commit, state}) =>{
if (state.contentList.length === 0) {
try {
let result = []
let response = await axios.get("http://backendapi/content")
for (let key in response.data) {
result.push(response.data[key])
}
commit('setContent', result);
} catch (error) {
// something went wrong
}
}
}
};

Categories

Resources