الإشعارات الفورية على الويب المفتوح

إذا سألت مجموعة من المطورين عن ميزات الأجهزة المحمولة التي تفتقدها على الويب، فتكون الإشعارات الفورية على رأس القائمة دائمًا.

بفضل الإشعارات الفورية، يمكن للمستخدمين تفعيل ميزة تلقّي التحديثات في الوقت المناسب من المواقع الإلكترونية التي يفضّلونها، وتتيح لك إعادة جذبهم بشكل فعّال من خلال محتوى مخصّص وجذّاب.

اعتبارًا من الإصدار 42 من Chrome، أصبحت Push API وNotification API متاحة لمطوّري البرامج.

تعتمد واجهة برمجة التطبيقات Push API في Chrome على بضعة أنواع مختلفة من التكنولوجيا، بما في ذلك بيانات تطبيق الويب ومشغّلو الخدمات. في هذه المشاركة، سنلقي نظرة على كل واحدة من هذه التكنولوجيات، ولكن سنناقش الحد الأدنى فقط لتفعيل المراسلة الفورية. للحصول على فهم أفضل لبعض الميزات الأخرى في البيانات وإمكانيات العمل بدون الاتصال بالإنترنت للعاملين في الخدمة، يُرجى الاطّلاع على الروابط أعلاه.

سنلقي أيضًا نظرة على ما ستتم إضافته إلى واجهة برمجة التطبيقات في الإصدارات المستقبلية من Chrome، وأخيرًا سنطرح الأسئلة الشائعة.

استخدام خدمة المراسلة الفورية في Chrome

يصف هذا القسم كل خطوة تحتاج إلى إكمالها لدعم الرسائل الفورية في تطبيق الويب.

تسجيل مشغّل الخدمات

هناك تبعية لوجود عامل خدمة لتنفيذ رسائل الدفع على الويب. ويرجع سبب ذلك إلى أنّه عند تلقّي رسالة فورية، يمكن للمتصفح بدء تشغيل عامل خدمة يعمل في الخلفية بدون فتح صفحة وإرسال حدث كي تتمكّن من تحديد طريقة التعامل مع رسالة الدفع هذه.

في ما يلي مثال على كيفية تسجيل مشغّل الخدمات في تطبيق الويب. وعند اكتمال التسجيل بنجاح، نستدعي العملية initialiseState() التي سنتناولها قريبًا.

var isPushEnabled = false;

…

window.addEventListener('load', function() {
    var pushButton = document.querySelector('.js-push-button');
    pushButton.addEventListener('click', function() {
    if (isPushEnabled) {
        unsubscribe();
    } else {
        subscribe();
    }
    });

    // Check that service workers are supported, if so, progressively
    // enhance and add push messaging support, otherwise continue without it.
    if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js')
    .then(initialiseState);
    } else {
    console.warn('Service workers aren\'t supported in this browser.');
    }
});

يعمل معالج النقر على الزر على اشتراك المستخدم في إرسال الرسائل أو إلغاء الاشتراك فيه. isPushEnabled هو متغيّر عمومي يتتبّع ببساطة ما إذا كانت رسائل الدفع مشترك أم لا. ستتم الإشارة إليها عبر مقتطفات التعليمات البرمجية.

بعد ذلك، نتحقّق من توفّر مشغّلي الخدمة قبل تسجيل ملف service-worker.js الذي يحتوي على منطق للتعامل مع رسالة الدفع. وهنا نخبر المتصفح ببساطة أن ملف JavaScript هذا هو مشغّل الخدمات لموقعنا.

إعداد الحالة الأولية

مثال على تجربة مستخدم خدمة المراسلة الفورية المفعَّلة وغير المفعَّلة في Chrome

بعد تسجيل مشغّل الخدمات، علينا ضبط حالة واجهة المستخدِم.

يتوقّع المستخدمون واجهة مستخدم بسيطة لتفعيل الرسائل الفورية أو إيقافها لموقعك الإلكتروني، كما يتوقّعون منهم أن تكون على اطّلاع دائم بأي تغييرات قد تطرأ. بمعنى آخر، إذا كانت الرسائل تُفعِّل رسائل الدفع لموقعك الإلكتروني، ثم تمت المغادرة والعودة مجددًا بعد أسبوع، من المفترض أن تُبرز واجهة المستخدم أنّه قد تم تفعيل الرسائل الفورية.

يمكنك العثور على بعض إرشادات تجربة المستخدم في هذا المستند، وسنركز في هذه المقالة على الجوانب الفنية.

في هذه المرحلة، قد تعتقد أنّ هناك حالتَين فقط يجب التعامل معه، وهما: حالة تفعيل أو إيقاف. هناك بعض الحالات الأخرى المحيطة بالإشعارات والتي يجب أخذها في الاعتبار.

مخطّط بياني يسلّط الضوء على الاعتبارات المختلفة لحالة الدفع في Chrome

هناك عدد من واجهات برمجة التطبيقات التي نحتاج إلى التحقق منها قبل تفعيل الزر، وإذا كان كل شيء متوافقًا، يمكننا تفعيل واجهة المستخدم وضبط الحالة الأولية للإشارة إلى ما إذا كانت رسائل الدفع مشتركًا أم لا.

بما أنّ معظم عمليات التحقّق هذه تؤدي إلى إيقاف واجهة المستخدم، يجب ضبط الحالة الأولية على "غير مفعّلة". ويساعد ذلك أيضًا في تجنُّب أي التباس إذا كانت هناك مشكلة في JavaScript على صفحتك، مثل تعذّر تنزيل ملف JS أو أوقف المستخدم JavaScript.

