NativeScript Firebase Notification Callback Issue - firebase

I have an application written in NativeScript. I use Firebase plugin for push notifications. The first problem is that when a device is in the background or it's killed I can't receive any notifications (sending only data parameter) but when I open the app then notification is generating.
The second problem is that when I try to send a push notification (the app is killed) with notification and data parameter the local notification is generating correctly but the callback is executed on notification click, not in the background (my view has not been updated by new data).
Here is part of NativeScript code:
firebase.init({
onMessageReceivedCallback: (message: Message) => {
console.log(`Title: ${message.title}`);
console.log(`Body: ${message.body}`);
console.log(`Data: ${message.data}`);
const data = message.data;
const action = data.action;
// STORE RESERVATION
if (action === ItemConstants.STORE_RESERVATION) {
const item = this.prepareOnlineData(data);
const result = this.databaseService.saveReservation(item);
if (result !== -1) {
console.log("Saved to local database!");
} else {
console.log("Error occurred!");
}
}
// DENY RESERVATION
if (action === ItemConstants.DENY_RESERVATION) {
this.databaseService.setReservationStatus(
ItemConstants.STATUS_DENIED, data.tracking_id
).then(result => {
console.log(result);
}, error => {
console.log(error);
});
}
}
}).catch(error => {});
and the part of PHP code that calls Google Service:
$fields = array(
'to' => $to,
'notification' => [
'title' => 'reservation title',
'body' => 'you have new reservation',
'sound' => 'default'
],
'data' => $data,
'priority' => 'high',
'delay_while_idle ' => true
);
// calling curl - google services.
Does anyone have an idea how to fix this issue?

Related

Handle facebook login with same account used with Google using firebase

I'm working on a react native project and I've came to a part where initially I implemented google sign in my project using react-native-google-signin and later on Facebook sign in using react-native-fbsdk packages with the help of firebase and both worked like a charm "individually".
The Problem
Let's say the user logged in using google account and it worked but later logged in using Facebook with the same account (I'm allowing only one email per user in firebase), I get an error
auth/account-exists-with-different-credentials
I want the user to be able to login using Facebook from the login screen or to be more specific to link his account from the login screen.
What have I tried?
I searched online and found some answers and got up with this solution or piece of code:
facebookSignin: async () => {
const result = await LoginManager.logInWithPermissions([
'public_profile',
'email',
]);
if (result.isCancelled) {
alert('User cancelled the login process');
this.setState({loginInProcess: false});
}
const data = await AccessToken.getCurrentAccessToken();
if (!data) {
alert('Something went wrong obtaining access token');
this.setState({loginInProcess: false});
}
const facebookCredential = auth.FacebookAuthProvider.credential(
data.accessToken,
);
await auth()
.signInWithCredential(facebookCredential)
// The problem starts here from the catch block
.catch((error) => {
if (
error.code === 'auth/account-exists-with-different-credential'
) {
var pendingCred = error.credential;
var email = error.email;
auth()
.fetchSignInMethodsForEmail(email)
.then(async (methods) => {
if (methods[0] === 'google.com') {
const {idToken} = await GoogleSignin.signIn();
const googleCredential = auth.GoogleAuthProvider.credential(
idToken,
);
auth()
.signInWithCredential(googleCredential)
.then((user) => {
user.linkWithCredential(pendingCred);
})
.catch((error) => console.log(error));
}
});
}
});
}
This code implements a function when triggered, if there is no user with the same email, it proceeds normally, however if there is an error (mentioned above), it will grant the user with a list of google accounts that are present in the user phone (google thing) and when he chooses his account (linked with google account) it doesn't work. The email isn't linked.
To be more specific, I would like somehow to not grant the user with all his google accounts but only with the email to be linked var email = error.email; (in the code snippet above) and for the Facebook provider to be linked successfully.
After a little of hard work, I've managed to make it work in react native and I'm gonna leave the answer here for peeps who are facing the same issue. Be ware that I used react-native-prompt-android to ask the user for confirming his password when trying to link with Facebook.
The user tries to sign with Facebook and gets this error:
auth/account-exists-with-different-credentials
This is how I handled it:
.catch((error) => {
// Catching the error
if (
error.code === 'auth/account-exists-with-different-credential'
) {
const _responseInfoCallback = (error, result) => {
if (error) {
alert('Error fetching data: ' + error.toString());
} else {
setEmail(result.email);
}
};
// Getting the email address instead of error.email from Facebook
const profileRequest = new GraphRequest(
'/me?fields=email',
null,
_responseInfoCallback,
);
new GraphRequestManager().addRequest(profileRequest).start();
if (email) {
auth()
.fetchSignInMethodsForEmail(email)
.then(async (methods) => {
// Checking the method
if (methods[0] === 'password') {
// Prompting the user to confirm/input his password for linking
const AsyncAlert = () => {
return new Promise((resolve, reject) => {
prompt(
'Password Confirmation',
'The email address is already linked with password account. Enter your password to process',
[
{
text: 'Cancel',
style: 'cancel',
},
{
text: 'Continue',
onPress: (password) =>
resolve(setPassword(password)),
},
],
{
type: 'secure-text',
cancelable: false,
placeholder: 'Password',
},
);
});
};
// Here the linking goes
await AsyncAlert().then(async () => {
await auth()
.signInWithEmailAndPassword(email, password)
.then(() => {
return auth().currentUser.linkWithCredential(
facebookCredential,
);
})
.catch(() => alert('Something went wrong'));
});
} else if (methods[0] === 'google.com') {
const {idToken} = await GoogleSignin.signIn(email);
const googleCredential = auth.GoogleAuthProvider.credential(
idToken,
);
await auth()
.signInWithCredential(googleCredential)
.then(() => {
return auth().currentUser.linkWithCredential(
facebookCredential,
);
});
}
});
} else {
alert('Something went wrong');
}
}
});

