Cloud Firestore-এ উপস্থিতি তৈরি করা

আপনি যে ধরনের অ্যাপ তৈরি করছেন তার উপর নির্ভর করে, আপনার কোন ব্যবহারকারী বা ডিভাইস অনলাইনে অ্যাক্টিভ আছে তা শনাক্ত করা আপনার জন্য উপযোগী হতে পারে — অন্যথায় "উপস্থিতি" শনাক্ত করা হিসাবে পরিচিত।

যেমন, আপনি যদি সোশ্যাল নেটওয়ার্কের মতো কোনও অ্যাপ তৈরি করেন অথবা IoT ডিভাইসের ফ্লিট ডেপ্লয় করেন, তাহলে এই তথ্য ব্যবহার করে অনলাইনে থাকা এবং চ্যাট করার জন্য ফ্রি বন্ধুদের তালিকা দেখাতে পারবেন অথবা আপনার IoT ডিভাইস "শেষবার দেখা" অনুযায়ী সাজাতে পারবেন।

Cloud Firestore-এ নেটিভভাবে প্রেজেন্সের সুবিধা নেই, তবে আপনি অন্যান্য Firebase প্রোডাক্ট ব্যবহার করে প্রেজেন্স সিস্টেম তৈরি করতে পারবেন।

সমাধান: Realtime Database-এর সাথে Cloud Functions

Firebase রিয়েলটাইম ডেটাবেসের নেটিভ উপস্থিতি ফিচারের সাথে Cloud Firestore কানেক্ট করতে, Cloud Functions ব্যবহার করুন।

কানেকশন স্ট্যাটাস রিপোর্ট করতে Realtime Database ব্যবহার করুন, তারপর সেই ডেটা Cloud Firestore-এ মিরর করতে Cloud Functions ব্যবহার করুন।

Realtime Database-এ উপস্থিতি ব্যবহার করা

প্রথমে, Realtime Database-এ একটি ঐতিহ্যগত উপস্থিতি সিস্টেম কীভাবে কাজ করে তা বিবেচনা করুন।

ওয়েব

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

এই উদাহরণটি একটি সম্পূর্ণ Realtime Database উপস্থিতি সিস্টেম। এটি একাধিক ডিসকানেকশন, ক্র্যাশ এবং আরও অনেক কিছু ম্যানেজ করে।

Cloud Firestore-এর সাথে কানেক্ট করা হচ্ছে

Cloud Firestore-এ একই ধরনের সমাধান প্রয়োগ করতে, একই Realtime Database কোড ব্যবহার করুন, তারপর Realtime Database ও Cloud Firestore সিঙ্ক করে রাখতে Cloud Functions ব্যবহার করুন।

আপনি যদি এখনও না করে থাকেন, তাহলে আপনার প্রোজেক্টে রিয়েল-টাইম ডেটাবেস যোগ করুন এবং উপরে উল্লেখ করা প্রেজেন্স সলিউশন অন্তর্ভুক্ত করুন।

এরপর আপনি নিম্নলিখিত পদ্ধতির মাধ্যমে Cloud Firestore-এ উপস্থিতির স্ট্যাটাস সিঙ্ক করবেন:

  1. স্থানীয়ভাবে, অফলাইন ডিভাইসের Cloud Firestore ক্যাশেতে যাতে অ্যাপটি জানতে পারে যে এটি অফলাইন।
  2. বিশ্বব্যাপী, একটি ক্লাউড ফাংশন ব্যবহার করে যাতে Cloud Firestore অ্যাক্সেস করা অন্য সব ডিভাইস জানতে পারে যে এই নির্দিষ্ট ডিভাইসটি অফলাইন আছে।

এই টিউটোরিয়ালে সাজেস্ট করা ফাংশন কোনও ক্লায়েন্ট অ্যাপে রান করতে পারবে না। সেগুলিকে অবশ্যই Cloud Functions for Firebase-এ ডেপ্লয় করতে হবে এবং সেগুলির জন্য Firebase অ্যাডমিন 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} ডকুমেন্ট শুনতে পারবেন এবং কানেকশন স্ট্যাটাস দেখানোর জন্য UI পরিবর্তন করতে ডেটা ব্যবহার করতে পারবেন।

ওয়েব

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

বিশ্বব্যাপী Cloud Firestore আপডেট করা হচ্ছে