<button class="js-push-button" disabled>
    Enable Push Messages
</button>

من خلال هذه الحالة الأولية، يمكننا تنفيذ عمليات التحقّق الموضّحة أعلاه بطريقة initialiseState()، أي بعد تسجيل مشغّل الخدمات.

// Once the service worker is registered set the initial state
function initialiseState() {
    // Are Notifications supported in the service worker?
    if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
    console.warn('Notifications aren\'t supported.');
    return;
    }

    // Check the current Notification permission.
    // If its denied, it's a permanent block until the
    // user changes the permission
    if (Notification.permission === 'denied') {
    console.warn('The user has blocked notifications.');
    return;
    }

    // Check if push messaging is supported
    if (!('PushManager' in window)) {
    console.warn('Push messaging isn\'t supported.');
    return;
    }

    // We need the service worker registration to check for a subscription
    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    // Do we already have a push message subscription?
    serviceWorkerRegistration.pushManager.getSubscription()
        .then(function(subscription) {
        // Enable any UI which subscribes / unsubscribes from
        // push messages.
        var pushButton = document.querySelector('.js-push-button');
        pushButton.disabled = false;

        if (!subscription) {
            // We aren't subscribed to push, so set UI
            // to allow the user to enable push
            return;
        }

        // Keep your server in sync with the latest subscriptionId
        sendSubscriptionToServer(subscription);

        // Set your UI to show they have subscribed for
        // push messages
        pushButton.textContent = 'Disable Push Messages';
        isPushEnabled = true;
        })
        .catch(function(err) {
        console.warn('Error during getSubscription()', err);
        });
    });
}

نظرة عامة مختصرة على هذه الخطوات:

  • نتحقّق من توفُّر "showNotification" في النموذج الأوّلي للتسجيل في ServiceWorker. بدونه، لن نتمكن من عرض إشعار من مشغّل الخدمات عند استلام رسالة فورية.
  • نتحقّق من قيمة Notification.permission الحالية للتأكّد من أنّها ليست "denied". يعني رفض الإذن أنّه لا يمكنك عرض الإشعارات حتى يغيِّر المستخدم الإذن يدويًا في المتصفّح.
  • للتحقّق مما إذا كانت الرسائل الفورية متاحة، تحقّق من توفّر PushManager في عنصر النافذة.
  • وأخيرًا، استخدمنا pushManager.getSubscription() للتحقق مما إذا كان لدينا اشتراك بالفعل أم لا. فإذا حدث ذلك، نرسل تفاصيل الاشتراك إلى خادمنا للتأكد من حصولنا على المعلومات الصحيحة وضبط واجهة المستخدم للإشارة إلى أنّ الرسائل الفورية مفعَّلة أو لا. سنلقي نظرة على التفاصيل الواردة في عنصر الاشتراك لاحقًا في هذه المقالة.

ننتظر حتى يتم حلّ المشكلة في "navigator.serviceWorker.ready" للتحقّق من توفُّر اشتراك وتفعيل زر الدفع لأنّه لا يمكنك الاشتراك في خدمة إرسال الرسائل الفورية إلا بعد أن يصبح عامل الخدمة نشطًا.

تتمثل الخطوة التالية في التعامل مع الحالات التي يريد فيها المستخدم تفعيل الرسائل الفورية، ولكن قبل أن نتمكّن من إجراء ذلك، نحتاج إلى إعداد مشروع على Google Developer Console وإضافة بعض المعلمات إلى ملف البيان لاستخدام المراسلة عبر السحابة الإلكترونية من Firebase (FCM)، المعروفة سابقًا باسم Google Cloud Messaging.

إنشاء مشروع على Firebase Developer Console

يستخدم Chrome خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" لمعالجة إرسال الرسائل الفورية وتسليمها، ولكن لاستخدام واجهة برمجة التطبيقات FCM API، يجب إعداد مشروع على Firebase Developer Console.

تتعلّق الخطوات التالية بمتصفحات Chrome وOpera لنظام التشغيل Android وSamsung وتستخدم خدمة "المراسلة عبر السحابة الإلكترونية من Firebase". وسنناقش طريقة عمل ذلك في المتصفحات الأخرى لاحقًا في المقالة.

إنشاء مشروع مطوّر جديد على Firebase

للبدء، تحتاج إلى إنشاء مشروع جديد على https://console.firebase.google.com/ بالنقر على "إنشاء مشروع جديد".

لقطة شاشة جديدة لمشروع Firebase

أضف اسم المشروع وأنشئه وسيتم نقلك إلى لوحة معلومات المشروع:

الصفحة الرئيسية لمشروع Firebase

من لوحة المعلومات هذه، انقر فوق الترس الموجود بجوار اسم مشروعك في الزاوية اليسرى العلوية وانقر على "إعدادات المشروع" (Project Settings).

قائمة إعدادات مشروع Firebase

في صفحة الإعدادات، انقر على علامة التبويب "المراسلة عبر السحابة الإلكترونية".

قائمة &quot;المراسلة عبر السحابة الإلكترونية من Firebase&quot; لمشروع Firebase

تحتوي هذه الصفحة على مفتاح واجهة برمجة التطبيقات لرسائل الدفع، وسنستخدمه لاحقًا، ومعرّف المرسِل الذي نحتاج إلى وضعه في بيان تطبيق الويب في القسم التالي.

إضافة بيان تطبيق ويب

