Skip to main content
Markdown

Live updates and notifications

A page that changes as things happen: an order moving from packed to shipped, a dashboard of the sales of the day, the progress of an export, and a notice that reaches one user in every tab and app they have open. Your server sends once it has saved the change, every page that follows the thing updates at once, and a page that was away for a moment gets what it missed.

Two browsers side by side: Ann marks order 1042 as shipped on the staff page of the shop, and the order page of Ben shows Shipped at once, without a reload

The rooms​

A room for each thing a page follows, named after it: order:1042 for an order, sales for the dashboard of the day, job:9 for an export. Your server sends to a room, and every page in it gets what was sent.

Your server decides who may follow what. A page that joins a room asks your server first, and a rule answers: a function your server registers once for every room whose name fits a pattern, such as order:{id}. WordPlus Cloud then holds the page to the answer (permissions). These rules say:

  1. An order is followed only by the customer who placed it.
  2. The sales dashboard only by your staff.
  3. An export only by the user who started it.
// 1. An order, such as order:1042: only its customer.
realtime.room( 'order:{id}', { history: 20 }, async ( user, { id } ) => ( await findOrder( id ) )?.userId === user );

// 2. The sales dashboard: only your staff.
realtime.room( 'sales', { history: 1 }, ( user ) => isStaff( user ) );

// 3. An export, such as job:9: only the user who started it.
realtime.room( 'job:{id}', { history: 1 }, async ( user, { id } ) => ( await findJob( id ) )?.userId === user );
  • history keeps the last updates of a room for 5 minutes, for a page that reloads or reconnects: 20 of an order, and the last one of the dashboard and of an export, so the page shows where things are.
  • Anyone else is refused, whatever their page tries.

Sending from your server​

After the change is saved, from the code that saved it:

await realtime.to( 'order:' + order.id ).emit( 'updated', { status: 'shipped', tracking: order.tracking } );
await realtime.to( 'sales' ).emit( 'sale', { total: todayTotal } );
await realtime.to( 'job:' + job.id ).emit( 'progress', { done: 340, total: 1000 } );
await realtime.toUser( order.userId ).emit( 'notice', { text: 'Your order shipped' } ); // every tab and app they have open

A worker that sends many updates sends them together: realtime.batch( [ … ] ), up to 100 events a request (sending).

The page​

const order = rt.join( 'order:42' );
order.on( 'updated', ( data ) => showStatus( data.status ) );

const job = rt.join( 'job:9' );
job.on( 'progress', ( { done, total } ) => ( bar.value = done / total ) );

rt.on( 'notice', ( data ) => toast( data.text ) ); // what your server sends this user

In React:

const [ status, setStatus ] = useState( order.status );
useRoom( `order:${ order.id }`, { updated: ( data ) => setStatus( data.status ) } );
useUserEvent( 'notice', ( data ) => toast( data.text ) );
  • A reconnect gets what it missed, in order, from the history of the room: a phone that went through a tunnel, a laptop that woke up. history: 1 keeps only the latest, which is all a progress bar needs.
  • Send after you save. A failed send is a missed live update, never a lost change: the page reads the order from your server when it opens.
  • Not back to the tab that made the change: except( tab ), with the X-Realtime-Tab header the page sends (sending).
  • A user with nothing open gets nothing from toUser(). Keep what they must see in your database, and show it when they come back.

Variations​

  • A board many people watch, such as the scores of a game or the stock of a sale: one public room (public: true), which a page joins without signing in and a CDN may cache.
  • A count of who looks at it: add count: true, and the page shows room.count (who is online and who is here).
  • The same updates in an iOS or Android app: the app joins the same rooms (iOS, Android).
  • In a WordPress plugin: wordplus_realtime()->room() and ->to()->emit() (WordPress rooms).