যদিও আমাদের অ্যাপ্লিকেশনটি নিজের কাছে অনলাইন উপস্থিতি সঠিকভাবে রিপোর্ট করে, এই স্ট্যাটাসটি এখনও অন্যান্য Cloud Firestore অ্যাপে সঠিক হবে না কারণ আমাদের "অফলাইন" স্ট্যাটাস রাইট শুধুমাত্র স্থানীয় এবং কানেকশন রিস্টোর করা হলে সিঙ্ক হবে না। এর মোকাবিলা করতে, আমরা একটি Cloud Function ব্যবহার করব যা Realtime Database-এ status/{uid} পাথ লক্ষ্য করে। রিয়েল-টাইম ডেটাবেসের ভ্যালু পরিবর্তন হলে, ভ্যালুটি Cloud Firestore-এর সাথে সিঙ্ক হবে যাতে সব ব্যবহারকারীর স্ট্যাটাস সঠিক থাকে।

Node.js

const functions = require('firebase-functions/v1');
const { initializeApp } = require('firebase-admin/app');
const { getFirestore } = require('firebase-admin/firestore');
initializeApp();

// Since this code will be running in the Cloud Functions environment
// we call initialize Firestore without any arguments because it
// detects authentication from the environment.
const firestore = getFirestore();

// Create a new function which is triggered on changes to /status/{uid}
// Note: This is a Realtime Database trigger, *not* Firestore.
exports.onUserStatusChanged = functions.database.ref('/status/{uid}').onUpdate(
    async (change, context) => {
      // Get the data written to Realtime Database
      const eventStatus = change.after.val();

      // Then use other event data to create a reference to the
      // corresponding Firestore document.
      const userStatusFirestoreRef = firestore.doc(`status/${context.params.uid}`);

      // It is likely that the Realtime Database change that triggered
      // this event has already been overwritten by a fast change in
      // online / offline status, so we'll re-read the current data
      // and compare the timestamps.
      const statusSnapshot = await change.after.ref.once('value');
      const status = statusSnapshot.val();
      functions.logger.log(status, eventStatus);
      // If the current timestamp for this data is newer than
      // the data that triggered this event, we exit this function.
      if (status.last_changed > eventStatus.last_changed) {
        return null;
      }

      // Otherwise, we convert the last_changed field to a Date
      eventStatus.last_changed = new Date(eventStatus.last_changed);

      // ... and write it to Firestore.
      return userStatusFirestoreRef.set(eventStatus);
    });

আপনি এই ফাংশনটি ডিপ্লয় করলে, আপনার কাছে 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 অ্যাপে উপস্থিতি যোগ করার জন্য Realtime Database ব্যবহার করা স্কেলেবল এবং কার্যকর হলেও এর কিছু সীমাবদ্ধতা রয়েছে:

  • ডিবাউন্সিং - Cloud Firestore-এ রিয়েল-টাইম পরিবর্তন শোনার সময়, এই সমাধান একাধিক পরিবর্তন ট্রিগার করতে পারে। এইসব পরিবর্তনের ফলে আপনার চাহিদার চেয়ে বেশি ইভেন্ট ট্রিগার হলে, ম্যানুয়ালি Cloud Firestore ইভেন্ট ডিবাউন্স করুন।
  • কানেক্টিভিটি - এই প্রয়োগ Realtime Database-এর সাথে কানেক্টিভিটি পরিমাপ করে, Cloud Firestore-এর সাথে নয়। প্রতিটি ডেটাবেসের সাথে কানেকশন স্ট্যাটাস একই না হলে, এই সমাধানটি ভুল উপস্থিতি স্ট্যাটাস রিপোর্ট করতে পারে।
  • Android - Android-এ, ৬০ সেকেন্ড কোনও অ্যাক্টিভিটি না হলে, Realtime Database ব্যাকএন্ড থেকে ডিসকানেক্ট হয়ে যায়। অ্যাক্টিভ না থাকার অর্থ হল কোনও ওপেন লিসেনার বা বাকি থাকা অপারেশন নেই। কানেকশন চালু রাখতে, আমরা আপনাকে .info/connected ছাড়াও অন্য কোনও পাথে ভ্যালু ইভেন্ট লিসেনার যোগ করতে সাজেস্ট করি। যেমন, প্রতিটি সেশনের শুরুতে আপনি FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() করতে পারেন। আরও তথ্যের জন্য, কানেকশন স্টেট শনাক্ত করা দেখুন।