لإرسال المحتوى بشكل فوري، يجب إضافة ملف بيان يحتوي على الحقل gcm_sender_id، لإنجاح الاشتراك في Push. هذه المعلمة مطلوبة فقط من قِبل Chrome وOpera لنظام التشغيل Android ومتصفّح Samsung حتى يتمكنوا من استخدام FCM أو GCM.

تستخدم هذه المتصفحات gcm_sender_id عند اشتراك جهاز مستخدم من خلال ميزة "المراسلة عبر السحابة الإلكترونية من Firebase". وهذا يعني أنّه بإمكان خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" التعرّف على جهاز المستخدم والتأكد من أنّ معرّف المُرسِل يتطابق مع مفتاح واجهة برمجة التطبيقات المقابل وأنّ المستخدم قد سمح لخادمك بإرسال رسائل الدفع إليه.

في ما يلي ملف بيان بسيط للغاية:

{
    "name": "Push Demo",
    "short_name": "Push Demo",
    "icons": [{
        "src": "images/icon-192x192.png",
        "sizes": "192x192",
        "type": "image/png"
        }],
    "start_url": "/index.html?homescreen=1",
    "display": "standalone",
    "gcm_sender_id": "<Your Sender ID Here>"
}

يجب ضبط القيمة gcm_sender_id على معرّف المُرسِل من مشروع Firebase.

بعد حفظ ملف البيان في مشروعك (يُعد Manifest.json اسمًا جيدًا)، أشِر إليه من HTML باستخدام العلامة التالية في رأس صفحتك.

<link rel="manifest" href="/manifest.json">

إذا لم تضِف بيان ويب يتضمّن هذه المَعلمات، ستحصل على استثناء عند محاولة اشتراك المستخدم لإرسال الرسائل، مع ظهور الخطأ "Registration failed - no sender id provided" أو "Registration failed - permission denied".

الاشتراك في خدمة Push Messaging

الآن بعد إعداد ملف البيان، يمكنك الرجوع إلى JavaScript في مواقعك الإلكترونية.

للاشتراك، يجب استدعاء الإجراء Unsubscribe() في الكائن PushManager، الذي يمكنك الوصول إليه من خلال ServiceWorkerRegistration.

ستطلب هذه الخطوة من المستخدم منح المصدر الإذن لإرسال إشعارات فورية. بدون هذا الإذن، لن تتمكن من الاشتراك بنجاح.

إذا تم حلّ التعهد الذي تم إرجاعه باستخدام طريقة Unsubscribe() ، سيظهر لك عنصر PushSubscription يحتوي على نقطة نهاية.

يجب حفظ نقطة النهاية على الخادم لكل مستخدم، لأنّك ستحتاج إليه لإرسال رسائل فورية في وقت لاحق.

يشترك الرمز التالي في اشتراك المستخدم في خدمة رسائل الدفع:

function subscribe() {
    // Disable the button so it can't be changed while
    // we process the permission request
    var pushButton = document.querySelector('.js-push-button');
    pushButton.disabled = true;

    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    serviceWorkerRegistration.pushManager.subscribe()
        .then(function(subscription) {
        // The subscription was successful
        isPushEnabled = true;
        pushButton.textContent = 'Disable Push Messages';
        pushButton.disabled = false;

        // TODO: Send the subscription.endpoint to your server
        // and save it to send a push message at a later date
        return sendSubscriptionToServer(subscription);
        })
        .catch(function(e) {
        if (Notification.permission === 'denied') {
            // The user denied the notification permission which
            // means we failed to subscribe and the user will need
            // to manually change the notification permission to
            // subscribe to push messages
            console.warn('Permission for Notifications was denied');
            pushButton.disabled = true;
        } else {
            // A problem occurred with the subscription; common reasons
            // include network errors, and lacking gcm_sender_id and/or
            // gcm_user_visible_only in the manifest.
            console.error('Unable to subscribe to push.', e);
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
        }
        });
    });
}

في هذه المرحلة، يكون تطبيق الويب جاهزًا لتلقي رسالة فورية، على الرغم من أنّ أي شيء لن يحدث حتى نضيف أداة معالجة أحداث الدفع إلى ملف مشغّل الخدمات.

أداة معالجة حدث الدفع لمشغّل الخدمات

عند تلقّي رسالة فورية (سنتحدث عن طريقة إرسال رسالة فورية في القسم التالي)، سيتم إرسال حدث فوري إلى عامل الخدمة، وعندئذٍ يجب عرض إشعار.

self.addEventListener('push', function(event) {
    console.log('Received a push message', event);

    var title = 'Yay a message.';
    var body = 'We have received a push message.';
    var icon = '/images/icon-192x192.png';
    var tag = 'simple-push-demo-notification-tag';

    event.waitUntil(
    self.registration.showNotification(title, {
        body: body,
        icon: icon,
        tag: tag
    })
    );
});

يسجِّل هذا الرمز أداة معالجة أحداث الدفع ويعرض إشعارًا يتضمن عنوانًا محددًا مسبقًا ونصًا أساسيًا ورمزًا وعلامة إشعار. في هذا المثال، ثمة خطوة محدّدة يجب تسليط الضوء عليها، وهي طريقة event.waitUntil(). وتتخذ هذه الطريقة وعدًا وتطيل عمر معالج الحدث (أو يمكن اعتباره إبقاء عامل الخدمة على قيد الحياة)، إلى أن تتم تسوية الوعد، في هذه الحالة، يكون الوعد الذي تم تمريره إلى event.waitUntil هو الوعد الذي تم إرجاعه من showNotification().

