Firebase Cloud Messaging with Functions not working after deploying - kotlin

What I'm trying to build is a chat app, I have tested my Firebase Cloud Messaging with Functions on Firebase Emulator it does work well as expected and the notification was received in real device, but after deploying the function to the console and testing it on the actual Firestore, nothing happened and no notifications were delivered, but in the emulator with the dummy data that I made using real Token stored on the database the notification works and the message received, I couldn't figure out what is wrong do I have to do something more to firestore.
note that using the Cloud Message console to send a test message with the device token works as well
here is the function
exports.chatNotification = functions.firestore.document('/inProcess/{documentId}/chat/{documentId}').onCreate((snap, context) => {
const data = snap.data();
const recipientId = data['recipientId'];
const senderName = data['senderName'];
const message = data['message'];
const db = admin.database().ref();
return db.child("users").child(recipientId).get().then(userRef =>{
const token = userRef.child('token').val();
const payload ={
notification: {
title: senderName,
body: message,
clickAction: "TabbarActivity"
},
data:{
UID: recipientId
}
};
return admin.messaging().sendToDevice(token, payload).then(res =>{
console.log("sent")
// this gets a printout on the firebase emulator means the function works fine, then I get the notification on my device with the same data that I used it works fine
});
})
})
on Android device
//FCM service
override fun onNewToken(p0: String) {
super.onNewToken(p0)
if (FirebaseAuth.getInstance().currentUser != null) {
val userid = firebaseAuth.currentUser?.uid ?: ""
val ref =
FirebaseDatabase.getInstance().reference.child("users").child(userid).child("token")
token = p0
ref.setValue(token)
}
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
if (remoteMessage.data.size >0){
Log.e(remoteMessage.data.toString(), remoteMessage.data.toString())
val title = remoteMessage.data["title"]
val message = remoteMessage.data["body"]
showNotification(applicationContext, title, message)
}else{
val title = remoteMessage.notification!!.title
val message = remoteMessage.notification!!.body
showNotification(applicationContext, title, message)
}
}
MainActivity.kt
FirebaseMessaging.getInstance().token.addOnCompleteListener {
if (it.isSuccessful) {
val token = it.result
val userid = firebaseAuth.currentUser?.uid ?: ""
val ref =
Firebase.database.getReference("users/${userid}/token")
ref.setValue(token)
}
}

Related

SignalR with React Native Expo:: NOTHING is happening

I have a mobile app with messaging functionality. The backend is a .NET Core 6.0 console application. The front end is a React Native Expo application. When a user hits the "send" button, it saves the message in the database and pushes the new message to the other user's screen (currently it's only one to one conversation). Everything works except when it's trying to push the new message to the recipient. I have tried other solutions on Stackoverflow but none so far has been helpful...
The .NET application is hosted on Azure and I am using the actual device to run the Expo app.
The new message is not pushed to the recipient's screen. I was getting some 404 errors before but turned out to be something else. I walked through the code and it executed the _messageHub.Clients.All.SendAsync("ReceivedMessage", message); fine but the other phone is not getting the new message.
React Native Expo code:
import { HubConnectionBuilder } from '#microsoft/signalr';
import React, { useEffect, useState } from 'react';
export default function Conversation({ navigation, route }: any) {
const [messages, setMessages] = useState([]);
const [message, setMessage] = useState("");
const conversation = route.params.data;
const personId = getCurrentUser().personId;
useEffect(() => {
loadMessages();
try {
const connection = new HubConnectionBuilder()
.withUrl(`${Settings.ApiUrl}/hubs-message`)
.build();
connection.start().then(() => console.log("started"));
connection.on("ReceivedMessage", message => {
console.log(message);
let updateMessage = messages;
updateMessage.unshift(message);
setMessages(updateMessage);
});
}
catch (e) { console.error("error here", e); }
}, []);
const sendMessage = () => {
postUser(`${Settings.ApiUrl}/v1/Message/Send`, {
message: message,
conversationId: conversation.id
}).then((data: any) => {
setMessage("");
let updateMessages = [...messages];
updateMessages.unshift(data.Message);
setMessages(updateMessages);
});
};
....
....
}
.NET Core code:
Program.cs
builder.Services.AddSignalR();
var app = builder.Build();
...
...
app.UseCors();
app.UseHttpsRedirection();
app.MapControllers();
app.UseAuthentication();
app.UseAuthorization();
app.MapHub<MessageHub>("/hubs-message");
MessageHub.cs //does not have much at the moment...
[Authorize]
public class MessageHub : Hub
{
}
NotificationService.cs //this is called by the MessageController after saving the message to the database to push the new message to the recipient.
public class NotificationService
{
private readonly IHubContext<MessageHub> _messageHub;
public NotificationService(IHubContext<MessageHub> messageHub)
{
_messageHub = messageHub;
}
...
...
if (newMessageSentData.ActiveOnConversationId == conversationId)
{
try
{
//push new message to receiver
_messageHub.Clients.All.SendAsync("ReceivedMessage", message);
}
catch (Exception ex)
{
//NO exception thrown
var a = ex.Message;
}
}
}
I was able to get it to work by moving to Azure SignalR. It's quite easy to set up. Here is a youtube video showing to to do that https://www.youtube.com/watch?v=wpQZrQ6Wnbw&t=759s.
Azure portal
Add the SignalR service to my resource group and copy the connection string under the Settings group
program.cs,
builder.Services.AddSignalR().AddAzureSignalR("[Azure SignalR Connection String]");
var app = builder.Build();
...
...
app.UseAzureSignalR(routes =>
{
routes.MapHub<MessageHub>("/hubs-message");
});

