Notification when app is closed

I am trying to achieve this but i don’t know how. My nose tells me i could do it with promise. So here is my problem:

I am using the plugin cordova-plugin-fcm-with-dependecy-updated to handle notifications coming from firebase. in my ondeviceready code i have the following:

        window.FCMPlugin.onNotification(function(data){
            if(data.wasTapped){
                if(data.tab)
                    app.tab.show(data.tab)
                else 
                    app.tab.show('#view-main')
                if(data.page)
                    app.views.main.router.navigate(data.page)
                
                if (typeof window.FCMPlugin.clearAllNotifications === "function")
                    window.FCMPlugin.clearAllNotifications();
            } else {
                app.notificationCallbackOnClose = app.notification.create({
                    icon: '<svg width="24px" height="24px" viewBox="0 0 34 29"><g stroke="none" stroke-width="1"><g><path d="M16.3 20.2c0-4.6 4.4-8.5 10-8.5 1.3 0 2.6.3 3.8.7 0-6.6-6.7-12-14.8-12C7 .4.2 6 .2 12.7.2 17 3 21 7.2 23v4.5c0 .5.4.8 1 .5l6-3h1.5c.7 0 1.4 0 2-.2-1-1.4-1.4-3-1.4-4.6zm10-6c-4.2 0-7.6 2.6-7.6 6s3.4 6.2 7.5 6.2h.5l1 .4 1.6 1c.5 0 1 0 1-.6v-1.8c2-1 3.4-3 3.4-5.2 0-3.4-3.3-6-7.5-6z"></path></g></g></svg>',
                    title: data.title,
                    titleRightText: moment().calendar(),
                    text: data.message,
                    closeOnClick: true,
                    closeTimeout: 3000,
                    on: {
                        click: function () {
                            if(data.tab)
                                app.tab.show(data.tab)
                            else 
                                app.tab.show('#view-main')
                            if(data.page)
                                app.views.current.router.navigate(data.page)
                        },
                    },
                });
                app.notificationCallbackOnClose.open();
                app.emit('notificationRecieved', data);
            }
        });

which handles the notificaton recieved and depending on wether the app is in foreground or background, it either shows a notification popup or not. It works quite well but in one situation it doesn’t.

When the app is closed, so it is neither in background nor in foreground ( from task manager it is closed) Tapping on the notification in the the notification center opens the app but doesn’t do the routing. What I want is, when someone presses on a notification where the payload has a specific page, the apps main view should navigate to that page. That does work in foreground and background but not when the app is closed.

Any idea on how i could solve that?

Did the onNotification callback actually get called in this case? It could happen that it is called before app init, so navigation doesn’t work. You can try to call app methods in it within some timeout, let’s say 500 ms

hmm how would you do that? would you put the callback outside of device ready?

I mean to wrap what is INSIDE of this callback with setTimeout:

window.FCMPlugin.onNotification(function(data){
  setTimeout(() => {
    // rest of code here
  }, 500)
});

i will try it out, but that wont be such a seamless experience for the user i guess

any other suggestion?

with timeout it doesn’t work it seems like the callback deosn’t fireup probably it must go outside of device ready and i have to think of a way to add some queuing or make it wait till the device is ready any suggestion how?

I thought about making a localstorage entry and check in device ready if it is there or not and do then the navigation

You can use async/await , since onNotification is a callback, code flow do not wait for execution of callback as it is asynchronous in nature. By using await you can tell the code to wait for till execution of callback finishes. In simple words, it makes execution of asynchronous code into synchronous one

I don‘t really understand how await works, i read the documentation but couldn‘t grasp it yet.

Based on what I understand is, I keep the callback in deviceready, but add await to the app specific functions? Is that all? But they are no promises they are just normal functions.

first of all await is not framework7 specific , it is a javascript conept. I will try to describe it using a example. Suppose you have a functionA which is asynchronous and you want it to execute sequentially. Why Sequentially?
1 Result returned by functionA will be used later
2 Changes done by functionA are required for further operation(which i believe is your use case)
Consider a simple code example.

let result='';
async function functionB(){
      result=await functionA(); //we'e waiting till this callback finishes its execution
     //or you can call it just like this
     await functionB();
}