RNFirebase v6 Push Notifications are not coming both iOS&Android

I am trying to send notifications from firebase console to my react-native app
I followed the poor documentation here as much as I understand: https://invertase.io/oss/react-native-firebase/v6/messaging/quick-start
I installed #react-native-firebase/app and /messaging and here is my code in component:
componentDidMount() {
this.reqNotifications()
this.checkNotificationPermission()
}
reqNotifications() {
requestNotifications(['alert', 'badge', 'sound']).then(({status, settings}) => {
console.log('NOTIFICATION STATUS' + status)
});
}
async checkNotificationPermission() {
const enabled = await messaging().hasPermission();
if (enabled) {
console.log('APPROVED');
await messaging().registerForRemoteNotifications()
messaging().getToken().then(token => console.log('token: >> ' + token))
} else {
console.log('NOT APPROVED');
}
}
I am requesting permission via react-native-permissions and permission request is
working.
My Apple APNs are OK on Apple and Firebase console
And I am getting my token by getToken() method on the code
succesfully.
But I cant send anything to device from firebase; nothing happening on neither foreground nor background . I tried with-token test and also tried normal but no, nothing happens.
I added this code to componentDidMount:
messaging().onMessage(async remoteMessage => {
console.log('FCM Message Data:', remoteMessage.data);
});
As I understand this subscribes for cloud messages and when I send some cloud message notification from firebase-console, I should get console output; but nothing happens.
I dont know what am I missing but I think there is a big update on this package and most of docs are for previous version and I really stuck here thanks for assist
for rnfirebase.io V6
componentDidMount = async () => {
this.checkNotificationPermission();
await messaging().requestPermission({provisional: true});
await messaging().registerDeviceForRemoteMessages();
await this.getFCMToken();
if (Platform.OS === 'android') {
this.createAndroidNotificationChannel();
}
this.backgroundState();
this.foregroundState();
};
checkNotificationPermission = () => {
firebase
.messaging()
.hasPermission()
.then(enabled => {
if (!enabled) {
this.promptForNotificationPermission();
}
});
};
promptForNotificationPermission = () => {
firebase
.messaging()
.requestPermission({provisional: true})
.then(() => {
console.log('Permission granted.');
})
.catch(() => {
console.log('Permission rejected.');
});
};
createAndroidNotificationChannel() {
const channel = new firebase.notifications.Android.Channel(
'channelId',
'Push Notification',
firebase.notifications.Android.Importance.Max,
).setDescription('Turn on to receive push notification');
firebase.notifications().android.createChannel(channel);
}
foregroundState = () => {
const unsubscribe = messaging().onMessage(async notification => {
console.log('Message handled in the foregroundState!', notification);
});
return unsubscribe;
};
// Register background handler
backgroundState = () => {
messaging().setBackgroundMessageHandler(async notification => {
console.log('Message handled in the background!', notification);
});
};

