แสดงสถานะ user online ด้วย Firebase (1)
Tags: firebase, presence
Published: 2020-02-23 14:35:26

---
title: แสดงสถานะ user online ด้วย Firebase (1)
published: true
description: แสดงสถานะ user online ด้วย Firebase (RTDB, Authen, และ Cloud Function) (1)
tags: firebase, presence
---

การแสดงสถานะ user online (หรือเรียกอีกอย่างว่า presence) คือการแสดงให้ admin เห็นว่า user A กำลัง online และใช้งาน app ของเราอยู่นะ และทันทีที่ user A ปิด browser หรือ logout ก็อัพเดทสถานะของ user A ทันทีว่า offline ซึ่งส่วนมากจะใช้ในระบบที่ต้องการเห็นสถานะ online/offline ของ user เช่น chat หรือพวก กระดานสนทนา อย่างเช่น discord 

โชคดีที่ Firebase สามารถทำ presence ได้ และมีตัวอย่างโค้ดให้ด้วย
ขอลอกมาให้ดูดังนี้

```js
// ดึง uid มาใช้งาน
const uid = firebase.auth().currentUser.uid
// สร้าง RTDB object เอาไว้ใช้งาน
const RTDB = firebase.database().ref()

// node พิเศษ เอามาดูได้ว่า uid ปัจจุบัน connect กับ Firebase อยู่หรือปล่าว
const amOnline = RTDB.child('.info/connected')
// ให้ RTDB ชี้ไปที่ /presence/{uid}
const presenceRef = RTDB.child('presence').child(uid)
// ให้ RTDB ชี้ไปที่ /sessions
const sessionsRef = RTDB.child('sessions')

amOnline.on('value', snapShot => {
  // ถ้ายังไม่ได้ connect กับ Firebase ก็ยังไม่ต้องทำอะไร
  if (!snapShot.val()) return

  // connect ได้แล้วสร้าง presence ได้เลย
  presenceRef.set(true)
  // ถ้า disconnect เมื่อไหร่ก็ลบไปเลย
  presenceRef.onDisconnect().remove()

  // สร้าง session ขึ้นมาใหม่ โดยเก็บ uid และ startedAt
  const session = sessionsRef.push({
    startedAt: firebase.database.ServerValue.TIMESTAMP,
    uid: uid,
  })

  // ในกรณีที่ disconnect ให้เพิ่ม endedAt ใน session ที่เพิ่งสร้างมาเมื่อกี้
  session.child('endedAt')
    .onDisconnect()
    .set(firebase.database.ServerValue.TIMESTAMP)
})
```
จากโค้ดข้างบน
* เก็บข้อมูล session ของ user ไว้ใน `/sessions` โดยให้มี 
  * `startedAt` = เวลาที่สร้าง session (ใช้ `firebase.database.ServerValue.TIMESTAMP` ก็เหมือนใช้ `NOW()` ใน MySQL)
  * `endedAt` = เวลาที่ปิด browser, ปิด tab
  * `uid` = uid ของ user 
* เก็บข้อมูลสถานะ online/offline ที่ `/presence` โดยจะใช้ key = uid 
* ทำ subscribe node พิเศษที่ชื่อว่า `.info/connected` ซึ่งจะเกิด value เมื่อ user connect กับ Firebase 
* เมื่อ connect ได้แล้วก็จะเอา uid ไปสร้าง `/sessions` และ `/presence`
* โดย session ให้ subscribe disconnect event ไว้ว่าถ้าปิด browser เมื่อไหร่ก็อัพเดทเพิ่ม `endedAt` ลงไป
* และสำหรับ presence ก็ subscribe disconnect event ไว้ว่าถ้าปิด browser เมื่อไหร่ก็ลบตัวเองทันที

เวลาที่เปิด RTDB จะเห็นข้อมูลประมาณนี้ (สมมุติว่า uid = XXX)
```
https://app.firebaseio.com/
+- app
   +- presence
      +- XXX: true
   +- sessions
      +- M0m-2FnKgEok1UJRg
         +- startedAt: 1582460581136
         +- uid: XXX
```

ส่วนการเอาไปใช้งาน
แค่ให้ Firebase ดึงข้อมูลจาก `RTDB/presence/{uid}` 
ถ้าหาเจอก็แสดงว่ากำลัง online อยู่

## ยังไม่สมบูรณ์ มีอะไรต้องแก้อีกนะ
แต่สำหรับโค้ดตัวอย่างมีจุดอ่อนดังนี้
1. กรณีที่ user reload page จะทำให้สถานะหยุด online แป๊ปนึง เพราะ Firebase จะโค้ดจะสร้าง session ใหม่
2. กรณีที่ user เปิดหลาย tab และปิดไปสัก tab นึง จะทำให้สถานะกลายเป็น offline ทั้งๆที่เปิดบาง tab ค้างไว้
3. กรณีที่ login หลาย device หรือหลาย browser ก็จะได้คล้ายๆกับข้อ 2.

ไว้รอบหน้าจะมาเล่าให้ฟังว่าแก้ไขอย่างไร :D

{% link ilumin/user-online-firebase-2-pa6 %}

## Note
* ดัดแปลงมาจาก [Build Presence in Cloud Firestore](https://firebase.google.com/docs/firestore/solutions/presence)