Firebase trying to return onAuthStateChanged - firebase

Trying to implement firebase authentication, the login, sign in, logout are working so trying to return if user is logged in or not.
The userID from my store is returning as undefined/not updating when trying to display it.
When checking in vue dev tools the userId never changes.
It is not logging as an error in my console just as undefined. in a single line with a grey textcolor
<template>
<div>
<button #click="log"></button>
</div>
</template>
<script>
import firebase from 'firebase'
import { mapState } from 'vuex'
export default {
name: 'Navigation',
data () {
return {
}
},
computed: {
...mapState({
userId: state => state.userId
})
},
methods: {
loggedOut() {
this.$store.dispatch('logout')
},
log() {
console.log(this.$store.userId)
}
}
}
</script>
^this is my vue file
import Vue from 'vue'
import Vuex from 'vuex'
import db from '../firebase'
import router from '../router';
// import firebase from '../firebase'
import firebase from 'firebase'
//https://github.com/robinvdvleuten/vuex-persistedstate
Vue.use(Vuex)
import createPersistedState from "vuex-persistedstate"
export const store = new Vuex.Store({
plugins: [createPersistedState()],
state: {
userId: ''
},
mutations: {
LOGOUT(state, userId) {
state.userId = '';
},
SET_USER_ID(state, userId) {
state.userId = userId;
}
},
actions: {
checkUserStatus({ commit, state }) {
return new Promise((resolve, reject) => {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
commit('SET_USER_ID', user.uid);
resolve(user.uid);
} else {
reject('User is not logged in.');
}
})
})
},
signIn(context, credentials) {
firebase.auth().createUserWithEmailAndPassword(credentials.username, credentials.password)
.then(data => {
router.push('/')
alert('Signed in!')
})
.catch(e => {
alert(e.message)
})
},
login(context, credentials) {
firebase.auth().signInWithEmailAndPassword(credentials.username, credentials.password)
.then(data => {
router.push('/')
alert('Logged inn')
})
.catch(e => {
console.log(e, ' Loggin failed')
alert(e.message)
})
},
logout(context) {
firebase.auth().signOut()
.then(data => {
router.push('/Login')
alert('logged out')
})
.catch(e => {
alert(e.message)
})
},
...
}
})
export default store

Related

Store State Issues - Next Redux Wrapper vs Redux ToolKit

