Firestore Angular2 Retrieve documents based on current user - firebase

I have started developing a mobile app using IONIC, ANGULAR against Google Firestore. This app will consume mostly documents based on the current user and most of my queries I will need to pass in this user. However, I am experiencing issues getting documents from firestore using the following code from my service to the page:
user-profile.service.ts
async get(){
// await this.afAuth.user.subscribe(currentUser => {
// if(currentUser){
// this.userId = currentUser.uid;
// console.log("User Current ID: " + this.userId);
console.log("PP:" +this.afAuth.auth.currentUser.uid)
return this.userProfilesCollection.doc<UserProfile>(this.afAuth.auth.currentUser.uid).snapshotChanges().pipe(
map(doc => {
const id = doc.payload.id;
const data = doc.payload.data();
return{id, ...data };
}
)
);
}
landing-page.page.ts
export class LandingPage implements OnInit {
userProfile : UserProfile;
constructor(
private authService : AuthService,
private loadingController : LoadingController,
private userProfileService: UserProfileService,
private router : Router
) {
}
ngOnInit() {
this.loadUserProfile();
}
async loadUserProfile() {
const loading = await this.loadingController.create({
message: 'Loading user profile'
});
await loading.present();
this.userProfileService.get().then(res=>{
console.log(res);
loading.dismiss();
})
// this.userProfileService.get().then(
// res =>
// {
// loading.dismiss();
// res.subscribe(c=>
// {
// this.userProfile = {
// cellphone: c.data.cellphone,
// dateOfBirth: c.data.dateOfBirth,
// email: c.data.email,
// firstname: c.data.firstname,
// gender: c.data.gender,
// id: c.data.id,
// lastname: c.data.lastname,
// telephone: c.data.telephone,
// userId: c.data.userId,
// website: c.data.website
// };
// });
// }
// );
}
}
Does anyone have a simple example how to achieve this, and I need to use the load profile to navigate across the application as the currently logged in user will be able to manage their profile and the list items (documents) linked to them?

Related

Firebase listUsers fails to get All users after a certain page

I'm using a pubsub firebase function (cron), and inside this function Im calling firebase auth users, to fill some missing data in a profile collection
Im paginating with the pageToken, the first token passed is undefined then I save it in a config db and read the token to get the next page
The issue is that I have 170K records, and listusers returns an undefined token at the 6th page (6k users) which is frsutrating
here is the code:
functions.pubsub
.schedule('*/30 * * * *')
.onRun(async () => {
const page = firestore.collection('config').doc('pageToken');
const doc = (await page.get()).data();
// Check if last page don't run again
const last = doc?.last;
if (last) return;
// Load page
const pageToken = doc?.pageToken || undefined;
let pageNumber = doc?.pageNumber as number;
return firebaseAdmin
.auth()
.listUsers(1000, pageToken)
.then(async listUsersResult => {
for (const userRecord of listUsersResult.users) {
// Fetch Profile
try {
const profile = await firestore
.collection('profiles')
.doc(userRecord.uid);
// data foramtting here
// compared profile data & fixed data
const payload = JSON.parse(
JSON.stringify({
...profileData,
...{
lastName,
firstName,
language,
...(!userRecord.phoneNumber && {
phone,
}),
},
})
);
// Profile doesn't exist : Create
if (!profileData && payload) {
await profile.create({
...payload,
...{
Migrated: true,
},
});
} else if (profileData && payload) {
const data = compare(profileData, payload);
if (data) {
// Profile exists: Update
await profile.update(data);
if (userRecord.phoneNumber)
await profile.update({
phone: firebaseAdmin.firestore.FieldValue.delete(),
});
}
}
} catch (err) {
functions.logger.error('Some Error', err);
}
}
if (!listUsersResult.pageToken) {
return await firestore
.collection('config')
.doc('pageToken')
.update({
last: true,
});
}
// List next batch of users.
pageNumber++;
return await firestore
.collection('config')
.doc('pageToken')
.update({
pageToken: listUsersResult.pageToken,
pageNumber,
});
});
});
so after in page 6, I have a last:true property added to the firestore however there is 164k data are missing
any idea ?