تعمل علامة الإشعار كمعرّف للإشعارات الفريدة. إذا أرسلنا رسالتين فوريتين إلى نقطة النهاية نفسها، مع مهلة قصيرة بينهما، وعرضنا إشعارات بالعلامة نفسها، سيعرض المتصفح الإشعار الأول ويستبدله بالإشعار الثاني عند تلقي رسالة الدفع.

وإذا كنت تريد عرض إشعارات متعددة في آنٍ واحد، استخدِم علامة مختلفة، أو لا تستخدم أي علامة على الإطلاق. سنلقي نظرة على مثال أكثر اكتمالاً لعرض إشعار لاحقًا في هذه المشاركة. في الوقت الحالي، لنجعل الأمور بسيطة ونرى ما إذا كان إرسال رسالة فورية سيؤدي إلى عرض هذا الإشعار.

إرسال رسالة فورية

لقد اشتركنا في الرسائل الفورية وعامل الخدمة جاهز لعرض الإشعار، لذا حان الوقت لإرسال رسالة فورية من خلال خدمة "المراسلة عبر السحابة الإلكترونية من Firebase".

ينطبق هذا فقط على المتصفحات التي تستخدم ميزة "المراسلة عبر السحابة الإلكترونية من Firebase".

عند إرسال المتغير PushSubscription.endpoint إلى خادمك، تكون نقطة نهاية "المراسلة عبر السحابة الإلكترونية من Firebase" خاصة. وهي تضم مَعلمة في نهاية عنوان URL، وهي registration_id.

قد تكون نقطة النهاية كمثال:

https://fcm.googleapis.com/fcm/send/APA91bHPffi8zclbIBDcToXN_LEpT6iA87pgR-J-MuuVVycM0SmptG-rXdCPKTM5pvKiHk2Ts-ukL1KV8exGOnurOAKdbvH9jcvg8h2gSi-zZJyToiiydjAJW6Fa9mE3_7vsNIgzF28KGspVmLUpMgYLBd1rxaVh-L4NDzD7HyTkhFOfwWiyVdKh__rEt15W9n2o6cZ8nxrP

عنوان URL الخاص بخدمة "المراسلة عبر السحابة الإلكترونية من Firebase" هو:

https://fcm.googleapis.com/fcm/send

سيكون registration_id:

APA91bHPffi8zclbIBDcToXN_LEpT6iA87pgR-J-MuuVVycM0SmptG-rXdCPKTM5pvKiHk2Ts-ukL1KV8exGOnurOAKdbvH9jcvg8h2gSi-zZJyToiiydjAJW6Fa9mE3_7vsNIgzF28KGspVmLUpMgYLBd1rxaVh-L4NDzD7HyTkhFOfwWiyVdKh__rEt15W9n2o6cZ8nxrP

وينطبق ذلك على المتصفّحات التي تستخدم "المراسلة عبر السحابة الإلكترونية من Firebase". في المتصفح العادي، ستحصل ببساطة على نقطة نهاية وقد تستدعي نقطة النهاية هذه بطريقة قياسية وستعمل بغض النظر عن عنوان URL.

ويعني هذا أنّه عليك التحقّق ممّا إذا كانت نقطة النهاية في خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" في خادمك، وإذا كانت كذلك، عليك استخراج تسجيل_id. للقيام بذلك في بايثون، يمكنك تنفيذ شيء مثل:

if endpoint.startswith('https://fcm.googleapis.com/fcm/send'):
    endpointParts = endpoint.split('/')
    registrationId = endpointParts[len(endpointParts) - 1]

    endpoint = 'https://fcm.googleapis.com/fcm/send'

بعد الحصول على رقم تعريف التسجيل، يمكنك إجراء مكالمة مع واجهة برمجة تطبيقات FCM API. يمكنك العثور على المستندات المرجعية حول واجهة برمجة تطبيقات FCM API هنا.

في ما يلي الاعتبارات الأساسية التي يجب مراعاتها عند الاتصال بخدمة "المراسلة عبر السحابة الإلكترونية من Firebase":

  • يجب إعداد عنوان تفويض بقيمة key=&lt;YOUR_API_KEY&gt; عند طلب واجهة برمجة التطبيقات، حيث يمثّل &lt;YOUR_API_KEY&gt; مفتاح واجهة برمجة التطبيقات من مشروع Firebase.
    • تستخدم خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" مفتاح واجهة برمجة التطبيقات للعثور على معرّف المُرسِل المناسب، والتأكد من أنّ المستخدم قد منح الإذن لمشروعك، وأخيرًا التأكّد من أنّ عنوان IP للخادم مُدرَج في القائمة المسموح بها لهذا المشروع.
  • يجب استخدام عنوان Content-Type مناسب لـ application/json أو application/x-www-form-urlencoded;charset=UTF-8 استنادًا إلى ما إذا كنت ترسل البيانات على هيئة JSON أو بيانات نموذج.
  • مصفوفة registration_ids: رقم تعريف التسجيل الذي تستخلصه من نقاط النهاية من المستخدمين.

يُرجى مراجعة المستندات حول كيفية إرسال الرسائل الفورية من الخادم، ولكن إذا أردت إجراء فحص سريع بشأن مشغّل الخدمات، يمكنك استخدام cURL لإرسال رسالة فورية إلى المتصفّح.

استبدِل &lt;YOUR_API_KEY&gt; و&lt;YOUR_REGISTRATION_ID&gt; في أمر cURL هذا برمزك الخاص وشغِّله من الوحدة الطرفية.

من المفترض أن ترى إشعارًا مميزًا:

    curl --header "Authorization: key=<YOUR_API_KEY>" --header
    "Content-Type: application/json" https://fcm.googleapis.com/fcm/send -d
    "{\"registration_ids\":[\"<YOUR_REGISTRATION_ID>\"]}"
