emrecamasuvi
10/6/2017 - 10:19 PM

ozgon.md

ozgon.md

  • Bu işin frontend tarafı için mikrofon bende.
  • Öncelikle uygulama yaptığınız sitenin localhost veya SSL'li bir sayfa olması gerekmekte. Örneğin bildirim pencerelerindeki resimlerde https'li resim linki vermezseniz; bildirimlerinizde resimler gözükmez. Bir de dip not; gizli sekmede de bildirimleri serviceWorker etkileşimlerini göremezsiniz.
  • Firebase konsolda projenizi açtıktan sonra firebase.js dosyasını sayfaya çağırmak gerekiyor. Biz ilgili .Js dosyasını sayfa yüklendikten sonra çağırıyoruz. Script'in onload eventinde de ilgili congib bilgilerimizle, instance'ımızı yaratıyoruz.
window.addEventListener("load", function(){
        loadScriptsWithCb("//www.gstatic.com/firebasejs/3.6.1/firebase.js", function(){
            // Initialize Firebase
            var firebaseConfig = {
                apiKey: "AIzxxx",
                authDomain: "xxx.firebaseapp.com",
                databaseURL: "https://xxx.firebaseio.com",
                storageBucket: "xxx.appspot.com",
                messagingSenderId: "49952xxx300"
            };
            if (!firebase.apps.length) {
                self.firebaseConstructor = firebase.initializeApp(firebaseConfig);
                self.messaging = firebaseConstructor.messaging();
            }
        });
    });
  • Sıra geldi bildirim iznine. UX fail olarak uygulama/siteniz örnek gösterilsin istemezseniz bildirim iznini uygun şekilde almanız şart. Önce kullanıcıya ne için bildirim göndermek istediğinizi tasarımınıza uygun şekilde göstermeli ancak kullanıcı kabul ederse; tarayıcının Notification iznini istemelisiniz. Kullanıcı bir kere "engelle" derse (ki günümüzdeki o kadar istismar edildi ki bu bildirimler; yeni reklam körlüğü bildirimleri engellemek oldu) bunu tersine çevirmeniz çok çok zor.
  • Kullanıcı gösterim izni istemezse bir süre bu "hayır" seçimini cookie'de tutabilir, ardından tekrar Notification iznini kullanıcıya doğru UX yaklaşımıyla sorabilirsiniz.
  • Modern tarayıcıların bildirim desteği epeydir var. Siz yine de progressive enhancement yaklaşımından hareketle yeni tarayıcılar için önce Notification tanımlı mı, kontrol etmeli. Tanımlıysa ve izin sonucu belirsiz ise ki 3 seçenekten default olanı budur; kullanıcınızı tarayıcısından da izin vermesi için ikna etme aşamasına geçebilirsiniz.
if (!!(<any>this.$window).Notification && (<any>this.$window).Notification.permission === "default")  {
    // devam
}
  • messaging namespace, firebase instance'da tetiklendiği için, messaging.requestPermission() ile tarayıcının native Notification isteğini tetikleyebiliriz. Firebase kullanmasaydık bu isteği native olarak da Notification.requestPermission() ile tetikleyebilirdik. Dönen sonuç Promise olacağı için .then(callback) ile bir sonraki adıma geçebiliriz.
window.messaging.requestPermission().then(function () {
    console.log('Notification izni temin edildi.');
});
  • dönen success callback içinde olmak güzel. Bu aşamada öncelikle kullanıcıyı rahatsız eden görsel tavsiyeleri, bildirim sunabildiğimiz shameless self promotionları cookie, localStorage vs içine yazarak kaldırmak gerekiyor; kaleyi fethettik; gönülleri de fethetmeli.
  • sırada hamle backend'in firebase servisini kullanması için gereken Token'ı kaydetmek. Bunun için önce firebase messaging'den kullanıcı (izin verdiği tarayıcı) için Token almalıyız.