Nuxt middleware: How to access vuex store?

I am trying to block user on client-side from editing another user's profile. My URL structure is like so:
/users/edit/XpuBjKFoLSRHJAloNg38Amqn2jQ2
Thus, if user tries to acccess path of another user (ie, http://localhost:3000/users/edit/blahdasd) I need to redirect him to homepage.
I tried to set up an anonymous middle ware like so on my page:
export default {
middleware({ store, params, redirect }) {
if (store.state.user.currentUser.uid !== params.uid) {
return redirect('/')
}
},
But, I get page error of:
Cannot read property 'uid' of null
So, how do I correctly access the store here? I have no problem accessing uid from computed property on same page:
user() {
return this.$store.state.user.currentUser
},
Update (more information):
Here is my edit user profile page:
export default {
middleware({ store, params, redirect }) {
if (store.state.user.currentUser.uid !== params.uid) {
// return redirect('/')
console.log(store.state.user.currentUser.uid)
console.log(params.uid)
}
},
computed: {
user() {
return this.$store.state.user.currentUser
},
And here is my store/user.js file:
export const state = () => ({
currentUser: null,
})
export const mutations = {
SET_AUTH_USER(state, payload) {
state.currentUser = payload
}
}
export const actions = {
async onAuthStateChangedAction({ commit, dispatch }, { authUser }) {
console.log('auth state changed....')
try {
if (authUser && authUser.emailVerified) {
const {
uid,
email,
emailVerified,
displayName = '',
photoURL,
metadata,
providerData,
providerId,
tenantId
} = authUser
commit('SET_AUTH_USER', {
uid,
email,
emailVerified,
displayName,
photoURL,
metadata,
providerData,
providerId,
tenantId
})
console.log('fetching profile...')
await dispatch('getUserProfile', authUser)
} else {
console.log('User logged out or not verified')
return null
}
} catch (error) {
console.error('Error with Auth State observer: ', error)
}
},

How can I build a one to one (private) chat application in react native using fire-base as a back-end?

I want to add a chat feature in my application, but the problem is while working with react-native-gifted-chat and firebase as a backend and its secured rules that gives an error of missing _id and user.
I tried using the firebase database and without using secured rules but the issue is it seems to be like a group chat rather than one to one (private) chat.
async UNSAFE_componentWillMount() {
const name = auth().currentUser.displayName;
const friendName = this.state.friendName;
this.setState({ name: name });
const ref = await database().ref(`chatmessages/`);
// Fetch the data snapshot
const snapshot = await ref.once('value');
console.log(snapshot, "Snapshot")
console.log(ref, "database");
}
componentDidMount() {
this.on(message => {
console.log(this.state.messages, 'old message')
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, message),
})
)
});
}
componentWillUnmount() {
this.off();
}
get uid() {
return (auth().currentUser || {}).uid;
}
get ref() {
return database().ref(`chatmessages/`)
// .set();
}
parse = async snapshot => {
const data = snapshot.val();
const userID = auth().currentUser.uid;
const friendID = this.state.friendID;
const validate = data.friend === friendID && data.user._id === userID ||
data.user._id === friendID && data.friend === userID;
console.log(data.user, data.user._id, data.user.name, "MEssage Data")
if (validate) {
const { timestamp: numberStamp, text, user, friend } = await data;
const { key: _id } = snapshot;
console.log(_id, user,'Firebase Message Id')
const timestamp = new Date(numberStamp);
const message = {
_id,
timestamp,
text,
user: data.user,
friend
};
console.log(message, "Gifted")
return message;
}
};
on = callback =>
this.ref
.limitToLast(20)
.on('child_added', snapshot => callback(this.parse(snapshot)));
get timestamp() {
return firebase.database.ServerValue.TIMESTAMP;
}
// send the message to the Backend
send = messages => {
for (let i = 0; i < messages.length; i++) {
const { text, user } = messages[i];
const message = {
text,
user,
friend: this.state.friendID,
timestamp: this.timestamp,
};
this.append(message);
}
};
append = message => this.ref.push(message);
// close the connection to the Backend
off() {
this.ref.off();
}
get user() {
return {
name: auth().currentUser.displayName,
_id: this.uid
};
}
render() {
<GiftedChat
text={this.state.text}
onInputTextChanged={text => this.setState({ text: text })}
messages={this.state.messages}
isAnimated
onSend={messages => this.send(messages)}
user={this.user}
renderActions={this.renderCustomActions}
/>
);
}
}
I want a one to one chat created with firebase and react-native-gifted-chat
It's essentially the same except you limit it to just two people. This article explains more on how to handle one to one chat https://medium.com/#edisondevadoss/react-native-chat-using-firebase-d4c0ef1ab0b5