مثال على رسالة فورية من Chrome لنظام Android

عند تطوير منطق الخلفية، لا تنسَ أنّ رأس التفويض وتنسيقه في نص POST هما خاصان بنقطة نهاية FCM، لذا عليك اكتشاف وقت استخدام نقطة النهاية للمراسلة عبر السحابة الإلكترونية من Firebase وإضافة الرأس وتنسيق نص POST بشكل مشروط. بالنسبة إلى المتصفحات الأخرى (ونأمل أن يكون Chrome في المستقبل)، ستحتاج إلى تنفيذ بروتوكول Web Push.

من الجوانب السلبية للتطبيق الحالي لواجهة برمجة تطبيقات Push في Chrome، وهو أنّه لا يمكنك إرسال أي بيانات من خلال رسالة فورية. كلا، لا شيء. وسبب ذلك هو أنه في أي عملية تنفيذ مستقبلية، سيتعين تشفير بيانات الحمولة على خادمك قبل إرسالها إلى نقطة نهاية رسائل الدفع. وبهذه الطريقة، لن تتمكن نقطة النهاية، مهما كانت خدمة الدفع، من عرض محتوى رسالة الدفع بسهولة. يوفر ذلك أيضًا الحماية من الثغرات الأمنية الأخرى مثل ضعف التحقق من شهادات HTTPS والهجمات الوسيطة بين خادمك وموفر خدمة إرسال البيانات. ومع ذلك، هذا التشفير غير متوافق بعد، لذا عليك تنفيذ عملية جلب للحصول على المعلومات المطلوبة لتعبئة الإشعار.

مثال على حدث فوري أكثر اكتمالاً

الإشعار الذي رأيناه حتى الآن بسيط إلى حد ما، وبقدر ما نستخدمه في العيّنات، لن يكون تغطية حالة استخدام حقيقية.

بشكل واقعي، سيرغب معظم الأشخاص في الحصول على بعض المعلومات من خادمهم قبل عرض الإشعار. وقد تكون هذه البيانات عبارة عن بيانات لملء عنوان الإشعار والرسالة بشيء محدد، أو الانتقال إلى خطوة أبعد والتخزين المؤقت لبعض الصفحات أو البيانات بحيث عندما ينقر المستخدم على الإشعار، يصبح كل شيء متاحًا على الفور عند فتح المتصفح، حتى إذا لم تكن الشبكة متاحة في ذلك الوقت.

في الرمز التالي، نجلب بعض البيانات من واجهة برمجة التطبيقات، ونحول الاستجابة إلى كائن ونستخدمها لتعبئة الإشعار.

self.addEventListener('push', function(event) {
    // Since there is no payload data with the first version
    // of push messages, we'll grab some data from
    // an API and use it to populate a notification
    event.waitUntil(
    fetch(SOME_API_ENDPOINT).then(function(response) {
        if (response.status !== 200) {
        // Either show a message to the user explaining the error
        // or enter a generic message and handle the
        // onnotificationclick event to direct the user to a web page
        console.log('Looks like there was a problem. Status Code: ' + response.status);
        throw new Error();
        }

        // Examine the text in the response
        return response.json().then(function(data) {
        if (data.error || !data.notification) {
            console.error('The API returned an error.', data.error);
            throw new Error();
        }

        var title = data.notification.title;
        var message = data.notification.message;
        var icon = data.notification.icon;
        var notificationTag = data.notification.tag;

        return self.registration.showNotification(title, {
            body: message,
            icon: icon,
            tag: notificationTag
        });
        });
    }).catch(function(err) {
        console.error('Unable to retrieve data', err);

        var title = 'An error occurred';
        var message = 'We were unable to get the information for this push message';
        var icon = URL_TO_DEFAULT_ICON;
        var notificationTag = 'notification-error';
        return self.registration.showNotification(title, {
            body: message,
            icon: icon,
            tag: notificationTag
        });
    })
    );
});

الجدير بالذكر مرة أخرى، الإشارة إلى أن event.waitUntil() تتعهد بالوعد الذي ينتج عنه الوعد الذي رجع إليه showNotification()، أي أن أداة معالجة الحدث لن تغادر إلى أن تكتمل مكالمة fetch() غير المتزامنة، ويتم عرض الإشعار.

ستلاحظ أننا نعرض إشعارًا حتى في حال حدوث خطأ. وذلك لأننا إذا لم نقم بذلك، فسيعرض Chrome إشعاره العام.

فتح عنوان URL عندما ينقر المستخدم على إشعار

عندما ينقر المستخدم على إشعار، يتم إرسال حدث notificationclick إلى مشغّل الخدمات. داخل المعالج، يمكنك اتخاذ الإجراء المناسب، مثل التركيز على علامة تبويب أو فتح نافذة بعنوان URL معين:

self.addEventListener('notificationclick', function(event) {
    console.log('On notification click: ', event.notification.tag);
    // Android doesn't close the notification when you click on it
    // See: http://crbug.com/463146
    event.notification.close();

    // This looks to see if the current is already open and
    // focuses if it is
    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) {
        return clients.openWindow('/');
        }
    })
    );
});

يفتح هذا المثال المتصفِّح على جذر الموقع الإلكتروني الأصلي من خلال التركيز على علامة تبويب حالية من المصدر نفسه في حال توفُّرها، وفتح علامة تبويب جديدة بأي شكل آخر.

تتوفّر مشاركة مخصّصة لبعض الإجراءات التي يمكنك تنفيذها باستخدام واجهة برمجة التطبيقات Notification API هنا.

