I have a ionic + firebase chat app and I'd like to send a notification to a user when a new message is received.
In the Ionic oficial documentation, the recommended plugin for this is:
https://github.com/katzer/cordova-plugin-local-notifications
But as you can see inside the repository, it hasnt been updated since last february and based in comments inside it, it seems like it doesnt work with last Android OS versions.
Does anyone know an alternative?
Thanks!
I had the same problem to implement push notifications in my app last month. This is the best tutorial to do that: https://medium.com/#senning/push-notifications-with-ionic-and-cordova-plugin-firebase-ab0c0cad3cc0
I customized this tutorial to a file: messaging.service.ts
import {Injectable} from '#angular/core';
import {ApiService} from './api.service';
import {AppApiResponse} from '../interfaces/interfaces'
import {Firebase} from "#ionic-native/firebase";
import {Platform} from "ionic-angular";
import {AngularFirestore} from "#angular/fire/firestore";
#Injectable()
export class MessagingService {
private userUid: string;
constructor(private firebase: Firebase,
public afs: AngularFirestore,
public platform: Platform) {
}
initializeFirebase(userUid) {
this.userUid = userUid;
if (!this.platform.is("core")) {
this.firebase.subscribe("all");
this.platform.is('android') ? this.initializeFirebaseAndroid() : this.initializeFirebaseIOS();
}
}
initializeFirebaseAndroid() {
this.firebase.getToken().then(token => {
this.saveTokenToFirestore(token);
console.log('token android= ' + JSON.stringify(token));
});
this.firebase.onTokenRefresh().subscribe(token => {
this.saveTokenToFirestore(token);
console.log('token refresh android= ' + JSON.stringify(token));
});
this.subscribeToPushNotifications();
}
initializeFirebaseIOS() {
this.firebase.grantPermission()
.then(() => {
this.firebase.getToken().then(token => {
this.saveTokenToFirestore(token);
console.log('token ios= ' + JSON.stringify(token));
});
this.firebase.onTokenRefresh().subscribe(token => {
this.saveTokenToFirestore(token);
console.log('token refresh ios= ' + JSON.stringify(token));
});
this.subscribeToPushNotifications();
})
.catch((error) => {
this.firebase.logError('push erro ios= ' + error);
});
}
subscribeToPushNotifications() {
this.firebase.onNotificationOpen().subscribe((response) => {
console.log('response push= ' + JSON.stringify(response));
if (response.tap) {
//Received while app in background (this should be the callback when a system notification is tapped)
//This is empty for our app since we just needed the notification to open the app
} else {
//received while app in foreground (show a toast)
}
});
}
private saveTokenToFirestore(token) {
if (!token) return;
const devicesRef = this.afs.collection('devices');
const docData = {
token,
userId: this.userUid,
};
return devicesRef.doc(token).set(docData)
}
}
To use in your code is just include to page constructor:
public msgService: MessagingService
And use:
try {
this.msgService.initializeFirebase(user.uid);
} catch (error) {
console.log('fire push erro= ' + error);
}
Related
So, I'm quite a noob when it comes to ionic 4 firebase integration. Basically I've managed to get the firebase authentication to work, but the problem is I can't seem to save the user's input in the sign up page to firebase database. I need it to work as I would like to link the user to his/her checklist.
This is my sign-up page.ts
import { Component, OnInit } from '#angular/core';
import { Router } from "#angular/router";
import { Platform, AlertController } from '#ionic/angular';
import { LoadingController, ToastController } from '#ionic/angular';
import { AngularFireAuth } from '#angular/fire/auth';
//disable side menu
import { MenuController } from '#ionic/angular';
#Component({
selector: 'app-signup',
templateUrl: './signup.page.html',
styleUrls: ['./signup.page.scss'],
})
export class SignupPage implements OnInit {
email: string = '';
password: string = '';
error: string = '';
username: string = '';
constructor(
private fireauth: AngularFireAuth,
public router: Router,
public menuCtrl: MenuController,
private toastController: ToastController,
private platform: Platform,
public loadingController: LoadingController,
public alertController: AlertController
) { }
async openLoader() {
const loading = await this.loadingController.create({
message: 'Please Wait ...',
duration: 2000
});
await loading.present();
}
async closeLoading() {
return await this.loadingController.dismiss();
}
signup() {
this.fireauth.auth.createUserWithEmailAndPassword(this.email, this.password)
.then(res => {
if (res.user) {
console.log(res.user);
this.updateProfile();
}
})
.catch(err => {
console.log(`login failed ${err}`);
this.error = err.message;
});
}
updateProfile() {
this.fireauth.auth.onAuthStateChanged((user) => {
if (user) {
console.log(user);
user.updateProfile({
displayName: this.username
})
.then(() => {
this.router.navigateByUrl('/login');
})
}
})
}
async presentToast(message, show_button, position, duration) {
const toast = await this.toastController.create({
message: message,
showCloseButton: show_button,
position: position,
duration: duration
});
toast.present();
}
ionViewWillEnter (){
this.menuCtrl.enable(false);
}
ngOnInit(){}
}
Actually, you aren't using the firebase database. The user.updateProfile method stores the displayName property in the Firebase Auth system. That information is saved in the Firebase Auth system.
I can get the push notification when my apps is on background/closed and I can get the data in the payload of the notifications when i tapped it and open
"notification": {
"title": "Chat Messages",
"body": `You have received chat messages from ${userName}`,
"sound": "default",
"click_action":"FCM_PLUGIN_ACTIVITY",
"icon":"fcm_push_icon"
},
"data": {
"user_id" : from_user_id,
"user_name" : userName,
"type" : notification_type
}
but there is a problem which I can't make it open a specific page after i tapped the notification when in background/closed.
Here is my code in the app.components.ts:
import { Component, ViewChild } from '#angular/core';
import { Platform, NavController } from 'ionic-angular';
import { StatusBar } from '#ionic-native/status-bar';
import { SplashScreen } from '#ionic-native/splash-screen';
import { FCM } from '#ionic-native/fcm';
import { LoginPage } from '../pages/login/login';
import { TestpagePage } from '../pages/testpage/testpage';
#Component({
templateUrl: 'app.html'
})
export class MyApp {
#ViewChild('myNav') navCtrl: NavController
rootPage:any = LoginPage;
constructor(fcm: FCM, platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
fcm.onNotification().subscribe(function(data) {
if(data.wasTapped) {
alert('HI');
if(data.type == 'messages') {
alert('HEY');
this.navCtrl.setRoot(TestpagePage);
}
} else {
alert('Untapped<br>' +
'User ID: ' + data.user_id +
'<br>User Name: ' + data.user_name +
'<br>Type: ' + data.type);
}
});
});
}
}
It will execute the 'HI' and 'HEY' alert inside the if(data.wasTapped) { but it seems like the this.navCtrl.setRoot('TestpagePage'); is not fired. I don't know why please help me!
So the problem have been solved.
Like #SurajRao suggested, instead of using fcm.onNotification().subscribe(function(data) { I followed his suggestion and updated my code to fcm.onNotification().subscribe((data) => { and it works.
on my login Page i've got a button that is calling this function
googleauth(){
if(this.auth.signInGoogle()){
alert("1");
if(this.auth.getcurrentUser().displayName){
alert("2");
this.gameStatus.players[0].name = this.auth.getcurrentUser().displayName;
alert("3");
this.navCtrl.setRoot(HomePage);
}
}else{
alert("googleauth else");
}
}
in my auth provider this is the signInGoogle() function
signInGoogle(){
this.googleplus.login({
'webClientId' : '',
'offline' : true
})
.then((res)=>{
const firecreds = firebase.auth.GoogleAuthProvider.credential(res.idToken);
firebase.auth().signInWithCredential(firecreds).then((success)=>{
alert("auth1");
return true;
}).catch((err)=>{
alert('Firebase auth failed ' + err);
})
}).catch((err)=>{
alert('Error:' + err);
return false;
});
}
This is whats displayed via alert on my phone when i click on the button:
googleauth else
auth1
auth1 is above return true, so the stuff inside if(this.auth.signInGoogle()){..} should be called, but instead the else {..} part is called.
the googleauth else alert is called before the auth1 alert is there something like a waiting function i have to use? is the functions running in threads? how can this happen? how could i fix it?
Thank you guys in advance
Edit
my account is shown in firebase authentication page
The whole authProvider
import { Injectable } from '#angular/core';
import 'rxjs/add/operator/map';
import { GooglePlus } from '#ionic-native/google-plus';
import * as firebase from 'firebase/app';
import { GamestatusProvider } from '../../providers/gamestatus/gamestatus';
#Injectable()
export class AuthProvider {
constructor(public googleplus: GooglePlus) {}
signInGoogle(){
this.googleplus.login({
'webClientId' : '..',
'offline' : true
})
.then((res)=>{
const firecreds = firebase.auth.GoogleAuthProvider.credential(res.idToken);
firebase.auth().signInWithCredential(firecreds).then((success)=>{
alert("auth1");
return true;
}).catch((err)=>{
alert('Firebase auth failed ' + err);
})
}).catch((err)=>{
alert('Error:' + err);
return false;
});
}
getcurrentUser(){
return firebase.auth().currentUser;
}
}
export class LoginPage {
constructor(public navCtrl: NavController, public navParams: NavParams, public auth: AuthProvider, public gameStatus: GamestatusProvider) {
firebase.auth().onAuthStateChanged(firebaseUser => {
if(firebaseUser){
this.navCtrl.setRoot(HomePage);
}
});
}
ionViewDidLoad() {
console.log('ionViewDidLoad LoginPage');
}
anonAuth(){
this.auth.signInAnonym();
}
googleAuth(){
this.auth.signInGoogle();
}
}
export class AuthProvider {
constructor(public googleplus: GooglePlus, public gameStatus: GamestatusProvider) {}
signInAnonym(){
firebase.auth().signInAnonymously();
}
signInGoogle(){
this.googleplus.login({
'webClientId' : webClientIdGooglePlusApi,
'offline' : true
})
.then((res)=>{
const firecreds = firebase.auth.GoogleAuthProvider.credential(res.idToken);
firebase.auth().signInWithCredential(firecreds).then((success)=>{
return true;
}).catch((err)=>{
alert('Firebase auth failed ' + err);
})
}).catch((err)=>{
alert('Error:' + err);
return false;
});
}
signOut(){
firebase.auth().signOut();
}
}
is Working, but i still would love to know why my first attempt didnt worked?
I am very new to ionic, currently working/learning with Ionic 2. I would like to show a toast when a user goes offline. I am currently able to do that as shown in my code below (toast shows whenever user goes offline). However what i would like to do is show the toast on http request (pull to refresh and infinite scroll). So that even when data is already loaded, the toast gets displayed when the user tries to pull to refresh on load more data through infinite scroll then they get notified that they are offline.
export class HomePage {
datas:any = [];
page:number =1;
connected: Subscription;
disconnected: Subscription;
constructor(private toast: ToastController, private network: Network, public navCtrl: NavController, private wpapi: Wpapi) {
this.getPosts();
}
displayNetworkUpdate(connectionState: string){
//let networkType = this.network.type
this.toast.create({
message: `You are currently ${connectionState}, please re connect your data`,
duration: 3000
}).present();
}
ionViewDidEnter() {
this.disconnected = this.network.onDisconnect().subscribe(data => {
console.log(data)
this.displayNetworkUpdate(data.type);
}, error => console.error(error));
}
getPosts() {
//this.page = '1';
//this.wpapi.index(this.page)
this.wpapi.index(1)
.subscribe(data => {
this.datas = data;
console.log(this.datas);
});
}
loadMore(infiniteScroll) {
this.page++;
this.wpapi.index( this.page ).subscribe( datas => {
// Loads posts from WordPress API
let length = datas["length"];
if( length === 0 ) {
infiniteScroll.complete();
return;
}
for (var i = length - 1; i >= 0; i--) {
this.datas.push( datas[i] );
}
infiniteScroll.complete();
});
}
doRefresh(refresher) {
this.wpapi.index(1)
.subscribe(data => {
this.datas = data;
refresher.complete();
});
}
ionViewWillLeave(){
this.connected.unsubscribe();
this.disconnected.unsubscribe();
}
}
This is what i'm doing. In my app.components i have the connection subscriber, beeing it offline or online, so if a user goes offline i save a conn boolean variable with false, if online i save true in my localStorage and present a toast saying it has gone offline (if gone online there's no need to present a toast).
network.onDisconnect().subscribe(() => {
storage.set('conn', false);
let conoff = ToastCtrl.create({
closeButtonText: 'Ok',
showCloseButton: true,
message: 'You're not connected to internet.',
position: 'top'
});
conoff.present();
});
You can create a service to do this, something like
import { Injectable } from '#angular/core';
import { Storage } from '#ionic/storage';
import { ToastController, Platform } from 'ionic-angular';
#Injectable()
export class Verificador {
constructor(public toast: ToastController, public storage: Storage, public platform: Platform) {
}
verifyConnection = (): Promise<boolean> => {
return new Promise<boolean>((res, rej) => {
this.storage.get('conn').then(conn => {
if (conn) {
res(true);
} else {
let t = this.toast.create({
closeButtonText: 'Ok',
showCloseButton: true,
message: 'You can't do this without internet.',
position: 'top'
});
t.present();
res(false);
}
})
})
}
}
So in every component, page entering, http call, you import that service/provider and call the verifyConnection function, if it returns true you just let the user do what it needs to do, if it's false the provider will show the toast.
import { ConnVerification} from '../../../providers/ConnVerification';
#IonicPage()
#Component({
selector: 'your-page',
templateUrl: 'your-page',
providers: [ConnVerification]
})
export class YourPage {
constructor(public verif: ConnVerification){}
myFunctionForSomething(){
this.verif.verifyConnection().then(conn =>{
if(conn){
// DO YOUR CODE
}
// NO NEED FOR ELSE SINCE IT'S HANDLED ON PROVIDER
})
}
}
Hope it helps :)
I write a test tabs ionic 2 app use sqlite plugin. I wrapper the sqlite as provier:
import { SQLite, Device } from 'ionic-native';
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
/*
Generated class for the SqliteHelper provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
#Injectable()
export class SqliteHelper {
public db : SQLite;
public log : string = "";
constructor(public http: Http) {
console.log('Hello SqliteHelper Provider');
}
public initDb() {
this.db = new SQLite();
this.log += "openDatabase。。。";
// if (Device.device.platform)
this.db.openDatabase({
name: "data.db",
location: "default"
}).then((data) =>{
this.log += ("open ok " + JSON.stringify(data));
}, (err) => {
this.log += ("open err " + err.message + " " + JSON.stringify(err));
});
}
public executeSql(statement: string, parms:any) {
return this.db.executeSql(statement, parms);
}
}
And init sqlitehelper in app.components :
constructor(platform: Platform, sqliteHelper : SqliteHelper, events: Events) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
sqliteHelper.initDb();
events.publish("sqlite:inited", null);
StatusBar.styleDefault();
Splashscreen.hide();
});
}
And I load data from the first tabs page in constructor with platform.ready, run it on android will cause err: cannot read property executeSql of undefined.
If I load data from a button click, it's ok. or I put the loaddata to the second page's constructor, it's ok too.why?who can help me , I want to put code to the first page and load data at page started.
I had the same error when i try to insert in database for that reason i added "executeSql" function inside transaction:
this.database.openDatabase({
name: "sis.db",
location: "default"
}).then(() => {
this.database.executeSql("CREATE TABLE IF NOT EXISTS profile(id integer primary key autoincrement NOT NULL ,name Text NOT NULL)", []).then((data) => { console.log('profile table created'); }, (error) => { console.log('Unable to create table profile'); })
this.database.transaction(tr => { tr.executeSql("insert into profile(id,name) values(12,'Ibrahim')", []); }).then(d => { console.log('Data inserted ya hima'); }, err => { console.error('unable to insert data into profile table'); });
this.database.executeSql("select id from profile", []).then(d => { console.log('inserted id=' + d.rows.item(0).app_id); }, err => { console.error('unable to get ID from profile table'); });
}, (error) => {console.error(error); }
);