I've created an Ionic chat app with firebase cloud functions. The push notifications are working with Android but not ios.
async getIosToken(token: string, userId: string): Promise<void> {
if (!FCM.hasPermission()) {
FCM.requestPushPermission()
.then(async (hasPerm) => {
if (hasPerm) {
const iosToken = await FCM.getAPNSToken();
if (iosToken === token) {
return;
} else {
this.saveToken(iosToken, userId);
}
}
});
} else {
const iosToken = await FCM.getAPNSToken();
if (iosToken === token) {
return;
} else {
this.saveToken(iosToken, userId);
}
}
}
saveToken(token: string, userId: string): void {
this.userSvc.saveTokenToFirestore(token, userId)
.then(() => {
this.storageSvc.setDeviceToken(token);
});
}
The iOS token is being saved to firebase...although it never prompted the user for request permissions.
I console logged the firebase cloud function and I can see the APNs token.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
exports.newChatNotification = functions.firestore
.document(`chat/{id}/messages/{doc}`)
.onWrite(async event => {
const message = event.after.data();
let data: any;
let device: any;
const db = admin.firestore();
console.log('message', message);
console.log('db', db);
if (message) { data = message; }
const receivingUserId = data ? data.receivingUserId : '';
const content = data ? data.content : '';
const sendingUserId = data ? data.sendingUserId : '';
console.log('payload', receivingUserId, sendingUserId);
const payload = {
notification: {
title: 'New message',
body: `${content}`,
click_action: 'FCM_PLUGIN_ACTIVITY'
},
data: {
page: 'tabs/travel-buddies',
}
};
console.log('payload2', payload);
const devicesRef = (await db.collection('devices').doc(`${receivingUserId}`).get()).data();
if (devicesRef) { device = devicesRef; }
const token = device ? device.token : '';
console.log('devicesRef', devicesRef, token);
return admin.messaging().sendToDevice(token, payload);
});
Here's the firebase cloud function console
I'm not sure how to troubleshoot why ios is not receiving a push notification because there aren't any errors and I'm getting the APNs token.
I've also tried updating the build system per Google searches online:
Any help would be greatly appreciated.
Related
Below I try to respond with a stream when I receive ticker updates.
+page.server.js:
import YahooFinanceTicker from "yahoo-finance-ticker";
const ticker = new YahooFinanceTicker();
const tickerListener = await ticker.subscribe(["BTC-USD"])
const stream = new ReadableStream({
start(controller) {
tickerListener.on("ticker", (ticker) => {
console.log(ticker.price);
controller.enqueue(ticker.price);
});
}
});
export async function load() {
return response????
};
Note: The YahooFinanceTicker can't run in the browser.
How to handle / set the response in the Sveltekit load function.
To my knowledge, the load functions cannot be used for this as their responses are JS/JSON serialized. You can use an endpoint in +server to return a Response object which can be constructed from a ReadableStream.
Solution: H.B. comment showed me the right direction to push unsollicited price ticker updates the client.
api route: yahoo-finance-ticker +server.js
import YahooFinanceTicker from "yahoo-finance-ticker";
const ticker = new YahooFinanceTicker();
const tickerListener = await ticker.subscribe(["BTC-USD"])
/** #type {import('./$types').RequestHandler} */
export function GET({ request }) {
const ac = new AbortController();
console.log("GET api: yahoo-finance-ticker")
const stream = new ReadableStream({
start(controller) {
tickerListener.on("ticker", (ticker) => {
console.log(ticker.price);
controller.enqueue(String(ticker.price));
}, { signal: ac.signal });
},
cancel() {
console.log("cancel and abort");
ac.abort();
},
})
return new Response(stream, {
headers: {
'content-type': 'text/event-stream',
}
});
}
page route: +page.svelte
<script>
let result = "";
async function getStream() {
const response = await fetch("/api/yahoo-finance-ticker");
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
while (true) {
const { value, done } = await reader.read();
console.log("resp", done, value);
if (done) break;
result += `${value}<br>`;
}
}
getStream();
</script>
<section>
<p>{#html result}</p>
</section>
I create an app with flutter using phone authentication by firebase, everything went fine with android, but in iOS I did all things in documentation, still get this error :
the phone auth credential was created with an empty verification id.
my code :
verifyPhoneNumner(String phoneNumber) async {
await auth.verifyPhoneNumber(
timeout: const Duration(seconds: 10),
phoneNumber: completeNumber(phoneNumber),
verificationCompleted: (AuthCredential authCredential) {},
verificationFailed: (authException) {},
codeSent: (String id, [int? forceResent]) {},
codeAutoRetrievalTimeout: (id) {
verificationId = id; // save id in provider to use it in veryfy otp
});
}
verifyOtp(String otp) async {
var credential = await auth.signInWithCredential(
PhoneAuthProvider.credential(
verificationId: verificationId, smsCode: otp)); // otp is textform
if (credential.user != null) {
return true;
} else {
return false;
}
}
The cancellation response from token.botframework.com is currently being displayed to screen like this:
{
"error": {
"code": "ServiceError",
"message": "Missing required query string parameter: code. Url = https://token.botframework.com/.auth/web/redirect?state=d48fb60ae4834fd8adabfe054a5eff74&error_description=The+user+chose+not+to+give+your+app+access+to+their+Dropbox+account.&error=access_denied"
}
}
How can I, instead, handle the cancellation gracefully? If the user cancels like this, I'd like to just have the auth-card-popup window close automatically.
This is for an action-type messaging extension app that I'm building. The sign-in process begins with an auth card. The bot is pointed at a Dropbox OAUTH2 connection. Here the relevant code that brings up the card:
const { TeamsActivityHandler, CardFactory } = require('botbuilder');
class MsgExtActionBot extends TeamsActivityHandler {
constructor() {
super();
this.connectionName = 'oauth2-provider';
}
async handleTeamsMessagingExtensionFetchTask(context, action) {
if (!await this.isAuthenticated(context)) {
return this.getSignInResponse(context);
}
}
async isAuthenticated(context) {
let tokenResponse = await context.adapter.getUserToken(
context,
this.connectionName
);
if (tokenResponse && tokenResponse.token) {
return true;
}
if (!context.activity.value.state) {
return false;
}
tokenResponse = await context.adapter.getUserToken(
context,
this.connectionName,
context.activity.value.state
);
if (tokenResponse && tokenResponse.token) {
return true;
}
return false;
}
async getSignInResponse(context) {
const signInLink = await context.adapter.getSignInLink(context, this.connectionName);
return {
composeExtension: {
type: 'auth',
suggestedActions: {
actions: [{
type: 'openUrl',
value: signInLink,
title: 'Please sign in'
}]
},
}
};
}
}
Can i fix my problems and what wrong in my code?
Problems
i use application for call to my clients from twilio number
When my managers call to US phone number, client see incoming call as "Unknown name"
When my manager calls - silence before connect to client in browser
Then client accepts call - and all ok
Server code
generateToken: () {
let clientName = 'twilio_agent';
let _id = req.body._id;
let phoneno = req.body.phoneno;
//
realmsProvider.get({_id: _id})
.then(data => {
if (!data) {
return res.status(404).json({ message: 'Account not found with _id:'+ _id });
}
var capability = new ClientCapability({
accountSid: data.sid,
authToken: data.authToken,
ttl: config.twilio.ttl
});
capability.addScope(
new ClientCapability.OutgoingClientScope({
applicationSid: data.application.sid,
clientName: data.name,
callerId: phoneno
}));
// incoming call
if (phoneno != null) {
capability.addScope(
new ClientCapability.IncomingClientScope(phoneno));
}
else {
capability.addScope(
new ClientCapability.IncomingClientScope(clientName));
}
var token = capability.toJwt();
return res.status(200).json({ token: token });
})
.catch(err => {
return utils.handleError(res, err);
});
}
voice: () {
let phoneNumber = req.body.PhoneNumber;
let callerId = req.body.CallerId;
let twiml = new VoiceResponse();
let dial = twiml.dial({
//https://www.twilio.com/docs/api/twiml/dial
answerOnBridge: true,
//phone number from
callerId : callerId,
//record all calls
record: 'record-from-answer-dual'
});
if (phoneNumber != null) {
dial.number(phoneNumber);
}
dial.client(callerId.replace(/\+/, ''));
res.type('text/xml');
res.send(twiml.toString());
}
Browser code
Twilio.Device.setup(token, {
debug: true,
enableRingingState: true,
sounds: {
incoming: 'URL-TO-MP3.mp3',
outgoing: 'URL-TO-MP3.mp3',
dtmf8: 'URL-TO-MP3.mp3'
}
});
function call(currentPhoneno, phoneTo, companyNameInTwilio) {
$scope.calling = 1;
if (!validatePhoneno(phoneTo)) {
growl.error('Bad phone number');
return;
}
var connection = Twilio.Device.connect({ // Connect our call.
CallerId: currentPhoneno, // Your Twilio number (Format: +15556667777).
PhoneNumber: phoneTo, // Number to call (Format: +15556667777).
From: companyNameInTwilio
});
connection.on('ringing', function(hasEarlyMedia) {
// some code for ringing
});
}
Is there a way to translate / localise notifications sent from Firebase?
I have my app setup to receive notifications successfully:
extension AppDelegate: UNUserNotificationCenterDelegate {
func setupNotifications() {
registerForRemoteNotifications()
setupNotificationTokenRefresh()
}
func registerForRemoteNotifications() {
let application = UIApplication.shared
UNUserNotificationCenter.current().delegate = self
application.registerForRemoteNotifications()
}
func setupNotificationTokenRefresh() {
storeNotificationToken()
_ = NotificationCenter.default.addObserver(
forName: .MessagingRegistrationTokenRefreshed,
object: nil,
queue: .current
) { [weak self] _ in
self?.storeNotificationToken()
}
}
private func storeNotificationToken() {
Messaging.messaging().token { [weak self] token, error in
if let error = error {
Log.error("Error fetching FCM registration token: \(error)")
} else if let token = token {
// save token
}
}
}
}
A payload is sent from a Firebase cloud function with a data object and I would like to access this data object and translate/localize the message sent.
I looked into several methods provided, but they seem to be about intercepting notifications only when the app is in the foreground, which is not what I am interested in.
The payload sent from the server:
const payload = {
notification: {
title: 'Friend request',
body: senderName + ' wants to add you as a friend'
},
data: {
senderUserId: friendRequestFrom,
type: 'friendRequest'
}
}
Because you are already using cloud functions, one way to accomplish that is to do the translation server-side with the Google Cloud Translation API. There is a good sample demonstrating how to do so with Node.js.
Say for example you are sending the notifications when new objects get added to the /notifications path in your real-time database. You can do something like this:
const Translate = require('#google-cloud/translate')
const functions = require('firebase-functions')
const projectId = 'YOUR_PROJECT_ID'
const translate = new Translate({
projectId: projectId,
});
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
exports.sendNotification = functions.database.ref(`/notifications/{notif_id}`)
.onWrite(event => {
const notification = event.data.val()
// Don't send when this isn't a new notification.
if (event.data.previous.exists()) {
return null
}
const user_id = notification.user_id
getLocalLanguageOfUser(user_id).then(language => {
if (language != 'en')
translateTo(language, notification).then(localised => {
return send(localised)
}).catch(err => {
return console.log(err)
})
} else { // it's English - no need to translate
return send(notification)
}
})
})
function getLocalLanguageOfUser(user_id) {
return new Promise((resolve, reject) => {
// default will be 'en' for English
firebase.database().ref(`users/${user_id}/language`)
.once('value').then(snapshot => {
resolve(snapshot.val() || 'en')
})
.catch(err => reject(err))
})
}
function translateTo(language, notification) {
return new Promise((resolve, reject) => {
const text = notification.text;
translate.translate(text, language).then(results => {
const translation = results[0];
resolve({
...notification,
text: translation
})
}).catch(err => reject(err))
})
}
function send(notification) {
// use your current setup to send the notification.
// 'text' key will already be localised.
}
Instead of body in your payload use body_loc_key and put the string in your application resourceId.
In onMessageRecieved:
String theKeyFromPayload= remotemessage.getNotification.getBodyLocalizationKey()
String resourceAppStatusString=theKeyFromPayload
Int resourceId= getResourceId(resourceAppStatusString, "string", this.getPackageName()
String finalBody = getResource(). getResourceName(resourceId);
finalBody is passed into the notification in your application.