إلغاء الاشتراك في جهاز مستخدم

لقد اشتركت في جهاز أحد المستخدمين ويتلقّى رسائل فورية، ولكن كيف يمكنك إلغاء الاشتراك؟

تتمثل الإجراءات الأساسية المطلوبة لإلغاء الاشتراك على أجهزة المستخدمين في استدعاء طريقة unsubscribe() في الكائن PushSubscription وإزالة نقطة النهاية من خوادمك (كي لا ترسل رسائل فورية تعلم أنه لن يتم استلامها). التعليمة البرمجية أدناه تفعل هذا بالضبط:

function unsubscribe() {
    var pushButton = document.querySelector('.js-push-button');
    pushButton.disabled = true;

    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    // To unsubscribe from push messaging, you need get the
    // subscription object, which you can call unsubscribe() on.
    serviceWorkerRegistration.pushManager.getSubscription().then(
        function(pushSubscription) {
        // Check we have a subscription to unsubscribe
        if (!pushSubscription) {
            // No subscription object, so set the state
            // to allow the user to subscribe to push
            isPushEnabled = false;
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
            return;
        }

        var subscriptionId = pushSubscription.subscriptionId;
        // TODO: Make a request to your server to remove
        // the subscriptionId from your data store so you
        // don't attempt to send them push messages anymore

        // We have a subscription, so call unsubscribe on it
        pushSubscription.unsubscribe().then(function(successful) {
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
            isPushEnabled = false;
        }).catch(function(e) {
            // We failed to unsubscribe, this can lead to
            // an unusual state, so may be best to remove
            // the users data from your data store and
            // inform the user that you have done so

            console.log('Unsubscription error: ', e);
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
        });
        }).catch(function(e) {
        console.error('Error thrown while unsubscribing from push messaging.', e);
        });
    });
}

إبقاء الاشتراك محدّثًا

قد لا تتم مزامنة الاشتراكات بين خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" وخادمك. تأكَّد من أنّ الخادم يحلّل نص الاستجابة لعبارة POST الخاصة بالإرسال عبر واجهة FCM API، ويبحث عن النتيجتين error:NotRegistered وcanonical_id، كما هو موضّح في مستندات FCM.

وقد لا تتوافق الاشتراكات أيضًا بين مشغّل الخدمة والخادم. على سبيل المثال، بعد نجاح الاشتراك أو إلغاء الاشتراك، قد يمنعك اتصال الشبكة غير المستقر من تحديث الخادم، أو قد يلغي المستخدم إذن الإشعارات، ما يؤدي إلى إلغاء الاشتراك تلقائيًا. ويمكنك التعامل مع هذه الحالات عن طريق التحقّق من نتيجة serviceWorkerRegistration.pushManager.getSubscription() بشكل دوري (على سبيل المثال، عند تحميل الصفحة) ومزامنتها مع الخادم. ننصحك أيضًا بالاشتراك تلقائيًا مرة أخرى إذا لم يعد لديك اشتراك، وإذا لم يعُد لديك اشتراك، يكون نوع الأذونات Notification.permission == 'granted'.

في sendSubscriptionToServer()، عليك مراعاة كيفية تعاملك مع طلبات الشبكة التي تعذّر تنفيذها عند تحديث endpoint. أحد الحلول هو تتبُّع حالة endpoint في ملف تعريف ارتباط لتحديد ما إذا كان الخادم يحتاج إلى أحدث التفاصيل أم لا.

تؤدي جميع الخطوات المذكورة أعلاه إلى تنفيذ كامل لرسائل الدفع على الويب في Chrome 46. لا تزال هناك ميزات مواصفة ستسهّل الأمور (مثل واجهة برمجة تطبيقات قياسية لتفعيل الرسائل الفورية)، ولكن هذا الإصدار يتيح لك بدء إنشاء رسائل فورية في تطبيقات الويب الخاصة بك اليوم.

كيفية تصحيح الأخطاء في تطبيق الويب

أثناء تنفيذ الرسائل الفورية، ستَظهر الأخطاء في مكان من مكانَين: صفحتك أو مشغّل الخدمات.

يمكن تصحيح الأخطاء في الصفحة باستخدام DevTools. لتصحيح أخطاء مشغّل الخدمة، لديك خياران:

  1. انتقِل إلى chrome://inspect > عمال الخدمة. لا يوفر هذا العرض الكثير من المعلومات غير عاملي الخدمة العاملين حاليًا.
  2. انتقِل إلى chrome://serviceworker-internals، ومن هنا يمكنك الاطّلاع على حالة عاملي الخدمة والاطّلاع على الأخطاء، إن وُجدت. تُعد هذه الصفحة مؤقتة إلى أن تحتوي "أدوات مطوري البرامج" على مجموعة ميزات مماثلة.

ومن أفضل النصائح التي يمكنني تقديمها لأي شخص جديد في مجال تشغيل الخدمات هي الاستفادة من مربّع الاختيار "فتح نافذة أدوات مطوري البرامج وإيقاف تنفيذ JavaScript مؤقتًا عند بدء تشغيل عامل الخدمة لتصحيح الأخطاء". سيضيف مربّع الاختيار هذا نقطة توقّف في بداية مشغّل الخدمات وإيقاف التنفيذ مؤقتًا، ما يتيح لك استئناف النص البرمجي الخاص بمشغِّل الخدمات أو الانتقال إليه ومعرفة ما إذا واجهت أي مشاكل.

لقطة شاشة تعرض مكان وضع علامة في مربّع اختيار تنفيذ الإيقاف المؤقت على Serviceworker-internals