vuex 3.6 why is my state object undefined when using it inside a method

I am building an application using vue 2.6.11 and vuex 3.6.0
The page I am building is for an event registration. The ActiveEvent is fetched from the database (Event ID, Date, Location etc) using an API
The registration form first asks for an email address. On blur of the email field we then fire the checkEmail(). This should do one or two API calls. The first call checks to see if we have the email address in the database and returns the ParticipantID, and if we do then a second call is made to see if the participant is already registered against this event using Event.EventID and ActiveUser.ParticipantID
The stucture of the page being loaded is a page component <EventDetail> called from the router. This has a main component <EventRegistration> which calls two separate sub-components: <EventRegistrationBlurb> which gets the state.ActiveEvent passed as a prop and <EventRegistrationForm> which is fetching the state.ActiveEvent directly. The outer component <EventRegistration> is responsible for fetching the Event data from the API and setting state.ActiveEvent which is does successfully,
What I am failing to understand is why when I call checkEmail in my component, this.ActiveEvent is undefined. The puter component is fetching the API and setting the state correctly as the blurb component is correctly rendering it. If I put the ActiveEvent object into the template for the EventRegistrationForm it renders correctly, it is just not being set in time for the binding to be made to the method checkEmail()
I have the following code in my sub-component <EventRegistrationForm>: (NOTE, ActiveEvent is set by an outer component and does get set correctly)
methods: {
...mapActions(['CheckParticipantByEmail']),
async checkEmail () {
const payload = {
email: this.form.email,
EventID: this.ActiveEvent.EventID // <-- THIS IS UNDEFINED???
}
await this.CheckParticipantByEmail(payload)
}
},
computed: {
...mapState(['ActiveEvent', 'ActiveUser'])
}
and then in my store:
state: {
ActiveEvent: {},
ActiveUser: {}
},
mutations: {
SET_ACTIVE_EVENT (state, payload) {
state.ActiveEvent = payload
},
CHECK_PARTICIPANT_BY_EMAIL (state, payload) {
state.ActiveUser = payload
},
GET_PARTICIPANT_FOR_EVENT (state, payload) {
state.ActiveUser = payload
}
},
actions: {
async CheckParticipantByEmail ({ commit }, payload) {
console.log('payload', payload)
const baseUrl = process.env.VUE_APP_API_URL
const url = `${baseUrl}getParticipantbyEmail`
const { email, EventID } = payload
const response = await axios.post(
url,
{
EmailAddress: email
}
)
const User = await response.data[0]
commit('CHECK_PARTICIPANT_BY_EMAIL', User)
if (User.ParticipantID > 0) {
const baseUrl = process.env.VUE_APP_API_URL
const url2 = `${baseUrl}getParticipantForEvent`
const payload2 = {
ParticipantID: User.ParticipantID,
EventID: EventID
}
alert('URL2: ' + url2)
alert('payload2 participant: ' + payload2.ParticipantID)
alert('payload2 event: ' + payload2.EventID)
const response2 = await axios.post(
url2,
payload2
)
// console.log('response: ', response.data[0])
const payload3 = response2.data[0]
commit('GET_PARTICIPANT_FOR_EVENT', payload3)
}
}
}
As usual, it turns out to be an interface error between the chair and the keyboard. This page is normally accessed from a list of events which is an array of objects where the identifier is EventID. When calling the separate events the identifier is just ID so the code in the payload2 should read
const payload2 = {
ParticipantID: User.ParticipantID,
EventID: ID // <- NOTE change of identifier.
}
I think I will update the API to return a consistent identifier and avoid the headache later on. Only wasted about 3 hours on this...

React Native Firebase push notification

