← กรณีการใช้งาน

การแจ้งเตือนในแอปแบบเรียลไทม์

แสดงการตอบกลับ การกล่าวถึง และการอนุมัติทันทีที่เกิดขึ้นในทุกแท็บที่เปิดอยู่ และส่งการแจ้งเตือนแบบพุชแทนเมื่อผู้ใช้ออฟไลน์

ผู้ใช้สังเกตได้เมื่อผลิตภัณฑ์ตอบสนองทันทีที่มีบางอย่างเกิดขึ้น เช่น มีคนแสดงความคิดเห็นในโพสต์ คำขอได้รับอนุมัติ หรือเพื่อนร่วมทีมกล่าวถึง การโพลล์ทุก 30 วินาทีเพื่อเช็กเรื่องเหล่านี้ทั้งช้าและเปลืองคำขอ เมื่อใช้ Hibira เซิร์ฟเวอร์ของคุณส่งการแจ้งเตือนเพียงครั้งเดียว แล้วทุกแท็บและทุกอุปกรณ์ที่ผู้ใช้เปิดอยู่จะแสดงผลภายในไม่กี่มิลลิวินาที เมื่อผู้ใช้ออฟไลน์ การแจ้งเตือนแบบพุชจะรับหน้าที่ต่อ

สิ่งที่ผู้ใช้ได้รับ

  • ป้ายตัวเลขบนกระดิ่งและข้อความแจ้งเตือนที่อัปเดตทันทีในทุกแท็บที่เปิดอยู่
  • การแจ้งเตือนเดียวกันบนโทรศัพท์เมื่อปิดแอปอยู่
  • เห็นความเคลื่อนไหวใหม่โดยไม่ต้องรีโหลด และตัวนับไม่ค้างเป็นค่าเก่า

ฟีเจอร์ที่ใช้

  • การยืนยันตัวตนผู้ใช้ (pusher.signin()) ผู้ใช้แต่ละคนจึงมีช่องของตัวเองที่มีแต่เขาอ่านได้
  • sendToUser ใน SDK เซิร์ฟเวอร์ทางการ ส่งถึงทุกซ็อกเก็ตของผู้ใช้คนนั้นบนทุกโหนด
  • การแจ้งเตือนแบบพุช ผ่าน API ที่เข้ากันได้กับ Beams สำหรับผู้ใช้ที่ไม่ได้เชื่อมต่ออยู่
  • ช่องส่วนตัวแบบเข้ารหัส (private-encrypted-) เมื่อการแจ้งเตือนมีข้อมูลที่ควรเข้ารหัสแบบต้นทางถึงปลายทาง

ขั้นที่ 1: ให้ผู้ใช้ลงชื่อเข้าใช้บนซ็อกเก็ต

เพิ่มเส้นทางยืนยันตัวตนผู้ใช้ไว้ข้างเส้นทางยืนยันสิทธิ์ช่อง เส้นทางนี้ทำงานบนเซิร์ฟเวอร์ของคุณและใช้ secret ของแอป

// Express
app.post('/pusher/user-auth', requireLogin, express.urlencoded({ extended: false }), (req, res) => {
  const auth = pusher.authenticateUser(req.body.socket_id, {
    id: String(req.user.id),
    user_info: { name: req.user.name },
  });
  res.json(auth);
});

ในเบราว์เซอร์ ให้เชื่อมต่อตามคู่มือ JavaScript แล้วลงชื่อเข้าใช้:

const pusher = new Pusher(import.meta.env.VITE_PUSHER_APP_KEY, {
  cluster: 'ap',
  wsHost: 'ws.hibira.io',
  wsPort: 443,
  wssPort: 443,
  forceTLS: true,
  enabledTransports: ['ws', 'wss'],
  userAuthentication: { endpoint: '/pusher/user-auth', transport: 'ajax' },
});
pusher.signin();

pusher.user.bind('notification', (n) => {
  bell.increment();
  toast.show(n.title, { href: n.url });
});

ขั้นที่ 2: ส่งจากเซิร์ฟเวอร์

บันทึกการแจ้งเตือนลงฐานข้อมูลก่อน เพื่อให้รายการยังอยู่หลังรีโหลด แล้วจึงส่ง:

const n = await db.notifications.create({ userId, title: 'Ann replied to your comment', url: '/posts/42#c7' });
await pusher.sendToUser(String(userId), 'notification', { id: n.id, title: n.title, url: n.url });

sendToUser หนึ่งครั้งไปถึงทุกแท็บและทุกอุปกรณ์ของผู้ใช้ และนับเป็นหนึ่งข้อความ ไม่ว่าจะมีกี่ซ็อกเก็ตที่ได้รับ

ขั้นที่ 3: ใช้พุชแทนเมื่อผู้ใช้ออฟไลน์

ให้ทุกหน้าที่ลงชื่อเข้าใช้แล้วสมัครรับช่องส่วนตัว เช่น private-user-42 และให้ endpoint ของ webhook รับ channel_occupied กับ channel_vacated เซิร์ฟเวอร์ของคุณก็จะรู้ว่าใครออนไลน์อยู่โดยไม่ต้องถาม:

app.post('/pusher/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
  const webhook = pusher.webhook({ headers: req.headers, rawBody: req.body.toString() });
  if (!webhook.isValid()) return res.sendStatus(401);
  for (const e of webhook.getEvents()) {
    const id = e.channel.match(/^private-user-(.+)$/)?.[1];
    if (id) await db.users.update(id, { online: e.name === 'channel_occupied' });
  }
  res.sendStatus(200);
});

เมื่อผู้ใช้ออฟไลน์ ให้ส่งไปยังอุปกรณ์ของเขาตามคู่มือการแจ้งเตือนแบบพุช:

if (!(await db.users.isOnline(userId))) {
  await beams.publishToUsers([String(userId)], {
    web: { notification: { title: n.title, deep_link: 'https://your-site.example' + n.url } },
    fcm: { notification: { title: n.title } },
    apns: { aps: { alert: { title: n.title } } },
  });
}

เคล็ดลับ

  • ให้ข้อมูลที่ส่งมีขนาดเล็ก (ID ชื่อเรื่อง และลิงก์) แล้วค่อยโหลดรายละเอียดเมื่อผู้ใช้เปิดดู อีเวนต์มีขนาดได้ไม่เกิน 10 KB (แผน Free 4 KB)
  • ทำเครื่องหมายว่าอ่านแล้วบนเซิร์ฟเวอร์และส่งอีเวนต์ notifications-read ป้ายตัวเลขจะหายไปพร้อมกันทุกแท็บ
  • ข้อมูลสุขภาพ การเงิน หรือบุคคล ให้ส่งไปที่ช่อง private-encrypted-user-42 แทน Hibira ส่งต่อเพียงข้อความที่เข้ารหัสแล้ว
  • ในแผน Free ให้เพิ่มโดเมนของเว็บไซต์เป็นต้นทางที่อนุญาตในการตั้งค่าแอป