تعزيز حضورك في Cloud Firestore

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

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

Cloud Firestore لا يتيح رصد حالة الاتصال بشكل تلقائي، ولكن يمكنك الاستفادة من منتجات Firebase الأخرى لإنشاء نظام لرصد حالة الاتصال.

الحل: Cloud Functions مع قاعدة بيانات الوقت الفعلي

لربط Cloud Firestore بميزة رصد حالة الاتصال التلقائية في Firebase Realtime Database ، استخدِم Cloud Functions.

استخدِم قاعدة بيانات الوقت الفعلي للإبلاغ عن حالة الاتصال، ثم استخدِم الوظائف السحابية لـ عكس هذه البيانات في Cloud Firestore.

استخدام ميزة رصد حالة الاتصال في قاعدة بيانات الوقت الفعلي

أولاً، عليك فهم طريقة عمل نظام رصد حالة الاتصال التقليدي في قاعدة بيانات الوقت الفعلي.

الويب

// Fetch the current user's ID from Firebase Authentication.
var uid = firebase.auth().currentUser.uid;

// Create a reference to this user's specific status node.
// This is where we will store data about being online/offline.
var userStatusDatabaseRef = firebase.database().ref('/status/' + uid);

// We'll create two constants which we will write to 
// the Realtime database when this device is offline
// or online.
var isOfflineForDatabase = {
    state: 'offline',
    last_changed: firebase.database.ServerValue.TIMESTAMP,
};

var isOnlineForDatabase = {
    state: 'online',
    last_changed: firebase.database.ServerValue.TIMESTAMP,
};

// Create a reference to the special '.info/connected' path in 
// Realtime Database. This path returns `true` when connected
// and `false` when disconnected.
firebase.database().ref('.info/connected').on('value', function(snapshot) {
    // If we're not currently connected, don't do anything.
    if (snapshot.val() == false) {
        return;
    };

    // If we are currently connected, then use the 'onDisconnect()' 
    // method to add a set which will only trigger once this 
    // client has disconnected by closing the app, 
    // losing internet, or any other means.
    userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() {
        // The promise returned from .onDisconnect().set() will
        // resolve as soon as the server acknowledges the onDisconnect() 
        // request, NOT once we've actually disconnected:
        // https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect

        // We can now safely set ourselves as 'online' knowing that the
        // server will mark us as offline once we lose connection.
        userStatusDatabaseRef.set(isOnlineForDatabase);
    });
});

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

الاتصال بـ Cloud Firestore

لتنفيذ حل مشابه في Cloud Firestore، استخدِم رمز قاعدة بيانات الوقت الفعلي نفسه، ثم استخدِم الوظائف السحابية للحفاظ على مزامنة قاعدة بيانات الوقت الفعلي وCloud Firestore.

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

بعد ذلك، ستتم مزامنة حالة الاتصال مع Cloud Firestore من خلال الطرق التالية:

  1. محليًا، في ذاكرة التخزين المؤقت Cloud Firestore للجهاز غير المتصل بالإنترنت لكي يعرف التطبيق أنّه غير متصل بالإنترنت
  2. على مستوى العالم، باستخدام Cloud Function لكي تعرف جميع الأجهزة الأخرى التي تصل إلى Cloud Firestore أنّ هذا الجهاز غير متصل بالإنترنت

لا يمكن تشغيل الدوال المقترَحة في هذا البرنامج التعليمي في تطبيق عميل. يجب نشرها في Cloud Functions for Firebase، وتتطلّب منطقًا من جهة الخادم من حزمة Firebase Admin SDK. للحصول على إرشادات مفصّلة، اطّلِع على مستندات Cloud Functions.

تعديل ذاكرة التخزين المؤقت المحلية لـ Cloud Firestore

لنلقِ نظرة على التغييرات المطلوبة لحل المشكلة الأولى، وهي تعديل Cloud Firestore ذاكرة التخزين المؤقت المحلية.

الويب

// ...
var userStatusFirestoreRef = firebase.firestore().doc('/status/' + uid);

// Firestore uses a different server timestamp value, so we'll 
// create two more constants for Firestore state.
var isOfflineForFirestore = {
    state: 'offline',
    last_changed: firebase.firestore.FieldValue.serverTimestamp(),
};

var isOnlineForFirestore = {
    state: 'online',
    last_changed: firebase.firestore.FieldValue.serverTimestamp(),
};