i believe this example gives you enough insight about how it works.
Plus, your assumption is wrong that onNotification is not a normal function, it’s a callback . You can use .then() to create promise to see whether it has executed correctly or returened errors.
I this must solve your problem.

  window.FCMPlugin.onNotification(async function(data){
        if(data.wasTapped){
            if(data.tab)
                app.tab.show(data.tab)
            else 
                app.tab.show('#view-main')
            if(data.page)
                app.views.main.router.navigate(data.page)
            
            if (typeof window.FCMPlugin.clearAllNotifications === "function")
                window.FCMPlugin.clearAllNotifications();
        } else {
            app.notificationCallbackOnClose = app.notification.create({
                icon: '<svg width="24px" height="24px" viewBox="0 0 34 29"><g stroke="none" stroke-width="1"><g><path d="M16.3 20.2c0-4.6 4.4-8.5 10-8.5 1.3 0 2.6.3 3.8.7 0-6.6-6.7-12-14.8-12C7 .4.2 6 .2 12.7.2 17 3 21 7.2 23v4.5c0 .5.4.8 1 .5l6-3h1.5c.7 0 1.4 0 2-.2-1-1.4-1.4-3-1.4-4.6zm10-6c-4.2 0-7.6 2.6-7.6 6s3.4 6.2 7.5 6.2h.5l1 .4 1.6 1c.5 0 1 0 1-.6v-1.8c2-1 3.4-3 3.4-5.2 0-3.4-3.3-6-7.5-6z"></path></g></g></svg>',
                title: data.title,
                titleRightText: moment().calendar(),
                text: data.message,
                closeOnClick: true,
                closeTimeout: 3000,
                on: {
                    click: function () {
                        if(data.tab)
                            app.tab.show(data.tab)
                        else 
                            app.tab.show('#view-main')
                        if(data.page)
                            app.views.current.router.navigate(data.page)
                    },
                },
            });
            await app.notificationCallbackOnClose.open();
            app.emit('notificationRecieved', data);
        }
    })
1 Like

Thanks a lot! I understand that await/async are js concepts. what i meant with app specific functions are those:

        if(data.tab)
            app.tab.show(data.tab)
        else 
            app.tab.show('#view-main')
        if(data.page)
            app.views.main.router.navigate(data.page)

so i have to add await to that aswell?

and why just await app.notificationCallbackOnClose.open();
but not for app.emit(‘notificationRecieved’, data);

these are all functions which only work when the app is initialized

above example is only for illustration purpose and i haven’t checked docs yet but you need to check that whether app.tab.show and other events are asynchronous or not. plus you also need to think that order of events is important or not. For example, you visit a page and its tab open automatically. Now if tab is openend earlier and page is visible next. It will not affect user experience as for him/her tab change occur at background. You need to think like that.
But in case of notification emited, first you need to ensure that notification call back is executed then app will emit .
In short, you need to think that order of execution is important or not and decide use of await/async accordingly

well it doesn’t work using async/await it doesn’t redirect the user when the app is closed. probably the callback onNotification doesn’t get called. I will try to place it outside of device ready.

doesn’t work either, when the function is outside of device ready it doesn’t even change the tab nor the page. although i am using await/async

Did you solve the problem. If yes, can you provide your code?

not really i tried everything but I fail to fix it so i kept it on my todo list

if anyone comes up with a solution would be thankful

The issue (I think) is that the plugin fires onNotification as fast as possible, not waiting for Framework7 to be ready. Which is to be expected.

I solved this in a very “low tech”, but solid working way :yum: I’ve put the plugin onNotification listener inside the deviceready callback. My notification listener saves the notification data to a global window var and starts an interval (500ms) which checks if Framework7 is ready. If so, it clears the interval and handles the notification data.

@nolimits4web Maybe solutions like this can be nominated for the “F7 Best practises and solutions for regular problems” topic :slight_smile:

In the deviceReady listener:

window.FCMPlugin.onNotification(function(data){
  window.notification_data = data;
  window.notification_interval = window.setInterval(function() {
    if (window.f7ready) {
       window.clearInterval(window.notification_interval);
       // call your handler or emit event, like:
       myNotificationHandler(window.notification_data);
       f7app.emit('notification', window.notification_data);
       window.notification_data = null;
    }
  }, 500);
});

And in my f7 initialization:

window.f7ready = false;

var app = new Framework7({
  ...
  on: {
    init: function () {
      // ... other initialization ...
      window.f7ready = true;
    },
  }
  ...
}

This works great for single view apps. I develop another app with tabbed layout, which has to wait until all tab views are initialized. For that app, I extended my solution with something like this:

var app = new Framework7({
  ...
  on: {
    init: function () {
      this.data.startup_view_count = $('#app .view').length;
    },

    pageInit: function(page) {

      if (this.data.startup_view_count > 0) {
        this.data.startup_view_count--;

        if (this.data.startup_view_count === 0) {
          if (this.device.cordova) { navigator.splashscreen.hide(); } // Hide splash screen when all tabs are initialized
          window.f7ready = true; // Allow the interval checker to handle the notification data
        }
      }

    }
  ...
}

Might not be the most elegant way, but it works great for my apps and never had any issues with notification events getting lost.

2 Likes

looks nice i will try it out!

i tried it out but still doesn’t work for me
for the time beeing i kept it on my todo

Do you mean it closed from app switch ?
Have you found the solution ?

No real solution, it doesn’t or can’t redirect a user to the desired view