إذا بدا أن هناك مشكلة بين خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" وحدث إرسال البيانات لمشغّل الخدمات، فهذا يعني أنّه لا يمكنك اتخاذ أي إجراء لتصحيح المشكلة، لأنّه لا تتوفّر طريقة لمعرفة ما إذا كان Chrome قد تلقّى أي رسائل. الأمر الأساسي الذي يجب التأكّد منه هو أنّ استجابة "المراسلة عبر السحابة الإلكترونية من Firebase" ناجحة عندما يجري الخادم طلب بيانات من واجهة برمجة التطبيقات. سيبدو شيئًا مثل:

{"multicast_id":1234567890,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1234567890"}]}

لاحِظ الردّ "success": 1. إذا لاحظت خطأ بدلاً من ذلك، يعني ذلك أنّ هناك خطأ في معرّف التسجيل في خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" وأنّه لا يتم إرسال رسالة الدفع إلى Chrome.

تصحيح أخطاء مشغِّلي الخدمات على Chrome لنظام Android

في الوقت الحالي، ليس من الواضح أنّ تصحيح أخطاء موظفي الخدمة على Chrome لنظام Android. عليك الانتقال إلى chrome://inspect، والبحث عن جهازك، والبحث عن عنصر قائمة يحمل الاسم Worker pid:...." مع عنوان URL لمشغّل الخدمات.

لقطة شاشة تعرض مكان عمل عاملي الخدمة في عمليات فحص Chrome

تجربة المستخدم للإشعارات الفورية

لقد عمل فريق Chrome معًا على إعداد مستند لأفضل الممارسات للإشعارات الفورية لتجربة المستخدم بالإضافة إلى وثيقة يتناول بعض الحالات الهامشية عند العمل مع الإشعارات الفورية.

مستقبل ميزة "المراسلة الفورية" على Chrome والويب المفتوح

يتناول هذا القسم بعض التفاصيل المتعلقة ببعض الأجزاء المحددة في متصفّح Chrome والتي يجب أن تكون على دراية بها حول عملية التنفيذ هذه وكيف ستختلف عن عمليات التنفيذ الأخرى في المتصفِّح.

بروتوكول Web Push ونقاط النهاية

يكمن جمال معيار Push API في أنّه من المفترض أن تكون قادرًا على الوصول إلى نقطة النهاية وتمريرها إلى خادمك وإرسال رسائل فورية من خلال تطبيق بروتوكول Web Push.

إنّ بروتوكول Web Push Protocol هو معيار جديد يمكن لموفّري خدمة الإرسال تنفيذه، ما يتيح للمطوّرين عدم القلق بشأن هوية موفِّر خدمة الإرسال. الهدف من ذلك هو تجنُّب الحاجة إلى الاشتراك للحصول على مفاتيح واجهة برمجة التطبيقات وإرسال بيانات منسّقة بشكل خاص، كما هو الحال مع خدمة "المراسلة عبر السحابة الإلكترونية من Firebase".

كان Chrome أول متصفِّح ينفِّذ واجهة برمجة التطبيقات Push، ولا تتوافق خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" مع بروتوكول Web Push، ولهذا السبب يتطلّب متصفِّح Chrome gcm_sender_id وتحتاج إلى استخدام واجهة برمجة التطبيقات المريحة لخدمة "المراسلة عبر السحابة الإلكترونية من Firebase".

يتمثّل هدف Chrome في استخدام بروتوكول Web Push مع متصفّح Chrome و"المراسلة عبر السحابة الإلكترونية من Firebase".

وحتى ذلك الحين، تحتاج إلى اكتشاف نقطة النهاية "https://fcm.googleapis.com/fcm/send" والتعامل معها بشكل منفصل عن نقاط النهاية الأخرى، بمعنى تنسيق بيانات الحمولة بطريقة محددة وإضافة مفتاح التفويض.

كيف يمكن تطبيق بروتوكول Web Push؟

يعمل Firefox Nightly حاليًا على إرسال البيانات، وسيكون على الأرجح المتصفّح الأول الذي يطبّق بروتوكول Web Push.

الأسئلة الشائعة

أين المواصفات؟

https://slightlyoff.github.io/ServiceWorker/spec/service_worker/ https://w3c.github.io/push-api/ https://notifications.spec.whatwg.org/

هل يمكنني منع تلقّي إشعارات مكرَّرة إذا كان للحضور على الويب مصادر متعددة أو إذا كان لديّ حضور على الويب وفي التطبيقات الأصلية؟

لا يتوفّر حل لهذه المشكلة في الوقت الحالي، ولكن يمكنك متابعة مستوى التقدّم على Chromium.

السيناريو المثالي هو توفّر نوع من المعرّف لجهاز المستخدم ثم إجراء مطابقة من جانب الخادم لرقم تعريف الاشتراك في التطبيق الأصلي وتطبيق الويب وتحديد الرقم المطلوب إرسال رسالة فورية إليه. يمكنك القيام بذلك عن طريق حجم الشاشة، وطراز الجهاز، ومشاركة مفتاح تم إنشاؤه بين تطبيق الويب والتطبيق الأصلي، ولكن لكل نهج إيجابيات وسلبيات.

لماذا أحتاج إلى gcm_sender_id؟

وهذا الإجراء مطلوب حتى يتسنى لكل من Chrome وOpera لنظام التشغيل Android ومتصفّح Samsung استخدام واجهة برمجة تطبيقات المراسلة عبر السحابة الإلكترونية من Firebase (FCM). والهدف من ذلك هو استخدام بروتوكول Web Push عند الانتهاء من إعداد المعيار وإتاحة استخدامه من قِبل خدمة "المراسلة عبر السحابة الإلكترونية من Firebase".

لمَ لا يتم استخدام Web Sockets أو Server-Sent Events (الأحداث المُرسَلة من الخادم) (EventSource)؟

وتكمن ميزة استخدام الرسائل الفورية في أنه حتى إذا تم إغلاق الصفحة، سيتم تنشيط عامل الخدمة وسيتمكّن من عرض إشعار. يتم إغلاق اتصال كل من Web Sockets وEventSource عند إغلاق الصفحة أو المتصفح.

ماذا لو لم أكن بحاجة إلى عرض الأحداث في الخلفية؟

في حال لم تكن بحاجة إلى التسليم في الخلفية، تُعدّ Web Sockets خيارًا رائعًا.

متى يمكنني استخدام الإشعارات الفورية بدون عرض إشعارات (أي الدفع التلقائي في الخلفية)؟

لا يوجد مخطّط زمني لموعد توفُّر هذه الميزة حتى الآن، ولكن هناك نية لتنفيذ المزامنة في الخلفية، وعلى الرغم من أنّه لم يتم تحديد ذلك أو تحديد ذلك، هناك بعض المناقشات حول تفعيل ميزة "إرسال بدون صوت" مع مزامنة الخلفية.

لماذا يتطلّب ذلك استخدام HTTPS؟ كيف يمكنني تفادي ذلك أثناء التطوير؟

يحتاج مشغّلو الخدمات إلى مصادر آمنة لضمان أنّ النص البرمجي لمشغّل الخدمات يأتي من المصدر المقصود وألّا يأتي من هجوم الوسيط. ويعني هذا في الوقت الحالي استخدام HTTPS على المواقع المباشرة، إلا أنّ المضيف المحلي سيعمل أثناء عملية التطوير.

كيف يبدو دعم المتصفّح؟

يتوافق متصفّح Chrome مع إصداره الثابت، كما تعمل Mozilla على الضغط باستمرار على المتصفِّح في Firefox Nightly. يمكنك الاطّلاع على تنفيذ خطأ Push API للحصول على مزيد من المعلومات وتتبُّع تنفيذ الإشعارات هنا.

هل يمكنني إزالة إشعار بعد فترة زمنية معيّنة؟

يتعذّر تنفيذ ذلك في الوقت الحالي، ولكنّنا ننوي إتاحة ميزة الدعم للحصول على قائمة بالإشعارات المرئية حاليًا. إذا كانت لديك حالة استخدام لضبط انتهاء الصلاحية للإشعار بعد أن يتم إنشائه، نودّ معرفة ذلك، لذا يُرجى إضافة تعليق وسنُعيد إرساله إلى فريق Chrome.

إذا كنت تريد إيقاف إرسال إشعار فوري إلى المستخدم فقط بعد فترة زمنية معيّنة ولا تهتم بالمدة التي يظل فيها الإشعار مرئيًا، يمكنك استخدام مَعلمة "وقت بقاء الإشعار" في خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" (ttl)، تعرَّف على مزيد من المعلومات هنا.

ما هي قيود إرسال الرسائل الفورية في Chrome؟

هناك بعض القيود الموضحة في هذه المشاركة:

  • يؤدي استخدام Chrome لـ "إدارة الشؤون التجارية والعقود" كخدمة إرسال الملفات إلى إنشاء عدد من المتطلبات الخاصة. نحن نعمل معًا لمعرفة ما إذا كان من الممكن إزالة بعضها في المستقبل.
  • عليك إظهار إشعار عندما تتلقّى رسالة فورية.
  • يحذر Chrome على سطح المكتب من أنه في حال عدم تشغيل Chrome، لن يتم استلام رسائل الدفع. يختلف ذلك عن نظام التشغيل ChromeOS وAndroid حيث سيتم دائمًا تلقّي رسائل الدفع.

ألا يجب استخدام واجهة Permissions API؟

تمّ تنفيذ Permission API في Chrome، لكنّها لن تكون بالضرورة متاحة في جميع المتصفّحات. مزيد من المعلومات

لماذا لا يفتح Chrome علامة التبويب السابقة عند النقر على إشعار؟

لا تؤثر هذه المشكلة إلا في الصفحات التي لا يتحكّم فيها حاليًا مشغّل الخدمات. يمكنك الاطّلاع على المزيد من المعلومات هنا.

ماذا لو كان الإشعار قديمًا بحلول الوقت الذي تلقّى فيه جهاز المستخدم الدفعة؟

عليك دائمًا إظهار إشعار عندما تتلقّى رسالة فورية. في السيناريو الذي تريد فيه إرسال إشعار ولكنه يكون مفيدًا لفترة زمنية معيّنة فقط، يمكنك استخدام المعلمة "time_to_live" على "إدارة الشؤون التجارية والعقود" حتى لا ترسل خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" الرسالة الفورية في حال مرور وقت انتهاء الصلاحية.

يمكنك الاطّلاع على مزيد من التفاصيل هنا.

ماذا يحدث إذا أرسلت 10 رسائل فورية ولكن أريد أن يتلقّى الجهاز رسالة واحدة فقط؟

تتضمّن خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" مَعلمة "تصغير_مفتاح" يمكنك استخدامها لتطلب من خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" استبدال أي رسالة معلّقة تحتوي على "مفتاح_التصغير" نفسه بالرسالة الجديدة.

يمكنك الاطّلاع على مزيد من التفاصيل هنا.