Create reducer about user state

I'm trying to apply reflux/ngrx on my current front-end project.
I want to take advantage of this in order to change a slight functionality: Change current user related tasks in order to use a single user state.
Current user related tasks: Currently, I'm using an traditional model in order to achieve user login process... UserService is able to check user credentials. Once it's been checked I store user information on an AppService:
export class LoginComponent implements OnInit {
private fb: FormBuilder;
private form:FormGroup;
private commty: UsersService;
private router: Router;
private appState: AppState;
private alerts: Array<Object>;
constructor()
{
this.alerts = [];
}
ngOnInit():void {
this.form = this.fb.group({
user: ['', Validators.required],
passwd: ['', Validators.minLength(6)]
});
}
public checkPasswd():void {
this.clearAlerts();
this.commty.checkPasswd(this.form.value.mail, this.form.value.passwd)
.subscribe(
(result: any) => {
this.appState.user = result;
this.router.navigate(['/app']);
},
(error: any) => {
this.addAlert(error.message);
}
);
}
private addAlert(message: string): void {
this.alerts.push({type: 'danger', msg: message});
}
public closeAlert(index): void {
this.alerts.splice(index, 1);
};
private clearAlerts(): void {
this.alerts.splice(0, this.alerts.length);
}
}
I'm a bit confused about how to move this code in order to use reflux/ngrx. I'ce read a bit about this topic, nevertheless I'm not quite able to figure out how to move my code. Up to now, I've created an single Store and User interfaces:
store.interface.ts:
export interface IStore {
user: IUser
sources: ISourceRedux;
}
user.interfcae.ts:
export interface IUser {
id: string;
name: string;
username: string;
customer: string;
}
The next step I think I need to do is to create reducers. This step is which I don't quite understand how build this code. Up to now
user.initialstate.ts:
export function initialUserState(): IUser {
return {
id: '',
name: '',
username: '',
customer: '',
sources: []
};
};
user.reducer.ts
export class User {
private static reducerName = 'USER_REDUCER';
public static reducer(user = initialUserState(), {type, payload}: Action) {
if (typeof User.mapActionsToMethod[type] === 'undefined') {
return user;
}
return User.mapActionsToMethod[type](user, type, payload);
}
// ---------------------------------------------------------------
// tslint:disable-next-line:member-ordering
private static mapActionsToMethod = {};
}
Which reducers I should create in order to:
Check credentials.
If credentials are right get this user and update User state store.
If credentials are wrong inform the process has failed.
Perhaps I'm merging concepts... I need some lights...
EDIT
public connect(user: string, currentPasswd: string, extraHttpRequestParams?: any): Observable<UserDTO> {
return this.checkPasswdWithHttpInfo(id, currentPasswd, extraHttpRequestParams)
.map((response: Response) => {
if (response.status === 204) {
return undefined;
} else {
return response.json();
}
}).catch((error: any) => {
if (error.status >= 500) {
return Observable.throw(new Error(error.status));
}
else { //if (error.status >= 400) {
const body = error.json() || '';
const code = body.error || JSON.stringify(body);
const message = body.message || JSON.stringify(body);
return Observable.throw(ApiError.create(code, message));
}
});
}
Ok so this is the next question of your "Integrate ngrx into my code" =).
What you're looking for is : https://github.com/ngrx/effects
The idea behind effects is that an effect let you catch an Action, do side effect (API call or whatever) and you can then dispatch another Action (often success or error).
Flow example to connect a user :
--| [from component] Dispatch action USER_CONNECT
--| [from user.effect.ts]
----| Catch action ofType('USER_CONNECT')
----| Do what you need to do (API call for ex)
----| When the response comes back :
------| If success : Dispatch USER_CONNECT_SUCCESS
------| If error : Dispatch USER_CONNECT_ERROR
Of course when you dispatch either USER_CONNECT_SUCCESS or USER_CONNECT_ERROR you can pass additional data in the payload (for example user information or the error).
Here's a full example :
#Injectable()
export class UsersEffects {
constructor(
private _actions$: Actions,
private _store$: Store<IStore>,
private _userService: UserService,
) { }
#Effect({ dispatch: true }) userConnect$: Observable<Action> = this._actions$
.ofType('USER_CONNECT')
.switchMap((action: Action) =>
this._userService.connect(action.payload.username, action.payload.password)
.map((res: Response) => {
if (!res.ok) {
throw new Error('Error while connecting user !');
}
const rslt = res.json();
return { type: 'USER_CONNECT_SUCCESS', payload: rslt };
})
.catch((err) => {
if (environment.debug) {
console.group();
console.warn('Error catched in users.effects.ts : ofType(USER_CONNECT)');
console.error(err);
console.groupEnd();
}
return Observable.of({
type: 'USER_CONNECT_ERROR',
payload: { error: err }
});
})
);
}
You can take a look into my project Pizza-Sync were I did something similar (except that I don't catch in case of error and do not dispatch if there's an error).