window).messaging.getToken().then(function (token: any) {
    sendTokenToServer(token);
});
  • ServiceWorkerlara bulaşmadan önce çokca methini duymuştum, en kabaca belirtmek gerekirse service workerlar assetleri cacheleme; işbu assetleri örneğin offline durumda bile net varmış gibi sunabilme ve tarayıcı açık/kapalıyken arkaplanda sistemi yormadan çalışıp notification/payloadData göndermemize yarıyor.
  • Bildirimleri service worker tarafında sunmamız için firebase özelinde sitenizin /root pathinde firebase-messaging-sw.js isimli bir dosya oluşturmanız gerekiyor. Bu firebase instance'ımızın arayacağı kaynak dosyası. firebase instance yaratırken, işbu dosya ismini register etti. Firebase kullanmazsanız, register işlerini sizin halletmeniz gerekiyor.
  • serviceWorker scope'u ilk başta garip gelebilir. tarayıcının window'u gibi, serviceWorker'ın "self" diye native bir üst katman objesi var. eventListenerlarımızı self'e ekleyeceğiz. ImportScripts ile firebase'in ilgili js dosyalarını çekip, tekrar instance yaratmamız gerekmekte.
  • oluşturduğumuz messaging instance'ın setBackgroundMessageHandler metotuyla token'ı kaydettiğimiz user'a backend'den payload gönderebiliriz. Bizim kullandığımız kodu aşağıda görebilirsiniz. callback içindeki self.registration bizim izin objemiz, showNotification metodu ise zurnanın zırt dediği yer.
messaging.setBackgroundMessageHandler(function(payload) {
    let obj = payload.data;
    const title = obj.title;
    let options = {
        body: obj.body,
        icon: obj.icon,
        timestamp: +new Date,
        badge: obj.badge,
        data: {
            payloadr: {
                memberNotificationId: obj.memberNotificationId,
                click_action: obj.click_action
            }
        },
        path: obj.click_action
    }
    if (!!obj.imageUrl) {
        options["image"] = obj.imageUrl;
    }
    if (!!obj.type) {
        options["tag"] = obj.type;
    }
    return self.registration.showNotification(title, options)
});
  • esasında hepsi bu. Bu işlem bildirimi gösterecektir; bildirime tıklandığında payload içindeki Url'i açması için ise notificationclick metoduna callback function eklememiz gerekiyor. Biz tıklanan bildirimi, tarayıcıda açmanın yanı sıra fetch Api ile okundu bildirimi endpointine Token ile vuruyor ve işbu bildirimi "okundu" olarak işaretletiyoruz.
self.addEventListener('notificationclick', function(event) {
    let obj = event.notification;
    let payloaderData = obj.data.payloadr;
    var checkAndMarkAsRead = function(){
        if (!payloaderData.memberNotificationId) {
            return;
        }
        var url = new URL(this.origin + "/membernotification/markasread"),
        params = {Id: payloaderData.memberNotificationId};
        messaging.getToken().then(function(token){
            params.token = token;
            Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
            fetch(url).then(function(response) {
                if (!response.ok) return new Error(response);
                console.log("ok", response);
            }).catch(function(err) {
                console.log(err);
            });
        });
    };

    event.notification.close();

    event.waitUntil(clients.matchAll({
        type: "window"
    }).then(function(clientList) {
        for (var i = 0; i < clientList.length; i++) {
            var client = clientList[i];
            if (client.url == '/' && 'focus' in client) {
                return client.focus();
            }
        }
        if (clients.openWindow){
            checkAndMarkAsRead();
            return clients.openWindow(payloaderData.click_action);
        }
    }));
});
  • sayfanız arkaplandaysa veya tarayıcınız kapalıyken çağırdığımız metotlar bu şekildeyken, ilgili site tarayıcıda açıkken de Js controller tarafında yine messaging instance üzerinde onMessage metodu ile bildirimleri sayfanız üzerinde de gösterebilirsiniz.
window.messaging.onMessage(function (payload) {
    let currentUrl = window.location.href;
    let payloaderData = payload.data;
    let checkAndMarkAsRead = function(){
        if (!payloaderData.memberNotificationId) {
            return;
        }
        let markReadObj = {
            Id: payloaderData.memberNotificationId
        }
        self.httpHelperService.httpPost("/membernotification/markasread", markReadObj, (res) => {
            console.log("marked As Read");
            window.open(payloaderData.click_action, '_blank');
        }, (err)=>{
            self.DialogService.warning({
                error: err.Message
            });
        }, { isNotLoading: true });
    };

    self.DialogService.successWithCallback({
        message: payloaderData.body,
        path: payloaderData.click_action,
        title: payloaderData.title,
        confirm: "Hemen Görüntüle"
    }, checkAndMarkAsRead);
});