Presence & Online Status — Realtime

Presence & Online Status Menampilkan siapa yang online — green dot di Slack, "last seen" di WhatsApp. Terlihat sederhana tapi surprisingly complex di skala…

Presence & Online Status

Menampilkan siapa yang online — green dot di Slack, "last seen" di WhatsApp. Terlihat sederhana tapi surprisingly complex di skala besar.

Basic Implementation

// Server: track connected users
const onlineUsers = new Map(); // userId → Set of socketIds

io.on("connection", (socket) => {
  const userId = socket.data.userId;

  // Mark online
  if (!onlineUsers.has(userId)) onlineUsers.set(userId, new Set());
  onlineUsers.get(userId).add(socket.id);

  // Broadcast status change
  io.emit("presence:online", { userId });

  socket.on("disconnect", () => {
    onlineUsers.get(userId)?.delete(socket.id);

    // Only mark offline if no more connections
    if (onlineUsers.get(userId)?.size === 0) {
      onlineUsers.delete(userId);
      io.emit("presence:offline", { userId });
    }
  });
});

// API: get online users
app.get("/api/presence", (req, res) => {
  res.json([...onlineUsers.keys()]);
});

Heartbeat Pattern

// Client sends heartbeat setiap 30 detik
setInterval(() => {
  socket.emit("heartbeat");
}, 30000);

// Server: mark user as "away" jika no heartbeat > 60s
// Status: online → away → offline

Scalable Presence (Redis)

// Single server: Map works
// Multiple servers: use Redis

// On connect
await redis.sadd("online_users", userId);
await redis.set(`presence:${userId}`, JSON.stringify({
  status: "online",
  lastSeen: Date.now(),
  device: socket.handshake.headers["user-agent"],
}));

// On disconnect
await redis.srem("online_users", userId);
await redis.set(`presence:${userId}`, JSON.stringify({
  status: "offline",
  lastSeen: Date.now(),
}));

// Check status
const isOnline = await redis.sismember("online_users", friendId);

Privacy Considerations

Yang akan kamu pelajari