AngularFire2 - Joining members and users

I am trying to list users conversations in an AngularFire2 app, which strucutre is the following:
chats
"chat1": {
title: "First chat",
lastMessage: "Hello world",
members: {
"user1": true,
"user2": true,
}
}
users
"user1": {
name: "Ben",
surname: "Bennsay"
}
"user2": {...}
I am trying to map and list chats in a way that i can easily display the chats participants names bellow the last message.
Question 1: This example differs a little bit from then official recommendation but i feel it would still be valid and scalable. Am i right ?
Question 2: How to actually join members and users to have a users array in my chats list ?
Here is what i have so far.
// retrieve chats "user1" participates in
this.afChatsRef = this.af.database.list(this.datastore(), {
query: {
orderByChild: "/members/user1", // by user id in members
equalTo: true,
}
}).map(chats => {
chats.map(chat => {
// HMMM? WHAT TO DO HERE ?
});
return chats;
});
Thanks, in advance.
UPDATE i have also tried the following, which does not seem quite right (and i cannot access user properties).
this.af.database.list(this.datastore()).map(chats => {
chats.map(chat => {
// chat.users = [];
for (var key in chat.members) {
this.af.database.object("/users/" + key).subscribe(user => {
chat.members[key] = user;
});
}
return chat;
});
console.log(chats);
return chats;
});
You want to return the nested map and fetch the users inside of that. Something like this;
// retrieve chats "user1" participates in
this.afChatsRef = this.af.database.list(...).map(chats => {
// Note the return!
return chats.map(chat => {
// Avoid side effects by storing members separate from the keys
chat.memberData = {};
// Iterate keys and download members
Object.keys(chat.members||{}).forEach(uid => {
// `users` represents a service to cache and load users on demand
chat.memberData[uid] = users.load(uid);
});
});
return chats;
});
Here's a good way to create the users service with caching:
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Rx';
import { AngularFireDatabase } from 'angularfire2/database';
#Injectable()
export class UserProvider {
db: AngularFireDatabase;
users: Map<String, Observable<User>>;
constructor(db: AngularFireDatabase) {
this.db = db;
this.users = new Map();
}
load(userid:string) : Observable<User> {
if( !this.users.has(userid) ) {
this.users.set(userid, this.db.object(`members/${userid}`).valueChanges());
}
return this.users.get(userid);
}
}
export interface User {
name:string;
nick:string;
}
And here is a working example of async joins in AngularFire2.

Resources