firebase.database().ref('.info/connected').on('value', function(snapshot) {
    if (snapshot.val() == false) {
        // Instead of simply returning, we'll also set Firestore's state
        // to 'offline'. This ensures that our Firestore cache is aware
        // of the switch to 'offline.'
        userStatusFirestoreRef.set(isOfflineForFirestore);
        return;
    };

    userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() {
        userStatusDatabaseRef.set(isOnlineForDatabase);

        // We'll also add Firestore set here for when we come online.
        userStatusFirestoreRef.set(isOnlineForFirestore);
    });
});

من خلال هذه التغييرات، نضمن الآن أنّ حالة المحلية Cloud Firestore ستعرض دائمًا حالة اتصال الجهاز بالإنترنت أو عدم اتصاله. ما يعني أنّه يمكنك الاستماع إلى المستند /status/{uid} واستخدام البيانات لتغيير واجهة المستخدم لعرض حالة الاتصال.

الويب

userStatusFirestoreRef.onSnapshot(function(doc) {
    var isOnline = doc.data().state == 'online';
    // ... use isOnline
});

تعديل Cloud Firestore على مستوى العالم

على الرغم من أنّ تطبيقنا يبلّغ عن حالة الاتصال بالإنترنت بشكل صحيح، لن تكون هذه الحالة دقيقة في تطبيقات Cloud Firestore الأخرى بعد لأنّ عملية كتابة حالة "غير متصل بالإنترنت" محلية فقط ولن تتم مزامنتها عند استعادة الاتصال. لمعالجة هذه المشكلة، سنستخدم Cloud Function تراقب المسار status/{uid} في Realtime Database. عندما تتغيّر قيمة قاعدة بيانات الوقت الفعلي، ستتم مزامنة القيمة مع Cloud Firestore لكي تكون حالات جميع المستخدمين صحيحة.

Node.js

firebase.firestore().collection('status')
    .where('state', '==', 'online')
    .onSnapshot(function(snapshot) {
        snapshot.docChanges().forEach(function(change) {
            if (change.type === 'added') {
                var msg = 'User ' + change.doc.id + ' is online.';
                console.log(msg);
                // ...
            }
            if (change.type === 'removed') {
                var msg = 'User ' + change.doc.id + ' is offline.';
                console.log(msg);
                // ...
            }
        });
    });

بعد نشر هذه الدالة، سيكون لديك نظام كامل لرصد حالة الاتصال يعمل مع Cloud Firestore. في ما يلي مثال على رصد أي مستخدمين يتصلون بالإنترنت أو ينقطعون عنه باستخدام طلب بحث where().

الويب

firebase.firestore().collection('status')
    .where('state', '==', 'online')
    .onSnapshot(function(snapshot) {
        snapshot.docChanges().forEach(function(change) {
            if (change.type === 'added') {
                var msg = 'User ' + change.doc.id + ' is online.';
                console.log(msg);
                // ...
            }
            if (change.type === 'removed') {
                var msg = 'User ' + change.doc.id + ' is offline.';
                console.log(msg);
                // ...
            }
        });
    });

القيود

إنّ استخدام قاعدة بيانات الوقت الفعلي لإضافة ميزة رصد حالة الاتصال إلى تطبيقك Cloud Firestore قابل للتوسّع وفعّال، ولكنّه يتضمّن بعض القيود:

  • إزالة الارتداد : عند الاستماع إلى التغييرات في الوقت الفعلي في Cloud Firestore، من المرجّح أن يؤدي هذا الحل إلى إطلاق تغييرات متعددة. إذا كانت هذه التغييرات تؤدي إلى إطلاق أحداث أكثر مما تريد، عليك إزالة الارتداد يدويًا من أحداث Cloud Firestore.
  • الاتصال - يقيس هذا التنفيذ الاتصال بـ Realtime Database، وليس بـ Cloud Firestore. إذا لم تكن حالة الاتصال بكل قاعدة بيانات هي نفسها، قد يعرض هذا الحل حالة اتصال غير صحيحة.
  • Android : على أجهزة Android، يتم قطع اتصال قاعدة بيانات الوقت الفعلي بالخادم بعد 60 ثانية من عدم النشاط. يعني عدم النشاط عدم وجود مستمعين مفتوحين أو عمليات معلّقة. لإبقاء الاتصال مفتوحًا، ننصحك بإضافة متتبِّع لأحداث القيم إلى مسار آخر غير .info/connected. على سبيل المثال، يمكنك تنفيذ FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() في بداية كل جلسة. لمزيد من المعلومات، اطّلِع على مقالة رصد حالة الاتصال.