حضور در Cloud Firestore

بسته به نوع اپلیکیشنی که می‌سازید، ممکن است تشخیص اینکه کدام یک از کاربران یا دستگاه‌های شما به طور فعال آنلاین هستند - که به عنوان تشخیص «حضور» نیز شناخته می‌شود - مفید باشد.

برای مثال، اگر در حال ساخت اپلیکیشنی مانند یک شبکه اجتماعی هستید یا در حال استقرار ناوگانی از دستگاه‌های اینترنت اشیا هستید، می‌توانید از این اطلاعات برای نمایش فهرستی از دوستانی که آنلاین هستند و می‌توانند آزادانه چت کنند، یا مرتب‌سازی دستگاه‌های اینترنت اشیا خود بر اساس «آخرین بازدید» استفاده کنید.

Cloud Firestore به طور بومی از حضور پشتیبانی نمی‌کند، اما می‌توانید از سایر محصولات Firebase برای ساخت یک سیستم حضور استفاده کنید.

راه حل: توابع ابری با پایگاه داده بلادرنگ

برای اتصال Cloud Firestore به ویژگی حضور بومی Firebase Realtime Database، از Cloud Functions استفاده کنید.

از پایگاه داده Realtime برای گزارش وضعیت اتصال استفاده کنید، سپس از توابع Cloud برای آینه کردن آن داده‌ها در Cloud Firestore استفاده کنید.

استفاده از حضور در پایگاه داده Realtime

ابتدا، نحوه عملکرد یک سیستم حضور و غیاب سنتی در پایگاه داده Realtime را در نظر بگیرید.

وب

// 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 از همان کد پایگاه داده Realtime استفاده کنید، سپس از توابع Cloud برای همگام‌سازی پایگاه داده Realtime و Cloud Firestore استفاده کنید.

اگر هنوز این کار را نکرده‌اید، Realtime Database را به پروژه خود اضافه کنید و راهکار presence فوق را نیز در آن بگنجانید.

در مرحله بعد، وضعیت حضور را از طریق روش‌های زیر با Cloud Firestore همگام‌سازی خواهید کرد:

  1. به صورت محلی، به حافظه پنهان Cloud Firestore دستگاه آفلاین تا برنامه بداند که آفلاین است.
  2. در سطح جهانی، با استفاده از یک تابع ابری، به طوری که تمام دستگاه‌های دیگر که به 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 استفاده خواهیم کرد که مسیر status/{uid} را در Realtime Database مشاهده می‌کند. هنگامی که مقدار Realtime Database تغییر می‌کند، مقدار با Cloud Firestore همگام‌سازی می‌شود تا وضعیت همه کاربران صحیح باشد.

نود جی اس

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);
                // ...
            }
        });
    });

محدودیت‌ها

استفاده از پایگاه داده Realtime برای افزودن حضور به برنامه Cloud Firestore شما مقیاس‌پذیر و مؤثر است اما محدودیت‌هایی دارد:

  • رفع انسداد - هنگام گوش دادن به تغییرات در لحظه در Cloud Firestore ، این راه حل احتمالاً باعث ایجاد چندین تغییر می‌شود. اگر این تغییرات باعث ایجاد رویدادهای بیشتری نسبت به آنچه شما می‌خواهید می‌شوند، رویدادهای Cloud Firestore را به صورت دستی رفع انسداد کنید.
  • اتصال - این پیاده‌سازی، اتصال به پایگاه داده‌ی Realtime را اندازه‌گیری می‌کند، نه به Cloud Firestore . اگر وضعیت اتصال به هر پایگاه داده یکسان نباشد، این راه‌حل ممکن است وضعیت حضور نادرستی را گزارش دهد.
  • اندروید - در اندروید، پایگاه داده بلادرنگ پس از 60 ثانیه عدم فعالیت، از بک‌اند جدا می‌شود. عدم فعالیت به معنای عدم وجود شنونده‌های باز یا عملیات در حال انتظار است. برای باز نگه داشتن اتصال، توصیه می‌کنیم یک شنونده رویداد value به مسیری علاوه بر .info/connected اضافه کنید. به عنوان مثال، می‌توانید FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() در ابتدای هر جلسه انجام دهید. برای اطلاعات بیشتر، به بخش تشخیص وضعیت اتصال مراجعه کنید.