I have a requirement to automatically send push notifications to my application when new data is inserted into firebase.
Is there any way to do so ?
Thanks !
You can use Firebase Functions as a middleware function for sending push notifications via FCM to the device If the database value is changed.
Adding an example from my FirebaseDBtoFCMFunction repo.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendPushNotification = functions.database
.ref('/users/{user_id}') // Put your path here with the params.
.onWrite(async (change, context) => {
try {
const { after } = change;
const { _data } = after;
const { deviceToken } = _data.receiver; // Always send the device token within the data entry.
if(!deviceToken) return;
const payload = {
notification: {
title: 'Notification',
body: `FCM notification triggered!`
},
data: context.params // Passing the path params along with the notification to the device. [optional]
};
return await admin.messaging().sendToDevice(deviceToken, payload);
} catch (ex) {
return console.error('Error:', ex.toString());
}
});
Inside your application add child_change (valueChanged) or child_add event for specific database location than when it changes, it will fired.
From doc.
FirebaseDatabase.DefaultInstance
.GetReference("Leaders").OrderByChild("score")
.ValueChanged += HandleValueChanged;
}
void HandleValueChanged(object sender, ValueChangedEventArgs args) {
if (args.DatabaseError != null) {
Debug.LogError(args.DatabaseError.Message);
return;
}
// Do something with the data in args.Snapshot
}
For nodejs value listener

Solidity issue passing parameters to a function

I have a Smart Contract with the function:
contract Example {
event claimed(address owner);
function claimStar() public {
owner = msg.sender;
emit claimed(msg.sender);
}
}
I'm using Truffle V5.0 and the Webpack box as a boiler plate code.
In my truffle-config.js file I have the in the networks configuration:
development:{
host:"127.0.0.1",
port: 9545,
network_id:"*"
}
Everything compile fine using:
- truffle develop
- compile
- migrate --reset
It says Truffle Develop started at http://127.0.0.1:9545
In my index.js file I have the following code:
import Web3 from "web3";
import starNotaryArtifact from "../../build/contracts/StarNotary.json";
const App = {
web3: null,
account: null,
meta: null,
start: async function() {
const { web3 } = this;
try {
// get contract instance
const networkId = await web3.eth.net.getId();
const deployedNetwork = starNotaryArtifact.networks[networkId];
this.meta = new web3.eth.Contract(
starNotaryArtifact.abi,
deployedNetwork.address,
);
// get accounts
const accounts = await web3.eth.getAccounts();
this.account = accounts[0];
} catch (error) {
console.error("Could not connect to contract or chain.");
}
},
setStatus: function(message) {
const status = document.getElementById("status");
status.innerHTML = message;
},
claimStarFunc: async function(){
const { claimStar } = this.meta.methods;
await claimStar();
App.setStatus("New Star Owner is " + this.account + ".");
}
};
window.App = App;
window.addEventListener("load", async function() {
if (window.ethereum) {
// use MetaMask's provider
App.web3 = new Web3(window.ethereum);
await window.ethereum.enable(); // get permission to access accounts
} else {
console.warn("No web3 detected. Falling back to http://127.0.0.1:9545. You should remove this fallback when you deploy live",);
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
App.web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:9545"),);
}
App.start();
});
In my browser I have Metamask installed and I added a Private Network with the same URL and also imported two accounts.
When I start the application and opened in the browser it opens Metamask to request permission because I'm using window.ethereum.enable();.
But when I click on the button to claim it doesn't do anything.
The normal behavior is to Metamask open a prompt to ask for confirmation but it never happen.
I created also a new property in the contract for test and it works fine showing me the value assigned in the contract constructor.
My question is, Am I missing something?
I also tried to change the functionawait claimStar(); to await claimStar({from: this.account}); but in this case I got an error saying that claimStar doesn't expect parameters.
I would appreciate any help. Thanks
I solved the issue, the problem was in the function claimStarFunc
It should be in this way:
claimStarFunc: async function(){
const { claimStar } = this.meta.methods;
await claimStar().send({from:this.account});
App.setStatus("New Star Owner is " + this.account + ".");
}
Because I'm sending a transaction.
Thanks

Get uid after anonymously signing in using AngularFire2 AngularFireAuth

Sign in works, however when I try to access the uid, it comes back null. I've seen the uid in console. I just can't seem to get it out of my service. Here are the relevant parts of my AuthService:
#Injectable()
export class AuthService {
uid: any = null;
constructor(private _firebaseAuth: AngularFireAuth, private router: Router) {
this._firebaseAuth.auth.onAuthStateChanged(function(user) {
if (user) {
this.uid = user.uid;
}
});
signInAnonymously() {
return this._firebaseAuth.auth.signInAnonymously().catch(function(error) {
const errorCode = error.code;
const errorMessage = error.message;
console.log(errorCode, errorMessage);
});
}
In my component, I call signInAnonymously() on a button click:
onGuestClick() {
let uid: any = null;
const guestSignIn = this.authService.signInAnonymously().then(() => {
uid = this.authService.uid;
console.log(uid); // returns null
this.toggleOnline(uid);
});
}
Auth state is changing, but my uid isn't being updated, or at least not before I call for it. Do I need to call on AuthStateChanged somewhere other than my service constructor? What am I doing wrong?
Solved by moving the function that needed the id from the component and into the auth service where access to the id was working.