FYI: Everything is working fine and all the flows are working as expected but logs are confusing.
I am using Redux ToolKit for managing redux state & using Next Redux Wrapper for Server Side rendering and caching queries.
CartSlice.js
import { createSlice } from '#reduxjs/toolkit';
export const cartSlice = createSlice({
name: 'cart',
initialState: { data: [] },
reducers: {
addToCart: (state, action) => {
const itemInCart = state.data.find((item) => item.id === action.payload.id);
if (itemInCart) {
itemInCart.quantity += action.payload.quantity;
} else {
state.data.push({ ...action.payload });
}
}
},
});
export const cartReducer = cartSlice.reducer;
export const { addToCart } = cartSlice.actions;
ServiceApi.js
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react'
import { HYDRATE } from 'next-redux-wrapper'
export const serviceApi = createApi({
reducerPath: 'serviceApi',
baseQuery: fetchBaseQuery({ baseUrl: '<base-url>' }),
extractRehydrationInfo(action, { reducerPath }) {
if (action.type === HYDRATE) {
return action.payload[reducerPath]
}
},
endpoints: (builder) => ({
getItemById: builder.query({ query: (id) => `id/${id}` }),
getItems: builder.query({ query: () => `/` }),
}),
})
export const { getRunningQueriesThunk } = serviceApi.util
export const { useGetItemByIdQuery, useGetItemsQuery } = serviceApi
export const { getItemById, getItems } = serviceApi.endpoints;
store.js
import { configureStore } from '#reduxjs/toolkit'
import { serviceApi } from './serviceApi'
import { createWrapper } from "next-redux-wrapper";
import { cartSlice } from "./cartSlice";
export const store = configureStore({
reducer: {
[cartSlice.name]: cartSlice.reducer,
[serviceApi.reducerPath]: serviceApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(serviceApi.middleware),
})
const makeStore = () => store
export const wrapper = createWrapper(makeStore, {debug: true});
Using getServerSideProps
export const getServerSideProps = wrapper.getServerSideProps(
(store) => async (context) => {
const { id } = context.query;
store.dispatch(serviceApi.endpoints.getItemById.initiate(parseInt(id)));
await Promise.all(store.dispatch(serviceApi.util.getRunningQueriesThunk()));
return {
props: {},
};
}
);
And using wrappedStore
const { store, props } = wrapper.useWrappedStore(pageProps);
On the UI flows everything is working as expected and i am able to add items in the cart and i can see the store state is getting updated by looking the UI.
But the logs from next redux wrapper is confusing:

'GiftedChat' API not rendering and unable to send/receive texts

So I'm basically developing a react-native Chat app using firebase as the backend and I'm stuck at this point where I am unable to send messages and not render the GiftedChat Api even. I'm pasting the Home component here can you please help me out. I'm trying to append the message to the giftedchat component and render it whenever i press the send button.
import React, { Component } from 'react' import { Text, View, Button }
from 'react-native' import { GiftedChat } from
'react-native-gifted-chat' import firebase from '../database/Firebase'
import AsyncStorage from '#react-native-community/async-storage'
class Home extends Component {
state = {
messages: [],
user: 'true',
userData: null
}
componentDidMount() {
const db = firebase.firestore()
const chatsRef = db.collection('chats')
this.readUser()
const unsubscribe = chatsRef.onSnapshot((querySnapshot) => {
const messagesFirestore = querySnapshot
.docChanges()
.filter(({ type }) => type === 'added')
.map(({ doc }) => {
const message = doc.data()
return { ...message, createdAt: message.createdAt.toDate() }
})
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
this.appendMessages(messagesFirestore)
})
return () => unsubscribe()
}
handleSend(messages) {
const writes = messages.map((m) => chatsRef.add(m))
Promise.all(writes)
}
appendMessages = (messages) => {
this.setState((previousMessages) => GiftedChat.append(previousMessages, messages))
}
async readUser() {
const userData = await AsyncStorage.getItem('userData')
if (userData) {
setUser(JSON.parse(userData))
}
}
async handlePress() {
const _id = Math.random().toString(36).substring(7)
const userData = { _id, name }
await AsyncStorage.setItem('userData', JSON.stringify(userData))
}
handleLogout = () => {
this.setState(() => ({
user: false
}))
}
render() {
if (this.state.user === 'true') {
return (<View>
<Button title="logout" style={{ width: 200 }} onPress={() => this.handleLogout()}></Button>
<GiftedChat messages={this.state.messages} user={this.state.userData}
onSend={() => this.handleSend()} />
</View>)
} else {
return (<View>
{this.props.navigation.navigate("Login")}
</View>)
}
} }
export default Home

React Native Firebase update information for all users in real time based on other users actions

Hi I'm currently trying to add users to a page using react native, redux, and firebase. When User 1 clicks join, they get added to the feed and likewise for other users. However, a problem I'm facing is when user 2 clicks join, they get added to the feed but don't get displayed on user 1's page unless the user 1 refocuses on the page after going away.
Here is my code for the page itself in react native
import React, { Component } from 'react';
import { Text, View, Button, TouchableOpacity, SafeAreaView, ScrollView, Image } from 'react-native';
import styles from '../styles.js'
import { connect } from 'react-redux'
import { FlatList } from 'react-native-gesture-handler';
import { FontAwesome5 } from '#expo/vector-icons';
import { Octicons } from '#expo/vector-icons';
import { FontAwesome } from '#expo/vector-icons';
import { addUser, removeUser, getLivingRoomUsers } from '../actions/livingRoomUser.js'
import { bindActionCreators } from 'redux'
class LivingRoom extends React.Component {
constructor(props) {
super(props);
this.state = {
inRoom: false,
isMuted: false
};
}
componentDidMount(){
this._unsubscribe = this.props.navigation.addListener('focus', () => {
this.props.getLivingRoomUsers()
});
}
joinRoom = () => {
this.props.addUser()
this.setState({ inRoom: true });
}
leaveRoom = () => {
this.props.removeUser(this.props.livingRoomUser)
this.setState({ inRoom: false });
}
...
render(){
return (
<View>
<SafeAreaView style={styles.livingRoomUserContainer}>
<FlatList
data={this.props.livingRoomUser.feed}
Here is my actions code for the redux portion:
export const addUser = () => {
return async (dispatch, getState) => {
try {
const { user } = getState()
const id = uuid.v4()
const livingRoomUser = {
id: id,
avatar: user.avatar,
username: user.username,
isMuted: false,
date: new Date().getTime(),
}
db.collection('livingroom').doc(id).set(livingRoomUser)
dispatch({
type: 'ADD_USER', payload: livingRoomUser
})
dispatch(getLivingRoomUsers())
} catch (e) {
alert(e)
}
}
}
export const removeUser = (livingRoomUser) => {
return async (dispatch, getState) => {
try {
db.collection('livingroom').doc(livingRoomUser.id).delete();
dispatch(getLivingRoomUsers())
//get living room users
} catch (e) {
alert(e)
}
}
}
export const getLivingRoomUsers = () => {
return async (dispatch, getState) => {
try {
const livingRoomUsers = await db.collection('livingroom').get()
let array = []
livingRoomUsers.forEach((livingRoomUser) => {
array.push(livingRoomUser.data())
})
dispatch({
type: 'GET_LIVING_ROOM_USERS', payload: orderBy(array, 'date', 'asc')
})
} catch (e) {
alert(e)
}
}
}
To summarize. I want the getUsers to be updated anytime someone adds/removes themself from the page. However, from my implementation currently actions only get updated for the current user and the feed only gets updated when the page is focused. How do I go about this?
use onSnapshot listener on the firestore then you can get the latest updates as the store change
export const getLivingRoomUsers = () => {
return async (dispatch, getState) => {
try {
db.collection('livingroom').onSnapshot(snapshot => {
let array = snapshot.docs.map(d => d.data());
dispatch({
type: 'GET_LIVING_ROOM_USERS',
payload: orderBy(array, 'date', 'asc'),
});
});
} catch (e) {
alert(e);
}
};
};

Why, while using useEffect() and .then() in Redux, I get an Error: Actions must be plain objects. Use custom middleware for async actions

using Redux and am now straggling with a signin and signout button while using oauth.
When I press on the button to logIn, the popup window appears and I can choose an account. But in the meantime the webpage throws an error.
I got the following error as stated in the title:
Error: Actions must be plain objects. Use custom middleware for async actions.
I am using hooks, in this case useEffect().then() to fetch the data.
1) Why?
2) Also do not know, why I am getting a warning: The 'onAuthChange' function makes the dependencies of useEffect Hook (at line 35) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'onAuthChange' definition into its own useCallback() Hook react-hooks/exhaustive-deps
Here is my code:
GoogleAuth.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { signIn, signOut } from "../actions";
const API_KEY = process.env.REACT_APP_API_KEY;
const GoogleAuth = () => {
const isSignedIn = useSelector((state) => state.auth.isSignedIn);
console.log("IsSignedIn useSelector: " + isSignedIn);
const dispatch = useDispatch();
const onAuthChange = () => {
if (isSignedIn) {
dispatch(signIn());
} else {
dispatch(signOut());
}
};
useEffect(
() => {
window.gapi.load("client:auth2", () => {
window.gapi.client
.init({
clientId: API_KEY,
scope: "email"
})
.then(() => {
onAuthChange(window.gapi.auth2.getAuthInstance().isSignedIn.get());
console.log("isSignedIn.get(): " + window.gapi.auth2.getAuthInstance().isSignedIn.get());
window.gapi.auth2.getAuthInstance().isSignedIn.listen(onAuthChange);
});
});
},
[ onAuthChange ]
);
const onSignInOnClick = () => {
dispatch(window.gapi.auth2.getAuthInstance().signIn());
};
const onSignOutOnClick = () => {
dispatch(window.gapi.auth2.getAuthInstance().signOut());
};
const renderAuthButton = () => {
if (isSignedIn === null) {
return null;
} else if (isSignedIn) {
return (
<button onClick={onSignOutOnClick} className="ui red google button">
<i className="google icon" />
Sign Out
</button>
);
} else {
return (
<button onClick={onSignInOnClick} className="ui red google button">
<i className="google icon" />
Sign In with Google
</button>
);
}
};
return <div>{renderAuthButton()}</div>;
};
export default GoogleAuth;
reducer/index.js
import { combineReducers } from "redux";
import authReducer from "./authReducer";
export default combineReducers({
auth: authReducer
});
reducers/authReducer.js
import { SIGN_IN, SIGN_OUT } from "../actions/types";
const INITIAL_STATE = {
isSignedIn: null
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case SIGN_IN:
return { ...state, isSignedIn: true };
case SIGN_OUT:
return { ...state, isSignedIn: false };
default:
return state;
}
};
actions/index.js
import { SIGN_IN, SIGN_OUT } from "./types";
export const signIn = () => {
return {
type: SIGN_IN
};
};
export const signOut = () => {
return {
type: SIGN_OUT
};
};
types.js
export const SIGN_IN = "SIGN_IN";
export const SIGN_OUT = "SIGN_OUT";
The reason of the first error is that, inside both onSignInOnClick and onSignInOnClick, dispatch() receives a Promise (since window.gapi.auth2.getAuthInstance().signIn() returns a Promise).
There are different solution to handle effects in redux, the simplest are redux promise or redux thunk.
Otherwise you can dispatch the { type: SIGN_IN } action, and write a custom middleware to handle it.
The reason of the second error, is that the onAuthChange is redefined on every render, as you can see here:
const f = () => () => 42
f() === f() // output: false
Here's a possible solution to fix the warning:
useEffect(() => {
const onAuthChange = () => {
if (isSignedIn) {
dispatch(signIn())
} else {
dispatch(signOut())
}
}
window.gapi.load('client:auth2', () => {
window.gapi.client
.init({
clientId: API_KEY,
scope: 'email',
})
.then(() => {
onAuthChange(window.gapi.auth2.getAuthInstance().isSignedIn.get())
console.log(
'isSignedIn.get(): ' +
window.gapi.auth2.getAuthInstance().isSignedIn.get(),
)
window.gapi.auth2.getAuthInstance().isSignedIn.listen(onAuthChange)
})
})
}, [isSignedIn])

Property 'catch' does not exist on type 'PromiseLike<void>

I'm getting this error in the following code. I'm using ionic3:
Property 'catch' does not exist on type 'PromiseLike
This is the link to the tutorial that I am following.
This error is showing in the VS Code.
This probably some updated syntax I'm not aware of
storetoken(t) {
this.afd.list(this.firestore).push({
uid: firebase.auth().currentUser.uid,
devtoken: t
}).then(() => {
alert('Token stored');
})
.catch(() => {
alert('Token not stored');
})
this.afd.list(this.firemsg).push({
sendername: 'vivek',
message: 'hello'
}).then(() => {
alert('Message stored');
})
.catch(() => {
alert('Message not stored');
})
}
**This is the entire code for the home.ts file which sends token onto firebase database:Please refer this as well, as i'm also getting another error:Error: Uncaught (in promise): Error: StaticInjectorError[AngularFireDatabase] when I remove the catch block. **
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { AngularFireDatabase } from 'angularfire2/database';
import firebase from 'firebase';
import { HttpClientModule } from '#angular/common/http';
import { HttpModule } from '#angular/http';
declare var FCMPlugin;
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
firestore = firebase.database().ref('/pushtokens');
firemsg = firebase.database().ref('/messages');
constructor(public navCtrl: NavController, public afd:
AngularFireDatabase) {
this.tokensetup().then((token) => {
this.storetoken(token);
})
}
ionViewDidLoad() {
FCMPlugin.onNotification(function(data){
if(data.wasTapped){
//Notification was received on device tray and tapped by the user.
alert( JSON.stringify(data) );
}else{
//Notification was received in foreground. Maybe the user needs to be
notified.
alert( JSON.stringify(data) );
}
});
FCMPlugin.onTokenRefresh(function(token){
alert( token );
});
}
tokensetup() {
var promise = new Promise((resolve, reject) => {
FCMPlugin.getToken(function(token){
resolve(token);
}, (err) => {
reject(err);
});
})
return promise;
}
storetoken(t) {
this.afd.list(this.firestore).push({
uid: firebase.auth().currentUser.uid,
devtoken: t
}).then(() => {
alert('Token stored');
}).catch(() => {
alert('Token not stored');
})
this.afd.list(this.firemsg).push({
sendername: 'vivek',
message: 'hello'
}).then(() => {
alert('Message stored');
}).catch(() => {
alert('Message not stored');
})
}
}
push returns a ThenableReference and not a Promise.
Combined Promise and Reference; resolves when write is complete, but can be used immediately as the Reference to the child location.
It means you can use it as a future reference to the written data.
See also in codebase.
ThenableReference Def here.
export interface ThenableReference extends Reference, PromiseLike<Reference> {}
Or you can do the recommended way i.e:
You cannot use catch currently.
this.afd.list(this.firestore).push({ uid: firebase.auth().currentUser.uid, devtoken: t });
Note: There is an open issue here if you want to follow it.

Resources