Error: storage/object-not-found when trying to upload large image file

I'm getting an error : storage/object-not-found when trying to upload a large image file in Google Cloud Storage using RxFire.
They say the image is not found in the bucket but when I check, i see them!
I tested with small images (100kb likely...) works great.
But tried with > 500kb images, doesn't work...
upload$
.pipe(
switchMap((event: any) => {
const name = Math.random().toString(36).substring(5);
const blob = event.target.files[0];
const type = blob.type.replace('image/', '');
const ref = storage.ref(`uploads/test/${name}.${type}`);
return put(ref, blob);
}),
map(snapshot => snapshot),
filter(snapshot => snapshot.totalBytes === snapshot.bytesTransferred),
mergeMap(snapshot => getDownloadURL(snapshot.ref))
)
.subscribe(url => {
console.log('Results', url)
}, (error) => {
// ERROR HERE
console.log('error', error)
})
Expected result : Upload working with big images
Actual results : Error
Uncaught t {code_: "storage/object-not-found", message_: "Firebase .
Storage: Object 'uploads/test/7xpbilmb.jpeg' does not exist.",
serverResponse_: "{↵ "error": {↵ "code": 404,↵ "message":
"Not Found. Could not get object"↵ }↵}", name_: "FirebaseError"}
You can do it both ways.
Promises
storageRef.put(blob, {customMetadata}).then(data => {
data.ref.getDownloadURL().then(url => {
// do whatever you want with url
});
});
Observables
downloadURL = new Subject();
this.downloadURL.pipe(
map(obs => obs),
concatAll()
).subscribe(url => {
// do whatever you want with url
});
let task = ref.put(blob, {customMetadata});
task.snapshotChanges().pipe(
finalize(() => this.downloadURL.next(ref.getDownloadURL()))
).subscribe();
This should be enough to get you your downloadURL. In case you wanna track upload progress with observables, here's the code for that:
task.percentageChanges().subscribe(progress => {
console.log('upload progress: ', progress);
if (res >= 100) {
// HOORAY!
}
});

angular-ngrx-data create custom effect

I am currently using ngrx in my app to manage state. I was looking at switching to ngrx-data for the simple CRUD operations and ran across my first question. This is my current regular ngrx effect and I want to know how to reproduce it with ngrx-data (create new action based on http response):
#Effect()
insertUser$: Observable<Action> = this.action$.pipe(
ofType(UsersActionTypes.UserInsert),
map((action: UserInsert) => action.payload),
mergeMap(payload =>
this.UserService.insert(<IUserPersistRequest>{
user: payload.User,
refreshToken: payload.loggedInUser.RefreshToken
}).pipe(
map((response: IUserPersistResponse) => {
return !response.accessToken
? new UserPersistSuccessLoginFailure()
: new UserInsertSuccess({
response: response,
User: payload.User
});
}),
catchError((error: string) => {
return of(new UserInsertFailure('UserInsert Failed'));
})
)
)
);
Any advice?

How to hide a push notification after some time?

I have to hide a push notification after 1 minute. What should I do in my service worker to achieve this?
You can use the Notification.close() method. You can get the Notification object with ServiceWorkerRegistration.getNotifications.
For example:
self.addEventListener('push', event => {
event.waitUntil(
self.registration.showNotification('Title', {
body: 'Body.',
})
.then(() => self.registration.getNotifications())
.then(notifications => {
setTimeout(() => notifications.forEach(notification => notification.close()), 60000);
})
);
});
You have to use Notification.close.
Also, to get the notifications, you have to use ServiceWorkerRegistration.getNotifications(). To identify a specific notification, you can use Notification.tag.
Finally, you must keep a Promise alive inside "waitUntil" to prevent the service-worker to shut down before your timeout triggers:
event.waitUntil(
self.registration.showNotification('Title', {body: 'Body.', tag: 'my-unique-tag'})
.then(() => new Promise(resolve => setTimeout(resolve, 60000)) // keep service worker alive
.then(() => self.registration.getNotifications())
.then(notifications => {
const notification = notifications.find(notification => notification.tag === 'my-unique-tag')
if (notification) {
notification.close()
}
})
);

Resources