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

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

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

اعتبارًا من الإصدار 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 متاح في النموذج الأوّلي ServiceWorkerRegistration. بدونه لن نتمكن من عرض إشعار من عامل الخدمة لدينا عند استلام رسالة فورية.
  • نتحقّق من قيمة Notification.permission الحالية للتأكّد من أنّها ليست "denied". يعني رفض الإذن أنّه لا يمكنك عرض الإشعارات حتى يغيّر المستخدم الإذن يدويًا في المتصفح.
  • للتحقّق من توفُّر ميزة المراسلة الفورية، نتحقق من توفُّر PushManager في كائن النافذة.
  • وأخيرًا، استخدمنا pushManager.getSubscription() للتحقق مما إذا كان لدينا اشتراك حاليًا أم لا. وفي هذه الحالة، نرسل تفاصيل الاشتراك إلى خادمنا للتأكّد من حصولنا على المعلومات الصحيحة، ونضبط واجهة المستخدم على الإشارة إلى أنّ خدمة المراسلة الفورية مفعَّلة من قبل أم لا. سنلقي نظرة على التفاصيل الموجودة في كائن الاشتراك لاحقًا في هذه المقالة.

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

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

إنشاء مشروع على وحدة تحكم مطوّري البرامج في Firebase

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

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

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

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

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

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

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

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

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

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

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

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

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

بالنسبة إلى الإشعارات الفورية، يجب إضافة ملف بيان يحتوي على الحقل 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".

الاشتراك في ميزة "المراسلة الفورية"

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

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

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

إذا تم حلّ التعهد الذي عرضه أسلوب subscription()، سيتم منحك عنصر 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" وإذا كانت كذلك، استخرِج معرّف التسجيل. للقيام بذلك في بايثون، يمكنك فعل شيء مثل:

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. يمكنك العثور على المستندات المرجعية حول واجهة برمجة تطبيقات FCM هنا.

في ما يلي الجوانب الرئيسية التي يجب تذكرها عند الاتصال بالمراسلة عبر السحابة الإلكترونية من 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، لذا اكتشف متى تكون نقطة النهاية لـ FCM وأضف العنوان ونسِّق نص POST بشكل مشروط. بالنسبة إلى المتصفحات الأخرى (ونأمل في Chrome في المستقبل)، عليك تنفيذ بروتوكول Web Push.

الجانب السلبي للتطبيق الحالي لواجهة برمجة التطبيقات Push API في 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، كما هو موضّح في مستندات المراسلة عبر السحابة الإلكترونية من Firebase.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

بروتوكول الدفع على الويب ونقاط النهاية

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

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

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

الهدف النهائي من Chrome هو المضي قدمًا في استخدام بروتوكول Web Push مع Chrome وFCM.

وحتى ذلك الحين، يجب رصد نقطة النهاية "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" دعمه.

لماذا لا تستخدم مآخذ الويب أو الأحداث المرسلة من الخادم (EventSource)؟

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

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

إذا كنت لا تحتاج إلى التسليم في الخلفية، فإن Web Socket هي خيار رائع.

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

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

لماذا يتطلب ذلك استخدام HTTPS؟ كيف يمكنني التغلب على هذه المشكلة أثناء التطوير؟

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

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

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

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

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

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

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

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

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

ألا علينا استخدام Permissions API؟

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

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

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

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

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

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

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

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

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